├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .mocharc.js ├── LICENSE ├── README.md ├── code-of-conduct.md ├── package-lock.json ├── package.json ├── prettier.config.js ├── renovate.json ├── rollup.config.js ├── src └── source-map.ts ├── test ├── cursory.test.js ├── fixtures.js ├── prettier.config.js ├── source-map-consumer.test.js ├── source-map-generator.test.js └── utils.js ├── tsconfig.build.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 100 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: ['@typescript-eslint'], 5 | extends: [ 6 | 'eslint:recommended', 7 | 'plugin:@typescript-eslint/recommended', 8 | 'prettier', 9 | ], 10 | rules: { 11 | '@typescript-eslint/consistent-type-imports': 'error', 12 | '@typescript-eslint/no-duplicate-imports': 'error', 13 | '@typescript-eslint/no-explicit-any': 'off', 14 | '@typescript-eslint/no-non-null-assertion': 'off', 15 | '@typescript-eslint/no-unused-vars': [ 16 | 'error', 17 | { 18 | argsIgnorePattern: '^_', 19 | }, 20 | ], 21 | 'no-constant-condition': 'off', 22 | 'no-unused-labels': 'off', 23 | }, 24 | overrides: [ 25 | { 26 | env: { 27 | node: true, 28 | mocha: true, 29 | }, 30 | files: ['test/**/*.js'], 31 | rules: { 32 | '@typescript-eslint/no-empty-function': 'off', 33 | '@typescript-eslint/no-explicit-any': 'off', 34 | '@typescript-eslint/no-var-requires': 'off', 35 | }, 36 | }, 37 | ], 38 | }; 39 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | latest-node-version: [16.x] 12 | node-version: [6.x, 8.x, 10.x, 12.x, 14.x, 16.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Build with Node.js ${{ matrix.latest-node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.latest-node-version }} 20 | - name: Build and test with latest 21 | run: | 22 | npm install 23 | npm run build 24 | npm run test:coverage 25 | - name: Switch to Node.js ${{ matrix.node-version }} 26 | uses: actions/setup-node@v1 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | - name: Test legacy node 30 | run: | 31 | npm install mocha@6.2.3 32 | npm run test:only 33 | env: 34 | CI: true 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | .DS_Store 5 | *.log 6 | .vscode 7 | .idea 8 | dist 9 | compiled 10 | .awcache 11 | .rpt2_cache 12 | docs 13 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec: ['test/**/*.test.js'], 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Justin Ridgewell 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @jridgewell/source-map 2 | 3 | > Packages `@jridgewell/trace-mapping` and `@jridgewell/gen-mapping` into the familiar source-map API 4 | 5 | This isn't the full API, but it's the core functionality. This wraps 6 | [@jridgewell/trace-mapping][trace-mapping] and [@jridgewell/gen-mapping][gen-mapping] 7 | implementations. 8 | 9 | ## Installation 10 | 11 | ```sh 12 | npm install @jridgewell/source-map 13 | ``` 14 | 15 | ## Usage 16 | 17 | TODO 18 | 19 | ### SourceMapConsumer 20 | 21 | ```typescript 22 | import { SourceMapConsumer } from '@jridgewell/source-map'; 23 | const smc = new SourceMapConsumer({ 24 | version: 3, 25 | names: ['foo'], 26 | sources: ['input.js'], 27 | mappings: 'AAAAA', 28 | }); 29 | ``` 30 | 31 | #### SourceMapConsumer.fromSourceMap(mapGenerator[, mapUrl]) 32 | 33 | Transforms a `SourceMapGenerator` into a `SourceMapConsumer`. 34 | 35 | ```typescript 36 | const smg = new SourceMapGenerator(); 37 | 38 | const smc = SourceMapConsumer.fromSourceMap(map); 39 | smc.originalPositionFor({ line: 1, column: 0 }); 40 | ``` 41 | 42 | #### SourceMapConsumer.prototype.originalPositionFor(generatedPosition) 43 | 44 | ```typescript 45 | const smc = new SourceMapConsumer(map); 46 | smc.originalPositionFor({ line: 1, column: 0 }); 47 | ``` 48 | 49 | #### SourceMapConsumer.prototype.mappings 50 | 51 | ```typescript 52 | const smc = new SourceMapConsumer(map); 53 | smc.mappings; // AAAA 54 | ``` 55 | 56 | #### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition) 57 | 58 | ```typescript 59 | const smc = new SourceMapConsumer(map); 60 | smc.allGeneratedpositionsfor({ line: 1, column: 5, source: "baz.ts" }); 61 | // [ 62 | // { line: 2, column: 8 } 63 | // ] 64 | ``` 65 | 66 | #### SourceMapConsumer.prototype.eachMapping(callback[, context[, order]]) 67 | 68 | > This implementation currently does not support the "order" parameter. 69 | > This function can only iterate in Generated order. 70 | 71 | ```typescript 72 | const smc = new SourceMapConsumer(map); 73 | smc.eachMapping((mapping) => { 74 | // { source: 'baz.ts', 75 | // generatedLine: 4, 76 | // generatedColumn: 5, 77 | // originalLine: 4, 78 | // originalColumn: 5, 79 | // name: null } 80 | }); 81 | ``` 82 | 83 | #### SourceMapConsumer.prototype.generatedPositionFor(originalPosition) 84 | 85 | ```typescript 86 | const smc = new SourceMapConsumer(map); 87 | smc.generatedPositionFor({ line: 1, column: 5, source: "baz.ts" }); 88 | // { line: 2, column: 8 } 89 | ``` 90 | 91 | #### SourceMapConsumer.prototype.hasContentsOfAllSources() 92 | 93 | ```typescript 94 | const smc = new SourceMapConsumer(map); 95 | smc.hasContentsOfAllSources(); 96 | // true 97 | ``` 98 | 99 | #### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing]) 100 | 101 | ```typescript 102 | const smc = new SourceMapConsumer(map); 103 | smc.generatedPositionFor("baz.ts"); 104 | // "export default ..." 105 | ``` 106 | 107 | #### SourceMapConsumer.prototype.version 108 | 109 | Returns the source map's version 110 | 111 | ### SourceMapGenerator 112 | 113 | ```typescript 114 | import { SourceMapGenerator } from '@jridgewell/source-map'; 115 | const smg = new SourceMapGenerator({ 116 | file: 'output.js', 117 | sourceRoot: 'https://example.com/', 118 | }); 119 | ``` 120 | 121 | #### SourceMapGenerator.fromSourceMap(map) 122 | 123 | Transform a `SourceMapConsumer` into a `SourceMapGenerator`. 124 | 125 | ```typescript 126 | const smc = new SourceMapConsumer(); 127 | const smg = SourceMapGenerator.fromSourceMap(smc); 128 | ``` 129 | 130 | #### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]]) 131 | 132 | > This method is not implemented yet 133 | 134 | #### SourceMapGenerator.prototype.addMapping(mapping) 135 | 136 | ```typescript 137 | const smg = new SourceMapGenerator(); 138 | smg.addMapping({ 139 | generated: { line: 1, column: 0 }, 140 | source: 'input.js', 141 | original: { line: 1, column: 0 }, 142 | name: 'foo', 143 | }); 144 | ``` 145 | 146 | #### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent) 147 | 148 | ```typescript 149 | const smg = new SourceMapGenerator(); 150 | smg.setSourceContent('input.js', 'foobar'); 151 | ``` 152 | 153 | #### SourceMapGenerator.prototype.toJSON() 154 | 155 | ```typescript 156 | const smg = new SourceMapGenerator(); 157 | smg.toJSON(); // { version: 3, names: [], sources: [], mappings: '' } 158 | ``` 159 | 160 | #### SourceMapGenerator.prototype.toString() 161 | 162 | ```typescript 163 | const smg = new SourceMapGenerator(); 164 | smg.toJSON(); // "{version:3,names:[],sources:[],mappings:''}" 165 | ``` 166 | 167 | #### SourceMapGenerator.prototype.toDecodedMap() 168 | 169 | ```typescript 170 | const smg = new SourceMapGenerator(); 171 | smg.toDecodedMap(); // { version: 3, names: [], sources: [], mappings: [] } 172 | ``` 173 | 174 | ## Known differences with other implementations 175 | 176 | This implementation has some differences with `source-map` and `source-map-js`. 177 | 178 | - `SourceMapConsumer.prototype.eachMapping()` 179 | - Does not support the `order` argument 180 | - `SourceMapGenerator.prototype.applySourceMap()` 181 | - Not implemented 182 | 183 | [trace-mapping]: https://github.com/jridgewell/trace-mapping/ 184 | [gen-mapping]: https://github.com/jridgewell/gen-mapping/ 185 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/source-map", 3 | "version": "0.3.6", 4 | "description": "Packages @jridgewell/trace-mapping and @jridgewell/gen-mapping into the familiar source-map API", 5 | "keywords": [ 6 | "sourcemap", 7 | "source", 8 | "map" 9 | ], 10 | "author": "Justin Ridgewell ", 11 | "license": "MIT", 12 | "repository": "https://github.com/jridgewell/source-map", 13 | "main": "dist/source-map.cjs", 14 | "module": "dist/source-map.mjs", 15 | "types": "dist/types/source-map.d.ts", 16 | "exports": { 17 | ".": [ 18 | { 19 | "types": "./dist/types/source-map.d.ts", 20 | "browser": "./dist/source-map.umd.js", 21 | "require": "./dist/source-map.cjs", 22 | "import": "./dist/source-map.mjs" 23 | }, 24 | "./dist/source-map.cjs" 25 | ], 26 | "./package.json": "./package.json" 27 | }, 28 | "files": [ 29 | "dist" 30 | ], 31 | "scripts": { 32 | "prebuild": "rm -rf dist", 33 | "build": "run-s -n build:*", 34 | "build:rollup": "rollup -c rollup.config.js", 35 | "build:ts": "tsc --project tsconfig.build.json", 36 | "lint": "run-s -n lint:*", 37 | "lint:prettier": "npm run test:lint:prettier -- --write", 38 | "lint:ts": "npm run test:lint:ts -- --fix", 39 | "test": "run-s -n test:lint test:only", 40 | "test:debug": "ts-mocha --inspect-brk", 41 | "test:lint": "run-s -n test:lint:*", 42 | "test:lint:prettier": "prettier --check '{src,test}/**/*.ts'", 43 | "test:lint:ts": "eslint '{src,test}/**/*.ts'", 44 | "test:only": "ts-mocha", 45 | "test:coverage": "c8 --reporter text --reporter html ts-mocha", 46 | "test:watch": "ts-mocha --watch", 47 | "prepublishOnly": "npm run preversion", 48 | "preversion": "run-s test build" 49 | }, 50 | "devDependencies": { 51 | "@rollup/plugin-node-resolve": "13.2.1", 52 | "@rollup/plugin-typescript": "8.3.0", 53 | "@types/mocha": "9.1.1", 54 | "@types/node": "17.0.30", 55 | "@typescript-eslint/eslint-plugin": "5.10.0", 56 | "@typescript-eslint/parser": "5.10.0", 57 | "c8": "7.11.0", 58 | "eslint": "8.7.0", 59 | "eslint-config-prettier": "8.3.0", 60 | "mocha": "10.0.0", 61 | "npm-run-all": "4.1.5", 62 | "prettier": "2.5.1", 63 | "rollup": "2.66.0", 64 | "ts-mocha": "10.0.0", 65 | "typescript": "4.5.5" 66 | }, 67 | "dependencies": { 68 | "@jridgewell/gen-mapping": "^0.3.5", 69 | "@jridgewell/trace-mapping": "^0.3.25" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | endOfLine: 'lf', 3 | printWidth: 100, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import typescript from '@rollup/plugin-typescript'; 3 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 4 | 5 | const packageDependencies = JSON.parse(fs.readFileSync('./package.json')).dependencies; 6 | 7 | function configure(mode) { 8 | let output; 9 | switch(mode) { 10 | case 'browser': 11 | output = { 12 | format: 'umd', 13 | name: 'sourceMap', 14 | dir: 'dist', 15 | entryFileNames: '[name].umd.js', 16 | sourcemap: true, 17 | exports: 'named', 18 | globals: { 19 | '@jridgewell/gen-mapping': 'genMapping', 20 | '@jridgewell/trace-mapping': 'traceMapping', 21 | }, 22 | } 23 | break; 24 | case 'esm': 25 | output = { format: 'es', dir: 'dist', entryFileNames: '[name].mjs', sourcemap: true }; 26 | break; 27 | case 'cjs': 28 | output = { format: 'commonjs', dir: 'dist', entryFileNames: '[name].cjs', sourcemap: true }; 29 | break; 30 | } 31 | 32 | return { 33 | input: 'src/source-map.ts', 34 | output, 35 | external: mode == 'browser' ? [] : Object.keys(packageDependencies), 36 | plugins: [ 37 | typescript({ 38 | tsconfig: './tsconfig.build.json', 39 | tslib: './throw-when-needed', 40 | }), 41 | nodeResolve(), 42 | ], 43 | watch: { 44 | include: 'src/**', 45 | }, 46 | }; 47 | } 48 | 49 | export default [configure('browser'), configure('esm'), configure('cjs')]; 50 | -------------------------------------------------------------------------------- /src/source-map.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnyMap, 3 | originalPositionFor, 4 | generatedPositionFor, 5 | allGeneratedPositionsFor, 6 | eachMapping, 7 | encodedMappings, 8 | sourceContentFor, 9 | } from '@jridgewell/trace-mapping'; 10 | import { 11 | GenMapping, 12 | maybeAddMapping, 13 | toDecodedMap, 14 | toEncodedMap, 15 | setSourceContent, 16 | fromMap, 17 | } from '@jridgewell/gen-mapping'; 18 | 19 | import type { 20 | TraceMap, 21 | SourceMapInput, 22 | SectionedSourceMapInput, 23 | DecodedSourceMap, 24 | } from '@jridgewell/trace-mapping'; 25 | export type { TraceMap, SourceMapInput, SectionedSourceMapInput, DecodedSourceMap }; 26 | 27 | import type { Mapping, EncodedSourceMap } from '@jridgewell/gen-mapping'; 28 | export type { Mapping, EncodedSourceMap }; 29 | 30 | export class SourceMapConsumer { 31 | private declare _map: TraceMap; 32 | declare file: TraceMap['file']; 33 | declare names: TraceMap['names']; 34 | declare sourceRoot: TraceMap['sourceRoot']; 35 | declare sources: TraceMap['sources']; 36 | declare sourcesContent: TraceMap['sourcesContent']; 37 | declare version: TraceMap['version']; 38 | 39 | constructor(map: ConstructorParameters[0], mapUrl: Parameters[1]) { 40 | const trace = (this._map = new AnyMap(map, mapUrl)); 41 | 42 | this.file = trace.file; 43 | this.names = trace.names; 44 | this.sourceRoot = trace.sourceRoot; 45 | this.sources = trace.resolvedSources; 46 | this.sourcesContent = trace.sourcesContent; 47 | this.version = trace.version; 48 | } 49 | 50 | static fromSourceMap(map: SourceMapGenerator, mapUrl: Parameters[1]) { 51 | // This is more performant if we receive 52 | // a @jridgewell/source-map SourceMapGenerator 53 | if (map.toDecodedMap) { 54 | return new SourceMapConsumer(map.toDecodedMap() as SectionedSourceMapInput, mapUrl); 55 | } 56 | 57 | // This is a fallback for `source-map` and `source-map-js` 58 | return new SourceMapConsumer(map.toJSON() as SectionedSourceMapInput, mapUrl); 59 | } 60 | 61 | get mappings(): string { 62 | return encodedMappings(this._map); 63 | } 64 | 65 | originalPositionFor( 66 | needle: Parameters[1], 67 | ): ReturnType { 68 | return originalPositionFor(this._map, needle); 69 | } 70 | 71 | generatedPositionFor( 72 | originalPosition: Parameters[1], 73 | ): ReturnType { 74 | return generatedPositionFor(this._map, originalPosition); 75 | } 76 | 77 | allGeneratedPositionsFor( 78 | originalPosition: Parameters[1], 79 | ): ReturnType[] { 80 | return allGeneratedPositionsFor(this._map, originalPosition); 81 | } 82 | 83 | hasContentsOfAllSources(): boolean { 84 | if (!this.sourcesContent || this.sourcesContent.length !== this.sources.length) { 85 | return false; 86 | } 87 | 88 | for (const content of this.sourcesContent) { 89 | if (content == null) { 90 | return false; 91 | } 92 | } 93 | 94 | return true; 95 | } 96 | 97 | sourceContentFor(source: string, nullOnMissing?: boolean): string | null { 98 | const sourceContent = sourceContentFor(this._map, source); 99 | if (sourceContent != null) { 100 | return sourceContent; 101 | } 102 | 103 | if (nullOnMissing) { 104 | return null; 105 | } 106 | throw new Error(`"${source}" is not in the SourceMap.`); 107 | } 108 | 109 | eachMapping( 110 | callback: Parameters[1], 111 | context?: any /*, order?: number*/, 112 | ): void { 113 | // order is ignored as @jridgewell/trace-map doesn't implement it 114 | eachMapping(this._map, context ? callback.bind(context) : callback); 115 | } 116 | 117 | destroy() { 118 | // noop. 119 | } 120 | } 121 | 122 | export class SourceMapGenerator { 123 | private declare _map: GenMapping; 124 | 125 | constructor(opts: ConstructorParameters[0] | GenMapping) { 126 | // TODO :: should this be duck-typed ? 127 | this._map = opts instanceof GenMapping ? opts : new GenMapping(opts); 128 | } 129 | 130 | static fromSourceMap(consumer: SourceMapConsumer) { 131 | return new SourceMapGenerator(fromMap(consumer)); 132 | } 133 | 134 | addMapping(mapping: Parameters[1]): ReturnType { 135 | maybeAddMapping(this._map, mapping); 136 | } 137 | 138 | setSourceContent( 139 | source: Parameters[1], 140 | content: Parameters[2], 141 | ): ReturnType { 142 | setSourceContent(this._map, source, content); 143 | } 144 | 145 | toJSON(): ReturnType { 146 | return toEncodedMap(this._map); 147 | } 148 | 149 | toString(): string { 150 | return JSON.stringify(this.toJSON()); 151 | } 152 | 153 | toDecodedMap(): ReturnType { 154 | return toDecodedMap(this._map); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /test/cursory.test.js: -------------------------------------------------------------------------------- 1 | const { SourceMapConsumer, SourceMapGenerator } = require('../src/source-map'); 2 | const assert = require('assert'); 3 | 4 | it('SourceMapGenerator', () => { 5 | const smg = new SourceMapGenerator({ 6 | file: 'output.js', 7 | sourceRoot: 'https://example.com/', 8 | }); 9 | 10 | smg.addMapping({ 11 | generated: { line: 1, column: 0 }, 12 | source: 'input.js', 13 | original: { line: 1, column: 0 }, 14 | name: 'foo', 15 | }); 16 | smg.setSourceContent('input.js', 'foobar'); 17 | 18 | assert.deepEqual(smg.toJSON(), { 19 | version: 3, 20 | file: 'output.js', 21 | sourceRoot: 'https://example.com/', 22 | names: ['foo'], 23 | sources: ['input.js'], 24 | sourcesContent: ['foobar'], 25 | mappings: 'AAAAA', 26 | ignoreList: [], 27 | }); 28 | 29 | assert.deepEqual(smg.toDecodedMap(), { 30 | version: 3, 31 | file: 'output.js', 32 | sourceRoot: 'https://example.com/', 33 | names: ['foo'], 34 | sources: ['input.js'], 35 | sourcesContent: ['foobar'], 36 | mappings: [[[0, 0, 0, 0, 0]]], 37 | ignoreList: [], 38 | }); 39 | }); 40 | 41 | it('SourceMapConsumer', () => { 42 | const smc = new SourceMapConsumer({ 43 | version: 3, 44 | file: 'output.js', 45 | sourceRoot: 'https://example.com/', 46 | names: ['foo'], 47 | sources: ['input.js'], 48 | sourcesContent: ['foobar'], 49 | mappings: 'AAAAA', 50 | }); 51 | 52 | assert.equal(smc.file, 'output.js'); 53 | assert.deepEqual(smc.names, ['foo']); 54 | assert.equal(smc.sourceRoot, 'https://example.com/'); 55 | assert.deepEqual(smc.sources, ['https://example.com/input.js']); 56 | assert.deepEqual(smc.sourcesContent, ['foobar']); 57 | 58 | assert.deepEqual(smc.originalPositionFor({ line: 1, column: 0 }), { 59 | source: 'https://example.com/input.js', 60 | line: 1, 61 | column: 0, 62 | name: 'foo', 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/fixtures.js: -------------------------------------------------------------------------------- 1 | // This is a test mapping which maps functions from two different files 2 | // (one.js and two.js) to a minified generated source. 3 | // 4 | // Here is one.js: 5 | // 6 | // ONE.foo = function (bar) { 7 | // return baz(bar); 8 | // }; 9 | // 10 | // Here is two.js: 11 | // 12 | // TWO.inc = function (n) { 13 | // return n + 1; 14 | // }; 15 | // 16 | // And here is the generated code (min.js): 17 | // 18 | // ONE.foo=function(a){return baz(a);}; 19 | // TWO.inc=function(a){return a+1;}; 20 | exports.testGeneratedCode = " ONE.foo=function(a){return baz(a);};\n"+ 21 | " TWO.inc=function(a){return a+1;};"; 22 | exports.testMap = { 23 | version: 3, 24 | file: 'min.js', 25 | names: ['bar', 'baz', 'n'], 26 | sources: ['one.js', 'two.js'], 27 | sourceRoot: '/the/root', 28 | sourcesContent: [null, null], 29 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 30 | }; 31 | exports.testMapNoSourceRoot = { 32 | version: 3, 33 | file: 'min.js', 34 | names: ['bar', 'baz', 'n'], 35 | sources: ['one.js', 'two.js'], 36 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 37 | }; 38 | exports.testMapEmptySourceRoot = { 39 | version: 3, 40 | file: 'min.js', 41 | names: ['bar', 'baz', 'n'], 42 | sources: ['one.js', 'two.js'], 43 | sourceRoot: '', 44 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 45 | }; 46 | exports.testMapSingleSource = { 47 | version: 3, 48 | file: 'min.js', 49 | names: ['bar', 'baz'], 50 | sources: ['one.js'], 51 | //sourceRoot: '', 52 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID' 53 | }; 54 | exports.testMapEmptyMappings = { 55 | version: 3, 56 | file: 'min.js', 57 | names: [], 58 | sources: ['one.js', 'two.js'], 59 | sourcesContent: [ 60 | ' ONE.foo = 1;', 61 | ' TWO.inc = 2;' 62 | ], 63 | //sourceRoot: '', 64 | mappings: '' 65 | }; 66 | exports.testMapEmptyMappingsRelativeSources = { 67 | version: 3, 68 | file: 'min.js', 69 | names: [], 70 | sources: ['./one.js', './two.js'], 71 | sourcesContent: [ 72 | ' ONE.foo = 1;', 73 | ' TWO.inc = 2;' 74 | ], 75 | sourceRoot: '/the/root', 76 | mappings: '' 77 | }; 78 | exports.testMapEmptyMappingsRelativeSources_generated = { 79 | version: 3, 80 | file: 'min.js', 81 | names: [], 82 | sources: ['/the/root/one.js', '/the/root/two.js'], 83 | sourcesContent: [ 84 | ' ONE.foo = 1;', 85 | ' TWO.inc = 2;' 86 | ], 87 | sourceRoot: '/the/root', 88 | mappings: '' 89 | }; 90 | exports.testMapMultiSourcesMappingRefersSingleSourceOnly = { 91 | version: 3, 92 | file: 'min.js', 93 | names: ['bar', 'baz'], 94 | sources: ['one.js', 'withoutMappings.js'], 95 | //sourceRoot: '', 96 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID' 97 | }; 98 | // This mapping is identical to above, but uses the indexed format instead. 99 | exports.indexedTestMap = { 100 | version: 3, 101 | file: 'min.js', 102 | sections: [ 103 | { 104 | offset: { 105 | line: 0, 106 | column: 0 107 | }, 108 | map: { 109 | version: 3, 110 | sources: [ 111 | "one.js" 112 | ], 113 | sourcesContent: [ 114 | ' ONE.foo = function (bar) {\n' + 115 | ' return baz(bar);\n' + 116 | ' };', 117 | ], 118 | names: [ 119 | "bar", 120 | "baz" 121 | ], 122 | mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID", 123 | file: "min.js", 124 | sourceRoot: "/the/root" 125 | } 126 | }, 127 | { 128 | offset: { 129 | line: 1, 130 | column: 0 131 | }, 132 | map: { 133 | version: 3, 134 | sources: [ 135 | "two.js" 136 | ], 137 | sourcesContent: [ 138 | ' TWO.inc = function (n) {\n' + 139 | ' return n + 1;\n' + 140 | ' };' 141 | ], 142 | names: [ 143 | "n" 144 | ], 145 | mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA", 146 | file: "min.js", 147 | sourceRoot: "/the/root" 148 | } 149 | } 150 | ] 151 | }; 152 | exports.indexedTestMapDifferentSourceRoots = { 153 | version: 3, 154 | file: 'min.js', 155 | sections: [ 156 | { 157 | offset: { 158 | line: 0, 159 | column: 0 160 | }, 161 | map: { 162 | version: 3, 163 | sources: [ 164 | "one.js" 165 | ], 166 | sourcesContent: [ 167 | ' ONE.foo = function (bar) {\n' + 168 | ' return baz(bar);\n' + 169 | ' };', 170 | ], 171 | names: [ 172 | "bar", 173 | "baz" 174 | ], 175 | mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID", 176 | file: "min.js", 177 | sourceRoot: "/the/root" 178 | } 179 | }, 180 | { 181 | offset: { 182 | line: 1, 183 | column: 0 184 | }, 185 | map: { 186 | version: 3, 187 | sources: [ 188 | "two.js" 189 | ], 190 | sourcesContent: [ 191 | ' TWO.inc = function (n) {\n' + 192 | ' return n + 1;\n' + 193 | ' };' 194 | ], 195 | names: [ 196 | "n" 197 | ], 198 | mappings: "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA", 199 | file: "min.js", 200 | sourceRoot: "/different/root" 201 | } 202 | } 203 | ] 204 | }; 205 | exports.testMapWithSourcesContent = { 206 | version: 3, 207 | file: 'min.js', 208 | names: ['bar', 'baz', 'n'], 209 | sources: ['one.js', 'two.js'], 210 | sourcesContent: [ 211 | ' ONE.foo = function (bar) {\n' + 212 | ' return baz(bar);\n' + 213 | ' };', 214 | ' TWO.inc = function (n) {\n' + 215 | ' return n + 1;\n' + 216 | ' };' 217 | ], 218 | sourceRoot: '/the/root', 219 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 220 | }; 221 | exports.testMapWithSourcesContent_generated = { 222 | version: 3, 223 | file: 'min.js', 224 | names: ['bar', 'baz', 'n'], 225 | sources: ['/the/root/one.js', '/the/root/two.js'], 226 | sourcesContent: [ 227 | ' ONE.foo = function (bar) {\n' + 228 | ' return baz(bar);\n' + 229 | ' };', 230 | ' TWO.inc = function (n) {\n' + 231 | ' return n + 1;\n' + 232 | ' };' 233 | ], 234 | sourceRoot: '/the/root', 235 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 236 | }; 237 | exports.testMapRelativeSources = { 238 | version: 3, 239 | file: 'min.js', 240 | names: ['bar', 'baz', 'n'], 241 | sources: ['./one.js', './two.js'], 242 | sourcesContent: [ 243 | ' ONE.foo = function (bar) {\n' + 244 | ' return baz(bar);\n' + 245 | ' };', 246 | ' TWO.inc = function (n) {\n' + 247 | ' return n + 1;\n' + 248 | ' };' 249 | ], 250 | sourceRoot: '/the/root', 251 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA' 252 | }; 253 | exports.emptyMap = { 254 | version: 3, 255 | file: 'min.js', 256 | names: [], 257 | sources: [], 258 | mappings: '' 259 | }; 260 | -------------------------------------------------------------------------------- /test/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = Object.assign({}, require('../prettier.config.js'), { 2 | trailingComma: 'es5', 3 | }); 4 | -------------------------------------------------------------------------------- /test/source-map-consumer.test.js: -------------------------------------------------------------------------------- 1 | const { SourceMapConsumer, SourceMapGenerator } = require('../src/source-map'); 2 | const fixtures = require('./fixtures'); 3 | const assert = require('assert'); 4 | 5 | describe('SourceMapConsumer.sourceContentFor', () => { 6 | it('test that we can get the original sources for the sources', () => { 7 | var map = new SourceMapConsumer(fixtures.testMapWithSourcesContent); 8 | var sources = map.sources; 9 | 10 | assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 11 | assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };'); 12 | assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 13 | assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };'); 14 | assert.throws(function () { 15 | map.sourceContentFor(""); 16 | }, Error); 17 | assert.throws(function () { 18 | map.sourceContentFor("/the/root/three.js"); 19 | }, Error); 20 | assert.throws(function () { 21 | map.sourceContentFor("three.js"); 22 | }, Error); 23 | }); 24 | 25 | // FIXME :: sourceContentFor does not support relative file resolution 26 | /*it('test that we can get the original source content with relative source paths', () => { 27 | var map = new SourceMapConsumer(fixtures.testMapRelativeSources); 28 | var sources = map.sources; 29 | 30 | assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 31 | assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };'); 32 | assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 33 | assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };'); 34 | assert.throws(function () { 35 | map.sourceContentFor(""); 36 | }, Error); 37 | assert.throws(function () { 38 | map.sourceContentFor("/the/root/three.js"); 39 | }, Error); 40 | assert.throws(function () { 41 | map.sourceContentFor("three.js"); 42 | }, Error); 43 | });*/ 44 | 45 | // FIXME :: rawSources only list the full path, and no way to access the inner sourceRoot to test 46 | /*it('test that we can get the original source content for the sources on an indexed source map', () => { 47 | var map = new SourceMapConsumer(fixtures.indexedTestMap); 48 | var sources = map.sources; 49 | 50 | assert.equal(map.sourceContentFor(sources[0]), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 51 | assert.equal(map.sourceContentFor(sources[1]), ' TWO.inc = function (n) {\n return n + 1;\n };'); 52 | assert.equal(map.sourceContentFor("one.js"), ' ONE.foo = function (bar) {\n return baz(bar);\n };'); 53 | assert.equal(map.sourceContentFor("two.js"), ' TWO.inc = function (n) {\n return n + 1;\n };'); 54 | assert.throws(function () { 55 | map.sourceContentFor(""); 56 | }, Error); 57 | assert.throws(function () { 58 | map.sourceContentFor("/the/root/three.js"); 59 | }, Error); 60 | assert.throws(function () { 61 | map.sourceContentFor("three.js"); 62 | }, Error); 63 | });*/ 64 | 65 | }) 66 | 67 | describe('SourceMapConsuper.hasContentsOfAllSources', () => { 68 | 69 | function createSourceMapWithoutSources() { 70 | var smg = new SourceMapGenerator({ 71 | sourceRoot: 'http://example.com/', 72 | file: 'foo.js' 73 | }); 74 | smg.addMapping({ 75 | original: { line: 1, column: 1 }, 76 | generated: { line: 2, column: 2 }, 77 | source: 'bar.js' 78 | }); 79 | smg.addMapping({ 80 | original: { line: 2, column: 2 }, 81 | generated: { line: 4, column: 4 }, 82 | source: 'baz.js', 83 | name: 'dirtMcGirt' 84 | }); 85 | 86 | return smg; 87 | } 88 | 89 | it('has no content', () => { 90 | const smg = createSourceMapWithoutSources(); 91 | 92 | var smc = SourceMapConsumer.fromSourceMap(smg); 93 | assert.equal(smc.hasContentsOfAllSources(), false); 94 | }); 95 | 96 | it('has partial content', () => { 97 | const smg = createSourceMapWithoutSources(); 98 | smg.setSourceContent('baz.js', 'baz.js content'); 99 | 100 | var smc = SourceMapConsumer.fromSourceMap(smg); 101 | assert.equal(smc.hasContentsOfAllSources(), false); 102 | }); 103 | 104 | it('has all content', () => { 105 | const smg = createSourceMapWithoutSources(); 106 | smg.setSourceContent('bar.js', 'bar.js content'); 107 | smg.setSourceContent('baz.js', 'baz.js content'); 108 | 109 | var smc = SourceMapConsumer.fromSourceMap(smg); 110 | assert.equal(smc.hasContentsOfAllSources(), true); 111 | }); 112 | }); 113 | 114 | it('SourceMapConsumer.fromSourceMap', () => { 115 | var smg = new SourceMapGenerator({ 116 | sourceRoot: 'http://example.com/', 117 | file: 'foo.js' 118 | }); 119 | smg.addMapping({ 120 | original: { line: 1, column: 1 }, 121 | generated: { line: 2, column: 2 }, 122 | source: 'bar.js' 123 | }); 124 | smg.addMapping({ 125 | original: { line: 2, column: 2 }, 126 | generated: { line: 4, column: 4 }, 127 | source: 'baz.js', 128 | name: 'dirtMcGirt' 129 | }); 130 | smg.setSourceContent('baz.js', 'baz.js content'); 131 | 132 | var smc = SourceMapConsumer.fromSourceMap(smg); 133 | assert.equal(smc.file, 'foo.js'); 134 | assert.equal(smc.sourceRoot, 'http://example.com/'); 135 | assert.equal(smc.sources.length, 2); 136 | assert.equal(smc.sources[0], 'http://example.com/bar.js'); 137 | assert.equal(smc.sources[1], 'http://example.com/baz.js'); 138 | assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content'); 139 | 140 | var pos = smc.originalPositionFor({ 141 | line: 2, 142 | column: 2 143 | }); 144 | assert.equal(pos.line, 1); 145 | assert.equal(pos.column, 1); 146 | assert.equal(pos.source, 'http://example.com/bar.js'); 147 | assert.equal(pos.name, null); 148 | 149 | pos = smc.generatedPositionFor({ 150 | line: 1, 151 | column: 1, 152 | source: 'http://example.com/bar.js' 153 | }); 154 | assert.equal(pos.line, 2); 155 | assert.equal(pos.column, 2); 156 | 157 | pos = smc.originalPositionFor({ 158 | line: 4, 159 | column: 4 160 | }); 161 | assert.equal(pos.line, 2); 162 | assert.equal(pos.column, 2); 163 | assert.equal(pos.source, 'http://example.com/baz.js'); 164 | assert.equal(pos.name, 'dirtMcGirt'); 165 | 166 | pos = smc.generatedPositionFor({ 167 | line: 2, 168 | column: 2, 169 | source: 'http://example.com/baz.js' 170 | }); 171 | assert.equal(pos.line, 4); 172 | assert.equal(pos.column, 4); 173 | }); 174 | 175 | describe("SourceMapConsumer.eachMapping", () => { 176 | it('test eachMapping', () => { 177 | var map; 178 | 179 | map = new SourceMapConsumer(fixtures.testMap); 180 | var previousLine = -Infinity; 181 | var previousColumn = -Infinity; 182 | map.eachMapping(function (mapping) { 183 | assert.ok(mapping.generatedLine >= previousLine); 184 | 185 | assert.ok(mapping.source === '/the/root/one.js' || mapping.source === '/the/root/two.js'); 186 | 187 | if (mapping.generatedLine === previousLine) { 188 | assert.ok(mapping.generatedColumn >= previousColumn); 189 | previousColumn = mapping.generatedColumn; 190 | } 191 | else { 192 | previousLine = mapping.generatedLine; 193 | previousColumn = -Infinity; 194 | } 195 | }); 196 | 197 | map = new SourceMapConsumer(fixtures.testMapNoSourceRoot); 198 | map.eachMapping(function (mapping) { 199 | assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js'); 200 | }); 201 | 202 | map = new SourceMapConsumer(fixtures.testMapEmptySourceRoot); 203 | map.eachMapping(function (mapping) { 204 | assert.ok(mapping.source === 'one.js' || mapping.source === 'two.js'); 205 | }); 206 | }); 207 | 208 | it('test eachMapping for indexed source maps', () => { 209 | var map = new SourceMapConsumer(fixtures.indexedTestMap); 210 | var previousLine = -Infinity; 211 | var previousColumn = -Infinity; 212 | map.eachMapping(function (mapping) { 213 | assert.ok(mapping.generatedLine >= previousLine); 214 | 215 | if (mapping.source) { 216 | assert.equal(mapping.source.indexOf(fixtures.testMap.sourceRoot), 0); 217 | } 218 | 219 | if (mapping.generatedLine === previousLine) { 220 | assert.ok(mapping.generatedColumn >= previousColumn); 221 | previousColumn = mapping.generatedColumn; 222 | } 223 | else { 224 | previousLine = mapping.generatedLine; 225 | previousColumn = -Infinity; 226 | } 227 | }); 228 | }); 229 | 230 | // Ordering isn't implemented 231 | 232 | /*it('test iterating over mappings in a different order', () => { 233 | var map = new SourceMapConsumer(fixtures.testMap); 234 | var previousLine = -Infinity; 235 | var previousColumn = -Infinity; 236 | var previousSource = ""; 237 | map.eachMapping(function (mapping) { 238 | assert.ok(mapping.source >= previousSource); 239 | 240 | if (mapping.source === previousSource) { 241 | assert.ok(mapping.originalLine >= previousLine); 242 | 243 | if (mapping.originalLine === previousLine) { 244 | assert.ok(mapping.originalColumn >= previousColumn); 245 | previousColumn = mapping.originalColumn; 246 | } 247 | else { 248 | previousLine = mapping.originalLine; 249 | previousColumn = -Infinity; 250 | } 251 | } 252 | else { 253 | previousSource = mapping.source; 254 | previousLine = -Infinity; 255 | previousColumn = -Infinity; 256 | } 257 | }, null, SourceMapConsumer.ORIGINAL_ORDER); 258 | }); 259 | 260 | it('test iterating over mappings in a different order in indexed source maps', () => { 261 | var map = new SourceMapConsumer(fixtures.indexedTestMap); 262 | var previousLine = -Infinity; 263 | var previousColumn = -Infinity; 264 | var previousSource = ""; 265 | map.eachMapping(function (mapping) { 266 | assert.ok(mapping.source >= previousSource); 267 | 268 | if (mapping.source === previousSource) { 269 | assert.ok(mapping.originalLine >= previousLine); 270 | 271 | if (mapping.originalLine === previousLine) { 272 | assert.ok(mapping.originalColumn >= previousColumn); 273 | previousColumn = mapping.originalColumn; 274 | } 275 | else { 276 | previousLine = mapping.originalLine; 277 | previousColumn = -Infinity; 278 | } 279 | } 280 | else { 281 | previousSource = mapping.source; 282 | previousLine = -Infinity; 283 | previousColumn = -Infinity; 284 | } 285 | }, null, SourceMapConsumer.ORIGINAL_ORDER); 286 | });*/ 287 | 288 | it('test that we can set the context for `this` in eachMapping', () => { 289 | var map = new SourceMapConsumer(fixtures.testMap); 290 | var context = {}; 291 | map.eachMapping(function () { 292 | assert.equal(this, context); 293 | }, context); 294 | }); 295 | 296 | it('test that we can set the context for `this` in eachMapping in indexed source maps', () => { 297 | var map = new SourceMapConsumer(fixtures.indexedTestMap); 298 | var context = {}; 299 | map.eachMapping(function () { 300 | assert.equal(this, context); 301 | }, context); 302 | }); 303 | }) 304 | 305 | describe("SourceMapConsumer.generatedPositionFor", () => { 306 | it('test sourceRoot + generatedPositionFor', () => { 307 | var map = new SourceMapGenerator({ 308 | sourceRoot: 'foo/bar', 309 | file: 'baz.js' 310 | }); 311 | map.addMapping({ 312 | original: { line: 1, column: 1 }, 313 | generated: { line: 2, column: 2 }, 314 | source: 'bang.coffee' 315 | }); 316 | map.addMapping({ 317 | original: { line: 5, column: 5 }, 318 | generated: { line: 6, column: 6 }, 319 | source: 'bang.coffee' 320 | }); 321 | map = new SourceMapConsumer(map.toString(), 'http://example.com/'); 322 | 323 | // Should handle without sourceRoot. 324 | let pos = map.generatedPositionFor({ 325 | line: 1, 326 | column: 1, 327 | source: 'bang.coffee' 328 | }); 329 | 330 | assert.equal(pos.line, 2); 331 | assert.equal(pos.column, 2); 332 | 333 | /* 334 | // TODO :: should these cases be handled as well ? 335 | 336 | // Should handle with sourceRoot. 337 | pos = map.generatedPositionFor({ 338 | line: 1, 339 | column: 1, 340 | source: 'foo/bar/bang.coffee' 341 | }); 342 | 343 | assert.equal(pos.line, 2); 344 | assert.equal(pos.column, 2); 345 | 346 | // Should handle absolute case. 347 | pos = map.generatedPositionFor({ 348 | line: 1, 349 | column: 1, 350 | source: 'http://example.com/foo/bar/bang.coffee' 351 | }); 352 | 353 | assert.equal(pos.line, 2); 354 | assert.equal(pos.column, 2); 355 | */ 356 | }); 357 | 358 | it('test sourceRoot + generatedPositionFor for path above the root', () => { 359 | var map = new SourceMapGenerator({ 360 | sourceRoot: 'foo/bar', 361 | file: 'baz.js' 362 | }); 363 | map.addMapping({ 364 | original: { line: 1, column: 1 }, 365 | generated: { line: 2, column: 2 }, 366 | source: '../bang.coffee' 367 | }); 368 | map = new SourceMapConsumer(map.toString()); 369 | 370 | // Should handle with sourceRoot. 371 | var pos = map.generatedPositionFor({ 372 | line: 1, 373 | column: 1, 374 | source: 'foo/bang.coffee' 375 | }); 376 | 377 | assert.equal(pos.line, 2); 378 | assert.equal(pos.column, 2); 379 | }); 380 | }) 381 | -------------------------------------------------------------------------------- /test/source-map-generator.test.js: -------------------------------------------------------------------------------- 1 | const { SourceMapConsumer, SourceMapGenerator } = require('../src/source-map'); 2 | const fixtures = require('./fixtures'); 3 | const utils = require('./utils') 4 | const assert = require('assert'); 5 | 6 | it('SourceMapGenerator.setSourceContent', function () { 7 | var map = new SourceMapGenerator({ 8 | file: 'min.js', 9 | sourceRoot: '/the/root' 10 | }); 11 | map.addMapping({ 12 | generated: { line: 1, column: 1 }, 13 | original: { line: 1, column: 1 }, 14 | source: 'one.js' 15 | }); 16 | map.addMapping({ 17 | generated: { line: 2, column: 1 }, 18 | original: { line: 1, column: 1 }, 19 | source: 'two.js' 20 | }); 21 | map.setSourceContent('one.js', 'one file content'); 22 | 23 | map = JSON.parse(map.toString()); 24 | assert.equal(map.sources[0], 'one.js'); 25 | assert.equal(map.sources[1], 'two.js'); 26 | assert.equal(map.sourcesContent[0], 'one file content'); 27 | assert.equal(map.sourcesContent[1], null); 28 | }); 29 | 30 | describe('SourceMapGenerator.addMapping', () => { 31 | it('test adding mappings (case 1)', () => { 32 | var map = new SourceMapGenerator({ 33 | file: 'generated-foo.js', 34 | sourceRoot: '.' 35 | }); 36 | 37 | assert.doesNotThrow(function () { 38 | map.addMapping({ 39 | generated: { line: 1, column: 1 } 40 | }); 41 | }); 42 | }); 43 | 44 | it('test adding mappings (case 2)', () => { 45 | var map = new SourceMapGenerator({ 46 | file: 'generated-foo.js', 47 | sourceRoot: '.' 48 | }); 49 | 50 | assert.doesNotThrow(function () { 51 | map.addMapping({ 52 | generated: { line: 1, column: 1 }, 53 | source: 'bar.js', 54 | original: { line: 1, column: 1 } 55 | }); 56 | }); 57 | }); 58 | 59 | it('test adding mappings (case 3)', () => { 60 | var map = new SourceMapGenerator({ 61 | file: 'generated-foo.js', 62 | sourceRoot: '.' 63 | }); 64 | 65 | assert.doesNotThrow(function () { 66 | map.addMapping({ 67 | generated: { line: 1, column: 1 }, 68 | source: 'bar.js', 69 | original: { line: 1, column: 1 }, 70 | name: 'someToken' 71 | }); 72 | }); 73 | }); 74 | 75 | it('test adding mappings (invalid)', () => { 76 | var map = new SourceMapGenerator({ 77 | file: 'generated-foo.js', 78 | sourceRoot: '.' 79 | }); 80 | 81 | // Not enough info. 82 | assert.throws(function () { 83 | map.addMapping({}); 84 | }); 85 | 86 | // Original file position, but no source. 87 | assert.doesNotThrow(function () { 88 | map.addMapping({ 89 | generated: { line: 1, column: 1 }, 90 | original: { line: 1, column: 1 } 91 | }); 92 | }); 93 | 94 | }); 95 | 96 | it('test adding mappings with skipValidation', () => { 97 | var map = new SourceMapGenerator({ 98 | file: 'generated-foo.js', 99 | sourceRoot: '.', 100 | skipValidation: true 101 | }); 102 | 103 | // Not enough info, caught by `util.getArgs` 104 | assert.throws(function () { 105 | map.addMapping({}); 106 | }); 107 | 108 | // Original file position, but no source. Not checked. 109 | assert.doesNotThrow(function () { 110 | map.addMapping({ 111 | generated: { line: 1, column: 1 }, 112 | original: { line: 1, column: 1 } 113 | }); 114 | }); 115 | }); 116 | 117 | it('test that the correct mappings are being generated', () => { 118 | var map = new SourceMapGenerator({ 119 | file: 'min.js', 120 | sourceRoot: '/the/root' 121 | }); 122 | 123 | map.addMapping({ 124 | generated: { line: 1, column: 1 }, 125 | original: { line: 1, column: 1 }, 126 | source: 'one.js' 127 | }); 128 | map.addMapping({ 129 | generated: { line: 1, column: 5 }, 130 | original: { line: 1, column: 5 }, 131 | source: 'one.js' 132 | }); 133 | map.addMapping({ 134 | generated: { line: 1, column: 9 }, 135 | original: { line: 1, column: 11 }, 136 | source: 'one.js' 137 | }); 138 | map.addMapping({ 139 | generated: { line: 1, column: 18 }, 140 | original: { line: 1, column: 21 }, 141 | source: 'one.js', 142 | name: 'bar' 143 | }); 144 | map.addMapping({ 145 | generated: { line: 1, column: 21 }, 146 | original: { line: 2, column: 3 }, 147 | source: 'one.js' 148 | }); 149 | map.addMapping({ 150 | generated: { line: 1, column: 28 }, 151 | original: { line: 2, column: 10 }, 152 | source: 'one.js', 153 | name: 'baz' 154 | }); 155 | map.addMapping({ 156 | generated: { line: 1, column: 32 }, 157 | original: { line: 2, column: 14 }, 158 | source: 'one.js', 159 | name: 'bar' 160 | }); 161 | 162 | map.addMapping({ 163 | generated: { line: 2, column: 1 }, 164 | original: { line: 1, column: 1 }, 165 | source: 'two.js' 166 | }); 167 | map.addMapping({ 168 | generated: { line: 2, column: 5 }, 169 | original: { line: 1, column: 5 }, 170 | source: 'two.js' 171 | }); 172 | map.addMapping({ 173 | generated: { line: 2, column: 9 }, 174 | original: { line: 1, column: 11 }, 175 | source: 'two.js' 176 | }); 177 | map.addMapping({ 178 | generated: { line: 2, column: 18 }, 179 | original: { line: 1, column: 21 }, 180 | source: 'two.js', 181 | name: 'n' 182 | }); 183 | map.addMapping({ 184 | generated: { line: 2, column: 21 }, 185 | original: { line: 2, column: 3 }, 186 | source: 'two.js' 187 | }); 188 | map.addMapping({ 189 | generated: { line: 2, column: 28 }, 190 | original: { line: 2, column: 10 }, 191 | source: 'two.js', 192 | name: 'n' 193 | }); 194 | 195 | map = JSON.parse(map.toString()); 196 | 197 | utils.assertEqualMaps(assert, map, fixtures.testMap); 198 | }); 199 | 200 | it('test that adding a mapping with an empty string name does not break generation', () => { 201 | var map = new SourceMapGenerator({ 202 | file: 'generated-foo.js', 203 | sourceRoot: '.' 204 | }); 205 | 206 | map.addMapping({ 207 | generated: { line: 1, column: 1 }, 208 | source: 'bar.js', 209 | original: { line: 1, column: 1 }, 210 | name: '' 211 | }); 212 | 213 | assert.doesNotThrow(function () { 214 | JSON.parse(map.toString()); 215 | }); 216 | }); 217 | }) 218 | 219 | describe('SourceMapGenerator.fromSourceMap', () => { 220 | it('test .fromSourceMap with sourcesContent', () => { 221 | var map = SourceMapGenerator.fromSourceMap( 222 | new SourceMapConsumer(fixtures.testMapWithSourcesContent)); 223 | utils.assertEqualMaps(assert, map.toJSON(), fixtures.testMapWithSourcesContent_generated); 224 | }); 225 | 226 | it('test .fromSourceMap with single source', () => { 227 | var map = SourceMapGenerator.fromSourceMap( 228 | new SourceMapConsumer(fixtures.testMapSingleSource)); 229 | utils.assertEqualMaps(assert, map.toJSON(), fixtures.testMapSingleSource); 230 | }); 231 | 232 | it('test .fromSourceMap with empty mappings', () => { 233 | var map = SourceMapGenerator.fromSourceMap( 234 | new SourceMapConsumer(fixtures.testMapEmptyMappings)); 235 | utils.assertEqualMaps(assert, map.toJSON(), fixtures.testMapEmptyMappings); 236 | }); 237 | 238 | it('test .fromSourceMap with empty mappings and relative sources', () => { 239 | var map = SourceMapGenerator.fromSourceMap( 240 | new SourceMapConsumer(fixtures.testMapEmptyMappingsRelativeSources)); 241 | utils.assertEqualMaps(assert, map.toJSON(), fixtures.testMapEmptyMappingsRelativeSources_generated); 242 | }); 243 | 244 | it('test .fromSourceMap with multiple sources where mappings refers only to single source', () => { 245 | var map = SourceMapGenerator.fromSourceMap( 246 | new SourceMapConsumer(fixtures.testMapMultiSourcesMappingRefersSingleSourceOnly)); 247 | utils.assertEqualMaps(assert, map.toJSON(), fixtures.testMapMultiSourcesMappingRefersSingleSourceOnly); 248 | }); 249 | }) 250 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 2 | exports.assertEqualMaps = function assertEqualMaps(assert, actualMap, expectedMap) { 3 | assert.equal(actualMap.version, expectedMap.version, "version mismatch"); 4 | assert.equal(actualMap.file, expectedMap.file, "file mismatch"); 5 | assert.deepEqual(actualMap.names, expectedMap.names, "names mismatch"); 6 | assert.deepEqual(actualMap.sources, expectedMap.sources, "sources mismatch"); 7 | 8 | // Source root doesn't match in tests with SourceMapGenerator.applySourceMap 9 | // Could be because of https://github.com/ampproject/remapping/blob/main/src/source-map-tree.ts#L85-L86 10 | /*const aSourceRoot = actualMap.sourceRoot; 11 | const eSourceRoot = expectedMap.sourceRoot; 12 | assert.equal(aSourceRoot, eSourceRoot, `sourceRoot mismatch: '${aSourceRoot}' != '${eSourceRoot}'`);*/ 13 | assert.equal(actualMap.mappings, expectedMap.mappings, `mappings mismatch`); 14 | 15 | if (actualMap.sourcesContent) { 16 | 17 | // The actualMap.sourcesContent could be an array of null, 18 | // Which is actually equivalent to not having the key at all 19 | const hasValues = actualMap.sourcesContent.filter(c => c != null).length > 0 20 | 21 | if (hasValues || expectedMap.sourceContent) { 22 | assert.deepEqual(actualMap.sourcesContent, 23 | expectedMap.sourcesContent, 24 | "sourcesContent mismatch"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2015", 5 | "module":"CommonJS", 6 | "lib": ["es2015", "dom"], 7 | "strict": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "declaration": true, 11 | "allowSyntheticDefaultImports": true, 12 | "declarationDir": "dist/types", 13 | "outDir": "dist/lib", 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "esModuleInterop": true, 17 | }, 18 | "exclude": [ 19 | "dist" 20 | ], 21 | "include": [ 22 | "src", 23 | "test" 24 | ] 25 | } 26 | --------------------------------------------------------------------------------