diff --git a/packages/vite/src/node/__tests__/plugins/css.spec.ts b/packages/vite/src/node/__tests__/plugins/css.spec.ts index 2deda4e3711072..e6446e5e9caf38 100644 --- a/packages/vite/src/node/__tests__/plugins/css.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/css.spec.ts @@ -1,5 +1,7 @@ import path from 'node:path' import { describe, expect, test } from 'vitest' +import type { InternalModuleFormat } from 'rolldown' +import MagicString from 'magic-string' import { resolveConfig } from '../../config' import type { InlineConfig } from '../../config' import { @@ -8,6 +10,7 @@ import { cssUrlRE, getEmptyChunkReplacer, hoistAtRules, + injectInlinedCSS, preprocessCSS, resolveLibCssFilename, } from '../../plugins/css' @@ -458,3 +461,336 @@ describe('resolveLibCssFilename', () => { expect(filename).toBe('custom-name.css') }) }) + +describe('injectInlinedCSS', () => { + function getInlinedCSSInjectedCode( + code: string, + format: InternalModuleFormat, + ) { + const s = new MagicString(code) + injectInlinedCSS( + s, + { + error(e) { + throw e + }, + }, + code, + format, + 'injectCSS();', + ) + return s.toString() + } + + test('should inject CSS for iife without exports from esm', async () => { + const result = getInlinedCSSInjectedCode( + `(function() { + +"use strict"; + +//#region src/index.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); + +//#endregion +})();`, + 'iife', + ) + expect(result).toMatchInlineSnapshot(` + "(function() { + + "use strict";injectCSS(); + + //#region src/index.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + + //#endregion + })();" + `) + }) + + test('should inject helper for iife without exports from cjs', async () => { + const result = getInlinedCSSInjectedCode( + `(function() { + + +//#region src/index.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); + +//#endregion +})();`, + 'iife', + ) + expect(result).toMatchInlineSnapshot(` + "(function() {injectCSS(); + + + //#region src/index.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + + //#endregion + })();" + `) + }) + + test('should inject helper for iife with exports', async () => { + const result = getInlinedCSSInjectedCode( + `var lib = (function(exports) { + + +//#region entry.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); +const foo = "foo"; + +//#endregion +exports.foo = foo; +return exports; +})({});`, + 'iife', + ) + expect(result).toMatchInlineSnapshot(` + "var lib = (function(exports) {injectCSS(); + + + //#region entry.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + const foo = "foo"; + + //#endregion + exports.foo = foo; + return exports; + })({});" + `) + }) + + test('should inject helper for iife with nested name', async () => { + const result = getInlinedCSSInjectedCode( + `this.nested = this.nested || {}; +this.nested.lib = (function(exports) { + +Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + +//#region a.ts + const foo = "foo"; + +//#endregion +exports.foo = foo; +return exports; +})({});`, + 'iife', + ) + expect(result).toMatchInlineSnapshot(` + "this.nested = this.nested || {}; + this.nested.lib = (function(exports) {injectCSS(); + + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + + //#region a.ts + const foo = "foo"; + + //#endregion + exports.foo = foo; + return exports; + })({});" + `) + }) + + test('should inject helper for umd without exports', async () => { + const result = getInlinedCSSInjectedCode( + `(function(factory) { + + typeof define === 'function' && define.amd ? define([], factory) : + factory(); +})(function() { + +//#region entry.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); + +//#endregion +});`, + 'umd', + ) + expect(result).toMatchInlineSnapshot(` + "(function(factory) { + + typeof define === 'function' && define.amd ? define([], factory) : + factory(); + })(function() {injectCSS(); + + //#region entry.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + + //#endregion + });" + `) + }) + + test('should inject helper for umd with exports', async () => { + const result = getInlinedCSSInjectedCode( + `(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.lib = {}))); +})(this, function(exports) { + +//#region entry.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); +const foo = "foo"; + +//#endregion +exports.foo = foo; +});`, + 'umd', + ) + expect(result).toMatchInlineSnapshot(` + "(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.lib = {}))); + })(this, function(exports) {injectCSS(); + + //#region entry.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + const foo = "foo"; + + //#endregion + exports.foo = foo; + });" + `) + }) + + test('should inject helper for umd with only default export', async () => { + const result = getInlinedCSSInjectedCode( + `(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define([], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.lib = factory())); +})(this, function() { + +//#region entry.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); +})(); +var index_default = "foo"; + +//#endregion +return index_default; +});`, + 'umd', + ) + expect(result).toMatchInlineSnapshot(` + "(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define([], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.lib = factory())); + })(this, function() {injectCSS(); + + //#region entry.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo"); + })(); + var index_default = "foo"; + + //#endregion + return index_default; + });" + `) + }) + + test('should inject helper for umd with nested name', async () => { + const result = getInlinedCSSInjectedCode( + `(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.nested = global.nested || {},global.nested.lib = {}))); +})(this, function(exports) { +Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + +//#region a.ts + const foo = "foo"; + +//#endregion +exports.foo = foo; +});`, + 'umd', + ) + expect(result).toMatchInlineSnapshot(` + "(function(global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.nested = global.nested || {},global.nested.lib = {}))); + })(this, function(exports) {injectCSS(); + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + + //#region a.ts + const foo = "foo"; + + //#endregion + exports.foo = foo; + });" + `) + }) + + test('should inject multiple helpers', async () => { + const result = getInlinedCSSInjectedCode( + `(function() { + +"use strict"; + +//#region src/index.js +(async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo", { ..."foo" }); +})(); + +//#endregion +})();`, + 'iife', + ) + expect(result).toMatchInlineSnapshot(` + "(function() { + + "use strict";injectCSS(); + + //#region src/index.js + (async () => { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + console.log("foo", { ..."foo" }); + })(); + + //#endregion + })();" + `) + }) +}) diff --git a/packages/vite/src/node/__tests__/plugins/oxc.spec.ts b/packages/vite/src/node/__tests__/plugins/oxc.spec.ts index 9dc2cc12176cc3..99093cdd95b61d 100644 --- a/packages/vite/src/node/__tests__/plugins/oxc.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/oxc.spec.ts @@ -1,31 +1,6 @@ import path from 'node:path' import { describe, expect, test } from 'vitest' -import type { InternalModuleFormat } from 'rolldown' -import { resolveConfig } from '../../config' -import { buildOxcPlugin, transformWithOxc } from '../../plugins/oxc' -import { PartialEnvironment } from '../../baseEnvironment' - -async function createBuildOxcPluginRenderChunk(target: string) { - const config = await resolveConfig( - { build: { target }, configFile: false }, - 'build', - ) - const instance = buildOxcPlugin() - const environment = new PartialEnvironment('client', config) - - return async (code: string, format: InternalModuleFormat) => { - // @ts-expect-error renderChunk should exist - const result = await instance.renderChunk.call( - { environment }, - code, - { - fileName: 'foo.ts', - }, - { format }, - ) - return result?.code || result - } -} +import { transformWithOxc } from '../../plugins/oxc' describe('transformWithOxc', () => { test('correctly overrides TS configuration and applies automatic transform', async () => { @@ -179,311 +154,3 @@ describe('transformWithOxc', () => { expect(result?.code).toContain('_decorateMetadata("design:type"') }) }) - -describe('renderChunk', () => { - test('should inject helper for iife without exports from esm', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function() { - -"use strict"; - -//#region src/index.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); - -//#endregion -})();`, - 'iife', - ) - expect(result).toMatchInlineSnapshot(` - "(function() { - "use strict";var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region src/index.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - //#endregion - })(); - " - `) - }) - - test('should inject helper for iife without exports from cjs', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function() { - - -//#region src/index.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); - -//#endregion -})();`, - 'iife', - ) - expect(result).toMatchInlineSnapshot(` - "(function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region src/index.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - //#endregion - })(); - " - `) - }) - - test('should inject helper for iife with exports', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `var lib = (function(exports) { - - -//#region entry.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); -const foo = "foo"; - -//#endregion -exports.foo = foo; -return exports; -})({});`, - 'iife', - ) - expect(result).toMatchInlineSnapshot(` - "var lib = (function(exports) {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region entry.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - const foo = "foo"; - //#endregion - exports.foo = foo; - return exports; - })({}); - " - `) - }) - - test('should inject helper for iife with nested name', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `this.nested = this.nested || {}; -this.nested.lib = (function(exports) { - -Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); - -//#region a.ts - const foo = "foo"; - -//#endregion -exports.foo = foo; -return exports; -})({});`, - 'iife', - ) - expect(result).toMatchInlineSnapshot(` - "this.nested = this.nested || {}; - this.nested.lib = (function(exports) { - Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); - //#region a.ts - const foo = "foo"; - //#endregion - exports.foo = foo; - return exports; - })({}); - " - `) - }) - - test('should inject helper for umd without exports', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function(factory) { - - typeof define === 'function' && define.amd ? define([], factory) : - factory(); -})(function() { - -//#region entry.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); - -//#endregion -});`, - 'umd', - ) - expect(result).toMatchInlineSnapshot(` - "(function(factory) { - typeof define === "function" && define.amd ? define([], factory) : factory(); - })(function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region entry.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - //#endregion - }); - " - `) - }) - - test('should inject helper for umd with exports', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function(global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.lib = {}))); -})(this, function(exports) { - -//#region entry.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); -const foo = "foo"; - -//#endregion -exports.foo = foo; -});`, - 'umd', - ) - expect(result).toMatchInlineSnapshot(` - "(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.lib = {})); - })(this, function(exports) {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region entry.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - const foo = "foo"; - //#endregion - exports.foo = foo; - }); - " - `) - }) - - test('should inject helper for umd with only default export', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function(global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define([], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.lib = factory())); -})(this, function() { - -//#region entry.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); -})(); -var index_default = "foo"; - -//#endregion -return index_default; -});`, - 'umd', - ) - expect(result).toMatchInlineSnapshot(` - "(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define([], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.lib = factory()); - })(this, function() {var babelHelpers_asyncToGenerator;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}babelHelpers_asyncToGenerator=t;})(); - - //#region entry.js - babelHelpers_asyncToGenerator(function* () { - yield new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo"); - })(); - var index_default = "foo"; - //#endregion - return index_default; - }); - " - `) - }) - - test('should inject helper for umd with nested name', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function(global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.nested = global.nested || {},global.nested.lib = {}))); -})(this, function(exports) { -Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); - -//#region a.ts - const foo = "foo"; - -//#endregion -exports.foo = foo; -});`, - 'umd', - ) - expect(result).toMatchInlineSnapshot(` - "(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory((global.nested = global.nested || {}, global.nested.lib = {}))); - })(this, function(exports) { - Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); - //#region a.ts - const foo = "foo"; - //#endregion - exports.foo = foo; - }); - " - `) - }) - - test('should inject multiple helpers', async () => { - const renderChunk = await createBuildOxcPluginRenderChunk('es2015') - const result = await renderChunk( - `(function() { - -"use strict"; - -//#region src/index.js -(async () => { - await new Promise((resolve) => setTimeout(resolve, 1e3)); - console.log("foo", { ..."foo" }); -})(); - -//#endregion -})();`, - 'iife', - ) - expect(result).toMatchInlineSnapshot(` - "(function() { - "use strict";var babelHelpers_asyncToGenerator, babelHelpers_objectSpread2;!(() => {function e(e,t,n,r,i,a,o){try{var s=e[a](o),c=s.value}catch(e){n(e);return}s.done?t(c):Promise.resolve(c).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,c,\`next\`,t)}function c(t){e(o,i,a,s,c,\`throw\`,t)}s(void 0)})}}function n(e){"@babel/helpers - typeof";return n=typeof Symbol==\`function\`&&typeof Symbol.iterator==\`symbol\`?function(e){return typeof e}:function(e){return e&&typeof Symbol==\`function\`&&e.constructor===Symbol&&e!==Symbol.prototype?\`symbol\`:typeof e},n(e)}function r(e,t){if(n(e)!=\`object\`||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,t||\`default\`);if(n(i)!=\`object\`)return i;throw TypeError(\`@@toPrimitive must return a primitive value.\`)}return(t===\`string\`?String:Number)(e)}function i(e){var t=r(e,\`string\`);return n(t)==\`symbol\`?t:t+\`\`}function a(e,t,n){return(t=i(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function s(e){for(var t=1;t setTimeout(resolve, 1e3)); - console.log("foo", babelHelpers_objectSpread2({}, "foo")); - })(); - //#endregion - })(); - " - `) - }) -}) diff --git a/packages/vite/src/node/build.ts b/packages/vite/src/node/build.ts index eb024c4a3a8aaf..761b9dd5fac23d 100644 --- a/packages/vite/src/node/build.ts +++ b/packages/vite/src/node/build.ts @@ -76,7 +76,6 @@ import { getHookHandler } from './plugins' import { BaseEnvironment } from './baseEnvironment' import type { MinimalPluginContextWithoutEnvironment, Plugin } from './plugin' import type { RollupPluginHooks } from './typeUtils' -import { buildOxcPlugin } from './plugins/oxc' import { type LicenseOptions, licensePlugin } from './plugins/license' import { BasicMinimalPluginContext, @@ -519,7 +518,6 @@ export async function resolveBuildPlugins(config: ResolvedConfig): Promise<{ ], post: [ ...(isBuild ? buildImportAnalysisPlugin(config) : []), - ...(config.nativePluginEnabledLevel >= 1 ? [] : [buildOxcPlugin()]), ...(config.build.minify === 'esbuild' ? [buildEsbuildPlugin()] : []), ...(isBuild ? [terserPlugin(config)] : []), ...(isBuild && !config.isWorker diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index 30021593c79a50..b504c1c697c8c5 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -6,6 +6,7 @@ import postcssrc from 'postcss-load-config' import type { ExistingRawSourceMap, InternalModuleFormat, + MinimalPluginContext, OutputAsset, OutputChunk, RenderedChunk, @@ -938,34 +939,8 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { `${style}.textContent = ${cssString};` + `document.head.appendChild(${style});` - let injectionPoint: number - if (opts.format === 'iife' || opts.format === 'umd') { - const m = ( - opts.format === 'iife' ? IIFE_BEGIN_RE : UMD_BEGIN_RE - ).exec(code) - if (!m) { - this.error('Injection point for inlined CSS not found') - return - } - injectionPoint = m.index + m[0].length - } else if (opts.format === 'es') { - // legacy build - if (code.startsWith('#!')) { - let secondLinePos = code.indexOf('\n') - if (secondLinePos === -1) { - secondLinePos = 0 - } - injectionPoint = secondLinePos - } else { - injectionPoint = 0 - } - } else { - this.error('Non supported format') - return - } - s ||= new MagicString(code) - s.appendRight(injectionPoint, injectCode) + injectInlinedCSS(s, this, code, opts.format, injectCode) } } else { // resolve public URL from CSS paths, we need to use absolute paths @@ -1161,6 +1136,37 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { } } +export function injectInlinedCSS( + s: MagicString, + ctx: Pick, + code: string, + format: InternalModuleFormat, + injectCode: string, +): void { + let injectionPoint: number + if (format === 'iife' || format === 'umd') { + const m = (format === 'iife' ? IIFE_BEGIN_RE : UMD_BEGIN_RE).exec(code) + if (!m) { + ctx.error('Injection point for inlined CSS not found') + } + injectionPoint = m.index + m[0].length + } else if (format === 'es') { + // legacy build + if (code.startsWith('#!')) { + let secondLinePos = code.indexOf('\n') + if (secondLinePos === -1) { + secondLinePos = 0 + } + injectionPoint = secondLinePos + } else { + injectionPoint = 0 + } + } else { + ctx.error('Non supported format') + } + s.appendRight(injectionPoint, injectCode) +} + export function cssAnalysisPlugin(config: ResolvedConfig): Plugin { return { name: 'vite:css-analysis', diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index da1f98826c5444..b35dee715e8af6 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -1,15 +1,11 @@ import path from 'node:path' import MagicString from 'magic-string' -import type { - ParseError as EsModuleLexerParseError, - ImportSpecifier, -} from 'es-module-lexer' +import type { ImportSpecifier } from 'es-module-lexer' import { init, parse as parseImports } from 'es-module-lexer' import type { SourceMap } from 'rolldown' import { viteBuildImportAnalysisPlugin as nativeBuildImportAnalysisPlugin } from 'rolldown/experimental' import type { RawSourceMap } from '@jridgewell/remapping' import convertSourceMap from 'convert-source-map' -import { exactRegex } from 'rolldown/filter' import { combineSourcemaps, generateCodeFrame, numberToPos } from '../utils' import { type Plugin, perEnvironmentPlugin } from '../plugin' import type { ResolvedConfig } from '../config' @@ -17,7 +13,6 @@ import { toOutputFilePathInJS } from '../build' import { genSourceMapUrl } from '../server/sourcemap' import type { PartialEnvironment } from '../baseEnvironment' import { removedPureCssFilesCache } from './css' -import { createParseErrorInfo } from './importAnalysis' type FileDep = { url: string @@ -34,16 +29,10 @@ type VitePreloadErrorEvent = Event & { payload: Error } export const isModernFlag = `__VITE_IS_MODERN__` export const preloadMethod = `__vitePreload` export const preloadMarker = `__VITE_PRELOAD__` -export const preloadBaseMarker = `__VITE_PRELOAD_BASE__` export const preloadHelperId = '\0vite/preload-helper.js' const preloadMarkerRE = new RegExp(preloadMarker, 'g') -const dynamicImportPrefixRE = /import\s*\(/ - -const dynamicImportTreeshakenRE = - /((?:\bconst\s+|\blet\s+|\bvar\s+|,\s*)(\{[^{}.=]+\})\s*=\s*await\s+import\([^)]+\))(?=\s*(?:$|[^[.]))|(\(\s*await\s+import\([^)]+\)\s*\)(\??\.[\w$]+))|\bimport\([^)]+\)(\s*\.then\(\s*(?:function\s*)?\(\s*\{([^{}.=]+)\}\))/g - function toRelativePath(filename: string, importer: string) { const relPath = path.posix.relative(path.posix.dirname(importer), filename) return relPath[0] === '.' ? relPath : `./${relPath}` @@ -219,185 +208,6 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] { const plugin: Plugin = { name: 'vite:build-import-analysis', - resolveId: { - filter: { id: exactRegex(preloadHelperId) }, - handler(id) { - return id - }, - }, - - load: { - filter: { id: exactRegex(preloadHelperId) }, - handler(_id) { - const preloadCode = getPreloadCode( - this.environment, - !!renderBuiltUrl, - isRelativeBase, - ) - return { code: preloadCode, moduleSideEffects: false } - }, - }, - - transform: { - filter: { code: dynamicImportPrefixRE }, - async handler(source, importer) { - await init - - let imports: readonly ImportSpecifier[] = [] - try { - imports = parseImports(source)[0] - } catch (_e: unknown) { - const e = _e as EsModuleLexerParseError - const { message, showCodeFrame } = createParseErrorInfo( - importer, - source, - ) - this.error(message, showCodeFrame ? e.idx : undefined) - } - - if (!imports.length) { - return null - } - - const insertPreload = getInsertPreload(this.environment) - // when wrapping dynamic imports with a preload helper, Rollup is unable to analyze the - // accessed variables for treeshaking. This below tries to match common accessed syntax - // to "copy" it over to the dynamic import wrapped by the preload helper. - const dynamicImports: Record< - number, - { declaration?: string; names?: string } - > = {} - - if (insertPreload) { - let match - while ((match = dynamicImportTreeshakenRE.exec(source))) { - /* handle `const {foo} = await import('foo')` - * - * match[1]: `const {foo} = await import('foo')` - * match[2]: `{foo}` - * import end: `const {foo} = await import('foo')_` - * ^ - */ - if (match[1]) { - dynamicImports[dynamicImportTreeshakenRE.lastIndex] = { - declaration: `const ${match[2]}`, - names: match[2]?.trim(), - } - continue - } - - /* handle `(await import('foo')).foo` - * - * match[3]: `(await import('foo')).foo` - * match[4]: `.foo` - * import end: `(await import('foo'))` - * ^ - */ - if (match[3]) { - let names = /\.([^.?]+)/.exec(match[4])?.[1] || '' - // avoid `default` keyword error - if (names === 'default') { - names = 'default: __vite_default__' - } - dynamicImports[ - dynamicImportTreeshakenRE.lastIndex - match[4]?.length - 1 - ] = { declaration: `const {${names}}`, names: `{ ${names} }` } - continue - } - - /* handle `import('foo').then(({foo})=>{})` - * - * match[5]: `.then(({foo})` - * match[6]: `foo` - * import end: `import('foo').` - * ^ - */ - const names = match[6]?.trim() - dynamicImports[ - dynamicImportTreeshakenRE.lastIndex - match[5]?.length - ] = { declaration: `const {${names}}`, names: `{ ${names} }` } - } - } - - let s: MagicString | undefined - const str = () => s || (s = new MagicString(source)) - let needPreloadHelper = false - - for (let index = 0; index < imports.length; index++) { - const { - s: start, - e: end, - ss: expStart, - se: expEnd, - d: dynamicIndex, - a: attributeIndex, - } = imports[index] - - const isDynamicImport = dynamicIndex > -1 - - // strip import attributes as we can process them ourselves - if (!isDynamicImport && attributeIndex > -1) { - str().remove(end + 1, expEnd) - } - - if ( - isDynamicImport && - insertPreload && - // Only preload static urls - (source[start] === '"' || - source[start] === "'" || - source[start] === '`') - ) { - needPreloadHelper = true - const { declaration, names } = dynamicImports[expEnd] || {} - if (names) { - /* transform `const {foo} = await import('foo')` - * to `const {foo} = await __vitePreload(async () => { const {foo} = await import('foo');return {foo}}, ...)` - * - * transform `import('foo').then(({foo})=>{})` - * to `__vitePreload(async () => { const {foo} = await import('foo');return { foo }},...).then(({foo})=>{})` - * - * transform `(await import('foo')).foo` - * to `__vitePreload(async () => { const {foo} = (await import('foo')).foo; return { foo }},...)).foo` - */ - str().prependLeft( - expStart, - `${preloadMethod}(async () => { ${declaration} = await `, - ) - str().appendRight(expEnd, `;return ${names}}`) - } else { - str().prependLeft(expStart, `${preloadMethod}(() => `) - } - - str().appendRight( - expEnd, - `,${isModernFlag}?${preloadMarker}:void 0${ - renderBuiltUrl || isRelativeBase ? ',import.meta.url' : '' - })`, - ) - } - } - - if ( - needPreloadHelper && - insertPreload && - !source.includes(`const ${preloadMethod} =`) - ) { - str().prepend( - `import { ${preloadMethod} } from "${preloadHelperId}";`, - ) - } - - if (s) { - return { - code: s.toString(), - map: this.environment.config.build.sourcemap - ? s.generateMap({ hires: 'boundary' }) - : null, - } - } - }, - }, renderChunk(code, _, { format }) { // make sure we only perform the preload logic in modern builds. @@ -414,11 +224,13 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] { return null }, - generateBundle({ format }, bundle) { + async generateBundle({ format }, bundle) { if (format !== 'es') { return } + await init + // If preload is not enabled, we parse through each imports and remove any imports to pure CSS chunks // as they are removed from the bundle if (!getInsertPreload(this.environment)) { @@ -744,28 +556,22 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] { }, } - if (config.nativePluginEnabledLevel >= 1) { - delete plugin.transform - delete plugin.resolveId - delete plugin.load - return [ - plugin, - perEnvironmentPlugin('native:import-analysis-build', (environment) => { - const preloadCode = getPreloadCode( - environment, - !!renderBuiltUrl, - isRelativeBase, - ) - return nativeBuildImportAnalysisPlugin({ - preloadCode, - insertPreload: getInsertPreload(environment), - // this field looks redundant, put a dummy value for now - optimizeModulePreloadRelativePaths: false, - renderBuiltUrl: !!renderBuiltUrl, - isRelativeBase, - }) - }), - ] - } - return [plugin] + return [ + plugin, + perEnvironmentPlugin('native:import-analysis-build', (environment) => { + const preloadCode = getPreloadCode( + environment, + !!renderBuiltUrl, + isRelativeBase, + ) + return nativeBuildImportAnalysisPlugin({ + preloadCode, + insertPreload: getInsertPreload(environment), + // this field looks redundant, put a dummy value for now + optimizeModulePreloadRelativePaths: false, + renderBuiltUrl: !!renderBuiltUrl, + isRelativeBase, + }) + }), + ] } diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts index 7d72afc58ce6e0..b9b0967d83e3b9 100644 --- a/packages/vite/src/node/plugins/oxc.ts +++ b/packages/vite/src/node/plugins/oxc.ts @@ -1,5 +1,4 @@ import path from 'node:path' -import url from 'node:url' import type { TransformOptions as OxcTransformOptions, TransformResult as OxcTransformResult, @@ -9,11 +8,10 @@ import { transformSync, } from 'rolldown/experimental' import type { RawSourceMap } from '@jridgewell/remapping' -import type { InternalModuleFormat, RollupError, SourceMap } from 'rolldown' -import { rolldown } from 'rolldown' +import type { RollupError, SourceMap } from 'rolldown' import { TSConfckParseError } from 'tsconfck' import colors from 'picocolors' -import { exactRegex, prefixRegex } from 'rolldown/filter' +import { prefixRegex } from 'rolldown/filter' import type { FSWatcher } from '#dep-types/chokidar' import { combineSourcemaps, @@ -472,196 +470,6 @@ export function oxcPlugin(config: ResolvedConfig): Plugin { } } -export const buildOxcPlugin = (): Plugin => { - return { - name: 'vite:oxc-transpile', - applyToEnvironment(environment) { - return environment.config.oxc !== false - }, - async renderChunk(code, chunk, opts) { - // avoid on legacy chunks since it produces legacy-unsafe code - // e.g. rewriting object properties into shorthands - if (this.environment.config.isOutputOptionsForLegacyChunks?.(opts)) { - return null - } - - const config = this.environment.config - const options = resolveOxcTranspileOptions(config, opts.format) - - if (!options) { - return null - } - - const res = await transformWithOxc( - code, - chunk.fileName, - options, - undefined, - config, - ) - for (const warning of res.warnings) { - this.environment.logger.warnOnce(warning) - } - - const runtimeHelpers = Object.entries(res.helpersUsed) - if (runtimeHelpers.length > 0) { - // The length is kept to avoid sourcemap generation - let newCode = res.code.replace( - /babelHelpers\.([A-Za-z_$][\w$]*)\b/g, - 'babelHelpers_$1', - ) - - const helpersCode = await generateRuntimeHelpers(runtimeHelpers) - switch (opts.format) { - case 'es': { - if (newCode.startsWith('#!')) { - let secondLinePos = newCode.indexOf('\n') - if (secondLinePos === -1) { - secondLinePos = 0 - } - // inject after hashbang - newCode = - newCode.slice(0, secondLinePos) + - helpersCode + - newCode.slice(secondLinePos) - if (res.map) { - res.map.mappings = res.map.mappings.replace(';', ';;') - } - } else { - newCode = helpersCode + newCode - if (res.map) { - res.map.mappings = ';' + res.map.mappings - } - } - break - } - case 'cjs': { - if (/^\s*['"]use strict['"];/.test(newCode)) { - // inject after use strict - newCode = newCode.replace( - /^\s*['"]use strict['"];/, - (m) => m + helpersCode, - ) - // no need to update sourcemap because the runtime helpers are injected in the same line with "use strict" - } else { - newCode = helpersCode + newCode - if (res.map) { - res.map.mappings = ';' + res.map.mappings - } - } - break - } - // runtime helpers needs to be injected inside the UMD and IIFE wrappers - // to avoid collision with other globals. - // We inject the helpers inside the wrappers. - // e.g. turn: - // (function(){ /*actual content/* })() - // into: - // (function(){ /*actual content/* })() - // Not using regex because it's too hard to rule out performance issues like #8738 #8099 #10900 #14065 - // Instead, using plain string index manipulation (indexOf, slice) which is simple and performant - // We don't need to create a MagicString here because both the helpers and - // the headers don't modify the sourcemap - case 'iife': - case 'umd': { - const m = ( - opts.format === 'iife' ? IIFE_BEGIN_RE : UMD_BEGIN_RE - ).exec(newCode) - if (!m) { - this.error(`Unexpected ${opts.format.toUpperCase()} format`) - return - } - const pos = m.index + m[0].length - newCode = - newCode.slice(0, pos) + helpersCode + '\n' + newCode.slice(pos) - break - } - default: { - opts.format satisfies never - } - } - res.code = newCode - } - - return res - }, - } -} - -export function resolveOxcTranspileOptions( - config: ResolvedConfig, - format: InternalModuleFormat, -): OxcTransformOptions | null { - const target = config.build.target - if (!target || target === 'esnext') { - return null - } - - return { - ...config.oxc, - helpers: { mode: 'External' }, - lang: 'js', - sourceType: format === 'es' ? 'module' : 'script', - target: target || undefined, - sourcemap: !!config.build.sourcemap, - } -} - -async function generateRuntimeHelpers( - runtimeHelpers: readonly [string, string][], -): Promise { - const isAsciiOnlyIdentifierRE = /^[A-Za-z_$][\w$]*$/ - const cjsExportRE = /\bexports\.([A-Za-z_$][\w$]*)\s*=/g - - const bundle = await rolldown({ - cwd: url.fileURLToPath(/** #__KEEP__ */ import.meta.url), - input: 'entrypoint', - platform: 'neutral', - logLevel: 'silent', - plugins: [ - { - name: 'entrypoint', - resolveId: { - filter: { id: exactRegex('entrypoint') }, - handler: (id) => id, - }, - load: { - filter: { id: exactRegex('entrypoint') }, - handler() { - return runtimeHelpers - .map( - ([name, helper]) => - `export { default as ${name} } from ${JSON.stringify(helper)};`, - ) - .join('\n') - }, - }, - }, - { - name: 'ensure-helper-names', - renderChunk(_code, chunk) { - if (chunk.exports.some((e) => !isAsciiOnlyIdentifierRE.test(e))) { - throw new Error( - `Expected all runtime helper export names to be ASCII-only. Got ${chunk.exports.filter((e) => !isAsciiOnlyIdentifierRE.test(e)).join(', ')}`, - ) - } - }, - }, - ], - }) - const output = await bundle.generate({ - format: 'cjs', - minify: true, - generatedCode: { symbols: false }, - }) - const outputCode = output.output[0].code - const exportNames = [...outputCode.matchAll(cjsExportRE)].map((m) => m[1]) - return ( - `var ${exportNames.map((n) => `babelHelpers_${n}`).join(', ')};` + - `!(() => {${outputCode.replace(cjsExportRE, 'babelHelpers_$1=')}})();` - ) -} - type OxcJsxOptions = Exclude export function convertEsbuildConfigToOxcConfig( diff --git a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts index 2a9c9da1b038e7..486f4d343fe5bd 100644 --- a/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts +++ b/playground/js-sourcemap/__tests__/js-sourcemap.spec.ts @@ -156,51 +156,27 @@ describe.runIf(isBuild)('build tests', () => { test('sourcemap is correct when preload information is injected', async () => { const js = findAssetFile(/after-preload-dynamic-[-\w]{8}\.js$/) const map = findAssetFile(/after-preload-dynamic-[-\w]{8}\.js\.map/) - if (process.env._VITE_TEST_JS_PLUGIN) { - expect(formatSourcemapForSnapshot(JSON.parse(map), js)) - .toMatchInlineSnapshot(` - SourceMap { - content: { - "debugId": "00000000-0000-0000-0000-000000000000", - "ignoreList": [], - "mappings": ";sqCAAA,OAAO,6BAAuB,wBAE9B,QAAQ,IAAI,wBAAuB", - "sources": [ - "../../after-preload-dynamic.js", - ], - "sourcesContent": [ - "import('./dynamic/dynamic-foo') - - console.log('after preload dynamic') - ", - ], - "version": 3, - }, - visualization: "https://evanw.github.io/source-map-visualization/#MTU1NQBjb25zdCBfX3ZpdGVfX21hcERlcHM9KGksbT1fX3ZpdGVfX21hcERlcHMsZD0obS5mfHwobS5mPVsiYXNzZXRzL2R5bmFtaWMtZm9vLXRpUHBTUURiLmpzIiwiYXNzZXRzL2R5bmFtaWMtZm9vLURzcUtSckV5LmNzcyJdKSkpPT5pLm1hcChpPT5kW2ldKTsKdmFyIGU9YG1vZHVsZXByZWxvYWRgLHQ9ZnVuY3Rpb24oZSl7cmV0dXJuYC9gK2V9LG49e307Y29uc3Qgcj1mdW5jdGlvbihyLGksYSl7bGV0IG89UHJvbWlzZS5yZXNvbHZlKCk7aWYoaSYmaS5sZW5ndGg+MCl7bGV0IHI9ZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoYGxpbmtgKSxzPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYG1ldGFbcHJvcGVydHk9Y3NwLW5vbmNlXWApLGM9cz8ubm9uY2V8fHM/LmdldEF0dHJpYnV0ZShgbm9uY2VgKTtmdW5jdGlvbiBsKGUpe3JldHVybiBQcm9taXNlLmFsbChlLm1hcChlPT5Qcm9taXNlLnJlc29sdmUoZSkudGhlbihlPT4oe3N0YXR1czpgZnVsZmlsbGVkYCx2YWx1ZTplfSksZT0+KHtzdGF0dXM6YHJlamVjdGVkYCxyZWFzb246ZX0pKSkpfW89bChpLm1hcChpPT57aWYoaT10KGksYSksaSBpbiBuKXJldHVybjtuW2ldPSEwO2xldCBvPWkuZW5kc1dpdGgoYC5jc3NgKSxzPW8/YFtyZWw9InN0eWxlc2hlZXQiXWA6YGA7aWYoYSlmb3IobGV0IGU9ci5sZW5ndGgtMTtlPj0wO2UtLSl7bGV0IHQ9cltlXTtpZih0LmhyZWY9PT1pJiYoIW98fHQucmVsPT09YHN0eWxlc2hlZXRgKSlyZXR1cm59ZWxzZSBpZihkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBsaW5rW2hyZWY9IiR7aX0iXSR7c31gKSlyZXR1cm47bGV0IGw9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudChgbGlua2ApO2lmKGwucmVsPW8/YHN0eWxlc2hlZXRgOmUsb3x8KGwuYXM9YHNjcmlwdGApLGwuY3Jvc3NPcmlnaW49YGAsbC5ocmVmPWksYyYmbC5zZXRBdHRyaWJ1dGUoYG5vbmNlYCxjKSxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKGwpLG8pcmV0dXJuIG5ldyBQcm9taXNlKChlLHQpPT57bC5hZGRFdmVudExpc3RlbmVyKGBsb2FkYCxlKSxsLmFkZEV2ZW50TGlzdGVuZXIoYGVycm9yYCwoKT0+dChFcnJvcihgVW5hYmxlIHRvIHByZWxvYWQgQ1NTIGZvciAke2l9YCkpKX0pfSkpfWZ1bmN0aW9uIHMoZSl7bGV0IHQ9bmV3IEV2ZW50KGB2aXRlOnByZWxvYWRFcnJvcmAse2NhbmNlbGFibGU6ITB9KTtpZih0LnBheWxvYWQ9ZSx3aW5kb3cuZGlzcGF0Y2hFdmVudCh0KSwhdC5kZWZhdWx0UHJldmVudGVkKXRocm93IGV9cmV0dXJuIG8udGhlbihlPT57Zm9yKGxldCB0IG9mIGV8fFtdKXQuc3RhdHVzPT09YHJlamVjdGVkYCYmcyh0LnJlYXNvbik7cmV0dXJuIHIoKS5jYXRjaChzKX0pfTtyKCgpPT5pbXBvcnQoYC4vZHluYW1pYy1mb28tdGlQcFNRRGIuanNgKSxfX3ZpdGVfX21hcERlcHMoWzAsMV0pKSxjb25zb2xlLmxvZyhgYWZ0ZXIgcHJlbG9hZCBkeW5hbWljYCk7ZXhwb3J0e3IgYXMgdH07Ci8vIyBkZWJ1Z0lkPTk5MWIzYWRkLWY2MWQtNDhiNy1hZDY1LThhZjVhODBmMzhkNwovLyMgc291cmNlTWFwcGluZ1VSTD1hZnRlci1wcmVsb2FkLWR5bmFtaWMtQ1pHenJkOWguanMubWFwMjc1AHsidmVyc2lvbiI6MywibWFwcGluZ3MiOiI7c3FDQUFBLE9BQU8sNkJBQXVCLHdCQUU5QixRQUFRLElBQUksd0JBQXVCIiwiaWdub3JlTGlzdCI6W10sInNvdXJjZXMiOlsiLi4vLi4vYWZ0ZXItcHJlbG9hZC1keW5hbWljLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCgnLi9keW5hbWljL2R5bmFtaWMtZm9vJylcblxuY29uc29sZS5sb2coJ2FmdGVyIHByZWxvYWQgZHluYW1pYycpXG4iXSwiZGVidWdJZCI6IjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCJ9" - } - `) - } else { - expect(formatSourcemapForSnapshot(JSON.parse(map), js)) - .toMatchInlineSnapshot(` - SourceMap { - content: { - "debugId": "00000000-0000-0000-0000-000000000000", - "ignoreList": [], - "mappings": ";sqCAAA,OAAO,qDAEP,QAAQ,IAAI,wBAAwB", - "sources": [ - "../../after-preload-dynamic.js", - ], - "sourcesContent": [ - "import('./dynamic/dynamic-foo') + expect(formatSourcemapForSnapshot(JSON.parse(map), js)) + .toMatchInlineSnapshot(` + SourceMap { + content: { + "debugId": "00000000-0000-0000-0000-000000000000", + "ignoreList": [], + "mappings": ";sqCAAA,OAAO,qDAEP,QAAQ,IAAI,wBAAwB", + "sources": [ + "../../after-preload-dynamic.js", + ], + "sourcesContent": [ + "import('./dynamic/dynamic-foo') - console.log('after preload dynamic') - ", - ], - "version": 3, - }, - visualization: "https://evanw.github.io/source-map-visualization/#MTU1NQBjb25zdCBfX3ZpdGVfX21hcERlcHM9KGksbT1fX3ZpdGVfX21hcERlcHMsZD0obS5mfHwobS5mPVsiYXNzZXRzL2R5bmFtaWMtZm9vLXRpUHBTUURiLmpzIiwiYXNzZXRzL2R5bmFtaWMtZm9vLURzcUtSckV5LmNzcyJdKSkpPT5pLm1hcChpPT5kW2ldKTsKdmFyIGU9YG1vZHVsZXByZWxvYWRgLHQ9ZnVuY3Rpb24oZSl7cmV0dXJuYC9gK2V9LG49e307Y29uc3Qgcj1mdW5jdGlvbihyLGksYSl7bGV0IG89UHJvbWlzZS5yZXNvbHZlKCk7aWYoaSYmaS5sZW5ndGg+MCl7bGV0IHI9ZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoYGxpbmtgKSxzPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYG1ldGFbcHJvcGVydHk9Y3NwLW5vbmNlXWApLGM9cz8ubm9uY2V8fHM/LmdldEF0dHJpYnV0ZShgbm9uY2VgKTtmdW5jdGlvbiBsKGUpe3JldHVybiBQcm9taXNlLmFsbChlLm1hcChlPT5Qcm9taXNlLnJlc29sdmUoZSkudGhlbihlPT4oe3N0YXR1czpgZnVsZmlsbGVkYCx2YWx1ZTplfSksZT0+KHtzdGF0dXM6YHJlamVjdGVkYCxyZWFzb246ZX0pKSkpfW89bChpLm1hcChpPT57aWYoaT10KGksYSksaSBpbiBuKXJldHVybjtuW2ldPSEwO2xldCBvPWkuZW5kc1dpdGgoYC5jc3NgKSxzPW8/YFtyZWw9InN0eWxlc2hlZXQiXWA6YGA7aWYoYSlmb3IobGV0IGU9ci5sZW5ndGgtMTtlPj0wO2UtLSl7bGV0IHQ9cltlXTtpZih0LmhyZWY9PT1pJiYoIW98fHQucmVsPT09YHN0eWxlc2hlZXRgKSlyZXR1cm59ZWxzZSBpZihkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBsaW5rW2hyZWY9IiR7aX0iXSR7c31gKSlyZXR1cm47bGV0IGw9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudChgbGlua2ApO2lmKGwucmVsPW8/YHN0eWxlc2hlZXRgOmUsb3x8KGwuYXM9YHNjcmlwdGApLGwuY3Jvc3NPcmlnaW49YGAsbC5ocmVmPWksYyYmbC5zZXRBdHRyaWJ1dGUoYG5vbmNlYCxjKSxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKGwpLG8pcmV0dXJuIG5ldyBQcm9taXNlKChlLHQpPT57bC5hZGRFdmVudExpc3RlbmVyKGBsb2FkYCxlKSxsLmFkZEV2ZW50TGlzdGVuZXIoYGVycm9yYCwoKT0+dChFcnJvcihgVW5hYmxlIHRvIHByZWxvYWQgQ1NTIGZvciAke2l9YCkpKX0pfSkpfWZ1bmN0aW9uIHMoZSl7bGV0IHQ9bmV3IEV2ZW50KGB2aXRlOnByZWxvYWRFcnJvcmAse2NhbmNlbGFibGU6ITB9KTtpZih0LnBheWxvYWQ9ZSx3aW5kb3cuZGlzcGF0Y2hFdmVudCh0KSwhdC5kZWZhdWx0UHJldmVudGVkKXRocm93IGV9cmV0dXJuIG8udGhlbihlPT57Zm9yKGxldCB0IG9mIGV8fFtdKXQuc3RhdHVzPT09YHJlamVjdGVkYCYmcyh0LnJlYXNvbik7cmV0dXJuIHIoKS5jYXRjaChzKX0pfTtyKCgpPT5pbXBvcnQoYC4vZHluYW1pYy1mb28tdGlQcFNRRGIuanNgKSxfX3ZpdGVfX21hcERlcHMoWzAsMV0pKSxjb25zb2xlLmxvZyhgYWZ0ZXIgcHJlbG9hZCBkeW5hbWljYCk7ZXhwb3J0e3IgYXMgdH07Ci8vIyBkZWJ1Z0lkPTk5MWIzYWRkLWY2MWQtNDhiNy1hZDY1LThhZjVhODBmMzhkNwovLyMgc291cmNlTWFwcGluZ1VSTD1hZnRlci1wcmVsb2FkLWR5bmFtaWMtQ1pHenJkOWguanMubWFwMjY3AHsidmVyc2lvbiI6MywibWFwcGluZ3MiOiI7c3FDQUFBLE9BQU8scURBRVAsUUFBUSxJQUFJLHdCQUF3QiIsImlnbm9yZUxpc3QiOltdLCJzb3VyY2VzIjpbIi4uLy4uL2FmdGVyLXByZWxvYWQtZHluYW1pYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQoJy4vZHluYW1pYy9keW5hbWljLWZvbycpXG5cbmNvbnNvbGUubG9nKCdhZnRlciBwcmVsb2FkIGR5bmFtaWMnKVxuIl0sImRlYnVnSWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAifQ==" - } - `) - } + console.log('after preload dynamic') + ", + ], + "version": 3, + }, + visualization: "https://evanw.github.io/source-map-visualization/#MTU1NQBjb25zdCBfX3ZpdGVfX21hcERlcHM9KGksbT1fX3ZpdGVfX21hcERlcHMsZD0obS5mfHwobS5mPVsiYXNzZXRzL2R5bmFtaWMtZm9vLXRpUHBTUURiLmpzIiwiYXNzZXRzL2R5bmFtaWMtZm9vLURzcUtSckV5LmNzcyJdKSkpPT5pLm1hcChpPT5kW2ldKTsKdmFyIGU9YG1vZHVsZXByZWxvYWRgLHQ9ZnVuY3Rpb24oZSl7cmV0dXJuYC9gK2V9LG49e307Y29uc3Qgcj1mdW5jdGlvbihyLGksYSl7bGV0IG89UHJvbWlzZS5yZXNvbHZlKCk7aWYoaSYmaS5sZW5ndGg+MCl7bGV0IHI9ZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoYGxpbmtgKSxzPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoYG1ldGFbcHJvcGVydHk9Y3NwLW5vbmNlXWApLGM9cz8ubm9uY2V8fHM/LmdldEF0dHJpYnV0ZShgbm9uY2VgKTtmdW5jdGlvbiBsKGUpe3JldHVybiBQcm9taXNlLmFsbChlLm1hcChlPT5Qcm9taXNlLnJlc29sdmUoZSkudGhlbihlPT4oe3N0YXR1czpgZnVsZmlsbGVkYCx2YWx1ZTplfSksZT0+KHtzdGF0dXM6YHJlamVjdGVkYCxyZWFzb246ZX0pKSkpfW89bChpLm1hcChpPT57aWYoaT10KGksYSksaSBpbiBuKXJldHVybjtuW2ldPSEwO2xldCBvPWkuZW5kc1dpdGgoYC5jc3NgKSxzPW8/YFtyZWw9InN0eWxlc2hlZXQiXWA6YGA7aWYoYSlmb3IobGV0IGU9ci5sZW5ndGgtMTtlPj0wO2UtLSl7bGV0IHQ9cltlXTtpZih0LmhyZWY9PT1pJiYoIW98fHQucmVsPT09YHN0eWxlc2hlZXRgKSlyZXR1cm59ZWxzZSBpZihkb2N1bWVudC5xdWVyeVNlbGVjdG9yKGBsaW5rW2hyZWY9IiR7aX0iXSR7c31gKSlyZXR1cm47bGV0IGw9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudChgbGlua2ApO2lmKGwucmVsPW8/YHN0eWxlc2hlZXRgOmUsb3x8KGwuYXM9YHNjcmlwdGApLGwuY3Jvc3NPcmlnaW49YGAsbC5ocmVmPWksYyYmbC5zZXRBdHRyaWJ1dGUoYG5vbmNlYCxjKSxkb2N1bWVudC5oZWFkLmFwcGVuZENoaWxkKGwpLG8pcmV0dXJuIG5ldyBQcm9taXNlKChlLHQpPT57bC5hZGRFdmVudExpc3RlbmVyKGBsb2FkYCxlKSxsLmFkZEV2ZW50TGlzdGVuZXIoYGVycm9yYCwoKT0+dChFcnJvcihgVW5hYmxlIHRvIHByZWxvYWQgQ1NTIGZvciAke2l9YCkpKX0pfSkpfWZ1bmN0aW9uIHMoZSl7bGV0IHQ9bmV3IEV2ZW50KGB2aXRlOnByZWxvYWRFcnJvcmAse2NhbmNlbGFibGU6ITB9KTtpZih0LnBheWxvYWQ9ZSx3aW5kb3cuZGlzcGF0Y2hFdmVudCh0KSwhdC5kZWZhdWx0UHJldmVudGVkKXRocm93IGV9cmV0dXJuIG8udGhlbihlPT57Zm9yKGxldCB0IG9mIGV8fFtdKXQuc3RhdHVzPT09YHJlamVjdGVkYCYmcyh0LnJlYXNvbik7cmV0dXJuIHIoKS5jYXRjaChzKX0pfTtyKCgpPT5pbXBvcnQoYC4vZHluYW1pYy1mb28tdGlQcFNRRGIuanNgKSxfX3ZpdGVfX21hcERlcHMoWzAsMV0pKSxjb25zb2xlLmxvZyhgYWZ0ZXIgcHJlbG9hZCBkeW5hbWljYCk7ZXhwb3J0e3IgYXMgdH07Ci8vIyBkZWJ1Z0lkPTk5MWIzYWRkLWY2MWQtNDhiNy1hZDY1LThhZjVhODBmMzhkNwovLyMgc291cmNlTWFwcGluZ1VSTD1hZnRlci1wcmVsb2FkLWR5bmFtaWMtQ1pHenJkOWguanMubWFwMjY3AHsidmVyc2lvbiI6MywibWFwcGluZ3MiOiI7c3FDQUFBLE9BQU8scURBRVAsUUFBUSxJQUFJLHdCQUF3QiIsImlnbm9yZUxpc3QiOltdLCJzb3VyY2VzIjpbIi4uLy4uL2FmdGVyLXByZWxvYWQtZHluYW1pYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQoJy4vZHluYW1pYy9keW5hbWljLWZvbycpXG5cbmNvbnNvbGUubG9nKCdhZnRlciBwcmVsb2FkIGR5bmFtaWMnKVxuIl0sImRlYnVnSWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAifQ==" + } + `) // verify sourcemap comment is preserved at the last line expect(js).toMatch( /\n\/\/# sourceMappingURL=after-preload-dynamic-[-\w]{8}\.js\.map\n?$/,