next_core/next_edge/
entry.rs

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