├── .editorconfig ├── .gitignore ├── .mocharc.cjs ├── README.md ├── esbuild.mjs ├── eslint.config.mjs ├── package.json ├── packages ├── gen-mapping │ ├── LICENSE │ ├── README.md │ ├── benchmark │ │ ├── amp.js.map │ │ ├── babel.min.js.map │ │ ├── index.mjs │ │ ├── package.json │ │ ├── preact.js.map │ │ └── react.js.map │ ├── package.json │ ├── src │ │ ├── gen-mapping.ts │ │ ├── set-array.ts │ │ ├── sourcemap-segment.ts │ │ └── types.ts │ ├── test │ │ ├── gen-mapping.test.ts │ │ └── set-array.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── remapping │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ ├── build-source-map-tree.ts │ │ ├── remapping.ts │ │ ├── source-map-tree.ts │ │ ├── source-map.ts │ │ └── types.ts │ ├── test │ │ ├── samples │ │ │ ├── null-source │ │ │ │ └── test.ts │ │ │ ├── sourceless-transform │ │ │ │ └── test.ts │ │ │ ├── transpile-concat-hires │ │ │ │ ├── babel.config.js │ │ │ │ ├── build.js │ │ │ │ ├── build.sh │ │ │ │ ├── files │ │ │ │ │ ├── bundle.js │ │ │ │ │ ├── bundle.js.map │ │ │ │ │ ├── main.js │ │ │ │ │ ├── main.js.map │ │ │ │ │ ├── main.mjs │ │ │ │ │ ├── placeholder.js │ │ │ │ │ ├── placeholder.js.map │ │ │ │ │ └── placeholder.mjs │ │ │ │ └── test.ts │ │ │ ├── transpile-concat-lowres │ │ │ │ ├── babel.config.js │ │ │ │ ├── build.js │ │ │ │ ├── build.sh │ │ │ │ ├── files │ │ │ │ │ ├── bundle.js │ │ │ │ │ ├── bundle.js.map │ │ │ │ │ ├── main.js │ │ │ │ │ ├── main.js.map │ │ │ │ │ ├── main.mjs │ │ │ │ │ ├── placeholder.js │ │ │ │ │ ├── placeholder.js.map │ │ │ │ │ └── placeholder.mjs │ │ │ │ └── test.ts │ │ │ ├── transpile-minify │ │ │ │ ├── babel.config.js │ │ │ │ ├── build.sh │ │ │ │ ├── files │ │ │ │ │ ├── helloworld.js │ │ │ │ │ ├── helloworld.js.map │ │ │ │ │ ├── helloworld.min.js │ │ │ │ │ ├── helloworld.min.js.map │ │ │ │ │ └── helloworld.mjs │ │ │ │ └── test.ts │ │ │ └── transpile-rollup │ │ │ │ ├── babel.config.js │ │ │ │ ├── build.sh │ │ │ │ ├── files │ │ │ │ ├── a.js │ │ │ │ ├── a.js.map │ │ │ │ ├── a.mjs │ │ │ │ ├── b.js │ │ │ │ ├── b.js.map │ │ │ │ ├── b.mjs │ │ │ │ ├── bundle.js │ │ │ │ ├── bundle.js.map │ │ │ │ ├── index.js │ │ │ │ ├── index.js.map │ │ │ │ └── index.mjs │ │ │ │ └── test.ts │ │ └── unit │ │ │ ├── build-source-map-tree.test.ts │ │ │ ├── remapping.test.ts │ │ │ ├── source-map-tree.test.ts │ │ │ ├── source-map.test.ts │ │ │ └── util.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── source-map │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ │ └── source-map.ts │ ├── test │ │ ├── cursory.test.ts │ │ ├── fixtures.ts │ │ ├── source-map-consumer.test.ts │ │ ├── source-map-generator.test.ts │ │ └── utils.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── sourcemap-codec │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── benchmark │ │ ├── amp.js.map │ │ ├── babel.min.js.map │ │ ├── chrome.js │ │ ├── index.js │ │ ├── package.json │ │ ├── preact.js.map │ │ ├── react.js.map │ │ └── vscode.map │ ├── package.json │ ├── src │ │ ├── scopes.ts │ │ ├── sourcemap-codec.ts │ │ ├── strings.ts │ │ └── vlq.ts │ ├── test │ │ ├── codec.test.ts │ │ └── scopes.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json └── trace-mapping │ ├── LICENSE │ ├── README.md │ ├── benchmark │ ├── amp.js.map │ ├── babel.min.js.map │ ├── chrome.mjs │ ├── index.mjs │ ├── package.json │ ├── preact.js.map │ ├── react.js.map │ └── vscode.map │ ├── package.json │ ├── 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 ├── prettier.config.js ├── tsconfig.json └── turbo.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | types 5 | coverage 6 | package-lock.json 7 | .idea 8 | .turbo 9 | *.tsbuildinfo 10 | -------------------------------------------------------------------------------- /.mocharc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | spec: ['test/**/{*.test,test}.ts'], 3 | 'node-option': ['import=tsx'], 4 | }; 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sourcemaps 2 | 3 | Monorepo for various sourcemap libraries. 4 | 5 | - [gen-mapping](./packages/gen-mapping) 6 | - [remapping](./packages/remapping) 7 | - [source-map](./packages/source-map) 8 | - [sourcemap-codec](./packages/sourcemap-codec) 9 | - [trace-mapping](./packages/trace-mapping) 10 | -------------------------------------------------------------------------------- /esbuild.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { dirname } from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import * as esbuild from 'esbuild'; 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)); 7 | const packagePath = process.cwd(); 8 | const files = process.argv.slice(2); 9 | if (files.length === 0) { 10 | throw new Error('must pass filename of entrypoints'); 11 | } 12 | 13 | const [tsconfig, packageJson] = [ 14 | readFileSync(`${__dirname}/tsconfig.json`, 'utf8'), 15 | readFileSync(`${packagePath}/package.json`, 'utf8'), 16 | ].map(JSON.parse); 17 | 18 | const external = { 19 | '@jridgewell/gen-mapping': 'genMapping', 20 | '@ampproject/remapping': 'remapping', 21 | '@jridgewell/source-map': 'sourceMap', 22 | '@jridgewell/sourcemap-codec': 'sourcemapCodec', 23 | '@jridgewell/trace-mapping': 'traceMapping', 24 | '@jridgewell/resolve-uri': 'resolveURI', 25 | }; 26 | 27 | /** @type {esbuild.Plugin} */ 28 | const externalize = { 29 | name: 'externalize', 30 | setup(build) { 31 | build.onResolve({ filter: /^[^./]/ }, ({ path }) => { 32 | if (!external[path]) { 33 | throw new Error(`unregistered external module "${path}"`); 34 | } 35 | return { path, external: true }; 36 | }); 37 | }, 38 | }; 39 | 40 | /** @type {esbuild.Plugin} */ 41 | const umd = { 42 | name: 'umd', 43 | setup(build) { 44 | const dependencies = Object.keys(packageJson.dependencies || {}).map((d) => { 45 | return `"${d}": global.${external[d]}`; 46 | }); 47 | 48 | build.initialOptions.banner = { 49 | js: ` 50 | (function (global, factory, e, m) { 51 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(require, exports, module) : 52 | typeof define === 'function' && define.amd ? define(factory) : 53 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(function(spec) { 54 | return {${dependencies.join(', ')}}[spec]; 55 | }, e = {}, m = { exports: e }), global.${external[packageJson.name]} = m.exports); 56 | })(this, (function (require, exports, module) { 57 | `.trim(), 58 | }; 59 | build.initialOptions.footer = { 60 | js: '}));', 61 | }; 62 | }, 63 | }; 64 | 65 | async function build(esm) { 66 | const build = await esbuild.build({ 67 | entryPoints: files.map((f) => `src/${f}`), 68 | outdir: 'dist', 69 | bundle: true, 70 | sourcemap: 'linked', 71 | sourcesContent: false, 72 | format: esm ? 'esm' : 'cjs', 73 | plugins: esm ? [externalize] : [externalize, umd], 74 | outExtension: esm ? { '.js': '.mjs' } : { '.js': '.umd.js' }, 75 | target: tsconfig.compilerOptions.target, 76 | }); 77 | 78 | if (build.errors.length > 0) { 79 | for (const message of build.errors) { 80 | console.error(message); 81 | } 82 | process.exit(1); 83 | } 84 | 85 | console.log(`Compiled ${esm ? 'esm' : 'cjs'}`); 86 | } 87 | 88 | build(true); 89 | build(false); 90 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from '@typescript-eslint/eslint-plugin'; 2 | import tsParser from '@typescript-eslint/parser'; 3 | import path from 'node:path'; 4 | import { fileURLToPath } from 'node:url'; 5 | import js from '@eslint/js'; 6 | import { FlatCompat } from '@eslint/eslintrc'; 7 | 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | const compat = new FlatCompat({ 11 | baseDirectory: __dirname, 12 | recommendedConfig: js.configs.recommended, 13 | allConfig: js.configs.all, 14 | }); 15 | 16 | export default [ 17 | ...compat 18 | .extends('eslint:recommended', 'plugin:@typescript-eslint/recommended') 19 | .map((config) => ({ 20 | ...config, 21 | files: ['**/*.ts'], 22 | })), 23 | { 24 | files: ['**/*.ts'], 25 | 26 | plugins: { 27 | '@typescript-eslint': typescriptEslint, 28 | }, 29 | 30 | languageOptions: { 31 | parser: tsParser, 32 | }, 33 | 34 | rules: { 35 | '@typescript-eslint/consistent-type-imports': 'error', 36 | '@typescript-eslint/no-explicit-any': 'off', 37 | '@typescript-eslint/no-non-null-assertion': 'off', 38 | 39 | '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 40 | 41 | 'no-constant-condition': 'off', 42 | 'no-unused-labels': 'off', 43 | 'prefer-rest-params': 'off', 44 | 'prefer-const': ['error', { destructuring: 'all' }], 45 | }, 46 | }, 47 | { 48 | files: ['/Users/jridgewell/src/jridgewell/sourcemaps/test/**/*.ts'], 49 | 50 | rules: { 51 | '@typescript-eslint/no-empty-function': 'off', 52 | '@typescript-eslint/no-explicit-any': 'off', 53 | }, 54 | }, 55 | ]; 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sourcemaps", 3 | "private": true, 4 | "description": "Monorepo for @jridgewell's collection of sourcemap packages", 5 | "scripts": { 6 | "benchmark": "turbo run benchmark", 7 | "build": "turbo run build", 8 | "lint": "turbo run lint", 9 | "test": "turbo run test --parallel" 10 | }, 11 | "workspaces": [ 12 | "packages/*" 13 | ], 14 | "author": "Justin Ridgewell ", 15 | "license": "MIT", 16 | "packageManager": "npm@10.0.0", 17 | "devDependencies": { 18 | "@eslint/eslintrc": "3.2.0", 19 | "@eslint/js": "9.15.0", 20 | "@types/mocha": "10.0.10", 21 | "@types/node": "22.9.3", 22 | "@types/sinon": "17.0.3", 23 | "benchmark": "2.1.4", 24 | "esbuild": "0.24.0", 25 | "eslint": "9.15.0", 26 | "mocha": "10.8.2", 27 | "npm-run-all": "4.1.5", 28 | "prettier": "3.3.3", 29 | "sinon": "19.0.2", 30 | "tsx": "4.19.2", 31 | "turbo": "2.3.1", 32 | "typescript": "5.7.2", 33 | "typescript-eslint": "8.15.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/gen-mapping/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 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 | -------------------------------------------------------------------------------- /packages/gen-mapping/README.md: -------------------------------------------------------------------------------- 1 | # @jridgewell/gen-mapping 2 | 3 | > Generate source maps 4 | 5 | `gen-mapping` allows you to generate a source map during transpilation or minification. 6 | With a source map, you're able to trace the original location in the source file, either in Chrome's 7 | DevTools or using a library like [`@jridgewell/trace-mapping`][trace-mapping]. 8 | 9 | You may already be familiar with the [`source-map`][source-map] package's `SourceMapGenerator`. This 10 | provides the same `addMapping` and `setSourceContent` API. 11 | 12 | ## Installation 13 | 14 | ```sh 15 | npm install @jridgewell/gen-mapping 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```typescript 21 | import { GenMapping, addMapping, setSourceContent, toEncodedMap, toDecodedMap } from '@jridgewell/gen-mapping'; 22 | 23 | const map = new GenMapping({ 24 | file: 'output.js', 25 | sourceRoot: 'https://example.com/', 26 | }); 27 | 28 | setSourceContent(map, 'input.js', `function foo() {}`); 29 | 30 | addMapping(map, { 31 | // Lines start at line 1, columns at column 0. 32 | generated: { line: 1, column: 0 }, 33 | source: 'input.js', 34 | original: { line: 1, column: 0 }, 35 | }); 36 | 37 | addMapping(map, { 38 | generated: { line: 1, column: 9 }, 39 | source: 'input.js', 40 | original: { line: 1, column: 9 }, 41 | name: 'foo', 42 | }); 43 | 44 | assert.deepEqual(toDecodedMap(map), { 45 | version: 3, 46 | file: 'output.js', 47 | names: ['foo'], 48 | sourceRoot: 'https://example.com/', 49 | sources: ['input.js'], 50 | sourcesContent: ['function foo() {}'], 51 | mappings: [ 52 | [ [0, 0, 0, 0], [9, 0, 0, 9, 0] ] 53 | ], 54 | }); 55 | 56 | assert.deepEqual(toEncodedMap(map), { 57 | version: 3, 58 | file: 'output.js', 59 | names: ['foo'], 60 | sourceRoot: 'https://example.com/', 61 | sources: ['input.js'], 62 | sourcesContent: ['function foo() {}'], 63 | mappings: 'AAAA,SAASA', 64 | }); 65 | ``` 66 | 67 | ### Smaller Sourcemaps 68 | 69 | Not everything needs to be added to a sourcemap, and needless markings can cause signficantly 70 | larger file sizes. `gen-mapping` exposes `maybeAddSegment`/`maybeAddMapping` APIs that will 71 | intelligently determine if this marking adds useful information. If not, the marking will be 72 | skipped. 73 | 74 | ```typescript 75 | import { maybeAddMapping } from '@jridgewell/gen-mapping'; 76 | 77 | const map = new GenMapping(); 78 | 79 | // Adding a sourceless marking at the beginning of a line isn't useful. 80 | maybeAddMapping(map, { 81 | generated: { line: 1, column: 0 }, 82 | }); 83 | 84 | // Adding a new source marking is useful. 85 | maybeAddMapping(map, { 86 | generated: { line: 1, column: 0 }, 87 | source: 'input.js', 88 | original: { line: 1, column: 0 }, 89 | }); 90 | 91 | // But adding another marking pointing to the exact same original location isn't, even if the 92 | // generated column changed. 93 | maybeAddMapping(map, { 94 | generated: { line: 1, column: 9 }, 95 | source: 'input.js', 96 | original: { line: 1, column: 0 }, 97 | }); 98 | 99 | assert.deepEqual(toEncodedMap(map), { 100 | version: 3, 101 | names: [], 102 | sources: ['input.js'], 103 | sourcesContent: [null], 104 | mappings: 'AAAA', 105 | }); 106 | ``` 107 | 108 | ## Benchmarks 109 | 110 | ``` 111 | node v18.0.0 112 | 113 | amp.js.map 114 | Memory Usage: 115 | gen-mapping: addSegment 5852872 bytes 116 | gen-mapping: addMapping 7716042 bytes 117 | source-map-js 6143250 bytes 118 | source-map-0.6.1 6124102 bytes 119 | source-map-0.8.0 6121173 bytes 120 | Smallest memory usage is gen-mapping: addSegment 121 | 122 | Adding speed: 123 | gen-mapping: addSegment x 441 ops/sec ±2.07% (90 runs sampled) 124 | gen-mapping: addMapping x 350 ops/sec ±2.40% (86 runs sampled) 125 | source-map-js: addMapping x 169 ops/sec ±2.42% (80 runs sampled) 126 | source-map-0.6.1: addMapping x 167 ops/sec ±2.56% (80 runs sampled) 127 | source-map-0.8.0: addMapping x 168 ops/sec ±2.52% (80 runs sampled) 128 | Fastest is gen-mapping: addSegment 129 | 130 | Generate speed: 131 | gen-mapping: decoded output x 150,824,370 ops/sec ±0.07% (102 runs sampled) 132 | gen-mapping: encoded output x 663 ops/sec ±0.22% (98 runs sampled) 133 | source-map-js: encoded output x 197 ops/sec ±0.45% (84 runs sampled) 134 | source-map-0.6.1: encoded output x 198 ops/sec ±0.33% (85 runs sampled) 135 | source-map-0.8.0: encoded output x 197 ops/sec ±0.06% (93 runs sampled) 136 | Fastest is gen-mapping: decoded output 137 | 138 | 139 | *** 140 | 141 | 142 | babel.min.js.map 143 | Memory Usage: 144 | gen-mapping: addSegment 37578063 bytes 145 | gen-mapping: addMapping 37212897 bytes 146 | source-map-js 47638527 bytes 147 | source-map-0.6.1 47690503 bytes 148 | source-map-0.8.0 47470188 bytes 149 | Smallest memory usage is gen-mapping: addMapping 150 | 151 | Adding speed: 152 | gen-mapping: addSegment x 31.05 ops/sec ±8.31% (43 runs sampled) 153 | gen-mapping: addMapping x 29.83 ops/sec ±7.36% (51 runs sampled) 154 | source-map-js: addMapping x 20.73 ops/sec ±6.22% (38 runs sampled) 155 | source-map-0.6.1: addMapping x 20.03 ops/sec ±10.51% (38 runs sampled) 156 | source-map-0.8.0: addMapping x 19.30 ops/sec ±8.27% (37 runs sampled) 157 | Fastest is gen-mapping: addSegment 158 | 159 | Generate speed: 160 | gen-mapping: decoded output x 381,379,234 ops/sec ±0.29% (96 runs sampled) 161 | gen-mapping: encoded output x 95.15 ops/sec ±2.98% (72 runs sampled) 162 | source-map-js: encoded output x 15.20 ops/sec ±7.41% (33 runs sampled) 163 | source-map-0.6.1: encoded output x 16.36 ops/sec ±10.46% (31 runs sampled) 164 | source-map-0.8.0: encoded output x 16.06 ops/sec ±6.45% (31 runs sampled) 165 | Fastest is gen-mapping: decoded output 166 | 167 | 168 | *** 169 | 170 | 171 | preact.js.map 172 | Memory Usage: 173 | gen-mapping: addSegment 416247 bytes 174 | gen-mapping: addMapping 419824 bytes 175 | source-map-js 1024619 bytes 176 | source-map-0.6.1 1146004 bytes 177 | source-map-0.8.0 1113250 bytes 178 | Smallest memory usage is gen-mapping: addSegment 179 | 180 | Adding speed: 181 | gen-mapping: addSegment x 13,755 ops/sec ±0.15% (98 runs sampled) 182 | gen-mapping: addMapping x 13,013 ops/sec ±0.11% (101 runs sampled) 183 | source-map-js: addMapping x 4,564 ops/sec ±0.21% (98 runs sampled) 184 | source-map-0.6.1: addMapping x 4,562 ops/sec ±0.11% (99 runs sampled) 185 | source-map-0.8.0: addMapping x 4,593 ops/sec ±0.11% (100 runs sampled) 186 | Fastest is gen-mapping: addSegment 187 | 188 | Generate speed: 189 | gen-mapping: decoded output x 379,864,020 ops/sec ±0.23% (93 runs sampled) 190 | gen-mapping: encoded output x 14,368 ops/sec ±4.07% (82 runs sampled) 191 | source-map-js: encoded output x 5,261 ops/sec ±0.21% (99 runs sampled) 192 | source-map-0.6.1: encoded output x 5,124 ops/sec ±0.58% (99 runs sampled) 193 | source-map-0.8.0: encoded output x 5,434 ops/sec ±0.33% (96 runs sampled) 194 | Fastest is gen-mapping: decoded output 195 | 196 | 197 | *** 198 | 199 | 200 | react.js.map 201 | Memory Usage: 202 | gen-mapping: addSegment 975096 bytes 203 | gen-mapping: addMapping 1102981 bytes 204 | source-map-js 2918836 bytes 205 | source-map-0.6.1 2885435 bytes 206 | source-map-0.8.0 2874336 bytes 207 | Smallest memory usage is gen-mapping: addSegment 208 | 209 | Adding speed: 210 | gen-mapping: addSegment x 4,772 ops/sec ±0.15% (100 runs sampled) 211 | gen-mapping: addMapping x 4,456 ops/sec ±0.13% (97 runs sampled) 212 | source-map-js: addMapping x 1,618 ops/sec ±0.24% (97 runs sampled) 213 | source-map-0.6.1: addMapping x 1,622 ops/sec ±0.12% (99 runs sampled) 214 | source-map-0.8.0: addMapping x 1,631 ops/sec ±0.12% (100 runs sampled) 215 | Fastest is gen-mapping: addSegment 216 | 217 | Generate speed: 218 | gen-mapping: decoded output x 379,107,695 ops/sec ±0.07% (99 runs sampled) 219 | gen-mapping: encoded output x 5,421 ops/sec ±1.60% (89 runs sampled) 220 | source-map-js: encoded output x 2,113 ops/sec ±1.81% (98 runs sampled) 221 | source-map-0.6.1: encoded output x 2,126 ops/sec ±0.10% (100 runs sampled) 222 | source-map-0.8.0: encoded output x 2,176 ops/sec ±0.39% (98 runs sampled) 223 | Fastest is gen-mapping: decoded output 224 | ``` 225 | 226 | [source-map]: https://www.npmjs.com/package/source-map 227 | [trace-mapping]: https://github.com/jridgewell/trace-mapping 228 | -------------------------------------------------------------------------------- /packages/gen-mapping/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 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/gen-mapping/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/gen-mapping", 3 | "version": "0.3.7", 4 | "description": "Generate source maps", 5 | "keywords": [ 6 | "source", 7 | "map" 8 | ], 9 | "main": "dist/gen-mapping.umd.js", 10 | "module": "dist/gen-mapping.mjs", 11 | "types": "types/gen-mapping.d.ts", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "types" 16 | ], 17 | "exports": { 18 | ".": [ 19 | { 20 | "types": "./types/gen-mapping.d.ts", 21 | "browser": "./dist/gen-mapping.umd.js", 22 | "require": "./dist/gen-mapping.umd.js", 23 | "import": "./dist/gen-mapping.mjs" 24 | }, 25 | "./dist/gen-mapping.umd.js" 26 | ], 27 | "./package.json": "./package.json" 28 | }, 29 | "scripts": { 30 | "benchmark": "run-s build:code benchmark:*", 31 | "benchmark:install": "cd benchmark && npm install", 32 | "benchmark:only": "node --expose-gc benchmark/index.js", 33 | "build": "run-s -n build:code build:types", 34 | "build:code": "node ../../esbuild.mjs gen-mapping.ts", 35 | "build:types": "tsc --project tsconfig.build.json", 36 | "test": "run-s -n test:types test:only test:format", 37 | "test:format": "prettier --check '{src,test}/**/*.ts'", 38 | "test:only": "mocha", 39 | "test:types": "eslint '{src,test}/**/*.ts'", 40 | "lint": "run-s -n lint:types lint:format", 41 | "lint:format": "npm run test:format -- --write", 42 | "lint:types": "npm run test:types -- --fix", 43 | "prepublishOnly": "npm run-s -n build test" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/jridgewell/sourcemaps.git", 48 | "directory": "packages/gen-mapping" 49 | }, 50 | "author": "Justin Ridgewell ", 51 | "license": "MIT", 52 | "dependencies": { 53 | "@jridgewell/sourcemap-codec": "^1.5.0", 54 | "@jridgewell/trace-mapping": "^0.3.24" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/gen-mapping/src/set-array.ts: -------------------------------------------------------------------------------- 1 | type Key = string | number | symbol; 2 | 3 | /** 4 | * SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the 5 | * index of the `key` in the backing array. 6 | * 7 | * This is designed to allow synchronizing a second array with the contents of the backing array, 8 | * like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`, 9 | * and there are never duplicates. 10 | */ 11 | export class SetArray { 12 | private declare _indexes: Record; 13 | declare array: readonly T[]; 14 | 15 | constructor() { 16 | this._indexes = { __proto__: null } as any; 17 | this.array = []; 18 | } 19 | } 20 | 21 | interface PublicSet { 22 | array: T[]; 23 | _indexes: SetArray['_indexes']; 24 | } 25 | 26 | /** 27 | * Typescript doesn't allow friend access to private fields, so this just casts the set into a type 28 | * with public access modifiers. 29 | */ 30 | function cast(set: SetArray): PublicSet { 31 | return set as any; 32 | } 33 | 34 | /** 35 | * Gets the index associated with `key` in the backing array, if it is already present. 36 | */ 37 | export function get(setarr: SetArray, key: T): number | undefined { 38 | return cast(setarr)._indexes[key]; 39 | } 40 | 41 | /** 42 | * Puts `key` into the backing array, if it is not already present. Returns 43 | * the index of the `key` in the backing array. 44 | */ 45 | export function put(setarr: SetArray, key: T): number { 46 | // The key may or may not be present. If it is present, it's a number. 47 | const index = get(setarr, key); 48 | if (index !== undefined) return index; 49 | 50 | const { array, _indexes: indexes } = cast(setarr); 51 | 52 | const length = array.push(key); 53 | return (indexes[key] = length - 1); 54 | } 55 | 56 | /** 57 | * Pops the last added item out of the SetArray. 58 | */ 59 | export function pop(setarr: SetArray): void { 60 | const { array, _indexes: indexes } = cast(setarr); 61 | if (array.length === 0) return; 62 | 63 | const last = array.pop()!; 64 | indexes[last] = undefined; 65 | } 66 | 67 | /** 68 | * Removes the key, if it exists in the set. 69 | */ 70 | export function remove(setarr: SetArray, key: T): void { 71 | const index = get(setarr, key); 72 | if (index === undefined) return; 73 | 74 | const { array, _indexes: indexes } = cast(setarr); 75 | for (let i = index + 1; i < array.length; i++) { 76 | const k = array[i]; 77 | array[i - 1] = k; 78 | indexes[k]!--; 79 | } 80 | indexes[key] = undefined; 81 | array.pop(); 82 | } 83 | -------------------------------------------------------------------------------- /packages/gen-mapping/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 | export type SourceMapSegment = 8 | | [GeneratedColumn] 9 | | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] 10 | | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex]; 11 | 12 | export const COLUMN = 0; 13 | export const SOURCES_INDEX = 1; 14 | export const SOURCE_LINE = 2; 15 | export const SOURCE_COLUMN = 3; 16 | export const NAMES_INDEX = 4; 17 | -------------------------------------------------------------------------------- /packages/gen-mapping/src/types.ts: -------------------------------------------------------------------------------- 1 | // import type { GeneratedRange, OriginalScope } from '@jridgewell/sourcemap-codec'; 2 | import type { SourceMapSegment } from './sourcemap-segment'; 3 | 4 | export interface SourceMapV3 { 5 | file?: string | null; 6 | names: readonly string[]; 7 | sourceRoot?: string; 8 | sources: readonly (string | null)[]; 9 | sourcesContent?: readonly (string | null)[]; 10 | version: 3; 11 | ignoreList?: readonly number[]; 12 | } 13 | 14 | export interface EncodedSourceMap extends SourceMapV3 { 15 | mappings: string; 16 | // originalScopes: string[]; 17 | // generatedRanges: string; 18 | } 19 | 20 | export interface DecodedSourceMap extends SourceMapV3 { 21 | mappings: readonly SourceMapSegment[][]; 22 | // originalScopes: readonly OriginalScope[][]; 23 | // generatedRanges: readonly GeneratedRange[]; 24 | } 25 | 26 | export interface Pos { 27 | line: number; // 1-based 28 | column: number; // 0-based 29 | } 30 | 31 | export interface OriginalPos extends Pos { 32 | source: string; 33 | } 34 | 35 | export interface BindingExpressionRange { 36 | start: Pos; 37 | expression: string; 38 | } 39 | 40 | // export type OriginalScopeInfo = [number, number, string[] | undefined]; 41 | // export type GeneratedRangeInfo = [GeneratedRange, string[] | undefined]; 42 | 43 | export type Mapping = 44 | | { 45 | generated: Pos; 46 | source: undefined; 47 | original: undefined; 48 | name: undefined; 49 | } 50 | | { 51 | generated: Pos; 52 | source: string; 53 | original: Pos; 54 | name: string; 55 | } 56 | | { 57 | generated: Pos; 58 | source: string; 59 | original: Pos; 60 | name: undefined; 61 | }; 62 | -------------------------------------------------------------------------------- /packages/gen-mapping/test/set-array.test.ts: -------------------------------------------------------------------------------- 1 | import { SetArray, get, put, pop, remove } from '../src/set-array'; 2 | import { strict as assert } from 'assert'; 3 | 4 | describe('SetArray', () => { 5 | describe('get()', () => { 6 | it('returns undefined if not present', () => { 7 | const array = new SetArray(); 8 | 9 | assert.equal(get(array, 'test'), undefined); 10 | assert.equal(get(array, 'foo'), undefined); 11 | 12 | put(array, 'test'); 13 | assert.equal(get(array, 'test'), 0); 14 | 15 | assert.equal(get(array, 'foo'), undefined); 16 | put(array, 'foo'); 17 | assert.equal(get(array, 'foo'), 1); 18 | 19 | assert.deepEqual(array.array, ['test', 'foo']); 20 | }); 21 | }); 22 | 23 | describe('put()', () => { 24 | it('puts string in if not present', () => { 25 | const array = new SetArray(); 26 | 27 | put(array, 'test'); 28 | assert.deepEqual(array.array, ['test']); 29 | put(array, 'test'); 30 | assert.deepEqual(array.array, ['test']); 31 | 32 | put(array, 'foo'); 33 | assert.deepEqual(array.array, ['test', 'foo']); 34 | put(array, 'bar'); 35 | assert.deepEqual(array.array, ['test', 'foo', 'bar']); 36 | 37 | put(array, 'bar'); 38 | assert.deepEqual(array.array, ['test', 'foo', 'bar']); 39 | put(array, 'foo'); 40 | assert.deepEqual(array.array, ['test', 'foo', 'bar']); 41 | }); 42 | 43 | it('returns index of string in array', () => { 44 | const array = new SetArray(); 45 | 46 | assert.equal(put(array, 'test'), 0); 47 | assert.equal(put(array, 'foo'), 1); 48 | assert.equal(put(array, 'bar'), 2); 49 | }); 50 | 51 | it('returns original index of string in array', () => { 52 | const array = new SetArray(); 53 | 54 | put(array, 'test'); 55 | put(array, 'foo'); 56 | put(array, 'bar'); 57 | 58 | assert.equal(put(array, 'test'), 0); 59 | assert.equal(put(array, 'foo'), 1); 60 | assert.equal(put(array, 'bar'), 2); 61 | }); 62 | 63 | it('handles empty string', () => { 64 | const array = new SetArray(); 65 | 66 | assert.equal(put(array, ''), 0); 67 | assert.equal(put(array, ''), 0); 68 | assert.deepEqual(array.array, ['']); 69 | }); 70 | }); 71 | 72 | describe('pop()', () => { 73 | it('removes last item from array', () => { 74 | const array = new SetArray(); 75 | 76 | put(array, 'test'); 77 | put(array, 'foo'); 78 | put(array, 'bar'); 79 | 80 | pop(array); 81 | assert.deepEqual(array.array, ['test', 'foo']); 82 | pop(array); 83 | assert.deepEqual(array.array, ['test']); 84 | pop(array); 85 | assert.deepEqual(array.array, []); 86 | }); 87 | 88 | it('unsets the key', () => { 89 | const array = new SetArray(); 90 | 91 | put(array, 'test'); 92 | pop(array); 93 | assert.equal(get(array, 'test'), undefined); 94 | }); 95 | 96 | it('putting afterwards writes to array at old index', () => { 97 | const array = new SetArray(); 98 | 99 | put(array, 'test'); 100 | pop(array); 101 | put(array, 'foo'); 102 | assert.deepEqual(array.array, ['foo']); 103 | }); 104 | 105 | it('getting after put gets old key ', () => { 106 | const array = new SetArray(); 107 | 108 | put(array, 'test'); 109 | pop(array); 110 | put(array, 'foo'); 111 | assert.equal(get(array, 'foo'), 0); 112 | }); 113 | }); 114 | 115 | describe('remove()', () => { 116 | it('removes item from array', () => { 117 | const array = new SetArray(); 118 | 119 | put(array, 'test'); 120 | put(array, 'foo'); 121 | put(array, 'bar'); 122 | 123 | remove(array, 'foo'); 124 | assert.deepEqual(array.array, ['test', 'bar']); 125 | remove(array, 'bar'); 126 | assert.deepEqual(array.array, ['test']); 127 | remove(array, 'test'); 128 | assert.deepEqual(array.array, []); 129 | }); 130 | 131 | it('unsets the key', () => { 132 | const array = new SetArray(); 133 | 134 | put(array, 'test'); 135 | remove(array, 'test'); 136 | assert.equal(get(array, 'test'), undefined); 137 | }); 138 | 139 | it('updates indexes of following keys', () => { 140 | const array = new SetArray(); 141 | 142 | put(array, 'test'); 143 | put(array, 'foo'); 144 | put(array, 'bar'); 145 | 146 | remove(array, 'foo'); 147 | assert.equal(get(array, 'test'), 0); 148 | assert.equal(get(array, 'bar'), 1); 149 | }); 150 | 151 | it('putting afterwards writes to array at old index', () => { 152 | const array = new SetArray(); 153 | 154 | put(array, 'test'); 155 | remove(array, 'test'); 156 | put(array, 'foo'); 157 | assert.deepEqual(array.array, ['foo']); 158 | }); 159 | 160 | it('getting after put gets old key ', () => { 161 | const array = new SetArray(); 162 | 163 | put(array, 'test'); 164 | remove(array, 'test'); 165 | put(array, 'foo'); 166 | assert.equal(get(array, 'foo'), 0); 167 | }); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /packages/gen-mapping/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/gen-mapping/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2015", 5 | "module": "es2015", 6 | "lib": ["es2015", "dom"], 7 | "strict": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "declaration": true, 11 | "allowSyntheticDefaultImports": true, 12 | "declarationDir": "dist/types", 13 | "noUnusedLocals": true, 14 | "noUnusedParameters": true, 15 | "checkJs": true 16 | }, 17 | "exclude": ["dist"], 18 | "include": ["src", "test"] 19 | } 20 | -------------------------------------------------------------------------------- /packages/remapping/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 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 | -------------------------------------------------------------------------------- /packages/remapping/README.md: -------------------------------------------------------------------------------- 1 | # @jridgewell/remapping 2 | 3 | > Remap sequential sourcemaps through transformations to point at the original source code 4 | 5 | Remapping allows you to take the sourcemaps generated through transforming your code and "remap" 6 | them to the original source locations. Think "my minified code, transformed with babel and bundled 7 | with webpack", all pointing to the correct location in your original source code. 8 | 9 | With remapping, none of your source code transformations need to be aware of the input's sourcemap, 10 | they only need to generate an output sourcemap. This greatly simplifies building custom 11 | transformations (think a find-and-replace). 12 | 13 | ## Installation 14 | 15 | ```sh 16 | npm install @jridgewell/remapping 17 | ``` 18 | 19 | ## Usage 20 | 21 | ```typescript 22 | function remapping( 23 | map: SourceMap | SourceMap[], 24 | loader: (file: string, ctx: LoaderContext) => (SourceMap | null | undefined), 25 | options?: { excludeContent: boolean, decodedMappings: boolean } 26 | ): SourceMap; 27 | 28 | // LoaderContext gives the loader the importing sourcemap, tree depth, the ability to override the 29 | // "source" location (where child sources are resolved relative to, or the location of original 30 | // source), and the ability to override the "content" of an original source for inclusion in the 31 | // output sourcemap. 32 | type LoaderContext = { 33 | readonly importer: string; 34 | readonly depth: number; 35 | source: string; 36 | content: string | null | undefined; 37 | } 38 | ``` 39 | 40 | `remapping` takes the final output sourcemap, and a `loader` function. For every source file pointer 41 | in the sourcemap, the `loader` will be called with the resolved path. If the path itself represents 42 | a transformed file (it has a sourcmap associated with it), then the `loader` should return that 43 | sourcemap. If not, the path will be treated as an original, untransformed source code. 44 | 45 | ```js 46 | // Babel transformed "helloworld.js" into "transformed.js" 47 | const transformedMap = JSON.stringify({ 48 | file: 'transformed.js', 49 | // 1st column of 2nd line of output file translates into the 1st source 50 | // file, line 3, column 2 51 | mappings: ';CAEE', 52 | sources: ['helloworld.js'], 53 | version: 3, 54 | }); 55 | 56 | // Uglify minified "transformed.js" into "transformed.min.js" 57 | const minifiedTransformedMap = JSON.stringify({ 58 | file: 'transformed.min.js', 59 | // 0th column of 1st line of output file translates into the 1st source 60 | // file, line 2, column 1. 61 | mappings: 'AACC', 62 | names: [], 63 | sources: ['transformed.js'], 64 | version: 3, 65 | }); 66 | 67 | const remapped = remapping( 68 | minifiedTransformedMap, 69 | (file, ctx) => { 70 | 71 | // The "transformed.js" file is an transformed file. 72 | if (file === 'transformed.js') { 73 | // The root importer is empty. 74 | console.assert(ctx.importer === ''); 75 | // The depth in the sourcemap tree we're currently loading. 76 | // The root `minifiedTransformedMap` is depth 0, and its source children are depth 1, etc. 77 | console.assert(ctx.depth === 1); 78 | 79 | return transformedMap; 80 | } 81 | 82 | // Loader will be called to load transformedMap's source file pointers as well. 83 | console.assert(file === 'helloworld.js'); 84 | // `transformed.js`'s sourcemap points into `helloworld.js`. 85 | console.assert(ctx.importer === 'transformed.js'); 86 | // This is a source child of `transformed`, which is a source child of `minifiedTransformedMap`. 87 | console.assert(ctx.depth === 2); 88 | return null; 89 | } 90 | ); 91 | 92 | console.log(remapped); 93 | // { 94 | // file: 'transpiled.min.js', 95 | // mappings: 'AAEE', 96 | // sources: ['helloworld.js'], 97 | // version: 3, 98 | // }; 99 | ``` 100 | 101 | In this example, `loader` will be called twice: 102 | 103 | 1. `"transformed.js"`, the first source file pointer in the `minifiedTransformedMap`. We return the 104 | associated sourcemap for it (its a transformed file, after all) so that sourcemap locations can 105 | be traced through it into the source files it represents. 106 | 2. `"helloworld.js"`, our original, unmodified source code. This file does not have a sourcemap, so 107 | we return `null`. 108 | 109 | The `remapped` sourcemap now points from `transformed.min.js` into locations in `helloworld.js`. If 110 | you were to read the `mappings`, it says "0th column of the first line output line points to the 1st 111 | column of the 2nd line of the file `helloworld.js`". 112 | 113 | ### Multiple transformations of a file 114 | 115 | As a convenience, if you have multiple single-source transformations of a file, you may pass an 116 | array of sourcemap files in the order of most-recent transformation sourcemap first. Note that this 117 | changes the `importer` and `depth` of each call to our loader. So our above example could have been 118 | written as: 119 | 120 | ```js 121 | const remapped = remapping( 122 | [minifiedTransformedMap, transformedMap], 123 | () => null 124 | ); 125 | 126 | console.log(remapped); 127 | // { 128 | // file: 'transpiled.min.js', 129 | // mappings: 'AAEE', 130 | // sources: ['helloworld.js'], 131 | // version: 3, 132 | // }; 133 | ``` 134 | 135 | ### Advanced control of the loading graph 136 | 137 | #### `source` 138 | 139 | The `source` property can overridden to any value to change the location of the current load. Eg, 140 | for an original source file, it allows us to change the location to the original source regardless 141 | of what the sourcemap source entry says. And for transformed files, it allows us to change the 142 | relative resolving location for child sources of the loaded sourcemap. 143 | 144 | ```js 145 | const remapped = remapping( 146 | minifiedTransformedMap, 147 | (file, ctx) => { 148 | 149 | if (file === 'transformed.js') { 150 | // We pretend the transformed.js file actually exists in the 'src/' directory. When the nested 151 | // source files are loaded, they will now be relative to `src/`. 152 | ctx.source = 'src/transformed.js'; 153 | return transformedMap; 154 | } 155 | 156 | console.assert(file === 'src/helloworld.js'); 157 | // We could futher change the source of this original file, eg, to be inside a nested directory 158 | // itself. This will be reflected in the remapped sourcemap. 159 | ctx.source = 'src/nested/transformed.js'; 160 | return null; 161 | } 162 | ); 163 | 164 | console.log(remapped); 165 | // { 166 | // …, 167 | // sources: ['src/nested/helloworld.js'], 168 | // }; 169 | ``` 170 | 171 | 172 | #### `content` 173 | 174 | The `content` property can be overridden when we encounter an original source file. Eg, this allows 175 | you to manually provide the source content of the original file regardless of whether the 176 | `sourcesContent` field is present in the parent sourcemap. It can also be set to `null` to remove 177 | the source content. 178 | 179 | ```js 180 | const remapped = remapping( 181 | minifiedTransformedMap, 182 | (file, ctx) => { 183 | 184 | if (file === 'transformed.js') { 185 | // transformedMap does not include a `sourcesContent` field, so usually the remapped sourcemap 186 | // would not include any `sourcesContent` values. 187 | return transformedMap; 188 | } 189 | 190 | console.assert(file === 'helloworld.js'); 191 | // We can read the file to provide the source content. 192 | ctx.content = fs.readFileSync(file, 'utf8'); 193 | return null; 194 | } 195 | ); 196 | 197 | console.log(remapped); 198 | // { 199 | // …, 200 | // sourcesContent: [ 201 | // 'console.log("Hello world!")', 202 | // ], 203 | // }; 204 | ``` 205 | 206 | ### Options 207 | 208 | #### excludeContent 209 | 210 | By default, `excludeContent` is `false`. Passing `{ excludeContent: true }` will exclude the 211 | `sourcesContent` field from the returned sourcemap. This is mainly useful when you want to reduce 212 | the size out the sourcemap. 213 | 214 | #### decodedMappings 215 | 216 | By default, `decodedMappings` is `false`. Passing `{ decodedMappings: true }` will leave the 217 | `mappings` field in a [decoded state](https://github.com/rich-harris/sourcemap-codec) instead of 218 | encoding into a VLQ string. 219 | -------------------------------------------------------------------------------- /packages/remapping/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/remapping", 3 | "version": "2.3.0", 4 | "description": "Remap sequential sourcemaps through transformations to point at the original source code", 5 | "keywords": [ 6 | "source", 7 | "map", 8 | "remap" 9 | ], 10 | "main": "dist/remapping.umd.js", 11 | "module": "dist/remapping.mjs", 12 | "types": "types/remapping.d.ts", 13 | "files": [ 14 | "dist", 15 | "src", 16 | "types" 17 | ], 18 | "exports": { 19 | ".": [ 20 | { 21 | "types": "./types/remapping.d.ts", 22 | "browser": "./dist/remapping.umd.js", 23 | "require": "./dist/remapping.umd.js", 24 | "import": "./dist/remapping.mjs" 25 | }, 26 | "./dist/remapping.umd.js" 27 | ], 28 | "./package.json": "./package.json" 29 | }, 30 | "scripts": { 31 | "benchmark": "run-s build:code benchmark:*", 32 | "benchmark:install": "cd benchmark && npm install", 33 | "benchmark:only": "node --expose-gc benchmark/index.js", 34 | "build": "run-s -n build:code build:types", 35 | "build:code": "node ../../esbuild.mjs remapping.ts", 36 | "build:types": "tsc --project tsconfig.build.json", 37 | "test": "run-s -n test:types test:only test:format", 38 | "test:format": "prettier --check '{src,test}/**/*.ts'", 39 | "test:only": "mocha", 40 | "test:types": "eslint '{src,test}/**/*.ts'", 41 | "lint": "run-s -n lint:types lint:format", 42 | "lint:format": "npm run test:format -- --write", 43 | "lint:types": "npm run test:types -- --fix", 44 | "prepublishOnly": "npm run-s -n build test" 45 | }, 46 | "repository": { 47 | "type": "git", 48 | "url": "git+https://github.com/jridgewell/sourcemaps.git", 49 | "directory": "packages/remapping" 50 | }, 51 | "author": "Justin Ridgewell ", 52 | "license": "MIT", 53 | "dependencies": { 54 | "@jridgewell/gen-mapping": "^0.3.5", 55 | "@jridgewell/trace-mapping": "^0.3.24" 56 | }, 57 | "devDependencies": { 58 | "source-map": "0.6.1" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/remapping/src/build-source-map-tree.ts: -------------------------------------------------------------------------------- 1 | import { TraceMap } from '@jridgewell/trace-mapping'; 2 | 3 | import { OriginalSource, MapSource } from './source-map-tree'; 4 | 5 | import type { Sources, MapSource as MapSourceType } from './source-map-tree'; 6 | import type { SourceMapInput, SourceMapLoader, LoaderContext } from './types'; 7 | 8 | function asArray(value: T | T[]): T[] { 9 | if (Array.isArray(value)) return value; 10 | return [value]; 11 | } 12 | 13 | /** 14 | * Recursively builds a tree structure out of sourcemap files, with each node 15 | * being either an `OriginalSource` "leaf" or a `SourceMapTree` composed of 16 | * `OriginalSource`s and `SourceMapTree`s. 17 | * 18 | * Every sourcemap is composed of a collection of source files and mappings 19 | * into locations of those source files. When we generate a `SourceMapTree` for 20 | * the sourcemap, we attempt to load each source file's own sourcemap. If it 21 | * does not have an associated sourcemap, it is considered an original, 22 | * unmodified source file. 23 | */ 24 | export default function buildSourceMapTree( 25 | input: SourceMapInput | SourceMapInput[], 26 | loader: SourceMapLoader, 27 | ): MapSourceType { 28 | const maps = asArray(input).map((m) => new TraceMap(m, '')); 29 | const map = maps.pop()!; 30 | 31 | for (let i = 0; i < maps.length; i++) { 32 | if (maps[i].sources.length > 1) { 33 | throw new Error( 34 | `Transformation map ${i} must have exactly one source file.\n` + 35 | 'Did you specify these with the most recent transformation maps first?', 36 | ); 37 | } 38 | } 39 | 40 | let tree = build(map, loader, '', 0); 41 | for (let i = maps.length - 1; i >= 0; i--) { 42 | tree = MapSource(maps[i], [tree]); 43 | } 44 | return tree; 45 | } 46 | 47 | function build( 48 | map: TraceMap, 49 | loader: SourceMapLoader, 50 | importer: string, 51 | importerDepth: number, 52 | ): MapSourceType { 53 | const { resolvedSources, sourcesContent, ignoreList } = map; 54 | 55 | const depth = importerDepth + 1; 56 | const children = resolvedSources.map((sourceFile: string | null, i: number): Sources => { 57 | // The loading context gives the loader more information about why this file is being loaded 58 | // (eg, from which importer). It also allows the loader to override the location of the loaded 59 | // sourcemap/original source, or to override the content in the sourcesContent field if it's 60 | // an unmodified source file. 61 | const ctx: LoaderContext = { 62 | importer, 63 | depth, 64 | source: sourceFile || '', 65 | content: undefined, 66 | ignore: undefined, 67 | }; 68 | 69 | // Use the provided loader callback to retrieve the file's sourcemap. 70 | // TODO: We should eventually support async loading of sourcemap files. 71 | const sourceMap = loader(ctx.source, ctx); 72 | 73 | const { source, content, ignore } = ctx; 74 | 75 | // If there is a sourcemap, then we need to recurse into it to load its source files. 76 | if (sourceMap) return build(new TraceMap(sourceMap, source), loader, source, depth); 77 | 78 | // Else, it's an unmodified source file. 79 | // The contents of this unmodified source file can be overridden via the loader context, 80 | // allowing it to be explicitly null or a string. If it remains undefined, we fall back to 81 | // the importing sourcemap's `sourcesContent` field. 82 | const sourceContent = 83 | content !== undefined ? content : sourcesContent ? sourcesContent[i] : null; 84 | const ignored = ignore !== undefined ? ignore : ignoreList ? ignoreList.includes(i) : false; 85 | return OriginalSource(source, sourceContent, ignored); 86 | }); 87 | 88 | return MapSource(map, children); 89 | } 90 | -------------------------------------------------------------------------------- /packages/remapping/src/remapping.ts: -------------------------------------------------------------------------------- 1 | import buildSourceMapTree from './build-source-map-tree'; 2 | import { traceMappings } from './source-map-tree'; 3 | import SourceMap from './source-map'; 4 | 5 | import type { SourceMapInput, SourceMapLoader, Options } from './types'; 6 | export type { 7 | SourceMapSegment, 8 | EncodedSourceMap, 9 | EncodedSourceMap as RawSourceMap, 10 | DecodedSourceMap, 11 | SourceMapInput, 12 | SourceMapLoader, 13 | LoaderContext, 14 | Options, 15 | } from './types'; 16 | export type { SourceMap }; 17 | 18 | /** 19 | * Traces through all the mappings in the root sourcemap, through the sources 20 | * (and their sourcemaps), all the way back to the original source location. 21 | * 22 | * `loader` will be called every time we encounter a source file. If it returns 23 | * a sourcemap, we will recurse into that sourcemap to continue the trace. If 24 | * it returns a falsey value, that source file is treated as an original, 25 | * unmodified source file. 26 | * 27 | * Pass `excludeContent` to exclude any self-containing source file content 28 | * from the output sourcemap. 29 | * 30 | * Pass `decodedMappings` to receive a SourceMap with decoded (instead of 31 | * VLQ encoded) mappings. 32 | */ 33 | export default function remapping( 34 | input: SourceMapInput | SourceMapInput[], 35 | loader: SourceMapLoader, 36 | options?: boolean | Options, 37 | ): SourceMap { 38 | const opts = 39 | typeof options === 'object' ? options : { excludeContent: !!options, decodedMappings: false }; 40 | const tree = buildSourceMapTree(input, loader); 41 | return new SourceMap(traceMappings(tree), opts); 42 | } 43 | -------------------------------------------------------------------------------- /packages/remapping/src/source-map-tree.ts: -------------------------------------------------------------------------------- 1 | import { GenMapping, maybeAddSegment, setIgnore, setSourceContent } from '@jridgewell/gen-mapping'; 2 | import { traceSegment, decodedMappings } from '@jridgewell/trace-mapping'; 3 | 4 | import type { TraceMap } from '@jridgewell/trace-mapping'; 5 | 6 | export type SourceMapSegmentObject = { 7 | column: number; 8 | line: number; 9 | name: string; 10 | source: string; 11 | content: string | null; 12 | ignore: boolean; 13 | }; 14 | 15 | export type OriginalSource = { 16 | map: null; 17 | sources: Sources[]; 18 | source: string; 19 | content: string | null; 20 | ignore: boolean; 21 | }; 22 | 23 | export type MapSource = { 24 | map: TraceMap; 25 | sources: Sources[]; 26 | source: string; 27 | content: null; 28 | ignore: false; 29 | }; 30 | 31 | export type Sources = OriginalSource | MapSource; 32 | 33 | const SOURCELESS_MAPPING = /* #__PURE__ */ SegmentObject('', -1, -1, '', null, false); 34 | const EMPTY_SOURCES: Sources[] = []; 35 | 36 | function SegmentObject( 37 | source: string, 38 | line: number, 39 | column: number, 40 | name: string, 41 | content: string | null, 42 | ignore: boolean, 43 | ): SourceMapSegmentObject { 44 | return { source, line, column, name, content, ignore }; 45 | } 46 | 47 | function Source( 48 | map: TraceMap, 49 | sources: Sources[], 50 | source: '', 51 | content: null, 52 | ignore: false, 53 | ): MapSource; 54 | function Source( 55 | map: null, 56 | sources: Sources[], 57 | source: string, 58 | content: string | null, 59 | ignore: boolean, 60 | ): OriginalSource; 61 | function Source( 62 | map: TraceMap | null, 63 | sources: Sources[], 64 | source: string | '', 65 | content: string | null, 66 | ignore: boolean, 67 | ): Sources { 68 | return { 69 | map, 70 | sources, 71 | source, 72 | content, 73 | ignore, 74 | } as any; 75 | } 76 | 77 | /** 78 | * MapSource represents a single sourcemap, with the ability to trace mappings into its child nodes 79 | * (which may themselves be SourceMapTrees). 80 | */ 81 | export function MapSource(map: TraceMap, sources: Sources[]): MapSource { 82 | return Source(map, sources, '', null, false); 83 | } 84 | 85 | /** 86 | * A "leaf" node in the sourcemap tree, representing an original, unmodified source file. Recursive 87 | * segment tracing ends at the `OriginalSource`. 88 | */ 89 | export function OriginalSource( 90 | source: string, 91 | content: string | null, 92 | ignore: boolean, 93 | ): OriginalSource { 94 | return Source(null, EMPTY_SOURCES, source, content, ignore); 95 | } 96 | 97 | /** 98 | * traceMappings is only called on the root level SourceMapTree, and begins the process of 99 | * resolving each mapping in terms of the original source files. 100 | */ 101 | export function traceMappings(tree: MapSource): GenMapping { 102 | // TODO: Eventually support sourceRoot, which has to be removed because the sources are already 103 | // fully resolved. We'll need to make sources relative to the sourceRoot before adding them. 104 | const gen = new GenMapping({ file: tree.map.file }); 105 | const { sources: rootSources, map } = tree; 106 | const rootNames = map.names; 107 | const rootMappings = decodedMappings(map); 108 | 109 | for (let i = 0; i < rootMappings.length; i++) { 110 | const segments = rootMappings[i]; 111 | 112 | for (let j = 0; j < segments.length; j++) { 113 | const segment = segments[j]; 114 | const genCol = segment[0]; 115 | let traced: SourceMapSegmentObject | null = SOURCELESS_MAPPING; 116 | 117 | // 1-length segments only move the current generated column, there's no source information 118 | // to gather from it. 119 | if (segment.length !== 1) { 120 | const source = rootSources[segment[1]]; 121 | traced = originalPositionFor( 122 | source, 123 | segment[2], 124 | segment[3], 125 | segment.length === 5 ? rootNames[segment[4]] : '', 126 | ); 127 | 128 | // If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a 129 | // respective segment into an original source. 130 | if (traced == null) continue; 131 | } 132 | 133 | const { column, line, name, content, source, ignore } = traced; 134 | 135 | maybeAddSegment(gen, i, genCol, source, line, column, name); 136 | if (source && content != null) setSourceContent(gen, source, content); 137 | if (ignore) setIgnore(gen, source, true); 138 | } 139 | } 140 | 141 | return gen; 142 | } 143 | 144 | /** 145 | * originalPositionFor is only called on children SourceMapTrees. It recurses down into its own 146 | * child SourceMapTrees, until we find the original source map. 147 | */ 148 | export function originalPositionFor( 149 | source: Sources, 150 | line: number, 151 | column: number, 152 | name: string, 153 | ): SourceMapSegmentObject | null { 154 | if (!source.map) { 155 | return SegmentObject(source.source, line, column, name, source.content, source.ignore); 156 | } 157 | 158 | const segment = traceSegment(source.map, line, column); 159 | 160 | // If we couldn't find a segment, then this doesn't exist in the sourcemap. 161 | if (segment == null) return null; 162 | // 1-length segments only move the current generated column, there's no source information 163 | // to gather from it. 164 | if (segment.length === 1) return SOURCELESS_MAPPING; 165 | 166 | return originalPositionFor( 167 | source.sources[segment[1]], 168 | segment[2], 169 | segment[3], 170 | segment.length === 5 ? source.map.names[segment[4]] : name, 171 | ); 172 | } 173 | -------------------------------------------------------------------------------- /packages/remapping/src/source-map.ts: -------------------------------------------------------------------------------- 1 | import { toDecodedMap, toEncodedMap } from '@jridgewell/gen-mapping'; 2 | 3 | import type { GenMapping } from '@jridgewell/gen-mapping'; 4 | import type { DecodedSourceMap, EncodedSourceMap, Options } from './types'; 5 | 6 | /** 7 | * A SourceMap v3 compatible sourcemap, which only includes fields that were 8 | * provided to it. 9 | */ 10 | export default class SourceMap { 11 | declare file?: string | null; 12 | declare mappings: EncodedSourceMap['mappings'] | DecodedSourceMap['mappings']; 13 | declare sourceRoot?: string; 14 | declare names: string[]; 15 | declare sources: (string | null)[]; 16 | declare sourcesContent?: (string | null)[]; 17 | declare version: 3; 18 | declare ignoreList: number[] | undefined; 19 | 20 | constructor(map: GenMapping, options: Options) { 21 | const out = options.decodedMappings ? toDecodedMap(map) : toEncodedMap(map); 22 | this.version = out.version; // SourceMap spec says this should be first. 23 | this.file = out.file; 24 | this.mappings = out.mappings as SourceMap['mappings']; 25 | this.names = out.names as SourceMap['names']; 26 | this.ignoreList = out.ignoreList as SourceMap['ignoreList']; 27 | this.sourceRoot = out.sourceRoot; 28 | 29 | this.sources = out.sources as SourceMap['sources']; 30 | if (!options.excludeContent) { 31 | this.sourcesContent = out.sourcesContent as SourceMap['sourcesContent']; 32 | } 33 | } 34 | 35 | toString(): string { 36 | return JSON.stringify(this); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/remapping/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { SourceMapInput } from '@jridgewell/trace-mapping'; 2 | 3 | export type { 4 | SourceMapSegment, 5 | DecodedSourceMap, 6 | EncodedSourceMap, 7 | } from '@jridgewell/trace-mapping'; 8 | 9 | export type { SourceMapInput }; 10 | 11 | export type LoaderContext = { 12 | readonly importer: string; 13 | readonly depth: number; 14 | source: string; 15 | content: string | null | undefined; 16 | ignore: boolean | undefined; 17 | }; 18 | 19 | export type SourceMapLoader = ( 20 | file: string, 21 | ctx: LoaderContext, 22 | ) => SourceMapInput | null | undefined | void; 23 | 24 | export type Options = { 25 | excludeContent?: boolean; 26 | decodedMappings?: boolean; 27 | }; 28 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/null-source/test.ts: -------------------------------------------------------------------------------- 1 | import { SourceMapConsumer, type RawSourceMap } from 'source-map'; 2 | import remapping from '../../../src/remapping'; 3 | import { assertMatchObject } from '../../unit/util'; 4 | 5 | describe('null-source segement', () => { 6 | const original: any = { 7 | version: '3', 8 | sources: ['source.ts'], 9 | names: [], 10 | mappings: 'AAAA,qC,aACA', 11 | sourcesContent: ["function say(msg) {console.log(msg)};say('hello');\nprocess.exit(1);"], 12 | }; 13 | const minified: any = { 14 | version: '3', 15 | sources: ['source.js'], 16 | names: ['say', 'msg', 'console', 'log', 'process', 'exit'], 17 | mappings: 'AAAA,SAASA,IAAIC,GAAMC,QAAQC,IAAIF,GAAMD,IAAI,SAASI,QAAQC,KAAK', 18 | }; 19 | 20 | it('minified code keeps null-source segment', () => { 21 | const remapped = remapping([minified, original], () => null); 22 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 23 | 24 | const console = consumer.originalPositionFor({ 25 | column: 20, 26 | line: 1, 27 | }); 28 | assertMatchObject(console, { 29 | column: 0, 30 | line: 1, 31 | source: 'source.ts', 32 | }); 33 | 34 | const say = consumer.originalPositionFor({ 35 | column: 38, 36 | line: 1, 37 | }); 38 | assertMatchObject(say, { 39 | column: null, 40 | line: null, 41 | source: null, 42 | }); 43 | 44 | const exit = consumer.originalPositionFor({ 45 | column: 53, 46 | line: 1, 47 | }); 48 | assertMatchObject(exit, { 49 | column: 0, 50 | line: 2, 51 | source: 'source.ts', 52 | }); 53 | }); 54 | 55 | it('null-source', () => { 56 | const remapped = remapping(original, () => null); 57 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 58 | 59 | const console = consumer.originalPositionFor({ 60 | column: 20, 61 | line: 1, 62 | }); 63 | assertMatchObject(console, { 64 | column: 0, 65 | line: 1, 66 | source: 'source.ts', 67 | }); 68 | 69 | const say = consumer.originalPositionFor({ 70 | column: 38, 71 | line: 1, 72 | }); 73 | assertMatchObject(say, { 74 | column: null, 75 | line: null, 76 | source: null, 77 | }); 78 | 79 | const exit = consumer.originalPositionFor({ 80 | column: 53, 81 | line: 1, 82 | }); 83 | assertMatchObject(exit, { 84 | column: 0, 85 | line: 2, 86 | source: 'source.ts', 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/sourceless-transform/test.ts: -------------------------------------------------------------------------------- 1 | import remapping, { type SourceMapLoader } from '../../../src/remapping'; 2 | import sinon from 'sinon'; 3 | import assert from 'node:assert/strict'; 4 | 5 | describe('source-less transform', () => { 6 | const original: any = { 7 | version: '3', 8 | sources: ['source.ts'], 9 | names: [], 10 | mappings: 'AAAA', 11 | sourcesContent: ['// hello'], 12 | }; 13 | const minified: any = { 14 | version: '3', 15 | sources: [], 16 | names: [], 17 | mappings: '', 18 | }; 19 | 20 | it('remapping with loader generates empty sourcemap', () => { 21 | let called = false; 22 | const loader = sinon.fake(() => { 23 | if (called) return null; 24 | called = true; 25 | return original; 26 | }); 27 | const remapped = remapping(minified, loader); 28 | 29 | assert.equal(loader.callCount, 0); 30 | assert.equal(remapped.sources.length, 0); 31 | assert.equal(remapped.mappings, ''); 32 | }); 33 | 34 | it('remapping with array shorthand generates empty sourcemap', () => { 35 | const loader = sinon.fake((() => null) as SourceMapLoader); 36 | const remapped = remapping([minified, original], loader); 37 | 38 | assert.equal(loader.callCount, 1); 39 | assert(loader.calledWith('source.ts', sinon.match.any)); 40 | assert.equal(remapped.sources.length, 0); 41 | assert.equal(remapped.mappings, ''); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(false); 3 | 4 | return { 5 | presets: [['@babel/preset-env', { modules: false }]], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/build.js: -------------------------------------------------------------------------------- 1 | const MagicString = require('magic-string'); 2 | const { readFileSync, writeFileSync } = require('fs'); 3 | 4 | function load(filename) { 5 | const contents = readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 6 | return new MagicString(contents, { filename }); 7 | } 8 | function save(filename, contents) { 9 | writeFileSync(`${__dirname}/files/${filename}`, contents); 10 | } 11 | 12 | const main = load('main.js'); 13 | const placeholder = load('placeholder.js'); 14 | 15 | const search = '/* PLACEHOLDER */'; 16 | const index = main.original.indexOf(search); 17 | 18 | const before = main.snip(0, index); 19 | const after = main.snip(index + search.length, main.length()); 20 | 21 | const bundle = new MagicString.Bundle(); 22 | bundle.addSource(before); 23 | bundle.addSource(placeholder); 24 | bundle.addSource(after); 25 | 26 | save('bundle.js', bundle.toString()); 27 | save('bundle.js.map', bundle.generateMap({ 28 | hires: true, 29 | includeContent: true, 30 | }).toString()); 31 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$(dirname $0)" 3 | NODE_BIN=`npm bin` 4 | NODE_MODULES=`npm prefix`/node_modules 5 | 6 | rm "$DIR"/files/*.js* 7 | 8 | if [ ! -f "$NODE_BIN/babel" ]; then 9 | npm install --no-save @babel/cli @babel/preset-env 10 | fi 11 | "$NODE_BIN/babel" "$DIR/files" --config-file "./$DIR/babel.config.js" --source-maps -d "$DIR/files" 12 | 13 | if [ ! -d "$NODE_MODULES/magic-string" ]; then 14 | npm install --no-save magic-string 15 | fi 16 | 17 | node "$DIR/build.js" 18 | npx prettier "$DIR/files/*.map" --parser json --write 19 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/bundle.js: -------------------------------------------------------------------------------- 1 | var foo = function foo() { 2 | return 'foo'; 3 | }; 4 | 5 | var bar = function bar() { 6 | return 'bar'; 7 | }; 8 | //# sourceMappingURL=placeholder.js.map 9 | 10 | 11 | 12 | var baz = function baz() { 13 | return 'baz'; 14 | }; 15 | //# sourceMappingURL=main.js.map 16 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/bundle.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": null, 4 | "sources": ["main.js", "placeholder.js"], 5 | "sourcesContent": [ 6 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar foo = function foo() {\n return 'foo';\n};\n/* PLACEHOLDER */\n\n\nvar baz = function baz() {\n return 'baz';\n};\n//# sourceMappingURL=main.js.map", 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar bar = function bar() {\n return 'bar';\n};\n//# sourceMappingURL=placeholder.js.map" 8 | ], 9 | "names": [], 10 | "mappings": "AAAA,CAAC,CAAC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACf,CAAC,CAAC;AACF;AClBA,CAAC,CAAC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AACH,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACf,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;uCAAC,vCDAtB;AACjB;AACA;AACA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACf,CAAC,CAAC;AACF,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC" 11 | } 12 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var foo = function foo() { 17 | return 'foo'; 18 | }; 19 | /* PLACEHOLDER */ 20 | 21 | 22 | var baz = function baz() { 23 | return 'baz'; 24 | }; 25 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/main.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["main.mjs"], 4 | "names": ["foo", "baz"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,IAAMA,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ;AAEA;;;AAEA,IAAMC,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst foo = () => 'foo';\n\n/* PLACEHOLDER */\n\nconst baz = () => 'baz';\n" 8 | ], 9 | "file": "main.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/main.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const foo = () => 'foo'; 18 | 19 | /* PLACEHOLDER */ 20 | 21 | const baz = () => 'baz'; 22 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/placeholder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var bar = function bar() { 17 | return 'bar'; 18 | }; 19 | //# sourceMappingURL=placeholder.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/placeholder.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["placeholder.mjs"], 4 | "names": ["bar"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,IAAMA,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst bar = () => 'bar';\n" 8 | ], 9 | "file": "placeholder.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/files/placeholder.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const bar = () => 'bar'; 18 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-hires/test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { SourceMapConsumer, type RawSourceMap } from 'source-map'; 3 | import remapping from '../../../src/remapping'; 4 | import { assertMatchObject } from '../../unit/util'; 5 | import assert from 'node:assert/strict'; 6 | import { dirname } from 'node:path'; 7 | import { fileURLToPath } from 'node:url'; 8 | 9 | const __dirname = dirname(fileURLToPath(import.meta.url)); 10 | 11 | function read(filename: string): string { 12 | return readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 13 | } 14 | 15 | describe('transpile then concatenate', () => { 16 | it('concated sections point to source files', () => { 17 | const map = read('bundle.js.map'); 18 | const remapped = remapping(map, (file) => { 19 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 20 | }); 21 | 22 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 23 | // the foo in bundle.js 24 | for (let j = 10; j <= 12; j++) { 25 | const foo = consumer.originalPositionFor({ 26 | column: j, 27 | line: 17, 28 | }); 29 | assertMatchObject(foo, { 30 | column: 18, 31 | line: 17, 32 | source: 'main.mjs', 33 | }); 34 | } 35 | 36 | // the bar in bundle.js 37 | for (let j = 10; j <= 12; j++) { 38 | const bar = consumer.originalPositionFor({ 39 | column: j, 40 | line: 36, 41 | }); 42 | assertMatchObject(bar, { 43 | column: 18, 44 | line: 17, 45 | source: 'placeholder.mjs', 46 | }); 47 | } 48 | 49 | //the baz in bundle.js 50 | for (let j = 10; j <= 12; j++) { 51 | const baz = consumer.originalPositionFor({ 52 | column: j, 53 | line: 43, 54 | }); 55 | assertMatchObject(baz, { 56 | column: 18, 57 | line: 21, 58 | source: 'main.mjs', 59 | }); 60 | } 61 | }); 62 | 63 | it('inherits sourcesContent of original sources', () => { 64 | const map = read('bundle.js.map'); 65 | const remapped = remapping(map, (file) => { 66 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 67 | }); 68 | 69 | assert.deepEqual(remapped.sourcesContent, [read('main.mjs'), read('placeholder.mjs')]); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(false); 3 | 4 | return { 5 | presets: [['@babel/preset-env', { modules: false }]], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/build.js: -------------------------------------------------------------------------------- 1 | const MagicString = require('magic-string'); 2 | const parser = require('@babel/parser'); 3 | const { default: traverse } = require('@babel/traverse'); 4 | const { readFileSync, writeFileSync } = require('fs'); 5 | 6 | function load(filename) { 7 | const contents = readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 8 | const s = new MagicString(contents, { filename }); 9 | const ast = parser.parse(contents); 10 | traverse.cheap(ast, (node) => { 11 | s.addSourcemapLocation(node.start); 12 | s.addSourcemapLocation(node.end); 13 | }); 14 | return s; 15 | } 16 | function save(filename, contents) { 17 | writeFileSync(`${__dirname}/files/${filename}`, contents); 18 | } 19 | 20 | const main = load('main.js'); 21 | const placeholder = load('placeholder.js'); 22 | 23 | const search = '/* PLACEHOLDER */'; 24 | const index = main.original.indexOf(search); 25 | 26 | const before = main.snip(0, index); 27 | const after = main.snip(index + search.length, main.length()); 28 | 29 | const bundle = new MagicString.Bundle(); 30 | bundle.addSource(before); 31 | bundle.addSource(placeholder); 32 | bundle.addSource(after); 33 | 34 | save('bundle.js', bundle.toString()); 35 | save('bundle.js.map', bundle.generateMap({ 36 | hires: false, 37 | includeContent: true, 38 | }).toString()); 39 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$(dirname $0)" 3 | NODE_BIN=`npm bin` 4 | NODE_MODULES=`npm prefix`/node_modules 5 | 6 | rm "$DIR"/files/*.js* 7 | 8 | if [ ! -f "$NODE_BIN/babel" ]; then 9 | npm install --no-save @babel/cli @babel/preset-env 10 | fi 11 | "$NODE_BIN/babel" "$DIR/files" --config-file "./$DIR/babel.config.js" --source-maps -d "$DIR/files" 12 | 13 | if [ ! -d "$NODE_MODULES/magic-string" ]; then 14 | npm install --no-save magic-string 15 | fi 16 | 17 | node "$DIR/build.js" 18 | npx prettier "$DIR/files/*.map" --parser json --write 19 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/bundle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var foo = function foo() { 17 | return 'foo'; 18 | }; 19 | 20 | /** 21 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 22 | * 23 | * Licensed under the Apache License, Version 2.0 (the "License"); 24 | * you may not use this file except in compliance with the License. 25 | * You may obtain a copy of the License at 26 | * 27 | * http://www.apache.org/licenses/LICENSE-2.0 28 | * 29 | * Unless required by applicable law or agreed to in writing, software 30 | * distributed under the License is distributed on an "AS IS" BASIS, 31 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | * See the License for the specific language governing permissions and 33 | * limitations under the License. 34 | */ 35 | var bar = function bar() { 36 | return 'bar'; 37 | }; 38 | //# sourceMappingURL=placeholder.js.map 39 | 40 | 41 | 42 | var baz = function baz() { 43 | return 'baz'; 44 | }; 45 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/bundle.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": null, 4 | "sources": ["main.js", "placeholder.js"], 5 | "sourcesContent": [ 6 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar foo = function foo() {\n return 'foo';\n};\n/* PLACEHOLDER */\n\n\nvar baz = function baz() {\n return 'baz';\n};\n//# sourceMappingURL=main.js.map", 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar bar = function bar() {\n return 'bar';\n};\n//# sourceMappingURL=placeholder.js.map" 8 | ], 9 | "names": [], 10 | "mappings": "AAAA;;;;;;;;;;;;;;;AAeA,IAAI,GAAG,GAAG,SAAS,GAAG,GAAG;EACvB,OAAO,KAAK,CAAC;CACd,CAAC;AACF;AClBA;;;;;;;;;;;;;;;AAeA,IAAI,GAAG,GAAG,SAAS,GAAG,GAAG;EACvB,OAAO,KAAK,CAAC;CACd,CAAC;;uCACqC,vCDAtB;;;AAGjB,IAAI,GAAG,GAAG,SAAS,GAAG,GAAG;EACvB,OAAO,KAAK,CAAC;CACd,CAAC;" 11 | } 12 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var foo = function foo() { 17 | return 'foo'; 18 | }; 19 | /* PLACEHOLDER */ 20 | 21 | 22 | var baz = function baz() { 23 | return 'baz'; 24 | }; 25 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/main.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["main.mjs"], 4 | "names": ["foo", "baz"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,IAAMA,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ;AACA;;;AACA,IAAMC,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst foo = () => 'foo';\n/* PLACEHOLDER */\nconst baz = () => 'baz';\n" 8 | ], 9 | "file": "main.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/main.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const foo = () => 'foo'; 18 | /* PLACEHOLDER */ 19 | const baz = () => 'baz'; 20 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/placeholder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var bar = function bar() { 17 | return 'bar'; 18 | }; 19 | //# sourceMappingURL=placeholder.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/placeholder.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["placeholder.mjs"], 4 | "names": ["bar"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,IAAMA,GAAG,GAAG,SAANA,GAAM;AAAA,SAAM,KAAN;AAAA,CAAZ", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst bar = () => 'bar';\n" 8 | ], 9 | "file": "placeholder.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/files/placeholder.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const bar = () => 'bar'; 18 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-concat-lowres/test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { SourceMapConsumer, type RawSourceMap } from 'source-map'; 3 | import remapping from '../../../src/remapping'; 4 | import { assertMatchObject } from '../../unit/util'; 5 | import assert from 'node:assert/strict'; 6 | import { dirname } from 'node:path'; 7 | import { fileURLToPath } from 'node:url'; 8 | 9 | const __dirname = dirname(fileURLToPath(import.meta.url)); 10 | 11 | function read(filename: string): string { 12 | return readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 13 | } 14 | 15 | describe('transpile then concatenate', () => { 16 | it('concated sections point to source files', () => { 17 | const map = read('bundle.js.map'); 18 | const remapped = remapping(map, (file) => { 19 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 20 | }); 21 | 22 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 23 | 24 | const foo = consumer.originalPositionFor({ 25 | column: 11, 26 | line: 17, 27 | }); 28 | assertMatchObject(foo, { 29 | column: 18, 30 | line: 17, 31 | source: 'main.mjs', 32 | }); 33 | 34 | const bar = consumer.originalPositionFor({ 35 | column: 11, 36 | line: 36, 37 | }); 38 | assertMatchObject(bar, { 39 | column: 18, 40 | line: 17, 41 | source: 'placeholder.mjs', 42 | }); 43 | 44 | const baz = consumer.originalPositionFor({ 45 | column: 11, 46 | line: 43, 47 | }); 48 | assertMatchObject(baz, { 49 | column: 18, 50 | line: 19, 51 | source: 'main.mjs', 52 | }); 53 | }); 54 | 55 | it('inherits sourcesContent of original sources', () => { 56 | const map = read('bundle.js.map'); 57 | const remapped = remapping(map, (file) => { 58 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 59 | }); 60 | 61 | assert.deepEqual(remapped.sourcesContent, [read('main.mjs'), read('placeholder.mjs')]); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(false); 3 | 4 | return { 5 | presets: [['@babel/preset-env', { modules: false }]], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR=`dirname $0` 3 | NODE_BIN=`npm bin` 4 | 5 | rm "$DIR"/files/*.js* 6 | 7 | if [ ! -f "$NODE_BIN/babel" ]; then 8 | npm install --no-save @babel/cli @babel/preset-env 9 | fi 10 | "$NODE_BIN/babel" "$DIR/files" --config-file "./$DIR/babel.config.js" --source-maps -d "$DIR/files" 11 | 12 | npx terser "$DIR/files/helloworld.js" -c --source-map "base='$DIR/files',includeSources" --comments all -o "$DIR/files/helloworld.min.js" 13 | npx prettier "$DIR/files/*.map" --parser json --write 14 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/files/helloworld.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | self.AMP_CONFIG = {}; 17 | 18 | var greet = function greet() { 19 | return alert("hello"); 20 | }; 21 | 22 | greet(); 23 | //# sourceMappingURL=helloworld.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/files/helloworld.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["helloworld.mjs"], 4 | "names": ["self", "AMP_CONFIG", "greet", "alert"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBAA,IAAI,CAACC,UAAL,GAAkB,EAAlB;;AAEA,IAAMC,KAAK,GAAG,SAARA,KAAQ;AAAA,SAAMC,KAAK,SAAX;AAAA,CAAd;;AAEAD,KAAK", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nself.AMP_CONFIG = {};\n\nconst greet = () => alert(`hello`);\n\ngreet();" 8 | ], 9 | "file": "helloworld.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/files/helloworld.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | self.AMP_CONFIG={};var greet=function(){return alert("hello")};greet(); 17 | //# sourceMappingURL=helloworld.js.map -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/files/helloworld.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["helloworld.js"], 4 | "names": ["self", "AMP_CONFIG", "greet", "alert"], 5 | "mappings": ";;;;;;;;;;;;;;;AAeAA,KAAKC,WAAa,GAElB,IAAIC,MAAQ,WACV,OAAOC,MAAM,UAGfD", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nself.AMP_CONFIG = {};\n\nvar greet = function greet() {\n return alert(\"hello\");\n};\n\ngreet();\n//# sourceMappingURL=helloworld.js.map" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/files/helloworld.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | self.AMP_CONFIG = {}; 18 | 19 | const greet = () => alert(`hello`); 20 | 21 | greet(); -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-minify/test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { SourceMapConsumer, type RawSourceMap } from 'source-map'; 3 | import remapping from '../../../src/remapping'; 4 | import assert from 'node:assert/strict'; 5 | import { dirname } from 'node:path'; 6 | import { fileURLToPath } from 'node:url'; 7 | 8 | const __dirname = dirname(fileURLToPath(import.meta.url)); 9 | 10 | function read(filename: string): string { 11 | return readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 12 | } 13 | 14 | describe('transpile then minify', () => { 15 | it('minify a transpiled source map', () => { 16 | const map = read('helloworld.min.js.map'); 17 | const remapped = remapping(map, (file) => { 18 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 19 | }); 20 | 21 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 22 | const alert = consumer.originalPositionFor({ 23 | column: 47, 24 | line: 16, 25 | }); 26 | assert.deepEqual(alert, { 27 | column: 20, 28 | line: 19, 29 | name: 'alert', 30 | source: 'helloworld.mjs', 31 | }); 32 | }); 33 | 34 | it('inherits sourcesContent of original source', () => { 35 | const map = read('helloworld.min.js.map'); 36 | const remapped = remapping(map, (file) => { 37 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 38 | }); 39 | 40 | assert.deepEqual(remapped.sourcesContent, [read('helloworld.mjs')]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(false); 3 | 4 | return { 5 | presets: [['@babel/preset-env', { modules: false }]], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DIR="$(dirname $0)" 3 | NODE_BIN=`npm bin` 4 | 5 | rm "$DIR"/files/*.js* 6 | 7 | if [ ! -f "$NODE_BIN/babel" ]; then 8 | npm install --no-save @babel/cli @babel/preset-env 9 | fi 10 | "$NODE_BIN/babel" "$DIR/files" --config-file "./$DIR/babel.config.js" --source-maps -d "$DIR/files" 11 | 12 | # Strip the sourceMappingURL to prevent rollup from auto collapsing sourcemaps 13 | for f in $DIR/files/*.js; do 14 | sed '$d' $f > $f.tmp 15 | mv $f.tmp $f 16 | done 17 | 18 | npx rollup -i "$DIR/files/index.js" -f cjs -o "$DIR/files/bundle.js" --sourcemap 19 | npx prettier "$DIR/files/*.map" --parser json --write 20 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/a.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export default (function () { 17 | return 'a'; 18 | }); 19 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/a.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["a.mjs"], 4 | "names": [], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,gBAAe;AAAA,SAAM,GAAN;AAAA,CAAf", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport default () => 'a';" 8 | ], 9 | "file": "a.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/a.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export default () => 'a'; -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/b.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export default (function () { 17 | return 'b'; 18 | }); 19 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/b.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["b.mjs"], 4 | "names": [], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,gBAAe;AAAA,SAAM,GAAN;AAAA,CAAf", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport default () => 'b';" 8 | ], 9 | "file": "b.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/b.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export default () => 'b'; -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/bundle.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { value: true }); 4 | 5 | /** 6 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | var a = (function () { 21 | return 'a'; 22 | }); 23 | 24 | /** 25 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 26 | * 27 | * Licensed under the Apache License, Version 2.0 (the "License"); 28 | * you may not use this file except in compliance with the License. 29 | * You may obtain a copy of the License at 30 | * 31 | * http://www.apache.org/licenses/LICENSE-2.0 32 | * 33 | * Unless required by applicable law or agreed to in writing, software 34 | * distributed under the License is distributed on an "AS IS" BASIS, 35 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 36 | * See the License for the specific language governing permissions and 37 | * limitations under the License. 38 | */ 39 | var b = (function () { 40 | return 'b'; 41 | }); 42 | 43 | exports.a = a; 44 | exports.b = b; 45 | //# sourceMappingURL=bundle.js.map 46 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/bundle.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "file": "bundle.js", 4 | "sources": ["a.js", "b.js"], 5 | "sourcesContent": [ 6 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport default (function () {\n return 'a';\n});\n", 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport default (function () {\n return 'b';\n});\n" 8 | ], 9 | "names": [], 10 | "mappings": ";;;;AAAA;;;;;;;;;;;;;;;AAeA,QAAe,CAAC,YAAY;EAC1B,OAAO,GAAG,CAAC;CACZ,EAAE;;ACjBH;;;;;;;;;;;;;;;AAeA,QAAe,CAAC,YAAY;EAC1B,OAAO,GAAG,CAAC;CACZ,EAAE;;;;;" 11 | } 12 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | export { default as a } from './a.js'; 17 | export { default as b } from './b.js'; 18 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/index.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sources": ["index.mjs"], 4 | "names": ["default", "a", "b"], 5 | "mappings": "AAAA;;;;;;;;;;;;;;;AAgBA,SAAQA,OAAO,IAAIC,CAAnB,QAA2B,QAA3B;AACA,SAAQD,OAAO,IAAIE,CAAnB,QAA2B,QAA3B", 6 | "sourcesContent": [ 7 | "/**\n * Copyright 2019 The AMP HTML Authors. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport {default as a} from './a.js';\nexport {default as b} from './b.js';" 8 | ], 9 | "file": "index.js" 10 | } 11 | -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/files/index.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 The AMP HTML Authors. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export {default as a} from './a.js'; 18 | export {default as b} from './b.js'; -------------------------------------------------------------------------------- /packages/remapping/test/samples/transpile-rollup/test.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { SourceMapConsumer, type RawSourceMap } from 'source-map'; 3 | import remapping from '../../../src/remapping'; 4 | import { assertMatchObject } from '../../unit/util'; 5 | import assert from 'node:assert/strict'; 6 | import { dirname } from 'node:path'; 7 | import { fileURLToPath } from 'node:url'; 8 | 9 | const __dirname = dirname(fileURLToPath(import.meta.url)); 10 | 11 | function read(filename: string): string { 12 | return readFileSync(`${__dirname}/files/${filename}`, 'utf8'); 13 | } 14 | 15 | describe('transpile then concatenate', () => { 16 | it('concated sections point to source files', () => { 17 | const map = read('bundle.js.map'); 18 | const remapped = remapping(map, (file) => { 19 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 20 | }); 21 | 22 | const consumer = new SourceMapConsumer(remapped as unknown as RawSourceMap); 23 | 24 | const a = consumer.originalPositionFor({ 25 | column: 11, 26 | line: 21, 27 | }); 28 | assertMatchObject(a, { 29 | column: 21, 30 | line: 17, 31 | source: 'a.mjs', 32 | }); 33 | 34 | const b = consumer.originalPositionFor({ 35 | column: 11, 36 | line: 40, 37 | }); 38 | assertMatchObject(b, { 39 | column: 21, 40 | line: 17, 41 | source: 'b.mjs', 42 | }); 43 | }); 44 | 45 | it('inherits sourcesContent of original sources', () => { 46 | const map = read('bundle.js.map'); 47 | const remapped = remapping(map, (file) => { 48 | return file.endsWith('.mjs') ? null : read(`${file}.map`); 49 | }); 50 | 51 | assert.deepEqual(remapped.sourcesContent, [read('a.mjs'), read('b.mjs')]); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /packages/remapping/test/unit/remapping.test.ts: -------------------------------------------------------------------------------- 1 | import remapping from '../../src/remapping'; 2 | import { type EncodedSourceMap } from '../../src/types'; 3 | import assert from 'node:assert/strict'; 4 | 5 | function stripUndefined(value: object) { 6 | return JSON.parse(JSON.stringify(value)); 7 | } 8 | 9 | describe('remapping', () => { 10 | const rawMap: EncodedSourceMap = { 11 | file: 'transpiled.min.js', 12 | // 0th column of 1st line of output file translates into the 1st source 13 | // file, line 2, column 1, using 1st name. 14 | mappings: 'AACCA', 15 | names: ['add'], 16 | sources: ['transpiled.js'], 17 | sourcesContent: ['1+1'], 18 | version: 3, 19 | ignoreList: [], 20 | }; 21 | const transpiledMap: EncodedSourceMap = { 22 | // 1st column of 2nd line of output file translates into the 1st source 23 | // file, line 3, column 2 24 | mappings: ';CAEE', 25 | names: [], 26 | sources: ['helloworld.js'], 27 | sourcesContent: ['\n\n 1 + 1;'], 28 | version: 3, 29 | ignoreList: [], 30 | }; 31 | const translatedMap: EncodedSourceMap = { 32 | file: 'transpiled.min.js', 33 | // 0th column of 1st line of output file translates into the 1st source 34 | // file, line 3, column 2, using first name 35 | mappings: 'AAEEA', 36 | names: ['add'], 37 | // TODO: support sourceRoot 38 | // sourceRoot: '', 39 | sources: ['helloworld.js'], 40 | sourcesContent: ['\n\n 1 + 1;'], 41 | version: 3, 42 | ignoreList: [], 43 | }; 44 | 45 | it('does not alter a lone sourcemap', () => { 46 | const map = remapping(rawMap, () => null); 47 | assert.deepEqual(stripUndefined(map), rawMap); 48 | }); 49 | 50 | it('traces SourceMapSegments through child sourcemaps', () => { 51 | const map = remapping(rawMap, (name: string) => { 52 | if (name === 'transpiled.js') { 53 | return transpiledMap; 54 | } 55 | }); 56 | 57 | assert.deepEqual(stripUndefined(map), translatedMap); 58 | }); 59 | 60 | it('traces transformations through sourcemap', () => { 61 | const maps = [rawMap, transpiledMap]; 62 | const map = remapping(maps, () => null); 63 | 64 | assert.deepEqual(stripUndefined(map), translatedMap); 65 | }); 66 | 67 | it('resolves sourcemaps realtive to sourceRoot', () => { 68 | const sourceRoot = 'foo/'; 69 | const map = remapping( 70 | { 71 | ...rawMap, 72 | sourceRoot, 73 | }, 74 | (name: string) => { 75 | if (name.endsWith('transpiled.js')) { 76 | return transpiledMap; 77 | } 78 | }, 79 | ); 80 | 81 | assert.deepEqual(stripUndefined(map), { 82 | ...translatedMap, 83 | // TODO: support sourceRoot 84 | // sourceRoot, 85 | sources: ['foo/helloworld.js'], 86 | }); 87 | }); 88 | 89 | it('resolves sourcemaps realtive to absolute sourceRoot', () => { 90 | const sourceRoot = 'https://foo.com/'; 91 | const map = remapping( 92 | { 93 | ...rawMap, 94 | sourceRoot, 95 | }, 96 | (name: string) => { 97 | if (name.endsWith('transpiled.js')) { 98 | return transpiledMap; 99 | } 100 | }, 101 | ); 102 | 103 | assert.deepEqual(stripUndefined(map), { 104 | ...translatedMap, 105 | // TODO: support sourceRoot 106 | // sourceRoot, 107 | sources: [`${sourceRoot}helloworld.js`], 108 | }); 109 | }); 110 | 111 | it('includes null sourceContent if sourcemap has no sourcesContent', () => { 112 | const map = remapping(rawMap, (name: string) => { 113 | if (name === 'transpiled.js') { 114 | return { 115 | ...transpiledMap, 116 | sourcesContent: undefined, 117 | }; 118 | } 119 | }); 120 | 121 | assert.deepEqual(map.sourcesContent, [null]); 122 | }); 123 | 124 | it('excludes null sourceContent if sourcemap is not self-containing', () => { 125 | const map = remapping(rawMap, (name: string) => { 126 | if (name === 'transpiled.js') { 127 | return { 128 | ...transpiledMap, 129 | sourcesContent: [null], 130 | }; 131 | } 132 | }); 133 | 134 | assert.deepEqual(map.sourcesContent, [null]); 135 | }); 136 | 137 | it('ignores if original source is ignored', () => { 138 | const map = remapping(rawMap, (name: string) => { 139 | if (name === 'transpiled.js') { 140 | return { 141 | ...transpiledMap, 142 | ignoreList: [0], 143 | }; 144 | } 145 | }); 146 | 147 | assert.deepEqual(map.ignoreList, [0]); 148 | }); 149 | 150 | it('unignores if sourcemap has no ignoreList', () => { 151 | const map = remapping(rawMap, (name: string) => { 152 | if (name === 'transpiled.js') { 153 | return { 154 | ...transpiledMap, 155 | ignoreList: undefined, 156 | }; 157 | } 158 | }); 159 | 160 | assert.deepEqual(map.ignoreList, []); 161 | }); 162 | 163 | it('unignores if sourcemap unignores original source', () => { 164 | const map = remapping(rawMap, (name: string) => { 165 | if (name === 'transpiled.js') { 166 | return { 167 | ...transpiledMap, 168 | ignoreList: [], 169 | }; 170 | } 171 | }); 172 | 173 | assert.deepEqual(map.ignoreList, []); 174 | }); 175 | 176 | describe('boolean options', () => { 177 | it('excludes sourcesContent if `excludeContent` is set', () => { 178 | const map = remapping( 179 | rawMap, 180 | (name: string) => { 181 | if (name === 'transpiled.js') { 182 | return transpiledMap; 183 | } 184 | }, 185 | true, 186 | ); 187 | 188 | assert(!('sourcesContent' in map)); 189 | }); 190 | }); 191 | 192 | describe('options bag', () => { 193 | it('excludes sourcesContent if `excludeContent` is set', () => { 194 | const map = remapping( 195 | rawMap, 196 | (name: string) => { 197 | if (name === 'transpiled.js') { 198 | return transpiledMap; 199 | } 200 | }, 201 | { excludeContent: true }, 202 | ); 203 | 204 | assert(!('sourcesContent' in map)); 205 | }); 206 | 207 | it('returns decoded sourcemap if `decodedMappings` is set', () => { 208 | const map = remapping( 209 | rawMap, 210 | (name: string) => { 211 | if (name === 'transpiled.js') { 212 | return transpiledMap; 213 | } 214 | }, 215 | { decodedMappings: true }, 216 | ); 217 | 218 | assert.deepEqual(map.mappings, [[[0, 0, 2, 2, 0]]]); 219 | }); 220 | }); 221 | }); 222 | -------------------------------------------------------------------------------- /packages/remapping/test/unit/source-map.test.ts: -------------------------------------------------------------------------------- 1 | import { GenMapping, addSegment, setIgnore, setSourceContent } from '@jridgewell/gen-mapping'; 2 | import SourceMap from '../../src/source-map'; 3 | import assert from 'node:assert/strict'; 4 | 5 | describe('SourceMap', () => { 6 | const opts = { 7 | excludeContent: false, 8 | decodedMappings: false, 9 | }; 10 | 11 | it('it is a compliant, v3 sourcemap', () => { 12 | const traced = new GenMapping(); 13 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 14 | 15 | const map = new SourceMap(traced, opts); 16 | assert.deepEqual(map.mappings, 'AAAA'); 17 | assert.deepEqual(map.names, []); 18 | assert.deepEqual(map.sources, ['file.js']); 19 | assert.deepEqual(map.version, 3); 20 | }); 21 | 22 | it('it can include a file', () => { 23 | const file = 'foobar.js'; 24 | const traced = new GenMapping({ file }); 25 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 26 | 27 | const map = new SourceMap(traced, opts); 28 | assert.deepEqual(map.file, file); 29 | }); 30 | 31 | // TODO: support sourceRoot 32 | it.skip('it can include a sourceRoot', () => { 33 | const sourceRoot = 'https://foo.com/'; 34 | const traced = new GenMapping({ sourceRoot }); 35 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 36 | 37 | const map = new SourceMap(traced, opts); 38 | assert.deepEqual(map.sourceRoot, sourceRoot); 39 | }); 40 | 41 | it('it can include a sourcesContent', () => { 42 | const content = '1 + 1'; 43 | const traced = new GenMapping(); 44 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 45 | setSourceContent(traced, 'file.js', content); 46 | 47 | const map = new SourceMap(traced, opts); 48 | assert.deepEqual(map.sourcesContent, [content]); 49 | }); 50 | 51 | it('sourcesContent can be manually excluded', () => { 52 | const content = '1 + 1'; 53 | const traced = new GenMapping(); 54 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 55 | setSourceContent(traced, 'file.js', content); 56 | 57 | const map = new SourceMap(traced, { ...opts, excludeContent: true }); 58 | assert(!('sourcesContent' in map)); 59 | }); 60 | 61 | it('it can include ignoreList', () => { 62 | const traced = new GenMapping(); 63 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 64 | setIgnore(traced, 'file.js'); 65 | 66 | const map = new SourceMap(traced, opts); 67 | assert.deepEqual(map.ignoreList, [0]); 68 | }); 69 | 70 | it('mappings can be decoded', () => { 71 | const traced = new GenMapping(); 72 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 73 | 74 | const map = new SourceMap(traced, { ...opts, decodedMappings: true }); 75 | assert.deepEqual(map.mappings, [[[0, 0, 0, 0]]]); 76 | }); 77 | 78 | describe('toString()', () => { 79 | it('returns the sourcemap in JSON', () => { 80 | const traced = new GenMapping(); 81 | addSegment(traced, 0, 0, 'file.js', 0, 0, ''); 82 | 83 | const map = new SourceMap(traced, opts); 84 | assert.deepEqual(JSON.parse(map.toString()), JSON.parse(JSON.stringify(map))); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /packages/remapping/test/unit/util.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict'; 2 | 3 | function match(received: object, expected: object, path: string[]) { 4 | for (const key in expected) { 5 | const value = (expected as any)[key]; 6 | if (value && typeof value === 'object') { 7 | match((received as any)[key], value, path.concat(key)); 8 | } else { 9 | assert.equal((received as any)[key], value, path.join('.')); 10 | } 11 | } 12 | } 13 | 14 | export function assertMatchObject(received: object | null, expected: object) { 15 | if (received === null) assert.equal(received, expected); 16 | return match(received as object, expected, []); 17 | } 18 | -------------------------------------------------------------------------------- /packages/remapping/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/remapping/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "types", 5 | "outDir": "dist" 6 | }, 7 | "include": ["src", "test"], 8 | "exclude": ["dist"] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /packages/source-map/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 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 | -------------------------------------------------------------------------------- /packages/source-map/README.md: -------------------------------------------------------------------------------- 1 | # @jridgewell/source-map 2 | 3 | > Packages `@jridgewell/trace-mapping` and `@jridgewell/gen-mapping` into the familiar source-map API 4 | 5 | This isn't the full API, but it's the core functionality. This wraps 6 | [@jridgewell/trace-mapping][trace-mapping] and [@jridgewell/gen-mapping][gen-mapping] 7 | implementations. 8 | 9 | ## Installation 10 | 11 | ```sh 12 | npm install @jridgewell/source-map 13 | ``` 14 | 15 | ## Usage 16 | 17 | TODO 18 | 19 | ### SourceMapConsumer 20 | 21 | ```typescript 22 | import { SourceMapConsumer } from '@jridgewell/source-map'; 23 | const smc = new SourceMapConsumer({ 24 | version: 3, 25 | names: ['foo'], 26 | sources: ['input.js'], 27 | mappings: 'AAAAA', 28 | }); 29 | ``` 30 | 31 | #### SourceMapConsumer.fromSourceMap(mapGenerator[, mapUrl]) 32 | 33 | Transforms a `SourceMapGenerator` into a `SourceMapConsumer`. 34 | 35 | ```typescript 36 | const smg = new SourceMapGenerator(); 37 | 38 | const smc = SourceMapConsumer.fromSourceMap(map); 39 | smc.originalPositionFor({ line: 1, column: 0 }); 40 | ``` 41 | 42 | #### SourceMapConsumer.prototype.originalPositionFor(generatedPosition) 43 | 44 | ```typescript 45 | const smc = new SourceMapConsumer(map); 46 | smc.originalPositionFor({ line: 1, column: 0 }); 47 | ``` 48 | 49 | #### SourceMapConsumer.prototype.mappings 50 | 51 | ```typescript 52 | const smc = new SourceMapConsumer(map); 53 | smc.mappings; // AAAA 54 | ``` 55 | 56 | #### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition) 57 | 58 | ```typescript 59 | const smc = new SourceMapConsumer(map); 60 | smc.allGeneratedpositionsfor({ line: 1, column: 5, source: "baz.ts" }); 61 | // [ 62 | // { line: 2, column: 8 } 63 | // ] 64 | ``` 65 | 66 | #### SourceMapConsumer.prototype.eachMapping(callback[, context[, order]]) 67 | 68 | > This implementation currently does not support the "order" parameter. 69 | > This function can only iterate in Generated order. 70 | 71 | ```typescript 72 | const smc = new SourceMapConsumer(map); 73 | smc.eachMapping((mapping) => { 74 | // { source: 'baz.ts', 75 | // generatedLine: 4, 76 | // generatedColumn: 5, 77 | // originalLine: 4, 78 | // originalColumn: 5, 79 | // name: null } 80 | }); 81 | ``` 82 | 83 | #### SourceMapConsumer.prototype.generatedPositionFor(originalPosition) 84 | 85 | ```typescript 86 | const smc = new SourceMapConsumer(map); 87 | smc.generatedPositionFor({ line: 1, column: 5, source: "baz.ts" }); 88 | // { line: 2, column: 8 } 89 | ``` 90 | 91 | #### SourceMapConsumer.prototype.hasContentsOfAllSources() 92 | 93 | ```typescript 94 | const smc = new SourceMapConsumer(map); 95 | smc.hasContentsOfAllSources(); 96 | // true 97 | ``` 98 | 99 | #### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing]) 100 | 101 | ```typescript 102 | const smc = new SourceMapConsumer(map); 103 | smc.generatedPositionFor("baz.ts"); 104 | // "export default ..." 105 | ``` 106 | 107 | #### SourceMapConsumer.prototype.version 108 | 109 | Returns the source map's version 110 | 111 | ### SourceMapGenerator 112 | 113 | ```typescript 114 | import { SourceMapGenerator } from '@jridgewell/source-map'; 115 | const smg = new SourceMapGenerator({ 116 | file: 'output.js', 117 | sourceRoot: 'https://example.com/', 118 | }); 119 | ``` 120 | 121 | #### SourceMapGenerator.fromSourceMap(map) 122 | 123 | Transform a `SourceMapConsumer` into a `SourceMapGenerator`. 124 | 125 | ```typescript 126 | const smc = new SourceMapConsumer(); 127 | const smg = SourceMapGenerator.fromSourceMap(smc); 128 | ``` 129 | 130 | #### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]]) 131 | 132 | > This method is not implemented yet 133 | 134 | #### SourceMapGenerator.prototype.addMapping(mapping) 135 | 136 | ```typescript 137 | const smg = new SourceMapGenerator(); 138 | smg.addMapping({ 139 | generated: { line: 1, column: 0 }, 140 | source: 'input.js', 141 | original: { line: 1, column: 0 }, 142 | name: 'foo', 143 | }); 144 | ``` 145 | 146 | #### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent) 147 | 148 | ```typescript 149 | const smg = new SourceMapGenerator(); 150 | smg.setSourceContent('input.js', 'foobar'); 151 | ``` 152 | 153 | #### SourceMapGenerator.prototype.toJSON() 154 | 155 | ```typescript 156 | const smg = new SourceMapGenerator(); 157 | smg.toJSON(); // { version: 3, names: [], sources: [], mappings: '' } 158 | ``` 159 | 160 | #### SourceMapGenerator.prototype.toString() 161 | 162 | ```typescript 163 | const smg = new SourceMapGenerator(); 164 | smg.toJSON(); // "{version:3,names:[],sources:[],mappings:''}" 165 | ``` 166 | 167 | #### SourceMapGenerator.prototype.toDecodedMap() 168 | 169 | ```typescript 170 | const smg = new SourceMapGenerator(); 171 | smg.toDecodedMap(); // { version: 3, names: [], sources: [], mappings: [] } 172 | ``` 173 | 174 | ## Known differences with other implementations 175 | 176 | This implementation has some differences with `source-map` and `source-map-js`. 177 | 178 | - `SourceMapConsumer.prototype.eachMapping()` 179 | - Does not support the `order` argument 180 | - `SourceMapGenerator.prototype.applySourceMap()` 181 | - Not implemented 182 | 183 | [trace-mapping]: https://github.com/jridgewell/trace-mapping/ 184 | [gen-mapping]: https://github.com/jridgewell/gen-mapping/ 185 | -------------------------------------------------------------------------------- /packages/source-map/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/source-map", 3 | "version": "0.3.6", 4 | "description": "Packages @jridgewell/trace-mapping and @jridgewell/gen-mapping into the familiar source-map API", 5 | "keywords": [ 6 | "sourcemap", 7 | "source", 8 | "map" 9 | ], 10 | "main": "dist/source-map.umd.js", 11 | "module": "dist/source-map.mjs", 12 | "types": "types/source-map.d.ts", 13 | "files": [ 14 | "dist", 15 | "src", 16 | "types" 17 | ], 18 | "exports": { 19 | ".": [ 20 | { 21 | "types": "./types/source-map.d.ts", 22 | "browser": "./dist/source-map.umd.js", 23 | "require": "./dist/source-map.umd.js", 24 | "import": "./dist/source-map.mjs" 25 | }, 26 | "./dist/source-map.umd.js" 27 | ], 28 | "./package.json": "./package.json" 29 | }, 30 | "scripts": { 31 | "benchmark": "run-s build:code benchmark:*", 32 | "benchmark:install": "cd benchmark && npm install", 33 | "benchmark:only": "node --expose-gc benchmark/index.js", 34 | "build": "run-s -n build:code build:types", 35 | "build:code": "node ../../esbuild.mjs source-map.ts", 36 | "build:types": "tsc --project tsconfig.build.json", 37 | "test": "run-s -n test:types test:only test:format", 38 | "test:format": "prettier --check '{src,test}/**/*.ts'", 39 | "test:only": "mocha", 40 | "test:types": "eslint '{src,test}/**/*.ts'", 41 | "lint": "run-s -n lint:types lint:format", 42 | "lint:format": "npm run test:format -- --write", 43 | "lint:types": "npm run test:types -- --fix", 44 | "prepublishOnly": "npm run-s -n build test" 45 | }, 46 | "repository": { 47 | "type": "git", 48 | "url": "git+https://github.com/jridgewell/sourcemaps.git", 49 | "directory": "packages/source-map" 50 | }, 51 | "author": "Justin Ridgewell ", 52 | "license": "MIT", 53 | "dependencies": { 54 | "@jridgewell/gen-mapping": "^0.3.5", 55 | "@jridgewell/trace-mapping": "^0.3.25" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/source-map/src/source-map.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AnyMap, 3 | originalPositionFor, 4 | generatedPositionFor, 5 | allGeneratedPositionsFor, 6 | eachMapping, 7 | encodedMappings, 8 | sourceContentFor, 9 | } from '@jridgewell/trace-mapping'; 10 | import { 11 | GenMapping, 12 | maybeAddMapping, 13 | toDecodedMap, 14 | toEncodedMap, 15 | setSourceContent, 16 | fromMap, 17 | } from '@jridgewell/gen-mapping'; 18 | 19 | import type { 20 | TraceMap, 21 | SourceMapInput, 22 | SectionedSourceMapInput, 23 | DecodedSourceMap, 24 | } from '@jridgewell/trace-mapping'; 25 | export type { TraceMap, SourceMapInput, SectionedSourceMapInput, DecodedSourceMap }; 26 | 27 | import type { Mapping, EncodedSourceMap } from '@jridgewell/gen-mapping'; 28 | export type { Mapping, EncodedSourceMap }; 29 | 30 | export class SourceMapConsumer { 31 | private declare _map: TraceMap; 32 | declare file: TraceMap['file']; 33 | declare names: TraceMap['names']; 34 | declare sourceRoot: TraceMap['sourceRoot']; 35 | declare sources: TraceMap['sources']; 36 | declare sourcesContent: TraceMap['sourcesContent']; 37 | declare version: TraceMap['version']; 38 | 39 | constructor( 40 | map: ConstructorParameters[0], 41 | mapUrl?: ConstructorParameters[1], 42 | ) { 43 | const trace = (this._map = new AnyMap(map, mapUrl)); 44 | 45 | this.file = trace.file; 46 | this.names = trace.names; 47 | this.sourceRoot = trace.sourceRoot; 48 | this.sources = trace.resolvedSources; 49 | this.sourcesContent = trace.sourcesContent; 50 | this.version = trace.version; 51 | } 52 | 53 | static fromSourceMap(map: SourceMapGenerator, mapUrl?: Parameters[1]) { 54 | // This is more performant if we receive 55 | // a @jridgewell/source-map SourceMapGenerator 56 | if (map.toDecodedMap) { 57 | return new SourceMapConsumer(map.toDecodedMap() as SectionedSourceMapInput, mapUrl); 58 | } 59 | 60 | // This is a fallback for `source-map` and `source-map-js` 61 | return new SourceMapConsumer(map.toJSON() as SectionedSourceMapInput, mapUrl); 62 | } 63 | 64 | get mappings(): string { 65 | return encodedMappings(this._map); 66 | } 67 | 68 | originalPositionFor( 69 | needle: Parameters[1], 70 | ): ReturnType { 71 | return originalPositionFor(this._map, needle); 72 | } 73 | 74 | generatedPositionFor( 75 | originalPosition: Parameters[1], 76 | ): ReturnType { 77 | return generatedPositionFor(this._map, originalPosition); 78 | } 79 | 80 | allGeneratedPositionsFor( 81 | originalPosition: Parameters[1], 82 | ): ReturnType[] { 83 | return allGeneratedPositionsFor(this._map, originalPosition); 84 | } 85 | 86 | hasContentsOfAllSources(): boolean { 87 | if (!this.sourcesContent || this.sourcesContent.length !== this.sources.length) { 88 | return false; 89 | } 90 | 91 | for (const content of this.sourcesContent) { 92 | if (content == null) { 93 | return false; 94 | } 95 | } 96 | 97 | return true; 98 | } 99 | 100 | sourceContentFor(source: string, nullOnMissing?: boolean): string | null { 101 | const sourceContent = sourceContentFor(this._map, source); 102 | if (sourceContent != null) { 103 | return sourceContent; 104 | } 105 | 106 | if (nullOnMissing) { 107 | return null; 108 | } 109 | throw new Error(`"${source}" is not in the SourceMap.`); 110 | } 111 | 112 | eachMapping( 113 | callback: Parameters[1], 114 | context?: any /*, order?: number*/, 115 | ): void { 116 | // order is ignored as @jridgewell/trace-map doesn't implement it 117 | eachMapping(this._map, context ? callback.bind(context) : callback); 118 | } 119 | 120 | destroy() { 121 | // noop. 122 | } 123 | } 124 | 125 | export class SourceMapGenerator { 126 | private declare _map: GenMapping; 127 | 128 | constructor(opts: ConstructorParameters[0] | GenMapping) { 129 | // TODO :: should this be duck-typed ? 130 | this._map = opts instanceof GenMapping ? opts : new GenMapping(opts); 131 | } 132 | 133 | static fromSourceMap(consumer: SourceMapConsumer) { 134 | return new SourceMapGenerator(fromMap(consumer)); 135 | } 136 | 137 | addMapping(mapping: Parameters[1]): ReturnType { 138 | maybeAddMapping(this._map, mapping); 139 | } 140 | 141 | setSourceContent( 142 | source: Parameters[1], 143 | content: Parameters[2], 144 | ): ReturnType { 145 | setSourceContent(this._map, source, content); 146 | } 147 | 148 | toJSON(): ReturnType { 149 | return toEncodedMap(this._map); 150 | } 151 | 152 | toString(): string { 153 | return JSON.stringify(this.toJSON()); 154 | } 155 | 156 | toDecodedMap(): ReturnType { 157 | return toDecodedMap(this._map); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /packages/source-map/test/cursory.test.ts: -------------------------------------------------------------------------------- 1 | import { SourceMapConsumer, SourceMapGenerator } from '../src/source-map'; 2 | import assert from 'node:assert/strict'; 3 | 4 | it('SourceMapGenerator', () => { 5 | const smg = new SourceMapGenerator({ 6 | file: 'output.js', 7 | sourceRoot: 'https://example.com/', 8 | }); 9 | 10 | smg.addMapping({ 11 | generated: { line: 1, column: 0 }, 12 | source: 'input.js', 13 | original: { line: 1, column: 0 }, 14 | name: 'foo', 15 | }); 16 | smg.setSourceContent('input.js', 'foobar'); 17 | 18 | assert.deepEqual(smg.toJSON(), { 19 | version: 3, 20 | file: 'output.js', 21 | sourceRoot: 'https://example.com/', 22 | names: ['foo'], 23 | sources: ['input.js'], 24 | sourcesContent: ['foobar'], 25 | mappings: 'AAAAA', 26 | ignoreList: [], 27 | }); 28 | 29 | assert.deepEqual(smg.toDecodedMap(), { 30 | version: 3, 31 | file: 'output.js', 32 | sourceRoot: 'https://example.com/', 33 | names: ['foo'], 34 | sources: ['input.js'], 35 | sourcesContent: ['foobar'], 36 | mappings: [[[0, 0, 0, 0, 0]]], 37 | ignoreList: [], 38 | }); 39 | }); 40 | 41 | it('SourceMapConsumer', () => { 42 | const smc = new SourceMapConsumer({ 43 | version: 3, 44 | file: 'output.js', 45 | sourceRoot: 'https://example.com/', 46 | names: ['foo'], 47 | sources: ['input.js'], 48 | sourcesContent: ['foobar'], 49 | mappings: 'AAAAA', 50 | }); 51 | 52 | assert.equal(smc.file, 'output.js'); 53 | assert.deepEqual(smc.names, ['foo']); 54 | assert.equal(smc.sourceRoot, 'https://example.com/'); 55 | assert.deepEqual(smc.sources, ['https://example.com/input.js']); 56 | assert.deepEqual(smc.sourcesContent, ['foobar']); 57 | 58 | assert.deepEqual(smc.originalPositionFor({ line: 1, column: 0 }), { 59 | source: 'https://example.com/input.js', 60 | line: 1, 61 | column: 0, 62 | name: 'foo', 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/source-map/test/fixtures.ts: -------------------------------------------------------------------------------- 1 | // This is a test mapping which maps functions from two different files 2 | // (one.js and two.js) to a minified generated source. 3 | // 4 | // Here is one.js: 5 | // 6 | // ONE.foo = function (bar) { 7 | // return baz(bar); 8 | // }; 9 | // 10 | // Here is two.js: 11 | // 12 | // TWO.inc = function (n) { 13 | // return n + 1; 14 | // }; 15 | // 16 | // And here is the generated code (min.js): 17 | // 18 | // ONE.foo=function(a){return baz(a);}; 19 | 20 | import { type EncodedSourceMap, type SectionedSourceMap } from '@jridgewell/trace-mapping'; 21 | 22 | // TWO.inc=function(a){return a+1;}; 23 | export const testGeneratedCode = 24 | ' ONE.foo=function(a){return baz(a);};\n' + ' TWO.inc=function(a){return a+1;};'; 25 | export const testMap: EncodedSourceMap = { 26 | version: 3, 27 | file: 'min.js', 28 | names: ['bar', 'baz', 'n'], 29 | sources: ['one.js', 'two.js'], 30 | sourceRoot: '/the/root', 31 | sourcesContent: [null, null], 32 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 33 | }; 34 | export const testMapNoSourceRoot: EncodedSourceMap = { 35 | version: 3, 36 | file: 'min.js', 37 | names: ['bar', 'baz', 'n'], 38 | sources: ['one.js', 'two.js'], 39 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 40 | }; 41 | export const testMapEmptySourceRoot: EncodedSourceMap = { 42 | version: 3, 43 | file: 'min.js', 44 | names: ['bar', 'baz', 'n'], 45 | sources: ['one.js', 'two.js'], 46 | sourceRoot: '', 47 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 48 | }; 49 | export const testMapSingleSource: EncodedSourceMap = { 50 | version: 3, 51 | file: 'min.js', 52 | names: ['bar', 'baz'], 53 | sources: ['one.js'], 54 | //sourceRoot: '', 55 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID', 56 | }; 57 | export const testMapEmptyMappings: EncodedSourceMap = { 58 | version: 3, 59 | file: 'min.js', 60 | names: [], 61 | sources: ['one.js', 'two.js'], 62 | sourcesContent: [' ONE.foo = 1;', ' TWO.inc = 2;'], 63 | //sourceRoot: '', 64 | mappings: '', 65 | }; 66 | export const testMapEmptyMappingsRelativeSources: EncodedSourceMap = { 67 | version: 3, 68 | file: 'min.js', 69 | names: [], 70 | sources: ['./one.js', './two.js'], 71 | sourcesContent: [' ONE.foo = 1;', ' TWO.inc = 2;'], 72 | sourceRoot: '/the/root', 73 | mappings: '', 74 | }; 75 | export const testMapEmptyMappingsRelativeSources_generated: EncodedSourceMap = { 76 | version: 3, 77 | file: 'min.js', 78 | names: [], 79 | sources: ['/the/root/one.js', '/the/root/two.js'], 80 | sourcesContent: [' ONE.foo = 1;', ' TWO.inc = 2;'], 81 | sourceRoot: '/the/root', 82 | mappings: '', 83 | }; 84 | export const testMapMultiSourcesMappingRefersSingleSourceOnly: EncodedSourceMap = { 85 | version: 3, 86 | file: 'min.js', 87 | names: ['bar', 'baz'], 88 | sources: ['one.js', 'withoutMappings.js'], 89 | //sourceRoot: '', 90 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID', 91 | }; 92 | // This mapping is identical to above, but uses the indexed format instead. 93 | export const indexedTestMap: SectionedSourceMap = { 94 | version: 3, 95 | file: 'min.js', 96 | sections: [ 97 | { 98 | offset: { 99 | line: 0, 100 | column: 0, 101 | }, 102 | map: { 103 | version: 3, 104 | sources: ['one.js'], 105 | sourcesContent: [' ONE.foo = function (bar) {\n' + ' return baz(bar);\n' + ' };'], 106 | names: ['bar', 'baz'], 107 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID', 108 | file: 'min.js', 109 | sourceRoot: '/the/root', 110 | }, 111 | }, 112 | { 113 | offset: { 114 | line: 1, 115 | column: 0, 116 | }, 117 | map: { 118 | version: 3, 119 | sources: ['two.js'], 120 | sourcesContent: [' TWO.inc = function (n) {\n' + ' return n + 1;\n' + ' };'], 121 | names: ['n'], 122 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA', 123 | file: 'min.js', 124 | sourceRoot: '/the/root', 125 | }, 126 | }, 127 | ], 128 | }; 129 | export const indexedTestMapDifferentSourceRoots: SectionedSourceMap = { 130 | version: 3, 131 | file: 'min.js', 132 | sections: [ 133 | { 134 | offset: { 135 | line: 0, 136 | column: 0, 137 | }, 138 | map: { 139 | version: 3, 140 | sources: ['one.js'], 141 | sourcesContent: [' ONE.foo = function (bar) {\n' + ' return baz(bar);\n' + ' };'], 142 | names: ['bar', 'baz'], 143 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID', 144 | file: 'min.js', 145 | sourceRoot: '/the/root', 146 | }, 147 | }, 148 | { 149 | offset: { 150 | line: 1, 151 | column: 0, 152 | }, 153 | map: { 154 | version: 3, 155 | sources: ['two.js'], 156 | sourcesContent: [' TWO.inc = function (n) {\n' + ' return n + 1;\n' + ' };'], 157 | names: ['n'], 158 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA', 159 | file: 'min.js', 160 | sourceRoot: '/different/root', 161 | }, 162 | }, 163 | ], 164 | }; 165 | export const testMapWithSourcesContent: EncodedSourceMap = { 166 | version: 3, 167 | file: 'min.js', 168 | names: ['bar', 'baz', 'n'], 169 | sources: ['one.js', 'two.js'], 170 | sourcesContent: [ 171 | ' ONE.foo = function (bar) {\n' + ' return baz(bar);\n' + ' };', 172 | ' TWO.inc = function (n) {\n' + ' return n + 1;\n' + ' };', 173 | ], 174 | sourceRoot: '/the/root', 175 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 176 | }; 177 | export const testMapWithSourcesContent_generated: EncodedSourceMap = { 178 | version: 3, 179 | file: 'min.js', 180 | names: ['bar', 'baz', 'n'], 181 | sources: ['/the/root/one.js', '/the/root/two.js'], 182 | sourcesContent: [ 183 | ' ONE.foo = function (bar) {\n' + ' return baz(bar);\n' + ' };', 184 | ' TWO.inc = function (n) {\n' + ' return n + 1;\n' + ' };', 185 | ], 186 | sourceRoot: '/the/root', 187 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 188 | }; 189 | export const testMapRelativeSources: EncodedSourceMap = { 190 | version: 3, 191 | file: 'min.js', 192 | names: ['bar', 'baz', 'n'], 193 | sources: ['./one.js', './two.js'], 194 | sourcesContent: [ 195 | ' ONE.foo = function (bar) {\n' + ' return baz(bar);\n' + ' };', 196 | ' TWO.inc = function (n) {\n' + ' return n + 1;\n' + ' };', 197 | ], 198 | sourceRoot: '/the/root', 199 | mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA', 200 | }; 201 | export const emptyMap: EncodedSourceMap = { 202 | version: 3, 203 | file: 'min.js', 204 | names: [], 205 | sources: [], 206 | mappings: '', 207 | }; 208 | -------------------------------------------------------------------------------- /packages/source-map/test/source-map-generator.test.ts: -------------------------------------------------------------------------------- 1 | import { type EncodedSourceMap } from '@jridgewell/trace-mapping'; 2 | import { SourceMapConsumer, SourceMapGenerator } from '../src/source-map'; 3 | import * as fixtures from './fixtures'; 4 | import * as utils from './utils'; 5 | import assert from 'node:assert/strict'; 6 | 7 | it('SourceMapGenerator.setSourceContent', function () { 8 | const gen = new SourceMapGenerator({ 9 | file: 'min.js', 10 | sourceRoot: '/the/root', 11 | }); 12 | gen.addMapping({ 13 | generated: { line: 1, column: 1 }, 14 | original: { line: 1, column: 1 }, 15 | source: 'one.js', 16 | }); 17 | gen.addMapping({ 18 | generated: { line: 2, column: 1 }, 19 | original: { line: 1, column: 1 }, 20 | source: 'two.js', 21 | }); 22 | gen.setSourceContent('one.js', 'one file content'); 23 | 24 | const map = JSON.parse(gen.toString()); 25 | assert.equal(map.sources[0], 'one.js'); 26 | assert.equal(map.sources[1], 'two.js'); 27 | assert.equal(map.sourcesContent[0], 'one file content'); 28 | assert.equal(map.sourcesContent[1], null); 29 | }); 30 | 31 | describe('SourceMapGenerator.addMapping', () => { 32 | it('test adding mappings (case 1)', () => { 33 | const map = new SourceMapGenerator({ 34 | file: 'generated-foo.js', 35 | sourceRoot: '.', 36 | }); 37 | 38 | assert.doesNotThrow(function () { 39 | map.addMapping({ 40 | generated: { line: 1, column: 1 }, 41 | }); 42 | }); 43 | }); 44 | 45 | it('test adding mappings (case 2)', () => { 46 | const map = new SourceMapGenerator({ 47 | file: 'generated-foo.js', 48 | sourceRoot: '.', 49 | }); 50 | 51 | assert.doesNotThrow(function () { 52 | map.addMapping({ 53 | generated: { line: 1, column: 1 }, 54 | source: 'bar.js', 55 | original: { line: 1, column: 1 }, 56 | }); 57 | }); 58 | }); 59 | 60 | it('test adding mappings (case 3)', () => { 61 | const map = new SourceMapGenerator({ 62 | file: 'generated-foo.js', 63 | sourceRoot: '.', 64 | }); 65 | 66 | assert.doesNotThrow(function () { 67 | map.addMapping({ 68 | generated: { line: 1, column: 1 }, 69 | source: 'bar.js', 70 | original: { line: 1, column: 1 }, 71 | name: 'someToken', 72 | }); 73 | }); 74 | }); 75 | 76 | it('test adding mappings (invalid)', () => { 77 | const map = new SourceMapGenerator({ 78 | file: 'generated-foo.js', 79 | sourceRoot: '.', 80 | }); 81 | 82 | // Not enough info. 83 | assert.throws(function () { 84 | map.addMapping({} as any); 85 | }); 86 | 87 | // Original file position, but no source. 88 | assert.doesNotThrow(function () { 89 | map.addMapping({ 90 | generated: { line: 1, column: 1 }, 91 | original: { line: 1, column: 1 }, 92 | } as any); 93 | }); 94 | }); 95 | 96 | it('test adding mappings with skipValidation', () => { 97 | const map = new SourceMapGenerator({ 98 | file: 'generated-foo.js', 99 | sourceRoot: '.', 100 | skipValidation: true, 101 | } as any); 102 | 103 | // Not enough info, caught by `util.getArgs` 104 | assert.throws(function () { 105 | map.addMapping({} as any); 106 | }); 107 | 108 | // Original file position, but no source. Not checked. 109 | assert.doesNotThrow(function () { 110 | map.addMapping({ 111 | generated: { line: 1, column: 1 }, 112 | original: { line: 1, column: 1 }, 113 | } as any); 114 | }); 115 | }); 116 | 117 | it('test that the correct mappings are being generated', () => { 118 | const gen = new SourceMapGenerator({ 119 | file: 'min.js', 120 | sourceRoot: '/the/root', 121 | }); 122 | 123 | gen.addMapping({ 124 | generated: { line: 1, column: 1 }, 125 | original: { line: 1, column: 1 }, 126 | source: 'one.js', 127 | }); 128 | gen.addMapping({ 129 | generated: { line: 1, column: 5 }, 130 | original: { line: 1, column: 5 }, 131 | source: 'one.js', 132 | }); 133 | gen.addMapping({ 134 | generated: { line: 1, column: 9 }, 135 | original: { line: 1, column: 11 }, 136 | source: 'one.js', 137 | }); 138 | gen.addMapping({ 139 | generated: { line: 1, column: 18 }, 140 | original: { line: 1, column: 21 }, 141 | source: 'one.js', 142 | name: 'bar', 143 | }); 144 | gen.addMapping({ 145 | generated: { line: 1, column: 21 }, 146 | original: { line: 2, column: 3 }, 147 | source: 'one.js', 148 | }); 149 | gen.addMapping({ 150 | generated: { line: 1, column: 28 }, 151 | original: { line: 2, column: 10 }, 152 | source: 'one.js', 153 | name: 'baz', 154 | }); 155 | gen.addMapping({ 156 | generated: { line: 1, column: 32 }, 157 | original: { line: 2, column: 14 }, 158 | source: 'one.js', 159 | name: 'bar', 160 | }); 161 | 162 | gen.addMapping({ 163 | generated: { line: 2, column: 1 }, 164 | original: { line: 1, column: 1 }, 165 | source: 'two.js', 166 | }); 167 | gen.addMapping({ 168 | generated: { line: 2, column: 5 }, 169 | original: { line: 1, column: 5 }, 170 | source: 'two.js', 171 | }); 172 | gen.addMapping({ 173 | generated: { line: 2, column: 9 }, 174 | original: { line: 1, column: 11 }, 175 | source: 'two.js', 176 | }); 177 | gen.addMapping({ 178 | generated: { line: 2, column: 18 }, 179 | original: { line: 1, column: 21 }, 180 | source: 'two.js', 181 | name: 'n', 182 | }); 183 | gen.addMapping({ 184 | generated: { line: 2, column: 21 }, 185 | original: { line: 2, column: 3 }, 186 | source: 'two.js', 187 | }); 188 | gen.addMapping({ 189 | generated: { line: 2, column: 28 }, 190 | original: { line: 2, column: 10 }, 191 | source: 'two.js', 192 | name: 'n', 193 | }); 194 | 195 | const map = JSON.parse(gen.toString()); 196 | 197 | utils.assertEqualMaps(map, fixtures.testMap); 198 | }); 199 | 200 | it('test that adding a mapping with an empty string name does not break generation', () => { 201 | const map = new SourceMapGenerator({ 202 | file: 'generated-foo.js', 203 | sourceRoot: '.', 204 | }); 205 | 206 | map.addMapping({ 207 | generated: { line: 1, column: 1 }, 208 | source: 'bar.js', 209 | original: { line: 1, column: 1 }, 210 | name: '', 211 | }); 212 | 213 | assert.doesNotThrow(function () { 214 | JSON.parse(map.toString()); 215 | }); 216 | }); 217 | }); 218 | 219 | describe('SourceMapGenerator.fromSourceMap', () => { 220 | it('test .fromSourceMap with sourcesContent', () => { 221 | const map = SourceMapGenerator.fromSourceMap( 222 | new SourceMapConsumer(fixtures.testMapWithSourcesContent), 223 | ); 224 | utils.assertEqualMaps( 225 | map.toJSON() as EncodedSourceMap, 226 | fixtures.testMapWithSourcesContent_generated, 227 | ); 228 | }); 229 | 230 | it('test .fromSourceMap with single source', () => { 231 | const map = SourceMapGenerator.fromSourceMap( 232 | new SourceMapConsumer(fixtures.testMapSingleSource), 233 | ); 234 | utils.assertEqualMaps(map.toJSON() as EncodedSourceMap, fixtures.testMapSingleSource); 235 | }); 236 | 237 | it('test .fromSourceMap with empty mappings', () => { 238 | const map = SourceMapGenerator.fromSourceMap( 239 | new SourceMapConsumer(fixtures.testMapEmptyMappings), 240 | ); 241 | utils.assertEqualMaps(map.toJSON() as EncodedSourceMap, fixtures.testMapEmptyMappings); 242 | }); 243 | 244 | it('test .fromSourceMap with empty mappings and relative sources', () => { 245 | const map = SourceMapGenerator.fromSourceMap( 246 | new SourceMapConsumer(fixtures.testMapEmptyMappingsRelativeSources), 247 | ); 248 | utils.assertEqualMaps( 249 | map.toJSON() as EncodedSourceMap, 250 | fixtures.testMapEmptyMappingsRelativeSources_generated, 251 | ); 252 | }); 253 | 254 | it('test .fromSourceMap with multiple sources where mappings refers only to single source', () => { 255 | const map = SourceMapGenerator.fromSourceMap( 256 | new SourceMapConsumer(fixtures.testMapMultiSourcesMappingRefersSingleSourceOnly), 257 | ); 258 | utils.assertEqualMaps( 259 | map.toJSON() as EncodedSourceMap, 260 | fixtures.testMapMultiSourcesMappingRefersSingleSourceOnly, 261 | ); 262 | }); 263 | }); 264 | -------------------------------------------------------------------------------- /packages/source-map/test/utils.ts: -------------------------------------------------------------------------------- 1 | import { type EncodedSourceMap } from '@jridgewell/trace-mapping'; 2 | import assert from 'node:assert/strict'; 3 | 4 | export function assertEqualMaps(actualMap: EncodedSourceMap, expectedMap: EncodedSourceMap) { 5 | assert.equal(actualMap.version, expectedMap.version, 'version mismatch'); 6 | assert.equal(actualMap.file, expectedMap.file, 'file mismatch'); 7 | assert.deepEqual(actualMap.names, expectedMap.names, 'names mismatch'); 8 | assert.deepEqual(actualMap.sources, expectedMap.sources, 'sources mismatch'); 9 | 10 | // Source root doesn't match in tests with SourceMapGenerator.applySourceMap 11 | // Could be because of https://github.com/ampproject/remapping/blob/main/src/source-map-tree.ts#L85-L86 12 | /*const aSourceRoot = actualMap.sourceRoot; 13 | const eSourceRoot = expectedMap.sourceRoot; 14 | assert.equal(aSourceRoot, eSourceRoot, `sourceRoot mismatch: '${aSourceRoot}' != '${eSourceRoot}'`);*/ 15 | assert.equal(actualMap.mappings, expectedMap.mappings, `mappings mismatch`); 16 | 17 | if (actualMap.sourcesContent) { 18 | // The actualMap.sourcesContent could be an array of null, 19 | // Which is actually equivalent to not having the key at all 20 | const hasValues = actualMap.sourcesContent.filter((c) => c != null).length > 0; 21 | 22 | if (hasValues || expectedMap.sourcesContent) { 23 | assert.deepEqual( 24 | actualMap.sourcesContent, 25 | expectedMap.sourcesContent, 26 | 'sourcesContent mismatch', 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/source-map/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": [ 8 | "src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/source-map/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2015", 5 | "module":"CommonJS", 6 | "lib": ["es2015", "dom"], 7 | "strict": true, 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "declaration": true, 11 | "allowSyntheticDefaultImports": true, 12 | "declarationDir": "dist/types", 13 | "outDir": "dist/lib", 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "esModuleInterop": true 17 | }, 18 | "exclude": [ 19 | "dist" 20 | ], 21 | "include": [ 22 | "src", 23 | "test" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # sourcemap-codec changelog 2 | 3 | ## 1.4.10 4 | 5 | * Sort mappings during decode ([f9d1b33f](https://github.com/jridgewell/sourcemap-codec/commit/f9d1b33f)) 6 | 7 | ## 1.4.9 8 | 9 | * Performance boost ([#86](https://github.com/Rich-Harris/sourcemap-codec/pull/86)) 10 | * Add native ESM entry point ([#85](https://github.com/Rich-Harris/sourcemap-codec/pull/85)) 11 | 12 | ## 1.4.8 13 | 14 | * Performance boost ([#80](https://github.com/Rich-Harris/sourcemap-codec/pull/80)) 15 | 16 | ## 1.4.7 17 | 18 | * Include .map files in package ([#73](https://github.com/Rich-Harris/sourcemap-codec/issues/73)) 19 | 20 | ## 1.4.6 21 | 22 | * Use arrays instead of typed arrays ([#79](https://github.com/Rich-Harris/sourcemap-codec/pull/79)) 23 | 24 | ## 1.4.5 25 | 26 | * Handle overflow cases ([#78](https://github.com/Rich-Harris/sourcemap-codec/pull/78)) 27 | 28 | ## 1.4.4 29 | 30 | * Use Uint32Array, yikes ([#77](https://github.com/Rich-Harris/sourcemap-codec/pull/77)) 31 | 32 | ## 1.4.3 33 | 34 | * Use Uint16Array to prevent overflow ([#75](https://github.com/Rich-Harris/sourcemap-codec/pull/75)) 35 | 36 | ## 1.4.2 37 | 38 | * GO EVEN FASTER ([#74](https://github.com/Rich-Harris/sourcemap-codec/pull/74)) 39 | 40 | ## 1.4.1 41 | 42 | * GO FASTER ([#71](https://github.com/Rich-Harris/sourcemap-codec/pull/71)) 43 | 44 | ## 1.4.0 45 | 46 | * Add TypeScript declarations ([#70](https://github.com/Rich-Harris/sourcemap-codec/pull/70)) 47 | 48 | ## 1.3.1 49 | 50 | * Update build process, expose `pkg.module` 51 | 52 | ## 1.3.0 53 | 54 | * Update build process 55 | 56 | ## 1.2.1 57 | 58 | * Add dist files to npm package 59 | 60 | ## 1.2.0 61 | 62 | * Add ES6 build 63 | * Update dependencies 64 | * Add test coverage 65 | 66 | ## 1.1.0 67 | 68 | * Fix bug with lines containing single-character segments 69 | * Add tests 70 | 71 | ## 1.0.0 72 | 73 | * First release 74 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 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 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/benchmark/chrome.js: -------------------------------------------------------------------------------- 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 | exports.SourceMap = SourceMap; 5 | function SourceMap(sourceMappingURL, payload) { 6 | if (!SourceMap.prototype._base64Map) { 7 | const base64Digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 8 | SourceMap.prototype._base64Map = {}; 9 | for (let i = 0; i < base64Digits.length; ++i) 10 | SourceMap.prototype._base64Map[base64Digits.charAt(i)] = i; 11 | } 12 | 13 | this._sourceMappingURL = sourceMappingURL; 14 | this._reverseMappingsBySourceURL = {}; 15 | this._mappings = []; 16 | // this._sources = {}; 17 | // this._sourceContentByURL = {}; 18 | this._parseMappingPayload(payload); 19 | } 20 | 21 | SourceMap.prototype = { 22 | /** 23 | * @param {SourceMapV3} mappingPayload 24 | */ 25 | _parseMappingPayload(mappingPayload) { 26 | if (mappingPayload.sections) this._parseSections(mappingPayload.sections); 27 | else this._parseMap(mappingPayload, 0, 0); 28 | }, 29 | 30 | /** 31 | * @param {Array.} sections 32 | */ 33 | _parseSections(sections) { 34 | for (let i = 0; i < sections.length; ++i) { 35 | const section = sections[i]; 36 | this._parseMap(section.map, section.offset.line, section.offset.column); 37 | } 38 | }, 39 | 40 | /** 41 | * @override 42 | */ 43 | _parseMap(map, lineNumber, columnNumber) { 44 | let sourceIndex = 0; 45 | let sourceLineNumber = 0; 46 | let sourceColumnNumber = 0; 47 | let nameIndex = 0; 48 | 49 | const sources = map.sources; 50 | 51 | const stringCharIterator = new SourceMap.StringCharIterator(map.mappings); 52 | let sourceURL = sources[sourceIndex]; 53 | 54 | while (true) { 55 | if (stringCharIterator.peek() === ',') stringCharIterator.next(); 56 | else { 57 | while (stringCharIterator.peek() === ';') { 58 | lineNumber += 1; 59 | columnNumber = 0; 60 | stringCharIterator.next(); 61 | } 62 | if (!stringCharIterator.hasNext()) break; 63 | } 64 | 65 | columnNumber += this._decodeVLQ(stringCharIterator); 66 | if (this._isSeparator(stringCharIterator.peek())) { 67 | this._mappings.push([lineNumber, columnNumber]); 68 | continue; 69 | } 70 | 71 | const sourceIndexDelta = this._decodeVLQ(stringCharIterator); 72 | if (sourceIndexDelta) { 73 | sourceIndex += sourceIndexDelta; 74 | sourceURL = sources[sourceIndex]; 75 | } 76 | sourceLineNumber += this._decodeVLQ(stringCharIterator); 77 | sourceColumnNumber += this._decodeVLQ(stringCharIterator); 78 | if (!this._isSeparator(stringCharIterator.peek())) 79 | nameIndex += this._decodeVLQ(stringCharIterator); 80 | 81 | this._mappings.push([ 82 | lineNumber, 83 | columnNumber, 84 | sourceURL, 85 | sourceLineNumber, 86 | sourceColumnNumber, 87 | ]); 88 | } 89 | 90 | for (let i = 0; i < this._mappings.length; ++i) { 91 | const mapping = this._mappings[i]; 92 | const url = mapping[2]; 93 | if (!url) continue; 94 | if (!this._reverseMappingsBySourceURL[url]) { 95 | this._reverseMappingsBySourceURL[url] = []; 96 | } 97 | const reverseMappings = this._reverseMappingsBySourceURL[url]; 98 | const sourceLine = mapping[3]; 99 | if (!reverseMappings[sourceLine]) { 100 | reverseMappings[sourceLine] = [mapping[0], mapping[1]]; 101 | } 102 | } 103 | }, 104 | 105 | /** 106 | * @param {string} char 107 | * @return {boolean} 108 | */ 109 | _isSeparator(char) { 110 | return char === ',' || char === ';'; 111 | }, 112 | 113 | /** 114 | * @param {SourceMap.StringCharIterator} stringCharIterator 115 | * @return {number} 116 | */ 117 | _decodeVLQ(stringCharIterator) { 118 | // Read unsigned value. 119 | let result = 0; 120 | let shift = 0; 121 | let digit; 122 | do { 123 | digit = this._base64Map[stringCharIterator.next()]; 124 | result += (digit & this._VLQ_BASE_MASK) << shift; 125 | shift += this._VLQ_BASE_SHIFT; 126 | } while (digit & this._VLQ_CONTINUATION_MASK); 127 | 128 | // Fix the sign. 129 | const negate = result & 1; 130 | // Use unsigned right shift, so that the 32nd bit is properly shifted 131 | // to the 31st, and the 32nd becomes unset. 132 | result >>>= 1; 133 | if (negate) { 134 | // We need to OR 0x80000000 here to ensure the 32nd bit (the sign bit 135 | // in a 32bit int) is always set for negative numbers. If `result` 136 | // were 1, (meaning `negate` is true and all other bits were zeros), 137 | // `result` would now be 0. But -0 doesn't flip the 32nd bit as 138 | // intended. All other numbers will successfully set the 32nd bit 139 | // without issue, so doing this is a noop for them. 140 | return -result | 0x80000000; 141 | } 142 | return result; 143 | }, 144 | 145 | _VLQ_BASE_SHIFT: 5, 146 | _VLQ_BASE_MASK: (1 << 5) - 1, 147 | _VLQ_CONTINUATION_MASK: 1 << 5, 148 | }; 149 | 150 | SourceMap.StringCharIterator = function StringCharIterator(string) { 151 | this._string = string; 152 | this._position = 0; 153 | }; 154 | 155 | SourceMap.StringCharIterator.prototype = { 156 | /** 157 | * @return {string} 158 | */ 159 | next() { 160 | return this._string.charAt(this._position++); 161 | }, 162 | 163 | /** 164 | * @return {string} 165 | */ 166 | peek() { 167 | return this._string.charAt(this._position); 168 | }, 169 | 170 | /** 171 | * @return {boolean} 172 | */ 173 | hasNext() { 174 | return this._position < this._string.length; 175 | }, 176 | }; 177 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/benchmark/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 3 | const { readdirSync, readFileSync } = require('fs'); 4 | const { join, relative } = require('path'); 5 | const Benchmark = require('benchmark'); 6 | const localCode = require('../'); 7 | const latestSourcemapCodec = require('jridgewell-sourcemap-codec'); 8 | const originalSourcemapCodec = require('sourcemap-codec'); 9 | const sourceMap061 = require('source-map'); 10 | const sourceMapWasm = require('source-map-wasm'); 11 | const ChromeMap = require('./chrome').SourceMap; 12 | 13 | const dir = relative(process.cwd(), __dirname); 14 | const diff = !!process.env.DIFF; 15 | 16 | console.log(`node ${process.version}\n`); 17 | 18 | function track(label, results, cb) { 19 | if (global.gc) global.gc(); 20 | const before = process.memoryUsage(); 21 | const ret = cb(); 22 | const after = process.memoryUsage(); 23 | const d = delta(before, after); 24 | console.log( 25 | `${label.padEnd(35, ' ')} ${String(d.heapUsed + d.external).padStart(10, ' ')} bytes`, 26 | ); 27 | results.push({ label, delta: d.heapUsed + d.external }); 28 | return ret; 29 | } 30 | 31 | function delta(before, after) { 32 | return { 33 | rss: after.rss - before.rss, 34 | heapTotal: after.heapTotal - before.heapTotal, 35 | heapUsed: after.heapUsed - before.heapUsed, 36 | external: after.external - before.external, 37 | arrayBuffers: after.arrayBuffers - before.arrayBuffers, 38 | }; 39 | } 40 | 41 | async function bench(file) { 42 | const map = JSON.parse(readFileSync(join(dir, file))); 43 | const encoded = map.mappings; 44 | const decoded = localCode.decode(encoded); 45 | const consumer061 = new sourceMap061.SourceMapConsumer(map); 46 | const consumerWasm = await new sourceMapWasm.SourceMapConsumer(map); 47 | let bench; 48 | 49 | const segments = decoded.reduce((cur, line) => { 50 | return cur + line.length; 51 | }, 0); 52 | console.log(file, `- ${segments} segments`); 53 | console.log(''); 54 | 55 | { 56 | console.log('Decode Memory Usage:'); 57 | const results = []; 58 | track(diff ? 'local code' : '@jridgewell/sourcemap-codec', results, () => { 59 | return localCode.decode(encoded); 60 | }); 61 | if (diff) { 62 | track(`@jridgewell/sourcemap-codec @latest`, results, () => { 63 | return latestSourcemapCodec.decode(encoded); 64 | }); 65 | } else { 66 | track('sourcemap-codec', results, () => { 67 | return originalSourcemapCodec.decode(encoded); 68 | }); 69 | track('source-map-0.6.1', results, () => { 70 | consumer061._parseMappings(encoded, ''); 71 | return consumer061; 72 | }); 73 | track('source-map-0.8.0', results, () => { 74 | consumerWasm.destroy(); 75 | consumerWasm._parseMappings(encoded, ''); 76 | return consumerWasm; 77 | }); 78 | track('chrome dev tools', results, () => { 79 | new ChromeMap('url', map); 80 | }); 81 | } 82 | const winner = results.reduce((min, cur) => { 83 | if (cur.delta < min.delta) return cur; 84 | return min; 85 | }); 86 | console.log(`Smallest memory usage is ${winner.label}`); 87 | } 88 | 89 | console.log(''); 90 | 91 | console.log('Decode speed:'); 92 | bench = new Benchmark.Suite().add(diff ? 'local code' : '@jridgewell/sourcemap-codec', () => { 93 | localCode.decode(encoded); 94 | }); 95 | if (diff) { 96 | bench = bench.add(`@jridgewell/sourcemap-codec @latest`, () => { 97 | latestSourcemapCodec.decode(encoded); 98 | }); 99 | } else { 100 | bench = bench 101 | .add('sourcemap-codec', () => { 102 | originalSourcemapCodec.decode(encoded); 103 | }) 104 | .add('source-map-0.6.1', () => { 105 | consumer061._parseMappings(encoded, ''); 106 | }) 107 | .add('source-map-0.8.0', () => { 108 | consumerWasm.destroy(); 109 | consumerWasm._parseMappings(encoded, ''); 110 | }) 111 | .add('chrome dev tools', () => { 112 | new ChromeMap('url', map); 113 | }); 114 | } 115 | // add listeners 116 | bench 117 | .on('error', ({ error }) => console.error(error)) 118 | .on('cycle', (event) => { 119 | console.log(String(event.target)); 120 | }) 121 | .on('complete', function () { 122 | console.log('Fastest is ' + this.filter('fastest').map('name')); 123 | }) 124 | .run({}); 125 | 126 | console.log(''); 127 | 128 | const generator061 = sourceMap061.SourceMapGenerator.fromSourceMap(consumer061); 129 | const generatorWasm = sourceMapWasm.SourceMapGenerator.fromSourceMap( 130 | await new sourceMapWasm.SourceMapConsumer(map), 131 | ); 132 | 133 | { 134 | console.log('Encode Memory Usage:'); 135 | const results = []; 136 | track(diff ? 'local code' : '@jridgewell/sourcemap-codec', results, () => { 137 | return localCode.encode(decoded); 138 | }); 139 | if (diff) { 140 | track(`@jridgewell/sourcemap-codec @latest`, results, () => { 141 | return latestSourcemapCodec.encode(decoded); 142 | }); 143 | } else { 144 | track('sourcemap-codec', results, () => { 145 | return originalSourcemapCodec.encode(decoded); 146 | }); 147 | track('source-map-0.6.1', results, () => { 148 | return generator061._serializeMappings(); 149 | }); 150 | track('source-map-0.8.0', results, () => { 151 | return generatorWasm._serializeMappings(); 152 | }); 153 | } 154 | const winner = results.reduce((min, cur) => { 155 | if (cur.delta < min.delta) return cur; 156 | return min; 157 | }); 158 | console.log(`Smallest memory usage is ${winner.label}`); 159 | } 160 | 161 | console.log(''); 162 | 163 | console.log('Encode speed:'); 164 | bench = new Benchmark.Suite().add(diff ? 'local code' : '@jridgewell/sourcemap-codec', () => { 165 | localCode.encode(decoded); 166 | }); 167 | if (diff) { 168 | bench = bench.add(`@jridgewell/sourcemap-codec @latest`, () => { 169 | latestSourcemapCodec.encode(decoded); 170 | }); 171 | } else { 172 | bench = bench 173 | .add('sourcemap-codec', () => { 174 | originalSourcemapCodec.encode(decoded); 175 | }) 176 | .add('source-map-0.6.1', () => { 177 | generator061._serializeMappings(); 178 | }) 179 | .add('source-map-0.8.0', () => { 180 | generatorWasm._serializeMappings(); 181 | }); 182 | } 183 | // add listeners 184 | bench 185 | .on('error', ({ error }) => console.error(error)) 186 | .on('cycle', (event) => { 187 | console.log(String(event.target)); 188 | }) 189 | .on('complete', function () { 190 | console.log('Fastest is ' + this.filter('fastest').map('name')); 191 | }) 192 | .run({}); 193 | } 194 | 195 | (async () => { 196 | const files = readdirSync(dir); 197 | let first = true; 198 | for (let i = 0; i < files.length; i++) { 199 | const file = files[i]; 200 | if (!file.endsWith('.map')) continue; 201 | 202 | if (!first) console.log('\n\n***\n\n'); 203 | first = false; 204 | 205 | await bench(file); 206 | } 207 | })(); 208 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarks", 3 | "devDependencies": { 4 | "jridgewell-sourcemap-codec": "npm:@jridgewell/sourcemap-codec@latest", 5 | "sourcemap-codec": "1.4.8", 6 | "source-map": "0.6.1", 7 | "source-map-wasm": "npm:source-map@0.8.0-beta.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@jridgewell/sourcemap-codec", 3 | "version": "1.5.0", 4 | "description": "Encode/decode sourcemap mappings", 5 | "keywords": [ 6 | "sourcemap", 7 | "vlq" 8 | ], 9 | "main": "dist/sourcemap-codec.umd.js", 10 | "module": "dist/sourcemap-codec.mjs", 11 | "types": "types/sourcemap-codec.d.ts", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "types" 16 | ], 17 | "exports": { 18 | ".": [ 19 | { 20 | "types": "./types/sourcemap-codec.d.ts", 21 | "browser": "./dist/sourcemap-codec.umd.js", 22 | "require": "./dist/sourcemap-codec.umd.js", 23 | "import": "./dist/sourcemap-codec.mjs" 24 | }, 25 | "./dist/sourcemap-codec.umd.js" 26 | ], 27 | "./package.json": "./package.json" 28 | }, 29 | "scripts": { 30 | "benchmark": "run-s build:code benchmark:*", 31 | "benchmark:install": "cd benchmark && npm install", 32 | "benchmark:only": "node --expose-gc benchmark/index.js", 33 | "build": "run-s -n build:code build:types", 34 | "build:code": "node ../../esbuild.mjs sourcemap-codec.ts", 35 | "build:types": "tsc --project tsconfig.build.json", 36 | "test": "run-s -n test:types test:only test:format", 37 | "test:format": "prettier --check '{src,test}/**/*.ts'", 38 | "test:only": "mocha", 39 | "test:types": "eslint '{src,test}/**/*.ts'", 40 | "lint": "run-s -n lint:types lint:format", 41 | "lint:format": "npm run test:format -- --write", 42 | "lint:types": "npm run test:types -- --fix", 43 | "prepublishOnly": "npm run-s -n build test" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/jridgewell/sourcemaps.git", 48 | "directory": "packages/sourcemap-codec" 49 | }, 50 | "author": "Justin Ridgewell ", 51 | "license": "MIT" 52 | } 53 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/src/sourcemap-codec.ts: -------------------------------------------------------------------------------- 1 | import { comma, decodeInteger, encodeInteger, hasMoreVlq, semicolon } from './vlq'; 2 | import { StringWriter, StringReader } from './strings'; 3 | 4 | export { 5 | decodeOriginalScopes, 6 | encodeOriginalScopes, 7 | decodeGeneratedRanges, 8 | encodeGeneratedRanges, 9 | } from './scopes'; 10 | export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes'; 11 | 12 | export type SourceMapSegment = 13 | | [number] 14 | | [number, number, number, number] 15 | | [number, number, number, number, number]; 16 | export type SourceMapLine = SourceMapSegment[]; 17 | export type SourceMapMappings = SourceMapLine[]; 18 | 19 | export function decode(mappings: string): SourceMapMappings { 20 | const { length } = mappings; 21 | const reader = new StringReader(mappings); 22 | const decoded: SourceMapMappings = []; 23 | let genColumn = 0; 24 | let sourcesIndex = 0; 25 | let sourceLine = 0; 26 | let sourceColumn = 0; 27 | let namesIndex = 0; 28 | 29 | do { 30 | const semi = reader.indexOf(';'); 31 | const line: SourceMapLine = []; 32 | let sorted = true; 33 | let lastCol = 0; 34 | genColumn = 0; 35 | 36 | while (reader.pos < semi) { 37 | let seg: SourceMapSegment; 38 | 39 | genColumn = decodeInteger(reader, genColumn); 40 | if (genColumn < lastCol) sorted = false; 41 | lastCol = genColumn; 42 | 43 | if (hasMoreVlq(reader, semi)) { 44 | sourcesIndex = decodeInteger(reader, sourcesIndex); 45 | sourceLine = decodeInteger(reader, sourceLine); 46 | sourceColumn = decodeInteger(reader, sourceColumn); 47 | 48 | if (hasMoreVlq(reader, semi)) { 49 | namesIndex = decodeInteger(reader, namesIndex); 50 | seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]; 51 | } else { 52 | seg = [genColumn, sourcesIndex, sourceLine, sourceColumn]; 53 | } 54 | } else { 55 | seg = [genColumn]; 56 | } 57 | 58 | line.push(seg); 59 | reader.pos++; 60 | } 61 | 62 | if (!sorted) sort(line); 63 | decoded.push(line); 64 | reader.pos = semi + 1; 65 | } while (reader.pos <= length); 66 | 67 | return decoded; 68 | } 69 | 70 | function sort(line: SourceMapSegment[]) { 71 | line.sort(sortComparator); 72 | } 73 | 74 | function sortComparator(a: SourceMapSegment, b: SourceMapSegment): number { 75 | return a[0] - b[0]; 76 | } 77 | 78 | export function encode(decoded: SourceMapMappings): string; 79 | export function encode(decoded: Readonly): string; 80 | export function encode(decoded: Readonly): string { 81 | const writer = new StringWriter(); 82 | let sourcesIndex = 0; 83 | let sourceLine = 0; 84 | let sourceColumn = 0; 85 | let namesIndex = 0; 86 | 87 | for (let i = 0; i < decoded.length; i++) { 88 | const line = decoded[i]; 89 | if (i > 0) writer.write(semicolon); 90 | if (line.length === 0) continue; 91 | 92 | let genColumn = 0; 93 | 94 | for (let j = 0; j < line.length; j++) { 95 | const segment = line[j]; 96 | if (j > 0) writer.write(comma); 97 | 98 | genColumn = encodeInteger(writer, segment[0], genColumn); 99 | 100 | if (segment.length === 1) continue; 101 | sourcesIndex = encodeInteger(writer, segment[1], sourcesIndex); 102 | sourceLine = encodeInteger(writer, segment[2], sourceLine); 103 | sourceColumn = encodeInteger(writer, segment[3], sourceColumn); 104 | 105 | if (segment.length === 4) continue; 106 | namesIndex = encodeInteger(writer, segment[4], namesIndex); 107 | } 108 | } 109 | 110 | return writer.flush(); 111 | } 112 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/src/strings.ts: -------------------------------------------------------------------------------- 1 | const bufLength = 1024 * 16; 2 | 3 | // Provide a fallback for older environments. 4 | const td = 5 | typeof TextDecoder !== 'undefined' 6 | ? /* #__PURE__ */ new TextDecoder() 7 | : typeof Buffer !== 'undefined' 8 | ? { 9 | decode(buf: Uint8Array): string { 10 | const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); 11 | return out.toString(); 12 | }, 13 | } 14 | : { 15 | decode(buf: Uint8Array): string { 16 | let out = ''; 17 | for (let i = 0; i < buf.length; i++) { 18 | out += String.fromCharCode(buf[i]); 19 | } 20 | return out; 21 | }, 22 | }; 23 | 24 | export class StringWriter { 25 | pos = 0; 26 | private out = ''; 27 | private buffer = new Uint8Array(bufLength); 28 | 29 | write(v: number): void { 30 | const { buffer } = this; 31 | buffer[this.pos++] = v; 32 | if (this.pos === bufLength) { 33 | this.out += td.decode(buffer); 34 | this.pos = 0; 35 | } 36 | } 37 | 38 | flush(): string { 39 | const { buffer, out, pos } = this; 40 | return pos > 0 ? out + td.decode(buffer.subarray(0, pos)) : out; 41 | } 42 | } 43 | 44 | export class StringReader { 45 | pos = 0; 46 | private declare buffer: string; 47 | 48 | constructor(buffer: string) { 49 | this.buffer = buffer; 50 | } 51 | 52 | next(): number { 53 | return this.buffer.charCodeAt(this.pos++); 54 | } 55 | 56 | peek(): number { 57 | return this.buffer.charCodeAt(this.pos); 58 | } 59 | 60 | indexOf(char: string): number { 61 | const { buffer, pos } = this; 62 | const idx = buffer.indexOf(char, pos); 63 | return idx === -1 ? buffer.length : idx; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/src/vlq.ts: -------------------------------------------------------------------------------- 1 | import type { StringReader, StringWriter } from './strings'; 2 | 3 | export const comma = ','.charCodeAt(0); 4 | export const semicolon = ';'.charCodeAt(0); 5 | 6 | const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 7 | const intToChar = new Uint8Array(64); // 64 possible chars. 8 | const charToInt = new Uint8Array(128); // z is 122 in ASCII 9 | 10 | for (let i = 0; i < chars.length; i++) { 11 | const c = chars.charCodeAt(i); 12 | intToChar[i] = c; 13 | charToInt[c] = i; 14 | } 15 | 16 | export function decodeInteger(reader: StringReader, relative: number): number { 17 | let value = 0; 18 | let shift = 0; 19 | let integer = 0; 20 | 21 | do { 22 | const c = reader.next(); 23 | integer = charToInt[c]; 24 | value |= (integer & 31) << shift; 25 | shift += 5; 26 | } while (integer & 32); 27 | 28 | const shouldNegate = value & 1; 29 | value >>>= 1; 30 | 31 | if (shouldNegate) { 32 | value = -0x80000000 | -value; 33 | } 34 | 35 | return relative + value; 36 | } 37 | 38 | export function encodeInteger(builder: StringWriter, num: number, relative: number): number { 39 | let delta = num - relative; 40 | 41 | delta = delta < 0 ? (-delta << 1) | 1 : delta << 1; 42 | do { 43 | let clamped = delta & 0b011111; 44 | delta >>>= 5; 45 | if (delta > 0) clamped |= 0b100000; 46 | builder.write(intToChar[clamped]); 47 | } while (delta > 0); 48 | 49 | return num; 50 | } 51 | 52 | export function hasMoreVlq(reader: StringReader, max: number) { 53 | if (reader.pos >= max) return false; 54 | return reader.peek() !== comma; 55 | } 56 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/test/codec.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | import { encode, decode, type SourceMapMappings } from '../src/sourcemap-codec'; 4 | import assert from 'node:assert/strict'; 5 | import * as fs from 'node:fs'; 6 | import * as path from 'node:path'; 7 | import { fileURLToPath } from 'node:url'; 8 | 9 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 10 | 11 | describe('sourcemap-codec', () => { 12 | // TODO more tests 13 | let tests: { encoded: string; decoded: SourceMapMappings; only?: true }[] = [ 14 | { 15 | encoded: 'AAAA', 16 | decoded: [[[0, 0, 0, 0]]], 17 | }, 18 | { 19 | encoded: ';;;', 20 | decoded: [[], [], [], []], 21 | }, 22 | { 23 | encoded: 'A,AAAC;;AACDE;', 24 | decoded: [[[0], [0, 0, 0, 1]], [], [[0, 0, 1, 0, 2]], []], 25 | }, 26 | { 27 | encoded: ';;;;EAEEA,EAAE,EAAC,CAAE;ECQY,UACC', 28 | decoded: [ 29 | [], 30 | [], 31 | [], 32 | [], 33 | [ 34 | [2, 0, 2, 2, 0], 35 | [4, 0, 2, 4], 36 | [6, 0, 2, 5], 37 | [7, 0, 2, 7], 38 | ], 39 | [ 40 | [2, 1, 10, 19], 41 | [12, 1, 11, 20], 42 | ], 43 | ], 44 | }, 45 | { 46 | encoded: 47 | 'AAAA,aAEA,IAAIA,eAAiB,oCAAoCC,cACzD,SAASC,IACRC,OAAOC,MAAOJ,eAAgB,GAG/B,IAAIK,iBAAmB,sBAAsBJ,cAC7C,SAASK,IACRH,OAAOC,MAAOC,iBAAkB,GAGjCH,IACAI', 48 | decoded: [ 49 | [ 50 | [0, 0, 0, 0], 51 | [13, 0, 2, 0], 52 | [17, 0, 2, 4, 0], 53 | [32, 0, 2, 21], 54 | [68, 0, 2, 57, 1], 55 | [82, 0, 3, 0], 56 | [91, 0, 3, 9, 2], 57 | [95, 0, 4, 1, 3], 58 | [102, 0, 4, 8, 4], 59 | [108, 0, 4, 15, 0], 60 | [123, 0, 4, 31], 61 | [126, 0, 7, 0], 62 | [130, 0, 7, 4, 5], 63 | [147, 0, 7, 23], 64 | [169, 0, 7, 45, 1], 65 | [183, 0, 8, 0], 66 | [192, 0, 8, 9, 6], 67 | [196, 0, 9, 1, 3], 68 | [203, 0, 9, 8, 4], 69 | [209, 0, 9, 15, 5], 70 | [226, 0, 9, 33], 71 | [229, 0, 12, 0, 2], 72 | [233, 0, 13, 0, 6], 73 | ], 74 | ], 75 | }, 76 | { 77 | encoded: 78 | 'CAAC,SAAUA,EAAQC,GACC,iBAAZC,SAA0C,oBAAXC,OAAyBF,IAC7C,mBAAXG,QAAyBA,OAAOC,IAAMD,OAAOH,GACnDA,IAHF,CAIEK,EAAM,WAAe,aAEtB,IAAIC,EAAiB,oCAAoCC,cAKzD,IAAIC,EAAmB,sBAAsBD,cAH5CE,OAAOC,MAAOJ,EAAgB,GAK9BG,OAAOC,MAAOF,EAAkB', 79 | decoded: [ 80 | [ 81 | [1, 0, 0, 1], 82 | [10, 0, 0, 11, 0], 83 | [12, 0, 0, 19, 1], 84 | [15, 0, 1, 20], 85 | [32, 0, 1, 8, 2], 86 | [41, 0, 1, 50], 87 | [61, 0, 1, 39, 3], 88 | [68, 0, 1, 64, 1], 89 | [72, 0, 2, 19], 90 | [91, 0, 2, 8, 4], 91 | [99, 0, 2, 33, 4], 92 | [106, 0, 2, 40, 5], 93 | [110, 0, 2, 46, 4], 94 | [117, 0, 2, 53, 1], 95 | [120, 0, 3, 2, 1], 96 | [124, 0, 0, 0], 97 | [125, 0, 4, 2, 6], 98 | [127, 0, 4, 8], 99 | [138, 0, 4, 23], 100 | [151, 0, 6, 1], 101 | [155, 0, 6, 5, 7], 102 | [157, 0, 6, 22], 103 | [193, 0, 6, 58, 8], 104 | [207, 0, 11, 1], 105 | [211, 0, 11, 5, 9], 106 | [213, 0, 11, 24], 107 | [235, 0, 11, 46, 8], 108 | [249, 0, 8, 2, 10], 109 | [256, 0, 8, 9, 11], 110 | [262, 0, 8, 16, 7], 111 | [264, 0, 8, 32], 112 | [267, 0, 13, 2, 10], 113 | [274, 0, 13, 9, 11], 114 | [280, 0, 13, 16, 9], 115 | [282, 0, 13, 34], 116 | ], 117 | ], 118 | }, 119 | { 120 | // Make sure Int16 isn't being used 121 | encoded: 'gw+BAAAA,w+BAAAA,w+BAAAA,w+BAAAA', 122 | decoded: [ 123 | [ 124 | [32000, 0, 0, 0, 0], 125 | [33000, 0, 0, 0, 0], 126 | [34000, 0, 0, 0, 0], 127 | [35000, 0, 0, 0, 0], 128 | ], 129 | ], 130 | }, 131 | { 132 | // Handle largest 32bit int 133 | encoded: '+/////D', 134 | decoded: [[[2147483647]]], 135 | }, 136 | { 137 | // Handle smallest 32bit int 138 | encoded: 'AAA+/////D,AAAE,AAAB', 139 | decoded: [ 140 | [ 141 | [0, 0, 0, 2147483647], 142 | [0, 0, 0, 2147483649], 143 | [0, 0, 0, 1], 144 | ], 145 | ], 146 | }, 147 | ]; 148 | 149 | const filtered = tests.filter((test) => { 150 | return test.only; 151 | }); 152 | 153 | tests = filtered.length ? filtered : tests; 154 | 155 | describe('decode()', () => { 156 | tests.forEach((test, i) => { 157 | it('decodes sample ' + i, () => { 158 | assert.deepEqual(decode(test.encoded), test.decoded); 159 | }); 160 | }); 161 | 162 | it('sorts during decoding', () => { 163 | const mappings: SourceMapMappings = [ 164 | [[1], [2], [3]], 165 | [[1], [3], [2]], 166 | [[2], [1], [3]], 167 | [[2], [3], [1]], 168 | [[3], [1], [2]], 169 | [[3], [2], [1]], 170 | ]; 171 | mappings.sort(() => (Math.random() < 0.5 ? 1 : -1)); 172 | 173 | const decoded = decode(encode(mappings)); 174 | const expected = mappings.map(() => { 175 | return [[1], [2], [3]]; 176 | }); 177 | 178 | assert.deepEqual(decoded, expected); 179 | }); 180 | }); 181 | 182 | describe('encode()', () => { 183 | tests.forEach((test, i) => { 184 | it('encodes sample ' + i, () => { 185 | assert.deepEqual(encode(test.decoded), test.encoded); 186 | }); 187 | }); 188 | }); 189 | 190 | describe('real world', () => { 191 | const dir = path.join(__dirname, '..', 'benchmark'); 192 | const files = fs.readdirSync(dir); 193 | files.forEach((file) => { 194 | if (path.extname(file) !== '.map') return; 195 | 196 | it(file, function () { 197 | this.timeout(5000); 198 | const { mappings } = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8')); 199 | assert.deepEqual(encode(decode(mappings)), mappings); 200 | }); 201 | }); 202 | }); 203 | }); 204 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/sourcemap-codec/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "types", 5 | "outDir": "dist" 6 | }, 7 | "include": ["src", "test"], 8 | "exclude": ["dist"] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /packages/trace-mapping/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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": "types/trace-mapping.d.ts", 12 | "files": [ 13 | "dist", 14 | "src", 15 | "types" 16 | ], 17 | "exports": { 18 | ".": [ 19 | { 20 | "types": "./types/trace-mapping.d.ts", 21 | "browser": "./dist/trace-mapping.umd.js", 22 | "require": "./dist/trace-mapping.umd.js", 23 | "import": "./dist/trace-mapping.mjs" 24 | }, 25 | "./dist/trace-mapping.umd.js" 26 | ], 27 | "./package.json": "./package.json" 28 | }, 29 | "scripts": { 30 | "benchmark": "run-s build:code benchmark:*", 31 | "benchmark:install": "cd benchmark && npm install", 32 | "benchmark:only": "node --expose-gc benchmark/index.js", 33 | "build": "run-s -n build:code build:types", 34 | "build:code": "node ../../esbuild.mjs trace-mapping.ts", 35 | "build:types": "tsc --project tsconfig.build.json", 36 | "test": "run-s -n test:types test:only test:format", 37 | "test:format": "prettier --check '{src,test}/**/*.ts'", 38 | "test:only": "mocha", 39 | "test:types": "eslint '{src,test}/**/*.ts'", 40 | "lint": "run-s -n lint:types lint:format", 41 | "lint:format": "npm run test:format -- --write", 42 | "lint:types": "npm run test:types -- --fix", 43 | "prepublishOnly": "npm run-s -n build test" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/jridgewell/sourcemaps.git", 48 | "directory": "packages/trace-mapping" 49 | }, 50 | "author": "Justin Ridgewell ", 51 | "license": "MIT", 52 | "dependencies": { 53 | "@jridgewell/resolve-uri": "^3.1.0", 54 | "@jridgewell/sourcemap-codec": "^1.4.14" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 | -------------------------------------------------------------------------------- /packages/trace-mapping/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 = 104 | T extends Array 105 | ? V[] | Readonly | RoArray | Readonly> 106 | : T extends object 107 | ? T | Readonly | RoObject | Readonly> 108 | : T; 109 | type RoArray = Ro[]; 110 | type RoObject = { [K in keyof T]: T[K] | Ro }; 111 | 112 | export function parse(map: T): Exclude { 113 | return typeof map === 'string' ? JSON.parse(map) : map; 114 | } 115 | -------------------------------------------------------------------------------- /packages/trace-mapping/test/flatten-map.test.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import assert from 'node:assert/strict'; 4 | import { 5 | FlattenMap, 6 | encodedMappings, 7 | decodedMappings, 8 | type SectionedSourceMap, 9 | type SourceMapSegment, 10 | } from '../src/trace-mapping'; 11 | 12 | describe('FlattenMap', () => { 13 | const map: SectionedSourceMap = { 14 | version: 3, 15 | file: 'sectioned.js', 16 | sections: [ 17 | { 18 | offset: { line: 1, column: 1 }, 19 | map: { 20 | version: 3, 21 | sections: [ 22 | { 23 | offset: { line: 0, column: 1 }, 24 | map: { 25 | version: 3, 26 | names: ['first'], 27 | sources: ['first.js'], 28 | sourcesContent: ['firstsource'], 29 | mappings: 'AAAAA,CAAC', 30 | ignoreList: [0], 31 | }, 32 | }, 33 | { 34 | offset: { line: 0, column: 2 }, 35 | map: { 36 | version: 3, 37 | names: ['second'], 38 | sources: ['second.js'], 39 | sourcesContent: ['secondsource'], 40 | mappings: 'AAAAA;AAAA', 41 | }, 42 | }, 43 | ], 44 | }, 45 | }, 46 | { 47 | offset: { line: 2, column: 0 }, 48 | map: { 49 | version: 3, 50 | sections: [ 51 | { 52 | offset: { line: 0, column: 0 }, 53 | map: { 54 | version: 3, 55 | names: ['third'], 56 | sources: ['third.js'], 57 | sourcesContent: ['thirdsource'], 58 | sourceRoot: 'nested', 59 | mappings: 'AAAAA,CAAA;AAAA', 60 | }, 61 | }, 62 | { 63 | offset: { line: 0, column: 1 }, 64 | map: { 65 | version: 3, 66 | names: [], 67 | sources: ['fourth.js'], 68 | sourcesContent: ['fourthsource'], 69 | mappings: [[[0, 0, 0, 0]]], 70 | ignoreList: [0], 71 | }, 72 | }, 73 | ], 74 | }, 75 | }, 76 | ], 77 | }; 78 | 79 | describe('map properties', () => { 80 | it('version', () => { 81 | const tracer = new FlattenMap(map); 82 | assert.equal(tracer.version, map.version); 83 | }); 84 | 85 | it('file', () => { 86 | const tracer = new FlattenMap(map); 87 | assert.equal(tracer.file, map.file); 88 | }); 89 | 90 | it('sourceRoot', () => { 91 | const tracer = new FlattenMap(map); 92 | assert.equal(tracer.sourceRoot, undefined); 93 | }); 94 | 95 | it('sources', () => { 96 | const tracer = new FlattenMap(map); 97 | assert.deepEqual(tracer.sources, ['first.js', 'second.js', 'nested/third.js', 'fourth.js']); 98 | }); 99 | 100 | it('names', () => { 101 | const tracer = new FlattenMap(map); 102 | assert.deepEqual(tracer.names, ['first', 'second', 'third']); 103 | }); 104 | 105 | it('encodedMappings', () => { 106 | const tracer = new FlattenMap(map); 107 | assert.equal(encodedMappings(tracer), ';EAAAA,CCAAC;ACAAC,CCAA'); 108 | }); 109 | 110 | it('decodedMappings', () => { 111 | const tracer = new FlattenMap(map); 112 | assert.deepEqual(decodedMappings(tracer), [ 113 | [], 114 | [ 115 | [2, 0, 0, 0, 0], 116 | [3, 1, 0, 0, 1], 117 | ], 118 | [ 119 | [0, 2, 0, 0, 2], 120 | [1, 3, 0, 0], 121 | ], 122 | ]); 123 | }); 124 | 125 | it('sourcesContent', () => { 126 | const tracer = new FlattenMap(map); 127 | assert.deepEqual(tracer.sourcesContent, [ 128 | 'firstsource', 129 | 'secondsource', 130 | 'thirdsource', 131 | 'fourthsource', 132 | ]); 133 | }); 134 | 135 | it('ignoreList', () => { 136 | const tracer = new FlattenMap(map); 137 | assert.deepEqual(tracer.ignoreList, [0, 3]); 138 | }); 139 | }); 140 | 141 | describe('typescript readonly type', () => { 142 | it('decoded source map', () => { 143 | // This is a TS lint test, not a real one. 144 | const decodedMap = { 145 | version: 3 as const, 146 | sources: ['input.js'] as readonly string[], 147 | names: [] as readonly string[], 148 | mappings: [] as readonly SourceMapSegment[][], 149 | sourcesContent: [] as readonly string[], 150 | }; 151 | 152 | new FlattenMap(decodedMap); 153 | }); 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /packages/trace-mapping/test/resolve.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict'; 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 | -------------------------------------------------------------------------------- /packages/trace-mapping/test/strip-filename.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert/strict'; 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 | -------------------------------------------------------------------------------- /packages/trace-mapping/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "emitDeclarationOnly": true, 5 | "rootDir": "src" 6 | }, 7 | "include": ["src"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/trace-mapping/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "declarationDir": "types", 5 | "outDir": "dist" 6 | }, 7 | "include": ["src", "test"], 8 | "exclude": ["dist"] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | endOfLine: 'lf', 3 | printWidth: 100, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "esModuleInterop": true, 6 | "lib": ["es2015", "dom"], 7 | "module": "esnext", 8 | "moduleResolution": "bundler", 9 | "strict": true, 10 | "target": "es2015", 11 | "typeRoots": ["node_modules/@types"] 12 | }, 13 | "references": [{ "path": "packages/sourcemap-codec" }] 14 | } 15 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "benchmark": {}, 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "inputs": ["../../esbuild.mjs", "../../tsconfig.json"], 7 | "outputs": ["dist/**"] 8 | }, 9 | "test": { 10 | "dependsOn": ["^build"] 11 | }, 12 | "lint": { 13 | "inputs": ["../../prettier.config.js", "../../tsconfig.json"] 14 | }, 15 | "dev": { "cache": false } 16 | }, 17 | "globalDependencies": ["tsconfig.json"] 18 | } 19 | --------------------------------------------------------------------------------