Skip to content

Commit 15df6d2

Browse files
authored
Use Regex to optimize node_module transpiling (codesandbox#794)
* Use Regex to optimize node_module transpiling * IE11 support
1 parent 097dfce commit 15df6d2

File tree

10 files changed

+127
-3
lines changed

10 files changed

+127
-3
lines changed

packages/app/src/sandbox/eval/manager.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import DependencyNotFoundError from '../errors/dependency-not-found-error';
2222
import ModuleNotFoundError from '../errors/module-not-found-error';
2323
import TestRunner from './tests/jest-lite';
2424
import dependenciesToQuery from '../npm/dependencies-to-query';
25+
import isESModule from './utils/is-es-module';
2526

2627
type Externals = {
2728
[name: string]: string,
@@ -186,7 +187,7 @@ export default class Manager {
186187

187188
// Check if module syntax, only transpile when that's NOT the case
188189
// TODO move this check to the packager
189-
if (!/^(import|export)\s/gm.test(module.code)) {
190+
if (!isESModule(module.code)) {
190191
module.requires = this.manifest.contents[path].requires;
191192
}
192193

@@ -734,7 +735,15 @@ export default class Manager {
734735
*/
735736
async save() {
736737
try {
737-
await localforage.setItem(this.id, this.serialize());
738+
const serialized = this.serialize();
739+
if (process.env.NODE_ENV === 'development') {
740+
debug(
741+
'Saving cache of ' +
742+
(JSON.stringify(serialized).length / 1024).toFixed(2) +
743+
' kb'
744+
);
745+
}
746+
await localforage.setItem(this.id, serialized);
738747
} catch (e) {
739748
if (process.env.NODE_ENV === 'development') {
740749
console.error(e);

packages/app/src/sandbox/eval/transpilers/babel/worker/__snapshots__/get-require-statements.test.js.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ Array [
3535
]
3636
`;
3737

38+
exports[`get-require-statements can find reexports 1`] = `
39+
Array [
40+
Object {
41+
"path": "./Hello",
42+
"type": "direct",
43+
},
44+
]
45+
`;
46+
3847
exports[`get-require-statements can find simple requires 1`] = `
3948
Array [
4049
Object {

packages/app/src/sandbox/eval/transpilers/babel/worker/babel-worker.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import infiniteLoops from './plugins/babel-plugin-transform-prevent-infinite-loo
99
import dynamicCSSModules from './plugins/babel-plugin-dynamic-css-modules';
1010

1111
import { buildWorkerError } from '../../utils/worker-error-handler';
12+
import isESModule from '../../../utils/is-es-module';
1213
import getDependencies from './get-require-statements';
1314

1415
import { evaluateFromPath, resetCache } from './evaluate';
@@ -17,6 +18,8 @@ import {
1718
getPrefixedPresetName,
1819
} from './get-prefixed-name';
1920

21+
import regexGetRequireStatements from './simple-get-require-statements';
22+
2023
let fsInitialized = false;
2124
let lastConfig = null;
2225

@@ -226,6 +229,27 @@ self.addEventListener('message', async event => {
226229
return;
227230
}
228231

232+
// When we find a node_module that already is commonjs we will just get the
233+
// dependencies from the file and return the same code. We get the dependencies
234+
// with a regex since commonjs modules just have `require` and regex is MUCH
235+
// faster than generating an AST from the code.
236+
if (path.startsWith('/node_modules') && !isESModule(code)) {
237+
regexGetRequireStatements(code).forEach(dependency => {
238+
self.postMessage({
239+
type: 'add-dependency',
240+
path: dependency.path,
241+
isGlob: dependency.type === 'glob',
242+
});
243+
});
244+
245+
self.postMessage({
246+
type: 'result',
247+
transpiledCode: code,
248+
});
249+
250+
return;
251+
}
252+
229253
const { disableCodeSandboxPlugins } = loaderOptions;
230254

231255
const babelUrl = babelTranspilerOptions && babelTranspilerOptions.babelURL;

packages/app/src/sandbox/eval/transpilers/babel/worker/get-require-statements.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ describe('get-require-statements', () => {
5858
testAst(code);
5959
});
6060

61+
it('can find reexports', () => {
62+
const code = `
63+
export * from './Hello';
64+
`;
65+
66+
testAst(code);
67+
});
68+
6169
it('can work with real life code', () => {
6270
const code = `
6371
import React from 'react'

packages/app/src/sandbox/eval/transpilers/babel/worker/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
require('core-js/fn/string/starts-with');
2+
require('core-js/fn/string/ends-with');
3+
require('core-js/fn/array/find');
4+
15
self.importScripts(
26
`${process.env.CODESANDBOX_HOST}/static/browserfs/browserfs.min.js`
37
);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* This is the regex version of getting all require statements, it makes the assumption
3+
* that the file is commonjs and only has `require()` statements.
4+
*/
5+
export default function getRequireStatements(code: string) {
6+
const results = [];
7+
code.split('\n').forEach(line => {
8+
const regex = /require\(['|"|`]([^"|'|`]*)['|"|`]\)|require\((.*)\)/;
9+
const match = line.match(regex);
10+
11+
if (match) {
12+
if (match[1]) {
13+
if (!results.find(r => r.type === 'direct' && r.path === match[1])) {
14+
results.push({
15+
type: 'direct',
16+
path: match[1],
17+
});
18+
}
19+
} else if (match[2]) {
20+
if (!results.find(r => r.type === 'glob' && r.path === match[2])) {
21+
results.push({
22+
type: 'glob',
23+
path: match[2],
24+
});
25+
}
26+
}
27+
}
28+
});
29+
30+
return results;
31+
}

packages/app/src/sandbox/eval/transpilers/typescript/__snapshots__/get-require-statements.test.js.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ Array [
3535
]
3636
`;
3737

38+
exports[`get-require-statements can find reexports 1`] = `
39+
Array [
40+
Object {
41+
"path": "./Hello",
42+
"type": "direct",
43+
},
44+
]
45+
`;
46+
3847
exports[`get-require-statements can find relative imports 1`] = `
3948
Array [
4049
Object {

packages/app/src/sandbox/eval/transpilers/typescript/get-require-statements.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ describe('get-require-statements', () => {
1414
expect(getRequireStatements(sourceFile, ts)).toMatchSnapshot();
1515
}
1616

17+
function testRegex(code) {
18+
const results = [];
19+
20+
code.split('\n').forEach(line => {
21+
const regex = /import\s.*['|"|`](.*)['|"|`]|require\((.*)\)|import\((.*)\)/;
22+
23+
const [, ...results] = line.match(regex);
24+
25+
const path = results.find(Boolean);
26+
results.push({ type: 'direct', path });
27+
});
28+
29+
return results;
30+
}
31+
1732
it('can find simple requires', () => {
1833
const code = `
1934
import React from 'react';
@@ -49,6 +64,14 @@ describe('get-require-statements', () => {
4964
testAst(code);
5065
});
5166

67+
it('can find reexports', () => {
68+
const code = `
69+
export * from './Hello';
70+
`;
71+
72+
testAst(code);
73+
});
74+
5275
it('can find relative imports', () => {
5376
const code = `
5477
import Hello from './Hello';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function isESModule(code: string) {
2+
return /^(import|export)\s/gm.test(code);
3+
}

packages/common/version.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ const versionNumber = Math.floor(preval`module.exports = Date.now();` / 1000);
1414

1515
const shortCommitSha = preval(`
1616
var execSync = require('child_process').execSync;
17-
module.exports = execSync('git rev-parse --short HEAD').toString().trim();
17+
try {
18+
module.exports = execSync('git rev-parse --short HEAD').toString().trim();
19+
} catch (e) {
20+
module.exports = 'unknown';
21+
}
1822
`);
1923

2024
export default preval(

0 commit comments

Comments
 (0)