next_core/next_edge/
unsupported.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_rcstr::RcStr;
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::{File, FileSystemPath};
6use turbopack_core::{
7    asset::AssetContent,
8    ident::AssetIdent,
9    resolve::{
10        ResolveResult,
11        options::{ImportMapResult, ImportMappingReplacement, ReplacedImportMapping},
12        parse::Request,
13        pattern::Pattern,
14    },
15    virtual_source::VirtualSource,
16};
17use turbopack_ecmascript::runtime_functions::TURBOPACK_EXPORT_NAMESPACE;
18
19/// Intercepts requests for the given request to `unsupported` error messages
20/// by returning a VirtualSource proxies to any import request to raise a
21/// runtime error.
22///
23/// This can be used by import map alias, refer `next_import_map` for the setup.
24#[turbo_tasks::value(shared)]
25pub struct NextEdgeUnsupportedModuleReplacer {}
26
27#[turbo_tasks::value_impl]
28impl NextEdgeUnsupportedModuleReplacer {
29    #[turbo_tasks::function]
30    pub fn new() -> Vc<Self> {
31        NextEdgeUnsupportedModuleReplacer {}.cell()
32    }
33}
34
35#[turbo_tasks::value_impl]
36impl ImportMappingReplacement for NextEdgeUnsupportedModuleReplacer {
37    #[turbo_tasks::function]
38    fn replace(&self, _capture: Vc<Pattern>) -> Vc<ReplacedImportMapping> {
39        ReplacedImportMapping::Ignore.cell()
40    }
41
42    #[turbo_tasks::function]
43    async fn result(
44        &self,
45        lookup_path: Vc<FileSystemPath>,
46        request: Vc<Request>,
47    ) -> Result<Vc<ImportMapResult>> {
48        let request = &*request.await?;
49        if let Request::Module { module, .. } = request {
50            // Call out to separate `unsupported_module_source` to only have a single Source cell
51            // for requests with different subpaths: `fs` and `fs/promises`.
52            let source = unsupported_module_source(lookup_path.root(), module.clone())
53                .to_resolved()
54                .await?;
55            Ok(ImportMapResult::Result(ResolveResult::source(ResolvedVc::upcast(source))).cell())
56        } else {
57            Ok(ImportMapResult::NoEntry.cell())
58        }
59    }
60}
61
62#[turbo_tasks::function]
63fn unsupported_module_source(root_path: Vc<FileSystemPath>, module: RcStr) -> Vc<VirtualSource> {
64    // packages/next/src/server/web/globals.ts augments global with
65    // `__import_unsupported` and necessary functions.
66    let code = formatdoc! {
67        r#"
68        {TURBOPACK_EXPORT_NAMESPACE}(__import_unsupported(`{module}`));
69        "#
70    };
71    let content = AssetContent::file(File::from(code).into());
72    VirtualSource::new_with_ident(
73        AssetIdent::from_path(root_path)
74            .with_modifier(Vc::cell(format!("unsupported edge import {module}").into())),
75        content,
76    )
77}