提交 8d9db180 编写于 作者: A Alex Dima

Add ability to generate standalone editor usages file

上级 3e5f169e
......@@ -134,7 +134,25 @@ function getTopLevelDeclaration(sourceFile, typeName) {
function getNodeText(sourceFile, node) {
return sourceFile.getFullText().substring(node.pos, node.end);
}
function getMassagedTopLevelDeclarationText(sourceFile, declaration) {
function hasModifier(modifiers, kind) {
if (modifiers) {
for (var i = 0; i < modifiers.length; i++) {
var mod = modifiers[i];
if (mod.kind === kind) {
return true;
}
}
}
return false;
}
function isStatic(member) {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration) {
return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword));
}
function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage) {
var result = getNodeText(sourceFile, declaration);
// if (result.indexOf('MonacoWorker') >= 0) {
// console.log('here!');
......@@ -142,6 +160,18 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) {
// }
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
var interfaceDeclaration = declaration;
var staticTypeName_1 = (isDefaultExport(interfaceDeclaration)
? importName + ".default"
: importName + "." + declaration.name.text);
var instanceTypeName_1 = staticTypeName_1;
var typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0);
if (typeParametersCnt > 0) {
var arr = [];
for (var i = 0; i < typeParametersCnt; i++) {
arr.push('any');
}
instanceTypeName_1 = instanceTypeName_1 + "<" + arr.join(',') + ">";
}
var members = interfaceDeclaration.members;
members.forEach(function (member) {
try {
......@@ -151,6 +181,15 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) {
result = result.replace(memberText, '');
// console.log('AFTER: ', result);
}
else {
var memberName = member.name.text;
if (isStatic(member)) {
usage.push("a = " + staticTypeName_1 + "." + memberName + ";");
}
else {
usage.push("a = (<" + instanceTypeName_1 + ">b)." + memberName + ";");
}
}
}
catch (err) {
// life..
......@@ -211,6 +250,16 @@ function generateDeclarationFile(out, inputFiles, recipe) {
var endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
var lines = recipe.split(endl);
var result = [];
var usageCounter = 0;
var usageImports = [];
var usage = [];
usage.push("var a;");
usage.push("var b;");
var generateUsageImport = function (moduleId) {
var importName = 'm' + (++usageCounter);
usageImports.push("import * as " + importName + " from '" + moduleId.replace(/\.d\.ts$/, '') + "';");
return importName;
};
lines.forEach(function (line) {
var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m1) {
......@@ -220,6 +269,7 @@ function generateDeclarationFile(out, inputFiles, recipe) {
if (!sourceFile_1) {
return;
}
var importName_1 = generateUsageImport(moduleId);
var replacer_1 = createReplacer(m1[2]);
var typeNames = m1[3].split(/,/);
typeNames.forEach(function (typeName) {
......@@ -232,7 +282,7 @@ function generateDeclarationFile(out, inputFiles, recipe) {
logErr('Cannot find type ' + typeName);
return;
}
result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration)));
result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration, importName_1, usage)));
});
return;
}
......@@ -244,6 +294,7 @@ function generateDeclarationFile(out, inputFiles, recipe) {
if (!sourceFile_2) {
return;
}
var importName_2 = generateUsageImport(moduleId);
var replacer_2 = createReplacer(m2[2]);
var typeNames = m2[3].split(/,/);
var typesToExcludeMap_1 = {};
......@@ -271,7 +322,7 @@ function generateDeclarationFile(out, inputFiles, recipe) {
}
}
}
result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration)));
result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration, importName_2, usage)));
});
return;
}
......@@ -282,9 +333,12 @@ function generateDeclarationFile(out, inputFiles, recipe) {
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
resultTxt = resultTxt.replace(/\bTPromise</g, 'Promise<');
resultTxt = format(resultTxt);
return resultTxt;
return [
resultTxt,
usageImports.join('\n') + "\n\n" + usage.join('\n')
];
}
function getFilesToWatch(out) {
function getIncludesInRecipe() {
var recipe = fs.readFileSync(RECIPE_PATH).toString();
var lines = recipe.split(/\r\n|\n|\r/);
var result = [];
......@@ -292,24 +346,27 @@ function getFilesToWatch(out) {
var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m1) {
var moduleId = m1[1];
result.push(moduleIdToPath(out, moduleId));
result.push(moduleId);
return;
}
var m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m2) {
var moduleId = m2[1];
result.push(moduleIdToPath(out, moduleId));
result.push(moduleId);
return;
}
});
return result;
}
function getFilesToWatch(out) {
return getIncludesInRecipe().map(function (moduleId) { return moduleIdToPath(out, moduleId); });
}
exports.getFilesToWatch = getFilesToWatch;
function run(out, inputFiles) {
log('Starting monaco.d.ts generation');
SOURCE_FILE_MAP = {};
var recipe = fs.readFileSync(RECIPE_PATH).toString();
var result = generateDeclarationFile(out, inputFiles, recipe);
var _a = generateDeclarationFile(out, inputFiles, recipe), result = _a[0], usageContent = _a[1];
var currentContent = fs.readFileSync(DECLARATION_PATH).toString();
log('Finished monaco.d.ts generation');
var one = currentContent.replace(/\r\n/gm, '\n');
......@@ -317,6 +374,7 @@ function run(out, inputFiles) {
var isTheSame = one === other;
return {
content: result,
usageContent: usageContent,
filePath: DECLARATION_PATH,
isTheSame: isTheSame
};
......@@ -326,3 +384,78 @@ function complainErrors() {
logErr('Not running monaco.d.ts generation due to compile errors');
}
exports.complainErrors = complainErrors;
var TypeScriptLanguageServiceHost = /** @class */ (function () {
function TypeScriptLanguageServiceHost(libs, files, compilerOptions) {
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
}
// --- language service host ---------------
TypeScriptLanguageServiceHost.prototype.getCompilationSettings = function () {
return this._compilerOptions;
};
TypeScriptLanguageServiceHost.prototype.getScriptFileNames = function () {
return ([]
.concat(Object.keys(this._libs))
.concat(Object.keys(this._files)));
};
TypeScriptLanguageServiceHost.prototype.getScriptVersion = function (fileName) {
return '1';
};
TypeScriptLanguageServiceHost.prototype.getProjectVersion = function () {
return '1';
};
TypeScriptLanguageServiceHost.prototype.getScriptSnapshot = function (fileName) {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
}
else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
}
else {
return ts.ScriptSnapshot.fromString('');
}
};
TypeScriptLanguageServiceHost.prototype.getScriptKind = function (fileName) {
return ts.ScriptKind.TS;
};
TypeScriptLanguageServiceHost.prototype.getCurrentDirectory = function () {
return '';
};
TypeScriptLanguageServiceHost.prototype.getDefaultLibFileName = function (options) {
return 'defaultLib:es5';
};
TypeScriptLanguageServiceHost.prototype.isDefaultLibFileName = function (fileName) {
return fileName === this.getDefaultLibFileName(this._compilerOptions);
};
return TypeScriptLanguageServiceHost;
}());
function execute() {
var OUTPUT_FILES = {};
var SRC_FILES = {};
var SRC_FILE_TO_EXPECTED_NAME = {};
getIncludesInRecipe().forEach(function (moduleId) {
if (/\.d\.ts$/.test(moduleId)) {
var fileName_1 = path.join(SRC, moduleId);
OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName_1).toString();
return;
}
var fileName = path.join(SRC, moduleId) + '.ts';
SRC_FILES[fileName] = fs.readFileSync(fileName).toString();
SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId);
});
var languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {}));
var t1 = Date.now();
Object.keys(SRC_FILES).forEach(function (fileName) {
var t = Date.now();
var emitOutput = languageService.getEmitOutput(fileName, true);
OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text;
console.log("Generating .d.ts for " + fileName + " took " + (Date.now() - t) + " ms");
});
console.log("Generating .d.ts took " + (Date.now() - t1) + " ms");
var result = run('src', OUTPUT_FILES);
console.log(result.filePath);
fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n'));
fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n'));
}
// execute();
......@@ -153,8 +153,30 @@ function getNodeText(sourceFile: ts.SourceFile, node: { pos: number; end: number
return sourceFile.getFullText().substring(node.pos, node.end);
}
function hasModifier(modifiers: ts.NodeArray<ts.Modifier>, kind: ts.SyntaxKind): boolean {
if (modifiers) {
for (let i = 0; i < modifiers.length; i++) {
let mod = modifiers[i];
if (mod.kind === kind) {
return true;
}
}
}
return false;
}
function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare): string {
function isStatic(member: ts.ClassElement | ts.TypeElement): boolean {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
return (
hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)
);
}
function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[]): string {
let result = getNodeText(sourceFile, declaration);
// if (result.indexOf('MonacoWorker') >= 0) {
// console.log('here!');
......@@ -163,7 +185,23 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
let interfaceDeclaration = <ts.InterfaceDeclaration | ts.ClassDeclaration>declaration;
let members: ts.NodeArray<ts.Node> = interfaceDeclaration.members;
const staticTypeName = (
isDefaultExport(interfaceDeclaration)
? `${importName}.default`
: `${importName}.${declaration.name.text}`
);
let instanceTypeName = staticTypeName;
const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0);
if (typeParametersCnt > 0) {
let arr: string[] = [];
for (let i = 0; i < typeParametersCnt; i++) {
arr.push('any');
}
instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`;
}
const members: ts.NodeArray<ts.ClassElement | ts.TypeElement> = interfaceDeclaration.members;
members.forEach((member) => {
try {
let memberText = getNodeText(sourceFile, member);
......@@ -171,6 +209,13 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
// console.log('BEFORE: ', result);
result = result.replace(memberText, '');
// console.log('AFTER: ', result);
} else {
const memberName = (<ts.Identifier | ts.StringLiteral>member.name).text;
if (isStatic(member)) {
usage.push(`a = ${staticTypeName}.${memberName};`);
} else {
usage.push(`a = (<${instanceTypeName}>b).${memberName};`);
}
}
} catch (err) {
// life..
......@@ -237,11 +282,24 @@ function createReplacer(data: string): (str: string) => string {
};
}
function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): string {
function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): [string, string] {
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
let lines = recipe.split(endl);
let result = [];
let result: string[] = [];
let usageCounter = 0;
let usageImports: string[] = [];
let usage: string[] = [];
usage.push(`var a;`);
usage.push(`var b;`);
const generateUsageImport = (moduleId: string) => {
let importName = 'm' + (++usageCounter);
usageImports.push(`import * as ${importName} from '${moduleId.replace(/\.d\.ts$/, '')}';`);
return importName;
};
lines.forEach(line => {
......@@ -254,6 +312,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m1[2]);
let typeNames = m1[3].split(/,/);
......@@ -267,7 +327,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri
logErr('Cannot find type ' + typeName);
return;
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration)));
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage)));
});
return;
}
......@@ -281,6 +341,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri
return;
}
const importName = generateUsageImport(moduleId);
let replacer = createReplacer(m2[2]);
let typeNames = m2[3].split(/,/);
......@@ -309,7 +371,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri
}
}
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration)));
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage)));
});
return;
}
......@@ -324,10 +386,13 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri
resultTxt = format(resultTxt);
return resultTxt;
return [
resultTxt,
`${usageImports.join('\n')}\n\n${usage.join('\n')}`
];
}
export function getFilesToWatch(out: string): string[] {
function getIncludesInRecipe(): string[] {
let recipe = fs.readFileSync(RECIPE_PATH).toString();
let lines = recipe.split(/\r\n|\n|\r/);
let result = [];
......@@ -337,14 +402,14 @@ export function getFilesToWatch(out: string): string[] {
let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m1) {
let moduleId = m1[1];
result.push(moduleIdToPath(out, moduleId));
result.push(moduleId);
return;
}
let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
if (m2) {
let moduleId = m2[1];
result.push(moduleIdToPath(out, moduleId));
result.push(moduleId);
return;
}
});
......@@ -352,8 +417,13 @@ export function getFilesToWatch(out: string): string[] {
return result;
}
export function getFilesToWatch(out: string): string[] {
return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId));
}
export interface IMonacoDeclarationResult {
content: string;
usageContent: string;
filePath: string;
isTheSame: boolean;
}
......@@ -363,7 +433,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona
SOURCE_FILE_MAP = {};
let recipe = fs.readFileSync(RECIPE_PATH).toString();
let result = generateDeclarationFile(out, inputFiles, recipe);
let [result, usageContent] = generateDeclarationFile(out, inputFiles, recipe);
let currentContent = fs.readFileSync(DECLARATION_PATH).toString();
log('Finished monaco.d.ts generation');
......@@ -374,6 +444,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona
return {
content: result,
usageContent: usageContent,
filePath: DECLARATION_PATH,
isTheSame
};
......@@ -382,3 +453,98 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona
export function complainErrors() {
logErr('Not running monaco.d.ts generation due to compile errors');
}
interface ILibMap { [libName: string]: string; }
interface IFileMap { [fileName: string]: string; }
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
private readonly _libs: ILibMap;
private readonly _files: IFileMap;
private readonly _compilerOptions: ts.CompilerOptions;
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
}
// --- language service host ---------------
getCompilationSettings(): ts.CompilerOptions {
return this._compilerOptions;
}
getScriptFileNames(): string[] {
return (
[]
.concat(Object.keys(this._libs))
.concat(Object.keys(this._files))
);
}
getScriptVersion(fileName: string): string {
return '1';
}
getProjectVersion(): string {
return '1';
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
} else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
} else {
return ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(fileName: string): ts.ScriptKind {
return ts.ScriptKind.TS;
}
getCurrentDirectory(): string {
return '';
}
getDefaultLibFileName(options: ts.CompilerOptions): string {
return 'defaultLib:es5';
}
isDefaultLibFileName(fileName: string): boolean {
return fileName === this.getDefaultLibFileName(this._compilerOptions);
}
}
function execute() {
const OUTPUT_FILES: { [file: string]: string; } = {};
const SRC_FILES: IFileMap = {};
const SRC_FILE_TO_EXPECTED_NAME: { [filename: string]: string; } = {};
getIncludesInRecipe().forEach((moduleId) => {
if (/\.d\.ts$/.test(moduleId)) {
let fileName = path.join(SRC, moduleId);
OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString();
return;
}
let fileName = path.join(SRC, moduleId) + '.ts';
SRC_FILES[fileName] = fs.readFileSync(fileName).toString();
SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId);
});
const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {}));
var t1 = Date.now();
Object.keys(SRC_FILES).forEach((fileName) => {
var t = Date.now();
const emitOutput = languageService.getEmitOutput(fileName, true);
OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text;
console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`);
});
console.log(`Generating .d.ts took ${Date.now() - t1} ms`);
const result = run('src', OUTPUT_FILES);
console.log(result.filePath);
fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n'));
fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n'));
}
// execute();
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册