Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Per-release notes are also published on each [GitHub Release](https://github.com

- `typescript::rewrite_str` and a public `RewriteOptions`: apply an output policy (minify, comments, inline source map) to plain JavaScript through the transformer-free rewrite pass the build already uses internally.
Consumers no longer route generated or copied JS through `compile_str_with`, whose Lit-preset transform may alter hand-written semantics.
- `PackageSpec::keep_tagged`: a keep-filter with a tag that joins the extraction cache key.
A `fn` pointer has no stable identity, so a tagless filter reuses a cached tree even after the filter's shape changed; the tag makes a filter change re-extract, and a tree cached without one re-extracts once on adoption.
- `examples/bundle`: a pure-frontend demo of `--bundle`, configured from the `web_modules` block in `package.json`.
- The `embedded` example bakes source maps and collects legal comments; the gh-pages CI dogfood covers `--comments collect` and `--no-minify-web-modules`.
- `--bundle` and `--bundle-entry <path>` (package.json `"bundle": {"entries": [...]}`, builder `Build::bundle` / `Build::bundle_entry`, action inputs `bundle`/`bundle-entries`) fold the built tree per entry point, inside the atomic build.
Expand Down
78 changes: 77 additions & 1 deletion src/core/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ pub struct PackageSpec {
/// Compile the package's TypeScript after extraction, so what lands in the
/// vendor tree is what a browser loads. Set by [`PackageSpec::as_source`].
compile: bool,
/// Names the keep-filter's shape in the extraction cache key. Set by
/// [`PackageSpec::keep_tagged`].
extract_tag: Option<String>,
}

impl PackageSpec {
Expand All @@ -136,6 +139,7 @@ impl PackageSpec {
extract: Extract::BrowserAssets,
imports: Imports::Auto,
compile: false,
extract_tag: None,
}
}

Expand All @@ -157,6 +161,7 @@ impl PackageSpec {
extract: Extract::BrowserAssets,
imports: Imports::None,
compile: false,
extract_tag: None,
}
}

Expand All @@ -177,6 +182,7 @@ impl PackageSpec {
extract: Extract::BrowserAssets,
imports: Imports::Auto,
compile: false,
extract_tag: None,
}
}

Expand Down Expand Up @@ -212,11 +218,23 @@ impl PackageSpec {
}

/// Shorthand for `.extract(Extract::Filter(keep))`.
///
/// A `fn` pointer has no stable identity, so a tagless filter reuses a
/// cached tree even after the filter's shape changed; use
/// [`keep_tagged`](Self::keep_tagged) to make a filter change re-extract.
pub fn keep(mut self, keep: fn(&str) -> Option<String>) -> Self {
self.extract = Extract::Filter(keep);
self
}

/// Like [`keep`](Self::keep), with a tag naming the filter's shape in the
/// extraction cache key. Changing the tag re-extracts the package; a tree
/// cached without a tag re-extracts once on adoption.
pub fn keep_tagged(mut self, tag: impl Into<String>, keep: fn(&str) -> Option<String>) -> Self {
self.extract_tag = Some(tag.into());
self.keep(keep)
}

/// Take the package's **sources** and compile them, for a package that publishes
/// no browser-usable build.
///
Expand Down Expand Up @@ -1161,6 +1179,15 @@ fn compiled_key(key: String, compile: bool) -> String {
}
}

/// Fold the keep-filter tag into the cache key, like [`compiled_key`] folds
/// the compile schema.
fn tagged_key(key: String, extract_tag: Option<&str>) -> String {
match extract_tag {
Some(tag) => format!("{key}+keep:{tag}"),
None => key,
}
}

/// Whether a git reference is a commit id, and so cannot move.
///
/// A branch or tag can be repointed at any time, which is what makes keying a cache on
Expand Down Expand Up @@ -1258,7 +1285,8 @@ fn vendor_inner(

// A known key can settle freshness without the network. A mutable reference
// cannot, so it always fetches and then compares the archive's contents.
let keyed = |key: String| compiled_key(key, spec.compile);
let keyed =
|key: String| tagged_key(compiled_key(key, spec.compile), spec.extract_tag.as_deref());
let cache_key = cache_key.map(keyed);
let fresh = |key: &Option<String>| {
key.as_deref()
Expand Down Expand Up @@ -1925,6 +1953,54 @@ mod tests {
);
}

#[test]
fn keep_tag_joins_the_cache_key() {
fn only_js(rel: &str) -> Option<String> {
rel.ends_with(".js").then(|| rel.to_string())
}
let tagless = PackageSpec::npm("lit", "^3").keep(only_js);
let tagged = PackageSpec::npm("lit", "^3").keep_tagged("js", only_js);
assert_eq!(
tagged_key("3.1.0".into(), tagless.extract_tag.as_deref()),
"3.1.0"
);
assert_eq!(
tagged_key("3.1.0".into(), tagged.extract_tag.as_deref()),
"3.1.0+keep:js"
);
}

#[test]
fn keep_tag_change_invalidates_the_marker() {
let tmp = tempfile::tempdir().unwrap();
let marker = tmp.path().join(".lit.version");
let dest = tmp.path().join("lit");
std::fs::create_dir_all(&dest).unwrap();
std::fs::write(dest.join("index.js"), "export {};").unwrap();

let extract = Extract::BrowserAssets;
cache::write_marker(&marker, &tagged_key("3.1.0".into(), Some("js+maps"))).unwrap();
assert!(is_up_to_date(
&marker,
&tagged_key("3.1.0".into(), Some("js+maps")),
&dest,
&extract
));
// A different tag re-extracts; so does dropping or adopting one.
assert!(!is_up_to_date(
&marker,
&tagged_key("3.1.0".into(), Some("js")),
&dest,
&extract
));
assert!(!is_up_to_date(
&marker,
&tagged_key("3.1.0".into(), None),
&dest,
&extract
));
}

#[test]
fn git_spec_defaults() {
let spec = PackageSpec::git("feathericons/feather", "v4.29.2");
Expand Down
Loading