From f285fc57a6c95a2d0107b9a552be4134cfe0b730 Mon Sep 17 00:00:00 2001 From: Lars den Bakker Date: Fri, 16 Oct 2020 16:29:47 +0200 Subject: [PATCH 1/3] feat(test-runner): run user plugins after builtin plugins --- .changeset/short-months-look.md | 5 +++++ packages/test-runner/src/startTestRunner.ts | 22 ++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 .changeset/short-months-look.md diff --git a/.changeset/short-months-look.md b/.changeset/short-months-look.md new file mode 100644 index 000000000..6270a2fd6 --- /dev/null +++ b/.changeset/short-months-look.md @@ -0,0 +1,5 @@ +--- +'@web/test-runner': patch +--- + +ensure user plugins are run after builtin plugins diff --git a/packages/test-runner/src/startTestRunner.ts b/packages/test-runner/src/startTestRunner.ts index a8299a7b7..8f70ef09e 100644 --- a/packages/test-runner/src/startTestRunner.ts +++ b/packages/test-runner/src/startTestRunner.ts @@ -201,25 +201,25 @@ export async function startTestRunner(options: StartTestRunnerOptions = {}) { config.plugins = []; } - if (config.esbuildTarget) { - config.plugins.push(esbuildPlugin(config.esbuildTarget)); - } - - if (config.nodeResolve) { - const userOptions = typeof config.nodeResolve === 'object' ? config.nodeResolve : undefined; - config.plugins!.push(nodeResolvePlugin(rootDir, config.preserveSymlinks, userOptions)); - } - // plugin with a noop transformImport hook, this will cause the dev server to analyze modules and // catch syntax errors. this way we still report syntax errors when the user has no flags enabled - config.plugins.push({ + config.plugins.unshift({ name: 'syntax-checker', transformImport() { return undefined; }, }); - config.plugins.push(setViewportPlugin(), emulateMediaPlugin(), setUserAgentPlugin()); + if (config.nodeResolve) { + const userOptions = typeof config.nodeResolve === 'object' ? config.nodeResolve : undefined; + config.plugins!.unshift(nodeResolvePlugin(rootDir, config.preserveSymlinks, userOptions)); + } + + if (config.esbuildTarget) { + config.plugins.unshift(esbuildPlugin(config.esbuildTarget)); + } + + config.plugins.unshift(setViewportPlugin(), emulateMediaPlugin(), setUserAgentPlugin()); const validatedConfig = validateCoreConfig(config); return defaultStartTestRunner(validatedConfig, groupConfigs, { autoExitProcess }); From c00fe3cfd816c0245182555914bd1437552efc20 Mon Sep 17 00:00:00 2001 From: Lars den Bakker Date: Fri, 16 Oct 2020 16:32:15 +0200 Subject: [PATCH 2/3] feat(dev-server): ensure user plugins are run after builtin plugins --- .changeset/short-months-look.md | 2 +- .changeset/sweet-starfishes-laugh.md | 6 ++++++ .../src/plugins/transformModuleImportsPlugin.ts | 11 ++++++++--- .../dev-server-core/src/server/createPlugins.ts | 8 ++++---- .../dev-server-core/src/server/createServer.ts | 14 +------------- packages/dev-server/src/config/parseConfig.ts | 14 +++++++------- 6 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 .changeset/sweet-starfishes-laugh.md diff --git a/.changeset/short-months-look.md b/.changeset/short-months-look.md index 6270a2fd6..ed6343795 100644 --- a/.changeset/short-months-look.md +++ b/.changeset/short-months-look.md @@ -2,4 +2,4 @@ '@web/test-runner': patch --- -ensure user plugins are run after builtin plugins +run user plugins after builtin plugins diff --git a/.changeset/sweet-starfishes-laugh.md b/.changeset/sweet-starfishes-laugh.md new file mode 100644 index 000000000..c717addfc --- /dev/null +++ b/.changeset/sweet-starfishes-laugh.md @@ -0,0 +1,6 @@ +--- +'@web/dev-server-core': patch +'@web/dev-server': patch +--- + +ensure user plugins are run after builtin plugins diff --git a/packages/dev-server-core/src/plugins/transformModuleImportsPlugin.ts b/packages/dev-server-core/src/plugins/transformModuleImportsPlugin.ts index 77a526dfc..124a49860 100644 --- a/packages/dev-server-core/src/plugins/transformModuleImportsPlugin.ts +++ b/packages/dev-server-core/src/plugins/transformModuleImportsPlugin.ts @@ -127,7 +127,6 @@ export async function transformImports( if (dynamicImportIndex === -1) { // static import - const importSpecifier = code.substring(start, end); const lines = code.slice(0, end).split('\n'); const line = lines.length; @@ -258,6 +257,7 @@ export function transformModuleImportsPlugin(plugins: Plugin[], rootDir: string) predicates.NOT(predicates.hasAttr('src')), ), ); + let transformed = false; for (const node of inlineModuleNodes) { const code = getTextContent(node); @@ -267,10 +267,15 @@ export function transformModuleImportsPlugin(plugins: Plugin[], rootDir: string) rootDir, importPlugins, ); - setTextContent(node, resolvedCode); + if (code !== resolvedCode) { + setTextContent(node, resolvedCode); + transformed = true; + } } - return { body: serializeHtml(documentAst) }; + if (transformed) { + return { body: serializeHtml(documentAst) }; + } } }, }; diff --git a/packages/dev-server-core/src/server/createPlugins.ts b/packages/dev-server-core/src/server/createPlugins.ts index f8c5b363e..8d082448e 100644 --- a/packages/dev-server-core/src/server/createPlugins.ts +++ b/packages/dev-server-core/src/server/createPlugins.ts @@ -11,13 +11,13 @@ export function createPlugins(config: DevServerCoreConfig) { plugins.push(mimeTypesPlugin(config.mimeTypes)); } - if (config.plugins?.some(pl => 'resolveImport' in pl || 'transformImport' in pl)) { - plugins.push(transformModuleImportsPlugin(config.plugins, config.rootDir)); - } - if (config.injectWebSocket && config.plugins?.some(pl => pl.injectWebSocket)) { plugins.push(webSocketsPlugin()); } + if (config.plugins?.some(pl => 'resolveImport' in pl || 'transformImport' in pl)) { + plugins.push(transformModuleImportsPlugin(config.plugins, config.rootDir)); + } + return plugins; } diff --git a/packages/dev-server-core/src/server/createServer.ts b/packages/dev-server-core/src/server/createServer.ts index d00fa1752..63508fb8a 100644 --- a/packages/dev-server-core/src/server/createServer.ts +++ b/packages/dev-server-core/src/server/createServer.ts @@ -32,19 +32,7 @@ export function createServer(cfg: DevServerCoreConfig, logger: Logger, fileWatch if (!cfg.plugins) { cfg.plugins = []; } - cfg.plugins.push(...plugins); - - // special case the legacy plugin, if it is given make sure the resolve module imports plugin - // runs before the legacy plugin because it compiles away module syntax. ideally we have a - // generic API for this, but we need to design that a bit more first - const indexOfLegacy = cfg.plugins.findIndex(p => p.name === 'legacy'); - let indexOfResolve = cfg.plugins.findIndex(p => p.name === 'resolve-module-imports'); - if (indexOfLegacy !== -1 && indexOfResolve !== -1) { - const legacy = cfg.plugins.splice(indexOfLegacy, 1)[0]; - // recompute after splicing - indexOfResolve = cfg.plugins.findIndex(p => p.name === 'resolve-module-imports'); - cfg.plugins.splice(indexOfResolve, 1, cfg.plugins[indexOfResolve], legacy); - } + cfg.plugins.unshift(...plugins); const middleware = createMiddleware(cfg, logger, fileWatcher); for (const m of middleware) { diff --git a/packages/dev-server/src/config/parseConfig.ts b/packages/dev-server/src/config/parseConfig.ts index a493b7e0b..bd610df18 100644 --- a/packages/dev-server/src/config/parseConfig.ts +++ b/packages/dev-server/src/config/parseConfig.ts @@ -71,20 +71,20 @@ export async function parseConfig( finalConfig.port = await getPortPromise({ port: 8000 }); } - // map flags to plugin - if (finalConfig?.esbuildTarget) { - finalConfig.plugins!.push(esbuildPlugin(finalConfig.esbuildTarget)); - } - if (finalConfig.nodeResolve) { const userOptions = typeof config.nodeResolve === 'object' ? config.nodeResolve : undefined; - finalConfig.plugins!.push( + finalConfig.plugins!.unshift( nodeResolvePlugin(finalConfig.rootDir!, config.preserveSymlinks, userOptions), ); } + // map flags to plugin + if (finalConfig?.esbuildTarget) { + finalConfig.plugins!.unshift(esbuildPlugin(finalConfig.esbuildTarget)); + } + if (finalConfig.watch) { - finalConfig.plugins!.push(watchPlugin()); + finalConfig.plugins!.unshift(watchPlugin()); } return finalConfig; From e06e436f005ff1dfddb01aba2dfce610fa651fe4 Mon Sep 17 00:00:00 2001 From: Lars den Bakker Date: Fri, 16 Oct 2020 16:32:36 +0200 Subject: [PATCH 3/3] fix(dev-server-legacy): only transform modules to systemjs --- .changeset/smart-spiders-smash.md | 5 +++ .../{createPlugins.ts => addPlugins.ts} | 16 +++---- .../src/server/createServer.ts | 18 +++++--- .../test/resolving.test.ts | 2 +- packages/dev-server-legacy/package.json | 1 + .../dev-server-legacy/src/babelTransform.ts | 12 ++++- packages/dev-server-legacy/src/constants.ts | 1 + .../src/injectPolyfillsLoader.ts | 7 ++- .../dev-server-legacy/src/legacyPlugin.ts | 18 +++++++- .../test/transform-html.test.ts | 45 +++++++++++++++---- .../test/transform-js.test.ts | 37 +++++++++++++-- packages/dev-server/src/config/parseConfig.ts | 3 +- 12 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 .changeset/smart-spiders-smash.md rename packages/dev-server-core/src/server/{createPlugins.ts => addPlugins.ts} (56%) create mode 100644 packages/dev-server-legacy/src/constants.ts diff --git a/.changeset/smart-spiders-smash.md b/.changeset/smart-spiders-smash.md new file mode 100644 index 000000000..83f28ffe0 --- /dev/null +++ b/.changeset/smart-spiders-smash.md @@ -0,0 +1,5 @@ +--- +'@web/dev-server-legacy': patch +--- + +only transform modules to systemjs diff --git a/packages/dev-server-core/src/server/createPlugins.ts b/packages/dev-server-core/src/server/addPlugins.ts similarity index 56% rename from packages/dev-server-core/src/server/createPlugins.ts rename to packages/dev-server-core/src/server/addPlugins.ts index 8d082448e..f2dea7e7d 100644 --- a/packages/dev-server-core/src/server/createPlugins.ts +++ b/packages/dev-server-core/src/server/addPlugins.ts @@ -1,23 +1,23 @@ import { DevServerCoreConfig } from '../DevServerCoreConfig'; -import { Plugin } from '../Plugin'; import { transformModuleImportsPlugin } from '../plugins/transformModuleImportsPlugin'; import { webSocketsPlugin } from '../web-sockets/webSocketsPlugin'; import { mimeTypesPlugin } from '../plugins/mimeTypesPlugin'; -export function createPlugins(config: DevServerCoreConfig) { - const plugins: Plugin[] = []; +export function addPlugins(config: DevServerCoreConfig) { + if (!config.plugins) { + config.plugins = []; + } if (config.mimeTypes && Object.keys(config.mimeTypes).length > 0) { - plugins.push(mimeTypesPlugin(config.mimeTypes)); + config.plugins.unshift(mimeTypesPlugin(config.mimeTypes)); } if (config.injectWebSocket && config.plugins?.some(pl => pl.injectWebSocket)) { - plugins.push(webSocketsPlugin()); + config.plugins.unshift(webSocketsPlugin()); } if (config.plugins?.some(pl => 'resolveImport' in pl || 'transformImport' in pl)) { - plugins.push(transformModuleImportsPlugin(config.plugins, config.rootDir)); + // transform module imports must happen after all other plugins did their regular transforms + config.plugins.push(transformModuleImportsPlugin(config.plugins, config.rootDir)); } - - return plugins; } diff --git a/packages/dev-server-core/src/server/createServer.ts b/packages/dev-server-core/src/server/createServer.ts index 63508fb8a..58d9b8b88 100644 --- a/packages/dev-server-core/src/server/createServer.ts +++ b/packages/dev-server-core/src/server/createServer.ts @@ -9,7 +9,7 @@ import net, { Server, Socket, ListenOptions } from 'net'; import { DevServerCoreConfig } from '../DevServerCoreConfig'; import { createMiddleware } from './createMiddleware'; import { Logger } from '../logger/Logger'; -import { createPlugins } from './createPlugins'; +import { addPlugins } from './addPlugins'; /** * A request handler that returns a 301 HTTP Redirect to the same location as the original @@ -28,11 +28,19 @@ function httpsRedirect(req: IncomingMessage, res: ServerResponse) { export function createServer(cfg: DevServerCoreConfig, logger: Logger, fileWatcher: FSWatcher) { const app = new Koa(); - const plugins = createPlugins(cfg); - if (!cfg.plugins) { - cfg.plugins = []; + addPlugins(cfg); + + // special case the legacy plugin, if it is given make sure the resolve module imports plugin + // runs before the legacy plugin because it compiles away module syntax. ideally we have a + // generic API for this, but we need to design that a bit more first + const indexOfLegacy = cfg.plugins!.findIndex(p => p.name === 'legacy'); + let indexOfResolve = cfg.plugins!.findIndex(p => p.name === 'resolve-module-imports'); + if (indexOfLegacy !== -1 && indexOfResolve !== -1) { + const legacy = cfg.plugins!.splice(indexOfLegacy, 1)[0]; + // recompute after splicing + indexOfResolve = cfg.plugins!.findIndex(p => p.name === 'resolve-module-imports'); + cfg.plugins!.splice(indexOfResolve, 1, cfg.plugins![indexOfResolve], legacy); } - cfg.plugins.unshift(...plugins); const middleware = createMiddleware(cfg, logger, fileWatcher); for (const m of middleware) { diff --git a/packages/dev-server-import-maps/test/resolving.test.ts b/packages/dev-server-import-maps/test/resolving.test.ts index a692a25b6..af0ae51a3 100644 --- a/packages/dev-server-import-maps/test/resolving.test.ts +++ b/packages/dev-server-import-maps/test/resolving.test.ts @@ -318,7 +318,7 @@ describe('resolving imports', () => { }); const text = await fetchText(`${host}/index.html`); - expectIncludes(text, ''); + expectIncludes(text, ''); server.stop(); }); diff --git a/packages/dev-server-legacy/package.json b/packages/dev-server-legacy/package.json index 278ae0883..b826d948b 100644 --- a/packages/dev-server-legacy/package.json +++ b/packages/dev-server-legacy/package.json @@ -45,6 +45,7 @@ "@babel/core": "^7.10.5", "@babel/plugin-proposal-dynamic-import": "^7.10.4", "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-transform-modules-systemjs": "^7.10.5", diff --git a/packages/dev-server-legacy/src/babelTransform.ts b/packages/dev-server-legacy/src/babelTransform.ts index 65b03ea7c..069e2e1aa 100644 --- a/packages/dev-server-legacy/src/babelTransform.ts +++ b/packages/dev-server-legacy/src/babelTransform.ts @@ -1,6 +1,6 @@ import { transformAsync, TransformOptions } from '@babel/core'; -export const config: TransformOptions = { +export const es5Config: TransformOptions = { caller: { name: '@web/dev-server-legacy', supportsStaticESM: true, @@ -28,6 +28,14 @@ export const config: TransformOptions = { require.resolve('@babel/plugin-syntax-import-meta'), require.resolve('@babel/plugin-syntax-class-properties'), require.resolve('@babel/plugin-syntax-numeric-separator'), + require.resolve('@babel/plugin-syntax-dynamic-import'), + ], +}; + +export const systemJsConfig: TransformOptions = { + ...es5Config, + plugins: [ + ...(es5Config.plugins ?? []), require.resolve('@babel/plugin-proposal-dynamic-import'), require.resolve('@babel/plugin-transform-modules-systemjs'), // systemjs adds template literals, we do systemjs after (potential) @@ -36,7 +44,7 @@ export const config: TransformOptions = { ], }; -export async function babelTransform(filename: string, source: string) { +export async function babelTransform(filename: string, source: string, config: TransformOptions) { const largeFile = source.length > 100000; const result = await transformAsync(source, { filename, diff --git a/packages/dev-server-legacy/src/constants.ts b/packages/dev-server-legacy/src/constants.ts new file mode 100644 index 000000000..467c52fc8 --- /dev/null +++ b/packages/dev-server-legacy/src/constants.ts @@ -0,0 +1 @@ +export const PARAM_TRANSFORM_SYSTEMJS = 'systemjs'; diff --git a/packages/dev-server-legacy/src/injectPolyfillsLoader.ts b/packages/dev-server-legacy/src/injectPolyfillsLoader.ts index 38e93522c..5aee2c8b7 100644 --- a/packages/dev-server-legacy/src/injectPolyfillsLoader.ts +++ b/packages/dev-server-legacy/src/injectPolyfillsLoader.ts @@ -9,6 +9,7 @@ import { GeneratedFile, File, } from 'polyfills-loader'; +import { PARAM_TRANSFORM_SYSTEMJS } from './constants'; import { findJsScripts } from './findJsScripts'; function findScripts(indexUrl: string, documentAst: DocumentAst) { @@ -22,13 +23,17 @@ function findScripts(indexUrl: string, documentAst: DocumentAst) { let src = getAttribute(scriptNode, 'src'); if (!src) { - src = `inline-script-${i}.js?source=${encodeURIComponent(indexUrl)}`; + const suffix = type === 'module' ? `&${PARAM_TRANSFORM_SYSTEMJS}=true` : ''; + src = `inline-script-${i}.js?source=${encodeURIComponent(indexUrl)}${suffix}`; inlineScripts.push({ path: src, type, content: getTextContent(scriptNode), }); inlineScriptNodes.push(scriptNode); + } else if (type === 'module') { + const separator = src.includes('?') ? '&' : '?'; + src = `${src}${separator}${PARAM_TRANSFORM_SYSTEMJS}=true`; } files.push({ diff --git a/packages/dev-server-legacy/src/legacyPlugin.ts b/packages/dev-server-legacy/src/legacyPlugin.ts index ba0b6e7b9..9fd328280 100644 --- a/packages/dev-server-legacy/src/legacyPlugin.ts +++ b/packages/dev-server-legacy/src/legacyPlugin.ts @@ -2,8 +2,9 @@ import { Plugin, Logger, getRequestFilePath, isInlineScriptRequest } from '@web/ import { GeneratedFile, PolyfillsConfig } from 'polyfills-loader'; import path from 'path'; import { isLegacyBrowser } from './isLegacyBrowser'; -import { babelTransform } from './babelTransform'; +import { babelTransform, es5Config, systemJsConfig } from './babelTransform'; import { injectPolyfillsLoader } from './injectPolyfillsLoader'; +import { PARAM_TRANSFORM_SYSTEMJS } from './constants'; interface inlineScripts { lastModified: string; @@ -103,8 +104,12 @@ export function legacyPlugin(options: LegacyPluginOptions = {}): Plugin { if (context.path.includes('/polyfills/')) { return; } + const config = + context.URL.searchParams.get(PARAM_TRANSFORM_SYSTEMJS) === 'true' + ? systemJsConfig + : es5Config; const filePath = getRequestFilePath(context, rootDir); - const transformed = await babelTransform(filePath, context.body); + const transformed = await babelTransform(filePath, context.body, config); context.body = transformed; return; } @@ -129,6 +134,15 @@ export function legacyPlugin(options: LegacyPluginOptions = {}): Plugin { }); } }, + + transformImport({ source, context }) { + if (!isLegacyBrowser(context, logger)) { + return; + } + + const prefix = source.includes('?') ? '&' : '?'; + return `${source}${prefix}${PARAM_TRANSFORM_SYSTEMJS}=true`; + }, }; } diff --git a/packages/dev-server-legacy/test/transform-html.test.ts b/packages/dev-server-legacy/test/transform-html.test.ts index 6feb70b69..87dd755c1 100644 --- a/packages/dev-server-legacy/test/transform-html.test.ts +++ b/packages/dev-server-legacy/test/transform-html.test.ts @@ -8,7 +8,8 @@ import { modernUserAgents, legacyUserAgents } from './userAgents'; const htmlBody = ` - + `; @@ -20,10 +21,11 @@ const inlineScriptHtmlBody = ` } + `; -describe('legacyPlugin()', function () { +describe('legacyPlugin - transform html', function () { this.timeout(10000); it(`does not do any work on a modern browser`, async () => { @@ -45,6 +47,7 @@ describe('legacyPlugin()', function () { const text = await fetchText(`${host}/index.html`, { headers: { 'user-agent': modernUserAgents['Chrome 78'] }, }); + expect(text.trim()).to.equal(htmlBody.trim()); server.stop(); }); @@ -71,11 +74,34 @@ describe('legacyPlugin()', function () { expectIncludes(text, 'function polyfillsLoader() {'); expectIncludes(text, "loadScript('./polyfills/regenerator-runtime."); expectIncludes(text, "loadScript('./polyfills/fetch."); - expectIncludes(text, "System.import('./foo.js');"); expectIncludes(text, "loadScript('./polyfills/systemjs."); server.stop(); }); + it(`injects systemjs param to inline modules`, async () => { + const { server, host } = await createTestServer({ + rootDir: __dirname, + plugins: [ + { + name: 'test', + serve(context) { + if (context.path === '/index.html') { + return htmlBody; + } + }, + }, + legacyPlugin(), + ], + }); + + const text = await fetchText(`${host}/index.html`, { + headers: { 'user-agent': legacyUserAgents['IE 11'] }, + }); + expectIncludes(text, "loadScript('./bar.js');"); + expectIncludes(text, "System.import('./foo.js?systemjs=true');"); + server.stop(); + }); + it(`handles inline scripts`, async () => { const { server, host } = await createTestServer({ rootDir: __dirname, @@ -95,7 +121,11 @@ describe('legacyPlugin()', function () { const text = await fetchText(`${host}/index.html`, { headers: { 'user-agent': legacyUserAgents['IE 11'] }, }); - expectIncludes(text, "System.import('./inline-script-0.js?source=%2Findex.html');"); + expectIncludes(text, "loadScript('./inline-script-0.js?source=%2Findex.html');"); + expectIncludes( + text, + "System.import('./inline-script-1.js?source=%2Findex.html&systemjs=true');", + ); server.stop(); }); @@ -118,12 +148,11 @@ describe('legacyPlugin()', function () { await fetchText(`${host}/index.html`, { headers: { 'user-agent': legacyUserAgents['IE 11'] }, }); - const text = await fetchText(`${host}/inline-script-0.js?source=%2Findex.html`, { + const text = await fetchText(`${host}/inline-script-1.js?source=%2Findex.html`, { headers: { 'user-agent': legacyUserAgents['IE 11'] }, }); - expectIncludes(text, 'System.register'); - expectIncludes(text, 'var InlineClass;'); - expectIncludes(text, 'function _classCallCheck(instance'); + expectIncludes(text, 'var InlineClass ='); + expectIncludes(text, '_classCallCheck(this, InlineClass);'); server.stop(); }); diff --git a/packages/dev-server-legacy/test/transform-js.test.ts b/packages/dev-server-legacy/test/transform-js.test.ts index 92a355348..f3bd9421f 100644 --- a/packages/dev-server-legacy/test/transform-js.test.ts +++ b/packages/dev-server-legacy/test/transform-js.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { createTestServer } from '@web/dev-server-core/test-helpers'; -import { fetchText, expectIncludes } from '@web/dev-server-core/test-helpers'; +import { fetchText, expectIncludes, expectNotIncludes } from '@web/dev-server-core/test-helpers'; import { legacyPlugin } from '../src/legacyPlugin'; import { modernUserAgents, legacyUserAgents } from './userAgents'; @@ -16,7 +16,7 @@ async function doImport() { console.log(window?.foo?.bar);`; -describe('legacyPlugin()', function () { +describe('legacyPlugin - transform js', function () { this.timeout(10000); for (const [name, userAgent] of Object.entries(modernUserAgents)) { @@ -64,11 +64,42 @@ describe('legacyPlugin()', function () { const text = await fetchText(`${host}/app.js`, { headers: { 'user-agent': userAgent }, }); + expectNotIncludes(text, 'System.register('); + expectIncludes(text, "import('./xyz.js?systemjs=true');"); + expectIncludes(text, 'function asyncGeneratorStep'); + expectIncludes(text, 'function _classCallCheck(instance'); + expectIncludes(text, '_asyncToGenerator'); + expectIncludes( + text, + 'console.log((_window = window) === null || _window === void 0 ? void 0 : (_window$foo = _window.foo) === null || _window$foo === void 0 ? void 0 : _window$foo.bar);', + ); + server.stop(); + }); + + it(`transforms to SystemJS when systemjs paramater is given ${name}`, async () => { + const { server, host } = await createTestServer({ + rootDir: __dirname, + plugins: [ + { + name: 'test', + serve(context) { + if (context.path === '/app.js') { + return modernCode; + } + }, + }, + legacyPlugin(), + ], + }); + + const text = await fetchText(`${host}/app.js?systemjs=true`, { + headers: { 'user-agent': userAgent }, + }); expectIncludes(text, 'System.register('); + expectIncludes(text, "_context.import('./xyz.js?systemjs=true');"); expectIncludes(text, 'function asyncGeneratorStep'); expectIncludes(text, 'function _classCallCheck(instance'); expectIncludes(text, '_asyncToGenerator'); - expectIncludes(text, "_context.import('./xyz.js');"); expectIncludes( text, 'console.log((_window = window) === null || _window === void 0 ? void 0 : (_window$foo = _window.foo) === null || _window$foo === void 0 ? void 0 : _window$foo.bar);', diff --git a/packages/dev-server/src/config/parseConfig.ts b/packages/dev-server/src/config/parseConfig.ts index bd610df18..ffa339d0d 100644 --- a/packages/dev-server/src/config/parseConfig.ts +++ b/packages/dev-server/src/config/parseConfig.ts @@ -73,7 +73,8 @@ export async function parseConfig( if (finalConfig.nodeResolve) { const userOptions = typeof config.nodeResolve === 'object' ? config.nodeResolve : undefined; - finalConfig.plugins!.unshift( + // do node resolve after user plugins, to allow user plugins to resolve imports + finalConfig.plugins!.push( nodeResolvePlugin(finalConfig.rootDir!, config.preserveSymlinks, userOptions), ); }