Skip to content

Commit

Permalink
Use fs and vm for async chunk loading in Node (#8722)
Browse files Browse the repository at this point in the history
### Description

Use `fs` and `vm` when loading chunks asynchronously in Node, to not
block the thread while reading large chunks

Closes PACK-411

### Testing Instructions

Using the `test/e2e/app-dir/dynamic/` Next.js tests
  • Loading branch information
mischnic committed Jul 16, 2024
1 parent e5af352 commit 6060256
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 17 deletions.
48 changes: 40 additions & 8 deletions crates/turbopack-ecmascript-runtime/js/src/nodejs/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type ModuleFactory = (
) => undefined;

const url = require("url");
const fs = require("fs/promises");
const vm = require("vm");

const moduleFactories: ModuleFactories = Object.create(null);
const moduleCache: ModuleCache = Object.create(null);
Expand Down Expand Up @@ -118,15 +120,45 @@ async function loadChunkAsync(
source: SourceInfo,
chunkData: ChunkData
): Promise<any> {
return new Promise<void>((resolve, reject) => {
try {
loadChunk(chunkData, source);
} catch (err) {
reject(err);
return;
const chunkPath = typeof chunkData === "string" ? chunkData : chunkData.path;
if (!chunkPath.endsWith(".js")) {
// We only support loading JS chunks in Node.js.
// This branch can be hit when trying to load a CSS chunk.
return;
}

const resolved = path.resolve(RUNTIME_ROOT, chunkPath);

try {
const contents = await fs.readFile(resolved, "utf-8");

const module = {
exports: {},
};
vm.runInThisContext(
"(function(module, exports, require, __dirname, __filename) {" +
contents +
"\n})",
resolved
)(module, module.exports, require, path.dirname(resolved), resolved);

const chunkModules: ModuleFactories = module.exports;
for (const [moduleId, moduleFactory] of Object.entries(chunkModules)) {
if (!moduleFactories[moduleId]) {
moduleFactories[moduleId] = moduleFactory;
}
}
resolve();
});
} catch (e) {
let errorMessage = `Failed to load chunk ${chunkPath}`;

if (source) {
errorMessage += ` from ${stringifySourceInfo(source)}`;
}

throw new Error(errorMessage, {
cause: e,
});
}
}

function loadWebAssembly(chunkPath: ChunkPath, imports: WebAssembly.Imports) {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6060256

Please sign in to comment.