2016-05-20 15:45:31 +00:00
|
|
|
/*---------------------------------------------------------------------------------------------
|
|
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
// PREREQUISITE:
|
|
|
|
// SET VSCODE_BUILD_DECLARATION_FILES=1
|
|
|
|
// run gulp watch once
|
|
|
|
|
|
|
|
import fs = require('fs');
|
|
|
|
import ts = require('typescript');
|
|
|
|
import path = require('path');
|
|
|
|
|
|
|
|
|
|
|
|
const SRC = path.join(__dirname, '../../src');
|
|
|
|
const OUT = path.join(__dirname, '../../out');
|
|
|
|
|
|
|
|
|
|
|
|
function moduleIdToPath(moduleId:string): string {
|
|
|
|
if (/\.d\.ts/.test(moduleId)) {
|
|
|
|
return path.join(SRC, moduleId);
|
|
|
|
}
|
|
|
|
return path.join(OUT, moduleId) + '.d.ts';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
let SOURCE_FILE_MAP: {[moduleId:string]:ts.SourceFile;} = {};
|
2016-05-20 15:45:31 +00:00
|
|
|
function getSourceFile(moduleId:string): ts.SourceFile {
|
|
|
|
if (!SOURCE_FILE_MAP[moduleId]) {
|
|
|
|
let filePath = moduleIdToPath(moduleId);
|
2016-05-31 10:38:54 +00:00
|
|
|
|
|
|
|
let fileContents: string;
|
|
|
|
try {
|
|
|
|
fileContents = fs.readFileSync(filePath).toString();
|
|
|
|
} catch (err) {
|
|
|
|
console.error('======================================================================');
|
|
|
|
console.error('=> Have you compiled (gulp watch) with env variable VSCODE_BUILD_DECLARATION_FILES=1 ?');
|
|
|
|
console.error('======================================================================');
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
|
2016-05-20 15:45:31 +00:00
|
|
|
let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5);
|
|
|
|
|
|
|
|
SOURCE_FILE_MAP[moduleId] = sourceFile;
|
|
|
|
}
|
|
|
|
return SOURCE_FILE_MAP[moduleId];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-25 21:56:13 +00:00
|
|
|
type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration;
|
2016-05-21 06:45:12 +00:00
|
|
|
type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement;
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-21 06:45:12 +00:00
|
|
|
function isDeclaration(a:TSTopLevelDeclare): a is TSTopLevelDeclaration {
|
2016-05-25 11:19:54 +00:00
|
|
|
return (
|
|
|
|
a.kind === ts.SyntaxKind.InterfaceDeclaration
|
|
|
|
|| a.kind === ts.SyntaxKind.EnumDeclaration
|
|
|
|
|| a.kind === ts.SyntaxKind.ClassDeclaration
|
|
|
|
|| a.kind === ts.SyntaxKind.TypeAliasDeclaration
|
|
|
|
|| a.kind === ts.SyntaxKind.FunctionDeclaration
|
2016-05-25 21:56:13 +00:00
|
|
|
|| a.kind === ts.SyntaxKind.ModuleDeclaration
|
2016-05-25 11:19:54 +00:00
|
|
|
);
|
2016-05-21 06:45:12 +00:00
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-21 06:45:12 +00:00
|
|
|
function visitTopLevelDeclarations(sourceFile:ts.SourceFile, visitor:(node:TSTopLevelDeclare)=>boolean): void {
|
2016-05-20 15:45:31 +00:00
|
|
|
let stop = false;
|
|
|
|
|
|
|
|
let visit = (node: ts.Node): void => {
|
|
|
|
if (stop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (node.kind) {
|
|
|
|
case ts.SyntaxKind.InterfaceDeclaration:
|
|
|
|
case ts.SyntaxKind.EnumDeclaration:
|
|
|
|
case ts.SyntaxKind.ClassDeclaration:
|
2016-05-21 06:45:12 +00:00
|
|
|
case ts.SyntaxKind.VariableStatement:
|
|
|
|
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
|
|
case ts.SyntaxKind.FunctionDeclaration:
|
2016-05-25 21:56:13 +00:00
|
|
|
case ts.SyntaxKind.ModuleDeclaration:
|
2016-05-21 06:45:12 +00:00
|
|
|
stop = visitor(<TSTopLevelDeclare>node);
|
2016-05-20 15:45:31 +00:00
|
|
|
}
|
|
|
|
|
2016-05-25 11:19:54 +00:00
|
|
|
// if (node.kind !== ts.SyntaxKind.SourceFile) {
|
2016-05-25 21:56:13 +00:00
|
|
|
// if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) {
|
2016-05-25 11:19:54 +00:00
|
|
|
// console.log('FOUND TEXT IN NODE: ' + ts.SyntaxKind[node.kind]);
|
|
|
|
// console.log(getNodeText(sourceFile, node));
|
|
|
|
// }
|
|
|
|
// }
|
2016-05-21 06:45:12 +00:00
|
|
|
|
2016-05-20 15:45:31 +00:00
|
|
|
if (stop) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ts.forEachChild(node, visit);
|
|
|
|
};
|
|
|
|
|
|
|
|
visit(sourceFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-21 06:45:12 +00:00
|
|
|
function getAllTopLevelDeclarations(sourceFile:ts.SourceFile): TSTopLevelDeclare[] {
|
|
|
|
let all:TSTopLevelDeclare[] = [];
|
2016-05-20 15:45:31 +00:00
|
|
|
visitTopLevelDeclarations(sourceFile, (node) => {
|
2016-05-25 21:56:13 +00:00
|
|
|
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
|
2016-05-25 11:19:54 +00:00
|
|
|
let interfaceDeclaration = <ts.InterfaceDeclaration>node;
|
|
|
|
let triviaStart = interfaceDeclaration.pos;
|
|
|
|
let triviaEnd = interfaceDeclaration.name.pos;
|
|
|
|
let triviaText = getNodeText(sourceFile, { pos: triviaStart, end: triviaEnd });
|
2016-05-25 21:56:13 +00:00
|
|
|
|
|
|
|
// // let nodeText = getNodeText(sourceFile, node);
|
|
|
|
// if (getNodeText(sourceFile, node).indexOf('SymbolKind') >= 0) {
|
|
|
|
// console.log('TRIVIA: ', triviaText);
|
|
|
|
// }
|
2016-05-25 11:19:54 +00:00
|
|
|
if (triviaText.indexOf('@internal') === -1) {
|
|
|
|
all.push(node);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let nodeText = getNodeText(sourceFile, node);
|
|
|
|
if (nodeText.indexOf('@internal') === -1) {
|
|
|
|
all.push(node);
|
|
|
|
}
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
return false /*continue*/;
|
|
|
|
});
|
|
|
|
return all;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-21 06:45:12 +00:00
|
|
|
function getTopLevelDeclaration(sourceFile:ts.SourceFile, typeName:string): TSTopLevelDeclare {
|
|
|
|
let result:TSTopLevelDeclare = null;
|
2016-05-20 15:45:31 +00:00
|
|
|
visitTopLevelDeclarations(sourceFile, (node) => {
|
2016-05-21 06:45:12 +00:00
|
|
|
if (isDeclaration(node)) {
|
|
|
|
if (node.name.text === typeName) {
|
|
|
|
result = node;
|
|
|
|
return true /*stop*/;
|
|
|
|
}
|
|
|
|
return false /*continue*/;
|
2016-05-20 15:45:31 +00:00
|
|
|
}
|
2016-05-21 06:45:12 +00:00
|
|
|
// node is ts.VariableStatement
|
2016-05-25 11:19:54 +00:00
|
|
|
if (getNodeText(sourceFile, node).indexOf(typeName) >= 0) {
|
|
|
|
result = node;
|
|
|
|
return true /*stop*/;
|
|
|
|
}
|
|
|
|
return false /*continue*/;
|
2016-05-20 15:45:31 +00:00
|
|
|
});
|
2016-05-25 11:19:54 +00:00
|
|
|
if (result === null) {
|
|
|
|
console.log('COULD NOT FIND ' + typeName + '!');
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-25 11:19:54 +00:00
|
|
|
function getNodeText(sourceFile:ts.SourceFile, node:{pos:number; end:number;}): string {
|
2016-05-20 15:45:31 +00:00
|
|
|
return sourceFile.getFullText().substring(node.pos, node.end);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-21 06:45:12 +00:00
|
|
|
function getMassagedTopLevelDeclarationText(sourceFile:ts.SourceFile, declaration: TSTopLevelDeclare): string {
|
2016-05-20 15:45:31 +00:00
|
|
|
let result = getNodeText(sourceFile, declaration);
|
2016-05-25 21:56:13 +00:00
|
|
|
// if (result.indexOf('MonacoWorker') >= 0) {
|
|
|
|
// console.log('here!');
|
|
|
|
// // console.log(ts.SyntaxKind[declaration.kind]);
|
|
|
|
// }
|
2016-05-25 13:38:02 +00:00
|
|
|
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
|
|
|
|
let interfaceDeclaration = <ts.InterfaceDeclaration | ts.ClassDeclaration>declaration;
|
2016-05-25 11:19:54 +00:00
|
|
|
|
2016-05-25 13:38:02 +00:00
|
|
|
let members:ts.NodeArray<ts.Node> = interfaceDeclaration.members;
|
2016-05-25 11:19:54 +00:00
|
|
|
members.forEach((member) => {
|
|
|
|
try {
|
|
|
|
let memberText = getNodeText(sourceFile, member);
|
2016-05-25 13:38:02 +00:00
|
|
|
if (memberText.indexOf('@internal') >= 0 || memberText.indexOf('private') >= 0) {
|
2016-05-25 11:19:54 +00:00
|
|
|
// console.log('BEFORE: ', result);
|
|
|
|
result = result.replace(memberText, '');
|
|
|
|
// console.log('AFTER: ', result);
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
// life..
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
result = result.replace(/export default/g, 'export');
|
|
|
|
result = result.replace(/export declare/g, 'export');
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-05-21 07:07:42 +00:00
|
|
|
function format(text:string): string {
|
|
|
|
let options = getDefaultOptions();
|
|
|
|
|
|
|
|
// Parse the source text
|
|
|
|
let sourceFile = ts.createSourceFile('file.ts', text, ts.ScriptTarget.Latest, /*setParentPointers*/ true);
|
|
|
|
|
|
|
|
// Get the formatting edits on the input sources
|
|
|
|
let edits = (<any>ts).formatting.formatDocument(sourceFile, getRuleProvider(options), options);
|
|
|
|
|
|
|
|
// Apply the edits on the input code
|
|
|
|
return applyEdits(text, edits);
|
|
|
|
|
|
|
|
function getRuleProvider(options: ts.FormatCodeOptions) {
|
|
|
|
// Share this between multiple formatters using the same options.
|
|
|
|
// This represents the bulk of the space the formatter uses.
|
|
|
|
let ruleProvider = new (<any>ts).formatting.RulesProvider();
|
|
|
|
ruleProvider.ensureUpToDate(options);
|
|
|
|
return ruleProvider;
|
|
|
|
}
|
|
|
|
|
|
|
|
function applyEdits(text: string, edits: ts.TextChange[]): string {
|
|
|
|
// Apply edits in reverse on the existing text
|
|
|
|
let result = text;
|
|
|
|
for (let i = edits.length - 1; i >= 0; i--) {
|
|
|
|
let change = edits[i];
|
|
|
|
let head = result.slice(0, change.span.start);
|
2016-05-25 11:19:54 +00:00
|
|
|
let tail = result.slice(change.span.start + change.span.length);
|
2016-05-21 07:07:42 +00:00
|
|
|
result = head + change.newText + tail;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDefaultOptions(): ts.FormatCodeOptions {
|
|
|
|
return {
|
|
|
|
IndentSize: 4,
|
|
|
|
TabSize: 4,
|
|
|
|
NewLineCharacter: '\r\n',
|
|
|
|
ConvertTabsToSpaces: true,
|
|
|
|
IndentStyle: ts.IndentStyle.Block,
|
|
|
|
|
|
|
|
InsertSpaceAfterCommaDelimiter: true,
|
|
|
|
InsertSpaceAfterSemicolonInForStatements: true,
|
|
|
|
InsertSpaceBeforeAndAfterBinaryOperators: true,
|
|
|
|
InsertSpaceAfterKeywordsInControlFlowStatements: true,
|
|
|
|
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
|
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
|
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
|
|
|
|
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: true,
|
|
|
|
PlaceOpenBraceOnNewLineForFunctions: false,
|
|
|
|
PlaceOpenBraceOnNewLineForControlBlocks: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-25 21:56:13 +00:00
|
|
|
function createReplacer(data:string): (str:string)=>string {
|
|
|
|
data = data || '';
|
|
|
|
let rawDirectives = data.split(';');
|
|
|
|
let directives: [RegExp,string][] = [];
|
|
|
|
rawDirectives.forEach((rawDirective) => {
|
|
|
|
if (rawDirective.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let pieces = rawDirective.split('=>');
|
|
|
|
let findStr = pieces[0];
|
|
|
|
let replaceStr = pieces[1];
|
|
|
|
|
|
|
|
findStr = findStr.replace(/[\-\\\{\}\*\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&');
|
|
|
|
findStr = '\\b' + findStr + '\\b';
|
|
|
|
directives.push([new RegExp(findStr, 'g'), replaceStr]);
|
|
|
|
});
|
|
|
|
|
|
|
|
return (str:string)=> {
|
|
|
|
for (let i = 0; i < directives.length; i++) {
|
|
|
|
str = str.replace(directives[i][0], directives[i][1]);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
export function generateDeclarationFile(recipe:string): string {
|
|
|
|
let lines = recipe.split(/\r\n|\n|\r/);
|
|
|
|
let result = [];
|
2016-05-20 15:45:31 +00:00
|
|
|
|
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
lines.forEach(line => {
|
2016-05-25 21:56:13 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
|
|
|
|
if (m1) {
|
|
|
|
console.log('HANDLING META: ' + line);
|
|
|
|
let moduleId = m1[1];
|
|
|
|
let sourceFile = getSourceFile(moduleId);
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
let replacer = createReplacer(m1[2]);
|
|
|
|
|
|
|
|
let typeNames = m1[3].split(/,/);
|
|
|
|
typeNames.forEach((typeName) => {
|
|
|
|
typeName = typeName.trim();
|
|
|
|
if (typeName.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let declaration = getTopLevelDeclaration(sourceFile, typeName);
|
|
|
|
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration)));
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/);
|
|
|
|
if (m2) {
|
|
|
|
console.log('HANDLING META: ' + line);
|
|
|
|
let moduleId = m2[1];
|
|
|
|
let sourceFile = getSourceFile(moduleId);
|
|
|
|
|
|
|
|
let replacer = createReplacer(m2[2]);
|
|
|
|
|
|
|
|
let typeNames = m2[3].split(/,/);
|
|
|
|
let typesToExcludeMap: {[typeName:string]:boolean;} = {};
|
|
|
|
let typesToExcludeArr: string[] = [];
|
|
|
|
typeNames.forEach((typeName) => {
|
|
|
|
typeName = typeName.trim();
|
|
|
|
if (typeName.length === 0) {
|
2016-05-22 10:10:11 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-05-31 10:38:54 +00:00
|
|
|
typesToExcludeMap[typeName] = true;
|
|
|
|
typesToExcludeArr.push(typeName);
|
|
|
|
});
|
|
|
|
|
|
|
|
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
|
|
|
|
if (isDeclaration(declaration)) {
|
|
|
|
if (typesToExcludeMap[declaration.name.text]) {
|
2016-05-25 11:19:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-05-31 10:38:54 +00:00
|
|
|
} else {
|
|
|
|
// node is ts.VariableStatement
|
|
|
|
let nodeText = getNodeText(sourceFile, declaration);
|
|
|
|
for (let i = 0; i < typesToExcludeArr.length; i++) {
|
|
|
|
if (nodeText.indexOf(typesToExcludeArr[i]) >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-05-25 11:19:54 +00:00
|
|
|
}
|
2016-05-31 10:38:54 +00:00
|
|
|
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration)));
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.push(line);
|
|
|
|
});
|
|
|
|
|
|
|
|
let resultTxt = result.join('\n');
|
|
|
|
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
|
|
|
|
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
|
|
|
|
resultTxt = resultTxt.replace(/\bTPromise</g, 'Promise<');
|
|
|
|
|
|
|
|
resultTxt = format(resultTxt);
|
|
|
|
|
|
|
|
resultTxt = resultTxt.replace(/\r\n/g, '\n');
|
|
|
|
return resultTxt;
|
|
|
|
}
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe');
|
|
|
|
let recipe = fs.readFileSync(RECIPE_PATH).toString();
|
|
|
|
let result = generateDeclarationFile(recipe);
|
2016-05-20 15:45:31 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts');
|
|
|
|
fs.writeFileSync(DECLARATION_PATH, result);
|
2016-05-21 07:07:42 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
// let result = generateDeclarationFile
|
2016-05-21 07:07:42 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
// var recipe = fs.readFileSync(path.join(__dirname, './monaco-editor.d.ts.recipe')).toString();
|
2016-05-21 07:07:42 +00:00
|
|
|
|
2016-05-31 10:38:54 +00:00
|
|
|
// fs.writeFileSync(path.join(__dirname, './monaco-editor.d.ts'), resultTxt);
|