Skip to content

Commit 304a438

Browse files
authored
Improve esmodules loading by writing a custom converter (codesandbox#3370)
* Improve esmodules loading by writing a custom converter * Polishment * Fix case * Fix issue * Big perf improvements * Improve support of metrics * Fix types * Fix linting issues * Fix default interop * Cleanup * Fix edge case, add benchmarking * Fix typings * Force esmodules * Fix recursive loops * Fix reexport with as * Tweaks to performance and benchmarking * Fix babel listing * Fix dynamically downloading modules * Cleanup reading tmodule and fixing resolving async files * Update measurement keys * Fix resolver * Force prod packager for development as well * Revert "Force prod packager for development as well" This reverts commit b95e40b. * Use new packger * Fix urls * Fix showing transpiler errors * Fix vue * Remove indirection of metrics
1 parent 905cc7a commit 304a438

File tree

26 files changed

+1852
-447
lines changed

26 files changed

+1852
-447
lines changed

packages/app/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"apollo-boost": "^0.4.4",
8787
"apollo-link-batch-http": "^1.2.12",
8888
"apollo-link-context": "^1.0.18",
89+
"astring": "^1.4.3",
8990
"axios": "^0.19.0",
9091
"babel-code-frame": "^6.26.0",
9192
"babel-macros": "^2.0.0",
@@ -115,6 +116,7 @@
115116
"downshift": "^1.0.0-rc.14",
116117
"eslint-plugin-react-hooks": "1.6.0",
117118
"eslint-plugin-vue": "^4.2.2",
119+
"estree-walker": "^1.0.1",
118120
"file-saver": "^1.3.3",
119121
"fontfaceobserver": "^2.0.13",
120122
"fuse.js": "^3.2.1",
@@ -146,6 +148,7 @@
146148
"lodash-es": "^4.17.2",
147149
"lru-cache": "^4.1.3",
148150
"match-sorter": "^1.8.1",
151+
"meriyah": "^1.9.7",
149152
"minimatch": "^3.0.4",
150153
"mobx": "^5.11.0",
151154
"mobx-react": "^6.1.1",

packages/app/src/sandbox/compile.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ async function compile({
495495
showFullScreen: firstLoad,
496496
}
497497
);
498-
metrics.endMeasure('dependencies', 'Dependencies');
498+
metrics.endMeasure('dependencies', { displayName: 'Dependencies' });
499499

500500
const shouldReloadManager =
501501
(isNewCombination && !firstLoad) || manager.id !== sandboxId;
@@ -547,7 +547,7 @@ async function compile({
547547
await manager.verifyTreeTranspiled();
548548
await manager.transpileModules(managerModuleToTranspile);
549549

550-
metrics.endMeasure('transpilation', 'Transpilation');
550+
metrics.endMeasure('transpilation', { displayName: 'Transpilation' });
551551

552552
dispatch({ type: 'status', status: 'evaluating' });
553553
manager.setStage('evaluation');
@@ -612,14 +612,16 @@ async function compile({
612612

613613
metrics.measure('external-resources');
614614
await handleExternalResources(externalResources);
615-
metrics.endMeasure('external-resources', 'External Resources');
615+
metrics.endMeasure('external-resources', {
616+
displayName: 'External Resources',
617+
});
616618

617619
const oldHTML = document.body.innerHTML;
618620
metrics.measure('evaluation');
619621
const evalled = manager.evaluateModule(managerModuleToTranspile, {
620622
force: isModuleView,
621623
});
622-
metrics.endMeasure('evaluation', 'Evaluation');
624+
metrics.endMeasure('evaluation', { displayName: 'Evaluation' });
623625

624626
const domChanged =
625627
!manager.preset.htmlDisabled && oldHTML !== document.body.innerHTML;
@@ -669,8 +671,8 @@ async function compile({
669671

670672
debug(`Total time: ${Date.now() - startTime}ms`);
671673

672-
metrics.endMeasure('compilation', 'Compilation');
673-
metrics.endMeasure('total', 'Total', { lastTime: 0 });
674+
metrics.endMeasure('compilation', { displayName: 'Compilation' });
675+
metrics.endMeasure('total', { displayName: 'Total', lastTime: 0 });
674676
dispatch({
675677
type: 'success',
676678
});

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

+106-84
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ParsedConfigurationFiles } from '@codesandbox/common/lib/templates/temp
99
import DependencyNotFoundError from 'sandbox-hooks/errors/dependency-not-found-error';
1010
import ModuleNotFoundError from 'sandbox-hooks/errors/module-not-found-error';
1111

12+
import { generateBenchmarkInterface } from '../utils/benchmark';
1213
import { Module } from './types/module';
1314
import TranspiledModule, {
1415
ChildModule,
@@ -28,6 +29,7 @@ import { packageFilter } from './utils/resolve-utils';
2829
import { ignoreNextCache, deleteAPICache, clearIndexedDBCache } from './cache';
2930
import { shouldTranspile } from './transpilers/babel/check';
3031
import { splitQueryFromPath } from './utils/query-path';
32+
import { measure, endMeasure } from '../utils/metrics';
3133

3234
declare const BrowserFS: any;
3335

@@ -122,7 +124,10 @@ export default class Manager {
122124
fileResolver: IFileResolver | undefined;
123125

124126
// List of modules that are being transpiled, to prevent duplicate jobs.
125-
transpileJobs: { [transpiledModuleId: string]: true };
127+
transpileJobs: {
128+
[transpiledModuleId: string]: true | Promise<TranspiledModule>;
129+
};
130+
126131
transpiledModulesByHash: { [hash: string]: TranspiledModule };
127132

128133
// All paths are resolved at least twice: during transpilation and evaluation.
@@ -161,6 +166,9 @@ export default class Manager {
161166
if (process.env.NODE_ENV === 'development') {
162167
// eslint-disable-next-line no-console
163168
console.log(this);
169+
170+
// Initialize benchmark logic
171+
getGlobal().Benchmark = generateBenchmarkInterface(this);
164172
}
165173

166174
BrowserFS.configure(
@@ -266,7 +274,7 @@ export default class Manager {
266274
return this.fileResolver.readFile(p).then(code => {
267275
this.addModule({ code, path: p });
268276

269-
callback(code);
277+
callback(null, code);
270278
});
271279
}
272280

@@ -499,6 +507,10 @@ export default class Manager {
499507
this.getTranspiledModules().map(tModule => tModule.resetCompilation());
500508
}
501509

510+
clearTranspilationCache() {
511+
this.getTranspiledModules().map(tModule => tModule.resetTranspilation());
512+
}
513+
502514
getModules(): Array<Module | ChildModule> {
503515
return values(this.transpiledModules).map(t => t.module);
504516
}
@@ -587,102 +599,111 @@ export default class Manager {
587599
currentPath: string,
588600
defaultExtensions = ['js', 'jsx', 'json']
589601
): Promise<Module> {
590-
return new Promise(promiseResolve => {
591-
const dirredPath = pathUtils.dirname(currentPath);
592-
if (this.cachedPaths[dirredPath] === undefined) {
593-
this.cachedPaths[dirredPath] = {};
594-
}
602+
const dirredPath = pathUtils.dirname(currentPath);
603+
if (this.cachedPaths[dirredPath] === undefined) {
604+
this.cachedPaths[dirredPath] = {};
605+
}
595606

596-
const cachedPath = this.cachedPaths[dirredPath][path];
607+
const cachedPath = this.cachedPaths[dirredPath][path];
597608

598-
let resolvedPath;
609+
if (cachedPath) {
610+
return Promise.resolve(this.transpiledModules[cachedPath].module);
611+
}
599612

600-
if (cachedPath) {
601-
resolvedPath = cachedPath;
602-
} else {
603-
const presetAliasedPath = this.getPresetAliasedPath(path);
613+
const measureKey = `resolve-async:${path}:${currentPath}`;
614+
return new Promise((promiseResolve, promiseReject) => {
615+
measure(measureKey);
616+
const presetAliasedPath = this.getPresetAliasedPath(path);
604617

605-
const aliasedPath = this.getAliasedDependencyPath(
606-
presetAliasedPath,
607-
currentPath
608-
);
609-
const shimmedPath = coreLibraries[aliasedPath] || aliasedPath;
618+
const aliasedPath = this.getAliasedDependencyPath(
619+
presetAliasedPath,
620+
currentPath
621+
);
622+
const shimmedPath = coreLibraries[aliasedPath] || aliasedPath;
610623

611-
if (NODE_LIBS.indexOf(shimmedPath) > -1) {
612-
this.cachedPaths[dirredPath][path] = shimmedPath;
613-
promiseResolve(getShimmedModuleFromPath(currentPath, path));
614-
return;
615-
}
624+
if (NODE_LIBS.indexOf(shimmedPath) > -1) {
625+
this.cachedPaths[dirredPath][path] = shimmedPath;
626+
promiseResolve(getShimmedModuleFromPath(currentPath, path));
627+
return;
628+
}
616629

617-
try {
618-
resolve(
619-
shimmedPath,
620-
{
621-
filename: currentPath,
622-
extensions: defaultExtensions.map(ext => '.' + ext),
623-
isFile: this.isFile,
624-
readFileSync: this.readFileSync,
625-
packageFilter,
626-
moduleDirectory: this.getModuleDirectories(),
627-
},
628-
(err, foundPath) => {
629-
if (err) {
630-
throw err;
631-
}
630+
resolve(
631+
shimmedPath,
632+
{
633+
filename: currentPath,
634+
extensions: defaultExtensions.map(ext => '.' + ext),
635+
isFile: this.isFile,
636+
readFileSync: this.readFileSync,
637+
packageFilter,
638+
moduleDirectory: this.getModuleDirectories(),
639+
},
640+
(err, foundPath) => {
641+
endMeasure(measureKey, { silent: true });
642+
if (err) {
643+
if (
644+
this.cachedPaths[dirredPath] &&
645+
this.cachedPaths[dirredPath][path]
646+
) {
647+
delete this.cachedPaths[dirredPath][path];
648+
}
632649

633-
this.cachedPaths[dirredPath][path] = foundPath;
650+
let connectedPath = shimmedPath;
651+
if (connectedPath.indexOf('/node_modules') !== 0) {
652+
connectedPath = /^(\w|@\w)/.test(shimmedPath)
653+
? pathUtils.join('/node_modules', shimmedPath)
654+
: pathUtils.join(pathUtils.dirname(currentPath), shimmedPath);
655+
}
634656

635-
if (foundPath === '//empty.js') {
636-
promiseResolve(getShimmedModuleFromPath(currentPath, path));
637-
return;
638-
}
657+
const isDependency = connectedPath.includes('/node_modules/');
639658

640-
if (!this.transpiledModules[foundPath]) {
641-
this.readFileSync(foundPath, code => {
642-
this.addModule({ path: foundPath, code });
643-
promiseResolve(this.transpiledModules[foundPath].module);
644-
});
645-
} else {
646-
promiseResolve(this.transpiledModules[foundPath].module);
647-
}
659+
connectedPath = connectedPath.replace('/node_modules/', '');
660+
661+
if (!isDependency) {
662+
promiseReject(
663+
new ModuleNotFoundError(shimmedPath, false, currentPath)
664+
);
648665
}
649-
);
650-
} catch (e) {
651-
if (
652-
this.cachedPaths[dirredPath] &&
653-
this.cachedPaths[dirredPath][path]
654-
) {
655-
delete this.cachedPaths[dirredPath][path];
656-
}
657666

658-
let connectedPath = shimmedPath;
659-
if (connectedPath.indexOf('/node_modules') !== 0) {
660-
connectedPath = /^(\w|@\w)/.test(shimmedPath)
661-
? pathUtils.join('/node_modules', shimmedPath)
662-
: pathUtils.join(pathUtils.dirname(currentPath), shimmedPath);
663-
}
667+
const dependencyName = getDependencyName(connectedPath);
668+
669+
if (
670+
this.manifest.dependencies.find(d => d.name === dependencyName) ||
671+
this.manifest.dependencyDependencies[dependencyName]
672+
) {
673+
promiseReject(
674+
new ModuleNotFoundError(connectedPath, true, currentPath)
675+
);
676+
} else {
677+
promiseReject(
678+
new DependencyNotFoundError(connectedPath, currentPath)
679+
);
680+
}
664681

665-
const isDependency = connectedPath.includes('/node_modules/');
682+
return;
683+
}
666684

667-
connectedPath = connectedPath.replace('/node_modules/', '');
685+
this.cachedPaths[dirredPath][path] = foundPath;
668686

669-
if (!isDependency) {
670-
throw new ModuleNotFoundError(shimmedPath, false, currentPath);
687+
if (foundPath === '//empty.js') {
688+
promiseResolve(getShimmedModuleFromPath(currentPath, path));
689+
return;
671690
}
672691

673-
const dependencyName = getDependencyName(connectedPath);
692+
if (!this.transpiledModules[foundPath]) {
693+
this.readFileSync(foundPath, (error, code) => {
694+
if (error) {
695+
promiseReject(error);
696+
return;
697+
}
674698

675-
if (
676-
this.manifest.dependencies.find(d => d.name === dependencyName) ||
677-
this.manifest.dependencyDependencies[dependencyName]
678-
) {
679-
throw new ModuleNotFoundError(connectedPath, true, currentPath);
699+
this.addModule({ path: foundPath, code });
700+
promiseResolve(this.transpiledModules[foundPath].module);
701+
});
680702
} else {
681-
throw new DependencyNotFoundError(connectedPath, currentPath);
703+
promiseResolve(this.transpiledModules[foundPath].module);
682704
}
683705
}
684-
}
685-
promiseResolve(this.transpiledModules[resolvedPath].module);
706+
);
686707
});
687708
}
688709

@@ -704,6 +725,8 @@ export default class Manager {
704725
if (cachedPath && this.transpiledModules[cachedPath]) {
705726
resolvedPath = cachedPath;
706727
} else {
728+
const measureKey = `resolve-sync:${path}:${currentPath}`;
729+
measure(measureKey);
707730
const presetAliasedPath = this.getPresetAliasedPath(path);
708731

709732
const aliasedPath = this.getAliasedDependencyPath(
@@ -726,6 +749,7 @@ export default class Manager {
726749
packageFilter,
727750
moduleDirectory: this.getModuleDirectories(),
728751
});
752+
endMeasure(measureKey, { silent: true });
729753

730754
this.cachedPaths[dirredPath][path] = resolvedPath;
731755

@@ -812,12 +836,10 @@ export default class Manager {
812836
const tModule =
813837
currentTModule || this.getTranspiledModule(this.modules['/package.json']); // Get arbitrary file from root
814838
try {
815-
return Promise.resolve(
816-
this.resolveTranspiledModule(
817-
path,
818-
tModule.module.path,
819-
ignoredExtensions
820-
)
839+
return this.resolveTranspiledModule(
840+
path,
841+
tModule.module.path,
842+
ignoredExtensions
821843
);
822844
} catch (e) {
823845
if (e.type === 'module-not-found' && e.isDependency) {

packages/app/src/sandbox/eval/presets/svelte/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const babelOptions = {
1919
export default function initialize() {
2020
const sveltePreset = new Preset('svelte', ['js', 'jsx', 'svelte'], {});
2121

22-
sveltePreset.registerTranspiler(module => /\.jsx?$/.test(module.path), [
22+
sveltePreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
2323
{ transpiler: babelTranspiler, options: babelOptions },
2424
]);
2525

0 commit comments

Comments
 (0)