Files
test-repo/scripts/generate-flow-estree-type-definition.js
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

119 lines
2.9 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
2025-12-07 07:20:19 +08:00
import fs from "node:fs/promises";
2025-12-07 16:02:13 +08:00
import url from "node:url";
import { codeFrameColumns } from "@babel/code-frame";
import * as ts from "typescript";
2026-06-11 01:04:51 +08:00
import * as prettier from "../node_modules/prettier/index.mjs";
2025-12-07 07:20:19 +08:00
const buildScriptVersion = "1";
2025-12-07 07:20:19 +08:00
const PROJECT_ROOT = new URL("../", import.meta.url);
const FLOW_TYPES = new URL(
2026-06-22 17:44:23 -07:00
"./node_modules/flow-estree/dist/types.js.flow",
2025-12-07 07:20:19 +08:00
PROJECT_ROOT,
);
const FLOW_TYPES_DTS = new URL(
"./src/language-js/types/flow-estree.d.ts",
PROJECT_ROOT,
);
const IS_CI = Boolean(process.env.CI);
2025-12-07 07:20:19 +08:00
async function generateFlowEstreeTypeDefinition() {
const {
default: { version },
} = await import("flow-estree/package.json", {
with: { type: "json" },
});
const versionComment = `// flow-estree:v${version}, build-script:v${buildScriptVersion}`;
2025-12-07 07:20:19 +08:00
if (!IS_CI) {
try {
const dtsText = await fs.readFile(FLOW_TYPES_DTS, "utf8");
if (dtsText.includes(versionComment + "\n")) {
return;
}
} catch {
// No op
}
}
let text = await fs.readFile(FLOW_TYPES, "utf8");
text = toDts(text);
const getRelativePath = (url) =>
new URL(url).href.slice(PROJECT_ROOT.href.length);
text = `
2025-12-07 07:20:19 +08:00
// ! Do NOT edit !
// Generated from '${getRelativePath(FLOW_TYPES)}'
// Run \`node ${getRelativePath(import.meta.url)}\` to update
${versionComment}
2025-12-07 07:20:19 +08:00
// spell-checker: disable
${text}
`;
text = await prettier.format(text, {
// Bug? should accept URL
filepath: "flow-estree.d.ts",
parser: "typescript",
});
2025-12-07 07:20:19 +08:00
await fs.writeFile(FLOW_TYPES_DTS, text);
2025-12-07 16:02:13 +08:00
const program = ts.createProgram([url.fileURLToPath(FLOW_TYPES_DTS)], {
strict: true,
});
const diagnostics = ts.getPreEmitDiagnostics(program);
2025-12-07 16:02:13 +08:00
if (diagnostics.length > 0) {
await fs.rm(FLOW_TYPES_DTS);
const [diagnostic] = diagnostics;
const { line, character: column } = ts.getLineAndCharacterOfPosition(
diagnostic.file,
diagnostic.start,
);
throw new Error(
diagnostic.messageText +
":\n" +
codeFrameColumns(
text,
{ start: { line: line + 1, column } },
{
message: `TS${diagnostic.code}: ${diagnostic.messageText}`,
},
),
);
}
2025-12-07 16:02:13 +08:00
}
function toDts(text) {
// Useless directive
2026-06-22 17:44:23 -07:00
text = text.replaceAll("'use strict';", "");
2025-12-07 16:02:13 +08:00
// `{foo: interface {}}` -> `{foo: {}}`
// `{foo: interface extends T {}}` -> `{foo: T & {}}`
text = text.replaceAll(
/(?<=: )interface(?: extends (?<type>\w+))?(?= \{)/g,
(...args) => {
const { type } = args.at(-1);
return type ? `${type} & ` : "";
},
2025-12-07 16:02:13 +08:00
);
2026-06-22 17:44:23 -07:00
// `ReadonlyArray<?T>` -> `ReadonlyArray<T | null>`
2025-12-07 16:02:13 +08:00
text = text.replaceAll(
2026-06-22 17:44:23 -07:00
/ReadonlyArray<\?(?<type>\w+)>/g,
"ReadonlyArray<$<type> | null>",
2025-12-07 16:02:13 +08:00
);
// `{[string]: T}` -> `{[key: string]: T}`
text = text.replaceAll(
/(?<=\n[ {2}]+\[)(?<type>string)(?=\]: )/g,
"key: $<type>",
2025-12-07 16:02:13 +08:00
);
return text;
}
await generateFlowEstreeTypeDefinition();