Remap sequential sourcemaps through transformations to point at the original source code
Installations
npm install @ampproject/remapping
Developer Guide
Typescript
Yes
Module System
CommonJS, ESM
Min. Node Version
>=6.0.0
Node Version
20.10.0
NPM Version
10.2.3
Score
99.3
Supply Chain
98.6
Quality
79
Maintenance
100
Vulnerability
100
License
Releases
Contributors
Languages
TypeScript (80.14%)
JavaScript (17.08%)
Shell (2.78%)
Developer
ampproject
Download Statistics
Total Downloads
3,753,038,260
Last Day
6,749,785
Last Week
30,842,327
Last Month
137,025,639
Last Year
1,672,396,231
GitHub Statistics
114 Stars
351 Commits
28 Forks
12 Watching
19 Branches
64 Contributors
Bundle Size
31.93 kB
Minified
8.94 kB
Minified + Gzipped
Package Meta Information
Latest Version
2.3.0
Package Id
@ampproject/remapping@2.3.0
Unpacked Size
77.10 kB
Size
15.20 kB
File Count
12
NPM Version
10.2.3
Node Version
20.10.0
Publised On
01 Mar 2024
Total Downloads
Cumulative downloads
Total Downloads
3,753,038,260
Last day
-3.9%
6,749,785
Compared to previous day
Last week
-14.4%
30,842,327
Compared to previous week
Last month
4.7%
137,025,639
Compared to previous month
Last year
29.6%
1,672,396,231
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
@ampproject/remapping
Remap sequential sourcemaps through transformations to point at the original source code
Remapping allows you to take the sourcemaps generated through transforming your code and "remap" them to the original source locations. Think "my minified code, transformed with babel and bundled with webpack", all pointing to the correct location in your original source code.
With remapping, none of your source code transformations need to be aware of the input's sourcemap, they only need to generate an output sourcemap. This greatly simplifies building custom transformations (think a find-and-replace).
Installation
1npm install @ampproject/remapping
Usage
1function remapping( 2 map: SourceMap | SourceMap[], 3 loader: (file: string, ctx: LoaderContext) => (SourceMap | null | undefined), 4 options?: { excludeContent: boolean, decodedMappings: boolean } 5): SourceMap; 6 7// LoaderContext gives the loader the importing sourcemap, tree depth, the ability to override the 8// "source" location (where child sources are resolved relative to, or the location of original 9// source), and the ability to override the "content" of an original source for inclusion in the 10// output sourcemap. 11type LoaderContext = { 12 readonly importer: string; 13 readonly depth: number; 14 source: string; 15 content: string | null | undefined; 16}
remapping
takes the final output sourcemap, and a loader
function. For every source file pointer
in the sourcemap, the loader
will be called with the resolved path. If the path itself represents
a transformed file (it has a sourcmap associated with it), then the loader
should return that
sourcemap. If not, the path will be treated as an original, untransformed source code.
1// Babel transformed "helloworld.js" into "transformed.js" 2const transformedMap = JSON.stringify({ 3 file: 'transformed.js', 4 // 1st column of 2nd line of output file translates into the 1st source 5 // file, line 3, column 2 6 mappings: ';CAEE', 7 sources: ['helloworld.js'], 8 version: 3, 9}); 10 11// Uglify minified "transformed.js" into "transformed.min.js" 12const minifiedTransformedMap = JSON.stringify({ 13 file: 'transformed.min.js', 14 // 0th column of 1st line of output file translates into the 1st source 15 // file, line 2, column 1. 16 mappings: 'AACC', 17 names: [], 18 sources: ['transformed.js'], 19 version: 3, 20}); 21 22const remapped = remapping( 23 minifiedTransformedMap, 24 (file, ctx) => { 25 26 // The "transformed.js" file is an transformed file. 27 if (file === 'transformed.js') { 28 // The root importer is empty. 29 console.assert(ctx.importer === ''); 30 // The depth in the sourcemap tree we're currently loading. 31 // The root `minifiedTransformedMap` is depth 0, and its source children are depth 1, etc. 32 console.assert(ctx.depth === 1); 33 34 return transformedMap; 35 } 36 37 // Loader will be called to load transformedMap's source file pointers as well. 38 console.assert(file === 'helloworld.js'); 39 // `transformed.js`'s sourcemap points into `helloworld.js`. 40 console.assert(ctx.importer === 'transformed.js'); 41 // This is a source child of `transformed`, which is a source child of `minifiedTransformedMap`. 42 console.assert(ctx.depth === 2); 43 return null; 44 } 45); 46 47console.log(remapped); 48// { 49// file: 'transpiled.min.js', 50// mappings: 'AAEE', 51// sources: ['helloworld.js'], 52// version: 3, 53// };
In this example, loader
will be called twice:
"transformed.js"
, the first source file pointer in theminifiedTransformedMap
. We return the associated sourcemap for it (its a transformed file, after all) so that sourcemap locations can be traced through it into the source files it represents."helloworld.js"
, our original, unmodified source code. This file does not have a sourcemap, so we returnnull
.
The remapped
sourcemap now points from transformed.min.js
into locations in helloworld.js
. If
you were to read the mappings
, it says "0th column of the first line output line points to the 1st
column of the 2nd line of the file helloworld.js
".
Multiple transformations of a file
As a convenience, if you have multiple single-source transformations of a file, you may pass an
array of sourcemap files in the order of most-recent transformation sourcemap first. Note that this
changes the importer
and depth
of each call to our loader. So our above example could have been
written as:
1const remapped = remapping( 2 [minifiedTransformedMap, transformedMap], 3 () => null 4); 5 6console.log(remapped); 7// { 8// file: 'transpiled.min.js', 9// mappings: 'AAEE', 10// sources: ['helloworld.js'], 11// version: 3, 12// };
Advanced control of the loading graph
source
The source
property can overridden to any value to change the location of the current load. Eg,
for an original source file, it allows us to change the location to the original source regardless
of what the sourcemap source entry says. And for transformed files, it allows us to change the
relative resolving location for child sources of the loaded sourcemap.
1const remapped = remapping( 2 minifiedTransformedMap, 3 (file, ctx) => { 4 5 if (file === 'transformed.js') { 6 // We pretend the transformed.js file actually exists in the 'src/' directory. When the nested 7 // source files are loaded, they will now be relative to `src/`. 8 ctx.source = 'src/transformed.js'; 9 return transformedMap; 10 } 11 12 console.assert(file === 'src/helloworld.js'); 13 // We could futher change the source of this original file, eg, to be inside a nested directory 14 // itself. This will be reflected in the remapped sourcemap. 15 ctx.source = 'src/nested/transformed.js'; 16 return null; 17 } 18); 19 20console.log(remapped); 21// { 22// …, 23// sources: ['src/nested/helloworld.js'], 24// };
content
The content
property can be overridden when we encounter an original source file. Eg, this allows
you to manually provide the source content of the original file regardless of whether the
sourcesContent
field is present in the parent sourcemap. It can also be set to null
to remove
the source content.
1const remapped = remapping( 2 minifiedTransformedMap, 3 (file, ctx) => { 4 5 if (file === 'transformed.js') { 6 // transformedMap does not include a `sourcesContent` field, so usually the remapped sourcemap 7 // would not include any `sourcesContent` values. 8 return transformedMap; 9 } 10 11 console.assert(file === 'helloworld.js'); 12 // We can read the file to provide the source content. 13 ctx.content = fs.readFileSync(file, 'utf8'); 14 return null; 15 } 16); 17 18console.log(remapped); 19// { 20// …, 21// sourcesContent: [ 22// 'console.log("Hello world!")', 23// ], 24// };
Options
excludeContent
By default, excludeContent
is false
. Passing { excludeContent: true }
will exclude the
sourcesContent
field from the returned sourcemap. This is mainly useful when you want to reduce
the size out the sourcemap.
decodedMappings
By default, decodedMappings
is false
. Passing { decodedMappings: true }
will leave the
mappings
field in a decoded state instead of
encoding into a VLQ string.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0
Reason
0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1
- Info: no jobLevel write permissions found
Reason
Found 1/26 approved changesets -- score normalized to 0
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/ampproject/remapping/nodejs.yml/main?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/ampproject/remapping/nodejs.yml/main?enable=pin
- Warn: npmCommand not pinned by hash: test/samples/transpile-concat-hires/build.sh:9
- Warn: npmCommand not pinned by hash: test/samples/transpile-concat-hires/build.sh:14
- Warn: npmCommand not pinned by hash: test/samples/transpile-concat-lowres/build.sh:9
- Warn: npmCommand not pinned by hash: test/samples/transpile-concat-lowres/build.sh:14
- Warn: npmCommand not pinned by hash: test/samples/transpile-minify/build.sh:8
- Warn: npmCommand not pinned by hash: test/samples/transpile-rollup/build.sh:8
- Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:22
- Info: 0 out of 2 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 7 npmCommand dependencies pinned
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 6 are checked with a SAST tool
Reason
12 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
Score
2.8
/10
Last Scanned on 2025-02-03
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More