Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support TLA #4345

Merged
merged 4 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ export interface RawExperiments {
incrementalRebuild: RawIncrementalRebuild
asyncWebAssembly: boolean
newSplitChunks: boolean
topLevelAwait: boolean
css: boolean
rspackFuture: RawRspackFuture
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_binding_options/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl RawOptionsApply for RawOptions {
},
async_web_assembly: self.experiments.async_web_assembly,
new_split_chunks: self.experiments.new_split_chunks,
top_level_await: self.experiments.top_level_await,
rspack_future: self.experiments.rspack_future.into(),
};
let optimization = IS_ENABLE_NEW_SPLIT_CHUNKS.set(&experiments.new_split_chunks, || {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct RawExperiments {
pub incremental_rebuild: RawIncrementalRebuild,
pub async_web_assembly: bool,
pub new_split_chunks: bool,
pub top_level_await: bool,
pub css: bool,
pub rspack_future: RawRspackFuture,
}
Expand Down
5 changes: 4 additions & 1 deletion crates/rspack_core/src/dependency/runtime_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ pub fn module_namespace_promise(
fake_type |= FakeNamespaceObjectMode::MERGE_PROPERTIES;
}
runtime_requirements.insert(RuntimeGlobals::CREATE_FAKE_NAMESPACE_OBJECT);
if compilation.module_graph.is_async(&module.identifier()) {
if matches!(
compilation.module_graph.is_async(&module.identifier()),
Some(true)
) {
if let Some(header) = header {
appending = format!(
".then(function() {{\n {header}\nreturn {}\n}})",
Expand Down
4 changes: 2 additions & 2 deletions crates/rspack_core/src/external_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,9 @@ impl Module for ExternalModule {
"this" => build_result.build_info.strict = false,
"system" => build_result.build_meta.exports_type = BuildMetaExportsType::Namespace,
"module" => build_result.build_meta.exports_type = BuildMetaExportsType::Namespace,
"script" | "promise" => build_result.build_meta.is_async = true,
"script" | "promise" => build_result.build_meta.has_top_level_await = true,
"import" => {
build_result.build_meta.is_async = true;
build_result.build_meta.has_top_level_await = true;
build_result.build_meta.exports_type = BuildMetaExportsType::Namespace;
}
_ => build_result.build_meta.exports_type = BuildMetaExportsType::Dynamic,
Expand Down
2 changes: 1 addition & 1 deletion crates/rspack_core/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl Display for ExportsArgument {
pub struct BuildMeta {
pub strict: bool,
pub strict_harmony_module: bool,
pub is_async: bool,
pub has_top_level_await: bool,
pub esm: bool,
pub exports_type: BuildMetaExportsType,
pub default_object: BuildMetaDefaultObject,
Expand Down
17 changes: 3 additions & 14 deletions crates/rspack_core/src/module_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,26 +347,15 @@ impl ModuleGraph {
.and_then(|mgm| mgm.get_issuer().get_module(self))
}

pub fn is_async(&self, module: &ModuleIdentifier) -> bool {
pub fn is_async(&self, module: &ModuleIdentifier) -> Option<bool> {
self
.module_graph_module_by_identifier(module)
.map(|mgm| {
mgm
.build_meta
.as_ref()
.expect("build_meta should be initialized")
.is_async
})
.unwrap_or_default()
.map(|mgm| mgm.is_async)
}

pub fn set_async(&mut self, module: &ModuleIdentifier) {
if let Some(mgm) = self.module_graph_module_by_identifier_mut(module) {
mgm
.build_meta
.as_mut()
.expect("build_meta should be initialized")
.is_async = true;
mgm.is_async = true
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_core/src/module_graph_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub struct ModuleGraphModule {
pub build_meta: Option<BuildMeta>,
pub exports: ExportsInfoId,
pub profile: Option<Box<ModuleProfile>>,
pub is_async: bool,
}

impl ModuleGraphModule {
Expand All @@ -55,6 +56,7 @@ impl ModuleGraphModule {
build_meta: None,
exports: exports_info_id,
profile: None,
is_async: false,
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/rspack_core/src/options/experiments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ pub struct Experiments {
pub incremental_rebuild: IncrementalRebuild,
pub async_web_assembly: bool,
pub new_split_chunks: bool,
pub top_level_await: bool,
pub rspack_future: RspackFuture,
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,29 @@ impl DependencyTemplate for HarmonyCompatibilityDependency {
module,
..
} = code_generatable_context;
let mgm = compilation
.module_graph
.module_graph_module_by_identifier(&module.identifier())
.expect("should have mgm");
// TODO __esModule is used
runtime_requirements.insert(RuntimeGlobals::MAKE_NAMESPACE_OBJECT);
runtime_requirements.insert(RuntimeGlobals::EXPORTS);
init_fragments.push(Box::new(NormalInitFragment::new(
format!(
"{}({});\n",
RuntimeGlobals::MAKE_NAMESPACE_OBJECT,
compilation
.module_graph
.module_graph_module_by_identifier(&module.identifier())
.expect("should have mgm")
.get_exports_argument()
mgm.get_exports_argument()
),
InitFragmentStage::StageHarmonyExports,
0,
InitFragmentKey::HarmonyCompatibility,
None,
)));

if compilation.module_graph.is_async(&module.identifier()) {
if matches!(
compilation.module_graph.is_async(&module.identifier()),
Some(true)
) {
runtime_requirements.insert(RuntimeGlobals::MODULE);
runtime_requirements.insert(RuntimeGlobals::ASYNC_MODULE);
init_fragments.push(Box::new(NormalInitFragment::new(
Expand All @@ -56,7 +59,7 @@ impl DependencyTemplate for HarmonyCompatibilityDependency {
InitFragmentStage::StageAsyncBoundary,
0,
InitFragmentKey::uniqie(),
Some("\n__webpack_async_result__();\n} catch(e) { __webpack_async_result__(e); } });".to_string()),
Some(format!("\n__webpack_async_result__();\n}} catch(e) {{ __webpack_async_result__(e); }} }}{});", if matches!(mgm.build_meta.as_ref().map(|meta| meta.has_top_level_await), Some(true)) { ", 1" } else { "" })),
)));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ pub fn harmony_import_dependency_apply<T: ModuleDependency>(
.module_graph
.get_import_var(&module.identifier(), module_dependency.request());
let key = module_dependency.request();
if compilation.module_graph.is_async(ref_module) {
let is_async_module = matches!(compilation.module_graph.is_async(ref_module), Some(true));
if is_async_module {
init_fragments.push(Box::new(NormalInitFragment::new(
content.0,
InitFragmentStage::StageHarmonyImports,
Expand Down Expand Up @@ -216,7 +217,7 @@ pub fn harmony_import_dependency_apply<T: ModuleDependency>(
RuntimeGlobals::REQUIRE,
RuntimeGlobals::EXPORT_STAR,
),
if compilation.module_graph.is_async(ref_module) {
if is_async_module {
InitFragmentStage::StageAsyncHarmonyImports
} else {
InitFragmentStage::StageHarmonyImports
Expand Down
48 changes: 21 additions & 27 deletions crates/rspack_plugin_javascript/src/parser_and_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,9 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
let use_simple_source_map = compiler_options.devtool.source_map();
let original_map = source.map(&MapOptions::new(!compiler_options.devtool.cheap()));
let source = source.source();
let mut ast = match crate::ast::parse(
source.to_string(),
syntax,
&resource_data.resource_path.to_string_lossy(),
module_type,
) {
Ok(ast) => ast,
Err(e) => {

macro_rules! bail {
($e:expr) => {{
return Ok(
ParseResult {
source: create_source(
Expand All @@ -79,9 +74,19 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
presentational_dependencies: vec![],
analyze_result: Default::default(),
}
.with_diagnostic(e.into()),
.with_diagnostic($e.into()),
);
}
}};
}

let mut ast = match crate::ast::parse(
source.to_string(),
syntax,
&resource_data.resource_path.to_string_lossy(),
module_type,
) {
Ok(ast) => ast,
Err(e) => bail!(e),
};

run_before_pass(
Expand All @@ -104,21 +109,7 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
module_type,
) {
Ok(ast) => ast,
Err(e) => {
return Ok(
ParseResult {
source: create_source(
source.to_string(),
resource_data.resource_path.to_string_lossy().to_string(),
use_simple_source_map,
),
dependencies: vec![],
presentational_dependencies: vec![],
analyze_result: Default::default(),
}
.with_diagnostic(e.into()),
);
}
Err(e) => bail!(e),
};

ast.transform(|program, context| {
Expand All @@ -134,7 +125,7 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
presentational_dependencies,
mut rewrite_usage_span,
import_map,
} = ast.visit(|program, context| {
} = match ast.visit(|program, context| {
scan_dependencies(
program,
context.unresolved_mark,
Expand All @@ -145,7 +136,10 @@ impl ParserAndGenerator for JavaScriptParserAndGenerator {
build_meta,
module_identifier,
)
});
}) {
Ok(result) => result,
Err(e) => bail!(e),
};

let analyze_result = if compiler_options.builtins.tree_shaking.enable() {
JsModule::new(&ast, &dependencies, module_identifier, compiler_options).analyze()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl Plugin for InferAsyncModulesPlugin {
.values()
.filter(|m| {
if let Some(meta) = &m.build_meta {
meta.is_async
meta.has_top_level_await
} else {
false
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
use rspack_core::{
BuildInfo, BuildMeta, BuildMetaExportsType, DependencyTemplate, ExportsArgument, ModuleArgument,
ModuleType,
ModuleIdentifier, ModuleType,
};
use swc_core::ecma::ast::{ModuleItem, Program};
use swc_core::ecma::visit::{noop_visit_type, Visit};
use rspack_error::internal_error;
use swc_core::ecma::ast::{ArrowExpr, AwaitExpr, Constructor, Function, ModuleItem, Program};
use swc_core::ecma::visit::{noop_visit_type, Visit, VisitWith};

use crate::dependency::HarmonyCompatibilityDependency;

// Port from https://github.com/webpack/webpack/blob/main/lib/dependencies/HarmonyDetectionParserPlugin.js
pub struct HarmonyDetectionScanner<'a> {
module_identifier: &'a ModuleIdentifier,
build_info: &'a mut BuildInfo,
build_meta: &'a mut BuildMeta,
module_type: &'a ModuleType,
top_level_await: bool,
code_generable_dependencies: &'a mut Vec<Box<dyn DependencyTemplate>>,
errors: &'a mut Vec<rspack_error::Error>,
}

impl<'a> HarmonyDetectionScanner<'a> {
pub fn new(
module_identifier: &'a ModuleIdentifier,
build_info: &'a mut BuildInfo,
build_meta: &'a mut BuildMeta,
module_type: &'a ModuleType,
top_level_await: bool,
code_generable_dependencies: &'a mut Vec<Box<dyn DependencyTemplate>>,
errors: &'a mut Vec<rspack_error::Error>,
) -> Self {
Self {
module_identifier,
build_info,
build_meta,
module_type,
top_level_await,
code_generable_dependencies,
errors,
}
}
}
Expand All @@ -49,9 +59,44 @@ impl Visit for HarmonyDetectionScanner<'_> {
self.build_meta.exports_argument = ExportsArgument::WebpackExports;
}

if has_top_level_await(program) {
if !self.top_level_await {
self.errors.push(internal_error!("The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enabled it)"));
} else if is_harmony || strict_harmony_module {
self.build_meta.has_top_level_await = true;
} else {
self.errors.push(internal_error!(
"Top-level-await is only supported in EcmaScript Modules: {}",
self.module_identifier
));
}
}

if strict_harmony_module {
self.build_meta.strict_harmony_module = true;
self.build_meta.module_argument = ModuleArgument::WebpackModule;
}
}
}

fn has_top_level_await(m: &Program) -> bool {
let mut visitor = TopLevelAwaitScanner::default();
m.visit_with(&mut visitor);
visitor.has_top_level_await
}

#[derive(Default)]
struct TopLevelAwaitScanner {
has_top_level_await: bool,
}

impl Visit for TopLevelAwaitScanner {
noop_visit_type!();
fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
fn visit_constructor(&mut self, _: &Constructor) {}
fn visit_function(&mut self, _: &Function) {}

fn visit_await_expr(&mut self, _: &AwaitExpr) {
self.has_top_level_await = true;
}
}
Loading
Loading