219 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# @jridgewell/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 @jridgewell/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.
 |