Repository navigation
perf: cut main.js by 10% with a fuse.js search port and leaner build output - #407
Merged
Merged
Conversation
main.js is the final artifact, so the 376 /* @__PURE__ */ and __NO_SIDE_EFFECTS__ comments the minifier kept only cost bytes. The parsed program is identical to before. main.js goes from 99,975 to 99,496 gzip bytes.
Svelte copies style-block comments verbatim into the CSS it injects from main.js, so every design note cost every user bytes. A style preprocessor now drops them at build time and keeps /*! license notices, so the notes stay in source. Outside the CSS strings the bundle is unchanged, and the CSS differs only in comments and whitespace. main.js goes from 99,496 to 98,463 gzip bytes.
Every engine Obsidian runs on supports unprefixed animation, transform and box-sizing, so the -webkit-, -moz- and -o- copies and the three prefixed @Keyframes never applied. In Obsidian the old and new rules give identical computed styles and keyframes for every bar. main.js goes from 98,463 to 98,297 gzip bytes.
PodNotes used fuse.js only for plain fuzzy matching of episode titles and template tag names. This ports that search path from Fuse.js 7.5.0 and drops the dependency, along with the per-list index cache that a port without an index no longer needs. A differential run against fuse.js 7.5.0 matched every result list in 405,000 comparisons. Searching 2,000 titles per keystroke got faster (median 4.6 ms against 5.8 ms). main.js goes from 98,297 to 89,692 gzip bytes.
The style preprocessor removed anything shaped like a comment, including text inside quoted strings such as content: "/* */". It now matches strings first and keeps them. Today's styles have no such strings, so the injected CSS is unchanged.
…notice fuzzySearch now takes the items and a text accessor and returns the matches, so neither caller maps indices back. Its Fuse.js attribution is a /*! notice that survives minification into main.js, and the Apache License 2.0 text sits next to the port.
The fixture records fuse.js 7.5.0 results for 150 title queries (14 longer than 64 characters) and 80 tag queries, with the options PodNotes used. Dropping the field norm, flipping findAllMatches, ignoring later pattern chunks, or keeping the best hit's score instead of the last makes it fail. The cache-era in-place mutation test goes, since nothing caches results anymore.
The stripper skipped /*! notices without consuming them, so a quote or a /* inside a notice changed what came after it. It now matches every comment whole and keeps notices, and a string continued across a CRLF line break stays one string.
Callers pass { findAllMatches } as they did to Fuse, instead of a bare
boolean. The shipped notice points at the license URL, since users
never receive fuzzySearch.LICENSE.
The fixture now covers a title listed after a longer sibling ("Rents",
then "Rent") and several titles that share a 49-character prefix, so
dropping the exact-match shortcut or the per-chunk score average fails
it. The generator and the options it used are in the repository, and
the test checks how many queries the fixture holds.
The generator resolves the fuse.js path from the working directory, and its header fetches fuse.js into /tmp and formats the output. The corpus gains a short title that a long query's tail chunk can match, so dropping the chunk location clamp also fails the fixture. Extra seed queries are a parameter instead of a check on which corpus is passed.
Deploying podnotes with
|
| Latest commit: |
57ddfbb
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d6aca9d5.podnotes.pages.dev |
| Branch Preview URL: | https://perf-bundle-diet.podnotes.pages.dev |
The regex removed text inside an unquoted url() and joined tokens that a comment separated, so one/**/two became onetwo. PostCSS, already in the toolchain through Vite, tokenizes the CSS and represents only comments between rules and declarations as nodes. Those are removed, license notices stay, and comments inside selectors or values are left alone. For today's components the injected CSS is unchanged apart from the class hashes. main.js goes from 89,839 to 89,833 gzip bytes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
main.js was 35 bytes under its 100,000-byte gzip gate, and the next units need room. This PR cuts it from 99,965 to 89,833 gzip bytes (-10.1%) with no change users can see.
src/utility/fuzzySearch.ts, a port of the Fuse.js 7.5.0 Bitap search that PodNotes used for episode search and the "Did you mean" tag hint. Results rank exactly as before, and a recorded fuse.js fixture now guards that in CI. The per-list index cache goes too. About -8,600 gzip bytes./*!license notices, so comments stay in source. PostCSS, already in the toolchain through Vite, removes only comments between rules and declarations, so strings,url()and comments that separate tokens stay intact. About -1,030.Left out on purpose: raising the build target to es2022 would save another 1,340 bytes, but Safari before 16 throws on some native class field initializers (WebKit bug 236843), and Obsidian 1.13 still supports iOS 15. Smaller cuts the audit found (duplicate path helpers, a regex literal, a lookup table) save under 60 bytes each. The gate is unchanged, and nothing moves out of main.js.
Verification
npm run size: 317,450 → 282,491 raw, 99,965 → 89,833 gzip against master.npm run test:e2epasses all 68 tests.obsidian://podnotesURI gives identical results on both builds, with no runtime errors.Evidence, scripts and the cut table:
~/.claude/orchestrate/podnotes/evidence/PERF1/on the build host.Release impact
No settings, data.json, API or URI change. fuse.js leaves the dependency list.
One open decision for the owner: main.js now carries a
/*!Fuse.js attribution with the Apache 2.0 license URL, and the full license text sits next to the port in the repository. Master shipped Fuse with no notice at all. Apache 2.0 asks redistributors to give recipients a copy of the license, and releases ship only main.js and manifest.json. Embedding the full text in main.js would cost about 3.8 KB gzip, so this PR does not.Docs at release
None.
Note
Replace Fuse.js with local
fuzzySearchand strip CSS comments to shrinkmain.jsby 10%TemplateEngineto use it, removing the Fuse.js dependency from package.jsonstripCssCommentsutility in css-comments.ts, wired into the Svelte style preprocessor in vite.config.ts; comments starting with!are keptsearchEpisodesdrops its WeakMap cache and signature checks; search ordering could drift from Fuse.js if the local scoring differs — the fixture tests are the guardMacroscope summarized 57ddfbb.