Split up specifiers in ImportDeclaration and ExportExternalDeclaration for better type safety
MODIFY
packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts
packages/@romejs/js-ast/modules/ExportExternalDeclaration.ts
@@ -24,7 +24,9 @@ export type ExportExternalDeclaration =
& JSNodeBase
& {
type: 'ExportExternalDeclaration';
- specifiers?: Array<AnyExportExternalSpecifier>;
+ defaultSpecifier?: ExportDefaultSpecifier;
+ namespaceSpecifier?: ExportNamespaceSpecifier;
+ namedSpecifiers: Array<ExportExternalSpecifier>;
source: StringLiteral;
exportKind?: ConstExportModuleKind;
};
@@ -33,7 +35,9 @@ export const exportExternalDeclaration =
createBuilder<ExportExternalDeclaration>('ExportExternalDeclaration', {
bindingKeys: {},
visitorKeys: {
- specifiers: true,
+ defaultSpecifier: true,
+ namespaceSpecifier: true,
+ namedSpecifiers: true,
source: true,
},
});

MODIFY
packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts
packages/@romejs/js-compiler/api/analyzeDependencies/visitors/es.ts
@@ -105,14 +105,31 @@ export default {
const specifiersKinds: Array<ConstImportModuleKind> = [];
const exportedNames: Array<AnalyzeDependencyName> = [];
- const {specifiers} = node;
- if (specifiers !== undefined) {
- for (const specifier of specifiers) {
- switch (specifier.type) {
- case 'ExportExternalSpecifier':
- {
- const kind = getImportKind(specifier.exportKind ||
- node.exportKind);
+ const {namedSpecifiers, defaultSpecifier, namespaceSpecifier} = node;
+
+ if (defaultSpecifier !== undefined) {
+ context.record(new ExportRecord({
+ type: 'external',
+ kind: 'value',
+ loc: defaultSpecifier.loc,
+ imported: 'default',
+ exported: defaultSpecifier.exported.name,
+ source: source.value,
+ }));
+ }
+
+ if (namespaceSpecifier !== undefined) {
+ context.record(new ExportRecord({
+ type: 'externalNamespace',
+ kind: 'value',
+ loc: namespaceSpecifier.loc,
+ exported: namespaceSpecifier.exported.name,
+ source: source.value,
+ }));
+ }
+
+ for (const specifier of namedSpecifiers) {
+ const kind = getImportKind(specifier.exportKind || node.exportKind);
specifiersKinds.push(kind);
exportedNames.push({
@@ -129,15 +146,6 @@ export default {
exported: specifier.exported.name,
source: source.value,
}));
- break;
- }
-
- case 'ExportNamespaceSpecifier':
- throw new Error('unimplemented');
-
- case 'ExportDefaultSpecifier':
- throw new Error('unimplemented');
- }
}
context.record(new ImportRecord({
@@ -151,7 +159,6 @@ export default {
all: false,
}));
}
- }
// TS: import A = require('B');
if (node.type === 'TSImportEqualsDeclaration' &&

MODIFY
packages/@romejs/js-compiler/scope/bindings.ts
packages/@romejs/js-compiler/scope/bindings.ts
@@ -56,9 +56,9 @@ export class ConstBinding extends Binding {
constructor(
opts: BindingOpts,
value: undefined | AnyNode,
- defaultKind?: string,
+ kind: string = 'constant',
) {
- super(opts, defaultKind);
+ super(opts, kind);
this.value = value;
}

MODIFY
packages/@romejs/js-compiler/transforms/compile/validation/optimizeExports.ts
packages/@romejs/js-compiler/transforms/compile/validation/optimizeExports.ts
@@ -36,7 +36,7 @@ export default {
if (binding !== undefined && binding instanceof ImportBinding &&
binding.meta.type === 'name') {
nodes.push(exportExternalDeclaration.create({
- specifiers: [
+ namedSpecifiers: [
exportExternalSpecifier.create({
local: identifier.quick(binding.meta.imported),
exported: specifier.exported,

MODIFY
packages/@romejs/js-compiler/transforms/compileForBundle/legacy/esToCJSTransform.ts
packages/@romejs/js-compiler/transforms/compileForBundle/legacy/esToCJSTransform.ts
@@ -12,8 +12,6 @@ import {
program,
stringLiteral,
ClassExpression,
- ExportLocalDeclaration,
- ExportExternalDeclaration,
} from '@romejs/js-ast';
import {
template,
@@ -83,25 +81,36 @@ export default {
continue;
}
- if (bodyNode.type === 'ExportLocalDeclaration' || bodyNode.type ===
- 'ExportExternalDeclaration') {
- // Ignore typed exports
+ if (bodyNode.type === 'ExportExternalDeclaration') {
if (bodyNode.exportKind === 'type') {
continue;
}
- let declaration: undefined | ExportLocalDeclaration['declaration'];
- let source: undefined | ExportExternalDeclaration['source'];
- const {specifiers} = bodyNode;
+ const {source} = bodyNode;
- if (bodyNode.type === 'ExportExternalDeclaration') {
- source = bodyNode.source;
+ // TODO defaultSpecifier and namespaceSpecifier
+ for (const specifier of bodyNode.namedSpecifiers) {
+ topBody.push(
+ template.statement`Object.defineProperty(exports, ${stringLiteral.create(
+ {
+ value: specifier.exported.name,
+ },
+ )}, {
+ get: function() {
+ return Rome.requireNamespace(${source}).${specifier.local};
+ },
+ })`,
+ );
+ }
}
if (bodyNode.type === 'ExportLocalDeclaration') {
- declaration = bodyNode.declaration;
+ if (bodyNode.exportKind === 'type') {
+ continue;
}
+ const {declaration, specifiers} = bodyNode;
+
if (declaration !== undefined) {
// Hoist function declarations
if (declaration.type === 'FunctionDeclaration') {
@@ -137,18 +146,6 @@ export default {
if (specifiers !== undefined) {
for (const specifier of specifiers) {
- if (specifier.type === 'ExportDefaultSpecifier') {
- // TODO only allowed for `source`
- }
-
- if (specifier.type === 'ExportNamespaceSpecifier') {
- // TODO only allowed for `source`
- }
-
- // TODO skip type exports
- if (specifier.type === 'ExportLocalSpecifier' || specifier.type ===
- 'ExportExternalSpecifier') {
- if (source === undefined) {
const binding = path.scope.getBinding(specifier.local.name);
if (binding instanceof FunctionBinding) {
@@ -163,20 +160,6 @@ export default {
template.statement`exports.${specifier.exported} = ${specifier.local};`,
);
}
- } else {
- topBody.push(
- template.statement`Object.defineProperty(exports, ${stringLiteral.create(
- {
- value: specifier.exported.name,
- },
- )}, {
- get: function() {
- return Rome.requireNamespace(${source}).${specifier.local};
- },
- })`,
- );
- }
- }
}
}

MODIFY
packages/@romejs/js-compiler/transforms/compileForBundle/modern/esToRefTransform.ts
packages/@romejs/js-compiler/transforms/compileForBundle/modern/esToRefTransform.ts
@@ -33,7 +33,6 @@ import {
bindingIdentifier,
staticPropertyKey,
variableDeclarationStatement,
- ExportExternalDeclaration,
ObjectProperties,
program,
blockStatement,
@@ -148,44 +147,36 @@ export default {
));
}
- if (child.type === 'ExportLocalDeclaration' || child.type ===
- 'ExportExternalDeclaration') {
- const {specifiers} = child;
-
- if (child.type === 'ExportLocalDeclaration' && child.declaration !==
- undefined) {
- throw new Error(
- 'No export declarations should be here as they have been removed by renameBindings',
- );
- }
-
- let source: undefined | ExportExternalDeclaration['source'];
if (child.type === 'ExportExternalDeclaration') {
- source = child.source;
- }
-
- if (specifiers !== undefined) {
- for (const specifier of specifiers) {
- if (specifier.type === 'ExportLocalSpecifier' ||
- specifier.type === 'ExportExternalSpecifier') {
- // The local binding has already been rewritten by renameBindings if it existed
- let local = specifier.local.name;
+ // TODO defaultSpecifier and namespaceSpecifier
+ const {source} = child;
+ for (const specifier of child.namedSpecifiers) {
// If this is an external export then use the correct name
- if (source !== undefined) {
const moduleId = getModuleId(source.value, opts);
if (moduleId === undefined) {
continue;
}
- local = getPrefixedName(local, moduleId, opts);
- }
+ const local = getPrefixedName(specifier.local.name, moduleId, opts);
exportNames.set(specifier.exported.name, local);
- } else {
- // TODO ???
}
}
+
+ if (child.type === 'ExportLocalDeclaration') {
+ if (child.declaration !== undefined) {
+ throw new Error(
+ 'No export declarations should be here as they have been removed by renameBindings',
+ );
+ }
+
+ const {specifiers} = child;
+ if (specifiers !== undefined) {
+ for (const specifier of specifiers) {
+ // The local binding has already been rewritten by renameBindings if it existed
+ exportNames.set(specifier.exported.name, specifier.local.name);
+ }
}
}
}

MODIFY
packages/@romejs/js-generator/generators/modules/ExportExternalDeclaration.ts
packages/@romejs/js-generator/generators/modules/ExportExternalDeclaration.ts
@@ -11,6 +11,7 @@ import {
ExportExternalDeclaration,
exportExternalDeclaration,
} from '@romejs/js-ast';
+import {printModuleSpecifiers} from './ImportDeclaration';
export default function ExportExternalDeclaration(
generator: Generator,
@@ -26,41 +27,8 @@ export default function ExportExternalDeclaration(
generator.space();
}
- if (node.specifiers === undefined) {
- throw new Error('Expected specifiers since there was no declaration');
- }
-
- const specifiers = node.specifiers.slice(0);
-
generator.multiline(node, (multiline, node) => {
- // print "special" specifiers first
- let hasSpecial = false;
- while (true) {
- const first = specifiers[0];
- if (first !== undefined && (first.type === 'ExportDefaultSpecifier' ||
- first.type === 'ExportNamespaceSpecifier')) {
- hasSpecial = true;
- generator.print(specifiers.shift(), node);
- if (specifiers.length) {
- generator.token(',');
- generator.space();
- }
- } else {
- break;
- }
- }
-
- if (specifiers.length || !specifiers.length && !hasSpecial) {
- generator.token('{');
- if (specifiers.length) {
- generator.printCommaList(specifiers, node, {
- multiline,
- trailing: true,
- });
- }
- generator.token('}');
- }
-
+ printModuleSpecifiers(generator, node, multiline);
generator.space();
generator.word('from');
generator.space();

MODIFY
packages/@romejs/js-generator/generators/modules/ImportDeclaration.ts
packages/@romejs/js-generator/generators/modules/ImportDeclaration.ts
*/
import Generator from '../../Generator';
-import {ImportDeclaration, importDeclaration, AnyNode} from '@romejs/js-ast';
+import {
+ ImportDeclaration,
+ importDeclaration,
+ ExportExternalDeclaration,
+ AnyNode,
+} from '@romejs/js-ast';
export default function ImportDeclaration(generator: Generator, node: AnyNode) {
node = importDeclaration.assert(node);
@@ -24,6 +29,24 @@ export default function ImportDeclaration(generator: Generator, node: AnyNode) {
if (namedSpecifiers.length > 0 || namespaceSpecifier !== undefined ||
defaultSpecifier !== undefined) {
+ printModuleSpecifiers(generator, node, multiline);
+ generator.space();
+ generator.word('from');
+ generator.space();
+ }
+
+ generator.print(node.source, node);
+ generator.semicolon();
+ }, {conditions: ['more-than-one-line']});
+}
+
+export function printModuleSpecifiers(
+ generator: Generator,
+ node: ImportDeclaration | ExportExternalDeclaration,
+ multiline: boolean,
+) {
+ const {namedSpecifiers, defaultSpecifier, namespaceSpecifier} = node;
+
if (defaultSpecifier !== undefined) {
generator.print(node.defaultSpecifier, node);
if (namedSpecifiers.length > 0 || namespaceSpecifier !== undefined) {
@@ -42,19 +65,13 @@ export default function ImportDeclaration(generator: Generator, node: AnyNode) {
if (namedSpecifiers.length > 0) {
generator.token('{');
- generator.printCommaList(namedSpecifiers, node, {
+ generator.printCommaList<
+ | ImportDeclaration['namedSpecifiers'][number]
+ | ExportExternalDeclaration['namedSpecifiers'][number]>(namedSpecifiers, node, {
multiline,
trailing: true,
- });
- generator.token('}');
}
-
- generator.space();
- generator.word('from');
- generator.space();
+ );
+ generator.token('}');
}
-
- generator.print(node.source, node);
- generator.semicolon();
- }, {conditions: ['more-than-one-line']});
}

MODIFY
packages/@romejs/js-parser/parser/modules.ts
packages/@romejs/js-parser/parser/modules.ts
@@ -29,6 +29,9 @@ import {
ImportDefaultSpecifier,
ImportNamespaceSpecifier,
ImportSpecifierLocal,
+ ExportExternalSpecifier,
+ ExportNamespaceSpecifier,
+ ExportDefaultSpecifier,
} from '@romejs/js-ast';
import {getBindingIdentifiers} from '@romejs/js-ast-utils';
import {
@@ -81,7 +84,7 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
let exportKind: ConstExportModuleKind = 'value';
let declaration: undefined | AnyStatement;
- let specifiers: undefined | Array<ExportLocalSpecifier>;
+ let localSpecifiers: undefined | Array<ExportLocalSpecifier>;
// export * from '...'';
if (shouldParseExportStar(parser)) {
@@ -89,12 +92,13 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
} else if (isExportDefaultSpecifier(parser)) {
const defStart = parser.getPosition();
const defExported = parseIdentifier(parser, true);
- let specifiers: Array<AnyExportExternalSpecifier> = [];
- specifiers.push(parser.finishNode(defStart, {
+ let namedSpecifiers: Array<ExportExternalSpecifier> = [];
+ let defaultSpecifier: ExportDefaultSpecifier = parser.finishNode(defStart, {
type: 'ExportDefaultSpecifier',
exported: defExported,
- }));
+ });
+ let namespaceSpecifier: undefined | ExportNamespaceSpecifier;
if (parser.match(tt.comma) && parser.lookaheadState().tokenType === tt.star) {
parser.expect(tt.comma);
@@ -102,26 +106,30 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
parser.expect(tt.star);
parser.expectContextual('as');
const exported = parseIdentifier(parser);
- specifiers.push(parser.finishNode(specifierStart, {
+ namespaceSpecifier = parser.finishNode(specifierStart, {
type: 'ExportNamespaceSpecifier',
exported,
- }));
+ });
} else {
- specifiers = [
- ...specifiers,
- ...convertLocalToExternalSpecifiers(
+ namedSpecifiers = convertLocalToExternalSpecifiers(
parser,
parseExportLocalSpecifiersMaybe(parser),
- ),
- ];
+ );
}
const source = parseExportFromExpect(parser);
- return createExportExternalDeclaration(parser, start, specifiers, source);
+ return createExportExternalDeclaration(
+ parser,
+ start,
+ defaultSpecifier,
+ namespaceSpecifier,
+ namedSpecifiers,
+ source,
+ );
} else if (parser.eat(tt._default)) {
// export default ...
const declaration = parseExportDefaultExpression(parser);
- checkExport(parser, specifiers, declaration, true, true);
+ checkExport(parser, localSpecifiers, declaration, true, true);
const node: ExportDefaultDeclaration = parser.finishNode(start, {
type: 'ExportDefaultDeclaration',
@@ -130,9 +138,12 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
return node;
} else if (shouldParseExportDeclaration(parser)) {
let source;
- ({declaration, source, specifiers, exportKind} = parseExportDeclaration(
- parser,
- ));
+ ({
+ declaration,
+ source,
+ localSpecifiers,
+ exportKind,
+ } = parseExportDeclaration(parser));
if (source !== undefined) {
if (declaration !== undefined) {
@@ -144,7 +155,9 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
return createExportExternalDeclaration(
parser,
start,
- specifiers,
+ undefined,
+ undefined,
+ convertLocalToExternalSpecifiers(parser, localSpecifiers),
source,
exportKind,
);
@@ -160,18 +173,25 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
description: descriptions.JS_PARSER.EXPORT_ASYNC_NO_FUNCTION_KEYWORD,
});
declaration = undefined;
- specifiers = [];
+ localSpecifiers = [];
} else {
// export { x, y as z } [from '...']';
- specifiers = parseExportSpecifiers(parser);
+ localSpecifiers = parseExportSpecifiers(parser);
const source = parseExportFrom(parser, false);
if (source !== undefined) {
- return createExportExternalDeclaration(parser, start, specifiers, source);
+ return createExportExternalDeclaration(
+ parser,
+ start,
+ undefined,
+ undefined,
+ convertLocalToExternalSpecifiers(parser, localSpecifiers),
+ source,
+ );
}
}
- checkExport(parser, specifiers, declaration, true, false);
+ checkExport(parser, localSpecifiers, declaration, true, false);
if (declaration !== undefined) {
if (declaration.type !== 'VariableDeclarationStatement' &&
@@ -193,7 +213,7 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
const node: ExportLocalDeclaration = parser.finishNode(start, {
type: 'ExportLocalDeclaration',
exportKind,
- specifiers,
+ specifiers: localSpecifiers,
declaration,
});
return node;
@@ -202,39 +222,40 @@ export function parseExport(parser: JSParser, start: Position): ParseExportResul
function createExportExternalDeclaration(
parser: JSParser,
start: Position,
- rawSpecifiers:
- | undefined
- | Array<ExportLocalSpecifier | AnyExportExternalSpecifier>,
-
+ defaultSpecifier: undefined | ExportDefaultSpecifier,
+ namespaceSpecifier: undefined | ExportNamespaceSpecifier,
+ namedSpecifiers: Array<ExportExternalSpecifier>,
source: StringLiteral,
exportKind?: ConstExportModuleKind,
): ExportExternalDeclaration {
- const specifiers = rawSpecifiers === undefined
- ? undefined : convertLocalToExternalSpecifiers(parser, rawSpecifiers);
- checkExport(parser, specifiers, undefined, true, false);
+ checkExport(
+ parser,
+ [defaultSpecifier, namespaceSpecifier, ...namedSpecifiers],
+ undefined,
+ true,
+ false,
+ );
return parser.finishNode(start, {
type: 'ExportExternalDeclaration',
exportKind,
source,
- specifiers,
+ namedSpecifiers,
+ defaultSpecifier,
+ namespaceSpecifier,
});
}
function convertLocalToExternalSpecifiers(
parser: JSParser,
- specifiers: Array<AnyExportExternalSpecifier | ExportLocalSpecifier>,
-): Array<AnyExportExternalSpecifier> {
+ specifiers: Array<ExportLocalSpecifier> = [],
+): Array<ExportExternalSpecifier> {
return specifiers.map((specifier) => {
- if (specifier.type === 'ExportLocalSpecifier') {
return {
...specifier,
type: 'ExportExternalSpecifier',
local: toIdentifier(parser, specifier.local),
};
- } else {
- return specifier;
- }
});
}
@@ -286,7 +307,7 @@ function parseExportDeclaration(
): {
exportKind: ConstExportModuleKind;
declaration?: AnyStatement;
- specifiers?: Array<ExportLocalSpecifier>;
+ localSpecifiers?: Array<ExportLocalSpecifier>;
source?: StringLiteral;
} {
if (parser.isContextual('type')) {
@@ -299,7 +320,7 @@ function parseExportDeclaration(
const source = parseExportFrom(parser, false);
return {
exportKind: 'type',
- specifiers,
+ localSpecifiers: specifiers,
source,
};
} else {
@@ -436,11 +457,11 @@ function parseExportStar(
parser.expect(tt.star);
if (parser.isContextual('as')) {
- const {source, specifiers} = parseExportNamespace(parser, exportKind);
+ const {source, namedSpecifiers} = parseExportNamespace(parser, exportKind);
return parser.finishNode(start, {
type: 'ExportExternalDeclaration',
exportKind,
- specifiers,
+ namedSpecifiers,
source,
});
} else {
@@ -461,7 +482,8 @@ function parseExportNamespace(
exportKind: undefined | ConstExportModuleKind,
): {
source: StringLiteral;
- specifiers: Array<AnyExportExternalSpecifier>;
+ namespaceSpecifier: ExportNamespaceSpecifier;
+ namedSpecifiers: Array<ExportExternalSpecifier>;
} {
if (exportKind === 'type') {
parser.addDiagnostic({
@@ -473,22 +495,18 @@ function parseExportNamespace(
parser.next();
const exported = parseIdentifier(parser, true);
- let specifiers: Array<AnyExportExternalSpecifier> = [];
-
- specifiers.push(parser.finishNode(specifierStart, {
+ const namespaceSpecifier = parser.finishNode(specifierStart, {
type: 'ExportNamespaceSpecifier',
exported,
- }));
+ });
- specifiers = [
- ...specifiers,
- ...convertLocalToExternalSpecifiers(parser, parseExportLocalSpecifiersMaybe(
+ const namedSpecifiers = convertLocalToExternalSpecifiers(
parser,
- )),
- ];
+ parseExportLocalSpecifiersMaybe(parser),
+ );
const source = parseExportFromExpect(parser);
- return {source, specifiers};
+ return {source, namespaceSpecifier, namedSpecifiers};
}
function shouldParseExportDeclaration(parser: JSParser): boolean {
@@ -504,7 +522,7 @@ function checkExport(
parser: JSParser,
specifiers:
| undefined
- | Array<ExportLocalSpecifier | AnyExportExternalSpecifier>,
+ | Array<undefined | ExportLocalSpecifier | AnyExportExternalSpecifier>,
declaration: undefined | AnyNode,
checkNames: boolean = false,
@@ -523,28 +541,6 @@ function checkExport(
return undefined;
}
- if (specifiers !== undefined && specifiers.length > 0) {
- // Named exports
- for (const specifier of specifiers) {
- checkDuplicateExports(parser, specifier, specifier.exported.name);
-
- if (specifier.type === 'ExportLocalSpecifier') {
- const {local} = specifier;
- if (local !== undefined) {
- // check for keywords used as local names
- checkReservedWord(
- parser,
- local.name,
- parser.getLoc(local),
- true,
- false,
- );
- }
- }
- }
- return undefined;
- }
-
if (declaration !== undefined) {
// Exported declarations
if (declaration.type === 'FunctionDeclaration') {
@@ -569,6 +565,31 @@ function checkExport(
}
}
}
+
+ if (specifiers !== undefined) {
+ // Named exports
+ for (const specifier of specifiers) {
+ if (specifier === undefined) {
+ continue;
+ }
+
+ checkDuplicateExports(parser, specifier, specifier.exported.name);
+
+ if (specifier.type === 'ExportLocalSpecifier') {
+ const {local} = specifier;
+ if (local !== undefined) {
+ // check for keywords used as local names
+ checkReservedWord(
+ parser,
+ local.name,
+ parser.getLoc(local),
+ true,
+ false,
+ );
+ }
+ }
+ }
+ }
}
function checkDuplicateExports(

Split up specifiers in ImportDeclaration and ExportExternalDeclaration for better type safety