Skip to content

Commit

Permalink
feat: support arbitrary module namespace identifier imports from cjs …
Browse files Browse the repository at this point in the history
…deps (#18236)
  • Loading branch information
sapphi-red authored Oct 4, 2024
1 parent f311ff3 commit 4389a91
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 35 deletions.
27 changes: 15 additions & 12 deletions packages/vite/src/node/__tests__/plugins/import.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('transformCjsImport', () => {
test('import specifier', () => {
expect(
transformCjsImport(
'import { useState, Component } from "react"',
'import { useState, Component, "👋" as fake } from "react"',
url,
rawUrl,
0,
Expand All @@ -28,7 +28,8 @@ describe('transformCjsImport', () => {
).toBe(
'import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; ' +
'const useState = __vite__cjsImport0_react["useState"]; ' +
'const Component = __vite__cjsImport0_react["Component"]',
'const Component = __vite__cjsImport0_react["Component"]; ' +
'const fake = __vite__cjsImport0_react["👋"]',
)
})

Expand Down Expand Up @@ -111,7 +112,7 @@ describe('transformCjsImport', () => {
test('export name specifier', () => {
expect(
transformCjsImport(
'export { useState, Component } from "react"',
'export { useState, Component, "👋" } from "react"',
url,
rawUrl,
0,
Expand All @@ -120,14 +121,15 @@ describe('transformCjsImport', () => {
),
).toBe(
'import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; ' +
'const __vite__cjsExport_useState = __vite__cjsImport0_react["useState"]; ' +
'const __vite__cjsExport_Component = __vite__cjsImport0_react["Component"]; ' +
'export { __vite__cjsExport_useState as useState, __vite__cjsExport_Component as Component }',
'const __vite__cjsExportI_useState = __vite__cjsImport0_react["useState"]; ' +
'const __vite__cjsExportI_Component = __vite__cjsImport0_react["Component"]; ' +
'const __vite__cjsExportL_1d0452e3 = __vite__cjsImport0_react["👋"]; ' +
'export { __vite__cjsExportI_useState as useState, __vite__cjsExportI_Component as Component, __vite__cjsExportL_1d0452e3 as "👋" }',
)

expect(
transformCjsImport(
'export { useState as useStateAlias, Component as ComponentAlias } from "react"',
'export { useState as useStateAlias, Component as ComponentAlias, "👋" as "👍" } from "react"',
url,
rawUrl,
0,
Expand All @@ -136,9 +138,10 @@ describe('transformCjsImport', () => {
),
).toBe(
'import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; ' +
'const __vite__cjsExport_useStateAlias = __vite__cjsImport0_react["useState"]; ' +
'const __vite__cjsExport_ComponentAlias = __vite__cjsImport0_react["Component"]; ' +
'export { __vite__cjsExport_useStateAlias as useStateAlias, __vite__cjsExport_ComponentAlias as ComponentAlias }',
'const __vite__cjsExportI_useStateAlias = __vite__cjsImport0_react["useState"]; ' +
'const __vite__cjsExportI_ComponentAlias = __vite__cjsImport0_react["Component"]; ' +
'const __vite__cjsExportL_5d57d39e = __vite__cjsImport0_react["👋"]; ' +
'export { __vite__cjsExportI_useStateAlias as useStateAlias, __vite__cjsExportI_ComponentAlias as ComponentAlias, __vite__cjsExportL_5d57d39e as "👍" }',
)
})

Expand Down Expand Up @@ -169,8 +172,8 @@ describe('transformCjsImport', () => {
),
).toBe(
'import __vite__cjsImport0_react from "./node_modules/.vite/deps/react.js"; ' +
'const __vite__cjsExport_React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react; ' +
'export { __vite__cjsExport_React as React }',
'const __vite__cjsExportI_React = __vite__cjsImport0_react.__esModule ? __vite__cjsImport0_react.default : __vite__cjsImport0_react; ' +
'export { __vite__cjsExportI_React as React }',
)

expect(
Expand Down
41 changes: 25 additions & 16 deletions packages/vite/src/node/plugins/importAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { StaticImport } from 'mlly'
import { ESM_STATIC_IMPORT_RE, parseStaticImport } from 'mlly'
import { makeLegalIdentifier } from '@rollup/pluginutils'
import type { PartialResolvedId } from 'rollup'
import type { Identifier } from 'estree'
import type { Identifier, Literal } from 'estree'
import {
CLIENT_DIR,
CLIENT_PUBLIC_PATH,
Expand All @@ -32,6 +32,7 @@ import {
createDebugger,
fsPathFromUrl,
generateCodeFrame,
getHash,
injectQuery,
isBuiltin,
isDataUrl,
Expand Down Expand Up @@ -965,11 +966,10 @@ export function transformCjsImport(
const exportNames: string[] = []
let defaultExports: string = ''
for (const spec of node.specifiers) {
if (
spec.type === 'ImportSpecifier' &&
spec.imported.type === 'Identifier'
) {
const importedName = spec.imported.name
if (spec.type === 'ImportSpecifier') {
const importedName = getIdentifierNameOrLiteralValue(
spec.imported,
) as string
const localName = spec.local.name
importNames.push({ importedName, localName })
} else if (spec.type === 'ImportDefaultSpecifier') {
Expand All @@ -979,26 +979,31 @@ export function transformCjsImport(
})
} else if (spec.type === 'ImportNamespaceSpecifier') {
importNames.push({ importedName: '*', localName: spec.local.name })
} else if (
spec.type === 'ExportSpecifier' &&
spec.exported.type === 'Identifier'
) {
} else if (spec.type === 'ExportSpecifier') {
// for ExportSpecifier, local name is same as imported name
// prefix the variable name to avoid clashing with other local variables
const importedName = (spec.local as Identifier).name
const importedName = getIdentifierNameOrLiteralValue(
spec.local,
) as string
// we want to specify exported name as variable and re-export it
const exportedName = spec.exported.name
const exportedName = getIdentifierNameOrLiteralValue(
spec.exported,
) as string
if (exportedName === 'default') {
defaultExports = makeLegalIdentifier(
`__vite__cjsExportDefault_${importIndex}`,
)
importNames.push({ importedName, localName: defaultExports })
} else {
const localName = makeLegalIdentifier(
`__vite__cjsExport_${exportedName}`,
)
const localName = `__vite__cjsExport${
spec.exported.type === 'Literal'
? `L_${getHash(spec.exported.value as string)}`
: 'I_' + spec.exported.name
}`
importNames.push({ importedName, localName })
exportNames.push(`${localName} as ${exportedName}`)
exportNames.push(
`${localName} as ${spec.exported.type === 'Literal' ? JSON.stringify(exportedName) : exportedName}`,
)
}
}
}
Expand Down Expand Up @@ -1033,6 +1038,10 @@ export function transformCjsImport(
}
}

function getIdentifierNameOrLiteralValue(node: Identifier | Literal) {
return node.type === 'Identifier' ? node.name : node.value
}

// Copied from `client/client.ts`. Only needed so we can inline inject this function for classic workers.
function __vite__injectQuery(url: string, queryToInject: string): string {
// skip urls that won't be handled by vite
Expand Down
9 changes: 2 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4389a91

Please sign in to comment.