Skip to content

Commit 70d5f5f

Browse files
author
pooya parsa
committed
feat: source-map support
1 parent 6cc37c5 commit 70d5f5f

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ function createBundle(bundle: Partial<Bundle> | string, options?: CreateBundleOp
4646
bundle: Bundle;
4747
evaluateEntry: (context: object) => any;
4848
evaluateModule: (filename: string, context: object) => any;
49+
rewriteErrorTrace: (err: Error) => Promise<Error>;
4950
}
5051
```
5152

@@ -76,6 +77,22 @@ type Bundle = {
7677
}
7778
```
7879
80+
### SourceMap Support
81+
82+
After creating bundle, a `rewriteErrorTrace` utility is exposed which you can use to rewrite traces:
83+
84+
```ts
85+
const { evaluateEntry, rewriteErrorTrace } = createBundle('path/to/bundle.json')
86+
87+
try {
88+
const entry = evaluateEntry(context)
89+
const app = await entry({})
90+
} catch (err) {
91+
await rewriteErrorTrace(err)
92+
throw err
93+
}
94+
```
95+
7996
## Credits
8097

8198
Inspired by [vue-server-renderer](https://www.npmjs.com/package/vue-server-renderer) made by [Evan You](https://github.com/yyx990803).

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
"test": "yarn lint && yarn jest",
2020
"types": "tsc --declarationDir types --declaration --emitDeclarationOnly"
2121
},
22+
"dependencies": {
23+
"source-map": "^0.7.3"
24+
},
2225
"devDependencies": {
2326
"@nuxtjs/eslint-config-typescript": "latest",
2427
"@rollup/plugin-typescript": "latest",

rollup.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import typescript from '@rollup/plugin-typescript'
44
export default [
55
{
66
input: './src/index.ts',
7-
external: NativeModule.builtinModules,
7+
external: [
8+
...NativeModule.builtinModules,
9+
'source-map'
10+
],
811
output: [
912
{ file: './dist/bundle-runner.cjs.js', format: 'cjs' },
1013
{ file: './dist/bundle-runner.esm.js', format: 'esm' }

src/bundle.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { isAbsolute, dirname } from 'path'
22
import fs from 'fs'
33
import { CreateEvaluateOptions, createEvaluateModule } from './module'
4+
import { createSourceMap } from './source-map'
45

56
export type Files = { [name: string]: any }
67

@@ -69,6 +70,7 @@ function loadBundle (bundle: Partial<Bundle> | string, basedir?: string): Bundle
6970

7071
export function createBundle (_bundle: Partial<Bundle> | string, options: CreateBundleOptions = {}) {
7172
const bundle = loadBundle(_bundle, options.basedir)
73+
const { rewriteErrorTrace } = createSourceMap(bundle.maps)
7274

7375
const evaluateModule = createEvaluateModule(bundle.files, options)
7476

@@ -79,6 +81,7 @@ export function createBundle (_bundle: Partial<Bundle> | string, options: Create
7981
return {
8082
bundle,
8183
evaluateModule,
82-
evaluateEntry
84+
evaluateEntry,
85+
rewriteErrorTrace
8386
}
8487
}

src/source-map.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { SourceMapConsumer } from 'source-map'
2+
3+
export type RawSourceMaps = {
4+
[source: string]: string
5+
}
6+
7+
type SourceMapConsumers = { [source: string]: Promise<SourceMapConsumer> }
8+
9+
const filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/
10+
const webpackRE = /^webpack:\/\/\//
11+
12+
export function createSourceMap (rawMaps: RawSourceMaps = {}) {
13+
const _consumersCache: SourceMapConsumers = {}
14+
15+
function getConsumer (source: string) {
16+
const rawMap = rawMaps[source]
17+
if (!rawMap) {
18+
return
19+
}
20+
if (!_consumersCache[source]) {
21+
_consumersCache[source] = Promise.resolve(new SourceMapConsumer(rawMap))
22+
}
23+
return _consumersCache[source]
24+
}
25+
26+
async function rewriteTraceLine (_trace: string) {
27+
const m = _trace.match(filenameRE)
28+
29+
if (!m) {
30+
return _trace
31+
}
32+
33+
const consumer = await getConsumer(m[1])
34+
if (!consumer) {
35+
return _trace
36+
}
37+
38+
const originalPosition = consumer.originalPositionFor({
39+
line: Number(m[2]),
40+
column: Number(m[3])
41+
})
42+
43+
if (!originalPosition.source) {
44+
return _trace
45+
}
46+
47+
const { source, line, column } = originalPosition
48+
const mappedPosition = `(${source.replace(webpackRE, '')}:${line}:${column})`
49+
const trace = _trace.replace(filenameRE, mappedPosition)
50+
return trace
51+
}
52+
53+
async function rewriteErrorTrace (err: Error): Promise<Error> {
54+
if (err && typeof err.stack === 'string') {
55+
const stack = err.stack.split('\n')
56+
const newStack = await Promise.all(stack.map(rewriteTraceLine))
57+
err.stack = newStack.join('\n')
58+
}
59+
return err
60+
}
61+
62+
return {
63+
rewriteErrorTrace
64+
}
65+
}

0 commit comments

Comments
 (0)