Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@ampproject/remapping
Advanced tools
Remap sequential sourcemaps through transformations to point at the original source code
The @ampproject/remapping npm package is used for source map transformation. It allows developers to take existing source maps and remap them to create new source maps that reflect the combined transformations of multiple build steps. This is particularly useful when dealing with complex build processes involving multiple tools that each generate their own source maps.
Remapping source maps
This feature allows you to remap source maps by providing the original source map and a chain of additional source maps. The package will then produce a new source map that reflects the composition of all provided source maps.
{"version":3,"file":"out.js","sources":["foo.js","bar.js"],"names":["src","maps","are","fun"],"mappings":"AAgBC,SAAQ,CAAEA"}
The 'source-map' package provides functionalities for generating and consuming source maps. It allows for the manipulation of source maps which is similar to what @ampproject/remapping offers, but it is a more general-purpose library with a broader scope of source map operations.
The 'combine-source-map' package is used for combining multiple source maps into a single source map. It is similar to @ampproject/remapping in that it deals with the transformation of source maps, but it focuses specifically on the combination aspect.
The 'multi-stage-sourcemap' package is designed to handle the case where multiple transformations are applied to code and each transformation has its own source map. It is similar to @ampproject/remapping in its purpose of remapping source maps through multiple stages.
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).
npm install @ampproject/remapping
function remapping(
map: SourceMap | SourceMap[],
loader: (file: string, ctx: LoaderContext) => (SourceMap | null | undefined),
options?: { excludeContent: boolean, decodedMappings: boolean }
): SourceMap;
// LoaderContext gives the loader the importing sourcemap, tree depth, the ability to override the
// "source" location (where child sources are resolved relative to, or the location of original
// source), and the ability to override the "content" of an original source for inclusion in the
// output sourcemap.
type LoaderContext = {
readonly importer: string;
readonly depth: number;
source: string;
content: string | null | undefined;
}
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.
// Babel transformed "helloworld.js" into "transformed.js"
const transformedMap = JSON.stringify({
file: 'transformed.js',
// 1st column of 2nd line of output file translates into the 1st source
// file, line 3, column 2
mappings: ';CAEE',
sources: ['helloworld.js'],
version: 3,
});
// Uglify minified "transformed.js" into "transformed.min.js"
const minifiedTransformedMap = JSON.stringify({
file: 'transformed.min.js',
// 0th column of 1st line of output file translates into the 1st source
// file, line 2, column 1.
mappings: 'AACC',
names: [],
sources: ['transformed.js'],
version: 3,
});
const remapped = remapping(
minifiedTransformedMap,
(file, ctx) => {
// The "transformed.js" file is an transformed file.
if (file === 'transformed.js') {
// The root importer is empty.
console.assert(ctx.importer === '');
// The depth in the sourcemap tree we're currently loading.
// The root `minifiedTransformedMap` is depth 0, and its source children are depth 1, etc.
console.assert(ctx.depth === 1);
return transformedMap;
}
// Loader will be called to load transformedMap's source file pointers as well.
console.assert(file === 'helloworld.js');
// `transformed.js`'s sourcemap points into `helloworld.js`.
console.assert(ctx.importer === 'transformed.js');
// This is a source child of `transformed`, which is a source child of `minifiedTransformedMap`.
console.assert(ctx.depth === 2);
return null;
}
);
console.log(remapped);
// {
// file: 'transpiled.min.js',
// mappings: 'AAEE',
// sources: ['helloworld.js'],
// version: 3,
// };
In this example, loader
will be called twice:
"transformed.js"
, the first source file pointer in the minifiedTransformedMap
. 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 return null
.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
".
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:
const remapped = remapping(
[minifiedTransformedMap, transformedMap],
() => null
);
console.log(remapped);
// {
// file: 'transpiled.min.js',
// mappings: 'AAEE',
// sources: ['helloworld.js'],
// version: 3,
// };
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.
const remapped = remapping(
minifiedTransformedMap,
(file, ctx) => {
if (file === 'transformed.js') {
// We pretend the transformed.js file actually exists in the 'src/' directory. When the nested
// source files are loaded, they will now be relative to `src/`.
ctx.source = 'src/transformed.js';
return transformedMap;
}
console.assert(file === 'src/helloworld.js');
// We could futher change the source of this original file, eg, to be inside a nested directory
// itself. This will be reflected in the remapped sourcemap.
ctx.source = 'src/nested/transformed.js';
return null;
}
);
console.log(remapped);
// {
// …,
// sources: ['src/nested/helloworld.js'],
// };
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.
const remapped = remapping(
minifiedTransformedMap,
(file, ctx) => {
if (file === 'transformed.js') {
// transformedMap does not include a `sourcesContent` field, so usually the remapped sourcemap
// would not include any `sourcesContent` values.
return transformedMap;
}
console.assert(file === 'helloworld.js');
// We can read the file to provide the source content.
ctx.content = fs.readFileSync(file, 'utf8');
return null;
}
);
console.log(remapped);
// {
// …,
// sourcesContent: [
// 'console.log("Hello world!")',
// ],
// };
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.
By default, decodedMappings
is false
. Passing { decodedMappings: true }
will leave the
mappings
field in a decoded state instead of
encoding into a VLQ string.
FAQs
Remap sequential sourcemaps through transformations to point at the original source code
The npm package @ampproject/remapping receives a total of 31,295,661 weekly downloads. As such, @ampproject/remapping popularity was classified as popular.
We found that @ampproject/remapping demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.