@lightspeed-js/core 0.1.0
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/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/compile.js +1494 -0
- package/dist/index.js +3060 -0
- package/dist/ssg.js +3620 -0
- package/dist/types/build.d.ts +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/src/agent/index.d.ts +175 -0
- package/dist/types/src/cli/commands/agent.d.ts +9 -0
- package/dist/types/src/cli/commands/build.d.ts +2 -0
- package/dist/types/src/cli/commands/create.d.ts +2 -0
- package/dist/types/src/cli/commands/dev.d.ts +2 -0
- package/dist/types/src/cli/commands/index.d.ts +3 -0
- package/dist/types/src/cli/index.d.ts +2 -0
- package/dist/types/src/cli/types/commands.d.ts +11 -0
- package/dist/types/src/cli/types/index.d.ts +1 -0
- package/dist/types/src/cli/utils/commands.d.ts +5 -0
- package/dist/types/src/cli/utils/index.d.ts +1 -0
- package/dist/types/src/compile/expressions.d.ts +76 -0
- package/dist/types/src/compile/index.d.ts +44 -0
- package/dist/types/src/compile/logic.d.ts +101 -0
- package/dist/types/src/manifest.d.ts +148 -0
- package/dist/types/src/marker.d.ts +85 -0
- package/dist/types/src/parser/index.d.ts +2 -0
- package/dist/types/src/parser/layout.d.ts +32 -0
- package/dist/types/src/parser/lsp.d.ts +56 -0
- package/dist/types/src/parser/route.d.ts +87 -0
- package/dist/types/src/parser/style-source-node.d.ts +19 -0
- package/dist/types/src/parser/style.d.ts +20 -0
- package/dist/types/src/parser/template.d.ts +32 -0
- package/dist/types/src/parser/test.d.ts +1 -0
- package/dist/types/src/runtime/bindings/BindingEvaluator.d.ts +174 -0
- package/dist/types/src/runtime/bindings/index.d.ts +2 -0
- package/dist/types/src/runtime/compiler/ImportTransformer.d.ts +11 -0
- package/dist/types/src/runtime/compiler/index.d.ts +1 -0
- package/dist/types/src/runtime/components/ComponentInstance.d.ts +110 -0
- package/dist/types/src/runtime/components/ComponentRegistry.d.ts +11 -0
- package/dist/types/src/runtime/components/index.d.ts +3 -0
- package/dist/types/src/runtime/components/types/index.d.ts +34 -0
- package/dist/types/src/runtime/core.d.ts +251 -0
- package/dist/types/src/runtime/diagnostics.d.ts +33 -0
- package/dist/types/src/runtime/dom/ForBlock.d.ts +127 -0
- package/dist/types/src/runtime/dom/Mounter.d.ts +223 -0
- package/dist/types/src/runtime/dom/StyleManager.d.ts +57 -0
- package/dist/types/src/runtime/dom/hydration.d.ts +35 -0
- package/dist/types/src/runtime/dom/index.d.ts +7 -0
- package/dist/types/src/runtime/events/EventBinder.d.ts +18 -0
- package/dist/types/src/runtime/events/index.d.ts +1 -0
- package/dist/types/src/runtime/globals/GlobalFunctions.d.ts +24 -0
- package/dist/types/src/runtime/globals/index.d.ts +1 -0
- package/dist/types/src/runtime/helpers.d.ts +141 -0
- package/dist/types/src/runtime/index.d.ts +16 -0
- package/dist/types/src/runtime/reactive/ReactiveProxy.d.ts +63 -0
- package/dist/types/src/runtime/reactive/UpdateScheduler.d.ts +9 -0
- package/dist/types/src/runtime/reactive/index.d.ts +2 -0
- package/dist/types/src/runtime/signals/SignalManager.d.ts +10 -0
- package/dist/types/src/runtime/signals/index.d.ts +3 -0
- package/dist/types/src/runtime/signals/signals.d.ts +16 -0
- package/dist/types/src/runtime/signals/types/index.d.ts +1 -0
- package/dist/types/src/runtime/signals/types/signals.d.ts +7 -0
- package/dist/types/src/runtime/state/Cell.d.ts +68 -0
- package/dist/types/src/runtime/state/CellManager.d.ts +101 -0
- package/dist/types/src/runtime/state/index.d.ts +4 -0
- package/dist/types/src/specifiers.d.ts +16 -0
- package/dist/types/src/ssg/index.d.ts +21 -0
- package/dist/types/src/ssg/rebuild.d.ts +49 -0
- package/dist/types/src/ssg/render.d.ts +104 -0
- package/dist/types/src/types/lsp.d.ts +22 -0
- package/dist/types/src/types/optional.d.ts +14 -0
- package/dist/types/src/types/virtual.d.ts +4 -0
- package/dist/types/src/vite/csp.d.ts +12 -0
- package/dist/types/src/vite/index.d.ts +2 -0
- package/dist/types/src/vite/layout.d.ts +17 -0
- package/dist/types/src/vite/plugin.d.ts +69 -0
- package/dist/types/src/vite/routes.d.ts +139 -0
- package/dist/types/test/adopted-styles.test.d.ts +1 -0
- package/dist/types/test/adoption.test.d.ts +1 -0
- package/dist/types/test/agent.test.d.ts +1 -0
- package/dist/types/test/audit.test.d.ts +1 -0
- package/dist/types/test/bindings.test.d.ts +1 -0
- package/dist/types/test/branches-in-loops.test.d.ts +1 -0
- package/dist/types/test/build.test.d.ts +1 -0
- package/dist/types/test/buildtime.test.d.ts +1 -0
- package/dist/types/test/built.test.d.ts +1 -0
- package/dist/types/test/cell-props.test.d.ts +1 -0
- package/dist/types/test/comments.test.d.ts +1 -0
- package/dist/types/test/compile-expressions.test.d.ts +1 -0
- package/dist/types/test/compile-logic.test.d.ts +1 -0
- package/dist/types/test/components.test.d.ts +1 -0
- package/dist/types/test/composition.test.d.ts +1 -0
- package/dist/types/test/declared-needs.test.d.ts +1 -0
- package/dist/types/test/diagnostics.test.d.ts +1 -0
- package/dist/types/test/dynamic.test.d.ts +1 -0
- package/dist/types/test/example.test.d.ts +1 -0
- package/dist/types/test/faq.test.d.ts +1 -0
- package/dist/types/test/flatten-nested.test.d.ts +1 -0
- package/dist/types/test/html-binding.test.d.ts +1 -0
- package/dist/types/test/hydration-report.test.d.ts +1 -0
- package/dist/types/test/hydration.test.d.ts +1 -0
- package/dist/types/test/layout-resolution.test.d.ts +1 -0
- package/dist/types/test/layout.test.d.ts +1 -0
- package/dist/types/test/lazy.test.d.ts +1 -0
- package/dist/types/test/manifest.test.d.ts +1 -0
- package/dist/types/test/model-props.test.d.ts +1 -0
- package/dist/types/test/model.test.d.ts +1 -0
- package/dist/types/test/mounting.test.d.ts +1 -0
- package/dist/types/test/needs.test.d.ts +1 -0
- package/dist/types/test/nested-rows.test.d.ts +1 -0
- package/dist/types/test/on-mounted.test.d.ts +1 -0
- package/dist/types/test/on-update.test.d.ts +1 -0
- package/dist/types/test/page-island.test.d.ts +1 -0
- package/dist/types/test/pages.test.d.ts +1 -0
- package/dist/types/test/reactivity.test.d.ts +1 -0
- package/dist/types/test/rebuild-keeps-logic.test.d.ts +1 -0
- package/dist/types/test/rebuild.test.d.ts +1 -0
- package/dist/types/test/reenumerate.test.d.ts +1 -0
- package/dist/types/test/refresh.test.d.ts +1 -0
- package/dist/types/test/refs.test.d.ts +1 -0
- package/dist/types/test/reorder.test.d.ts +1 -0
- package/dist/types/test/route.test.d.ts +1 -0
- package/dist/types/test/routes-build.test.d.ts +1 -0
- package/dist/types/test/routes-dev.test.d.ts +1 -0
- package/dist/types/test/routes-emit.test.d.ts +1 -0
- package/dist/types/test/routes-runtime.test.d.ts +1 -0
- package/dist/types/test/setup.d.ts +59 -0
- package/dist/types/test/show.test.d.ts +1 -0
- package/dist/types/test/skeleton-adoption.test.d.ts +1 -0
- package/dist/types/test/slots.test.d.ts +1 -0
- package/dist/types/test/specifiers.test.d.ts +1 -0
- package/dist/types/test/ssg.test.d.ts +1 -0
- package/dist/types/test/state.test.d.ts +1 -0
- package/dist/types/test/static-islands.test.d.ts +1 -0
- package/dist/types/test/stats.test.d.ts +1 -0
- package/dist/types/test/structural.test.d.ts +1 -0
- package/dist/types/test/style-sources.test.d.ts +1 -0
- package/dist/types/test/styles.test.d.ts +1 -0
- package/dist/types/test/surgical-rebuild.test.d.ts +1 -0
- package/dist/types/test/tables.test.d.ts +1 -0
- package/dist/types/test/unmount.test.d.ts +1 -0
- package/dist/types/test/verbatim.test.d.ts +1 -0
- package/dist/vite.js +5655 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference path="./src/types/virtual.d.ts" />
|
|
2
|
+
/// <reference path="./src/types/optional.d.ts" />
|
|
3
|
+
/// <reference path="./src/types/lsp.d.ts" />
|
|
4
|
+
export { createLightspeedApp, LightspeedRuntime } from "./src/runtime/core.js";
|
|
5
|
+
export { registerPageExpressions } from "./src/runtime/core.js";
|
|
6
|
+
export { reactive, defineSignal, onMounted, onUnmounted, defineProps, shared, } from "./src/runtime/helpers.js";
|
|
7
|
+
export type { ComponentDefinition } from "./src/runtime/components/index.js";
|
|
8
|
+
export type { SignalAPI } from "./src/runtime/signals/index.js";
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { ComponentDefinition } from "../runtime/components/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The build agent: rebuilds a site when its content changes.
|
|
4
|
+
*
|
|
5
|
+
* It exists because **incremental rebuilds need persistent state**. A CI job
|
|
6
|
+
* starts from a clean checkout every time, so it can only ever do a full
|
|
7
|
+
* build. The agent keeps the previous `dist` and manifest between runs, which
|
|
8
|
+
* is what lets a changed entry re-render one page instead of a thousand.
|
|
9
|
+
*
|
|
10
|
+
* It runs *in the site's repo*, because rebuilding needs the site's own
|
|
11
|
+
* components and toolchain - and it holds a repo checkout but never database
|
|
12
|
+
* credentials, which is the boundary the whole architecture is drawn around.
|
|
13
|
+
*/
|
|
14
|
+
export interface RebuildRequest {
|
|
15
|
+
/**
|
|
16
|
+
* Build-time cell keys whose content changed - the CMS's publish payload.
|
|
17
|
+
*
|
|
18
|
+
* Each is passed to `rebuildPages` separately, so only the pages that read
|
|
19
|
+
* it are re-rendered. Empty means "rebuild everything with build-time
|
|
20
|
+
* content", which is the honest fallback when a caller does not know.
|
|
21
|
+
*/
|
|
22
|
+
cells?: string[];
|
|
23
|
+
/** Where the site lives. */
|
|
24
|
+
root: string;
|
|
25
|
+
/** Build output, relative to root. */
|
|
26
|
+
dist?: string;
|
|
27
|
+
/** Pull before rebuilding. Off for tests. */
|
|
28
|
+
pull?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* A separate content checkout to build against.
|
|
31
|
+
*
|
|
32
|
+
* Content and code want different version control: code branches and merges,
|
|
33
|
+
* content is a single moving line owned by the CMS. Keeping them in one repo
|
|
34
|
+
* forces content to follow code's branching model, which means resolving
|
|
35
|
+
* content conflicts by hand when a feature branch merges.
|
|
36
|
+
*
|
|
37
|
+
* The agent joins them rather than git doing it - no submodules, no pointer
|
|
38
|
+
* commits, and each history stays clean. A path rather than a repo URL, so
|
|
39
|
+
* the source can later be a sync target or an API dump without this
|
|
40
|
+
* changing.
|
|
41
|
+
*/
|
|
42
|
+
content?: string;
|
|
43
|
+
/** Commit and push the rebuilt output. */
|
|
44
|
+
push?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface RebuildReport {
|
|
47
|
+
/** Output paths re-rendered, in order. */
|
|
48
|
+
rebuilt: string[];
|
|
49
|
+
/** Pages considered and skipped, with why. */
|
|
50
|
+
skipped: Array<{
|
|
51
|
+
page: string;
|
|
52
|
+
reason: string;
|
|
53
|
+
}>;
|
|
54
|
+
cells: string[];
|
|
55
|
+
pushed: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Drop the cache, for a caller that knows the source moved. */
|
|
58
|
+
export declare function forgetDefinitions(): void;
|
|
59
|
+
/** Compile every `.lsp` under the site's source directory. */
|
|
60
|
+
export declare function siteDefinitions(root: string, dir?: string): Promise<ComponentDefinition[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Rebuild what the given content keys affect.
|
|
63
|
+
*
|
|
64
|
+
* One `rebuildPages` call per key rather than one for everything: the manifest
|
|
65
|
+
* records which cells each *output* read, so a per-entry key resolves to a
|
|
66
|
+
* single page. Passing them together would lose that precision.
|
|
67
|
+
*/
|
|
68
|
+
export declare function rebuild(request: RebuildRequest): Promise<RebuildReport>;
|
|
69
|
+
/**
|
|
70
|
+
* Verify a webhook before doing any work.
|
|
71
|
+
*
|
|
72
|
+
* A shared secret compared in constant time. The agent is never public, but
|
|
73
|
+
* "never public" is a deployment claim rather than a property, and this is the
|
|
74
|
+
* one endpoint that can make it push to a repo.
|
|
75
|
+
*/
|
|
76
|
+
export declare function authorised(header: string | null, secret: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* The rebuild keys a commit message records.
|
|
79
|
+
*
|
|
80
|
+
* The CMS writes them into the commit body when it publishes, so **the commit
|
|
81
|
+
* is the record and a webhook is only a nudge**. A missed or duplicated
|
|
82
|
+
* webhook then costs nothing: the work is still described in git, and the
|
|
83
|
+
* agent finds it on the next wake.
|
|
84
|
+
*
|
|
85
|
+
* The alternative - the webhook carrying the payload - makes delivery
|
|
86
|
+
* critical. Lose one and the site is silently stale with nothing recording
|
|
87
|
+
* that a rebuild was owed.
|
|
88
|
+
*/
|
|
89
|
+
export declare function cellsFromCommits(log: string): string[];
|
|
90
|
+
export interface Pending {
|
|
91
|
+
/** Commit the site is at now. */
|
|
92
|
+
head: string;
|
|
93
|
+
/** Keys to rebuild. Empty means nothing to do. */
|
|
94
|
+
cells: string[];
|
|
95
|
+
/** No recorded state: everything with build-time content is stale. */
|
|
96
|
+
full: boolean;
|
|
97
|
+
/** Whether the range changed content, source, or nothing. */
|
|
98
|
+
kind: ChangeKind;
|
|
99
|
+
/** Dependencies moved, so a build needs an install first. */
|
|
100
|
+
install: boolean;
|
|
101
|
+
/** Commit the content repo is at, when it is separate. */
|
|
102
|
+
contentHead?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* What has happened since the agent last built.
|
|
106
|
+
*
|
|
107
|
+
* Reads every commit between the recorded state and `HEAD`, so a run that
|
|
108
|
+
* covers three missed publishes rebuilds the union of their keys rather than
|
|
109
|
+
* only the newest.
|
|
110
|
+
*/
|
|
111
|
+
export declare function pendingWork(root: string, content?: string): Promise<Pending>;
|
|
112
|
+
export interface AgentState {
|
|
113
|
+
/** Last site commit built. */
|
|
114
|
+
lastCommit: string | null;
|
|
115
|
+
/** Last content commit built, when content is a separate repo. */
|
|
116
|
+
lastContentCommit?: string | null;
|
|
117
|
+
}
|
|
118
|
+
export declare function readState(root: string): Promise<AgentState>;
|
|
119
|
+
export declare function writeState(root: string, commit: string, contentCommit?: string | null): Promise<void>;
|
|
120
|
+
/** What a range of commits requires. */
|
|
121
|
+
export type ChangeKind = "none" | "content" | "source";
|
|
122
|
+
/**
|
|
123
|
+
* Decide from the changed paths what kind of rebuild is owed.
|
|
124
|
+
*
|
|
125
|
+
* The two are not interchangeable. `rebuildPages` deliberately skips the
|
|
126
|
+
* bundler and re-renders page shells that already carry hashed asset tags -
|
|
127
|
+
* exactly right for content, and **wrong for a component change**, which
|
|
128
|
+
* produces new bundles the shells do not reference. Doing the cheap thing
|
|
129
|
+
* after a source change yields fresh HTML pointing at stale JavaScript, which
|
|
130
|
+
* looks like it worked.
|
|
131
|
+
*
|
|
132
|
+
* Deciding here rather than at the trigger means a developer push, a CMS
|
|
133
|
+
* publish and a manual run all reach the right answer without anyone choosing
|
|
134
|
+
* a mode - and without the CMS needing any part in deploying source.
|
|
135
|
+
*/
|
|
136
|
+
export declare function classifyChange(files: string[]): ChangeKind;
|
|
137
|
+
/** Whether dependencies moved, and a build needs an install first. */
|
|
138
|
+
export declare function needsInstall(files: string[]): boolean;
|
|
139
|
+
/** Paths touched between the recorded state and HEAD. */
|
|
140
|
+
export declare function changedFiles(root: string, from: string, to: string): Promise<string[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Run the site's own build.
|
|
143
|
+
*
|
|
144
|
+
* `bun run build` rather than a hardcoded `vite build`: the site owns its
|
|
145
|
+
* toolchain, and the agent is that site building itself.
|
|
146
|
+
*/
|
|
147
|
+
export declare function fullBuild(root: string, install: boolean, content?: string): Promise<void>;
|
|
148
|
+
export interface PreviewRequest {
|
|
149
|
+
/** The cell holding this entry's content, e.g. `cms:exhibitions:my-show`. */
|
|
150
|
+
entryCell: string;
|
|
151
|
+
/** The cell the route enumerates from, e.g. `cms:exhibitions`. */
|
|
152
|
+
indexCell: string;
|
|
153
|
+
/** The entry's slug, which decides its URL. */
|
|
154
|
+
slug: string;
|
|
155
|
+
/** The content to render - the editor's current values, saved or not. */
|
|
156
|
+
data: Record<string, unknown>;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Render a page with unpublished content, without publishing it.
|
|
160
|
+
*
|
|
161
|
+
* The CMS sends the content **in the request**, so the agent still never talks
|
|
162
|
+
* to a database - the boundary the whole architecture rests on holds even for
|
|
163
|
+
* previewing drafts.
|
|
164
|
+
*
|
|
165
|
+
* Deliberately the same `renderPage` the build uses. A preview rendered by any
|
|
166
|
+
* other path would drift from what publishing actually produces, and a preview
|
|
167
|
+
* that lies is worse than no preview.
|
|
168
|
+
*/
|
|
169
|
+
export declare function preview(request: PreviewRequest & {
|
|
170
|
+
root: string;
|
|
171
|
+
dist?: string;
|
|
172
|
+
}): Promise<{
|
|
173
|
+
html: string;
|
|
174
|
+
path: string;
|
|
175
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run the build agent.
|
|
4
|
+
*
|
|
5
|
+
* Not a library call but a process, which is why it is a CLI command rather
|
|
6
|
+
* than an export: it holds a repo checkout and the previous build between
|
|
7
|
+
* runs, and that persistence is the whole reason it exists.
|
|
8
|
+
*/
|
|
9
|
+
export declare const agentCommand: Command;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./commands.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { loadCommands } from "./commands.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every expression a template evaluates, with the scope it evaluates in.
|
|
3
|
+
*
|
|
4
|
+
* The runtime compiles these with `new Function` today, which is why a page
|
|
5
|
+
* needs a CSP with `unsafe-eval`. Compiling them at build instead needs one
|
|
6
|
+
* thing the flat extractor in `parser/template.ts` cannot give: **the loop
|
|
7
|
+
* aliases in scope at each expression**, because `item` in `{{ item.name }}`
|
|
8
|
+
* is a parameter of the compiled function, not a free variable.
|
|
9
|
+
*
|
|
10
|
+
* Those aliases are static - they come from `$for="item in items"` in the
|
|
11
|
+
* markup - so the build can know them exactly.
|
|
12
|
+
*
|
|
13
|
+
* The key format matches `BindingEvaluator`'s compile cache deliberately, so
|
|
14
|
+
* the runtime can look a compiled function up by the same key it would have
|
|
15
|
+
* cached it under. That keeps expression *source* in the DOM, which is what
|
|
16
|
+
* adoption re-binds from - so nothing about the emitted markup changes.
|
|
17
|
+
*/
|
|
18
|
+
export interface TemplateExpression {
|
|
19
|
+
/** The expression source, exactly as the DOM carries it. */
|
|
20
|
+
expr: string;
|
|
21
|
+
/** Loop aliases in scope, outermost first. */
|
|
22
|
+
params: string[];
|
|
23
|
+
/** Which binding produced it, for diagnostics. */
|
|
24
|
+
kind: string;
|
|
25
|
+
/** True for `$model`, which also needs a writer. */
|
|
26
|
+
writable: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Matches `BindingEvaluator`'s compile cache key. */
|
|
29
|
+
export declare function expressionKey(expr: string, params: string[]): string;
|
|
30
|
+
/** `item in items` → alias and the iterable expression. */
|
|
31
|
+
export declare function parseForExpression(value: string): {
|
|
32
|
+
alias?: string;
|
|
33
|
+
items: string;
|
|
34
|
+
};
|
|
35
|
+
export interface ExtractOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The component's handler names, in declaration order.
|
|
38
|
+
*
|
|
39
|
+
* An **expression** event handler - `@click="toggle(item)"` rather than
|
|
40
|
+
* `@click="toggle"` - is compiled with the component's handlers as
|
|
41
|
+
* parameters, so it can call them. The order matches the order `setup`
|
|
42
|
+
* registers them, which is the order they are declared.
|
|
43
|
+
*/
|
|
44
|
+
handlers?: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare function extractExpressions(template: string, options?: ExtractOptions): TemplateExpression[];
|
|
47
|
+
/**
|
|
48
|
+
* The expression table a component ships.
|
|
49
|
+
*
|
|
50
|
+
* Keyed the way `BindingEvaluator` keys its compile cache, so the runtime can
|
|
51
|
+
* look up what it would otherwise have compiled. A `$model` binding also emits
|
|
52
|
+
* a writer under the same key.
|
|
53
|
+
*
|
|
54
|
+
* The table is global at runtime rather than per component: a compiled
|
|
55
|
+
* expression depends only on its source and its scope, so two components
|
|
56
|
+
* sharing an expression share a function, and merging tables cannot conflict.
|
|
57
|
+
*/
|
|
58
|
+
export declare function expressionTableSource(found: TemplateExpression[]): string;
|
|
59
|
+
/** The same table, built as real functions - for the build and for tests. */
|
|
60
|
+
export declare function buildExpressionTable(found: TemplateExpression[]): {
|
|
61
|
+
expressions: Record<string, Function>;
|
|
62
|
+
writers: Record<string, Function>;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Handlers a template binds that its logic never declares.
|
|
66
|
+
*
|
|
67
|
+
* `@click="publish"` compiles to a direct handler lookup, so a name that was
|
|
68
|
+
* never declared fails at *click time* with nothing wrong at build. The
|
|
69
|
+
* compiler already knows both halves - every `data-on-*` value, and every
|
|
70
|
+
* declaration - so the mismatch is detectable before anyone opens the page.
|
|
71
|
+
*
|
|
72
|
+
* Only bare names are checked. An expression handler like `remove(todo)` is
|
|
73
|
+
* compiled with the declared handlers as parameters, so an unknown name there
|
|
74
|
+
* is a different failure with a different message.
|
|
75
|
+
*/
|
|
76
|
+
export declare function missingHandlers(template: string, declared: string[]): string[];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a `.lsp` source into the definition the runtime mounts.
|
|
3
|
+
*
|
|
4
|
+
* The Vite plugin does this during a build, which is fine until someone wants
|
|
5
|
+
* to test a component *outside* a build - a consumer's test suite has no Vite,
|
|
6
|
+
* so without this there is no way to exercise a component at all. Tela hit this
|
|
7
|
+
* trying to test its field registry.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately the same path the plugin uses rather than a parallel one: a test
|
|
10
|
+
* that compiles differently to the build is not testing the build.
|
|
11
|
+
*/
|
|
12
|
+
import { parseLSP } from "../parser/index.js";
|
|
13
|
+
import { transformTemplate } from "../parser/template.js";
|
|
14
|
+
import { scopeStyles, scopeIdFor } from "../parser/style.js";
|
|
15
|
+
import type { ComponentDefinition } from "../runtime/components/index.js";
|
|
16
|
+
export interface CompileSource {
|
|
17
|
+
/** Component id, normally the filename without its extension, lowercased. */
|
|
18
|
+
id: string;
|
|
19
|
+
source: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Compile a set of components together.
|
|
23
|
+
*
|
|
24
|
+
* Together, because a component's template can only be compiled once the ids of
|
|
25
|
+
* the components it mounts are known - a tag is only a component tag if
|
|
26
|
+
* something answers to that name.
|
|
27
|
+
*/
|
|
28
|
+
export declare function compileComponents(sources: CompileSource[], transpile?: (logic: string) => string,
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a module a component's logic imports.
|
|
31
|
+
*
|
|
32
|
+
* Optional: components that import nothing need no loader. Supplied by the
|
|
33
|
+
* build and by the agent, which both have a filesystem to resolve against.
|
|
34
|
+
*/
|
|
35
|
+
load?: (specifier: string) => Promise<Record<string, unknown>>,
|
|
36
|
+
/**
|
|
37
|
+
* Read a `<style src="…">`, relative to the component that asked for it.
|
|
38
|
+
*
|
|
39
|
+
* Optional, like `load`: a component sharing no styles needs none. Anything
|
|
40
|
+
* with a filesystem passes `nodeStyleReader()`.
|
|
41
|
+
*/
|
|
42
|
+
readStyle?: (specifier: string, from: string) => string): Promise<Record<string, ComponentDefinition>>;
|
|
43
|
+
export { parseLSP, transformTemplate, scopeStyles, scopeIdFor };
|
|
44
|
+
export { compileLogic, buildSetup, setupSource } from "./logic.js";
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a component's `<logic>` into the body of a real function.
|
|
3
|
+
*
|
|
4
|
+
* Logic is currently a *string* executed with `new Function` once per
|
|
5
|
+
* instance, which is why a component cannot import anything: an `import`
|
|
6
|
+
* statement inside `new Function` is a syntax error, so the two specifiers
|
|
7
|
+
* Lightspeed understands are rewritten and the rest are dropped.
|
|
8
|
+
*
|
|
9
|
+
* Compiling it at build instead means:
|
|
10
|
+
*
|
|
11
|
+
* - **project modules import normally**, resolved and chunked by the bundler
|
|
12
|
+
* - **no `new Function` in the browser**, so a page can run under a CSP with
|
|
13
|
+
* no `unsafe-eval` (see [[ADR-016]])
|
|
14
|
+
* - stack traces point at the `.lsp` file rather than `<anonymous>`
|
|
15
|
+
*
|
|
16
|
+
* This module only produces source. Evaluating it is the build's business, and
|
|
17
|
+
* the build may use `eval` freely - CSP constrains the browser, not the
|
|
18
|
+
* toolchain.
|
|
19
|
+
*/
|
|
20
|
+
export interface ImportBinding {
|
|
21
|
+
/** The module specifier. */
|
|
22
|
+
source: string;
|
|
23
|
+
/** Local name → exported name, with `default` and `*` as themselves. */
|
|
24
|
+
names: Array<{
|
|
25
|
+
local: string;
|
|
26
|
+
imported: string;
|
|
27
|
+
}>;
|
|
28
|
+
/** The original statement, for the emitted module. */
|
|
29
|
+
statement: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CompiledLogic {
|
|
32
|
+
/** Real import statements, hoisted to module scope. */
|
|
33
|
+
imports: string[];
|
|
34
|
+
/**
|
|
35
|
+
* The same imports, decomposed.
|
|
36
|
+
*
|
|
37
|
+
* The body references these as free variables, so the client gets them from
|
|
38
|
+
* module scope while the build supplies them as function parameters - one
|
|
39
|
+
* body, two ways of binding it, and no divergence between the paths.
|
|
40
|
+
*/
|
|
41
|
+
bindings: ImportBinding[];
|
|
42
|
+
/** The body of `setup(__ctx)`. */
|
|
43
|
+
body: string;
|
|
44
|
+
/** Component ids this logic imports, for the island graph. */
|
|
45
|
+
components: string[];
|
|
46
|
+
}
|
|
47
|
+
/** The context keys `setup` destructures. Must match `createExecutionContext`. */
|
|
48
|
+
export declare const CONTEXT_KEYS: readonly ["_componentInstance", "__lightspeedRuntime", "_getComponent", "_addHandler"];
|
|
49
|
+
/** `./Button.lsp` → `button` */
|
|
50
|
+
export declare function componentIdFrom(path: string): string;
|
|
51
|
+
export declare function compileLogic(logic: string): CompiledLogic;
|
|
52
|
+
/**
|
|
53
|
+
* Top-level declarations, so the template can bind to them.
|
|
54
|
+
*
|
|
55
|
+
* Regex rather than a parser because the shapes that matter are declarations
|
|
56
|
+
* at the start of a line, and the epilogue guards every name with a `typeof`
|
|
57
|
+
* check - a name matched here that never reached scope costs nothing.
|
|
58
|
+
*/
|
|
59
|
+
export declare function declaredNames(logic: string): string[];
|
|
60
|
+
/** The full `setup` function source, for a module or for build-time eval. */
|
|
61
|
+
export declare function setupSource(compiled: CompiledLogic): string;
|
|
62
|
+
/** Decompose an import statement into the names it binds. */
|
|
63
|
+
export declare function parseImport(statement: string): ImportBinding;
|
|
64
|
+
/**
|
|
65
|
+
* Build a runnable `setup` from logic, for the build and for tests.
|
|
66
|
+
*
|
|
67
|
+
* Uses `new Function`, which is exactly what the shipped runtime must stop
|
|
68
|
+
* doing - but a CSP governs the browser, not the toolchain. Build-time
|
|
69
|
+
* rendering and the test suite both need a callable function, and neither ends
|
|
70
|
+
* up in anyone's page.
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildSetup(logic: string, options?: {
|
|
73
|
+
/** Transpile TypeScript before compiling, if needed. */
|
|
74
|
+
transpile?: (source: string) => string;
|
|
75
|
+
/** Resolve a specifier to a module namespace, for project imports. */
|
|
76
|
+
load?: (source: string) => Promise<Record<string, unknown>>;
|
|
77
|
+
}): Promise<((ctx: Record<string, unknown>) => void) | undefined>;
|
|
78
|
+
/**
|
|
79
|
+
* Declarations that are *functions*, which is what becomes a handler.
|
|
80
|
+
*
|
|
81
|
+
* The epilogue registers a name only when `typeof x === "function"`, so this
|
|
82
|
+
* has to agree with it - an event expression is compiled with the handler list
|
|
83
|
+
* as parameters, and a list that disagrees compiles the wrong signature.
|
|
84
|
+
*
|
|
85
|
+
* Statically undecidable in one case: `const f = makeHandler()` is a function
|
|
86
|
+
* at runtime and unknowable here. See the `$handlers` note in [[ADR-016]].
|
|
87
|
+
*/
|
|
88
|
+
export declare function declaredHandlers(logic: string): string[];
|
|
89
|
+
/**
|
|
90
|
+
* The handler names an event expression is compiled with, in order.
|
|
91
|
+
*
|
|
92
|
+
* Shipped with the component so the build and the runtime cannot disagree.
|
|
93
|
+
* Reading them from the instance at runtime - `[...owner.handlers.keys()]` -
|
|
94
|
+
* makes the compiled signature depend on what happened to be registered,
|
|
95
|
+
* which is not knowable at build for `const f = makeHandler()`.
|
|
96
|
+
*
|
|
97
|
+
* Every declaration is included rather than only the ones that look like
|
|
98
|
+
* functions: a name that turns out not to be one is simply `undefined`, which
|
|
99
|
+
* costs a parameter, while a name that is missing breaks the expression.
|
|
100
|
+
*/
|
|
101
|
+
export declare function handlerParams(logic: string): string[];
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the build manifest lives.
|
|
3
|
+
*
|
|
4
|
+
* Outside `dist`, because it is build metadata rather than site output and
|
|
5
|
+
* `dist` is served publicly. Read by `rebuildPages` and by any server that has
|
|
6
|
+
* to honour a runtime-resolved route.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MANIFEST_DIR = ".lightspeed";
|
|
9
|
+
export declare const MANIFEST_FILE = "manifest.json";
|
|
10
|
+
/**
|
|
11
|
+
* The one manifest file that *does* belong in `dist`.
|
|
12
|
+
*
|
|
13
|
+
* A server hosting the output has to resolve runtime routes, so it needs the
|
|
14
|
+
* route list at request time. Nothing else is serving data - page shells,
|
|
15
|
+
* skeletons and the component graph are build metadata - so only this travels
|
|
16
|
+
* with the site.
|
|
17
|
+
*/
|
|
18
|
+
export declare const ROUTES_FILE = "lightspeed-routes.json";
|
|
19
|
+
/** Ships with every page: `$show` hides via attribute, not inline style. */
|
|
20
|
+
export declare const HIDDEN_RULE = "[data-lsp-hidden]{display:none!important}";
|
|
21
|
+
import type { ComponentDefinition } from "./runtime/components/index.js";
|
|
22
|
+
/**
|
|
23
|
+
* What the build knows about each component, and what the page needs to load it.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately keyed by **component**, not by island or by page. A component's
|
|
26
|
+
* compiled output is a function of its own source, so a page addition or a
|
|
27
|
+
* content change can never invalidate it - which is what makes incremental
|
|
28
|
+
* rebuilds possible. Rendering a page reads this; it never has to rebuild it.
|
|
29
|
+
*
|
|
30
|
+
* Internal for now. The `version` field is what makes it safe to change.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MANIFEST_VERSION = 1;
|
|
33
|
+
export interface ComponentEntry {
|
|
34
|
+
/** Chunk URL. Absent until per-component chunking exists. */
|
|
35
|
+
url?: string;
|
|
36
|
+
/** Other chunks this one needs, for precise preload hints. */
|
|
37
|
+
imports?: string[];
|
|
38
|
+
/** Critical CSS, already scoped. Content at build time; a URL once emitted. */
|
|
39
|
+
css?: string;
|
|
40
|
+
/** Deferred CSS, applied when the component hydrates. */
|
|
41
|
+
cssDeferred?: string;
|
|
42
|
+
/** Style scope hash, so a skeleton can be stamped without loading the chunk. */
|
|
43
|
+
scope?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Components this one's template mounts.
|
|
46
|
+
*
|
|
47
|
+
* Distinct from `imports`: that is the chunk graph, this is the island graph.
|
|
48
|
+
* Preloading has to be transitive over *this* - an island hydrating on load
|
|
49
|
+
* needs its children's chunks immediately, even though they are separate
|
|
50
|
+
* chunks.
|
|
51
|
+
*/
|
|
52
|
+
uses: string[];
|
|
53
|
+
/** Mounts a component named by an expression, so it can reach anything. */
|
|
54
|
+
dynamic?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Placeholder markup. Needed client-side because a deferred island must
|
|
57
|
+
* render its skeleton *without* fetching the component - otherwise deferral
|
|
58
|
+
* defeats itself. Only matters where there is no build-rendered HTML: dev,
|
|
59
|
+
* and imperative mounts.
|
|
60
|
+
*/
|
|
61
|
+
skeleton?: string;
|
|
62
|
+
}
|
|
63
|
+
/** What a page needs to be re-rendered without running the bundler. */
|
|
64
|
+
export interface PageEntry {
|
|
65
|
+
/**
|
|
66
|
+
* The page's markup *before* islands were rendered, with the build's asset
|
|
67
|
+
* tags already injected.
|
|
68
|
+
*
|
|
69
|
+
* Kept because `dist/page.html` is finished output - re-rendering that would
|
|
70
|
+
* render already-rendered content. The shell is the input the build itself
|
|
71
|
+
* used, so a rebuild is the same operation with fresher data.
|
|
72
|
+
*/
|
|
73
|
+
shell: string;
|
|
74
|
+
/** Build-time cell keys this page read, so a rebuild can be targeted. */
|
|
75
|
+
content: string[];
|
|
76
|
+
}
|
|
77
|
+
export interface Manifest {
|
|
78
|
+
version: number;
|
|
79
|
+
runtime?: {
|
|
80
|
+
url: string;
|
|
81
|
+
};
|
|
82
|
+
components: Record<string, ComponentEntry>;
|
|
83
|
+
/** Keyed by output filename, e.g. `blog.html`. */
|
|
84
|
+
pages?: Record<string, PageEntry>;
|
|
85
|
+
/** Every URL the build emitted. */
|
|
86
|
+
routes?: RouteEntry[];
|
|
87
|
+
/** Per page, the `style-src` hashes a host must allow. */
|
|
88
|
+
styleHashes?: Record<string, string[]>;
|
|
89
|
+
}
|
|
90
|
+
export interface RouteEntry {
|
|
91
|
+
page: string;
|
|
92
|
+
pattern?: string;
|
|
93
|
+
path: string;
|
|
94
|
+
/** Route params this output was rendered with, for re-rendering just it. */
|
|
95
|
+
params?: Record<string, string>;
|
|
96
|
+
/** Build-time cells this specific output read. */
|
|
97
|
+
content?: string[];
|
|
98
|
+
/**
|
|
99
|
+
* The cell this route enumerated from.
|
|
100
|
+
*
|
|
101
|
+
* An **enumeration** dependency, not a content one. The index decides *which
|
|
102
|
+
* pages exist*; it does not decide what an existing page renders. Treating
|
|
103
|
+
* it as content meant renaming one entry re-rendered every page in the
|
|
104
|
+
* collection - measured at 1000 pages as costing the same as a full build.
|
|
105
|
+
*/
|
|
106
|
+
from?: string;
|
|
107
|
+
/** Where a runtime-resolved route is served from, when not the path itself. */
|
|
108
|
+
file?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Whether a template mounts a component named by an expression.
|
|
112
|
+
*
|
|
113
|
+
* The build cannot know what `<Dynamic $is="field.type" />` will resolve to,
|
|
114
|
+
* so a page containing one can reach *any* component. That is what makes it
|
|
115
|
+
* the exception to page-scoping below.
|
|
116
|
+
*/
|
|
117
|
+
export declare function hasDynamicMarker(template: string): boolean;
|
|
118
|
+
/** Component ids referenced by a compiled template. */
|
|
119
|
+
export declare function componentsUsedIn(template: string): string[];
|
|
120
|
+
/** Every island marker in a template, with the trigger it declares. */
|
|
121
|
+
export declare function markersIn(template: string): Array<{
|
|
122
|
+
c: string;
|
|
123
|
+
hydrate?: string;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Components a page needs *immediately*.
|
|
127
|
+
*
|
|
128
|
+
* Seeded from islands the page hydrates on load, then followed through their
|
|
129
|
+
* templates - a child with no trigger of its own is needed as soon as its
|
|
130
|
+
* parent is. Anything left over is only ever fetched on a trigger.
|
|
131
|
+
*
|
|
132
|
+
* The point of separating them: components that always load together should be
|
|
133
|
+
* bundled together, because splitting things that are always fetched as a set
|
|
134
|
+
* costs compression and buys nothing.
|
|
135
|
+
*/
|
|
136
|
+
export declare function eagerComponents(pageHtml: string, templates: Record<string, string>): string[];
|
|
137
|
+
export declare function buildManifest(definitions: ComponentDefinition[]): Manifest;
|
|
138
|
+
/**
|
|
139
|
+
* Everything a page can reach, following `uses` transitively.
|
|
140
|
+
*
|
|
141
|
+
* A page carries only this much of the manifest, so a landing page with one
|
|
142
|
+
* island does not ship a map of the whole project.
|
|
143
|
+
*/
|
|
144
|
+
export declare function componentsForPage(pageHtml: string, manifest: Manifest): string[];
|
|
145
|
+
/** The subset of the manifest a page inlines for its runtime. */
|
|
146
|
+
export declare function manifestForPage(pageHtml: string, manifest: Manifest): Manifest;
|
|
147
|
+
/** Critical CSS for one page, in dependency order. */
|
|
148
|
+
export declare function criticalCssForPage(pageHtml: string, manifest: Manifest): string;
|