Skip to content

Commit e02b0ff

Browse files
authored
[Babel 8] Create TSTemplateLiteralType (#17066)
* breaking: create TSTemplateLiteralType * update test fixtures * ignore TSTemplateLiteralType in printer test * purge failing prettier tests * rename expressions to types * fix: generate TSLiteralType when the template does not contain types * restore template literals transform * update test fixtures * define TSTemplateLiteralType for Babel 7 Since the generator requires the types information * refactor: extract _printTemplate helper * Apply template literal range fixes to TSTemplateLiteralType
1 parent 616f887 commit e02b0ff

File tree

32 files changed

+553
-128
lines changed

32 files changed

+553
-128
lines changed

eslint/babel-eslint-parser/src/convert/convertAST.cts

+4-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ const convertNodesVisitor = {
8787
}
8888

8989
// template string range fixes
90-
if (node.type === "TemplateLiteral") {
90+
if (
91+
node.type === "TemplateLiteral" ||
92+
node.type === "TSTemplateLiteralType"
93+
) {
9194
for (let i = 0; i < node.quasis.length; i++) {
9295
const q = node.quasis[i];
9396
q.range[0] -= 1;

packages/babel-generator/src/generators/template-literals.ts

+24-17
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,34 @@ export function TemplateElement(this: Printer) {
2020
throw new Error("TemplateElement printing is handled in TemplateLiteral");
2121
}
2222

23-
export function TemplateLiteral(this: Printer, node: t.TemplateLiteral) {
24-
const quasis = node.quasis;
23+
export type TemplateLiteralBase = t.Node & {
24+
quasis: t.TemplateElement[];
25+
};
2526

27+
export function _printTemplate<T extends t.Node>(
28+
this: Printer,
29+
node: TemplateLiteralBase,
30+
substitutions: T[],
31+
) {
32+
const quasis = node.quasis;
2633
let partRaw = "`";
27-
28-
for (let i = 0; i < quasis.length; i++) {
34+
for (let i = 0; i < quasis.length - 1; i++) {
2935
partRaw += quasis[i].value.raw;
30-
31-
if (i + 1 < quasis.length) {
32-
this.token(partRaw + "${", true);
33-
this.print(node.expressions[i]);
34-
partRaw = "}";
35-
36-
// In Babel 7 we have indivirual tokens for ${ and }, so the automatic
37-
// catchup logic does not work. Manually look for those tokens.
38-
if (!process.env.BABEL_8_BREAKING && this.tokenMap) {
39-
const token = this.tokenMap.findMatching(node, "}", i);
40-
if (token) this._catchUpTo(token.loc.start);
41-
}
36+
this.token(partRaw + "${", true);
37+
this.print(substitutions[i]);
38+
partRaw = "}";
39+
40+
// In Babel 7 we have individual tokens for ${ and }, so the automatic
41+
// catchup logic does not work. Manually look for those tokens.
42+
if (!process.env.BABEL_8_BREAKING && this.tokenMap) {
43+
const token = this.tokenMap.findMatching(node, "}", i);
44+
if (token) this._catchUpTo(token.loc.start);
4245
}
4346
}
44-
47+
partRaw += quasis[quasis.length - 1].value.raw;
4548
this.token(partRaw + "`", true);
4649
}
50+
51+
export function TemplateLiteral(this: Printer, node: t.TemplateLiteral) {
52+
this._printTemplate(node, node.expressions);
53+
}

packages/babel-generator/src/generators/typescript.ts

+7
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,13 @@ function tokenIfPlusMinus(self: Printer, tok: true | "+" | "-") {
498498
}
499499
}
500500

501+
export function TSTemplateLiteralType(
502+
this: Printer,
503+
node: t.TSTemplateLiteralType,
504+
) {
505+
this._printTemplate(node, node.types);
506+
}
507+
501508
export function TSLiteralType(this: Printer, node: t.TSLiteralType) {
502509
this.print(node.literal);
503510
}

packages/babel-generator/test/printer.skip-bundled.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ describe("Printer", () => {
2424
if (
2525
type === "TSClassImplements" ||
2626
type === "TSEnumBody" ||
27-
type === "TSInterfaceHeritage"
27+
type === "TSInterfaceHeritage" ||
28+
type === "TSTemplateLiteralType"
2829
) {
2930
return;
3031
}

packages/babel-parser/src/plugins/typescript/index.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -1257,10 +1257,35 @@ export default (superClass: ClassWithMixin<typeof Parser, IJSXParserMixin>) =>
12571257
return this.finishNode(node, "TSLiteralType");
12581258
}
12591259

1260-
tsParseTemplateLiteralType(): N.TsLiteralType {
1261-
const node = this.startNode<N.TsLiteralType>();
1262-
node.literal = super.parseTemplate(false);
1263-
return this.finishNode(node, "TSLiteralType");
1260+
tsParseTemplateLiteralType(): N.TsTemplateLiteralType | N.TsLiteralType {
1261+
if (process.env.BABEL_8_BREAKING) {
1262+
const startLoc = this.state.startLoc;
1263+
let curElt = this.parseTemplateElement(false);
1264+
const quasis = [curElt];
1265+
if (curElt.tail) {
1266+
const node = this.startNodeAt<N.TsLiteralType>(startLoc);
1267+
const literal = this.startNodeAt<N.TemplateLiteral>(startLoc);
1268+
literal.expressions = [];
1269+
literal.quasis = quasis;
1270+
node.literal = this.finishNode(literal, "TemplateLiteral");
1271+
return this.finishNode(node, "TSLiteralType");
1272+
} else {
1273+
const substitutions: N.TsType[] = [];
1274+
while (!curElt.tail) {
1275+
substitutions.push(this.tsParseType());
1276+
this.readTemplateContinuation();
1277+
quasis.push((curElt = this.parseTemplateElement(false)));
1278+
}
1279+
const node = this.startNodeAt<N.TsTemplateLiteralType>(startLoc);
1280+
node.types = substitutions;
1281+
node.quasis = quasis;
1282+
return this.finishNode(node, "TSTemplateLiteralType");
1283+
}
1284+
} else {
1285+
const node = this.startNode<N.TsLiteralType>();
1286+
node.literal = super.parseTemplate(false);
1287+
return this.finishNode(node, "TSLiteralType");
1288+
}
12641289
}
12651290

12661291
parseTemplateSubstitution(): N.TsType | N.Expression {

packages/babel-parser/src/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1599,6 +1599,7 @@ export type TsType =
15991599
| TsIndexedAccessType
16001600
| TsMappedType
16011601
| TsLiteralType // TODO: This probably shouldn't be included here.
1602+
| TsTemplateLiteralType
16021603
| TsImportType
16031604
| TsTypePredicate;
16041605

@@ -1755,6 +1756,12 @@ export interface TsMappedType extends TsTypeBase {
17551756
nameType: TsType | undefined | null;
17561757
}
17571758

1759+
export interface TsTemplateLiteralType extends TsTypeBase {
1760+
type: "TSTemplateLiteralType";
1761+
quasis: TemplateElement[];
1762+
types: TsType[];
1763+
}
1764+
17581765
export interface TsLiteralType extends TsTypeBase {
17591766
type: "TSLiteralType";
17601767
literal: NumericLiteral | StringLiteral | BooleanLiteral | TemplateLiteral;
@@ -2153,6 +2160,7 @@ export type Node =
21532160
| TsQualifiedName
21542161
| TsRestType
21552162
| TsSatisfiesExpression
2163+
| TsTemplateLiteralType
21562164
| TsThisType
21572165
| TsTupleType
21582166
| TsTypeAliasDeclaration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let x: `foo`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"BABEL_8_BREAKING": false
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":13,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":13,"index":13}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":13,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":13,"index":13}},
7+
"sourceType": "module",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "VariableDeclaration",
12+
"start":0,"end":13,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":13,"index":13}},
13+
"declarations": [
14+
{
15+
"type": "VariableDeclarator",
16+
"start":4,"end":12,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":12,"index":12}},
17+
"id": {
18+
"type": "Identifier",
19+
"start":4,"end":12,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":12,"index":12},"identifierName":"x"},
20+
"name": "x",
21+
"typeAnnotation": {
22+
"type": "TSTypeAnnotation",
23+
"start":5,"end":12,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":12,"index":12}},
24+
"typeAnnotation": {
25+
"type": "TSLiteralType",
26+
"start":7,"end":12,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":12,"index":12}},
27+
"literal": {
28+
"type": "TemplateLiteral",
29+
"start":7,"end":12,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":12,"index":12}},
30+
"expressions": [],
31+
"quasis": [
32+
{
33+
"type": "TemplateElement",
34+
"start":8,"end":11,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":11,"index":11}},
35+
"value": {
36+
"raw": "foo",
37+
"cooked": "foo"
38+
},
39+
"tail": true
40+
}
41+
]
42+
}
43+
}
44+
}
45+
},
46+
"init": null
47+
}
48+
],
49+
"kind": "let"
50+
}
51+
],
52+
"directives": [],
53+
"extra": {
54+
"topLevelAwait": false
55+
}
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"BABEL_8_BREAKING": true
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
let x: `foo-${bar}`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"BABEL_8_BREAKING": false
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}},
7+
"sourceType": "module",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "VariableDeclaration",
12+
"start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}},
13+
"declarations": [
14+
{
15+
"type": "VariableDeclarator",
16+
"start":4,"end":19,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":19,"index":19}},
17+
"id": {
18+
"type": "Identifier",
19+
"start":4,"end":19,"loc":{"start":{"line":1,"column":4,"index":4},"end":{"line":1,"column":19,"index":19},"identifierName":"x"},
20+
"name": "x",
21+
"typeAnnotation": {
22+
"type": "TSTypeAnnotation",
23+
"start":5,"end":19,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":19,"index":19}},
24+
"typeAnnotation": {
25+
"type": "TSLiteralType",
26+
"start":7,"end":19,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":19,"index":19}},
27+
"literal": {
28+
"type": "TemplateLiteral",
29+
"start":7,"end":19,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":19,"index":19}},
30+
"expressions": [
31+
{
32+
"type": "TSTypeReference",
33+
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17}},
34+
"typeName": {
35+
"type": "Identifier",
36+
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17},"identifierName":"bar"},
37+
"name": "bar"
38+
}
39+
}
40+
],
41+
"quasis": [
42+
{
43+
"type": "TemplateElement",
44+
"start":8,"end":12,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":12,"index":12}},
45+
"value": {
46+
"raw": "foo-",
47+
"cooked": "foo-"
48+
},
49+
"tail": false
50+
},
51+
{
52+
"type": "TemplateElement",
53+
"start":18,"end":18,"loc":{"start":{"line":1,"column":18,"index":18},"end":{"line":1,"column":18,"index":18}},
54+
"value": {
55+
"raw": "",
56+
"cooked": ""
57+
},
58+
"tail": true
59+
}
60+
]
61+
}
62+
}
63+
}
64+
},
65+
"init": null
66+
}
67+
],
68+
"kind": "let"
69+
}
70+
],
71+
"directives": [],
72+
"extra": {
73+
"topLevelAwait": false
74+
}
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"BABEL_8_BREAKING": true
3+
}

packages/babel-parser/test/fixtures/typescript/types/literal-string-2/output.json

+30-34
Original file line numberDiff line numberDiff line change
@@ -22,43 +22,39 @@
2222
"type": "TSTypeAnnotation",
2323
"start":5,"end":19,"loc":{"start":{"line":1,"column":5,"index":5},"end":{"line":1,"column":19,"index":19}},
2424
"typeAnnotation": {
25-
"type": "TSLiteralType",
25+
"type": "TSTemplateLiteralType",
2626
"start":7,"end":19,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":19,"index":19}},
27-
"literal": {
28-
"type": "TemplateLiteral",
29-
"start":7,"end":19,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":19,"index":19}},
30-
"expressions": [
31-
{
32-
"type": "TSTypeReference",
33-
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17}},
34-
"typeName": {
35-
"type": "Identifier",
36-
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17},"identifierName":"bar"},
37-
"name": "bar"
38-
}
27+
"types": [
28+
{
29+
"type": "TSTypeReference",
30+
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17}},
31+
"typeName": {
32+
"type": "Identifier",
33+
"start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17},"identifierName":"bar"},
34+
"name": "bar"
3935
}
40-
],
41-
"quasis": [
42-
{
43-
"type": "TemplateElement",
44-
"start":8,"end":12,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":12,"index":12}},
45-
"value": {
46-
"raw": "foo-",
47-
"cooked": "foo-"
48-
},
49-
"tail": false
36+
}
37+
],
38+
"quasis": [
39+
{
40+
"type": "TemplateElement",
41+
"start":8,"end":12,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":12,"index":12}},
42+
"value": {
43+
"raw": "foo-",
44+
"cooked": "foo-"
5045
},
51-
{
52-
"type": "TemplateElement",
53-
"start":18,"end":18,"loc":{"start":{"line":1,"column":18,"index":18},"end":{"line":1,"column":18,"index":18}},
54-
"value": {
55-
"raw": "",
56-
"cooked": ""
57-
},
58-
"tail": true
59-
}
60-
]
61-
}
46+
"tail": false
47+
},
48+
{
49+
"type": "TemplateElement",
50+
"start":18,"end":18,"loc":{"start":{"line":1,"column":18,"index":18},"end":{"line":1,"column":18,"index":18}},
51+
"value": {
52+
"raw": "",
53+
"cooked": ""
54+
},
55+
"tail": true
56+
}
57+
]
6258
}
6359
}
6460
},

0 commit comments

Comments
 (0)