feat(optimizer): don't qwik-transform lib builds (#6850)

* feat(optimizer): don't qwik-transform lib builds

this way libraries won't use internal APIs that may break between minor versions

* formatted changeset

---------

Co-authored-by: Shai Reznik <shairez@users.noreply.github.com>
This commit is contained in:
Wout Mertens
2024-09-09 15:32:40 +02:00
committed by GitHub
parent d918278f9f
commit 0c6496fd74
6 changed files with 179 additions and 148 deletions

View File

@@ -0,0 +1,7 @@
---
'@builder.io/qwik': patch
---
✨ Lib builds no longer perform qwik transformation.
This prevents using unstable internal APIs, and doesn't make a difference for the end user. Library authors are strongly urged to push a new library patch version built with this qwik version, and to add `| ^2.0.0` to their accepted qwik version range.

View File

@@ -30,7 +30,9 @@ test:
test-update:
if ! cargo test --manifest-path packages/qwik/src/optimizer/core/Cargo.toml; then \
cd packages/qwik/src/optimizer/core/src/snapshots/ && for i in *.new; do f=$$(basename $$i .new); mv $$i $$f; done; \
cd packages/qwik/src/optimizer/core/src/snapshots/; \
for i in *.new; do f=$$(basename $$i .new); mv $$i $$f; done; \
cd -; \
cargo test --manifest-path packages/qwik/src/optimizer/core/Cargo.toml; \
fi

View File

@@ -154,8 +154,7 @@ pub fn transform_fs(config: TransformFsOptions) -> Result<TransformOutput, Error
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
// If you don't specify is_server, the safe value is true
// For libraries, is_server has to be true because we neet to emit extra code
is_server: config.mode == EmitMode::Lib || config.is_server.unwrap_or(true),
is_server: config.is_server.unwrap_or(true),
})
})
.reduce(|| Ok(TransformOutput::new()), |x, y| Ok(x?.append(&mut y?)))?;
@@ -200,8 +199,7 @@ pub fn transform_modules(config: TransformModulesOptions) -> Result<TransformOut
strip_ctx_name: config.strip_ctx_name.as_deref(),
strip_event_handlers: config.strip_event_handlers,
// If you don't specify is_server, the safe value is true
// For libraries, is_server has to be true because we neet to emit extra code
is_server: config.mode == EmitMode::Lib || config.is_server.unwrap_or(true),
is_server: config.is_server.unwrap_or(true),
})
});

View File

@@ -13,7 +13,7 @@ use crate::const_replace::ConstReplacerVisitor;
use crate::entry_strategy::EntryPolicy;
use crate::filter_exports::StripExportsVisitor;
use crate::props_destructuring::transform_props_destructuring;
use crate::transform::{QwikTransform, QwikTransformOptions, SegmentKind};
use crate::transform::{QwikTransform, QwikTransformOptions, Segment, SegmentKind};
use crate::utils::{Diagnostic, DiagnosticCategory, DiagnosticScope, SourceLocation};
use crate::EntryStrategy;
use path_slash::PathExt;
@@ -68,6 +68,7 @@ pub enum EmitMode {
Prod,
Lib,
Dev,
Test,
}
pub struct TransformCodeOptions<'a> {
@@ -294,64 +295,57 @@ pub fn transform_code(config: TransformCodeOptions) -> Result<TransformOutput, a
// Collect import/export metadata
let mut collect = global_collect(&program);
transform_props_destructuring(&mut program, &mut collect, &config.core_module);
let mut qt: Option<QwikTransform<'_>> = None;
let mut segments: Vec<Segment> = Vec::new();
// Replace const values
// Don't further process library code
// It will be processed during client build
// This way no internal API usage is published
if config.mode != EmitMode::Lib {
let is_dev = config.mode == EmitMode::Dev;
let mut const_replacer =
ConstReplacerVisitor::new(config.is_server, is_dev, &collect);
program.visit_mut_with(&mut const_replacer);
}
let mut qwik_transform = QwikTransform::new(QwikTransformOptions {
path_data: &path_data,
entry_policy: config.entry_policy,
explicit_extensions: config.explicit_extensions,
extension: extension.clone(),
comments: Some(&comments),
global_collect: collect,
scope: config.scope,
mode: config.mode,
core_module: config.core_module,
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name,
strip_ctx_name: config.strip_ctx_name,
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
cm: Lrc::clone(&source_map),
});
// Run main transform
program = program.fold_with(&mut qwik_transform);
// reconstruct destructured props for signal forwarding
transform_props_destructuring(
&mut program,
&mut collect,
&config.core_module,
);
let mut treeshaker = Treeshaker::new();
// replace const values
if config.mode != EmitMode::Test {
let mut const_replacer =
ConstReplacerVisitor::new(config.is_server, is_dev, &collect);
program.visit_mut_with(&mut const_replacer);
}
if config.minify != MinifyMode::None {
program.visit_mut_with(&mut treeshaker.marker);
// split into segments
let mut qwik_transform = QwikTransform::new(QwikTransformOptions {
path_data: &path_data,
entry_policy: config.entry_policy,
explicit_extensions: config.explicit_extensions,
extension: extension.clone(),
comments: Some(&comments),
global_collect: collect,
scope: config.scope,
mode: config.mode,
core_module: config.core_module,
entry_strategy: config.entry_strategy,
reg_ctx_name: config.reg_ctx_name,
strip_ctx_name: config.strip_ctx_name,
strip_event_handlers: config.strip_event_handlers,
is_server: config.is_server,
cm: Lrc::clone(&source_map),
});
program = program.fold_with(&mut qwik_transform);
program = program.fold_with(&mut simplify::simplifier(
unresolved_mark,
simplify::Config {
dce: simplify::dce::Config {
preserve_imports_with_side_effects: false,
..Default::default()
},
..Default::default()
},
));
}
if matches!(
config.entry_strategy,
EntryStrategy::Inline | EntryStrategy::Hoist
) {
program.visit_mut_with(&mut SideEffectVisitor::new(
&qwik_transform.options.global_collect,
&path_data,
config.src_dir,
));
} else if config.minify != MinifyMode::None && !config.is_server {
program.visit_mut_with(&mut treeshaker.cleaner);
if treeshaker.cleaner.did_drop {
let mut treeshaker = Treeshaker::new();
if config.minify != MinifyMode::None {
// remove all side effects from client, step 1
if !config.is_server {
program.visit_mut_with(&mut treeshaker.marker);
}
// simplify & strip unused code
program = program.fold_with(&mut simplify::simplifier(
unresolved_mark,
simplify::Config {
@@ -363,90 +357,123 @@ pub fn transform_code(config: TransformCodeOptions) -> Result<TransformOutput, a
},
));
}
if matches!(
config.entry_strategy,
EntryStrategy::Inline | EntryStrategy::Hoist
) {
program.visit_mut_with(&mut SideEffectVisitor::new(
&qwik_transform.options.global_collect,
&path_data,
config.src_dir,
));
} else if config.minify != MinifyMode::None && !config.is_server {
// remove all side effects from client, step 2
program.visit_mut_with(&mut treeshaker.cleaner);
if treeshaker.cleaner.did_drop {
program = program.fold_with(&mut simplify::simplifier(
unresolved_mark,
simplify::Config {
dce: simplify::dce::Config {
preserve_imports_with_side_effects: false,
..Default::default()
},
..Default::default()
},
));
}
}
segments = qwik_transform.segments.clone();
qt = Some(qwik_transform);
}
program.visit_mut_with(&mut hygiene_with_config(Default::default()));
program.visit_mut_with(&mut fixer(None));
let segments = qwik_transform.segments;
let mut modules: Vec<TransformModule> = Vec::with_capacity(segments.len() + 10);
let comments_maps = comments.clone().take_all();
for h in segments.into_iter() {
let is_entry = h.entry.is_none();
let path_str = h.data.path.to_string();
let path = if path_str.is_empty() {
path_str
} else {
[&path_str, "/"].concat()
};
let segment_path = [
path,
[&h.canonical_filename, ".", &h.data.extension].concat(),
]
.concat();
let need_handle_watch =
might_need_handle_watch(&h.data.ctx_kind, &h.data.ctx_name);
// Now process each segment
if !segments.is_empty() {
let q = qt.as_ref().unwrap();
for h in segments.into_iter() {
let is_entry = h.entry.is_none();
let path_str = h.data.path.to_string();
let path = if path_str.is_empty() {
path_str
} else {
[&path_str, "/"].concat()
};
let segment_path = [
path,
[&h.canonical_filename, ".", &h.data.extension].concat(),
]
.concat();
let need_handle_watch =
might_need_handle_watch(&h.data.ctx_kind, &h.data.ctx_name);
let (mut segment_module, comments) = new_module(NewModuleCtx {
expr: h.expr,
path: &path_data,
name: &h.name,
local_idents: &h.data.local_idents,
scoped_idents: &h.data.scoped_idents,
need_transform: h.data.need_transform,
explicit_extensions: qwik_transform.options.explicit_extensions,
global: &qwik_transform.options.global_collect,
core_module: &qwik_transform.options.core_module,
need_handle_watch,
leading_comments: comments_maps.0.clone(),
trailing_comments: comments_maps.1.clone(),
})?;
if config.minify != MinifyMode::None {
segment_module = segment_module.fold_with(&mut simplify::simplifier(
unresolved_mark,
simplify::Config {
dce: simplify::dce::Config {
preserve_imports_with_side_effects: false,
..Default::default()
},
..Default::default()
},
));
let (mut segment_module, comments) = new_module(NewModuleCtx {
expr: h.expr,
path: &path_data,
name: &h.name,
local_idents: &h.data.local_idents,
scoped_idents: &h.data.scoped_idents,
need_transform: h.data.need_transform,
explicit_extensions: q.options.explicit_extensions,
global: &q.options.global_collect,
core_module: &q.options.core_module,
need_handle_watch,
leading_comments: comments_maps.0.clone(),
trailing_comments: comments_maps.1.clone(),
})?;
// we don't need to remove side effects because the optimizer only moves what's really used
if config.minify != MinifyMode::None {
segment_module =
segment_module.fold_with(&mut simplify::simplifier(
unresolved_mark,
simplify::Config {
dce: simplify::dce::Config {
preserve_imports_with_side_effects: false,
..Default::default()
},
..Default::default()
},
));
}
segment_module
.visit_mut_with(&mut hygiene_with_config(Default::default()));
segment_module.visit_mut_with(&mut fixer(None));
let (code, map) = emit_source_code(
Lrc::clone(&source_map),
Some(comments),
&segment_module,
config.root_dir,
config.source_maps,
)
.unwrap();
modules.push(TransformModule {
code,
map,
is_entry,
path: segment_path,
order: h.hash,
segment: Some(SegmentAnalysis {
origin: h.data.origin,
name: h.name,
entry: h.entry,
extension: h.data.extension,
canonical_filename: h.canonical_filename,
path: h.data.path,
parent: h.data.parent_segment,
ctx_kind: h.data.ctx_kind,
ctx_name: h.data.ctx_name,
captures: !h.data.scoped_idents.is_empty(),
display_name: h.data.display_name,
hash: h.data.hash,
loc: (h.span.lo.0, h.span.hi.0),
}),
});
}
segment_module.visit_mut_with(&mut hygiene_with_config(Default::default()));
segment_module.visit_mut_with(&mut fixer(None));
let (code, map) = emit_source_code(
Lrc::clone(&source_map),
Some(comments),
&segment_module,
config.root_dir,
config.source_maps,
)
.unwrap();
modules.push(TransformModule {
code,
map,
is_entry,
path: segment_path,
order: h.hash,
segment: Some(SegmentAnalysis {
origin: h.data.origin,
name: h.name,
entry: h.entry,
extension: h.data.extension,
canonical_filename: h.canonical_filename,
path: h.data.path,
parent: h.data.parent_segment,
ctx_kind: h.data.ctx_kind,
ctx_name: h.data.ctx_name,
captures: !h.data.scoped_idents.is_empty(),
display_name: h.data.display_name,
hash: h.data.hash,
loc: (h.span.lo.0, h.span.hi.0),
}),
});
}
let (code, map) = match program {

View File

@@ -3285,7 +3285,7 @@ export const Local = component$(() => {
source_maps: true,
minify: MinifyMode::Simplify,
explicit_extensions: true,
mode: EmitMode::Lib,
mode: EmitMode::Test,
manual_chunks: None,
entry_strategy: EntryStrategy::Segment,
transpile_ts: true,
@@ -3323,10 +3323,10 @@ export const Greeter = component$(() => {
"#;
let options = vec![
(EmitMode::Lib, EntryStrategy::Segment, true),
(EmitMode::Lib, EntryStrategy::Single, true),
(EmitMode::Lib, EntryStrategy::Component, true),
// (EmitMode::Lib, EntryStrategy::Inline, true),
(EmitMode::Test, EntryStrategy::Segment, true),
(EmitMode::Test, EntryStrategy::Single, true),
(EmitMode::Test, EntryStrategy::Component, true),
// (EmitMode::Test, EntryStrategy::Inline, true),
(EmitMode::Prod, EntryStrategy::Segment, true),
(EmitMode::Prod, EntryStrategy::Single, true),
(EmitMode::Prod, EntryStrategy::Component, true),
@@ -3335,10 +3335,10 @@ export const Greeter = component$(() => {
(EmitMode::Dev, EntryStrategy::Single, true),
(EmitMode::Dev, EntryStrategy::Component, true),
// (EmitMode::Dev, EntryStrategy::Inline, true),
(EmitMode::Lib, EntryStrategy::Segment, false),
(EmitMode::Lib, EntryStrategy::Single, false),
(EmitMode::Lib, EntryStrategy::Component, false),
// (EmitMode::Lib, EntryStrategy::Inline, false),
(EmitMode::Test, EntryStrategy::Segment, false),
(EmitMode::Test, EntryStrategy::Single, false),
(EmitMode::Test, EntryStrategy::Component, false),
// (EmitMode::Test, EntryStrategy::Inline, false),
(EmitMode::Prod, EntryStrategy::Segment, false),
(EmitMode::Prod, EntryStrategy::Single, false),
(EmitMode::Prod, EntryStrategy::Component, false),
@@ -3365,7 +3365,7 @@ export const Greeter = component$(() => {
minify: MinifyMode::Simplify,
root_dir: None,
explicit_extensions: true,
mode: EmitMode::Lib,
mode: EmitMode::Test,
manual_chunks: None,
entry_strategy: EntryStrategy::Segment,
transpile_ts: true,
@@ -3556,9 +3556,6 @@ export const Counter = component$(() => {
"#
.to_string(),
transpile_jsx: true,
mode: EmitMode::Lib,
// make sure it overrides it
is_server: Some(false),
..TestInput::default()
});
}
@@ -3627,7 +3624,7 @@ impl TestInput {
preserve_filenames: false,
explicit_extensions: false,
snapshot: true,
mode: EmitMode::Lib,
mode: EmitMode::Test,
scope: None,
core_module: None,
reg_ctx_name: None,

View File

@@ -335,7 +335,7 @@ impl<'a> QwikTransform<'a> {
let hash = hasher.finish();
let hash64 = base64(hash);
let symbol_name = if matches!(self.options.mode, EmitMode::Dev | EmitMode::Lib) {
let symbol_name = if matches!(self.options.mode, EmitMode::Dev | EmitMode::Test) {
format!("{}_{}", display_name, hash64)
} else {
format!("s_{}", hash64)
@@ -386,7 +386,7 @@ impl<'a> QwikTransform<'a> {
};
parse_symbol_name(
symbol_name,
matches!(self.options.mode, EmitMode::Dev | EmitMode::Lib),
matches!(self.options.mode, EmitMode::Dev | EmitMode::Test),
)
};