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. |