Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): add sourcemap `x_google_ignoreLis…
Browse files Browse the repository at this point in the history
…t` support for esbuild builder

When using the esbuild-based browser application builder with source maps enabled, the Chrome
DevTools `x_google_ignoreList` extension will be added to JavaScript source maps. This extension
supports an improved developer experience with Chrome DevTools. For more information, please see
https://developer.chrome.com/articles/x-google-ignore-list/

(cherry picked from commit b0d04f7)
  • Loading branch information
clydin authored and alan-agius4 committed Apr 26, 2023
1 parent a5cb461 commit 766c146
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 0 deletions.
Expand Up @@ -11,6 +11,7 @@ import MagicString, { Bundle } from 'magic-string';
import assert from 'node:assert';
import { readFile } from 'node:fs/promises';
import { NormalizedBrowserOptions } from './options';
import { createSourcemapIngorelistPlugin } from './sourcemap-ignorelist-plugin';

/**
* Create an esbuild 'build' options object for all global scripts defined in the user provied
Expand Down Expand Up @@ -53,6 +54,7 @@ export function createGlobalScriptsBundleOptions(options: NormalizedBrowserOptio
platform: 'neutral',
preserveSymlinks,
plugins: [
createSourcemapIngorelistPlugin(),
{
name: 'angular-global-scripts',
setup(build) {
Expand Down
Expand Up @@ -31,6 +31,7 @@ import { LoadResultCache } from './load-result-cache';
import { BrowserEsbuildOptions, NormalizedBrowserOptions, normalizeOptions } from './options';
import { shutdownSassWorkerPool } from './sass-plugin';
import { Schema as BrowserBuilderOptions } from './schema';
import { createSourcemapIngorelistPlugin } from './sourcemap-ignorelist-plugin';
import { createStylesheetBundleOptions } from './stylesheets';
import type { ChangedFiles } from './watcher';

Expand Down Expand Up @@ -369,6 +370,7 @@ function createCodeBundleOptions(
platform: 'browser',
preserveSymlinks,
plugins: [
createSourcemapIngorelistPlugin(),
createCompilerPlugin(
// JS/TS options
{
Expand Down
@@ -0,0 +1,85 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import type { Plugin } from 'esbuild';

/**
* The field identifier for the sourcemap Chrome Devtools ignore list extension.
*
* Following the naming conventions from https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm
*/
const IGNORE_LIST_ID = 'x_google_ignoreList';

/**
* Minimal sourcemap object required to create the ignore list.
*/
interface SourceMap {
sources: string[];
[IGNORE_LIST_ID]?: number[];
}

/**
* Creates an esbuild plugin that updates generated sourcemaps to include the Chrome
* DevTools ignore list extension. All source files that originate from a node modules
* directory are added to the ignore list by this plugin.
*
* For more information, see https://developer.chrome.com/articles/x-google-ignore-list/
* @returns An esbuild plugin.
*/
export function createSourcemapIngorelistPlugin(): Plugin {
return {
name: 'angular-sourcemap-ignorelist',
setup(build): void {
if (!build.initialOptions.sourcemap) {
return;
}

build.onEnd((result) => {
if (!result.outputFiles) {
return;
}

for (const file of result.outputFiles) {
// Only process sourcemap files
if (!file.path.endsWith('.map')) {
continue;
}

const contents = Buffer.from(file.contents);

// Avoid parsing sourcemaps that have no node modules references
if (!contents.includes('node_modules/')) {
continue;
}

const map = JSON.parse(contents.toString('utf-8')) as SourceMap;
const ignoreList = [];

// Check and store the index of each source originating from a node modules directory
for (let index = 0; index < map.sources.length; ++index) {
if (
map.sources[index].startsWith('node_modules/') ||
map.sources[index].includes('/node_modules/')
) {
ignoreList.push(index);
}
}

// Avoid regenerating the source map if nothing changed
if (ignoreList.length === 0) {
continue;
}

// Update the sourcemap in the output file
map[IGNORE_LIST_ID] = ignoreList;
file.contents = Buffer.from(JSON.stringify(map), 'utf-8');
}
});
},
};
}
Expand Up @@ -123,5 +123,18 @@ describeBuilder(buildEsbuildBrowser, BROWSER_BUILDER_INFO, (harness) => {
harness.expectFile('dist/main.js.map').content.toContain('/core/index.ts');
harness.expectFile('dist/main.js.map').content.toContain('/common/index.ts');
});

it('should add "x_google_ignoreList" extension to script sourcemap files when true', async () => {
harness.useTarget('build', {
...BASE_OPTIONS,
sourceMap: true,
});

const { result } = await harness.executeOnce();

expect(result?.success).toBe(true);

harness.expectFile('dist/main.js.map').content.toContain('"x_google_ignoreList"');
});
});
});

0 comments on commit 766c146

Please sign in to comment.