@reformer/builder 1.0.0 → 3.0.0-beta.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 +108 -8
- package/bin/reformer-builder.d.mts +36 -0
- package/bin/reformer-builder.mjs +112 -15
- package/dist/assets/App-EWxVyd71.js +1148 -0
- package/dist/assets/BootError-8gklkOEj.js +1 -0
- package/dist/assets/CodeEditor-152n3d6e.js +2 -0
- package/dist/assets/RawJsonEditor-CkDLPhsI.js +1 -0
- package/dist/assets/boot-CGsYMAQp.js +2 -0
- package/dist/assets/{freemarker2-Dc63E2SW.js → freemarker2-BQAfMb99.js} +2 -2
- package/dist/assets/handlebars-Brdeuzw5.js +1 -0
- package/dist/assets/html-BjYaOi8p.js +1 -0
- package/dist/assets/index-4oDdZ-Yj.js +3 -0
- package/dist/assets/index-BLGbxQaR.js +104 -0
- package/dist/assets/index-BLqONGUt.js +68 -0
- package/dist/assets/index-BgGwGI4q.js +4709 -0
- package/dist/assets/index-CFDCSwb2.js +1 -0
- package/dist/assets/index-CSZOipZ8.js +10 -0
- package/dist/assets/index-Crsub4wy.js +5028 -0
- package/dist/assets/index-DIgOR-JL.css +1 -0
- package/dist/assets/index-DzAasWzr.css +1 -0
- package/dist/assets/index-Uhx9KLCG.js +11 -0
- package/dist/assets/javascript-CTKhG5-q.js +1 -0
- package/dist/assets/{jsonMode-CemXzEMj.js → jsonMode-D3mpWOQQ.js} +3 -3
- package/dist/assets/liquid-CSjp0MaB.js +1 -0
- package/dist/assets/load-DQmdgTkV.js +11 -0
- package/dist/assets/mdx-Bc5quPBk.js +1 -0
- package/dist/assets/{monaco-setup-wGOKhcUo.js → monaco-setup-BytXwQeQ.js} +34 -34
- package/dist/assets/python-DuAl67Eo.js +1 -0
- package/dist/assets/razor-BbXkrvwj.js +1 -0
- package/dist/assets/reformer-ui-kit-11-DNMUPUyd.js +1 -0
- package/dist/assets/reformer-ui-kit-1LAJcT8b.js +1 -0
- package/dist/assets/typescript-0wJoIq5V.js +1 -0
- package/dist/assets/typescript-Bjd-gS8c.js +433 -0
- package/dist/assets/useFormField-BLbvmpme-4e6SCAMU.js +1 -0
- package/dist/assets/xml-bMgZPd2t.js +1 -0
- package/dist/assets/yaml-yApvoFdI.js +1 -0
- package/dist/index.html +2 -2
- package/package.json +9 -2
- package/runtime-config.schema.json +82 -0
- package/dist/assets/CodeEditor-C27y3JNW.js +0 -2
- package/dist/assets/RawJsonEditor-BS5r3i2P.js +0 -1
- package/dist/assets/handlebars-CN4ggFMI.js +0 -1
- package/dist/assets/html-BJ_0FZ4N.js +0 -1
- package/dist/assets/index-BVjlK0OP.js +0 -817
- package/dist/assets/index-DYDLCcUp.css +0 -1
- package/dist/assets/javascript-ChLWbrPC.js +0 -1
- package/dist/assets/liquid-DT0mZNaG.js +0 -1
- package/dist/assets/mdx-D1JAVlmB.js +0 -1
- package/dist/assets/python-D3Fng_k0.js +0 -1
- package/dist/assets/razor-BXCxNJBR.js +0 -1
- package/dist/assets/typescript-BtZpkOs3.js +0 -1
- package/dist/assets/xml-CeLodlPg.js +0 -1
- package/dist/assets/yaml-DSrp7EaV.js +0 -1
package/README.md
CHANGED
|
@@ -19,6 +19,9 @@ npm i -D @reformer/builder
|
|
|
19
19
|
npx reformer-builder # поднимет локальный сервер и откроет браузер
|
|
20
20
|
npx reformer-builder --port 5000
|
|
21
21
|
npx reformer-builder --no-open # не открывать браузер автоматически
|
|
22
|
+
|
|
23
|
+
# локальная кастомизация клиента: свой каталог компонентов и/или конфиг билдера
|
|
24
|
+
npx reformer-builder --catalog ./component-catalog.json --config ./reformer-builder.config.json
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
Или добавьте скрипт в свой `package.json`:
|
|
@@ -37,13 +40,18 @@ npm run builder
|
|
|
37
40
|
|
|
38
41
|
### Опции CLI
|
|
39
42
|
|
|
40
|
-
| Опция
|
|
41
|
-
|
|
|
42
|
-
| `-p, --port <n>`
|
|
43
|
-
| `--host <h>`
|
|
44
|
-
| `--no-open`
|
|
45
|
-
|
|
|
46
|
-
|
|
|
43
|
+
| Опция | По умолчанию | Описание |
|
|
44
|
+
| ------------------ | ------------ | --------------------------------------------------- |
|
|
45
|
+
| `-p, --port <n>` | `4321` | Порт (если занят — берётся следующий свободный) |
|
|
46
|
+
| `--host <h>` | `127.0.0.1` | Хост |
|
|
47
|
+
| `--no-open` | — | Не открывать браузер автоматически |
|
|
48
|
+
| `--catalog <path>` | — | JSON-каталог компонентов (замещает вшитый) |
|
|
49
|
+
| `--config <path>` | — | JSON-конфиг билдера (палитра, UI, брендинг, проект) |
|
|
50
|
+
| `-h, --help` | — | Справка |
|
|
51
|
+
| `-v, --version` | — | Версия |
|
|
52
|
+
|
|
53
|
+
Без `--catalog`/`--config` билдер пытается подхватить `component-catalog.json` и
|
|
54
|
+
`reformer-builder.config.json` из текущей папки; если их нет — работает на вшитых дефолтах.
|
|
47
55
|
|
|
48
56
|
## Режимы работы
|
|
49
57
|
|
|
@@ -53,6 +61,36 @@ npm run builder
|
|
|
53
61
|
Arc, Brave).
|
|
54
62
|
- **Mode A** — создать новую схему с нуля и выгрузить/скопировать JSON или сгенерированный код.
|
|
55
63
|
|
|
64
|
+
## Шаблоны форм
|
|
65
|
+
|
|
66
|
+
Новая форма создаётся из **шаблона** — набора файлов с плейсхолдерами вместо имени формы. Панель
|
|
67
|
+
«Шаблоны» в левом сайдбаре показывает все доступные шаблоны, генерация — ПКМ в дереве файлов →
|
|
68
|
+
«Сгенерировать → Выбрать шаблон…» (имя формы, шаблон и какие его файлы создать).
|
|
69
|
+
|
|
70
|
+
Три источника шаблонов:
|
|
71
|
+
|
|
72
|
+
- **встроенные** — «Простая форма» (одна страница полей) и «Пошаговая форма» (визард на два шага с
|
|
73
|
+
адаптером к ui-kit `FormWizard`). Оба дают полный набор: модель, схема, валидация, поведение формы
|
|
74
|
+
и UI, реестр, страница-компонент — она тянет остальные файлы, без них не соберётся;
|
|
75
|
+
- **проект** — папки в `.reformer/templates/<slug>/` (каталог переопределяется
|
|
76
|
+
`project.templatesDir`). Обычные файлы + `template.json` с именем и описанием: шаблон едет в git,
|
|
77
|
+
правится руками в IDE и работает у всей команды;
|
|
78
|
+
- **локальный** — сохранённый в IndexedDB браузера, доступен и без открытого проекта.
|
|
79
|
+
|
|
80
|
+
Свой шаблон делается из готовой формы: выделить её файлы в дереве (⌘/Ctrl+клик — добавить к выбору,
|
|
81
|
+
⇧+клик — диапазон) → ПКМ → «Создать шаблон…». Имя формы-источника (базовое имя) заменяется
|
|
82
|
+
плейсхолдерами во всех написаниях, чтобы при генерации подставлялось имя новой формы:
|
|
83
|
+
|
|
84
|
+
| Плейсхолдер | Форма `user-profile` |
|
|
85
|
+
| --------------- | -------------------- |
|
|
86
|
+
| `__FormName__` | `UserProfile` |
|
|
87
|
+
| `__formName__` | `userProfile` |
|
|
88
|
+
| `__form-name__` | `user-profile` |
|
|
89
|
+
| `__form_name__` | `user_profile` |
|
|
90
|
+
|
|
91
|
+
Плейсхолдеры работают и в именах файлов (`__form-name__.css`), поэтому шаблон переживает любое имя
|
|
92
|
+
новой формы. Бинарные файлы (картинки, шрифты) в шаблон не попадают.
|
|
93
|
+
|
|
56
94
|
## Каталог компонентов (`@reformer/ui-kit/catalog`)
|
|
57
95
|
|
|
58
96
|
Палитра билдера строится из **каталога компонентов** `@reformer/ui-kit` — это
|
|
@@ -63,7 +101,69 @@ npm run builder
|
|
|
63
101
|
|
|
64
102
|
Каталог **вшивается в бандл на этапе сборки** билдера (Vite инлайнит импорт
|
|
65
103
|
`@reformer/ui-kit/catalog`), поэтому опубликованный `@reformer/builder` самодостаточен и
|
|
66
|
-
показывает набор компонентов той версии `@reformer/ui-kit`, с которой был собран.
|
|
104
|
+
показывает набор компонентов той версии `@reformer/ui-kit`, с которой был собран. Этот вшитый
|
|
105
|
+
каталог можно **переопределить при локальном старте** флагом `--catalog` (см. ниже) — без
|
|
106
|
+
пересборки билдера.
|
|
107
|
+
|
|
108
|
+
`@reformer/ui-kit` — не единственный вариант: билдер держит несколько китов и версий, активный
|
|
109
|
+
выбирается в переключателе тулбара. Как подключить свой кит — другую версию, форк или стороннюю
|
|
110
|
+
дизайн-систему — см. [docs/instructions/add-ui-kit.md](docs/instructions/add-ui-kit.md).
|
|
111
|
+
|
|
112
|
+
## Локальная кастомизация (`--catalog` / `--config`)
|
|
113
|
+
|
|
114
|
+
Клиент может передать при локальном старте два JSON-файла — они читаются launcher'ом и
|
|
115
|
+
применяются в браузере на бутстрапе. Оба **опциональны**: если файла нет — берутся встроенные
|
|
116
|
+
дефолты (билдер стартует и работает как обычно). Если переданный файл невалиден — билдер
|
|
117
|
+
показывает экран с ошибкой валидации (не запускается с частичной конфигурацией).
|
|
118
|
+
|
|
119
|
+
- **`--catalog <path>`** — JSON-каталог компонентов по тому же контракту, что
|
|
120
|
+
`@reformer/ui-kit/catalog` (`{ version, components: [{ name, role, propsSchema, category? }] }`).
|
|
121
|
+
Замещает вшитый каталог: палитра и инспектор строятся из него.
|
|
122
|
+
- **`--config <path>`** — конфиг поведения самого билдера. Все секции опциональны:
|
|
123
|
+
|
|
124
|
+
```jsonc
|
|
125
|
+
{
|
|
126
|
+
"$schema": "./node_modules/@reformer/builder/runtime-config.schema.json",
|
|
127
|
+
"version": "1.0",
|
|
128
|
+
// Брендинг оболочки
|
|
129
|
+
"branding": {
|
|
130
|
+
"productName": "Acme Forms",
|
|
131
|
+
"title": "Acme Builder",
|
|
132
|
+
"logoUrl": "data:image/svg+xml;...",
|
|
133
|
+
},
|
|
134
|
+
// Палитра: категории, порядок разделов, свёрнутые по умолчанию, глифы-бейджи
|
|
135
|
+
"palette": {
|
|
136
|
+
"categoryByName": { "AcmeRating": "Поля ввода" },
|
|
137
|
+
"order": ["Поля ввода", "Выбор и переключатели", "Контейнеры", "Действия"],
|
|
138
|
+
"collapsedByDefault": ["HTML", "Типографика"],
|
|
139
|
+
"glyphs": { "AcmeRating": "Ar" },
|
|
140
|
+
},
|
|
141
|
+
// Доступные компоненты: whitelist/blacklist + тоглы синтетических записей билдера
|
|
142
|
+
"components": {
|
|
143
|
+
"exclude": ["Chart", "Carousel"],
|
|
144
|
+
"synthetic": {
|
|
145
|
+
"html": true,
|
|
146
|
+
"htmlTags": ["div", "section", "fieldset"],
|
|
147
|
+
"formArray": true,
|
|
148
|
+
"wizard": false,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
// Дефолты интерфейса при старте
|
|
152
|
+
"ui": { "theme": "light", "leftPanel": "palette", "rightOpen": true, "preview": "wire" },
|
|
153
|
+
// Работа с проектом (Mode B discovery), каталог шаблонов + стартовая схема Mode A (null — пустая форма)
|
|
154
|
+
"project": {
|
|
155
|
+
"ignoreDirs": ["fixtures"],
|
|
156
|
+
"templatesDir": ".reformer/templates",
|
|
157
|
+
"seedSchema": null,
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Семантика слияния: карты (`palette.categoryByName`, `palette.glyphs`) и списки безопасных
|
|
163
|
+
дефолтов (`project.ignoreDirs`, `project.skipFiles`, `project.formSchemaMarkers`) **дополняют**
|
|
164
|
+
встроенные; `palette.order`, `palette.collapsedByDefault`, `components.*`, `ui.*`, `branding.*` —
|
|
165
|
+
**замещают** дефолт, если заданы. JSON Schema контракта конфига публикуется в пакете как
|
|
166
|
+
`@reformer/builder/runtime-config.schema.json` (для автодополнения в редакторе через `$schema`).
|
|
67
167
|
|
|
68
168
|
> **Требование к упаковке `@reformer/ui-kit`:** чтобы subpath `@reformer/ui-kit/catalog`
|
|
69
169
|
> резолвился у внешних потребителей, `component-catalog.json` обязан входить в поле `files`
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типы для тестируемых экспортов launcher'а (`reformer-builder.mjs` — zero-dependency JS без
|
|
3
|
+
* деклараций). Позволяет `.ts`-тестам импортировать хелперы без implicit-any (TS7016).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface LauncherOpts {
|
|
7
|
+
port: number;
|
|
8
|
+
host: string;
|
|
9
|
+
open: boolean;
|
|
10
|
+
help: boolean;
|
|
11
|
+
version: boolean;
|
|
12
|
+
/** Путь к каталогу компонентов (`--catalog`) или `null` (авто-детект в cwd). */
|
|
13
|
+
catalog: string | null;
|
|
14
|
+
/** Путь к конфигу билдера (`--config`) или `null` (авто-детект в cwd). */
|
|
15
|
+
config: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RuntimeBundleResult {
|
|
19
|
+
payload: { catalog: unknown; config: unknown };
|
|
20
|
+
sources: { catalog: string | null; config: string | null };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** URL раздачи клиентского bundle. */
|
|
24
|
+
export const RUNTIME_BUNDLE_URL: string;
|
|
25
|
+
|
|
26
|
+
export function parseArgs(argv: string[]): LauncherOpts;
|
|
27
|
+
|
|
28
|
+
export function loadRuntimeBundle(
|
|
29
|
+
opts: { catalog: string | null; config: string | null },
|
|
30
|
+
cwd: string
|
|
31
|
+
): Promise<RuntimeBundleResult>;
|
|
32
|
+
|
|
33
|
+
export function createRequestHandler(
|
|
34
|
+
indexHtmlPath: string,
|
|
35
|
+
runtimeBundleBody: Buffer
|
|
36
|
+
): (req: unknown, res: unknown) => Promise<void>;
|
package/bin/reformer-builder.mjs
CHANGED
|
@@ -7,15 +7,27 @@
|
|
|
7
7
|
* поднимает `dist/` на localhost и открывает браузер. Серверной логики у билдера нет — вся работа с
|
|
8
8
|
* файлами проекта идёт в браузере через File System Access API (нужен Chromium-браузер).
|
|
9
9
|
*
|
|
10
|
+
* Клиент может передать 2 JSON-файла для локальной кастомизации: каталог компонентов (--catalog)
|
|
11
|
+
* и конфиг билдера (--config). Launcher читает их с диска и отдаёт SPA по /__reformer-builder/
|
|
12
|
+
* runtime.json; без флагов пытается авто-подхватить одноимённые файлы из cwd. Отсутствие файлов —
|
|
13
|
+
* билдер работает на вшитых дефолтах.
|
|
14
|
+
*
|
|
10
15
|
* Использование:
|
|
11
|
-
* npx reformer-builder [--port <n>] [--host <h>] [--no-open]
|
|
16
|
+
* npx reformer-builder [--port <n>] [--host <h>] [--no-open] [--catalog <path>] [--config <path>]
|
|
12
17
|
*/
|
|
13
18
|
|
|
14
19
|
import { createServer } from 'node:http';
|
|
15
20
|
import { readFile, stat } from 'node:fs/promises';
|
|
16
21
|
import { spawn } from 'node:child_process';
|
|
17
22
|
import { fileURLToPath } from 'node:url';
|
|
18
|
-
import { extname, join, normalize, sep } from 'node:path';
|
|
23
|
+
import { extname, join, normalize, resolve, sep } from 'node:path';
|
|
24
|
+
|
|
25
|
+
/** URL, по которому SPA забирает клиентский bundle (совпадает с RUNTIME_BUNDLE_PATH в src/config/load). */
|
|
26
|
+
export const RUNTIME_BUNDLE_URL = '/__reformer-builder/runtime.json';
|
|
27
|
+
|
|
28
|
+
/** Имена файлов для авто-детекта в cwd, если флаги `--catalog`/`--config` не заданы. */
|
|
29
|
+
const DEFAULT_CATALOG_FILE = 'component-catalog.json';
|
|
30
|
+
const DEFAULT_CONFIG_FILE = 'reformer-builder.config.json';
|
|
19
31
|
|
|
20
32
|
// Без хвостового разделителя: иначе `distDir + sep` даёт двойной слэш и проверка
|
|
21
33
|
// границы каталога в resolveFsPath() никогда не совпадает (все запросы → 400).
|
|
@@ -51,8 +63,64 @@ async function readVersion() {
|
|
|
51
63
|
}
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Прочитать и распарсить клиентский JSON-файл. Явно переданный (`--catalog`/`--config`) файл
|
|
68
|
+
* обязателен — при ошибке чтения/парсинга завершаемся с сообщением. Авто-детект: отсутствие файла
|
|
69
|
+
* (ENOENT) — тихий пропуск (fallback на вшитые дефолты); присутствует, но битый JSON — ошибка
|
|
70
|
+
* (файл явно предназначен к использованию).
|
|
71
|
+
*/
|
|
72
|
+
async function readRuntimeFile(path, { explicit, label }) {
|
|
73
|
+
let text;
|
|
74
|
+
try {
|
|
75
|
+
text = await readFile(path, 'utf8');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (explicit) {
|
|
78
|
+
console.error(`reformer-builder: не удалось прочитать ${label} "${path}": ${err.message}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
return null; // авто-детект: файла нет — работаем на вшитых дефолтах
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(text);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error(`reformer-builder: невалидный JSON в ${label} "${path}": ${err.message}`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Собрать runtime-bundle для SPA из файлов клиента. Пути берутся из флагов, иначе — авто-детект
|
|
93
|
+
* одноимённых файлов в cwd. Структурную валидацию делает SPA (реюз validateCatalog + AJV конфига);
|
|
94
|
+
* здесь ловим только отсутствие/JSON-синтаксис.
|
|
95
|
+
*/
|
|
96
|
+
export async function loadRuntimeBundle(opts, cwd) {
|
|
97
|
+
const catalogPath = resolve(cwd, opts.catalog ?? DEFAULT_CATALOG_FILE);
|
|
98
|
+
const configPath = resolve(cwd, opts.config ?? DEFAULT_CONFIG_FILE);
|
|
99
|
+
const catalog = await readRuntimeFile(catalogPath, {
|
|
100
|
+
explicit: Boolean(opts.catalog),
|
|
101
|
+
label: 'каталог (--catalog)',
|
|
102
|
+
});
|
|
103
|
+
const config = await readRuntimeFile(configPath, {
|
|
104
|
+
explicit: Boolean(opts.config),
|
|
105
|
+
label: 'конфиг (--config)',
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
payload: { catalog: catalog ?? null, config: config ?? null },
|
|
109
|
+
sources: { catalog: catalog ? catalogPath : null, config: config ? configPath : null },
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function parseArgs(argv) {
|
|
114
|
+
const opts = {
|
|
115
|
+
port: 4321,
|
|
116
|
+
host: '127.0.0.1',
|
|
117
|
+
open: true,
|
|
118
|
+
help: false,
|
|
119
|
+
version: false,
|
|
120
|
+
// Явно переданные клиентом пути (null — не задан, будет авто-детект в cwd).
|
|
121
|
+
catalog: null,
|
|
122
|
+
config: null,
|
|
123
|
+
};
|
|
56
124
|
for (let i = 0; i < argv.length; i++) {
|
|
57
125
|
const a = argv[i];
|
|
58
126
|
if (a === '--help' || a === '-h') opts.help = true;
|
|
@@ -63,6 +131,10 @@ function parseArgs(argv) {
|
|
|
63
131
|
else if (a.startsWith('--port=')) opts.port = Number(a.slice('--port='.length));
|
|
64
132
|
else if (a === '--host') opts.host = String(argv[++i]);
|
|
65
133
|
else if (a.startsWith('--host=')) opts.host = a.slice('--host='.length);
|
|
134
|
+
else if (a === '--catalog') opts.catalog = String(argv[++i]);
|
|
135
|
+
else if (a.startsWith('--catalog=')) opts.catalog = a.slice('--catalog='.length);
|
|
136
|
+
else if (a === '--config') opts.config = String(argv[++i]);
|
|
137
|
+
else if (a.startsWith('--config=')) opts.config = a.slice('--config='.length);
|
|
66
138
|
else {
|
|
67
139
|
console.error(`reformer-builder: неизвестный аргумент "${a}" (см. --help)`);
|
|
68
140
|
process.exit(1);
|
|
@@ -82,11 +154,16 @@ function printHelp() {
|
|
|
82
154
|
npx reformer-builder [опции]
|
|
83
155
|
|
|
84
156
|
Опции:
|
|
85
|
-
-p, --port <n>
|
|
86
|
-
--host <h>
|
|
87
|
-
--no-open
|
|
88
|
-
|
|
89
|
-
|
|
157
|
+
-p, --port <n> Порт (по умолчанию 4321; занят — берётся следующий свободный)
|
|
158
|
+
--host <h> Хост (по умолчанию 127.0.0.1)
|
|
159
|
+
--no-open Не открывать браузер автоматически
|
|
160
|
+
--catalog <path> Каталог компонентов (JSON) для палитры/инспектора
|
|
161
|
+
--config <path> Конфиг билдера (JSON): палитра, доступные компоненты, дефолты UI, брендинг
|
|
162
|
+
-h, --help Показать эту справку
|
|
163
|
+
-v, --version Показать версию
|
|
164
|
+
|
|
165
|
+
Без --catalog/--config билдер пытается подхватить component-catalog.json и
|
|
166
|
+
reformer-builder.config.json из текущей папки; если их нет — работает на вшитых дефолтах.
|
|
90
167
|
|
|
91
168
|
Примечание: режим «открыть папку проекта» (File System Access API) работает только в
|
|
92
169
|
Chromium-браузерах (Chrome/Edge/Arc/Brave).`);
|
|
@@ -142,7 +219,7 @@ async function sendFile(res, filePath, statusCode = 200) {
|
|
|
142
219
|
res.end(body);
|
|
143
220
|
}
|
|
144
221
|
|
|
145
|
-
function createRequestHandler(indexHtmlPath) {
|
|
222
|
+
export function createRequestHandler(indexHtmlPath, runtimeBundleBody) {
|
|
146
223
|
return async (req, res) => {
|
|
147
224
|
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
148
225
|
res.writeHead(405, { Allow: 'GET, HEAD' });
|
|
@@ -150,6 +227,16 @@ function createRequestHandler(indexHtmlPath) {
|
|
|
150
227
|
return;
|
|
151
228
|
}
|
|
152
229
|
const pathname = (req.url || '/').split('?')[0].split('#')[0];
|
|
230
|
+
// Клиентский runtime-bundle (каталог/конфиг из файлов) — отдаём ДО резолва static/dist.
|
|
231
|
+
if (pathname === RUNTIME_BUNDLE_URL) {
|
|
232
|
+
res.writeHead(200, {
|
|
233
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
234
|
+
'Content-Length': runtimeBundleBody.length,
|
|
235
|
+
'Cache-Control': 'no-cache',
|
|
236
|
+
});
|
|
237
|
+
res.end(req.method === 'HEAD' ? undefined : runtimeBundleBody);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
153
240
|
const target = resolveFsPath(pathname === '/' ? '/index.html' : pathname);
|
|
154
241
|
if (target === null) {
|
|
155
242
|
res.writeHead(400);
|
|
@@ -233,7 +320,12 @@ async function main() {
|
|
|
233
320
|
process.exit(1);
|
|
234
321
|
}
|
|
235
322
|
|
|
236
|
-
|
|
323
|
+
// Клиентский конфиг/каталог из файлов (флаги или авто-детект в cwd). Отдаётся SPA по
|
|
324
|
+
// RUNTIME_BUNDLE_URL; отсутствие файлов ⇒ пустой bundle ⇒ билдер на вшитых дефолтах.
|
|
325
|
+
const runtime = await loadRuntimeBundle(opts, process.cwd());
|
|
326
|
+
const runtimeBundleBody = Buffer.from(JSON.stringify(runtime.payload));
|
|
327
|
+
|
|
328
|
+
const server = createServer(createRequestHandler(indexHtmlPath, runtimeBundleBody));
|
|
237
329
|
let port;
|
|
238
330
|
try {
|
|
239
331
|
port = await listenWithFallback(server, opts.host, opts.port);
|
|
@@ -246,6 +338,8 @@ async function main() {
|
|
|
246
338
|
const url = `http://${displayHost}:${port}/`;
|
|
247
339
|
console.log(`\n reformer-builder v${await readVersion()}`);
|
|
248
340
|
console.log(` Локальный сервер: ${url}`);
|
|
341
|
+
if (runtime.sources.catalog) console.log(` Каталог из файла: ${runtime.sources.catalog}`);
|
|
342
|
+
if (runtime.sources.config) console.log(` Конфиг из файла: ${runtime.sources.config}`);
|
|
249
343
|
console.log(` Режим «открыть папку проекта» требует Chromium-браузер (File System Access API).`);
|
|
250
344
|
console.log(` Остановить: Ctrl+C\n`);
|
|
251
345
|
|
|
@@ -260,7 +354,10 @@ async function main() {
|
|
|
260
354
|
process.on('SIGTERM', shutdown);
|
|
261
355
|
}
|
|
262
356
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
357
|
+
// Не запускаем сервер при импорте из тестов (юнит-тесты дёргают экспортированные хелперы).
|
|
358
|
+
if (process.env.REFORMER_BUILDER_TEST !== '1') {
|
|
359
|
+
main().catch((err) => {
|
|
360
|
+
console.error(`reformer-builder: ${err?.stack || err}`);
|
|
361
|
+
process.exit(1);
|
|
362
|
+
});
|
|
363
|
+
}
|