diff --git a/README.md b/README.md index 6ac4c80..50dda7f 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,11 @@ You can pass an options argument: ```js const s = new MagicString(someCode, { - // both these options will be used if you later - // call `bundle.addSource( s )` - see below + // these options will be used if you later call `bundle.addSource( s )` - see below filename: 'foo.js', - indentExclusionRanges: [/*...*/] + indentExclusionRanges: [/*...*/], + // market source as ignore in DevTools, see below #Bundling + ignoreList: false }); ``` @@ -251,6 +252,15 @@ bundle.addSource({ content: new MagicString('console.log( answer )') }); +// Sources can be marked as ignore-listed, which provides a hint to debuggers +// to not step into this code and also don't show the source files depending +// on user preferences. +bundle.addSource({ + filename: 'some-3rdparty-library.js', + content: new MagicString('function myLib(){}'), + ignoreList: false // <-- +}) + // Advanced: a source can include an `indentExclusionRanges` property // alongside `filename` and `content`. This will be passed to `s.indent()` // - see documentation above diff --git a/index.d.ts b/index.d.ts index 2ec2bba..8235604 100644 --- a/index.d.ts +++ b/index.d.ts @@ -65,7 +65,17 @@ export class SourceMap { export class Bundle { constructor(options?: BundleOptions); - addSource(source: MagicString | { filename?: string, content: MagicString }): Bundle; + /** + * Adds the specified source to the bundle, which can either be a `MagicString` object directly, + * or an options object that holds a magic string `content` property and optionally provides + * a `filename` for the source within the bundle, as well as an optional `ignoreList` hint + * (which defaults to `false`). The `filename` is used when constructing the source map for the + * bundle, to identify this `source` in the source map's `sources` field. The `ignoreList` hint + * is used to populate the `x_google_ignoreList` extension field in the source map, which is a + * mechanism for tools to signal to debuggers that certain sources should be ignored by default + * (depending on user preferences). + */ + addSource(source: MagicString | { filename?: string, content: MagicString, ignoreList?: boolean }): Bundle; append(str: string, options?: BundleOptions): Bundle; clone(): Bundle; generateMap(options?: SourceMapOptions): SourceMap; @@ -117,7 +127,7 @@ export default class MagicString { append(content: string): MagicString; /** * Appends the specified content at the index in the original string. - * If a range *ending* with index is subsequently moved, the insert will be moved with it. + * If a range *ending* with index is subsequently moved, the insert will be moved with it. * See also `s.prependLeft(...)`. */ appendLeft(index: number, content: string): MagicString; @@ -162,13 +172,13 @@ export default class MagicString { */ move(start: number, end: number, index: number): MagicString; /** - * Replaces the characters from `start` to `end` with `content`, along with the appended/prepended content in + * Replaces the characters from `start` to `end` with `content`, along with the appended/prepended content in * that range. The same restrictions as `s.remove()` apply. * * The fourth argument is optional. It can have a storeName property — if true, the original name will be stored * for later inclusion in a sourcemap's names array — and a contentOnly property which determines whether only * the content is overwritten, or anything that was appended/prepended to the range as well. - * + * * It may be preferred to use `s.update(...)` instead if you wish to avoid overwriting the appended/prepended content. */ overwrite(start: number, end: number, content: string, options?: boolean | OverwriteOptions): MagicString; @@ -181,7 +191,7 @@ export default class MagicString { */ update(start: number, end: number, content: string, options?: boolean | UpdateOptions): MagicString; /** - * Prepends the string with the specified content. + * Prepends the string with the specified content. */ prepend(content: string): MagicString; /** diff --git a/src/Bundle.js b/src/Bundle.js index 04d6f20..9c76f65 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -31,7 +31,7 @@ export default class Bundle { ); } - ['filename', 'indentExclusionRanges', 'separator'].forEach((option) => { + ['filename', 'ignoreList', 'indentExclusionRanges', 'separator'].forEach((option) => { if (!hasOwnProp.call(source, option)) source[option] = source.content[option]; }); @@ -84,6 +84,7 @@ export default class Bundle { generateDecodedMap(options = {}) { const names = []; + let x_google_ignoreList = undefined; this.sources.forEach((source) => { Object.keys(source.content.storedNames).forEach((name) => { if (!~names.indexOf(name)) names.push(name); @@ -141,6 +142,13 @@ export default class Bundle { if (magicString.outro) { mappings.advance(magicString.outro); } + + if (source.ignoreList && sourceIndex !== -1) { + if (x_google_ignoreList === undefined) { + x_google_ignoreList = []; + } + x_google_ignoreList.push(sourceIndex); + } }); return { @@ -153,6 +161,7 @@ export default class Bundle { }), names, mappings: mappings.raw, + x_google_ignoreList, }; } diff --git a/src/MagicString.js b/src/MagicString.js index f8bfad0..30cba57 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -34,6 +34,7 @@ export default class MagicString { sourcemapLocations: { writable: true, value: new BitSet() }, storedNames: { writable: true, value: {} }, indentStr: { writable: true, value: undefined }, + ignoreList: { writable: true, value: options.ignoreList }, }); if (DEBUG) { @@ -168,6 +169,7 @@ export default class MagicString { sourcesContent: options.includeContent ? [this.original] : undefined, names, mappings: mappings.raw, + x_google_ignoreList: this.ignoreList ? [sourceIndex] : undefined }; } diff --git a/test/MagicString.Bundle.js b/test/MagicString.Bundle.js index 425aa38..9f1b7db 100644 --- a/test/MagicString.Bundle.js +++ b/test/MagicString.Bundle.js @@ -27,17 +27,32 @@ describe('MagicString.Bundle', () => { assert.strictEqual(b.sources[0].indentExclusionRanges, array); }); + it('should accept ignore-list hint', () => { + const b = new MagicString.Bundle(); + const foo = new MagicString('foo', {filename: 'foo.js'}); + const bar = new MagicString('bar', {filename: 'bar.js'}); + + b.addSource({content: foo, ignoreList: true}); + b.addSource({content: bar, ignoreList: false}); + assert.strictEqual(b.sources[0].content, foo); + assert.strictEqual(b.sources[0].ignoreList, true); + assert.strictEqual(b.sources[1].content, bar); + assert.strictEqual(b.sources[1].ignoreList, false); + }); + it('respects MagicString init options with { content: source }', () => { const b = new MagicString.Bundle(); const array = []; const source = new MagicString('abcdefghijkl', { filename: 'foo.js', + ignoreList: false, indentExclusionRanges: array }); b.addSource({ content: source }); assert.strictEqual(b.sources[0].content, source); assert.strictEqual(b.sources[0].filename, 'foo.js'); + assert.strictEqual(b.sources[0].ignoreList, false); assert.strictEqual(b.sources[0].indentExclusionRanges, array); }); }); @@ -345,6 +360,29 @@ describe('MagicString.Bundle', () => { assert.equal(loc.source, 'three.js'); }); + it('should generate x_google_ignoreList correctly', () => { + const b = new MagicString.Bundle(); + + const one = new MagicString('function one () {}', { filename: 'one.js' }); + const two = new MagicString('function two () {}', { filename: 'two.js' }); + const three = new MagicString('function three () {}', { filename: 'three.js' }); + const four = new MagicString('function four () {}', { filename: 'four.js' }); + + b.addSource({ content: one, ignoreList: false }); + b.addSource({ content: two, ignoreList: true }); + b.addSource({ content: three, ignoreList: true }); + b.addSource({ content: four }); + + const map = b.generateMap({ + file: 'output.js' + }); + + assert.deepEqual(map.x_google_ignoreList, [ + map.sources.indexOf('two.js'), + map.sources.indexOf('three.js') + ]); + }); + it('handles prepended content', () => { const b = new MagicString.Bundle(); diff --git a/test/MagicString.js b/test/MagicString.js index f1bfe5b..07d1aff 100644 --- a/test/MagicString.js +++ b/test/MagicString.js @@ -13,6 +13,12 @@ describe('MagicString', () => { assert.equal(s.filename, 'foo.js'); }); + + it('stores ignore-list hint', () => { + const s = new MagicString('abc', { ignoreList: true }); + + assert.equal(s.ignoreList, true); + }); }); describe('append', () => { @@ -418,6 +424,16 @@ describe('MagicString', () => { const map = s.generateMap(); assert.equal(map.mappings, 'IAAA'); }); + + it('generates x_google_ignoreList', () => { + const s = new MagicString('function foo(){}', { + ignoreList: true + }); + + const map = s.generateMap({ source: 'foo.js' }); + assert.deepEqual(map.sources, ['foo.js']); + assert.deepEqual(map.x_google_ignoreList, [0]); + }); }); describe('getIndentString', () => {