Load expensive node modules lazily

上级 277a8262
......@@ -16,8 +16,6 @@ const cp = require('child_process');
const compilation = require('./lib/compilation');
const monacoapi = require('./lib/monaco-api');
const fs = require('fs');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
let root = path.dirname(__dirname);
let sha1 = util.getVersion(root);
......@@ -369,6 +367,9 @@ gulp.task('editor-distro',
);
const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => {
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const result = es.through();
const webpackConfigPath = path.join(root, 'build/monaco/monaco.webpack.config.js');
......
......@@ -9,17 +9,13 @@ require('events').EventEmitter.defaultMaxListeners = 100;
const gulp = require('gulp');
const path = require('path');
const nodeUtil = require('util');
const tsb = require('gulp-tsb');
const es = require('event-stream');
const filter = require('gulp-filter');
const webpack = require('webpack');
const util = require('./lib/util');
const task = require('./lib/task');
const watcher = require('./lib/watch');
const createReporter = require('./lib/reporter').createReporter;
const glob = require('glob');
const sourcemaps = require('gulp-sourcemaps');
const nlsDev = require('vscode-nls-dev');
const root = path.dirname(__dirname);
const commit = util.getVersion(root);
const plumber = require('gulp-plumber');
......@@ -29,10 +25,48 @@ const ext = require('./lib/extensions');
const extensionsPath = path.join(path.dirname(__dirname), 'extensions');
const compilations = glob.sync('**/tsconfig.json', {
cwd: extensionsPath,
ignore: ['**/out/**', '**/node_modules/**']
});
// To save 250ms for each gulp startup, we are caching the result here
// const compilations = glob.sync('**/tsconfig.json', {
// cwd: extensionsPath,
// ignore: ['**/out/**', '**/node_modules/**']
// });
const compilations = [
'configuration-editing/build/tsconfig.json',
'configuration-editing/tsconfig.json',
'css-language-features/client/tsconfig.json',
'css-language-features/server/tsconfig.json',
'debug-auto-launch/tsconfig.json',
'debug-server-ready/tsconfig.json',
'emmet/tsconfig.json',
'extension-editing/tsconfig.json',
'git-ui/tsconfig.json',
'git/tsconfig.json',
'github-authentication/tsconfig.json',
'github/tsconfig.json',
'grunt/tsconfig.json',
'gulp/tsconfig.json',
'html-language-features/client/tsconfig.json',
'html-language-features/server/tsconfig.json',
'image-preview/tsconfig.json',
'jake/tsconfig.json',
'json-language-features/client/tsconfig.json',
'json-language-features/server/tsconfig.json',
'markdown-language-features/preview-src/tsconfig.json',
'markdown-language-features/tsconfig.json',
'merge-conflict/tsconfig.json',
'microsoft-authentication/tsconfig.json',
'npm/tsconfig.json',
'php-language-features/tsconfig.json',
'python/tsconfig.json',
'search-result/tsconfig.json',
'typescript-language-features/test-workspace/tsconfig.json',
'typescript-language-features/tsconfig.json',
'vscode-api-tests/tsconfig.json',
'vscode-colorize-tests/tsconfig.json',
'vscode-custom-editor-tests/tsconfig.json',
'vscode-notebook-tests/tsconfig.json',
'vscode-test-resolver/tsconfig.json'
];
const getBaseUrl = out => `https://ticino.blob.core.windows.net/sourcemaps/${commit}/${out}`;
......@@ -64,6 +98,10 @@ const tasks = compilations.map(function (tsconfigFile) {
}
function createPipeline(build, emitError) {
const nlsDev = require('vscode-nls-dev');
const tsb = require('gulp-tsb');
const sourcemaps = require('gulp-sourcemaps');
const reporter = createReporter('extensions');
overrideOptions.inlineSources = Boolean(build);
......@@ -181,6 +219,7 @@ gulp.task(watchWebExtensionsTask);
exports.watchWebExtensionsTask = watchWebExtensionsTask;
async function buildWebExtensions(isWatch) {
const webpack = require('webpack');
const webpackConfigLocations = await nodeUtil.promisify(glob)(
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
......
......@@ -6,13 +6,13 @@
const gulp = require('gulp');
const filter = require('gulp-filter');
const es = require('event-stream');
const gulpeslint = require('gulp-eslint');
const vfs = require('vinyl-fs');
const path = require('path');
const task = require('./lib/task');
const { all, jsHygieneFilter, tsHygieneFilter, hygiene } = require('./hygiene');
gulp.task('eslint', () => {
const gulpeslint = require('gulp-eslint');
return vfs
.src(all, { base: '.', follow: true, allowEmpty: true })
.pipe(filter(jsHygieneFilter.concat(tsHygieneFilter)))
......
......@@ -14,10 +14,8 @@ const task = require('./lib/task');
const vfs = require('vinyl-fs');
const flatmap = require('gulp-flatmap');
const gunzip = require('gulp-gunzip');
const untar = require('gulp-untar');
const File = require('vinyl');
const fs = require('fs');
const remote = require('gulp-remote-retry-src');
const rename = require('gulp-rename');
const filter = require('gulp-filter');
const cp = require('child_process');
......@@ -77,6 +75,9 @@ if (defaultNodeTask) {
}
function nodejs(platform, arch) {
const remote = require('gulp-remote-retry-src');
const untar = require('gulp-untar');
if (arch === 'ia32') {
arch = 'x86';
}
......
......@@ -11,13 +11,10 @@ const os = require('os');
const cp = require('child_process');
const path = require('path');
const es = require('event-stream');
const azure = require('gulp-azure-storage');
const electron = require('gulp-atom-electron');
const vfs = require('vinyl-fs');
const rename = require('gulp-rename');
const replace = require('gulp-replace');
const filter = require('gulp-filter');
const json = require('gulp-json-editor');
const _ = require('underscore');
const util = require('./lib/util');
const task = require('./lib/task');
......@@ -143,6 +140,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op
platform = platform || process.platform;
return () => {
const electron = require('gulp-atom-electron');
const json = require('gulp-json-editor');
const out = sourceFolderName;
const checksums = computeChecksums(out, [
......@@ -503,6 +503,8 @@ gulp.task(task.define(
task.series(
generateVSCodeConfigurationTask,
() => {
const azure = require('gulp-azure-storage');
if (!shouldSetupSettingsSearch()) {
const branch = process.env.BUILD_SOURCEBRANCH;
console.log(`Only runs on master and release branches, not ${branch}`);
......
......@@ -5,8 +5,6 @@
const filter = require('gulp-filter');
const es = require('event-stream');
const gulpeslint = require('gulp-eslint');
const tsfmt = require('typescript-formatter');
const VinylFile = require('vinyl');
const vfs = require('vinyl-fs');
const path = require('path');
......@@ -165,6 +163,9 @@ const copyrightHeaderLines = [
];
function hygiene(some) {
const gulpeslint = require('gulp-eslint');
const tsfmt = require('typescript-formatter');
let errorCount = 0;
const productJson = es.through(function (file) {
......
......@@ -8,9 +8,6 @@ exports.watchTask = exports.compileTask = void 0;
const es = require("event-stream");
const fs = require("fs");
const gulp = require("gulp");
const bom = require("gulp-bom");
const sourcemaps = require("gulp-sourcemaps");
const tsb = require("gulp-tsb");
const path = require("path");
const monacodts = require("./monaco-api");
const nls = require("./nls");
......@@ -36,10 +33,13 @@ function getTypeScriptCompilerOptions(src) {
return options;
}
function createCompile(src, build, emitError) {
const tsb = require('gulp-tsb');
const sourcemaps = require('gulp-sourcemaps');
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
const overrideOptions = Object.assign(Object.assign({}, getTypeScriptCompilerOptions(src)), { inlineSources: Boolean(build) });
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
function pipeline(token) {
const bom = require('gulp-bom');
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path)));
......@@ -52,7 +52,7 @@ function createCompile(src, build, emitError) {
.pipe(util.loadSourcemaps())
.pipe(compilation(token))
.pipe(noDeclarationsFilter)
.pipe(build ? nls() : es.through())
.pipe(build ? nls.nls() : es.through())
.pipe(noDeclarationsFilter.restore)
.pipe(sourcemaps.write('.', {
addComment: false,
......
......@@ -8,9 +8,6 @@
import * as es from 'event-stream';
import * as fs from 'fs';
import * as gulp from 'gulp';
import * as bom from 'gulp-bom';
import * as sourcemaps from 'gulp-sourcemaps';
import * as tsb from 'gulp-tsb';
import * as path from 'path';
import * as monacodts from './monaco-api';
import * as nls from './nls';
......@@ -41,12 +38,17 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions {
}
function createCompile(src: string, build: boolean, emitError?: boolean) {
const tsb = <typeof import('gulp-tsb')>require('gulp-tsb');
const sourcemaps = <typeof import('gulp-sourcemaps')>require('gulp-sourcemaps');
const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json');
const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) };
const compilation = tsb.create(projectPath, overrideOptions, false, err => reporter(err));
function pipeline(token?: util.ICancellationToken) {
const bom = <typeof import('gulp-bom')>require('gulp-bom');
const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path));
const tsFilter = util.filter(data => /\.ts$/.test(data.path));
......@@ -61,7 +63,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) {
.pipe(util.loadSourcemaps())
.pipe(compilation(token))
.pipe(noDeclarationsFilter)
.pipe(build ? nls() : es.through())
.pipe(build ? nls.nls() : es.through())
.pipe(noDeclarationsFilter.restore)
.pipe(sourcemaps.write('.', {
addComment: false,
......
......@@ -9,10 +9,8 @@ const fs = require("fs");
const path = require("path");
const vfs = require("vinyl-fs");
const filter = require("gulp-filter");
const json = require("gulp-json-editor");
const _ = require("underscore");
const util = require("./util");
const electron = require('gulp-atom-electron');
const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
......@@ -80,6 +78,8 @@ exports.config = {
};
function getElectron(arch) {
return () => {
const electron = require('gulp-atom-electron');
const json = require('gulp-json-editor');
const electronOpts = _.extend({}, exports.config, {
platform: process.platform,
arch: arch === 'armhf' ? 'arm' : arch,
......
......@@ -9,12 +9,9 @@ import * as fs from 'fs';
import * as path from 'path';
import * as vfs from 'vinyl-fs';
import * as filter from 'gulp-filter';
import * as json from 'gulp-json-editor';
import * as _ from 'underscore';
import * as util from './util';
const electron = require('gulp-atom-electron');
const root = path.dirname(path.dirname(__dirname));
const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8'));
const commit = util.getVersion(root);
......@@ -86,6 +83,9 @@ export const config = {
function getElectron(arch: string): () => NodeJS.ReadWriteStream {
return () => {
const electron = require('gulp-atom-electron');
const json = <typeof import('gulp-json-editor')>require('gulp-json-editor');
const electronOpts = _.extend({}, config, {
platform: process.platform,
arch: arch === 'armhf' ? 'arm' : arch,
......
......@@ -11,20 +11,15 @@ const glob = require("glob");
const gulp = require("gulp");
const path = require("path");
const File = require("vinyl");
const vsce = require("vsce");
const stats_1 = require("./stats");
const util2 = require("./util");
const remote = require("gulp-remote-retry-src");
const vzip = require('gulp-vinyl-zip');
const filter = require("gulp-filter");
const rename = require("gulp-rename");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const buffer = require('gulp-buffer');
const json = require("gulp-json-editor");
const jsoncParser = require("jsonc-parser");
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const util = require('./util');
const root = path.dirname(path.dirname(__dirname));
const commit = util.getVersion(root);
......@@ -88,6 +83,9 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
}
}
}
const vsce = require('vsce');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
......@@ -149,6 +147,7 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName) {
}
function fromLocalNormal(extensionPath) {
const result = es.through();
const vsce = require('vsce');
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn })
.then(fileNames => {
const files = fileNames
......@@ -170,6 +169,8 @@ const baseHeaders = {
'X-Market-User-Id': '291C1CD0-051A-4123-9B4B-30D60EF52EE2',
};
function fromMarketplace(extensionName, version, metadata) {
const remote = require('gulp-remote-retry-src');
const json = require('gulp-json-editor');
const [publisher, name] = extensionName.split('.');
const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`;
fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...');
......
......@@ -10,20 +10,15 @@ import * as gulp from 'gulp';
import * as path from 'path';
import { Stream } from 'stream';
import * as File from 'vinyl';
import * as vsce from 'vsce';
import { createStatsStream } from './stats';
import * as util2 from './util';
import remote = require('gulp-remote-retry-src');
const vzip = require('gulp-vinyl-zip');
import filter = require('gulp-filter');
import rename = require('gulp-rename');
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
const buffer = require('gulp-buffer');
import json = require('gulp-json-editor');
import * as jsoncParser from 'jsonc-parser';
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const util = require('./util');
const root = path.dirname(path.dirname(__dirname));
const commit = util.getVersion(root);
......@@ -97,6 +92,10 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
}
}
const vsce = <typeof import('vsce')>require('vsce');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
......@@ -175,6 +174,8 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string):
function fromLocalNormal(extensionPath: string): Stream {
const result = es.through();
const vsce = <typeof import('vsce')>require('vsce');
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn })
.then(fileNames => {
const files = fileNames
......@@ -200,6 +201,9 @@ const baseHeaders = {
};
export function fromMarketplace(extensionName: string, version: string, metadata: any): Stream {
const remote = require('gulp-remote-retry-src');
const json = <typeof import('gulp-json-editor')>require('gulp-json-editor');
const [publisher, name] = extensionName.split('.');
const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`;
......
......@@ -6,7 +6,6 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.execute = exports.run3 = exports.DeclarationResolver = exports.FSProvider = exports.RECIPE_PATH = void 0;
const fs = require("fs");
const ts = require("typescript");
const path = require("path");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
......@@ -18,7 +17,7 @@ const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts');
function logErr(message, ...rest) {
fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest);
}
function isDeclaration(a) {
function isDeclaration(ts, a) {
return (a.kind === ts.SyntaxKind.InterfaceDeclaration
|| a.kind === ts.SyntaxKind.EnumDeclaration
|| a.kind === ts.SyntaxKind.ClassDeclaration
......@@ -26,7 +25,7 @@ function isDeclaration(a) {
|| a.kind === ts.SyntaxKind.FunctionDeclaration
|| a.kind === ts.SyntaxKind.ModuleDeclaration);
}
function visitTopLevelDeclarations(sourceFile, visitor) {
function visitTopLevelDeclarations(ts, sourceFile, visitor) {
let stop = false;
let visit = (node) => {
if (stop) {
......@@ -49,9 +48,9 @@ function visitTopLevelDeclarations(sourceFile, visitor) {
};
visit(sourceFile);
}
function getAllTopLevelDeclarations(sourceFile) {
function getAllTopLevelDeclarations(ts, sourceFile) {
let all = [];
visitTopLevelDeclarations(sourceFile, (node) => {
visitTopLevelDeclarations(ts, sourceFile, (node) => {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
let interfaceDeclaration = node;
let triviaStart = interfaceDeclaration.pos;
......@@ -71,10 +70,10 @@ function getAllTopLevelDeclarations(sourceFile) {
});
return all;
}
function getTopLevelDeclaration(sourceFile, typeName) {
function getTopLevelDeclaration(ts, sourceFile, typeName) {
let result = null;
visitTopLevelDeclarations(sourceFile, (node) => {
if (isDeclaration(node) && node.name) {
visitTopLevelDeclarations(ts, sourceFile, (node) => {
if (isDeclaration(ts, node) && node.name) {
if (node.name.text === typeName) {
result = node;
return true /*stop*/;
......@@ -104,18 +103,18 @@ function hasModifier(modifiers, kind) {
}
return false;
}
function isStatic(member) {
function isStatic(ts, member) {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration) {
function isDefaultExport(ts, declaration) {
return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword)
&& hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword));
}
function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums) {
function getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums) {
let result = getNodeText(sourceFile, declaration);
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
let interfaceDeclaration = declaration;
const staticTypeName = (isDefaultExport(interfaceDeclaration)
const staticTypeName = (isDefaultExport(ts, interfaceDeclaration)
? `${importName}.default`
: `${importName}.${declaration.name.text}`);
let instanceTypeName = staticTypeName;
......@@ -137,7 +136,7 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName,
else {
const memberName = member.name.text;
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
if (isStatic(member)) {
if (isStatic(ts, member)) {
usage.push(`a = ${staticTypeName}${memberAccess};`);
}
else {
......@@ -191,7 +190,7 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName,
}
return result;
}
function format(text, endl) {
function format(ts, text, endl) {
const REALLY_FORMAT = false;
text = preformat(text, endl);
if (!REALLY_FORMAT) {
......@@ -336,7 +335,7 @@ function createReplacer(data) {
});
return createReplacerFromDirectives(directives);
}
function generateDeclarationFile(recipe, sourceFileGetter) {
function generateDeclarationFile(ts, recipe, sourceFileGetter) {
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
let lines = recipe.split(endl);
let result = [];
......@@ -379,14 +378,14 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
if (typeName.length === 0) {
return;
}
let declaration = getTopLevelDeclaration(sourceFile, typeName);
let declaration = getTopLevelDeclaration(ts, sourceFile, typeName);
if (!declaration) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${typeName}`);
failed = true;
return;
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
});
return;
}
......@@ -413,8 +412,8 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
typesToExcludeMap[typeName] = true;
typesToExcludeArr.push(typeName);
});
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
if (isDeclaration(declaration) && declaration.name) {
getAllTopLevelDeclarations(ts, sourceFile).forEach((declaration) => {
if (isDeclaration(ts, declaration) && declaration.name) {
if (typesToExcludeMap[declaration.name.text]) {
return;
}
......@@ -428,7 +427,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
}
}
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
});
return;
}
......@@ -450,7 +449,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
resultTxt = format(resultTxt, endl);
resultTxt = format(ts, resultTxt, endl);
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
enums.sort((e1, e2) => {
if (e1.enumName < e2.enumName) {
......@@ -471,7 +470,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
''
].concat(enums.map(e => e.text)).join(endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
resultEnums = format(resultEnums, endl);
resultEnums = format(ts, resultEnums, endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
return {
result: resultTxt,
......@@ -479,9 +478,9 @@ function generateDeclarationFile(recipe, sourceFileGetter) {
enums: resultEnums
};
}
function _run(sourceFileGetter) {
function _run(ts, sourceFileGetter) {
const recipe = fs.readFileSync(exports.RECIPE_PATH).toString();
const t = generateDeclarationFile(recipe, sourceFileGetter);
const t = generateDeclarationFile(ts, recipe, sourceFileGetter);
if (!t) {
return null;
}
......@@ -521,6 +520,7 @@ class CacheEntry {
class DeclarationResolver {
constructor(_fsProvider) {
this._fsProvider = _fsProvider;
this.ts = require('typescript');
this._sourceFileCache = Object.create(null);
}
invalidateCache(moduleId) {
......@@ -555,25 +555,26 @@ class DeclarationResolver {
if (/\.d\.ts$/.test(moduleId)) {
// const mtime = this._fsProvider.statFileSync()
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
return new CacheEntry(ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5), mtime);
return new CacheEntry(this.ts.createSourceFile(fileName, fileContents, this.ts.ScriptTarget.ES5), mtime);
}
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
const fileMap = {
'file.ts': fileContents
};
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, {}, fileMap, {}));
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
return new CacheEntry(ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5), mtime);
return new CacheEntry(this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5), mtime);
}
}
exports.DeclarationResolver = DeclarationResolver;
function run3(resolver) {
const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId);
return _run(sourceFileGetter);
return _run(resolver.ts, sourceFileGetter);
}
exports.run3 = run3;
class TypeScriptLanguageServiceHost {
constructor(libs, files, compilerOptions) {
constructor(ts, libs, files, compilerOptions) {
this._ts = ts;
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
......@@ -595,17 +596,17 @@ class TypeScriptLanguageServiceHost {
}
getScriptSnapshot(fileName) {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
}
else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
}
else {
return ts.ScriptSnapshot.fromString('');
return this._ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName) {
return ts.ScriptKind.TS;
return this._ts.ScriptKind.TS;
}
getCurrentDirectory() {
return '';
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as ts from 'typescript';
import type * as ts from 'typescript';
import * as path from 'path';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
......@@ -26,7 +26,7 @@ type SourceFileGetter = (moduleId: string) => ts.SourceFile | null;
type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration;
type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement;
function isDeclaration(a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
function isDeclaration(ts: typeof import('typescript'), a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
return (
a.kind === ts.SyntaxKind.InterfaceDeclaration
|| a.kind === ts.SyntaxKind.EnumDeclaration
......@@ -37,7 +37,7 @@ function isDeclaration(a: TSTopLevelDeclare): a is TSTopLevelDeclaration {
);
}
function visitTopLevelDeclarations(sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void {
function visitTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile, visitor: (node: TSTopLevelDeclare) => boolean): void {
let stop = false;
let visit = (node: ts.Node): void => {
......@@ -66,9 +66,9 @@ function visitTopLevelDeclarations(sourceFile: ts.SourceFile, visitor: (node: TS
}
function getAllTopLevelDeclarations(sourceFile: ts.SourceFile): TSTopLevelDeclare[] {
function getAllTopLevelDeclarations(ts: typeof import('typescript'), sourceFile: ts.SourceFile): TSTopLevelDeclare[] {
let all: TSTopLevelDeclare[] = [];
visitTopLevelDeclarations(sourceFile, (node) => {
visitTopLevelDeclarations(ts, sourceFile, (node) => {
if (node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration || node.kind === ts.SyntaxKind.ModuleDeclaration) {
let interfaceDeclaration = <ts.InterfaceDeclaration>node;
let triviaStart = interfaceDeclaration.pos;
......@@ -90,10 +90,10 @@ function getAllTopLevelDeclarations(sourceFile: ts.SourceFile): TSTopLevelDeclar
}
function getTopLevelDeclaration(sourceFile: ts.SourceFile, typeName: string): TSTopLevelDeclare | null {
function getTopLevelDeclaration(ts: typeof import('typescript'), sourceFile: ts.SourceFile, typeName: string): TSTopLevelDeclare | null {
let result: TSTopLevelDeclare | null = null;
visitTopLevelDeclarations(sourceFile, (node) => {
if (isDeclaration(node) && node.name) {
visitTopLevelDeclarations(ts, sourceFile, (node) => {
if (isDeclaration(ts, node) && node.name) {
if (node.name.text === typeName) {
result = node;
return true /*stop*/;
......@@ -127,24 +127,24 @@ function hasModifier(modifiers: ts.NodeArray<ts.Modifier> | undefined, kind: ts.
return false;
}
function isStatic(member: ts.ClassElement | ts.TypeElement): boolean {
function isStatic(ts: typeof import('typescript'), member: ts.ClassElement | ts.TypeElement): boolean {
return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword);
}
function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
function isDefaultExport(ts: typeof import('typescript'), 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[], enums: IEnumEntry[]): string {
function getMassagedTopLevelDeclarationText(ts: typeof import('typescript'), sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[], enums: IEnumEntry[]): string {
let result = getNodeText(sourceFile, declaration);
if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) {
let interfaceDeclaration = <ts.InterfaceDeclaration | ts.ClassDeclaration>declaration;
const staticTypeName = (
isDefaultExport(interfaceDeclaration)
isDefaultExport(ts, interfaceDeclaration)
? `${importName}.default`
: `${importName}.${declaration.name!.text}`
);
......@@ -168,7 +168,7 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
} else {
const memberName = (<ts.Identifier | ts.StringLiteral>member.name).text;
const memberAccess = (memberName.indexOf('.') >= 0 ? `['${memberName}']` : `.${memberName}`);
if (isStatic(member)) {
if (isStatic(ts, member)) {
usage.push(`a = ${staticTypeName}${memberAccess};`);
} else {
usage.push(`a = (<${instanceTypeName}>b)${memberAccess};`);
......@@ -222,7 +222,7 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati
return result;
}
function format(text: string, endl: string): string {
function format(ts: typeof import('typescript'), text: string, endl: string): string {
const REALLY_FORMAT = false;
text = preformat(text, endl);
......@@ -396,7 +396,7 @@ interface IEnumEntry {
text: string;
}
function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null {
function generateDeclarationFile(ts: typeof import('typescript'), recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null {
const endl = /\r\n/.test(recipe) ? '\r\n' : '\n';
let lines = recipe.split(endl);
......@@ -452,14 +452,14 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
if (typeName.length === 0) {
return;
}
let declaration = getTopLevelDeclaration(sourceFile, typeName);
let declaration = getTopLevelDeclaration(ts, sourceFile, typeName);
if (!declaration) {
logErr(`While handling ${line}`);
logErr(`Cannot find ${typeName}`);
failed = true;
return;
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
});
return;
}
......@@ -491,8 +491,8 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
typesToExcludeArr.push(typeName);
});
getAllTopLevelDeclarations(sourceFile).forEach((declaration) => {
if (isDeclaration(declaration) && declaration.name) {
getAllTopLevelDeclarations(ts, sourceFile).forEach((declaration) => {
if (isDeclaration(ts, declaration) && declaration.name) {
if (typesToExcludeMap[declaration.name.text]) {
return;
}
......@@ -505,7 +505,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
}
}
}
result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums)));
result.push(replacer(getMassagedTopLevelDeclarationText(ts, sourceFile, declaration, importName, usage, enums)));
});
return;
}
......@@ -530,7 +530,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri');
resultTxt = resultTxt.replace(/\bEvent</g, 'IEvent<');
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
resultTxt = format(resultTxt, endl);
resultTxt = format(ts, resultTxt, endl);
resultTxt = resultTxt.split(/\r\n|\n|\r/).join(endl);
enums.sort((e1, e2) => {
......@@ -553,7 +553,7 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet
''
].concat(enums.map(e => e.text)).join(endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
resultEnums = format(resultEnums, endl);
resultEnums = format(ts, resultEnums, endl);
resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl);
return {
......@@ -571,9 +571,9 @@ export interface IMonacoDeclarationResult {
isTheSame: boolean;
}
function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null {
function _run(ts: typeof import('typescript'), sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null {
const recipe = fs.readFileSync(RECIPE_PATH).toString();
const t = generateDeclarationFile(recipe, sourceFileGetter);
const t = generateDeclarationFile(ts, recipe, sourceFileGetter);
if (!t) {
return null;
}
......@@ -617,9 +617,11 @@ class CacheEntry {
export class DeclarationResolver {
public readonly ts: typeof import('typescript');
private _sourceFileCache: { [moduleId: string]: CacheEntry | null; };
constructor(private readonly _fsProvider: FSProvider) {
this.ts = <typeof import('typescript')>require('typescript');
this._sourceFileCache = Object.create(null);
}
......@@ -659,7 +661,7 @@ export class DeclarationResolver {
// const mtime = this._fsProvider.statFileSync()
const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString();
return new CacheEntry(
ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5),
this.ts.createSourceFile(fileName, fileContents, this.ts.ScriptTarget.ES5),
mtime
);
}
......@@ -667,10 +669,10 @@ export class DeclarationResolver {
const fileMap: IFileMap = {
'file.ts': fileContents
};
const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {}));
const service = this.ts.createLanguageService(new TypeScriptLanguageServiceHost(this.ts, {}, fileMap, {}));
const text = service.getEmitOutput('file.ts', true, true).outputFiles[0].text;
return new CacheEntry(
ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5),
this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.ES5),
mtime
);
}
......@@ -678,7 +680,7 @@ export class DeclarationResolver {
export function run3(resolver: DeclarationResolver): IMonacoDeclarationResult | null {
const sourceFileGetter = (moduleId: string) => resolver.getDeclarationSourceFile(moduleId);
return _run(sourceFileGetter);
return _run(resolver.ts, sourceFileGetter);
}
......@@ -689,11 +691,13 @@ interface IFileMap { [fileName: string]: string; }
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
private readonly _ts: typeof import('typescript');
private readonly _libs: ILibMap;
private readonly _files: IFileMap;
private readonly _compilerOptions: ts.CompilerOptions;
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
constructor(ts: typeof import('typescript'), libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
this._ts = ts;
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
......@@ -719,15 +723,15 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
} else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
} else {
return ts.ScriptSnapshot.fromString('');
return this._ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName: string): ts.ScriptKind {
return ts.ScriptKind.TS;
return this._ts.ScriptKind.TS;
}
getCurrentDirectory(): string {
return '';
......
......@@ -3,7 +3,8 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const ts = require("typescript");
Object.defineProperty(exports, "__esModule", { value: true });
exports.nls = void 0;
const lazy = require("lazy.js");
const event_stream_1 = require("event-stream");
const File = require("vinyl");
......@@ -16,7 +17,7 @@ var CollectStepResult;
CollectStepResult[CollectStepResult["No"] = 2] = "No";
CollectStepResult[CollectStepResult["NoAndRecurse"] = 3] = "NoAndRecurse";
})(CollectStepResult || (CollectStepResult = {}));
function collect(node, fn) {
function collect(ts, node, fn) {
const result = [];
function loop(node) {
const stepResult = fn(node);
......@@ -69,14 +70,16 @@ function nls() {
if (!typescript) {
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
}
nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
}));
return event_stream_1.duplex(input, output);
}
function isImportNode(node) {
exports.nls = nls;
function isImportNode(ts, node) {
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
}
(function (nls_1) {
var _nls;
(function (_nls) {
function fileFrom(file, contents, path = file.path) {
return new File({
contents: Buffer.from(contents),
......@@ -85,17 +88,14 @@ function isImportNode(node) {
path: path
});
}
nls_1.fileFrom = fileFrom;
function mappedPositionFrom(source, lc) {
return { source, line: lc.line + 1, column: lc.character };
}
nls_1.mappedPositionFrom = mappedPositionFrom;
function lcFrom(position) {
return { line: position.line - 1, character: position.column };
}
nls_1.lcFrom = lcFrom;
class SingleFileServiceHost {
constructor(options, filename, contents) {
constructor(ts, options, filename, contents) {
this.options = options;
this.filename = filename;
this.getCompilationSettings = () => this.options;
......@@ -108,20 +108,19 @@ function isImportNode(node) {
this.lib = ts.ScriptSnapshot.fromString('');
}
}
nls_1.SingleFileServiceHost = SingleFileServiceHost;
function isCallExpressionWithinTextSpanCollectStep(textSpan, node) {
function isCallExpressionWithinTextSpanCollectStep(ts, textSpan, node) {
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
return CollectStepResult.No;
}
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
}
function analyze(contents, options = {}) {
function analyze(ts, contents, options = {}) {
const filename = 'file.ts';
const serviceHost = new SingleFileServiceHost(Object.assign(clone(options), { noResolve: true }), filename, contents);
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
const service = ts.createLanguageService(serviceHost);
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
// all imports
const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
// import nls = require('vs/nls');
const importEqualsDeclarations = imports
.filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration)
......@@ -152,7 +151,7 @@ function isImportNode(node) {
.flatten()
.filter(r => !r.isWriteAccess)
// find the deepest call expressions AST nodes that contain those references
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
.map(a => lazy(a).last())
.filter(n => !!n)
.map(n => n)
......@@ -178,7 +177,7 @@ function isImportNode(node) {
// find the deepest call expressions AST nodes that contain those references
const localizeCallExpressions = localizeReferences
.concat(namedLocalizeReferences)
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
.map(a => lazy(a).last())
.filter(n => !!n)
.map(n => n);
......@@ -199,7 +198,6 @@ function isImportNode(node) {
nlsExpressions: nlsExpressions.toArray()
};
}
nls_1.analyze = analyze;
class TextModel {
constructor(contents) {
const regex = /\r\n|\r|\n/g;
......@@ -250,9 +248,8 @@ function isImportNode(node) {
.flatten().toArray().join('');
}
}
nls_1.TextModel = TextModel;
function patchJavascript(patches, contents, moduleId) {
const model = new nls.TextModel(contents);
const model = new TextModel(contents);
// patch the localize calls
lazy(patches).reverse().each(p => model.apply(p));
// patch the 'vs/nls' imports
......@@ -261,7 +258,6 @@ function isImportNode(node) {
model.set(0, patchedFirstLine);
return model.toString();
}
nls_1.patchJavascript = patchJavascript;
function patchSourcemap(patches, rsm, smc) {
const smg = new sm.SourceMapGenerator({
file: rsm.file,
......@@ -297,9 +293,8 @@ function isImportNode(node) {
}
return JSON.parse(smg.toString());
}
nls_1.patchSourcemap = patchSourcemap;
function patch(moduleId, typescript, javascript, sourcemap) {
const { localizeCalls, nlsExpressions } = analyze(typescript);
function patch(ts, moduleId, typescript, javascript, sourcemap) {
const { localizeCalls, nlsExpressions } = analyze(ts, typescript);
if (localizeCalls.length === 0) {
return { javascript, sourcemap };
}
......@@ -332,13 +327,13 @@ function isImportNode(node) {
sourcemap = patchSourcemap(patches, sourcemap, smc);
return { javascript, sourcemap, nlsKeys, nls };
}
nls_1.patch = patch;
function patchFiles(javascriptFile, typescript) {
const ts = require('typescript');
// hack?
const moduleId = javascriptFile.relative
.replace(/\.js$/, '')
.replace(/\\/g, '/');
const { javascript, sourcemap, nlsKeys, nls } = patch(moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap);
const { javascript, sourcemap, nlsKeys, nls } = patch(ts, moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap);
const result = [fileFrom(javascriptFile, javascript)];
result[0].sourceMap = sourcemap;
if (nlsKeys) {
......@@ -349,6 +344,5 @@ function isImportNode(node) {
}
return result;
}
nls_1.patchFiles = patchFiles;
})(nls || (nls = {}));
module.exports = nls;
_nls.patchFiles = patchFiles;
})(_nls || (_nls = {}));
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as ts from 'typescript';
import type * as ts from 'typescript';
import * as lazy from 'lazy.js';
import { duplex, through } from 'event-stream';
import * as File from 'vinyl';
......@@ -21,7 +21,7 @@ enum CollectStepResult {
NoAndRecurse
}
function collect(node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
function collect(ts: typeof import('typescript'), node: ts.Node, fn: (node: ts.Node) => CollectStepResult): ts.Node[] {
const result: ts.Node[] = [];
function loop(node: ts.Node) {
......@@ -65,7 +65,7 @@ define([], [${ wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`;
/**
* Returns a stream containing the patched JavaScript and source maps.
*/
function nls(): NodeJS.ReadWriteStream {
export function nls(): NodeJS.ReadWriteStream {
const input = through();
const output = input.pipe(through(function (f: FileSourceMap) {
if (!f.sourceMap) {
......@@ -87,48 +87,48 @@ function nls(): NodeJS.ReadWriteStream {
return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`));
}
nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
_nls.patchFiles(f, typescript).forEach(f => this.emit('data', f));
}));
return duplex(input, output);
}
function isImportNode(node: ts.Node): boolean {
function isImportNode(ts: typeof import('typescript'), node: ts.Node): boolean {
return node.kind === ts.SyntaxKind.ImportDeclaration || node.kind === ts.SyntaxKind.ImportEqualsDeclaration;
}
module nls {
module _nls {
export interface INlsStringResult {
interface INlsStringResult {
javascript: string;
sourcemap: sm.RawSourceMap;
nls?: string;
nlsKeys?: string;
}
export interface ISpan {
interface ISpan {
start: ts.LineAndCharacter;
end: ts.LineAndCharacter;
}
export interface ILocalizeCall {
interface ILocalizeCall {
keySpan: ISpan;
key: string;
valueSpan: ISpan;
value: string;
}
export interface ILocalizeAnalysisResult {
interface ILocalizeAnalysisResult {
localizeCalls: ILocalizeCall[];
nlsExpressions: ISpan[];
}
export interface IPatch {
interface IPatch {
span: ISpan;
content: string;
}
export function fileFrom(file: File, contents: string, path: string = file.path) {
function fileFrom(file: File, contents: string, path: string = file.path) {
return new File({
contents: Buffer.from(contents),
base: file.base,
......@@ -137,20 +137,20 @@ module nls {
});
}
export function mappedPositionFrom(source: string, lc: ts.LineAndCharacter): sm.MappedPosition {
function mappedPositionFrom(source: string, lc: ts.LineAndCharacter): sm.MappedPosition {
return { source, line: lc.line + 1, column: lc.character };
}
export function lcFrom(position: sm.Position): ts.LineAndCharacter {
function lcFrom(position: sm.Position): ts.LineAndCharacter {
return { line: position.line - 1, character: position.column };
}
export class SingleFileServiceHost implements ts.LanguageServiceHost {
class SingleFileServiceHost implements ts.LanguageServiceHost {
private file: ts.IScriptSnapshot;
private lib: ts.IScriptSnapshot;
constructor(private options: ts.CompilerOptions, private filename: string, contents: string) {
constructor(ts: typeof import('typescript'), private options: ts.CompilerOptions, private filename: string, contents: string) {
this.file = ts.ScriptSnapshot.fromString(contents);
this.lib = ts.ScriptSnapshot.fromString('');
}
......@@ -163,7 +163,7 @@ module nls {
getDefaultLibFileName = () => 'lib.d.ts';
}
function isCallExpressionWithinTextSpanCollectStep(textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
function isCallExpressionWithinTextSpanCollectStep(ts: typeof import('typescript'), textSpan: ts.TextSpan, node: ts.Node): CollectStepResult {
if (!ts.textSpanContainsTextSpan({ start: node.pos, length: node.end - node.pos }, textSpan)) {
return CollectStepResult.No;
}
......@@ -171,14 +171,14 @@ module nls {
return node.kind === ts.SyntaxKind.CallExpression ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse;
}
export function analyze(contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult {
function analyze(ts: typeof import('typescript'), contents: string, options: ts.CompilerOptions = {}): ILocalizeAnalysisResult {
const filename = 'file.ts';
const serviceHost = new SingleFileServiceHost(Object.assign(clone(options), { noResolve: true }), filename, contents);
const serviceHost = new SingleFileServiceHost(ts, Object.assign(clone(options), { noResolve: true }), filename, contents);
const service = ts.createLanguageService(serviceHost);
const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true);
// all imports
const imports = lazy(collect(sourceFile, n => isImportNode(n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse));
// import nls = require('vs/nls');
const importEqualsDeclarations = imports
......@@ -215,7 +215,7 @@ module nls {
.filter(r => !r.isWriteAccess)
// find the deepest call expressions AST nodes that contain those references
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
.map(a => lazy(a).last())
.filter(n => !!n)
.map(n => <ts.CallExpression>n)
......@@ -246,7 +246,7 @@ module nls {
// find the deepest call expressions AST nodes that contain those references
const localizeCallExpressions = localizeReferences
.concat(namedLocalizeReferences)
.map(r => collect(sourceFile, n => isCallExpressionWithinTextSpanCollectStep(r.textSpan, n)))
.map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n)))
.map(a => lazy(a).last())
.filter(n => !!n)
.map(n => <ts.CallExpression>n);
......@@ -270,7 +270,7 @@ module nls {
};
}
export class TextModel {
class TextModel {
private lines: string[];
private lineEndings: string[];
......@@ -336,8 +336,8 @@ module nls {
}
}
export function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string {
const model = new nls.TextModel(contents);
function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string {
const model = new TextModel(contents);
// patch the localize calls
lazy(patches).reverse().each(p => model.apply(p));
......@@ -350,7 +350,7 @@ module nls {
return model.toString();
}
export function patchSourcemap(patches: IPatch[], rsm: sm.RawSourceMap, smc: sm.SourceMapConsumer): sm.RawSourceMap {
function patchSourcemap(patches: IPatch[], rsm: sm.RawSourceMap, smc: sm.SourceMapConsumer): sm.RawSourceMap {
const smg = new sm.SourceMapGenerator({
file: rsm.file,
sourceRoot: rsm.sourceRoot
......@@ -395,8 +395,8 @@ module nls {
return JSON.parse(smg.toString());
}
export function patch(moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
const { localizeCalls, nlsExpressions } = analyze(typescript);
function patch(ts: typeof import('typescript'), moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult {
const { localizeCalls, nlsExpressions } = analyze(ts, typescript);
if (localizeCalls.length === 0) {
return { javascript, sourcemap };
......@@ -438,12 +438,14 @@ module nls {
}
export function patchFiles(javascriptFile: File, typescript: string): File[] {
const ts = <typeof import('typescript')>require('typescript');
// hack?
const moduleId = javascriptFile.relative
.replace(/\.js$/, '')
.replace(/\\/g, '/');
const { javascript, sourcemap, nlsKeys, nls } = patch(
ts,
moduleId,
typescript,
javascriptFile.contents.toString(),
......@@ -464,5 +466,3 @@ module nls {
return result;
}
}
export = nls;
......@@ -8,17 +8,12 @@ exports.minifyTask = exports.optimizeTask = exports.loaderConfig = void 0;
const es = require("event-stream");
const gulp = require("gulp");
const concat = require("gulp-concat");
const minifyCSS = require("gulp-cssnano");
const filter = require("gulp-filter");
const flatmap = require("gulp-flatmap");
const sourcemaps = require("gulp-sourcemaps");
const uglify = require("gulp-uglify");
const composer = require("gulp-uglify/composer");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const path = require("path");
const pump = require("pump");
const terser = require("terser");
const VinylFile = require("vinyl");
const bundle = require("./bundle");
const i18n_1 = require("./i18n");
......@@ -124,6 +119,7 @@ function optimizeTask(opts) {
const out = opts.out;
const fileContentMapper = opts.fileContentMapper || ((contents, _path) => contents);
return function () {
const sourcemaps = require('gulp-sourcemaps');
const bundlesStream = es.through(); // this stream will contain the bundled files
const resourcesStream = es.through(); // this stream will contain the resources
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
......@@ -171,6 +167,8 @@ exports.optimizeTask = optimizeTask;
* to have a file "context" to include our copyright only once per file.
*/
function uglifyWithCopyrights() {
const composer = require('gulp-uglify/composer');
const terser = require('terser');
const preserveComments = (f) => {
return (_node, comment) => {
const text = comment.value;
......@@ -212,6 +210,9 @@ function uglifyWithCopyrights() {
function minifyTask(src, sourceMapBaseUrl) {
const sourceMappingURL = sourceMapBaseUrl ? ((f) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined;
return cb => {
const minifyCSS = require('gulp-cssnano');
const uglify = require('gulp-uglify');
const sourcemaps = require('gulp-sourcemaps');
const jsFilter = filter('**/*.js', { restore: true });
const cssFilter = filter('**/*.css', { restore: true });
pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), uglifyWithCopyrights(), jsFilter.restore, cssFilter, minifyCSS({ reduceIdents: false }), cssFilter.restore, sourcemaps.mapSources((sourcePath) => {
......
......@@ -8,17 +8,12 @@
import * as es from 'event-stream';
import * as gulp from 'gulp';
import * as concat from 'gulp-concat';
import * as minifyCSS from 'gulp-cssnano';
import * as filter from 'gulp-filter';
import * as flatmap from 'gulp-flatmap';
import * as sourcemaps from 'gulp-sourcemaps';
import * as uglify from 'gulp-uglify';
import * as composer from 'gulp-uglify/composer';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
import * as path from 'path';
import * as pump from 'pump';
import * as terser from 'terser';
import * as VinylFile from 'vinyl';
import * as bundle from './bundle';
import { Language, processNlsFiles } from './i18n';
......@@ -184,6 +179,8 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents);
return function () {
const sourcemaps = <typeof import('gulp-sourcemaps')>require('gulp-sourcemaps');
const bundlesStream = es.through(); // this stream will contain the bundled files
const resourcesStream = es.through(); // this stream will contain the resources
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
......@@ -243,6 +240,9 @@ declare class FileWithCopyright extends VinylFile {
* to have a file "context" to include our copyright only once per file.
*/
function uglifyWithCopyrights(): NodeJS.ReadWriteStream {
const composer = <typeof import('gulp-uglify/composer')>require('gulp-uglify/composer');
const terser = <typeof import('terser')>require('terser');
const preserveComments = (f: FileWithCopyright) => {
return (_node: any, comment: { value: string; type: string; }) => {
const text = comment.value;
......@@ -291,6 +291,10 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) =>
const sourceMappingURL = sourceMapBaseUrl ? ((f: any) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined;
return cb => {
const minifyCSS = <typeof import('gulp-cssnano')>require('gulp-cssnano');
const uglify = <typeof import('gulp-uglify')>require('gulp-uglify');
const sourcemaps = <typeof import('gulp-sourcemaps')>require('gulp-sourcemaps');
const jsFilter = filter('**/*.js', { restore: true });
const cssFilter = filter('**/*.css', { restore: true });
......
......@@ -5,7 +5,6 @@
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createESMSourcesAndResources2 = exports.extractEditor = void 0;
const ts = require("typescript");
const fs = require("fs");
const path = require("path");
const tss = require("./treeshaking");
......@@ -29,6 +28,7 @@ function writeFile(filePath, contents) {
}
function extractEditor(options) {
var _a;
const ts = require('typescript');
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
let compilerOptions;
if (tsConfig.extends) {
......@@ -115,6 +115,7 @@ function extractEditor(options) {
}
exports.extractEditor = extractEditor;
function createESMSourcesAndResources2(options) {
const ts = require('typescript');
const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder);
const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder);
const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder);
......
......@@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';
import * as tss from './treeshaking';
......@@ -31,6 +30,8 @@ function writeFile(filePath: string, contents: Buffer | string): void {
}
export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void {
const ts = <typeof import('typescript')>require('typescript');
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
let compilerOptions: { [key: string]: any };
if (tsConfig.extends) {
......@@ -134,6 +135,8 @@ export interface IOptions2 {
}
export function createESMSourcesAndResources2(options: IOptions2): void {
const ts = <typeof import('typescript')>require('typescript');
const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder);
const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder);
const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder);
......
......@@ -8,7 +8,6 @@ exports.submitAllStats = exports.createStatsStream = void 0;
const es = require("event-stream");
const fancyLog = require("fancy-log");
const ansiColors = require("ansi-colors");
const appInsights = require("applicationinsights");
class Entry {
constructor(name, totalCount, totalSize) {
this.name = name;
......@@ -73,6 +72,7 @@ function createStatsStream(group, log) {
}
exports.createStatsStream = createStatsStream;
function submitAllStats(productJson, commit) {
const appInsights = require('applicationinsights');
const sorted = [];
// move entries for single files to the front
_entries.forEach(value => {
......
......@@ -9,7 +9,6 @@ import * as es from 'event-stream';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
import * as File from 'vinyl';
import * as appInsights from 'applicationinsights';
class Entry {
constructor(readonly name: string, public totalCount: number, public totalSize: number) { }
......@@ -75,6 +74,7 @@ export function createStatsStream(group: string, log?: boolean): es.ThroughStrea
}
export function submitAllStats(productJson: any, commit: string): Promise<boolean> {
const appInsights = <typeof import('applicationinsights')>require('applicationinsights');
const sorted: Entry[] = [];
// move entries for single files to the front
......
......@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.shake = exports.toStringShakeLevel = exports.ShakeLevel = void 0;
const fs = require("fs");
const path = require("path");
const ts = require("typescript");
const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts'));
var ShakeLevel;
(function (ShakeLevel) {
......@@ -41,7 +40,8 @@ function printDiagnostics(options, diagnostics) {
}
}
function shake(options) {
const languageService = createTypeScriptLanguageService(options);
const ts = require('typescript');
const languageService = createTypeScriptLanguageService(ts, options);
const program = languageService.getProgram();
const globalDiagnostics = program.getGlobalDiagnostics();
if (globalDiagnostics.length > 0) {
......@@ -58,14 +58,14 @@ function shake(options) {
printDiagnostics(options, semanticDiagnostics);
throw new Error(`Compilation Errors encountered.`);
}
markNodes(languageService, options);
return generateResult(languageService, options.shakeLevel);
markNodes(ts, languageService, options);
return generateResult(ts, languageService, options.shakeLevel);
}
exports.shake = shake;
//#region Discovery, LanguageService & Setup
function createTypeScriptLanguageService(options) {
function createTypeScriptLanguageService(ts, options) {
// Discover referenced files
const FILES = discoverAndReadFiles(options);
const FILES = discoverAndReadFiles(ts, options);
// Add fake usage files
options.inlineEntryPoints.forEach((inlineEntryPoint, index) => {
FILES[`inlineEntryPoint.${index}.ts`] = inlineEntryPoint;
......@@ -76,15 +76,15 @@ function createTypeScriptLanguageService(options) {
FILES[typing] = fs.readFileSync(filePath).toString();
});
// Resolve libs
const RESOLVED_LIBS = processLibFiles(options);
const RESOLVED_LIBS = processLibFiles(ts, options);
const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options;
const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions);
const host = new TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions);
return ts.createLanguageService(host);
}
/**
* Read imports and follow them until all files have been handled
*/
function discoverAndReadFiles(options) {
function discoverAndReadFiles(ts, options) {
const FILES = {};
const in_queue = Object.create(null);
const queue = [];
......@@ -137,7 +137,7 @@ function discoverAndReadFiles(options) {
/**
* Read lib files and follow lib references
*/
function processLibFiles(options) {
function processLibFiles(ts, options) {
const stack = [...options.compilerOptions.lib];
const result = {};
while (stack.length > 0) {
......@@ -161,7 +161,8 @@ function processLibFiles(options) {
* A TypeScript language service host
*/
class TypeScriptLanguageServiceHost {
constructor(libs, files, compilerOptions) {
constructor(ts, libs, files, compilerOptions) {
this._ts = ts;
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
......@@ -183,17 +184,17 @@ class TypeScriptLanguageServiceHost {
}
getScriptSnapshot(fileName) {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
}
else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
}
else {
return ts.ScriptSnapshot.fromString('');
return this._ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName) {
return ts.ScriptKind.TS;
return this._ts.ScriptKind.TS;
}
getCurrentDirectory() {
return '';
......@@ -240,7 +241,7 @@ function nodeOrChildIsBlack(node) {
}
return false;
}
function markNodes(languageService, options) {
function markNodes(ts, languageService, options) {
const program = languageService.getProgram();
if (!program) {
throw new Error('Could not get program from language service');
......@@ -342,7 +343,7 @@ function markNodes(languageService, options) {
if (!referenceSourceFile) {
continue;
}
const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false);
const referenceNode = getTokenAtPosition(ts, referenceSourceFile, reference.textSpan.start, false, false);
if (ts.isMethodDeclaration(referenceNode.parent)
|| ts.isPropertyDeclaration(referenceNode.parent)
|| ts.isGetAccessor(referenceNode.parent)
......@@ -408,7 +409,7 @@ function markNodes(languageService, options) {
}
const nodeSourceFile = node.getSourceFile();
const loop = (node) => {
const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node);
const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node);
if (symbolImportNode) {
setColor(symbolImportNode, 2 /* Black */);
}
......@@ -420,7 +421,7 @@ function markNodes(languageService, options) {
// (they can be the declaration of a module import)
continue;
}
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
enqueue_black(declaration.name);
for (let j = 0; j < declaration.members.length; j++) {
const member = declaration.members[j];
......@@ -484,7 +485,7 @@ function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) {
}
return false;
}
function generateResult(languageService, shakeLevel) {
function generateResult(ts, languageService, shakeLevel) {
const program = languageService.getProgram();
if (!program) {
throw new Error('Could not get program from language service');
......@@ -614,11 +615,11 @@ function generateResult(languageService, shakeLevel) {
}
//#endregion
//#region Utils
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration) {
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration) {
if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
for (const heritageClause of declaration.heritageClauses) {
for (const type of heritageClause.types) {
const symbol = findSymbolFromHeritageType(checker, type);
const symbol = findSymbolFromHeritageType(ts, checker, type);
if (symbol) {
const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
......@@ -630,22 +631,22 @@ function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker,
}
return false;
}
function findSymbolFromHeritageType(checker, type) {
function findSymbolFromHeritageType(ts, checker, type) {
if (ts.isExpressionWithTypeArguments(type)) {
return findSymbolFromHeritageType(checker, type.expression);
return findSymbolFromHeritageType(ts, checker, type.expression);
}
if (ts.isIdentifier(type)) {
return getRealNodeSymbol(checker, type)[0];
return getRealNodeSymbol(ts, checker, type)[0];
}
if (ts.isPropertyAccessExpression(type)) {
return findSymbolFromHeritageType(checker, type.name);
return findSymbolFromHeritageType(ts, checker, type.name);
}
return null;
}
/**
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
*/
function getRealNodeSymbol(checker, node) {
function getRealNodeSymbol(ts, checker, node) {
const getPropertySymbolsFromContextualType = ts.getPropertySymbolsFromContextualType;
const getContainingObjectLiteralElement = ts.getContainingObjectLiteralElement;
const getNameFromPropertyName = ts.getNameFromPropertyName;
......@@ -758,7 +759,7 @@ function getRealNodeSymbol(checker, node) {
return [null, null];
}
/** Get the token whose text contains the position */
function getTokenAtPosition(sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) {
function getTokenAtPosition(ts, sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) {
let current = sourceFile;
outer: while (true) {
// find the child that contains 'position'
......
......@@ -7,7 +7,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import type * as ts from 'typescript';
const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts'));
......@@ -82,7 +82,8 @@ function printDiagnostics(options: ITreeShakingOptions, diagnostics: ReadonlyArr
}
export function shake(options: ITreeShakingOptions): ITreeShakingResult {
const languageService = createTypeScriptLanguageService(options);
const ts = <typeof import('typescript')>require('typescript');
const languageService = createTypeScriptLanguageService(ts, options);
const program = languageService.getProgram()!;
const globalDiagnostics = program.getGlobalDiagnostics();
......@@ -103,15 +104,15 @@ export function shake(options: ITreeShakingOptions): ITreeShakingResult {
throw new Error(`Compilation Errors encountered.`);
}
markNodes(languageService, options);
markNodes(ts, languageService, options);
return generateResult(languageService, options.shakeLevel);
return generateResult(ts, languageService, options.shakeLevel);
}
//#region Discovery, LanguageService & Setup
function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.LanguageService {
function createTypeScriptLanguageService(ts: typeof import('typescript'), options: ITreeShakingOptions): ts.LanguageService {
// Discover referenced files
const FILES = discoverAndReadFiles(options);
const FILES = discoverAndReadFiles(ts, options);
// Add fake usage files
options.inlineEntryPoints.forEach((inlineEntryPoint, index) => {
......@@ -125,18 +126,18 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu
});
// Resolve libs
const RESOLVED_LIBS = processLibFiles(options);
const RESOLVED_LIBS = processLibFiles(ts, options);
const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options;
const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions);
const host = new TypeScriptLanguageServiceHost(ts, RESOLVED_LIBS, FILES, compilerOptions);
return ts.createLanguageService(host);
}
/**
* Read imports and follow them until all files have been handled
*/
function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap {
function discoverAndReadFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): IFileMap {
const FILES: IFileMap = {};
const in_queue: { [module: string]: boolean; } = Object.create(null);
......@@ -199,7 +200,7 @@ function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap {
/**
* Read lib files and follow lib references
*/
function processLibFiles(options: ITreeShakingOptions): ILibMap {
function processLibFiles(ts: typeof import('typescript'), options: ITreeShakingOptions): ILibMap {
const stack: string[] = [...options.compilerOptions.lib];
const result: ILibMap = {};
......@@ -232,11 +233,13 @@ interface IFileMap { [fileName: string]: string; }
*/
class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
private readonly _ts: typeof import('typescript');
private readonly _libs: ILibMap;
private readonly _files: IFileMap;
private readonly _compilerOptions: ts.CompilerOptions;
constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
constructor(ts: typeof import('typescript'), libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) {
this._ts = ts;
this._libs = libs;
this._files = files;
this._compilerOptions = compilerOptions;
......@@ -262,15 +265,15 @@ class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost {
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
if (this._files.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._files[fileName]);
return this._ts.ScriptSnapshot.fromString(this._files[fileName]);
} else if (this._libs.hasOwnProperty(fileName)) {
return ts.ScriptSnapshot.fromString(this._libs[fileName]);
return this._ts.ScriptSnapshot.fromString(this._libs[fileName]);
} else {
return ts.ScriptSnapshot.fromString('');
return this._ts.ScriptSnapshot.fromString('');
}
}
getScriptKind(_fileName: string): ts.ScriptKind {
return ts.ScriptKind.TS;
return this._ts.ScriptKind.TS;
}
getCurrentDirectory(): string {
return '';
......@@ -320,7 +323,7 @@ function nodeOrChildIsBlack(node: ts.Node): boolean {
return false;
}
function markNodes(languageService: ts.LanguageService, options: ITreeShakingOptions) {
function markNodes(ts: typeof import('typescript'), languageService: ts.LanguageService, options: ITreeShakingOptions) {
const program = languageService.getProgram();
if (!program) {
throw new Error('Could not get program from language service');
......@@ -446,7 +449,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
continue;
}
const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false);
const referenceNode = getTokenAtPosition(ts, referenceSourceFile, reference.textSpan.start, false, false);
if (
ts.isMethodDeclaration(referenceNode.parent)
|| ts.isPropertyDeclaration(referenceNode.parent)
......@@ -522,7 +525,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
const nodeSourceFile = node.getSourceFile();
const loop = (node: ts.Node) => {
const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node);
const [symbol, symbolImportNode] = getRealNodeSymbol(ts, checker, node);
if (symbolImportNode) {
setColor(symbolImportNode, NodeColor.Black);
}
......@@ -536,7 +539,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt
continue;
}
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program, checker, declaration)) {
if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) && !isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts, program, checker, declaration)) {
enqueue_black(declaration.name!);
for (let j = 0; j < declaration.members.length; j++) {
......@@ -607,7 +610,7 @@ function nodeIsInItsOwnDeclaration(nodeSourceFile: ts.SourceFile, node: ts.Node,
return false;
}
function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult {
function generateResult(ts: typeof import('typescript'), languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult {
const program = languageService.getProgram();
if (!program) {
throw new Error('Could not get program from language service');
......@@ -752,11 +755,11 @@ function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLe
//#region Utils
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(ts: typeof import('typescript'), program: ts.Program, checker: ts.TypeChecker, declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
if (!program.isSourceFileDefaultLibrary(declaration.getSourceFile()) && declaration.heritageClauses) {
for (const heritageClause of declaration.heritageClauses) {
for (const type of heritageClause.types) {
const symbol = findSymbolFromHeritageType(checker, type);
const symbol = findSymbolFromHeritageType(ts, checker, type);
if (symbol) {
const decl = symbol.valueDeclaration || (symbol.declarations && symbol.declarations[0]);
if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
......@@ -769,15 +772,15 @@ function isLocalCodeExtendingOrInheritingFromDefaultLibSymbol(program: ts.Progra
return false;
}
function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null {
function findSymbolFromHeritageType(ts: typeof import('typescript'), checker: ts.TypeChecker, type: ts.ExpressionWithTypeArguments | ts.Expression | ts.PrivateIdentifier): ts.Symbol | null {
if (ts.isExpressionWithTypeArguments(type)) {
return findSymbolFromHeritageType(checker, type.expression);
return findSymbolFromHeritageType(ts, checker, type.expression);
}
if (ts.isIdentifier(type)) {
return getRealNodeSymbol(checker, type)[0];
return getRealNodeSymbol(ts, checker, type)[0];
}
if (ts.isPropertyAccessExpression(type)) {
return findSymbolFromHeritageType(checker, type.name);
return findSymbolFromHeritageType(ts, checker, type.name);
}
return null;
}
......@@ -785,7 +788,7 @@ function findSymbolFromHeritageType(checker: ts.TypeChecker, type: ts.Expression
/**
* Returns the node's symbol and the `import` node (if the symbol resolved from a different module)
*/
function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | null, ts.Declaration | null] {
function getRealNodeSymbol(ts: typeof import('typescript'), checker: ts.TypeChecker, node: ts.Node): [ts.Symbol | null, ts.Declaration | null] {
// Use some TypeScript internals to avoid code duplication
type ObjectLiteralElementWithName = ts.ObjectLiteralElement & { name: ts.PropertyName; parent: ts.ObjectLiteralExpression | ts.JsxAttributes };
......@@ -913,7 +916,7 @@ function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol |
}
/** Get the token whose text contains the position */
function getTokenAtPosition(sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node {
function getTokenAtPosition(ts: typeof import('typescript'), sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node {
let current: ts.Node = sourceFile;
outer: while (true) {
// find the child that contains 'position'
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册