Skip to content

Commit

Permalink
Add support for arbitrary module namespace identifiers (#4770)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Dec 28, 2022
1 parent 57fa7e0 commit 5aa1cce
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 23 deletions.
46 changes: 26 additions & 20 deletions src/Module.ts
Expand Up @@ -9,12 +9,13 @@ import { nodeConstructors } from './ast/nodes';
import ExportAllDeclaration from './ast/nodes/ExportAllDeclaration';
import ExportDefaultDeclaration from './ast/nodes/ExportDefaultDeclaration';
import type ExportNamedDeclaration from './ast/nodes/ExportNamedDeclaration';
import type Identifier from './ast/nodes/Identifier';
import Identifier from './ast/nodes/Identifier';
import type ImportDeclaration from './ast/nodes/ImportDeclaration';
import ImportDefaultSpecifier from './ast/nodes/ImportDefaultSpecifier';
import type ImportExpression from './ast/nodes/ImportExpression';
import ImportNamespaceSpecifier from './ast/nodes/ImportNamespaceSpecifier';
import Literal from './ast/nodes/Literal';
import type MetaProperty from './ast/nodes/MetaProperty';
import * as NodeType from './ast/nodes/NodeType';
import Program from './ast/nodes/Program';
import TemplateLiteral from './ast/nodes/TemplateLiteral';
import VariableDeclaration from './ast/nodes/VariableDeclaration';
Expand Down Expand Up @@ -880,26 +881,26 @@ export default class Module {
return localVariable;
}

const importDeclaration = this.importDescriptions.get(name);
if (importDeclaration) {
const otherModule = importDeclaration.module;
const importDescription = this.importDescriptions.get(name);
if (importDescription) {
const otherModule = importDescription.module;

if (otherModule instanceof Module && importDeclaration.name === '*') {
if (otherModule instanceof Module && importDescription.name === '*') {
return otherModule.namespace;
}

const [declaration] = getVariableForExportNameRecursive(
otherModule,
importDeclaration.name,
importDescription.name,
importerForSideEffects || this,
isExportAllSearch,
searchedNamesAndModules
);

if (!declaration) {
return this.error(
errorMissingExport(importDeclaration.name, this.id, otherModule.id),
importDeclaration.start
errorMissingExport(importDescription.name, this.id, otherModule.id),
importDescription.start
);
}

Expand Down Expand Up @@ -983,13 +984,13 @@ export default class Module {

const source = node.source.value;
this.addSource(source, node);
for (const specifier of node.specifiers) {
const name = specifier.exported.name;
for (const { exported, local, start } of node.specifiers) {
const name = exported instanceof Literal ? exported.value : exported.name;
this.reexportDescriptions.set(name, {
localName: specifier.local.name,
localName: local instanceof Literal ? local.value : local.name,
module: null as never, // filled in later,
source,
start: specifier.start
start
});
}
} else if (node.declaration) {
Expand All @@ -1012,9 +1013,10 @@ export default class Module {
} else {
// export { foo, bar, baz }

for (const specifier of node.specifiers) {
const localName = specifier.local.name;
const exportedName = specifier.exported.name;
for (const { local, exported } of node.specifiers) {
// except for reexports, local must be an Identifier
const localName = (local as Identifier).name;
const exportedName = exported instanceof Identifier ? exported.name : exported.value;
this.exports.set(exportedName, { identifier: null, localName });
}
}
Expand All @@ -1024,10 +1026,14 @@ export default class Module {
const source = node.source.value;
this.addSource(source, node);
for (const specifier of node.specifiers) {
const isDefault = specifier.type === NodeType.ImportDefaultSpecifier;
const isNamespace = specifier.type === NodeType.ImportNamespaceSpecifier;

const name = isDefault ? 'default' : isNamespace ? '*' : specifier.imported.name;
const name =
specifier instanceof ImportDefaultSpecifier
? 'default'
: specifier instanceof ImportNamespaceSpecifier
? '*'
: specifier.imported instanceof Identifier
? specifier.imported.name
: specifier.imported.value;
this.importDescriptions.set(specifier.local.name, {
module: null as never, // filled in later
name,
Expand Down
5 changes: 3 additions & 2 deletions src/ast/nodes/ExportSpecifier.ts
@@ -1,10 +1,11 @@
import type Identifier from './Identifier';
import type Literal from './Literal';
import type * as NodeType from './NodeType';
import { NodeBase } from './shared/Node';

export default class ExportSpecifier extends NodeBase {
declare exported: Identifier;
declare local: Identifier;
declare exported: Identifier | Literal<string>;
declare local: Identifier | Literal<string>;
declare type: NodeType.tExportSpecifier;

protected applyDeoptimizations() {}
Expand Down
3 changes: 2 additions & 1 deletion src/ast/nodes/ImportSpecifier.ts
@@ -1,9 +1,10 @@
import type Identifier from './Identifier';
import type Literal from './Literal';
import type * as NodeType from './NodeType';
import { NodeBase } from './shared/Node';

export default class ImportSpecifier extends NodeBase {
declare imported: Identifier;
declare imported: Identifier | Literal<string>;
declare local: Identifier;
declare type: NodeType.tImportSpecifier;

Expand Down
@@ -0,0 +1,3 @@
module.exports = {
description: 'supports arbitrary module namespace identifiers'
};
@@ -0,0 +1,2 @@
const foo = 42;
export { foo as ' πŸ˜† ' };
@@ -0,0 +1,3 @@
import { ' πŸ™„ ' as foo } from './reexport.js';

assert.strictEqual(foo, 42);
@@ -0,0 +1 @@
export { ' πŸ˜† ' as ' πŸ™„ ' } from './foo.js';

0 comments on commit 5aa1cce

Please sign in to comment.