Skip to content

Commit 82d3acf

Browse files
authored
feat: New resolver and exports support (#6148)
* replace resolver * update download npm module as well * remove dep * fix issues with @babel/runtime exports * replace more browser-resolve references * cleanup download from error * test the module not found errors * slightly more docs * fix types * add package caching * small performance tweak * cleanup * reset resolver cache after downloading module * add cjs to react-app * support cjs extension * fix react-ts? * improve regex for matching react modules
1 parent 6063817 commit 82d3acf

File tree

35 files changed

+1414
-261
lines changed

35 files changed

+1414
-261
lines changed

.eslintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
],
6464
"jsx-a11y/label-has-for": "off",
6565
"no-bitwise": "off",
66-
"arrow-body-style": ["error", "as-needed"],
66+
"arrow-body-style": "off",
6767
"no-restricted-globals": "off",
6868
"no-restricted-syntax": "off",
6969
"lines-between-class-members": [

packages/app/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@
107107
"babel-standalone": "^6.25.0",
108108
"base64-loader": "^1.0.0",
109109
"browser-detect": "^0.2.28",
110-
"browser-resolve": "CompuIves/node-browser-resolve",
111110
"circular-json": "^0.4.0",
112111
"codemirror": "^5.27.4",
113112
"codesandbox-api": "0.0.32",

packages/app/src/sandbox/eval/presets/angular-cli/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ async function addAngularCLIResources(manager: Manager) {
172172
export default function initialize() {
173173
const preset = new Preset(
174174
'angular-cli',
175-
['web.ts', 'ts', 'json', 'web.tsx', 'tsx', 'js'],
175+
['web.ts', 'ts', 'json', 'web.tsx', 'tsx', 'js', 'cjs'],
176176
{},
177177
{
178178
setup: async manager => {
@@ -251,7 +251,7 @@ export default function initialize() {
251251
{ transpiler: csbDynamicImportTranspiler },
252252
]);
253253

254-
preset.registerTranspiler(module => /\.m?js$/.test(module.path), [
254+
preset.registerTranspiler(module => /\.(m|c)?js$/.test(module.path), [
255255
{
256256
transpiler: babelTranspiler,
257257
options: {

packages/app/src/sandbox/eval/presets/create-react-app-typescript/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default function initialize() {
1414
'tsx',
1515
'js',
1616
'mjs',
17+
'cjs',
1718
]);
1819

1920
preset.registerTranspiler(module => /\.css$/.test(module.path), [
@@ -67,7 +68,7 @@ export default function initialize() {
6768
};
6869
preset.registerTranspiler(
6970
module =>
70-
/\.m?(t|j)sx?$/.test(module.path) && !module.path.endsWith('.d.ts'),
71+
/\.(c|m)?(t|j)sx?$/.test(module.path) && !module.path.endsWith('.d.ts'),
7172
[
7273
{
7374
transpiler: babelTranspiler,

packages/app/src/sandbox/eval/presets/create-react-app/utils/reactPreset.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const reactPreset = babelConfig => {
4040
'json',
4141
'web.jsx',
4242
'jsx',
43+
'cjs',
4344
],
4445
aliases,
4546
{
@@ -74,9 +75,9 @@ export const reactPreset = babelConfig => {
7475

7576
preset.registerTranspiler(
7677
module =>
77-
!module.path.startsWith('/node_modules') &&
78-
/\.m?(t|j)sx?$/.test(module.path) &&
79-
!module.path.endsWith('.d.ts'),
78+
/^(?!\/node_modules\/).*\.(((m|c)?jsx?)|tsx)$/.test(
79+
module.path
80+
),
8081
[
8182
{
8283
transpiler: babelTranspiler,
@@ -99,9 +100,12 @@ export const reactPreset = babelConfig => {
99100
}
100101

101102
preset.registerTranspiler(
102-
module =>
103-
/\.m?(t|j)sx?$/.test(module.path) &&
104-
!module.path.endsWith('.d.ts'),
103+
module => {
104+
return (
105+
/\.(m|c)?(t|j)sx?$/.test(module.path) &&
106+
!module.path.endsWith('.d.ts')
107+
);
108+
},
105109
[{ transpiler: babelTranspiler, options: babelConfig }]
106110
);
107111

packages/app/src/sandbox/eval/presets/cxjs/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function initialize() {
1818
{}
1919
);
2020

21-
cxjsPreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
21+
cxjsPreset.registerTranspiler(module => /\.(c|m)?jsx?$/.test(module.path), [
2222
{
2323
transpiler: babelTranspiler,
2424
options: {

packages/app/src/sandbox/eval/presets/dojo/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default function initialize() {
4242
{ transpiler: csbDynamicImportTranspiler },
4343
]);
4444

45-
preset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
45+
preset.registerTranspiler(module => /\.(c|m)?jsx?$/.test(module.path), [
4646
{
4747
transpiler: babelTranspiler,
4848
options: {

packages/app/src/sandbox/eval/presets/parcel/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export default function initialize() {
2525
'parcel',
2626
[
2727
'js',
28+
'mjs',
29+
'cjs',
2830
'jsx',
2931
'ts',
3032
'tsx',
@@ -54,7 +56,7 @@ export default function initialize() {
5456
{ transpiler: babelTranspiler },
5557
]);
5658

57-
parcelPreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
59+
parcelPreset.registerTranspiler(module => /\.(m|c)?jsx?$/.test(module.path), [
5860
{
5961
transpiler: babelTranspiler,
6062
options: {

packages/app/src/sandbox/eval/presets/preact-cli/preset.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,19 @@ export default function PreactPreset() {
2424
}
2525
);
2626

27-
preactPreset.registerTranspiler(module => /\.m?(t|j)sx?$/.test(module.path), [
28-
{
29-
transpiler: babelTranspiler,
30-
options: {
31-
isV7: true,
32-
compileNodeModulesWithEnv: true,
33-
// config is derived from babelrc at packages/common/src/templates/configuration/babelrc/index.ts
27+
preactPreset.registerTranspiler(
28+
module => /\.(m|c)?(t|j)sx?$/.test(module.path),
29+
[
30+
{
31+
transpiler: babelTranspiler,
32+
options: {
33+
isV7: true,
34+
compileNodeModulesWithEnv: true,
35+
// config is derived from babelrc at packages/common/src/templates/configuration/babelrc/index.ts
36+
},
3437
},
35-
},
36-
]);
38+
]
39+
);
3740

3841
// For these routes we need to enable css modules
3942
const cssModulesPaths = [

packages/app/src/sandbox/eval/presets/reason/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function initialize() {
2020
{ transpiler: stylesTranspiler },
2121
]);
2222

23-
preset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
23+
preset.registerTranspiler(module => /\.(m|c)?jsx?$/.test(module.path), [
2424
{ transpiler: babelTranspiler },
2525
]);
2626

packages/app/src/sandbox/eval/presets/stencil/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default function initialize() {
2727
{ transpiler: stencilTranspiler },
2828
]);
2929

30-
stencilPreset.registerTranspiler(module => /\.mjs$/.test(module.path), [
30+
stencilPreset.registerTranspiler(module => /\.(m|c)js$/.test(module.path), [
3131
{ transpiler: stencilTranspiler },
3232
{ transpiler: babelTranspiler, options: babelOptions },
3333
]);

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

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

25-
sveltePreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
25+
sveltePreset.registerTranspiler(module => /\.(m|c)?jsx?$/.test(module.path), [
2626
{ transpiler: babelTranspiler, options: babelOptions },
2727
]);
2828

packages/app/src/sandbox/eval/presets/vue-cli/v2.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export default function initialize(vuePreset: Preset) {
6262
});
6363
}
6464

65-
vuePreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
65+
vuePreset.registerTranspiler(module => /\.(m|c)?jsx?$/.test(module.path), [
6666
{ transpiler: babelTranspiler },
6767
]);
6868
vuePreset.registerTranspiler(module => /\.m?tsx?$/.test(module.path), [

packages/app/src/sandbox/eval/presets/vue-cli/v3.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default async function initialize(vuePreset: Preset) {
6565
});
6666
}
6767

68-
vuePreset.registerTranspiler(module => /\.m?jsx?$/.test(module.path), [
68+
vuePreset.registerTranspiler(module => /\.(m|c)?jsx?$/.test(module.path), [
6969
{ transpiler: babelTranspiler },
7070
]);
7171
vuePreset.registerTranspiler(module => /\.m?tsx?$/.test(module.path), [

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

+3-5
Original file line numberDiff line numberDiff line change
@@ -529,16 +529,14 @@ async function compile(opts: any) {
529529
dependencies,
530530
};
531531
} catch (err) {
532-
if (
533-
!fsInitialized &&
534-
(err.message.indexOf('Cannot find module') > -1 || err.code === 'EIO')
535-
) {
532+
const isModuleNotFoundError = err.code === 'MODULE_NOT_FOUND';
533+
if (!fsInitialized && (isModuleNotFoundError || err.code === 'EIO')) {
536534
// BrowserFS was needed but wasn't initialized
537535
await waitForFs(loaderContextId);
538536
return compile(opts);
539537
}
540538

541-
if (err.message.indexOf('Cannot find module') > -1) {
539+
if (isModuleNotFoundError) {
542540
// Try to download the file and all dependencies, retry compilation then
543541
await downloadFromError({
544542
error: err,

packages/app/src/sandbox/eval/transpilers/babel/worker/dynamic-download.ts

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import resolve from 'browser-resolve';
21
import { getGlobal } from '@codesandbox/common/lib/utils/global';
2+
import { ModuleNotFoundError } from 'sandpack-core/lib/resolver/errors/ModuleNotFound';
3+
34
import getRequireStatements from './simple-get-require-statements';
4-
import { packageFilter } from '../../../utils/resolve-utils';
55
import { convertEsModule } from '../ast/convert-esmodule';
66
import { generateCode, parseModule } from '../ast/utils';
77
import { ChildHandler } from '../../worker-transpiler/child-handler';
8+
import { resolve } from './utils/resolve';
89

910
const global = getGlobal();
1011
const path = global.BrowserFS.BFSRequire('path');
@@ -110,11 +111,9 @@ function downloadRequires(
110111
}
111112

112113
try {
113-
resolve.sync(foundR.path, {
114+
resolve(foundR.path, {
114115
filename: currentPath,
115116
extensions: ['.js', '.json'],
116-
moduleDirectory: ['node_modules'],
117-
packageFilter,
118117
});
119118
} catch (err) {
120119
await downloadFromError({
@@ -218,22 +217,34 @@ export async function downloadPath(
218217
return r;
219218
}
220219

220+
function extractPathFromError(err: Error | ModuleNotFoundError): string {
221+
if (err instanceof ModuleNotFoundError) {
222+
return err.filepath;
223+
}
224+
225+
if (err.message.indexOf('Cannot find module') > -1) {
226+
const dep = err.message.match(/Cannot find module '(.*?)'/)[1];
227+
const from = err.message.match(/from '(.*?)'/)[1];
228+
const absolutePath = dep.startsWith('.') ? path.join(from, dep) : dep;
229+
230+
return absolutePath;
231+
}
232+
233+
return null;
234+
}
235+
221236
export function downloadFromError(opts: {
222237
error: Error;
223238
childHandler: ChildHandler;
224239
loaderContextId: number;
225240
}) {
226241
const { error, childHandler, loaderContextId } = opts;
227-
if (error.message.indexOf('Cannot find module') > -1) {
228-
const dep = error.message.match(/Cannot find module '(.*?)'/)[1];
229-
const from = error.message.match(/from '(.*?)'/)[1];
230-
const absolutePath = dep.startsWith('.') ? path.join(from, dep) : dep;
231-
232-
return downloadPath(absolutePath, {
242+
const moduleSpecifier = extractPathFromError(error);
243+
if (moduleSpecifier) {
244+
return downloadPath(moduleSpecifier, {
233245
childHandler,
234246
loaderContextId,
235247
});
236248
}
237-
238249
return Promise.resolve();
239250
}

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

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import resolve from 'browser-resolve';
21
import hashsum from 'hash-sum';
32
import * as events from 'events';
43
import * as crypto from 'crypto';
@@ -8,8 +7,8 @@ import type FSType from 'fs';
87
import isESModule from 'sandbox/eval/utils/is-es-module';
98
import evaluateCode from 'sandpack-core/lib/runner/eval';
109
import detectOldBrowser from '@codesandbox/common/lib/detect-old-browser';
11-
import { packageFilter } from '../../../utils/resolve-utils';
12-
import { patchedResolve } from './utils/resolvePatch';
10+
11+
import { resolve } from './utils/resolve';
1312
import { getBabelTypes } from './utils/babelTypes';
1413

1514
let cache = {};
@@ -67,7 +66,7 @@ export default function evaluate(
6766
}
6867

6968
if (requirePath === 'resolve') {
70-
return patchedResolve();
69+
return (...args) => resolve(...args);
7170
}
7271

7372
if (requirePath === 'babel-register') {
@@ -132,11 +131,9 @@ export default function evaluate(
132131

133132
const resolvedPath =
134133
cachedPaths[dirName][requirePath] ||
135-
resolve.sync(requirePath, {
134+
resolve(requirePath, {
136135
filename: path,
137136
extensions: ['.js', '.json'],
138-
moduleDirectory: ['node_modules'],
139-
packageFilter,
140137
});
141138

142139
cachedPaths[dirName][requirePath] = resolvedPath;
@@ -206,11 +203,9 @@ export function evaluateFromPath(
206203
availablePlugins: Object,
207204
availablePresets: Object
208205
) {
209-
const resolvedPath = resolve.sync(path, {
210-
filename: currentPath,
206+
const resolvedPath = resolve(path, {
207+
filename: path,
211208
extensions: ['.js', '.json'],
212-
moduleDirectory: ['node_modules'],
213-
packageFilter,
214209
});
215210

216211
const code = fs.readFileSync(resolvedPath).toString();

packages/app/src/sandbox/eval/transpilers/babel/worker/utils/macrosPatch.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as macrosPlugin from 'babel-plugin-macros';
2-
import { patchedResolve } from './resolvePatch';
2+
import { resolve } from './resolve';
33

44
// eslint-disable-next-line
55
let finalExports = function m(babel, options) {
66
return macrosPlugin(babel, {
77
...options,
88
resolvePath(source, basePath) {
9-
return patchedResolve().sync(source, {
10-
basePath,
9+
return resolve(source, {
10+
filename: basePath + '/index',
1111
});
1212
},
1313
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import {
2+
resolveSync,
3+
IResolveOptionsInput,
4+
} from 'sandpack-core/lib/resolver/resolver';
5+
import gensync from 'gensync';
6+
7+
/**
8+
* Patch 'resolve' to configure it to resolve esmodules by default. babel-plugin-macros goes
9+
* for the commonjs version, which we don't download anymore by default.
10+
*/
11+
export function resolve(
12+
specifier: string,
13+
opts: Partial<IResolveOptionsInput>
14+
) {
15+
const fs = global.BrowserFS.BFSRequire('fs');
16+
17+
const isFile = p => {
18+
try {
19+
const stats = fs.statSync(p);
20+
return stats.isFile();
21+
} catch (err) {
22+
return false;
23+
}
24+
};
25+
26+
const resolvedPath = resolveSync(specifier, {
27+
filename: '/index.js', // idk...
28+
extensions: ['.js', '.mjs', '.json', '.ts', '.tsx'],
29+
moduleDirectories: ['node_modules'],
30+
...opts,
31+
isFile: gensync({
32+
sync: p => isFile(p),
33+
}),
34+
readFile: gensync({
35+
sync: p => fs.readFileSync(p, 'utf8'),
36+
}),
37+
});
38+
return resolvedPath;
39+
}

0 commit comments

Comments
 (0)