mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-12 03:20:56 -06:00
needsUpdate may be wrong when the branch changes; these ones are now so fast thanks to being pure JS that we can just always run their contents and be sure that the outputs are right.
163 lines
5.6 KiB
JavaScript
163 lines
5.6 KiB
JavaScript
import path from "path";
|
|
import fs from "fs";
|
|
|
|
/** @typedef {{
|
|
category: string;
|
|
code: number;
|
|
reportsUnnecessary?: {};
|
|
reportsDeprecated?: {};
|
|
isEarly?: boolean;
|
|
elidedInCompatabilityPyramid?: boolean;
|
|
}} DiagnosticDetails */
|
|
void 0;
|
|
|
|
/** @typedef {Map<string, DiagnosticDetails>} InputDiagnosticMessageTable */
|
|
|
|
async function main() {
|
|
if (process.argv.length < 3) {
|
|
console.log("Usage:");
|
|
console.log("\tnode processDiagnosticMessages.mjs <diagnostic-json-input-file>");
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @param {string} fileName
|
|
* @param {string} contents
|
|
*/
|
|
async function writeFile(fileName, contents) {
|
|
const filePath = path.join(path.dirname(inputFilePath), fileName);
|
|
try {
|
|
const existingContents = await fs.promises.readFile(filePath, "utf-8");
|
|
if (existingContents === contents) {
|
|
return;
|
|
}
|
|
}
|
|
catch {
|
|
// Just write the file.
|
|
}
|
|
|
|
await fs.promises.writeFile(filePath, contents, { encoding: "utf-8" });
|
|
}
|
|
|
|
const inputFilePath = process.argv[2].replace(/\\/g, "/");
|
|
console.log(`Reading diagnostics from ${inputFilePath}`);
|
|
const inputStr = await fs.promises.readFile(inputFilePath, { encoding: "utf-8" });
|
|
|
|
/** @type {{ [key: string]: DiagnosticDetails }} */
|
|
const diagnosticMessagesJson = JSON.parse(inputStr);
|
|
|
|
/** @type {InputDiagnosticMessageTable} */
|
|
const diagnosticMessages = new Map();
|
|
for (const key in diagnosticMessagesJson) {
|
|
if (Object.hasOwnProperty.call(diagnosticMessagesJson, key)) {
|
|
diagnosticMessages.set(key, diagnosticMessagesJson[key]);
|
|
}
|
|
}
|
|
|
|
const infoFileOutput = buildInfoFileOutput(diagnosticMessages, inputFilePath);
|
|
checkForUniqueCodes(diagnosticMessages);
|
|
await writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
|
|
|
|
const messageOutput = buildDiagnosticMessageOutput(diagnosticMessages);
|
|
await writeFile("diagnosticMessages.generated.json", messageOutput);
|
|
}
|
|
|
|
/**
|
|
* @param {InputDiagnosticMessageTable} diagnosticTable
|
|
*/
|
|
function checkForUniqueCodes(diagnosticTable) {
|
|
/** @type {Record<number, true | undefined>} */
|
|
const allCodes = [];
|
|
diagnosticTable.forEach(({ code }) => {
|
|
if (allCodes[code]) {
|
|
throw new Error(`Diagnostic code ${code} appears more than once.`);
|
|
}
|
|
allCodes[code] = true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {InputDiagnosticMessageTable} messageTable
|
|
* @param {string} inputFilePathRel
|
|
* @returns {string}
|
|
*/
|
|
function buildInfoFileOutput(messageTable, inputFilePathRel) {
|
|
const result = [
|
|
"// <auto-generated />",
|
|
`// generated from '${inputFilePathRel}'`,
|
|
"",
|
|
"import { DiagnosticCategory, DiagnosticMessage } from \"./types\";",
|
|
"",
|
|
"function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {",
|
|
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };",
|
|
"}",
|
|
"",
|
|
"/** @internal */",
|
|
"export const Diagnostics = {",
|
|
];
|
|
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => {
|
|
const propName = convertPropertyName(name);
|
|
const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : "";
|
|
const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : "";
|
|
const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : "";
|
|
|
|
result.push(` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),`);
|
|
});
|
|
|
|
result.push("};");
|
|
|
|
return result.join("\r\n");
|
|
}
|
|
|
|
/**
|
|
* @param {InputDiagnosticMessageTable} messageTable
|
|
* @returns {string}
|
|
*/
|
|
function buildDiagnosticMessageOutput(messageTable) {
|
|
/** @type {Record<string, string>} */
|
|
const result = {};
|
|
|
|
messageTable.forEach(({ code }, name) => {
|
|
const propName = convertPropertyName(name);
|
|
result[createKey(propName, code)] = name;
|
|
});
|
|
|
|
return JSON.stringify(result, undefined, 2).replace(/\r?\n/g, "\r\n");
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @param {number} code
|
|
* @returns {string}
|
|
*/
|
|
function createKey(name, code) {
|
|
return name.slice(0, 100) + "_" + code;
|
|
}
|
|
|
|
/**
|
|
* @param {string} origName
|
|
* @returns {string}
|
|
*/
|
|
function convertPropertyName(origName) {
|
|
let result = origName.split("").map(char => {
|
|
if (char === "*") return "_Asterisk";
|
|
if (char === "/") return "_Slash";
|
|
if (char === ":") return "_Colon";
|
|
return /\w/.test(char) ? char : "_";
|
|
}).join("");
|
|
|
|
// get rid of all multi-underscores
|
|
result = result.replace(/_+/g, "_");
|
|
|
|
// remove any leading underscore, unless it is followed by a number.
|
|
result = result.replace(/^_([^\d])/, "$1");
|
|
|
|
// get rid of all trailing underscores.
|
|
result = result.replace(/_$/, "");
|
|
|
|
return result;
|
|
}
|
|
|
|
main();
|