diff --git a/dist/bundler/bundler.js b/dist/bundler/bundler.js index d6e123bd..f52bfae0 100644 --- a/dist/bundler/bundler.js +++ b/dist/bundler/bundler.js @@ -1,8 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var async = require("async"); -var combineSourceMap = require("combine-source-map"); -var convertSourceMap = require("convert-source-map"); var fs = require("fs"); var lodash = require("lodash"); var os = require("os"); @@ -12,13 +10,14 @@ var benchmark_1 = require("../shared/benchmark"); var PathTool = require("../shared/path-tool"); var bundle_item_1 = require("./bundle-item"); var Bundler = (function () { - function Bundler(config, dependencyWalker, globals, log, project, resolver, transformer, validator) { + function Bundler(config, dependencyWalker, globals, log, project, resolver, sourceMap, transformer, validator) { this.config = config; this.dependencyWalker = dependencyWalker; this.globals = globals; this.log = log; this.project = project; this.resolver = resolver; + this.sourceMap = sourceMap; this.transformer = transformer; this.validator = validator; this.BUNDLE_DELAY = 500; @@ -55,7 +54,7 @@ var Bundler = (function () { var benchmark = new benchmark_1.Benchmark(); this.transformer.applyTsTransforms(this.bundleQueue, function () { _this.bundleQueue.forEach(function (queued) { - queued.item = new bundle_item_1.BundleItem(queued.file.path, queued.file.originalPath, _this.createInlineSourceMap(queued)); + queued.item = new bundle_item_1.BundleItem(queued.file.path, queued.file.originalPath, _this.sourceMap.createInlineSourceMap(queued)); }); var dependencyCount = _this.dependencyWalker.collectTypescriptDependencies(_this.bundleQueue); if (_this.shouldBundle(dependencyCount)) { @@ -66,18 +65,6 @@ var Bundler = (function () { } }); }; - Bundler.prototype.createInlineSourceMap = function (queued) { - var inlined = queued.emitOutput.outputText; - if (queued.emitOutput.sourceMapText) { - var map = convertSourceMap - .fromJSON(queued.emitOutput.sourceMapText) - .addProperty("sourcesContent", [queued.emitOutput.sourceFile.text]); - inlined = convertSourceMap.removeMapFileComments(queued.emitOutput.outputText) + map.toComment(); - // used by Karma to log errors with original source code line numbers - queued.file.sourceMap = map.toObject(); - } - return inlined; - }; Bundler.prototype.shouldBundle = function (dependencyCount) { if (this.config.hasPreprocessor("commonjs")) { this.log.debug("Preprocessor 'commonjs' detected, code will NOT be bundled"); @@ -183,35 +170,23 @@ var Bundler = (function () { Bundler.prototype.writeMainBundleFile = function (onMainBundleFileWritten) { var _this = this; var bundle = "(function(global){" + os.EOL + "global.wrappers={};" + os.EOL; - var sourcemap = combineSourceMap.create(); - var line = this.getNumberOfNewlines(bundle); + this.sourceMap.initialize(bundle); this.bundleBuffer.forEach(function (bundleItem) { - if (_this.config.bundlerOptions.sourceMap) { - var sourceFile = path.relative(_this.config.karma.basePath, bundleItem.filename); - sourcemap.addFile({ sourceFile: path.join("/base", sourceFile), source: bundleItem.source }, { line: line }); - } + _this.sourceMap.addFile(bundleItem); var wrapped = _this.addLoaderFunction(bundleItem, false); bundle += wrapped; - if (_this.config.bundlerOptions.sourceMap) { - line += _this.getNumberOfNewlines(wrapped); - } + _this.sourceMap.offsetLineNumber(wrapped); }); bundle += this.createEntrypointFilenames() + "})(this);" + os.EOL; - if (this.config.bundlerOptions.sourceMap) { - bundle += sourcemap.comment(); - } + bundle += this.sourceMap.getComment(); + this.validator.validate(bundle, this.bundleFile.name); fs.writeFile(this.bundleFile.name, bundle, function (error) { if (error) { throw error; } - _this.validator.validate(bundle, _this.bundleFile.name); onMainBundleFileWritten(); }); }; - Bundler.prototype.getNumberOfNewlines = function (source) { - var newlines = source.match(/\n/g); - return newlines ? newlines.length : 0; - }; return Bundler; }()); exports.Bundler = Bundler; diff --git a/dist/bundler/resolve/source-reader.js b/dist/bundler/resolve/source-reader.js index 1e9282be..321346a2 100644 --- a/dist/bundler/resolve/source-reader.js +++ b/dist/bundler/resolve/source-reader.js @@ -1,7 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var acorn = require("acorn"); -var combineSourceMap = require("combine-source-map"); var fs = require("fs"); var os = require("os"); var SourceReader = (function () { @@ -13,7 +12,7 @@ var SourceReader = (function () { SourceReader.prototype.read = function (bundleItem, onSourceRead) { var _this = this; this.readFile(bundleItem, function (source) { - bundleItem.source = combineSourceMap.removeComments(source); + bundleItem.source = source; bundleItem.ast = _this.createAbstractSyntaxTree(bundleItem); _this.transformer.applyTransforms(bundleItem, function () { _this.assertValidNonScriptSource(bundleItem); diff --git a/dist/bundler/source-map.js b/dist/bundler/source-map.js new file mode 100644 index 00000000..31d6614e --- /dev/null +++ b/dist/bundler/source-map.js @@ -0,0 +1,69 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var combineSourceMap = require("combine-source-map"); +var convertSourceMap = require("convert-source-map"); +var fs = require("fs"); +var path = require("path"); +var SourceMap = (function () { + function SourceMap(config) { + this.config = config; + this.line = 0; + } + SourceMap.prototype.initialize = function (bundle) { + this.combiner = combineSourceMap.create(); + this.line = this.getNumberOfNewlines(bundle); + }; + SourceMap.prototype.createInlineSourceMap = function (queued) { + var inlined = queued.emitOutput.outputText; + if (queued.emitOutput.sourceMapText) { + var map = convertSourceMap + .fromJSON(queued.emitOutput.sourceMapText) + .addProperty("sourcesContent", [queued.emitOutput.sourceFile.text]); + inlined = combineSourceMap.removeComments(queued.emitOutput.outputText) + map.toComment(); + // used by Karma to log errors with original source code line numbers + queued.file.sourceMap = map.toObject(); + } + return inlined; + }; + SourceMap.prototype.addFile = function (bundleItem) { + if (this.config.bundlerOptions.sourceMap) { + this.loadFileFromComment(bundleItem); + var sourceFile = path.relative(this.config.karma.basePath, bundleItem.filename); + this.combiner.addFile({ sourceFile: path.join("/base", sourceFile), source: bundleItem.source }, { line: this.line }); + } + bundleItem.source = combineSourceMap.removeComments(bundleItem.source); + }; + SourceMap.prototype.offsetLineNumber = function (wrappedSource) { + if (this.config.bundlerOptions.sourceMap) { + this.line += this.getNumberOfNewlines(wrappedSource); + } + }; + SourceMap.prototype.getComment = function () { + return this.config.bundlerOptions.sourceMap ? this.combiner.comment() : ""; + }; + SourceMap.prototype.loadFileFromComment = function (bundleItem) { + var commentMatch = convertSourceMap.mapFileCommentRegex.exec(bundleItem.source); + if (commentMatch && !commentMatch[1].startsWith("data:")) { + var dirname_1 = path.dirname(bundleItem.filename); + var mapFilename = path.join(dirname_1, commentMatch[1]); + var mapJson = fs.readFileSync(mapFilename, "utf-8"); + var map = convertSourceMap.fromJSON(mapJson); + if (!map.getProperty("sourcesContent")) { + var sourcesContent_1 = []; + map.getProperty("sources").forEach(function (source) { + var sourceFilename = path.join(dirname_1, source); + var sourceContent = fs.readFileSync(sourceFilename, "utf-8"); + sourcesContent_1.push(sourceContent); + }); + map.addProperty("sourcesContent", sourcesContent_1); + } + bundleItem.source = combineSourceMap.removeComments(bundleItem.source) + map.toComment(); + } + }; + SourceMap.prototype.getNumberOfNewlines = function (source) { + var newlines = source.match(/\n/g); + return newlines ? newlines.length : 0; + }; + return SourceMap; +}()); +exports.SourceMap = SourceMap; diff --git a/dist/index.js b/dist/index.js index 0b5d7e97..3d1e9fd3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6,6 +6,7 @@ var dependency_walker_1 = require("./bundler/dependency-walker"); var globals_1 = require("./bundler/globals"); var resolver_1 = require("./bundler/resolve/resolver"); var source_reader_1 = require("./bundler/resolve/source-reader"); +var source_map_1 = require("./bundler/source-map"); var transformer_1 = require("./bundler/transformer"); var validator_1 = require("./bundler/validator"); var compiler_1 = require("./compiler/compiler"); @@ -37,7 +38,8 @@ var validator = new validator_1.Validator(configuration); var sourceReader = new source_reader_1.SourceReader(configuration, loggers.sourceReader, transformer); var resolver = new resolver_1.Resolver(configuration, dependencyWalker, loggers.resolver, sourceReader); var globals = new globals_1.Globals(configuration, resolver); -var bundler = new bundler_1.Bundler(configuration, dependencyWalker, globals, loggers.bundler, project, resolver, transformer, validator); +var sourceMap = new source_map_1.SourceMap(configuration); +var bundler = new bundler_1.Bundler(configuration, dependencyWalker, globals, loggers.bundler, project, resolver, sourceMap, transformer, validator); var framework = new framework_1.Framework(bundler, configuration, resolver); var preprocessor = new preprocessor_1.Preprocessor(bundler, compiler, configuration, coverage, sharedProcessedFiles); var reporter = new reporter_1.Reporter(configuration, sharedProcessedFiles, threshold); diff --git a/src/bundler/bundler.ts b/src/bundler/bundler.ts index 69658c9d..c1231fb7 100644 --- a/src/bundler/bundler.ts +++ b/src/bundler/bundler.ts @@ -1,6 +1,4 @@ import * as async from "async"; -import * as combineSourceMap from "combine-source-map"; -import * as convertSourceMap from "convert-source-map"; import * as fs from "fs"; import * as lodash from "lodash"; import * as os from "os"; @@ -16,6 +14,7 @@ import { Configuration } from "../shared/configuration"; import { File } from "../shared/file"; import { Project } from "../shared/project"; import { Globals } from "./globals"; +import { SourceMap } from "./source-map"; import PathTool = require("../shared/path-tool"); import { BundleCallback } from "./bundle-callback"; import { BundleItem } from "./bundle-item"; @@ -46,6 +45,7 @@ export class Bundler { private log: Logger, private project: Project, private resolver: Resolver, + private sourceMap: SourceMap, private transformer: Transformer, private validator: Validator) { } @@ -78,7 +78,7 @@ export class Bundler { this.transformer.applyTsTransforms(this.bundleQueue, () => { this.bundleQueue.forEach((queued) => { queued.item = new BundleItem( - queued.file.path, queued.file.originalPath, this.createInlineSourceMap(queued)); + queued.file.path, queued.file.originalPath, this.sourceMap.createInlineSourceMap(queued)); }); let dependencyCount = this.dependencyWalker.collectTypescriptDependencies(this.bundleQueue); @@ -92,21 +92,6 @@ export class Bundler { }); } - private createInlineSourceMap(queued: Queued): string { - let inlined = queued.emitOutput.outputText; - if (queued.emitOutput.sourceMapText) { - - let map = convertSourceMap - .fromJSON(queued.emitOutput.sourceMapText) - .addProperty("sourcesContent", [queued.emitOutput.sourceFile.text]); - inlined = convertSourceMap.removeMapFileComments(queued.emitOutput.outputText) + map.toComment(); - - // used by Karma to log errors with original source code line numbers - queued.file.sourceMap = map.toObject(); - } - return inlined; - } - private shouldBundle(dependencyCount: number): boolean { if (this.config.hasPreprocessor("commonjs")) { this.log.debug("Preprocessor 'commonjs' detected, code will NOT be bundled"); @@ -230,42 +215,28 @@ export class Bundler { private writeMainBundleFile(onMainBundleFileWritten: { (): void } ) { let bundle = "(function(global){" + os.EOL + "global.wrappers={};" + os.EOL; - let sourcemap = combineSourceMap.create(); - let line = this.getNumberOfNewlines(bundle); + this.sourceMap.initialize(bundle); this.bundleBuffer.forEach((bundleItem) => { - if (this.config.bundlerOptions.sourceMap) { - let sourceFile = path.relative(this.config.karma.basePath, bundleItem.filename); - sourcemap.addFile( - { sourceFile: path.join("/base", sourceFile), source: bundleItem.source }, - { line } - ); - } + this.sourceMap.addFile(bundleItem); let wrapped = this.addLoaderFunction(bundleItem, false); bundle += wrapped; - if (this.config.bundlerOptions.sourceMap) { - line += this.getNumberOfNewlines(wrapped); - } + + this.sourceMap.offsetLineNumber(wrapped); }); bundle += this.createEntrypointFilenames() + "})(this);" + os.EOL; - if (this.config.bundlerOptions.sourceMap) { - bundle += sourcemap.comment(); - } + bundle += this.sourceMap.getComment(); + + this.validator.validate(bundle, this.bundleFile.name); fs.writeFile(this.bundleFile.name, bundle, (error) => { if (error) { throw error; } - this.validator.validate(bundle, this.bundleFile.name); onMainBundleFileWritten(); }); } - - private getNumberOfNewlines(source: any) { - let newlines = source.match(/\n/g); - return newlines ? newlines.length : 0; - } } diff --git a/src/bundler/resolve/source-reader.ts b/src/bundler/resolve/source-reader.ts index 13bb3b52..63bd327c 100644 --- a/src/bundler/resolve/source-reader.ts +++ b/src/bundler/resolve/source-reader.ts @@ -1,5 +1,4 @@ import * as acorn from "acorn"; -import * as combineSourceMap from "combine-source-map"; import * as ESTree from "estree"; import * as fs from "fs"; import * as os from "os"; @@ -20,7 +19,7 @@ export class SourceReader { this.readFile(bundleItem, (source: string) => { - bundleItem.source = combineSourceMap.removeComments(source); + bundleItem.source = source; bundleItem.ast = this.createAbstractSyntaxTree(bundleItem); this.transformer.applyTransforms(bundleItem, () => { diff --git a/src/bundler/source-map.ts b/src/bundler/source-map.ts new file mode 100644 index 00000000..e072f3af --- /dev/null +++ b/src/bundler/source-map.ts @@ -0,0 +1,92 @@ +import * as combineSourceMap from "combine-source-map"; +import * as convertSourceMap from "convert-source-map"; +import * as fs from "fs"; +import * as path from "path"; + +import { Configuration } from "../shared/configuration"; +import { BundleItem } from "./bundle-item"; +import { Queued } from "./queued"; + +export class SourceMap { + + private combiner: Combiner; + private line: number = 0; + + constructor(private config: Configuration) {} + + public initialize(bundle: string) { + this.combiner = combineSourceMap.create(); + this.line = this.getNumberOfNewlines(bundle); + } + + public createInlineSourceMap(queued: Queued): string { + let inlined = queued.emitOutput.outputText; + if (queued.emitOutput.sourceMapText) { + + let map = convertSourceMap + .fromJSON(queued.emitOutput.sourceMapText) + .addProperty("sourcesContent", [queued.emitOutput.sourceFile.text]); + inlined = combineSourceMap.removeComments(queued.emitOutput.outputText) + map.toComment(); + + // used by Karma to log errors with original source code line numbers + queued.file.sourceMap = map.toObject(); + } + return inlined; + } + + public addFile(bundleItem: BundleItem) { + + if (this.config.bundlerOptions.sourceMap) { + + this.loadFileFromComment(bundleItem); + + let sourceFile = path.relative(this.config.karma.basePath, bundleItem.filename); + this.combiner.addFile( + { sourceFile: path.join("/base", sourceFile), source: bundleItem.source }, + { line: this.line } + ); + } + + bundleItem.source = combineSourceMap.removeComments(bundleItem.source); + } + + public offsetLineNumber(wrappedSource: string) { + if (this.config.bundlerOptions.sourceMap) { + this.line += this.getNumberOfNewlines(wrappedSource); + } + } + + public getComment() { + return this.config.bundlerOptions.sourceMap ? this.combiner.comment() : ""; + } + + public loadFileFromComment(bundleItem: BundleItem) { + + let commentMatch = convertSourceMap.mapFileCommentRegex.exec(bundleItem.source); + + if (commentMatch && !commentMatch[1].startsWith("data:")) { + let dirname = path.dirname(bundleItem.filename); + let mapFilename = path.join(dirname, commentMatch[1]); + let mapJson = fs.readFileSync(mapFilename, "utf-8"); + let map = convertSourceMap.fromJSON(mapJson); + + if (!map.getProperty("sourcesContent")) { + + let sourcesContent: string[] = []; + map.getProperty("sources").forEach((source: string) => { + let sourceFilename = path.join(dirname, source); + let sourceContent = fs.readFileSync(sourceFilename, "utf-8"); + sourcesContent.push(sourceContent); + }); + map.addProperty("sourcesContent", sourcesContent); + } + + bundleItem.source = combineSourceMap.removeComments(bundleItem.source) + map.toComment(); + } + } + + private getNumberOfNewlines(source: any) { + let newlines = source.match(/\n/g); + return newlines ? newlines.length : 0; + } +} diff --git a/src/index.ts b/src/index.ts index 071c1985..16e9a935 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { DependencyWalker } from "./bundler/dependency-walker"; import { Globals } from "./bundler/globals"; import { Resolver } from "./bundler/resolve/resolver"; import { SourceReader } from "./bundler/resolve/source-reader"; +import { SourceMap } from "./bundler/source-map"; import { Transformer } from "./bundler/transformer"; import { Validator } from "./bundler/validator"; @@ -46,9 +47,10 @@ let validator = new Validator(configuration); let sourceReader = new SourceReader(configuration, loggers.sourceReader, transformer); let resolver = new Resolver(configuration, dependencyWalker, loggers.resolver, sourceReader); let globals = new Globals(configuration, resolver); +let sourceMap = new SourceMap(configuration); let bundler = new Bundler(configuration, dependencyWalker, globals, loggers.bundler, - project, resolver, transformer, validator); + project, resolver, sourceMap, transformer, validator); let framework = new Framework(bundler, configuration, resolver); let preprocessor = new Preprocessor(bundler, compiler, configuration, coverage, sharedProcessedFiles);