Skip to content

Commit

Permalink
feat: Automatically generate cooked for templateElement (#14757)
Browse files Browse the repository at this point in the history
Co-authored-by: Nicol貌 Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
liuxingbaoyu and nicolo-ribaudo committed Jul 21, 2022
1 parent b58e35b commit 32d4f6e
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/babel-helper-string-parser/src/index.ts
Expand Up @@ -65,6 +65,7 @@ export function readStringContents(
for (;;) {
if (pos >= length) {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
out += input.slice(chunkStart, pos);
break;
}
const ch = input.charCodeAt(pos);
Expand Down
1 change: 1 addition & 0 deletions packages/babel-types/package.json
Expand Up @@ -24,6 +24,7 @@
}
},
"dependencies": {
"@babel/helper-string-parser": "workspace:^",
"@babel/helper-validator-identifier": "workspace:^",
"to-fast-properties": "condition:BABEL_8_BREAKING ? ^3.0.0 : ^2.0.0"
},
Expand Down
5 changes: 4 additions & 1 deletion packages/babel-types/scripts/utils/stringifyValidator.js
Expand Up @@ -8,7 +8,10 @@ export default function stringifyValidator(validator, nodePrefix) {
}

if (validator.chainOf) {
return stringifyValidator(validator.chainOf[1], nodePrefix);
const ret = stringifyValidator(validator.chainOf[1], nodePrefix);
return Array.isArray(ret) && ret.length === 1 && ret[0] === "any"
? stringifyValidator(validator.chainOf[0], nodePrefix)
: ret;
}

if (validator.oneOf) {
Expand Down
57 changes: 49 additions & 8 deletions packages/babel-types/src/definitions/core.ts
Expand Up @@ -2,6 +2,7 @@ import is from "../validators/is";
import isValidIdentifier from "../validators/isValidIdentifier";
import { isKeyword, isReservedWord } from "@babel/helper-validator-identifier";
import type * as t from "..";
import { readStringContents } from "@babel/helper-string-parser";

import {
BINARY_OPERATORS,
Expand Down Expand Up @@ -1965,15 +1966,55 @@ defineType("TemplateElement", {
builder: ["value", "tail"],
fields: {
value: {
validate: assertShape({
raw: {
validate: assertValueType("string"),
},
cooked: {
validate: assertValueType("string"),
optional: true,
validate: chain(
assertShape({
raw: {
validate: assertValueType("string"),
},
cooked: {
validate: assertValueType("string"),
optional: true,
},
}),
function templateElementCookedValidator(node: t.TemplateElement) {
const raw = node.value.raw;

let str,
containsInvalid,
unterminatedCalled = false;
try {
const error = () => {
throw new Error();
};
({ str, containsInvalid } = readStringContents(
"template",
raw,
0,
0,
0,
{
unterminated() {
unterminatedCalled = true;
},
strictNumericEscape: error,
invalidEscapeSequence: error,
numericSeparatorInEscapeSequence: error,
unexpectedNumericSeparator: error,
invalidDigit: error,
invalidCodePoint: error,
},
));
} catch {
// TODO: When https://github.com/babel/babel/issues/14775 is fixed
// we can remove the try/catch block.
unterminatedCalled = true;
containsInvalid = true;
}
if (!unterminatedCalled) throw new Error("Invalid raw");

node.value.cooked = containsInvalid ? null : str;
},
}),
),
},
tail: {
default: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-types/src/definitions/utils.ts
Expand Up @@ -51,7 +51,7 @@ export type Validator = (
((node: t.Node, key: string, val: any) => void);

export type FieldOptions = {
default?: any;
default?: string | number | boolean | [];
optional?: boolean;
validate?: Validator;
};
Expand Down
Expand Up @@ -16,6 +16,7 @@ Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "foo",
"raw": "foo",
},
}
Expand All @@ -36,6 +37,43 @@ exports[`builders es2015 templateElement should validate 5`] = `
Property raw expected type of string but got undefined"
`;

exports[`builders es2015 templateElement should validate 6`] = `
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": null,
"raw": "\\\\u",
},
}
`;

exports[`builders es2015 templateElement should validate 7`] = `
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "B",
"raw": "\\\\x42",
},
}
`;

exports[`builders es2015 templateElement should validate 8`] = `
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "B",
"raw": "\\\\x42",
},
}
`;

exports[`builders es2015 templateElement should validate 9`] = `"Invalid raw"`;

exports[`builders es2015 templateElement should validate 10`] = `"Invalid raw"`;

exports[`builders es2015 templateLiteral should validate 1`] = `
Object {
"expressions": Array [],
Expand All @@ -44,6 +82,7 @@ Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "foo",
"raw": "foo",
},
},
Expand All @@ -65,13 +104,15 @@ Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "foo",
"raw": "foo",
},
},
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "bar",
"raw": "bar",
},
},
Expand Down
16 changes: 16 additions & 0 deletions packages/babel-types/test/builders/es2015/templateElement.js
Expand Up @@ -19,6 +19,22 @@ describe("builders", function () {
).toThrowErrorMatchingSnapshot();

expect(() => t.templateElement("foo")).toThrowErrorMatchingSnapshot();

expect(t.templateElement({ raw: "\\u" })).toMatchSnapshot();

expect(t.templateElement({ raw: "\\x42" })).toMatchSnapshot();

expect(
t.templateElement({ raw: "\\x42", cooked: "123" }),
).toMatchSnapshot();

expect(() =>
t.templateElement({ raw: "`" }),
).toThrowErrorMatchingSnapshot();

expect(() =>
t.templateElement({ raw: "${" }),
).toThrowErrorMatchingSnapshot();
});
});
describe("templateLiteral", function () {
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Expand Up @@ -3748,6 +3748,7 @@ __metadata:
resolution: "@babel/types@workspace:packages/babel-types"
dependencies:
"@babel/generator": "workspace:^"
"@babel/helper-string-parser": "workspace:^"
"@babel/helper-validator-identifier": "workspace:^"
"@babel/parser": "workspace:^"
chalk: ^4.1.0
Expand Down

0 comments on commit 32d4f6e

Please sign in to comment.