solid-tag-runtime 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ARCHITECTURE.md CHANGED
@@ -518,77 +518,166 @@ This avoids introducing a second template language, implicit lexical scope rules
518
518
 
519
519
  ## 9. Compiler adapter boundary
520
520
 
521
- The runtime engine itself does not depend on Acorn or the `solid-tag` AST.
521
+ The runtime engine remains independent of the `solid-tag` AST. `0.0.13` formalizes a linker-ready **CompiledArtifact** boundary so compiler work can be cached before runtime-specific linking.
522
522
 
523
- It requires a compiler adapter with two operations:
523
+ Preferred compiler contract:
524
524
 
525
525
  ```ts
526
526
  interface RuntimeCompiler {
527
- analyze(source, context): {
528
- imports: ImportReference[];
529
- needsHtmlRuntime?: boolean;
530
- };
531
-
532
- transform(source, context): {
533
- code: string;
534
- diagnostics?: unknown[];
535
- };
527
+ fingerprint?: string | ((context: CompileContext) => string | Promise<string>);
528
+ cacheContext?: (context: CompileContext) => unknown | Promise<unknown>;
529
+
530
+ compile?(
531
+ source: string,
532
+ context: CompileContext,
533
+ ): CompiledArtifact | Promise<CompiledArtifact>;
534
+
535
+ // Transitional compatibility path:
536
+ analyze?(source, context): { imports: ImportReference[] };
537
+ transform?(source, context): { code: string; diagnostics?: unknown[] };
536
538
  }
537
539
  ```
538
540
 
539
- The default adapter lives in `src/compiler.js` and uses:
541
+ The default adapter in `src/compiler.js` uses `solid-tag` and provides a stable fingerprint. JSX transformation runs **before** linking and intentionally leaves module specifiers unresolved. The resulting artifact is analyzed after transformation so import-reference offsets point into compiled JavaScript, not the original JSX source.
540
542
 
541
- ```text
542
- solid-tag.parseJSX()
543
- solid-tag.transformModule()
543
+ Conceptual artifact:
544
+
545
+ ```ts
546
+ interface CompiledArtifact {
547
+ schema: 1;
548
+ code: string;
549
+ imports: ImportReference[];
550
+ diagnostics: unknown[];
551
+ sourceMap?: string;
552
+ }
544
553
  ```
545
554
 
555
+ The older `analyze()` + `transform()` pair remains supported for custom compilers. For that compatibility path the runtime transforms first, then analyzes the transformed code to construct the same artifact shape.
556
+
557
+ A custom compiler may compile normally without a fingerprint, but persistent artifact reuse is disabled because the runtime must never guess compiler identity.
558
+
546
559
  ### Why this boundary exists
547
560
 
548
- It allows future compiler implementations without rewriting the runtime module system.
561
+ It separates:
549
562
 
550
- Possible future adapters include:
563
+ ```text
564
+ compiler
565
+ syntax transformation + module-reference analysis
551
566
 
552
- - a TSX-capable `solid-tag` backend
553
- - another JSX parser
554
- - pre-tagged source processing
555
- - reverse/tagged transformations for tooling
567
+ linker
568
+ runtime resolution + dependency URL rewriting
569
+ ```
556
570
 
557
- The module graph must remain independent of parser details.
571
+ That boundary supports persistent compile caching and also keeps future TSX compilers, compiler workers, build-time artifacts, and alternate parsers independent from graph/link execution.
558
572
 
559
- ## 10. Linking pipeline
573
+ ## 10. Compile, cache, and linking pipeline
560
574
 
561
- For a source module `/features/Counter.jsx`:
575
+ The source-module pipeline is now explicitly:
562
576
 
563
577
  ```text
564
578
  source
565
579
  ↓
566
- compiler.analyze()
580
+ compile source syntax
567
581
  ↓
568
- collect static imports / re-exports / literal dynamic imports
582
+ CompiledArtifact
583
+ │ transformed code
584
+ │ unresolved import specifiers
585
+ │ import offsets into transformed code
586
+ │ diagnostics/source map
587
+ │
588
+ ├──────── optional persistent cache boundary
589
+ │
569
590
  ↓
570
- resolve each specifier
591
+ resolve imports against current runtime graph
571
592
  ↓
572
- ensure virtual dependencies have module URLs
593
+ ensure current dependency module URLs
573
594
  ↓
574
- rewrite import string literals to linked URLs
595
+ rewrite artifact import ranges
575
596
  ↓
576
- solid-tag transformation (for JSX format)
597
+ linked executable JavaScript
577
598
  ↓
578
- create native module URL
599
+ module URL backend
579
600
  ↓
580
- import(moduleUrl)
601
+ native import/evaluation
602
+ ```
603
+
604
+ For `/features/Counter.jsx`, the persistent artifact may still contain:
605
+
606
+ ```js
607
+ import { createSignal } from "solid-js";
608
+ import html from "@solidjs/html";
609
+ import { Button } from "../ui/Button.jsx";
610
+ ```
611
+
612
+ Only the linker turns those specifiers into current host bridges, external URLs, or generated runtime module URLs.
613
+
614
+ ### Persistent compile cache
615
+
616
+ `0.0.13` adds an opt-in persistent compile cache.
617
+
618
+ The stored artifact is **pre-link**. Never persist:
619
+
620
+ - dependency Blob/data URLs
621
+ - linked executable source
622
+ - module URL backend output
623
+ - native module namespaces/import promises
624
+ - host bridge identities
625
+ - custom resolver output
626
+
627
+ Logical compile identity is content-addressed from:
628
+
629
+ ```text
630
+ artifact ABI
631
+ + compiler fingerprint
632
+ + source format
633
+ + compile-affecting context hash
634
+ + exact source SHA-256
635
+ ```
636
+
637
+ Module ID is excluded by default. Dependency source/version, resolver state, host values, and module URL backend are also excluded because they belong to linking/evaluation rather than compilation.
638
+
639
+ This means identical source can reuse one artifact under multiple module IDs while relative imports are still linked independently per importer.
640
+
641
+ Built-in stores:
642
+
643
+ ```text
644
+ createIndexedDBCompileCache() recommended persistent browser backend
645
+ createLocalStorageCompileCache() convenience/small-artifact backend
646
+ createMemoryCompileCache() tests/benchmarks/process-local reuse
581
647
  ```
582
648
 
583
- Example graph:
649
+ Persistent caching is configured on `createRuntime({ compileCache })` and remains fully opt-in.
650
+
651
+ ### Invalidation layers
652
+
653
+ The runtime now distinguishes:
584
654
 
585
655
  ```text
586
- /features/Counter.jsx
587
- ├── solid-js → host bridge module
588
- ├── @solidjs/html → host bridge module
589
- └── ../ui/Button.jsx → compiled virtual source URL
656
+ compile artifact
657
+ linked executable graph
658
+ native evaluated module identity
590
659
  ```
591
660
 
661
+ Normal `runtime.invalidate(id)` invalidates linked/evaluated state while preserving the reusable compiled artifact. `runtime.invalidate(id, { compile: true })` additionally forces only the requested module to compile fresh on its next link. Dependents relink but retain their own compiler artifacts.
662
+
663
+ `runtime.update(id, newSource)` naturally computes a different content key for the changed source. Old persistent artifacts are not eagerly deleted because they may still be useful to another runtime, tab, module ID, or undo/revert operation.
664
+
665
+ Public `runtime.compile()` retains its existing meaning: it returns linked/executable code and URL. Internal persistent artifacts are not silently substituted into the public result.
666
+
667
+ ### Cache controls
668
+
669
+ ```text
670
+ use read reusable artifact; compile/write on miss
671
+ refresh compile requested module fresh and replace artifact
672
+ bypass compile requested module fresh without persistent read/write
673
+ ```
674
+
675
+ Explicit persistent management lives under `runtime.compileCache` and is separate from graph removal/invalidation.
676
+
677
+ ### Failure model
678
+
679
+ Persistent caching is an optimization. Read/write/validation/pruning failures emit compile-cache lifecycle diagnostics and fall back to normal compilation whenever possible. Explicit management operations may surface the storage error because the caller explicitly requested that storage mutation.
680
+
592
681
  ## 11. Resolution rules
593
682
 
594
683
  Resolution order is conceptually:
@@ -1254,3 +1343,24 @@ Rules now enforced:
1254
1343
  7. The existing graph invariant remains unchanged: all declarations in a discovered script batch are defined before entry/render execution or recovery mounts are allowed to succeed.
1255
1344
 
1256
1345
  This separates three orderings that must all be correct: DOM parsing order, custom-element upgrade/connection timing, and runtime module-definition timing.
1346
+
1347
+ ### 0.0.13 — pre-link persistent compile cache and structured documentation
1348
+
1349
+ Decision: persistent caching stores only fully syntax-compiled, linker-ready artifacts. Runtime-specific dependency resolution, generated module URLs, host bridge identity, and native evaluation remain session-local.
1350
+
1351
+ Rules now enforced:
1352
+
1353
+ 1. JSX transformation happens before runtime linking; import metadata is collected against transformed code so cached offsets remain valid.
1354
+ 2. Persistent artifact identity includes artifact ABI, compiler fingerprint, source format, compile-affecting context, and exact source SHA-256.
1355
+ 3. Module ID, dependency versions, resolver state, host values, and URL-backend choice are not part of the default compile key.
1356
+ 4. Identical source may therefore share an artifact across module IDs while relative imports are linked separately for each importer.
1357
+ 5. The default `solid-tag` compiler exposes a stable fingerprint. Custom compilers without a fingerprint continue to work but persistent caching is bypassed rather than guessed.
1358
+ 6. `runtime.invalidate()` preserves compiler artifacts by default. `{ compile: true }` forces only the requested module to compile fresh; dependents relink without forced recompilation.
1359
+ 7. `runtime.compile(id, { cache: "use" | "refresh" | "bypass" })` controls compiler-artifact lookup while preserving the existing public linked/executable compile result.
1360
+ 8. `runtime.compileCache.invalidate()` and `.clear()` manage stored artifacts without deleting runtime module definitions.
1361
+ 9. IndexedDB, localStorage, and memory providers share one async store contract. Provider failures never make normal compilation unavailable. Quota-style write failures trigger pruning plus one retry; a failed retry becomes an observational cache error while the freshly compiled in-memory artifact remains usable.
1362
+ 10. Optional `maxAge`, `maxEntries`, and `maxBytes` bounds provide deliberately simple pruning; sophisticated global LRU and cross-tab locking remain deferred.
1363
+ 11. Compile-cache lifecycle events remain observational and integrate with the existing typed subscription system.
1364
+ 12. Package documentation is now organized under `docs/`, with feature guides and `docs/api/` references. The root README is an entry point; this architecture file remains the engineering handoff/decision log.
1365
+ 13. HTML documentation explicitly requires `<solid-render></solid-render>` rather than XML-style `<solid-render />`; custom elements are not HTML void elements and self-closing syntax can accidentally capture following DOM as initial `props.children`.
1366
+