/// interface DiagnosticDetails { category: string; code: number; } interface InputDiagnosticMessageTable { [msg: string]: DiagnosticDetails; } interface IIndexable { [key: string]: V; } function main(): void { if (sys.args.length < 1) { sys.write("Usage:" + sys.newLine) sys.write("\tnode processDiagnosticMessages.js " + sys.newLine); return; } var inputFilePath = sys.args[0].replace(/\\/g, "/"); var inputStr = sys.readFile(inputFilePath); var diagnosticMesages: InputDiagnosticMessageTable = JSON.parse(inputStr); var names = Utilities.getObjectKeys(diagnosticMesages); var nameMap = buildUniqueNameMap(names); var infoFileOutput = buildInfoFileOutput(diagnosticMesages, nameMap); // TODO: Fix path joining var inputDirectory = inputFilePath.substr(0,inputFilePath.lastIndexOf("/")); var fileOutputPath = inputDirectory + "/diagnosticInformationMap.generated.ts"; sys.writeFile(fileOutputPath, infoFileOutput); } function buildUniqueNameMap(names: string[]): IIndexable { var nameMap: IIndexable = {}; var uniqueNames = NameGenerator.ensureUniqueness(names, /*isFixed */ undefined, /* isCaseSensitive */ false); for (var i = 0; i < names.length; i++) { nameMap[names[i]] = uniqueNames[i]; } return nameMap; } function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: IIndexable): string { var result = '// \r\n' + '/// \r\n' + 'module ts {\r\n' + ' export var Diagnostics = {\r\n'; var names = Utilities.getObjectKeys(messageTable); for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; result += ' ' + convertPropertyName(nameMap[name]) + ': { code: ' + diagnosticDetails.code + ', category: DiagnosticCategory.' + diagnosticDetails.category + ', key: "' + name.replace('"', '\\"') + '" },\r\n'; } result += ' };\r\n}'; return result; } function convertPropertyName(origName: string): string { var 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; } module NameGenerator { export function ensureUniqueness( names: string[], isFixed: boolean[]= names.map(() => false), isCaseSensitive: boolean = true): string[] { var names = names.map(x => x); ensureUniquenessInPlace(names, isFixed, isCaseSensitive); return names; } function ensureUniquenessInPlace(names: string[], isFixed: boolean[], isCaseSensitive: boolean): void { for (var i = 0; i < names.length; i++) { var name = names[i]; var collisionIndices = Utilities.collectMatchingIndices(name, names, isCaseSensitive); // We will always have one "collision" because getCollisionIndices returns the index of name itself as well; // so if we only have one collision, then there are no issues. if (collisionIndices.length < 2) { continue; } handleCollisions(name, names, isFixed, collisionIndices, isCaseSensitive); } } function handleCollisions(name: string, proposedNames: string[], isFixed: boolean[], collisionIndices: number[], isCaseSensitive: boolean): void { var suffix = 1; for (var i = 0; i < collisionIndices.length; i++) { var collisionIndex = collisionIndices[i]; if (isFixed[collisionIndex]) { // can't do anything about this name. continue; } while (true) { var newName = name + suffix++; if (proposedNames.some((name) => Utilities.stringEquals(name, newName, isCaseSensitive))) { proposedNames[collisionIndex] = newName; break; } } } } } module Utilities { /// Return a list of all indices where a string occurs. export function collectMatchingIndices(name: string, proposedNames: string[], isCaseSensitive: boolean = true): number[] { var matchingIndices: number[] = []; for (var i = 0; i < proposedNames.length; i++) { if (stringEquals(name, proposedNames[i], isCaseSensitive)) { matchingIndices.push(i); } } return matchingIndices; } export function stringEquals(s1: string, s2: string, caseSensitive: boolean = true): boolean { if (!caseSensitive) { s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); } return s1 == s2; } // Like Object.keys export function getObjectKeys(obj: any): string[]{ var result: string[] = []; for (var name in obj) { if (obj.hasOwnProperty(name)) { result.push(name); } } return result; } } main();