sygnal 6.6.0 → 7.0.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/CHANGELOG.md +132 -0
- package/README.md +2 -0
- package/dist/astro/index.cjs.js +32 -9
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +32 -9
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs.map +1 -1
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +87 -4
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +86 -5
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/guide/accessibility.md +2 -0
- package/dist/guide/ai-chat.md +6 -6
- package/dist/guide/behaviors.md +1 -1
- package/dist/guide/element-commands.md +4 -2
- package/dist/guide/forms-reference.md +89 -3
- package/dist/guide/forms.md +8 -8
- package/dist/guide/http.md +77 -7
- package/dist/guide/migrating-to-7.md +314 -0
- package/dist/guide/recipes/ai-summarize.md +2 -1
- package/dist/guide/recipes/i18n.md +1 -1
- package/dist/guide/ui/tabs.md +5 -4
- package/dist/guide/ui/tooltip.md +24 -0
- package/dist/index.cjs.js +772 -318
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +198 -31
- package/dist/index.esm.js +772 -318
- package/dist/index.esm.js.map +1 -1
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/ui.cjs.js +96 -11
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.esm.js +96 -12
- package/dist/ui.esm.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/dist/vite/plugin.cjs.js +19 -9
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +19 -9
- package/dist/vite/plugin.mjs.map +1 -1
- package/llms.txt +17 -16
- package/package.json +1 -1
- package/src/astro/index.d.ts +8 -0
- package/src/astro/index.ts +18 -0
- package/src/core/actions.ts +28 -21
- package/src/core/define.ts +2 -0
- package/src/core/hooks.ts +2 -2
- package/src/core/hosts/collection.ts +4 -3
- package/src/core/instance.ts +60 -16
- package/src/core/runtime.ts +5 -1
- package/src/extra/ai/chat/behavior.ts +3 -3
- package/src/extra/ai/commandBar.ts +3 -3
- package/src/extra/ai/compose/behavior.ts +3 -3
- package/src/extra/ai/transports/shared.ts +8 -2
- package/src/extra/behaviors.ts +21 -0
- package/src/extra/diagnostics/checks/fetch.ts +21 -1
- package/src/extra/diagnostics/checks/forms.ts +4 -3
- package/src/extra/diagnostics/checks/index.ts +1 -0
- package/src/extra/diagnostics/checks/next.ts +3 -0
- package/src/extra/diagnostics/checks/public.d.ts +12 -0
- package/src/extra/diagnostics/checks/timingAudit.ts +47 -0
- package/src/extra/diagnostics/codes.ts +13 -0
- package/src/extra/fetchDriver.ts +112 -7
- package/src/extra/fetchEventSource.ts +113 -0
- package/src/extra/form.ts +68 -26
- package/src/extra/formHelpers.ts +3 -1
- package/src/extra/lens.ts +3 -2
- package/src/extra/queryCache.ts +4 -3
- package/src/extra/socketDriver.ts +10 -4
- package/src/extra/testing.ts +68 -11
- package/src/index.d.ts +197 -30
- package/src/index.ts +1 -0
- package/src/shared.ts +3 -0
- package/src/ui/dialog.ts +3 -2
- package/src/ui/tooltips.ts +92 -0
- package/src/ui.d.ts +35 -0
- package/src/ui.ts +1 -0
- package/src/vite/graph.ts +11 -3
- package/src/vite/plugin.d.ts +7 -0
- package/src/vite/plugin.ts +10 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,138 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Sygnal are listed here. Versions follow [semantic versioning](https://semver.org). Releases before 5.4.0 are described in the [GitHub releases](https://github.com/tpresley/sygnal/releases) and tags.
|
|
4
4
|
|
|
5
|
+
## 7.0.0 — 2026-10-19
|
|
6
|
+
|
|
7
|
+
**Read [Migrating to 7.0](https://sygnal.js.org/guide/migrating-to-7/) before upgrading.** 7.0
|
|
8
|
+
has one reducer timing contract and no option to switch back; apps that aren't ready stay on 6.x.
|
|
9
|
+
|
|
10
|
+
**Measured impact.** Size: the kanban gate is (a) 42,844 B with `nativeGlobalThis: false` (6.6.0:
|
|
11
|
+
42,495 B; budget raised from 42,700 B to 43,000 B for the binding lifetimes) and (b) 38,834 B by
|
|
12
|
+
default. Most of the growth is the binding-lifetime bookkeeping; the single timing contract is
|
|
13
|
+
smaller than the opt-in switch it replaced (−181 B against the 7.0 candidate). The timing audit is
|
|
14
|
+
in the dev diagnostics entry (0 production bytes), and the upload, SSE, form and `tooltips()`
|
|
15
|
+
features add nothing to an app that doesn't use them. The perf count gate is within its limits.
|
|
16
|
+
|
|
17
|
+
**Companion packages.**
|
|
18
|
+
- **`sygnal-check` 0.11.0:** checks reducer timing and binding changes against the Sygnal version
|
|
19
|
+
installed for each source package (7: every output gets the committed state, changed known
|
|
20
|
+
targets remount; 6: the 6.x contracts). New **SYG256** (an output guard that rejects the state
|
|
21
|
+
its own action writes, in a Sygnal 7 project); **SYG454** (an unkeyed dynamic `byId` selection)
|
|
22
|
+
only for an installed Sygnal 6. SYG731 accepts state announced through text, `aria-label`,
|
|
23
|
+
`aria-live` or `role="status"`; SYG609 also reports intent sources with no driver; `progress`
|
|
24
|
+
counts as a reply-action key (SYG102 / SYG112). Explanations for SYG257 and SYG636. Its caret
|
|
25
|
+
range doesn't reach 0.11.0 from `^0.10.0`, so update the dev dependency.
|
|
26
|
+
- **`create-sygnal-app` 2.9.0:** every template depends on `sygnal` ^7.0.0 and `sygnal-check`
|
|
27
|
+
^0.11.0 and has `npm run check` (TypeScript templates: sygnal-check and `tsc --noEmit`).
|
|
28
|
+
Scaffolding into a directory that holds only `.git` and/or `.DS_Store` keeps that metadata, and
|
|
29
|
+
`.` gets a valid package name from the directory.
|
|
30
|
+
- **VS Code extension 0.8.0:** bundles `sygnal-check` 0.11.0 for Sygnal 7 and existing Sygnal 6
|
|
31
|
+
apps; workspace checkers need 0.11.0 or later (earlier ones fall back to the bundled checker).
|
|
32
|
+
- **Claude Code plugin 0.2.0:** the `sygnal-dev` skill describes Sygnal 7; a project's
|
|
33
|
+
`sygnal-check` older than 0.11.0 is refused only when the project has Sygnal 7.
|
|
34
|
+
|
|
35
|
+
### Breaking: STATE runs first, and output sinks see its result
|
|
36
|
+
|
|
37
|
+
In every model entry STATE now runs first, whatever the key order, and every other sink (EFFECT,
|
|
38
|
+
EVENTS, PARENT, ELEMENT, HTTP and other drivers) gets the committed state, calculated fields
|
|
39
|
+
included, as `state` and `props.state`. The new `props.previousState` (fourth argument) is the
|
|
40
|
+
state from before the action; a STATE reducer gets the old state in both. 6.x gave every reducer
|
|
41
|
+
the state from before the action.
|
|
42
|
+
|
|
43
|
+
- An output that computed the update itself now applies it twice: in
|
|
44
|
+
`INC: { STATE: s => ({ ...s, n: s.n + 1 }), PARENT: s => ({ n: s.n + 1 }) }`, PARENT becomes
|
|
45
|
+
`s => ({ n: s.n })`.
|
|
46
|
+
- A value STATE clears or deletes (a submitted draft, a removed record) is read from
|
|
47
|
+
`previousState`.
|
|
48
|
+
- A guard that admits the action (a loading or status flag STATE sets) tests `previousState`.
|
|
49
|
+
Keep the output guard: STATE returning `ABORT` doesn't cancel the other sinks.
|
|
50
|
+
- When STATE returns `ABORT` or the same state, or throws, the other sinks still run, with
|
|
51
|
+
`state === previousState`.
|
|
52
|
+
- When STATE removes the component's own state (`STATE: () => undefined` in a Collection item, or
|
|
53
|
+
a child bound with `state="x"` / `byId()` deleting its record), the other sinks get its last
|
|
54
|
+
state, so `REMOVE: { STATE: () => undefined, EVENTS: event('REMOVED', (s) => s.id) }` works as
|
|
55
|
+
before.
|
|
56
|
+
- Behaviors: the first argument is the slice after STATE, `props.state` the host state after
|
|
57
|
+
STATE, `props.previousState` the host state before the action; the old slice is
|
|
58
|
+
`props.previousState[key]`. Every built-in behavior (form, chat, compose, commandBar, undo,
|
|
59
|
+
pager, selection, sortable, and the `sygnal/ui` dialog, popover, tooltip, tabs, accordion and
|
|
60
|
+
disclosure) is on this contract.
|
|
61
|
+
- Libraries that support 6 and 7 read old values as `props.previousState ?? state`.
|
|
62
|
+
|
|
63
|
+
### New: the timing audit (SYG257)
|
|
64
|
+
|
|
65
|
+
An opt-in dev check for upgrading 6.x code: with `renderComponent(C, { timingAudit: true })`,
|
|
66
|
+
`sygnal({ diagnostics: { timingAudit: true } })` in `vite.config`, `configureTimingAudit(true)`
|
|
67
|
+
from `'sygnal/diagnostics'` or `globalThis.__SYGNAL_TIMING_AUDIT__ = true`, every output sink that
|
|
68
|
+
reads a top-level state field its action's STATE changed reports **SYG257** (info, once per
|
|
69
|
+
component, action, sink and field). Keep `state` where the new value is meant; read
|
|
70
|
+
`previousState` where the old one was. It is off by default because in code written for 7.0
|
|
71
|
+
reading the new value is the intent, and it adds nothing to production builds.
|
|
72
|
+
|
|
73
|
+
sygnal-check's **SYG256** (warning) reports an output guard that contradicts the same action's
|
|
74
|
+
literal STATE write, the loading/admission pitfall above. sygnal-check reads the installed
|
|
75
|
+
Sygnal version per package; **SYG454** (a `byId()` selection without a key) applies only to
|
|
76
|
+
Sygnal 6.
|
|
77
|
+
|
|
78
|
+
### Breaking: a child follows the record its state binding points at
|
|
79
|
+
|
|
80
|
+
When a child's `state` path, or its `byId()` field, id or idKey, changes, Sygnal disposes the old
|
|
81
|
+
child and mounts a new one: `DISPOSE` runs with the last state, pending requests, delayed `next()`
|
|
82
|
+
actions and subscriptions are cancelled, initialization reruns and local state starts from its
|
|
83
|
+
defaults. The child's DOM is replaced, so focus, caret, scroll position and in-progress input
|
|
84
|
+
inside it are lost. The same applies to Collection/VirtualCollection `from` and Switchable
|
|
85
|
+
`state` (all items or pages, hidden ones included, even when ids overlap). Deleting the bound
|
|
86
|
+
record disposes the child too, after the deleting action's outputs; re-adding the same id gets a
|
|
87
|
+
new child, never the old pending work. A new `byId()` call with the same arguments, field edits
|
|
88
|
+
and reordering keep the child. `key={selected.id}` is no longer needed for a selection.
|
|
89
|
+
|
|
90
|
+
**A stable explicit key no longer keeps a child across a target change**: lift state that must
|
|
91
|
+
survive the change into the parent. A child with a custom lens keeps its binding until its key
|
|
92
|
+
changes.
|
|
93
|
+
|
|
94
|
+
### New: uploads, SSE auth, form files and types, `tooltips()`
|
|
95
|
+
|
|
96
|
+
- **Upload and download progress** on `makeFetchDriver` requests: `progress: 'UPLOADING'` (a reply
|
|
97
|
+
action) or `progress: true` (`HTTP.progress(category)`) sends `{ phase, loaded, total, fraction }`
|
|
98
|
+
steps, throttled to one every 50 ms per phase. Such a request goes over XMLHttpRequest, the only
|
|
99
|
+
way to see upload progress in every browser; fetch-only `init` options it can't honour are
|
|
100
|
+
SYG636. Tests: `t.progress('HTTP', { loaded, total }, 'UPLOADING')`.
|
|
101
|
+
- **`multipart: { file, title }`** on a request: the multipart/form-data body as data, the way
|
|
102
|
+
`json` is; a model never builds a `FormData`.
|
|
103
|
+
- **The `form` behavior** takes file inputs (a field's value is its `File`, `multiple` an array,
|
|
104
|
+
validated by the schema), keeps the submit's progress in `state.form.progress`
|
|
105
|
+
(`progress: 'form.PROGRESS'`) and counts start-overs in `state.form.resets` (key a file input
|
|
106
|
+
with it). `request: { url }` (or `(data, state) => request`) sends a valid submit itself, as
|
|
107
|
+
JSON or multipart, with `form.DONE` / `form.ERRORS` wired: the common save needs no model entry.
|
|
108
|
+
`form.RESET` from a click goes back to the start values (it used to take the event as values).
|
|
109
|
+
- **Form types from the schema**: a typed Standard Schema (zod, valibot, arktype) types
|
|
110
|
+
`state.form.values` and `fields.x.value`; `FormOutput<typeof uses.form>` is the submit data.
|
|
111
|
+
- **SSE connections take `headers`** (`{ authorization: 'Bearer ' + state.token }`): such a stream
|
|
112
|
+
is read with fetch (`Last-Event-ID`, `retry`), since EventSource can't send headers.
|
|
113
|
+
- **`tooltips()`** in `sygnal/ui`: one behavior and one state slice for every tooltip of a
|
|
114
|
+
component (glossary terms, icon toolbars), one tip at a time.
|
|
115
|
+
- `Component<State, any>` keeps `state` typed; SYG731 accepts state announced through text,
|
|
116
|
+
`aria-label`, `aria-live` or `role="status"`; sygnal-check's SYG609 also reports intent sources
|
|
117
|
+
with no driver; a test wait that times out on the fake clock names the pending timer; every
|
|
118
|
+
create-sygnal-app template has `npm run check` (TypeScript: sygnal-check and `tsc --noEmit`).
|
|
119
|
+
|
|
120
|
+
### Also included
|
|
121
|
+
|
|
122
|
+
- A child that fails to instantiate is no longer retried on every parent render.
|
|
123
|
+
- State-wait timeout messages report queued simulated inputs; the testing guide separates
|
|
124
|
+
serialized simulated input from synchronous native bursts.
|
|
125
|
+
- TypeScript tests can use `t.query<HTMLInputElement>(selector)` and `t.queryAll<…>(selector)`
|
|
126
|
+
(type assertions, not runtime checks); typed control tokens keep their inference.
|
|
127
|
+
- The combined Rollup build exits normally (compiler watcher lifecycle fix).
|
|
128
|
+
- The release workflow checks the CHANGELOG headings of the new versions before the gates and
|
|
129
|
+
any publish.
|
|
130
|
+
|
|
131
|
+
### Changed
|
|
132
|
+
|
|
133
|
+
- **The app graph's banner line says why it's missing.** When the dev server's graph route is on but the project has no `sygnal-check` 0.10.0 or later, `sygnal/vite` prints `➜ Sygnal graph: needs sygnal-check 0.10.0 or later in this project (npm i -D sygnal-check)` instead of nothing (the URL already answered 404 with the same hint).
|
|
134
|
+
- **`astro dev` prints the graph page's address** under Astro's own banner (Astro never runs Vite's URL printing), and the Astro integration takes the `graph` option (`sygnal({ graph: false })`).
|
|
135
|
+
- **The repository's examples link `sygnal-check`** (`file:../../sygnal-check`, as they link `sygnal`), so `npm run dev` in any of them serves the app graph; `examples/mcp-app` moves off `sygnal-check` ^0.7.0.
|
|
136
|
+
|
|
5
137
|
## 6.6.0 — 2026-10-16
|
|
6
138
|
|
|
7
139
|
The app graph as an interactive page: `sygnal-check --graph=html`, live with `--watch` or at `/__sygnal/graph` in the Vite dev server, in a VS Code panel and in Sygnal DevTools. See [The App Graph](https://sygnal.js.org/guide/app-graph/).
|
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ A reactive component framework with pure functions, zero side effects, and autom
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
> **Upgrading from 6.x?** Sygnal 7.0 changes reducer timing: output sinks (EFFECT, EVENTS, PARENT, HTTP, ...) now see the state after the action's STATE update. See [Migrating to 7.0](https://sygnal.js.org/guide/migrating-to-7/).
|
|
13
|
+
|
|
12
14
|
## Why Sygnal?
|
|
13
15
|
|
|
14
16
|
- **Pure components** — Views are plain functions. All side effects are handled by drivers, outside your code.
|
package/dist/astro/index.cjs.js
CHANGED
|
@@ -867,17 +867,25 @@ function graphMiddleware(server, root, engine, ping = 25000) {
|
|
|
867
867
|
res.end(req.method === 'HEAD' ? undefined : body);
|
|
868
868
|
};
|
|
869
869
|
}
|
|
870
|
-
/**
|
|
871
|
-
|
|
870
|
+
/** The banner line when the project has no sygnal-check with the graph page (it used to print nothing). */
|
|
871
|
+
const GRAPH_MISSING_LINE = ' ➜ Sygnal graph: needs sygnal-check 0.10.0 or later in this project (npm i -D sygnal-check)';
|
|
872
|
+
/**
|
|
873
|
+
* Adds the page's URL under Vite's own when the dev server prints them, or, when the project's
|
|
874
|
+
* sygnal-check is missing or too old for the page (`available` false), a line saying what to install.
|
|
875
|
+
*/
|
|
876
|
+
function printGraphUrl(server, available = true) {
|
|
872
877
|
const print = server?.printUrls;
|
|
873
878
|
if (typeof print !== 'function')
|
|
874
879
|
return;
|
|
875
880
|
server.printUrls = function (...args) {
|
|
876
881
|
const out = print.apply(this, args);
|
|
877
882
|
try {
|
|
883
|
+
const logger = server.config?.logger || console;
|
|
878
884
|
const local = server.resolvedUrls?.local?.[0];
|
|
879
|
-
if (
|
|
880
|
-
|
|
885
|
+
if (!available)
|
|
886
|
+
logger.info(GRAPH_MISSING_LINE);
|
|
887
|
+
else if (local)
|
|
888
|
+
logger.info(` ➜ Sygnal graph: ${new URL(local).origin}${GRAPH_PATH}`);
|
|
881
889
|
}
|
|
882
890
|
catch (_) { }
|
|
883
891
|
return out;
|
|
@@ -1016,7 +1024,8 @@ function printGraphUrl(server) {
|
|
|
1016
1024
|
* skipped silently when missing; a sygnal-check without the page keeps the checker as in 5)
|
|
1017
1025
|
* builds it with createLiveProject on start and after each change: one build gives the page,
|
|
1018
1026
|
* the check overlay / console findings of 5, and the component stamps. Its URL is printed
|
|
1019
|
-
* under Vite's ("➜ Sygnal graph: http://localhost:5173/__sygnal/graph"
|
|
1027
|
+
* under Vite's ("➜ Sygnal graph: http://localhost:5173/__sygnal/graph"; without a sygnal-check
|
|
1028
|
+
* 0.10.0+ in the project, a line saying so instead). The modules of the
|
|
1020
1029
|
* scanned files get a tail setting a non-enumerable `__sygnalKey` (the static 'file#name'
|
|
1021
1030
|
* key, D521) on each top-level component function, which the runtime inspect() reports as
|
|
1022
1031
|
* `key`; with DevTools on, the dev entries also import 'virtual:sygnal/graph'
|
|
@@ -1084,7 +1093,7 @@ function sygnal(options = {}) {
|
|
|
1084
1093
|
// Files whose `import ... from 'sygnal'` resolves to the run() wrapper
|
|
1085
1094
|
const runtimeImporters = new Set();
|
|
1086
1095
|
// Statements run before the app starts: dev flag (+ strict flag)
|
|
1087
|
-
const flags = DEV_FLAG + (diagnostics.strict ? STRICT_FLAG : '');
|
|
1096
|
+
const flags = DEV_FLAG + (diagnostics.strict ? STRICT_FLAG : '') + (diagnostics.timingAudit ? AUDIT_FLAG : '');
|
|
1088
1097
|
const devImports = `import 'sygnal/diagnostics';import '${DEV_CLIENT}';`;
|
|
1089
1098
|
// D77: the DevTools bridge, dev only. First, so the diagnostics entry finds it.
|
|
1090
1099
|
// E-1: the MCP endpoint needs the bridge, so `mcp` turns it on
|
|
@@ -1282,8 +1291,7 @@ function sygnal(options = {}) {
|
|
|
1282
1291
|
// W-2 (D543): the page and its stream, behind the MCP endpoint's guard; its URL in the banner
|
|
1283
1292
|
if (graphOn && server?.middlewares) {
|
|
1284
1293
|
server.middlewares.use(graphMiddleware(server, root, engine));
|
|
1285
|
-
|
|
1286
|
-
printGraphUrl(server);
|
|
1294
|
+
printGraphUrl(server, graphAvailable(root));
|
|
1287
1295
|
}
|
|
1288
1296
|
},
|
|
1289
1297
|
transform(code, id, opts) {
|
|
@@ -1568,7 +1576,7 @@ function normalizeDiagnostics(value) {
|
|
|
1568
1576
|
if (!MODES.includes(mode)) {
|
|
1569
1577
|
throw new Error(`[vite-plugin-sygnal] diagnostics mode must be one of ${MODES.map(m => `'${m}'`).join(', ')}; got ${JSON.stringify(mode)}`);
|
|
1570
1578
|
}
|
|
1571
|
-
return { mode, strict: !!o.strict, ignore: Array.isArray(o.ignore) ? o.ignore.map(String) : [] };
|
|
1579
|
+
return { mode, strict: !!o.strict, timingAudit: !!o.timingAudit, ignore: Array.isArray(o.ignore) ? o.ignore.map(String) : [] };
|
|
1572
1580
|
}
|
|
1573
1581
|
/** Module id without a query or hash. */
|
|
1574
1582
|
function cleanId(id) {
|
|
@@ -1946,6 +1954,8 @@ function startChecker(server, root, mod, setup, opts, engine) {
|
|
|
1946
1954
|
}
|
|
1947
1955
|
const DEV_FLAG = 'if (globalThis.__SYGNAL_DEV__ === undefined) globalThis.__SYGNAL_DEV__ = true;';
|
|
1948
1956
|
const STRICT_FLAG = 'if (globalThis.__SYGNAL_STRICT__ === undefined) globalThis.__SYGNAL_STRICT__ = true;';
|
|
1957
|
+
// 7.0: the opt-in timing audit (SYG257, 'sygnal/diagnostics' checks/timingAudit.ts)
|
|
1958
|
+
const AUDIT_FLAG = 'if (globalThis.__SYGNAL_TIMING_AUDIT__ === undefined) globalThis.__SYGNAL_TIMING_AUDIT__ = true;';
|
|
1949
1959
|
// A top-level (column 0) run(Component, ...) statement: bare, or declaring a
|
|
1950
1960
|
// new binding. Assignments to existing variables, nested calls and indented
|
|
1951
1961
|
// calls (e.g. inside a function or test callback) do not match (B-007). It is
|
|
@@ -2084,10 +2094,12 @@ const rootDir = (root) =>
|
|
|
2084
2094
|
// @ts-ignore — Node's process
|
|
2085
2095
|
!root ? process.cwd() : typeof root === 'string' ? root : node_url.fileURLToPath(root);
|
|
2086
2096
|
function sygnalAstroIntegration(options = {}) {
|
|
2097
|
+
let projectRoot;
|
|
2087
2098
|
return {
|
|
2088
2099
|
name: SYGNAL_RENDERER_NAME,
|
|
2089
2100
|
hooks: {
|
|
2090
2101
|
'astro:config:setup': ({ addRenderer, updateConfig, command, config }) => {
|
|
2102
|
+
projectRoot = rootDir(config?.root);
|
|
2091
2103
|
addRenderer({
|
|
2092
2104
|
name: SYGNAL_RENDERER_NAME,
|
|
2093
2105
|
clientEntrypoint: 'sygnal/astro/client',
|
|
@@ -2117,6 +2129,7 @@ function sygnalAstroIntegration(options = {}) {
|
|
|
2117
2129
|
check: options.check,
|
|
2118
2130
|
nativeGlobalThis: options.nativeGlobalThis,
|
|
2119
2131
|
devtools: options.devtools,
|
|
2132
|
+
graph: options.graph,
|
|
2120
2133
|
})];
|
|
2121
2134
|
}
|
|
2122
2135
|
else if (options.nativeGlobalThis !== false) {
|
|
@@ -2129,6 +2142,16 @@ function sygnalAstroIntegration(options = {}) {
|
|
|
2129
2142
|
vite.plugins = [...(vite.plugins || []), onErrorPlugin(hook)];
|
|
2130
2143
|
updateConfig({ vite });
|
|
2131
2144
|
},
|
|
2145
|
+
// W-2: Astro prints its own banner (Vite's printUrls never runs), so the graph page's line goes here
|
|
2146
|
+
'astro:server:start': ({ address, logger }) => {
|
|
2147
|
+
if (options.graph === false || !projectRoot)
|
|
2148
|
+
return;
|
|
2149
|
+
const print = (msg) => (logger ? logger.info(msg.replace(/^\s*➜\s*/, '')) : console.log(msg));
|
|
2150
|
+
if (!graphAvailable(projectRoot))
|
|
2151
|
+
print(GRAPH_MISSING_LINE);
|
|
2152
|
+
else if (address?.port)
|
|
2153
|
+
print(` ➜ Sygnal graph: http://localhost:${address.port}${GRAPH_PATH}`);
|
|
2154
|
+
},
|
|
2132
2155
|
},
|
|
2133
2156
|
};
|
|
2134
2157
|
}
|