Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Handle .mts and .cts files in @babel/preset-typescript #13838

Merged
merged 4 commits into from Oct 28, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/babel-helper-fixtures/src/index.ts
Expand Up @@ -64,7 +64,7 @@ function shouldIgnore(name, ignore?: Array<string>) {
);
}

const EXTENSIONS = [".js", ".mjs", ".ts", ".tsx"];
const EXTENSIONS = [".js", ".mjs", ".ts", ".tsx", ".cts", ".mts"];

function findFile(filepath: string, allowJSON?: boolean) {
const matches = [];
Expand Down Expand Up @@ -134,6 +134,7 @@ function pushTask(taskName, taskDir, suite, suiteName) {
? taskOpts.BABEL_8_BREAKING === false
: taskOpts.BABEL_8_BREAKING === true),
options: taskOpts,
doNotSetSourceType: taskOpts.DO_NOT_SET_SOURCE_TYPE,
externalHelpers:
taskOpts.externalHelpers ??
!!tryResolve("@babel/plugin-external-helpers"),
Expand Down Expand Up @@ -162,6 +163,7 @@ function pushTask(taskName, taskDir, suite, suiteName) {
};

delete taskOpts.BABEL_8_BREAKING;
delete taskOpts.DO_NOT_SET_SOURCE_TYPE;

// If there's node requirement, check it before pushing task
if (taskOpts.minNodeVersion) {
Expand Down
Expand Up @@ -230,6 +230,7 @@ function run(task) {
expect: expected,
exec,
options: opts,
doNotSetSourceType,
optionsDir,
validateLogs,
ignoreOutput,
Expand All @@ -245,7 +246,7 @@ function run(task) {
filename: self.loc,
filenameRelative: self.filename,
sourceFileName: self.filename,
sourceType: "script",
...(doNotSetSourceType ? {} : { sourceType: "script" }),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was quite annoying: since user-defined options have precedence over preset-defined options, it disabled preset-typescript's extension-based sourceType inference.

Removing this default makes all the tests fail because of the output.js file extension (Babel defaults to modules, so the test runner expects .mjs extensions) -- I'll handle it in a separate PR.

babelrc: false,
configFile: false,
inputSourceMap: task.inputSourceMap || undefined,
Expand Down
52 changes: 49 additions & 3 deletions packages/babel-parser/src/plugins/typescript/index.js
Expand Up @@ -135,6 +135,10 @@ const TSErrors = makeErrorTemplates(
"Private elements cannot have an accessibility modifier ('%0').",
ReadonlyForMethodSignature:
"'readonly' modifier can only appear on a property declaration or index signature.",
ReservedArrowTypeParam:
"This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `<T,>() => ...`.",
ReservedTypeAssertion:
"This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.",
SetAccesorCannotHaveOptionalParameter:
"A 'set' accessor cannot have an optional parameter.",
SetAccesorCannotHaveRestParameter:
Expand Down Expand Up @@ -359,12 +363,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseDelimitedList<T: N.Node>(
kind: ParsingContext,
parseElement: () => T,
refTrailingCommaPos?: { value: number },
): T[] {
return nonNull(
this.tsParseDelimitedListWorker(
kind,
parseElement,
/* expectSuccess */ true,
refTrailingCommaPos,
),
);
}
Expand All @@ -377,13 +383,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
kind: ParsingContext,
parseElement: () => ?T,
expectSuccess: boolean,
refTrailingCommaPos?: { value: number },
): ?(T[]) {
const result = [];
let trailingCommaPos = -1;

for (;;) {
if (this.tsIsListTerminator(kind)) {
break;
}
trailingCommaPos = -1;

const element = parseElement();
if (element == null) {
Expand All @@ -392,6 +401,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
result.push(element);

if (this.eat(tt.comma)) {
trailingCommaPos = this.state.lastTokStart;
continue;
}

Expand All @@ -406,6 +416,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return undefined;
}

if (refTrailingCommaPos) {
refTrailingCommaPos.value = trailingCommaPos;
}

return result;
}

Expand All @@ -414,6 +428,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
parseElement: () => T,
bracket: boolean,
skipFirstToken: boolean,
refTrailingCommaPos?: { value: number },
): T[] {
if (!skipFirstToken) {
if (bracket) {
Expand All @@ -423,7 +438,11 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}
}

const result = this.tsParseDelimitedList(kind, parseElement);
const result = this.tsParseDelimitedList(
kind,
parseElement,
refTrailingCommaPos,
);

if (bracket) {
this.expect(tt.bracketR);
Expand Down Expand Up @@ -524,15 +543,21 @@ export default (superClass: Class<Parser>): Class<Parser> =>
this.unexpected();
}

const refTrailingCommaPos = { value: -1 };

node.params = this.tsParseBracketedList(
"TypeParametersOrArguments",
this.tsParseTypeParameter.bind(this),
/* bracket */ false,
/* skipFirstToken */ true,
refTrailingCommaPos,
);
if (node.params.length === 0) {
this.raise(node.start, TSErrors.EmptyTypeParameters);
}
if (refTrailingCommaPos.value !== -1) {
this.addExtra(node, "trailingComma", refTrailingCommaPos.value);
}
return this.finishNode(node, "TSTypeParameterDeclaration");
}

Expand Down Expand Up @@ -1403,6 +1428,10 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}

tsParseTypeAssertion(): N.TsTypeAssertion {
if (this.getPluginOption("typescript", "disallowAmbiguousJSXLike")) {
this.raise(this.state.start, TSErrors.ReservedTypeAssertion);
}

const node: N.TsTypeAssertion = this.startNode();
const _const = this.tsTryNextParseConstantContext();
node.typeAnnotation = _const || this.tsNextThenParseType();
Expand Down Expand Up @@ -2854,7 +2883,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>

// Either way, we're looking at a '<': tt.jsxTagStart or relational.

let typeParameters: N.TsTypeParameterDeclaration;
let typeParameters: ?N.TsTypeParameterDeclaration;
state = state || this.state.clone();

const arrow = this.tryParse(abort => {
Expand All @@ -2878,7 +2907,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
}, state);

/*:: invariant(arrow.node != null) */
if (!arrow.error && !arrow.aborted) return arrow.node;
if (!arrow.error && !arrow.aborted) {
// This error is reported outside of the this.tryParse call so that
// in case of <T>(x) => 2, we don't consider <T>(x) as a type assertion
// because of this error.
if (typeParameters) this.reportReservedArrowTypeParam(typeParameters);
return arrow.node;
}

if (!jsx) {
// Try parsing a type cast instead of an arrow function.
Expand All @@ -2903,6 +2938,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (arrow.node) {
/*:: invariant(arrow.failState) */
this.state = arrow.failState;
if (typeParameters) this.reportReservedArrowTypeParam(typeParameters);
return arrow.node;
}

Expand All @@ -2919,6 +2955,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
throw jsx?.error || arrow.error || typeCast?.error;
}

reportReservedArrowTypeParam(node: any) {
if (
node.params.length === 1 &&
!node.extra?.trailingComma &&
this.getPluginOption("typescript", "disallowAmbiguousJSXLike")
) {
this.raise(node.start, TSErrors.ReservedArrowTypeParam);
}
}

// Handle type assertions
parseMaybeUnary(refExpressionErrors?: ?ExpressionErrors): N.Expression {
if (!this.hasPlugin("jsx") && this.isRelational("<")) {
Expand Down
@@ -0,0 +1,3 @@
{
"plugins": [["typescript", { "disallowAmbiguousJSXLike": true }]]
}
@@ -0,0 +1 @@
<T>x;
@@ -0,0 +1,38 @@
{
"type": "File",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}},
"errors": [
"SyntaxError: This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead. (1:0)"
],
"program": {
"type": "Program",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}},
"expression": {
"type": "TSTypeAssertion",
"start":0,"end":4,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":4}},
"typeAnnotation": {
"type": "TSTypeReference",
"start":1,"end":2,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":2}},
"typeName": {
"type": "Identifier",
"start":1,"end":2,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":2},"identifierName":"T"},
"name": "T"
}
},
"expression": {
"type": "Identifier",
"start":3,"end":4,"loc":{"start":{"line":1,"column":3},"end":{"line":1,"column":4},"identifierName":"x"},
"name": "x"
}
}
}
],
"directives": []
}
}
@@ -0,0 +1,2 @@
<T>() => 1;
<T>(x) => 1;
@@ -0,0 +1,3 @@
{
"BABEL_8_BREAKING": false
}
@@ -0,0 +1,87 @@
{
"type": "File",
"start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":12}},
"errors": [
"SyntaxError: This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `<T,>() => ...`. (1:0)",
"SyntaxError: This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `<T,>() => ...`. (2:0)"
],
"program": {
"type": "Program",
"start":0,"end":24,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":12}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start":0,"end":11,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":11}},
"expression": {
"type": "ArrowFunctionExpression",
"start":0,"end":10,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":10}},
"id": null,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "NumericLiteral",
"start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"typeParameters": {
"type": "TSTypeParameterDeclaration",
"start":0,"end":3,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":3}},
"params": [
{
"type": "TSTypeParameter",
"start":1,"end":2,"loc":{"start":{"line":1,"column":1},"end":{"line":1,"column":2}},
"name": "T"
}
]
}
}
},
{
"type": "ExpressionStatement",
"start":12,"end":24,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":12}},
"expression": {
"type": "ArrowFunctionExpression",
"start":12,"end":23,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":11}},
"id": null,
"generator": false,
"async": false,
"params": [
{
"type": "Identifier",
"start":16,"end":17,"loc":{"start":{"line":2,"column":4},"end":{"line":2,"column":5},"identifierName":"x"},
"name": "x"
}
],
"body": {
"type": "NumericLiteral",
"start":22,"end":23,"loc":{"start":{"line":2,"column":10},"end":{"line":2,"column":11}},
"extra": {
"rawValue": 1,
"raw": "1"
},
"value": 1
},
"typeParameters": {
"type": "TSTypeParameterDeclaration",
"start":12,"end":15,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":3}},
"params": [
{
"type": "TSTypeParameter",
"start":13,"end":14,"loc":{"start":{"line":2,"column":1},"end":{"line":2,"column":2}},
"name": "T"
}
]
}
}
}
],
"directives": []
}
}
@@ -0,0 +1,2 @@
<T,>() => 1;
<T,>(x) => 1;
@@ -0,0 +1,3 @@
{
"BABEL_8_BREAKING": false
}