Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions e2e/react_native_0_60x/__tests__/explore-bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import path from 'path';
import fs from 'fs';
import { installDeps } from '../../utils/common';
import { bundleForPlatform, cleanup } from '../../utils/bundle';
import { spawnSync } from 'child_process';

const BIN_PATH = path.resolve(
__dirname,
'../../../packages/haul-explore/bin/haul-explore.js'
);
const NYC_BIN = path.resolve(__dirname, '../../../node_modules/.bin/nyc');
const NYC_ARGS = [
'--silent',
'--no-clean',
'--exclude-after-remap',
'false',
'--cwd',
path.join(__dirname, '../../'),
];
const PROJECT_FIXTURE = path.join(
__dirname,
'../../../fixtures',
'react_native_with_haul_0_60x'
);

const RESULT_PATH = path.join(PROJECT_FIXTURE, 'dist/output.json');

describe('test exploring bundle', () => {
beforeAll(() => installDeps(PROJECT_FIXTURE));
beforeEach(() => cleanup(PROJECT_FIXTURE));
afterAll(() => cleanup(PROJECT_FIXTURE));

it('should return correct results for standard android bundle', () => {
const bundlePath = bundleForPlatform(PROJECT_FIXTURE, 'android', {
dev: false,
});
const args = [bundlePath, bundlePath + '.map', `--json`, RESULT_PATH];
const result = spawnSync(NYC_BIN, [...NYC_ARGS, BIN_PATH, ...(args || [])]);

expect(result.stderr.length).toBe(0);
expect(fs.existsSync(RESULT_PATH)).toBeTruthy();

const output = fs.readFileSync(RESULT_PATH);
const resultJSON = JSON.parse(output.toString());
const bundleInformation = resultJSON.results[0];

expect(bundleInformation.totalBytes).toBeGreaterThan(0);
expect(Object.keys(bundleInformation.files).length).toBeGreaterThan(0);
expect(bundleInformation.bundleName).toEqual(bundlePath);
});

it('should return correct results for standard ios bundle', () => {
const bundlePath = bundleForPlatform(PROJECT_FIXTURE, 'ios', {
dev: false,
});
const args = [bundlePath, bundlePath + '.map', `--json`, RESULT_PATH];
const result = spawnSync(NYC_BIN, [...NYC_ARGS, BIN_PATH, ...(args || [])]);

expect(result.stderr.length).toBe(0);
expect(fs.existsSync(RESULT_PATH)).toBeTruthy();

const output = fs.readFileSync(RESULT_PATH);
const resultJSON = JSON.parse(output.toString());
const bundleInformation = resultJSON.results[0];

expect(bundleInformation.totalBytes).toBeGreaterThan(0);
expect(Object.keys(bundleInformation.files).length).toBeGreaterThan(0);
expect(bundleInformation.bundleName).toEqual(bundlePath);
});
});
76 changes: 76 additions & 0 deletions e2e/react_native_0_60x/__tests__/explore-ram-bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import path from 'path';
import fs from 'fs';
import { installDeps } from '../../utils/common';
import { bundleForPlatform, cleanup } from '../../utils/bundle';
import { spawnSync } from 'child_process';

const BIN_PATH = path.resolve(
__dirname,
'../../../packages/haul-explore/bin/haul-explore.js'
);
const NYC_BIN = path.resolve(__dirname, '../../../node_modules/.bin/nyc');
const NYC_ARGS = [
'--silent',
'--no-clean',
'--exclude-after-remap',
'false',
'--cwd',
path.join(__dirname, '../../'),
];
const PROJECT_FIXTURE = path.join(
__dirname,
'../../../fixtures',
'react_native_with_haul_0_60x'
);

const RESULT_PATH = path.join(PROJECT_FIXTURE, 'dist/output.json');

describe('test exploring ram bundle', () => {
beforeAll(() => installDeps(PROJECT_FIXTURE));
beforeEach(() => cleanup(PROJECT_FIXTURE));
afterAll(() => cleanup(PROJECT_FIXTURE));

it('should return correct results for ram android bundle', () => {
const bundlePath = bundleForPlatform(PROJECT_FIXTURE, 'android', {
ramBundle: true,
dev: false,
});
const unbundleFilePath = path.join(
path.dirname(bundlePath),
'js-modules/UNBUNDLE'
);
const args = [unbundleFilePath, bundlePath + '.map', `--json`, RESULT_PATH];
const result = spawnSync(NYC_BIN, [...NYC_ARGS, BIN_PATH, ...(args || [])]);

expect(fs.existsSync(RESULT_PATH)).toBeTruthy();
expect(result.stderr.length).toBe(0);

const output = fs.readFileSync(RESULT_PATH);
const resultJSON = JSON.parse(output.toString());
const bundleInformation = resultJSON.results[0];

expect(bundleInformation.totalBytes).toBeGreaterThan(0);
expect(Object.keys(bundleInformation.files).length).toBeGreaterThan(0);
expect(bundleInformation.bundleName).toEqual(path.basename(bundlePath));
});

it('should return correct results for ram ios bundle', () => {
const bundlePath = bundleForPlatform(PROJECT_FIXTURE, 'ios', {
ramBundle: true,
dev: false,
});
const args = [bundlePath, bundlePath + '.map', `--json`, RESULT_PATH];
const result = spawnSync(NYC_BIN, [...NYC_ARGS, BIN_PATH, ...(args || [])]);

expect(result.stderr.length).toBe(0);
expect(fs.existsSync(RESULT_PATH)).toBeTruthy();

const output = fs.readFileSync(RESULT_PATH);
const resultJSON = JSON.parse(output.toString());
const bundleInformation = resultJSON.results[0];

expect(bundleInformation.totalBytes).toBeGreaterThan(0);
expect(Object.keys(bundleInformation.files).length).toBeGreaterThan(0);
expect(bundleInformation.bundleName).toEqual(path.basename(bundlePath));
});
});
5 changes: 3 additions & 2 deletions e2e/utils/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import rimraf from 'rimraf';
export function bundleForPlatform(
projectDir: string,
platform: string,
{ ramBundle }: { ramBundle?: boolean } = {}
{ ramBundle, dev = true }: { ramBundle?: boolean; dev?: boolean } = {}
) {
const bundlePath = path.resolve(
projectDir,
Expand All @@ -20,12 +20,13 @@ export function bundleForPlatform(
bundlePath,
'--assets-dest',
path.resolve(projectDir, 'dist'),
'--dev',
dev ? 'true' : 'false',
]);

if (stdout.match(/(error ▶︎ |ERROR)/g)) {
throw new Error(stdout);
}

return bundlePath;
}

Expand Down
27 changes: 27 additions & 0 deletions packages/haul-explore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# @haul-bundler/explore

Wrapper for source-map-explorer that allows you to explore and analyse RAM React Native bundles

Under the hood it is using source-map-explorer by danvk

You can read more about source-map-explorer here: https://github.com/danvk/source-map-explorer.

## Usage

Installation:

```bash
yarn add @haul-bundler/explore
```

Usage:

```bash
yarn haul-explore <bundle path> <source.map path> --[html | tsv | json] [file]
```

Bundle path and source map path are required. Output type is optional (html by default)

If you want to use it with file ram bandle, you need to pass path to UNBUNDLE as 'bundle path'

If filename specified output will be saved to specified file otherwise it will be opened in browser.
2 changes: 2 additions & 0 deletions packages/haul-explore/bin/haul-explore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('../build/index');
31 changes: 31 additions & 0 deletions packages/haul-explore/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": "0.15.0",
"name": "@haul-bundler/explore",
"description": "Explore and analyse your React Native bundle",
"author": "Pawel Szymanski <[email protected]>",
"repository": "github:callstack/haul",
"license": "MIT",
"bugs": "https://github.com/callstack/haul/issues",
"files": [
"build/",
"bin/"
],
"bin": {
"haul-explore": "bin/haul-explore.js"
},
"engines": {
"node": ">=10.x"
},
"dependencies": {
"@babel/core": "7.4.3",
"@babel/plugin-proposal-class-properties": "7.4.0",
"@babel/plugin-transform-flow-strip-types": "7.4.0",
"@babel/preset-env": "7.4.3",
"@babel/preset-typescript": "7.3.3",
"hasha": "^5.0.0",
"metro": "^0.56.4",
"source-map": "^0.7.3",
"source-map-explorer": "git://github.com/pan-pawel/source-map-explorer.git#source-map-explorer-v3.2.2-gitpkg",
"yargs": "^15.1.0"
}
}
96 changes: 96 additions & 0 deletions packages/haul-explore/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import yargs from 'yargs';
import {
explore,
writeHtmlToTempFile,
ExploreOptions,
} from 'source-map-explorer';
import sourceMapForRamBundle from './ram-bundle';
// eslint-disable-next-line import/no-extraneous-dependencies
import RamBundleParser from 'metro/src/lib/RamBundleParser';
import path from 'path';

import fs from 'fs';

const argv = yargs
.strict()
.scriptName('explore')
.demandCommand(2, 'bundle and source map files must be specified')
.options({
json: {
type: 'string',
description:
'If filename specified save output as JSON to specified file otherwise output to stdout.',
conflicts: ['tsv', 'html'],
},
tsv: {
type: 'string',
description:
'If filename specified save output as TSV to specified file otherwise output to stdout.',
conflicts: ['json', 'html'],
},
html: {
type: 'string',
description:
'If filename specified save output as HTML to specified file otherwise output to stdout rather than opening a browser.',
conflicts: ['json', 'tsv'],
},
})
.group(['json', 'tsv', 'html'], 'Output:')
.parse();

const bundle = path.resolve(argv._[0]);
const sourceMap = path.resolve(argv._[1]);
const outputFormat =
typeof argv.json === 'string'
? 'json'
: typeof argv.tsv === 'string'
? 'tsv'
: 'html';
const options: ExploreOptions = {
output: {
format: outputFormat,
},
};
const outputFile = argv[outputFormat] || '';

try {
const bundleFile = fs.readFileSync(bundle);
new RamBundleParser(bundleFile);
sourceMapForRamBundle(bundle, sourceMap, options, false)
.then(result => {
returnResults(result);
})
.catch(err => {
console.log(err);
process.exit(1);
});
} catch (err) {
if (path.basename(bundle) === 'UNBUNDLE') {
sourceMapForRamBundle(bundle, sourceMap, options, true)
.then(result => {
console.log('ASDASDASDADASD');
returnResults(result);
})
.catch(err => {
console.log(err);
process.exit(1);
});
} else {
explore([bundle, sourceMap], options)
.then(result => {
returnResults(result);
})
.catch(err => {
console.log(err);
process.exit(1);
});
}
}

function returnResults(result: any) {
if (outputFile.length > 0) {
fs.writeFileSync(outputFile, result.output);
} else {
writeHtmlToTempFile(result.output);
}
}
66 changes: 66 additions & 0 deletions packages/haul-explore/src/ram-bundle/computeFileSizes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import RamBundleParser from 'metro/src/lib/RamBundleParser';
import { FileSizes } from 'source-map-explorer';
import fs from 'fs';
import path from 'path';

export const UNMAPPED_KEY = '[unmapped]';
export const NO_SOURCE_KEY = '[no source]';

type SourceMapData = {
codeFileContent: string;
consumer: any;
};
type FileDataMap = {
[key: string]: {
size: number;
};
};

export function computeFileSizes(
sourceMapData: SourceMapData,
bundlePath: string,
splitted: boolean
): FileSizes {
const { consumer } = sourceMapData;

const bundle = fs.readFileSync(bundlePath);
const parser = splitted ? null : new RamBundleParser(bundle);

let files: FileDataMap = {};
let mappedBytes = 0;
const bundleDirnamePath = path.dirname(bundlePath);
consumer._sections.map((section: any) => {
const file = section.consumer.file;
const line = parseInt(file.match(/(\d)+\.js/)[1], 10);
const source = section.consumer._sources.size()
? section.consumer._sources.at(0)
: NO_SOURCE_KEY;
const moduleCode = splitted
? fs.readFileSync(path.join(bundleDirnamePath, file))
: parser?.getModule(line);
const rangeByteLength = moduleCode ? Buffer.byteLength(moduleCode) : 0;
if (!files[source]) {
files[source] = { size: 0 };
}
files[source].size += rangeByteLength;

mappedBytes += rangeByteLength;
});

const totalBytes = mappedBytes;
const unmappedBytes = totalBytes - mappedBytes;
const eolBytes = 0;
const sourceMapCommentBytes = 0;

files[UNMAPPED_KEY] = { size: unmappedBytes };

return {
totalBytes,
mappedBytes,
unmappedBytes,
files,
eolBytes,
sourceMapCommentBytes,
};
}
Loading