next.js/crates/next-core/src/base_loader_tree.rs
base_loader_tree.rs129 lines3.9 KB
use anyhow::Result;
use indoc::formatdoc;
use turbo_rcstr::RcStr;
use turbo_tasks::{FxIndexMap, ResolvedVc, ValueToStringRef, Vc};
use turbo_tasks_fs::FileSystemPath;
use turbopack::{ModuleAssetContext, transition::Transition};
use turbopack_core::{
    file_source::FileSource,
    module::Module,
    reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType},
    source::Source,
};
use turbopack_ecmascript::{magic_identifier, utils::StringifyJs};

pub struct BaseLoaderTreeBuilder {
    pub inner_assets: FxIndexMap<RcStr, ResolvedVc<Box<dyn Module>>>,
    counter: usize,
    pub imports: Vec<RcStr>,
    pub module_asset_context: ResolvedVc<ModuleAssetContext>,
    pub server_component_transition: ResolvedVc<Box<dyn Transition>>,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppDirModuleType {
    Page,
    DefaultPage,
    Error,
    Layout,
    Loading,
    Template,
    NotFound,
    Forbidden,
    Unauthorized,
    GlobalError,
    GlobalNotFound,
}

impl AppDirModuleType {
    pub fn name(&self) -> &'static str {
        match self {
            AppDirModuleType::Page => "page",
            AppDirModuleType::DefaultPage => "defaultPage",
            AppDirModuleType::Error => "error",
            AppDirModuleType::Layout => "layout",
            AppDirModuleType::Loading => "loading",
            AppDirModuleType::Template => "template",
            AppDirModuleType::NotFound => "not-found",
            AppDirModuleType::Forbidden => "forbidden",
            AppDirModuleType::Unauthorized => "unauthorized",
            AppDirModuleType::GlobalError => "global-error",
            AppDirModuleType::GlobalNotFound => "global-not-found",
        }
    }
}

impl BaseLoaderTreeBuilder {
    pub fn new(
        module_asset_context: ResolvedVc<ModuleAssetContext>,
        server_component_transition: ResolvedVc<Box<dyn Transition>>,
    ) -> Self {
        BaseLoaderTreeBuilder {
            inner_assets: FxIndexMap::default(),
            counter: 0,
            imports: Vec::new(),
            module_asset_context,
            server_component_transition,
        }
    }

    pub fn unique_number(&mut self) -> usize {
        let i = self.counter;
        self.counter += 1;
        i
    }

    pub fn process_source(&self, source: Vc<Box<dyn Source>>) -> Vc<Box<dyn Module>> {
        let reference_type =
            ReferenceType::EcmaScriptModules(EcmaScriptModulesReferenceSubType::Undefined);

        self.server_component_transition
            .process(source, *self.module_asset_context, reference_type)
            .module()
    }

    pub fn process_module(&self, module: Vc<Box<dyn Module>>) -> Vc<Box<dyn Module>> {
        self.server_component_transition
            .process_module(module, *self.module_asset_context)
    }

    pub async fn create_module_tuple_code(
        &mut self,
        module_type: AppDirModuleType,
        path: FileSystemPath,
    ) -> Result<String> {
        let name = module_type.name();
        let i = self.unique_number();
        let identifier = magic_identifier::mangle(&format!("{name} #{i}"));

        self.imports.push(
            formatdoc!(
                r#"
                const {} = () => require(/*turbopackChunkingType: shared*/"MODULE_{}");
                "#,
                identifier,
                i
            )
            .into(),
        );

        let module = self
            .process_source(Vc::upcast(FileSource::new(path.clone())))
            .to_resolved()
            .await?;

        self.inner_assets
            .insert(format!("MODULE_{i}").into(), module);

        // Use the original source path, not the transformed module path.
        // This is important for MDX files where page.mdx becomes page.mdx.tsx after
        // transformation, but the font manifest uses the original source path.
        let module_path = path.to_string_ref().await?;

        Ok(format!(
            "[{identifier}, {path}]",
            path = StringifyJs(&module_path),
        ))
    }
}
Quest for Codev2.0.0
/
SIGN IN