next_core/next_edge/
entry.rs

1use indoc::formatdoc;
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ResolvedVc, Value, Vc, fxindexmap};
4use turbo_tasks_fs::{File, FileSystemPath};
5use turbopack_core::{
6    asset::AssetContent, context::AssetContext, module::Module, reference_type::ReferenceType,
7    virtual_source::VirtualSource,
8};
9use turbopack_ecmascript::utils::StringifyJs;
10
11#[turbo_tasks::function]
12pub async fn wrap_edge_entry(
13    asset_context: Vc<Box<dyn AssetContext>>,
14    project_root: Vc<FileSystemPath>,
15    entry: ResolvedVc<Box<dyn Module>>,
16    pathname: RcStr,
17) -> Vc<Box<dyn Module>> {
18    // The wrapped module could be an async module, we handle that with the proxy
19    // here. The comma expression makes sure we don't call the function with the
20    // module as the "this" arg.
21    // Turn exports into functions that are also a thenable. This way you can await the whole object
22    // or  exports (e.g. for Components) or call them directly as though they are async functions
23    // (e.g. edge functions/middleware, this is what the Edge Runtime does).
24    // Catch promise to prevent UnhandledPromiseRejectionWarning, this will be propagated through
25    // the awaited export(s) anyway.
26    let source = formatdoc!(
27        r#"
28            self._ENTRIES ||= {{}};
29            const modProm = import('MODULE');
30            modProm.catch(() => {{}});
31            self._ENTRIES[{}] = new Proxy(modProm, {{
32                get(modProm, name) {{
33                    if (name === "then") {{
34                        return (res, rej) => modProm.then(res, rej);
35                    }}
36                    let result = (...args) => modProm.then((mod) => (0, mod[name])(...args));
37                    result.then = (res, rej) => modProm.then((mod) => mod[name]).then(res, rej);
38                    return result;
39                }},
40            }});
41        "#,
42        StringifyJs(&format_args!("middleware_{pathname}"))
43    );
44    let file = File::from(source);
45
46    // TODO(alexkirsz) Figure out how to name this virtual asset.
47    let virtual_source = VirtualSource::new(
48        project_root.join("edge-wrapper.js".into()),
49        AssetContent::file(file.into()),
50    );
51
52    let inner_assets = fxindexmap! {
53        "MODULE".into() => entry
54    };
55
56    asset_context
57        .process(
58            Vc::upcast(virtual_source),
59            Value::new(ReferenceType::Internal(ResolvedVc::cell(inner_assets))),
60        )
61        .module()
62}