Skip to content

Commit 40132a8

Browse files
authored
Compiler updates (codesandbox#853)
1. Better performance for path resolving 2. Clearer error messages for dependency not found 3. We now skip the `browser` field for `babel-core` and `@babel/core`, since they just _work_ in CodeSandbox 4. Put `getAliasedPath` in a common flow, so it doesn't have to be called multiple times 5. Add `main` field compatibility for the parcel template 6. Add `alias` field support for parcel 7. Support for node modules node-like resolving 8. `require.resolve` support Fixes codesandbox#467
1 parent 89ea753 commit 40132a8

File tree

11 files changed

+220
-168
lines changed

11 files changed

+220
-168
lines changed

packages/app/src/sandbox/compile.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,7 @@ const PREINSTALLED_DEPENDENCIES = [
180180
'babel-plugin-transform-prevent-infinite-loops',
181181
'babel-plugin-transform-vue-jsx',
182182
'babel-plugin-jsx-pragmatic',
183-
184183
'@babel/core',
185-
186184
'flow-bin',
187185
];
188186

packages/app/src/sandbox/errors/dependency-not-found-error.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { actions, dispatch } from 'codesandbox-api';
33
import SandboxError from './sandbox-error';
44

55
export default class DependencyNotFoundError extends SandboxError {
6-
constructor(dependencyName: string) {
6+
constructor(dependencyName: string, fromPath: ?string) {
77
super();
88
this.path = dependencyName;
99

@@ -23,6 +23,10 @@ export default class DependencyNotFoundError extends SandboxError {
2323

2424
this.name = 'DependencyNotFoundError';
2525
this.message = `Could not find dependency: '${parsedName}'`;
26+
27+
if (fromPath) {
28+
this.message += ` relative to '${fromPath}'`;
29+
}
2630
}
2731
type = 'dependency-not-found';
2832
severity = 'error';

packages/app/src/sandbox/errors/module-not-found-error.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
import SandboxError from './sandbox-error';
44

55
export default class ModuleNotFoundError extends SandboxError {
6-
constructor(path: string, isDependency: boolean) {
6+
constructor(path: string, isDependency: boolean, currentPath?: string) {
77
super();
88
this.path = path;
99
this.isDependency = isDependency;
1010

1111
this.name = 'ModuleNotFoundError';
1212
this.message = `Could not find module in path: '${path}'`;
13+
14+
if (currentPath) {
15+
this.message += ` relative to '${currentPath}'`;
16+
}
1317
}
1418
type = 'module-not-found';
1519
severity = 'error';

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@ export default function(
2020
const globalsCode = ', ' + Object.keys(globals).join(', ');
2121
const globalsValues = Object.keys(globals).map(k => globals[k]);
2222
try {
23-
const newCode = `(function evaluate(require, module, exports, process, setImmediate, Buffer, global${globalsCode}) {${code}\n})`;
23+
const newCode = `(function evaluate(require, module, exports, process, setImmediate, global${globalsCode}) {${code}\n})`;
2424
// eslint-disable-next-line no-eval
2525
(0, eval)(newCode).apply(this, [
2626
require,
2727
asUMD ? undefined : module,
2828
asUMD ? undefined : exports,
2929
process,
3030
setImmediate,
31-
Buffer,
3231
asUMD ? undefined : global,
3332
...globalsValues,
3433
]);

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

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

2627
import { ignoreNextCache, deleteAPICache } from './cache';
2728

@@ -57,6 +58,19 @@ export type Manifest = {
5758
};
5859

5960
const NODE_LIBS = ['dgram', 'net', 'tls', 'fs', 'module', 'child_process'];
61+
// For these dependencies we don't want to follow along with the `browser` field
62+
const SKIPPED_BROWSER_FIELD_DEPENDENCIES = ['babel-core', '@babel/core'].reduce(
63+
(result, next) => ({
64+
...result,
65+
[`/node_modules/${next}/package.json`]: true,
66+
}),
67+
{}
68+
);
69+
const SHIMMED_MODULE: Module = {
70+
path: pathUtils.join('/node_modules', 'empty', 'index.js'),
71+
code: `// empty`,
72+
requires: [],
73+
};
6074
const debug = _debug('cs:compiler:manager');
6175

6276
export default class Manager {
@@ -177,6 +191,13 @@ export default class Manager {
177191
code: this.manifest.contents[path].content,
178192
};
179193

194+
if (SKIPPED_BROWSER_FIELD_DEPENDENCIES[path]) {
195+
const pJsonCode = JSON.parse(this.manifest.contents[path].content);
196+
// eslint-disable-next-line
197+
delete pJsonCode.browser;
198+
module.code = JSON.stringify(pJsonCode, null, 2);
199+
}
200+
180201
// Check if module syntax, only transpile when that's NOT the case
181202
// TODO move this check to the packager
182203
if (!isESModule(module.code)) {
@@ -220,8 +241,11 @@ export default class Manager {
220241
}
221242
}
222243

223-
evaluateTranspiledModule(transpiledModule: TranspiledModule) {
224-
return transpiledModule.evaluate(this);
244+
evaluateTranspiledModule(
245+
transpiledModule: TranspiledModule,
246+
initiator?: TranspiledModule
247+
) {
248+
return transpiledModule.evaluate(this, undefined, initiator);
225249
}
226250

227251
addModule(module: Module) {
@@ -422,82 +446,86 @@ export default class Manager {
422446
currentPath: string,
423447
defaultExtensions: Array<string> = ['js', 'jsx', 'json']
424448
): Module {
425-
const aliasedPath = this.getAliasedDependencyPath(path, currentPath);
426-
const shimmedPath = coreLibraries[aliasedPath] || aliasedPath;
427-
428449
const dirredPath = pathUtils.dirname(currentPath);
429-
430-
if (!this.cachedPaths[dirredPath]) {
450+
if (this.cachedPaths[dirredPath] === undefined) {
431451
this.cachedPaths[dirredPath] = {};
432452
}
433-
const cachedPath = this.cachedPaths[dirredPath][shimmedPath];
434-
try {
435-
let resolvedPath;
436453

437-
if (cachedPath) {
438-
resolvedPath = cachedPath;
439-
} else {
454+
const cachedPath = this.cachedPaths[dirredPath][path];
455+
456+
let resolvedPath;
457+
458+
if (cachedPath) {
459+
resolvedPath = cachedPath;
460+
} else {
461+
const presetAliasedPath = this.preset
462+
.getAliasedPath(path)
463+
.replace(/.*\{\{sandboxRoot\}\}/, '');
464+
465+
const aliasedPath = this.getAliasedDependencyPath(
466+
presetAliasedPath,
467+
currentPath
468+
);
469+
const shimmedPath = coreLibraries[aliasedPath] || aliasedPath;
470+
471+
if (NODE_LIBS.includes(shimmedPath)) {
472+
return SHIMMED_MODULE;
473+
}
474+
475+
try {
440476
resolvedPath = resolve.sync(shimmedPath, {
441477
filename: currentPath,
442478
extensions: defaultExtensions.map(ext => '.' + ext),
443479
isFile: this.isFile,
444480
readFileSync: this.readFileSync,
445-
packageFilter: p => {
446-
if (!p.main && p.module) {
447-
// eslint-disable-next-line
448-
p.main = p.module;
449-
}
450-
451-
return p;
452-
},
481+
packageFilter,
453482
moduleDirectory: ['node_modules', this.envVariables.NODE_PATH].filter(
454483
Boolean
455484
),
456485
});
457486

458-
this.cachedPaths[dirredPath][shimmedPath] = resolvedPath;
459-
}
487+
this.cachedPaths[dirredPath][path] = resolvedPath;
460488

461-
if (NODE_LIBS.includes(shimmedPath) || resolvedPath === '//empty.js') {
462-
return {
463-
path: pathUtils.join('/node_modules', 'empty', 'index.js'),
464-
code: `// empty`,
465-
requires: [],
466-
};
467-
}
489+
if (!this.transpiledModules[resolvedPath]) {
490+
throw new Error(`Could not find '${resolvedPath}' in local files.`);
491+
}
492+
} catch (e) {
493+
if (
494+
this.cachedPaths[dirredPath] &&
495+
this.cachedPaths[dirredPath][path]
496+
) {
497+
delete this.cachedPaths[dirredPath][path];
498+
}
468499

469-
return this.transpiledModules[resolvedPath].module;
470-
} catch (e) {
471-
if (
472-
this.cachedPaths[dirredPath] &&
473-
this.cachedPaths[dirredPath][shimmedPath]
474-
) {
475-
delete this.cachedPaths[dirredPath][shimmedPath];
476-
}
500+
let connectedPath = /^(\w|@\w)/.test(shimmedPath)
501+
? pathUtils.join('/node_modules', shimmedPath)
502+
: pathUtils.join(pathUtils.dirname(currentPath), shimmedPath);
477503

478-
let connectedPath = /^(\w|@\w)/.test(shimmedPath)
479-
? pathUtils.join('/node_modules', shimmedPath)
480-
: pathUtils.join(pathUtils.dirname(currentPath), shimmedPath);
504+
const isDependency = connectedPath.includes('/node_modules/');
481505

482-
const isDependency = connectedPath.includes('/node_modules/');
506+
connectedPath = connectedPath.replace('/node_modules/', '');
483507

484-
connectedPath = connectedPath.replace('/node_modules/', '');
508+
if (!isDependency) {
509+
throw new ModuleNotFoundError(shimmedPath, false, currentPath);
510+
}
485511

486-
if (!isDependency) {
487-
throw new ModuleNotFoundError(shimmedPath, false);
488-
}
512+
const dependencyName = getDependencyName(connectedPath);
489513

490-
const dependencyName = getDependencyName(connectedPath);
514+
if (
515+
this.manifest.dependencies.find(d => d.name === dependencyName) ||
516+
this.manifest.dependencyDependencies[dependencyName]
517+
) {
518+
throw new ModuleNotFoundError(connectedPath, true, currentPath);
519+
} else {
520+
throw new DependencyNotFoundError(connectedPath, currentPath);
521+
}
522+
}
491523

492-
if (
493-
this.manifest.dependencies.find(d => d.name === dependencyName) ||
494-
this.manifest.dependencyDependencies[dependencyName]
495-
) {
496-
throw new ModuleNotFoundError(connectedPath, true);
497-
} else {
498-
throw new DependencyNotFoundError(connectedPath);
524+
if (resolvedPath === '//empty.js') {
525+
return SHIMMED_MODULE;
499526
}
500527
}
528+
return this.transpiledModules[resolvedPath].module;
501529
}
502530

503531
downloadDependency(
@@ -556,12 +584,8 @@ export default class Manager {
556584
// pop() mutates queryPath, queryPath is now just the loaders
557585
const modulePath = queryPath.pop();
558586

559-
const newPath = this.preset
560-
.getAliasedPath(modulePath)
561-
.replace(/.*\{\{sandboxRoot\}\}/, '');
562-
563587
const module = this.resolveModule(
564-
newPath,
588+
modulePath,
565589
currentPath,
566590
ignoredExtensions || this.preset.ignoredExtensions
567591
);
@@ -685,7 +709,8 @@ export default class Manager {
685709
debug(
686710
`Generated update diff, updating ${
687711
transpiledModulesToUpdate.length
688-
} modules.`
712+
} modules.`,
713+
transpiledModulesToUpdate
689714
);
690715

691716
return Promise.all(

0 commit comments

Comments
 (0)