@reformer/builder-plugin-cli 1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -0
  3. package/dist/cli.d.ts +26 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +207 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/build.d.ts +57 -0
  8. package/dist/commands/build.d.ts.map +1 -0
  9. package/dist/commands/build.js +261 -0
  10. package/dist/commands/build.js.map +1 -0
  11. package/dist/commands/create.d.ts +31 -0
  12. package/dist/commands/create.d.ts.map +1 -0
  13. package/dist/commands/create.js +43 -0
  14. package/dist/commands/create.js.map +1 -0
  15. package/dist/commands/dev.d.ts +35 -0
  16. package/dist/commands/dev.d.ts.map +1 -0
  17. package/dist/commands/dev.js +79 -0
  18. package/dist/commands/dev.js.map +1 -0
  19. package/dist/commands/dry-run.d.ts +38 -0
  20. package/dist/commands/dry-run.d.ts.map +1 -0
  21. package/dist/commands/dry-run.js +148 -0
  22. package/dist/commands/dry-run.js.map +1 -0
  23. package/dist/commands/findings.d.ts +31 -0
  24. package/dist/commands/findings.d.ts.map +1 -0
  25. package/dist/commands/findings.js +14 -0
  26. package/dist/commands/findings.js.map +1 -0
  27. package/dist/commands/pack.d.ts +33 -0
  28. package/dist/commands/pack.d.ts.map +1 -0
  29. package/dist/commands/pack.js +78 -0
  30. package/dist/commands/pack.js.map +1 -0
  31. package/dist/commands/template.d.ts +34 -0
  32. package/dist/commands/template.d.ts.map +1 -0
  33. package/dist/commands/template.js +109 -0
  34. package/dist/commands/template.js.map +1 -0
  35. package/dist/commands/validate.d.ts +43 -0
  36. package/dist/commands/validate.d.ts.map +1 -0
  37. package/dist/commands/validate.js +128 -0
  38. package/dist/commands/validate.js.map +1 -0
  39. package/dist/index.d.ts +12 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +11 -0
  42. package/dist/index.js.map +1 -0
  43. package/package.json +58 -0
@@ -0,0 +1,261 @@
1
+ /**
2
+ * `reformer-plugin build`: исходники плагина → каталог, который оболочка грузит как есть.
3
+ *
4
+ * ## Что получается
5
+ *
6
+ * `manifest.json` (тот же, с `main: "main.js"` и `styles.file: "styles.css"`), один `main.js`,
7
+ * `styles.css`, если стили объявлены, и словари по тем же путям, что в исходниках.
8
+ *
9
+ * - **`main.js` — CommonJS.** Линковщик оболочки исполняет модули как CommonJS, и готовый JS
10
+ * идёт у него без транспиляции (`shell/platform/modules/loader`): собери мы ESM, оболочке
11
+ * пришлось бы будить транспилятор ради уже собранного файла.
12
+ * - **Модули рантайма — внешние, ровно по списку** `PLUGIN_RUNTIME_MODULES`. Вложенная копия
13
+ * React или ядра форм — второй экземпляр и тихая поломка. Импорт `@reformer/*` или
14
+ * `@builder/*`, которого в списке нет, — ОТКАЗ сборки: вложить его нельзя (тот же второй
15
+ * экземпляр), а оставить внешним — значит плагин, падающий на спецификаторе при загрузке.
16
+ * - **Пакеты стеков — вкладываются** (`PLUGIN_BUNDLED_PACKAGES`): чистые функции без синглтонов,
17
+ * которых оболочка не подставляет, потому что стека не знает. Вместе с ними вкладывается то,
18
+ * что они сами берут из `@reformer/*` вне списка рантайма (глубокие импорты `@reformer/mcp`
19
+ * у стека ReFormer), — это их внутренность, а не выбор автора плагина. Модули рантайма и из
20
+ * пакета стека остаются внешними.
21
+ * - **CSS из кода — отказ.** Стили плагина объявляются в манифесте (`styles`), и только тогда
22
+ * оболочка их изолирует. Импорт `.css` из кода дал бы таблицу, о которой манифест молчит.
23
+ *
24
+ * ## Что проверяется на выходе
25
+ *
26
+ * Собранный каталог проходит то, что пройдёт у оболочки: разбор манифеста поставки `project`
27
+ * (с каталогом, названным по `id`), потолок числа файлов кода и «сухую» активацию
28
+ * (`./dry-run`) — узнавание плагина в экспортах, `id` кода и `provides`.
29
+ *
30
+ * ## Каталог вывода не чужой
31
+ *
32
+ * Перед записью каталог очищается, но только если он пуст или в нём лежит сборка ЭТОГО ЖЕ
33
+ * плагина (манифест с тем же `id`). Иначе — отказ: `--out ~/project` не должен стирать проект.
34
+ *
35
+ * @module @reformer/builder-plugin-cli/commands/build
36
+ */
37
+ import { existsSync, readFileSync } from 'node:fs';
38
+ import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
39
+ import { dirname, join, resolve } from 'node:path';
40
+ import { isBundledPluginModule, isPluginCodeFile, parsePluginManifest, PLUGIN_FILE_LIMIT, PLUGIN_MANIFEST_FILE, PLUGIN_RUNTIME_MODULES, } from '@reformer/builder-plugin-api/tooling';
41
+ import * as esbuild from 'esbuild';
42
+ import { dryActivate } from './dry-run.js';
43
+ import { validatePlugin } from './validate.js';
44
+ /** Имена, под которыми сборка кладёт код и стили. */
45
+ const BUILT_MAIN = 'main.js';
46
+ const BUILT_STYLES = 'styles.css';
47
+ const fail = (...findings) => ({ ok: false, findings });
48
+ /** Спецификатор пакета — не путь: ни относительный, ни абсолютный (в том числе `D:\`). */
49
+ function isBareSpecifier(path) {
50
+ return !path.startsWith('.') && !path.startsWith('/') && !/^[a-zA-Z]:[\\/]/.test(path);
51
+ }
52
+ /**
53
+ * Имя пакета, которому принадлежит файл, — по ближайшему `package.json` вверх по дереву.
54
+ *
55
+ * По ИМЕНИ, а не по пути: пакет стека приходит и из `node_modules`, и ссылкой рабочей области
56
+ * (`packages/…`), и путь у них разный, а имя одно.
57
+ */
58
+ const ownerCache = new Map();
59
+ function ownerPackageOf(file) {
60
+ let dir = dirname(file);
61
+ const visited = [];
62
+ for (;;) {
63
+ const cached = ownerCache.get(dir);
64
+ if (cached !== undefined) {
65
+ for (const seen of visited)
66
+ ownerCache.set(seen, cached);
67
+ return cached;
68
+ }
69
+ visited.push(dir);
70
+ const manifest = join(dir, 'package.json');
71
+ if (existsSync(manifest)) {
72
+ let name = null;
73
+ try {
74
+ const value = JSON.parse(readFileSync(manifest, 'utf8')).name;
75
+ name = typeof value === 'string' ? value : null;
76
+ }
77
+ catch {
78
+ name = null;
79
+ }
80
+ for (const seen of visited)
81
+ ownerCache.set(seen, name);
82
+ return name;
83
+ }
84
+ const parent = dirname(dir);
85
+ if (parent === dir) {
86
+ for (const seen of visited)
87
+ ownerCache.set(seen, null);
88
+ return null;
89
+ }
90
+ dir = parent;
91
+ }
92
+ }
93
+ /**
94
+ * Модули рантайма — внешние; пакеты стеков и их внутренности — вкладываются; прочее
95
+ * `@reformer/*` — отказ с объяснением.
96
+ */
97
+ const runtimeModules = {
98
+ name: 'reformer-runtime-modules',
99
+ setup(build) {
100
+ build.onResolve({ filter: /.*/ }, (args) => {
101
+ if (args.kind === 'entry-point' || !isBareSpecifier(args.path))
102
+ return undefined;
103
+ if (PLUGIN_RUNTIME_MODULES.includes(args.path))
104
+ return { path: args.path, external: true };
105
+ if (isBundledPluginModule(args.path))
106
+ return undefined;
107
+ const owner = args.importer === '' ? null : ownerPackageOf(args.importer);
108
+ if (owner !== null && isBundledPluginModule(owner))
109
+ return undefined;
110
+ if (args.path.startsWith('@reformer/') || args.path.startsWith('@builder/')) {
111
+ return {
112
+ errors: [
113
+ {
114
+ text: `модуль «${args.path}» оболочка не подставляет, а вкладывать его в сборку нельзя — ` +
115
+ 'это был бы второй экземпляр пакета',
116
+ detail: 'module-unavailable',
117
+ },
118
+ ],
119
+ };
120
+ }
121
+ return undefined;
122
+ });
123
+ },
124
+ };
125
+ async function prepareOutDir(outDir, id) {
126
+ let entries;
127
+ try {
128
+ entries = await readdir(outDir);
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ if (entries.length > 0) {
134
+ let previousId;
135
+ try {
136
+ const text = await readFile(join(outDir, PLUGIN_MANIFEST_FILE), 'utf8');
137
+ previousId = JSON.parse(text).id;
138
+ }
139
+ catch {
140
+ previousId = undefined;
141
+ }
142
+ if (previousId !== id) {
143
+ return {
144
+ code: 'output-not-ours',
145
+ message: `каталог вывода «${outDir}» не пуст и не содержит сборку «${id}»: ` +
146
+ 'очищать чужой каталог сборка не станет',
147
+ };
148
+ }
149
+ }
150
+ await rm(outDir, { recursive: true, force: true });
151
+ return undefined;
152
+ }
153
+ function buildFailure(error) {
154
+ const messages = error.errors;
155
+ if (messages === undefined || messages.length === 0) {
156
+ return [
157
+ { code: 'build-failed', message: error instanceof Error ? error.message : String(error) },
158
+ ];
159
+ }
160
+ return messages.map((message) => ({
161
+ code: message.detail === 'module-unavailable' ? 'module-unavailable' : 'build-failed',
162
+ message: message.text,
163
+ ...(message.location === null ? {} : { file: message.location.file }),
164
+ }));
165
+ }
166
+ export async function buildPlugin(options) {
167
+ const dir = resolve(options.dir);
168
+ const outDir = resolve(dir, options.outDir ?? 'dist');
169
+ const validated = await validatePlugin(dir);
170
+ if (!validated.ok)
171
+ return fail(...validated.findings);
172
+ const { manifest } = validated;
173
+ let code;
174
+ try {
175
+ const result = await esbuild.build({
176
+ absWorkingDir: dir,
177
+ entryPoints: [join(dir, manifest.main)],
178
+ outfile: join(outDir, BUILT_MAIN),
179
+ bundle: true,
180
+ write: false,
181
+ format: 'cjs',
182
+ platform: 'browser',
183
+ target: 'es2020',
184
+ jsx: 'automatic',
185
+ logLevel: 'silent',
186
+ plugins: [runtimeModules],
187
+ });
188
+ const css = result.outputFiles.find((file) => file.path.endsWith('.css'));
189
+ if (css !== undefined) {
190
+ return fail({
191
+ code: 'css-from-code',
192
+ message: 'код импортирует CSS. Стили плагина объявляются в манифесте полем «styles» — ' +
193
+ 'только такую таблицу оболочка изолирует',
194
+ });
195
+ }
196
+ const js = result.outputFiles.find((file) => file.path.endsWith('.js'));
197
+ code = js?.text ?? '';
198
+ }
199
+ catch (error) {
200
+ return fail(...buildFailure(error));
201
+ }
202
+ let styles;
203
+ if (manifest.styles !== undefined) {
204
+ try {
205
+ const result = await esbuild.build({
206
+ absWorkingDir: dir,
207
+ entryPoints: [join(dir, manifest.styles.file)],
208
+ outfile: join(outDir, BUILT_STYLES),
209
+ bundle: true,
210
+ write: false,
211
+ logLevel: 'silent',
212
+ });
213
+ styles = result.outputFiles.find((file) => file.path.endsWith('.css'))?.text ?? '';
214
+ }
215
+ catch (error) {
216
+ return fail(...buildFailure(error));
217
+ }
218
+ }
219
+ // Манифест вывода — ИСХОДНЫЙ JSON с двумя заменами, а не сериализация разобранного:
220
+ // разбор нормализует и подставляет умолчания, и автор увидел бы в сборке не свой файл.
221
+ const raw = JSON.parse(await readFile(join(dir, PLUGIN_MANIFEST_FILE), 'utf8'));
222
+ raw.main = BUILT_MAIN;
223
+ if (manifest.styles !== undefined) {
224
+ raw.styles = { ...raw.styles, file: BUILT_STYLES };
225
+ }
226
+ const manifestText = `${JSON.stringify(raw, null, 2)}\n`;
227
+ const files = new Map([
228
+ [PLUGIN_MANIFEST_FILE, manifestText],
229
+ [BUILT_MAIN, code],
230
+ ]);
231
+ if (styles !== undefined)
232
+ files.set(BUILT_STYLES, styles);
233
+ const messages = manifest.contributes?.messages ?? {};
234
+ for (const file of Object.values(messages)) {
235
+ files.set(file, await readFile(join(dir, file), 'utf8'));
236
+ }
237
+ // Проверки выхода — ДО записи: не прошедшая их сборка не должна затирать прошлую рабочую.
238
+ const shipped = parsePluginManifest(manifestText, { kind: 'project', dir: manifest.id });
239
+ if (!shipped.ok)
240
+ return fail(shipped.problem);
241
+ const codeFiles = [...files.keys()].filter(isPluginCodeFile).length;
242
+ if (codeFiles > PLUGIN_FILE_LIMIT) {
243
+ return fail({
244
+ code: 'too-many-files',
245
+ message: `в сборке ${String(codeFiles)} файлов кода, оболочка читает не больше ${String(PLUGIN_FILE_LIMIT)}`,
246
+ });
247
+ }
248
+ const dryRun = await dryActivate(code, manifest);
249
+ if (dryRun.findings.length > 0)
250
+ return fail(...dryRun.findings);
251
+ const refused = await prepareOutDir(outDir, manifest.id);
252
+ if (refused !== undefined)
253
+ return fail(refused);
254
+ for (const [path, content] of files) {
255
+ const target = join(outDir, path);
256
+ await mkdir(dirname(target), { recursive: true });
257
+ await writeFile(target, content, 'utf8');
258
+ }
259
+ return { ok: true, manifest, outDir, files: [...files.keys()], notices: dryRun.notices };
260
+ }
261
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `reformer-plugin create <каталог>`: новый плагин из шаблона.
3
+ *
4
+ * Идентификатор проверяется РАЗБОРОМ манифеста, а не своим выражением: шаблон собирает манифест
5
+ * и отдаёт его `parsePluginSourceManifest`. Так «годится ли id» решает то же правило, что
6
+ * у оболочки, и CLI не может разрешить идентификатор, который оболочка не примет.
7
+ *
8
+ * В непустой каталог не пишет: перезапись чужих файлов шаблоном — не та ошибка, которую
9
+ * хочется обнаружить после.
10
+ *
11
+ * @module @reformer/builder-plugin-cli/commands/create
12
+ */
13
+ export interface CreateOptions {
14
+ /** Каталог нового плагина. Создаётся, если его нет; обязан быть пустым, если есть. */
15
+ readonly dir: string;
16
+ /** Идентификатор; умолчание — имя каталога. */
17
+ readonly id?: string;
18
+ /** Подпись в списке плагинов; умолчание — идентификатор. */
19
+ readonly name?: string;
20
+ readonly cliVersion: string;
21
+ }
22
+ export type CreateResult = {
23
+ readonly ok: true;
24
+ readonly dir: string;
25
+ readonly files: readonly string[];
26
+ } | {
27
+ readonly ok: false;
28
+ readonly message: string;
29
+ };
30
+ export declare function createPlugin(options: CreateOptions): Promise<CreateResult>;
31
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,MAAM,WAAW,aAAa;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAUrD,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAmBhF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `reformer-plugin create <каталог>`: новый плагин из шаблона.
3
+ *
4
+ * Идентификатор проверяется РАЗБОРОМ манифеста, а не своим выражением: шаблон собирает манифест
5
+ * и отдаёт его `parsePluginSourceManifest`. Так «годится ли id» решает то же правило, что
6
+ * у оболочки, и CLI не может разрешить идентификатор, который оболочка не примет.
7
+ *
8
+ * В непустой каталог не пишет: перезапись чужих файлов шаблоном — не та ошибка, которую
9
+ * хочется обнаружить после.
10
+ *
11
+ * @module @reformer/builder-plugin-cli/commands/create
12
+ */
13
+ import { mkdir, readdir, writeFile } from 'node:fs/promises';
14
+ import { basename, dirname, join, resolve } from 'node:path';
15
+ import { parsePluginSourceManifest } from '@reformer/builder-plugin-api/tooling';
16
+ import { manifestTemplate, pluginTemplate } from './template.js';
17
+ async function isEmptyOrMissing(dir) {
18
+ try {
19
+ return (await readdir(dir)).length === 0;
20
+ }
21
+ catch (error) {
22
+ return error.code === 'ENOENT';
23
+ }
24
+ }
25
+ export async function createPlugin(options) {
26
+ const dir = resolve(options.dir);
27
+ const id = options.id ?? basename(dir);
28
+ const name = options.name ?? id;
29
+ const checked = parsePluginSourceManifest(manifestTemplate({ id, name }));
30
+ if (!checked.ok)
31
+ return { ok: false, message: checked.problem.message };
32
+ if (!(await isEmptyOrMissing(dir))) {
33
+ return { ok: false, message: `каталог «${dir}» не пуст: шаблон не перезаписывает файлы` };
34
+ }
35
+ const files = pluginTemplate({ id, name, cliVersion: options.cliVersion });
36
+ for (const [path, content] of Object.entries(files)) {
37
+ const target = join(dir, path);
38
+ await mkdir(dirname(target), { recursive: true });
39
+ await writeFile(target, content, 'utf8');
40
+ }
41
+ return { ok: true, dir, files: Object.keys(files) };
42
+ }
43
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/commands/create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAgBjE,KAAK,UAAU,gBAAgB,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAAC;IAC5D,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAsB;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;IAEhC,MAAM,OAAO,GAAG,yBAAyB,CAAC,gBAAgB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1E,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAExE,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACnC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,GAAG,2CAA2C,EAAE,CAAC;IAC5F,CAAC;IAED,MAAM,KAAK,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3E,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC/B,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `reformer-plugin dev --project <каталог>`: сборка прямо в каталог плагинов открытого проекта
3
+ * и пересборка на каждое сохранение.
4
+ *
5
+ * Пишет в `<проект>/.ui_builder/plugins/<id>/` — туда, где загрузчик оболочки плагин и найдёт.
6
+ * Отдельного подкаталога «для разработки» нет: загрузчик считает плагином каждый
7
+ * подкаталог каталога плагинов, и `development/` стал бы плагином с именем `development`.
8
+ *
9
+ * Перезагрузку делает оболочка, а не CLI: плагин, помеченный в списке «в разработке», перечитывается
10
+ * при возврате фокуса в окно (`shell/platform/plugin/dev-watch`). Так правка во внешнем редакторе
11
+ * доходит до билдера без канала между ними — File System Access наблюдать за файлами не даёт.
12
+ *
13
+ * Наблюдение — `fs.watch` с `recursive`, поэтому Node не ниже 20. Каталоги `node_modules`, вывода
14
+ * и скрытые не наблюдаются: сборка сама пишет файлы, и реакция на свою же запись зациклила бы её.
15
+ *
16
+ * @module @reformer/builder-plugin-cli/commands/dev
17
+ */
18
+ import { type BuildResult } from './build.js';
19
+ export interface DevOptions {
20
+ /** Каталог исходников плагина. */
21
+ readonly dir: string;
22
+ /** Корень проекта билдера, в котором плагин будет подхвачен. */
23
+ readonly project: string;
24
+ /** Пауза после последнего изменения перед пересборкой, мс. */
25
+ readonly debounceMs?: number;
26
+ /** Вызывается после каждой сборки, первой — сразу. */
27
+ readonly onBuild: (result: BuildResult) => void;
28
+ }
29
+ export interface DevSession {
30
+ /** Первая сборка: завершается, когда её результат уже отдан в `onBuild`. */
31
+ readonly ready: Promise<void>;
32
+ close(): void;
33
+ }
34
+ export declare function startDev(options: DevOptions): DevSession;
35
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,IAAI,IAAI,CAAC;CACf;AAeD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CA0CxD"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `reformer-plugin dev --project <каталог>`: сборка прямо в каталог плагинов открытого проекта
3
+ * и пересборка на каждое сохранение.
4
+ *
5
+ * Пишет в `<проект>/.ui_builder/plugins/<id>/` — туда, где загрузчик оболочки плагин и найдёт.
6
+ * Отдельного подкаталога «для разработки» нет: загрузчик считает плагином каждый
7
+ * подкаталог каталога плагинов, и `development/` стал бы плагином с именем `development`.
8
+ *
9
+ * Перезагрузку делает оболочка, а не CLI: плагин, помеченный в списке «в разработке», перечитывается
10
+ * при возврате фокуса в окно (`shell/platform/plugin/dev-watch`). Так правка во внешнем редакторе
11
+ * доходит до билдера без канала между ними — File System Access наблюдать за файлами не даёт.
12
+ *
13
+ * Наблюдение — `fs.watch` с `recursive`, поэтому Node не ниже 20. Каталоги `node_modules`, вывода
14
+ * и скрытые не наблюдаются: сборка сама пишет файлы, и реакция на свою же запись зациклила бы её.
15
+ *
16
+ * @module @reformer/builder-plugin-cli/commands/dev
17
+ */
18
+ import { watch } from 'node:fs';
19
+ import { readFile } from 'node:fs/promises';
20
+ import { join, resolve, sep } from 'node:path';
21
+ import { PLUGIN_CATALOG_DIR, PLUGIN_MANIFEST_FILE } from '@reformer/builder-plugin-api/tooling';
22
+ import { buildPlugin } from './build.js';
23
+ const IGNORED = new Set(['node_modules', 'dist']);
24
+ /** Каталог вывода для плагина в проекте. `id` читается из манифеста на каждой сборке. */
25
+ async function outDirFor(dir, project) {
26
+ try {
27
+ const text = await readFile(join(dir, PLUGIN_MANIFEST_FILE), 'utf8');
28
+ const id = JSON.parse(text).id;
29
+ return typeof id === 'string' && id !== '' ? join(project, PLUGIN_CATALOG_DIR, id) : undefined;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ export function startDev(options) {
36
+ const dir = resolve(options.dir);
37
+ const project = resolve(options.project);
38
+ const debounceMs = options.debounceMs ?? 100;
39
+ let running = Promise.resolve();
40
+ let pending = false;
41
+ let timer;
42
+ let closed = false;
43
+ // Сборки строго по одной: изменение во время сборки не теряется, а откладывает ещё одну.
44
+ const rebuild = () => {
45
+ if (pending)
46
+ return running;
47
+ pending = true;
48
+ running = running.then(async () => {
49
+ pending = false;
50
+ if (closed)
51
+ return;
52
+ // Без манифеста (или без id) каталог вывода не вычислить — сборка сама объяснит почему.
53
+ const outDir = (await outDirFor(dir, project)) ?? join(dir, 'dist');
54
+ options.onBuild(await buildPlugin({ dir, outDir }));
55
+ });
56
+ return running;
57
+ };
58
+ const ready = rebuild();
59
+ const watcher = watch(dir, { recursive: true }, (_, filename) => {
60
+ if (filename === null)
61
+ return;
62
+ const first = filename.split(sep)[0] ?? '';
63
+ if (first.startsWith('.') || IGNORED.has(first))
64
+ return;
65
+ if (timer !== undefined)
66
+ clearTimeout(timer);
67
+ timer = setTimeout(() => void rebuild(), debounceMs);
68
+ });
69
+ return {
70
+ ready,
71
+ close() {
72
+ closed = true;
73
+ if (timer !== undefined)
74
+ clearTimeout(timer);
75
+ watcher.close();
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * «Сухая» активация собранного плагина: исполнить `main.js` под Node и посмотреть, что он
3
+ * зарегистрирует, — ради двух отказов, которые оболочка иначе покажет только при включении.
4
+ *
5
+ * - **`not-a-plugin` и `id-mismatch`** — экспорты точки входа узнаются правилом загрузчика
6
+ * (`pluginFromExports` из контракта), `id` сверяется с манифестом.
7
+ * - **`provides-unregistered`** — оболочка после `activate` смотрит, занят ли в реестре служб
8
+ * слот каждой объявленной возможности. Здесь то же: реестр служб контекста записывает,
9
+ * под какими идентификаторами плагин что-то зарегистрировал.
10
+ *
11
+ * ## Что настоящее, а что нет
12
+ *
13
+ * Настоящий — пакет контракта (`@reformer/builder-plugin-api`, он же `@builder/sdk`): токены
14
+ * служб в нём — объекты с `id`, и регистрация под подменным токеном дала бы ложный отказ.
15
+ * Остальные модули рантайма (React, ядро форм, кит) и всё в контексте, кроме реестра служб, —
16
+ * заглушки, принимающие любое обращение. Подними мы их по-настоящему, проверка требовала бы DOM
17
+ * и всего билдера.
18
+ *
19
+ * Отсюда граница честности. Если код падает НА ЗАГЛУШКЕ — при исполнении модуля или в `activate`,
20
+ * — это не отказ: вне оболочки такое бывает у рабочих плагинов (например, `activate` читает
21
+ * значение службы, которой здесь нет). Такой исход возвращается ЗАМЕТКОЙ «не проверено»,
22
+ * а не отказом, и сборка проходит. Отказом становится только то, что видно наверняка.
23
+ *
24
+ * @module @reformer/builder-plugin-cli/commands/dry-run
25
+ */
26
+ import { type PluginManifestBase } from '@reformer/builder-plugin-api/tooling';
27
+ import type { Finding } from './findings.js';
28
+ export interface DryRunResult {
29
+ readonly findings: readonly Finding[];
30
+ /** Почему проверку не удалось довести до конца. Не отказ. */
31
+ readonly notices: readonly string[];
32
+ }
33
+ /**
34
+ * @param code собранный `main.js` (CommonJS)
35
+ * @param manifest манифест, против которого сверяются `id` и `provides`
36
+ */
37
+ export declare function dryActivate(code: string, manifest: Pick<PluginManifestBase, 'id' | 'provides'>): Promise<DryRunResult>;
38
+ //# sourceMappingURL=dry-run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dry-run.d.ts","sourceRoot":"","sources":["../../src/commands/dry-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sCAAsC,CAAC;AAE9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AA6BD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,UAAU,CAAC,GACpD,OAAO,CAAC,YAAY,CAAC,CAoGvB"}