diff --git a/dist/rollup-plugin-typescript2.cjs.js b/dist/rollup-plugin-typescript2.cjs.js index 67156af..2853d80 100644 --- a/dist/rollup-plugin-typescript2.cjs.js +++ b/dist/rollup-plugin-typescript2.cjs.js @@ -6,6 +6,7 @@ var ts = require('typescript'); var _ = require('lodash'); var graph = require('graphlib'); var hash = require('object-hash'); +var mkdirp = require('mkdirp'); var rollupPluginutils = require('rollup-pluginutils'); var path = require('path'); @@ -63,23 +64,25 @@ var Cache = (function () { function Cache(cacheRoot, options, rootFilenames) { this.cacheRoot = cacheRoot; this.options = options; + this.cacheVersion = "0"; this.treeComplete = false; - this.cacheRoot = this.cacheRoot + "/" + hash.sha1({ rootFilenames: rootFilenames, options: this.options }); - fs.mkdirSync(this.cacheRoot); - var dependencyTreeFile = this.cacheRoot + "/tree"; - if (fs.existsSync(dependencyTreeFile)) { - var data = fs.readFileSync(this.cacheRoot + "/tree", "utf8"); - this.dependencyTree = graph.json.read(JSON.parse(data)); - } - else - this.dependencyTree = new graph.Graph({ directed: true }); + this.cacheRoot = this.cacheRoot + "/" + hash.sha1({ version: this.cacheVersion, rootFilenames: rootFilenames, options: this.options }); + mkdirp.sync(this.cacheRoot); + this.dependencyTree = new graph.Graph({ directed: true }); this.dependencyTree.setDefaultNodeLabel(function (_node) { return { dirty: false }; }); } Cache.prototype.walkTree = function (cb) { - _.each(graph.alg.topsort(this.dependencyTree), function (id) { return cb(id); }); + var acyclic = graph.alg.isAcyclic(this.dependencyTree); + if (acyclic) { + _.each(graph.alg.topsort(this.dependencyTree), function (id) { return cb(id); }); + return; + } + // console.log("cycles detected in dependency graph, not sorting"); + _.each(this.dependencyTree.nodes(), function (id) { return cb(id); }); }; Cache.prototype.setDependency = function (importee, importer) { - // importer -> importee + // console.log(importer, "->", importee); + // importee -> importer this.dependencyTree.setEdge(importer, importee); }; Cache.prototype.lastDependencySet = function () { @@ -92,14 +95,25 @@ var Cache = (function () { Cache.prototype.isDirty = function (id, _snapshot, checkImports) { var _this = this; var label = this.dependencyTree.node(id); - if (checkImports || label.dirty) + if (!label) + return false; + if (!checkImports || label.dirty) return label.dirty; var dependencies = graph.alg.dijkstra(this.dependencyTree, id); - return _.some(_.keys(dependencies), function (dependencyId) { return _this.dependencyTree.node(dependencyId).dirty; }); + return _.some(dependencies, function (dependency, node) { + if (!node || dependency.distance === Infinity) + return false; + var l = _this.dependencyTree.node(node); + var dirty = l === undefined ? true : l.dirty; + if (dirty) + console.log("dirty: " + id + " -> " + node); + return dirty; + }); }; Cache.prototype.getCompiled = function (id, snapshot, transform) { var path$$1 = this.makePath(id, snapshot); if (!fs.existsSync(path$$1) || this.isDirty(id, snapshot, false)) { + // console.log(`compile cache miss: ${id}`); var data = transform(); this.setCache(path$$1, id, snapshot, data); return data; @@ -109,7 +123,8 @@ var Cache = (function () { Cache.prototype.getDiagnostics = function (id, snapshot, check) { var path$$1 = this.makePath(id, snapshot) + ".diagnostics"; if (!fs.existsSync(path$$1) || this.isDirty(id, snapshot, true)) { - var data = check(); + // console.log(`diagnostics cache miss: ${id}`); + var data = this.convert(check()); this.setCache(path$$1, id, snapshot, data); return data; } @@ -125,6 +140,18 @@ var Cache = (function () { var data = snapshot.getText(0, snapshot.getLength()); return this.cacheRoot + "/" + hash.sha1({ data: data, id: id }); }; + Cache.prototype.convert = function (data) { + return _.map(data, function (diagnostic) { + var entry = { + 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) + ")"; + } + return entry; + }); + }; return Cache; }()); @@ -170,35 +197,31 @@ catch (e) { } function parseTsConfig() { var fileName = findFile(process.cwd(), "tsconfig.json"); + if (!fileName) + throw new Error("couldn't find 'tsconfig.json' in " + process.cwd()); var text = ts.sys.readFile(fileName); var result = ts.parseConfigFileTextToJson(fileName, text); var configParseResult = ts.parseJsonConfigFileContent(result.config, ts.sys, path.dirname(fileName), getOptionsOverrides(), fileName); return configParseResult; } function printDiagnostics(diagnostics) { - diagnostics.forEach(function (diagnostic) { - var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); - if (diagnostic.file) { - var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character; - console.log(diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + "): " + message); - } + _.each(diagnostics, function (diagnostic) { + if (diagnostic.fileLine) + console.log(diagnostic.fileLine + ": " + diagnostic.flatMessage); else - console.log(message); + console.log(diagnostic.flatMessage); }); } function typescript(options) { options = __assign({}, options); var filter = rollupPluginutils.createFilter(options.include || ["*.ts+(|x)", "**/*.ts+(|x)"], options.exclude || ["*.d.ts", "**/*.d.ts"]); - delete options.include; - delete options.exclude; var parsedConfig = parseTsConfig(); var servicesHost = new LanguageServiceHost(parsedConfig); var services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); - var cache = new Cache(process.cwd(), parsedConfig.options, parsedConfig.fileNames); + var cache = new Cache(process.cwd() + "/.rts2_cache", parsedConfig.options, parsedConfig.fileNames); return { resolveId: function (importee, importer) { - cache.setDependency(importee, importer); if (importee === TSLIB) return "\0" + TSLIB; if (!importer) @@ -208,6 +231,8 @@ function typescript(options) { if (result.resolvedModule && result.resolvedModule.resolvedFileName) { if (_.endsWith(result.resolvedModule.resolvedFileName, ".d.ts")) return null; + if (filter(result.resolvedModule.resolvedFileName)) + cache.setDependency(result.resolvedModule.resolvedFileName, importer); return result.resolvedModule.resolvedFileName; } return null; @@ -220,17 +245,17 @@ function typescript(options) { transform: function (code, id) { var _this = this; if (!filter(id)) - return null; + return undefined; var snapshot = servicesHost.setSnapshot(id, code); var result = cache.getCompiled(id, snapshot, function () { var output = services.getEmitOutput(id); if (output.emitSkipped) _this.error({ message: "failed to transpile " + id }); var transpiled = _.find(output.outputFiles, function (entry) { return _.endsWith(entry.name, ".js"); }); - var map = _.find(output.outputFiles, function (entry) { return _.endsWith(entry.name, ".map"); }); + var map$$1 = _.find(output.outputFiles, function (entry) { return _.endsWith(entry.name, ".map"); }); return { code: transpiled ? transpiled.text : undefined, - map: map ? JSON.parse(map.text) : { mappings: "" }, + map: map$$1 ? JSON.parse(map$$1.text) : { mappings: "" }, }; }); return result; @@ -239,16 +264,17 @@ function typescript(options) { cache.lastDependencySet(); 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 (diagnostics.length !== 0) { - console.log(id); - printDiagnostics(diagnostics); - } + printDiagnostics(diagnostics); }); }, }; diff --git a/dist/rollup-plugin-typescript2.es.js b/dist/rollup-plugin-typescript2.es.js index a09aa01..36c1ba8 100644 --- a/dist/rollup-plugin-typescript2.es.js +++ b/dist/rollup-plugin-typescript2.es.js @@ -1,14 +1,16 @@ /* eslint-disable */ -import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; import * as fs from 'fs'; import { ModuleKind, ScriptSnapshot, createDocumentRegistry, createLanguageService, flattenDiagnosticMessageText, getDefaultLibFilePath, nodeModuleNameResolver, parseConfigFileTextToJson, parseJsonConfigFileContent, sys } from 'typescript'; import * as ts from 'typescript'; -import { each, endsWith, find, has, keys, some } from 'lodash'; +import { each, endsWith, find, has, map, some } from 'lodash'; import * as _ from 'lodash'; -import { Graph, alg, json } from 'graphlib'; +import { Graph, alg } from 'graphlib'; import * as graph from 'graphlib'; import { sha1 } from 'object-hash'; import * as hash from 'object-hash'; +import { sync } from 'mkdirp'; +import * as mkdirp from 'mkdirp'; import { createFilter } from 'rollup-pluginutils'; import { dirname, sep } from 'path'; import * as path from 'path'; @@ -67,23 +69,25 @@ var Cache = (function () { function Cache(cacheRoot, options, rootFilenames) { this.cacheRoot = cacheRoot; this.options = options; + this.cacheVersion = "0"; this.treeComplete = false; - this.cacheRoot = this.cacheRoot + "/" + sha1({ rootFilenames: rootFilenames, options: this.options }); - mkdirSync(this.cacheRoot); - var dependencyTreeFile = this.cacheRoot + "/tree"; - if (existsSync(dependencyTreeFile)) { - var data = readFileSync(this.cacheRoot + "/tree", "utf8"); - this.dependencyTree = json.read(JSON.parse(data)); - } - else - this.dependencyTree = new Graph({ directed: true }); + this.cacheRoot = this.cacheRoot + "/" + sha1({ version: this.cacheVersion, rootFilenames: rootFilenames, options: this.options }); + sync(this.cacheRoot); + this.dependencyTree = new Graph({ directed: true }); this.dependencyTree.setDefaultNodeLabel(function (_node) { return { dirty: false }; }); } Cache.prototype.walkTree = function (cb) { - each(alg.topsort(this.dependencyTree), function (id) { return cb(id); }); + var acyclic = alg.isAcyclic(this.dependencyTree); + if (acyclic) { + each(alg.topsort(this.dependencyTree), function (id) { return cb(id); }); + return; + } + // console.log("cycles detected in dependency graph, not sorting"); + each(this.dependencyTree.nodes(), function (id) { return cb(id); }); }; Cache.prototype.setDependency = function (importee, importer) { - // importer -> importee + // console.log(importer, "->", importee); + // importee -> importer this.dependencyTree.setEdge(importer, importee); }; Cache.prototype.lastDependencySet = function () { @@ -96,14 +100,25 @@ var Cache = (function () { Cache.prototype.isDirty = function (id, _snapshot, checkImports) { var _this = this; var label = this.dependencyTree.node(id); - if (checkImports || label.dirty) + if (!label) + return false; + if (!checkImports || label.dirty) return label.dirty; var dependencies = alg.dijkstra(this.dependencyTree, id); - return some(keys(dependencies), function (dependencyId) { return _this.dependencyTree.node(dependencyId).dirty; }); + return some(dependencies, function (dependency, node) { + if (!node || dependency.distance === Infinity) + return false; + var l = _this.dependencyTree.node(node); + var dirty = l === undefined ? true : l.dirty; + if (dirty) + console.log("dirty: " + id + " -> " + node); + return dirty; + }); }; Cache.prototype.getCompiled = function (id, snapshot, transform) { var path$$1 = this.makePath(id, snapshot); if (!existsSync(path$$1) || this.isDirty(id, snapshot, false)) { + // console.log(`compile cache miss: ${id}`); var data = transform(); this.setCache(path$$1, id, snapshot, data); return data; @@ -113,7 +128,8 @@ var Cache = (function () { Cache.prototype.getDiagnostics = function (id, snapshot, check) { var path$$1 = this.makePath(id, snapshot) + ".diagnostics"; if (!existsSync(path$$1) || this.isDirty(id, snapshot, true)) { - var data = check(); + // console.log(`diagnostics cache miss: ${id}`); + var data = this.convert(check()); this.setCache(path$$1, id, snapshot, data); return data; } @@ -129,6 +145,18 @@ var Cache = (function () { var data = snapshot.getText(0, snapshot.getLength()); return this.cacheRoot + "/" + sha1({ data: data, id: id }); }; + Cache.prototype.convert = function (data) { + return map(data, function (diagnostic) { + var entry = { + 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) + ")"; + } + return entry; + }); + }; return Cache; }()); @@ -174,35 +202,31 @@ catch (e) { } function parseTsConfig() { var fileName = findFile(process.cwd(), "tsconfig.json"); + if (!fileName) + throw new Error("couldn't find 'tsconfig.json' in " + process.cwd()); var text = sys.readFile(fileName); var result = parseConfigFileTextToJson(fileName, text); var configParseResult = parseJsonConfigFileContent(result.config, sys, dirname(fileName), getOptionsOverrides(), fileName); return configParseResult; } function printDiagnostics(diagnostics) { - diagnostics.forEach(function (diagnostic) { - var message = flattenDiagnosticMessageText(diagnostic.messageText, "\n"); - if (diagnostic.file) { - var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character; - console.log(diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + "): " + message); - } + each(diagnostics, function (diagnostic) { + if (diagnostic.fileLine) + console.log(diagnostic.fileLine + ": " + diagnostic.flatMessage); else - console.log(message); + console.log(diagnostic.flatMessage); }); } function typescript(options) { options = __assign({}, options); var filter = createFilter(options.include || ["*.ts+(|x)", "**/*.ts+(|x)"], options.exclude || ["*.d.ts", "**/*.d.ts"]); - delete options.include; - delete options.exclude; var parsedConfig = parseTsConfig(); var servicesHost = new LanguageServiceHost(parsedConfig); var services = createLanguageService(servicesHost, createDocumentRegistry()); - var cache = new Cache(process.cwd(), parsedConfig.options, parsedConfig.fileNames); + var cache = new Cache(process.cwd() + "/.rts2_cache", parsedConfig.options, parsedConfig.fileNames); return { resolveId: function (importee, importer) { - cache.setDependency(importee, importer); if (importee === TSLIB) return "\0" + TSLIB; if (!importer) @@ -212,6 +236,8 @@ function typescript(options) { if (result.resolvedModule && result.resolvedModule.resolvedFileName) { if (endsWith(result.resolvedModule.resolvedFileName, ".d.ts")) return null; + if (filter(result.resolvedModule.resolvedFileName)) + cache.setDependency(result.resolvedModule.resolvedFileName, importer); return result.resolvedModule.resolvedFileName; } return null; @@ -224,17 +250,17 @@ function typescript(options) { transform: function (code, id) { var _this = this; if (!filter(id)) - return null; + return undefined; var snapshot = servicesHost.setSnapshot(id, code); var result = cache.getCompiled(id, snapshot, function () { var output = services.getEmitOutput(id); if (output.emitSkipped) _this.error({ message: "failed to transpile " + id }); var transpiled = find(output.outputFiles, function (entry) { return endsWith(entry.name, ".js"); }); - var map = find(output.outputFiles, function (entry) { return endsWith(entry.name, ".map"); }); + var map$$1 = find(output.outputFiles, function (entry) { return endsWith(entry.name, ".map"); }); return { code: transpiled ? transpiled.text : undefined, - map: map ? JSON.parse(map.text) : { mappings: "" }, + map: map$$1 ? JSON.parse(map$$1.text) : { mappings: "" }, }; }); return result; @@ -243,16 +269,17 @@ function typescript(options) { cache.lastDependencySet(); 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 (diagnostics.length !== 0) { - console.log(id); - printDiagnostics(diagnostics); - } + printDiagnostics(diagnostics); }); }, }; diff --git a/package.json b/package.json index fa05e13..53b1d34 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,12 @@ "postinstall": "typings install" }, "dependencies": { - "tslib": "^1.5.0", - "lodash": "^4.17.4", "graphlib": "^2.1.1", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", "object-hash": "^1.1.5", - "rollup-pluginutils": "^2.0.1" + "rollup-pluginutils": "^2.0.1", + "tslib": "^1.5.0" }, "peerDependencies": { "typescript": "^2.0" @@ -37,6 +38,7 @@ "@alexlur/rollup-plugin-typescript": "^0.8.1", "@types/graphlib": "^2.1.3", "@types/lodash": "^4.14.52", + "@types/mkdirp": "^0.3.29", "@types/node": "^6.0.53", "@types/object-hash": "^0.5.28", "rimraf": "^2.5.4", diff --git a/rollup.config.js b/rollup.config.js index f307304..cc1330b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -13,7 +13,8 @@ export default { 'typescript', 'lodash', 'graphlib', - 'object-hash' + 'object-hash', + 'mkdirp' ], plugins: [ diff --git a/src/cache.ts b/src/cache.ts index f07b056..44bfbe4 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -3,11 +3,12 @@ import * as graph from "graphlib"; import * as hash from "object-hash"; import * as fs from "fs"; import * as _ from "lodash"; +import * as mkdirp from "mkdirp"; export interface ICode { - code: string; - map: string; + code: string | undefined; + map: string | undefined; } interface INodeLabel @@ -16,46 +17,55 @@ interface INodeLabel hash?: string; } +export interface IDiagnostics +{ + flatMessage: string; + fileLine?: string; +} + export class Cache { + private cacheVersion = "0"; private dependencyTree: graph.Graph; private treeComplete: boolean = false; constructor(private cacheRoot: string, private options: ts.CompilerOptions, rootFilenames: string[]) { - this.cacheRoot = `${this.cacheRoot}/${hash.sha1({ rootFilenames, options: this.options })}`; - fs.mkdirSync(this.cacheRoot); + this.cacheRoot = `${this.cacheRoot}/${hash.sha1({ version: this.cacheVersion, rootFilenames, options: this.options })}`; - let dependencyTreeFile = `${this.cacheRoot}/tree`; - if (fs.existsSync(dependencyTreeFile)) - { - let data = fs.readFileSync(`${this.cacheRoot}/tree`, "utf8"); - - this.dependencyTree = graph.json.read(JSON.parse(data)); - } - else - this.dependencyTree = new graph.Graph({ directed: true }); + mkdirp.sync(this.cacheRoot); + this.dependencyTree = new graph.Graph({ directed: true }); this.dependencyTree.setDefaultNodeLabel((_node: string) => { return { dirty: false }; }); } - public walkTree(cb: (id: string) => void | false) + public walkTree(cb: (id: string) => void | false): void { - _.each(graph.alg.topsort(this.dependencyTree), (id: string) => cb(id)); + const acyclic = graph.alg.isAcyclic(this.dependencyTree); + + if (acyclic) + { + _.each(graph.alg.topsort(this.dependencyTree), (id: string) => cb(id)); + return; + } + + // console.log("cycles detected in dependency graph, not sorting"); + _.each(this.dependencyTree.nodes(), (id: string) => cb(id)); } public setDependency(importee: string, importer: string): void { - // importer -> importee + // console.log(importer, "->", importee); + // importee -> importer this.dependencyTree.setEdge(importer, importee); } - public lastDependencySet() + public lastDependencySet(): void { this.treeComplete = true; } - public markAsDirty(id: string, _snapshot: ts.IScriptSnapshot) + public markAsDirty(id: string, _snapshot: ts.IScriptSnapshot): void { this.dependencyTree.setNode(id, { dirty: true }); } @@ -65,12 +75,27 @@ export class Cache { let label = this.dependencyTree.node(id) as INodeLabel; - if (checkImports || label.dirty) + if (!label) + return false; + + if (!checkImports || label.dirty) return label.dirty; let dependencies = graph.alg.dijkstra(this.dependencyTree, id); - return _.some(_.keys(dependencies), (dependencyId: string) => (this.dependencyTree.node(dependencyId) as INodeLabel).dirty); + return _.some(dependencies, (dependency, node) => + { + if (!node || dependency.distance === Infinity) + return false; + + let l = this.dependencyTree.node(node) as INodeLabel | undefined; + let dirty = l === undefined ? true : l.dirty; + + if (dirty) + console.log(`dirty: ${id} -> ${node}`); + + return dirty; + }); } public getCompiled(id: string, snapshot: ts.IScriptSnapshot, transform: () => ICode | undefined): ICode | undefined @@ -79,6 +104,7 @@ export class Cache if (!fs.existsSync(path) || this.isDirty(id, snapshot, false)) { + // console.log(`compile cache miss: ${id}`); let data = transform(); this.setCache(path, id, snapshot, data); return data; @@ -87,21 +113,22 @@ export class Cache return JSON.parse(fs.readFileSync(path, "utf8")) as ICode; } - public getDiagnostics(id: string, snapshot: ts.IScriptSnapshot, check: () => ts.Diagnostic[]): ts.Diagnostic[] + public getDiagnostics(id: string, snapshot: ts.IScriptSnapshot, check: () => ts.Diagnostic[]): IDiagnostics[] { let path = `${this.makePath(id, snapshot)}.diagnostics`; if (!fs.existsSync(path) || this.isDirty(id, snapshot, true)) { - let data = check(); + // console.log(`diagnostics cache miss: ${id}`); + let data = this.convert(check()); this.setCache(path, id, snapshot, data); return data; } - return JSON.parse(fs.readFileSync(path, "utf8")) as ts.Diagnostic[]; + return JSON.parse(fs.readFileSync(path, "utf8")) as IDiagnostics[]; } - private setCache(path: string, id: string, snapshot: ts.IScriptSnapshot, data: ts.Diagnostic[] | ICode | undefined): void + private setCache(path: string, id: string, snapshot: ts.IScriptSnapshot, data: IDiagnostics[] | ICode | undefined): void { if (data === undefined) return; @@ -116,4 +143,23 @@ export class Cache let data = snapshot.getText(0, snapshot.getLength()); return `${this.cacheRoot}/${hash.sha1({ data, id })}`; } + + private convert(data: ts.Diagnostic[]): IDiagnostics[] + { + return _.map(data, (diagnostic) => + { + let entry: IDiagnostics = + { + flatMessage: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"), + }; + + if (diagnostic.file) + { + let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + entry.fileLine = `${diagnostic.file.fileName} (${line + 1},${character + 1})`; + } + + return entry; + }); + } } diff --git a/src/index.ts b/src/index.ts index 35ed8ce..78ff31a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { LanguageServiceHost } from "./host"; -import { Cache, ICode } from "./cache"; +import { Cache, ICode, IDiagnostics } from "./cache"; import * as ts from "typescript"; import { createFilter } from "rollup-pluginutils"; import * as fs from "fs"; @@ -63,6 +63,9 @@ try function parseTsConfig() { const fileName = findFile(process.cwd(), "tsconfig.json"); + if (!fileName) + throw new Error(`couldn't find 'tsconfig.json' in ${process.cwd()}`); + const text = ts.sys.readFile(fileName); const result = ts.parseConfigFileTextToJson(fileName, text); const configParseResult = ts.parseJsonConfigFileContent(result.config, ts.sys, path.dirname(fileName), getOptionsOverrides(), fileName); @@ -79,44 +82,41 @@ interface Context warn(message: Message): void; error(message: Message): void; } -function printDiagnostics(diagnostics: ts.Diagnostic[]) +function printDiagnostics(diagnostics: IDiagnostics[]) { - diagnostics.forEach((diagnostic) => + _.each(diagnostics, (diagnostic) => { - let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); - if (diagnostic.file) - { - let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); - console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); - } + if (diagnostic.fileLine) + console.log(`${diagnostic.fileLine}: ${diagnostic.flatMessage}`); else - console.log(message); + console.log(diagnostic.flatMessage); }); }; -export default function typescript (options: any) +interface IOptions +{ + include?: string; + exclude?: string; +} + +export default function typescript (options: IOptions) { options = { ... options }; const filter = createFilter(options.include || [ "*.ts+(|x)", "**/*.ts+(|x)" ], options.exclude || [ "*.d.ts", "**/*.d.ts" ]); - delete options.include; - delete options.exclude; - let parsedConfig = parseTsConfig(); const servicesHost = new LanguageServiceHost(parsedConfig); const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); - const cache = new Cache(process.cwd(), parsedConfig.options, parsedConfig.fileNames); + const cache = new Cache(`${process.cwd()}/.rts2_cache`, parsedConfig.options, parsedConfig.fileNames); return { resolveId(importee: string, importer: string) { - cache.setDependency(importee, importer); - if (importee === TSLIB) return "\0" + TSLIB; @@ -132,6 +132,9 @@ export default function typescript (options: any) if (_.endsWith(result.resolvedModule.resolvedFileName, ".d.ts")) return null; + if (filter(result.resolvedModule.resolvedFileName)) + cache.setDependency(result.resolvedModule.resolvedFileName, importer); + return result.resolvedModule.resolvedFileName; } @@ -146,13 +149,12 @@ export default function typescript (options: any) return undefined; }, - transform(this: Context, code: string, id: string): ICode | null + transform(this: Context, code: string, id: string): ICode | undefined { if (!filter(id)) - return null; + return undefined; const snapshot = servicesHost.setSnapshot(id, code); - let result = cache.getCompiled(id, snapshot, () => { const output = services.getEmitOutput(id); @@ -160,8 +162,8 @@ export default function typescript (options: any) if (output.emitSkipped) this.error({ message: `failed to transpile ${id}`}); - const transpiled: ts.OutputFile = _.find(output.outputFiles, (entry: ts.OutputFile) => _.endsWith(entry.name, ".js") ); - const map: ts.OutputFile = _.find(output.outputFiles, (entry: ts.OutputFile) => _.endsWith(entry.name, ".map") ); + const transpiled = _.find(output.outputFiles, (entry: ts.OutputFile) => _.endsWith(entry.name, ".js") ); + const map = _.find(output.outputFiles, (entry: ts.OutputFile) => _.endsWith(entry.name, ".map") ); return { code: transpiled ? transpiled.text : undefined, @@ -179,6 +181,13 @@ export default function typescript (options: any) cache.walkTree((id: string) => { const snapshot = servicesHost.getScriptSnapshot(id); + + if (!snapshot) + { + console.log(`failed lo load snapshot for ${id}`); + return; + } + const diagnostics = cache.getDiagnostics(id, snapshot, () => { return services @@ -186,11 +195,8 @@ export default function typescript (options: any) .concat(services.getSyntacticDiagnostics(id)) .concat(services.getSemanticDiagnostics(id)); }); - if (diagnostics.length !== 0) - { - console.log(id); - printDiagnostics(diagnostics); - } + + printDiagnostics(diagnostics); }); }, }; diff --git a/tsconfig.json b/tsconfig.json index 7f6146b..26ac9c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,8 @@ "listFiles": true, "pretty": true, "moduleResolution": "node", - "noEmitOnError": true + "noEmitOnError": true, + "strictNullChecks": true }, "include": [ "typings/**/*.d.ts",