You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					219 lines
				
				7.1 KiB
			
		
		
			
		
	
	
					219 lines
				
				7.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# @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.
							 |