From 176347d65281cc8880099242e8fe0c1684c6f5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 6 Aug 2014 11:37:56 +0200 Subject: [PATCH 1/5] Added loader-utils as dep --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0893d47..d8923c0 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dependencies": { "source-map": "^0.1.33", "typescript": "^1.0.0", - "typescript-api": "^1.0.0" + "typescript-api": "^1.0.0", + "loader-utils": "^0.2.2" }, "repository": { "type": "git", From 5e32f3a9f32c814fc342ca9e3e6e5d596dbca75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Mon, 25 Aug 2014 13:24:33 +0200 Subject: [PATCH 2/5] Fixed issue with scope --- index.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 02f340e..6f5c3f6 100644 --- a/index.js +++ b/index.js @@ -39,9 +39,10 @@ function dumpDiagnostics(loader, diagnostics) { function useCache() { var query = loaderUtils.parseQuery(this.query); + + var cacheIsDisabled = (query.cache === 'false'); - if(query.cache === false) return false; - return true; + return !cacheIsDisabled; } var kResolverHost = { @@ -125,7 +126,9 @@ var kInstance = new Instance(); module.exports = function (source) { this.cacheable && this.cacheable(true); - + + useCache = useCache.bind(this); + if(!useCache()) { kInstance.clearDependencies(); } From 17c57a67ca6c9eca3654d552f74dc02676669757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Tue, 26 Aug 2014 16:14:20 +0200 Subject: [PATCH 3/5] Added better cache to handle changing files --- index.js | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index 6f5c3f6..2c9f2ce 100644 --- a/index.js +++ b/index.js @@ -37,22 +37,18 @@ function dumpDiagnostics(loader, diagnostics) { return hadErrors; } -function useCache() { - var query = loaderUtils.parseQuery(this.query); - - var cacheIsDisabled = (query.cache === 'false'); - - return !cacheIsDisabled; -} - var kResolverHost = { snapshotCache: {}, - getScriptSnapshot: function (fileName) { - var snapshot = this.snapshotCache[fileName]; - if (!useCache() || !snapshot) { - snapshot = this.snapshotCache[fileName] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); + getScriptSnapshotModified: function (fileName) { + var lastModified = fs.statSync(fileName).mtime; + var snapshot = this.snapshotCache[fileName + lastModified]; + if (!snapshot) { + snapshot = this.snapshotCache[fileName + lastModified] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); } - return snapshot; + return {lastModified: lastModified, snapshot: snapshot}; + }, + getScriptSnapshot: function (fileName) { + return this.getScriptSnapshotModified(fileName).snapshot; }, resolveRelativePath: function (file, from) { return path.resolve(from, file); @@ -76,18 +72,18 @@ function Instance() { Instance.prototype = { addFileIfNecessary: function (path, references) { - if (useCache() && this.dependencies[path]) { - return; - } - var scriptSnapshot = kResolverHost.getScriptSnapshot(path); - this.dependencies[path] = true; + var scriptSnapshotInfo = kResolverHost.getScriptSnapshotModified(path); + var cacheKey = path + scriptSnapshotInfo.lastModified; + if(this.dependencies[cacheKey]) return; + this.dependencies[cacheKey] = true; this.compiler.addFile( path, - scriptSnapshot, + scriptSnapshotInfo.snapshot, ts.ByteOrderMark.Utf8, 0, // version true, // isOpen references); // referencedFiles + }, clearDependencies: function () { this.dependencies = []; @@ -124,19 +120,15 @@ Instance.prototype = { var kInstance = new Instance(); +var analyzedCache = {}; + module.exports = function (source) { this.cacheable && this.cacheable(true); - useCache = useCache.bind(this); - - if(!useCache()) { - kInstance.clearDependencies(); - } - var hadErrors = false; var resolutionResults = ts.ReferenceResolver.resolve([this.resourcePath], kResolverHost, true); - + resolutionResults.diagnostics.forEach(function (diag) { var wasError = handleDiagnostic(this, diag); hadErrors = hadErrors || wasError; @@ -157,13 +149,22 @@ module.exports = function (source) { // Start looking for errors resolutionResults.resolvedFiles.forEach(function (file) { + var lastModified = fs.statSync(file.path); + var cacheKey = file.path + lastModified; + + if(analyzedCache[cacheKey]) return; + analyzedCache[cacheKey] = true; + var syntaxErrors = kInstance.compiler.getSyntacticDiagnostics(file.path); + if (syntaxErrors.length > 0) { dumpDiagnostics(this, syntaxErrors); hadErrors = true; return; } + var semanticErrors = kInstance.compiler.getSemanticDiagnostics(file.path); + if (semanticErrors.length > 0) { dumpDiagnostics(this, semanticErrors); hadErrors = true; From 366e4d1e49880a8f5a1b27606f8cc7da34afd332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Tue, 26 Aug 2014 16:40:20 +0200 Subject: [PATCH 4/5] Correct indent --- index.js | 301 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 152 insertions(+), 149 deletions(-) diff --git a/index.js b/index.js index 2c9f2ce..4d4072c 100644 --- a/index.js +++ b/index.js @@ -5,179 +5,182 @@ var ts = require('typescript-api'); var sm = require('source-map'); var loaderUtils = require('loader-utils'); -var kCompilationOptions = (function () { - var settings = new ts.CompilationSettings(); - settings.codeGenTarget = ts.LanguageVersion.EcmaScript5; - settings.moduleGenTarget = ts.ModuleGenTarget.Synchronous; - settings.mapSourceFiles = true; - return ts.ImmutableCompilationSettings.fromCompilationSettings(settings); +var kCompilationOptions = (function() { + var settings = new ts.CompilationSettings(); + settings.codeGenTarget = ts.LanguageVersion.EcmaScript5; + settings.moduleGenTarget = ts.ModuleGenTarget.Synchronous; + settings.mapSourceFiles = true; + return ts.ImmutableCompilationSettings.fromCompilationSettings(settings); })(); function handleDiagnostic(loader, diag) { - var wasError = false; - var info = diag.info(); - var formattedMessage = util.format("%s(%d,%d) %s", diag.fileName(), diag.line()+1, diag.character()+1, diag.text()); - if (info.category == ts.DiagnosticCategory.Warning) { - loader.emitWarning(formattedMessage); - } else if (info.category == ts.DiagnosticCategory.Error) { - loader.emitError(formattedMessage); - wasError = true; - } else { - console.info(info.message); - } - return wasError; + var wasError = false; + var info = diag.info(); + var formattedMessage = util.format("%s(%d,%d) %s", diag.fileName(), diag.line() + 1, diag.character() + 1, diag.text()); + if (info.category == ts.DiagnosticCategory.Warning) { + loader.emitWarning(formattedMessage); + } else if (info.category == ts.DiagnosticCategory.Error) { + loader.emitError(formattedMessage); + wasError = true; + } else { + console.info(info.message); + } + return wasError; } function dumpDiagnostics(loader, diagnostics) { - var hadErrors = false; - diagnostics.forEach(function (diag) { - var wasError = handleDiagnostic(loader, diag); - hadErrors = hadErrors || wasError; - }); - return hadErrors; + var hadErrors = false; + diagnostics.forEach(function(diag) { + var wasError = handleDiagnostic(loader, diag); + hadErrors = hadErrors || wasError; + }); + return hadErrors; } var kResolverHost = { - snapshotCache: {}, - getScriptSnapshotModified: function (fileName) { - var lastModified = fs.statSync(fileName).mtime; - var snapshot = this.snapshotCache[fileName + lastModified]; - if (!snapshot) { - snapshot = this.snapshotCache[fileName + lastModified] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); - } - return {lastModified: lastModified, snapshot: snapshot}; - }, - getScriptSnapshot: function (fileName) { - return this.getScriptSnapshotModified(fileName).snapshot; - }, - resolveRelativePath: function (file, from) { - return path.resolve(from, file); - }, - fileExists: function (path) { - return fs.existsSync(path); - }, - directoryExists: function (path) { - return fs.existsSync(path) && fs.statSync(path).isDirectory(); - }, - getParentDirectory: function (p) { - var dir = path.dirname(p); - return dir === p ? null : dir; - }, + snapshotCache: {}, + getScriptSnapshotModified: function(fileName) { + var lastModified = fs.statSync(fileName).mtime; + var snapshot = this.snapshotCache[fileName + lastModified]; + if (!snapshot) { + snapshot = this.snapshotCache[fileName + lastModified] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); + } + return { + lastModified: lastModified, + snapshot: snapshot + }; + }, + getScriptSnapshot: function(fileName) { + return this.getScriptSnapshotModified(fileName).snapshot; + }, + resolveRelativePath: function(file, from) { + return path.resolve(from, file); + }, + fileExists: function(path) { + return fs.existsSync(path); + }, + directoryExists: function(path) { + return fs.existsSync(path) && fs.statSync(path).isDirectory(); + }, + getParentDirectory: function(p) { + var dir = path.dirname(p); + return dir === p ? null : dir; + }, }; function Instance() { - this.compiler = new ts.TypeScriptCompiler(new ts.NullLogger(), kCompilationOptions); - this.dependencies = {}; + this.compiler = new ts.TypeScriptCompiler(new ts.NullLogger(), kCompilationOptions); + this.dependencies = {}; } Instance.prototype = { - addFileIfNecessary: function (path, references) { - var scriptSnapshotInfo = kResolverHost.getScriptSnapshotModified(path); - var cacheKey = path + scriptSnapshotInfo.lastModified; - if(this.dependencies[cacheKey]) return; - this.dependencies[cacheKey] = true; - this.compiler.addFile( - path, - scriptSnapshotInfo.snapshot, - ts.ByteOrderMark.Utf8, - 0, // version - true, // isOpen - references); // referencedFiles - - }, - clearDependencies: function () { - this.dependencies = []; - }, - compiledOutputFor: function (sourcePath, source) { - var output = this.compiler.emit(sourcePath, function (pathToResolve) { - throw new Error("No idea how to resolve path " + pathToResolve); - }); - - var transformedSource; - var sourceMap; - - output.outputFiles.forEach(function (f) { - if (f.fileType == ts.OutputFileType.JavaScript) { - // Strip the source map reference as it's not needed and is a bit confusing. - transformedSource = f.text.replace(/\/\/# sourceMappingURL=.+/, "", "m");; - } else if (f.fileType == ts.OutputFileType.SourceMap) { - sourceMap = JSON.parse(f.text); - } - }); - - if (sourceMap) { - delete sourceMap.file; - sourceMap.sources = [path.relative(path.resolve('.'), sourcePath)]; - sourceMap.sourcesContent = [source]; + addFileIfNecessary: function(path, references) { + var scriptSnapshotInfo = kResolverHost.getScriptSnapshotModified(path); + var cacheKey = path + scriptSnapshotInfo.lastModified; + if (this.dependencies[cacheKey]) return; + this.dependencies[cacheKey] = true; + this.compiler.addFile( + path, + scriptSnapshotInfo.snapshot, + ts.ByteOrderMark.Utf8, + 0, // version + true, // isOpen + references); // referencedFiles + + }, + clearDependencies: function() { + this.dependencies = []; + }, + compiledOutputFor: function(sourcePath, source) { + var output = this.compiler.emit(sourcePath, function(pathToResolve) { + throw new Error("No idea how to resolve path " + pathToResolve); + }); + + var transformedSource; + var sourceMap; + + output.outputFiles.forEach(function(f) { + if (f.fileType == ts.OutputFileType.JavaScript) { + // Strip the source map reference as it's not needed and is a bit confusing. + transformedSource = f.text.replace(/\/\/# sourceMappingURL=.+/, "", "m");; + } else if (f.fileType == ts.OutputFileType.SourceMap) { + sourceMap = JSON.parse(f.text); + } + }); + + if (sourceMap) { + delete sourceMap.file; + sourceMap.sources = [path.relative(path.resolve('.'), sourcePath)]; + sourceMap.sourcesContent = [source]; + } + + return { + source: transformedSource, + sourceMap: sourceMap, + }; } - - return { - source: transformedSource, - sourceMap: sourceMap, - }; - } }; var kInstance = new Instance(); var analyzedCache = {}; -module.exports = function (source) { - this.cacheable && this.cacheable(true); - - var hadErrors = false; - - var resolutionResults = ts.ReferenceResolver.resolve([this.resourcePath], kResolverHost, true); - - resolutionResults.diagnostics.forEach(function (diag) { - var wasError = handleDiagnostic(this, diag); - hadErrors = hadErrors || wasError; - }, this); - - if (hadErrors) { - return this.callback("Failed during compilation"); - } - - // Load the standard library out of the typescript module itself - var typescriptDefaultLib = path.dirname(require.resolve('typescript')) + "/lib.d.ts"; - this.addDependency(typescriptDefaultLib); - kInstance.addFileIfNecessary(typescriptDefaultLib); - resolutionResults.resolvedFiles.forEach(function (file) { - this.addDependency(file.path); - kInstance.addFileIfNecessary(file.path, file.referencedFiles); - }, this); - - // Start looking for errors - resolutionResults.resolvedFiles.forEach(function (file) { - var lastModified = fs.statSync(file.path); - var cacheKey = file.path + lastModified; - - if(analyzedCache[cacheKey]) return; - analyzedCache[cacheKey] = true; - - var syntaxErrors = kInstance.compiler.getSyntacticDiagnostics(file.path); - - if (syntaxErrors.length > 0) { - dumpDiagnostics(this, syntaxErrors); - hadErrors = true; - return; - } - - var semanticErrors = kInstance.compiler.getSemanticDiagnostics(file.path); - - if (semanticErrors.length > 0) { - dumpDiagnostics(this, semanticErrors); - hadErrors = true; - return; +module.exports = function(source) { + this.cacheable && this.cacheable(true); + + var hadErrors = false; + + var resolutionResults = ts.ReferenceResolver.resolve([this.resourcePath], kResolverHost, true); + + resolutionResults.diagnostics.forEach(function(diag) { + var wasError = handleDiagnostic(this, diag); + hadErrors = hadErrors || wasError; + }, this); + + if (hadErrors) { + return this.callback("Failed during compilation"); } - }, this); - if (hadErrors) { - return this.callback("Failed during compilation"); - } + // Load the standard library out of the typescript module itself + var typescriptDefaultLib = path.dirname(require.resolve('typescript')) + "/lib.d.ts"; + this.addDependency(typescriptDefaultLib); + kInstance.addFileIfNecessary(typescriptDefaultLib); + resolutionResults.resolvedFiles.forEach(function(file) { + this.addDependency(file.path); + kInstance.addFileIfNecessary(file.path, file.referencedFiles); + }, this); + + // Start looking for errors + resolutionResults.resolvedFiles.forEach(function(file) { + var lastModified = fs.statSync(file.path); + var cacheKey = file.path + lastModified; + + if (!analyzedCache[cacheKey]) { + analyzedCache[cacheKey] = true; + var syntaxErrors = kInstance.compiler.getSyntacticDiagnostics(file.path); + + if (syntaxErrors.length > 0) { + dumpDiagnostics(this, syntaxErrors); + hadErrors = true; + return; + } + } + + var semanticErrors = kInstance.compiler.getSemanticDiagnostics(file.path); + + if (semanticErrors.length > 0) { + dumpDiagnostics(this, semanticErrors); + hadErrors = true; + return; + } + }, this); + + if (hadErrors) { + return this.callback("Failed during compilation"); + } - var output = kInstance.compiledOutputFor(this.resourcePath, source); + var output = kInstance.compiledOutputFor(this.resourcePath, source); - this.compiled = true; - return this.callback(null, output.source, output.sourceMap); + this.compiled = true; + return this.callback(null, output.source, output.sourceMap); } From 0b30414c1ce3acaef27470130e98cea4f7e5221a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Wed, 27 Aug 2014 15:21:57 +0200 Subject: [PATCH 5/5] Completed whole new smart cache system with modified time support --- index.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 4d4072c..31f0481 100644 --- a/index.js +++ b/index.js @@ -39,11 +39,16 @@ function dumpDiagnostics(loader, diagnostics) { var kResolverHost = { snapshotCache: {}, + snapshotModifiedCache: {}, + getScriptSnapshotModified: function(fileName) { - var lastModified = fs.statSync(fileName).mtime; - var snapshot = this.snapshotCache[fileName + lastModified]; + var lastModified = fs.statSync(fileName).mtime.getTime(); + + var snapshot = (lastModified && this.snapshotModifiedCache[fileName] === lastModified) ? this.snapshotCache[fileName] : false; + if (!snapshot) { - snapshot = this.snapshotCache[fileName + lastModified] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); + this.snapshotModifiedCache[fileName] = lastModified; + snapshot = this.snapshotCache[fileName] = ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); } return { lastModified: lastModified, @@ -75,10 +80,11 @@ function Instance() { Instance.prototype = { addFileIfNecessary: function(path, references) { - var scriptSnapshotInfo = kResolverHost.getScriptSnapshotModified(path); - var cacheKey = path + scriptSnapshotInfo.lastModified; - if (this.dependencies[cacheKey]) return; - this.dependencies[cacheKey] = true; + var scriptSnapshotInfo = kResolverHost.getScriptSnapshotModified(path); + + if(this.dependencies[path] === scriptSnapshotInfo.lastModified) return; + this.dependencies[path] = scriptSnapshotInfo.lastModified; + this.compiler.addFile( path, scriptSnapshotInfo.snapshot, @@ -152,11 +158,10 @@ module.exports = function(source) { // Start looking for errors resolutionResults.resolvedFiles.forEach(function(file) { - var lastModified = fs.statSync(file.path); - var cacheKey = file.path + lastModified; - - if (!analyzedCache[cacheKey]) { - analyzedCache[cacheKey] = true; + var lastModified = fs.statSync(file.path).value; + + if (!analyzedCache[file.path] === lastModified) { + analyzedCache[file.path] = lastModified; var syntaxErrors = kInstance.compiler.getSyntacticDiagnostics(file.path); if (syntaxErrors.length > 0) {