@purview-dev/astroc4 1.0.0-prerelease.4 → 1.0.0-prerelease.6

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/README.md CHANGED
@@ -1,45 +1,64 @@
1
+ [![npm version](https://img.shields.io/npm/v/@purview-dev/astroc4.svg)](https://www.npmjs.com/package/@purview-dev/astroc4)
2
+
1
3
  # astroc4
2
4
 
3
- A Starlight plugin for interactive [LikeC4](https://likec4.dev) architecture
4
- diagrams.
5
+ Interactive [LikeC4](https://likec4.dev) architecture diagrams for Astro and
6
+ Starlight.
5
7
 
6
8
  - `<LikeC4View viewId="…" />` — render a view from the site's `.c4` model.
7
- - ` ```likec4 ` fences in `.mdx` — either inline DSL, compiled and laid out at
8
- build time, or an empty fence with `view="…"` pointing at the model.
9
- - LikeC4 syntax highlighting for Expressive Code (`.c4` files, ` ```c4 `),
10
- derived at build time from the Langium grammar in
11
- [likec4/likec4](https://github.com/likec4/likec4) — no `ec.config.mjs`.
12
- - Every UI string translatable through Starlight's i18n.
13
-
14
- Full documentation lives in [`docs/wiki`](docs/wiki/index.md) (published with
15
- `mkdocs.yml`).
9
+ - ` ```likec4 ` fences in `.md` and `.mdx` — inline DSL or a `.c4` file, compiled
10
+ and laid out at build time, or an empty fence with `view="…"` pointing at the
11
+ model.
12
+ - Projects from anywhere: discovered under the site root, or named in the
13
+ options, with any LikeC4 config file (`likec4.config.json`, `.likec4rc`,
14
+ `likec4.config.ts`, …) and their `imageAliases` unchanged.
15
+ - LikeC4 syntax highlighting for Expressive Code (` ```c4 `), derived at build
16
+ time from the Langium grammar in [likec4/likec4](https://github.com/likec4/likec4)
17
+ — no Expressive Code config under Starlight.
18
+ - Every UI string translatable through Starlight's i18n, and every colour a
19
+ `--likec4-*` token a site can set.
20
+
21
+ It works in **plain Astro** as well: `@purview-dev/astroc4/integration` is an
22
+ `AstroIntegration` that adds the same model compiler and fence transform. See
23
+ [Plain Astro](docs/wiki/guide/Plain-Astro.md) and the runnable example in
24
+ [`examples/astro`](examples/astro).
25
+
26
+ Full documentation lives in [`docs/wiki`](docs/wiki/README.md), grouped into
27
+ `guide/` and `contributing/`.
16
28
 
17
29
  ## Install
18
30
 
19
31
  ```sh
20
- bun add @purview-dev/astroc4@prerelease # 1.0.0-prerelease.N
32
+ bun add @purview-dev/astroc4
21
33
  ```
22
34
 
23
- Published to npm under the `prerelease` dist-tag, so `bun add @purview-dev/astroc4` keeps
24
- resolving the last stable release. Astroc4 declares its peer dependencies, so
25
- Bun and npm install them with it; under pnpm add them explicitly (`astro`,
26
- `@astrojs/starlight`, `@astrojs/react`, `@astrojs/markdown-satteri`, `likec4`,
27
- `@likec4/core`, `react`, `react-dom`, `shiki`).
28
-
29
- Working on astroc4 itself? `bun add @purview-dev/astroc4@file:../path/to/astroc4` copies the
30
- package into `node_modules`, so run `bun install` in the site again after
31
- changing the plugin.
35
+ Only prereleases (`1.0.0-prerelease.N`) are published so far; `latest` and
36
+ `prerelease` both point at the newest one, and `@prerelease` will keep tracking
37
+ prereleases once a stable version ships. Astroc4 declares its peer dependencies,
38
+ so Bun and npm install them with it; under pnpm add them explicitly (`astro`,
39
+ `@astrojs/react`, `@astrojs/markdown-satteri`, `likec4`, `@likec4/core`,
40
+ `react`, `react-dom`, `shiki`, and `@astrojs/starlight` if you use the plugin —
41
+ Starlight is an optional peer, because plain Astro does not need it).
42
+
43
+ `astro add @purview-dev/astroc4` wires the **plain-Astro** shape for you: it
44
+ installs the package and writes `integrations: [astroc4()]`, which works because
45
+ the default export implements the Astro integration protocol too. In a Starlight
46
+ site move that call into `starlight({ plugins: [astroc4()] })`; left in
47
+ `integrations`, the diagrams still render but the translated UI and LikeC4
48
+ highlighting do not, and the plugin warns about it.
32
49
 
33
50
  ## Use
34
51
 
35
- ```js
36
- // astro.config.mjs
52
+ ### With Starlight
53
+
54
+ ```ts
55
+ // astro.config.ts
56
+ import { defineConfig } from 'astro/config';
37
57
  import starlight from '@astrojs/starlight';
38
- import react from '@astrojs/react';
39
58
  import astroc4 from '@purview-dev/astroc4';
40
59
 
41
60
  export default defineConfig({
42
- integrations: [starlight({ plugins: [astroc4()] }), react()],
61
+ integrations: [starlight({ title: 'My docs', plugins: [astroc4()] })],
43
62
  });
44
63
  ```
45
64
 
@@ -49,35 +68,100 @@ import LikeC4View from '@purview-dev/astroc4/components/LikeC4View.astro';
49
68
  <LikeC4View viewId="index" height={560} />
50
69
  ```
51
70
 
52
- The plugin registers `LikeC4VitePlugin`, which compiles every
53
- `likec4.config.json` project under the site root. Keep exactly the projects you
54
- mean to render there: the first one found is the default for `<LikeC4View>`
55
- without a `projectId`, so a stray `.c4` project (a vendored example, say) can
56
- silently replace your own.
71
+ `@astrojs/react` is added for you when the site has no React renderer (the
72
+ islands are `client:only="react"`); list `react()` yourself if you prefer it
73
+ explicit.
74
+
75
+ ### With Astro (no Starlight)
76
+
77
+ ```ts
78
+ // astro.config.ts
79
+ import { defineConfig } from 'astro/config';
80
+ import mdx from '@astrojs/mdx';
81
+ import expressiveCode from 'astro-expressive-code';
82
+ import astroc4 from '@purview-dev/astroc4/integration';
83
+ import { likec4Language } from '@purview-dev/astroc4/expressive-code';
84
+
85
+ export default defineConfig({
86
+ // Expressive Code must come before mdx() — its own requirement.
87
+ integrations: [expressiveCode({ shiki: { langs: [likec4Language] } }), astroc4(), mdx()],
88
+ });
89
+ ```
90
+
91
+ The site supplies `@astrojs/mdx` (for `<LikeC4View>` and fences in `.mdx`;
92
+ fences in plain `.md` work without it) and, for highlighting,
93
+ `astro-expressive-code` given `likec4Language` — Starlight has an
94
+ `expressiveCode` option for the plugin to fill in, Astro does not.
95
+ [`examples/astro`](examples/astro) is a working site with exactly this config
96
+ (`bun run dev:astro`).
97
+
98
+ ### Projects
99
+
100
+ A diagram comes from one of three places:
101
+
102
+ - **A discovered project** — every folder under the site root with a LikeC4
103
+ config file. All of LikeC4's names are read, the way LikeC4 reads them:
104
+ `.likec4rc`, `.likec4.config.json` and `likec4.config.json` as JSON5,
105
+ `likec4.config.{ts,mts,cts,js,mjs,cjs}` bundled with esbuild. The project is
106
+ named by its config's `name`, or after its folder.
107
+ - **A named project** — `projects: { <name>: <folder> }` adds LikeC4 projects
108
+ from anywhere: a sibling folder, another checkout, an absolute path. The key is
109
+ the name pages use. The project's own config, model and icons are used exactly
110
+ as they are.
111
+ - **A single file** — `<LikeC4View file="diagrams/flow.c4">`, or a fence's
112
+ `file="…"`, compiled at build time like an inline fence.
113
+
114
+ ```ts
115
+ // src/likec4/likec4.config.ts
116
+ import { defineConfig } from 'likec4/config';
117
+
118
+ export default defineConfig({ name: 'docs', imageAliases: { '@': './images' } });
119
+ ```
120
+
121
+ ```ts
122
+ starlight({
123
+ plugins: [astroc4({ projects: { architecture: '../../docs/architecture/likec4' } })],
124
+ });
125
+ ```
126
+
127
+ ```mdx
128
+ <LikeC4View project="architecture" viewId="c1-context" />
129
+ ```
130
+
131
+ With exactly one project, `project` may be left out. With more than one,
132
+ leaving it out fails the build and lists the names — for `<LikeC4View>` and
133
+ empty fences, in `.md` and `.mdx` alike. An unknown name fails the build the same
134
+ way. Two projects with the same name, or whose configs declare the same `name`,
135
+ fail the build naming both folders.
136
+
137
+ Named projects are compiled through links in `.astro/astroc4/workspace` (Astro's
138
+ cache folder, which Astro sites already ignore); edits to their `.c4` files
139
+ reload in `astro dev` like any other.
57
140
 
58
141
  ### Options
59
142
 
60
143
  | Option | Default | |
61
144
  | --- | --- | --- |
62
- | `vitePlugin` | `{}` | Passed to `LikeC4VitePlugin`, e.g. `{ workspace: 'diagrams' }`. |
145
+ | `projects` | `{}` | Named LikeC4 project folders, relative to the site root or absolute. See above. |
146
+ | `vitePlugin` | `{}` | Passed to `LikeC4VitePlugin`. Setting `workspace` or `languageServices` takes discovery over: `projects` is then ignored (with a warning). |
63
147
  | `fences` | `true` | Turn ` ```likec4 ` fences into diagrams. |
64
- | `highlighting` | `true` | Highlight LikeC4 source. See below. |
148
+ | `highlighting` | `true` | Highlight LikeC4 source. Starlight only — a plain Astro site passes `likec4Language` to Expressive Code. See below. |
65
149
 
66
150
  ### Syntax highlighting
67
151
 
68
- The plugin reads `packages/language-server/src/like-c4.langium` from
69
- `likec4/likec4`, derives a TextMate grammar from it, and registers that with
70
- the site's Expressive Code — so ` ```c4 ` fences, ` ```likec4 ` fences and
152
+ Under Starlight the plugin reads `packages/language-server/src/like-c4.langium`
153
+ from `likec4/likec4`, derives a TextMate grammar from it, and registers that with
154
+ the site's Expressive Code — so ` ```c4 ` fences, ` ```likec4 ` source and
71
155
  `<Code lang="likec4">` all highlight with no configuration. Highlighting
72
156
  follows the language: when upstream adds a shape or a keyword, the next build
73
157
  picks it up.
74
158
 
75
- ```js
159
+ ```ts
76
160
  starlight({
77
161
  plugins: [
78
162
  astroc4({
79
163
  highlighting: {
80
- ref: 'v1.59.4', // branch, tag or commit SHA; default 'main'
164
+ ref: 'v1.60.0', // branch, tag or commit SHA; default 'main'
81
165
  },
82
166
  }),
83
167
  ],
@@ -93,11 +177,12 @@ starlight({
93
177
  | `cacheTtl` | `3600` | Seconds a cached fetch stays fresh. |
94
178
  | `refresh` | `false` | Ignore the cache and refetch. |
95
179
  | `cacheDir` | `node_modules/.cache/astroc4` | Where to cache. |
96
- | `baseUrl` | `raw.githubusercontent.com` | Raw host to read from. |
180
+ | `baseUrl` | `https://raw.githubusercontent.com` | Raw host to read from. |
97
181
  | `timeout` | `15000` | Request timeout in milliseconds. |
98
182
 
99
- `ASTROC4_REF` and `ASTROC4_OFFLINE=1` override `ref` and `offline`, which keeps
100
- CI configuration out of the config file.
183
+ `ASTROC4_REF` overrides `ref`, and `ASTROC4_OFFLINE=1` stands in for `offline`
184
+ when the config does not set it, which keeps CI configuration out of the config
185
+ file.
101
186
 
102
187
  The grammar is resolved while Astro loads its config, so `astro dev`,
103
188
  `astro build`, `astro check` and `astro sync` all fail on a `ref` that does not
@@ -106,36 +191,48 @@ LikeC4 grammar — the same way any other build error does. Air-gapped builds us
106
191
  `offline: true`; builds that must not hard-fail use `onError: 'warn'`, which
107
192
  falls back to the last fetched grammar and then to the bundled snapshot.
108
193
 
109
- Fetched grammars are cached per `repo@ref`, so switching `ref` never thrashes
110
- and repeated builds do not touch the network.
111
-
112
- A site that already has an `ec.config.mjs` keeps working, and can pin the
113
- bundled grammar there instead:
114
-
115
- ```js
116
- // ec.config.mjs
117
- import { defineEcConfig } from '@astrojs/starlight/expressive-code';
118
- import { likec4Language } from '@purview-dev/astroc4/expressive-code';
119
-
120
- export default defineEcConfig({ shiki: { langs: [likec4Language] } });
121
- ```
122
-
123
- Expressive Code concatenates `shiki.langs` when it merges an `ec.config.mjs`
124
- with the integration options, so its own languages and the plugin's coexist.
194
+ A Starlight site that already has an `ec.config.mjs` keeps working: Expressive
195
+ Code concatenates `shiki.langs` when it merges that file with the plugin's
196
+ options, so its own languages and LikeC4 coexist.
125
197
 
126
198
  ### `<LikeC4View>` props
127
199
 
128
200
  | Prop | Type | Default |
129
201
  | --- | --- | --- |
130
202
  | `viewId` | `string` | required |
131
- | `projectId` | `string` | first project |
203
+ | `project` | `string` | the only project; required when there are several |
204
+ | `file` | `string` | a `.c4` file relative to `src/`, instead of a project |
205
+ | `projectId` | `string` | deprecated alias of `project` |
132
206
  | `title` / `description` | `string` | translated defaults |
133
207
  | `height` | `number` | `480` |
134
208
  | `collapsible` / `defaultOpen` | `boolean` | `true` |
135
209
 
210
+ `file` must name a `.c4` file under the site's `src/` folder; anything else
211
+ fails the build. A single file has no config, so it has no `imageAliases`: use a
212
+ pack, a URL or a `public/` icon there.
213
+
136
214
  ### Fence options
137
215
 
138
- ` ```likec4 view="…" title="…" description="…" height=420 collapsible=false defaultOpen=false `
216
+ ` ```likec4 view="…" project="…" file="…" title="…" description="…" height=420 collapsible=false defaultOpen=false `
217
+
218
+ `file` is relative to the Markdown page and must stay inside the site. Fences in
219
+ `.md` and `.mdx` take the same options; in `.md` the UI strings are English.
220
+
221
+ ### Styling
222
+
223
+ Set any token on an ancestor — `:root` works on any site:
224
+
225
+ ```css
226
+ :root {
227
+ --likec4-border: #d4d4d8;
228
+ --likec4-accent: #0046be;
229
+ }
230
+ ```
231
+
232
+ `--likec4-border`, `-surface`, `-surface-alt`, `-text`, `-text-muted`,
233
+ `-accent`, `-skeleton`, `-danger`, `-danger-soft` and `-danger-text`. Unset,
234
+ they fall back to Starlight's `--sl-color-*`, then to neutral greys. See
235
+ [Components](docs/wiki/guide/Components.md#styling).
139
236
 
140
237
  ### Translating
141
238
 
@@ -145,35 +242,83 @@ because some are filled in by the browser, not by i18next.
145
242
 
146
243
  ## Icons
147
244
 
148
- Use a bundled pack (`azure:`, `aws:`, `gcp:`, `tech:`, `bootstrap:`), an
149
- absolute URL, or a file under the site's `public/` referenced by a path
150
- relative to the `.c4` file. Don't use LikeC4's `imageAliases`: the compiler
151
- bakes a build-machine `file://` path into the client bundle.
245
+ Every form LikeC4 accepts works, in discovered and named projects alike:
246
+
247
+ - a bundled pack (`azure:`, `aws:`, `gcp:`, `tech:`, `bootstrap:`, `md:`);
248
+ - an absolute URL;
249
+ - `imageAliases` from the project's config — `"@": "../../.assets/images"` and
250
+ `icon @/azure/entra.svg` — with no change to the project;
251
+ - a path relative to the `.c4` file, inside or outside the project.
252
+
253
+ Local files are rendered by LikeC4 itself (SVGs inline) and also served at
254
+ `/_astroc4/<project>/<path>` — `@/azure/entra.svg` becomes
255
+ `/_astroc4/architecture/azure/entra.svg`, a named alias `@brand` adds a `brand/`
256
+ segment — and copied into the build output. Files under `public/` keep their
257
+ public URL. No `file://` path from the build machine reaches the bundle, and an
258
+ icon that does not exist fails the build. Nothing is written into your source
259
+ tree.
260
+
261
+ ## Troubleshooting
262
+
263
+ **A green `astro build` is not proof that a diagram renders.** The islands are
264
+ `client:only` React, so a build can succeed while every frame stays on "Loading
265
+ architecture diagram…" — open the page in `astro dev`, or run
266
+ `bun scripts/smoke.ts` / `bun scripts/hydration.ts <url>` against it.
267
+
268
+ | Symptom | Cause | Fix |
269
+ | --- | --- | --- |
270
+ | `The requested module '/node_modules/react/compiler-runtime.js' does not provide an export named 'c'` in the console, diagrams stuck on the loading placeholder | `likec4/react` (1.59.4 and later) is compiled with the React Compiler and imports `c` from React 19's CommonJS `react/compiler-runtime`; in dev Vite serves it unoptimized, so the island never mounts. Production builds bundle it, which is why the build still passes. | Fixed in `1.0.0-prerelease.5`: the plugin adds `react/compiler-runtime` to `vite.optimizeDeps.include`. On an older version, or to confirm by hand: `vite: { optimizeDeps: { include: ['react/compiler-runtime'] } }`. |
271
+ | Build fails with `could not fetch the LikeC4 grammar from https://raw.githubusercontent.com/…` | The grammar is resolved while the config loads, and the machine cannot reach GitHub. | `highlighting: { offline: true }` (bundled snapshot) or `onError: 'warn'`; `ASTROC4_OFFLINE=1` in CI. |
272
+ | Build fails with `Several LikeC4 projects exist (…); pass project="…"` | The site has more than one project (discovered or named) and a `<LikeC4View>` or fence names none. | Pass `project="<name>"`; the message lists the names. |
273
+ | Build fails with `two LikeC4 projects are named "…"` | A named project and a discovered one, or two folders, share a name or a config `name`. | Rename one, or name the configured project differently. |
274
+ | Build fails with `…likec4.config… could not be read` | The config does not parse (JSON5) or does not bundle (JS/TS). | Fix the error after the path; a JS/TS config's imports other than `likec4/config` must resolve from its folder. |
275
+ | A diagram shows "Unknown LikeC4 projectId" | Only on a site that sets `vitePlugin.workspace` itself, where astroc4 does not know the projects. | Pass a `project` the workspace contains. |
276
+ | `Cannot find module …/@purview-dev/astroc4/dist/index.js` | That version shipped without its build output (only `1.0.0-prerelease.3` did). | Move to `1.0.0-prerelease.4` or later. |
277
+
278
+ Everything else — highlighting that produces one colour, fences left as code,
279
+ Windows module-runner crashes — is in
280
+ [Troubleshooting](docs/wiki/Troubleshooting.md).
152
281
 
153
282
  ## Develop
154
283
 
155
284
  ```sh
156
285
  bun install
157
- bun run dev # playground at http://localhost:4321 (English + French)
158
- bun run test # hermetic unit suite: Langium parsing, grammar, fences, plugin
159
- bun run test:network # the same, plus a check against the live likec4/likec4
160
- bun run test:site # pack the tarball and build a real Starlight site from it
161
- bun run check # astro check over playground/ and src/
286
+ bun run dev # Starlight playground at http://localhost:4321 (English + French)
287
+ bun run dev:astro # plain-Astro example at http://localhost:4322
288
+ bun run test # hermetic unit suite: config loading, grammar, fences, plugin
289
+ bun run test:network # the live check against likec4/likec4
290
+ bun run test:site # pack the tarball, install it into copies of playground/ and
291
+ # examples/astro/, build both, run the full smoke suite against
292
+ # the Starlight copy, and check the plain one hydrates
293
+ bun run check # astro check over playground/, examples/astro/ and src/, tsc over scripts/
162
294
  bun run grammar:check # fail if the committed grammar snapshot is stale
163
295
  bun run build && bun run preview
296
+ bun run build:astro # static build of the plain-Astro example
164
297
  bun scripts/smoke.ts http://localhost:4321 # headless: hydration, icons, highlighting, i18n
298
+ bun scripts/hydration.ts http://localhost:4321 # headless: one diagram, nothing else
165
299
  ```
166
300
 
167
- `Justfile` wraps each of those (`just verify` runs what the PR workflow runs).
168
- `src/grammar/likec4.tmLanguage.json` and `src/grammar/like-c4.langium` are
169
- generated — `bun run grammar:sync [--ref <tag>]` refreshes them from upstream,
170
- and never by hand.
171
-
172
- CI lives in `.github/workflows`: `pr.yml` runs the organisation gate plus the
173
- unit suite, the browser smoke test and the packed-consumer build; `release.yml`
174
- publishes to npm with **trusted publishing** — an OIDC token, no stored npm
175
- secret — under the `prerelease` dist-tag for prerelease versions, then tags the
176
- release. See [`docs/wiki/Repository-CI-CD.md`](docs/wiki/Repository-CI-CD.md).
301
+ The source is TypeScript throughout, configs included (`astro.config.ts`), with
302
+ `.ts` relative imports; `bun run build:lib` bundles it into `dist/`.
303
+ `playground/` is the Starlight development site; `examples/astro/` is the
304
+ plain-Astro one, and `test:site` copies it so the checked-in example is what the
305
+ suite builds. Both import the package by name — Node's self-reference resolves
306
+ it from the repository, which *is* the package.
307
+
308
+ `test:site` and the two browser scripts need Chromium
309
+ (`bunx playwright install chromium`).
310
+
311
+ `Justfile` wraps each of those (`just verify` runs the PR gate's chain plus
312
+ `test:site`). `src/grammar/likec4.tmLanguage.json` and
313
+ `src/grammar/like-c4.langium` are generated — `bun run grammar:sync [--ref <tag>]`
314
+ refreshes them from upstream, and never by hand.
315
+
316
+ CI lives in `.github/workflows`: `pr.yml` runs the organisation gate (grammar
317
+ snapshot, type-check, build, unit suite), the live grammar check, the browser
318
+ smoke test and the packed-consumer build; `release.yml` publishes to npm with
319
+ **trusted publishing** — an OIDC token, no stored npm secret — under the
320
+ `prerelease` dist-tag for prerelease versions, then tags the release. See
321
+ [`docs/wiki/contributing/Repository-CI-CD.md`](docs/wiki/contributing/Repository-CI-CD.md).
177
322
 
178
323
  Open the folder in VS Code and install the recommended **LikeC4** extension
179
324
  for the language server: diagnostics, go-to-definition, hover and a live
@@ -4,10 +4,6 @@ import { type GrammarSourceOptions } from './grammar/source.js';
4
4
  export declare const likec4Language: LanguageRegistration & {
5
5
  aliases: string[];
6
6
  };
7
- /**
8
- * Resolve the grammar from likec4/likec4 at the ref you ask for and return it
9
- * as a Shiki language registration.
10
- */
11
7
  export declare function loadLikeC4Language(options?: GrammarSourceOptions): Promise<LanguageRegistration & {
12
8
  aliases: string[];
13
9
  }>;