Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Port babel-parser changes from 2023-01-11 to 2023-03-14 #786

Merged
merged 2 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions spec-compliance-tests/babel-tests/check-babel-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ es2015/yield/without-argument
es2018/async-generators/for-await-async-of
es2020/bigint/decimal-as-property-name
estree/class-private-property/flow
experimental/decorators/export-decorated-class
experimental/decorators/export-default-decorated-class
experimental/decorators/parenthesized // Uses obsolete syntax @(a)()
experimental/decorators/parenthesized-createParenthesizedExpressions // Uses obsolete syntax @(a)()
flow/anonymous-function-no-parens-types/good_15
Expand Down
12 changes: 10 additions & 2 deletions src/parser/plugins/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ function tsParseImportType(): void {
}

function tsParseTypeParameter(): void {
eat(tt._const);
const hadIn = eat(tt._in);
const hadOut = eatContextual(ContextualKeyword._out);
eat(tt._const);
if ((hadIn || hadOut) && !match(tt.name)) {
// The "in" or "out" keyword must have actually been the type parameter
// name, so set it as the name.
Expand Down Expand Up @@ -1298,8 +1300,14 @@ export function tsTryParseExport(): boolean {
semicolon();
return true;
} else {
if (isContextual(ContextualKeyword._type) && lookaheadType() === tt.braceL) {
next();
if (isContextual(ContextualKeyword._type)) {
const nextType = lookaheadType();
// export type {foo} from 'a';
// export type * from 'a';'
// export type * as ns from 'a';'
if (nextType === tt.braceL || nextType === tt.star) {
next();
}
}
return false;
}
Expand Down
57 changes: 48 additions & 9 deletions src/transformers/CJSImportTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ export default class CJSImportTransformer extends Transformer {
return true;
} else if (
this.tokens.matches2(tt._export, tt._class) ||
this.tokens.matches3(tt._export, tt._abstract, tt._class)
this.tokens.matches3(tt._export, tt._abstract, tt._class) ||
this.tokens.matches2(tt._export, tt.at)
) {
this.processExportClass();
return true;
Expand All @@ -315,16 +316,31 @@ export default class CJSImportTransformer extends Transformer {
this.processExportStar();
return true;
} else if (
this.tokens.matches3(tt._export, tt.name, tt.braceL) &&
this.tokens.matches2(tt._export, tt.name) &&
this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, ContextualKeyword._type)
) {
// TS `export type {` case: just remove the export entirely.
// export type {a};
// export type {a as b};
// export type {a} from './b';
// export type * from './b';
// export type * as ns from './b';
this.tokens.removeInitialToken();
while (!this.tokens.matches1(tt.braceR)) {
this.tokens.removeToken();
if (this.tokens.matches1(tt.braceL)) {
while (!this.tokens.matches1(tt.braceR)) {
this.tokens.removeToken();
}
this.tokens.removeToken();
} else {
// *
this.tokens.removeToken();
if (this.tokens.matches1(tt._as)) {
// as
this.tokens.removeToken();
// ns
this.tokens.removeToken();
}
}
this.tokens.removeToken();

// Remove type re-export `... } from './T'`
if (
this.tokens.matchesContextual(ContextualKeyword._from) &&
Expand Down Expand Up @@ -483,17 +499,17 @@ export default class CJSImportTransformer extends Transformer {
this.tokens.appendCode(` exports.default = ${name};`);
} else if (
this.tokens.matches4(tt._export, tt._default, tt._class, tt.name) ||
this.tokens.matches5(tt._export, tt._default, tt._abstract, tt._class, tt.name)
this.tokens.matches5(tt._export, tt._default, tt._abstract, tt._class, tt.name) ||
this.tokens.matches3(tt._export, tt._default, tt.at)
) {
this.tokens.removeInitialToken();
this.tokens.removeToken();
this.copyDecorators();
if (this.tokens.matches1(tt._abstract)) {
this.tokens.removeToken();
}
const name = this.rootTransformer.processNamedClass();
this.tokens.appendCode(` exports.default = ${name};`);
} else if (this.tokens.matches3(tt._export, tt._default, tt.at)) {
throw new Error("Export default statements with decorators are not yet supported.");
// After this point, this is a plain "export default E" statement.
} else if (
shouldElideDefaultExport(this.isTypeScriptTransformEnabled, this.tokens, this.declarationInfo)
Expand All @@ -520,6 +536,28 @@ export default class CJSImportTransformer extends Transformer {
}
}

private copyDecorators(): void {
while (this.tokens.matches1(tt.at)) {
this.tokens.copyToken();
if (this.tokens.matches1(tt.parenL)) {
this.tokens.copyExpectedToken(tt.parenL);
this.rootTransformer.processBalancedCode();
this.tokens.copyExpectedToken(tt.parenR);
} else {
this.tokens.copyExpectedToken(tt.name);
while (this.tokens.matches1(tt.dot)) {
this.tokens.copyExpectedToken(tt.dot);
this.tokens.copyExpectedToken(tt.name);
}
if (this.tokens.matches1(tt.parenL)) {
this.tokens.copyExpectedToken(tt.parenL);
this.rootTransformer.processBalancedCode();
this.tokens.copyExpectedToken(tt.parenR);
}
}
}
}

/**
* Transform a declaration like `export var`, `export let`, or `export const`.
*/
Expand Down Expand Up @@ -719,6 +757,7 @@ export default class CJSImportTransformer extends Transformer {
*/
private processExportClass(): void {
this.tokens.removeInitialToken();
this.copyDecorators();
if (this.tokens.matches1(tt._abstract)) {
this.tokens.removeToken();
}
Expand Down
25 changes: 20 additions & 5 deletions src/transformers/ESMImportTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,31 @@ export default class ESMImportTransformer extends Transformer {
return this.processNamedExports();
}
if (
this.tokens.matches3(tt._export, tt.name, tt.braceL) &&
this.tokens.matches2(tt._export, tt.name) &&
this.tokens.matchesContextualAtIndex(this.tokens.currentIndex() + 1, ContextualKeyword._type)
) {
// TS `export type {` case: just remove the export entirely.
// export type {a};
// export type {a as b};
// export type {a} from './b';
// export type * from './b';
// export type * as ns from './b';
this.tokens.removeInitialToken();
while (!this.tokens.matches1(tt.braceR)) {
this.tokens.removeToken();
if (this.tokens.matches1(tt.braceL)) {
while (!this.tokens.matches1(tt.braceR)) {
this.tokens.removeToken();
}
this.tokens.removeToken();
} else {
// *
this.tokens.removeToken();
if (this.tokens.matches1(tt._as)) {
// as
this.tokens.removeToken();
// ns
this.tokens.removeToken();
}
}
this.tokens.removeToken();

// Remove type re-export `... } from './T'`
if (
this.tokens.matchesContextual(ContextualKeyword._from) &&
Expand Down
16 changes: 16 additions & 0 deletions test/imports-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,22 @@ module.exports = exports.default;
);
});

it("allows decorators before or after export in CJS", () => {
assertResult(
`
@dec1 export @dec2 class Foo {}
@dec3 export default @dec4 class Bar {}
export default @(1 + 1) @(foo.bar()) @a.b.c @d.e() @g.h(1, 2, 3) class Baz {}
`,
`"use strict";${ESMODULE_PREFIX}
@dec1 @dec2 class Foo {} exports.Foo = Foo;
@dec3 @dec4 class Bar {} exports.default = Bar;
@(1 + 1) @(foo.bar()) @a.b.c @d.e() @g.h(1, 2, 3) class Baz {} exports.default = Baz;
`,
{transforms: ["imports"]},
);
});

it("implements basic live bindings", () => {
assertMultiFileOutput(
{
Expand Down
14 changes: 14 additions & 0 deletions test/sucrase-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,20 @@ describe("sucrase", () => {
);
});

it("allows decorators before and after export keyword", () => {
assertResult(
`
@dec1 export @dec2 class Foo {}
@dec3 export default @dec4 class Bar {}
`,
`
@dec1 export @dec2 class Foo {}
@dec3 export default @dec4 class Bar {}
`,
{disableESTransforms: true, transforms: []},
);
});

it("allows destructuring private fields", () => {
// Example from https://github.com/tc39/proposal-destructuring-private
assertResult(
Expand Down
64 changes: 64 additions & 0 deletions test/typescript-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,32 @@ describe("typescript transform", () => {
);
});

it("supports `export type * from` in CJS mode", () => {
assertTypeScriptResult(
`
export type * from './T';
export type * as ns from './T';
`,
`"use strict";${ESMODULE_PREFIX}
;
;
`,
);
});

it("supports `export type * from` in ESM mode", () => {
assertTypeScriptESMResult(
`
export type * from './T';
export type * as ns from './T';
`,
`
;
;
`,
);
});

it("properly handles default args in constructors", () => {
assertTypeScriptResult(
`
Expand Down Expand Up @@ -3589,4 +3615,42 @@ describe("typescript transform", () => {
{transforms: ["typescript"], disableESTransforms: true},
);
});

it("allows const modifier on type parameters", () => {
assertResult(
`
function a<const T>() {}
function b<const T extends U>() {}
class C<const T> {}
class D<in const T> {}
class E<const in T> {}
`,
`
function a() {}
function b() {}
class C {}
class D {}
class E {}
`,
{transforms: ["typescript"]},
);
});

it("allows keywords in tuple labels", () => {
assertResult(
`
type T = [
function: () => {},
string: string
]
`,
`




`,
{transforms: ["typescript"]},
);
});
});