Skip to main content

turbopack_ecmascript/
text_source_transform.rs

1use anyhow::{Result, bail};
2use turbo_tasks::Vc;
3use turbo_tasks_fs::{File, FileContent};
4use turbopack_core::{
5    asset::{Asset, AssetContent},
6    context::AssetContext,
7    source::Source,
8    source_transform::SourceTransform,
9    virtual_source::VirtualSource,
10};
11
12use crate::utils::{StringifyJs, inline_source_map_comment};
13
14/// A source transform that converts any file into an ES module that exports
15/// the file's content as a default string export.
16///
17/// This is used for `import text from './file.txt' with { type: 'text' }`.
18#[turbo_tasks::value]
19pub struct TextSourceTransform;
20
21#[turbo_tasks::value_impl]
22impl TextSourceTransform {
23    #[turbo_tasks::function]
24    pub fn new() -> Vc<Self> {
25        TextSourceTransform.cell()
26    }
27}
28
29#[turbo_tasks::value_impl]
30impl SourceTransform for TextSourceTransform {
31    #[turbo_tasks::function]
32    async fn transform(
33        self: Vc<Self>,
34        source: Vc<Box<dyn Source>>,
35        _asset_context: Vc<Box<dyn AssetContext>>,
36    ) -> Result<Vc<Box<dyn Source>>> {
37        let ident = source.ident().owned().await?;
38        let content = source.content().file_content().await?;
39        let text = match &*content {
40            FileContent::Content(data) => data.content().to_str()?,
41            FileContent::NotFound => {
42                // This shouldn't happen because the import was already resolved
43                bail!("File not found: {:?}", ident.path);
44            }
45        };
46
47        // Generate ES module with inline source map pointing back to the original file.
48        let code = format!(
49            "\"use turbopack no side effects\";\nexport default {};\n{}",
50            StringifyJs(&text),
51            inline_source_map_comment(&ident.path.path, &text)
52        );
53
54        // Rename to .mjs so module rules recognize it as ESM.
55        // The inline source map ensures debuggers show the original file.
56
57        let new_ident = ident.rename_as("*.[text].mjs").into_vc();
58
59        Ok(Vc::upcast(VirtualSource::new_with_ident(
60            new_ident,
61            AssetContent::file(FileContent::Content(File::from(code)).cell()),
62        )))
63    }
64}