diff --git a/.changeset/calm-keys-jog.md b/.changeset/calm-keys-jog.md new file mode 100644 index 000000000..131a0675d --- /dev/null +++ b/.changeset/calm-keys-jog.md @@ -0,0 +1,7 @@ +--- +'@linaria/babel-preset': patch +'@linaria/tags': patch +'@linaria/utils': patch +--- + +Update tags processor to insert appropriate import/request for ESM/CommonJS. diff --git a/packages/babel/src/helper-module-imports.d.ts b/packages/babel/src/helper-module-imports.d.ts index b27fcf17e..d71c570e4 100644 --- a/packages/babel/src/helper-module-imports.d.ts +++ b/packages/babel/src/helper-module-imports.d.ts @@ -1,11 +1,92 @@ declare module '@babel/helper-module-imports' { import type { NodePath } from '@babel/traverse'; - import type { Identifier } from '@babel/types'; + import type * as t from '@babel/types'; + + type ImportOptions = { + /** + * The module being referenced. + */ + importedSource: string | null; + /** + * The type of module being imported: + * + * * 'es6' - An ES6 module. + * * 'commonjs' - A CommonJS module. (Default) + */ + importedType: 'es6' | 'commonjs'; + /** + * The type of interop behavior for namespace/default/named when loading + * CommonJS modules. + * + * ## 'babel' (Default) + * + * Load using Babel's interop. + * + * If '.__esModule' is true, treat as 'compiled', else: + * + * * Namespace: A copy of the module.exports with .default + * populated by the module.exports object. + * * Default: The module.exports value. + * * Named: The .named property of module.exports. + * + * The 'ensureLiveReference' has no effect on the liveness of these. + * + * ## 'compiled' + * + * Assume the module is ES6 compiled to CommonJS. Useful to avoid injecting + * interop logic if you are confident that the module is a certain format. + * + * * Namespace: The root module.exports object. + * * Default: The .default property of the namespace. + * * Named: The .named property of the namespace. + * + * Will return erroneous results if the imported module is _not_ compiled + * from ES6 with Babel. + * + * ## 'uncompiled' + * + * Assume the module is _not_ ES6 compiled to CommonJS. Used a simplified + * access pattern that doesn't require additional function calls. + * + * Will return erroneous results if the imported module _is_ compiled + * from ES6 with Babel. + * + * * Namespace: The module.exports object. + * * Default: The module.exports object. + * * Named: The .named property of module.exports. + */ + importedInterop: 'babel' | 'node' | 'compiled' | 'uncompiled'; + /** + * The type of CommonJS interop included in the environment that will be + * loading the output code. + * + * * 'babel' - CommonJS modules load with Babel's interop. (Default) + * * 'node' - CommonJS modules load with Node's interop. + * + * See descriptions in 'importedInterop' for more details. + */ + importingInterop: 'babel' | 'node'; + /** + * Define whether the import should be loaded before or after the existing imports. + * "after" is only allowed inside ECMAScript modules, since it's not possible to + * reliably pick the location _after_ require() calls but _before_ other code in CJS. + */ + importPosition: 'before' | 'after'; + + nameHint?: string; + blockHoist?: number; + }; + + function addDefault( + path: NodePath, + importedSource: string, + opts?: Partial + ): t.Identifier; function addNamed( path: NodePath, name: string, importedSource: string, - opts?: { nameHint: string } - ): Identifier; + opts?: Partial + ): t.Identifier; } diff --git a/packages/babel/src/utils/getTagProcessor.ts b/packages/babel/src/utils/getTagProcessor.ts index 8c9ec7629..6e57816b9 100644 --- a/packages/babel/src/utils/getTagProcessor.ts +++ b/packages/babel/src/utils/getTagProcessor.ts @@ -2,7 +2,7 @@ import { readFileSync } from 'fs'; import { basename, dirname, join } from 'path'; import { types as t } from '@babel/core'; -import { addNamed } from '@babel/helper-module-imports'; +import { addDefault, addNamed } from '@babel/helper-module-imports'; import type { NodePath } from '@babel/traverse'; import type { Expression, @@ -327,10 +327,16 @@ function getBuilderForIdentifier( }); }; + const importedType = imports.some((i) => i.type === 'esm') + ? 'es6' + : 'commonjs'; + const astService = { ...t, - addNamedImport: (name: string, importedSource: string) => - addNamed(path, name, importedSource), + addDefaultImport: (importedSource: string, nameHint?: string) => + addDefault(path, importedSource, { importedType, nameHint }), + addNamedImport: (name: string, importedSource: string, nameHint?: string) => + addNamed(path, name, importedSource, { importedType, nameHint }), }; return (...args: BuilderArgs) => diff --git a/packages/tags/src/BaseProcessor.ts b/packages/tags/src/BaseProcessor.ts index 74cfeddd8..e45b5330d 100644 --- a/packages/tags/src/BaseProcessor.ts +++ b/packages/tags/src/BaseProcessor.ts @@ -51,7 +51,12 @@ export default abstract class BaseProcessor { public constructor( params: Params, protected readonly astService: typeof t & { - addNamedImport: (name: string, source: string) => Identifier; + addDefaultImport: (source: string, nameHint?: string) => Identifier; + addNamedImport: ( + name: string, + source: string, + nameHint?: string + ) => Identifier; }, public readonly location: SourceLocation | null, protected readonly replacer: ( diff --git a/packages/utils/src/collectExportsAndImports.ts b/packages/utils/src/collectExportsAndImports.ts index 6fbd0fdc6..c44953b3d 100644 --- a/packages/utils/src/collectExportsAndImports.ts +++ b/packages/utils/src/collectExportsAndImports.ts @@ -45,6 +45,7 @@ export interface IImport { imported: string | 'default' | '*'; local: NodePath; source: string; + type: 'cjs' | 'dynamic' | 'esm'; } export interface IExport { @@ -97,7 +98,7 @@ const collectors: { if (isType(path)) return []; const imported = getValue(path.get('imported')); const local = path.get('local'); - return [{ imported, local, source }]; + return [{ imported, local, source, type: 'esm' }]; }, ImportDefaultSpecifier( @@ -105,7 +106,7 @@ const collectors: { source ): IImport[] { const local = path.get('local'); - return [{ imported: 'default', local, source }]; + return [{ imported: 'default', local, source, type: 'esm' }]; }, ImportNamespaceSpecifier( @@ -113,7 +114,7 @@ const collectors: { source ): IImport[] { const local = path.get('local'); - return unfoldNamespaceImport({ imported: '*', local, source }); + return unfoldNamespaceImport({ imported: '*', local, source, type: 'esm' }); }, }; @@ -305,7 +306,12 @@ function collectFromDynamicImport(path: NodePath, state: IState): void { // Is it `const something = await import("something")`? if (key === 'init' && container.isVariableDeclarator()) { importFromVariableDeclarator(container, isAwaited).map((prop) => - state.imports.push({ imported: prop.what, local: prop.as, source }) + state.imports.push({ + imported: prop.what, + local: prop.as, + source, + type: 'dynamic', + }) ); } } @@ -411,6 +417,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported, local: id, source, + type: 'cjs', }); state.imports.push(...unfolded); } else { @@ -418,6 +425,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported, local: id, source, + type: 'cjs', }); } } @@ -445,6 +453,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported: getValue(property), local: id, source, + type: 'cjs', }); } else { warn( @@ -460,6 +469,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported: getValue(property), local: container, source, + type: 'cjs', }); } @@ -474,6 +484,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported: '*', local: prop.as, source, + type: 'cjs', }); state.imports.push(...unfolded); @@ -482,6 +493,7 @@ function collectFromRequire(path: NodePath, state: IState): void { imported: prop.what, local: prop.as, source, + type: 'cjs', }); } });