Skip to content

Commit

Permalink
Watch (#8)
Browse files Browse the repository at this point in the history
* - partial fix for watch mode (#6)

* - trying to detect watch mode

* - support for watch mode
  • Loading branch information
ezolenko authored Mar 15, 2017
1 parent 1dbe1cc commit 96b2846
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/node_modules
/npm-debug.log
/typings
/.rts2_cache
/.rpt2_cache
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ Plugin takes following options:

On windows typescript resolver favors POSIX path, while commonjs plugin (and maybe others?) uses native path as module id. This can result in `namedExports` being ignored if rollup happened to use typescript's resolution. Set to true to pass resolved module path through `resolve()` to match up with `rollup-plugin-commonjs`.

### Watch mode

The way typescript handles type-only imports and ambient types effectively hides them from rollup watch, because import statements are not generated and changing them doesn't trigger a rebuild.

Otherwise the plugin should work in watch mode. Make sure to run a normal build after watch session to catch any type errors.

### Version

This plugin currently requires TypeScript `2.0+`.
Expand Down
137 changes: 91 additions & 46 deletions dist/rollup-plugin-typescript2.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ var LanguageServiceHost = (function () {
this.snapshots = {};
this.versions = {};
}
LanguageServiceHost.prototype.reset = function () {
this.snapshots = {};
this.versions = {};
};
LanguageServiceHost.prototype.setSnapshot = function (fileName, data) {
var snapshot = ts.ScriptSnapshot.fromString(data);
this.snapshots[fileName] = snapshot;
Expand All @@ -131,8 +135,8 @@ var LanguageServiceHost = (function () {
LanguageServiceHost.prototype.getCurrentDirectory = function () {
return this.cwd;
};
LanguageServiceHost.prototype.getScriptVersion = function (_fileName) {
return (this.versions[_fileName] || 0).toString();
LanguageServiceHost.prototype.getScriptVersion = function (fileName) {
return (this.versions[fileName] || 0).toString();
};
LanguageServiceHost.prototype.getScriptFileNames = function () {
return this.parsedConfig.fileNames;
Expand All @@ -158,6 +162,7 @@ var RollingCache = (function () {
function RollingCache(cacheRoot, checkNewCache) {
this.cacheRoot = cacheRoot;
this.checkNewCache = checkNewCache;
this.rolled = false;
this.oldCacheRoot = this.cacheRoot + "/cache";
this.newCacheRoot = this.cacheRoot + "/cache_";
fs.emptyDirSync(this.newCacheRoot);
Expand All @@ -166,6 +171,8 @@ var RollingCache = (function () {
* @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.rolled)
return false;
if (this.checkNewCache && fs.existsSync(this.newCacheRoot + "/" + name))
return true;
return fs.existsSync(this.oldCacheRoot + "/" + name);
Expand All @@ -177,6 +184,8 @@ var RollingCache = (function () {
* @returns true if old cache contains all names and nothing more
*/
RollingCache.prototype.match = function (names) {
if (this.rolled)
return false;
if (!fs.existsSync(this.oldCacheRoot))
return names.length === 0; // empty folder matches
return _.isEqual(fs.readdirSync(this.oldCacheRoot).sort(), names.sort());
Expand All @@ -190,27 +199,29 @@ var RollingCache = (function () {
return fs.readJsonSync(this.oldCacheRoot + "/" + name, "utf8");
};
RollingCache.prototype.write = function (name, data) {
if (this.rolled)
return;
if (data === undefined)
return;
if (this.checkNewCache)
fs.writeJsonSync(this.newCacheRoot + "/" + name, data);
if (this.rolled)
fs.writeJsonSync(this.oldCacheRoot + "/" + name, data);
else
fs.writeJson(this.newCacheRoot + "/" + name, data, { encoding: "utf8" }, function () { });
fs.writeJsonSync(this.newCacheRoot + "/" + name, data);
};
RollingCache.prototype.touch = function (name) {
if (this.checkNewCache)
fs.ensureFileSync(this.newCacheRoot + "/" + name);
else
fs.ensureFile(this.newCacheRoot + "/" + name, function () { });
if (this.rolled)
return;
fs.ensureFileSync(this.newCacheRoot + "/" + name);
};
/**
* 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 () { });
});
if (this.rolled)
return;
this.rolled = true;
fs.removeSync(this.oldCacheRoot);
fs.move(this.newCacheRoot, this.oldCacheRoot, function () { });
};
return RollingCache;
}());
Expand All @@ -228,13 +239,13 @@ function convertDiagnostic(data) {
return entry;
});
}
var Cache = (function () {
function Cache(host, cache, options, rootFilenames, context) {
var TsCache = (function () {
function TsCache(host, cache, options, rootFilenames, context) {
var _this = this;
this.host = host;
this.options = options;
this.context = context;
this.cacheVersion = "2";
this.cacheVersion = "3";
this.ambientTypesDirty = false;
this.cacheDir = cache + "/" + hash.sha1({
version: this.cacheVersion,
Expand All @@ -253,25 +264,34 @@ var Cache = (function () {
this.init();
this.checkAmbientTypes();
}
Cache.prototype.clean = function () {
TsCache.prototype.clean = function () {
this.context.info(colors.blue("cleaning cache: " + this.cacheDir));
fs.emptyDirSync(this.cacheDir);
this.init();
};
Cache.prototype.setDependency = function (importee, importer) {
TsCache.prototype.setDependency = function (importee, importer) {
// importee -> importer
this.context.debug(colors.blue("dependency") + " '" + importee + "'");
this.context.debug(" imported by '" + importer + "'");
this.dependencyTree.setEdge(importer, importee);
};
Cache.prototype.done = function () {
TsCache.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(colors.yellow("import tree has cycles"));
_.each(this.dependencyTree.nodes(), function (id) { return cb(id); });
};
TsCache.prototype.done = function () {
this.context.info(colors.blue("rolling caches"));
this.codeCache.roll();
this.semanticDiagnosticsCache.roll();
this.syntacticDiagnosticsCache.roll();
this.typesCache.roll();
};
Cache.prototype.getCompiled = function (id, snapshot, transform) {
TsCache.prototype.getCompiled = function (id, snapshot, transform) {
var name = this.makeName(id, snapshot);
this.context.info(colors.blue("transpiling") + " '" + id + "'");
this.context.debug(" cache: '" + this.codeCache.path(name) + "'");
Expand All @@ -287,13 +307,13 @@ var Cache = (function () {
this.codeCache.write(name, data);
return data;
};
Cache.prototype.getSyntacticDiagnostics = function (id, snapshot, check) {
TsCache.prototype.getSyntacticDiagnostics = function (id, snapshot, check) {
return this.getDiagnostics(this.syntacticDiagnosticsCache, id, snapshot, check);
};
Cache.prototype.getSemanticDiagnostics = function (id, snapshot, check) {
TsCache.prototype.getSemanticDiagnostics = function (id, snapshot, check) {
return this.getDiagnostics(this.semanticDiagnosticsCache, id, snapshot, check);
};
Cache.prototype.checkAmbientTypes = function () {
TsCache.prototype.checkAmbientTypes = function () {
var _this = this;
this.context.debug(colors.blue("Ambient types:"));
var typeNames = _.filter(this.ambientTypes, function (snapshot) { return snapshot.snapshot !== undefined; })
Expand All @@ -307,7 +327,7 @@ var Cache = (function () {
this.context.info(colors.yellow("ambient types changed, redoing all semantic diagnostics"));
_.each(typeNames, function (name) { return _this.typesCache.touch(name); });
};
Cache.prototype.getDiagnostics = function (cache, id, snapshot, check) {
TsCache.prototype.getDiagnostics = function (cache, id, snapshot, check) {
var name = this.makeName(id, snapshot);
this.context.debug(" cache: '" + cache.path(name) + "'");
if (!cache.exists(name) || this.isDirty(id, snapshot, true)) {
Expand All @@ -322,17 +342,17 @@ var Cache = (function () {
cache.write(name, data);
return data;
};
Cache.prototype.init = function () {
TsCache.prototype.init = function () {
this.codeCache = new RollingCache(this.cacheDir + "/code", true);
this.typesCache = new RollingCache(this.cacheDir + "/types", false);
this.syntacticDiagnosticsCache = new RollingCache(this.cacheDir + "/syntacticDiagnostics", false);
this.semanticDiagnosticsCache = new RollingCache(this.cacheDir + "/semanticDiagnostics", false);
this.typesCache = new RollingCache(this.cacheDir + "/types", true);
this.syntacticDiagnosticsCache = new RollingCache(this.cacheDir + "/syntacticDiagnostics", true);
this.semanticDiagnosticsCache = new RollingCache(this.cacheDir + "/semanticDiagnostics", true);
};
Cache.prototype.markAsDirty = function (id, _snapshot) {
TsCache.prototype.markAsDirty = function (id, _snapshot) {
this.dependencyTree.setNode(id, { dirty: true });
};
// returns true if node or any of its imports or any of global types changed
Cache.prototype.isDirty = function (id, _snapshot, checkImports) {
TsCache.prototype.isDirty = function (id, _snapshot, checkImports) {
var _this = this;
var label = this.dependencyTree.node(id);
if (!label)
Expand All @@ -352,11 +372,11 @@ var Cache = (function () {
return dirty;
});
};
Cache.prototype.makeName = function (id, snapshot) {
TsCache.prototype.makeName = function (id, snapshot) {
var data = snapshot.getText(0, snapshot.getLength());
return hash.sha1({ data: data, id: id });
};
return Cache;
return TsCache;
}());

// tslint:disable-next-line:no-var-requires
Expand Down Expand Up @@ -432,20 +452,23 @@ function typescript(options) {
abortOnError: true,
rollupCommonJSResolveHack: false,
});
var watchMode = false;
var round = 0;
var targetCount = 0;
var context = new ConsoleContext(options.verbosity, "rpt2: ");
context.info("Typescript version: " + ts.version);
context.debug("Options: " + JSON.stringify(options, undefined, 4));
var filter$$1 = createFilter(options.include, options.exclude);
var parsedConfig = parseTsConfig(context);
var servicesHost = new LanguageServiceHost(parsedConfig);
var services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
var cache = new Cache(servicesHost, options.cacheRoot, parsedConfig.options, parsedConfig.fileNames, context);
var cleanTranspile = true;
var service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
var cache = new TsCache(servicesHost, options.cacheRoot, parsedConfig.options, parsedConfig.fileNames, context);
var noErrors = true;
if (options.clean)
cache.clean();
// printing compiler option errors
if (options.check)
printDiagnostics(context, convertDiagnostic(services.getCompilerOptionsDiagnostics()));
printDiagnostics(context, convertDiagnostic(service.getCompilerOptionsDiagnostics()));
return {
resolveId: function (importee, importer) {
if (importee === TSLIB)
Expand Down Expand Up @@ -481,14 +504,14 @@ function typescript(options) {
var snapshot = servicesHost.setSnapshot(id, code);
// getting compiled file from cache or from ts
var result = cache.getCompiled(id, snapshot, function () {
var output = services.getEmitOutput(id);
var output = service.getEmitOutput(id);
if (output.emitSkipped) {
cleanTranspile = false;
noErrors = false;
// always checking on fatal errors, even if options.check is set to false
var diagnostics = cache.getSyntacticDiagnostics(id, snapshot, function () {
return services.getSyntacticDiagnostics(id);
return service.getSyntacticDiagnostics(id);
}).concat(cache.getSemanticDiagnostics(id, snapshot, function () {
return services.getSemanticDiagnostics(id);
return service.getSemanticDiagnostics(id);
}));
printDiagnostics(contextWrapper, diagnostics);
// since no output was generated, aborting compilation
Expand All @@ -503,20 +526,42 @@ function typescript(options) {
});
if (options.check) {
var diagnostics = cache.getSyntacticDiagnostics(id, snapshot, function () {
return services.getSyntacticDiagnostics(id);
return service.getSyntacticDiagnostics(id);
}).concat(cache.getSemanticDiagnostics(id, snapshot, function () {
return services.getSemanticDiagnostics(id);
return service.getSemanticDiagnostics(id);
}));
if (diagnostics.length !== 0)
cleanTranspile = false;
noErrors = false;
printDiagnostics(contextWrapper, diagnostics);
}
return result;
},
ongenerate: function () {
cache.done();
if (!cleanTranspile)
ongenerate: function (bundleOptions) {
if (_.isArray(bundleOptions.targets))
targetCount = bundleOptions.targets.length;
if (round >= targetCount) {
watchMode = true;
round = 0;
}
context.debug("generating target " + round + " of " + bundleOptions.targets.length);
if (watchMode && round === 0) {
context.debug("running in watch mode");
// hack to fix ts lagging
servicesHost.reset();
service.cleanupSemanticCache();
cache.walkTree(function (id) {
var diagnostics = convertDiagnostic(service.getSyntacticDiagnostics(id)).concat(convertDiagnostic(service.getSemanticDiagnostics(id)));
if (diagnostics.length > 0)
noErrors = false;
printDiagnostics(context, diagnostics);
});
}
if (!noErrors) {
noErrors = true;
context.info(colors.yellow("there were errors or warnings above."));
}
cache.done();
round++;
},
};
}
Expand Down
Loading

0 comments on commit 96b2846

Please sign in to comment.