| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- # @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
- ```sh
- npm install @ampproject/remapping
- ```
- ## Usage
- ```typescript
- 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.
- ```js
- // 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:
- 1. `"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.
- 2. `"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`".
- ### 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:
- ```js
- const remapped = remapping(
- [minifiedTransformedMap, transformedMap],
- () => null
- );
- console.log(remapped);
- // {
- // file: 'transpiled.min.js',
- // mappings: 'AAEE',
- // sources: ['helloworld.js'],
- // version: 3,
- // };
- ```
- ### 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.
- ```js
- 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.
- ```js
- 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!")',
- // ],
- // };
- ```
- ### 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](https://github.com/rich-harris/sourcemap-codec) instead of
- encoding into a VLQ string.
|