Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

gulp-tsb

Package Overview
Dependencies
Maintainers
1
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gulp-tsb - npm Package Compare versions

Comparing version 1.4.1 to 1.4.2

4

package.json
{
"name": "gulp-tsb",
"version": "1.4.1",
"version": "1.4.2",
"author": "Johannes Rieken <johannes.rieken@gmail.com>",
"description": "An incremental TypeScript builder for gulp",
"description": "A gulp plugin for very fast TypeScript compilation.",
"main": "./src/index",

@@ -7,0 +7,0 @@ "repository": {

@@ -6,3 +6,5 @@ gulp-tsb

A gulp plugin for incremental TypeScript compilation. This plugin uses reverse dependencies of import-require statements and only works well with external modules (amd, commonjs).
A gulp plugin for **very fast** TypeScript compilation. This plugin works by
* keeping a compiler alive to improve speed (at the cost of memory)
* always recompiling the smallest set of files possible

@@ -9,0 +11,0 @@ ## Usage

@@ -6,35 +6,20 @@ /// <reference path="../typings/node/node.d.ts" />

var fs = require('fs');
var vinyl = require('vinyl');
var path = require('path');
var crypto = require('crypto');
var utils = require('./utils');
var gutil = require('gulp-util');
var ts = require('./typescript/typescriptServices');
var Vinyl = require('vinyl');
function createTypeScriptBuilder(config) {
var host = new LanguageServiceHost(createCompilationSettings(config)), languageService = ts.createLanguageService(host, ts.createDocumentRegistry()), oldErrors = Object.create(null), headUsed = process.memoryUsage().heapUsed;
function createCompilationSettings(config) {
// language version
if (!config['target']) {
config['target'] = ts.ScriptTarget.ES3;
var settings = createCompilationSettings(config), host = new LanguageServiceHost(settings), service = ts.createLanguageService(host, ts.createDocumentRegistry()), lastBuildVersion = Object.create(null), lastDtsHash = Object.create(null), userWantsDeclarations = settings.declaration, oldErrors = Object.create(null), headUsed = process.memoryUsage().heapUsed;
// always emit declaraction files
host.getCompilationSettings().declaration = true;
if (!host.getCompilationSettings().noLib) {
var defaultLib = host.getDefaultLibFilename();
host.addScriptSnapshot(defaultLib, new ScriptSnapshot(fs.readFileSync(defaultLib), fs.statSync(defaultLib)));
}
function log(topic, message) {
if (config.verbose) {
gutil.log(gutil.colors.cyan(topic), message);
}
else if (/ES3/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES3;
}
else if (/ES5/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES5;
}
else if (/ES6/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES6;
}
// module generation
if (/commonjs/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.CommonJS;
}
else if (/amd/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.AMD;
}
var result = config;
// if(config.verbose) {
// gutil.log(JSON.stringify(result));
// }
return result;
}

@@ -56,63 +41,164 @@ function printDiagnostic(diag, onError) {

}
if (!host.getCompilationSettings().noLib) {
var defaultLib = host.getDefaultLibFilename();
host.addScriptSnapshot(defaultLib, new ScriptSnapshot(fs.readFileSync(defaultLib), fs.statSync(defaultLib)));
function file(file) {
var snapshot = new ScriptSnapshot(file.contents, file.stat);
host.addScriptSnapshot(file.path, snapshot);
}
return {
build: function (out, onError) {
var task = host.createSnapshotAndAdviseValidation(), newErrors = Object.create(null), t1 = Date.now();
// (1) check for syntax errors
task.changed.forEach(function (fileName) {
if (config.verbose) {
gutil.log(gutil.colors.cyan('[check syntax]'), fileName);
function build(out, onError) {
var filenames = host.getScriptFileNames(), newErrors = Object.create(null), checkedThisRound = Object.create(null), filesWithShapeChanges = [], t1 = Date.now();
function shouldCheck(filename) {
if (checkedThisRound[filename]) {
return false;
}
else {
checkedThisRound[filename] = true;
return true;
}
}
for (var i = 0, len = filenames.length; i < len; i++) {
var filename = filenames[i], version = host.getScriptVersion(filename);
if (lastBuildVersion[filename] === version) {
continue;
}
var output = service.getEmitOutput(filename), checkSyntax = false, checkSemantics = false, dtsHash = undefined;
// emit output has fast as possible
output.outputFiles.forEach(function (file) {
if (/\.d\.ts$/.test(file.name)) {
dtsHash = crypto.createHash('md5').update(file.text).digest('base64');
if (!userWantsDeclarations) {
// don't leak .d.ts files if users don't want them
return;
}
}
delete oldErrors[fileName];
languageService.getSyntacticDiagnostics(fileName).forEach(function (diag) {
printDiagnostic(diag, onError);
utils.collections.lookupOrInsert(newErrors, fileName, []).push(diag);
});
log('[emit output]', file.name);
out(new Vinyl({
path: file.name,
contents: new Buffer(file.text)
}));
});
// (2) emit
task.changed.forEach(function (fileName) {
if (config.verbose) {
gutil.log(gutil.colors.cyan('[emit code]'), fileName);
switch (output.emitOutputStatus) {
case ts.EmitReturnStatus.Succeeded:
break;
case ts.EmitReturnStatus.AllOutputGenerationSkipped:
log('[syntax errors]', filename);
checkSyntax = true;
break;
case ts.EmitReturnStatus.JSGeneratedWithSemanticErrors:
case ts.EmitReturnStatus.DeclarationGenerationSkipped:
log('[semantic errors]', filename);
checkSemantics = true;
break;
case ts.EmitReturnStatus.EmitErrorsEncountered:
case ts.EmitReturnStatus.CompilerOptionsErrors:
default:
// don't really know what to do with these
checkSyntax = true;
checkSemantics = true;
break;
}
// print and store syntax and semantic errors
delete oldErrors[filename];
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []);
if (checkSyntax) {
diagnostics.push.apply(diagnostics, service.getSyntacticDiagnostics(filename));
}
if (checkSemantics) {
diagnostics.push.apply(diagnostics, service.getSemanticDiagnostics(filename));
}
diagnostics.forEach(function (diag) {
printDiagnostic(diag, onError);
});
// dts comparing
if (dtsHash && lastDtsHash[filename] !== dtsHash) {
lastDtsHash[filename] = dtsHash;
if (service.getSourceFile(filename).externalModuleIndicator) {
filesWithShapeChanges.push(filename);
}
var output = languageService.getEmitOutput(fileName);
output.outputFiles.forEach(function (file) {
out(new vinyl({
path: file.name,
contents: new Buffer(file.text)
}));
else {
filesWithShapeChanges.unshift(filename);
}
}
lastBuildVersion[filename] = version;
checkedThisRound[filename] = true;
}
if (filesWithShapeChanges.length === 0) {
}
else if (!service.getSourceFile(filesWithShapeChanges[0]).externalModuleIndicator) {
// at least one internal module changes which means that
// we have to type check all others
log('[shape changes]', 'internal module changed → FULL check required');
host.getScriptFileNames().forEach(function (filename) {
if (!shouldCheck(filename)) {
return;
}
log('[semantic check*]', filename);
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []);
service.getSemanticDiagnostics(filename).forEach(function (diag) {
diagnostics.push(diag);
printDiagnostic(diag, onError);
});
});
// (3) semantic check
task.changedOrDependencyChanged.forEach(function (fileName) {
if (config.verbose) {
gutil.log(gutil.colors.cyan('[check semantics]'), fileName);
}
else {
// reverse dependencies
log('[shape changes]', 'external module changed → check REVERSE dependencies');
var needsSemanticCheck = [];
filesWithShapeChanges.forEach(function (filename) { return host.collectDependents(filename, needsSemanticCheck); });
while (needsSemanticCheck.length) {
var filename = needsSemanticCheck.pop();
if (!shouldCheck(filename)) {
continue;
}
delete oldErrors[fileName];
languageService.getSemanticDiagnostics(fileName).forEach(function (diag) {
log('[semantic check*]', filename);
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []), hasSemanticErrors = false;
service.getSemanticDiagnostics(filename).forEach(function (diag) {
diagnostics.push(diag);
printDiagnostic(diag, onError);
utils.collections.lookupOrInsert(newErrors, fileName, []).push(diag);
hasSemanticErrors = true;
});
});
// (4) dump old errors
utils.collections.forEach(oldErrors, function (entry) {
entry.value.forEach(function (diag) { return printDiagnostic(diag, onError); });
newErrors[entry.key] = entry.value;
});
oldErrors = newErrors;
if (config.verbose) {
var headNow = process.memoryUsage().heapUsed, MB = 1024 * 1024;
gutil.log('[tsb]', 'time:', gutil.colors.yellow((Date.now() - t1) + 'ms'), 'mem:', gutil.colors.cyan(Math.ceil(headNow / MB) + 'MB'), gutil.colors.bgCyan('Δ' + Math.ceil((headNow - headUsed) / MB)));
headUsed = headNow;
if (!hasSemanticErrors) {
host.collectDependents(filename, needsSemanticCheck);
}
}
},
file: function (file) {
var snapshot = new ScriptSnapshot(file.contents, file.stat);
host.addScriptSnapshot(file.path, snapshot);
}
// (4) dump old errors
utils.collections.forEach(oldErrors, function (entry) {
entry.value.forEach(function (diag) { return printDiagnostic(diag, onError); });
newErrors[entry.key] = entry.value;
});
oldErrors = newErrors;
if (config.verbose) {
var headNow = process.memoryUsage().heapUsed, MB = 1024 * 1024;
gutil.log('[tsb]', 'time:', gutil.colors.yellow((Date.now() - t1) + 'ms'), 'mem:', gutil.colors.cyan(Math.ceil(headNow / MB) + 'MB'), gutil.colors.bgCyan('Δ' + Math.ceil((headNow - headUsed) / MB)));
headUsed = headNow;
}
}
return {
file: file,
build: build
};
}
exports.createTypeScriptBuilder = createTypeScriptBuilder;
function createCompilationSettings(config) {
// language version
if (!config['target']) {
config['target'] = ts.ScriptTarget.ES3;
}
else if (/ES3/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES3;
}
else if (/ES5/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES5;
}
else if (/ES6/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES6;
}
// module generation
if (/commonjs/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.CommonJS;
}
else if (/amd/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.AMD;
}
return config;
}
var ScriptSnapshot = (function () {

@@ -141,82 +227,2 @@ function ScriptSnapshot(buffer, stat) {

})();
var ProjectSnapshot = (function () {
function ProjectSnapshot(host) {
this._captureState(host);
}
ProjectSnapshot.prototype._captureState = function (host) {
var _this = this;
this._dependencies = new utils.graph.Graph(function (s) { return s; });
this._versions = Object.create(null);
host.getScriptFileNames().forEach(function (fileName) {
fileName = path.normalize(fileName);
// (1) paths and versions
_this._versions[fileName] = host.getScriptVersion(fileName);
// (2) dependency graph for *.ts files
if (!fileName.match(/.*\.d\.ts$/)) {
var snapshot = host.getScriptSnapshot(fileName), info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true);
info.referencedFiles.forEach(function (ref) {
var resolvedPath = path.resolve(path.dirname(fileName), ref.filename), normalizedPath = path.normalize(resolvedPath);
_this._dependencies.inertEdge(fileName, normalizedPath);
// console.log(fileName + ' -> ' + normalizedPath);
});
info.importedFiles.forEach(function (ref) {
var stopDirname = path.normalize(host.getCurrentDirectory()), dirname = fileName;
while (dirname.indexOf(stopDirname) === 0) {
dirname = path.dirname(dirname);
var resolvedPath = path.resolve(dirname, ref.filename), normalizedPath = path.normalize(resolvedPath);
// try .ts
if (['.ts', '.d.ts'].some(function (suffix) {
var candidate = normalizedPath + suffix;
if (host.getScriptSnapshot(candidate)) {
_this._dependencies.inertEdge(fileName, candidate);
// console.log(fileName + ' -> ' + candidate);
return true;
}
return false;
})) {
break;
}
;
}
});
}
});
};
ProjectSnapshot.prototype.whatToValidate = function (host) {
var _this = this;
var changed = [], added = [], removed = [];
// compile file delta (changed, added, removed)
var idx = Object.create(null);
host.getScriptFileNames().forEach(function (fileName) { return idx[fileName] = host.getScriptVersion(fileName); });
utils.collections.forEach(this._versions, function (entry) {
var versionNow = idx[entry.key];
if (typeof versionNow === 'undefined') {
// removed
removed.push(entry.key);
}
else if (typeof versionNow === 'string' && versionNow !== entry.value) {
// changed
changed.push(entry.key);
}
delete idx[entry.key];
});
// cos we removed all we saw earlier
added = Object.keys(idx);
// what to validate?
var syntax = changed.concat(added), semantic = [];
if (removed.length > 0 || added.length > 0) {
semantic = host.getScriptFileNames();
}
else {
// validate every change file *plus* the files
// that depend on the changed file
changed.forEach(function (fileName) { return _this._dependencies.traverse(fileName, false, function (data) { return semantic.push(data); }); });
}
return {
changed: syntax,
changedOrDependencyChanged: semantic
};
};
return ProjectSnapshot;
})();
var LanguageServiceHost = (function () {

@@ -227,2 +233,4 @@ function LanguageServiceHost(settings) {

this._defaultLib = path.normalize(path.join(__dirname, 'typescript', 'lib.d.ts'));
this._dependencies = new utils.graph.Graph(function (s) { return s; });
this._dependenciesRecomputeList = [];
}

@@ -238,17 +246,24 @@ LanguageServiceHost.prototype.log = function (s) {

};
LanguageServiceHost.prototype.getScriptVersion = function (fileName) {
fileName = path.normalize(fileName);
return this._snapshots[fileName].getVersion();
LanguageServiceHost.prototype.getScriptVersion = function (filename) {
filename = path.normalize(filename);
return this._snapshots[filename].getVersion();
};
LanguageServiceHost.prototype.getScriptIsOpen = function (fileName) {
LanguageServiceHost.prototype.getScriptIsOpen = function (filename) {
return false;
};
LanguageServiceHost.prototype.getScriptSnapshot = function (fileName) {
fileName = path.normalize(fileName);
return this._snapshots[fileName];
LanguageServiceHost.prototype.getScriptSnapshot = function (filename) {
filename = path.normalize(filename);
return this._snapshots[filename];
};
LanguageServiceHost.prototype.addScriptSnapshot = function (fileName, snapshot) {
fileName = path.normalize(fileName);
var old = this._snapshots[fileName];
this._snapshots[fileName] = snapshot;
LanguageServiceHost.prototype.addScriptSnapshot = function (filename, snapshot) {
filename = path.normalize(filename);
var old = this._snapshots[filename];
if (!old || old.getVersion() !== snapshot.getVersion()) {
this._dependenciesRecomputeList.push(filename);
var node = this._dependencies.lookup(filename);
if (node) {
node.outgoing = Object.create(null);
}
}
this._snapshots[filename] = snapshot;
return old;

@@ -268,17 +283,43 @@ };

};
LanguageServiceHost.prototype.createSnapshotAndAdviseValidation = function () {
var ret;
if (!this._projectSnapshot) {
ret = {
changed: this.getScriptFileNames(),
changedOrDependencyChanged: this.getScriptFileNames()
};
// ---- dependency management
LanguageServiceHost.prototype.collectDependents = function (filename, target) {
while (this._dependenciesRecomputeList.length) {
this._processFile(this._dependenciesRecomputeList.pop());
}
else {
ret = this._projectSnapshot.whatToValidate(this);
filename = path.normalize(filename);
var node = this._dependencies.lookup(filename);
if (node) {
utils.collections.forEach(node.incoming, function (entry) { return target.push(entry.key); });
}
this._projectSnapshot = new ProjectSnapshot(this);
return ret;
};
LanguageServiceHost.prototype._processFile = function (filename) {
var _this = this;
if (filename.match(/.*\.d\.ts$/)) {
return;
}
filename = path.normalize(filename);
var snapshot = this.getScriptSnapshot(filename), info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true);
// (1) ///-references
info.referencedFiles.forEach(function (ref) {
var resolvedPath = path.resolve(path.dirname(filename), ref.filename), normalizedPath = path.normalize(resolvedPath);
_this._dependencies.inertEdge(filename, normalizedPath);
});
// (2) import-require statements
info.importedFiles.forEach(function (ref) {
var stopDirname = path.normalize(_this.getCurrentDirectory()), dirname = filename, found = false;
while (!found && dirname.indexOf(stopDirname) === 0) {
dirname = path.dirname(dirname);
var resolvedPath = path.resolve(dirname, ref.filename), normalizedPath = path.normalize(resolvedPath);
if (_this.getScriptSnapshot(normalizedPath + '.ts')) {
_this._dependencies.inertEdge(filename, normalizedPath + '.ts');
found = true;
}
else if (_this.getScriptSnapshot(normalizedPath + '.d.ts')) {
_this._dependencies.inertEdge(filename, normalizedPath + '.d.ts');
found = true;
}
}
});
};
return LanguageServiceHost;
})();

@@ -8,7 +8,8 @@ /// <reference path="../typings/node/node.d.ts" />

import fs = require('fs');
import vinyl = require('vinyl');
import path = require('path');
import crypto = require('crypto');
import utils = require('./utils');
import gutil = require('gulp-util');
import ts = require('./typescript/typescriptServices');
import Vinyl = require('vinyl');

@@ -21,59 +22,44 @@ export interface IConfiguration {

export interface IFileDelta {
added?:vinyl[];
changed?:vinyl[];
deleted?:vinyl[];
}
export interface ITypeScriptBuilder {
build(out: (file:vinyl)=>void, onError:(err:any)=>void): void;
file(file: vinyl): void;
build(out: (file: Vinyl) => void, onError: (err: any) => void): void;
file(file: Vinyl): void;
}
export function createTypeScriptBuilder(config:IConfiguration): ITypeScriptBuilder {
var host = new LanguageServiceHost(createCompilationSettings(config)),
languageService = ts.createLanguageService(host, ts.createDocumentRegistry()),
oldErrors: { [path:string]: ts.Diagnostic[] } = Object.create(null),
export function createTypeScriptBuilder(config: IConfiguration): ITypeScriptBuilder {
var settings = createCompilationSettings(config),
host = new LanguageServiceHost(settings),
service = ts.createLanguageService(host, ts.createDocumentRegistry()),
lastBuildVersion: { [path: string]: string } = Object.create(null),
lastDtsHash: { [path: string]: string } = Object.create(null),
userWantsDeclarations = settings.declaration,
oldErrors: { [path: string]: ts.Diagnostic[] } = Object.create(null),
headUsed = process.memoryUsage().heapUsed;
// always emit declaraction files
host.getCompilationSettings().declaration = true;
function createCompilationSettings(config:IConfiguration): ts.CompilerOptions {
// language version
if(!config['target']) {
config['target'] = ts.ScriptTarget.ES3;
} else if(/ES3/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES3;
} else if(/ES5/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES5;
} else if(/ES6/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES6;
if (!host.getCompilationSettings().noLib) {
var defaultLib = host.getDefaultLibFilename();
host.addScriptSnapshot(defaultLib, new ScriptSnapshot(fs.readFileSync(defaultLib), fs.statSync(defaultLib)));
}
function log(topic: string, message: string): void {
if(config.verbose) {
gutil.log(gutil.colors.cyan(topic), message);
}
// module generation
if (/commonjs/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.CommonJS;
} else if (/amd/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.AMD;
}
var result = <ts.CompilerOptions> config;
// if(config.verbose) {
// gutil.log(JSON.stringify(result));
// }
return result;
}
function printDiagnostic(diag:ts.Diagnostic, onError:(err:any)=>void):void {
function printDiagnostic(diag: ts.Diagnostic, onError: (err: any) => void): void {
var lineAndCh = diag.file.getLineAndCharacterFromPosition(diag.start),
message:string;
if(!config.json) {
message = utils.strings.format('{0}({1},{2}): {3}',
diag.file.filename,
lineAndCh.line,
lineAndCh.character,
message: string;
if (!config.json) {
message = utils.strings.format('{0}({1},{2}): {3}',
diag.file.filename,
lineAndCh.line,
lineAndCh.character,
diag.messageText);
} else {

@@ -87,93 +73,211 @@ message = JSON.stringify({

}
onError(message);
}
if(!host.getCompilationSettings().noLib) {
var defaultLib = host.getDefaultLibFilename();
host.addScriptSnapshot(defaultLib, new ScriptSnapshot(fs.readFileSync(defaultLib), fs.statSync(defaultLib)));
function file(file: Vinyl): void {
var snapshot = new ScriptSnapshot(file.contents, file.stat);
host.addScriptSnapshot(file.path, snapshot);
}
return {
build: (out: (file:vinyl)=>void, onError: (err: any) => void) => {
var task = host.createSnapshotAndAdviseValidation(),
newErrors: { [path: string]: ts.Diagnostic[] } = Object.create(null),
t1 = Date.now();
function build(out: (file: Vinyl) => void, onError: (err: any) => void): void {
var filenames = host.getScriptFileNames(),
newErrors: { [path: string]: ts.Diagnostic[] } = Object.create(null),
checkedThisRound: { [path: string]: boolean } = Object.create(null),
filesWithShapeChanges: string[] = [],
t1 = Date.now();
function shouldCheck(filename: string): boolean {
if (checkedThisRound[filename]) {
return false;
} else {
checkedThisRound[filename] = true;
return true;
}
}
for (var i = 0, len = filenames.length; i < len; i++) {
var filename = filenames[i],
version = host.getScriptVersion(filename);
if (lastBuildVersion[filename] === version) {
// unchanged since the last time
continue;
}
var output = service.getEmitOutput(filename),
checkSyntax = false,
checkSemantics = false,
dtsHash: string = undefined;
// (1) check for syntax errors
task.changed.forEach(fileName => {
if(config.verbose) {
gutil.log(gutil.colors.cyan('[check syntax]'), fileName);
// emit output has fast as possible
output.outputFiles.forEach(file => {
if (/\.d\.ts$/.test(file.name)) {
dtsHash = crypto.createHash('md5')
.update(file.text)
.digest('base64');
if (!userWantsDeclarations) {
// don't leak .d.ts files if users don't want them
return;
}
}
delete oldErrors[fileName];
log('[emit output]', file.name);
languageService.getSyntacticDiagnostics(fileName).forEach(diag => {
printDiagnostic(diag, onError);
utils.collections.lookupOrInsert(newErrors, fileName, []).push(diag);
});
out(new Vinyl({
path: file.name,
contents: new Buffer(file.text)
}));
});
// (2) emit
task.changed.forEach(fileName => {
if(config.verbose) {
gutil.log(gutil.colors.cyan('[emit code]'), fileName);
// make use of emit status
switch (output.emitOutputStatus) {
case ts.EmitReturnStatus.Succeeded:
// good
break;
case ts.EmitReturnStatus.AllOutputGenerationSkipped:
log('[syntax errors]', filename);
checkSyntax = true;
break;
case ts.EmitReturnStatus.JSGeneratedWithSemanticErrors:
case ts.EmitReturnStatus.DeclarationGenerationSkipped:
log('[semantic errors]', filename);
checkSemantics = true;
break;
case ts.EmitReturnStatus.EmitErrorsEncountered:
case ts.EmitReturnStatus.CompilerOptionsErrors:
default:
// don't really know what to do with these
checkSyntax = true;
checkSemantics = true;
break;
}
// print and store syntax and semantic errors
delete oldErrors[filename];
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []);
if (checkSyntax) {
diagnostics.push.apply(diagnostics, service.getSyntacticDiagnostics(filename));
}
if (checkSemantics) {
diagnostics.push.apply(diagnostics, service.getSemanticDiagnostics(filename));
}
diagnostics.forEach(diag => {
printDiagnostic(diag, onError);
});
// dts comparing
if (dtsHash && lastDtsHash[filename] !== dtsHash) {
lastDtsHash[filename] = dtsHash;
if (service.getSourceFile(filename).externalModuleIndicator) {
filesWithShapeChanges.push(filename);
} else {
filesWithShapeChanges.unshift(filename);
}
var output = languageService.getEmitOutput(fileName);
output.outputFiles.forEach(file => {
out(new vinyl({
path: file.name,
contents: new Buffer(file.text)
}));
}
lastBuildVersion[filename] = version;
checkedThisRound[filename] = true;
}
if (filesWithShapeChanges.length === 0) {
// nothing to do here
} else if (!service.getSourceFile(filesWithShapeChanges[0]).externalModuleIndicator) {
// at least one internal module changes which means that
// we have to type check all others
log('[shape changes]', 'internal module changed → FULL check required');
host.getScriptFileNames().forEach(filename => {
if(!shouldCheck(filename)) {
return;
}
log('[semantic check*]', filename);
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []);
service.getSemanticDiagnostics(filename).forEach(diag => {
diagnostics.push(diag);
printDiagnostic(diag, onError);
});
});
// (3) semantic check
task.changedOrDependencyChanged.forEach(fileName => {
} else {
// reverse dependencies
log('[shape changes]', 'external module changed → check REVERSE dependencies');
var needsSemanticCheck: string[] = [];
filesWithShapeChanges.forEach(filename => host.collectDependents(filename, needsSemanticCheck));
while(needsSemanticCheck.length) {
var filename = needsSemanticCheck.pop();
if(!shouldCheck(filename)) {
continue;
}
log('[semantic check*]', filename);
var diagnostics = utils.collections.lookupOrInsert(newErrors, filename, []),
hasSemanticErrors = false;
if(config.verbose) {
gutil.log(gutil.colors.cyan('[check semantics]'), fileName);
}
delete oldErrors[fileName];
languageService.getSemanticDiagnostics(fileName).forEach(diag => {
service.getSemanticDiagnostics(filename).forEach(diag => {
diagnostics.push(diag);
printDiagnostic(diag, onError);
utils.collections.lookupOrInsert(newErrors, fileName, []).push(diag);
hasSemanticErrors = true;
});
});
// (4) dump old errors
utils.collections.forEach(oldErrors, entry => {
entry.value.forEach(diag => printDiagnostic(diag, onError));
newErrors[entry.key] = entry.value;
});
oldErrors = newErrors;
if(config.verbose) {
var headNow = process.memoryUsage().heapUsed,
MB = 1024 * 1024;
gutil.log(
'[tsb]',
'time:',
gutil.colors.yellow((Date.now() - t1) + 'ms'),
'mem:',
gutil.colors.cyan(Math.ceil(headNow / MB) + 'MB'),
gutil.colors.bgCyan('Δ' + Math.ceil((headNow - headUsed) / MB)));
headUsed = headNow;
if(!hasSemanticErrors) {
host.collectDependents(filename, needsSemanticCheck);
}
}
},
}
file: (file) => {
var snapshot = new ScriptSnapshot(file.contents, file.stat);
host.addScriptSnapshot(file.path, snapshot);
// (4) dump old errors
utils.collections.forEach(oldErrors, entry => {
entry.value.forEach(diag => printDiagnostic(diag, onError));
newErrors[entry.key] = entry.value;
});
oldErrors = newErrors;
if (config.verbose) {
var headNow = process.memoryUsage().heapUsed,
MB = 1024 * 1024;
gutil.log(
'[tsb]',
'time:',
gutil.colors.yellow((Date.now() - t1) + 'ms'),
'mem:',
gutil.colors.cyan(Math.ceil(headNow / MB) + 'MB'),
gutil.colors.bgCyan('Δ' + Math.ceil((headNow - headUsed) / MB)));
headUsed = headNow;
}
}
return {
file,
build
};
}
function createCompilationSettings(config: IConfiguration): ts.CompilerOptions {
// language version
if (!config['target']) {
config['target'] = ts.ScriptTarget.ES3;
} else if (/ES3/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES3;
} else if (/ES5/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES5;
} else if (/ES6/i.test(String(config['target']))) {
config['target'] = ts.ScriptTarget.ES6;
}
// module generation
if (/commonjs/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.CommonJS;
} else if (/amd/i.test(String(config['module']))) {
config['module'] = ts.ModuleKind.AMD;
}
return <ts.CompilerOptions> config;
}
class ScriptSnapshot implements ts.IScriptSnapshot {

@@ -184,4 +288,4 @@

private _mtime: Date;
constructor(buffer:Buffer, stat:fs.Stats) {
constructor(buffer: Buffer, stat: fs.Stats) {
this._text = buffer.toString();

@@ -191,7 +295,7 @@ this._lineStarts = ts.computeLineStarts(this._text);

}
public getVersion():string {
public getVersion(): string {
return this._mtime.toUTCString();
}
public getText(start: number, end: number): string {

@@ -208,4 +312,4 @@ return this._text.substring(start, end);

}
public getChangeRange(oldSnapshot:ts.IScriptSnapshot):ts.TextChangeRange {
public getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange {
return null;

@@ -215,117 +319,2 @@ }

interface IValidationTask {
changed: string[];
changedOrDependencyChanged: string[];
}
class ProjectSnapshot {
private _dependencies: utils.graph.Graph<string>;
private _versions: { [path: string]: string; };
constructor(host:ts.LanguageServiceHost) {
this._captureState(host);
}
private _captureState(host:ts.LanguageServiceHost):void {
this._dependencies = new utils.graph.Graph<string>(s => s);
this._versions = Object.create(null);
host.getScriptFileNames().forEach(fileName => {
fileName = path.normalize(fileName);
// (1) paths and versions
this._versions[fileName] = host.getScriptVersion(fileName);
// (2) dependency graph for *.ts files
if(!fileName.match(/.*\.d\.ts$/)) {
var snapshot = host.getScriptSnapshot(fileName),
info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true);
info.referencedFiles.forEach(ref => {
var resolvedPath = path.resolve(path.dirname(fileName), ref.filename),
normalizedPath = path.normalize(resolvedPath);
this._dependencies.inertEdge(fileName, normalizedPath);
// console.log(fileName + ' -> ' + normalizedPath);
});
info.importedFiles.forEach(ref => {
var stopDirname = path.normalize(host.getCurrentDirectory()),
dirname = fileName;
while(dirname.indexOf(stopDirname) === 0) {
dirname = path.dirname(dirname);
var resolvedPath = path.resolve(dirname, ref.filename),
normalizedPath = path.normalize(resolvedPath);
// try .ts
if (['.ts', '.d.ts'].some(suffix => {
var candidate = normalizedPath + suffix;
if (host.getScriptSnapshot(candidate)) {
this._dependencies.inertEdge(fileName, candidate);
// console.log(fileName + ' -> ' + candidate);
return true;
}
return false;
})) {
// found, ugly code!
break;
};
}
});
}
});
}
public whatToValidate(host: ts.LanguageServiceHost):IValidationTask {
var changed: string[] = [],
added: string[] = [],
removed: string[] = [];
// compile file delta (changed, added, removed)
var idx: { [path: string]: string } = Object.create(null);
host.getScriptFileNames().forEach(fileName => idx[fileName] = host.getScriptVersion(fileName));
utils.collections.forEach(this._versions, entry => {
var versionNow = idx[entry.key];
if(typeof versionNow === 'undefined') {
// removed
removed.push(entry.key);
} else if(typeof versionNow === 'string' && versionNow !== entry.value) {
// changed
changed.push(entry.key);
}
delete idx[entry.key];
});
// cos we removed all we saw earlier
added = Object.keys(idx);
// what to validate?
var syntax = changed.concat(added),
semantic: string[] = [];
if(removed.length > 0 || added.length > 0) {
semantic = host.getScriptFileNames();
} else {
// validate every change file *plus* the files
// that depend on the changed file
changed.forEach(fileName => this._dependencies.traverse(fileName, false, data => semantic.push(data)));
}
return {
changed: syntax,
changedOrDependencyChanged: semantic
};
}
}
class LanguageServiceHost implements ts.LanguageServiceHost {

@@ -336,55 +325,65 @@

private _defaultLib: string;
private _projectSnapshot: ProjectSnapshot;
constructor(settings:ts.CompilerOptions) {
private _dependencies: utils.graph.Graph<string>;
private _dependenciesRecomputeList: string[];
constructor(settings: ts.CompilerOptions) {
this._settings = settings;
this._snapshots = Object.create(null);
this._defaultLib = path.normalize(path.join(__dirname, 'typescript', 'lib.d.ts'));
this._dependencies = new utils.graph.Graph<string>(s => s);
this._dependenciesRecomputeList = [];
}
log(s: string): void {
// nothing
}
getCompilationSettings(): ts.CompilerOptions {
return this._settings;
}
getScriptFileNames(): string[] {
return Object.keys(this._snapshots);
}
getScriptVersion(fileName: string): string {
fileName = path.normalize(fileName);
return this._snapshots[fileName].getVersion();
getScriptVersion(filename: string): string {
filename = path.normalize(filename);
return this._snapshots[filename].getVersion();
}
getScriptIsOpen(fileName: string): boolean {
getScriptIsOpen(filename: string): boolean {
return false;
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
fileName = path.normalize(fileName);
return this._snapshots[fileName];
getScriptSnapshot(filename: string): ts.IScriptSnapshot {
filename = path.normalize(filename);
return this._snapshots[filename];
}
addScriptSnapshot(fileName:string, snapshot:ScriptSnapshot):ScriptSnapshot {
fileName = path.normalize(fileName);
var old = this._snapshots[fileName];
this._snapshots[fileName] = snapshot;
addScriptSnapshot(filename: string, snapshot: ScriptSnapshot): ScriptSnapshot {
filename = path.normalize(filename);
var old = this._snapshots[filename];
if(!old || old.getVersion() !== snapshot.getVersion()) {
this._dependenciesRecomputeList.push(filename);
var node = this._dependencies.lookup(filename);
if(node) {
node.outgoing = Object.create(null);
}
}
this._snapshots[filename] = snapshot;
return old;
}
getLocalizedDiagnosticMessages(): any {
return null;
}
getCancellationToken(): ts.CancellationToken {
return { isCancellationRequested: () => false };
}
getCurrentDirectory(): string {
return process.cwd();
}
getDefaultLibFilename(): string {

@@ -394,15 +393,53 @@ return this._defaultLib;

createSnapshotAndAdviseValidation():IValidationTask {
var ret: IValidationTask;
if(!this._projectSnapshot) {
ret = {
changed: this.getScriptFileNames(),
changedOrDependencyChanged: this.getScriptFileNames()
};
} else {
ret = this._projectSnapshot.whatToValidate(this);
// ---- dependency management
collectDependents(filename: string, target: string[]): void {
while (this._dependenciesRecomputeList.length) {
this._processFile(this._dependenciesRecomputeList.pop());
}
this._projectSnapshot = new ProjectSnapshot(this);
return ret;
filename = path.normalize(filename);
var node = this._dependencies.lookup(filename);
if (node) {
utils.collections.forEach(node.incoming, entry => target.push(entry.key));
}
}
}
_processFile(filename: string): void {
if(filename.match(/.*\.d\.ts$/)) {
return;
}
filename = path.normalize(filename);
var snapshot = this.getScriptSnapshot(filename),
info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true);
// (1) ///-references
info.referencedFiles.forEach(ref => {
var resolvedPath = path.resolve(path.dirname(filename), ref.filename),
normalizedPath = path.normalize(resolvedPath);
this._dependencies.inertEdge(filename, normalizedPath);
});
// (2) import-require statements
info.importedFiles.forEach(ref => {
var stopDirname = path.normalize(this.getCurrentDirectory()),
dirname = filename,
found = false;
while (!found && dirname.indexOf(stopDirname) === 0) {
dirname = path.dirname(dirname);
var resolvedPath = path.resolve(dirname, ref.filename),
normalizedPath = path.normalize(resolvedPath);
if(this.getScriptSnapshot(normalizedPath + '.ts')) {
this._dependencies.inertEdge(filename, normalizedPath + '.ts');
found = true;
} else if(this.getScriptSnapshot(normalizedPath + '.d.ts')) {
this._dependencies.inertEdge(filename, normalizedPath + '.d.ts');
found = true;
}
}
});
}
}

@@ -5,5 +5,5 @@

var hasOwnProperty = Object.prototype.hasOwnProperty;
export function lookup<T>(collection:{[keys:string]:T}, key:string):T {
if(hasOwnProperty.call(collection, key)) {
export function lookup<T>(collection: { [keys: string]: T }, key: string): T {
if (hasOwnProperty.call(collection, key)) {
return collection[key];

@@ -13,9 +13,9 @@ }

}
export function insert<T>(collection:{[keys:string]:T}, key:string, value:T):void {
export function insert<T>(collection: { [keys: string]: T }, key: string, value: T): void {
collection[key] = value;
}
export function lookupOrInsert<T>(collection:{[keys:string]:T}, key:string, value:T):T {
if(hasOwnProperty.call(collection, key)) {
export function lookupOrInsert<T>(collection: { [keys: string]: T }, key: string, value: T): T {
if (hasOwnProperty.call(collection, key)) {
return collection[key];

@@ -27,8 +27,8 @@ } else {

}
export function forEach<T>(collection:{[keys:string]:T}, callback:(entry:{key:string; value:T;})=>void):void {
export function forEach<T>(collection: { [keys: string]: T }, callback: (entry: { key: string; value: T; }) => void): void {
for (var key in collection) {
if (hasOwnProperty.call(collection, key)) {
callback({
key: key,
key: key,
value: collection[key]

@@ -39,4 +39,4 @@ });

}
export function contains(collection:{[keys:string]:any}, key:string):boolean {
export function contains(collection: { [keys: string]: any }, key: string): boolean {
return hasOwnProperty.call(collection, key);

@@ -52,7 +52,7 @@ }

export var empty = '';
export var eolUnix = '\r\n';
export function format(value:string, ...rest:any[]):string {
return value.replace(/({\d+})/g, function(match) {
export function format(value: string, ...rest: any[]): string {
return value.replace(/({\d+})/g, function (match) {
var index = match.substring(1, match.length - 1);

@@ -65,28 +65,28 @@ return rest[index] || match;

export module graph {
export interface Node<T> {
data:T;
incoming:{[key:string]:Node<T>};
outgoing:{[key:string]:Node<T>};
data: T;
incoming: { [key: string]: Node<T> };
outgoing: { [key: string]: Node<T> };
}
export function newNode<T>(data:T):Node<T> {
export function newNode<T>(data: T): Node<T> {
return {
data: data,
incoming: {},
data: data,
incoming: {},
outgoing: {}
};
}
export class Graph<T> {
private _nodes:{[key:string]:Node<T>} = {};
constructor(private _hashFn:(element:T)=>string) {
private _nodes: { [key: string]: Node<T> } = {};
constructor(private _hashFn: (element: T) => string) {
// empty
}
public traverse(start:T, inwards:boolean, callback:(data:T)=>void):void {
public traverse(start: T, inwards: boolean, callback: (data: T) => void): void {
var startNode = this.lookup(start);
if(!startNode) {
if (!startNode) {
return;

@@ -96,6 +96,6 @@ }

}
private _traverse(node:Node<T>, inwards:boolean, seen:{[key:string]:boolean}, callback:(data:T)=>void):void {
private _traverse(node: Node<T>, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void {
var key = this._hashFn(node.data);
if(collections.contains(seen, key)) {
if (collections.contains(seen, key)) {
return;

@@ -106,17 +106,17 @@ }

var nodes = inwards ? node.outgoing : node.incoming;
collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback));
collections.forEach(nodes,(entry) => this._traverse(entry.value, inwards, seen, callback));
}
public inertEdge(from:T, to:T):void {
var fromNode = this.lookupOrInsertNode(from),
public inertEdge(from: T, to: T): void {
var fromNode = this.lookupOrInsertNode(from),
toNode = this.lookupOrInsertNode(to);
fromNode.outgoing[this._hashFn(to)] = toNode;
toNode.incoming[this._hashFn(from)] = fromNode;
}
public removeNode(data:T):void {
public removeNode(data: T): void {
var key = this._hashFn(data);
delete this._nodes[key];
collections.forEach(this._nodes, (entry) => {
collections.forEach(this._nodes,(entry) => {
delete entry.value.outgoing[key];

@@ -126,20 +126,20 @@ delete entry.value.incoming[key];

}
public lookupOrInsertNode(data:T):Node<T> {
public lookupOrInsertNode(data: T): Node<T> {
var key = this._hashFn(data),
node = collections.lookup(this._nodes, key);
if(!node) {
if (!node) {
node = newNode(data);
this._nodes[key] = node;
}
return node;
}
public lookup(data:T):Node<T> {
public lookup(data: T): Node<T> {
return collections.lookup(this._nodes, this._hashFn(data));
}
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc