Skip to content

Commit fdffa84

Browse files
authored
Packager fixes (codesandbox#253)
* Packager fixes * Enable packager by default * Flip more switches * Fix svelte * Fix react-ts * Use custom threshold for apollo sandbox * Remove flow typed changes * Allow resolving of versioned dependencies * Set experimentalPackager defaults to false * Make dynamic fetching robust & provide dynamic polyfills for node libraries * Revert "Set experimentalPackager defaults to false" This reverts commit 1d078ea. * Fix file resolving for main dependencies * Revert "Revert "Set experimentalPackager defaults to false"" This reverts commit dcbb1a4. * Use production search on local server
1 parent bf467fb commit fdffa84

File tree

18 files changed

+436
-187
lines changed

18 files changed

+436
-187
lines changed

integration-tests/tests/sandboxes.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const SANDBOXES = [
77
'svelte',
88
'react-ts',
99
'github/reactjs/redux/tree/master/examples/todomvc',
10-
'jvlrl98xw3',
10+
{ id: 'jvlrl98xw3', threshold: 0.05 },
1111
'vVoQVk78',
1212
'github/faceyspacey/redux-first-router-codesandbox/tree/master',
1313
'mZRjw05yp',
@@ -20,6 +20,7 @@ const SANDBOXES = [
2020
SANDBOXES.forEach(sandbox => {
2121
const id = sandbox.id || sandbox;
2222
const root = sandbox.root || '#root';
23+
const threshold = sandbox.threshold || 0.01;
2324
let browser = puppeteer.launch({
2425
args: ['--no-sandbox', '--disable-setuid-sandbox'],
2526
});
@@ -45,7 +46,7 @@ SANDBOXES.forEach(sandbox => {
4546

4647
expect(screenshot).toMatchImageSnapshot({
4748
customDiffConfig: {
48-
threshold: 0.01, // 1% threshold
49+
threshold,
4950
},
5051
customSnapshotIdentifier: id.split('/').join('-'),
5152
});

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"extract-text-webpack-plugin": "^2.1.0",
4747
"file-loader": "^0.11.1",
4848
"filesize": "^3.5.6",
49-
"flow-bin": "^0.48.0",
49+
"flow-bin": "^0.57.3",
5050
"fs-extra": "^2.1.2",
5151
"gulp-replace": "^0.5.4",
5252
"gzip-size": "3.0.0",
@@ -101,10 +101,10 @@
101101
"babel-plugin-transform-remove-strict-mode": "^0.0.2",
102102
"babel-plugin-transform-vue-jsx": "^3.5.0",
103103
"babel-polyfill": "^6.23.0",
104-
"babel-preset-env": "^1.6.0",
105104
"babel-preset-stage-0": "^6.24.1",
106105
"babel-standalone": "^6.25.0",
107106
"base64-loader": "^1.0.0",
107+
"browser-resolve": "CompuIves/node-browser-resolve",
108108
"codemirror": "^5.27.4",
109109
"codesandbox-api": "^0.0.12",
110110
"color": "^0.11.4",

src/app/utils/config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
export const ALGOLIA_API_KEY = '7675b9e87ed7dd5bbf18f3e5310f92d6';
22
export const ALGOLIA_APPLICATION_ID = 'ZACZHDBO7S';
33
export const ALGOLIA_DEFAULT_INDEX =
4-
process.env.NODE_ENV === 'production' ? 'prod_sandboxes' : 'dev_sandboxes';
4+
process.env.NODE_ENV === 'production' || process.env.LOCAL_SERVER
5+
? 'prod_sandboxes'
6+
: 'dev_sandboxes';
57

68
export const STRIPE_API_KEY =
79
process.env.NODE_ENV === 'production'

src/sandbox/compile.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ async function compile({
120120
updateManager(sandboxId, template, managerModules, experimentalPackager),
121121
]);
122122

123-
const { externals = {} } = manifest;
124-
123+
const { externals = {} } = manifest || {};
125124
if (experimentalPackager) {
126125
manager.setManifest(manifest);
127126
} else {

src/sandbox/eval/loaders/eval.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// @flow
2+
import buildProcess from './utils/process';
23

34
/* eslint-disable no-unused-vars */
45
export default function(code: string, require: Function, exports) {
56
const module = { exports: {} };
67
const global = window;
7-
const process = { env: { NODE_ENV: 'development' } };
8+
const process = buildProcess();
89

910
try {
1011
eval(code); // eslint-disable-line no-eval
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const process = {};
2+
process.title = 'browser';
3+
process.browser = true;
4+
process.env = { NODE_ENV: 'development' };
5+
process.argv = [];
6+
process.version = ''; // empty string to avoid regexp issues
7+
process.versions = {};
8+
9+
function noop() {}
10+
11+
process.on = noop;
12+
process.addListener = noop;
13+
process.once = noop;
14+
process.off = noop;
15+
process.removeListener = noop;
16+
process.removeAllListeners = noop;
17+
process.emit = noop;
18+
process.prependListener = noop;
19+
process.prependOnceListener = noop;
20+
21+
process.listeners = function(name) {
22+
return [];
23+
};
24+
25+
process.binding = function(name) {
26+
throw new Error('process.binding is not supported');
27+
};
28+
29+
process.cwd = function() {
30+
return '/';
31+
};
32+
process.chdir = function(dir) {
33+
throw new Error('process.chdir is not supported');
34+
};
35+
process.umask = function() {
36+
return 0;
37+
};
38+
39+
export default function build() {
40+
return process;
41+
}

src/sandbox/eval/manager.js

Lines changed: 113 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// @flow
22
import { flattenDeep, uniq, values } from 'lodash';
3+
import resolve from 'browser-resolve';
34

45
import * as pathUtils from 'common/utils/path';
56

67
import type { Module } from './entities/module';
78
import TranspiledModule from './transpiled-module';
89
import Preset from './presets';
9-
import nodeResolvePath from './utils/node-resolve-path';
10-
import fetchModule from './npm/fetch-npm-module';
10+
import fetchModule, { getCombinedMetas } from './npm/fetch-npm-module';
11+
import coreLibraries from './npm/get-core-libraries';
12+
import getDependencyName from './utils/get-dependency-name';
1113
import DependencyNotFoundError from '../errors/dependency-not-found-error';
1214
import ModuleNotFoundError from '../errors/module-not-found-error';
1315

@@ -20,15 +22,21 @@ type ModuleObject = {
2022
};
2123

2224
export type Manifest = {
23-
aliases: {
24-
[path: string]: string | false,
25-
},
2625
contents: {
2726
[path: string]: { content: string, requires: Array<string> },
2827
},
2928
dependencies: Array<{ name: string, version: string }>,
3029
dependencyDependencies: {
31-
[name: string]: string,
30+
[name: string]: {
31+
semver: string,
32+
resolved: string,
33+
parents: string[],
34+
},
35+
},
36+
dependencyAliases: {
37+
[name: string]: {
38+
[depName: string]: string,
39+
},
3240
},
3341
};
3442

@@ -73,8 +81,21 @@ export default class Manager {
7381
this.externals = externals;
7482
}
7583

76-
setManifest(manifest: Manifest) {
77-
this.manifest = manifest;
84+
setManifest(manifest: ?Manifest) {
85+
this.manifest = manifest || {
86+
contents: {},
87+
dependencies: [],
88+
dependencyDependencies: {},
89+
dependencyAliases: {},
90+
};
91+
92+
Object.keys(this.manifest.contents).forEach(path => {
93+
this.addModule({
94+
path,
95+
code: this.manifest.contents[path].content,
96+
requires: this.manifest.contents[path].requires,
97+
});
98+
});
7899
}
79100

80101
evaluateModule(module: Module) {
@@ -187,94 +208,102 @@ export default class Manager {
187208
return values(this.transpiledModules).map(t => t.module);
188209
}
189210

190-
resolveModule(
191-
path: string,
192-
defaultExtensions: Array<string> = ['js', 'jsx', 'json']
193-
): Module {
194-
const moduleObject = this.transpiledModules;
195-
const foundPath = nodeResolvePath(path, moduleObject, defaultExtensions);
211+
getAliasedDependencyPath(path: string, currentPath: string) {
212+
const isDependency = /^(\w|@\w)/.test(path);
213+
214+
if (!isDependency) {
215+
return path;
216+
}
196217

197-
if (foundPath && moduleObject[foundPath]) {
198-
return moduleObject[foundPath].module;
218+
const isCurrentPathDependency = currentPath.startsWith('/node_modules');
219+
if (!isCurrentPathDependency) {
220+
return path;
199221
}
200222

201-
throw new ModuleNotFoundError(path, false);
223+
const dependencyName = getDependencyName(path);
224+
const previousDependencyName = getDependencyName(currentPath);
225+
226+
if (
227+
this.manifest.dependencyAliases[previousDependencyName] &&
228+
this.manifest.dependencyAliases[previousDependencyName][dependencyName]
229+
) {
230+
const aliasedDependencyName = this.manifest.dependencyAliases[
231+
previousDependencyName
232+
][dependencyName];
233+
234+
return path.replace(dependencyName, aliasedDependencyName);
235+
}
236+
237+
return path;
202238
}
203239

204-
resolveDependency(
240+
resolveModule(
205241
path: string,
242+
currentPath: string,
206243
defaultExtensions: Array<string> = ['js', 'jsx', 'json']
207244
): Module {
208-
let depPath = path.replace('/node_modules/', '');
245+
const aliasedPath = this.getAliasedDependencyPath(path, currentPath);
246+
const shimmedPath = coreLibraries[aliasedPath] || aliasedPath;
247+
try {
248+
const resolvedPath = resolve.sync(shimmedPath, {
249+
filename: currentPath,
250+
extensions: defaultExtensions.map(ext => '.' + ext),
251+
isFile: p => !!this.transpiledModules[p] || !!getCombinedMetas()[p],
252+
readFileSync: p => {
253+
if (this.transpiledModules[p]) {
254+
return this.transpiledModules[p].module.code;
255+
}
256+
257+
const err = new Error('Could not find ' + p);
258+
err.code = 'ENOENT';
259+
260+
throw err;
261+
},
262+
});
263+
264+
if (NODE_LIBS.includes(resolvedPath)) {
265+
return {
266+
path: pathUtils.join('/node_modules', resolvedPath),
267+
code: `// empty`,
268+
requires: [],
269+
};
270+
}
209271

210-
const aliasedPath = nodeResolvePath(
211-
depPath,
212-
this.manifest.aliases,
213-
defaultExtensions
214-
);
272+
return this.transpiledModules[resolvedPath].module;
273+
} catch (e) {
274+
let connectedPath = /^(\w|@\w)/.test(shimmedPath)
275+
? pathUtils.join('/node_modules', shimmedPath)
276+
: pathUtils.join(pathUtils.dirname(currentPath), shimmedPath);
215277

216-
depPath = aliasedPath == null ? depPath : aliasedPath;
278+
const isDependency = connectedPath.includes('/node_modules/');
217279

218-
const alias = this.manifest.aliases[depPath];
219-
// So aliased to not be included in the bundle, decided by browser object on pkg
220-
if (alias === false) {
221-
return {
222-
path: pathUtils.join('/node_modules', depPath),
223-
code: 'module.exports = null;',
224-
requires: [],
225-
};
226-
}
280+
connectedPath = connectedPath.replace('/node_modules/', '');
227281

228-
// Check if the module is already there
229-
const foundPath = nodeResolvePath(
230-
path,
231-
this.transpiledModules,
232-
defaultExtensions
233-
);
234-
if (foundPath && this.transpiledModules[foundPath]) {
235-
return this.transpiledModules[foundPath].module;
236-
}
282+
if (!isDependency) {
283+
throw new ModuleNotFoundError(shimmedPath, false);
284+
}
237285

238-
const resolvedPath = nodeResolvePath(
239-
alias || depPath,
240-
this.manifest.contents,
241-
defaultExtensions
242-
);
243-
if (resolvedPath && this.manifest.contents[resolvedPath]) {
244-
return {
245-
path: pathUtils.join('/node_modules', resolvedPath),
246-
code: this.manifest.contents[resolvedPath].content,
247-
requires: this.manifest.contents[resolvedPath].requires,
248-
};
249-
}
286+
const dependencyName = getDependencyName(connectedPath);
250287

251-
const dependencyParts = depPath.split('/');
252-
const dependencyName = depPath.startsWith('@')
253-
? `${dependencyParts[0]}/${dependencyParts[1]}`
254-
: dependencyParts[0];
255-
256-
if (NODE_LIBS.includes(dependencyName)) {
257-
return {
258-
path: pathUtils.join('/node_modules', depPath),
259-
code: `// empty`,
260-
requires: [],
261-
};
262-
}
263-
264-
if (
265-
this.manifest.dependencies.find(d => d.name === dependencyName) ||
266-
this.manifest.dependencyDependencies[dependencyName]
267-
) {
268-
throw new ModuleNotFoundError(alias || depPath, true);
269-
} else {
270-
throw new DependencyNotFoundError(alias || depPath);
288+
if (
289+
this.manifest.dependencies.find(d => d.name === dependencyName) ||
290+
this.manifest.dependencyDependencies[dependencyName]
291+
) {
292+
throw new ModuleNotFoundError(connectedPath, true);
293+
} else {
294+
throw new DependencyNotFoundError(connectedPath);
295+
}
271296
}
272297
}
273298

274-
async downloadDependency(path: string): Promise<TranspiledModule> {
299+
async downloadDependency(
300+
path: string,
301+
currentPath: string
302+
): Promise<TranspiledModule> {
275303
return fetchModule(
276304
path,
277-
this.manifest,
305+
currentPath,
306+
this,
278307
this.preset.ignoredExtensions
279308
).then(module => this.getTranspiledModule(module));
280309
}
@@ -294,28 +323,15 @@ export default class Manager {
294323
// pop() mutates queryPath, queryPath is now just the loaders
295324
const modulePath = queryPath.pop();
296325

297-
let module;
298-
299-
let newPath = pathUtils
300-
.join(
301-
pathUtils.dirname(currentPath),
302-
this.preset.getAliasedPath(modulePath)
303-
)
326+
const newPath = this.preset
327+
.getAliasedPath(modulePath)
304328
.replace(/.*\{\{sandboxRoot\}\}/, '');
305329

306-
if (/^(\w|@\w)/.test(modulePath) && !modulePath.includes('!')) {
307-
// Prepend node_modules and go to root if it is a dependency
308-
newPath = pathUtils.join(
309-
'/node_modules',
310-
this.preset.getAliasedPath(modulePath)
311-
);
312-
}
313-
314-
if (newPath.startsWith('/node_modules')) {
315-
module = this.resolveDependency(newPath, this.preset.ignoredExtensions);
316-
} else {
317-
module = this.resolveModule(newPath, this.preset.ignoredExtensions);
318-
}
330+
const module = this.resolveModule(
331+
newPath,
332+
currentPath,
333+
this.preset.ignoredExtensions
334+
);
319335

320336
return this.getTranspiledModule(module, queryPath.join('!'));
321337
}

0 commit comments

Comments
 (0)