Skip to content

Commit

Permalink
edge runtime also has externals (#6566)
Browse files Browse the repository at this point in the history
### Description

Add externals handling code to edge runtime

### Testing Instructions

<!--
  Give a quick description of steps to test your changes.
-->


Closes PACK-2034
  • Loading branch information
sokra authored Nov 26, 2023
1 parent e61d1ff commit 12cdfed
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 75 deletions.
13 changes: 13 additions & 0 deletions crates/turbopack-core/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,19 @@ impl Environment {
})
}

#[turbo_tasks::function]
pub async fn supports_wasm(self: Vc<Self>) -> Result<Vc<bool>> {
let this = self.await?;
Ok(match this.execution {
ExecutionEnvironment::NodeJsBuildTime(..) | ExecutionEnvironment::NodeJsLambda(_) => {
Vc::cell(true)
}
ExecutionEnvironment::Browser(_) => Vc::cell(false),
ExecutionEnvironment::EdgeWorker(_) => Vc::cell(false),
ExecutionEnvironment::Custom(_) => todo!(),
})
}

#[turbo_tasks::function]
pub async fn resolve_extensions(self: Vc<Self>) -> Result<Vc<Vec<String>>> {
let env = self.await?;
Expand Down
51 changes: 33 additions & 18 deletions crates/turbopack-ecmascript-runtime/js/src/build/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference path="../shared/runtime-utils.ts" />
/// <reference path="../shared-node/node-utils.ts" />
/// <reference path="../shared-node/node-externals-utils.ts" />
/// <reference path="../shared-node/node-wasm-utils.ts" />

declare var RUNTIME_PUBLIC_PATH: string;
declare var OUTPUT_ROOT: string;
Expand All @@ -19,21 +20,21 @@ enum SourceType {

type SourceInfo =
| {
type: SourceType.Runtime;
chunkPath: ChunkPath;
}
type: SourceType.Runtime;
chunkPath: ChunkPath;
}
| {
type: SourceType.Parent;
parentId: ModuleId;
};
type: SourceType.Parent;
parentId: ModuleId;
};

type ExternalRequire = (id: ModuleId) => Exports | EsmNamespaceObject;
type ExternalImport = (id: ModuleId) => Promise<Exports | EsmNamespaceObject>;

interface TurbopackNodeBuildContext extends TurbopackBaseContext {
p: ResolveAbsolutePath
U: RelativeURL,
R: ResolvePathFromModule,
p: ResolveAbsolutePath;
U: RelativeURL;
R: ResolvePathFromModule;
x: ExternalRequire;
y: ExternalImport;
}
Expand All @@ -44,11 +45,14 @@ type ModuleFactory = (
) => undefined;

const path = require("path");
const url = require('url');
const url = require("url");

const relativePathToRuntimeRoot = path.relative(RUNTIME_PUBLIC_PATH, ".");
// Compute the relative path to the `distDir`.
const relativePathToDistRoot = path.relative(path.join(OUTPUT_ROOT, RUNTIME_PUBLIC_PATH), ".");
const relativePathToDistRoot = path.relative(
path.join(OUTPUT_ROOT, RUNTIME_PUBLIC_PATH),
"."
);
const RUNTIME_ROOT = path.resolve(__filename, relativePathToRuntimeRoot);
// Compute the absolute path to the root, by stripping distDir from the absolute path to this file.
const ABSOLUTE_ROOT = path.resolve(__filename, relativePathToDistRoot);
Expand Down Expand Up @@ -91,27 +95,38 @@ var relativeURL = function (this: any, inputUrl: string) {
values.pathname = outputUrl.replace(/[?#].*/, "");
values.origin = values.protocol = "";
values.toString = values.toJSON = (..._args: Array<any>) => outputUrl;
for (var key in values) Object.defineProperty(this, key, { enumerable: true, configurable: true, value: values[key] });
}
for (var key in values)
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
value: values[key],
});
};

relativeURL.prototype = URL.prototype;

/**
* Returns an absolute path to the given module's id.
*/
function createResolvePathFromModule(resolver: (moduleId: string) => Exports): (moduleId: string) => string {
function createResolvePathFromModule(
resolver: (moduleId: string) => Exports
): (moduleId: string) => string {
return function resolvePathFromModule(moduleId: string): string {
const exported = resolver(moduleId);
const exportedPath = exported?.default ?? exported;
if (typeof exportedPath !== 'string') {
if (typeof exportedPath !== "string") {
return exported as any;
}

const strippedAssetPrefix = exportedPath.slice(ASSET_PREFIX.length);
const resolved = path.resolve(ABSOLUTE_ROOT, OUTPUT_ROOT, strippedAssetPrefix);
const resolved = path.resolve(
ABSOLUTE_ROOT,
OUTPUT_ROOT,
strippedAssetPrefix
);

return url.pathToFileURL(resolved);
}
};
}

function loadChunk(chunkData: ChunkData): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
*/

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

interface RequireContextEntry {
// Only the Node.js backend has this flag.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

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

type ChunkRunner = {
requiredChunks: Set<ChunkPath>;
Expand All @@ -15,15 +16,22 @@ type ChunkRunner = {

let BACKEND: RuntimeBackend;

function augmentContext(context: TurbopackDevBaseContext): TurbopackDevContext {
return context;
type ExternalRequire = (
id: ModuleId,
esm?: boolean
) => Exports | EsmNamespaceObject;
type ExternalImport = (id: ModuleId) => Promise<Exports | EsmNamespaceObject>;

interface TurbopackDevContext extends TurbopackDevBaseContext {
x: ExternalRequire;
y: ExternalImport;
}

function commonJsRequireContext(
entry: RequireContextEntry,
sourceModule: Module
): Exports {
return commonJsRequire(sourceModule, entry.id());
function augmentContext(context: TurbopackDevBaseContext): TurbopackDevContext {
const nodejsContext = context as TurbopackDevContext;
nodejsContext.x = externalRequire;
nodejsContext.y = externalImport;
return nodejsContext;
}

async function loadWebAssembly(
Expand All @@ -36,14 +44,14 @@ async function loadWebAssembly(

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

// [TODO] should this behaves same as dom runtime?
function resolveAbsolutePath(modulePath?: string) {
throw new Error('resolveAbsolutePath is not implemented');
throw new Error("resolveAbsolutePath is not implemented");
}

(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,39 +62,3 @@ externalRequire.resolve = (
) => {
return require.resolve(id, options);
};

function readWebAssemblyAsResponse(path: string) {
const { createReadStream } = require("fs") as typeof import("fs");
const { Readable } = require("stream") as typeof import("stream");

const stream = createReadStream(path);

// @ts-ignore unfortunately there's a slight type mismatch with the stream.
return new Response(Readable.toWeb(stream), {
headers: {
"content-type": "application/wasm",
},
});
}

async function compileWebAssemblyFromPath(
path: string
): Promise<WebAssembly.Module> {
const response = readWebAssemblyAsResponse(path);

return await WebAssembly.compileStreaming(response);
}

async function instantiateWebAssemblyFromPath(
path: string,
importsObj: WebAssembly.Imports
): Promise<Exports> {
const response = readWebAssemblyAsResponse(path);

const { instance } = await WebAssembly.instantiateStreaming(
response,
importsObj
);

return instance.exports;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// <reference path="../shared/runtime-utils.ts" />

function readWebAssemblyAsResponse(path: string) {
const { createReadStream } = require("fs") as typeof import("fs");
const { Readable } = require("stream") as typeof import("stream");

const stream = createReadStream(path);

// @ts-ignore unfortunately there's a slight type mismatch with the stream.
return new Response(Readable.toWeb(stream), {
headers: {
"content-type": "application/wasm",
},
});
}

async function compileWebAssemblyFromPath(
path: string
): Promise<WebAssembly.Module> {
const response = readWebAssemblyAsResponse(path);

return await WebAssembly.compileStreaming(response);
}

async function instantiateWebAssemblyFromPath(
path: string,
importsObj: WebAssembly.Imports
): Promise<Exports> {
const response = readWebAssemblyAsResponse(path);

const { instance } = await WebAssembly.instantiateStreaming(
response,
importsObj
);

return instance.exports;
}
11 changes: 8 additions & 3 deletions crates/turbopack-ecmascript-runtime/src/build_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@ 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/node-utils.ts".to_string());
let shared_node_external_utils_code = embed_static_code(
asset_context,
"shared-node/node-externals-utils.ts".to_string(),
);
let shared_node_wasm_utils_code =
embed_static_code(asset_context, "shared-node/node-wasm-utils.ts".to_string());
let runtime_code = embed_static_code(asset_context, "build/runtime.ts".to_string());

let mut code = CodeBuilder::default();
code.push_code(&*shared_runtime_utils_code.await?);
code.push_code(&*shared_node_utils_code.await?);
code.push_code(&*shared_node_external_utils_code.await?);
code.push_code(&*shared_node_wasm_utils_code.await?);
code.push_code(&*runtime_code.await?);

Ok(Code::cell(code.build()))
Expand Down
14 changes: 12 additions & 2 deletions crates/turbopack-ecmascript-runtime/src/dev_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,19 @@ pub async fn get_dev_runtime_code(
code.push_code(&*shared_runtime_utils_code.await?);
code.push_code(&*runtime_base_code.await?);

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

Expand Down
5 changes: 4 additions & 1 deletion crates/turbopack-ecmascript/src/chunk/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ impl EcmascriptChunkItemContent {
async_module_options: Vc<OptionAsyncModuleOptions>,
) -> Result<Vc<Self>> {
let refresh = *chunking_context.has_react_refresh().await?;
let externals = *chunking_context.environment().node_externals().await?;
let externals = *chunking_context
.environment()
.supports_commonjs_externals()
.await?;

let content = content.await?;
Ok(EcmascriptChunkItemContent {
Expand Down

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

Large diffs are not rendered by default.

0 comments on commit 12cdfed

Please sign in to comment.