Skip to content

Commit 459a21b

Browse files
committed
feat: source-map-explorer
1 parent a9d84cd commit 459a21b

File tree

8 files changed

+229
-13
lines changed

8 files changed

+229
-13
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
require('../build/index');

packages/haul-explore/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"version": "0.15.0",
3+
"name": "@haul-bundler/explore",
4+
"description": "Source map explorer",
5+
"files": [
6+
"build/",
7+
"bin/"
8+
],
9+
"bin": {
10+
"haul-explore": "bin/haul-explore.js"
11+
},
12+
"engines": {
13+
"node": ">=10.x"
14+
},
15+
"dependencies": {
16+
"@babel/core": "7.4.3",
17+
"@babel/plugin-proposal-class-properties": "7.4.0",
18+
"@babel/plugin-transform-flow-strip-types": "7.4.0",
19+
"@babel/preset-env": "7.4.3",
20+
"@babel/preset-typescript": "7.3.3",
21+
"hasha": "^5.0.0",
22+
"metro": "^0.56.4",
23+
"source-map": "^0.7.3",
24+
"source-map-explorer": "^3.2.2",
25+
"yargs": "^15.1.0"
26+
},
27+
"devDependencies": {
28+
"@types/btoa": "^1.2.3",
29+
"@types/chai": "^4.2.7",
30+
"@types/chai-as-promised": "^7.1.2",
31+
"@types/concat-stream": "^1.6.0",
32+
"@types/convert-source-map": "^1.5.1",
33+
"@types/cross-spawn": "^6.0.1",
34+
"@types/ejs": "^3.0.0",
35+
"@types/escape-html": "0.0.20",
36+
"@types/glob": "^7.1.1",
37+
"@types/lodash": "^4.14.149",
38+
"@types/node": "^13.x",
39+
"@types/rimraf": "^2.0.3",
40+
"@types/temp": "^0.8.34",
41+
"@types/yargs": "^15.0.0"
42+
}
43+
}

packages/haul-explore/src/index.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import yargs from 'yargs';
2+
import {
3+
explore,
4+
writeHtmlToTempFile,
5+
ExploreOptions,
6+
} from 'source-map-explorer';
7+
import sourceMapForRamBundle from './ram-bundle';
8+
// eslint-disable-next-line import/no-extraneous-dependencies
9+
import RamBundleParser from 'metro/src/lib/RamBundleParser';
10+
import path from 'path';
11+
12+
import fs from 'fs';
13+
14+
const argv = yargs
15+
.strict()
16+
.scriptName('explore')
17+
.demandCommand(2, 'bundle and source map files must be specified')
18+
.options({
19+
json: {
20+
type: 'string',
21+
description:
22+
'If filename specified save output as JSON to specified file otherwise output to stdout.',
23+
conflicts: ['tsv', 'html'],
24+
},
25+
tsv: {
26+
type: 'string',
27+
description:
28+
'If filename specified save output as TSV to specified file otherwise output to stdout.',
29+
conflicts: ['json', 'html'],
30+
},
31+
html: {
32+
type: 'string',
33+
description:
34+
'If filename specified save output as HTML to specified file otherwise output to stdout rather than opening a browser.',
35+
conflicts: ['json', 'tsv'],
36+
},
37+
})
38+
.group(['json', 'tsv', 'html'], 'Output:')
39+
.parse();
40+
41+
const bundle = path.resolve(argv._[0]);
42+
const sourceMap = path.resolve(argv._[1]);
43+
const options: ExploreOptions = {
44+
output: {
45+
format:
46+
typeof argv.json === 'string'
47+
? 'json'
48+
: typeof argv.tsv === 'string'
49+
? 'tsv'
50+
: 'html',
51+
},
52+
};
53+
54+
try {
55+
const bundleFile = fs.readFileSync(bundle);
56+
new RamBundleParser(bundleFile);
57+
sourceMapForRamBundle(bundle, sourceMap, options, false);
58+
} catch (err) {
59+
if (path.basename(bundle) === 'UNBUNDLE') {
60+
sourceMapForRamBundle(bundle, sourceMap, options, true);
61+
} else {
62+
explore([bundle, sourceMap], options)
63+
.then(result => {
64+
return writeHtmlToTempFile(result.output);
65+
})
66+
.catch(err => console.log(err));
67+
}
68+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import RamBundleParser from 'metro/src/lib/RamBundleParser';
3+
import { FileSizes } from 'source-map-explorer';
4+
import fs from 'fs';
5+
import path from 'path';
6+
7+
export const UNMAPPED_KEY = '[unmapped]';
8+
export const NO_SOURCE_KEY = '[no source]';
9+
10+
type SourceMapData = {
11+
codeFileContent: string;
12+
consumer: any;
13+
};
14+
type FileDataMap = {
15+
[key: string]: {
16+
size: number;
17+
};
18+
};
19+
20+
export function computeFileSizes(
21+
sourceMapData: SourceMapData,
22+
bundlePath: string,
23+
splitted: boolean
24+
): FileSizes {
25+
const { consumer } = sourceMapData;
26+
27+
const bundle = fs.readFileSync(bundlePath);
28+
const parser = splitted ? null : new RamBundleParser(bundle);
29+
30+
let files: FileDataMap = {};
31+
let mappedBytes = 0;
32+
const bundleDirnamePath = path.dirname(bundlePath);
33+
consumer._sections.map((section: any) => {
34+
const line = section.generatedOffset.generatedLine - 1;
35+
const source = section.consumer._sources.size()
36+
? section.consumer._sources.at(0)
37+
: NO_SOURCE_KEY;
38+
const moduleCode = splitted
39+
? fs.readFileSync(path.join(bundleDirnamePath, `${line}.js`))
40+
: parser && parser.getModule(line);
41+
const rangeByteLength = moduleCode ? Buffer.byteLength(moduleCode) : 0;
42+
if (!files[source]) {
43+
files[source] = { size: 0 };
44+
}
45+
files[source].size += rangeByteLength;
46+
47+
mappedBytes += rangeByteLength;
48+
});
49+
50+
const totalBytes = mappedBytes;
51+
const unmappedBytes = totalBytes - mappedBytes;
52+
const eolBytes = 0;
53+
const sourceMapCommentBytes = 0;
54+
55+
files[UNMAPPED_KEY] = { size: unmappedBytes };
56+
57+
return {
58+
totalBytes,
59+
mappedBytes,
60+
unmappedBytes,
61+
files,
62+
eolBytes,
63+
sourceMapCommentBytes,
64+
};
65+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {
2+
loadSourceMap,
3+
adjustSourcePaths,
4+
getExploreResult,
5+
saveOutputToFile,
6+
writeHtmlToTempFile,
7+
ExploreOptions,
8+
} from 'source-map-explorer';
9+
import { computeFileSizes } from './computeFileSizes';
10+
11+
export default function sourceMapForRamBundle(
12+
bundle: string,
13+
sourceMap: string,
14+
options: ExploreOptions,
15+
splitted: boolean
16+
) {
17+
loadSourceMap(bundle, sourceMap)
18+
.then(sourceMapData => {
19+
const sizes = computeFileSizes(sourceMapData, bundle, splitted);
20+
const files = adjustSourcePaths(sizes.files, options);
21+
const bundles = [
22+
{
23+
...sizes,
24+
bundleName: 'index.android.bundle',
25+
files,
26+
},
27+
];
28+
const result = getExploreResult(bundles, options);
29+
saveOutputToFile(result, options);
30+
return writeHtmlToTempFile(result.output);
31+
})
32+
.catch(err => console.log(err));
33+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"rootDir": "src",
5+
"outDir": "build",
6+
"jsx": "react"
7+
}
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module 'metro/src/lib/RamBundleParser' {
2+
export default class RamBundleParser {
3+
constructor(buffer: Buffer);
4+
getStartupCode(): string;
5+
getModule(id: number): string;
6+
}
7+
}

packages/haul-ram-bundle-webpack-plugin/src/IndexRamBundle.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import MAGIC_NUMBER from 'metro/src/shared/output/RamBundle/magic-number';
22
import webpack from 'webpack';
33
import { RawSource } from 'webpack-sources';
44
import { Module } from './WebpackRamBundlePlugin';
5-
import { countLines } from './utils';
65

76
/***
87
* Reference: https://github.com/facebook/metro/blob/master/packages/metro/src/shared/output/RamBundle/as-indexed-file.js
@@ -97,23 +96,14 @@ export default class IndexRamBundle {
9796
map: Object;
9897
}>,
9998
};
100-
101-
const bundleParts = bundle.toString().split('\n');
102-
let lineOffset =
103-
bundleParts.findIndex(line => /__haul_.+\.l\(/gm.test(line)) + 1;
104-
this.rawModules.forEach(sourceModule => {
99+
this.rawModules.forEach((sourceModule, index) => {
105100
indexMap.sections.push({
106101
offset: {
107-
line: lineOffset,
108-
column:
109-
bundleParts[lineOffset - 1]
110-
.split('')
111-
.findIndex(line => /__haul_.+\.l\(/gm.test(line)) + 1,
102+
line: index + 1, // line is 1-based
103+
column: 0,
112104
},
113105
map: sourceModule.map,
114106
});
115-
116-
lineOffset += countLines(sourceModule.source);
117107
});
118108
compilation.assets[sourceMapFilename] = new RawSource(
119109
JSON.stringify(indexMap)

0 commit comments

Comments
 (0)