next_core/next_edge/
unsupported.rs

1use anyhow::Result;
2use indoc::formatdoc;
3use turbo_tasks::{ResolvedVc, Vc};
4use turbo_tasks_fs::{File, FileSystemPath};
5use turbopack_core::{
6    asset::AssetContent,
7    ident::AssetIdent,
8    resolve::{
9        ResolveResult,
10        options::{ImportMapResult, ImportMappingReplacement, ReplacedImportMapping},
11        parse::Request,
12        pattern::Pattern,
13    },
14    virtual_source::VirtualSource,
15};
16use turbopack_ecmascript::runtime_functions::TURBOPACK_EXPORT_NAMESPACE;
17
18/// Intercepts requests for the given request to `unsupported` error messages
19/// by returning a VirtualSource proxies to any import request to raise a
20/// runtime error.
21///
22/// This can be used by import map alias, refer `next_import_map` for the setup.
23#[turbo_tasks::value(shared)]
24pub struct NextEdgeUnsupportedModuleReplacer {}
25
26#[turbo_tasks::value_impl]
27impl NextEdgeUnsupportedModuleReplacer {
28    #[turbo_tasks::function]
29    pub fn new() -> Vc<Self> {
30        NextEdgeUnsupportedModuleReplacer {}.cell()
31    }
32}
33
34#[turbo_tasks::value_impl]
35impl ImportMappingReplacement for NextEdgeUnsupportedModuleReplacer {
36    #[turbo_tasks::function]
37    fn replace(&self, _capture: Vc<Pattern>) -> Vc<ReplacedImportMapping> {
38        ReplacedImportMapping::Ignore.cell()
39    }
40
41    #[turbo_tasks::function]
42    async fn result(
43        &self,
44        lookup_path: FileSystemPath,
45        request: Vc<Request>,
46    ) -> Result<Vc<ImportMapResult>> {
47        let request = &*request.await?;
48        if let Request::Module { module, .. } = request {
49            // Call out to separate `unsupported_module_source` to only have a single Source cell
50            // for requests with different subpaths: `fs` and `fs/promises`.
51            let source =
52                unsupported_module_source(lookup_path.root().owned().await?, 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: FileSystemPath, module: Pattern) -> 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        module = module.as_constant_string().map(ToString::to_string).unwrap_or_else(|| module.describe_as_string()),
71    };
72    let content = AssetContent::file(File::from(code).into());
73    VirtualSource::new_with_ident(
74        AssetIdent::from_path(root_path).with_modifier(
75            format!("unsupported edge import {}", module.describe_as_string()).into(),
76        ),
77        content,
78    )
79}