Repository navigation
Harden the compiler and checker, speed up scoping, and fix Cloudflare base-path staging - #105
Open
collin-thomas wants to merge 38 commits into
Open
collin-thomas wants to merge 38 commits into
collin-thomas wants to merge 38 commits into
Conversation
…es only that preview’s theme, and saves to theme-demo. Outside the preview, it still uses the site-wide theme setting. Updated the theme-toggle.html:23, styles.css:106, theme-toggle.md:18, and examples.spec.ts:29.
…pilation, CSS scoping, JS/TypeScript scoping, directive execution (build, routes, server, stream), static checking (--check), and dist/ artifact emission.
Here is a summary of the bugs discovered, fixed, and covered with tests:
1. CSS & JS Whole-Identifier Scoping (No Substring Clobbering)
CSS Scoping (styles.ts): prefixKeyframes, scopeCssCustomProperties, scopeLayerNames, scopeViewTransitionNames, and scopeAnchorNames previously looped over declared names sequentially (forEach), which allowed a later or shorter declared name (such as b or --a) to match inside an earlier generated name (all minified hashes begin with b...) or as a prefix of a longer name (--a-b), corrupting the emitted CSS. All five passes now rewrite whole identifiers in a single pass with shieldCssStrings protection, and shieldCssStrings now uses createContentShield so nested shielding is re-entrant.
JS DOM Selector Scoping (javascript.ts): prefixElementAttribute previously iterated uniqueAttributesToReplace.forEach(...) sequentially over each <script> block, allowing a short class/id/name token (such as b) to corrupt an earlier minified token (b0123...) in className =, setAttribute("class", ...), getElementsByClassName(...), and classList.*. It now rewrites tokens in a single lookup pass per AST/call pattern and also supports space-separated multi-class strings in getElementsByClassName("a b").
2. Raw-Text, Comment, and Attribute Boundary Hardening
Slot & Prop Injection (components.ts):
injectProps, injectPropAttributes, parseNamedSlots, and replaceDefaultSlots now shield/mask HTML comments and <script>/<style>/<textarea> bodies so example markers in comments or code strings are never mutated.
parseNamedSlots now supports single-quoted data-bascik-slot='name' and closing tags with trailing whitespace (</div >).
mergeAttributesOntoRoot now matches class= and style= with a leading whitespace boundary so data-class="..." or data-style="..." on a component's root element is never corrupted.
Element & ID Class Injection (styles.ts, javascript.ts):
addElementClassesInHtml, addIdClassesInHtml, and replaceSafeAttr now shield HTML comments and <style>/<textarea> raw text so commented-out markup or <textarea> values are not mutated.
<meta> tag shielding in prefixElementAttribute is now quote-aware (> inside content="a > b") and uses createContentShield.
Page Head <style> Minification & Unresolved Tag Scan (processing.ts):
Page-level <style> minification under minify.css: true previously replaced the tag with <style>${minifiedCss}</style>, dropping media="..." or other attributes on the <style> tag. It now preserves the original opening and closing tags.
The unresolved component warning scan now uses maskRawTextContent so > inside a quoted attribute on <script>/<style>/<textarea> does not terminate raw-text stripping early.
Script Type & External src Detection (script-types.ts, typescript.ts, processing.ts, javascript.ts, script-source-lines.ts):
getScriptType, withoutTypeAttribute, and src= checks now match whole attribute names (data-type and data-src are no longer mistaken for type and src), and transformTypeScriptScriptTags / minifyScriptTagsInHtml ignore <script> tags inside HTML comments and <textarea>.
3. Directive Conflicts, CSP Hashes, Sitemap, and --check
Directive Conflict Parity (build-scripts.ts, routes.ts): Added data-bascik-build + data-bascik-stream and data-bascik-routes + data-bascik-stream conflict guards at build time (matching server-scripts.ts and check.ts), and updated executeRoutesScript to use findComponentRoot and quote-aware getHtmlAttributeValue.
CSP Hashes (csp-hashes.ts): computePageCspHashes now blanks HTML comments, <textarea>/<title>/<noscript>/<xmp>, and <script> bodies (before scanning <style>) so <script> or <style> text inside comments or JS strings is never hashed into csp-hashes.json.
Sitemap Exclusion (sitemap.ts): isPageExcludedFromSitemap now strips comments and raw-text elements before checking for <meta name="bascik-sitemap" content="exclude">, uses quote-aware <meta> matching, and falls back to checking the emitted dist/ HTML for dynamically generated route pages ([slug].html).
bascik --check (check.ts, compatibility-checker.ts):
extractOpenTagData and maskPreservedSubtrees now use quote-aware open-tag matching.
Component structure order and compatibility checks now ignore comments and raw-text/code blocks, skip Node directive scripts when checking browser JS rules, and strip comments before matching CSS/JS compatibility regexes.
…workspace have now been run and verified.
1. Fixed Failing E2E Test (dynamic-dom.test.ts)
Cause: dynamic-dom.test.ts previously had an outdated assertion expecting li.setAttribute("class", "item unscoped-item") to leave "item unscoped-item" untouched because the old single-token setAttribute("class", ...) regex failed to match space-separated multi-class strings (even though the JS-only class discovery pass in javascript.ts already split setAttribute("class", "x y") on whitespace).
Resolution: Updated dynamic-dom.test.ts and compatibility.md to reflect that setAttribute("class", "item unscoped-item") scopes known component classes (item
→
→ bascik__dynamic-dom__item) while preserving unknown global classes (unscoped-item), matching el.className = ... and getElementsByClassName(...).
2. New E2E Coverage Added for Compiler & Scoping Fixes
scoping-boundary.test.ts & scoping-boundary.html & scoping-boundary-test.html:
Verifies <textarea> raw text containing <button id="boundary-btn" class="boundary-box" data-bascik-prop-sample="x"> is never mutated by element/ID/class/prop passes.
Verifies short and prefix-overlapping class names (.a, .b, .a-b) and CSS custom properties (--v, --v-long) scope cleanly without clobbering, and document.getElementsByClassName("a b") matches multi-class elements across instances.
Verifies <script data-src="/lazy-chunk.js"> is recognized as an inline script (not skipped as external src).
Verifies root attribute inheritance (class="passed-root-class" style="outline-width: 2px;") preserves data-class and data-style on the component root.
typescript-strip.test.ts & ts-strip-test.html:
Verifies <script data-type="custom-meta" data-src="/lazy.js" type="text/typescript"> strips TypeScript and type="text/typescript" while preserving data-type and data-src in the emitted HTML across all server modes.
slots-test.html (slots.test.ts):
Verifies single-quoted data-bascik-slot='footer' and closing tags with trailing whitespace (</div >) are extracted and slotted in browser E2E runs.
Eliminated Per-Instance scopedCssCache Misses on ID-Bearing Components (javascript.ts)
Before: scopedCssCache included JSON.stringify(component.scopedIdNames) in its cache key. Because scopedIdNames holds per-instance IDs (bascik__comp__<instanceId>__btn), every component template containing an id="..." attribute missed scopedCssCache on every single instance and re-ran all 10 CSS regex/shielding passes from scratch.
Now: rewriteIdReferencesInCss (for url(#id) SVG/CSS fragment references) runs on component.cssFileContent before the cache lookup when url( is present, and scopedIdKey is removed from the cache key. Components with ordinary HTML IDs and #id CSS selectors now hit scopedCssCache across all instances while components with url(#id) CSS fragments still get per-instance CSS.
Consolidated addElementClassesInHtml into a Single Pass (styles.ts)
Before: Looped over elementsConvertedClasses and ran result = result.replace(new RegExp('<' + element + ...), ...) once per styled element tag (e.g., 10 full HTML scans for a component styling 10 HTML elements).
Now: Compiles a single combined alternation regex (<(p|h1|h2|div|...)(?![\w.:-])...>) and injects the corresponding bascik__<scope>__el__<tag> class in one pass over the shielded HTML.
Fast-Path Early Returns Across CSS & HTML Scoping Passes (styles.ts, javascript.ts, id-references.ts, components.ts, processing.ts)
styles.ts: Added cheap substring guards before calling shieldCssStrings() / maskCssSyntax() in getKeyframeNames (@Keyframes), removeIdSelectors ([id), convertCssIdSelectorsToClasses (#), scopeViewTransitionNames (view-transition-name), scopeCounterStyleNames (@counter-style), scopeAnchorNames (anchor-name / @position-try), and scopeInlineStyleTags (<style).
javascript.ts: Skips the entire "id" or "name" prefixElementAttribute pass (avoiding comment shielding, <style>/<textarea> shielding, and tag scanning) when the component template contains no id= or name= attribute and no data-bascik-preserve directive. Also skips quote-range scanning in replaceSafeAttr for tags without =, and skips rewriteIdReferencesInStyleTags when no <style is present.
id-references.ts: Added QUICK_ID_REF_RE and usemap guards in rewriteIdReferencesInHtml and rewriteUsemapReferencesInHtml so tags and templates without ID-reference attributes skip shielding and per-attribute regex replacements.
components.ts & processing.ts: injectProps returns immediately when props is empty and the template has no data-bascik-prop- markers. recursivelyTranspile skips the while (childSearchFrom < slotTemplate.length) nested-component slot-protection scan when neither the component template nor usage innerContent contains data-bascik-slot.
Deduplicated Component Script Dependency Scanning per Page (processing.ts)
Before: transpilePage looped over allUsedComponents (every component instance on the page) and called collectAllScriptDeps on each one—re-scanning the same component's raw source 50+ times on pages with repeated components.
Now: Deduplicates by comp.name (seenDepComponents) so collectAllScriptDeps runs at most once per unique component used on a page.
Scoped Cache Invalidation in watchSourceCycles & Package Resolution Memoization (watch-source.ts, package-identity.ts, build-scripts.ts)
watch-source.ts: Direct page .html edits in exec.watch mode now call clearBuildScriptCaches(path) for the edited page instead of wiping all build-script caches and componentListCache globally on every edit.
package-identity.ts: Memoizes resolved external package specifiers (resolvedSpecMemo) so import.meta.resolve + realpath is not repeated for every build script tag across every page, while still reading entry and package.json files so live package edits invalidate cache keys accurately.
Regression & Parity Tests Added
pipeline-efficiency.test.ts:
Verifies scopedCssCache hits on repeated instances of ID-bearing components while preserving per-instance CSS when url(#id) fragments are used.
Verifies byte-for-byte parity of single-pass addElementClassesInHtml + addIdClassesInHtml across comments, scripts, textareas, and existing attributes.
Verifies rewriteIdReferencesInHtml across all single-ID, ID-list, fragment (href, xlink:href), SVG presentation (fill, stroke, filter, clip-path, mask, marker-*), and inline style attributes.
Verifies full CSS scoping parity (@layer, @Property, @counter-style, @container, @position-try, ::view-transition-*, @Keyframes, #id, element selectors, and code preservation) with minify.identifiers both false and true.
Property-based fast-check suite verifying deterministic cold-vs-warm cache output across nested and sibling component trees.
watch-source.integration.test.ts:
Added tests verifying that direct page HTML edits keep componentListCache and unrelated build-script memos warm, while component edits invalidate componentListCache and clear per-path build-script caches.
…tage microbenchmarks, a dedicated pipeline-heavy site, the create-bascik starter site, and the full 100-page docs site (both build and live dev-edit timelines). All full-site comparisons were run using yarn workspace @bascik/bascik profile:compare and profile:compiler on isolated tmp fixtures with alternating round order (baseline -> candidate on odd rounds, candidate -> baseline on even rounds) and verified byteIdentical: true across every emitted HTML file. 1. Pipeline Stage Microbenchmarks (Direct Head-to-Head) Comparing the pre-optimization compiler snapshot (f8eabb2) against the optimized compiler in the same Node process across 5 interleaved rounds: Pipeline Stage / Workload Before After Speedup rewriteIdReferencesInHtml (IDs present, 0 ID-ref attrs) 2,115.8 ms 85.4 ms -96.0% (24.77x faster) computePackageIdentity ("marked", 1,000 calls) 1,242.9 ms 117.1 ms -90.6% (10.62x faster) rewriteIdReferencesInHtml (mixed tags + aria-*/href) 2,307.7 ms 225.6 ms -90.2% (10.23x faster) recursivelyTranspile (40 ID-bearing components w/ dynamic data-bascik-attr-*) 7,222.7 ms 3,788.0 ms -47.6% (1.91x faster) recursivelyTranspile (30 nested no-slot components) 405.1 ms 369.8 ms -8.7% (1.10x faster) addElementClassesInHtml (11 element rules, 5x template) 424.5 ms 397.2 ms -6.4% (1.07x faster) 2. Dedicated Pipeline-Heavy Site (perf-site) Workload: 25 pages, 10 components with id attributes, element CSS selectors, #id CSS selectors, aria-labelledby/href="#..." references, data-bascik-attr-* + data-bascik-prop-* bindings (1,000 total component instances across the build), and a data-bascik-build script importing marked on every page (n = 5 paired rounds, byteIdentical: true). Metric (Median of 5 Rounds) Before After Improvement Warm Build readyMs (full CLI wall time) 1,124.5 ms 672.8 ms -40.2% (1.67x faster) Warm Build batchMs (page compilation batch) 1,080.0 ms 634.0 ms -41.3% (1.70x faster) Warm Build sum(workMs) (synchronous JS work) 847.0 ms 589.0 ms -30.5% (1.44x faster) Warm Build CPU time (user + system) 1,283.5 ms 892.2 ms -30.5% (-391 ms CPU) Cold Build readyMs (including child script spawns) 1,136.8 ms 940.0 ms -17.3% (1.21x faster) Cold Build batchMs 1,090.0 ms 901.0 ms -17.3% (1.21x faster) Why this site sees the largest end-to-end gain: Every component instance has dynamic attribute bindings (data-bascik-attr-*) and id attributes. Previously, scopedCssCache missed on every single instance (1,000 CSS pipeline runs per build) because scopedIdNames was baked into the cache key, and computePackageIdentity("marked") re-resolved and re-tokenized marked's entry bundle 25 times per build. Now scopedCssCache hits across all instances after the first per component (10 misses, 990 hits), and computePackageIdentity memoizes specifier resolution and AST specifier extraction. 3. create-bascik Starter Site (starter-site) Workload: Freshly scaffolded starter app (2 pages, 3 components, n = 5 paired rounds, byteIdentical: true). Metric (Median of 5 Rounds) Before After Improvement Cold Build batchMs 96.0 ms 85.0 ms -11.5% Cold Build sum(workMs) 32.2 ms 23.8 ms -26.1% (1.35x faster) Warm Build batchMs 52.0 ms 49.0 ms -5.8% Warm Build sum(workMs) 31.0 ms 26.7 ms -13.9% (1.16x faster) 4. Repository docs Site (100 pages, workers: true) Workload: All 100 docs pages, 689 data-bascik-build scripts, 6 exec scripts, 8 worker threads (n = 3 paired rounds, byteIdentical: true). Metric (Median of 3 Rounds) Before After Improvement Warm Build readyMs 4,225.5 ms 3,716.7 ms -12.0% (-509 ms) Warm Build batchMs 3,910.0 ms 3,470.0 ms -11.3% (-440 ms) Warm Build sum(workMs) (across all 100 pages) 14,605.0 ms 13,147.0 ms -10.0% (-1,458 ms) Cold Build sum(workMs) 12,715.0 ms 11,646.0 ms -8.4% (-1,069 ms) Cold Build CPU time (main process) 21,610.4 ms 19,823.9 ms -8.3% (-1,786 ms) Dev Mode: Single Page Edit (src/pages/index.html) 452.8 ms (3,155 readFiles) 422.8 ms (3,059 readFiles) -6.6% wall time, ~96 fewer component file reads per edit Additional Refinements Landed During Benchmarking While running the profiler harness, we also identified and landed five additional hot-path improvements: package-identity.ts: Memoized collectPackageSpecifiers(entry) by entry file content (entrySpecifiersMemo) in computePackageIdentity so large external package entrypoints (like marked) are not re-tokenized by the JS scanner on every build script cache check (10.6x speedup on computePackageIdentity). javascript.ts: Cached the extracted set of CSS class names per stylesheet in cssDeclaredClassesCache, checked tagMatch.includes("${attrName}=") in replaceSafeAttr, and added a fast path in namespaceScriptTags when no <script tag is present. styles.ts: Added a /class=/i fast path in injectClassIntoTag so elements without an existing class attribute skip the character-by-character quote scanner. shielding.ts: Added tag-presence checks in shieldElementContents and maskElementContents before compiling and running per-tag regexes, and a data-bascik-preserve fast path in stripPreserveDirectives. compiler-timeline.ts: Updated the repository docs fixture setup to copy sibling examples and skills directories referenced by bascik.config.ts.
…nd documented across the docs site, Copilot instructions, and profiler agent definition so any future contributor or agent can run the exact same three-tier comparison in one command. What Was Added & Updated Built-in Pipeline Benchmark Fixture (--fixture pipeline in bench) profile-fixture.ts: Added createPipelineBenchmarkFixture(), which generates the 25-page, 10-component, 1,000-instance synthetic benchmark project (exercising id scoping, element and #id CSS rules, aria-* and href="#..." ID reference rewriting, dynamic data-bascik-attr-* and data-bascik-prop-* bindings, and external package build scripts). compiler-compare.ts: Added --fixture pipeline support so you no longer have to hand-roll a temporary project to benchmark compiler changes: compiler-timeline.test.ts & README.md: Added unit tests and CLI documentation for --fixture pipeline. New Dedicated Internals Docs Page (/internals/benchmarking) benchmarking.md & benchmarking.html: Created the new Benchmarking & Profiling page covering: Throughput microbenchmarks (yarn pkg:bench) Private report directory isolation rules (0700 outside the repo) Step-by-step git worktree snapshot setup (avoiding git stash) Paired compiler comparisons (profile:compare) for the built-in --fixture pipeline, the create-bascik starter site, and the full docs site, including byteIdentical: true verification and how to read comparison.json Compiler timelines and live dev-edit (--edits) profiling (profile:compiler) Runtime workload captures (profile:workload) nav.ts, internals.md, and testing.md: Linked /internals/benchmarking in the Internals navigation, overview list, and the Testing Internals page. Copilot Instructions & Profiler Agent Updates copilot-instructions.md: Added the Repeatable Benchmarking & Compiler Comparisons (bench) section detailing the git worktree baseline rule, same-path paired build requirement for HTML byte parity, the three-tier benchmark matrix (--fixture pipeline, starter-site, docs), microbenchmarks, and tmp cleanup. bascik-profiler.agent.md: Updated the Bascik Performance & Profiling agent with the profile:compare and profile:compiler commands and the 5-step worktree comparison workflow.
…compatibility rule contract tests
…oted directive syntax
…nd CSS/JS scoping symmetry
…l 7 migration site torture tests
…ev rebuilds, align create-bascik LSP version pin, and consume dev E2E fixture reloads
…SP hashes, empty-valued preserve, and --fix/--check parity - csp-hashes: find scripts and styles with the WHATWG tokenizer so <!--, <textarea>, or <title> inside a script body no longer corrupts or drops hashes (strict CSP blocked valid scripts) - html-patterns: add hasHtmlAttribute and isPageAwareBuildOpenTag; data-bascik-build="pages" or "page-x" and attribute-like text in quoted values no longer defer a script - shielding: data-bascik-preserve="" (the bare form DOM serializers write) preserves id, name, and class like the bare directive - lsp/extension: quote-aware form attribute reader; unquoted external action URLs are recognized and empty preserve satisfies the name check; parity guard - check: new unknown-directive-value warning; --fix edits only usages --check reports (not comments, scripts, or textarea text) - js-document-class-query: cover compound/backtick selectors and getElementsByClassName(...)[0]/.item(0); skip selectors anchored on a scoped #id - cli: document --lint, --fix, and --only in help with a drift test; docs and skills updated
…ty, and file URLs with a trailing slash - adapter-cloudflare: stage public files below a non-root base (public/docs/...), keeping _worker.js and _routes.json at the public root. Every static page and asset under base: "/docs/" was a 404 on Pages and Workers while bascik --server served it - adapter-cloudflare: route the aliases of a static 404.html through the Worker and answer them with status 404, matching the Node server instead of the asset layer's 200 - server: a non-HTML extension path ending in / (/style.css/) is no longer served as the file; it falls through to page lookup and 404s like static hosts and the asset layer - workerd parity tests for both variants under a non-root base, plus the 404 page at its own URL; docs for base staging, 404 routing, and static alias redirects
…se metadata timing
…d it now passes 3 out of 3 runs. Before the change it failed every run. The test now holds every page except the slowest to median * 5, so a growing queue-wait staircase still fails it. The slowest page, which pays the one-time cold cost, is held to max(median * 10, 150) ms. The sum-versus-wall-time assertion is unchanged.
…set Fingerprinting. It covers: What Bascik does: it emits the hashes but doesn't set a policy. The recipe: a short post exec script that hashes each page's inline scripts and inserts a <meta http-equiv> policy. Meta tag limits: it can't carry frame-ancestors, sandbox or report-uri. Third-party scripts and 'unsafe-eval': when they're needed. Styles: why inline style="" attributes are harder. Checking it: how to see violations. It mentions that a post script can't read csp-hashes.json. It never mentions Cloudflare's 100-rule limit. The reason a <meta> tag works is simply that it needs no host configuration. Other docs changes: Deployment overview: the per-page _headers example is gone. It now has a short paragraph linking to the how-to. FAQ and Lighthouse Best Practices: they link to the how-to. Asset Fingerprinting: its sentence pointing at the removed example is reworded. Testing the script. I ran the exact script from the page against a copy of the built docs. Four pages loaded in a browser with no CSP violations. An injected script was blocked, which shows the check can fail.
feat(logging): add option to pin server URL in interactive terminal test(worker-pool): add tests for task settlement and worker behavior fix(worker-pool): prevent overwriting tasks for busy workers feat(transpile): improve build completion messages with color docs: update skill documentation for clarity and accuracy chore: add pinServerUrl to known defaults in config chore: update yarn.lock for dependency management
…5 s default to 30 s. Run on their own, all 7 cases pass and the typecheck is clean. The full yarn pkg:integration run, which is where the timeouts happened, is still going in the background, so I haven't confirmed the fix there yet. The change is in archetype-torture.integration.test.ts, on the it.each named "builds and passes --check on real example site from migration-tutorials-oct052026". Each case runs git archive, a full --build and a --check as separate processes. When the whole suite runs at once, that adds up to more than 5 s. 30 s matches the other tests in that file that start processes or a browser. I also added a comment saying why the timeout is longer.
- Introduced a new CI job for running unit tests on Windows. - Implemented `runNpm` and `npmInvocation` functions to handle npm commands across platforms. - Updated documentation to clarify Windows support and symlink behavior.
- Removed the `pinServerUrl` option from the logging configuration and related code. - Introduced `--no-pin` flag to disable pinning the server address in the terminal. - Updated documentation to reflect changes in logging options and added descriptions for new flags. - Enhanced CLI tests to validate behavior of `--quiet` and `--no-pin` flags. - Refactored logging functions to utilize a centralized logging level management system. - Ensured backward compatibility by providing informative error messages for deprecated configurations.
…and create examples/package.json
Co-authored-by: collin-thomas <2100124+collin-thomas@users.noreply.github.com>
Co-authored-by: collin-thomas <2100124+collin-thomas@users.noreply.github.com>
Deploying bascik with
|
| Latest commit: |
986bffd
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://2ed67997.bascik.pages.dev |
| Branch Preview URL: | https://pre-release-102.bascik.pages.dev |
This branch has not been deployed
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
Pre-launch hardening. A compiler audit fixes scoping, raw-text, and directive edge cases, the scoping pipeline gets faster with identical output, the checker and language server catch more problems, and the Cloudflare adapter now serves sites deployed under a non-root base path.
Compiler fixes
prefixKeyframes,scopeCssCustomProperties,scopeLayerNames,scopeViewTransitionNames, andscopeAnchorNamesrewrote names one declaration at a time, so a short name such asbor--acould match inside a generated name or a longer name (--a-b). Each pass now rewrites whole identifiers in one step. The JS side (prefixElementAttribute) does the same and handles multi-class strings such asgetElementsByClassName("a b")andsetAttribute("class", "a b").<script>,<style>, and<textarea>bodies. Slots accept single quotes and</div >.data-classanddata-styleon a component root are no longer mistaken forclassandstyle.<style>minification keepsmediaand other attributes.data-typeanddata-srcare no longer read astypeandsrc, and script tags inside comments or<textarea>are skipped.data-bascik-buildwithdata-bascik-stream(androuteswithstream) now conflict at build time, matching the server and--check.data-bascik-preserve=""preservesid,name, andclasslike the bare directive.<!--,<textarea>, or<title>inside a script body no longer drops or corrupts hashes. Sitemap exclusion ignores<meta>text inside comments and raw text and falls back to emitted HTML for dynamic routes.data-bascik-buildvalues (pages,page-x) no longer defer a script because of attribute-like text inside quoted values.Checker and language server
bascik --checkis quote-aware, ignores comments and raw text, and skips Node directive scripts when checking browser JS rules. Newunknown-directive-valuewarning.--fixedits only what--checkreports.js-document-class-querywarning fordocument.querySelector('.class'), which is document-wide underdeduplicateCss. It covers compound and backtick selectors andgetElementsByClassName(...)[0], and skips selectors anchored on a scoped#id.--lintalias is removed.--checkis the one command.Performance
Output is byte-identical to before on every fixture compared.
scopedCssCacheno longer keys on per-instance scoped IDs unless the CSS hasurl(#id)references, so components with ordinary IDs hit the cache across instances.addElementClassesInHtmlmakes one pass instead of one per styled tag.@keyframes,[id,#,<style, ID-reference attributes, props, slots).collectAllScriptDepsruns once per component per page. Package specifier resolution and entry scanning are memoized inpackage-identity.ts.--fixture pipelineoption in the bench harness generates a 25-page, 1,000-instance benchmark site, and a new/internals/benchmarkingpage documents the paired baseline and candidate workflow.dev-boot-transpile-timing.integration.test.tsassumed every page costs about the median. After the cache fix one cold page carries the one-time cost, so the test now holds the slowest page tomax(median * 10, 150)ms and every other page tomedian * 5.Cloudflare adapter
public/docs/...) while_worker.jsand_routes.jsonstay at the public root. Previously every static page and asset underbase: "/docs/"was a 404 on Pages and Workers.404.htmlgo through the Worker and answer with status 404, matching the Node server./(for example/style.css/) is no longer served as the file. It falls through to page lookup and 404s, like static hosts.1.0.0-rc.6, with peer dependency@bascik/bascikat^1.0.0-rc.7.Tests, docs, and CI
cross-platform-crlfandscoping-differentialtests.:is()exception, post-phase metadata timing, base-path staging, 404 routing, and static alias redirects. The docs_headersfile gains a security headers rule (frame-ancestors 'none',X-Frame-Options,nosniff, referrer policy).Replaces #102, whose PR merge ref stopped updating, so GitHub Actions no longer ran on it. Includes #103 (build the language server before integration tests).