mirror of
https://github.com/hanzoai/babel.git
synced 2026-09-24 16:56:45 +00:00
173 lines
6.7 KiB
TypeScript
173 lines
6.7 KiB
TypeScript
import fs from "node:fs";
|
|
// Always use the latest available version of Unicode!
|
|
// https://tc39.github.io/ecma262/#sec-conformance
|
|
import packageJson from "../package.json" with { type: "json" };
|
|
import regenerate from "regenerate";
|
|
import assert from "node:assert";
|
|
|
|
const unicodePackageNamePrefix = "@unicode/unicode-";
|
|
// Use findLast to ensure it returns the latest version of Unicode
|
|
const unicodePackageName = Object.keys(packageJson.devDependencies).findLast(
|
|
name => name.startsWith(unicodePackageNamePrefix)
|
|
);
|
|
|
|
type UnicodeRange = {
|
|
readonly begin: number;
|
|
readonly end: number;
|
|
};
|
|
|
|
import unicode16Start from "@unicode/unicode-16.0.0/Binary_Property/ID_Start/ranges.js";
|
|
import unicode16Cont from "@unicode/unicode-16.0.0/Binary_Property/ID_Continue/ranges.js";
|
|
|
|
const start = (
|
|
await import(`${unicodePackageName}/Binary_Property/ID_Start/ranges.js`)
|
|
).default;
|
|
const cont = (
|
|
await import(`${unicodePackageName}/Binary_Property/ID_Continue/ranges.js`)
|
|
).default;
|
|
|
|
function rangesToRegenerate(ranges: readonly UnicodeRange[]): regenerate {
|
|
return ranges.reduce(
|
|
(set: regenerate, { begin, end }: UnicodeRange) =>
|
|
set.addRange(begin, end - 1),
|
|
regenerate()
|
|
);
|
|
}
|
|
|
|
// The minimum Babel 8 required version of Node.js (22.18.0) supports Unicode 16
|
|
const unicode16StartSet = rangesToRegenerate(unicode16Start).removeRange(
|
|
0,
|
|
0x7f
|
|
);
|
|
const unicode16ContSet = rangesToRegenerate(unicode16Cont).removeRange(0, 0x7f);
|
|
|
|
const unicodeLatestStartSet = rangesToRegenerate(start).removeRange(0, 0x7f);
|
|
const unicodeLatestContSet = rangesToRegenerate(cont).removeRange(0, 0x7f);
|
|
|
|
// Check if the latest Unicode version has removed any characters from ID_Start or ID_Continue that were present in Unicode 16.0.0.
|
|
// If so, we may have to add negative lookahead to the regexes in this package. In general this should not happen per Unicode stability policy.
|
|
const incompatibleStartSet = unicode16StartSet
|
|
.clone()
|
|
.remove(unicodeLatestStartSet);
|
|
const incompatibleContinueSet = unicode16ContSet
|
|
.clone()
|
|
.remove(unicodeLatestContSet);
|
|
assert(
|
|
incompatibleStartSet.toArray().length === 0,
|
|
`${unicodePackageName} has removed some characters from ID_Start that were present in Unicode 16.0.0: ${incompatibleStartSet.toString()}`
|
|
);
|
|
assert(
|
|
incompatibleContinueSet.toArray().length === 0,
|
|
`${unicodePackageName} has removed some characters from ID_Continue that were present in Unicode 16.0.0: ${incompatibleContinueSet.toString()}`
|
|
);
|
|
|
|
const startSet = unicodeLatestStartSet.clone().remove(unicode16StartSet);
|
|
|
|
const contSetWithoutStart = unicodeLatestContSet
|
|
.clone()
|
|
.remove(unicode16ContSet)
|
|
.remove(startSet);
|
|
|
|
function esc(code: number) {
|
|
if (code <= 0xff) return "\\x" + code.toString(16).padStart(2, "0");
|
|
return "\\u" + code.toString(16).padStart(4, "0");
|
|
}
|
|
|
|
type IdentifierRegexData = {
|
|
bmp: string;
|
|
supplementary: number[];
|
|
};
|
|
|
|
function generate(regenerateSet: regenerate) {
|
|
const supplementary = [];
|
|
let re = "";
|
|
// @ts-expect-error access internal data property
|
|
const ranges = regenerateSet.data;
|
|
for (let i = 0, at = 0x10000; i < ranges.length; i += 2) {
|
|
const from = ranges[i],
|
|
to = ranges[i + 1] - 1;
|
|
if (to <= 0xffff) {
|
|
if (from === to) re += esc(from);
|
|
else if (from + 1 === to) re += esc(from) + esc(to);
|
|
else re += esc(from) + "-" + esc(to);
|
|
} else {
|
|
supplementary.push(from - at, to - from);
|
|
at = to;
|
|
}
|
|
}
|
|
return { bmp: re, supplementary };
|
|
}
|
|
|
|
function generateIdentifierRegex(
|
|
isBrowser: boolean,
|
|
startData: IdentifierRegexData,
|
|
contData: IdentifierRegexData
|
|
) {
|
|
let result = "";
|
|
result += `// Do not edit this file manually. It is generated by \`scripts/generate-identifier-regex.ts\`.
|
|
/*
|
|
Reason to disable eslint rules:
|
|
- no-misleading-character-class: This rule is disabled because it will check if a combining mark (\\p{M}) is following a base character. It is possible that an identifier char is not following a base character.
|
|
- regexp/no-misleading-unicode-character: The regexp plugin uses Intl.Segmenter to check whether a grapheme cluster can be represented by multiple code points and then report if there is such case. It is possible that two consecutive identifier chars may form a GC, but the intention here is to explicitly form a character set from the given character sets
|
|
- regexp/no-obscure-range: The regexps in this file are soup of "obscure" Unicode characters.
|
|
- regexp/use-ignore-case: This rule is disabled because if we used ignoreCase, the regex will depend on the runtime's Unicode support, which may cause different behavior in different environments.
|
|
*/
|
|
|
|
/* eslint no-misleading-character-class: off, regexp/no-obscure-range: off, regexp/no-misleading-unicode-character: off, regexp/use-ignore-case: off */
|
|
|
|
// ## Character categories
|
|
// Big ugly regular expressions that match characters in the ID_Start / ID_Continue
|
|
// binary properties. These are only applied when a character is found
|
|
// to actually have a code point larger than 0x80.
|
|
// Generated by \`scripts/generate-identifier-regex.ts\`.
|
|
`;
|
|
if (isBrowser) {
|
|
result += `/* prettier-ignore */
|
|
const bmpIdentifierStartChars = "${startData.bmp}";
|
|
/* prettier-ignore */
|
|
const bmpIdentifierChars = "${contData.bmp}";
|
|
export const bmpIdentifierStart = new RegExp(
|
|
"[" + bmpIdentifierStartChars + "]",
|
|
);
|
|
export const bmpIdentifier = new RegExp(
|
|
"[" + bmpIdentifierStartChars + bmpIdentifierChars + "]",
|
|
);
|
|
`;
|
|
} else {
|
|
result += `/* prettier-ignore */
|
|
export const bmpIdentifierStart = /[\\p{ID_Start}${startData.bmp}]/u;
|
|
/* prettier-ignore */
|
|
export const bmpIdentifier = /[\\p{ID_Continue}${startData.bmp}${contData.bmp}]/u;
|
|
`;
|
|
}
|
|
|
|
result += `
|
|
// These are a run-length and offset-encoded representation of the
|
|
// >0xffff code points that are a valid part of identifiers. The
|
|
// offset starts at 0x10000, and each pair of numbers represents an
|
|
// offset to the next range, and then a size of the range. They were
|
|
// generated by \`scripts/generate-identifier-regex.ts\`.
|
|
/* prettier-ignore */
|
|
export const supplementaryIdentifierStartCodes = ${JSON.stringify(startData.supplementary)};
|
|
/* prettier-ignore */
|
|
export const supplementaryIdentifierCodes = ${JSON.stringify(contData.supplementary)};
|
|
`;
|
|
return result;
|
|
}
|
|
|
|
const startData = generate(startSet);
|
|
const contData = generate(contSetWithoutStart);
|
|
fs.writeFileSync(
|
|
new URL("../src/identifier-regex.ts", import.meta.url),
|
|
generateIdentifierRegex(false, startData, contData)
|
|
);
|
|
|
|
const startDataBrowser = generate(unicodeLatestStartSet);
|
|
const contDataBrowser = generate(
|
|
unicodeLatestContSet.clone().remove(unicodeLatestStartSet)
|
|
);
|
|
fs.writeFileSync(
|
|
new URL("../src/identifier-regex-browser.ts", import.meta.url),
|
|
generateIdentifierRegex(true, startDataBrowser, contDataBrowser)
|
|
);
|