- options

This commit is contained in:
Eugene Zolenko 2017-02-10 18:24:28 -07:00
parent c485cd3aa6
commit 37fe6093b7
8 changed files with 397 additions and 131 deletions

View File

@ -1,3 +1,4 @@
{
"typescript.tsdk": "./node_modules/typescript/lib"
"editor.tabSize": 4,
"editor.useTabStops": true
}

View File

@ -8,6 +8,7 @@ var graph = require('graphlib');
var hash = require('object-hash');
var rollupPluginutils = require('rollup-pluginutils');
var path = require('path');
var colors = require('colors/safe');
const __assign = Object.assign || function (target) {
for (var source, i = 1; i < arguments.length; i++) {
@ -21,6 +22,42 @@ const __assign = Object.assign || function (target) {
return target;
};
var VerbosityLevel;
(function (VerbosityLevel) {
VerbosityLevel[VerbosityLevel["Error"] = 0] = "Error";
VerbosityLevel[VerbosityLevel["Warning"] = 1] = "Warning";
VerbosityLevel[VerbosityLevel["Info"] = 2] = "Info";
VerbosityLevel[VerbosityLevel["Debug"] = 3] = "Debug";
})(VerbosityLevel || (VerbosityLevel = {}));
var ConsoleContext = (function () {
function ConsoleContext(verbosity, prefix) {
if (prefix === void 0) { prefix = ""; }
this.verbosity = verbosity;
this.prefix = prefix;
}
ConsoleContext.prototype.warn = function (message) {
if (this.verbosity < VerbosityLevel.Warning)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.error = function (message) {
if (this.verbosity < VerbosityLevel.Error)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.info = function (message) {
if (this.verbosity < VerbosityLevel.Info)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.debug = function (message) {
if (this.verbosity < VerbosityLevel.Debug)
return;
console.log("" + this.prefix + message);
};
return ConsoleContext;
}());
var LanguageServiceHost = (function () {
function LanguageServiceHost(parsedConfig) {
this.parsedConfig = parsedConfig;
@ -59,7 +96,15 @@ var LanguageServiceHost = (function () {
return LanguageServiceHost;
}());
/**
* Saves data in new cache folder or reads it from old one.
* Avoids perpetually growing cache and situations when things need to consider changed and then reverted data to be changed.
*/
var RollingCache = (function () {
/**
* @param cacheRoot: root folder for the cache
* @param checkNewCache: whether to also look in new cache when reading from cache
*/
function RollingCache(cacheRoot, checkNewCache) {
this.cacheRoot = cacheRoot;
this.checkNewCache = checkNewCache;
@ -67,16 +112,25 @@ var RollingCache = (function () {
this.newCacheRoot = this.cacheRoot + "/cache_";
fs.emptyDirSync(this.newCacheRoot);
}
/**
* @returns true if name exist in old cache (or either old of new cache if checkNewCache is true)
*/
RollingCache.prototype.exists = function (name) {
if (this.checkNewCache && fs.existsSync(this.newCacheRoot + "/" + name))
return true;
return fs.existsSync(this.oldCacheRoot + "/" + name);
};
/**
* @returns true if old cache contains all names and nothing more
*/
RollingCache.prototype.match = function (names) {
if (!fs.existsSync(this.oldCacheRoot))
return false;
return names.length === 0; // empty folder matches
return _.isEqual(fs.readdirSync(this.oldCacheRoot).sort(), names.sort());
};
/**
* @returns data for name, must exist in old cache (or either old of new cache if checkNewCache is true)
*/
RollingCache.prototype.read = function (name) {
if (this.checkNewCache && fs.existsSync(this.newCacheRoot + "/" + name))
return fs.readJsonSync(this.newCacheRoot + "/" + name, "utf8");
@ -85,54 +139,72 @@ var RollingCache = (function () {
RollingCache.prototype.write = function (name, data) {
if (data === undefined)
return;
fs.writeJson(this.newCacheRoot + "/" + name, data, { encoding: "utf8" }, function () { });
if (this.checkNewCache)
fs.writeJsonSync(this.newCacheRoot + "/" + name, data);
else
fs.writeJson(this.newCacheRoot + "/" + name, data, { encoding: "utf8" }, function () { });
};
RollingCache.prototype.touch = function (name) {
fs.ensureFile(this.newCacheRoot + "/" + name, function () { });
if (this.checkNewCache)
fs.ensureFileSync(this.newCacheRoot + "/" + name);
else
fs.ensureFile(this.newCacheRoot + "/" + name, function () { });
};
/**
* clears old cache and moves new in its place
*/
RollingCache.prototype.roll = function () {
var _this = this;
fs.remove(this.oldCacheRoot, function () {
fs.move(_this.newCacheRoot, _this.oldCacheRoot, function () { });
fs.move(_this.newCacheRoot, _this.oldCacheRoot, function () { });
});
};
return RollingCache;
}());
var Cache = (function () {
function Cache(host, cache, options, rootFilenames) {
function Cache(host, cache, options, rootFilenames, context) {
var _this = this;
this.host = host;
this.options = options;
this.context = context;
this.cacheVersion = "1";
this.typesDirty = false;
this.ambientTypesDirty = false;
this.cacheDir = cache + "/" + hash.sha1({ version: this.cacheVersion, rootFilenames: rootFilenames, options: this.options });
this.codeCache = new RollingCache(this.cacheDir + "/code", true);
this.typesCache = new RollingCache(this.cacheDir + "/types", false);
this.diagnosticsCache = new RollingCache(this.cacheDir + "/diagnostics", false);
this.dependencyTree = new graph.Graph({ directed: true });
this.dependencyTree.setDefaultNodeLabel(function (_node) { return { dirty: false }; });
this.types = _.filter(rootFilenames, function (file) { return _.endsWith(file, ".d.ts"); })
this.ambientTypes = _.filter(rootFilenames, function (file) { return _.endsWith(file, ".d.ts"); })
.map(function (id) { return { id: id, snapshot: _this.host.getScriptSnapshot(id) }; });
}
Cache.prototype.clean = function () {
this.context.info("cleaning cache: " + this.cacheDir);
fs.emptyDirSync(this.cacheDir);
};
Cache.prototype.walkTree = function (cb) {
var acyclic = graph.alg.isAcyclic(this.dependencyTree);
if (acyclic) {
_.each(graph.alg.topsort(this.dependencyTree), function (id) { return cb(id); });
return;
}
this.context.info("import tree has cycles");
_.each(this.dependencyTree.nodes(), function (id) { return cb(id); });
};
Cache.prototype.setDependency = function (importee, importer) {
// importee -> importer
this.context.debug(importee + " -> " + importer);
this.dependencyTree.setEdge(importer, importee);
};
Cache.prototype.compileDone = function () {
var _this = this;
var typeNames = _.filter(this.types, function (snaphot) { return snaphot.snapshot !== undefined; })
var typeNames = _.filter(this.ambientTypes, function (snaphot) { return snaphot.snapshot !== undefined; })
.map(function (snaphot) { return _this.makeName(snaphot.id, snaphot.snapshot); });
// types dirty if any d.ts changed, added or removed
this.typesDirty = !this.typesCache.match(typeNames);
this.ambientTypesDirty = !this.typesCache.match(typeNames);
if (this.ambientTypesDirty)
this.context.info("ambient types changed, redoing all diagnostics");
_.each(typeNames, function (name) { return _this.typesCache.touch(name); });
};
Cache.prototype.diagnosticsDone = function () {
@ -143,12 +215,13 @@ var Cache = (function () {
Cache.prototype.getCompiled = function (id, snapshot, transform) {
var name = this.makeName(id, snapshot);
if (!this.codeCache.exists(name) || this.isDirty(id, snapshot, false)) {
console.log("compile cache miss: " + id);
this.context.debug("fresh transpile for: " + id);
var data_1 = transform();
this.codeCache.write(name, data_1);
this.markAsDirty(id, snapshot);
return data_1;
}
this.context.debug("old transpile for: " + id);
var data = this.codeCache.read(name);
this.codeCache.write(name, data);
return data;
@ -156,17 +229,19 @@ var Cache = (function () {
Cache.prototype.getDiagnostics = function (id, snapshot, check) {
var name = this.makeName(id, snapshot);
if (!this.diagnosticsCache.exists(name) || this.isDirty(id, snapshot, true)) {
console.log("diag cache miss: " + id);
this.context.debug("fresh diagnostics for: " + id);
var data_2 = this.convert(check());
this.diagnosticsCache.write(name, data_2);
this.markAsDirty(id, snapshot);
return data_2;
}
this.context.debug("old diagnostics for: " + id);
var data = this.diagnosticsCache.read(name);
this.diagnosticsCache.write(name, data);
return data;
};
Cache.prototype.markAsDirty = function (id, _snapshot) {
this.context.debug("changed: " + id);
this.dependencyTree.setNode(id, { dirty: true });
};
// returns true if node or any of its imports or any of global types changed
@ -177,7 +252,7 @@ var Cache = (function () {
return false;
if (!checkImports || label.dirty)
return label.dirty;
if (this.typesDirty)
if (this.ambientTypesDirty)
return true;
var dependencies = graph.alg.dijkstra(this.dependencyTree, id);
return _.some(dependencies, function (dependency, node) {
@ -186,7 +261,7 @@ var Cache = (function () {
var l = _this.dependencyTree.node(node);
var dirty = l === undefined ? true : l.dirty;
if (dirty)
console.log(("dirty: " + id + " -> " + node).gray);
_this.context.debug("import changed: " + id + " -> " + node);
return dirty;
});
};
@ -197,11 +272,11 @@ var Cache = (function () {
Cache.prototype.convert = function (data) {
return _.map(data, function (diagnostic) {
var entry = {
flatMessage: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n").yellow,
flatMessage: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
};
if (diagnostic.file) {
var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character;
entry.fileLine = (diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + ")").white;
entry.fileLine = diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + ")";
}
return entry;
});
@ -223,17 +298,15 @@ function getOptionsOverrides() {
// MIT Licenced
function findFile(cwd, filename) {
var fp = cwd ? (cwd + "/" + filename) : filename;
if (fs.existsSync(fp)) {
if (fs.existsSync(fp))
return fp;
}
var segs = cwd.split(path.sep);
var len = segs.length;
while (len--) {
cwd = segs.slice(0, len).join("/");
fp = cwd + "/" + filename;
if (fs.existsSync(fp)) {
if (fs.existsSync(fp))
return fp;
}
}
return null;
}
@ -258,22 +331,33 @@ function parseTsConfig() {
var configParseResult = ts.parseJsonConfigFileContent(result.config, ts.sys, path.dirname(fileName), getOptionsOverrides(), fileName);
return configParseResult;
}
function printDiagnostics(diagnostics) {
function printDiagnostics(context, diagnostics) {
_.each(diagnostics, function (diagnostic) {
if (diagnostic.fileLine)
console.log(diagnostic.fileLine + ": " + diagnostic.flatMessage);
context.warn(diagnostic.fileLine + ": " + colors.yellow(diagnostic.flatMessage));
else
console.log(diagnostic.flatMessage);
context.warn(colors.yellow(diagnostic.flatMessage));
});
}
function typescript(options) {
options = __assign({}, options);
var filter$$1 = rollupPluginutils.createFilter(options.include || ["*.ts+(|x)", "**/*.ts+(|x)"], options.exclude || ["*.d.ts", "**/*.d.ts"]);
_.defaults(options, {
check: true,
verbose: VerbosityLevel.Info,
clean: false,
cacheRoot: process.cwd() + "/.rts2_cache",
include: ["*.ts+(|x)", "**/*.ts+(|x)"],
exclude: ["*.d.ts", "**/*.d.ts"],
});
var filter$$1 = rollupPluginutils.createFilter(options.include, options.exclude);
var parsedConfig = parseTsConfig();
var servicesHost = new LanguageServiceHost(parsedConfig);
var services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
var cache = new Cache(servicesHost, process.cwd() + "/.rts2_cache", parsedConfig.options, parsedConfig.fileNames);
var context = new ConsoleContext(options.verbose, "");
var cache = new Cache(servicesHost, options.cacheRoot, parsedConfig.options, parsedConfig.fileNames, context);
if (options.clean)
cache.clean();
return {
resolveId: function (importee, importer) {
if (importee === TSLIB)
@ -304,7 +388,7 @@ function typescript(options) {
var result = cache.getCompiled(id, snapshot, function () {
var output = services.getEmitOutput(id);
if (output.emitSkipped)
_this.error({ message: "failed to transpile " + id });
_this.error({ message: colors.red("failed to transpile " + id) });
var transpiled = _.find(output.outputFiles, function (entry) { return _.endsWith(entry.name, ".js"); });
var map$$1 = _.find(output.outputFiles, function (entry) { return _.endsWith(entry.name, ".map"); });
return {
@ -316,20 +400,22 @@ function typescript(options) {
},
outro: function () {
cache.compileDone();
cache.walkTree(function (id) {
var snapshot = servicesHost.getScriptSnapshot(id);
if (!snapshot) {
console.log("failed lo load snapshot for " + id);
return;
}
var diagnostics = cache.getDiagnostics(id, snapshot, function () {
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
if (options.check) {
cache.walkTree(function (id) {
var snapshot = servicesHost.getScriptSnapshot(id);
if (!snapshot) {
context.error(colors.red("failed lo load snapshot for " + id));
return;
}
var diagnostics = cache.getDiagnostics(id, snapshot, function () {
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
});
printDiagnostics(context, diagnostics);
});
printDiagnostics(diagnostics);
});
}
cache.diagnosticsDone();
},
};

View File

@ -1,9 +1,9 @@
/* eslint-disable */
import { emptyDirSync, ensureFile, existsSync, move, readFileSync, readJsonSync, readdirSync, remove, writeJson } from 'fs-extra';
import { emptyDirSync, ensureFile, ensureFileSync, existsSync, move, readFileSync, readJsonSync, readdirSync, remove, writeJson, writeJsonSync } from 'fs-extra';
import * as fs from 'fs-extra';
import { ModuleKind, ScriptSnapshot, createDocumentRegistry, createLanguageService, flattenDiagnosticMessageText, getDefaultLibFilePath, nodeModuleNameResolver, parseConfigFileTextToJson, parseJsonConfigFileContent, sys } from 'typescript';
import * as ts from 'typescript';
import { each, endsWith, filter, find, has, isEqual, map, some } from 'lodash';
import { defaults, each, endsWith, filter, find, has, isEqual, map, some } from 'lodash';
import * as _ from 'lodash';
import { Graph, alg } from 'graphlib';
import * as graph from 'graphlib';
@ -12,6 +12,8 @@ import * as hash from 'object-hash';
import { createFilter } from 'rollup-pluginutils';
import { dirname, sep } from 'path';
import * as path from 'path';
import { red, yellow } from 'colors/safe';
import * as colors from 'colors/safe';
const __assign = Object.assign || function (target) {
for (var source, i = 1; i < arguments.length; i++) {
@ -25,6 +27,42 @@ const __assign = Object.assign || function (target) {
return target;
};
var VerbosityLevel;
(function (VerbosityLevel) {
VerbosityLevel[VerbosityLevel["Error"] = 0] = "Error";
VerbosityLevel[VerbosityLevel["Warning"] = 1] = "Warning";
VerbosityLevel[VerbosityLevel["Info"] = 2] = "Info";
VerbosityLevel[VerbosityLevel["Debug"] = 3] = "Debug";
})(VerbosityLevel || (VerbosityLevel = {}));
var ConsoleContext = (function () {
function ConsoleContext(verbosity, prefix) {
if (prefix === void 0) { prefix = ""; }
this.verbosity = verbosity;
this.prefix = prefix;
}
ConsoleContext.prototype.warn = function (message) {
if (this.verbosity < VerbosityLevel.Warning)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.error = function (message) {
if (this.verbosity < VerbosityLevel.Error)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.info = function (message) {
if (this.verbosity < VerbosityLevel.Info)
return;
console.log("" + this.prefix + message);
};
ConsoleContext.prototype.debug = function (message) {
if (this.verbosity < VerbosityLevel.Debug)
return;
console.log("" + this.prefix + message);
};
return ConsoleContext;
}());
var LanguageServiceHost = (function () {
function LanguageServiceHost(parsedConfig) {
this.parsedConfig = parsedConfig;
@ -63,7 +101,15 @@ var LanguageServiceHost = (function () {
return LanguageServiceHost;
}());
/**
* Saves data in new cache folder or reads it from old one.
* Avoids perpetually growing cache and situations when things need to consider changed and then reverted data to be changed.
*/
var RollingCache = (function () {
/**
* @param cacheRoot: root folder for the cache
* @param checkNewCache: whether to also look in new cache when reading from cache
*/
function RollingCache(cacheRoot, checkNewCache) {
this.cacheRoot = cacheRoot;
this.checkNewCache = checkNewCache;
@ -71,16 +117,25 @@ var RollingCache = (function () {
this.newCacheRoot = this.cacheRoot + "/cache_";
emptyDirSync(this.newCacheRoot);
}
/**
* @returns true if name exist in old cache (or either old of new cache if checkNewCache is true)
*/
RollingCache.prototype.exists = function (name) {
if (this.checkNewCache && existsSync(this.newCacheRoot + "/" + name))
return true;
return existsSync(this.oldCacheRoot + "/" + name);
};
/**
* @returns true if old cache contains all names and nothing more
*/
RollingCache.prototype.match = function (names) {
if (!existsSync(this.oldCacheRoot))
return false;
return names.length === 0; // empty folder matches
return isEqual(readdirSync(this.oldCacheRoot).sort(), names.sort());
};
/**
* @returns data for name, must exist in old cache (or either old of new cache if checkNewCache is true)
*/
RollingCache.prototype.read = function (name) {
if (this.checkNewCache && existsSync(this.newCacheRoot + "/" + name))
return readJsonSync(this.newCacheRoot + "/" + name, "utf8");
@ -89,54 +144,72 @@ var RollingCache = (function () {
RollingCache.prototype.write = function (name, data) {
if (data === undefined)
return;
writeJson(this.newCacheRoot + "/" + name, data, { encoding: "utf8" }, function () { });
if (this.checkNewCache)
writeJsonSync(this.newCacheRoot + "/" + name, data);
else
writeJson(this.newCacheRoot + "/" + name, data, { encoding: "utf8" }, function () { });
};
RollingCache.prototype.touch = function (name) {
ensureFile(this.newCacheRoot + "/" + name, function () { });
if (this.checkNewCache)
ensureFileSync(this.newCacheRoot + "/" + name);
else
ensureFile(this.newCacheRoot + "/" + name, function () { });
};
/**
* clears old cache and moves new in its place
*/
RollingCache.prototype.roll = function () {
var _this = this;
remove(this.oldCacheRoot, function () {
move(_this.newCacheRoot, _this.oldCacheRoot, function () { });
move(_this.newCacheRoot, _this.oldCacheRoot, function () { });
});
};
return RollingCache;
}());
var Cache = (function () {
function Cache(host, cache, options, rootFilenames) {
function Cache(host, cache, options, rootFilenames, context) {
var _this = this;
this.host = host;
this.options = options;
this.context = context;
this.cacheVersion = "1";
this.typesDirty = false;
this.ambientTypesDirty = false;
this.cacheDir = cache + "/" + sha1({ version: this.cacheVersion, rootFilenames: rootFilenames, options: this.options });
this.codeCache = new RollingCache(this.cacheDir + "/code", true);
this.typesCache = new RollingCache(this.cacheDir + "/types", false);
this.diagnosticsCache = new RollingCache(this.cacheDir + "/diagnostics", false);
this.dependencyTree = new Graph({ directed: true });
this.dependencyTree.setDefaultNodeLabel(function (_node) { return { dirty: false }; });
this.types = filter(rootFilenames, function (file) { return endsWith(file, ".d.ts"); })
this.ambientTypes = filter(rootFilenames, function (file) { return endsWith(file, ".d.ts"); })
.map(function (id) { return { id: id, snapshot: _this.host.getScriptSnapshot(id) }; });
}
Cache.prototype.clean = function () {
this.context.info("cleaning cache: " + this.cacheDir);
emptyDirSync(this.cacheDir);
};
Cache.prototype.walkTree = function (cb) {
var acyclic = alg.isAcyclic(this.dependencyTree);
if (acyclic) {
each(alg.topsort(this.dependencyTree), function (id) { return cb(id); });
return;
}
this.context.info("import tree has cycles");
each(this.dependencyTree.nodes(), function (id) { return cb(id); });
};
Cache.prototype.setDependency = function (importee, importer) {
// importee -> importer
this.context.debug(importee + " -> " + importer);
this.dependencyTree.setEdge(importer, importee);
};
Cache.prototype.compileDone = function () {
var _this = this;
var typeNames = filter(this.types, function (snaphot) { return snaphot.snapshot !== undefined; })
var typeNames = filter(this.ambientTypes, function (snaphot) { return snaphot.snapshot !== undefined; })
.map(function (snaphot) { return _this.makeName(snaphot.id, snaphot.snapshot); });
// types dirty if any d.ts changed, added or removed
this.typesDirty = !this.typesCache.match(typeNames);
this.ambientTypesDirty = !this.typesCache.match(typeNames);
if (this.ambientTypesDirty)
this.context.info("ambient types changed, redoing all diagnostics");
each(typeNames, function (name) { return _this.typesCache.touch(name); });
};
Cache.prototype.diagnosticsDone = function () {
@ -147,12 +220,13 @@ var Cache = (function () {
Cache.prototype.getCompiled = function (id, snapshot, transform) {
var name = this.makeName(id, snapshot);
if (!this.codeCache.exists(name) || this.isDirty(id, snapshot, false)) {
console.log("compile cache miss: " + id);
this.context.debug("fresh transpile for: " + id);
var data_1 = transform();
this.codeCache.write(name, data_1);
this.markAsDirty(id, snapshot);
return data_1;
}
this.context.debug("old transpile for: " + id);
var data = this.codeCache.read(name);
this.codeCache.write(name, data);
return data;
@ -160,17 +234,19 @@ var Cache = (function () {
Cache.prototype.getDiagnostics = function (id, snapshot, check) {
var name = this.makeName(id, snapshot);
if (!this.diagnosticsCache.exists(name) || this.isDirty(id, snapshot, true)) {
console.log("diag cache miss: " + id);
this.context.debug("fresh diagnostics for: " + id);
var data_2 = this.convert(check());
this.diagnosticsCache.write(name, data_2);
this.markAsDirty(id, snapshot);
return data_2;
}
this.context.debug("old diagnostics for: " + id);
var data = this.diagnosticsCache.read(name);
this.diagnosticsCache.write(name, data);
return data;
};
Cache.prototype.markAsDirty = function (id, _snapshot) {
this.context.debug("changed: " + id);
this.dependencyTree.setNode(id, { dirty: true });
};
// returns true if node or any of its imports or any of global types changed
@ -181,7 +257,7 @@ var Cache = (function () {
return false;
if (!checkImports || label.dirty)
return label.dirty;
if (this.typesDirty)
if (this.ambientTypesDirty)
return true;
var dependencies = alg.dijkstra(this.dependencyTree, id);
return some(dependencies, function (dependency, node) {
@ -190,7 +266,7 @@ var Cache = (function () {
var l = _this.dependencyTree.node(node);
var dirty = l === undefined ? true : l.dirty;
if (dirty)
console.log(("dirty: " + id + " -> " + node).gray);
_this.context.debug("import changed: " + id + " -> " + node);
return dirty;
});
};
@ -201,11 +277,11 @@ var Cache = (function () {
Cache.prototype.convert = function (data) {
return map(data, function (diagnostic) {
var entry = {
flatMessage: flattenDiagnosticMessageText(diagnostic.messageText, "\n").yellow,
flatMessage: flattenDiagnosticMessageText(diagnostic.messageText, "\n"),
};
if (diagnostic.file) {
var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character;
entry.fileLine = (diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + ")").white;
entry.fileLine = diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + ")";
}
return entry;
});
@ -227,17 +303,15 @@ function getOptionsOverrides() {
// MIT Licenced
function findFile(cwd, filename) {
var fp = cwd ? (cwd + "/" + filename) : filename;
if (existsSync(fp)) {
if (existsSync(fp))
return fp;
}
var segs = cwd.split(sep);
var len = segs.length;
while (len--) {
cwd = segs.slice(0, len).join("/");
fp = cwd + "/" + filename;
if (existsSync(fp)) {
if (existsSync(fp))
return fp;
}
}
return null;
}
@ -262,22 +336,33 @@ function parseTsConfig() {
var configParseResult = parseJsonConfigFileContent(result.config, sys, dirname(fileName), getOptionsOverrides(), fileName);
return configParseResult;
}
function printDiagnostics(diagnostics) {
function printDiagnostics(context, diagnostics) {
each(diagnostics, function (diagnostic) {
if (diagnostic.fileLine)
console.log(diagnostic.fileLine + ": " + diagnostic.flatMessage);
context.warn(diagnostic.fileLine + ": " + yellow(diagnostic.flatMessage));
else
console.log(diagnostic.flatMessage);
context.warn(yellow(diagnostic.flatMessage));
});
}
function typescript(options) {
options = __assign({}, options);
var filter$$1 = createFilter(options.include || ["*.ts+(|x)", "**/*.ts+(|x)"], options.exclude || ["*.d.ts", "**/*.d.ts"]);
defaults(options, {
check: true,
verbose: VerbosityLevel.Info,
clean: false,
cacheRoot: process.cwd() + "/.rts2_cache",
include: ["*.ts+(|x)", "**/*.ts+(|x)"],
exclude: ["*.d.ts", "**/*.d.ts"],
});
var filter$$1 = createFilter(options.include, options.exclude);
var parsedConfig = parseTsConfig();
var servicesHost = new LanguageServiceHost(parsedConfig);
var services = createLanguageService(servicesHost, createDocumentRegistry());
var cache = new Cache(servicesHost, process.cwd() + "/.rts2_cache", parsedConfig.options, parsedConfig.fileNames);
var context = new ConsoleContext(options.verbose, "");
var cache = new Cache(servicesHost, options.cacheRoot, parsedConfig.options, parsedConfig.fileNames, context);
if (options.clean)
cache.clean();
return {
resolveId: function (importee, importer) {
if (importee === TSLIB)
@ -308,7 +393,7 @@ function typescript(options) {
var result = cache.getCompiled(id, snapshot, function () {
var output = services.getEmitOutput(id);
if (output.emitSkipped)
_this.error({ message: "failed to transpile " + id });
_this.error({ message: red("failed to transpile " + id) });
var transpiled = find(output.outputFiles, function (entry) { return endsWith(entry.name, ".js"); });
var map$$1 = find(output.outputFiles, function (entry) { return endsWith(entry.name, ".map"); });
return {
@ -320,20 +405,22 @@ function typescript(options) {
},
outro: function () {
cache.compileDone();
cache.walkTree(function (id) {
var snapshot = servicesHost.getScriptSnapshot(id);
if (!snapshot) {
console.log("failed lo load snapshot for " + id);
return;
}
var diagnostics = cache.getDiagnostics(id, snapshot, function () {
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
if (options.check) {
cache.walkTree(function (id) {
var snapshot = servicesHost.getScriptSnapshot(id);
if (!snapshot) {
context.error(red("failed lo load snapshot for " + id));
return;
}
var diagnostics = cache.getDiagnostics(id, snapshot, function () {
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
});
printDiagnostics(context, diagnostics);
});
printDiagnostics(diagnostics);
});
}
cache.diagnosticsDone();
},
};

View File

@ -20,8 +20,7 @@
"scripts": {
"prebuild": "rimraf dist/*",
"build": "rollup -c",
"lint": "tslint -c ./tslint.json src/*.ts",
"postinstall": "typings install"
"lint": "tslint -c ./tslint.json src/*.ts"
},
"dependencies": {
"colors": "^1.1.2",
@ -33,7 +32,8 @@
"tslib": "^1.5.0"
},
"peerDependencies": {
"typescript": "^2.0"
"typescript": "^2.0",
"tslib": "^1.5.0"
},
"devDependencies": {
"@alexlur/rollup-plugin-typescript": "^0.8.1",

View File

@ -13,7 +13,8 @@ export default {
'typescript',
'lodash',
'graphlib',
'object-hash'
'object-hash',
'colors/safe'
],
plugins: [

View File

@ -1,9 +1,10 @@
import { IContext } from "./context";
import * as ts from "typescript";
import * as graph from "graphlib";
import * as hash from "object-hash";
import * as _ from "lodash";
import * as colors from "colors/safe";
import { RollingCache } from "./rollingcache";
import * as fs from "fs-extra";
export interface ICode
{
@ -32,14 +33,14 @@ export class Cache
{
private cacheVersion = "1";
private dependencyTree: graph.Graph;
private types: ITypeSnapshot[];
private typesDirty = false;
private ambientTypes: ITypeSnapshot[];
private ambientTypesDirty = false;
private cacheDir: string;
private codeCache: RollingCache<ICode | undefined>;
private typesCache: RollingCache<string>;
private diagnosticsCache: RollingCache<IDiagnostics[]>;
constructor(private host: ts.LanguageServiceHost, cache: string, private options: ts.CompilerOptions, rootFilenames: string[])
constructor(private host: ts.LanguageServiceHost, cache: string, private options: ts.CompilerOptions, rootFilenames: string[], private context: IContext)
{
this.cacheDir = `${cache}/${hash.sha1({ version: this.cacheVersion, rootFilenames, options: this.options })}`;
@ -50,11 +51,17 @@ export class Cache
this.dependencyTree = new graph.Graph({ directed: true });
this.dependencyTree.setDefaultNodeLabel((_node: string) => { return { dirty: false }; });
this.types = _
this.ambientTypes = _
.filter(rootFilenames, (file) => _.endsWith(file, ".d.ts"))
.map((id) => { return { id, snapshot: this.host.getScriptSnapshot(id) }; });
}
public clean()
{
this.context.info(`cleaning cache: ${this.cacheDir}`);
fs.emptyDirSync(this.cacheDir);
}
public walkTree(cb: (id: string) => void | false): void
{
const acyclic = graph.alg.isAcyclic(this.dependencyTree);
@ -65,23 +72,29 @@ export class Cache
return;
}
this.context.info("import tree has cycles");
_.each(this.dependencyTree.nodes(), (id: string) => cb(id));
}
public setDependency(importee: string, importer: string): void
{
// importee -> importer
this.context.debug(`${importee} -> ${importer}`);
this.dependencyTree.setEdge(importer, importee);
}
public compileDone(): void
{
let typeNames = _
.filter(this.types, (snaphot) => snaphot.snapshot !== undefined)
.filter(this.ambientTypes, (snaphot) => snaphot.snapshot !== undefined)
.map((snaphot) => this.makeName(snaphot.id, snaphot.snapshot!));
// types dirty if any d.ts changed, added or removed
this.typesDirty = !this.typesCache.match(typeNames);
this.ambientTypesDirty = !this.typesCache.match(typeNames);
if (this.ambientTypesDirty)
this.context.info("ambient types changed, redoing all diagnostics");
_.each(typeNames, (name) => this.typesCache.touch(name));
}
@ -99,13 +112,16 @@ export class Cache
if (!this.codeCache.exists(name) || this.isDirty(id, snapshot, false))
{
console.log(`compile cache miss: ${id}`);
this.context.debug(`fresh transpile for: ${id}`);
let data = transform();
this.codeCache.write(name, data);
this.markAsDirty(id, snapshot);
return data;
}
this.context.debug(`old transpile for: ${id}`);
let data = this.codeCache.read(name);
this.codeCache.write(name, data);
return data;
@ -117,13 +133,16 @@ export class Cache
if (!this.diagnosticsCache.exists(name) || this.isDirty(id, snapshot, true))
{
console.log(`diag cache miss: ${id}`);
this.context.debug(`fresh diagnostics for: ${id}`);
let data = this.convert(check());
this.diagnosticsCache.write(name, data);
this.markAsDirty(id, snapshot);
return data;
}
this.context.debug(`old diagnostics for: ${id}`);
let data = this.diagnosticsCache.read(name);
this.diagnosticsCache.write(name, data);
return data;
@ -131,6 +150,7 @@ export class Cache
private markAsDirty(id: string, _snapshot: ts.IScriptSnapshot): void
{
this.context.debug(`changed: ${id}`);
this.dependencyTree.setNode(id, { dirty: true });
}
@ -145,7 +165,7 @@ export class Cache
if (!checkImports || label.dirty)
return label.dirty;
if (this.typesDirty)
if (this.ambientTypesDirty)
return true;
let dependencies = graph.alg.dijkstra(this.dependencyTree, id);
@ -159,7 +179,7 @@ export class Cache
let dirty = l === undefined ? true : l.dirty;
if (dirty)
console.log(colors.gray(`dirty: ${id} -> ${node}`));
this.context.debug(`import changed: ${id} -> ${node}`);
return dirty;
});

61
src/context.ts Normal file
View File

@ -0,0 +1,61 @@
interface Message
{
message: string;
}
export interface IRollupContext
{
warn(message: Message | string): void;
error(message: Message | string): void;
}
export interface IContext
{
warn(message: string): void;
error(message: string): void;
info(message: string): void;
debug(message: string): void;
}
export enum VerbosityLevel
{
Error = 0,
Warning,
Info,
Debug,
}
export class ConsoleContext implements IContext
{
constructor(private verbosity: VerbosityLevel, private prefix: string = "")
{
}
public warn(message: string): void
{
if (this.verbosity < VerbosityLevel.Warning)
return;
console.log(`${this.prefix}${message}`);
}
public error(message: string): void
{
if (this.verbosity < VerbosityLevel.Error)
return;
console.log(`${this.prefix}${message}`);
}
public info(message: string): void
{
if (this.verbosity < VerbosityLevel.Info)
return;
console.log(`${this.prefix}${message}`);
}
public debug(message: string): void
{
if (this.verbosity < VerbosityLevel.Debug)
return;
console.log(`${this.prefix}${message}`);
}
}

View File

@ -1,3 +1,4 @@
import { IContext, ConsoleContext, IRollupContext, VerbosityLevel } from "./context";
import { LanguageServiceHost } from "./host";
import { Cache, ICode, IDiagnostics } from "./cache";
import * as ts from "typescript";
@ -26,9 +27,7 @@ function findFile(cwd: string, filename: string)
let fp = cwd ? (cwd + "/" + filename) : filename;
if (fs.existsSync(fp))
{
return fp;
}
const segs = cwd.split(path.sep);
let len = segs.length;
@ -38,9 +37,7 @@ function findFile(cwd: string, filename: string)
cwd = segs.slice(0, len).join("/");
fp = cwd + "/" + filename;
if (fs.existsSync(fp))
{
return fp;
}
}
return null;
@ -73,37 +70,42 @@ function parseTsConfig()
return configParseResult;
}
interface Message
{
message: string;
}
interface Context
{
warn(message: Message): void;
error(message: Message): void;
}
function printDiagnostics(diagnostics: IDiagnostics[])
function printDiagnostics(context: IContext, diagnostics: IDiagnostics[])
{
_.each(diagnostics, (diagnostic) =>
{
if (diagnostic.fileLine)
console.log(`${diagnostic.fileLine}: ${colors.yellow(diagnostic.flatMessage)}`);
context.warn(`${diagnostic.fileLine}: ${colors.yellow(diagnostic.flatMessage)}`);
else
console.log(colors.yellow(diagnostic.flatMessage));
context.warn(colors.yellow(diagnostic.flatMessage));
});
};
interface IOptions
{
include?: string;
exclude?: string;
include: string;
exclude: string;
check: boolean;
verbose: number;
clean: boolean;
cacheRoot: string;
}
export default function typescript (options: IOptions)
{
options = { ... options };
const filter = createFilter(options.include || [ "*.ts+(|x)", "**/*.ts+(|x)" ], options.exclude || [ "*.d.ts", "**/*.d.ts" ]);
_.defaults(options,
{
check: true,
verbose: VerbosityLevel.Info,
clean: false,
cacheRoot: `${process.cwd()}/.rts2_cache`,
include: [ "*.ts+(|x)", "**/*.ts+(|x)" ],
exclude: [ "*.d.ts", "**/*.d.ts" ],
});
const filter = createFilter(options.include, options.exclude);
let parsedConfig = parseTsConfig();
@ -111,7 +113,12 @@ export default function typescript (options: IOptions)
const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
const cache = new Cache(servicesHost, `${process.cwd()}/.rts2_cache`, parsedConfig.options, parsedConfig.fileNames);
const context = new ConsoleContext(options.verbose, "");
const cache = new Cache(servicesHost, options.cacheRoot, parsedConfig.options, parsedConfig.fileNames, context);
if (options.clean)
cache.clean();
return {
@ -149,7 +156,7 @@ export default function typescript (options: IOptions)
return undefined;
},
transform(this: Context, code: string, id: string): ICode | undefined
transform(this: IRollupContext, code: string, id: string): ICode | undefined
{
if (!filter(id))
return undefined;
@ -178,26 +185,29 @@ export default function typescript (options: IOptions)
{
cache.compileDone();
cache.walkTree((id: string) =>
if (options.check)
{
const snapshot = servicesHost.getScriptSnapshot(id);
if (!snapshot)
cache.walkTree((id: string) =>
{
console.log(colors.red(`failed lo load snapshot for ${id}`));
return;
}
const snapshot = servicesHost.getScriptSnapshot(id);
const diagnostics = cache.getDiagnostics(id, snapshot, () =>
{
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
if (!snapshot)
{
context.error(colors.red(`failed lo load snapshot for ${id}`));
return;
}
const diagnostics = cache.getDiagnostics(id, snapshot, () =>
{
return services
.getCompilerOptionsDiagnostics()
.concat(services.getSyntacticDiagnostics(id))
.concat(services.getSemanticDiagnostics(id));
});
printDiagnostics(context, diagnostics);
});
printDiagnostics(diagnostics);
});
}
cache.diagnosticsDone();
},