@alfacapital-tech/c4builder 0.3.0-rc.3 → 0.4.0-rc.1
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 +109 -23
- package/dist/cli/commands/check.js +80 -0
- package/dist/cli/dispatch.js +5 -1
- package/dist/core/render/d2renderer.js +12 -1
- package/package.json +1 -1
package/README.MD
CHANGED
|
@@ -33,9 +33,11 @@ I strongly encourage people to check them out first but if you want to dive righ
|
|
|
33
33
|
`npm install` (no tag) always installs the latest stable release. Release candidates are
|
|
34
34
|
opt-in only — see [Installing a release candidate](#installing-a-release-candidate).
|
|
35
35
|
|
|
36
|
-
**Requirements:**
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
**Requirements:** Node.js ≥ 20.19. Local diagram generation needs Java 17+: a system
|
|
37
|
+
`java` (`JAVA_HOME`/`PATH`) is used when present, otherwise c4builder downloads a private
|
|
38
|
+
JRE into its cache automatically (`c4builder jre install` pre-warms it, `jre info` shows
|
|
39
|
+
what would be used). Graphviz is **not** required — layout is computed by PlantUML's
|
|
40
|
+
built-in Java engine (Smetana). The PlantUML jar and the diagram font are bundled.
|
|
39
41
|
|
|
40
42
|
With `diagramFormat: png`, diagrams are produced by rasterizing their SVG with
|
|
41
43
|
[`@resvg/resvg-js`](https://github.com/thx/resvg-js) (a bundled dependency, prebuilt
|
|
@@ -62,22 +64,57 @@ A sample project is available at [C4Builder-Demo](https://github.com/adrianvlupu
|
|
|
62
64
|
## Available Commands
|
|
63
65
|
|
|
64
66
|
```bash
|
|
65
|
-
|
|
67
|
+
$ c4builder --help
|
|
66
68
|
Usage: index [options]
|
|
67
69
|
|
|
68
70
|
Options:
|
|
69
|
-
-V, --version
|
|
70
|
-
new
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
-V, --version output the version number
|
|
72
|
+
--new create a new project from template
|
|
73
|
+
--name <name> project name for --new (skips the name prompt)
|
|
74
|
+
-y, --yes non-interactive --new: defaults, no prompts
|
|
75
|
+
(requires --name)
|
|
76
|
+
--force for `jre install`: force JRE download even if
|
|
77
|
+
system java is present
|
|
78
|
+
--config change configuration for the current directory
|
|
79
|
+
-c, --config-file <.c4builder> set the configuration file relative path
|
|
80
|
+
--list display the current configuration
|
|
81
|
+
--reset clear all configuration
|
|
82
|
+
--site serve the generated site
|
|
83
|
+
-w, --watch watch for changes and rebuild
|
|
84
|
+
-o, --open open the generated site in the browser (with
|
|
85
|
+
--site)
|
|
86
|
+
--docs a brief explanation for the available
|
|
87
|
+
configuration options
|
|
88
|
+
--system-fonts render with system fonts instead of the
|
|
89
|
+
bundled one (not deterministic)
|
|
90
|
+
-p, --port <n> port used for serving the generated site
|
|
91
|
+
-h, --help display help for command
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
There is also a `jre` subcommand: `c4builder jre install [--force]` pre-downloads the
|
|
95
|
+
private JRE cache, `c4builder jre info` prints which Java would be used.
|
|
96
|
+
|
|
97
|
+
### Validating single diagrams
|
|
98
|
+
|
|
99
|
+
`c4builder check <file...>` validates individual `.puml` / `.iuml` / `.d2` files with the
|
|
100
|
+
same engines the build uses (bundled PlantUML jar + system/managed Java, bundled D2) —
|
|
101
|
+
no project or `.c4builder` needed, nothing is written. Handy for pre-commit hooks and CI:
|
|
102
|
+
exit code 0 when every file compiles, 1 otherwise; errors are printed as
|
|
103
|
+
`✗ file: line N: <engine message>`.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
c4builder check src/c4/context.puml lib/shared.iuml diagrams/flow.d2
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`!include` paths resolve relative to the checked file's directory; a `.iuml` library is
|
|
110
|
+
validated by including it into an empty diagram.
|
|
111
|
+
|
|
112
|
+
A minimal pre-commit hook (`.git/hooks/pre-commit`) that validates staged diagrams:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
#!/bin/sh
|
|
116
|
+
files=$(git diff --cached --name-only --diff-filter=ACM -- '*.puml' '*.iuml' '*.d2')
|
|
117
|
+
[ -z "$files" ] || exec c4builder check $files
|
|
81
118
|
```
|
|
82
119
|
|
|
83
120
|
By default, running `c4builder` will attempt to build the project.
|
|
@@ -217,7 +254,7 @@ The generated markdown file makes for an easy single page readme for the reposit
|
|
|
217
254
|
|
|
218
255
|
### Docsify site with a sidebar
|
|
219
256
|
|
|
220
|
-
After choosing to generate a docsify site, it can be viewed locally by running `c4builder site` and accessing `http://localhost:3000`;
|
|
257
|
+
After choosing to generate a docsify site, it can be viewed locally by running `c4builder --site` and accessing `http://localhost:3000`;
|
|
221
258
|
|
|
222
259
|
The site is minimalistic including only a sidebar with the System/Container/Components hierarchy.
|
|
223
260
|
|
|
@@ -264,24 +301,51 @@ Pre-release builds (`0.x.y-rc.N`) are published under the npm dist-tag `rc` and
|
|
|
264
301
|
|
|
265
302
|
`npm install` without a tag keeps installing the latest stable version.
|
|
266
303
|
|
|
304
|
+
## Docker
|
|
305
|
+
|
|
306
|
+
The image ships everything needed for a build (Node, JRE, PlantUML jar, fonts, D2) —
|
|
307
|
+
mount a project into `/pwd`:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
docker run --rm -v "$PWD:/pwd" ghcr.io/alfacapital-tech/c4builder:latest c4builder
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Tags (in sync with npm dist-tags):
|
|
314
|
+
|
|
315
|
+
| tag | meaning |
|
|
316
|
+
|---|---|
|
|
317
|
+
| `X.Y.Z`, `sha-…` | immutable releases / commits |
|
|
318
|
+
| `latest` | latest stable release |
|
|
319
|
+
| `rc` | latest release candidate |
|
|
320
|
+
| `edge` | last master commit |
|
|
321
|
+
| `pr-N` | pull request builds |
|
|
322
|
+
|
|
323
|
+
Use the image directly in CI (GitHub Actions, GitLab CI) as the job image — the
|
|
324
|
+
`c4builder` binary is on `PATH`.
|
|
325
|
+
|
|
267
326
|
## Releasing
|
|
268
327
|
|
|
269
328
|
Publishing is triggered by pushing a git tag `v<version>` — pushes/merges to `master` alone
|
|
270
329
|
do not publish.
|
|
271
330
|
|
|
272
|
-
|
|
331
|
+
Every user-facing PR adds a line to the `## Unreleased` section of the change log below;
|
|
332
|
+
the release commit renames it to `## vX.Y.Z`.
|
|
333
|
+
|
|
334
|
+
**Release candidate** (from `master`, as many as needed — consumers pinned to `rc`
|
|
335
|
+
pick each one up automatically):
|
|
273
336
|
|
|
274
337
|
```
|
|
275
|
-
npm version
|
|
338
|
+
npm version X.Y.Z-rc.1 -m "rc: %s" # first candidate
|
|
339
|
+
npm version prerelease -m "rc: %s" # next ones: rc.2, rc.3, …
|
|
276
340
|
git push --follow-tags
|
|
277
341
|
```
|
|
278
342
|
|
|
279
|
-
Publishes to dist-tag `rc
|
|
343
|
+
Publishes to npm dist-tag `rc` and docker tag `rc`; `latest` is untouched.
|
|
280
344
|
|
|
281
|
-
**Final release
|
|
345
|
+
**Final release** (rename `## Unreleased` → `## vX.Y.Z` first):
|
|
282
346
|
|
|
283
347
|
```
|
|
284
|
-
npm version
|
|
348
|
+
npm version X.Y.Z -m "release: %s"
|
|
285
349
|
git push --follow-tags
|
|
286
350
|
```
|
|
287
351
|
|
|
@@ -289,9 +353,31 @@ Publishes to dist-tag `latest`.
|
|
|
289
353
|
|
|
290
354
|
# Change log
|
|
291
355
|
|
|
356
|
+
## Unreleased
|
|
357
|
+
|
|
358
|
+
- `c4builder check <file...>` — validate individual `.puml` / `.iuml` / `.d2` files with the
|
|
359
|
+
bundled engines, without a project build (#13)
|
|
360
|
+
- D2 compile errors are printed as readable `file:line:col: message` lines instead of raw JSON
|
|
361
|
+
- Regression test for two adjacent `` embeds (#12 — the bug itself was fixed in 0.3.0)
|
|
362
|
+
|
|
292
363
|
## v0.3.0
|
|
293
364
|
|
|
294
|
-
|
|
365
|
+
Full TypeScript rewrite (ESM, Node.js ≥ 20.19) with the same CLI and `.c4builder` format.
|
|
366
|
+
|
|
367
|
+
- PlantUML renders via a direct `java` call with the built-in Smetana layout — graphviz
|
|
368
|
+
is no longer needed; the jar is bundled (`vendor/`)
|
|
369
|
+
- Java itself is optional: system java ≥ 17 is used when present, otherwise a private
|
|
370
|
+
JRE is downloaded automatically (`c4builder jre install` / `jre info`)
|
|
371
|
+
- Second diagram backend: [D2](https://d2lang.com) (`.d2` files, optional
|
|
372
|
+
`@terrastruct/d2` dependency, imports supported)
|
|
373
|
+
- `diagramFormat: png` rasterizes SVG with `@resvg/resvg-js` — deterministic PNG for
|
|
374
|
+
PlantUML and D2 alike
|
|
375
|
+
- Bundled Nimbus Sans font for identical diagram geometry everywhere;
|
|
376
|
+
`useSystemFonts` / `--system-fonts` opts out
|
|
377
|
+
- Config is validated (zod): typos and wrong types fail with a clear message
|
|
378
|
+
- Docker image on GHCR with a tag scheme in sync with npm dist-tags (see [Docker](#docker))
|
|
379
|
+
- PDF output removed — use the docsify site or the single/collection markdown outputs
|
|
380
|
+
instead (the `pdf`/`pdfCss` options no longer exist)
|
|
295
381
|
|
|
296
382
|
## v0.2.20
|
|
297
383
|
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { VENDOR_DIR } from "../../util/paths.js";
|
|
5
|
+
import { VENDORED_JAR } from "../../util/utils.js";
|
|
6
|
+
import { resolveJava } from "../../core/render/jre.js";
|
|
7
|
+
import { renderDiagram } from "../../core/render/diagrams.js";
|
|
8
|
+
import { renderD2, teardownD2 } from "../../core/render/d2renderer.js";
|
|
9
|
+
// `c4builder check <file...>` — валидация отдельных диаграмм ТЕМ ЖЕ движком, что и
|
|
10
|
+
// сборка (вендорный PlantUML jar + системная/managed java, бандл D2), без проекта и
|
|
11
|
+
// `.c4builder`. Рендер идёт в память и выбрасывается: проверяем ровно то, что упало бы
|
|
12
|
+
// в сборке (системный `plantuml` может отличаться версией). Код выхода 0 — все файлы ок,
|
|
13
|
+
// 1 — есть ошибка (движка, неизвестного расширения или чтения файла).
|
|
14
|
+
// stderr PlantUML в -pipe: `ERROR` / позиция / описание. Позиция — 0-based строка
|
|
15
|
+
// диаграммы, для файла с одной диаграммой это file:N+1.
|
|
16
|
+
// ponytail: в файле с несколькими @startuml позиция считается от начала блока — смещение
|
|
17
|
+
// не учитываем, добавить, если такие файлы станут проверять.
|
|
18
|
+
const PUML_ERROR = /^ERROR\n(\d+)\n(.*)$/m;
|
|
19
|
+
const JAR_PATH = path.join(VENDOR_DIR, VENDORED_JAR.jar);
|
|
20
|
+
// JRE резолвится лениво и один раз: список только из .d2 java не трогает.
|
|
21
|
+
let javaPromise = null;
|
|
22
|
+
const getJava = () => {
|
|
23
|
+
javaPromise ??= resolveJava({ log: (m) => console.log(chalk.gray(m)) }).then((r) => r.path);
|
|
24
|
+
return javaPromise;
|
|
25
|
+
};
|
|
26
|
+
const checkPlantUml = async (abs) => {
|
|
27
|
+
// .iuml — библиотека без @startuml: сам по себе PlantUML «рендерит» его без ошибок,
|
|
28
|
+
// поэтому проверяем через включение в пустую диаграмму.
|
|
29
|
+
const isLib = path.extname(abs) === '.iuml';
|
|
30
|
+
const content = isLib ? `@startuml\n!include ${abs}\n@enduml\n` : await readFile(abs);
|
|
31
|
+
const isDitaa = /@startditaa/i.test(content.toString());
|
|
32
|
+
try {
|
|
33
|
+
await renderDiagram(content, {
|
|
34
|
+
javaBin: await getJava(),
|
|
35
|
+
jarPath: JAR_PATH,
|
|
36
|
+
includePath: path.dirname(abs),
|
|
37
|
+
format: isDitaa ? 'png' : 'svg',
|
|
38
|
+
charset: 'UTF-8',
|
|
39
|
+
isDitaa,
|
|
40
|
+
useSystemFonts: false
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
const m = e.message.match(PUML_ERROR);
|
|
45
|
+
if (!m)
|
|
46
|
+
throw e;
|
|
47
|
+
// Для .iuml позиция относится к обёртке, а не к файлу — не показываем.
|
|
48
|
+
throw new Error(isLib ? m[2] : `строка ${Number(m[1]) + 1}: ${m[2]}`);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const CHECKERS = {
|
|
52
|
+
'.puml': checkPlantUml,
|
|
53
|
+
'.iuml': checkPlantUml,
|
|
54
|
+
'.d2': (abs) => renderD2(abs)
|
|
55
|
+
};
|
|
56
|
+
export default async (files) => {
|
|
57
|
+
if (files.length === 0) {
|
|
58
|
+
console.log(chalk.red('использование: c4builder check <file...> (.puml | .iuml | .d2)'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
let failed = 0;
|
|
62
|
+
// ponytail: последовательно — на сотнях файлов добавить пул как в generateImages.
|
|
63
|
+
for (const file of files) {
|
|
64
|
+
const abs = path.resolve(file);
|
|
65
|
+
const check = CHECKERS[path.extname(abs).toLowerCase()];
|
|
66
|
+
try {
|
|
67
|
+
if (!check)
|
|
68
|
+
throw new Error('неизвестное расширение (ожидается .puml, .iuml или .d2)');
|
|
69
|
+
await check(abs);
|
|
70
|
+
console.log(chalk.green(`✓ ${file}`));
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
failed++;
|
|
74
|
+
console.error(chalk.red(`✗ ${file}: ${e.message}`));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
await teardownD2();
|
|
78
|
+
if (failed)
|
|
79
|
+
process.exit(1);
|
|
80
|
+
};
|
package/dist/cli/dispatch.js
CHANGED
|
@@ -6,6 +6,7 @@ import Configstore from 'configstore';
|
|
|
6
6
|
import cmdHelp from "./commands/help.js";
|
|
7
7
|
import cmdNewProject from "./commands/new.js";
|
|
8
8
|
import cmdJre from "./commands/jre.js";
|
|
9
|
+
import cmdCheck from "./commands/check.js";
|
|
9
10
|
import cmdList from "./commands/list.js";
|
|
10
11
|
import cmdSite from "./commands/site.js";
|
|
11
12
|
import cmdCollect from "./wizard/collect.js";
|
|
@@ -118,12 +119,15 @@ export default async () => {
|
|
|
118
119
|
throw new InvalidArgumentError('ожидается TCP-порт: целое число 1..65535');
|
|
119
120
|
return Number(v);
|
|
120
121
|
})
|
|
121
|
-
.allowExcessArguments() // позиционные аргументы
|
|
122
|
+
.allowExcessArguments() // позиционные аргументы подкоманд `jre <action>` и `check <file...>`
|
|
122
123
|
.parse(process.argv);
|
|
123
124
|
const opts = program.opts();
|
|
124
125
|
// Прогрев JRE — до загрузки конфига проекта и intro: команда самостоятельна.
|
|
125
126
|
if (program.args[0] === 'jre')
|
|
126
127
|
return cmdJre(program.args.slice(1), { force: opts.force });
|
|
128
|
+
// Проверка отдельных диаграмм — тоже без конфига проекта.
|
|
129
|
+
if (program.args[0] === 'check')
|
|
130
|
+
return cmdCheck(program.args.slice(1));
|
|
127
131
|
let conf = { get: () => { } };
|
|
128
132
|
let cacheConf = { get: () => { }, set: () => { }, clear: () => { } };
|
|
129
133
|
// Лок сборки — рядом с конфигом проекта (см. util/lock.ts). Заполняется вместе с conf.
|
|
@@ -159,6 +159,17 @@ const buildCompileRequest = (entryAbs) => {
|
|
|
159
159
|
return { fs: fsMap, inputPath: toKey(path.resolve(entryAbs)) };
|
|
160
160
|
};
|
|
161
161
|
// Рендер .d2 → SVG (Buffer). Ошибки компиляции пробрасываются с путём файла.
|
|
162
|
+
// Ошибка компиляции движок отдаёт JSON-массивом [{range, errmsg}] — вытаскиваем
|
|
163
|
+
// человекочитаемые errmsg (`file:line:col: текст`); иной формат отдаём как есть.
|
|
164
|
+
const d2ErrorText = (message) => {
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(message);
|
|
167
|
+
if (Array.isArray(parsed) && parsed.every((x) => typeof x?.errmsg === 'string'))
|
|
168
|
+
return parsed.map((x) => x.errmsg).join('\n');
|
|
169
|
+
}
|
|
170
|
+
catch { }
|
|
171
|
+
return message;
|
|
172
|
+
};
|
|
162
173
|
const renderD2 = async (entryAbs, { layout = 'dagre' } = {}) => {
|
|
163
174
|
const d2 = await getD2();
|
|
164
175
|
const { fs: fsMap, inputPath } = buildCompileRequest(entryAbs);
|
|
@@ -168,7 +179,7 @@ const renderD2 = async (entryAbs, { layout = 'dagre' } = {}) => {
|
|
|
168
179
|
}
|
|
169
180
|
catch (err) {
|
|
170
181
|
const e = err;
|
|
171
|
-
throw new Error(`Ошибка компиляции D2 (${entryAbs}):\n${e.message || e}`);
|
|
182
|
+
throw new Error(`Ошибка компиляции D2 (${entryAbs}):\n${d2ErrorText(e.message || String(e))}`);
|
|
172
183
|
}
|
|
173
184
|
let svg;
|
|
174
185
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfacapital-tech/c4builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-rc.1",
|
|
4
4
|
"description": "A CLI tool designed to compile a folder structure of markdowns and plant uml files into a site, single file markdown or a collection of markdowns with links",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|