Skip to content

Commit c4728db

Browse files
Merge pull request #522 from preactjs/default-loaders
Allow plugins to overwrite built-in loaders
2 parents de5293a + 31c83e7 commit c4728db

File tree

17 files changed

+218
-29
lines changed

17 files changed

+218
-29
lines changed

.changeset/spotty-terms-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'wmr': patch
3+
---
4+
5+
Allow plugins to overwrite url and json loading behavior. Default loading semantics will only apply if no other prefix is present on the import specifier
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { IMPLICIT_URL } from '../plugins/url-plugin.js';
2+
import { transformImports } from './transform-imports.js';
3+
4+
/**
5+
* Add default loaders to import specifiers if none are
6+
* present already.
7+
* @returns {import("wmr").Plugin}
8+
*/
9+
export function defaultLoaders() {
10+
return {
11+
name: 'default-loaders',
12+
async transform(code, id) {
13+
return await transformImports(code, id, {
14+
resolveId(specifier) {
15+
const hasPrefix = /^[-\w]+:/.test(specifier);
16+
17+
if (!hasPrefix) {
18+
if (specifier.endsWith('.json')) {
19+
return `json:${specifier}`;
20+
} else if (IMPLICIT_URL.test(specifier)) {
21+
return `url:${specifier}`;
22+
}
23+
}
24+
return null;
25+
}
26+
});
27+
}
28+
};
29+
}

packages/wmr/src/lib/plugins.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import copyAssetsPlugin from '../plugins/copy-assets-plugin.js';
2020
import nodeBuiltinsPlugin from '../plugins/node-builtins-plugin.js';
2121
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
2222
import visualizer from 'rollup-plugin-visualizer';
23+
import { defaultLoaders } from './default-loaders.js';
2324

2425
/**
2526
* @param {import("wmr").Options} options
@@ -74,6 +75,9 @@ export function getPlugins(options) {
7475

7576
...plugins.slice(split),
7677

78+
// Apply default loaders to unprefixed paths
79+
defaultLoaders(),
80+
7781
production && optimizeGraphPlugin({ publicPath }),
7882
minify && minifyCssPlugin({ sourcemap }),
7983
production && copyAssetsPlugin({ cwd }),

packages/wmr/src/plugins/json-plugin.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ export default function jsonPlugin({ cwd }) {
2020
return {
2121
name: 'json-plugin',
2222
async resolveId(id, importer) {
23-
if (id[0] === '\0' || !id.endsWith('.json')) return;
23+
if (!id.startsWith(IMPORT_PREFIX)) return;
2424

25-
// always process prefixed imports
26-
if (id.startsWith(IMPORT_PREFIX)) {
27-
id = id.slice(IMPORT_PREFIX.length);
28-
}
25+
id = id.slice(IMPORT_PREFIX.length);
2926

3027
const resolved = await this.resolve(id, importer, { skipSelf: true });
3128
return resolved && INTERNAL_PREFIX + resolved.id;
@@ -34,7 +31,7 @@ export default function jsonPlugin({ cwd }) {
3431
// an internal prefix we can get rid of the whole
3532
// loading logic here and let rollup handle that part.
3633
async load(id) {
37-
if (!id.endsWith('.json')) return null;
34+
if (!id.startsWith(INTERNAL_PREFIX)) return null;
3835

3936
if (id.startsWith(INTERNAL_PREFIX)) {
4037
id = id.slice(INTERNAL_PREFIX.length);

packages/wmr/src/plugins/url-plugin.js

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { relative, basename, sep, posix } from 'path';
22
import { promises as fs } from 'fs';
33

4-
const IMPLICIT_URL = /\.(?:png|jpe?g|gif|webp|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$/i;
4+
export const IMPLICIT_URL = /\.(?:png|jpe?g|gif|webp|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$/i;
55

66
const escapeUrl = url => url.replace(/#/g, '%23').replace(/'/g, "\\'").replace(/"/g, '\\"');
77

@@ -12,23 +12,15 @@ const escapeUrl = url => url.replace(/#/g, '%23').replace(/'/g, "\\'").replace(/
1212
* @returns {import('rollup').Plugin}
1313
*/
1414
export default function urlPlugin({ inline, cwd } = {}) {
15+
const PREFIX = 'url:';
16+
const INTERNAL_PREFIX = '\0url:';
17+
1518
return {
1619
name: 'url-plugin',
1720
async resolveId(id, importer) {
18-
if (id[0] === '\0' || id[0] === '\b') return;
21+
if (!id.startsWith(PREFIX)) return;
1922

20-
if (id.startsWith('url:')) {
21-
// explicit `url:` prefix
22-
id = id.slice(4);
23-
} else if (!inline) {
24-
// In dev mode, we eagerly process un-prefixed non-source files.
25-
// This would be an infinite Rollup loop for prod, which instead uses load().
26-
return;
27-
} else if (!IMPLICIT_URL.test(id)) {
28-
// implicitly apply `url:` all files except source code
29-
return;
30-
// ^ no prefix, not an implicit URL (in dev) - nothing to do.
31-
}
23+
id = id.slice(PREFIX.length);
3224

3325
const resolved = await this.resolve(id, importer, { skipSelf: true });
3426
if (!resolved) return;
@@ -41,18 +33,13 @@ export default function urlPlugin({ inline, cwd } = {}) {
4133
external: true
4234
};
4335
}
44-
resolved.id = `\0url:${resolved.id}`;
36+
resolved.id = `${INTERNAL_PREFIX}${resolved.id}`;
4537
return resolved;
4638
},
4739
async load(id) {
48-
if (id.startsWith('\0url:')) {
49-
// explicit `url:` prefix (generated by our resolveId):
50-
id = id.slice(5);
51-
} else if (!IMPLICIT_URL.test(id)) {
52-
// implicitly apply `url:` images, fonts, etc.
53-
return;
54-
// ^ no prefix, not an implicit URL - nothing to do
55-
}
40+
if (!id.startsWith(INTERNAL_PREFIX)) return;
41+
42+
id = id.slice(INTERNAL_PREFIX.length);
5643

5744
const fileId = this.emitFile({
5845
type: 'asset',

packages/wmr/test/fixtures.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ describe('fixtures', () => {
7676
});
7777
});
7878

79+
it('should allow overwriting default json loader', async () => {
80+
await loadFixture('overwrite-loader-json', env);
81+
instance = await runWmrFast(env.tmp.path);
82+
const text = await getOutput(env, instance);
83+
expect(text).toMatch(/foobarbaz/);
84+
});
85+
86+
it('should allow overwriting default url loader', async () => {
87+
await loadFixture('overwrite-loader-url', env);
88+
instance = await runWmrFast(env.tmp.path);
89+
const text = await getOutput(env, instance);
90+
91+
expect(text).toMatch(/my-url: \/foo\.svg\?asset/);
92+
expect(text).toMatch(/url: \/foo\.svg\?asset/);
93+
expect(text).toMatch(/fallback: \/foo\.svg\?asset/);
94+
});
95+
7996
describe('empty', () => {
8097
it('should print warning for missing index.html file in public dir', async () => {
8198
await loadFixture('empty', env);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"bar": "bar"
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"baz": "baz"
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"foo": "foo"
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div id="app"></div>
2+
<script type="module" src="index.js"></script>

0 commit comments

Comments
 (0)