Skip to content

Commit

Permalink
failsafe_parse()
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Aug 29, 2024
1 parent adc8a0d commit 347d0fd
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 129 deletions.
2 changes: 1 addition & 1 deletion crates/next-api/src/dynamic_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ async fn build_dynamic_imports_map_for_module(

// https://github.com/vercel/next.js/pull/56389#discussion_r1349336374
// don't emit specific error as we expect there's a parse error already reported
let ParseResult::Ok { program, .. } = &*ecmascript_asset.failsafe_parse().await? else {
let ParseResult::Ok { program, .. } = &*ecmascript_asset.failsafe_parse(None).await? else {
return Ok(Vc::cell(None));
};

Expand Down
2 changes: 1 addition & 1 deletion crates/next-core/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ pub async fn parse_config_from_source(module: Vc<Box<dyn Module>>) -> Result<Vc<
globals,
eval_context,
..
} = &*ecmascript_asset.failsafe_parse().await?
} = &*ecmascript_asset.failsafe_parse(None).await?
{
for item in &module_ast.body {
if let Some(decl) = item
Expand Down
147 changes: 136 additions & 11 deletions turbopack/crates/turbopack-ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ pub mod typescript;
pub mod utils;
pub mod webpack;

use std::fmt::{Display, Formatter};
use std::{
fmt::{Display, Formatter},
future::Future,
sync::Arc,
};

use analyzer::graph::EvalContext;
use anyhow::Result;
use chunk::EcmascriptChunkItem;
use code_gen::CodeGenerateable;
Expand All @@ -44,21 +49,30 @@ use references::esm::UrlRewriteBehavior;
pub use references::{AnalyzeEcmascriptModuleResult, TURBOPACK_HELPER};
use serde::{Deserialize, Serialize};
pub use static_code::StaticEcmascriptCode;
use swc_comments::ImmutableComments;
use swc_core::{
common::GLOBALS,
base::SwcComments,
common::{
errors::{Handler, HANDLER},
GLOBALS,
},
ecma::{
codegen::{text_writer::JsWriter, Emitter},
transforms::base::helpers::{Helpers, HELPERS},
visit::{VisitMutWith, VisitMutWithAstPath},
},
};
pub use transform::{
CustomTransformer, EcmascriptInputTransform, EcmascriptInputTransforms, OptionTransformPlugin,
TransformContext, TransformPlugin, UnsupportedServerActionIssue,
};
use tree_shake::{part_of_module, split};
use turbo_tasks::{
trace::TraceRawVcs, RcStr, ReadRef, TaskInput, TryJoinIterExt, Value, ValueToString, Vc,
trace::TraceRawVcs, util::WrapFuture, RcStr, ReadRef, TaskInput, TryJoinIterExt, Value,
ValueToString, Vc,
};
use turbo_tasks_fs::{rope::Rope, FileJsonContent, FileSystemPath};
use turbo_tasks_hash::hash_xxh3_hash64;
use turbopack_core::{
asset::{Asset, AssetContent},
chunk::{
Expand All @@ -79,6 +93,7 @@ use turbopack_core::{
};
// TODO remove this
pub use turbopack_resolve::ecmascript as resolve;
use turbopack_swc_utils::emitter::IssueEmitter;

use self::{
chunk::{EcmascriptChunkItemContent, EcmascriptChunkType, EcmascriptExports},
Expand Down Expand Up @@ -257,7 +272,12 @@ pub struct EcmascriptModuleAsset {

#[turbo_tasks::value_trait]
pub trait EcmascriptParsable {
fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>>;
/// This function accpets `part` because we need to apply some transforms after splitting the
/// module.
///
/// If we don't accept `part`, we would need a way to ensure that all callers of
/// `failsafe_parse` applies the same transforms.
fn failsafe_parse(self: Vc<Self>, part: Option<Vc<ModulePart>>) -> Result<Vc<ParseResult>>;

fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>>;

Expand Down Expand Up @@ -342,8 +362,11 @@ impl ModuleTypeResult {
#[turbo_tasks::value_impl]
impl EcmascriptParsable for EcmascriptModuleAsset {
#[turbo_tasks::function]
async fn failsafe_parse(self: Vc<Self>) -> Result<Vc<ParseResult>> {
let real_result = self.parse();
async fn failsafe_parse(
self: Vc<Self>,
part: Option<Vc<ModulePart>>,
) -> Result<Vc<ParseResult>> {
let real_result = self.parse(part);
let real_result_value = real_result.await?;
let this = self.await?;
let result_value = if matches!(*real_result_value, ParseResult::Ok { .. }) {
Expand All @@ -359,7 +382,7 @@ impl EcmascriptParsable for EcmascriptModuleAsset {

#[turbo_tasks::function]
async fn parse_original(self: Vc<Self>) -> Result<Vc<ParseResult>> {
Ok(self.failsafe_parse())
Ok(self.failsafe_parse(None))
}

#[turbo_tasks::function]
Expand All @@ -383,7 +406,7 @@ impl EcmascriptAnalyzable for EcmascriptModuleAsset {
) -> Result<Vc<EcmascriptModuleContent>> {
let this = self.await?;

let parsed = self.parse();
let parsed = self.parse(None);

Ok(EcmascriptModuleContent::new_without_analysis(
parsed,
Expand All @@ -398,7 +421,7 @@ impl EcmascriptAnalyzable for EcmascriptModuleAsset {
chunking_context: Vc<Box<dyn ChunkingContext>>,
async_module_info: Option<Vc<AsyncModuleInfo>>,
) -> Result<Vc<EcmascriptModuleContent>> {
let parsed = self.parse().resolve().await?;
let parsed = self.parse(None).resolve().await?;

let analyze = self.analyze().await?;

Expand Down Expand Up @@ -479,8 +502,17 @@ impl EcmascriptModuleAsset {
}

#[turbo_tasks::function]
pub fn parse(&self) -> Vc<ParseResult> {
parse(self.source, Value::new(self.ty), self.transforms)
pub fn parse(&self, part: Option<Vc<ModulePart>>) -> Vc<ParseResult> {
let parsed = parse(self.source, Value::new(self.ty), self.transforms);

let parsed = if let Some(part) = part {
let split_data = split(self.source.ident(), self.source, parsed);
part_of_module(split_data, part)
} else {
parsed
};

apply_transforms(self.source, parsed, self.transforms_after_split)
}

#[turbo_tasks::function]
Expand Down Expand Up @@ -922,6 +954,99 @@ async fn gen_content_with_visitors(
}
}

#[turbo_tasks::function]
async fn apply_transforms(
source: Vc<Box<dyn Source>>,
parsed: Vc<ParseResult>,
transforms: Vc<EcmascriptInputTransforms>,
) -> Result<Vc<ParseResult>> {
let transforms = &*transforms.await?;

let ParseResult::Ok {
program,
comments,
eval_context,
globals,
source_map,
top_level_mark,
} = &*parsed.await?
else {
return Ok(parsed);
};

let merged_comments = SwcComments::default();
for c in comments.leading.iter() {
merged_comments.leading.insert(*c.0, c.1.clone());
}
for c in comments.trailing.iter() {
merged_comments.trailing.insert(*c.0, c.1.clone());
}

let handler = Handler::with_emitter(
true,
false,
Box::new(IssueEmitter::new(
source,
source_map.clone(),
Some("Ecmascript file had an error".into()),
)),
);

// TODO: Optimize this
let fs_path_vc = source.ident().path();
let fs_path = &*fs_path_vc.await?;
let file_path_hash = hash_xxh3_hash64(&*source.ident().to_string().await?) as u128;

WrapFuture::new(
async {
let mut program = program.clone();

let transform_context = TransformContext {
comments: &merged_comments,
source_map,
top_level_mark: *top_level_mark,
unresolved_mark: eval_context.unresolved_mark,
file_path_str: &fs_path.path,
file_name_str: fs_path.file_name(),
file_name_hash: file_path_hash,
file_path: fs_path_vc,
};

let span = tracing::trace_span!("transforms");
for transform in transforms.iter() {
transform.apply(&mut program, &transform_context).await?;
}
drop(span);

let comments = Arc::new(ImmutableComments::new(merged_comments));

let eval_context = EvalContext::new(
&program,
eval_context.unresolved_mark,
*top_level_mark,
Some(&comments),
None,
);

Ok(ParseResult::Ok {
program,
comments: comments.clone(),
eval_context,
globals: globals.clone(),
source_map: source_map.clone(),
top_level_mark: *top_level_mark,
}
.cell())
},
|f, cx| {
GLOBALS.set(globals, || {
HANDLER.set(&handler, || HELPERS.set(&Helpers::new(true), || f.poll(cx)))
})
},
)
.await
}

pub fn register() {
turbo_tasks::register();
turbo_tasks_fs::register();
Expand Down
Loading

0 comments on commit 347d0fd

Please sign in to comment.