@json-to-office/jto 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +23 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +5639 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/client/assets/HomePage-CGK1cPFp.js +99 -0
  7. package/dist/client/assets/HomePage-CGK1cPFp.js.map +1 -0
  8. package/dist/client/assets/JsonEditorPage-DKaFqYpM.js +3 -0
  9. package/dist/client/assets/JsonEditorPage-DKaFqYpM.js.map +1 -0
  10. package/dist/client/assets/NotFoundPage-Et1QhbQw.js +2 -0
  11. package/dist/client/assets/NotFoundPage-Et1QhbQw.js.map +1 -0
  12. package/dist/client/assets/button-B5W5GwSc.js +2 -0
  13. package/dist/client/assets/button-B5W5GwSc.js.map +1 -0
  14. package/dist/client/assets/docx-preview-CUjVWdSJ.js +34 -0
  15. package/dist/client/assets/docx-preview-CUjVWdSJ.js.map +1 -0
  16. package/dist/client/assets/editor-CH_tiHyn.js +9 -0
  17. package/dist/client/assets/editor-CH_tiHyn.js.map +1 -0
  18. package/dist/client/assets/editor-monaco-json-8I2epq6g.js +6 -0
  19. package/dist/client/assets/editor-monaco-json-8I2epq6g.js.map +1 -0
  20. package/dist/client/assets/index-B9dV-vCI.css +1 -0
  21. package/dist/client/assets/index-DactvF4v.js +3 -0
  22. package/dist/client/assets/index-DactvF4v.js.map +1 -0
  23. package/dist/client/assets/monaco-editor-DzeBDgmk.js +12 -0
  24. package/dist/client/assets/monaco-editor-DzeBDgmk.js.map +1 -0
  25. package/dist/client/assets/preview-CJlTPgks.js +3 -0
  26. package/dist/client/assets/preview-CJlTPgks.js.map +1 -0
  27. package/dist/client/assets/query-vendor-UL64zsGL.js +15 -0
  28. package/dist/client/assets/query-vendor-UL64zsGL.js.map +1 -0
  29. package/dist/client/assets/radix-ui-CXavUarI.js +2 -0
  30. package/dist/client/assets/radix-ui-CXavUarI.js.map +1 -0
  31. package/dist/client/assets/react-vendor-DhdcN9D5.js +102 -0
  32. package/dist/client/assets/react-vendor-DhdcN9D5.js.map +1 -0
  33. package/dist/client/assets/settings-store-provider-DuCKtwqs.js +2 -0
  34. package/dist/client/assets/settings-store-provider-DuCKtwqs.js.map +1 -0
  35. package/dist/client/assets/state-vendor-CAMVKh-F.js +6 -0
  36. package/dist/client/assets/state-vendor-CAMVKh-F.js.map +1 -0
  37. package/dist/client/assets/ui-vendor-C6DsfOoA.js +247 -0
  38. package/dist/client/assets/ui-vendor-C6DsfOoA.js.map +1 -0
  39. package/dist/client/css/preview/docxjs.css +11 -0
  40. package/dist/client/icon.svg +5 -0
  41. package/dist/client/index.html +23 -0
  42. package/dist/client/templates/Charts Demo.pptx.json +174 -0
  43. package/dist/client/templates/Company Branding.pptx.json +143 -0
  44. package/dist/client/templates/Dashboard.pptx.json +91 -0
  45. package/dist/client/templates/Product Launch.pptx.json +87 -0
  46. package/dist/client/templates/Sales Deck.pptx.json +80 -0
  47. package/dist/client/templates/Wiseair.pptx.json +5382 -0
  48. package/dist/client/templates/themes/corporate.pptx.theme.json +32 -0
  49. package/dist/client/templates/themes/minimal.pptx.theme.json +32 -0
  50. package/dist/client/templates/themes/vibrant.pptx.theme.json +32 -0
  51. package/dist/index.d.ts +387 -0
  52. package/dist/index.js +2303 -0
  53. package/dist/index.js.map +1 -0
  54. package/package.json +147 -0
package/dist/index.js ADDED
@@ -0,0 +1,2303 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/format-adapter.ts
9
+ import * as path from "path";
10
+ import * as fs from "fs";
11
+ var DocxFormatAdapter = class {
12
+ name = "docx";
13
+ extension = ".docx";
14
+ label = "document";
15
+ defaultPort = 3003;
16
+ async generateBuffer(json, options) {
17
+ const core = await import("@json-to-office/core-docx");
18
+ const docDefinition = typeof json === "string" ? JSON.parse(json) : json;
19
+ const customThemes = await this.loadCustomThemes(options);
20
+ return await core.generateBufferFromJson(docDefinition, { customThemes });
21
+ }
22
+ async createGenerator(plugins, options) {
23
+ const core = await import("@json-to-office/core-docx");
24
+ const hasPlugins = plugins.length > 0;
25
+ const pluginNames = plugins.map((p) => p.name);
26
+ if (!hasPlugins) {
27
+ return {
28
+ generateBuffer: async (document) => {
29
+ const docDefinition = typeof document === "string" ? JSON.parse(document) : document;
30
+ const customThemes = await this.loadCustomThemes(options);
31
+ return await core.generateBufferFromJson(docDefinition, { customThemes });
32
+ },
33
+ hasPlugins: false,
34
+ pluginNames: []
35
+ };
36
+ }
37
+ const theme = await this.resolveTheme(options);
38
+ let generator = core.createDocumentGenerator({
39
+ theme,
40
+ debug: process.env.DEBUG === "true"
41
+ });
42
+ for (const plugin of plugins) {
43
+ generator = generator.addComponent(plugin);
44
+ }
45
+ return {
46
+ generateBuffer: async (document) => {
47
+ const docDefinition = typeof document === "string" ? JSON.parse(document) : document;
48
+ const validationResult = generator.validate(docDefinition);
49
+ if (!validationResult.valid) {
50
+ const errors = validationResult.errors || [];
51
+ throw new Error(
52
+ `Document validation failed:
53
+ ${errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n")}`
54
+ );
55
+ }
56
+ const result = await generator.generateBuffer(docDefinition);
57
+ return result.buffer;
58
+ },
59
+ getStandardComponentsDefinition: generator.getStandardComponentsDefinition ? (config) => generator.getStandardComponentsDefinition(config) : void 0,
60
+ hasPlugins: true,
61
+ pluginNames
62
+ };
63
+ }
64
+ parseJson(input) {
65
+ return typeof input === "string" ? JSON.parse(input) : input;
66
+ }
67
+ validateDocument(_doc) {
68
+ return { valid: true };
69
+ }
70
+ generateSchema(_options) {
71
+ return null;
72
+ }
73
+ getBuiltinThemes() {
74
+ try {
75
+ const core = __require("@json-to-office/core-docx");
76
+ return core.themes || {};
77
+ } catch {
78
+ return {};
79
+ }
80
+ }
81
+ async resolveTheme(options) {
82
+ const core = await import("@json-to-office/core-docx");
83
+ if (options.themePath) {
84
+ try {
85
+ if (options.themePath.endsWith(".json")) {
86
+ return await core.loadThemeFromFile(options.themePath);
87
+ } else {
88
+ const themePath = path.resolve(process.cwd(), options.themePath);
89
+ const themeModule = await import(themePath);
90
+ return themeModule.default || themeModule.theme;
91
+ }
92
+ } catch (error) {
93
+ console.warn(
94
+ `Failed to load theme from ${options.themePath}: ${error.message}`
95
+ );
96
+ }
97
+ }
98
+ if (typeof options.theme === "string") {
99
+ const builtInTheme = core.themes?.[options.theme];
100
+ if (builtInTheme) return builtInTheme;
101
+ if (options.theme.endsWith(".json") && fs.existsSync(options.theme)) {
102
+ try {
103
+ return await core.loadThemeFromFile(options.theme);
104
+ } catch {
105
+ }
106
+ }
107
+ try {
108
+ return await core.loadThemeFromJson(options.theme);
109
+ } catch {
110
+ }
111
+ }
112
+ if (typeof options.theme === "object" && options.theme !== null) {
113
+ return options.theme;
114
+ }
115
+ return core.themes?.minimal || {};
116
+ }
117
+ async loadCustomThemes(options) {
118
+ const core = await import("@json-to-office/core-docx");
119
+ const customThemes = {};
120
+ if (options.customThemes) {
121
+ Object.assign(customThemes, options.customThemes);
122
+ }
123
+ if (typeof options.theme === "object" && options.theme !== null) {
124
+ customThemes.custom = options.theme;
125
+ }
126
+ if (options.themePath) {
127
+ try {
128
+ if (options.themePath.endsWith(".json")) {
129
+ customThemes.custom = await core.loadThemeFromFile(options.themePath);
130
+ } else {
131
+ const themePath = path.resolve(process.cwd(), options.themePath);
132
+ const themeModule = await import(themePath);
133
+ customThemes.custom = themeModule.default || themeModule.theme;
134
+ }
135
+ } catch (error) {
136
+ console.warn(
137
+ `Failed to load theme from ${options.themePath}: ${error.message}`
138
+ );
139
+ }
140
+ }
141
+ return Object.keys(customThemes).length > 0 ? customThemes : void 0;
142
+ }
143
+ async getComponentCacheStats() {
144
+ try {
145
+ const core = await import("@json-to-office/core-docx");
146
+ const stats = core.getComponentCacheStats?.();
147
+ if (!stats) return null;
148
+ const componentStats = Array.from(
149
+ stats.componentStats.entries()
150
+ ).map(([, s]) => {
151
+ const total = s.hits + s.misses;
152
+ const hitRate = total > 0 ? s.hits / total : 0;
153
+ return {
154
+ type: s.name,
155
+ hits: s.hits,
156
+ misses: s.misses,
157
+ avgProcessTime: s.avgProcessTime,
158
+ avgSize: s.avgSize,
159
+ entries: s.entries,
160
+ hitRate,
161
+ missRate: total > 0 ? s.misses / total : 0,
162
+ totalRequests: total,
163
+ memoryUsage: s.entries * s.avgSize,
164
+ efficiencyScore: Math.round(hitRate * 100)
165
+ };
166
+ });
167
+ return {
168
+ entries: stats.entries,
169
+ totalSize: stats.totalSize,
170
+ hitRate: stats.hitRate,
171
+ missRate: stats.missRate,
172
+ totalHits: stats.totalHits,
173
+ totalMisses: stats.totalMisses,
174
+ avgResponseTime: stats.avgResponseTime,
175
+ evictions: stats.evictions,
176
+ componentStats
177
+ };
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+ async getComponentCacheAnalytics() {
183
+ try {
184
+ const core = await import("@json-to-office/core-docx");
185
+ const stats = core.getComponentCacheStats?.();
186
+ if (!stats) return null;
187
+ const analytics = new core.ComponentCacheAnalytics();
188
+ const report = analytics.analyzeCache(stats);
189
+ return {
190
+ ...report,
191
+ componentMetrics: report.componentMetrics.map((m) => ({
192
+ componentType: m.componentName,
193
+ hitRate: m.hitRate,
194
+ totalRequests: m.totalRequests,
195
+ avgHitTime: m.avgHitTime,
196
+ avgMissTime: m.avgMissTime,
197
+ efficiencyScore: m.efficiencyScore,
198
+ memoryUsage: m.memoryUsage,
199
+ timeSaved: m.timeSaved,
200
+ costBenefitRatio: m.costBenefitRatio
201
+ })),
202
+ recommendations: report.recommendations.map((r) => ({
203
+ componentType: r.componentName,
204
+ type: r.type,
205
+ description: r.description,
206
+ expectedImprovement: r.expectedImprovement,
207
+ priority: r.priority,
208
+ reasoning: r.reasoning
209
+ }))
210
+ };
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ };
216
+ var PptxFormatAdapter = class {
217
+ name = "pptx";
218
+ extension = ".pptx";
219
+ label = "presentation";
220
+ defaultPort = 3004;
221
+ async generateBuffer(json, options) {
222
+ const core = await import("@json-to-office/core-pptx");
223
+ const docDefinition = typeof json === "string" ? JSON.parse(json) : json;
224
+ const customThemes = await this.loadCustomThemes(options);
225
+ return await core.generateBufferFromJson(docDefinition, { customThemes });
226
+ }
227
+ async createGenerator(plugins, options) {
228
+ const core = await import("@json-to-office/core-pptx");
229
+ const hasPlugins = plugins.length > 0;
230
+ const pluginNames = plugins.map((p) => p.name);
231
+ return {
232
+ generateBuffer: async (document) => {
233
+ const docDefinition = typeof document === "string" ? JSON.parse(document) : document;
234
+ const customThemes = await this.loadCustomThemes(options);
235
+ return await core.generateBufferFromJson(docDefinition, { customThemes });
236
+ },
237
+ hasPlugins,
238
+ pluginNames
239
+ };
240
+ }
241
+ parseJson(input) {
242
+ return typeof input === "string" ? JSON.parse(input) : input;
243
+ }
244
+ validateDocument(_doc) {
245
+ return { valid: true };
246
+ }
247
+ generateSchema(_options) {
248
+ return null;
249
+ }
250
+ getBuiltinThemes() {
251
+ try {
252
+ const core = __require("@json-to-office/core-pptx");
253
+ return core.pptxThemes || {};
254
+ } catch {
255
+ return {};
256
+ }
257
+ }
258
+ async resolveTheme(options) {
259
+ const core = await import("@json-to-office/core-pptx");
260
+ const themes = core.pptxThemes || {};
261
+ if (options.themePath) {
262
+ try {
263
+ if (options.themePath.endsWith(".json")) {
264
+ const content = fs.readFileSync(
265
+ path.resolve(process.cwd(), options.themePath),
266
+ "utf-8"
267
+ );
268
+ return JSON.parse(content);
269
+ } else {
270
+ const themePath = path.resolve(process.cwd(), options.themePath);
271
+ const themeModule = await import(themePath);
272
+ return themeModule.default || themeModule.theme;
273
+ }
274
+ } catch (error) {
275
+ console.warn(
276
+ `Failed to load theme from ${options.themePath}: ${error.message}`
277
+ );
278
+ }
279
+ }
280
+ if (typeof options.theme === "string") {
281
+ const builtIn = themes[options.theme] || core.getPptxTheme?.(options.theme);
282
+ if (builtIn) return builtIn;
283
+ if (options.theme.endsWith(".json") && fs.existsSync(options.theme)) {
284
+ try {
285
+ const content = fs.readFileSync(
286
+ path.resolve(process.cwd(), options.theme),
287
+ "utf-8"
288
+ );
289
+ return JSON.parse(content);
290
+ } catch {
291
+ }
292
+ }
293
+ }
294
+ if (typeof options.theme === "object" && options.theme !== null) {
295
+ return options.theme;
296
+ }
297
+ return themes.minimal || {};
298
+ }
299
+ async loadCustomThemes(options) {
300
+ const customThemes = {};
301
+ if (options.customThemes) {
302
+ Object.assign(customThemes, options.customThemes);
303
+ }
304
+ if (typeof options.theme === "object" && options.theme !== null) {
305
+ customThemes.custom = options.theme;
306
+ }
307
+ if (options.themePath) {
308
+ try {
309
+ if (options.themePath.endsWith(".json")) {
310
+ const content = fs.readFileSync(
311
+ path.resolve(process.cwd(), options.themePath),
312
+ "utf-8"
313
+ );
314
+ customThemes.custom = JSON.parse(content);
315
+ } else {
316
+ const themePath = path.resolve(process.cwd(), options.themePath);
317
+ const themeModule = await import(themePath);
318
+ customThemes.custom = themeModule.default || themeModule.theme;
319
+ }
320
+ } catch (error) {
321
+ console.warn(
322
+ `Failed to load theme from ${options.themePath}: ${error.message}`
323
+ );
324
+ }
325
+ }
326
+ return Object.keys(customThemes).length > 0 ? customThemes : void 0;
327
+ }
328
+ };
329
+ function createAdapter(format) {
330
+ switch (format) {
331
+ case "docx":
332
+ return new DocxFormatAdapter();
333
+ case "pptx":
334
+ return new PptxFormatAdapter();
335
+ default:
336
+ throw new Error(`Unknown format: ${format}`);
337
+ }
338
+ }
339
+
340
+ // src/services/plugin-loader.ts
341
+ import * as path2 from "path";
342
+ import { pathToFileURL } from "url";
343
+ var globalTsxUnregister;
344
+ var tsxInitializationPromise;
345
+ var PluginLoader = class {
346
+ tsxUnregister;
347
+ async initialize() {
348
+ if (globalTsxUnregister) {
349
+ this.tsxUnregister = globalTsxUnregister;
350
+ return;
351
+ }
352
+ if (tsxInitializationPromise) {
353
+ await tsxInitializationPromise;
354
+ this.tsxUnregister = globalTsxUnregister;
355
+ return;
356
+ }
357
+ tsxInitializationPromise = (async () => {
358
+ try {
359
+ const { register } = await import("tsx/esm/api");
360
+ globalTsxUnregister = register();
361
+ this.tsxUnregister = globalTsxUnregister;
362
+ } catch {
363
+ console.warn("tsx not available, TypeScript module loading may fail");
364
+ }
365
+ })();
366
+ await tsxInitializationPromise;
367
+ }
368
+ async loadPlugin(filePath) {
369
+ try {
370
+ if (!filePath.endsWith(".ts")) {
371
+ return null;
372
+ }
373
+ if (!this.tsxUnregister) {
374
+ await this.initialize();
375
+ }
376
+ const fileUrl = pathToFileURL(filePath).href;
377
+ const module = await import(`${fileUrl}?t=${Date.now()}`);
378
+ return this.extractComponent(module, filePath);
379
+ } catch (error) {
380
+ if (process.env.DEBUG) {
381
+ console.error(`Failed to load plugin from ${filePath}:`, error);
382
+ } else {
383
+ console.warn(
384
+ `Failed to load plugin from ${path2.basename(filePath)}: ${error.message}`
385
+ );
386
+ }
387
+ return null;
388
+ }
389
+ }
390
+ async loadPlugins(filePaths) {
391
+ const plugins = /* @__PURE__ */ new Map();
392
+ const results = await Promise.allSettled(
393
+ filePaths.map(async (filePath) => {
394
+ const module = await this.loadPlugin(filePath);
395
+ return { filePath, module };
396
+ })
397
+ );
398
+ for (const result of results) {
399
+ if (result.status === "fulfilled" && result.value.module) {
400
+ plugins.set(result.value.filePath, result.value.module);
401
+ }
402
+ }
403
+ return plugins;
404
+ }
405
+ extractComponent(module, filePath) {
406
+ if (module.default && this.isValidComponent(module.default)) {
407
+ return module.default;
408
+ }
409
+ const componentExports = Object.entries(module).filter(
410
+ ([key]) => key.endsWith("Component") || key.endsWith("component") || key.endsWith("Module") || key.endsWith("module")
411
+ ).map(([_, value]) => value);
412
+ for (const exportedValue of componentExports) {
413
+ if (this.isValidComponent(exportedValue)) {
414
+ return exportedValue;
415
+ }
416
+ }
417
+ for (const [key, value] of Object.entries(module)) {
418
+ if (this.isValidComponent(value)) {
419
+ if (process.env.DEBUG) {
420
+ console.log(
421
+ `Found component in export '${key}' from ${path2.basename(filePath)}`
422
+ );
423
+ }
424
+ return value;
425
+ }
426
+ }
427
+ return null;
428
+ }
429
+ isValidComponent(obj) {
430
+ if (!obj || typeof obj !== "object") {
431
+ return false;
432
+ }
433
+ const hasName = typeof obj.name === "string" && obj.name.length > 0;
434
+ if (obj.versions && typeof obj.versions === "object") {
435
+ const entries = Object.values(obj.versions);
436
+ return hasName && entries.length > 0 && entries.some(
437
+ (entry) => entry && typeof entry === "object" && entry.propsSchema && typeof entry.propsSchema === "object" && typeof entry.render === "function"
438
+ );
439
+ }
440
+ return false;
441
+ }
442
+ cleanup() {
443
+ this.tsxUnregister = void 0;
444
+ }
445
+ };
446
+
447
+ // src/services/plugin-discovery.ts
448
+ import * as path6 from "path";
449
+ import * as fs5 from "fs/promises";
450
+
451
+ // src/services/file-scanner.ts
452
+ import { glob } from "glob";
453
+ import * as path3 from "path";
454
+ import * as fs2 from "fs/promises";
455
+ var FileSystemScanner = class {
456
+ FILE_PATTERNS = {
457
+ plugin: {
458
+ type: "plugin",
459
+ pattern: "*.component.ts",
460
+ monorepoPatterns: [
461
+ "packages/*/src/**/*.component.ts",
462
+ "packages/**/src/**/*.component.ts",
463
+ "apps/*/src/**/*.component.ts",
464
+ "apps/**/src/**/*.component.ts",
465
+ "libs/*/src/**/*.component.ts",
466
+ "libs/**/src/**/*.component.ts",
467
+ "components/**/*.component.ts",
468
+ "plugins/**/*.component.ts",
469
+ "src/plugins/**/*.component.ts",
470
+ "src/components/**/*.component.ts"
471
+ ]
472
+ },
473
+ "docx-document": {
474
+ type: "docx-document",
475
+ pattern: "*.docx.json",
476
+ monorepoPatterns: [
477
+ "packages/*/src/**/*.docx.json",
478
+ "packages/**/src/**/*.docx.json",
479
+ "apps/*/src/**/*.docx.json",
480
+ "apps/**/src/**/*.docx.json",
481
+ "libs/*/src/**/*.docx.json",
482
+ "libs/**/src/**/*.docx.json",
483
+ "documents/**/*.docx.json",
484
+ "src/documents/**/*.docx.json",
485
+ "templates/**/*.docx.json",
486
+ "src/templates/**/*.docx.json"
487
+ ]
488
+ },
489
+ "pptx-document": {
490
+ type: "pptx-document",
491
+ pattern: "*.pptx.json",
492
+ monorepoPatterns: [
493
+ "packages/*/src/**/*.pptx.json",
494
+ "packages/**/src/**/*.pptx.json",
495
+ "apps/*/src/**/*.pptx.json",
496
+ "apps/**/src/**/*.pptx.json",
497
+ "libs/*/src/**/*.pptx.json",
498
+ "libs/**/src/**/*.pptx.json",
499
+ "documents/**/*.pptx.json",
500
+ "src/documents/**/*.pptx.json",
501
+ "templates/**/*.pptx.json",
502
+ "src/templates/**/*.pptx.json"
503
+ ]
504
+ },
505
+ "pptx-theme": {
506
+ type: "pptx-theme",
507
+ pattern: "*.pptx.theme.json",
508
+ monorepoPatterns: [
509
+ "packages/*/src/**/*.pptx.theme.json",
510
+ "packages/**/src/**/*.pptx.theme.json",
511
+ "apps/*/src/**/*.pptx.theme.json",
512
+ "apps/**/src/**/*.pptx.theme.json",
513
+ "libs/*/src/**/*.pptx.theme.json",
514
+ "libs/**/src/**/*.pptx.theme.json",
515
+ "themes/**/*.pptx.theme.json",
516
+ "src/themes/**/*.pptx.theme.json",
517
+ "templates/**/*.pptx.theme.json",
518
+ "src/templates/**/*.pptx.theme.json"
519
+ ]
520
+ },
521
+ "docx-theme": {
522
+ type: "docx-theme",
523
+ pattern: "*.docx.theme.json",
524
+ monorepoPatterns: [
525
+ "packages/*/src/**/*.docx.theme.json",
526
+ "packages/**/src/**/*.docx.theme.json",
527
+ "apps/*/src/**/*.docx.theme.json",
528
+ "apps/**/src/**/*.docx.theme.json",
529
+ "libs/*/src/**/*.docx.theme.json",
530
+ "libs/**/src/**/*.docx.theme.json",
531
+ "themes/**/*.docx.theme.json",
532
+ "src/themes/**/*.docx.theme.json",
533
+ "templates/**/*.docx.theme.json",
534
+ "src/templates/**/*.docx.theme.json"
535
+ ]
536
+ }
537
+ };
538
+ EXCLUDE_PATTERNS = [
539
+ "**/node_modules/**",
540
+ "**/dist/**",
541
+ "**/build/**",
542
+ "**/.git/**",
543
+ "**/coverage/**",
544
+ "**/.next/**",
545
+ "**/.turbo/**",
546
+ "**/tmp/**",
547
+ "**/.cache/**",
548
+ "**/out/**"
549
+ ];
550
+ async scan(basePath, fileType = "plugin", options = {}) {
551
+ const {
552
+ maxDepth = 10,
553
+ excludeNodeModules = true,
554
+ additionalIgnore = []
555
+ } = options;
556
+ const patterns = this.buildPatterns(fileType, maxDepth);
557
+ const ignore = this.buildIgnorePatterns(
558
+ excludeNodeModules,
559
+ additionalIgnore
560
+ );
561
+ try {
562
+ const files = await glob(patterns, {
563
+ cwd: basePath,
564
+ absolute: true,
565
+ ignore,
566
+ nodir: true
567
+ });
568
+ return files;
569
+ } catch {
570
+ return [];
571
+ }
572
+ }
573
+ async scanMonorepoLocations(rootPath, fileType = "plugin") {
574
+ const monorepoPatterns = this.FILE_PATTERNS[fileType].monorepoPatterns || [];
575
+ const allFiles = [];
576
+ for (const pattern of monorepoPatterns) {
577
+ try {
578
+ const matches = await glob(pattern, {
579
+ cwd: rootPath,
580
+ absolute: true,
581
+ ignore: this.EXCLUDE_PATTERNS
582
+ });
583
+ allFiles.push(...matches);
584
+ } catch {
585
+ }
586
+ }
587
+ return allFiles;
588
+ }
589
+ async hasPackageJson(dirPath) {
590
+ try {
591
+ await fs2.access(path3.join(dirPath, "package.json"));
592
+ return true;
593
+ } catch {
594
+ return false;
595
+ }
596
+ }
597
+ isInNodeModules(currentPath) {
598
+ return currentPath.includes("node_modules");
599
+ }
600
+ buildPatterns(fileType, maxDepth) {
601
+ const filePattern = this.FILE_PATTERNS[fileType].pattern;
602
+ const patterns = [filePattern];
603
+ for (let depth = 1; depth <= maxDepth; depth++) {
604
+ const depthPattern = Array(depth).fill("*").join("/") + "/" + filePattern;
605
+ patterns.push(depthPattern);
606
+ }
607
+ return patterns;
608
+ }
609
+ buildIgnorePatterns(excludeNodeModules, additionalIgnore) {
610
+ let ignore = [...this.EXCLUDE_PATTERNS];
611
+ if (!excludeNodeModules) {
612
+ ignore = ignore.filter((pattern) => !pattern.includes("node_modules"));
613
+ }
614
+ return [...ignore, ...additionalIgnore];
615
+ }
616
+ deduplicatePaths(paths) {
617
+ const uniquePaths = new Set(paths.map((p) => path3.resolve(p)));
618
+ return Array.from(uniquePaths);
619
+ }
620
+ };
621
+
622
+ // src/services/plugin-metadata.ts
623
+ import * as path4 from "path";
624
+ import * as fs3 from "fs/promises";
625
+ import { latestVersion } from "@json-to-office/shared";
626
+ var PluginMetadataExtractor = class {
627
+ cwd;
628
+ constructor(cwd) {
629
+ this.cwd = cwd || process.cwd();
630
+ }
631
+ async extract(component, filePath) {
632
+ const location = this.determineLocation(filePath);
633
+ const relativePath = path4.relative(this.cwd, filePath);
634
+ const versions = component.versions || {};
635
+ const versionKeys = Object.keys(versions);
636
+ const latestVer = versionKeys.length > 0 ? latestVersion(versionKeys) : void 0;
637
+ const latestEntry = latestVer ? versions[latestVer] : void 0;
638
+ const metadata = {
639
+ name: component.name,
640
+ description: latestEntry?.description,
641
+ version: latestVer,
642
+ filePath,
643
+ relativePath,
644
+ location,
645
+ hasChildren: latestEntry?.hasChildren === true,
646
+ schema: {
647
+ raw: latestEntry?.propsSchema
648
+ }
649
+ };
650
+ if (latestEntry?.propsSchema) {
651
+ try {
652
+ metadata.schema.jsonSchema = this.typeboxToJsonSchema(
653
+ latestEntry.propsSchema
654
+ );
655
+ metadata.schema.properties = this.extractProperties(
656
+ latestEntry.propsSchema
657
+ );
658
+ } catch {
659
+ }
660
+ }
661
+ try {
662
+ metadata.examples = await this.extractExamples(filePath);
663
+ } catch {
664
+ metadata.examples = [];
665
+ }
666
+ return metadata;
667
+ }
668
+ determineLocation(filePath) {
669
+ const resolvedPath = path4.resolve(filePath);
670
+ const resolvedCwd = path4.resolve(this.cwd);
671
+ if (path4.dirname(resolvedPath) === resolvedCwd) {
672
+ return "current";
673
+ }
674
+ if (resolvedPath.startsWith(resolvedCwd)) {
675
+ return "downstream";
676
+ }
677
+ return "upstream";
678
+ }
679
+ typeboxToJsonSchema(schema) {
680
+ try {
681
+ const jsonSchema = JSON.parse(JSON.stringify(schema));
682
+ delete jsonSchema[Symbol.for("TypeBox.Kind")];
683
+ delete jsonSchema.static;
684
+ return jsonSchema;
685
+ } catch {
686
+ return schema;
687
+ }
688
+ }
689
+ extractProperties(schema) {
690
+ const properties = {};
691
+ try {
692
+ if (schema.type === "object" && schema.properties) {
693
+ for (const [key, value] of Object.entries(schema.properties)) {
694
+ const prop = value;
695
+ let type = prop.type || "unknown";
696
+ let enumValues;
697
+ if (prop.anyOf && Array.isArray(prop.anyOf)) {
698
+ const literals = prop.anyOf.filter(
699
+ (item) => item.type === "string" && item.const !== void 0
700
+ ).map((item) => item.const);
701
+ if (literals.length > 0) {
702
+ type = "string";
703
+ enumValues = literals;
704
+ } else {
705
+ const types = prop.anyOf.map((item) => item.type).filter(Boolean);
706
+ type = types.length > 0 ? types.join(" | ") : "union";
707
+ }
708
+ }
709
+ properties[key] = {
710
+ type,
711
+ description: prop.description,
712
+ required: schema.required?.includes(key) || false,
713
+ default: prop.default,
714
+ enum: enumValues || prop.enum,
715
+ pattern: prop.pattern,
716
+ minimum: prop.minimum,
717
+ maximum: prop.maximum,
718
+ minLength: prop.minLength,
719
+ maxLength: prop.maxLength
720
+ };
721
+ Object.keys(properties[key]).forEach((k) => {
722
+ if (properties[key][k] === void 0) {
723
+ delete properties[key][k];
724
+ }
725
+ });
726
+ }
727
+ }
728
+ } catch {
729
+ }
730
+ return properties;
731
+ }
732
+ async extractExamples(filePath) {
733
+ const examples = [];
734
+ const normalizeExampleProps = (input) => {
735
+ if (input && typeof input === "object" && "props" in input) {
736
+ return input.props;
737
+ }
738
+ return input;
739
+ };
740
+ try {
741
+ const content = await fs3.readFile(filePath, "utf-8");
742
+ const exampleRegex = /@example\s*(?:<caption>(.*?)<\/caption>)?\s*\*?\s*```(?:json|typescript|ts)?\s*([\s\S]*?)```/gi;
743
+ let match;
744
+ while ((match = exampleRegex.exec(content)) !== null) {
745
+ const title = match[1]?.trim();
746
+ let codeBlock = match[2];
747
+ codeBlock = codeBlock.split("\n").map((line) => line.replace(/^\s*\*\s?/, "")).join("\n").trim();
748
+ const jsonMatch = codeBlock.match(/\{[\s\S]*\}/);
749
+ if (jsonMatch) {
750
+ try {
751
+ const props = JSON.parse(jsonMatch[0]);
752
+ examples.push({
753
+ title,
754
+ props: normalizeExampleProps(props)
755
+ });
756
+ } catch {
757
+ const propsMatch = codeBlock.match(/props:\s*\{[\s\S]*?\}/);
758
+ if (propsMatch) {
759
+ try {
760
+ const configStr = propsMatch[0].replace(/props:\s*/, "");
761
+ const cleanConfig = configStr.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2":').replace(/'/g, '"').replace(/,(\s*[}\]])/g, "$1").replace(/,\s*$/, "");
762
+ const props = JSON.parse(cleanConfig);
763
+ examples.push({ title, props });
764
+ } catch {
765
+ }
766
+ }
767
+ }
768
+ }
769
+ }
770
+ const inlineExampleRegex = /\/\/\s*Example:\s*(\{.*?\})/g;
771
+ while ((match = inlineExampleRegex.exec(content)) !== null) {
772
+ try {
773
+ const props = JSON.parse(match[1]);
774
+ examples.push({ props: normalizeExampleProps(props) });
775
+ } catch {
776
+ }
777
+ }
778
+ } catch {
779
+ }
780
+ return examples;
781
+ }
782
+ async extractBatch(components) {
783
+ const metadataList = [];
784
+ for (const [filePath, component] of components) {
785
+ try {
786
+ const metadata = await this.extract(component, filePath);
787
+ metadataList.push(metadata);
788
+ } catch (error) {
789
+ console.warn(`Failed to extract metadata from ${filePath}:`, error);
790
+ }
791
+ }
792
+ return metadataList;
793
+ }
794
+ };
795
+
796
+ // src/utils/project-root.ts
797
+ import * as fs4 from "fs";
798
+ import * as path5 from "path";
799
+ var PROJECT_ROOT_MARKERS = [
800
+ "pnpm-workspace.yaml",
801
+ "pnpm-workspace.yml",
802
+ "turbo.json",
803
+ "lerna.json",
804
+ "nx.json",
805
+ ".git",
806
+ "package.json"
807
+ ];
808
+ function hasWorkspaces(packageJsonPath) {
809
+ try {
810
+ const content = fs4.readFileSync(packageJsonPath, "utf-8");
811
+ const pkg = JSON.parse(content);
812
+ return !!(pkg.workspaces || pkg.private === true);
813
+ } catch {
814
+ return false;
815
+ }
816
+ }
817
+ function findProjectRoot(startPath) {
818
+ let currentPath = path5.resolve(startPath || process.cwd());
819
+ const root = path5.parse(currentPath).root;
820
+ while (currentPath !== root) {
821
+ for (const marker of PROJECT_ROOT_MARKERS) {
822
+ const markerPath = path5.join(currentPath, marker);
823
+ if (fs4.existsSync(markerPath)) {
824
+ if (marker === "package.json") {
825
+ if (hasWorkspaces(markerPath)) {
826
+ return currentPath;
827
+ }
828
+ continue;
829
+ }
830
+ return currentPath;
831
+ }
832
+ }
833
+ const parentPath = path5.dirname(currentPath);
834
+ if (parentPath === currentPath) break;
835
+ currentPath = parentPath;
836
+ }
837
+ const fallbackPackageJson = path5.join(
838
+ startPath || process.cwd(),
839
+ "package.json"
840
+ );
841
+ if (fs4.existsSync(fallbackPackageJson)) {
842
+ return startPath || process.cwd();
843
+ }
844
+ return null;
845
+ }
846
+ function getProjectRoot(startPath) {
847
+ const root = findProjectRoot(startPath);
848
+ if (!root) {
849
+ console.warn(
850
+ "Warning: Could not detect project root, using current directory"
851
+ );
852
+ return process.cwd();
853
+ }
854
+ return root;
855
+ }
856
+ function isWithinProject(targetPath, projectRoot) {
857
+ const resolvedTarget = path5.resolve(targetPath);
858
+ const resolvedRoot = path5.resolve(projectRoot);
859
+ return resolvedTarget.startsWith(resolvedRoot);
860
+ }
861
+ function resolveScopePath(scope, projectRoot) {
862
+ const resolved = path5.resolve(projectRoot, scope);
863
+ if (!isWithinProject(resolved, projectRoot)) {
864
+ throw new Error(`Scope path "${scope}" is outside the project root`);
865
+ }
866
+ if (!fs4.existsSync(resolved)) {
867
+ throw new Error(`Scope directory not found: ${scope}`);
868
+ }
869
+ const stats = fs4.statSync(resolved);
870
+ if (!stats.isDirectory()) {
871
+ throw new Error(`Scope must be a directory, not a file: ${scope}`);
872
+ }
873
+ return resolved;
874
+ }
875
+
876
+ // src/services/plugin-discovery.ts
877
+ var PluginDiscoveryService = class {
878
+ scanner;
879
+ loader;
880
+ metadataExtractor;
881
+ options;
882
+ searchPath;
883
+ projectRoot;
884
+ constructor(options = {}) {
885
+ this.options = options;
886
+ this.scanner = new FileSystemScanner();
887
+ this.loader = new PluginLoader();
888
+ this.projectRoot = getProjectRoot();
889
+ if (options.scope) {
890
+ try {
891
+ this.searchPath = resolveScopePath(options.scope, this.projectRoot);
892
+ } catch (error) {
893
+ console.error(`Error: ${error.message}`);
894
+ process.exit(1);
895
+ }
896
+ } else {
897
+ this.searchPath = this.projectRoot;
898
+ }
899
+ this.metadataExtractor = new PluginMetadataExtractor(this.searchPath);
900
+ }
901
+ async discover() {
902
+ return this.discoverPlugins();
903
+ }
904
+ async discoverPlugins() {
905
+ const startPath = this.searchPath;
906
+ try {
907
+ await this.loader.initialize();
908
+ const allPluginPaths = await this.searchDownstream(startPath, "plugin");
909
+ const uniquePaths = this.scanner.deduplicatePaths(allPluginPaths);
910
+ if (this.options.verbose) {
911
+ console.log(`Found ${uniquePaths.length} potential plugin files`);
912
+ }
913
+ const loadedModules = await this.loader.loadPlugins(uniquePaths);
914
+ if (this.options.verbose) {
915
+ console.log(`Successfully loaded ${loadedModules.size} plugins`);
916
+ }
917
+ const metadata = await this.metadataExtractor.extractBatch(loadedModules);
918
+ metadata.sort((a, b) => {
919
+ const locationOrder = {
920
+ current: 0,
921
+ downstream: 1,
922
+ upstream: 2
923
+ };
924
+ const locationDiff = locationOrder[a.location] - locationOrder[b.location];
925
+ if (locationDiff !== 0) return locationDiff;
926
+ return a.name.localeCompare(b.name);
927
+ });
928
+ return metadata;
929
+ } finally {
930
+ this.loader.cleanup();
931
+ }
932
+ }
933
+ async discoverDocuments(format) {
934
+ const startPath = this.searchPath;
935
+ const discoveryType = format === "pptx" ? "pptx-document" : "docx-document";
936
+ const ext = format === "pptx" ? ".pptx.json" : ".docx.json";
937
+ const allDocumentPaths = await this.searchDownstream(startPath, discoveryType);
938
+ const uniquePaths = this.scanner.deduplicatePaths(allDocumentPaths);
939
+ const metadata = [];
940
+ for (const docPath of uniquePaths) {
941
+ try {
942
+ const content = await fs5.readFile(docPath, "utf-8");
943
+ const json = JSON.parse(content);
944
+ const location = this.getFileLocation(docPath, startPath);
945
+ metadata.push({
946
+ name: path6.basename(docPath, ext),
947
+ path: docPath,
948
+ location,
949
+ type: json.name || "document",
950
+ title: json.props?.title || json.title,
951
+ description: json.props?.metadata?.description || json.description,
952
+ theme: json.props?.theme || json.theme
953
+ });
954
+ } catch {
955
+ }
956
+ }
957
+ metadata.sort((a, b) => {
958
+ const locationOrder = { current: 0, downstream: 1, upstream: 2 };
959
+ const locationDiff = locationOrder[a.location] - locationOrder[b.location];
960
+ if (locationDiff !== 0) return locationDiff;
961
+ return a.name.localeCompare(b.name);
962
+ });
963
+ return metadata;
964
+ }
965
+ async discoverThemes(format) {
966
+ const startPath = this.searchPath;
967
+ const discoveryType = format === "pptx" ? "pptx-theme" : "docx-theme";
968
+ const ext = format === "pptx" ? ".pptx.theme.json" : ".docx.theme.json";
969
+ const allThemePaths = await this.searchDownstream(startPath, discoveryType);
970
+ const uniquePaths = this.scanner.deduplicatePaths(allThemePaths);
971
+ const metadata = [];
972
+ for (const themePath of uniquePaths) {
973
+ try {
974
+ const content = await fs5.readFile(themePath, "utf-8");
975
+ const json = JSON.parse(content);
976
+ const location = this.getFileLocation(themePath, startPath);
977
+ metadata.push({
978
+ name: path6.basename(themePath, ext),
979
+ path: themePath,
980
+ location,
981
+ description: json.description || json.metadata?.description
982
+ });
983
+ } catch {
984
+ }
985
+ }
986
+ metadata.sort((a, b) => {
987
+ const locationOrder = { current: 0, downstream: 1, upstream: 2 };
988
+ const locationDiff = locationOrder[a.location] - locationOrder[b.location];
989
+ if (locationDiff !== 0) return locationDiff;
990
+ return a.name.localeCompare(b.name);
991
+ });
992
+ return metadata;
993
+ }
994
+ async getDocumentContent(name, format) {
995
+ const documents = await this.discoverDocuments(format);
996
+ const document = documents.find((doc) => doc.name === name);
997
+ if (!document) throw new Error(`Document '${name}' not found`);
998
+ return await fs5.readFile(document.path, "utf-8");
999
+ }
1000
+ async getThemeContent(name, format) {
1001
+ const themes = await this.discoverThemes(format);
1002
+ const theme = themes.find((t) => t.name === name);
1003
+ if (!theme) throw new Error(`Theme '${name}' not found`);
1004
+ return await fs5.readFile(theme.path, "utf-8");
1005
+ }
1006
+ async discoverAll(format) {
1007
+ const [plugins, documents, themes] = await Promise.all([
1008
+ this.discoverPlugins(),
1009
+ this.discoverDocuments(format),
1010
+ this.discoverThemes(format)
1011
+ ]);
1012
+ return { plugins, documents, themes };
1013
+ }
1014
+ getFileLocation(filePath, startPath) {
1015
+ const relative3 = path6.relative(startPath, filePath);
1016
+ if (relative3 === path6.basename(filePath)) {
1017
+ return "current";
1018
+ }
1019
+ return "downstream";
1020
+ }
1021
+ async searchDownstream(startPath, fileType = "plugin") {
1022
+ const resolvedPath = path6.resolve(startPath);
1023
+ const plugins = await this.scanner.scan(resolvedPath, fileType, {
1024
+ maxDepth: this.options.maxDepth || 10,
1025
+ excludeNodeModules: !this.options.includeNodeModules
1026
+ });
1027
+ if (await this.scanner.hasPackageJson(resolvedPath)) {
1028
+ const monorepoPlugins = await this.scanner.scanMonorepoLocations(
1029
+ resolvedPath,
1030
+ fileType
1031
+ );
1032
+ const downstreamMonorepoPlugins = monorepoPlugins.filter((p) => {
1033
+ const relative3 = path6.relative(resolvedPath, p);
1034
+ return !relative3.startsWith("..");
1035
+ });
1036
+ plugins.push(...downstreamMonorepoPlugins);
1037
+ }
1038
+ return plugins;
1039
+ }
1040
+ async hasPlugins() {
1041
+ const plugins = await this.discoverPlugins();
1042
+ return plugins.length > 0;
1043
+ }
1044
+ async getPluginByName(name) {
1045
+ const plugins = await this.discoverPlugins();
1046
+ return plugins.find((p) => p.name === name);
1047
+ }
1048
+ };
1049
+
1050
+ // src/services/plugin-registry.ts
1051
+ import * as path7 from "path";
1052
+
1053
+ // src/services/cache-events.ts
1054
+ import { EventEmitter } from "events";
1055
+ var CacheEventEmitter = class extends EventEmitter {
1056
+ emit(event, ...args) {
1057
+ return super.emit(event, ...args);
1058
+ }
1059
+ on(event, listener) {
1060
+ return super.on(event, listener);
1061
+ }
1062
+ off(event, listener) {
1063
+ return super.off(event, listener);
1064
+ }
1065
+ once(event, listener) {
1066
+ return super.once(event, listener);
1067
+ }
1068
+ };
1069
+ var cacheEvents = new CacheEventEmitter();
1070
+ function invalidateAllCaches() {
1071
+ cacheEvents.emit("cache:invalidate");
1072
+ cacheEvents.emit("schema:invalidate");
1073
+ cacheEvents.emit("generator:invalidate");
1074
+ }
1075
+
1076
+ // src/services/plugin-registry.ts
1077
+ var PluginRegistry = class _PluginRegistry {
1078
+ static instance;
1079
+ plugins = /* @__PURE__ */ new Map();
1080
+ pluginPaths = /* @__PURE__ */ new Map();
1081
+ pluginMetadata = /* @__PURE__ */ new Map();
1082
+ loader;
1083
+ discoveryService;
1084
+ constructor() {
1085
+ this.loader = new PluginLoader();
1086
+ this.discoveryService = new PluginDiscoveryService();
1087
+ }
1088
+ static getInstance() {
1089
+ if (!_PluginRegistry.instance) {
1090
+ _PluginRegistry.instance = new _PluginRegistry();
1091
+ }
1092
+ return _PluginRegistry.instance;
1093
+ }
1094
+ notifyCacheInvalidation() {
1095
+ try {
1096
+ invalidateAllCaches();
1097
+ } catch {
1098
+ }
1099
+ }
1100
+ async loadPlugin(pathOrName) {
1101
+ try {
1102
+ await this.loader.initialize();
1103
+ const pluginPath = await this.resolvePluginPath(pathOrName);
1104
+ if (!pluginPath) {
1105
+ throw new Error(`Plugin '${pathOrName}' not found`);
1106
+ }
1107
+ const module = await this.loader.loadPlugin(pluginPath);
1108
+ if (module) {
1109
+ this.plugins.set(module.name, module);
1110
+ this.pluginPaths.set(module.name, pluginPath);
1111
+ this.notifyCacheInvalidation();
1112
+ } else {
1113
+ throw new Error(`Failed to load valid component from ${pluginPath}`);
1114
+ }
1115
+ } catch (error) {
1116
+ throw new Error(
1117
+ `Failed to load plugin '${pathOrName}': ${error.message}`
1118
+ );
1119
+ }
1120
+ }
1121
+ async loadPlugins(pathsOrNames) {
1122
+ const errors = [];
1123
+ try {
1124
+ for (const pathOrName of pathsOrNames) {
1125
+ try {
1126
+ await this.loadPlugin(pathOrName);
1127
+ } catch (error) {
1128
+ errors.push(error.message);
1129
+ }
1130
+ }
1131
+ if (errors.length > 0) {
1132
+ throw new Error(
1133
+ `Failed to load ${errors.length} plugin(s): ${errors.join("; ")}`
1134
+ );
1135
+ }
1136
+ } finally {
1137
+ this.loader.cleanup();
1138
+ }
1139
+ }
1140
+ async loadPluginsFromMetadata(pluginMetadata) {
1141
+ let loadedCount = 0;
1142
+ for (const metadata of pluginMetadata) {
1143
+ try {
1144
+ const pluginPath = metadata.filePath;
1145
+ const module = await this.loader.loadPlugin(pluginPath);
1146
+ if (module) {
1147
+ this.plugins.set(module.name, module);
1148
+ this.pluginPaths.set(module.name, pluginPath);
1149
+ this.pluginMetadata.set(module.name, metadata);
1150
+ loadedCount++;
1151
+ this.notifyCacheInvalidation();
1152
+ }
1153
+ } catch {
1154
+ }
1155
+ }
1156
+ return loadedCount;
1157
+ }
1158
+ async loadPluginsFromDirectory(dir) {
1159
+ try {
1160
+ await this.loader.initialize();
1161
+ const discovery = new PluginDiscoveryService({
1162
+ scope: dir,
1163
+ maxDepth: 5
1164
+ });
1165
+ const pluginMetadata = await discovery.discover();
1166
+ return await this.loadPluginsFromMetadata(pluginMetadata);
1167
+ } catch (error) {
1168
+ throw new Error(
1169
+ `Failed to load plugins from directory: ${error.message}`
1170
+ );
1171
+ } finally {
1172
+ this.loader.cleanup();
1173
+ }
1174
+ }
1175
+ async discoverAndLoad() {
1176
+ try {
1177
+ await this.loader.initialize();
1178
+ const pluginMetadata = await this.discoveryService.discover();
1179
+ const discovered = pluginMetadata.length;
1180
+ if (discovered === 0) {
1181
+ return { discovered: 0, loaded: 0 };
1182
+ }
1183
+ const loaded = await this.loadPluginsFromMetadata(pluginMetadata);
1184
+ return { discovered, loaded };
1185
+ } catch (error) {
1186
+ throw new Error(`Failed to discover and load plugins: ${error.message}`);
1187
+ } finally {
1188
+ this.loader.cleanup();
1189
+ }
1190
+ }
1191
+ async resolvePluginName(name) {
1192
+ if (this.pluginPaths.has(name)) {
1193
+ return this.pluginPaths.get(name);
1194
+ }
1195
+ try {
1196
+ const pluginMetadata = await this.discoveryService.discover();
1197
+ const metadata = pluginMetadata.find((p) => p.name === name);
1198
+ if (metadata) return metadata.filePath;
1199
+ } catch {
1200
+ }
1201
+ return void 0;
1202
+ }
1203
+ async resolvePluginPath(pathOrName) {
1204
+ if (pathOrName.endsWith(".ts") || pathOrName.endsWith(".js")) {
1205
+ return path7.resolve(process.cwd(), pathOrName);
1206
+ }
1207
+ return await this.resolvePluginName(pathOrName);
1208
+ }
1209
+ getPlugins() {
1210
+ return Array.from(this.plugins.values());
1211
+ }
1212
+ getPluginNames() {
1213
+ return Array.from(this.plugins.keys());
1214
+ }
1215
+ getPlugin(name) {
1216
+ return this.plugins.get(name);
1217
+ }
1218
+ hasPlugins() {
1219
+ return this.plugins.size > 0;
1220
+ }
1221
+ getPluginCount() {
1222
+ return this.plugins.size;
1223
+ }
1224
+ clear() {
1225
+ this.plugins.clear();
1226
+ this.pluginPaths.clear();
1227
+ this.pluginMetadata.clear();
1228
+ this.loader.cleanup();
1229
+ this.notifyCacheInvalidation();
1230
+ }
1231
+ static cleanup() {
1232
+ if (_PluginRegistry.instance) {
1233
+ _PluginRegistry.instance.clear();
1234
+ }
1235
+ }
1236
+ getPluginMetadata(name) {
1237
+ return this.pluginMetadata.get(name);
1238
+ }
1239
+ getAllPluginMetadata() {
1240
+ return Array.from(this.pluginMetadata.values());
1241
+ }
1242
+ };
1243
+
1244
+ // src/services/generator-factory.ts
1245
+ var GeneratorFactory = class {
1246
+ registry;
1247
+ adapter;
1248
+ constructor(adapter) {
1249
+ this.registry = PluginRegistry.getInstance();
1250
+ this.adapter = adapter;
1251
+ }
1252
+ async createGenerator(options = {}) {
1253
+ const plugins = this.registry.getPlugins();
1254
+ return this.adapter.createGenerator(plugins, options);
1255
+ }
1256
+ async generate(document, options = {}) {
1257
+ const generator = await this.createGenerator(options);
1258
+ return await generator.generateBuffer(document);
1259
+ }
1260
+ getPluginInfo() {
1261
+ return {
1262
+ hasPlugins: this.registry.hasPlugins(),
1263
+ count: this.registry.getPluginCount(),
1264
+ names: this.registry.getPluginNames()
1265
+ };
1266
+ }
1267
+ };
1268
+
1269
+ // src/services/plugin-resolver.ts
1270
+ import * as fs6 from "fs/promises";
1271
+ import * as path8 from "path";
1272
+ var PluginResolver = class {
1273
+ discoveryService;
1274
+ discoveredPlugins = /* @__PURE__ */ new Map();
1275
+ lastDiscoveryTime = 0;
1276
+ CACHE_DURATION = 5 * 60 * 1e3;
1277
+ constructor() {
1278
+ this.discoveryService = new PluginDiscoveryService();
1279
+ }
1280
+ async resolve(input) {
1281
+ const pathResult = await this.resolveAsPath(input);
1282
+ if (pathResult) return pathResult;
1283
+ const nameResult = await this.resolveAsName(input);
1284
+ if (nameResult) return nameResult;
1285
+ await this.refreshDiscoveryCache();
1286
+ const discoveredResult = await this.resolveAsName(input);
1287
+ if (discoveredResult) return discoveredResult;
1288
+ throw await this.createNotFoundError(input);
1289
+ }
1290
+ async resolveMultiple(inputs) {
1291
+ const resolved = /* @__PURE__ */ new Map();
1292
+ const errors = [];
1293
+ for (const input of inputs) {
1294
+ try {
1295
+ const resolvedPath = await this.resolve(input);
1296
+ resolved.set(input, resolvedPath);
1297
+ } catch (error) {
1298
+ errors.push(`${input}: ${error.message}`);
1299
+ }
1300
+ }
1301
+ if (errors.length > 0 && errors.length === inputs.length) {
1302
+ throw new Error(`Failed to resolve plugins:
1303
+ ${errors.join("\n")}`);
1304
+ }
1305
+ return resolved;
1306
+ }
1307
+ async resolveAsPath(input) {
1308
+ if (!this.looksLikePath(input)) return null;
1309
+ const resolvedPath = path8.resolve(process.cwd(), input);
1310
+ try {
1311
+ const stats = await fs6.stat(resolvedPath);
1312
+ if (stats.isFile()) return resolvedPath;
1313
+ } catch {
1314
+ }
1315
+ return null;
1316
+ }
1317
+ async resolveAsName(name) {
1318
+ if (this.discoveredPlugins.size === 0 || this.isCacheExpired()) {
1319
+ await this.refreshDiscoveryCache();
1320
+ }
1321
+ const plugin = this.discoveredPlugins.get(name);
1322
+ if (plugin) return plugin.filePath;
1323
+ const lowerName = name.toLowerCase();
1324
+ for (const [pluginName, metadata] of this.discoveredPlugins) {
1325
+ if (pluginName.toLowerCase() === lowerName) return metadata.filePath;
1326
+ }
1327
+ return null;
1328
+ }
1329
+ async refreshDiscoveryCache() {
1330
+ try {
1331
+ const plugins = await this.discoveryService.discover();
1332
+ this.discoveredPlugins.clear();
1333
+ for (const plugin of plugins) {
1334
+ this.discoveredPlugins.set(plugin.name, plugin);
1335
+ }
1336
+ this.lastDiscoveryTime = Date.now();
1337
+ } catch (error) {
1338
+ console.warn(`Plugin discovery failed: ${error.message}`);
1339
+ }
1340
+ }
1341
+ isCacheExpired() {
1342
+ return Date.now() - this.lastDiscoveryTime > this.CACHE_DURATION;
1343
+ }
1344
+ looksLikePath(input) {
1345
+ if (input.endsWith(".ts") || input.endsWith(".js")) return true;
1346
+ if (input.includes("/") || input.includes("\\")) return true;
1347
+ if (input.startsWith("./") || input.startsWith("../")) return true;
1348
+ if (path8.isAbsolute(input)) return true;
1349
+ return false;
1350
+ }
1351
+ async createNotFoundError(input) {
1352
+ let message = `Plugin '${input}' not found.`;
1353
+ if (this.discoveredPlugins.size > 0) {
1354
+ const suggestions = this.findSimilarNames(input);
1355
+ if (suggestions.length > 0) {
1356
+ message += "\n\nDid you mean one of these?";
1357
+ suggestions.forEach((name) => {
1358
+ message += `
1359
+ - ${name}`;
1360
+ });
1361
+ } else {
1362
+ message += "\n\nAvailable plugins:";
1363
+ Array.from(this.discoveredPlugins.keys()).slice(0, 5).forEach((name) => {
1364
+ message += `
1365
+ - ${name}`;
1366
+ });
1367
+ if (this.discoveredPlugins.size > 5) {
1368
+ message += `
1369
+ ... and ${this.discoveredPlugins.size - 5} more`;
1370
+ }
1371
+ }
1372
+ }
1373
+ message += "\n\nUse 'jto <format> discover' to list all available plugins.";
1374
+ return new Error(message);
1375
+ }
1376
+ findSimilarNames(input) {
1377
+ const inputLower = input.toLowerCase();
1378
+ const suggestions = [];
1379
+ for (const name of this.discoveredPlugins.keys()) {
1380
+ const nameLower = name.toLowerCase();
1381
+ if (nameLower.includes(inputLower) || inputLower.includes(nameLower)) {
1382
+ suggestions.push(name);
1383
+ } else if (nameLower.startsWith(inputLower[0])) {
1384
+ suggestions.push(name);
1385
+ }
1386
+ if (suggestions.length >= 3) break;
1387
+ }
1388
+ return suggestions;
1389
+ }
1390
+ async getAvailablePluginNames() {
1391
+ if (this.discoveredPlugins.size === 0 || this.isCacheExpired()) {
1392
+ await this.refreshDiscoveryCache();
1393
+ }
1394
+ return Array.from(this.discoveredPlugins.keys());
1395
+ }
1396
+ clearCache() {
1397
+ this.discoveredPlugins.clear();
1398
+ this.lastDiscoveryTime = 0;
1399
+ }
1400
+ };
1401
+
1402
+ // src/services/schema-generator.ts
1403
+ import * as path9 from "path";
1404
+ import * as fs8 from "fs/promises";
1405
+
1406
+ // src/services/typebox-exporter.ts
1407
+ import * as fs7 from "fs/promises";
1408
+ var TypeBoxExporter = class {
1409
+ formatName;
1410
+ constructor(formatName = "docx") {
1411
+ this.formatName = formatName;
1412
+ }
1413
+ get sharedPackage() {
1414
+ return this.formatName === "docx" ? "@json-to-office/shared-docx" : "@json-to-office/shared-pptx";
1415
+ }
1416
+ async exportDocumentSchema(schema, outputPath, customComponents) {
1417
+ const hasCustomComponents = customComponents.length > 0;
1418
+ const content = [
1419
+ "/**",
1420
+ ` * Generated TypeBox Document Schema (${this.formatName.toUpperCase()})`,
1421
+ hasCustomComponents ? " * Includes standard components and custom plugin components" : " * Includes standard components only",
1422
+ " * Generated on: " + (/* @__PURE__ */ new Date()).toISOString(),
1423
+ " */",
1424
+ "",
1425
+ "import { Type, type Static } from '@sinclair/typebox';",
1426
+ `// Import component schemas from '${this.sharedPackage}'`,
1427
+ "",
1428
+ "export const DocumentSchema = Type.Any(); // TODO: generate full recursive schema",
1429
+ "",
1430
+ "export type DocumentType = Static<typeof DocumentSchema>;",
1431
+ ""
1432
+ ].join("\n");
1433
+ await fs7.writeFile(outputPath, content, "utf-8");
1434
+ }
1435
+ async exportThemeSchema(schema, outputPath) {
1436
+ const content = [
1437
+ "/**",
1438
+ ` * Generated TypeBox Theme Schema (${this.formatName.toUpperCase()})`,
1439
+ " * Generated on: " + (/* @__PURE__ */ new Date()).toISOString(),
1440
+ " */",
1441
+ "",
1442
+ "import { type Static } from '@sinclair/typebox';",
1443
+ "",
1444
+ `export { ThemeConfigSchema } from '${this.sharedPackage}';`,
1445
+ "",
1446
+ `import { ThemeConfigSchema } from '${this.sharedPackage}';`,
1447
+ "export type ThemeType = Static<typeof ThemeConfigSchema>;",
1448
+ ""
1449
+ ].join("\n");
1450
+ await fs7.writeFile(outputPath, content, "utf-8");
1451
+ }
1452
+ async exportComponentSchema(componentName, schema, outputPath, metadata) {
1453
+ const pascalName = this.toPascalCase(componentName);
1454
+ const content = [
1455
+ "/**",
1456
+ ` * Generated TypeBox Component Schema: ${componentName}`,
1457
+ metadata?.description ? ` * ${metadata.description}` : "",
1458
+ " * Generated on: " + (/* @__PURE__ */ new Date()).toISOString(),
1459
+ " */",
1460
+ "",
1461
+ "import { Type, type Static } from '@sinclair/typebox';",
1462
+ "",
1463
+ `export const ${pascalName}ComponentSchema = Type.Object({`,
1464
+ ` name: Type.Literal('${componentName}'),`,
1465
+ " id: Type.Optional(Type.String()),",
1466
+ " props: Type.Any(),",
1467
+ "});",
1468
+ "",
1469
+ `export type ${pascalName}ComponentType = Static<typeof ${pascalName}ComponentSchema>;`,
1470
+ ""
1471
+ ].filter((line) => line !== "").join("\n");
1472
+ await fs7.writeFile(outputPath, content, "utf-8");
1473
+ }
1474
+ toPascalCase(str) {
1475
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
1476
+ }
1477
+ };
1478
+
1479
+ // src/services/schema-generator.ts
1480
+ var SchemaGenerator = class {
1481
+ registry;
1482
+ typeboxExporter;
1483
+ formatName;
1484
+ constructor(formatName = "docx") {
1485
+ this.registry = PluginRegistry.getInstance();
1486
+ this.typeboxExporter = new TypeBoxExporter(formatName);
1487
+ this.formatName = formatName;
1488
+ }
1489
+ async generateAndExportSchemas(outputDir, options = {}) {
1490
+ const {
1491
+ includeDocument = true,
1492
+ includeTheme = true,
1493
+ split = false,
1494
+ format = "json"
1495
+ } = options;
1496
+ const results = {};
1497
+ await fs8.mkdir(outputDir, { recursive: true });
1498
+ const customComponents = this.getCustomComponents();
1499
+ if (includeDocument) {
1500
+ const documentPath = await this.generateDocumentSchema(
1501
+ outputDir,
1502
+ customComponents,
1503
+ format
1504
+ );
1505
+ results.document = documentPath;
1506
+ }
1507
+ if (includeTheme) {
1508
+ const themePath = await this.generateThemeSchema(outputDir, format);
1509
+ results.theme = themePath;
1510
+ }
1511
+ if (split) {
1512
+ const componentPaths = await this.generateComponentSchemas(
1513
+ outputDir,
1514
+ customComponents,
1515
+ format
1516
+ );
1517
+ results.components = componentPaths;
1518
+ }
1519
+ return results;
1520
+ }
1521
+ async generateDocumentSchema(outputDir, customComponents, format) {
1522
+ if (this.formatName === "docx") {
1523
+ const { generateUnifiedDocumentSchema } = await import("@json-to-office/shared-docx");
1524
+ const schema = generateUnifiedDocumentSchema({
1525
+ includeStandardComponents: true,
1526
+ includeTheme: false,
1527
+ customComponents: customComponents.map((m) => ({
1528
+ name: m.name,
1529
+ propsSchema: m.propsSchema,
1530
+ hasChildren: m.hasChildren,
1531
+ versionedProps: m.versionedProps
1532
+ })),
1533
+ title: "JSON Document Definition",
1534
+ description: customComponents.length > 0 ? "Document definition with standard and custom plugin components" : "Document definition with standard components"
1535
+ });
1536
+ if (format === "json") {
1537
+ const { convertToJsonSchema, exportSchemaToFile } = await import("@json-to-office/shared");
1538
+ const jsonSchema = convertToJsonSchema(schema, {
1539
+ $id: "document.schema.json"
1540
+ });
1541
+ const outputPath = path9.join(outputDir, "document.schema.json");
1542
+ await exportSchemaToFile(jsonSchema, outputPath, { prettyPrint: true });
1543
+ return outputPath;
1544
+ } else {
1545
+ const outputPath = path9.join(outputDir, "document.schema.ts");
1546
+ await this.typeboxExporter.exportDocumentSchema(
1547
+ schema,
1548
+ outputPath,
1549
+ customComponents
1550
+ );
1551
+ return outputPath;
1552
+ }
1553
+ } else {
1554
+ const { generateUnifiedDocumentSchema } = await import("@json-to-office/shared-pptx");
1555
+ const schema = generateUnifiedDocumentSchema({
1556
+ customComponents: customComponents.map((m) => ({
1557
+ name: m.name,
1558
+ versions: m.versionedProps ? m.versionedProps.map((vp) => ({
1559
+ version: vp.version,
1560
+ propsSchema: vp.propsSchema,
1561
+ hasChildren: vp.hasChildren
1562
+ })) : [
1563
+ {
1564
+ version: "1.0.0",
1565
+ propsSchema: m.propsSchema,
1566
+ hasChildren: m.hasChildren
1567
+ }
1568
+ ]
1569
+ }))
1570
+ });
1571
+ if (format === "json") {
1572
+ const { convertToJsonSchema, exportSchemaToFile } = await import("@json-to-office/shared");
1573
+ const jsonSchema = convertToJsonSchema(schema, {
1574
+ $id: "presentation.schema.json"
1575
+ });
1576
+ const outputPath = path9.join(outputDir, "presentation.schema.json");
1577
+ await exportSchemaToFile(jsonSchema, outputPath, { prettyPrint: true });
1578
+ return outputPath;
1579
+ } else {
1580
+ const outputPath = path9.join(outputDir, "presentation.schema.ts");
1581
+ await this.typeboxExporter.exportDocumentSchema(
1582
+ schema,
1583
+ outputPath,
1584
+ customComponents
1585
+ );
1586
+ return outputPath;
1587
+ }
1588
+ }
1589
+ }
1590
+ async generateThemeSchema(outputDir, format) {
1591
+ const sharedPkg = this.formatName === "docx" ? "@json-to-office/shared-docx" : "@json-to-office/shared-pptx";
1592
+ const shared = await import(sharedPkg);
1593
+ const ThemeConfigSchema = shared.ThemeConfigSchema;
1594
+ if (format === "json") {
1595
+ const { convertToJsonSchema, exportSchemaToFile } = await import("@json-to-office/shared");
1596
+ const jsonSchema = convertToJsonSchema(ThemeConfigSchema, {
1597
+ $id: "theme.schema.json",
1598
+ title: "Theme Configuration",
1599
+ description: "Theme configuration for styling"
1600
+ });
1601
+ const outputPath = path9.join(outputDir, "theme.schema.json");
1602
+ await exportSchemaToFile(jsonSchema, outputPath, { prettyPrint: true });
1603
+ return outputPath;
1604
+ } else {
1605
+ const outputPath = path9.join(outputDir, "theme.schema.ts");
1606
+ await this.typeboxExporter.exportThemeSchema(
1607
+ ThemeConfigSchema,
1608
+ outputPath
1609
+ );
1610
+ return outputPath;
1611
+ }
1612
+ }
1613
+ async generateComponentSchemas(outputDir, customComponents, format) {
1614
+ const componentsDir = path9.join(outputDir, "components");
1615
+ await fs8.mkdir(componentsDir, { recursive: true });
1616
+ const paths = [];
1617
+ const standardComponents = await this.getStandardComponentSchemas();
1618
+ for (const [type, schema] of Object.entries(standardComponents)) {
1619
+ if (format === "json") {
1620
+ const { convertToJsonSchema, exportSchemaToFile } = await import("@json-to-office/shared");
1621
+ const jsonSchema = convertToJsonSchema(schema, {
1622
+ $id: `${type}.schema.json`,
1623
+ title: `${type} Component`,
1624
+ description: `${type} component configuration`
1625
+ });
1626
+ const outputPath = path9.join(componentsDir, `${type}.schema.json`);
1627
+ await exportSchemaToFile(jsonSchema, outputPath, { prettyPrint: true });
1628
+ paths.push(outputPath);
1629
+ } else {
1630
+ const outputPath = path9.join(componentsDir, `${type}.schema.ts`);
1631
+ await this.typeboxExporter.exportComponentSchema(type, schema, outputPath);
1632
+ paths.push(outputPath);
1633
+ }
1634
+ }
1635
+ for (const customComponent of customComponents) {
1636
+ if (format === "json") {
1637
+ const { convertToJsonSchema, exportSchemaToFile } = await import("@json-to-office/shared");
1638
+ const jsonSchema = convertToJsonSchema(customComponent.propsSchema, {
1639
+ $id: `${customComponent.name}.schema.json`,
1640
+ title: `${customComponent.name} Component`,
1641
+ description: `Custom ${customComponent.name} component configuration`
1642
+ });
1643
+ const outputPath = path9.join(
1644
+ componentsDir,
1645
+ `${customComponent.name}.schema.json`
1646
+ );
1647
+ await exportSchemaToFile(jsonSchema, outputPath, { prettyPrint: true });
1648
+ paths.push(outputPath);
1649
+ } else {
1650
+ const outputPath = path9.join(
1651
+ componentsDir,
1652
+ `${customComponent.name}.schema.ts`
1653
+ );
1654
+ await this.typeboxExporter.exportComponentSchema(
1655
+ customComponent.name,
1656
+ customComponent.propsSchema,
1657
+ outputPath
1658
+ );
1659
+ paths.push(outputPath);
1660
+ }
1661
+ }
1662
+ return paths;
1663
+ }
1664
+ async getStandardComponentSchemas() {
1665
+ if (this.formatName === "docx") {
1666
+ const shared = await import("@json-to-office/shared-docx");
1667
+ return {
1668
+ report: shared.ReportPropsSchema,
1669
+ section: shared.SectionPropsSchema,
1670
+ columns: shared.ColumnsPropsSchema,
1671
+ heading: shared.HeadingPropsSchema,
1672
+ paragraph: shared.ParagraphPropsSchema,
1673
+ image: shared.ImagePropsSchema,
1674
+ statistic: shared.StatisticPropsSchema,
1675
+ table: shared.TablePropsSchema,
1676
+ header: shared.HeaderPropsSchema,
1677
+ footer: shared.FooterPropsSchema,
1678
+ list: shared.ListPropsSchema
1679
+ };
1680
+ } else {
1681
+ const shared = await import("@json-to-office/shared-pptx");
1682
+ return {
1683
+ presentation: shared.PresentationPropsSchema,
1684
+ slide: shared.SlidePropsSchema,
1685
+ text: shared.TextPropsSchema,
1686
+ image: shared.PptxImagePropsSchema,
1687
+ shape: shared.ShapePropsSchema,
1688
+ table: shared.PptxTablePropsSchema
1689
+ };
1690
+ }
1691
+ }
1692
+ getCustomComponents() {
1693
+ const loadedPlugins = this.registry.getPlugins();
1694
+ const customComponents = [];
1695
+ for (const plugin of loadedPlugins) {
1696
+ const component = plugin;
1697
+ const versions = component.versions;
1698
+ if (component.name && versions && typeof versions === "object") {
1699
+ const versionKeys = Object.keys(versions);
1700
+ if (versionKeys.length > 0) {
1701
+ const versionedProps = versionKeys.map((v) => ({
1702
+ version: v,
1703
+ propsSchema: versions[v].propsSchema,
1704
+ hasChildren: versions[v].hasChildren === true
1705
+ }));
1706
+ const hasChildren = versionKeys.some(
1707
+ (v) => versions[v].hasChildren === true
1708
+ );
1709
+ const latestKey = versionKeys.reduce((a, b) => a > b ? a : b);
1710
+ customComponents.push({
1711
+ name: component.name,
1712
+ propsSchema: versions[latestKey].propsSchema,
1713
+ hasChildren,
1714
+ versionedProps
1715
+ });
1716
+ }
1717
+ }
1718
+ }
1719
+ return customComponents;
1720
+ }
1721
+ };
1722
+
1723
+ // src/services/json-validator.ts
1724
+ import { readFileSync as readFileSync3, statSync as statSync2, readdirSync } from "fs";
1725
+ import { resolve as resolve8, join as join4, extname } from "path";
1726
+ import * as glob2 from "glob";
1727
+ var JsonValidator = class {
1728
+ format;
1729
+ constructor(format = "docx") {
1730
+ this.format = format;
1731
+ }
1732
+ async validate(pathOrPattern, options = {}) {
1733
+ const results = [];
1734
+ const files = await this.getFilesToValidate(pathOrPattern, options);
1735
+ for (const file of files) {
1736
+ const result = await this.validateFile(file, options);
1737
+ results.push(result);
1738
+ }
1739
+ return results;
1740
+ }
1741
+ async validateFile(filePath, options = {}) {
1742
+ const absolutePath = resolve8(filePath);
1743
+ try {
1744
+ const content = readFileSync3(absolutePath, "utf-8");
1745
+ let jsonData;
1746
+ try {
1747
+ jsonData = JSON.parse(content);
1748
+ } catch (error) {
1749
+ return {
1750
+ file: filePath,
1751
+ valid: false,
1752
+ errors: [
1753
+ {
1754
+ path: "root",
1755
+ message: `Invalid JSON: ${error.message}`,
1756
+ code: "json_parse_error"
1757
+ }
1758
+ ]
1759
+ };
1760
+ }
1761
+ const validationType = options.type === "auto" || !options.type ? this.detectType(jsonData) : options.type;
1762
+ if (options.schema) {
1763
+ return await this.validateWithCustomSchema(
1764
+ filePath,
1765
+ jsonData,
1766
+ options.schema,
1767
+ options.strict
1768
+ );
1769
+ } else if (validationType === "document") {
1770
+ return await this.validateAsDocument(
1771
+ filePath,
1772
+ jsonData,
1773
+ content,
1774
+ options.strict
1775
+ );
1776
+ } else if (validationType === "theme") {
1777
+ return await this.validateAsTheme(
1778
+ filePath,
1779
+ jsonData,
1780
+ content,
1781
+ options.strict
1782
+ );
1783
+ } else {
1784
+ return {
1785
+ file: filePath,
1786
+ valid: false,
1787
+ errors: [
1788
+ {
1789
+ path: "root",
1790
+ message: "Could not determine JSON type (document or theme). Use --type to specify.",
1791
+ code: "unknown_type"
1792
+ }
1793
+ ]
1794
+ };
1795
+ }
1796
+ } catch (error) {
1797
+ return {
1798
+ file: filePath,
1799
+ valid: false,
1800
+ errors: [
1801
+ {
1802
+ path: "file",
1803
+ message: error.message,
1804
+ code: "file_error"
1805
+ }
1806
+ ]
1807
+ };
1808
+ }
1809
+ }
1810
+ async validateAsDocument(filePath, jsonData, jsonString, strict) {
1811
+ try {
1812
+ if (this.format === "docx") {
1813
+ const { validate, validateStrict } = await import("@json-to-office/shared-docx");
1814
+ const validator = strict ? validateStrict : validate;
1815
+ const result = validator.jsonDocument(jsonString);
1816
+ return {
1817
+ file: filePath,
1818
+ valid: result.valid,
1819
+ type: "document",
1820
+ errors: result.errors?.map((e) => ({
1821
+ ...e,
1822
+ code: e.code || "VALIDATION_ERROR"
1823
+ })),
1824
+ warnings: result.warnings?.map((e) => ({
1825
+ ...e,
1826
+ code: e.code || "WARNING"
1827
+ }))
1828
+ };
1829
+ } else {
1830
+ return {
1831
+ file: filePath,
1832
+ valid: true,
1833
+ type: "document"
1834
+ };
1835
+ }
1836
+ } catch {
1837
+ return { file: filePath, valid: true, type: "document" };
1838
+ }
1839
+ }
1840
+ async validateAsTheme(filePath, jsonData, jsonString, strict) {
1841
+ try {
1842
+ if (this.format === "docx") {
1843
+ const { validate, validateStrict } = await import("@json-to-office/shared-docx");
1844
+ const validator = strict ? validateStrict : validate;
1845
+ const result = validator.jsonTheme(jsonString);
1846
+ return {
1847
+ file: filePath,
1848
+ valid: result.valid,
1849
+ type: "theme",
1850
+ errors: result.errors?.map((e) => ({
1851
+ ...e,
1852
+ code: e.code || "VALIDATION_ERROR"
1853
+ }))
1854
+ };
1855
+ } else {
1856
+ return { file: filePath, valid: true, type: "theme" };
1857
+ }
1858
+ } catch {
1859
+ return { file: filePath, valid: true, type: "theme" };
1860
+ }
1861
+ }
1862
+ async validateWithCustomSchema(filePath, jsonData, schemaPath, strict) {
1863
+ try {
1864
+ const schemaContent = readFileSync3(resolve8(schemaPath), "utf-8");
1865
+ const schema = JSON.parse(schemaContent);
1866
+ const Ajv = (await import("ajv")).default;
1867
+ const addFormats = (await import("ajv-formats")).default;
1868
+ const ajv = new Ajv({
1869
+ allErrors: true,
1870
+ verbose: true,
1871
+ strict: strict ?? false
1872
+ });
1873
+ addFormats(ajv);
1874
+ const validate = ajv.compile(schema);
1875
+ const valid = validate(jsonData);
1876
+ const errors = validate.errors?.map((error) => ({
1877
+ path: error.instancePath || "root",
1878
+ message: error.message || "Validation error",
1879
+ code: error.keyword || "validation_error",
1880
+ value: error.data
1881
+ })) || [];
1882
+ return {
1883
+ file: filePath,
1884
+ valid,
1885
+ type: "custom",
1886
+ errors: valid ? void 0 : errors
1887
+ };
1888
+ } catch (error) {
1889
+ return {
1890
+ file: filePath,
1891
+ valid: false,
1892
+ type: "custom",
1893
+ errors: [
1894
+ {
1895
+ path: "schema",
1896
+ message: `Failed to load or compile schema: ${error.message}`,
1897
+ code: "schema_error"
1898
+ }
1899
+ ]
1900
+ };
1901
+ }
1902
+ }
1903
+ detectType(jsonData) {
1904
+ if (jsonData.name === "docx" || jsonData.name === "pptx" || jsonData.children && Array.isArray(jsonData.children) || jsonData.slides && Array.isArray(jsonData.slides) || jsonData.props && jsonData.props.metadata?.title) {
1905
+ return "document";
1906
+ }
1907
+ if (jsonData.colors || jsonData.fonts || jsonData.styles || jsonData.pageSetup || jsonData.componentDefaults || jsonData.name && (jsonData.colors || jsonData.fonts)) {
1908
+ return "theme";
1909
+ }
1910
+ return null;
1911
+ }
1912
+ async getFilesToValidate(pathOrPattern, options) {
1913
+ const resolvedPath = resolve8(pathOrPattern);
1914
+ try {
1915
+ const stats = statSync2(resolvedPath);
1916
+ if (stats.isFile()) {
1917
+ return [resolvedPath];
1918
+ } else if (stats.isDirectory()) {
1919
+ if (options.recursive) {
1920
+ const pattern = join4(resolvedPath, "**/*.json");
1921
+ return glob2.glob(pattern, {
1922
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"]
1923
+ });
1924
+ } else {
1925
+ const files = readdirSync(resolvedPath);
1926
+ return files.filter((file) => extname(file).toLowerCase() === ".json").map((file) => join4(resolvedPath, file));
1927
+ }
1928
+ }
1929
+ } catch {
1930
+ return glob2.glob(pathOrPattern, {
1931
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**"]
1932
+ });
1933
+ }
1934
+ return [];
1935
+ }
1936
+ formatError(error, indent = 0) {
1937
+ const spaces = " ".repeat(indent);
1938
+ let output = `${spaces}* ${error.path}: ${error.message}`;
1939
+ if (error.line && error.column) {
1940
+ output += ` (line ${error.line}, column ${error.column})`;
1941
+ }
1942
+ if (error.suggestion) {
1943
+ output += `
1944
+ ${spaces} -> ${error.suggestion}`;
1945
+ }
1946
+ return output;
1947
+ }
1948
+ formatResultsAsJson(results) {
1949
+ return JSON.stringify(results, null, 2);
1950
+ }
1951
+ };
1952
+
1953
+ // src/config/plugin-config.ts
1954
+ import * as fs9 from "fs/promises";
1955
+ import * as path10 from "path";
1956
+ import { cosmiconfig } from "cosmiconfig";
1957
+ var PluginConfigService = class _PluginConfigService {
1958
+ static instance;
1959
+ config = null;
1960
+ configPath = null;
1961
+ static CONFIG_FILES = [
1962
+ ".json-to-office.config.json",
1963
+ ".json-to-office.config.js",
1964
+ "json-to-office.config.json",
1965
+ "json-to-office.config.js",
1966
+ ".json-to-officerc",
1967
+ ".json-to-officerc.json",
1968
+ ".json-to-officerc.js",
1969
+ // Legacy support
1970
+ ".json-to-docx.config.json",
1971
+ ".json-to-docx.config.js",
1972
+ "json-to-docx.config.json",
1973
+ ".json-to-pptx.config.json",
1974
+ ".json-to-pptx.config.js",
1975
+ "json-to-pptx.config.json"
1976
+ ];
1977
+ constructor() {
1978
+ }
1979
+ static getInstance() {
1980
+ if (!_PluginConfigService.instance) {
1981
+ _PluginConfigService.instance = new _PluginConfigService();
1982
+ }
1983
+ return _PluginConfigService.instance;
1984
+ }
1985
+ async loadConfig(startPath) {
1986
+ try {
1987
+ const explorer = cosmiconfig("json-to-office", {
1988
+ searchPlaces: _PluginConfigService.CONFIG_FILES,
1989
+ stopDir: path10.parse(startPath || process.cwd()).root
1990
+ });
1991
+ const result = await explorer.search(startPath);
1992
+ if (result) {
1993
+ this.config = result.config;
1994
+ this.configPath = result.filepath;
1995
+ return this.config;
1996
+ }
1997
+ } catch (error) {
1998
+ console.warn(`Failed to load configuration: ${error.message}`);
1999
+ }
2000
+ const packageConfig = await this.loadFromPackageJson(startPath);
2001
+ if (packageConfig) {
2002
+ this.config = packageConfig;
2003
+ return this.config;
2004
+ }
2005
+ return null;
2006
+ }
2007
+ async loadFromPackageJson(startPath) {
2008
+ try {
2009
+ const packagePath = path10.join(startPath || process.cwd(), "package.json");
2010
+ const packageContent = await fs9.readFile(packagePath, "utf-8");
2011
+ const packageJson = JSON.parse(packageContent);
2012
+ const config = packageJson["json-to-office"] || packageJson["json-to-docx"] || packageJson["json-to-pptx"];
2013
+ if (config) {
2014
+ this.configPath = packagePath;
2015
+ return config;
2016
+ }
2017
+ } catch {
2018
+ }
2019
+ return null;
2020
+ }
2021
+ getConfig() {
2022
+ return this.config;
2023
+ }
2024
+ getConfigPath() {
2025
+ return this.configPath;
2026
+ }
2027
+ mergeWithOptions(options) {
2028
+ const base = this.config || {};
2029
+ return {
2030
+ ...base,
2031
+ ...options,
2032
+ discovery: {
2033
+ ...base.discovery,
2034
+ ...options.discovery
2035
+ },
2036
+ validation: {
2037
+ ...base.validation,
2038
+ ...options.validation
2039
+ },
2040
+ plugins: this.mergeArrays(base.plugins, options.plugins),
2041
+ pluginDirs: this.mergeArrays(base.pluginDirs, options.pluginDirs),
2042
+ aliases: {
2043
+ ...base.aliases,
2044
+ ...options.aliases
2045
+ }
2046
+ };
2047
+ }
2048
+ mergeArrays(arr1, arr2) {
2049
+ if (!arr1 && !arr2) return void 0;
2050
+ if (!arr1) return arr2;
2051
+ if (!arr2) return arr1;
2052
+ return Array.from(/* @__PURE__ */ new Set([...arr1, ...arr2]));
2053
+ }
2054
+ resolveAlias(name) {
2055
+ if (this.config?.aliases && this.config.aliases[name]) {
2056
+ return this.config.aliases[name];
2057
+ }
2058
+ return name;
2059
+ }
2060
+ getConfiguredPlugins() {
2061
+ return this.config?.plugins || [];
2062
+ }
2063
+ getPluginDirectories() {
2064
+ const dirs = this.config?.pluginDirs || [];
2065
+ if (this.configPath) {
2066
+ const configDir = path10.dirname(this.configPath);
2067
+ return dirs.map((dir) => {
2068
+ if (path10.isAbsolute(dir)) return dir;
2069
+ return path10.resolve(configDir, dir);
2070
+ });
2071
+ }
2072
+ return dirs.map((dir) => path10.resolve(process.cwd(), dir));
2073
+ }
2074
+ isAutoDiscoverEnabled() {
2075
+ return this.config?.autoDiscover ?? false;
2076
+ }
2077
+ async saveConfig(config, filePath) {
2078
+ const targetPath = filePath || this.configPath || path10.join(process.cwd(), ".json-to-office.config.json");
2079
+ const content = JSON.stringify(config, null, 2);
2080
+ await fs9.writeFile(targetPath, content, "utf-8");
2081
+ this.config = config;
2082
+ this.configPath = targetPath;
2083
+ }
2084
+ async createDefaultConfig(filePath) {
2085
+ const defaultConfig2 = {
2086
+ plugins: [],
2087
+ pluginDirs: ["./plugins", "./custom-components"],
2088
+ autoDiscover: false,
2089
+ aliases: {},
2090
+ theme: "minimal",
2091
+ discovery: {
2092
+ maxDepth: 5,
2093
+ includeNodeModules: false
2094
+ },
2095
+ validation: {
2096
+ strict: false,
2097
+ allowUnknownFields: true
2098
+ }
2099
+ };
2100
+ await this.saveConfig(defaultConfig2, filePath);
2101
+ }
2102
+ clearConfig() {
2103
+ this.config = null;
2104
+ this.configPath = null;
2105
+ }
2106
+ };
2107
+
2108
+ // src/config/loader.ts
2109
+ import { existsSync as existsSync3 } from "fs";
2110
+ import { readFile as readFile4 } from "fs/promises";
2111
+ import { resolve as resolve10 } from "path";
2112
+ import { pathToFileURL as pathToFileURL2 } from "url";
2113
+
2114
+ // src/config/schema.ts
2115
+ import { Type } from "@sinclair/typebox";
2116
+ var ConfigSchema = Type.Object({
2117
+ mode: Type.Union([Type.Literal("development"), Type.Literal("production")], {
2118
+ default: "development"
2119
+ }),
2120
+ server: Type.Object({
2121
+ port: Type.Number({ default: 3003, minimum: 0, maximum: 65535 }),
2122
+ host: Type.String({ default: "localhost" }),
2123
+ cors: Type.Optional(
2124
+ Type.Object({
2125
+ origin: Type.Union([Type.String(), Type.Array(Type.String())]),
2126
+ credentials: Type.Boolean({ default: true })
2127
+ })
2128
+ )
2129
+ }),
2130
+ api: Type.Object({
2131
+ basePath: Type.String({ default: "/api" }),
2132
+ rateLimit: Type.Optional(
2133
+ Type.Object({
2134
+ windowMs: Type.Number({ default: 6e4 }),
2135
+ max: Type.Number({ default: 100 })
2136
+ })
2137
+ ),
2138
+ upload: Type.Object({
2139
+ maxFileSize: Type.Number({ default: 10 * 1024 * 1024 }),
2140
+ allowedMimeTypes: Type.Array(Type.String(), {
2141
+ default: ["image/jpeg", "image/png", "image/gif", "image/svg+xml"]
2142
+ })
2143
+ })
2144
+ }),
2145
+ playground: Type.Object({
2146
+ enabled: Type.Boolean({ default: true }),
2147
+ root: Type.String({ default: "../../apps/web-react" }),
2148
+ features: Type.Object({
2149
+ livePreview: Type.Boolean({ default: true }),
2150
+ templateLibrary: Type.Boolean({ default: true }),
2151
+ componentBuilder: Type.Boolean({ default: false }),
2152
+ collaboration: Type.Boolean({ default: false })
2153
+ })
2154
+ }),
2155
+ development: Type.Object({
2156
+ hmr: Type.Boolean({ default: true }),
2157
+ hmrPort: Type.Optional(Type.Number()),
2158
+ sourceMap: Type.Boolean({ default: true }),
2159
+ verbose: Type.Boolean({ default: false })
2160
+ }),
2161
+ paths: Type.Object({
2162
+ templates: Type.String({ default: "./templates" }),
2163
+ modules: Type.String({ default: "./modules" }),
2164
+ cache: Type.String({ default: "./.cache" })
2165
+ })
2166
+ });
2167
+
2168
+ // src/config/loader.ts
2169
+ import { Value } from "@sinclair/typebox/value";
2170
+
2171
+ // src/config/defaults.ts
2172
+ var defaultConfig = {
2173
+ mode: "development",
2174
+ server: {
2175
+ port: 3003,
2176
+ host: "localhost",
2177
+ cors: {
2178
+ origin: "*",
2179
+ credentials: true
2180
+ }
2181
+ },
2182
+ api: {
2183
+ basePath: "/api",
2184
+ upload: {
2185
+ maxFileSize: 10 * 1024 * 1024,
2186
+ allowedMimeTypes: [
2187
+ "image/jpeg",
2188
+ "image/png",
2189
+ "image/gif",
2190
+ "image/svg+xml"
2191
+ ]
2192
+ }
2193
+ },
2194
+ playground: {
2195
+ enabled: true,
2196
+ root: "../../apps/web-react",
2197
+ features: {
2198
+ livePreview: true,
2199
+ templateLibrary: true,
2200
+ componentBuilder: false,
2201
+ collaboration: false
2202
+ }
2203
+ },
2204
+ development: {
2205
+ hmr: true,
2206
+ sourceMap: true,
2207
+ verbose: false
2208
+ },
2209
+ paths: {
2210
+ templates: "./templates",
2211
+ modules: "./modules",
2212
+ cache: "./.cache"
2213
+ }
2214
+ };
2215
+
2216
+ // src/config/loader.ts
2217
+ var CONFIG_FILES = [
2218
+ "json-to-office.config.ts",
2219
+ "json-to-office.config.js",
2220
+ "json-to-office.config.mjs",
2221
+ "json-to-office.config.json",
2222
+ // Legacy support
2223
+ "json-to-docx.config.ts",
2224
+ "json-to-docx.config.js",
2225
+ "json-to-docx.config.json",
2226
+ "json-to-pptx.config.ts",
2227
+ "json-to-pptx.config.js",
2228
+ "json-to-pptx.config.json"
2229
+ ];
2230
+ async function loadConfig(configPath) {
2231
+ let userConfig = {};
2232
+ const configFile = configPath || await findConfigFile();
2233
+ if (configFile) {
2234
+ try {
2235
+ userConfig = await loadConfigFile(configFile);
2236
+ } catch (error) {
2237
+ console.warn(`Warning: Failed to load config file: ${error.message}`);
2238
+ }
2239
+ }
2240
+ const config = deepMerge(defaultConfig, userConfig);
2241
+ if (!Value.Check(ConfigSchema, config)) {
2242
+ const errors = [...Value.Errors(ConfigSchema, config)];
2243
+ console.warn("Warning: Invalid configuration detected:", errors);
2244
+ return defaultConfig;
2245
+ }
2246
+ return config;
2247
+ }
2248
+ async function findConfigFile() {
2249
+ const cwd = process.cwd();
2250
+ for (const filename of CONFIG_FILES) {
2251
+ const filepath = resolve10(cwd, filename);
2252
+ if (existsSync3(filepath)) {
2253
+ return filepath;
2254
+ }
2255
+ }
2256
+ return null;
2257
+ }
2258
+ async function loadConfigFile(filepath) {
2259
+ const ext = filepath.split(".").pop();
2260
+ if (ext === "json") {
2261
+ const content = await readFile4(filepath, "utf-8");
2262
+ return JSON.parse(content);
2263
+ }
2264
+ const fileUrl = pathToFileURL2(filepath).href;
2265
+ const module = await import(fileUrl);
2266
+ return module.default || module;
2267
+ }
2268
+ function deepMerge(target, source) {
2269
+ const output = { ...target };
2270
+ if (isObject(target) && isObject(source)) {
2271
+ Object.keys(source).forEach((key) => {
2272
+ if (isObject(source[key])) {
2273
+ if (!(key in target)) {
2274
+ Object.assign(output, { [key]: source[key] });
2275
+ } else {
2276
+ output[key] = deepMerge(target[key], source[key]);
2277
+ }
2278
+ } else {
2279
+ Object.assign(output, { [key]: source[key] });
2280
+ }
2281
+ });
2282
+ }
2283
+ return output;
2284
+ }
2285
+ function isObject(item) {
2286
+ return item && typeof item === "object" && !Array.isArray(item);
2287
+ }
2288
+ export {
2289
+ DocxFormatAdapter,
2290
+ GeneratorFactory,
2291
+ JsonValidator,
2292
+ PluginConfigService,
2293
+ PluginDiscoveryService,
2294
+ PluginLoader,
2295
+ PluginMetadataExtractor,
2296
+ PluginRegistry,
2297
+ PluginResolver,
2298
+ PptxFormatAdapter,
2299
+ SchemaGenerator,
2300
+ createAdapter,
2301
+ loadConfig
2302
+ };
2303
+ //# sourceMappingURL=index.js.map