Fix ExportExternalDeclaration specifier comment attachment - fixes
MODIFY
packages/@romejs/js-parser/parser/modules.ts
packages/@romejs/js-parser/parser/modules.ts
@@ -96,7 +96,7 @@ export function parseExport(
const defStart = parser.getPosition();
const defExported = parseIdentifier(parser, true);
- let namedSpecifiers: Array<ExportExternalSpecifier> = [];
+ let namedSpecifiers: Array<ExportLocalSpecifier> = [];
let defaultSpecifier: ExportDefaultSpecifier = parser.finishNode(defStart, {
type: 'ExportDefaultSpecifier',
exported: defExported,
@@ -114,10 +114,7 @@ export function parseExport(
exported,
});
} else {
- namedSpecifiers = convertLocalToExternalSpecifiers(
- parser,
- parseExportLocalSpecifiersMaybe(parser),
- );
+ namedSpecifiers = parseExportLocalSpecifiersMaybe(parser);
}
const source = parseExportFromExpect(parser);
@@ -132,7 +129,11 @@ export function parseExport(
} else if (parser.eat(tt._default)) {
// export default ...
const declaration = parseExportDefaultExpression(parser);
- checkExport(parser, localSpecifiers, declaration, true, true);
+ checkExport(parser, {
+ specifiers: localSpecifiers,
+ declaration,
+ isDefault: true,
+ });
const node: ExportDefaultDeclaration = parser.finishNode(start, {
type: 'ExportDefaultDeclaration',
@@ -160,7 +161,7 @@ export function parseExport(
start,
undefined,
undefined,
- convertLocalToExternalSpecifiers(parser, localSpecifiers),
+ localSpecifiers === undefined ? [] : localSpecifiers,
source,
exportKind,
);
@@ -188,13 +189,17 @@ export function parseExport(
start,
undefined,
undefined,
- convertLocalToExternalSpecifiers(parser, localSpecifiers),
+ localSpecifiers,
source,
);
}
}
- checkExport(parser, localSpecifiers, declaration, true, false);
+ checkExport(parser, {
+ specifiers: localSpecifiers,
+ declaration,
+ isDefault: false,
+ });
if (declaration !== undefined) {
if (declaration.type !== 'VariableDeclarationStatement' &&
@@ -232,26 +237,33 @@ function createExportExternalDeclaration(
start: Position,
defaultSpecifier: undefined | ExportDefaultSpecifier,
namespaceSpecifier: undefined | ExportNamespaceSpecifier,
- namedSpecifiers: Array<ExportExternalSpecifier>,
+ namedSpecifiers: Array<ExportLocalSpecifier>,
source: StringLiteral,
exportKind?: ConstExportModuleKind,
): ExportExternalDeclaration {
- checkExport(
- parser,
- [defaultSpecifier, namespaceSpecifier, ...namedSpecifiers],
- undefined,
- true,
- false,
- );
+ checkExport(parser, {
+ specifiers: [defaultSpecifier, namespaceSpecifier, ...namedSpecifiers],
+ declaration: undefined,
+ isDefault: false,
+ localIsExternal: true,
+ });
- return parser.finishNode(start, {
+ const node = parser.finishNode(start, {
type: 'ExportExternalDeclaration',
exportKind,
source,
- namedSpecifiers,
+ namedSpecifiers: [],
defaultSpecifier,
namespaceSpecifier,
});
+
+ // We convert the specifiers after we've finished the ExportExternalDeclaration node
+ // as the comment attachment logic may mess with the specifiers and so we need to
+ // clone them after
+ return {
+ ...node,
+ namedSpecifiers: convertLocalToExternalSpecifiers(parser, namedSpecifiers),
+ };
}
function convertLocalToExternalSpecifiers(
@@ -531,19 +543,21 @@ function shouldParseExportDeclaration(parser: JSParser): boolean {
function checkExport(
parser: JSParser,
- specifiers: undefined | Array<
+ {
+ specifiers,
+ declaration,
+ localIsExternal = false,
+ isDefault = false,
+ }: {
+ localIsExternal?: boolean;
+ specifiers?: Array<
| undefined
| ExportLocalSpecifier
- | AnyExportExternalSpecifier>,
-
- declaration: undefined | AnyNode,
- checkNames: boolean = false,
- isDefault: boolean = false,
+ | AnyExportExternalSpecifier>;
+ declaration?: AnyNode;
+ isDefault: boolean;
+ },
): void {
- if (checkNames === false) {
- return undefined;
- }
-
// Check for duplicate exports
if (isDefault) {
// Default exports
@@ -587,7 +601,7 @@ function checkExport(
checkDuplicateExports(parser, specifier, specifier.exported.name);
- if (specifier.type === 'ExportLocalSpecifier') {
+ if (specifier.type === 'ExportLocalSpecifier' && !localIsExternal) {
const {local} = specifier;
if (local !== undefined) {
// check for keywords used as local names

ADD
packages/@romejs/js-parser/test-fixtures/comments/export-external/input.js
+/* LEADING */
+export {FOO} from './bar';

ADD
packages/@romejs/js-parser/test-fixtures/comments/export-external/input.test.md
+# `index.test.ts`
+
+**DO NOT MODIFY**. This file has been autogenerated. Run `rome test packages/@romejs/js-parser/index.test.ts --update-snapshots` to update.
+
+## `comments > export-external`
+
+```javascript
+Program {
+ corrupt: false
+ directives: Array []
+ filename: 'input.js'
+ hasHoistedVars: false
+ interpreter: undefined
+ mtime: undefined
+ sourceType: 'script'
+ syntax: Array []
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 0
+ index: 41
+ line: 3
+ }
+ start: Object {
+ column: 0
+ index: 0
+ line: 1
+ }
+ }
+ comments: Array [
+ CommentBlock {
+ id: '0'
+ value: ' LEADING '
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 13
+ index: 13
+ line: 1
+ }
+ start: Object {
+ column: 0
+ index: 0
+ line: 1
+ }
+ }
+ }
+ ]
+ diagnostics: Array [
+ Object {
+ origins: Array [Object {category: 'js-parser'}]
+ description: Object {
+ category: 'parse/js'
+ message: PARTIAL_BLESSED_DIAGNOSTIC_MESSAGE {value: '<emphasis>import</emphasis> and <emphasis>export</emphasis> can only appear in a module'}
+ advice: Array [
+ log {
+ category: 'info'
+ message: 'Change the extension to <emphasis>.mjs</emphasis> to turn this file into a module'
+ }
+ log {
+ category: 'info'
+ message: 'Add <emphasis>"type": "module"</emphasis> to your <filelink emphasis target="package.json" />'
+ }
+ ]
+ }
+ location: Object {
+ filename: 'input.js'
+ mtime: undefined
+ sourceType: 'script'
+ end: Object {
+ column: 26
+ index: 40
+ line: 2
+ }
+ start: Object {
+ column: 0
+ index: 14
+ line: 2
+ }
+ }
+ }
+ ]
+ body: Array [
+ ExportExternalDeclaration {
+ defaultSpecifier: undefined
+ exportKind: undefined
+ namespaceSpecifier: undefined
+ leadingComments: Array ['0']
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 26
+ index: 40
+ line: 2
+ }
+ start: Object {
+ column: 0
+ index: 14
+ line: 2
+ }
+ }
+ source: StringLiteral {
+ value: './bar'
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 25
+ index: 39
+ line: 2
+ }
+ start: Object {
+ column: 18
+ index: 32
+ line: 2
+ }
+ }
+ }
+ namedSpecifiers: Array [
+ ExportExternalSpecifier {
+ leadingComments: undefined
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 11
+ index: 25
+ line: 2
+ }
+ start: Object {
+ column: 8
+ index: 22
+ line: 2
+ }
+ }
+ local: Identifier {
+ name: 'FOO'
+ leadingComments: undefined
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 11
+ index: 25
+ line: 2
+ }
+ start: Object {
+ column: 8
+ index: 22
+ line: 2
+ }
+ }
+ }
+ exported: Identifier {
+ name: 'FOO'
+ innerComments: undefined
+ leadingComments: undefined
+ trailingComments: undefined
+ loc: Object {
+ filename: 'input.js'
+ end: Object {
+ column: 11
+ index: 25
+ line: 2
+ }
+ start: Object {
+ column: 8
+ index: 22
+ line: 2
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+}
+```

Fix ExportExternalDeclaration specifier comment attachment - fixes #293