mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(parser): require a string source after export ... from (#26389)
Named exports must have a string literal module source once `from` is present. The parser previously combined consuming `from` with checking whether the next token was a literal:
```rust
if self.eat(Kind::From) && self.cur_kind().is_literal()
```
When the next token was not a literal, `from` had already been consumed, but parsing fell back to a local export. This incorrectly accepted `export {} from;`, `export {} from` at EOF, and this input:
```js
export {} from
foo();
```
In the last case, the newline allowed automatic semicolon insertion, so the parser accepted a local empty export followed by `foo()` despite the incomplete `from` clause.
Remove the literal-kind guard so consuming `from` always leads to `parse_literal_string()`. That helper already requires a string literal and handles malformed input through the existing parser diagnostics. A missing source now produces a syntax error instead of becoming a local export, while unterminated strings and comments retain the standard lexer diagnostics.
Valid local exports such as `export { foo };` and re-exports such as `export { foo } from "./module.js";` keep their existing behavior, including a module source placed on the next line.
This commit is contained in:
1 parent
5303f5c884
commit
1400a0f8be
10 files changed
+50
-8
No files matched your search
@@ -560,7 +560,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
})
|
||||
});
|
||||
self.expect(Kind::RCurly);
|
||||
let (source, with_clause) = if self.eat(Kind::From) && self.cur_kind().is_literal() {
|
||||
let (source, with_clause) = if self.eat(Kind::From) {
|
||||
let source = self.parse_literal_string();
|
||||
(Some(source), self.parse_import_attributes())
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export {} from
|
||||
@@ -0,0 +1,2 @@
|
||||
export {} from
|
||||
foo();
|
||||
@@ -0,0 +1 @@
|
||||
export {} from;
|
||||
@@ -0,0 +1 @@
|
||||
export { "\xG" as foo } from
|
||||
@@ -0,0 +1 @@
|
||||
export {} from /*
|
||||
@@ -0,0 +1 @@
|
||||
export {} from "
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 255/255 (100.00%)
|
||||
Positive Passed: 255/255 (100.00%)
|
||||
AST Parsed : 261/261 (100.00%)
|
||||
Positive Passed: 261/261 (100.00%)
|
||||
@@ -9916,12 +9916,11 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
|
||||
1 │ import { foo, bar }
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[babel/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-module-specifier/input.js:1:18]
|
||||
× Unexpected token
|
||||
╭─[babel/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-module-specifier/input.js:1:19]
|
||||
1 │ export {foo} from bar
|
||||
· ▲
|
||||
· ───
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected `from` but found `,`
|
||||
╭─[babel/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-named-after-named/input.js:1:13]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 84/84 (100.00%)
|
||||
Positive Passed: 84/84 (100.00%)
|
||||
Negative Passed: 171/171 (100.00%)
|
||||
Negative Passed: 177/177 (100.00%)
|
||||
|
||||
× Cannot assign to 'arguments' in strict mode
|
||||
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
|
||||
@@ -341,6 +341,42 @@ Negative Passed: 171/171 (100.00%)
|
||||
╰────
|
||||
help: If you want to use `export =`, remove other `export`s and put all of them to the right hand value of `export =`. If you want to use `export`s, remove `export =` statement.
|
||||
|
||||
× Unexpected token
|
||||
╭─[misc/fail/export-from-missing-source-eof.js:2:1]
|
||||
1 │ export {} from
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[misc/fail/export-from-missing-source-newline.js:2:1]
|
||||
1 │ export {} from
|
||||
2 │ foo();
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[misc/fail/export-from-missing-source-semicolon.js:1:15]
|
||||
1 │ export {} from;
|
||||
· ─
|
||||
╰────
|
||||
|
||||
× Invalid escape sequence
|
||||
╭─[misc/fail/export-from-prior-lexer-error.js:1:11]
|
||||
1 │ export { "\xG" as foo } from
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× Unterminated multiline comment
|
||||
╭─[misc/fail/export-from-unterminated-comment.js:1:16]
|
||||
1 │ export {} from /*
|
||||
· ───
|
||||
╰────
|
||||
|
||||
× Unterminated string
|
||||
╭─[misc/fail/export-from-unterminated-source.js:1:16]
|
||||
1 │ export {} from "
|
||||
· ──
|
||||
╰────
|
||||
|
||||
× HTML comments are not allowed in modules
|
||||
╭─[misc/fail/html-comment-in-module.mjs:1:1]
|
||||
1 │ <!-- comment
|
||||
|
||||
Reference in new issue
Block a user