├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .mocharc.js ├── LICENSE ├── README.md ├── benchmark ├── amp.js.map ├── babel.min.js.map ├── chrome.mjs ├── index.mjs ├── package-lock.json ├── package.json ├── preact.js.map ├── react.js.map └── vscode.map ├── package-lock.json ├── package.json ├── prettier.config.js ├── rollup.config.mjs ├── src ├── binary-search.ts ├── by-source.ts ├── flatten-map.ts ├── resolve.ts ├── sort.ts ├── sourcemap-segment.ts ├── strip-filename.ts ├── trace-mapping.ts └── types.ts ├── test ├── binary-search.test.ts ├── flatten-map.test.ts ├── resolve.test.ts ├── strip-filename.test.ts └── trace-mapping.test.ts ├── 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 | /* eslint-env node */ 2 | 3 | module.exports = { 4 | root: true, 5 | parser: '@typescript-eslint/parser', 6 | plugins: ['@typescript-eslint', 'no-only-tests'], 7 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 8 | rules: { 9 | '@typescript-eslint/consistent-type-imports': 'error', 10 | '@typescript-eslint/no-explicit-any': 'off', 11 | '@typescript-eslint/no-non-null-assertion': 'off', 12 | '@typescript-eslint/no-unused-vars': [ 13 | 'error', 14 | { 15 | argsIgnorePattern: '^_', 16 | }, 17 | ], 18 | 'no-constant-condition': 'off', 19 | 'no-unused-labels': 'off', 20 | 'prefer-rest-params': 'off', 21 | 'prefer-const': [ 22 | 'error', 23 | { 24 | destructuring: 'all', 25 | }, 26 | ], 27 | }, 28 | overrides: [ 29 | { 30 | files: ['test/**/*.ts'], 31 | rules: { 32 | '@typescript-eslint/no-empty-function': 'off', 33 | '@typescript-eslint/no-explicit-any': 'off', 34 | 'no-only-tests/no-only-tests': 'error', 35 | }, 36 | }, 37 | ], 38 | }; 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /test/samples/** text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.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: [20.x] 12 | node-version: [18.x, 20.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:only 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 node 30 | run: | 31 | node dist/trace-mapping.umd.js 32 | - name: Benchmark 33 | run: | 34 | npm run benchmark:install 35 | npm run benchmark:only 36 | env: 37 | CI: true 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | .rpt2_cache 5 | .vscode 6 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec: ['test/**/*.test.ts'], 3 | 'node-option': ['import=tsx'], 4 | }; 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 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. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @jridgewell/trace-mapping 2 | 3 | > Trace the original position through a source map 4 | 5 | `trace-mapping` allows you to take the line and column of an output file and trace it to the 6 | original location in the source file through a source map. 7 | 8 | You may already be familiar with the [`source-map`][source-map] package's `SourceMapConsumer`. This 9 | provides the same `originalPositionFor` and `generatedPositionFor` API, without requiring WASM. 10 | 11 | ## Installation 12 | 13 | ```sh 14 | npm install @jridgewell/trace-mapping 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```typescript 20 | import { 21 | TraceMap, 22 | originalPositionFor, 23 | generatedPositionFor, 24 | sourceContentFor, 25 | isIgnored, 26 | } from '@jridgewell/trace-mapping'; 27 | 28 | const tracer = new TraceMap({ 29 | version: 3, 30 | sources: ['input.js'], 31 | sourcesContent: ['content of input.js'], 32 | names: ['foo'], 33 | mappings: 'KAyCIA', 34 | ignoreList: [], 35 | }); 36 | 37 | // Lines start at line 1, columns at column 0. 38 | const traced = originalPositionFor(tracer, { line: 1, column: 5 }); 39 | assert.deepEqual(traced, { 40 | source: 'input.js', 41 | line: 42, 42 | column: 4, 43 | name: 'foo', 44 | }); 45 | 46 | const content = sourceContentFor(tracer, traced.source); 47 | assert.strictEqual(content, 'content for input.js'); 48 | 49 | const generated = generatedPositionFor(tracer, { 50 | source: 'input.js', 51 | line: 42, 52 | column: 4, 53 | }); 54 | assert.deepEqual(generated, { 55 | line: 1, 56 | column: 5, 57 | }); 58 | 59 | const ignored = isIgnored(tracer, 'input.js'); 60 | assert.equal(ignored, false); 61 | ``` 62 | 63 | We also provide a lower level API to get the actual segment that matches our line and column. Unlike 64 | `originalPositionFor`, `traceSegment` uses a 0-base for `line`: 65 | 66 | ```typescript 67 | import { traceSegment } from '@jridgewell/trace-mapping'; 68 | 69 | // line is 0-base. 70 | const traced = traceSegment(tracer, /* line */ 0, /* column */ 5); 71 | 72 | // Segments are [outputColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex] 73 | // Again, line is 0-base and so is sourceLine 74 | assert.deepEqual(traced, [5, 0, 41, 4, 0]); 75 | ``` 76 | 77 | ### SectionedSourceMaps 78 | 79 | The sourcemap spec defines a special `sections` field that's designed to handle concatenation of 80 | output code with associated sourcemaps. This type of sourcemap is rarely used (no major build tool 81 | produces it), but if you are hand coding a concatenation you may need it. We provide an `AnyMap` 82 | helper that can receive either a regular sourcemap or a `SectionedSourceMap` and returns a 83 | `TraceMap` instance: 84 | 85 | ```typescript 86 | import { AnyMap } from '@jridgewell/trace-mapping'; 87 | const fooOutput = 'foo'; 88 | const barOutput = 'bar'; 89 | const output = [fooOutput, barOutput].join('\n'); 90 | 91 | const sectioned = new AnyMap({ 92 | version: 3, 93 | sections: [ 94 | { 95 | // 0-base line and column 96 | offset: { line: 0, column: 0 }, 97 | // fooOutput's sourcemap 98 | map: { 99 | version: 3, 100 | sources: ['foo.js'], 101 | names: ['foo'], 102 | mappings: 'AAAAA', 103 | }, 104 | }, 105 | { 106 | // barOutput's sourcemap will not affect the first line, only the second 107 | offset: { line: 1, column: 0 }, 108 | map: { 109 | version: 3, 110 | sources: ['bar.js'], 111 | names: ['bar'], 112 | mappings: 'AAAAA', 113 | }, 114 | }, 115 | ], 116 | }); 117 | 118 | const traced = originalPositionFor(sectioned, { 119 | line: 2, 120 | column: 0, 121 | }); 122 | 123 | assert.deepEqual(traced, { 124 | source: 'bar.js', 125 | line: 1, 126 | column: 0, 127 | name: 'bar', 128 | }); 129 | ``` 130 | 131 | ## Benchmarks 132 | 133 | ``` 134 | node v20.10.0 135 | 136 | amp.js.map - 45120 segments 137 | 138 | Memory Usage: 139 | trace-mapping decoded 414164 bytes 140 | trace-mapping encoded 6274352 bytes 141 | source-map-js 10968904 bytes 142 | source-map-0.6.1 17587160 bytes 143 | source-map-0.8.0 8812155 bytes 144 | Chrome dev tools 8672912 bytes 145 | Smallest memory usage is trace-mapping decoded 146 | 147 | Init speed: 148 | trace-mapping: decoded JSON input x 205 ops/sec ±0.19% (88 runs sampled) 149 | trace-mapping: encoded JSON input x 405 ops/sec ±1.47% (88 runs sampled) 150 | trace-mapping: decoded Object input x 4,645 ops/sec ±0.15% (98 runs sampled) 151 | trace-mapping: encoded Object input x 458 ops/sec ±1.63% (91 runs sampled) 152 | source-map-js: encoded Object input x 75.48 ops/sec ±1.64% (67 runs sampled) 153 | source-map-0.6.1: encoded Object input x 39.37 ops/sec ±1.44% (53 runs sampled) 154 | Chrome dev tools: encoded Object input x 150 ops/sec ±1.76% (79 runs sampled) 155 | Fastest is trace-mapping: decoded Object input 156 | 157 | Trace speed (random): 158 | trace-mapping: decoded originalPositionFor x 44,946 ops/sec ±0.16% (99 runs sampled) 159 | trace-mapping: encoded originalPositionFor x 37,995 ops/sec ±1.81% (89 runs sampled) 160 | source-map-js: encoded originalPositionFor x 9,230 ops/sec ±1.36% (93 runs sampled) 161 | source-map-0.6.1: encoded originalPositionFor x 8,057 ops/sec ±0.84% (96 runs sampled) 162 | source-map-0.8.0: encoded originalPositionFor x 28,198 ops/sec ±1.12% (91 runs sampled) 163 | Chrome dev tools: encoded originalPositionFor x 46,276 ops/sec ±1.35% (95 runs sampled) 164 | Fastest is Chrome dev tools: encoded originalPositionFor 165 | 166 | Trace speed (ascending): 167 | trace-mapping: decoded originalPositionFor x 204,406 ops/sec ±0.19% (97 runs sampled) 168 | trace-mapping: encoded originalPositionFor x 196,695 ops/sec ±0.24% (99 runs sampled) 169 | source-map-js: encoded originalPositionFor x 11,948 ops/sec ±0.94% (99 runs sampled) 170 | source-map-0.6.1: encoded originalPositionFor x 10,730 ops/sec ±0.36% (100 runs sampled) 171 | source-map-0.8.0: encoded originalPositionFor x 51,427 ops/sec ±0.21% (98 runs sampled) 172 | Chrome dev tools: encoded originalPositionFor x 162,615 ops/sec ±0.18% (98 runs sampled) 173 | Fastest is trace-mapping: decoded originalPositionFor 174 | 175 | 176 | *** 177 | 178 | 179 | babel.min.js.map - 347793 segments 180 | 181 | Memory Usage: 182 | trace-mapping decoded 18504 bytes 183 | trace-mapping encoded 35428008 bytes 184 | source-map-js 51676808 bytes 185 | source-map-0.6.1 63367136 bytes 186 | source-map-0.8.0 43158400 bytes 187 | Chrome dev tools 50721552 bytes 188 | Smallest memory usage is trace-mapping decoded 189 | 190 | Init speed: 191 | trace-mapping: decoded JSON input x 17.82 ops/sec ±6.35% (35 runs sampled) 192 | trace-mapping: encoded JSON input x 31.57 ops/sec ±7.50% (43 runs sampled) 193 | trace-mapping: decoded Object input x 867 ops/sec ±0.74% (94 runs sampled) 194 | trace-mapping: encoded Object input x 33.83 ops/sec ±7.66% (46 runs sampled) 195 | source-map-js: encoded Object input x 6.58 ops/sec ±3.31% (20 runs sampled) 196 | source-map-0.6.1: encoded Object input x 4.23 ops/sec ±3.43% (15 runs sampled) 197 | Chrome dev tools: encoded Object input x 22.14 ops/sec ±3.79% (41 runs sampled) 198 | Fastest is trace-mapping: decoded Object input 199 | 200 | Trace speed (random): 201 | trace-mapping: decoded originalPositionFor x 78,234 ops/sec ±1.48% (29 runs sampled) 202 | trace-mapping: encoded originalPositionFor x 60,761 ops/sec ±1.35% (21 runs sampled) 203 | source-map-js: encoded originalPositionFor x 51,448 ops/sec ±2.17% (89 runs sampled) 204 | source-map-0.6.1: encoded originalPositionFor x 47,221 ops/sec ±1.99% (15 runs sampled) 205 | source-map-0.8.0: encoded originalPositionFor x 84,002 ops/sec ±1.45% (27 runs sampled) 206 | Chrome dev tools: encoded originalPositionFor x 106,457 ops/sec ±1.38% (37 runs sampled) 207 | Fastest is Chrome dev tools: encoded originalPositionFor 208 | 209 | Trace speed (ascending): 210 | trace-mapping: decoded originalPositionFor x 930,943 ops/sec ±0.25% (99 runs sampled) 211 | trace-mapping: encoded originalPositionFor x 843,545 ops/sec ±0.34% (97 runs sampled) 212 | source-map-js: encoded originalPositionFor x 114,510 ops/sec ±1.37% (36 runs sampled) 213 | source-map-0.6.1: encoded originalPositionFor x 87,412 ops/sec ±0.72% (92 runs sampled) 214 | source-map-0.8.0: encoded originalPositionFor x 197,709 ops/sec ±0.89% (59 runs sampled) 215 | Chrome dev tools: encoded originalPositionFor x 688,983 ops/sec ±0.33% (98 runs sampled) 216 | Fastest is trace-mapping: decoded originalPositionFor 217 | 218 | 219 | *** 220 | 221 | 222 | preact.js.map - 1992 segments 223 | 224 | Memory Usage: 225 | trace-mapping decoded 33136 bytes 226 | trace-mapping encoded 254240 bytes 227 | source-map-js 837488 bytes 228 | source-map-0.6.1 961928 bytes 229 | source-map-0.8.0 54384 bytes 230 | Chrome dev tools 709680 bytes 231 | Smallest memory usage is trace-mapping decoded 232 | 233 | Init speed: 234 | trace-mapping: decoded JSON input x 3,709 ops/sec ±0.13% (99 runs sampled) 235 | trace-mapping: encoded JSON input x 6,447 ops/sec ±0.22% (101 runs sampled) 236 | trace-mapping: decoded Object input x 83,062 ops/sec ±0.23% (100 runs sampled) 237 | trace-mapping: encoded Object input x 14,980 ops/sec ±0.28% (100 runs sampled) 238 | source-map-js: encoded Object input x 2,544 ops/sec ±0.16% (99 runs sampled) 239 | source-map-0.6.1: encoded Object input x 1,221 ops/sec ±0.37% (97 runs sampled) 240 | Chrome dev tools: encoded Object input x 4,241 ops/sec ±0.39% (93 runs sampled) 241 | Fastest is trace-mapping: decoded Object input 242 | 243 | Trace speed (random): 244 | trace-mapping: decoded originalPositionFor x 91,028 ops/sec ±0.14% (94 runs sampled) 245 | trace-mapping: encoded originalPositionFor x 84,348 ops/sec ±0.26% (98 runs sampled) 246 | source-map-js: encoded originalPositionFor x 26,998 ops/sec ±0.23% (98 runs sampled) 247 | source-map-0.6.1: encoded originalPositionFor x 18,049 ops/sec ±0.26% (100 runs sampled) 248 | source-map-0.8.0: encoded originalPositionFor x 41,916 ops/sec ±0.28% (98 runs sampled) 249 | Chrome dev tools: encoded originalPositionFor x 88,616 ops/sec ±0.14% (98 runs sampled) 250 | Fastest is trace-mapping: decoded originalPositionFor 251 | 252 | Trace speed (ascending): 253 | trace-mapping: decoded originalPositionFor x 319,960 ops/sec ±0.16% (100 runs sampled) 254 | trace-mapping: encoded originalPositionFor x 302,153 ops/sec ±0.18% (100 runs sampled) 255 | source-map-js: encoded originalPositionFor x 35,574 ops/sec ±0.19% (100 runs sampled) 256 | source-map-0.6.1: encoded originalPositionFor x 19,943 ops/sec ±0.12% (101 runs sampled) 257 | source-map-0.8.0: encoded originalPositionFor x 54,648 ops/sec ±0.20% (99 runs sampled) 258 | Chrome dev tools: encoded originalPositionFor x 278,319 ops/sec ±0.17% (102 runs sampled) 259 | Fastest is trace-mapping: decoded originalPositionFor 260 | 261 | 262 | *** 263 | 264 | 265 | react.js.map - 5726 segments 266 | 267 | Memory Usage: 268 | trace-mapping decoded 10872 bytes 269 | trace-mapping encoded 681512 bytes 270 | source-map-js 2563944 bytes 271 | source-map-0.6.1 2150864 bytes 272 | source-map-0.8.0 88680 bytes 273 | Chrome dev tools 1149576 bytes 274 | Smallest memory usage is trace-mapping decoded 275 | 276 | Init speed: 277 | trace-mapping: decoded JSON input x 1,887 ops/sec ±0.28% (99 runs sampled) 278 | trace-mapping: encoded JSON input x 4,749 ops/sec ±0.48% (97 runs sampled) 279 | trace-mapping: decoded Object input x 74,236 ops/sec ±0.11% (99 runs sampled) 280 | trace-mapping: encoded Object input x 5,752 ops/sec ±0.38% (100 runs sampled) 281 | source-map-js: encoded Object input x 806 ops/sec ±0.19% (97 runs sampled) 282 | source-map-0.6.1: encoded Object input x 418 ops/sec ±0.33% (94 runs sampled) 283 | Chrome dev tools: encoded Object input x 1,524 ops/sec ±0.57% (92 runs sampled) 284 | Fastest is trace-mapping: decoded Object input 285 | 286 | Trace speed (random): 287 | trace-mapping: decoded originalPositionFor x 620,201 ops/sec ±0.33% (96 runs sampled) 288 | trace-mapping: encoded originalPositionFor x 579,548 ops/sec ±0.35% (97 runs sampled) 289 | source-map-js: encoded originalPositionFor x 230,983 ops/sec ±0.62% (54 runs sampled) 290 | source-map-0.6.1: encoded originalPositionFor x 158,145 ops/sec ±0.80% (46 runs sampled) 291 | source-map-0.8.0: encoded originalPositionFor x 343,801 ops/sec ±0.55% (96 runs sampled) 292 | Chrome dev tools: encoded originalPositionFor x 659,649 ops/sec ±0.49% (98 runs sampled) 293 | Fastest is Chrome dev tools: encoded originalPositionFor 294 | 295 | Trace speed (ascending): 296 | trace-mapping: decoded originalPositionFor x 2,368,079 ops/sec ±0.32% (98 runs sampled) 297 | trace-mapping: encoded originalPositionFor x 2,134,039 ops/sec ±2.72% (87 runs sampled) 298 | source-map-js: encoded originalPositionFor x 290,120 ops/sec ±2.49% (82 runs sampled) 299 | source-map-0.6.1: encoded originalPositionFor x 187,613 ops/sec ±0.86% (49 runs sampled) 300 | source-map-0.8.0: encoded originalPositionFor x 479,569 ops/sec ±0.65% (96 runs sampled) 301 | Chrome dev tools: encoded originalPositionFor x 2,048,414 ops/sec ±0.24% (98 runs sampled) 302 | Fastest is trace-mapping: decoded originalPositionFor 303 | 304 | 305 | *** 306 | 307 | 308 | vscode.map - 2141001 segments 309 | 310 | Memory Usage: 311 | trace-mapping decoded 5206584 bytes 312 | trace-mapping encoded 208370336 bytes 313 | source-map-js 278493008 bytes 314 | source-map-0.6.1 391564048 bytes 315 | source-map-0.8.0 257508787 bytes 316 | Chrome dev tools 291053000 bytes 317 | Smallest memory usage is trace-mapping decoded 318 | 319 | Init speed: 320 | trace-mapping: decoded JSON input x 1.63 ops/sec ±33.88% (9 runs sampled) 321 | trace-mapping: encoded JSON input x 3.29 ops/sec ±36.13% (13 runs sampled) 322 | trace-mapping: decoded Object input x 103 ops/sec ±0.93% (77 runs sampled) 323 | trace-mapping: encoded Object input x 5.42 ops/sec ±28.54% (19 runs sampled) 324 | source-map-js: encoded Object input x 1.07 ops/sec ±13.84% (7 runs sampled) 325 | source-map-0.6.1: encoded Object input x 0.60 ops/sec ±2.43% (6 runs sampled) 326 | Chrome dev tools: encoded Object input x 2.61 ops/sec ±22.00% (11 runs sampled) 327 | Fastest is trace-mapping: decoded Object input 328 | 329 | Trace speed (random): 330 | trace-mapping: decoded originalPositionFor x 257,019 ops/sec ±0.97% (93 runs sampled) 331 | trace-mapping: encoded originalPositionFor x 179,163 ops/sec ±0.83% (92 runs sampled) 332 | source-map-js: encoded originalPositionFor x 73,337 ops/sec ±1.35% (87 runs sampled) 333 | source-map-0.6.1: encoded originalPositionFor x 38,797 ops/sec ±1.66% (88 runs sampled) 334 | source-map-0.8.0: encoded originalPositionFor x 107,758 ops/sec ±1.94% (45 runs sampled) 335 | Chrome dev tools: encoded originalPositionFor x 188,550 ops/sec ±1.85% (79 runs sampled) 336 | Fastest is trace-mapping: decoded originalPositionFor 337 | 338 | Trace speed (ascending): 339 | trace-mapping: decoded originalPositionFor x 447,621 ops/sec ±3.64% (94 runs sampled) 340 | trace-mapping: encoded originalPositionFor x 323,698 ops/sec ±5.20% (88 runs sampled) 341 | source-map-js: encoded originalPositionFor x 78,387 ops/sec ±1.69% (89 runs sampled) 342 | source-map-0.6.1: encoded originalPositionFor x 41,016 ops/sec ±3.01% (25 runs sampled) 343 | source-map-0.8.0: encoded originalPositionFor x 124,204 ops/sec ±0.90% (92 runs sampled) 344 | Chrome dev tools: encoded originalPositionFor x 230,087 ops/sec ±2.61% (93 runs sampled) 345 | Fastest is trace-mapping: decoded originalPositionFor 346 | ``` 347 | 348 | [source-map]: https://www.npmjs.com/package/source-map 349 | -------------------------------------------------------------------------------- /benchmark/chrome.mjs: -------------------------------------------------------------------------------- 1 | // This is an approximation of Chrome's source map decoding. 2 | // https://source.chromium.org/chromium/chromium/src/+/main:v8/tools/sourcemap.mjs;drc=7a90c32032759a1596fb9a0549cced1b89f42c5f 3 | 4 | export function SourceMap(sourceMappingURL, payload) { 5 | if (!SourceMap.prototype._base64Map) { 6 | const base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 7 | SourceMap.prototype._base64Map = {}; 8 | for (let i = 0; i < base64Digits.length; ++i) 9 | SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; 10 | } 11 | 12 | this._sourceMappingURL = sourceMappingURL; 13 | this._reverseMappingsBySourceURL = {}; 14 | this._mappings = []; 15 | this._sources = {}; 16 | this._sourceContentByURL = {}; 17 | this._parseMappingPayload(payload); 18 | } 19 | 20 | SourceMap.prototype = { 21 | /** 22 | * @return {string[]} 23 | */ 24 | sources() { 25 | return Object.keys(this._sources); 26 | }, 27 | 28 | /** 29 | * @param {string} sourceURL 30 | * @return {string|undefined} 31 | */ 32 | sourceContent(sourceURL) { 33 | return this._sourceContentByURL[sourceURL]; 34 | }, 35 | 36 | /** 37 | * @param {SourceMapV3} mappingPayload 38 | */ 39 | _parseMappingPayload(mappingPayload) { 40 | if (mappingPayload.sections) this._parseSections(mappingPayload.sections); 41 | else this._parseMap(mappingPayload, 0, 0); 42 | }, 43 | 44 | /** 45 | * @param {Array.} sections 46 | */ 47 | _parseSections(sections) { 48 | for (let i = 0; i < sections.length; ++i) { 49 | const section = sections[i]; 50 | this._parseMap(section.map, section.offset.line, section.offset.column); 51 | } 52 | }, 53 | 54 | /** 55 | * @param {number} lineNumber in compiled resource 56 | * @param {number} columnNumber in compiled resource 57 | * @return {?Array} 58 | */ 59 | findEntry(lineNumber, columnNumber) { 60 | let first = 0; 61 | let count = this._mappings.length; 62 | while (count > 1) { 63 | const step = count >> 1; 64 | const middle = first + step; 65 | const mapping = this._mappings[middle]; 66 | if (lineNumber < mapping[0] || (lineNumber === mapping[0] && columnNumber < mapping[1])) 67 | count = step; 68 | else { 69 | first = middle; 70 | count -= step; 71 | } 72 | } 73 | const entry = this._mappings[first]; 74 | if ( 75 | !first && 76 | entry && 77 | (lineNumber < entry[0] || (lineNumber === entry[0] && columnNumber < entry[1])) 78 | ) 79 | return null; 80 | return entry; 81 | }, 82 | 83 | /** 84 | * @param {string} sourceURL of the originating resource 85 | * @param {number} lineNumber in the originating resource 86 | * @return {Array} 87 | */ 88 | findEntryReversed(sourceURL, lineNumber) { 89 | const mappings = this._reverseMappingsBySourceURL[sourceURL]; 90 | for (; lineNumber < mappings.length; ++lineNumber) { 91 | const mapping = mappings[lineNumber]; 92 | if (mapping) return mapping; 93 | } 94 | return this._mappings[0]; 95 | }, 96 | 97 | /** 98 | * @override 99 | */ 100 | _parseMap(map, lineNumber, columnNumber) { 101 | let sourceIndex = 0; 102 | let sourceLineNumber = 0; 103 | let sourceColumnNumber = 0; 104 | let nameIndex = 0; 105 | 106 | const sources = []; 107 | const originalToCanonicalURLMap = {}; 108 | for (let i = 0; i < map.sources.length; ++i) { 109 | const originalSourceURL = map.sources[i]; 110 | let sourceRoot = map.sourceRoot || ''; 111 | if (sourceRoot && !sourceRoot.endsWith('/')) sourceRoot += '/'; 112 | const href = sourceRoot + originalSourceURL; 113 | const url = ParsedURL.completeURL(this._sourceMappingURL, href) || href; 114 | originalToCanonicalURLMap[originalSourceURL] = url; 115 | sources.push(url); 116 | this._sources[url] = true; 117 | 118 | if (map.sourcesContent && map.sourcesContent[i]) { 119 | this._sourceContentByURL[url] = map.sourcesContent[i]; 120 | } 121 | } 122 | 123 | const stringCharIterator = new SourceMap.StringCharIterator(map.mappings); 124 | let sourceURL = sources[sourceIndex]; 125 | 126 | while (true) { 127 | if (stringCharIterator.peek() === ',') stringCharIterator.next(); 128 | else { 129 | while (stringCharIterator.peek() === ';') { 130 | lineNumber += 1; 131 | columnNumber = 0; 132 | stringCharIterator.next(); 133 | } 134 | if (!stringCharIterator.hasNext()) break; 135 | } 136 | 137 | columnNumber += this._decodeVLQ(stringCharIterator); 138 | if (this._isSeparator(stringCharIterator.peek())) { 139 | this._mappings.push([lineNumber, columnNumber]); 140 | continue; 141 | } 142 | 143 | const sourceIndexDelta = this._decodeVLQ(stringCharIterator); 144 | if (sourceIndexDelta) { 145 | sourceIndex += sourceIndexDelta; 146 | sourceURL = sources[sourceIndex]; 147 | } 148 | sourceLineNumber += this._decodeVLQ(stringCharIterator); 149 | sourceColumnNumber += this._decodeVLQ(stringCharIterator); 150 | if (!this._isSeparator(stringCharIterator.peek())) 151 | nameIndex += this._decodeVLQ(stringCharIterator); 152 | 153 | this._mappings.push([ 154 | lineNumber, 155 | columnNumber, 156 | sourceURL, 157 | sourceLineNumber, 158 | sourceColumnNumber, 159 | ]); 160 | } 161 | 162 | for (let i = 0; i < this._mappings.length; ++i) { 163 | const mapping = this._mappings[i]; 164 | const url = mapping[2]; 165 | if (!url) continue; 166 | if (!this._reverseMappingsBySourceURL[url]) { 167 | this._reverseMappingsBySourceURL[url] = []; 168 | } 169 | const reverseMappings = this._reverseMappingsBySourceURL[url]; 170 | const sourceLine = mapping[3]; 171 | if (!reverseMappings[sourceLine]) { 172 | reverseMappings[sourceLine] = [mapping[0], mapping[1]]; 173 | } 174 | } 175 | }, 176 | 177 | /** 178 | * @param {string} char 179 | * @return {boolean} 180 | */ 181 | _isSeparator(char) { 182 | return char === ',' || char === ';'; 183 | }, 184 | 185 | /** 186 | * @param {SourceMap.StringCharIterator} stringCharIterator 187 | * @return {number} 188 | */ 189 | _decodeVLQ(stringCharIterator) { 190 | // Read unsigned value. 191 | let result = 0; 192 | let shift = 0; 193 | let digit; 194 | do { 195 | digit = this._base64Map[stringCharIterator.next()]; 196 | result += (digit & this._VLQ_BASE_MASK) << shift; 197 | shift += this._VLQ_BASE_SHIFT; 198 | } while (digit & this._VLQ_CONTINUATION_MASK); 199 | 200 | // Fix the sign. 201 | const negate = result & 1; 202 | // Use unsigned right shift, so that the 32nd bit is properly shifted 203 | // to the 31st, and the 32nd becomes unset. 204 | result >>>= 1; 205 | if (negate) { 206 | // We need to OR 0x80000000 here to ensure the 32nd bit (the sign bit 207 | // in a 32bit int) is always set for negative numbers. If `result` 208 | // were 1, (meaning `negate` is true and all other bits were zeros), 209 | // `result` would now be 0. But -0 doesn't flip the 32nd bit as 210 | // intended. All other numbers will successfully set the 32nd bit 211 | // without issue, so doing this is a noop for them. 212 | return -result | 0x80000000; 213 | } 214 | return result; 215 | }, 216 | 217 | _VLQ_BASE_SHIFT: 5, 218 | _VLQ_BASE_MASK: (1 << 5) - 1, 219 | _VLQ_CONTINUATION_MASK: 1 << 5, 220 | }; 221 | 222 | SourceMap.StringCharIterator = function StringCharIterator(string) { 223 | this._string = string; 224 | this._position = 0; 225 | }; 226 | 227 | SourceMap.StringCharIterator.prototype = { 228 | /** 229 | * @return {string} 230 | */ 231 | next() { 232 | return this._string.charAt(this._position++); 233 | }, 234 | 235 | /** 236 | * @return {string} 237 | */ 238 | peek() { 239 | return this._string.charAt(this._position); 240 | }, 241 | 242 | /** 243 | * @return {boolean} 244 | */ 245 | hasNext() { 246 | return this._position < this._string.length; 247 | }, 248 | }; 249 | 250 | function normalizePath(path) { 251 | if (path.indexOf('..') === -1 && path.indexOf('.') === -1) { 252 | return path; 253 | } 254 | // Remove leading slash (will be added back below) so we 255 | // can handle all (including empty) segments consistently. 256 | const segments = (path[0] === '/' ? path.substring(1) : path).split('/'); 257 | const normalizedSegments = []; 258 | for (const segment of segments) { 259 | if (segment === '.') { 260 | continue; 261 | } else if (segment === '..') { 262 | normalizedSegments.pop(); 263 | } else { 264 | normalizedSegments.push(segment); 265 | } 266 | } 267 | let normalizedPath = normalizedSegments.join('/'); 268 | if (path[0] === '/' && normalizedPath) { 269 | normalizedPath = '/' + normalizedPath; 270 | } 271 | if ( 272 | normalizedPath[normalizedPath.length - 1] !== '/' && 273 | (path[path.length - 1] === '/' || 274 | segments[segments.length - 1] === '.' || 275 | segments[segments.length - 1] === '..') 276 | ) { 277 | normalizedPath = normalizedPath + '/'; 278 | } 279 | return normalizedPath; 280 | } 281 | export function schemeIs(url, scheme) { 282 | try { 283 | return new URL(url).protocol === scheme; 284 | } catch (e) { 285 | return false; 286 | } 287 | } 288 | export class ParsedURL { 289 | #displayNameInternal; 290 | #dataURLDisplayNameInternal; 291 | constructor(url) { 292 | this.isValid = false; 293 | this.url = url; 294 | this.scheme = ''; 295 | this.user = ''; 296 | this.host = ''; 297 | this.port = ''; 298 | this.path = ''; 299 | this.queryParams = ''; 300 | this.fragment = ''; 301 | this.folderPathComponents = ''; 302 | this.lastPathComponent = ''; 303 | const isBlobUrl = this.url.startsWith('blob:'); 304 | const urlToMatch = isBlobUrl ? url.substring(5) : url; 305 | const match = urlToMatch.match(ParsedURL.urlRegex()); 306 | if (match) { 307 | this.isValid = true; 308 | if (isBlobUrl) { 309 | this.blobInnerScheme = match[2].toLowerCase(); 310 | this.scheme = 'blob'; 311 | } else { 312 | this.scheme = match[2].toLowerCase(); 313 | } 314 | this.user = match[3] ?? ''; 315 | this.host = match[4] ?? ''; 316 | this.port = match[5] ?? ''; 317 | this.path = match[6] ?? '/'; 318 | this.queryParams = match[7] ?? ''; 319 | this.fragment = match[8] ?? ''; 320 | } else { 321 | if (this.url.startsWith('data:')) { 322 | this.scheme = 'data'; 323 | return; 324 | } 325 | if (this.url.startsWith('blob:')) { 326 | this.scheme = 'blob'; 327 | return; 328 | } 329 | if (this.url === 'about:blank') { 330 | this.scheme = 'about'; 331 | return; 332 | } 333 | this.path = this.url; 334 | } 335 | const lastSlashExceptTrailingIndex = this.path.lastIndexOf('/', this.path.length - 2); 336 | if (lastSlashExceptTrailingIndex !== -1) { 337 | this.lastPathComponent = this.path.substring(lastSlashExceptTrailingIndex + 1); 338 | } else { 339 | this.lastPathComponent = this.path; 340 | } 341 | const lastSlashIndex = this.path.lastIndexOf('/'); 342 | if (lastSlashIndex !== -1) { 343 | this.folderPathComponents = this.path.substring(0, lastSlashIndex); 344 | } 345 | } 346 | static fromString(string) { 347 | const parsedURL = new ParsedURL(string.toString()); 348 | if (parsedURL.isValid) { 349 | return parsedURL; 350 | } 351 | return null; 352 | } 353 | static preEncodeSpecialCharactersInPath(path) { 354 | // Based on net::FilePathToFileURL. Ideally we would handle 355 | // '\\' as well on non-Windows file systems. 356 | for (const specialChar of ['%', ';', '#', '?', ' ']) { 357 | path = path.replaceAll(specialChar, encodeURIComponent(specialChar)); 358 | } 359 | return path; 360 | } 361 | static rawPathToEncodedPathString(path) { 362 | const partiallyEncoded = ParsedURL.preEncodeSpecialCharactersInPath(path); 363 | if (path.startsWith('/')) { 364 | return new URL(partiallyEncoded, 'file:///').pathname; 365 | } 366 | // URL prepends a '/' 367 | return new URL('/' + partiallyEncoded, 'file:///').pathname.substring(1); 368 | } 369 | /** 370 | * @param name Must not be encoded 371 | */ 372 | static encodedFromParentPathAndName(parentPath, name) { 373 | return ParsedURL.concatenate(parentPath, '/', ParsedURL.preEncodeSpecialCharactersInPath(name)); 374 | } 375 | /** 376 | * @param name Must not be encoded 377 | */ 378 | static urlFromParentUrlAndName(parentUrl, name) { 379 | return ParsedURL.concatenate(parentUrl, '/', ParsedURL.preEncodeSpecialCharactersInPath(name)); 380 | } 381 | static encodedPathToRawPathString(encPath) { 382 | return decodeURIComponent(encPath); 383 | } 384 | static rawPathToUrlString(fileSystemPath) { 385 | let preEncodedPath = ParsedURL.preEncodeSpecialCharactersInPath( 386 | fileSystemPath.replace(/\\/g, '/'), 387 | ); 388 | preEncodedPath = preEncodedPath.replace(/\\/g, '/'); 389 | if (!preEncodedPath.startsWith('file://')) { 390 | if (preEncodedPath.startsWith('/')) { 391 | preEncodedPath = 'file://' + preEncodedPath; 392 | } else { 393 | preEncodedPath = 'file:///' + preEncodedPath; 394 | } 395 | } 396 | return new URL(preEncodedPath).toString(); 397 | } 398 | static relativePathToUrlString(relativePath, baseURL) { 399 | const preEncodedPath = ParsedURL.preEncodeSpecialCharactersInPath( 400 | relativePath.replace(/\\/g, '/'), 401 | ); 402 | return new URL(preEncodedPath, baseURL).toString(); 403 | } 404 | static urlToRawPathString(fileURL, isWindows) { 405 | console.assert(fileURL.startsWith('file://'), 'This must be a file URL.'); 406 | const decodedFileURL = decodeURIComponent(fileURL); 407 | if (isWindows) { 408 | return decodedFileURL.substring('file:///'.length).replace(/\//g, '\\'); 409 | } 410 | return decodedFileURL.substring('file://'.length); 411 | } 412 | static sliceUrlToEncodedPathString(url, start) { 413 | return url.substring(start); 414 | } 415 | static substr(devToolsPath, from, length) { 416 | return devToolsPath.substr(from, length); 417 | } 418 | static substring(devToolsPath, start, end) { 419 | return devToolsPath.substring(start, end); 420 | } 421 | static prepend(prefix, devToolsPath) { 422 | return prefix + devToolsPath; 423 | } 424 | static concatenate(devToolsPath, ...appendage) { 425 | return devToolsPath.concat(...appendage); 426 | } 427 | static trim(devToolsPath) { 428 | return devToolsPath.trim(); 429 | } 430 | static slice(devToolsPath, start, end) { 431 | return devToolsPath.slice(start, end); 432 | } 433 | static join(devToolsPaths, separator) { 434 | return devToolsPaths.join(separator); 435 | } 436 | static split(devToolsPath, separator, limit) { 437 | return devToolsPath.split(separator, limit); 438 | } 439 | static toLowerCase(devToolsPath) { 440 | return devToolsPath.toLowerCase(); 441 | } 442 | static isValidUrlString(str) { 443 | return new ParsedURL(str).isValid; 444 | } 445 | static urlWithoutHash(url) { 446 | const hashIndex = url.indexOf('#'); 447 | if (hashIndex !== -1) { 448 | return url.substr(0, hashIndex); 449 | } 450 | return url; 451 | } 452 | static urlRegex() { 453 | if (ParsedURL.urlRegexInstance) { 454 | return ParsedURL.urlRegexInstance; 455 | } 456 | // RegExp groups: 457 | // 1 - scheme, hostname, ?port 458 | // 2 - scheme (using the RFC3986 grammar) 459 | // 3 - ?user:password 460 | // 4 - hostname 461 | // 5 - ?port 462 | // 6 - ?path 463 | // 7 - ?query 464 | // 8 - ?fragment 465 | const schemeRegex = /([A-Za-z][A-Za-z0-9+.-]*):\/\//; 466 | const userRegex = /(?:([A-Za-z0-9\-._~%!$&'()*+,;=:]*)@)?/; 467 | const hostRegex = /((?:\[::\d?\])|(?:[^\s\/:]*))/; 468 | const portRegex = /(?::([\d]+))?/; 469 | const pathRegex = /(\/[^#?]*)?/; 470 | const queryRegex = /(?:\?([^#]*))?/; 471 | const fragmentRegex = /(?:#(.*))?/; 472 | ParsedURL.urlRegexInstance = new RegExp( 473 | '^(' + 474 | schemeRegex.source + 475 | userRegex.source + 476 | hostRegex.source + 477 | portRegex.source + 478 | ')' + 479 | pathRegex.source + 480 | queryRegex.source + 481 | fragmentRegex.source + 482 | '$', 483 | ); 484 | return ParsedURL.urlRegexInstance; 485 | } 486 | static extractPath(url) { 487 | const parsedURL = this.fromString(url); 488 | return parsedURL ? parsedURL.path : ''; 489 | } 490 | static extractOrigin(url) { 491 | const parsedURL = this.fromString(url); 492 | return parsedURL ? parsedURL.securityOrigin() : ''; 493 | } 494 | static extractExtension(url) { 495 | url = ParsedURL.urlWithoutHash(url); 496 | const indexOfQuestionMark = url.indexOf('?'); 497 | if (indexOfQuestionMark !== -1) { 498 | url = url.substr(0, indexOfQuestionMark); 499 | } 500 | const lastIndexOfSlash = url.lastIndexOf('/'); 501 | if (lastIndexOfSlash !== -1) { 502 | url = url.substr(lastIndexOfSlash + 1); 503 | } 504 | const lastIndexOfDot = url.lastIndexOf('.'); 505 | if (lastIndexOfDot !== -1) { 506 | url = url.substr(lastIndexOfDot + 1); 507 | const lastIndexOfPercent = url.indexOf('%'); 508 | if (lastIndexOfPercent !== -1) { 509 | return url.substr(0, lastIndexOfPercent); 510 | } 511 | return url; 512 | } 513 | return ''; 514 | } 515 | static extractName(url) { 516 | let index = url.lastIndexOf('/'); 517 | const pathAndQuery = index !== -1 ? url.substr(index + 1) : url; 518 | index = pathAndQuery.indexOf('?'); 519 | return index < 0 ? pathAndQuery : pathAndQuery.substr(0, index); 520 | } 521 | static completeURL(baseURL, href) { 522 | // Return special URLs as-is. 523 | const trimmedHref = href.trim(); 524 | if ( 525 | trimmedHref.startsWith('data:') || 526 | trimmedHref.startsWith('blob:') || 527 | trimmedHref.startsWith('javascript:') || 528 | trimmedHref.startsWith('mailto:') 529 | ) { 530 | return href; 531 | } 532 | // Return absolute URLs with normalized path and other components as-is. 533 | const parsedHref = this.fromString(trimmedHref); 534 | if (parsedHref && parsedHref.scheme) { 535 | const securityOrigin = parsedHref.securityOrigin(); 536 | const pathText = normalizePath(parsedHref.path); 537 | const queryText = parsedHref.queryParams && `?${parsedHref.queryParams}`; 538 | const fragmentText = parsedHref.fragment && `#${parsedHref.fragment}`; 539 | return securityOrigin + pathText + queryText + fragmentText; 540 | } 541 | const parsedURL = this.fromString(baseURL); 542 | if (!parsedURL) { 543 | return null; 544 | } 545 | if (parsedURL.isDataURL()) { 546 | return href; 547 | } 548 | if (href.length > 1 && href.charAt(0) === '/' && href.charAt(1) === '/') { 549 | // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol). 550 | return parsedURL.scheme + ':' + href; 551 | } 552 | const securityOrigin = parsedURL.securityOrigin(); 553 | const pathText = parsedURL.path; 554 | const queryText = parsedURL.queryParams ? '?' + parsedURL.queryParams : ''; 555 | // Empty href resolves to a URL without fragment. 556 | if (!href.length) { 557 | return securityOrigin + pathText + queryText; 558 | } 559 | if (href.charAt(0) === '#') { 560 | return securityOrigin + pathText + queryText + href; 561 | } 562 | if (href.charAt(0) === '?') { 563 | return securityOrigin + pathText + href; 564 | } 565 | const hrefMatches = href.match(/^[^#?]*/); 566 | if (!hrefMatches || !href.length) { 567 | throw new Error('Invalid href'); 568 | } 569 | let hrefPath = hrefMatches[0]; 570 | const hrefSuffix = href.substring(hrefPath.length); 571 | if (hrefPath.charAt(0) !== '/') { 572 | hrefPath = parsedURL.folderPathComponents + '/' + hrefPath; 573 | } 574 | return securityOrigin + normalizePath(hrefPath) + hrefSuffix; 575 | } 576 | static splitLineAndColumn(string) { 577 | // Only look for line and column numbers in the path to avoid matching port numbers. 578 | const beforePathMatch = string.match(ParsedURL.urlRegex()); 579 | let beforePath = ''; 580 | let pathAndAfter = string; 581 | if (beforePathMatch) { 582 | beforePath = beforePathMatch[1]; 583 | pathAndAfter = string.substring(beforePathMatch[1].length); 584 | } 585 | const lineColumnRegEx = /(?::(\d+))?(?::(\d+))?$/; 586 | const lineColumnMatch = lineColumnRegEx.exec(pathAndAfter); 587 | let lineNumber; 588 | let columnNumber; 589 | console.assert(Boolean(lineColumnMatch)); 590 | if (!lineColumnMatch) { 591 | return { url: string, lineNumber: 0, columnNumber: 0 }; 592 | } 593 | if (typeof lineColumnMatch[1] === 'string') { 594 | lineNumber = parseInt(lineColumnMatch[1], 10); 595 | // Immediately convert line and column to 0-based numbers. 596 | lineNumber = isNaN(lineNumber) ? undefined : lineNumber - 1; 597 | } 598 | if (typeof lineColumnMatch[2] === 'string') { 599 | columnNumber = parseInt(lineColumnMatch[2], 10); 600 | columnNumber = isNaN(columnNumber) ? undefined : columnNumber - 1; 601 | } 602 | let url = 603 | beforePath + pathAndAfter.substring(0, pathAndAfter.length - lineColumnMatch[0].length); 604 | if (lineColumnMatch[1] === undefined && lineColumnMatch[2] === undefined) { 605 | const wasmCodeOffsetRegex = /wasm-function\[\d+\]:0x([a-z0-9]+)$/g; 606 | const wasmCodeOffsetMatch = wasmCodeOffsetRegex.exec(pathAndAfter); 607 | if (wasmCodeOffsetMatch && typeof wasmCodeOffsetMatch[1] === 'string') { 608 | url = ParsedURL.removeWasmFunctionInfoFromURL(url); 609 | columnNumber = parseInt(wasmCodeOffsetMatch[1], 16); 610 | columnNumber = isNaN(columnNumber) ? undefined : columnNumber; 611 | } 612 | } 613 | return { url, lineNumber, columnNumber }; 614 | } 615 | static removeWasmFunctionInfoFromURL(url) { 616 | const wasmFunctionRegEx = /:wasm-function\[\d+\]/; 617 | const wasmFunctionIndex = url.search(wasmFunctionRegEx); 618 | if (wasmFunctionIndex === -1) { 619 | return url; 620 | } 621 | return ParsedURL.substring(url, 0, wasmFunctionIndex); 622 | } 623 | static beginsWithWindowsDriveLetter(url) { 624 | return /^[A-Za-z]:/.test(url); 625 | } 626 | static beginsWithScheme(url) { 627 | return /^[A-Za-z][A-Za-z0-9+.-]*:/.test(url); 628 | } 629 | static isRelativeURL(url) { 630 | return !this.beginsWithScheme(url) || this.beginsWithWindowsDriveLetter(url); 631 | } 632 | get displayName() { 633 | if (this.#displayNameInternal) { 634 | return this.#displayNameInternal; 635 | } 636 | if (this.isDataURL()) { 637 | return this.dataURLDisplayName(); 638 | } 639 | if (this.isBlobURL()) { 640 | return this.url; 641 | } 642 | if (this.isAboutBlank()) { 643 | return this.url; 644 | } 645 | this.#displayNameInternal = this.lastPathComponent; 646 | if (!this.#displayNameInternal) { 647 | this.#displayNameInternal = (this.host || '') + '/'; 648 | } 649 | if (this.#displayNameInternal === '/') { 650 | this.#displayNameInternal = this.url; 651 | } 652 | return this.#displayNameInternal; 653 | } 654 | dataURLDisplayName() { 655 | if (this.#dataURLDisplayNameInternal) { 656 | return this.#dataURLDisplayNameInternal; 657 | } 658 | if (!this.isDataURL()) { 659 | return ''; 660 | } 661 | this.#dataURLDisplayNameInternal = trimEndWithMaxLength(this.url, 20); 662 | return this.#dataURLDisplayNameInternal; 663 | } 664 | isAboutBlank() { 665 | return this.url === 'about:blank'; 666 | } 667 | isDataURL() { 668 | return this.scheme === 'data'; 669 | } 670 | isHttpOrHttps() { 671 | return this.scheme === 'http' || this.scheme === 'https'; 672 | } 673 | isBlobURL() { 674 | return this.url.startsWith('blob:'); 675 | } 676 | lastPathComponentWithFragment() { 677 | return this.lastPathComponent + (this.fragment ? '#' + this.fragment : ''); 678 | } 679 | domain() { 680 | if (this.isDataURL()) { 681 | return 'data:'; 682 | } 683 | return this.host + (this.port ? ':' + this.port : ''); 684 | } 685 | securityOrigin() { 686 | if (this.isDataURL()) { 687 | return 'data:'; 688 | } 689 | const scheme = this.isBlobURL() ? this.blobInnerScheme : this.scheme; 690 | return scheme + '://' + this.domain(); 691 | } 692 | urlWithoutScheme() { 693 | if (this.scheme && this.url.startsWith(this.scheme + '://')) { 694 | return this.url.substring(this.scheme.length + 3); 695 | } 696 | return this.url; 697 | } 698 | static { 699 | this.urlRegexInstance = null; 700 | } 701 | } 702 | export const trimEndWithMaxLength = (str, maxLength) => { 703 | if (str.length <= maxLength) { 704 | return String(str); 705 | } 706 | return str.substr(0, maxLength - 1) + '\u2026'; 707 | }; 708 | -------------------------------------------------------------------------------- /benchmark/index.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | import { readdirSync, readFileSync } from 'fs'; 4 | import { fileURLToPath } from 'url'; 5 | import { dirname, join, relative } from 'path'; 6 | import Benchmark from 'benchmark'; 7 | import { decode } from '@jridgewell/sourcemap-codec'; 8 | import { 9 | TraceMap as CurrentTraceMap, 10 | traceSegment as currentTraceSegment, 11 | } from '../dist/trace-mapping.mjs'; 12 | import { 13 | TraceMap as LatestTraceMap, 14 | traceSegment as latestTraceSegment, 15 | } from '../dist/trace-mapping.mjs'; 16 | import { SourceMapConsumer as SourceMapConsumerJs } from 'source-map-js'; 17 | import { SourceMapConsumer as SourceMapConsumer061 } from 'source-map'; 18 | import { SourceMapConsumer as SourceMapConsumerWasm } from 'source-map-wasm'; 19 | import { SourceMap as ChromeMap } from './chrome.mjs'; 20 | 21 | const dir = relative(process.cwd(), dirname(fileURLToPath(import.meta.url))); 22 | const diff = !!process.env.DIFF; 23 | 24 | console.log(`node ${process.version}\n`); 25 | 26 | async function track(label, results, cb) { 27 | if (global.gc) global.gc(); 28 | const before = process.memoryUsage(); 29 | const ret = await cb(); 30 | const after = process.memoryUsage(); 31 | const d = delta(before, after); 32 | console.log( 33 | `${label.padEnd(25, ' ')} ${String(d.heapUsed + d.external).padStart(10, ' ')} bytes`, 34 | ); 35 | results.push({ label, delta: d.heapUsed + d.external }); 36 | return ret; 37 | } 38 | 39 | function delta(before, after) { 40 | return { 41 | rss: after.rss - before.rss, 42 | heapTotal: after.heapTotal - before.heapTotal, 43 | heapUsed: after.heapUsed - before.heapUsed, 44 | external: after.external - before.external, 45 | arrayBuffers: after.arrayBuffers - before.arrayBuffers, 46 | }; 47 | } 48 | 49 | async function bench(file) { 50 | const map = JSON.parse(readFileSync(join(dir, file))); 51 | const encodedMapData = map; 52 | const encodedMapDataJson = JSON.stringify(map); 53 | const decodedMapData = { ...map, mappings: decode(map.mappings) }; 54 | const decodedMapDataJson = JSON.stringify(decodedMapData); 55 | 56 | const lines = decodedMapData.mappings; 57 | const segments = lines.reduce((cur, line) => { 58 | return cur + line.length; 59 | }, 0); 60 | console.log(file, `- ${segments} segments`); 61 | console.log(''); 62 | 63 | console.log('Memory Usage:'); 64 | const results = []; 65 | let benchmark, 66 | currentDecoded, 67 | currentEncoded, 68 | latestDecoded, 69 | latestEncoded, 70 | smcjs, 71 | smc061, 72 | smcWasm, 73 | chromeMap; 74 | currentDecoded = await track('trace-mapping decoded', results, () => { 75 | const decoded = new CurrentTraceMap(decodedMapData); 76 | currentTraceSegment(decoded, 0, 0); 77 | return decoded; 78 | }); 79 | currentEncoded = await track('trace-mapping encoded', results, () => { 80 | const encoded = new CurrentTraceMap(encodedMapData); 81 | currentTraceSegment(encoded, 0, 0); 82 | return encoded; 83 | }); 84 | if (diff) { 85 | latestDecoded = await track('trace-mapping latest decoded', results, () => { 86 | const decoded = new LatestTraceMap(decodedMapData); 87 | latestTraceSegment(decoded, 0, 0); 88 | return decoded; 89 | }); 90 | latestEncoded = await track('trace-mapping latest encoded', results, () => { 91 | const encoded = new LatestTraceMap(encodedMapData); 92 | latestTraceSegment(encoded, 0, 0); 93 | return encoded; 94 | }); 95 | } else { 96 | smcjs = await track('source-map-js', results, () => { 97 | const smcjs = new SourceMapConsumerJs(encodedMapData); 98 | smcjs.originalPositionFor({ line: 1, column: 0 }); 99 | return smcjs; 100 | }); 101 | smc061 = await track('source-map-0.6.1', results, () => { 102 | const smc061 = new SourceMapConsumer061(encodedMapData); 103 | smc061.originalPositionFor({ line: 1, column: 0 }); 104 | return smc061; 105 | }); 106 | smcWasm = await track('source-map-0.8.0', results, async () => { 107 | const smcWasm = await new SourceMapConsumerWasm(encodedMapData); 108 | smcWasm.originalPositionFor({ line: 1, column: 0 }); 109 | return smcWasm; 110 | }); 111 | chromeMap = await track('Chrome dev tools', results, async () => { 112 | const map = new ChromeMap('url', encodedMapData); 113 | map.findEntry(0, 0); 114 | return map; 115 | }); 116 | } 117 | const winner = results.reduce((min, cur) => { 118 | if (cur.delta < min.delta) return cur; 119 | return min; 120 | }); 121 | console.log(`Smallest memory usage is ${winner.label}`); 122 | 123 | console.log(''); 124 | 125 | console.log('Init speed:'); 126 | benchmark = new Benchmark.Suite() 127 | .add('trace-mapping: decoded JSON input', () => { 128 | currentTraceSegment(new CurrentTraceMap(decodedMapDataJson), 0, 0); 129 | }) 130 | .add('trace-mapping: encoded JSON input', () => { 131 | currentTraceSegment(new CurrentTraceMap(encodedMapDataJson), 0, 0); 132 | }) 133 | .add('trace-mapping: decoded Object input', () => { 134 | currentTraceSegment(new CurrentTraceMap(decodedMapData), 0, 0); 135 | }) 136 | .add('trace-mapping: encoded Object input', () => { 137 | currentTraceSegment(new CurrentTraceMap(encodedMapData), 0, 0); 138 | }); 139 | if (diff) { 140 | benchmark = benchmark 141 | .add('trace-mapping latest: decoded JSON input', () => { 142 | latestTraceSegment(new LatestTraceMap(decodedMapDataJson), 0, 0); 143 | }) 144 | .add('trace-mapping latest: encoded JSON input', () => { 145 | latestTraceSegment(new LatestTraceMap(encodedMapDataJson), 0, 0); 146 | }) 147 | .add('trace-mapping latest: decoded Object input', () => { 148 | latestTraceSegment(new LatestTraceMap(decodedMapData), 0, 0); 149 | }) 150 | .add('trace-mapping latest: encoded Object input', () => { 151 | latestTraceSegment(new LatestTraceMap(encodedMapData), 0, 0); 152 | }); 153 | } else { 154 | benchmark = benchmark 155 | .add('source-map-js: encoded Object input', () => { 156 | new SourceMapConsumerJs(encodedMapData).originalPositionFor({ line: 1, column: 0 }); 157 | }) 158 | .add('source-map-0.6.1: encoded Object input', () => { 159 | new SourceMapConsumer061(encodedMapData).originalPositionFor({ line: 1, column: 0 }); 160 | }) 161 | .add('Chrome dev tools: encoded Object input', () => { 162 | new ChromeMap('url', encodedMapData).findEntry(0, 0); 163 | }); 164 | // WASM isn't tested because its async and OOMs. 165 | // .add('source-map-0.8.0: encoded Object input', () => { }) 166 | } 167 | // add listeners 168 | benchmark 169 | .on('error', (event) => console.error(event.target.error)) 170 | .on('cycle', (event) => { 171 | console.log(String(event.target)); 172 | }) 173 | .on('complete', function () { 174 | console.log('Fastest is ' + this.filter('fastest').map('name')); 175 | }) 176 | .run({}); 177 | 178 | console.log(''); 179 | 180 | console.log('Trace speed (random):'); 181 | benchmark = new Benchmark.Suite() 182 | .add('trace-mapping: decoded originalPositionFor', () => { 183 | const i = Math.floor(Math.random() * lines.length); 184 | const line = lines[i]; 185 | if (line.length === 0) return; 186 | const shift = Math.ceil(line.length / 100); 187 | for (let _ = 0; _ < line.length; _ += shift) { 188 | const j = Math.floor(Math.random() * line.length); 189 | const column = line[j][0]; 190 | currentTraceSegment(currentDecoded, i, column); 191 | } 192 | }) 193 | .add('trace-mapping: encoded originalPositionFor', () => { 194 | const i = Math.floor(Math.random() * lines.length); 195 | const line = lines[i]; 196 | if (line.length === 0) return; 197 | const shift = Math.ceil(line.length / 100); 198 | for (let _ = 0; _ < line.length; _ += shift) { 199 | const j = Math.floor(Math.random() * line.length); 200 | const column = line[j][0]; 201 | currentTraceSegment(currentEncoded, i, column); 202 | } 203 | }); 204 | if (diff) { 205 | benchmark = benchmark 206 | .add('trace-mapping latest: decoded originalPositionFor', () => { 207 | const i = Math.floor(Math.random() * lines.length); 208 | const line = lines[i]; 209 | if (line.length === 0) return; 210 | const shift = Math.ceil(line.length / 100); 211 | for (let _ = 0; _ < line.length; _ += shift) { 212 | const j = Math.floor(Math.random() * line.length); 213 | const column = line[j][0]; 214 | latestTraceSegment(latestDecoded, i, column); 215 | } 216 | }) 217 | .add('trace-mapping latest: encoded originalPositionFor', () => { 218 | const i = Math.floor(Math.random() * lines.length); 219 | const line = lines[i]; 220 | if (line.length === 0) return; 221 | const shift = Math.ceil(line.length / 100); 222 | for (let _ = 0; _ < line.length; _ += shift) { 223 | const j = Math.floor(Math.random() * line.length); 224 | const column = line[j][0]; 225 | latestTraceSegment(latestEncoded, i, column); 226 | } 227 | }); 228 | } else { 229 | benchmark = benchmark 230 | .add('source-map-js: encoded originalPositionFor', () => { 231 | const i = Math.floor(Math.random() * lines.length); 232 | const line = lines[i]; 233 | if (line.length === 0) return; 234 | const shift = Math.ceil(line.length / 100); 235 | for (let _ = 0; _ < line.length; _ += shift) { 236 | const j = Math.floor(Math.random() * line.length); 237 | const column = line[j][0]; 238 | smcjs.originalPositionFor({ line: i + 1, column }); 239 | } 240 | }) 241 | .add('source-map-0.6.1: encoded originalPositionFor', () => { 242 | const i = Math.floor(Math.random() * lines.length); 243 | const line = lines[i]; 244 | if (line.length === 0) return; 245 | const shift = Math.ceil(line.length / 100); 246 | for (let _ = 0; _ < line.length; _ += shift) { 247 | const j = Math.floor(Math.random() * line.length); 248 | const column = line[j][0]; 249 | smc061.originalPositionFor({ line: i + 1, column }); 250 | } 251 | }) 252 | .add('source-map-0.8.0: encoded originalPositionFor', () => { 253 | const i = Math.floor(Math.random() * lines.length); 254 | const line = lines[i]; 255 | if (line.length === 0) return; 256 | const shift = Math.ceil(line.length / 100); 257 | for (let _ = 0; _ < line.length; _ += shift) { 258 | const j = Math.floor(Math.random() * line.length); 259 | const column = line[j][0]; 260 | smcWasm.originalPositionFor({ line: i + 1, column }); 261 | } 262 | }) 263 | .add('Chrome dev tools: encoded originalPositionFor', () => { 264 | const i = Math.floor(Math.random() * lines.length); 265 | const line = lines[i]; 266 | if (line.length === 0) return; 267 | const shift = Math.ceil(line.length / 100); 268 | for (let _ = 0; _ < line.length; _ += shift) { 269 | const j = Math.floor(Math.random() * line.length); 270 | const column = line[j][0]; 271 | chromeMap.findEntry(i, column); 272 | } 273 | }); 274 | } 275 | // add listeners 276 | benchmark 277 | .on('error', (event) => console.error(event.target.error)) 278 | .on('cycle', (event) => { 279 | console.log(String(event.target)); 280 | }) 281 | .on('complete', function () { 282 | console.log('Fastest is ' + this.filter('fastest').map('name')); 283 | }) 284 | .run({}); 285 | 286 | console.log(''); 287 | 288 | console.log('Trace speed (ascending):'); 289 | benchmark = new Benchmark.Suite() 290 | .add('trace-mapping: decoded originalPositionFor', () => { 291 | const i = Math.floor(Math.random() * lines.length); 292 | const line = lines[i]; 293 | if (line.length === 0) return; 294 | const shift = Math.ceil(line.length / 100); 295 | for (let j = 0; j < line.length; j += shift) { 296 | const column = line[j][0]; 297 | currentTraceSegment(currentDecoded, i, column); 298 | } 299 | }) 300 | .add('trace-mapping: encoded originalPositionFor', () => { 301 | const i = Math.floor(Math.random() * lines.length); 302 | const line = lines[i]; 303 | if (line.length === 0) return; 304 | const shift = Math.ceil(line.length / 100); 305 | for (let j = 0; j < line.length; j += shift) { 306 | const column = line[j][0]; 307 | currentTraceSegment(currentEncoded, i, column); 308 | } 309 | }); 310 | if (diff) { 311 | benchmark = benchmark 312 | .add('trace-mapping latest: decoded originalPositionFor', () => { 313 | const i = Math.floor(Math.random() * lines.length); 314 | const line = lines[i]; 315 | if (line.length === 0) return; 316 | const shift = Math.ceil(line.length / 100); 317 | for (let j = 0; j < line.length; j += shift) { 318 | const column = line[j][0]; 319 | latestTraceSegment(latestDecoded, i, column); 320 | } 321 | }) 322 | .add('trace-mapping latest: encoded originalPositionFor', () => { 323 | const i = Math.floor(Math.random() * lines.length); 324 | const line = lines[i]; 325 | if (line.length === 0) return; 326 | const shift = Math.ceil(line.length / 100); 327 | for (let j = 0; j < line.length; j += shift) { 328 | const column = line[j][0]; 329 | latestTraceSegment(latestEncoded, i, column); 330 | } 331 | }); 332 | } else { 333 | benchmark = benchmark 334 | .add('source-map-js: encoded originalPositionFor', () => { 335 | const i = Math.floor(Math.random() * lines.length); 336 | const line = lines[i]; 337 | if (line.length === 0) return; 338 | const shift = Math.ceil(line.length / 100); 339 | for (let j = 0; j < line.length; j += shift) { 340 | const column = line[j][0]; 341 | smcjs.originalPositionFor({ line: i + 1, column }); 342 | } 343 | }) 344 | .add('source-map-0.6.1: encoded originalPositionFor', () => { 345 | const i = Math.floor(Math.random() * lines.length); 346 | const line = lines[i]; 347 | if (line.length === 0) return; 348 | const shift = Math.ceil(line.length / 100); 349 | for (let j = 0; j < line.length; j += shift) { 350 | const column = line[j][0]; 351 | smc061.originalPositionFor({ line: i + 1, column }); 352 | } 353 | }) 354 | .add('source-map-0.8.0: encoded originalPositionFor', () => { 355 | const i = Math.floor(Math.random() * lines.length); 356 | const line = lines[i]; 357 | if (line.length === 0) return; 358 | const shift = Math.ceil(line.length / 100); 359 | for (let j = 0; j < line.length; j += shift) { 360 | const column = line[j][0]; 361 | smcWasm.originalPositionFor({ line: i + 1, column }); 362 | } 363 | }) 364 | .add('Chrome dev tools: encoded originalPositionFor', () => { 365 | const i = Math.floor(Math.random() * lines.length); 366 | const line = lines[i]; 367 | if (line.length === 0) return; 368 | const shift = Math.ceil(line.length / 100); 369 | for (let j = 0; j < line.length; j += shift) { 370 | const column = line[j][0]; 371 | chromeMap.findEntry(i, column); 372 | } 373 | }); 374 | } 375 | // add listeners 376 | benchmark 377 | .on('error', (event) => console.error(event.target.error)) 378 | .on('cycle', (event) => { 379 | console.log(String(event.target)); 380 | }) 381 | .on('complete', function () { 382 | console.log('Fastest is ' + this.filter('fastest').map('name')); 383 | }) 384 | .run({}); 385 | 386 | if (smcWasm) smcWasm.destroy(); 387 | } 388 | 389 | async function run(files) { 390 | let first = true; 391 | for (const file of files) { 392 | if (!file.endsWith('.map')) continue; 393 | if (file !== 'vscode.map') continue; 394 | 395 | if (!first) console.log('\n\n***\n\n'); 396 | first = false; 397 | 398 | await bench(file); 399 | } 400 | } 401 | run(readdirSync(dir)); 402 | -------------------------------------------------------------------------------- /benchmark/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarks", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "benchmarks", 8 | "devDependencies": { 9 | "source-map": "0.6.1", 10 | "source-map-js": "1.0.2", 11 | "source-map-wasm": "npm:source-map@0.8.0-beta.0", 12 | "trace-map": "npm:@jridgewell/trace-mapping@latest" 13 | } 14 | }, 15 | "node_modules/@jridgewell/resolve-uri": { 16 | "version": "3.1.2", 17 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 18 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 19 | "dev": true, 20 | "engines": { 21 | "node": ">=6.0.0" 22 | } 23 | }, 24 | "node_modules/@jridgewell/sourcemap-codec": { 25 | "version": "1.4.15", 26 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 27 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 28 | "dev": true 29 | }, 30 | "node_modules/lodash.sortby": { 31 | "version": "4.7.0", 32 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 33 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", 34 | "dev": true 35 | }, 36 | "node_modules/punycode": { 37 | "version": "2.1.1", 38 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 39 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 40 | "dev": true, 41 | "engines": { 42 | "node": ">=6" 43 | } 44 | }, 45 | "node_modules/source-map": { 46 | "version": "0.6.1", 47 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 48 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 49 | "dev": true, 50 | "engines": { 51 | "node": ">=0.10.0" 52 | } 53 | }, 54 | "node_modules/source-map-js": { 55 | "version": "1.0.2", 56 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 57 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 58 | "dev": true, 59 | "engines": { 60 | "node": ">=0.10.0" 61 | } 62 | }, 63 | "node_modules/source-map-wasm": { 64 | "name": "source-map", 65 | "version": "0.8.0-beta.0", 66 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", 67 | "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", 68 | "dev": true, 69 | "dependencies": { 70 | "whatwg-url": "^7.0.0" 71 | }, 72 | "engines": { 73 | "node": ">= 8" 74 | } 75 | }, 76 | "node_modules/tr46": { 77 | "version": "1.0.1", 78 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", 79 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", 80 | "dev": true, 81 | "dependencies": { 82 | "punycode": "^2.1.0" 83 | } 84 | }, 85 | "node_modules/trace-map": { 86 | "name": "@jridgewell/trace-mapping", 87 | "version": "0.3.25", 88 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 89 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 90 | "dev": true, 91 | "dependencies": { 92 | "@jridgewell/resolve-uri": "^3.1.0", 93 | "@jridgewell/sourcemap-codec": "^1.4.14" 94 | } 95 | }, 96 | "node_modules/webidl-conversions": { 97 | "version": "4.0.2", 98 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", 99 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", 100 | "dev": true 101 | }, 102 | "node_modules/whatwg-url": { 103 | "version": "7.1.0", 104 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", 105 | "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", 106 | "dev": true, 107 | "dependencies": { 108 | "lodash.sortby": "^4.7.0", 109 | "tr46": "^1.0.1", 110 | "webidl-conversions": "^4.0.2" 111 | } 112 | } 113 | }, 114 | "dependencies": { 115 | "@jridgewell/resolve-uri": { 116 | "version": "3.1.2", 117 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 118 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 119 | "dev": true 120 | }, 121 | "@jridgewell/sourcemap-codec": { 122 | "version": "1.4.15", 123 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", 124 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", 125 | "dev": true 126 | }, 127 | "lodash.sortby": { 128 | "version": "4.7.0", 129 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 130 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", 131 | "dev": true 132 | }, 133 | "punycode": { 134 | "version": "2.1.1", 135 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 136 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 137 | "dev": true 138 | }, 139 | "source-map": { 140 | "version": "0.6.1", 141 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 142 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 143 | "dev": true 144 | }, 145 | "source-map-js": { 146 | "version": "1.0.2", 147 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 148 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 149 | "dev": true 150 | }, 151 | "source-map-wasm": { 152 | "version": "npm:source-map@0.8.0-beta.0", 153 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", 154 | "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", 155 | "dev": true, 156 | "requires": { 157 | "whatwg-url": "^7.0.0" 158 | } 159 | }, 160 | "tr46": { 161 | "version": "1.0.1", 162 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", 163 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", 164 | "dev": true, 165 | "requires": { 166 | "punycode": "^2.1.0" 167 | } 168 | }, 169 | "trace-map": { 170 | "version": "npm:@jridgewell/trace-mapping@0.3.25", 171 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 172 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 173 | "dev": true, 174 | "requires": { 175 | "@jridgewell/resolve-uri": "^3.1.0", 176 | "@jridgewell/sourcemap-codec": "^1.4.14" 177 | } 178 | }, 179 | "webidl-conversions": { 180 | "version": "4.0.2", 181 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", 182 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", 183 | "dev": true 184 | }, 185 | "whatwg-url": { 186 | "version": "7.1.0", 187 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", 188 | "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", 189 | "dev": true, 190 | "requires": { 191 | "lodash.sortby": "^4.7.0", 192 | "tr46": "^1.0.1", 193 | "webidl-conversions": "^4.0.2" 194 | } 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarks", 3 | "devDependencies": { 4 | "source-map": "0.6.1", 5 | "source-map-js": "1.0.2", 6 | "source-map-wasm": "npm:source-map@0.8.0-beta.0", 7 | "trace-map": "npm:@jridgewell/trace-mapping@latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /benchmark/react.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["react.development.js"],"names":["global","factory","exports","module","define","amd","self","React","this","ReactVersion","REACT_ELEMENT_TYPE","REACT_PORTAL_TYPE","Fragment","StrictMode","Profiler","REACT_PROVIDER_TYPE","REACT_CONTEXT_TYPE","REACT_FORWARD_REF_TYPE","Suspense","REACT_SUSPENSE_LIST_TYPE","REACT_MEMO_TYPE","REACT_LAZY_TYPE","REACT_BLOCK_TYPE","REACT_SERVER_BLOCK_TYPE","REACT_FUNDAMENTAL_TYPE","REACT_SCOPE_TYPE","REACT_OPAQUE_ID_TYPE","REACT_DEBUG_TRACING_MODE_TYPE","REACT_OFFSCREEN_TYPE","REACT_LEGACY_HIDDEN_TYPE","Symbol","for","symbolFor","MAYBE_ITERATOR_SYMBOL","iterator","FAUX_ITERATOR_SYMBOL","getIteratorFn","maybeIterable","maybeIterator","hasOwnProperty","Object","prototype","_assign","to","from","key","call","assign","target","sources","TypeError","nextIndex","arguments","length","nextSource","ReactCurrentDispatcher","current","ReactCurrentBatchConfig","transition","ReactCurrentOwner","ReactDebugCurrentFrame","currentExtraStackFrame","setExtraStackFrame","stack","getCurrentStack","getStackAddendum","impl","IsSomeRendererActing","ReactSharedInternals","warn","format","_len","args","Array","_key","printWarning","error","_len2","_key2","level","concat","argsWithFormat","map","item","unshift","Function","apply","console","didWarnStateUpdateForUnmountedComponent","warnNoop","publicInstance","callerName","_constructor","constructor","componentName","displayName","name","warningKey","ReactNoopUpdateQueue","isMounted","enqueueForceUpdate","callback","enqueueReplaceState","completeState","enqueueSetState","partialState","emptyObject","freeze","Component","props","context","updater","refs","isReactComponent","setState","Error","forceUpdate","deprecatedAPIs","replaceState","defineDeprecationWarning","methodName","info","defineProperty","get","undefined","fnName","ComponentDummy","PureComponent","pureComponentPrototype","isPureReactComponent","createRef","refObject","seal","getWrappedName","outerType","innerType","wrapperName","functionName","getContextName","type","getComponentName","tag","$$typeof","provider","_context","render","_render","lazyComponent","payload","_payload","init","_init","x","hasOwnProperty$1","RESERVED_PROPS","ref","__self","__source","specialPropKeyWarningShown","specialPropRefWarningShown","didWarnAboutStringRefs","hasValidRef","config","getter","getOwnPropertyDescriptor","isReactWarning","hasValidKey","defineKeyPropWarningGetter","warnAboutAccessingKey","configurable","defineRefPropWarningGetter","warnAboutAccessingRef","warnIfStringRefCannotBeAutoConverted","stateNode","ReactElement","source","owner","element","_owner","_store","enumerable","writable","value","createElement","children","propName","childrenLength","childArray","i","defaultProps","cloneAndReplaceKey","oldElement","newKey","newElement","_self","_source","cloneElement","isValidElement","object","SEPARATOR","SUBSEPARATOR","escape","escapeRegex","escaperLookup","escapedString","replace","match","didWarnAboutMaps","userProvidedKeyEscapeRegex","escapeUserProvidedKey","text","getElementKey","index","toString","mapIntoArray","array","escapedPrefix","nameSoFar","invokeCallback","_child","mappedChild","childKey","isArray","escapedChildKey","c","push","child","nextName","subtreeCount","nextNamePrefix","iteratorFn","iterableChildren","entries","step","ii","next","done","childrenString","keys","join","mapChildren","func","result","count","countChildren","n","forEachChildren","forEachFunc","forEachContext","toArray","onlyChild","createContext","defaultValue","calculateChangedBits","_calculateChangedBits","_currentValue","_currentValue2","_threadCount","Provider","Consumer","hasWarnedAboutUsingNestedContextConsumers","hasWarnedAboutUsingConsumerProvider","hasWarnedAboutDisplayNameOnConsumer","defineProperties","set","_Provider","_currentRenderer","_currentRenderer2","Uninitialized","Pending","Resolved","Rejected","lazyInitializer","_status","ctor","_result","thenable","pending","then","moduleObject","defaultExport","default","resolved","rejected","lazy","lazyType","propTypes","newDefaultProps","newPropTypes","forwardRef","elementType","ownName","enableScopeAPI","isValidElementType","memo","compare","resolveDispatcher","dispatcher","useContext","Context","unstable_observedBits","realContext","useState","initialState","useReducer","reducer","initialArg","useRef","initialValue","useEffect","create","deps","useLayoutEffect","useCallback","useMemo","useImperativeHandle","useDebugValue","formatterFn","disabledDepth","prevLog","prevInfo","prevWarn","prevError","prevGroup","prevGroupCollapsed","prevGroupEnd","disabledLog","__reactDisabledLog","disableLogs","log","group","groupCollapsed","groupEnd","reenableLogs","ReactCurrentDispatcher$1","prefix","describeBuiltInComponentFrame","ownerFn","trim","reentry","componentFrameCache","PossiblyWeakMap","WeakMap","Map","describeNativeComponentFrame","fn","construct","frame","control","previousPrepareStackTrace","prepareStackTrace","previousDispatcher","Fake","Reflect","sample","sampleLines","split","controlLines","s","_frame","syntheticFrame","describeFunctionComponentFrame","shouldConstruct","describeUnknownElementTypeFrameInDEV","loggedTypeFailures","ReactDebugCurrentFrame$1","setCurrentlyValidatingElement","checkPropTypes","typeSpecs","values","location","has","bind","typeSpecName","error$1","err","ex","message","setCurrentlyValidatingElement$1","propTypesMisspellWarningShown","getDeclarationErrorAddendum","getSourceInfoErrorAddendum","fileName","lineNumber","getSourceInfoErrorAddendumForProps","elementProps","ownerHasKeyUseWarning","getCurrentComponentErrorInfo","parentType","parentName","validateExplicitKey","validated","currentComponentErrorInfo","childOwner","validateChildKeys","node","validatePropTypes","PropTypes","_name","getDefaultProps","isReactClassApproved","validateFragmentProps","fragment","createElementWithValidation","validType","sourceInfo","typeString","didWarnAboutDeprecatedCreateFactory","createFactoryWithValidation","validatedFactory","cloneElementWithValidation","enableSchedulerDebugging","enableProfiling","requestHostCallback","requestHostTimeout","cancelHostTimeout","shouldYieldToHost","requestPaint","getCurrentTime","forceFrameRate","hasPerformanceNow","performance","now","localPerformance","localDate","Date","initialTime","window","MessageChannel","_callback","_timeoutID","_flushCallback","currentTime","hasRemainingTime","e","setTimeout","cb","ms","clearTimeout","_setTimeout","_clearTimeout","requestAnimationFrame","cancelAnimationFrame","isMessageLoopRunning","scheduledHostCallback","taskTimeoutID","yieldInterval","deadline","fps","Math","floor","performWorkUntilDeadline","hasTimeRemaining","hasMoreWork","port","postMessage","channel","port2","port1","onmessage","heap","siftUp","peek","first","pop","last","siftDown","parentIndex","parent","leftIndex","left","rightIndex","right","a","b","diff","sortIndex","id","ImmediatePriority","UserBlockingPriority","NormalPriority","LowPriority","IdlePriority","markTaskErrored","task","maxSigned31BitInt","IMMEDIATE_PRIORITY_TIMEOUT","USER_BLOCKING_PRIORITY_TIMEOUT","NORMAL_PRIORITY_TIMEOUT","LOW_PRIORITY_TIMEOUT","IDLE_PRIORITY_TIMEOUT","taskQueue","timerQueue","taskIdCounter","currentTask","currentPriorityLevel","isPerformingWork","isHostCallbackScheduled","isHostTimeoutScheduled","advanceTimers","timer","startTime","expirationTime","handleTimeout","flushWork","firstTimer","previousPriorityLevel","workLoop","isQueued","priorityLevel","didUserCallbackTimeout","continuationCallback","unstable_runWithPriority","eventHandler","unstable_next","unstable_wrapCallback","parentPriorityLevel","unstable_scheduleCallback","options","delay","timeout","newTask","unstable_pauseExecution","unstable_continueExecution","unstable_getFirstCallbackNode","unstable_cancelCallback","unstable_getCurrentPriorityLevel","unstable_requestPaint","unstable_Profiling","Scheduler","__proto__","unstable_ImmediatePriority","unstable_UserBlockingPriority","unstable_NormalPriority","unstable_IdlePriority","unstable_LowPriority","unstable_shouldYield","unstable_now","unstable_forceFrameRate","DEFAULT_THREAD_ID","interactionIDCounter","threadIDCounter","interactionsRef","subscriberRef","Set","unstable_clear","prevInteractions","unstable_getCurrent","unstable_getThreadID","unstable_trace","timestamp","threadID","interaction","__count","interactions","add","subscriber","returnValue","onInteractionTraced","onWorkStarted","onWorkStopped","onInteractionScheduledWorkCompleted","unstable_wrap","wrappedInteractions","onWorkScheduled","forEach","hasRun","wrapped","cancel","onWorkCanceled","subscribers","unstable_subscribe","size","unstable_unsubscribe","delete","didCatchError","caughtError","SchedulerTracing","__interactionsRef","__subscriberRef","ReactSharedInternals$1","frozenObject","createElement$1","cloneElement$1","createFactory","Children","only","__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED","version"],"mappings":"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"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/trace-mapping", 3 | "version": "0.3.25", 4 | "description": "Trace the original position through a source map", 5 | "keywords": [ 6 | "source", 7 | "map" 8 | ], 9 | "main": "dist/trace-mapping.umd.js", 10 | "module": "dist/trace-mapping.mjs", 11 | "types": "dist/types/trace-mapping.d.ts", 12 | "files": [ 13 | "dist" 14 | ], 15 | "exports": { 16 | ".": [ 17 | { 18 | "types": "./dist/types/trace-mapping.d.ts", 19 | "browser": "./dist/trace-mapping.umd.js", 20 | "require": "./dist/trace-mapping.umd.js", 21 | "import": "./dist/trace-mapping.mjs" 22 | }, 23 | "./dist/trace-mapping.umd.js" 24 | ], 25 | "./package.json": "./package.json" 26 | }, 27 | "author": "Justin Ridgewell ", 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/jridgewell/trace-mapping.git" 31 | }, 32 | "license": "MIT", 33 | "scripts": { 34 | "benchmark": "run-s build:rollup benchmark:*", 35 | "benchmark:install": "cd benchmark && npm install", 36 | "benchmark:only": "node --expose-gc benchmark/index.mjs", 37 | "build": "run-s -n build:*", 38 | "build:rollup": "rollup -c rollup.config.mjs", 39 | "build:ts": "tsc --project tsconfig.build.json", 40 | "lint": "run-s -n lint:*", 41 | "lint:prettier": "npm run test:lint:prettier -- --write", 42 | "lint:ts": "npm run test:lint:ts -- --fix", 43 | "prebuild": "rm -rf dist", 44 | "prepublishOnly": "npm run preversion", 45 | "preversion": "run-s test build", 46 | "test": "run-s -n test:lint test:only", 47 | "test:debug": "mocha --inspect-brk", 48 | "test:lint": "run-s -n test:lint:*", 49 | "test:lint:prettier": "prettier --check '{src,test}/**/*.ts' '**/*.md'", 50 | "test:lint:ts": "eslint '{src,test}/**/*.ts'", 51 | "test:only": "c8 mocha", 52 | "test:watch": "mocha --watch" 53 | }, 54 | "devDependencies": { 55 | "@rollup/plugin-typescript": "11.1.6", 56 | "@types/mocha": "10.0.6", 57 | "@types/node": "20.11.20", 58 | "@typescript-eslint/eslint-plugin": "6.18.1", 59 | "@typescript-eslint/parser": "6.18.1", 60 | "benchmark": "2.1.4", 61 | "c8": "9.0.0", 62 | "esbuild": "0.19.11", 63 | "eslint": "8.56.0", 64 | "eslint-config-prettier": "9.1.0", 65 | "eslint-plugin-no-only-tests": "3.1.0", 66 | "mocha": "10.3.0", 67 | "npm-run-all": "4.1.5", 68 | "prettier": "3.1.1", 69 | "rollup": "4.9.4", 70 | "tsx": "4.7.0", 71 | "typescript": "5.3.3" 72 | }, 73 | "dependencies": { 74 | "@jridgewell/resolve-uri": "^3.1.0", 75 | "@jridgewell/sourcemap-codec": "^1.4.14" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | endOfLine: 'lf', 3 | printWidth: 100, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | proseWrap: 'always', 7 | }; 8 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript'; 2 | 3 | function configure(esm) { 4 | return { 5 | input: 'src/trace-mapping.ts', 6 | output: esm 7 | ? { 8 | format: 'es', 9 | dir: 'dist', 10 | entryFileNames: '[name].mjs', 11 | sourcemap: true, 12 | exports: 'named', 13 | } 14 | : { 15 | format: 'umd', 16 | name: 'traceMapping', 17 | dir: 'dist', 18 | entryFileNames: '[name].umd.js', 19 | sourcemap: true, 20 | exports: 'named', 21 | globals: { 22 | '@jridgewell/resolve-uri': 'resolveURI', 23 | '@jridgewell/sourcemap-codec': 'sourcemapCodec', 24 | }, 25 | }, 26 | plugins: [typescript({ 27 | tsconfig: './tsconfig.build.json', 28 | tslib: './throw-when-needed', 29 | })], 30 | watch: { 31 | include: 'src/**', 32 | }, 33 | }; 34 | } 35 | 36 | export default [configure(false), configure(true)]; 37 | -------------------------------------------------------------------------------- /src/binary-search.ts: -------------------------------------------------------------------------------- 1 | import type { SourceMapSegment, ReverseSegment } from './sourcemap-segment'; 2 | import { COLUMN } from './sourcemap-segment'; 3 | 4 | export type MemoState = { 5 | lastKey: number; 6 | lastNeedle: number; 7 | lastIndex: number; 8 | }; 9 | 10 | export let found = false; 11 | 12 | /** 13 | * A binary search implementation that returns the index if a match is found. 14 | * If no match is found, then the left-index (the index associated with the item that comes just 15 | * before the desired index) is returned. To maintain proper sort order, a splice would happen at 16 | * the next index: 17 | * 18 | * ```js 19 | * const array = [1, 3]; 20 | * const needle = 2; 21 | * const index = binarySearch(array, needle, (item, needle) => item - needle); 22 | * 23 | * assert.equal(index, 0); 24 | * array.splice(index + 1, 0, needle); 25 | * assert.deepEqual(array, [1, 2, 3]); 26 | * ``` 27 | */ 28 | export function binarySearch( 29 | haystack: SourceMapSegment[] | ReverseSegment[], 30 | needle: number, 31 | low: number, 32 | high: number, 33 | ): number { 34 | while (low <= high) { 35 | const mid = low + ((high - low) >> 1); 36 | const cmp = haystack[mid][COLUMN] - needle; 37 | 38 | if (cmp === 0) { 39 | found = true; 40 | return mid; 41 | } 42 | 43 | if (cmp < 0) { 44 | low = mid + 1; 45 | } else { 46 | high = mid - 1; 47 | } 48 | } 49 | 50 | found = false; 51 | return low - 1; 52 | } 53 | 54 | export function upperBound( 55 | haystack: SourceMapSegment[] | ReverseSegment[], 56 | needle: number, 57 | index: number, 58 | ): number { 59 | for (let i = index + 1; i < haystack.length; index = i++) { 60 | if (haystack[i][COLUMN] !== needle) break; 61 | } 62 | return index; 63 | } 64 | 65 | export function lowerBound( 66 | haystack: SourceMapSegment[] | ReverseSegment[], 67 | needle: number, 68 | index: number, 69 | ): number { 70 | for (let i = index - 1; i >= 0; index = i--) { 71 | if (haystack[i][COLUMN] !== needle) break; 72 | } 73 | return index; 74 | } 75 | 76 | export function memoizedState(): MemoState { 77 | return { 78 | lastKey: -1, 79 | lastNeedle: -1, 80 | lastIndex: -1, 81 | }; 82 | } 83 | 84 | /** 85 | * This overly complicated beast is just to record the last tested line/column and the resulting 86 | * index, allowing us to skip a few tests if mappings are monotonically increasing. 87 | */ 88 | export function memoizedBinarySearch( 89 | haystack: SourceMapSegment[] | ReverseSegment[], 90 | needle: number, 91 | state: MemoState, 92 | key: number, 93 | ): number { 94 | const { lastKey, lastNeedle, lastIndex } = state; 95 | 96 | let low = 0; 97 | let high = haystack.length - 1; 98 | if (key === lastKey) { 99 | if (needle === lastNeedle) { 100 | found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle; 101 | return lastIndex; 102 | } 103 | 104 | if (needle >= lastNeedle) { 105 | // lastIndex may be -1 if the previous needle was not found. 106 | low = lastIndex === -1 ? 0 : lastIndex; 107 | } else { 108 | high = lastIndex; 109 | } 110 | } 111 | state.lastKey = key; 112 | state.lastNeedle = needle; 113 | 114 | return (state.lastIndex = binarySearch(haystack, needle, low, high)); 115 | } 116 | -------------------------------------------------------------------------------- /src/by-source.ts: -------------------------------------------------------------------------------- 1 | import { COLUMN, SOURCES_INDEX, SOURCE_LINE, SOURCE_COLUMN } from './sourcemap-segment'; 2 | import { memoizedBinarySearch, upperBound } from './binary-search'; 3 | 4 | import type { ReverseSegment, SourceMapSegment } from './sourcemap-segment'; 5 | import type { MemoState } from './binary-search'; 6 | 7 | export type Source = { 8 | __proto__: null; 9 | [line: number]: Exclude[]; 10 | }; 11 | 12 | // Rebuilds the original source files, with mappings that are ordered by source line/column instead 13 | // of generated line/column. 14 | export default function buildBySources( 15 | decoded: readonly SourceMapSegment[][], 16 | memos: MemoState[], 17 | ): Source[] { 18 | const sources: Source[] = memos.map(buildNullArray); 19 | 20 | for (let i = 0; i < decoded.length; i++) { 21 | const line = decoded[i]; 22 | for (let j = 0; j < line.length; j++) { 23 | const seg = line[j]; 24 | if (seg.length === 1) continue; 25 | 26 | const sourceIndex = seg[SOURCES_INDEX]; 27 | const sourceLine = seg[SOURCE_LINE]; 28 | const sourceColumn = seg[SOURCE_COLUMN]; 29 | const originalSource = sources[sourceIndex]; 30 | const originalLine = (originalSource[sourceLine] ||= []); 31 | const memo = memos[sourceIndex]; 32 | 33 | // The binary search either found a match, or it found the left-index just before where the 34 | // segment should go. Either way, we want to insert after that. And there may be multiple 35 | // generated segments associated with an original location, so there may need to move several 36 | // indexes before we find where we need to insert. 37 | let index = upperBound( 38 | originalLine, 39 | sourceColumn, 40 | memoizedBinarySearch(originalLine, sourceColumn, memo, sourceLine), 41 | ); 42 | 43 | memo.lastIndex = ++index; 44 | insert(originalLine, index, [sourceColumn, i, seg[COLUMN]]); 45 | } 46 | } 47 | 48 | return sources; 49 | } 50 | 51 | function insert(array: T[], index: number, value: T) { 52 | for (let i = array.length; i > index; i--) { 53 | array[i] = array[i - 1]; 54 | } 55 | array[index] = value; 56 | } 57 | 58 | // Null arrays allow us to use ordered index keys without actually allocating contiguous memory like 59 | // a real array. We use a null-prototype object to avoid prototype pollution and deoptimizations. 60 | // Numeric properties on objects are magically sorted in ascending order by the engine regardless of 61 | // the insertion order. So, by setting any numeric keys, even out of order, we'll get ascending 62 | // order when iterating with for-in. 63 | function buildNullArray(): T { 64 | return { __proto__: null } as T; 65 | } 66 | -------------------------------------------------------------------------------- /src/flatten-map.ts: -------------------------------------------------------------------------------- 1 | import { TraceMap, presortedDecodedMap, decodedMappings } from './trace-mapping'; 2 | import { 3 | COLUMN, 4 | SOURCES_INDEX, 5 | SOURCE_LINE, 6 | SOURCE_COLUMN, 7 | NAMES_INDEX, 8 | } from './sourcemap-segment'; 9 | import { parse } from './types'; 10 | 11 | import type { 12 | DecodedSourceMap, 13 | DecodedSourceMapXInput, 14 | EncodedSourceMapXInput, 15 | SectionedSourceMapXInput, 16 | SectionedSourceMapInput, 17 | SectionXInput, 18 | Ro, 19 | } from './types'; 20 | import type { SourceMapSegment } from './sourcemap-segment'; 21 | 22 | type FlattenMap = { 23 | new (map: Ro, mapUrl?: string | null): TraceMap; 24 | (map: Ro, mapUrl?: string | null): TraceMap; 25 | }; 26 | 27 | export const FlattenMap: FlattenMap = function (map, mapUrl) { 28 | const parsed = parse(map as SectionedSourceMapInput); 29 | 30 | if (!('sections' in parsed)) { 31 | return new TraceMap(parsed as DecodedSourceMapXInput | EncodedSourceMapXInput, mapUrl); 32 | } 33 | 34 | const mappings: SourceMapSegment[][] = []; 35 | const sources: string[] = []; 36 | const sourcesContent: (string | null)[] = []; 37 | const names: string[] = []; 38 | const ignoreList: number[] = []; 39 | 40 | recurse( 41 | parsed, 42 | mapUrl, 43 | mappings, 44 | sources, 45 | sourcesContent, 46 | names, 47 | ignoreList, 48 | 0, 49 | 0, 50 | Infinity, 51 | Infinity, 52 | ); 53 | 54 | const joined: DecodedSourceMap = { 55 | version: 3, 56 | file: parsed.file, 57 | names, 58 | sources, 59 | sourcesContent, 60 | mappings, 61 | ignoreList, 62 | }; 63 | 64 | return presortedDecodedMap(joined); 65 | } as FlattenMap; 66 | 67 | function recurse( 68 | input: SectionedSourceMapXInput, 69 | mapUrl: string | null | undefined, 70 | mappings: SourceMapSegment[][], 71 | sources: string[], 72 | sourcesContent: (string | null)[], 73 | names: string[], 74 | ignoreList: number[], 75 | lineOffset: number, 76 | columnOffset: number, 77 | stopLine: number, 78 | stopColumn: number, 79 | ) { 80 | const { sections } = input; 81 | for (let i = 0; i < sections.length; i++) { 82 | const { map, offset } = sections[i]; 83 | 84 | let sl = stopLine; 85 | let sc = stopColumn; 86 | if (i + 1 < sections.length) { 87 | const nextOffset = sections[i + 1].offset; 88 | sl = Math.min(stopLine, lineOffset + nextOffset.line); 89 | 90 | if (sl === stopLine) { 91 | sc = Math.min(stopColumn, columnOffset + nextOffset.column); 92 | } else if (sl < stopLine) { 93 | sc = columnOffset + nextOffset.column; 94 | } 95 | } 96 | 97 | addSection( 98 | map, 99 | mapUrl, 100 | mappings, 101 | sources, 102 | sourcesContent, 103 | names, 104 | ignoreList, 105 | lineOffset + offset.line, 106 | columnOffset + offset.column, 107 | sl, 108 | sc, 109 | ); 110 | } 111 | } 112 | 113 | function addSection( 114 | input: SectionXInput['map'], 115 | mapUrl: string | null | undefined, 116 | mappings: SourceMapSegment[][], 117 | sources: string[], 118 | sourcesContent: (string | null)[], 119 | names: string[], 120 | ignoreList: number[], 121 | lineOffset: number, 122 | columnOffset: number, 123 | stopLine: number, 124 | stopColumn: number, 125 | ) { 126 | const parsed = parse(input); 127 | if ('sections' in parsed) return recurse(...(arguments as unknown as Parameters)); 128 | 129 | const map = new TraceMap(parsed, mapUrl); 130 | const sourcesOffset = sources.length; 131 | const namesOffset = names.length; 132 | const decoded = decodedMappings(map); 133 | const { resolvedSources, sourcesContent: contents, ignoreList: ignores } = map; 134 | 135 | append(sources, resolvedSources); 136 | append(names, map.names); 137 | 138 | if (contents) append(sourcesContent, contents); 139 | else for (let i = 0; i < resolvedSources.length; i++) sourcesContent.push(null); 140 | 141 | if (ignores) for (let i = 0; i < ignores.length; i++) ignoreList.push(ignores[i] + sourcesOffset); 142 | 143 | for (let i = 0; i < decoded.length; i++) { 144 | const lineI = lineOffset + i; 145 | 146 | // We can only add so many lines before we step into the range that the next section's map 147 | // controls. When we get to the last line, then we'll start checking the segments to see if 148 | // they've crossed into the column range. But it may not have any columns that overstep, so we 149 | // still need to check that we don't overstep lines, too. 150 | if (lineI > stopLine) return; 151 | 152 | // The out line may already exist in mappings (if we're continuing the line started by a 153 | // previous section). Or, we may have jumped ahead several lines to start this section. 154 | const out = getLine(mappings, lineI); 155 | // On the 0th loop, the section's column offset shifts us forward. On all other lines (since the 156 | // map can be multiple lines), it doesn't. 157 | const cOffset = i === 0 ? columnOffset : 0; 158 | 159 | const line = decoded[i]; 160 | for (let j = 0; j < line.length; j++) { 161 | const seg = line[j]; 162 | const column = cOffset + seg[COLUMN]; 163 | 164 | // If this segment steps into the column range that the next section's map controls, we need 165 | // to stop early. 166 | if (lineI === stopLine && column >= stopColumn) return; 167 | 168 | if (seg.length === 1) { 169 | out.push([column]); 170 | continue; 171 | } 172 | 173 | const sourcesIndex = sourcesOffset + seg[SOURCES_INDEX]; 174 | const sourceLine = seg[SOURCE_LINE]; 175 | const sourceColumn = seg[SOURCE_COLUMN]; 176 | out.push( 177 | seg.length === 4 178 | ? [column, sourcesIndex, sourceLine, sourceColumn] 179 | : [column, sourcesIndex, sourceLine, sourceColumn, namesOffset + seg[NAMES_INDEX]], 180 | ); 181 | } 182 | } 183 | } 184 | 185 | function append(arr: T[], other: T[]) { 186 | for (let i = 0; i < other.length; i++) arr.push(other[i]); 187 | } 188 | 189 | function getLine(arr: T[][], index: number): T[] { 190 | for (let i = arr.length; i <= index; i++) arr[i] = []; 191 | return arr[index]; 192 | } 193 | -------------------------------------------------------------------------------- /src/resolve.ts: -------------------------------------------------------------------------------- 1 | import resolveUri from '@jridgewell/resolve-uri'; 2 | import stripFilename from './strip-filename'; 3 | 4 | type Resolve = (source: string | null) => string; 5 | export default function resolver( 6 | mapUrl: string | null | undefined, 7 | sourceRoot: string | undefined, 8 | ): Resolve { 9 | const from = stripFilename(mapUrl); 10 | // The sourceRoot is always treated as a directory, if it's not empty. 11 | // https://github.com/mozilla/source-map/blob/8cb3ee57/lib/util.js#L327 12 | // https://github.com/chromium/chromium/blob/da4adbb3/third_party/blink/renderer/devtools/front_end/sdk/SourceMap.js#L400-L401 13 | const prefix = sourceRoot ? sourceRoot + '/' : ''; 14 | 15 | return (source) => resolveUri(prefix + (source || ''), from); 16 | } 17 | -------------------------------------------------------------------------------- /src/sort.ts: -------------------------------------------------------------------------------- 1 | import { COLUMN } from './sourcemap-segment'; 2 | 3 | import type { SourceMapSegment } from './sourcemap-segment'; 4 | 5 | export default function maybeSort( 6 | mappings: SourceMapSegment[][], 7 | owned: boolean, 8 | ): SourceMapSegment[][] { 9 | const unsortedIndex = nextUnsortedSegmentLine(mappings, 0); 10 | if (unsortedIndex === mappings.length) return mappings; 11 | 12 | // If we own the array (meaning we parsed it from JSON), then we're free to directly mutate it. If 13 | // not, we do not want to modify the consumer's input array. 14 | if (!owned) mappings = mappings.slice(); 15 | 16 | for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) { 17 | mappings[i] = sortSegments(mappings[i], owned); 18 | } 19 | return mappings; 20 | } 21 | 22 | function nextUnsortedSegmentLine(mappings: SourceMapSegment[][], start: number): number { 23 | for (let i = start; i < mappings.length; i++) { 24 | if (!isSorted(mappings[i])) return i; 25 | } 26 | return mappings.length; 27 | } 28 | 29 | function isSorted(line: SourceMapSegment[]): boolean { 30 | for (let j = 1; j < line.length; j++) { 31 | if (line[j][COLUMN] < line[j - 1][COLUMN]) { 32 | return false; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | function sortSegments(line: SourceMapSegment[], owned: boolean): SourceMapSegment[] { 39 | if (!owned) line = line.slice(); 40 | return line.sort(sortComparator); 41 | } 42 | 43 | function sortComparator(a: SourceMapSegment, b: SourceMapSegment): number { 44 | return a[COLUMN] - b[COLUMN]; 45 | } 46 | -------------------------------------------------------------------------------- /src/sourcemap-segment.ts: -------------------------------------------------------------------------------- 1 | type GeneratedColumn = number; 2 | type SourcesIndex = number; 3 | type SourceLine = number; 4 | type SourceColumn = number; 5 | type NamesIndex = number; 6 | 7 | type GeneratedLine = number; 8 | 9 | export type SourceMapSegment = 10 | | [GeneratedColumn] 11 | | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] 12 | | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex]; 13 | 14 | export type ReverseSegment = [SourceColumn, GeneratedLine, GeneratedColumn]; 15 | 16 | export const COLUMN = 0; 17 | export const SOURCES_INDEX = 1; 18 | export const SOURCE_LINE = 2; 19 | export const SOURCE_COLUMN = 3; 20 | export const NAMES_INDEX = 4; 21 | 22 | export const REV_GENERATED_LINE = 1; 23 | export const REV_GENERATED_COLUMN = 2; 24 | -------------------------------------------------------------------------------- /src/strip-filename.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Removes everything after the last "/", but leaves the slash. 3 | */ 4 | export default function stripFilename(path: string | undefined | null): string { 5 | if (!path) return ''; 6 | const index = path.lastIndexOf('/'); 7 | return path.slice(0, index + 1); 8 | } 9 | -------------------------------------------------------------------------------- /src/trace-mapping.ts: -------------------------------------------------------------------------------- 1 | import { encode, decode } from '@jridgewell/sourcemap-codec'; 2 | 3 | import resolver from './resolve'; 4 | import maybeSort from './sort'; 5 | import buildBySources from './by-source'; 6 | import { 7 | memoizedState, 8 | memoizedBinarySearch, 9 | upperBound, 10 | lowerBound, 11 | found as bsFound, 12 | } from './binary-search'; 13 | import { 14 | COLUMN, 15 | SOURCES_INDEX, 16 | SOURCE_LINE, 17 | SOURCE_COLUMN, 18 | NAMES_INDEX, 19 | REV_GENERATED_LINE, 20 | REV_GENERATED_COLUMN, 21 | } from './sourcemap-segment'; 22 | import { parse } from './types'; 23 | 24 | import type { SourceMapSegment, ReverseSegment } from './sourcemap-segment'; 25 | import type { 26 | SourceMapV3, 27 | DecodedSourceMap, 28 | EncodedSourceMap, 29 | InvalidOriginalMapping, 30 | OriginalMapping, 31 | InvalidGeneratedMapping, 32 | GeneratedMapping, 33 | SourceMapInput, 34 | Needle, 35 | SourceNeedle, 36 | SourceMap, 37 | EachMapping, 38 | Bias, 39 | XInput, 40 | SectionedSourceMap, 41 | Ro, 42 | } from './types'; 43 | import type { Source } from './by-source'; 44 | import type { MemoState } from './binary-search'; 45 | 46 | export type { SourceMapSegment } from './sourcemap-segment'; 47 | export type { 48 | SourceMap, 49 | DecodedSourceMap, 50 | EncodedSourceMap, 51 | Section, 52 | SectionedSourceMap, 53 | SourceMapV3, 54 | Bias, 55 | EachMapping, 56 | GeneratedMapping, 57 | InvalidGeneratedMapping, 58 | InvalidOriginalMapping, 59 | Needle, 60 | OriginalMapping, 61 | OriginalMapping as Mapping, 62 | SectionedSourceMapInput, 63 | SourceMapInput, 64 | SourceNeedle, 65 | XInput, 66 | EncodedSourceMapXInput, 67 | DecodedSourceMapXInput, 68 | SectionedSourceMapXInput, 69 | SectionXInput, 70 | } from './types'; 71 | 72 | interface PublicMap { 73 | _encoded: TraceMap['_encoded']; 74 | _decoded: TraceMap['_decoded']; 75 | _decodedMemo: TraceMap['_decodedMemo']; 76 | _bySources: TraceMap['_bySources']; 77 | _bySourceMemos: TraceMap['_bySourceMemos']; 78 | } 79 | 80 | const LINE_GTR_ZERO = '`line` must be greater than 0 (lines start at line 1)'; 81 | const COL_GTR_EQ_ZERO = '`column` must be greater than or equal to 0 (columns start at column 0)'; 82 | 83 | export const LEAST_UPPER_BOUND = -1; 84 | export const GREATEST_LOWER_BOUND = 1; 85 | 86 | export { FlattenMap, FlattenMap as AnyMap } from './flatten-map'; 87 | 88 | export class TraceMap implements SourceMap { 89 | declare version: SourceMapV3['version']; 90 | declare file: SourceMapV3['file']; 91 | declare names: SourceMapV3['names']; 92 | declare sourceRoot: SourceMapV3['sourceRoot']; 93 | declare sources: SourceMapV3['sources']; 94 | declare sourcesContent: SourceMapV3['sourcesContent']; 95 | declare ignoreList: SourceMapV3['ignoreList']; 96 | 97 | declare resolvedSources: string[]; 98 | private declare _encoded: string | undefined; 99 | 100 | private declare _decoded: SourceMapSegment[][] | undefined; 101 | private declare _decodedMemo: MemoState; 102 | 103 | private declare _bySources: Source[] | undefined; 104 | private declare _bySourceMemos: MemoState[] | undefined; 105 | 106 | constructor(map: Ro, mapUrl?: string | null) { 107 | const isString = typeof map === 'string'; 108 | if (!isString && (map as unknown as { _decodedMemo: any })._decodedMemo) return map as TraceMap; 109 | 110 | const parsed = parse(map as Exclude); 111 | 112 | const { version, file, names, sourceRoot, sources, sourcesContent } = parsed; 113 | this.version = version; 114 | this.file = file; 115 | this.names = names || []; 116 | this.sourceRoot = sourceRoot; 117 | this.sources = sources; 118 | this.sourcesContent = sourcesContent; 119 | this.ignoreList = parsed.ignoreList || (parsed as XInput).x_google_ignoreList || undefined; 120 | 121 | const resolve = resolver(mapUrl, sourceRoot); 122 | this.resolvedSources = sources.map(resolve); 123 | 124 | const { mappings } = parsed; 125 | if (typeof mappings === 'string') { 126 | this._encoded = mappings; 127 | this._decoded = undefined; 128 | } else if (Array.isArray(mappings)) { 129 | this._encoded = undefined; 130 | this._decoded = maybeSort(mappings, isString); 131 | } else if ((parsed as unknown as SectionedSourceMap).sections) { 132 | throw new Error(`TraceMap passed sectioned source map, please use AnyMap export instead`); 133 | } else { 134 | throw new Error(`invalid source map: ${JSON.stringify(parsed)}`); 135 | } 136 | 137 | this._decodedMemo = memoizedState(); 138 | this._bySources = undefined; 139 | this._bySourceMemos = undefined; 140 | } 141 | } 142 | 143 | /** 144 | * Typescript doesn't allow friend access to private fields, so this just casts the map into a type 145 | * with public access modifiers. 146 | */ 147 | function cast(map: unknown): PublicMap { 148 | return map as any; 149 | } 150 | 151 | /** 152 | * Returns the encoded (VLQ string) form of the SourceMap's mappings field. 153 | */ 154 | export function encodedMappings(map: TraceMap): EncodedSourceMap['mappings'] { 155 | return (cast(map)._encoded ??= encode(cast(map)._decoded!)); 156 | } 157 | 158 | /** 159 | * Returns the decoded (array of lines of segments) form of the SourceMap's mappings field. 160 | */ 161 | export function decodedMappings(map: TraceMap): Readonly { 162 | return (cast(map)._decoded ||= decode(cast(map)._encoded!)); 163 | } 164 | 165 | /** 166 | * A low-level API to find the segment associated with a generated line/column (think, from a 167 | * stack trace). Line and column here are 0-based, unlike `originalPositionFor`. 168 | */ 169 | export function traceSegment( 170 | map: TraceMap, 171 | line: number, 172 | column: number, 173 | ): Readonly | null { 174 | const decoded = decodedMappings(map); 175 | 176 | // It's common for parent source maps to have pointers to lines that have no 177 | // mapping (like a "//# sourceMappingURL=") at the end of the child file. 178 | if (line >= decoded.length) return null; 179 | 180 | const segments = decoded[line]; 181 | const index = traceSegmentInternal( 182 | segments, 183 | cast(map)._decodedMemo, 184 | line, 185 | column, 186 | GREATEST_LOWER_BOUND, 187 | ); 188 | 189 | return index === -1 ? null : segments[index]; 190 | } 191 | 192 | /** 193 | * A higher-level API to find the source/line/column associated with a generated line/column 194 | * (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in 195 | * `source-map` library. 196 | */ 197 | export function originalPositionFor( 198 | map: TraceMap, 199 | needle: Needle, 200 | ): OriginalMapping | InvalidOriginalMapping { 201 | let { line, column, bias } = needle; 202 | line--; 203 | if (line < 0) throw new Error(LINE_GTR_ZERO); 204 | if (column < 0) throw new Error(COL_GTR_EQ_ZERO); 205 | 206 | const decoded = decodedMappings(map); 207 | 208 | // It's common for parent source maps to have pointers to lines that have no 209 | // mapping (like a "//# sourceMappingURL=") at the end of the child file. 210 | if (line >= decoded.length) return OMapping(null, null, null, null); 211 | 212 | const segments = decoded[line]; 213 | const index = traceSegmentInternal( 214 | segments, 215 | cast(map)._decodedMemo, 216 | line, 217 | column, 218 | bias || GREATEST_LOWER_BOUND, 219 | ); 220 | 221 | if (index === -1) return OMapping(null, null, null, null); 222 | 223 | const segment = segments[index]; 224 | if (segment.length === 1) return OMapping(null, null, null, null); 225 | 226 | const { names, resolvedSources } = map; 227 | return OMapping( 228 | resolvedSources[segment[SOURCES_INDEX]], 229 | segment[SOURCE_LINE] + 1, 230 | segment[SOURCE_COLUMN], 231 | segment.length === 5 ? names[segment[NAMES_INDEX]] : null, 232 | ); 233 | } 234 | 235 | /** 236 | * Finds the generated line/column position of the provided source/line/column source position. 237 | */ 238 | export function generatedPositionFor( 239 | map: TraceMap, 240 | needle: SourceNeedle, 241 | ): GeneratedMapping | InvalidGeneratedMapping { 242 | const { source, line, column, bias } = needle; 243 | return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND, false); 244 | } 245 | 246 | /** 247 | * Finds all generated line/column positions of the provided source/line/column source position. 248 | */ 249 | export function allGeneratedPositionsFor(map: TraceMap, needle: SourceNeedle): GeneratedMapping[] { 250 | const { source, line, column, bias } = needle; 251 | // SourceMapConsumer uses LEAST_UPPER_BOUND for some reason, so we follow suit. 252 | return generatedPosition(map, source, line, column, bias || LEAST_UPPER_BOUND, true); 253 | } 254 | 255 | /** 256 | * Iterates each mapping in generated position order. 257 | */ 258 | export function eachMapping(map: TraceMap, cb: (mapping: EachMapping) => void): void { 259 | const decoded = decodedMappings(map); 260 | const { names, resolvedSources } = map; 261 | 262 | for (let i = 0; i < decoded.length; i++) { 263 | const line = decoded[i]; 264 | for (let j = 0; j < line.length; j++) { 265 | const seg = line[j]; 266 | 267 | const generatedLine = i + 1; 268 | const generatedColumn = seg[0]; 269 | let source = null; 270 | let originalLine = null; 271 | let originalColumn = null; 272 | let name = null; 273 | if (seg.length !== 1) { 274 | source = resolvedSources[seg[1]]; 275 | originalLine = seg[2] + 1; 276 | originalColumn = seg[3]; 277 | } 278 | if (seg.length === 5) name = names[seg[4]]; 279 | 280 | cb({ 281 | generatedLine, 282 | generatedColumn, 283 | source, 284 | originalLine, 285 | originalColumn, 286 | name, 287 | } as EachMapping); 288 | } 289 | } 290 | } 291 | 292 | function sourceIndex(map: TraceMap, source: string): number { 293 | const { sources, resolvedSources } = map; 294 | let index = sources.indexOf(source); 295 | if (index === -1) index = resolvedSources.indexOf(source); 296 | return index; 297 | } 298 | 299 | /** 300 | * Retrieves the source content for a particular source, if its found. Returns null if not. 301 | */ 302 | export function sourceContentFor(map: TraceMap, source: string): string | null { 303 | const { sourcesContent } = map; 304 | if (sourcesContent == null) return null; 305 | const index = sourceIndex(map, source); 306 | return index === -1 ? null : sourcesContent[index]; 307 | } 308 | 309 | /** 310 | * Determines if the source is marked to ignore by the source map. 311 | */ 312 | export function isIgnored(map: TraceMap, source: string): boolean { 313 | const { ignoreList } = map; 314 | if (ignoreList == null) return false; 315 | const index = sourceIndex(map, source); 316 | return index === -1 ? false : ignoreList.includes(index); 317 | } 318 | 319 | /** 320 | * A helper that skips sorting of the input map's mappings array, which can be expensive for larger 321 | * maps. 322 | */ 323 | export function presortedDecodedMap(map: DecodedSourceMap, mapUrl?: string): TraceMap { 324 | const tracer = new TraceMap(clone(map, []), mapUrl); 325 | cast(tracer)._decoded = map.mappings; 326 | return tracer; 327 | } 328 | 329 | /** 330 | * Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects 331 | * a sourcemap, or to JSON.stringify. 332 | */ 333 | export function decodedMap( 334 | map: TraceMap, 335 | ): Omit & { mappings: readonly SourceMapSegment[][] } { 336 | return clone(map, decodedMappings(map)); 337 | } 338 | 339 | /** 340 | * Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects 341 | * a sourcemap, or to JSON.stringify. 342 | */ 343 | export function encodedMap(map: TraceMap): EncodedSourceMap { 344 | return clone(map, encodedMappings(map)); 345 | } 346 | 347 | function clone( 348 | map: TraceMap | DecodedSourceMap, 349 | mappings: T, 350 | ): T extends string ? EncodedSourceMap : DecodedSourceMap { 351 | return { 352 | version: map.version, 353 | file: map.file, 354 | names: map.names, 355 | sourceRoot: map.sourceRoot, 356 | sources: map.sources, 357 | sourcesContent: map.sourcesContent, 358 | mappings, 359 | ignoreList: map.ignoreList || (map as XInput).x_google_ignoreList, 360 | } as any; 361 | } 362 | 363 | function OMapping(source: null, line: null, column: null, name: null): InvalidOriginalMapping; 364 | function OMapping( 365 | source: string, 366 | line: number, 367 | column: number, 368 | name: string | null, 369 | ): OriginalMapping; 370 | function OMapping( 371 | source: string | null, 372 | line: number | null, 373 | column: number | null, 374 | name: string | null, 375 | ): OriginalMapping | InvalidOriginalMapping { 376 | return { source, line, column, name } as any; 377 | } 378 | 379 | function GMapping(line: null, column: null): InvalidGeneratedMapping; 380 | function GMapping(line: number, column: number): GeneratedMapping; 381 | function GMapping( 382 | line: number | null, 383 | column: number | null, 384 | ): GeneratedMapping | InvalidGeneratedMapping { 385 | return { line, column } as any; 386 | } 387 | 388 | function traceSegmentInternal( 389 | segments: SourceMapSegment[], 390 | memo: MemoState, 391 | line: number, 392 | column: number, 393 | bias: Bias, 394 | ): number; 395 | function traceSegmentInternal( 396 | segments: ReverseSegment[], 397 | memo: MemoState, 398 | line: number, 399 | column: number, 400 | bias: Bias, 401 | ): number; 402 | function traceSegmentInternal( 403 | segments: SourceMapSegment[] | ReverseSegment[], 404 | memo: MemoState, 405 | line: number, 406 | column: number, 407 | bias: Bias, 408 | ): number { 409 | let index = memoizedBinarySearch(segments, column, memo, line); 410 | if (bsFound) { 411 | index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index); 412 | } else if (bias === LEAST_UPPER_BOUND) index++; 413 | 414 | if (index === -1 || index === segments.length) return -1; 415 | return index; 416 | } 417 | 418 | function sliceGeneratedPositions( 419 | segments: ReverseSegment[], 420 | memo: MemoState, 421 | line: number, 422 | column: number, 423 | bias: Bias, 424 | ): GeneratedMapping[] { 425 | let min = traceSegmentInternal(segments, memo, line, column, GREATEST_LOWER_BOUND); 426 | 427 | // We ignored the bias when tracing the segment so that we're guarnateed to find the first (in 428 | // insertion order) segment that matched. Even if we did respect the bias when tracing, we would 429 | // still need to call `lowerBound()` to find the first segment, which is slower than just looking 430 | // for the GREATEST_LOWER_BOUND to begin with. The only difference that matters for us is when the 431 | // binary search didn't match, in which case GREATEST_LOWER_BOUND just needs to increment to 432 | // match LEAST_UPPER_BOUND. 433 | if (!bsFound && bias === LEAST_UPPER_BOUND) min++; 434 | 435 | if (min === -1 || min === segments.length) return []; 436 | 437 | // We may have found the segment that started at an earlier column. If this is the case, then we 438 | // need to slice all generated segments that match _that_ column, because all such segments span 439 | // to our desired column. 440 | const matchedColumn = bsFound ? column : segments[min][COLUMN]; 441 | 442 | // The binary search is not guaranteed to find the lower bound when a match wasn't found. 443 | if (!bsFound) min = lowerBound(segments, matchedColumn, min); 444 | const max = upperBound(segments, matchedColumn, min); 445 | 446 | const result = []; 447 | for (; min <= max; min++) { 448 | const segment = segments[min]; 449 | result.push(GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN])); 450 | } 451 | return result; 452 | } 453 | 454 | function generatedPosition( 455 | map: TraceMap, 456 | source: string, 457 | line: number, 458 | column: number, 459 | bias: Bias, 460 | all: false, 461 | ): GeneratedMapping | InvalidGeneratedMapping; 462 | function generatedPosition( 463 | map: TraceMap, 464 | source: string, 465 | line: number, 466 | column: number, 467 | bias: Bias, 468 | all: true, 469 | ): GeneratedMapping[]; 470 | function generatedPosition( 471 | map: TraceMap, 472 | source: string, 473 | line: number, 474 | column: number, 475 | bias: Bias, 476 | all: boolean, 477 | ): GeneratedMapping | InvalidGeneratedMapping | GeneratedMapping[] { 478 | line--; 479 | if (line < 0) throw new Error(LINE_GTR_ZERO); 480 | if (column < 0) throw new Error(COL_GTR_EQ_ZERO); 481 | 482 | const { sources, resolvedSources } = map; 483 | let sourceIndex = sources.indexOf(source); 484 | if (sourceIndex === -1) sourceIndex = resolvedSources.indexOf(source); 485 | if (sourceIndex === -1) return all ? [] : GMapping(null, null); 486 | 487 | const generated = (cast(map)._bySources ||= buildBySources( 488 | decodedMappings(map), 489 | (cast(map)._bySourceMemos = sources.map(memoizedState)), 490 | )); 491 | 492 | const segments = generated[sourceIndex][line]; 493 | if (segments == null) return all ? [] : GMapping(null, null); 494 | 495 | const memo = cast(map)._bySourceMemos![sourceIndex]; 496 | 497 | if (all) return sliceGeneratedPositions(segments, memo, line, column, bias); 498 | 499 | const index = traceSegmentInternal(segments, memo, line, column, bias); 500 | if (index === -1) return GMapping(null, null); 501 | 502 | const segment = segments[index]; 503 | return GMapping(segment[REV_GENERATED_LINE] + 1, segment[REV_GENERATED_COLUMN]); 504 | } 505 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import type { SourceMapSegment } from './sourcemap-segment'; 2 | import type { GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND, TraceMap } from './trace-mapping'; 3 | 4 | export interface SourceMapV3 { 5 | file?: string | null; 6 | names: string[]; 7 | sourceRoot?: string; 8 | sources: (string | null)[]; 9 | sourcesContent?: (string | null)[]; 10 | version: 3; 11 | ignoreList?: number[]; 12 | } 13 | 14 | export interface EncodedSourceMap extends SourceMapV3 { 15 | mappings: string; 16 | } 17 | 18 | export interface DecodedSourceMap extends SourceMapV3 { 19 | mappings: SourceMapSegment[][]; 20 | } 21 | 22 | export interface Section { 23 | offset: { line: number; column: number }; 24 | map: EncodedSourceMap | DecodedSourceMap | SectionedSourceMap; 25 | } 26 | 27 | export interface SectionedSourceMap { 28 | file?: string | null; 29 | sections: Section[]; 30 | version: 3; 31 | } 32 | 33 | export type OriginalMapping = { 34 | source: string | null; 35 | line: number; 36 | column: number; 37 | name: string | null; 38 | }; 39 | 40 | export type InvalidOriginalMapping = { 41 | source: null; 42 | line: null; 43 | column: null; 44 | name: null; 45 | }; 46 | 47 | export type GeneratedMapping = { 48 | line: number; 49 | column: number; 50 | }; 51 | export type InvalidGeneratedMapping = { 52 | line: null; 53 | column: null; 54 | }; 55 | 56 | export type Bias = typeof GREATEST_LOWER_BOUND | typeof LEAST_UPPER_BOUND; 57 | 58 | export type XInput = { x_google_ignoreList?: SourceMapV3['ignoreList'] }; 59 | export type EncodedSourceMapXInput = EncodedSourceMap & XInput; 60 | export type DecodedSourceMapXInput = DecodedSourceMap & XInput; 61 | export type SectionedSourceMapXInput = Omit & { 62 | sections: SectionXInput[]; 63 | }; 64 | export type SectionXInput = Omit & { 65 | map: SectionedSourceMapInput; 66 | }; 67 | 68 | export type SourceMapInput = string | EncodedSourceMapXInput | DecodedSourceMapXInput | TraceMap; 69 | export type SectionedSourceMapInput = SourceMapInput | SectionedSourceMapXInput; 70 | 71 | export type Needle = { line: number; column: number; bias?: Bias }; 72 | export type SourceNeedle = { source: string; line: number; column: number; bias?: Bias }; 73 | 74 | export type EachMapping = 75 | | { 76 | generatedLine: number; 77 | generatedColumn: number; 78 | source: null; 79 | originalLine: null; 80 | originalColumn: null; 81 | name: null; 82 | } 83 | | { 84 | generatedLine: number; 85 | generatedColumn: number; 86 | source: string | null; 87 | originalLine: number; 88 | originalColumn: number; 89 | name: string | null; 90 | }; 91 | 92 | export abstract class SourceMap { 93 | declare version: SourceMapV3['version']; 94 | declare file: SourceMapV3['file']; 95 | declare names: SourceMapV3['names']; 96 | declare sourceRoot: SourceMapV3['sourceRoot']; 97 | declare sources: SourceMapV3['sources']; 98 | declare sourcesContent: SourceMapV3['sourcesContent']; 99 | declare resolvedSources: SourceMapV3['sources']; 100 | declare ignoreList: SourceMapV3['ignoreList']; 101 | } 102 | 103 | export type Ro = T extends Array 104 | ? V[] | Readonly | RoArray | Readonly> 105 | : T extends object 106 | ? T | Readonly | RoObject | Readonly> 107 | : T; 108 | type RoArray = Ro[]; 109 | type RoObject = { [K in keyof T]: T[K] | Ro }; 110 | 111 | export function parse(map: T): Exclude { 112 | return typeof map === 'string' ? JSON.parse(map) : map; 113 | } 114 | -------------------------------------------------------------------------------- /test/binary-search.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'assert'; 2 | import { binarySearch, found, memoizedState, memoizedBinarySearch } from '../src/binary-search'; 3 | 4 | type SourceMapSegment = [number]; 5 | 6 | describe('binary search', () => { 7 | it('returns index of match', () => { 8 | const array: SourceMapSegment[] = []; 9 | 10 | array.push([0]); 11 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 12 | 13 | array.push([1]); 14 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 15 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 16 | 17 | array.push([2]); 18 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 19 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 20 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 21 | 22 | array.push([3]); 23 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 24 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 25 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 26 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 3); 27 | 28 | array.push([4]); 29 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 30 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 31 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 32 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 3); 33 | assert.equal(binarySearch(array, 4, 0, array.length - 1), 4); 34 | 35 | array.push([5]); 36 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 37 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 38 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 39 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 3); 40 | assert.equal(binarySearch(array, 4, 0, array.length - 1), 4); 41 | assert.equal(binarySearch(array, 5, 0, array.length - 1), 5); 42 | }); 43 | 44 | it('for non-match returns index for value lower than needle', () => { 45 | // Test middles, which have a number left and right of index. 46 | const array: SourceMapSegment[] = []; 47 | 48 | array.push([0]); 49 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 50 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 51 | 52 | array.push([2]); 53 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 54 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 55 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 1); 56 | 57 | array.push([4]); 58 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 59 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 60 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 1); 61 | assert.equal(binarySearch(array, 5, 0, array.length - 1), 2); 62 | 63 | array.push([6]); 64 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 65 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 66 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 1); 67 | assert.equal(binarySearch(array, 5, 0, array.length - 1), 2); 68 | assert.equal(binarySearch(array, 7, 0, array.length - 1), 3); 69 | 70 | array.push([8]); 71 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 72 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 73 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 1); 74 | assert.equal(binarySearch(array, 5, 0, array.length - 1), 2); 75 | assert.equal(binarySearch(array, 7, 0, array.length - 1), 3); 76 | assert.equal(binarySearch(array, 9, 0, array.length - 1), 4); 77 | 78 | array.push([10]); 79 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 80 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 0); 81 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 1); 82 | assert.equal(binarySearch(array, 5, 0, array.length - 1), 2); 83 | assert.equal(binarySearch(array, 7, 0, array.length - 1), 3); 84 | assert.equal(binarySearch(array, 9, 0, array.length - 1), 4); 85 | assert.equal(binarySearch(array, 11, 0, array.length - 1), 5); 86 | }); 87 | 88 | it('needle is lower than all elements returns -1', () => { 89 | const array: SourceMapSegment[] = []; 90 | const needle = -1; 91 | 92 | array.push([0]); 93 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 94 | 95 | array.push([1]); 96 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 97 | 98 | array.push([2]); 99 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 100 | 101 | array.push([3]); 102 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 103 | 104 | array.push([4]); 105 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 106 | 107 | array.push([5]); 108 | assert.equal(binarySearch(array, needle, 0, array.length - 1), -1); 109 | }); 110 | 111 | it('needle is higher than all elements returns last index', () => { 112 | const array: SourceMapSegment[] = []; 113 | const needle = 2 ** 16; 114 | 115 | array.push([0]); 116 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 117 | 118 | array.push([1]); 119 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 120 | 121 | array.push([2]); 122 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 123 | 124 | array.push([3]); 125 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 126 | 127 | array.push([4]); 128 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 129 | 130 | array.push([5]); 131 | assert.equal(binarySearch(array, needle, 0, array.length - 1), array.length - 1); 132 | }); 133 | 134 | it('empty array returns -1', () => { 135 | const array: SourceMapSegment[] = []; 136 | 137 | assert.equal(binarySearch(array, -1, 0, array.length - 1), -1); 138 | assert.equal(binarySearch(array, 0, 0, array.length - 1), -1); 139 | assert.equal(binarySearch(array, 1, 0, array.length - 1), -1); 140 | }); 141 | 142 | it('multiple items in array returns any match', () => { 143 | const array: SourceMapSegment[] = []; 144 | const needle = 1; 145 | 146 | array.push([needle]); 147 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 0); 148 | 149 | array.push([needle]); 150 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 0); 151 | 152 | array.push([needle]); 153 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 1); 154 | 155 | array.push([needle]); 156 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 1); 157 | 158 | array.push([needle]); 159 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 2); 160 | 161 | array.push([needle]); 162 | assert.equal(binarySearch(array, needle, 0, array.length - 1), 2); 163 | }); 164 | 165 | describe('low', () => { 166 | it('low equals needle index returns needle index', () => { 167 | const array: SourceMapSegment[] = []; 168 | 169 | array.push([0]); 170 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 171 | 172 | array.push([1]); 173 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 174 | assert.equal(binarySearch(array, 1, 1, array.length - 1), 1); 175 | 176 | array.push([2]); 177 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 178 | assert.equal(binarySearch(array, 1, 1, array.length - 1), 1); 179 | assert.equal(binarySearch(array, 2, 2, array.length - 1), 2); 180 | 181 | array.push([3]); 182 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 183 | assert.equal(binarySearch(array, 1, 1, array.length - 1), 1); 184 | assert.equal(binarySearch(array, 2, 2, array.length - 1), 2); 185 | assert.equal(binarySearch(array, 3, 3, array.length - 1), 3); 186 | 187 | array.push([4]); 188 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 189 | assert.equal(binarySearch(array, 1, 1, array.length - 1), 1); 190 | assert.equal(binarySearch(array, 2, 2, array.length - 1), 2); 191 | assert.equal(binarySearch(array, 3, 3, array.length - 1), 3); 192 | assert.equal(binarySearch(array, 4, 4, array.length - 1), 4); 193 | 194 | array.push([5]); 195 | assert.equal(binarySearch(array, 0, 0, array.length - 1), 0); 196 | assert.equal(binarySearch(array, 1, 1, array.length - 1), 1); 197 | assert.equal(binarySearch(array, 2, 2, array.length - 1), 2); 198 | assert.equal(binarySearch(array, 3, 3, array.length - 1), 3); 199 | assert.equal(binarySearch(array, 4, 4, array.length - 1), 4); 200 | assert.equal(binarySearch(array, 5, 5, array.length - 1), 5); 201 | }); 202 | 203 | it('low higher than needle index returns left of high', () => { 204 | const array: SourceMapSegment[] = []; 205 | 206 | array.push([0]); 207 | 208 | array.push([1]); 209 | assert.equal(binarySearch(array, 0, 1, array.length - 1), 0); 210 | 211 | array.push([2]); 212 | assert.equal(binarySearch(array, 0, 1, array.length - 1), 0); 213 | assert.equal(binarySearch(array, 0, 2, array.length - 1), 1); 214 | assert.equal(binarySearch(array, 1, 2, array.length - 1), 1); 215 | 216 | array.push([3]); 217 | assert.equal(binarySearch(array, 0, 1, array.length - 1), 0); 218 | assert.equal(binarySearch(array, 0, 2, array.length - 1), 1); 219 | assert.equal(binarySearch(array, 0, 3, array.length - 1), 2); 220 | assert.equal(binarySearch(array, 1, 2, array.length - 1), 1); 221 | assert.equal(binarySearch(array, 1, 3, array.length - 1), 2); 222 | assert.equal(binarySearch(array, 2, 3, array.length - 1), 2); 223 | 224 | array.push([4]); 225 | assert.equal(binarySearch(array, 0, 1, array.length - 1), 0); 226 | assert.equal(binarySearch(array, 0, 2, array.length - 1), 1); 227 | assert.equal(binarySearch(array, 0, 3, array.length - 1), 2); 228 | assert.equal(binarySearch(array, 0, 4, array.length - 1), 3); 229 | assert.equal(binarySearch(array, 1, 2, array.length - 1), 1); 230 | assert.equal(binarySearch(array, 1, 3, array.length - 1), 2); 231 | assert.equal(binarySearch(array, 1, 4, array.length - 1), 3); 232 | assert.equal(binarySearch(array, 2, 3, array.length - 1), 2); 233 | assert.equal(binarySearch(array, 2, 4, array.length - 1), 3); 234 | assert.equal(binarySearch(array, 3, 4, array.length - 1), 3); 235 | }); 236 | 237 | it('low lower than needle index returns needle index', () => { 238 | const array: SourceMapSegment[] = []; 239 | 240 | array.push([0]); 241 | 242 | array.push([1]); 243 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 244 | 245 | array.push([2]); 246 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 247 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 248 | assert.equal(binarySearch(array, 2, 1, array.length - 1), 2); 249 | 250 | array.push([3]); 251 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 252 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 253 | assert.equal(binarySearch(array, 2, 1, array.length - 1), 2); 254 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 3); 255 | assert.equal(binarySearch(array, 3, 1, array.length - 1), 3); 256 | assert.equal(binarySearch(array, 3, 2, array.length - 1), 3); 257 | 258 | array.push([4]); 259 | assert.equal(binarySearch(array, 1, 0, array.length - 1), 1); 260 | assert.equal(binarySearch(array, 2, 0, array.length - 1), 2); 261 | assert.equal(binarySearch(array, 2, 1, array.length - 1), 2); 262 | assert.equal(binarySearch(array, 3, 0, array.length - 1), 3); 263 | assert.equal(binarySearch(array, 3, 1, array.length - 1), 3); 264 | assert.equal(binarySearch(array, 3, 2, array.length - 1), 3); 265 | assert.equal(binarySearch(array, 4, 0, array.length - 1), 4); 266 | assert.equal(binarySearch(array, 4, 1, array.length - 1), 4); 267 | assert.equal(binarySearch(array, 4, 2, array.length - 1), 4); 268 | assert.equal(binarySearch(array, 4, 3, array.length - 1), 4); 269 | }); 270 | }); 271 | 272 | describe('high', () => { 273 | it('high equals needle index returns needle index', () => { 274 | const array: SourceMapSegment[] = []; 275 | 276 | array.push([0]); 277 | assert.equal(binarySearch(array, 0, 0, 0), 0); 278 | 279 | array.push([1]); 280 | assert.equal(binarySearch(array, 0, 0, 0), 0); 281 | assert.equal(binarySearch(array, 1, 0, 1), 1); 282 | 283 | array.push([2]); 284 | assert.equal(binarySearch(array, 0, 0, 0), 0); 285 | assert.equal(binarySearch(array, 1, 0, 1), 1); 286 | assert.equal(binarySearch(array, 2, 0, 2), 2); 287 | 288 | array.push([3]); 289 | assert.equal(binarySearch(array, 0, 0, 0), 0); 290 | assert.equal(binarySearch(array, 1, 0, 1), 1); 291 | assert.equal(binarySearch(array, 2, 0, 2), 2); 292 | assert.equal(binarySearch(array, 3, 0, 3), 3); 293 | 294 | array.push([4]); 295 | assert.equal(binarySearch(array, 0, 0, 0), 0); 296 | assert.equal(binarySearch(array, 1, 0, 1), 1); 297 | assert.equal(binarySearch(array, 2, 0, 2), 2); 298 | assert.equal(binarySearch(array, 3, 0, 3), 3); 299 | assert.equal(binarySearch(array, 4, 0, 4), 4); 300 | 301 | array.push([5]); 302 | assert.equal(binarySearch(array, 0, 0, 0), 0); 303 | assert.equal(binarySearch(array, 1, 0, 1), 1); 304 | assert.equal(binarySearch(array, 2, 0, 2), 2); 305 | assert.equal(binarySearch(array, 3, 0, 3), 3); 306 | assert.equal(binarySearch(array, 4, 0, 4), 4); 307 | assert.equal(binarySearch(array, 5, 0, 5), 5); 308 | }); 309 | 310 | it('high higher than needle index returns needle index', () => { 311 | const array: SourceMapSegment[] = []; 312 | 313 | array.push([0]); 314 | 315 | array.push([1]); 316 | assert.equal(binarySearch(array, 0, 0, 1), 0); 317 | 318 | array.push([2]); 319 | assert.equal(binarySearch(array, 0, 0, 1), 0); 320 | assert.equal(binarySearch(array, 0, 0, 2), 0); 321 | assert.equal(binarySearch(array, 1, 0, 2), 1); 322 | 323 | array.push([3]); 324 | assert.equal(binarySearch(array, 0, 0, 1), 0); 325 | assert.equal(binarySearch(array, 0, 0, 2), 0); 326 | assert.equal(binarySearch(array, 0, 0, 3), 0); 327 | assert.equal(binarySearch(array, 1, 0, 2), 1); 328 | assert.equal(binarySearch(array, 1, 0, 3), 1); 329 | assert.equal(binarySearch(array, 2, 0, 3), 2); 330 | 331 | array.push([4]); 332 | assert.equal(binarySearch(array, 0, 0, 1), 0); 333 | assert.equal(binarySearch(array, 0, 0, 2), 0); 334 | assert.equal(binarySearch(array, 0, 0, 3), 0); 335 | assert.equal(binarySearch(array, 0, 0, 4), 0); 336 | assert.equal(binarySearch(array, 1, 0, 2), 1); 337 | assert.equal(binarySearch(array, 1, 0, 3), 1); 338 | assert.equal(binarySearch(array, 1, 0, 4), 1); 339 | assert.equal(binarySearch(array, 2, 0, 3), 2); 340 | assert.equal(binarySearch(array, 2, 0, 4), 2); 341 | assert.equal(binarySearch(array, 3, 0, 4), 3); 342 | }); 343 | 344 | it('high lower than needle index returns high', () => { 345 | const array: SourceMapSegment[] = []; 346 | 347 | array.push([0]); 348 | 349 | array.push([1]); 350 | assert.equal(binarySearch(array, 1, 0, 0), 0); 351 | 352 | array.push([2]); 353 | assert.equal(binarySearch(array, 1, 0, 0), 0); 354 | assert.equal(binarySearch(array, 2, 0, 0), 0); 355 | assert.equal(binarySearch(array, 2, 0, 1), 1); 356 | 357 | array.push([3]); 358 | assert.equal(binarySearch(array, 1, 0, 0), 0); 359 | assert.equal(binarySearch(array, 2, 0, 0), 0); 360 | assert.equal(binarySearch(array, 2, 0, 1), 1); 361 | assert.equal(binarySearch(array, 3, 0, 0), 0); 362 | assert.equal(binarySearch(array, 3, 0, 1), 1); 363 | assert.equal(binarySearch(array, 3, 0, 2), 2); 364 | 365 | array.push([4]); 366 | assert.equal(binarySearch(array, 1, 0, 0), 0); 367 | assert.equal(binarySearch(array, 2, 0, 0), 0); 368 | assert.equal(binarySearch(array, 2, 0, 1), 1); 369 | assert.equal(binarySearch(array, 3, 0, 0), 0); 370 | assert.equal(binarySearch(array, 3, 0, 1), 1); 371 | assert.equal(binarySearch(array, 3, 0, 2), 2); 372 | assert.equal(binarySearch(array, 4, 0, 0), 0); 373 | assert.equal(binarySearch(array, 4, 0, 1), 1); 374 | assert.equal(binarySearch(array, 4, 0, 2), 2); 375 | assert.equal(binarySearch(array, 4, 0, 3), 3); 376 | }); 377 | }); 378 | }); 379 | 380 | describe('memoizedBinarySearch', () => { 381 | const array: SourceMapSegment[] = [[1], [5], [10]]; 382 | 383 | it('refinds same index', () => { 384 | const memo = memoizedState(); 385 | 386 | assert.equal(memoizedBinarySearch(array, 6, memo, 0), 1); 387 | assert.equal(memoizedBinarySearch(array, 6, memo, 0), 1); 388 | }); 389 | 390 | it('restores found state', () => { 391 | const memo1 = memoizedState(); 392 | const memo2 = memoizedState(); 393 | 394 | assert.equal(memoizedBinarySearch(array, 0, memo1, 0), -1); 395 | assert.equal(found, false); 396 | 397 | assert.equal(memoizedBinarySearch(array, 5, memo2, 0), 1); 398 | assert.equal(found, true); 399 | 400 | assert.equal(memoizedBinarySearch(array, 0, memo1, 0), -1); 401 | assert.equal(found, false); 402 | 403 | assert.equal(memoizedBinarySearch(array, 5, memo2, 0), 1); 404 | assert.equal(found, true); 405 | }); 406 | }); 407 | -------------------------------------------------------------------------------- /test/flatten-map.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { strict as assert } from 'assert'; 4 | import { FlattenMap, encodedMappings, decodedMappings } from '../src/trace-mapping'; 5 | 6 | import type { SectionedSourceMap, SourceMapSegment } from '../src/trace-mapping'; 7 | 8 | describe('FlattenMap', () => { 9 | const map: SectionedSourceMap = { 10 | version: 3, 11 | file: 'sectioned.js', 12 | sections: [ 13 | { 14 | offset: { line: 1, column: 1 }, 15 | map: { 16 | version: 3, 17 | sections: [ 18 | { 19 | offset: { line: 0, column: 1 }, 20 | map: { 21 | version: 3, 22 | names: ['first'], 23 | sources: ['first.js'], 24 | sourcesContent: ['firstsource'], 25 | mappings: 'AAAAA,CAAC', 26 | ignoreList: [0], 27 | }, 28 | }, 29 | { 30 | offset: { line: 0, column: 2 }, 31 | map: { 32 | version: 3, 33 | names: ['second'], 34 | sources: ['second.js'], 35 | sourcesContent: ['secondsource'], 36 | mappings: 'AAAAA;AAAA', 37 | }, 38 | }, 39 | ], 40 | }, 41 | }, 42 | { 43 | offset: { line: 2, column: 0 }, 44 | map: { 45 | version: 3, 46 | sections: [ 47 | { 48 | offset: { line: 0, column: 0 }, 49 | map: { 50 | version: 3, 51 | names: ['third'], 52 | sources: ['third.js'], 53 | sourcesContent: ['thirdsource'], 54 | sourceRoot: 'nested', 55 | mappings: 'AAAAA,CAAA;AAAA', 56 | }, 57 | }, 58 | { 59 | offset: { line: 0, column: 1 }, 60 | map: { 61 | version: 3, 62 | names: [], 63 | sources: ['fourth.js'], 64 | sourcesContent: ['fourthsource'], 65 | mappings: [[[0, 0, 0, 0]]], 66 | ignoreList: [0], 67 | }, 68 | }, 69 | ], 70 | }, 71 | }, 72 | ], 73 | }; 74 | 75 | describe('map properties', () => { 76 | it('version', () => { 77 | const tracer = new FlattenMap(map); 78 | assert.equal(tracer.version, map.version); 79 | }); 80 | 81 | it('file', () => { 82 | const tracer = new FlattenMap(map); 83 | assert.equal(tracer.file, map.file); 84 | }); 85 | 86 | it('sourceRoot', () => { 87 | const tracer = new FlattenMap(map); 88 | assert.equal(tracer.sourceRoot, undefined); 89 | }); 90 | 91 | it('sources', () => { 92 | const tracer = new FlattenMap(map); 93 | assert.deepEqual(tracer.sources, ['first.js', 'second.js', 'nested/third.js', 'fourth.js']); 94 | }); 95 | 96 | it('names', () => { 97 | const tracer = new FlattenMap(map); 98 | assert.deepEqual(tracer.names, ['first', 'second', 'third']); 99 | }); 100 | 101 | it('encodedMappings', () => { 102 | const tracer = new FlattenMap(map); 103 | assert.equal(encodedMappings(tracer), ';EAAAA,CCAAC;ACAAC,CCAA'); 104 | }); 105 | 106 | it('decodedMappings', () => { 107 | const tracer = new FlattenMap(map); 108 | assert.deepEqual(decodedMappings(tracer), [ 109 | [], 110 | [ 111 | [2, 0, 0, 0, 0], 112 | [3, 1, 0, 0, 1], 113 | ], 114 | [ 115 | [0, 2, 0, 0, 2], 116 | [1, 3, 0, 0], 117 | ], 118 | ]); 119 | }); 120 | 121 | it('sourcesContent', () => { 122 | const tracer = new FlattenMap(map); 123 | assert.deepEqual(tracer.sourcesContent, [ 124 | 'firstsource', 125 | 'secondsource', 126 | 'thirdsource', 127 | 'fourthsource', 128 | ]); 129 | }); 130 | 131 | it('ignoreList', () => { 132 | const tracer = new FlattenMap(map); 133 | assert.deepEqual(tracer.ignoreList, [0, 3]); 134 | }); 135 | }); 136 | 137 | describe('typescript readonly type', () => { 138 | it('decoded source map', () => { 139 | // This is a TS lint test, not a real one. 140 | const decodedMap = { 141 | version: 3 as const, 142 | sources: ['input.js'] as readonly string[], 143 | names: [] as readonly string[], 144 | mappings: [] as readonly SourceMapSegment[][], 145 | sourcesContent: [] as readonly string[], 146 | }; 147 | 148 | new FlattenMap(decodedMap); 149 | }); 150 | }); 151 | }); 152 | -------------------------------------------------------------------------------- /test/resolve.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'assert'; 2 | import resolver from '../src/resolve'; 3 | 4 | describe('resolve', () => { 5 | it('unresolved without sourceRoot', () => { 6 | const resolve = resolver(undefined, undefined); 7 | assert.equal(resolve('input.js'), 'input.js'); 8 | }); 9 | 10 | it('relative to mapUrl', () => { 11 | const resolve = resolver('foo/script.js.map', undefined); 12 | assert.equal(resolve('input.js'), 'foo/input.js'); 13 | }); 14 | 15 | it('relative to sourceRoot', () => { 16 | const resolve = resolver(undefined, 'foo'); 17 | assert.equal(resolve('input.js'), 'foo/input.js'); 18 | }); 19 | 20 | it('relative to mapUrl then sourceRoot', () => { 21 | const resolve = resolver('foo/script.js.map', 'bar'); 22 | assert.equal(resolve('input.js'), 'foo/bar/input.js'); 23 | }); 24 | 25 | it('prepends sourceRoot to source before resolving', () => { 26 | const resolve = resolver('foo/script.js.map', 'bar'); 27 | assert.equal(resolve('/input.js'), 'foo/bar/input.js'); 28 | }); 29 | 30 | it('skips undefined sourceRoot before resolving', () => { 31 | const resolve = resolver('foo/script.js.map', undefined); 32 | assert.equal(resolve('/input.js'), '/input.js'); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/strip-filename.test.ts: -------------------------------------------------------------------------------- 1 | import { strict as assert } from 'assert'; 2 | import stripFilename from '../src/strip-filename'; 3 | 4 | describe('stripFilename', () => { 5 | it('returns empty string for empty string', () => { 6 | assert.equal(stripFilename(''), ''); 7 | }); 8 | 9 | it('returns empty string if no directory', () => { 10 | assert.equal(stripFilename('foo'), ''); 11 | }); 12 | 13 | it('it trims filename from directory path', () => { 14 | assert.equal(stripFilename('/foo/bar/baz'), '/foo/bar/'); 15 | assert.equal(stripFilename('/foo/bar'), '/foo/'); 16 | }); 17 | 18 | it('it does nothing if trailing slash', () => { 19 | assert.equal(stripFilename('/foo/bar/baz/'), '/foo/bar/baz/'); 20 | assert.equal(stripFilename('/foo/bar/'), '/foo/bar/'); 21 | assert.equal(stripFilename('/foo/'), '/foo/'); 22 | assert.equal(stripFilename('/'), '/'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/trace-mapping.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { strict as assert } from 'assert'; 4 | import { encode, decode } from '@jridgewell/sourcemap-codec'; 5 | 6 | import { 7 | TraceMap, 8 | encodedMappings, 9 | decodedMappings, 10 | traceSegment, 11 | originalPositionFor, 12 | generatedPositionFor, 13 | presortedDecodedMap, 14 | sourceContentFor, 15 | eachMapping, 16 | GREATEST_LOWER_BOUND, 17 | LEAST_UPPER_BOUND, 18 | allGeneratedPositionsFor, 19 | isIgnored, 20 | } from '../src/trace-mapping'; 21 | 22 | import type { 23 | SourceMapInput, 24 | EncodedSourceMap, 25 | DecodedSourceMap, 26 | EachMapping, 27 | SourceMapSegment, 28 | } from '../src/trace-mapping'; 29 | 30 | describe('TraceMap', () => { 31 | const decodedMap: DecodedSourceMap = { 32 | version: 3, 33 | sources: ['input.js'], 34 | sourceRoot: 'https://astexplorer.net/', 35 | names: ['foo', 'bar', 'Error'], 36 | mappings: [ 37 | [ 38 | [0, 0, 0, 0], 39 | [9, 0, 0, 9, 0], 40 | [12, 0, 0, 0], 41 | [13, 0, 0, 13, 1], 42 | [16, 0, 0, 0], 43 | [18, 0, 0, 33], 44 | ], 45 | [ 46 | [4, 0, 1, 4], 47 | [8, 0, 1, 10], 48 | [12, 0, 1, 14, 2], 49 | [17, 0, 1, 10], 50 | [18, 0, 1, 20], 51 | [32, 0, 1, 10], 52 | [33, 0, 1, 4], 53 | ], 54 | [[0, 0, 2, 1]], 55 | [], 56 | [ 57 | [0, 0, 3, 0, 0], 58 | [3, 0, 3, 3], 59 | ], 60 | ], 61 | sourcesContent: [ 62 | "function foo(bar: number): never {\n throw new Error('Intentional.');\n}\nfoo();", 63 | ], 64 | }; 65 | const encodedMap: EncodedSourceMap = { 66 | ...decodedMap, 67 | mappings: encode(decodedMap.mappings), 68 | }; 69 | function replaceField( 70 | map: DecodedSourceMap | EncodedSourceMap | string, 71 | field: keyof (DecodedSourceMap | EncodedSourceMap), 72 | value: any, 73 | ): SourceMapInput { 74 | if (typeof map !== 'string') { 75 | return { 76 | ...map, 77 | [field]: value, 78 | }; 79 | } 80 | 81 | map = JSON.parse(map); 82 | (map as any)[field] = value; 83 | return JSON.stringify(map); 84 | } 85 | 86 | function testSuite(map: DecodedSourceMap | EncodedSourceMap | string) { 87 | return () => { 88 | describe('map properties', () => { 89 | it('version', () => { 90 | const tracer = new TraceMap(map); 91 | assert.equal(tracer.version, decodedMap.version); 92 | }); 93 | 94 | it('file', () => { 95 | const tracer = new TraceMap(map); 96 | assert.equal(tracer.file, decodedMap.file); 97 | }); 98 | 99 | it('sourceRoot', () => { 100 | const tracer = new TraceMap(map); 101 | assert.equal(tracer.sourceRoot, decodedMap.sourceRoot); 102 | }); 103 | 104 | it('sources', () => { 105 | const tracer = new TraceMap(map); 106 | assert.deepEqual(tracer.sources, decodedMap.sources); 107 | }); 108 | 109 | it('names', () => { 110 | const tracer = new TraceMap(map); 111 | assert.deepEqual(tracer.names, decodedMap.names); 112 | }); 113 | 114 | it('encodedMappings', () => { 115 | const tracer = new TraceMap(map); 116 | assert.equal(encodedMappings(tracer), encodedMap.mappings); 117 | }); 118 | 119 | it('decodedMappings', () => { 120 | const tracer = new TraceMap(map); 121 | assert.deepEqual(decodedMappings(tracer), decodedMap.mappings); 122 | }); 123 | 124 | it('sourcesContent', () => { 125 | const tracer = new TraceMap(map); 126 | assert.deepEqual(tracer.sourcesContent, decodedMap.sourcesContent); 127 | }); 128 | 129 | describe('sourceContentFor', () => { 130 | it('returns null if no sourcesContent', () => { 131 | const tracer = new TraceMap(replaceField(map, 'sourcesContent', undefined)); 132 | const source = tracer.sources[0]!; 133 | assert.equal(sourceContentFor(tracer, source), null); 134 | }); 135 | 136 | it('returns null if source not found', () => { 137 | const tracer = new TraceMap(map); 138 | assert.equal(sourceContentFor(tracer, 'foobar'), null); 139 | }); 140 | 141 | it('returns sourceContent for source', () => { 142 | const tracer = new TraceMap(map); 143 | const source = tracer.sources[0]!; 144 | assert.equal(sourceContentFor(tracer, source), decodedMap.sourcesContent![0]); 145 | }); 146 | 147 | it('returns sourceContent for resolved source', () => { 148 | const tracer = new TraceMap(map); 149 | const source = tracer.resolvedSources[0]!; 150 | assert.equal(sourceContentFor(tracer, source), decodedMap.sourcesContent![0]); 151 | }); 152 | }); 153 | 154 | it('ignoreList', () => { 155 | const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); 156 | assert.deepEqual(tracer.ignoreList, [0]); 157 | }); 158 | 159 | describe('isIgnored', () => { 160 | it('returns false if no ignoreList', () => { 161 | const tracer = new TraceMap(replaceField(map, 'ignoreList', undefined)); 162 | const source = tracer.sources[0]!; 163 | assert.equal(isIgnored(tracer, source), false); 164 | }); 165 | 166 | it('returns false if source not found', () => { 167 | const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); 168 | assert.equal(isIgnored(tracer, 'foobar'), false); 169 | }); 170 | 171 | it('returns false if not ignored', () => { 172 | const tracer = new TraceMap(replaceField(map, 'ignoreList', [])); 173 | const source = tracer.sources[0]!; 174 | assert.equal(isIgnored(tracer, source), false); 175 | }); 176 | 177 | it('returns true if ignored', () => { 178 | const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); 179 | const source = tracer.sources[0]!; 180 | assert.equal(isIgnored(tracer, source), true); 181 | }); 182 | 183 | it('returns ignored for resolved source', () => { 184 | const tracer = new TraceMap(replaceField(map, 'ignoreList', [0])); 185 | const source = tracer.resolvedSources[0]!; 186 | assert.equal(isIgnored(tracer, source), true); 187 | }); 188 | 189 | it('supports deprecated x_google_ignoreList', () => { 190 | const tracer = new TraceMap( 191 | replaceField(map, 'x_google_ignoreList' as 'ignoreList', [0]), 192 | ); 193 | const source = tracer.sources[0]!; 194 | assert.equal(isIgnored(tracer, source), true); 195 | }); 196 | }); 197 | 198 | describe('resolvedSources', () => { 199 | it('unresolved without sourceRoot', () => { 200 | const tracer = new TraceMap(replaceField(map, 'sourceRoot', undefined)); 201 | assert.deepEqual(tracer.resolvedSources, ['input.js']); 202 | }); 203 | 204 | it('relative to mapUrl', () => { 205 | const tracer = new TraceMap( 206 | replaceField(map, 'sourceRoot', undefined), 207 | 'foo/script.js.map', 208 | ); 209 | assert.deepEqual(tracer.resolvedSources, ['foo/input.js']); 210 | }); 211 | 212 | it('relative to sourceRoot', () => { 213 | const tracer = new TraceMap(replaceField(map, 'sourceRoot', 'foo')); 214 | assert.deepEqual(tracer.resolvedSources, ['foo/input.js']); 215 | }); 216 | 217 | it('relative to mapUrl then sourceRoot', () => { 218 | const tracer = new TraceMap( 219 | replaceField(map, 'sourceRoot', 'bar'), 220 | 'foo/script.js.map', 221 | ); 222 | assert.deepEqual(tracer.resolvedSources, ['foo/bar/input.js']); 223 | }); 224 | }); 225 | }); 226 | 227 | it('traceSegment', () => { 228 | const { mappings } = decodedMap; 229 | const tracer = new TraceMap(map); 230 | 231 | // This comes before any segment on line 2, but importantly there are segments on line 1. If 232 | // binary searchign returns the last segment of line 1, we've failed. 233 | assert.equal(traceSegment(tracer, 1, 0), null); 234 | 235 | for (let line = 0; line < mappings.length; line++) { 236 | const segmentLine = mappings[line]; 237 | 238 | for (let j = 0; j < segmentLine.length; j++) { 239 | const segment = segmentLine[j]; 240 | const next = j + 1 < segmentLine.length ? segmentLine[j + 1] : null; 241 | const nextColumn = next?.[0] ?? segment[0] + 2; 242 | 243 | for (let column = segment[0]; column < nextColumn; column++) { 244 | const traced = traceSegment(tracer, line, column); 245 | assert.deepEqual(traced, segment, `{ line: ${line}, column: ${column} }`); 246 | } 247 | } 248 | } 249 | }); 250 | 251 | it('originalPositionFor', () => { 252 | const tracer = new TraceMap(map); 253 | 254 | assert.deepEqual(originalPositionFor(tracer, { line: 2, column: 13 }), { 255 | source: 'https://astexplorer.net/input.js', 256 | line: 2, 257 | column: 14, 258 | name: 'Error', 259 | }); 260 | 261 | assert.deepEqual( 262 | originalPositionFor(tracer, { line: 2, column: 13, bias: GREATEST_LOWER_BOUND }), 263 | { 264 | source: 'https://astexplorer.net/input.js', 265 | line: 2, 266 | column: 14, 267 | name: 'Error', 268 | }, 269 | ); 270 | 271 | assert.deepEqual( 272 | originalPositionFor(tracer, { line: 2, column: 13, bias: LEAST_UPPER_BOUND }), 273 | { 274 | source: 'https://astexplorer.net/input.js', 275 | line: 2, 276 | column: 10, 277 | name: null, 278 | }, 279 | ); 280 | 281 | assert.deepEqual(originalPositionFor(tracer, { line: 100, column: 13 }), { 282 | source: null, 283 | line: null, 284 | column: null, 285 | name: null, 286 | }); 287 | 288 | assert.throws(() => { 289 | originalPositionFor(tracer, { line: 0, column: 13 }); 290 | }); 291 | 292 | assert.throws(() => { 293 | originalPositionFor(tracer, { line: 1, column: -1 }); 294 | }); 295 | }); 296 | 297 | it('generatedPositionFor', () => { 298 | const tracer = new TraceMap(map); 299 | 300 | assert.deepEqual(generatedPositionFor(tracer, { source: 'input.js', line: 4, column: 3 }), { 301 | line: 5, 302 | column: 3, 303 | }); 304 | 305 | assert.deepEqual(generatedPositionFor(tracer, { source: 'input.js', line: 1, column: 0 }), { 306 | line: 1, 307 | column: 0, 308 | }); 309 | 310 | assert.deepEqual( 311 | generatedPositionFor(tracer, { source: 'input.js', line: 1, column: 33 }), 312 | { 313 | line: 1, 314 | column: 18, 315 | }, 316 | ); 317 | 318 | assert.deepEqual( 319 | generatedPositionFor(tracer, { source: 'input.js', line: 1, column: 14 }), 320 | { 321 | line: 1, 322 | column: 13, 323 | }, 324 | ); 325 | 326 | assert.deepEqual( 327 | generatedPositionFor(tracer, { 328 | source: 'input.js', 329 | line: 1, 330 | column: 14, 331 | bias: GREATEST_LOWER_BOUND, 332 | }), 333 | { 334 | line: 1, 335 | column: 13, 336 | }, 337 | ); 338 | 339 | assert.deepEqual( 340 | generatedPositionFor(tracer, { 341 | source: 'input.js', 342 | line: 1, 343 | column: 14, 344 | bias: LEAST_UPPER_BOUND, 345 | }), 346 | { 347 | line: 1, 348 | column: 18, 349 | }, 350 | ); 351 | 352 | assert.deepEqual(generatedPositionFor(tracer, { source: 'input.js', line: 4, column: 0 }), { 353 | line: 5, 354 | column: 0, 355 | }); 356 | }); 357 | 358 | it('allGeneratedPositionsFor', () => { 359 | const tracer = new TraceMap(map); 360 | 361 | assert.deepEqual( 362 | allGeneratedPositionsFor(tracer, { 363 | source: 'input.js', 364 | line: 1, 365 | column: 33, 366 | }), 367 | [{ line: 1, column: 18 }], 368 | ); 369 | 370 | assert.deepEqual( 371 | allGeneratedPositionsFor(tracer, { 372 | source: 'input.js', 373 | line: 2, 374 | column: 9, 375 | }), 376 | [ 377 | { line: 2, column: 8 }, 378 | { line: 2, column: 17 }, 379 | { line: 2, column: 32 }, 380 | ], 381 | ); 382 | 383 | assert.deepEqual( 384 | allGeneratedPositionsFor(tracer, { 385 | source: 'input.js', 386 | line: 2, 387 | column: 9, 388 | bias: LEAST_UPPER_BOUND, 389 | }), 390 | [ 391 | { line: 2, column: 8 }, 392 | { line: 2, column: 17 }, 393 | { line: 2, column: 32 }, 394 | ], 395 | ); 396 | 397 | assert.deepEqual( 398 | allGeneratedPositionsFor(tracer, { 399 | source: 'input.js', 400 | line: 2, 401 | column: 9, 402 | bias: GREATEST_LOWER_BOUND, 403 | }), 404 | [ 405 | { line: 2, column: 4 }, 406 | { line: 2, column: 33 }, 407 | ], 408 | ); 409 | 410 | assert.deepEqual( 411 | allGeneratedPositionsFor(tracer, { 412 | source: 'input.js', 413 | line: 2, 414 | column: 10, 415 | }), 416 | [ 417 | { line: 2, column: 8 }, 418 | { line: 2, column: 17 }, 419 | { line: 2, column: 32 }, 420 | ], 421 | ); 422 | 423 | assert.deepEqual( 424 | allGeneratedPositionsFor(tracer, { 425 | source: 'input.js', 426 | line: 2, 427 | column: 10, 428 | bias: GREATEST_LOWER_BOUND, 429 | }), 430 | [ 431 | { line: 2, column: 8 }, 432 | { line: 2, column: 17 }, 433 | { line: 2, column: 32 }, 434 | ], 435 | ); 436 | 437 | assert.deepEqual( 438 | allGeneratedPositionsFor(tracer, { source: 'input.js', line: 100, column: 13 }), 439 | [], 440 | ); 441 | 442 | assert.deepEqual( 443 | allGeneratedPositionsFor(tracer, { source: 'input.js', line: 1, column: 100 }), 444 | [], 445 | ); 446 | 447 | assert.deepEqual( 448 | allGeneratedPositionsFor(tracer, { source: 'input.js', line: 1, column: 10 }), 449 | [{ line: 1, column: 13 }], 450 | ); 451 | }); 452 | }; 453 | } 454 | 455 | describe('decoded source map', testSuite(decodedMap)); 456 | describe('json decoded source map', testSuite(JSON.stringify(decodedMap))); 457 | describe('encoded source map', testSuite(encodedMap)); 458 | describe('json encoded source map', testSuite(JSON.stringify(encodedMap))); 459 | 460 | describe('unordered mappings', () => { 461 | const mappings = decodedMap.mappings.map((line) => { 462 | return line.slice().reverse(); 463 | }); 464 | const reversedDecoded: DecodedSourceMap = { 465 | ...decodedMap, 466 | mappings, 467 | }; 468 | const reversedEncoded: EncodedSourceMap = { 469 | ...encodedMap, 470 | mappings: encode(mappings), 471 | }; 472 | 473 | function macro(map: SourceMapInput) { 474 | return () => { 475 | const tracer = new TraceMap(map); 476 | assert.deepEqual(decodedMappings(tracer), decodedMap.mappings); 477 | }; 478 | } 479 | it('decoded source map', macro(reversedDecoded)); 480 | it('json decoded source map', macro(JSON.stringify(reversedDecoded))); 481 | it('encoded source map', macro(reversedEncoded)); 482 | it('json encoded source map', macro(JSON.stringify(reversedEncoded))); 483 | }); 484 | 485 | describe('empty mappings with lines', () => { 486 | const decoded: DecodedSourceMap = { 487 | ...decodedMap, 488 | mappings: decode(';;;;;;;;;;;;;;;;'), 489 | }; 490 | const encoded: EncodedSourceMap = { 491 | ...encodedMap, 492 | mappings: ';;;;;;;;;;;;;;;;', 493 | }; 494 | 495 | function macro(map: SourceMapInput) { 496 | return () => { 497 | const tracer = new TraceMap(map); 498 | for (let i = 0; i < decoded.mappings.length; i++) { 499 | assert.equal(traceSegment(tracer, i, 0), null, `{ line: ${i} }`); 500 | } 501 | }; 502 | } 503 | 504 | it('decoded source map', macro(decoded)); 505 | it('json decoded source map', macro(JSON.stringify(decoded))); 506 | it('encoded source map', macro(encoded)); 507 | it('json encoded source map', macro(JSON.stringify(encoded))); 508 | }); 509 | 510 | describe('eachMapping', () => { 511 | const mappings = decodedMap.mappings.flatMap((line, i) => { 512 | return line.map((seg): EachMapping => { 513 | return { 514 | generatedLine: i + 1, 515 | generatedColumn: seg[0], 516 | source: seg.length === 1 ? null : `https://astexplorer.net/${decodedMap.sources[seg[1]]}`, 517 | originalLine: seg.length === 1 ? null : seg[2] + 1, 518 | originalColumn: seg.length === 1 ? null : seg[3], 519 | name: seg.length === 5 ? decodedMap.names[seg[4]] : null, 520 | } as any; 521 | }); 522 | }); 523 | 524 | function macro(map: SourceMapInput) { 525 | return () => { 526 | const tracer = new TraceMap(map); 527 | let i = 0; 528 | eachMapping(tracer, (mapping) => { 529 | assert.deepEqual(mapping, mappings[i++]); 530 | }); 531 | }; 532 | } 533 | 534 | it('decoded source map', macro(decodedMap)); 535 | it('json decoded source map', macro(JSON.stringify(decodedMap))); 536 | it('encoded source map', macro(encodedMap)); 537 | it('json encoded source map', macro(JSON.stringify(encodedMap))); 538 | }); 539 | 540 | describe('presortedDecodedMap', () => { 541 | it('propagates decoded mappings without sorting', () => { 542 | const mappings = decodedMap.mappings.map((line) => { 543 | return line.slice().reverse(); 544 | }); 545 | const reversedDecoded: DecodedSourceMap = { 546 | ...decodedMap, 547 | mappings: mappings.map((line) => line.slice()), 548 | }; 549 | 550 | const tracer = presortedDecodedMap(reversedDecoded); 551 | assert.deepEqual(decodedMappings(tracer), mappings); 552 | }); 553 | 554 | it('ignores non-sourcemap fields from output', () => { 555 | // `map` will contain a `_encoded` field equal to the encoded map's, a _decoded equal to [], 556 | // and a _decodedMemo field. This fooled the duck-type early return detection, and preserved 557 | // invalid values on the presorted tracer. 558 | // https://github.com/facebook/jest/issues/12998#issuecomment-1212426850 559 | const map = Object.assign({}, new TraceMap(encodedMap), { mappings: [] }); 560 | const tracer = presortedDecodedMap(map); 561 | 562 | assert.equal(encodedMappings(tracer), ''); 563 | }); 564 | }); 565 | 566 | describe('typescript readonly type', () => { 567 | it('decoded source map', () => { 568 | // This is a TS lint test, not a real one. 569 | 570 | const decodedMap = { 571 | version: 3 as const, 572 | sources: ['input.js'] as readonly string[], 573 | names: [] as readonly string[], 574 | mappings: [] as readonly SourceMapSegment[][], 575 | sourcesContent: [] as readonly string[], 576 | }; 577 | 578 | new TraceMap(decodedMap); 579 | }); 580 | }); 581 | }); 582 | -------------------------------------------------------------------------------- /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 | "esModuleInterop": true, 5 | "target": "es2015", 6 | "module": "es2015", 7 | "lib": ["es2015"], 8 | "strict": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "declaration": true, 12 | "allowSyntheticDefaultImports": true, 13 | "declarationDir": "dist/types", 14 | "typeRoots": ["node_modules/@types"] 15 | }, 16 | "exclude": ["dist"], 17 | "include": ["src", "test"] 18 | } 19 | --------------------------------------------------------------------------------