From 714c911f7e33fa3bf557a07f3a094757d9e8b1dd Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 31 Jan 2024 10:39:13 +0100 Subject: [PATCH] fix(browser): don't exclude node builtins from optimization (#5082) --- packages/browser/src/node/esmInjector.ts | 21 +-- packages/browser/src/node/index.ts | 13 +- packages/utils/src/display.ts | 4 +- pnpm-lock.yaml | 80 ++++++----- test/browser/package.json | 1 + test/browser/specs/run-vitest.mjs | 2 +- test/browser/specs/runner.test.mjs | 7 +- test/browser/test/polyfill-lib.test.ts | 7 + test/browser/test/utils.test.ts | 6 + test/core/test/injector-esm.test.ts | 168 +++++++++++++++-------- 10 files changed, 189 insertions(+), 120 deletions(-) create mode 100644 test/browser/test/polyfill-lib.test.ts create mode 100644 test/browser/test/utils.test.ts diff --git a/packages/browser/src/node/esmInjector.ts b/packages/browser/src/node/esmInjector.ts index 775a29003c69..0edc80480dad 100644 --- a/packages/browser/src/node/esmInjector.ts +++ b/packages/browser/src/node/esmInjector.ts @@ -38,8 +38,6 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex const hoistIndex = 0 - let hasInjected = false - // this will transform import statements into dynamic ones, if there are imports // it will keep the import as is, if we don't need to mock anything // in browser environment it will wrap the module value with "vitest_wrap_module" function @@ -76,7 +74,6 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex } function defineExport(position: number, name: string, local = name) { - hasInjected = true s.appendLeft( position, `\nObject.defineProperty(${viInjectedKey}, "${name}", ` @@ -170,7 +167,6 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex // named hoistable/class exports // export default function foo() {} // export default class A {} - hasInjected = true const { name } = node.declaration.id s.remove(node.start, node.start + 15 /* 'export default '.length */) s.append( @@ -180,7 +176,6 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex } else { // anonymous default exports - hasInjected = true s.update( node.start, node.start + 14 /* 'export default'.length */, @@ -196,13 +191,10 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex s.remove(node.start, node.end) const importId = defineImportAll(node.source.value as string) // hoist re-exports near the defined import so they are immediately exported - if (node.exported) { + if (node.exported) defineExport(hoistIndex, node.exported.name, `${importId}`) - } - else { - hasInjected = true + else s.appendLeft(hoistIndex, `${viExportAllHelper}(${viInjectedKey}, ${importId});\n`) - } } } @@ -244,11 +236,10 @@ export function injectVitestModule(code: string, id: string, parse: PluginContex }, }) - if (hasInjected) { - // make sure "__vi_injected__" is declared as soon as possible - s.prepend(`const ${viInjectedKey} = { [Symbol.toStringTag]: "Module" };\n`) - s.append(`\nexport { ${viInjectedKey} }`) - } + // make sure "__vi_injected__" is declared as soon as possible + // prepend even if file doesn't export anything + s.prepend(`const ${viInjectedKey} = { [Symbol.toStringTag]: "Module" };\n`) + s.append(`\nexport { ${viInjectedKey} }`) return { ast, diff --git a/packages/browser/src/node/index.ts b/packages/browser/src/node/index.ts index 8f69c8820085..35e852961bf2 100644 --- a/packages/browser/src/node/index.ts +++ b/packages/browser/src/node/index.ts @@ -1,7 +1,6 @@ import { fileURLToPath } from 'node:url' import { resolve } from 'node:path' -import { builtinModules } from 'node:module' import sirv from 'sirv' import type { Plugin } from 'vite' import type { WorkspaceProject } from 'vitest/node' @@ -61,18 +60,19 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => { 'vitest/runners', ], exclude: [ - ...builtinModules, 'vitest', 'vitest/utils', 'vitest/browser', 'vitest/runners', '@vitest/utils', + + // loupe is manually transformed + 'loupe', ], include: [ 'vitest > @vitest/utils > pretty-format', 'vitest > @vitest/snapshot > pretty-format', 'vitest > diff-sequences', - 'vitest > loupe', 'vitest > pretty-format', 'vitest > pretty-format > ansi-styles', 'vitest > pretty-format > ansi-regex', @@ -81,6 +81,13 @@ export default (project: WorkspaceProject, base = '/'): Plugin[] => { }, } }, + transform(code, id) { + if (id.includes('loupe/loupe.js')) { + const exportsList = ['custom', 'inspect', 'registerConstructor', 'registerStringTag'] + const codeAppend = exportsList.map(i => `export const ${i} = globalThis.loupe.${i}`).join('\n') + return `${code}\n${codeAppend}\nexport default globalThis.loupe` + } + }, async resolveId(id) { if (!/\?browserv=\w+$/.test(id)) return diff --git a/packages/utils/src/display.ts b/packages/utils/src/display.ts index 3ba7454cebb7..4c9b78e63307 100644 --- a/packages/utils/src/display.ts +++ b/packages/utils/src/display.ts @@ -1,6 +1,6 @@ // since this is already part of Vitest via Chai, we can just reuse it without increasing the size of bundle // @ts-expect-error doesn't have types -import { inspect as loupe } from 'loupe' +import * as loupe from 'loupe' interface LoupeOptions { showHidden?: boolean | undefined @@ -101,7 +101,7 @@ export function format(...args: unknown[]) { export function inspect(obj: unknown, options: LoupeOptions = {}): string { if (options.truncate === 0) options.truncate = Number.POSITIVE_INFINITY - return loupe(obj, options) + return loupe.inspect(obj, options) } export function objDisplay(obj: unknown, options: LoupeOptions = {}): string { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70bfc497104d..09eba076f3f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1527,6 +1527,9 @@ importers: safaridriver: specifier: ^0.0.4 version: 0.0.4 + url: + specifier: ^0.11.3 + version: 0.11.3 vitest: specifier: workspace:* version: link:../../packages/vitest @@ -5875,7 +5878,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -5896,7 +5899,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -5933,7 +5936,7 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 jest-mock: 27.5.1 dev: true @@ -5950,7 +5953,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 20.11.10 + '@types/node': 20.11.11 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -5979,7 +5982,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -6110,7 +6113,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.11.10 + '@types/node': 20.11.11 '@types/yargs': 16.0.7 chalk: 4.1.2 dev: true @@ -9212,7 +9215,7 @@ packages: resolution: {integrity: sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==} dependencies: '@types/connect': 3.4.37 - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/braces@3.0.1: @@ -9233,7 +9236,7 @@ packages: /@types/connect@3.4.37: resolution: {integrity: sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/cookie@0.4.1: @@ -9300,7 +9303,7 @@ packages: /@types/express-serve-static-core@4.17.39: resolution: {integrity: sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 '@types/qs': 6.9.9 '@types/range-parser': 1.2.6 '@types/send': 0.17.3 @@ -9354,7 +9357,7 @@ packages: /@types/graceful-fs@4.1.8: resolution: {integrity: sha512-NhRH7YzWq8WiNKVavKPBmtLYZHxNY19Hh+az28O/phfp68CF45pMFud+ZzJ8ewnxnC5smIdF3dqFeiSUQ5I+pw==} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/hast@2.3.4: @@ -9504,7 +9507,7 @@ packages: /@types/node-fetch@2.6.7: resolution: {integrity: sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 form-data: 4.0.0 dev: true @@ -9519,8 +9522,8 @@ packages: undici-types: 5.26.5 dev: true - /@types/node@20.11.10: - resolution: {integrity: sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==} + /@types/node@20.11.11: + resolution: {integrity: sha512-PlJCXfb57Jrman0H1BxO2+Q7qwih2Mwk7T6Gvixj+SK4mqs4RWOGMMoP6p/LFa3UrP2CZOO6ai6otd7J/TB6Ug==} dependencies: undici-types: 5.26.5 dev: true @@ -9680,7 +9683,7 @@ packages: /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/resolve@1.20.2: @@ -9698,7 +9701,7 @@ packages: resolution: {integrity: sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==} dependencies: '@types/mime': 1.3.4 - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/serve-static@1.15.4: @@ -9706,7 +9709,7 @@ packages: dependencies: '@types/http-errors': 2.0.3 '@types/mime': 3.0.3 - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /@types/set-cookie-parser@2.4.2: @@ -12806,13 +12809,6 @@ packages: engines: {node: '>=6'} dev: true - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.1 - dev: true - /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} dependencies: @@ -18602,7 +18598,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -18737,7 +18733,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -18755,7 +18751,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 jest-mock: 27.5.1 jest-util: 27.5.1 dev: true @@ -18799,7 +18795,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.8 - '@types/node': 20.11.10 + '@types/node': 20.11.11 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -18839,7 +18835,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -18919,7 +18915,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 dev: true /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): @@ -18980,7 +18976,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.11 @@ -19045,7 +19041,7 @@ packages: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 20.11.10 + '@types/node': 20.11.11 graceful-fs: 4.2.11 dev: true @@ -19096,7 +19092,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -19133,7 +19129,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 20.11.10 + '@types/node': 20.11.11 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -22231,11 +22227,6 @@ packages: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} dev: true - /punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} - dev: true - /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -24359,7 +24350,7 @@ packages: resolution: {integrity: sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==} engines: {node: '>= 0.4'} dependencies: - call-bind: 1.0.2 + call-bind: 1.0.5 define-properties: 1.2.0 es-abstract: 1.20.4 dev: true @@ -25184,7 +25175,7 @@ packages: engines: {node: '>=6'} dependencies: psl: 1.9.0 - punycode: 2.3.0 + punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 dev: true @@ -25219,7 +25210,7 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} dependencies: - punycode: 2.3.0 + punycode: 2.3.1 dev: true /tr46@4.1.1: @@ -25967,6 +25958,13 @@ packages: querystring: 0.2.0 dev: true + /url@0.11.3: + resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} + dependencies: + punycode: 1.4.1 + qs: 6.11.2 + dev: true + /use-sync-external-store@1.2.0(react@18.2.0): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: diff --git a/test/browser/package.json b/test/browser/package.json index c6eadd18a4bd..0422c12f71d6 100644 --- a/test/browser/package.json +++ b/test/browser/package.json @@ -16,6 +16,7 @@ "@vitest/cjs-lib": "link:./cjs-lib", "execa": "^7.1.1", "safaridriver": "^0.0.4", + "url": "^0.11.3", "vitest": "workspace:*" } } diff --git a/test/browser/specs/run-vitest.mjs b/test/browser/specs/run-vitest.mjs index 79d2528201a7..278f05ce69dd 100644 --- a/test/browser/specs/run-vitest.mjs +++ b/test/browser/specs/run-vitest.mjs @@ -20,7 +20,7 @@ export default async function runVitest(moreArgs = []) { const browserResult = await readFile('./browser.json', 'utf-8') const browserResultJson = JSON.parse(browserResult) - const getPassed = results => results.filter(result => result.status === 'passed') + const getPassed = results => results.filter(result => result.status === 'passed' && !result.mesage) const getFailed = results => results.filter(result => result.status === 'failed') const passedTests = getPassed(browserResultJson.testResults) diff --git a/test/browser/specs/runner.test.mjs b/test/browser/specs/runner.test.mjs index 83e169911e06..dcf75b12c989 100644 --- a/test/browser/specs/runner.test.mjs +++ b/test/browser/specs/runner.test.mjs @@ -11,10 +11,11 @@ const { } = await runVitest() await test('tests are actually running', async () => { - assert.ok(browserResultJson.testResults.length === 12, 'Not all the tests have been run') - assert.ok(passedTests.length === 10, 'Some tests failed') - assert.ok(failedTests.length === 2, 'Some tests have passed but should fail') + assert.equal(browserResultJson.testResults.length, 14, 'Not all the tests have been run') + assert.equal(passedTests.length, 12, 'Some tests failed') + assert.equal(failedTests.length, 2, 'Some tests have passed but should fail') + assert.doesNotMatch(stderr, /has been externalized for browser compatibility/, 'doesn\'t have any externalized modules') assert.doesNotMatch(stderr, /Unhandled Error/, 'doesn\'t have any unhandled errors') }) diff --git a/test/browser/test/polyfill-lib.test.ts b/test/browser/test/polyfill-lib.test.ts new file mode 100644 index 000000000000..7c3c91c3f2c1 --- /dev/null +++ b/test/browser/test/polyfill-lib.test.ts @@ -0,0 +1,7 @@ +// eslint-disable-next-line unicorn/prefer-node-protocol +import * as url from 'url' +import { expect, test } from 'vitest' + +test('url is polifylled because it\'s installed in dependencies', () => { + expect(url.format).toBeDefined() +}) diff --git a/test/browser/test/utils.test.ts b/test/browser/test/utils.test.ts new file mode 100644 index 000000000000..1e004a202c12 --- /dev/null +++ b/test/browser/test/utils.test.ts @@ -0,0 +1,6 @@ +import { inspect } from 'vitest/utils' +import { expect, it } from 'vitest' + +it('utils package correctly uses loupe', async () => { + expect(inspect({ test: 1 })).toBe('{ test: 1 }') +}) diff --git a/test/core/test/injector-esm.test.ts b/test/core/test/injector-esm.test.ts index 1bf269a70155..22eabedb7ce3 100644 --- a/test/core/test/injector-esm.test.ts +++ b/test/core/test/injector-esm.test.ts @@ -15,8 +15,10 @@ test('default import', async () => { expect( injectSimpleCode('import foo from \'vue\';console.log(foo.bar)'), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - console.log(__vi_esm_0__.default.bar)" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + console.log(__vi_esm_0__.default.bar) + export { __vi_inject__ }" `) }) @@ -26,8 +28,10 @@ test('named import', async () => { 'import { ref } from \'vue\';function foo() { return ref(0) }', ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function foo() { return __vi_esm_0__.ref(0) }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function foo() { return __vi_esm_0__.ref(0) } + export { __vi_inject__ }" `) }) @@ -37,8 +41,10 @@ test('namespace import', async () => { 'import * as vue from \'vue\';function foo() { return vue.ref(0) }', ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function foo() { return __vi_esm_0__.ref(0) }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function foo() { return __vi_esm_0__.ref(0) } + export { __vi_inject__ }" `) }) @@ -173,8 +179,10 @@ test('hoist import to top', async () => { 'path.resolve(\'server.js\');import path from \'node:path\';', ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'node:path' - __vi_esm_0__.default.resolve('server.js');" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'node:path' + __vi_esm_0__.default.resolve('server.js'); + export { __vi_inject__ }" `) }) @@ -200,8 +208,10 @@ test('do not rewrite method definition', async () => { 'import { fn } from \'vue\';class A { fn() { fn() } }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - class A { fn() { __vi_esm_0__.fn() } }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + class A { fn() { __vi_esm_0__.fn() } } + export { __vi_inject__ }" `) }) @@ -210,8 +220,10 @@ test('do not rewrite when variable is in scope', async () => { 'import { fn } from \'vue\';function A(){ const fn = () => {}; return { fn }; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A(){ const fn = () => {}; return { fn }; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A(){ const fn = () => {}; return { fn }; } + export { __vi_inject__ }" `) }) @@ -221,8 +233,10 @@ test('do not rewrite when variable is in scope with object destructuring', async 'import { fn } from \'vue\';function A(){ let {fn, test} = {fn: \'foo\', test: \'bar\'}; return { fn }; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A(){ let {fn, test} = {fn: 'foo', test: 'bar'}; return { fn }; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A(){ let {fn, test} = {fn: 'foo', test: 'bar'}; return { fn }; } + export { __vi_inject__ }" `) }) @@ -232,8 +246,10 @@ test('do not rewrite when variable is in scope with array destructuring', async 'import { fn } from \'vue\';function A(){ let [fn, test] = [\'foo\', \'bar\']; return { fn }; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A(){ let [fn, test] = ['foo', 'bar']; return { fn }; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A(){ let [fn, test] = ['foo', 'bar']; return { fn }; } + export { __vi_inject__ }" `) }) @@ -244,8 +260,10 @@ test('rewrite variable in string interpolation in function nested arguments', as 'import { fn } from \'vue\';function A({foo = `test${fn}`} = {}){ return {}; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A({foo = \`test\${__vi_esm_0__.fn}\`} = {}){ return {}; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A({foo = \`test\${__vi_esm_0__.fn}\`} = {}){ return {}; } + export { __vi_inject__ }" `) }) @@ -255,8 +273,10 @@ test('rewrite variables in default value of destructuring params', async () => { 'import { fn } from \'vue\';function A({foo = fn}){ return {}; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A({foo = __vi_esm_0__.fn}){ return {}; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A({foo = __vi_esm_0__.fn}){ return {}; } + export { __vi_inject__ }" `) }) @@ -265,8 +285,10 @@ test('do not rewrite when function declaration is in scope', async () => { 'import { fn } from \'vue\';function A(){ function fn() {}; return { fn }; }', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' - function A(){ function fn() {}; return { fn }; }" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' + function A(){ function fn() {}; return { fn }; } + export { __vi_inject__ }" `) }) @@ -275,8 +297,10 @@ test('do not rewrite catch clause', async () => { 'import {error} from \'./dependency\';try {} catch(error) {}', ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from './dependency' - try {} catch(error) {}" + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from './dependency' + try {} catch(error) {} + export { __vi_inject__ }" `) }) @@ -287,9 +311,11 @@ test('should declare variable for imported super class', async () => { 'import { Foo } from \'./dependency\';' + 'class A extends Foo {}', ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from './dependency' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from './dependency' const Foo = __vi_esm_0__.Foo; - class A extends Foo {}" + class A extends Foo {} + export { __vi_inject__ }" `) // exported classes: should prepend the declaration at root level, before the @@ -375,7 +401,8 @@ test('overwrite bindings', async () => { + 'function g() { const f = () => { const inject = true }; console.log(inject) }\n', ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' const a = { inject: __vi_esm_0__.inject } const b = { test: __vi_esm_0__.inject } function c() { const { test: inject } = { test: true }; console.log(inject) } @@ -383,14 +410,19 @@ test('overwrite bindings', async () => { function f() { console.log(__vi_esm_0__.inject) } function e() { const { inject } = { inject: true } } function g() { const f = () => { const inject = true }; console.log(__vi_esm_0__.inject) } - " + + export { __vi_inject__ }" `) }) test('Empty array pattern', async () => { expect( injectSimpleCode('const [, LHS, RHS] = inMatch;'), - ).toMatchInlineSnapshot('"const [, LHS, RHS] = inMatch;"') + ).toMatchInlineSnapshot(` + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + const [, LHS, RHS] = inMatch; + export { __vi_inject__ }" + `) }) test('function argument destructure', async () => { @@ -404,13 +436,15 @@ function c({ _ = bar() + foo() }) {} `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foo' const a = ({ _ = __vi_esm_0__.foo() }) => {} function b({ _ = __vi_esm_0__.bar() }) {} function c({ _ = __vi_esm_0__.bar() + __vi_esm_0__.foo() }) {} - " + + export { __vi_inject__ }" `) }) @@ -426,14 +460,16 @@ const a = () => { `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foo' const a = () => { const { type: n = 'bar' } = {} console.log(n) } - " + + export { __vi_inject__ }" `) // #9585 @@ -449,7 +485,8 @@ const foo = {} `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foo' const foo = {} @@ -457,7 +494,8 @@ const foo = {} { const { [__vi_esm_0__.n]: m } = foo } - " + + export { __vi_inject__ }" `) }) @@ -492,7 +530,8 @@ objRest() `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' @@ -518,7 +557,8 @@ objRest() __vi_esm_0__.set() __vi_esm_0__.rest() __vi_esm_0__.objRest() - " + + export { __vi_inject__ }" `) }) @@ -542,7 +582,8 @@ const obj = { `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foo' @@ -557,7 +598,8 @@ const obj = { [bar]: () => {}, bar(foo) {} } - " + + export { __vi_inject__ }" `) }) @@ -574,7 +616,8 @@ class A { `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' @@ -584,7 +627,8 @@ class A { remove = 1 add = null } - " + + export { __vi_inject__ }" `) }) @@ -606,7 +650,8 @@ class A { `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foo' @@ -619,7 +664,8 @@ class A { #foo() {} bar(foo) {} } - " + + export { __vi_inject__ }" `) }) @@ -652,7 +698,8 @@ bbb() `, ), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'vue' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'vue' @@ -676,7 +723,8 @@ bbb() __vi_esm_0__.aaa() __vi_esm_0__.bbb() - " + + export { __vi_inject__ }" `) }) @@ -697,14 +745,16 @@ test('jsx', async () => { const result = await transformWithEsbuild(code, id) expect(injectSimpleCode(result.code)) .toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'react' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'react' import { __vi_inject__ as __vi_esm_1__ } from 'foo' function Bar({ Slot: Slot2 = /* @__PURE__ */ __vi_esm_0__.default.createElement(__vi_esm_1__.Foo, null) }) { return /* @__PURE__ */ __vi_esm_0__.default.createElement(__vi_esm_0__.default.Fragment, null, /* @__PURE__ */ __vi_esm_0__.default.createElement(Slot2, null)); } - " + + export { __vi_inject__ }" `) }) @@ -777,7 +827,8 @@ for (const test in tests) { console.log(test) }`), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from './test.js' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from './test.js' for (const test of tests) { @@ -788,7 +839,8 @@ for (const test in tests) { } for (const test in tests) { console.log(test) - }" + } + export { __vi_inject__ }" `) }) @@ -892,7 +944,8 @@ function test() { return [foo, bar] }`), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foobar' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foobar' function test() { @@ -900,7 +953,8 @@ function test() { var foo = () => { var why = 'would' }, bar = 'someone' } return [foo, bar] - }" + } + export { __vi_inject__ }" `) }) @@ -919,7 +973,8 @@ function test() { return bar; }`), ).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from 'foobar' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from 'foobar' function test() { @@ -930,7 +985,8 @@ function test() { } try {} catch (baz){ baz }; return __vi_esm_0__.bar; - }" + } + export { __vi_inject__ }" `) }) @@ -947,7 +1003,8 @@ test('avoid binding ClassExpression', () => { `, ) expect(result).toMatchInlineSnapshot(` - "import { __vi_inject__ as __vi_esm_0__ } from './foo' + "const __vi_inject__ = { [Symbol.toStringTag]: "Module" }; + import { __vi_inject__ as __vi_esm_0__ } from './foo' console.log(__vi_esm_0__.default, __vi_esm_0__.Bar); @@ -956,6 +1013,7 @@ test('avoid binding ClassExpression', () => { bar: class Bar {} } const Baz = class extends __vi_esm_0__.default {} - " + + export { __vi_inject__ }" `) })