Skip to content

Commit

Permalink
Major source map overhaul, #144
Browse files Browse the repository at this point in the history
  • Loading branch information
erikbarke committed Aug 12, 2017
1 parent 62288ae commit 493060a
Show file tree
Hide file tree
Showing 14 changed files with 108 additions and 83 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ If the defaults aren't enough, the settings can be configured from `karma.conf.j
* **karmaTypescriptConfig.bundlerOptions.resolve.directories** - An array of directories where modules will be recursively looked up.<br/>
Defaults to `["node_modules"]`.

* **karmaTypescriptConfig.bundlerOptions.sourceMap** - A boolean indicating whether source maps should be generated for imported modules in the bundle, useful for debugging in a browser.
For more debugging options, please see `karmaTypescriptConfig.coverageOptions.instrumentation`.</br>
Defaults to `false`.

* **karmaTypescriptConfig.bundlerOptions.transforms** - An array of functions altering or replacing compiled Typescript code/Javascript
code loaded from `node_modules` before bundling it.
For more detailed documentation on transforms, please refer to the [Transforms API section](#transforms-api) in this document.<br/>
Expand All @@ -181,8 +185,10 @@ If the defaults aren't enough, the settings can be configured from `karma.conf.j
the karma process will exit with `ts.ExitStatus.DiagnosticsPresent_OutputsSkipped` if any compilation errors occur.

* **karmaTypescriptConfig.coverageOptions.instrumentation** - A boolean indicating whether the code should be instrumented,
set to `false` to see the original Typescript code when debugging.<br/>
Defaults to true.
set this property to `false` to see the original Typescript code when debugging.
Please note that setting this property to `true` requires the Typescript compiler option `sourceMap` to also be set to `true`.
For more debugging options, please see `karmaTypescriptConfig.coverageOptions.sourceMap`.<br/>
Defaults to `true`.

* **karmaTypescriptConfig.coverageOptions.exclude** - A `RegExp` object or an array of `RegExp` objects for filtering which files should be excluded from coverage instrumentation.<br/>
Defaults to `/\.(d|spec|test)\.ts$/i` which excludes &ast;.d.ts, &ast;.spec.ts and &ast;.test.ts (case insensitive).
Expand Down Expand Up @@ -319,6 +325,7 @@ karmaTypescriptConfig: {
extensions: [".js", ".json"],
directories: ["node_modules"]
},
sourceMap: false,
transforms: [require("karma-typescript-es6-transform")()],
validateSyntax: true
},
Expand Down
41 changes: 35 additions & 6 deletions dist/bundler/bundler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"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");
Expand All @@ -9,7 +11,6 @@ var tmp = require("tmp");
var benchmark_1 = require("../shared/benchmark");
var PathTool = require("../shared/path-tool");
var bundle_item_1 = require("./bundle-item");
var SourceMap = require("./source-map");
var Bundler = (function () {
function Bundler(config, dependencyWalker, globals, log, project, resolver, transformer, validator) {
this.config = config;
Expand Down Expand Up @@ -54,7 +55,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, SourceMap.create(queued.file, queued.emitOutput.sourceFile.text, queued.emitOutput));
queued.item = new bundle_item_1.BundleItem(queued.file.path, queued.file.originalPath, _this.createInlineSourceMap(queued));
});
var dependencyCount = _this.dependencyWalker.collectTypescriptDependencies(_this.bundleQueue);
if (_this.shouldBundle(dependencyCount)) {
Expand All @@ -65,6 +66,18 @@ 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");
Expand Down Expand Up @@ -169,12 +182,24 @@ var Bundler = (function () {
};
Bundler.prototype.writeMainBundleFile = function (onMainBundleFileWritten) {
var _this = this;
var bundle = "(function(global){" + os.EOL +
"global.wrappers={};" + os.EOL;
var bundle = "(function(global){" + os.EOL + "global.wrappers={};" + os.EOL;
var sourcemap = combineSourceMap.create();
var line = this.getNumberOfNewlines(bundle);
this.bundleBuffer.forEach(function (bundleItem) {
bundle += _this.addLoaderFunction(bundleItem, false);
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 });
}
var wrapped = _this.addLoaderFunction(bundleItem, false);
bundle += wrapped;
if (_this.config.bundlerOptions.sourceMap) {
line += _this.getNumberOfNewlines(wrapped);
}
});
bundle += this.createEntrypointFilenames() + "})(this);";
bundle += this.createEntrypointFilenames() + "})(this);" + os.EOL;
if (this.config.bundlerOptions.sourceMap) {
bundle += sourcemap.comment();
}
fs.writeFile(this.bundleFile.name, bundle, function (error) {
if (error) {
throw error;
Expand All @@ -183,6 +208,10 @@ var Bundler = (function () {
onMainBundleFileWritten();
});
};
Bundler.prototype.getNumberOfNewlines = function (source) {
var newlines = source.match(/\n/g);
return newlines ? newlines.length : 0;
};
return Bundler;
}());
exports.Bundler = Bundler;
4 changes: 2 additions & 2 deletions dist/bundler/resolve/source-reader.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"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 SourceMap = require("../source-map");
var SourceReader = (function () {
function SourceReader(config, log, transformer) {
this.config = config;
Expand All @@ -13,7 +13,7 @@ var SourceReader = (function () {
SourceReader.prototype.read = function (bundleItem, onSourceRead) {
var _this = this;
this.readFile(bundleItem, function (source) {
bundleItem.source = SourceMap.deleteComment(source);
bundleItem.source = combineSourceMap.removeComments(source);
bundleItem.ast = _this.createAbstractSyntaxTree(bundleItem);
_this.transformer.applyTransforms(bundleItem, function () {
_this.assertValidNonScriptSource(bundleItem);
Expand Down
20 changes: 0 additions & 20 deletions dist/bundler/source-map.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var path = require("path");
function create(file, source, emitOutput) {
var result = emitOutput.outputText;
var map;
var datauri;
if (emitOutput.sourceMapText) {
map = JSON.parse(emitOutput.sourceMapText);
map.sources[0] = path.basename(file.originalPath);
map.sourcesContent = [source];
map.file = path.basename(file.path);
file.sourceMap = map;
datauri = "data:application/json;charset=utf-8;base64," + new Buffer(JSON.stringify(map)).toString("base64");
result = result.replace(createComment(file), "//# sourceMappingURL=" + datauri);
}
return result;
}
exports.create = create;
function createComment(file) {
return "//# sourceMappingURL=" + path.basename(file.path) + ".map";
}
exports.createComment = createComment;
function deleteComment(source) {
return source.replace(/\/\/#\s?sourceMappingURL\s?=\s?.*\.map/g, "");
}
exports.deleteComment = deleteComment;
7 changes: 3 additions & 4 deletions dist/istanbul/coverage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var SourceMap = require("../bundler/source-map");
var Coverage = (function () {
function Coverage(config) {
this.config = config;
Expand Down Expand Up @@ -28,15 +27,15 @@ var Coverage = (function () {
}
if (!this.config.coverageOptions.instrumentation ||
this.isExcluded(this.config.coverageOptions.exclude, file.originalPath) ||
this.hasNoOutput(file, emitOutput)) {
this.hasNoOutput(emitOutput)) {
this.log.debug("Excluding file %s from instrumentation", file.originalPath);
callback(bundled);
return;
}
this.coveragePreprocessor(bundled, file, callback);
};
Coverage.prototype.hasNoOutput = function (file, emitOutput) {
return emitOutput.outputText === SourceMap.createComment(file);
Coverage.prototype.hasNoOutput = function (emitOutput) {
return emitOutput.outputText.startsWith("//# sourceMappingURL=");
};
Coverage.prototype.isExcluded = function (regex, path) {
if (Array.isArray(regex)) {
Expand Down
1 change: 1 addition & 0 deletions dist/shared/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ var Configuration = (function () {
directories: ["node_modules"],
extensions: [".js", ".json", ".ts", ".tsx"]
},
sourceMap: false,
transforms: [],
validateSyntax: true
};
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@
"browser-resolve": "^1.11.0",
"browserify-zlib": "^0.2.0",
"buffer": "^5.0.6",
"combine-source-map": "^0.8.0",
"console-browserify": "^1.1.0",
"constants-browserify": "^1.0.0",
"convert-source-map": "^1.5.0",
"crypto-browserify": "^3.11.1",
"diff": "^3.2.0",
"domain-browser": "^1.1.7",
Expand Down Expand Up @@ -107,6 +109,8 @@
"@types/acorn": "^4.0.0",
"@types/async": "^2.0.38",
"@types/browser-resolve": "0.0.4",
"@types/combine-source-map": "^0.8.0",
"@types/convert-source-map": "^1.3.33",
"@types/diff": "0.0.31",
"@types/glob": "^5.0.30",
"@types/istanbul": "^0.4.29",
Expand Down
1 change: 1 addition & 0 deletions src/api/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface BundlerOptions {
ignore?: string[];
noParse?: string[];
resolve?: Resolve;
sourceMap: boolean;
transforms?: Transform[];
validateSyntax?: boolean;
}
Expand Down
52 changes: 45 additions & 7 deletions src/bundler/bundler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
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";
Expand All @@ -20,7 +22,6 @@ import { BundleItem } from "./bundle-item";
import { DependencyWalker } from "./dependency-walker";
import { Queued } from "./queued";
import { Resolver } from "./resolve/resolver";
import SourceMap = require("./source-map");
import { Transformer } from "./transformer";
import { Validator } from "./validator";

Expand Down Expand Up @@ -76,8 +77,8 @@ export class Bundler {

this.transformer.applyTsTransforms(this.bundleQueue, () => {
this.bundleQueue.forEach((queued) => {
queued.item = new BundleItem(queued.file.path, queued.file.originalPath,
SourceMap.create(queued.file, queued.emitOutput.sourceFile.text, queued.emitOutput));
queued.item = new BundleItem(
queued.file.path, queued.file.originalPath, this.createInlineSourceMap(queued));
});

let dependencyCount = this.dependencyWalker.collectTypescriptDependencies(this.bundleQueue);
Expand All @@ -91,6 +92,21 @@ 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");
Expand Down Expand Up @@ -213,14 +229,31 @@ export class Bundler {

private writeMainBundleFile(onMainBundleFileWritten: { (): void } ) {

let bundle = "(function(global){" + os.EOL +
"global.wrappers={};" + os.EOL;
let bundle = "(function(global){" + os.EOL + "global.wrappers={};" + os.EOL;
let sourcemap = combineSourceMap.create();
let line = this.getNumberOfNewlines(bundle);

this.bundleBuffer.forEach((bundleItem) => {
bundle += this.addLoaderFunction(bundleItem, false);

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 }
);
}

let wrapped = this.addLoaderFunction(bundleItem, false);
bundle += wrapped;
if (this.config.bundlerOptions.sourceMap) {
line += this.getNumberOfNewlines(wrapped);
}
});

bundle += this.createEntrypointFilenames() + "})(this);";
bundle += this.createEntrypointFilenames() + "})(this);" + os.EOL;
if (this.config.bundlerOptions.sourceMap) {
bundle += sourcemap.comment();
}

fs.writeFile(this.bundleFile.name, bundle, (error) => {
if (error) {
Expand All @@ -230,4 +263,9 @@ export class Bundler {
onMainBundleFileWritten();
});
}

private getNumberOfNewlines(source: any) {
let newlines = source.match(/\n/g);
return newlines ? newlines.length : 0;
}
}
4 changes: 2 additions & 2 deletions src/bundler/resolve/source-reader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
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";
Expand All @@ -7,7 +8,6 @@ import { Logger } from "log4js";

import { Configuration } from "../../shared/configuration";
import { BundleItem } from "../bundle-item";
import SourceMap = require("../source-map");
import { Transformer } from "../transformer";

export class SourceReader {
Expand All @@ -20,7 +20,7 @@ export class SourceReader {

this.readFile(bundleItem, (source: string) => {

bundleItem.source = SourceMap.deleteComment(source);
bundleItem.source = combineSourceMap.removeComments(source);
bundleItem.ast = this.createAbstractSyntaxTree(bundleItem);

this.transformer.applyTransforms(bundleItem, () => {
Expand Down
35 changes: 0 additions & 35 deletions src/bundler/source-map.ts

This file was deleted.

Loading

0 comments on commit 493060a

Please sign in to comment.