From 951b3a6c26db8ec6d47d709c70ecdc917b452b67 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 29 Aug 2021 22:03:45 +0200 Subject: [PATCH] Fix virtual specifiers starting with `/@` not being supported --- .../src/plugins/wmr/styles/styles-plugin.js | 20 +++++++++++------ packages/wmr/src/wmr-middleware.js | 14 ++++++++++-- packages/wmr/test/fixtures.test.js | 12 ++++++++++ .../fixtures/virtual-id-at/public/index.html | 2 ++ .../fixtures/virtual-id-at/public/index.js | 1 + .../fixtures/virtual-id-at/wmr.config.mjs | 22 +++++++++++++++++++ packages/wmr/test/production.test.js | 18 +++++++++++++++ 7 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 packages/wmr/test/fixtures/virtual-id-at/public/index.html create mode 100644 packages/wmr/test/fixtures/virtual-id-at/public/index.js create mode 100644 packages/wmr/test/fixtures/virtual-id-at/wmr.config.mjs diff --git a/packages/wmr/src/plugins/wmr/styles/styles-plugin.js b/packages/wmr/src/plugins/wmr/styles/styles-plugin.js index 08ef0f5ca..d8c6e911d 100644 --- a/packages/wmr/src/plugins/wmr/styles/styles-plugin.js +++ b/packages/wmr/src/plugins/wmr/styles/styles-plugin.js @@ -99,16 +99,22 @@ export default function wmrStylesPlugin({ root, hot, production, alias, sourcema } }); + // Preserve asset path to avoid file clashes: + // foo/styles.module.css + // bar/styles.module.css + // Both files above should not overwrite each other. + // We don't have that problem in production, because + // assets are hashed + let fileName; + if (!production) { + fileName = id.startsWith('/@') ? `@id/${id}` : id; + fileName += '?asset'; + } + const ref = this.emitFile({ type: 'asset', name: basename(id).replace(/\.(s[ac]ss|less)$/, '.css'), - // Preserve asset path to avoid file clashes: - // foo/styles.module.css - // bar/styles.module.css - // Both files above should not overwrite each other. - // We don't have that problem in production, because - // assets are hashed - fileName: !production ? id + '?asset' : undefined, + fileName, source }); diff --git a/packages/wmr/src/wmr-middleware.js b/packages/wmr/src/wmr-middleware.js index 47c968782..c52db7604 100644 --- a/packages/wmr/src/wmr-middleware.js +++ b/packages/wmr/src/wmr-middleware.js @@ -242,6 +242,12 @@ export default function wmrMiddleware(options) { // Virtual paths have no exact file match, so we don't set `file` hasIdPrefix = true; id = path.slice('/@id/'.length); + + // Add back leading slash if it was part of the virtual id. + // Example: `/@windicss/windi.css` + if (req.path.startsWith('/@id//')) { + id = '/' + id; + } } else if (path.startsWith('/@alias/')) { id = posix.normalize(path.slice('/@alias/'.length)); @@ -534,7 +540,11 @@ export const TRANSFORMS = { const resolved = await NonRollup.resolveId(spec, file); if (resolved) { spec = typeof resolved == 'object' ? resolved.id : resolved; - if (/^(\/|\\|[a-z]:\\)/i.test(spec)) { + // Some rollup plugins use absolute paths as virtual identifiers :/ + // Example: `/@windicss/windi.css` + if (/^\/@/.test(spec)) { + spec = `/@id/${spec}`; + } else if (/^(\/|\\|[a-z]:\\)/i.test(spec)) { spec = relative(dirname(file), spec).split(sep).join(posix.sep); if (!/^\.?\.?\//.test(spec)) { spec = './' + spec; @@ -579,7 +589,7 @@ export const TRANSFORMS = { if (aliased) spec = aliased; } - if (!spec.startsWith('/@alias/') && !/^\0?\.?\.?[/\\]/.test(spec)) { + if (!spec.startsWith('/@id/') && !spec.startsWith('/@alias/') && !/^\0?\.?\.?[/\\]/.test(spec)) { // Check if the spec is an alias const aliased = matchAlias(alias, spec); if (aliased) spec = aliased; diff --git a/packages/wmr/test/fixtures.test.js b/packages/wmr/test/fixtures.test.js index 638f126e6..6a32fa0f4 100644 --- a/packages/wmr/test/fixtures.test.js +++ b/packages/wmr/test/fixtures.test.js @@ -111,6 +111,18 @@ describe('fixtures', () => { expect(text).toMatch('it works'); }); + // Issue #811 + it('should support virtual ids starting with /@', async () => { + await loadFixture('virtual-id-at', env); + instance = await runWmrFast(env.tmp.path); + await getOutput(env, instance); + + await waitForPass(async () => { + const color = await env.page.$eval('h1', el => getComputedStyle(el).color); + expect(color).toBe('rgb(255, 0, 0)'); + }); + }); + it('should prioritize extensionless import by extension array', async () => { await loadFixture('import-priority', env); instance = await runWmrFast(env.tmp.path); diff --git a/packages/wmr/test/fixtures/virtual-id-at/public/index.html b/packages/wmr/test/fixtures/virtual-id-at/public/index.html new file mode 100644 index 000000000..90bddcd02 --- /dev/null +++ b/packages/wmr/test/fixtures/virtual-id-at/public/index.html @@ -0,0 +1,2 @@ +

foo

+ diff --git a/packages/wmr/test/fixtures/virtual-id-at/public/index.js b/packages/wmr/test/fixtures/virtual-id-at/public/index.js new file mode 100644 index 000000000..27ca024e4 --- /dev/null +++ b/packages/wmr/test/fixtures/virtual-id-at/public/index.js @@ -0,0 +1 @@ +import 'virtual:windi.css'; diff --git a/packages/wmr/test/fixtures/virtual-id-at/wmr.config.mjs b/packages/wmr/test/fixtures/virtual-id-at/wmr.config.mjs new file mode 100644 index 000000000..74778a0d8 --- /dev/null +++ b/packages/wmr/test/fixtures/virtual-id-at/wmr.config.mjs @@ -0,0 +1,22 @@ +export default function () { + const ID = 'virtual:windi.css'; + const VIRTUAL_ID = '/@windicss/windi.css'; + + return { + plugins: [ + { + name: 'virtual-id-plugin', + resolveId(id) { + if (id === ID) { + return VIRTUAL_ID; + } + }, + load(id) { + if (id === VIRTUAL_ID) { + return `h1 { color: red; }`; + } + } + } + ] + }; +} diff --git a/packages/wmr/test/production.test.js b/packages/wmr/test/production.test.js index 0bbe64801..984ecf04a 100644 --- a/packages/wmr/test/production.test.js +++ b/packages/wmr/test/production.test.js @@ -128,6 +128,24 @@ describe('production', () => { expect(text).toMatch(/it works/); }); + // Issue #811 + it('should support virtual ids starting with /@', async () => { + await loadFixture('virtual-id-at', env); + instance = await runWmr(env.tmp.path, 'build'); + const code = await instance.done; + expect(code).toEqual(0); + + const { address, stop } = serveStatic(path.join(env.tmp.path, 'dist')); + cleanup.push(stop); + + await env.page.goto(address, { + waitUntil: ['networkidle0', 'load'] + }); + + const color = await env.page.$eval('h1', el => getComputedStyle(el).color); + expect(color).toBe('rgb(255, 0, 0)'); + }); + it("should preserve './' for relative specifiers", async () => { await loadFixture('plugin-resolve', env); instance = await runWmr(env.tmp.path, 'build');