From e825cfac8c4c7a9c2e1ba6ec660ec8c860c1acaf Mon Sep 17 00:00:00 2001 From: Filip Sobol Date: Wed, 9 Oct 2024 17:57:02 +0200 Subject: [PATCH] Fix missing `inputs` in the esbuild reports (#7) * Fix missing "inputs" in the report created by the esbuild plugin. * Set `provenance` via env variable --- .changeset/perfect-peaches-tease.md | 6 +++ .changeset/witty-humans-pump.md | 5 ++ .github/workflows/publish.yml | 1 + packages/sonda/package.json | 1 + packages/sonda/src/bundlers/esbuild.ts | 20 +++++++- packages/sonda/src/report.ts | 5 +- packages/sonda/src/sourcemap/map.ts | 68 ++++++++++++++++---------- pnpm-lock.yaml | 28 ++++++----- 8 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 .changeset/perfect-peaches-tease.md create mode 100644 .changeset/witty-humans-pump.md diff --git a/.changeset/perfect-peaches-tease.md b/.changeset/perfect-peaches-tease.md new file mode 100644 index 0000000..e2180a8 --- /dev/null +++ b/.changeset/perfect-peaches-tease.md @@ -0,0 +1,6 @@ +--- +"sonda": patch +"unplugin-sourcemaps": patch +--- + +Configure npm package provenance diff --git a/.changeset/witty-humans-pump.md b/.changeset/witty-humans-pump.md new file mode 100644 index 0000000..f739ef8 --- /dev/null +++ b/.changeset/witty-humans-pump.md @@ -0,0 +1,5 @@ +--- +"sonda": patch +--- + +Fix missing "inputs" in the report created by the esbuild plugin. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 08d3b72..cced2e4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,3 +44,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: true diff --git a/packages/sonda/package.json b/packages/sonda/package.json index ebe171c..fc06797 100644 --- a/packages/sonda/package.json +++ b/packages/sonda/package.json @@ -41,6 +41,7 @@ }, "dependencies": { "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", "open": "^10.1.0" }, "devDependencies": { diff --git a/packages/sonda/src/bundlers/esbuild.ts b/packages/sonda/src/bundlers/esbuild.ts index 83d14aa..9311941 100644 --- a/packages/sonda/src/bundlers/esbuild.ts +++ b/packages/sonda/src/bundlers/esbuild.ts @@ -1,7 +1,9 @@ +import { resolve } from 'path'; import { normalizeOptions } from '../utils'; import { generateReportFromAssets } from '../report/generate'; import type { Plugin } from 'esbuild'; import type { Options, JsonReport } from '../types'; +import { addSourcesToInputs } from '../sourcemap/map'; export function SondaEsbuildPlugin( options?: Partial ): Plugin { return { @@ -14,10 +16,15 @@ export function SondaEsbuildPlugin( options?: Partial ): Plugin { return console.error( 'Metafile is required for SondaEsbuildPlugin to work.' ); } + const cwd = process.cwd(); + const normalizedOptions = normalizeOptions( options ); + + // Esbuild already reads the existing source maps, so there's no need to do it again + normalizedOptions.detailed = false; + const inputs = Object .entries( result.metafile.inputs ) .reduce( ( acc, [ path, data ] ) => { - acc[ path ] = { bytes: data.bytes, format: data.format ?? 'unknown', @@ -25,6 +32,17 @@ export function SondaEsbuildPlugin( options?: Partial ): Plugin { belongsTo: null, }; + /** + * Because esbuild already reads the existing source maps, there may be + * cases where some report "outputs" include "inputs" don't exist in the + * main "inputs" object. To avoid this, we parse each esbuild input and + * add its sources to the "inputs" object. + */ + addSourcesToInputs( + resolve( cwd, path ), + acc + ); + return acc; }, {} as JsonReport[ 'inputs' ] ); diff --git a/packages/sonda/src/report.ts b/packages/sonda/src/report.ts index 76e452c..43b265a 100644 --- a/packages/sonda/src/report.ts +++ b/packages/sonda/src/report.ts @@ -2,6 +2,7 @@ import { dirname, resolve } from 'path'; import { fileURLToPath } from 'url'; import { readFileSync } from 'fs'; import { loadCodeAndMap } from 'load-source-map'; +import { decode } from '@jridgewell/sourcemap-codec'; import { mapSourceMap } from './sourcemap/map.js'; import { getBytesPerSource, getSizes } from './sourcemap/bytes.js'; import type { @@ -62,7 +63,9 @@ function processAsset( } const { code, map } = maybeCodeMap; - const mapped = mapSourceMap( map, dirname( asset ), inputs, options ); + const mapped = options.detailed + ? mapSourceMap( map, dirname( asset ), inputs ) + : { ...map, mappings: decode( map.mappings ) }; mapped.sources = mapped.sources.map( source => normalizePath( source! ) ); diff --git a/packages/sonda/src/sourcemap/map.ts b/packages/sonda/src/sourcemap/map.ts index 1d36609..5411f57 100644 --- a/packages/sonda/src/sourcemap/map.ts +++ b/packages/sonda/src/sourcemap/map.ts @@ -2,13 +2,12 @@ import { default as remapping, type DecodedSourceMap, type EncodedSourceMap } fr import { loadCodeAndMap } from 'load-source-map'; import { resolve } from 'path'; import { normalizePath } from '../utils'; -import type { Options, ReportInput } from '../types'; +import type { CodeMap, ReportInput } from '../types'; export function mapSourceMap( map: EncodedSourceMap, dirPath: string, - inputs: Record, - options: Options + inputs: Record ): DecodedSourceMap { const alreadyRemapped = new Set(); const remapped = remapping( map, ( file, ctx ) => { @@ -18,40 +17,55 @@ export function mapSourceMap( alreadyRemapped.add( file ); - const codeMap = loadCodeAndMap( resolve( dirPath, file ) ); + const codeMap = addSourcesToInputs( + resolve( dirPath, file ), + inputs + ); if ( !codeMap ) { return; } - if ( !options.detailed ) { - return null; - } + ctx.content ??= codeMap.code; - const parentPath = normalizePath( file ); - const format = inputs[ parentPath ]?.format ?? 'unknown'; + return codeMap.map; + }, { decodedMappings: true } ); - codeMap.map?.sources - .filter( source => source !== null ) - .forEach( ( source, index ) => { - const normalizedPath = normalizePath( source ); + return remapped as DecodedSourceMap; +} - if ( parentPath === normalizedPath ) { - return; - } +/** + * Loads the source map of a given file and adds its "sources" to the given inputs object. + */ +export function addSourcesToInputs( + path: string, + inputs: Record +): CodeMap | null { + const codeMap = loadCodeAndMap( path ); - inputs[ normalizedPath ] = { - bytes: Buffer.byteLength( codeMap.map!.sourcesContent?.[ index ] ?? '' ), - format, - imports: [], - belongsTo: parentPath - }; - } ); + if ( !codeMap ) { + return null; + } - ctx.content ??= codeMap.code; + const parentPath = normalizePath( path ); + const format = inputs[ parentPath ]?.format ?? 'unknown'; - return codeMap.map; - }, { decodedMappings: true } ); + codeMap.map?.sources + .filter( source => source !== null ) + .forEach( ( source, index ) => { + const normalizedPath = normalizePath( source ); - return remapped as DecodedSourceMap; + if ( parentPath === normalizedPath ) { + return; + } + + inputs[ normalizedPath ] = { + bytes: Buffer.byteLength( codeMap.map!.sourcesContent?.[ index ] ?? '' ), + format, + imports: [], + belongsTo: parentPath + }; + } ); + + return codeMap; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f3849d0..b4dd227 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,9 @@ importers: '@ampproject/remapping': specifier: ^2.3.0 version: 2.3.0 + '@jridgewell/sourcemap-codec': + specifier: ^1.5.0 + version: 1.5.0 open: specifier: ^10.1.0 version: 10.1.0 @@ -103,7 +106,7 @@ importers: version: 4.24.0 webpack: specifier: ^5.89.0 - version: 5.94.0(esbuild@0.23.1) + version: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1) packages/unplugin-sourcemaps: dependencies: @@ -6553,27 +6556,28 @@ snapshots: term-size@2.2.1: {} - terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4)): + terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.33.0 - webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4) + webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1) optionalDependencies: '@swc/core': 1.7.26(@swc/helpers@0.5.13) + esbuild: 0.23.1 - terser-webpack-plugin@5.3.10(esbuild@0.23.1)(webpack@5.94.0(esbuild@0.23.1)): + terser-webpack-plugin@5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4)): dependencies: '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 serialize-javascript: 6.0.2 terser: 5.33.0 - webpack: 5.94.0(esbuild@0.23.1) + webpack: 5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4) optionalDependencies: - esbuild: 0.23.1 + '@swc/core': 1.7.26(@swc/helpers@0.5.13) terser@5.33.0: dependencies: @@ -6754,7 +6758,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4): + webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1): dependencies: '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 @@ -6776,17 +6780,15 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4)) + terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1)(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(esbuild@0.23.1)) watchpack: 2.4.2 webpack-sources: 3.2.3 - optionalDependencies: - webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack@5.94.0) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack@5.94.0(esbuild@0.23.1): + webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4): dependencies: '@types/estree': 1.0.6 '@webassemblyjs/ast': 1.12.1 @@ -6808,9 +6810,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(esbuild@0.23.1)(webpack@5.94.0(esbuild@0.23.1)) + terser-webpack-plugin: 5.3.10(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack@5.94.0(@swc/core@1.7.26(@swc/helpers@0.5.13))(webpack-cli@5.1.4)) watchpack: 2.4.2 webpack-sources: 3.2.3 + optionalDependencies: + webpack-cli: 5.1.4(webpack-bundle-analyzer@4.10.2)(webpack@5.94.0) transitivePeerDependencies: - '@swc/core' - esbuild