Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ default-members = [
"crates/turbopack-swc-utils",
"crates/turbopack-test-utils",
"crates/turbopack-tests",
"crates/turbopack-wasm",
"xtask",
]

Expand Down Expand Up @@ -126,6 +127,7 @@ turbopack-static = { path = "crates/turbopack-static" }
turbopack-swc-utils = { path = "crates/turbopack-swc-utils" }
turbopack-test-utils = { path = "crates/turbopack-test-utils" }
turbopack-tests = { path = "crates/turbopack-tests" }
turbopack-wasm = { path = "crates/turbopack-wasm" }
turbopath = { path = "crates/turborepo-paths" }
turborepo = { path = "crates/turborepo" }
turborepo-api-client = { path = "crates/turborepo-api-client" }
Expand Down
11 changes: 9 additions & 2 deletions crates/turbopack-ecmascript-runtime/js/src/build/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// <reference path="../shared/runtime-utils.ts" />
/// <reference path="../shared-node/require.ts" />
/// <reference path="../shared-node/node-utils.ts" />

declare var RUNTIME_PUBLIC_PATH: string;

Expand Down Expand Up @@ -52,7 +52,7 @@ function loadChunk(chunkPath: ChunkPath) {
return;
}

const resolved = require.resolve(path.resolve(RUNTIME_ROOT, chunkPath));
const resolved = path.resolve(RUNTIME_ROOT, chunkPath);
const chunkModules: ModuleFactories = require(resolved);

for (const [moduleId, moduleFactory] of Object.entries(chunkModules)) {
Expand All @@ -74,6 +74,12 @@ function loadChunkAsync(source: SourceInfo, chunkPath: string): Promise<void> {
});
}

function loadWebAssembly(chunkPath: ChunkPath, imports: WebAssembly.Imports) {
const resolved = path.resolve(RUNTIME_ROOT, chunkPath);

return loadWebAssemblyFromPath(resolved, imports);
}

function instantiateModule(id: ModuleId, source: SourceInfo): Module {
const moduleFactory = moduleFactories[id];
if (typeof moduleFactory !== "function") {
Expand Down Expand Up @@ -134,6 +140,7 @@ function instantiateModule(id: ModuleId, source: SourceInfo): Module {
m: module,
c: moduleCache,
l: loadChunkAsync.bind(null, { type: SourceType.Parent, parentId: id }),
w: loadWebAssembly,
g: globalThis,
__dirname: module.id.replace(/(^|\/)[\/]+$/, ""),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
// environment
"lib": ["ESNext"],
// environment, we need WebWorker for WebAssembly types (not part of @types/node yet)
"lib": ["ESNext", "WebWorker"],
"types": ["node"]
},
"include": ["*.ts"]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ declare var augmentContext: (
context: TurbopackDevBaseContext
) => TurbopackDevContext;
declare var commonJsRequireContext: CommonJsRequireContext;
declare var loadWebAssembly: (source: SourceInfo, wasmChunkPath: ChunkPath, imports: WebAssembly.Imports) => Exports;
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ function instantiateModule(id: ModuleId, source: SourceInfo): Module {

// NOTE(alexkirsz) This can fail when the module encounters a runtime error.
try {
const sourceInfo: SourceInfo = { type: SourceType.Parent, parentId: id };

runModuleExecutionHooks(module, (refresh) => {
moduleFactory.call(
module.exports,
Expand All @@ -331,7 +333,8 @@ function instantiateModule(id: ModuleId, source: SourceInfo): Module {
n: exportNamespace.bind(null, module),
m: module,
c: moduleCache,
l: loadChunk.bind(null, { type: SourceType.Parent, parentId: id }),
l: loadChunk.bind(null, sourceInfo),
w: loadWebAssembly.bind(null, sourceInfo),
g: globalThis,
k: refresh,
__dirname: module.id.replace(/(^|\/)\/+$/, ""),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
// environment
"lib": ["ESNext"],
"target": "ESNext"
// environment, we need WebWorker for WebAssembly types
"lib": ["ESNext", "WebWorker"]
},
"include": ["runtime-base.ts", "dummy.ts"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ function commonJsRequireContext(
return commonJsRequire(sourceModule, entry.id());
}

async function loadWebAssembly(
_source: SourceInfo,
wasmChunkPath: ChunkPath,
importsObj: WebAssembly.Imports
): Promise<Exports> {
const chunkUrl = `/${getChunkRelativeUrl(wasmChunkPath)}`;

const req = fetch(chunkUrl);
const { instance } = await WebAssembly.instantiateStreaming(req, importsObj);

return instance.exports;
}

(() => {
BACKEND = {
async registerChunk(chunkPath, params) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

/// <reference path="../base/runtime-base.ts" />
/// <reference path="../../../shared-node/require.ts" />
/// <reference path="../../../shared-node/node-utils.ts" />

interface RequireContextEntry {
// Only the Node.js backend has this flag.
Expand All @@ -28,6 +28,33 @@ function augmentContext(context: TurbopackDevBaseContext): TurbopackDevContext {
return nodejsContext;
}

function resolveChunkPath(chunkPath: ChunkPath, source: SourceInfo) {
let fromChunkPath = undefined;
switch (source.type) {
case SourceType.Runtime:
fromChunkPath = source.chunkPath;
break;
case SourceType.Parent:
fromChunkPath = getFirstModuleChunk(source.parentId);
break;
case SourceType.Update:
break;
}

const path = require("node:path");
return path.resolve(__dirname, path.posix.relative(path.dirname(fromChunkPath), chunkPath));
}

function loadWebAssembly(
source: SourceInfo,
chunkPath: ChunkPath,
imports: WebAssembly.Imports
) {
const resolved = resolveChunkPath(chunkPath, source);

return loadWebAssemblyFromPath(resolved, imports);
}

let BACKEND: RuntimeBackend;

(() => {
Expand Down Expand Up @@ -67,28 +94,14 @@ let BACKEND: RuntimeBackend;
return;
}

let fromChunkPath = undefined;
switch (source.type) {
case SourceType.Runtime:
fromChunkPath = source.chunkPath;
break;
case SourceType.Parent:
fromChunkPath = getFirstModuleChunk(source.parentId);
break;
case SourceType.Update:
break;
}

// We'll only mark the chunk as loaded once the script has been executed,
// which happens in `registerChunk`. Hence the absence of `resolve()`.
const path = require("path");
const resolved = require.resolve(
"./" + path.relative(path.dirname(fromChunkPath), chunkPath)
);
const resolved = resolveChunkPath(chunkPath, source);

require(resolved);
}
})();

function _eval({ code, url, map }: EcmascriptModuleEntry): ModuleFactory {
function _eval(_: EcmascriptModuleEntry): ModuleFactory {
throw new Error("HMR evaluation is not implemented on this backend");
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
// environment
"lib": ["ESNext"],
// environment, we need WebWorker for WebAssembly types (not part of @types/node yet)
"lib": ["ESNext", "WebWorker"],
"types": ["node"]
},
"include": ["*.ts"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ function commonJsRequireContext(
return commonJsRequire(sourceModule, entry.id());
}

async function loadWebAssembly(
_source: SourceInfo,
_id: ModuleId,
_importsObj: any
): Promise<Exports> {
throw new Error("loading WebAssemly is not supported");
}

(() => {
BACKEND = {
// The "none" runtime expects all chunks within the same chunk group to be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,15 @@ externalRequire.resolve = (
) => {
return require.resolve(id, options);
};

async function loadWebAssemblyFromPath(
path: string,
importsObj: WebAssembly.Imports
): Promise<Exports> {
const { readFile } = require("fs/promises") as typeof import("fs/promises");

const buffer = await readFile(path);
const { instance } = await WebAssembly.instantiate(buffer, importsObj);

return instance.exports;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
// environment
"lib": ["ESNext"]
// environment, we need WebWorker for WebAssembly types (not part of @types/node yet)
"lib": ["ESNext", "WebWorker"],
"types": ["node"]
},
"include": ["*.ts"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ExportNamespace = (namespace: any) => void;
type DynamicExport = (object: Record<string, any>) => void;

type LoadChunk = (chunkPath: ChunkPath) => Promise<any> | undefined;
type LoadWebAssembly = (wasmChunkPath: ChunkPath, imports: WebAssembly.Imports) => Exports;

type ModuleCache = Record<ModuleId, Module>;
type ModuleFactories = Record<ModuleId, ModuleFactory>;
Expand Down Expand Up @@ -57,6 +58,7 @@ interface TurbopackBaseContext {
m: Module;
c: ModuleCache;
l: LoadChunk;
w: LoadWebAssembly,
g: typeof globalThis;
__dirname: string;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
// environment
"lib": ["ESNext"]
// environment, we need WebWorker for WebAssembly types
"lib": ["ESNext", "WebWorker"]
},
"include": ["*.ts"]
}
2 changes: 1 addition & 1 deletion crates/turbopack-ecmascript-runtime/src/build_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub async fn get_build_runtime_code(environment: Vc<Environment>) -> Result<Vc<C
let shared_runtime_utils_code =
embed_static_code(asset_context, "shared/runtime-utils.ts".to_string());
let shared_node_utils_code =
embed_static_code(asset_context, "shared-node/require.ts".to_string());
embed_static_code(asset_context, "shared-node/node-utils.ts".to_string());
let runtime_code = embed_static_code(asset_context, "build/runtime.ts".to_string());

let mut code = CodeBuilder::default();
Expand Down
2 changes: 1 addition & 1 deletion crates/turbopack-ecmascript-runtime/src/dev_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub async fn get_dev_runtime_code(

if matches!(chunk_loading, ChunkLoading::NodeJs) {
code.push_code(
&*embed_static_code(asset_context, "shared-node/require.ts".to_string()).await?,
&*embed_static_code(asset_context, "shared-node/node-utils.ts".to_string()).await?,
);
}

Expand Down
7 changes: 7 additions & 0 deletions crates/turbopack-ecmascript/src/chunk/esm_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub(crate) struct EsmScope {
scc_graph: DiGraphMap<Vc<EsmScopeScc>, ()>,
}

/// Represents a strongly connected component in the EsmScope graph.
///
/// See https://en.wikipedia.org/wiki/Strongly_connected_component
#[turbo_tasks::value(transparent)]
pub(crate) struct EsmScopeScc(Vec<Vc<Box<dyn EcmascriptChunkPlaceable>>>);

Expand All @@ -33,6 +36,7 @@ pub(crate) struct EsmScopeSccs(Vec<Vc<EsmScopeScc>>);

#[turbo_tasks::value_impl]
impl EsmScope {
/// Create a new [EsmScope] from the availability root given.
#[turbo_tasks::function]
pub(crate) async fn new(availability_info: Value<AvailabilityInfo>) -> Result<Vc<Self>> {
let assets = if let Some(root) = availability_info.current_availability_root() {
Expand Down Expand Up @@ -78,6 +82,8 @@ impl EsmScope {
Ok(Self::cell(EsmScope { scc_map, scc_graph }))
}

/// Gets the [EsmScopeScc] for a given [EcmascriptChunkPlaceable] if it's
/// part of this graph.
#[turbo_tasks::function]
pub(crate) async fn get_scc(
self: Vc<Self>,
Expand All @@ -88,6 +94,7 @@ impl EsmScope {
Ok(Vc::cell(this.scc_map.get(&placeable).copied()))
}

/// Returns all direct children of an [EsmScopeScc].
#[turbo_tasks::function]
pub(crate) async fn get_scc_children(
self: Vc<Self>,
Expand Down
6 changes: 6 additions & 0 deletions crates/turbopack-ecmascript/src/chunk/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ impl EcmascriptChunkItemContent {
if this.options.exports {
args.push("e: exports");
}
if this.options.wasm {
args.push("w: __turbopack_wasm__");
}
let mut code = CodeBuilder::default();
let args = FormatIter(|| args.iter().copied().intersperse(", "));
if this.options.this {
Expand Down Expand Up @@ -157,6 +160,9 @@ pub struct EcmascriptChunkItemOptions {
/// or is importing async modules).
pub async_module: Option<AsyncModuleOptions>,
pub this: bool,
/// Whether this chunk item's module factory should include
/// `__turbopack_wasm__` to load WebAssembly.
pub wasm: bool,
pub placeholder_for_future_extensions: (),
}

Expand Down
2 changes: 1 addition & 1 deletion crates/turbopack-ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub mod magic_identifier;
pub(crate) mod manifest;
pub mod parse;
mod path_visitor;
pub(crate) mod references;
pub mod references;
pub mod resolve;
pub(crate) mod special_cases;
pub(crate) mod static_code;
Expand Down
Loading