@xyd-js/documan 0.1.0-xyd.29 → 0.1.0-xyd.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,169 +1,917 @@
1
- // src/run/build.ts
2
- import path from "node:path";
3
- import fs from "node:fs";
4
- import { fileURLToPath } from "node:url";
5
- import { execSync } from "child_process";
1
+ // src/build.ts
2
+ import path2 from "path";
3
+ import fs2 from "fs";
6
4
  import { build as viteBuild } from "vite";
7
5
  import tsconfigPaths from "vite-tsconfig-paths";
8
- import { reactRouter } from "@xyd-js/react-router-dev/vite";
6
+
7
+ // src/utils.ts
8
+ import path from "path";
9
+ import fs from "fs";
10
+ import { fileURLToPath } from "url";
11
+ import { execSync } from "child_process";
12
+ import crypto from "crypto";
13
+ import { createServer } from "vite";
14
+ import { reactRouter } from "@react-router/dev/vite";
15
+ import { IconSet } from "@iconify/tools";
16
+ import { readSettings, pluginDocs } from "@xyd-js/plugin-docs";
9
17
  import { vitePlugins as xydContentVitePlugins } from "@xyd-js/content/vite";
10
- import { pluginZero } from "@xyd-js/plugin-zero";
18
+
19
+ // ../xyd-host/package.json
20
+ var package_default = {
21
+ name: "@xyd-js/host",
22
+ version: "0.1.0-xyd.0",
23
+ type: "module",
24
+ scripts: {},
25
+ dependencies: {
26
+ "@xyd-js/core": "workspace:*",
27
+ "@xyd-js/components": "workspace:*",
28
+ "@xyd-js/framework": "workspace:*",
29
+ "@xyd-js/composer": "workspace:*",
30
+ "@xyd-js/themes": "workspace:*",
31
+ "@xyd-js/atlas": "workspace:*",
32
+ "@xyd-js/theme-poetry": "workspace:*",
33
+ "@xyd-js/theme-cosmo": "workspace:*",
34
+ "@xyd-js/theme-opener": "workspace:*",
35
+ "@xyd-js/theme-picasso": "workspace:*",
36
+ "@xyd-js/plugin-orama": "workspace:*",
37
+ "@xyd-js/plugin-algolia": "workspace:*",
38
+ "@react-router/dev": "^7.5.0",
39
+ "@react-router/node": "^7.5.0",
40
+ "@react-router/serve": "^7.5.0",
41
+ isbot: "^5",
42
+ "react-router": "^7.5.0"
43
+ },
44
+ devDependencies: {
45
+ autoprefixer: "^10.4.20",
46
+ postcss: "^8.4.47",
47
+ semver: "^7.6.3",
48
+ vite: "^6.1.0",
49
+ "vite-tsconfig-paths": "^5.1.4"
50
+ }
51
+ };
52
+
53
+ // src/const.ts
54
+ var HOST_FOLDER_PATH = ".xyd/host";
55
+ var CACHE_FOLDER_PATH = ".xyd/.cache";
56
+ var BUILD_FOLDER_PATH = ".xyd/build";
57
+ var SUPPORTED_SETTINGS_FILES = [
58
+ "docs.json",
59
+ "docs.ts",
60
+ "docs.tsx"
61
+ ];
62
+ var SUPPORTED_WATCH_FILES = [
63
+ ".md",
64
+ ".mdx",
65
+ ...SUPPORTED_SETTINGS_FILES
66
+ ];
67
+
68
+ // src/cli.ts
69
+ import readline from "readline";
70
+ import cliSpinners from "cli-spinners";
71
+ var CLI = class {
72
+ spinner;
73
+ spinnerInterval = null;
74
+ currentFrame = 0;
75
+ currentMessage = "";
76
+ isSpinning = false;
77
+ constructor(spinnerType = "dots") {
78
+ this.spinner = cliSpinners[spinnerType];
79
+ }
80
+ startSpinner(message) {
81
+ if (this.isSpinning) {
82
+ this.stopSpinner();
83
+ }
84
+ this.currentMessage = message;
85
+ this.isSpinning = true;
86
+ this.currentFrame = 0;
87
+ this.write(`${this.spinner.frames[0]} ${this.currentMessage}`);
88
+ this.spinnerInterval = setInterval(() => {
89
+ const frame = this.spinner.frames[this.currentFrame];
90
+ this.write(`${frame} ${this.currentMessage}`);
91
+ this.currentFrame = (this.currentFrame + 1) % this.spinner.frames.length;
92
+ }, this.spinner.interval);
93
+ }
94
+ stopSpinner() {
95
+ if (this.spinnerInterval) {
96
+ clearInterval(this.spinnerInterval);
97
+ this.spinnerInterval = null;
98
+ }
99
+ this.isSpinning = false;
100
+ this.clearLine();
101
+ }
102
+ clearLine() {
103
+ readline.clearLine(process.stdout, 0);
104
+ readline.cursorTo(process.stdout, 0);
105
+ }
106
+ write(message) {
107
+ this.clearLine();
108
+ process.stdout.write(message);
109
+ }
110
+ log(message) {
111
+ if (this.isSpinning) {
112
+ this.stopSpinner();
113
+ }
114
+ console.log(message);
115
+ }
116
+ error(message) {
117
+ if (this.isSpinning) {
118
+ this.stopSpinner();
119
+ }
120
+ console.error(message);
121
+ }
122
+ updateMessage(message) {
123
+ this.currentMessage = message;
124
+ }
125
+ };
126
+ var cli = new CLI();
127
+
128
+ // src/utils.ts
11
129
  var __filename = fileURLToPath(import.meta.url);
12
130
  var __dirname = path.dirname(__filename);
13
- async function build() {
14
- const resp = await pluginZero();
15
- if (!resp) {
16
- throw new Error("PluginZero not found");
131
+ var HOST_VERSION = package_default?.version;
132
+ async function appInit(options) {
133
+ const readPreloadSettings = await readSettings();
134
+ if (!readPreloadSettings) {
135
+ throw new Error("cannot preload settings");
136
+ }
137
+ const preloadSettings = typeof readPreloadSettings === "string" ? JSON.parse(readPreloadSettings) : readPreloadSettings;
138
+ {
139
+ if (!preloadSettings.integrations?.search) {
140
+ preloadSettings.integrations = {
141
+ ...preloadSettings.integrations || {},
142
+ search: {
143
+ orama: true
144
+ }
145
+ };
146
+ }
147
+ const plugins = integrationsToPlugins(preloadSettings.integrations);
148
+ if (preloadSettings.plugins) {
149
+ preloadSettings.plugins = [...plugins, ...preloadSettings.plugins];
150
+ } else {
151
+ preloadSettings.plugins = plugins;
152
+ }
17
153
  }
18
- const buildDir = path.join(process.cwd(), ".xyd/build");
154
+ let resolvedPlugins = [];
19
155
  {
20
- const packageJsonPath = path.join(buildDir, "package.json");
21
- const packageJsonContent = {
22
- type: "module",
23
- scripts: {},
24
- dependencies: {
25
- // TODO: better
26
- "@xyd-js/content": "latest",
27
- "@xyd-js/components": "latest",
28
- "@xyd-js/framework": "latest",
29
- "@xyd-js/theme-poetry": "latest",
30
- "@react-router/node": "7.1.1",
31
- "isbot": "^5"
156
+ resolvedPlugins = await loadPlugins(preloadSettings) || [];
157
+ const userUniformVitePlugins = [];
158
+ resolvedPlugins?.forEach((p) => {
159
+ if (p.uniform) {
160
+ userUniformVitePlugins.push(...p.uniform);
161
+ }
162
+ });
163
+ globalThis.__xydUserUniformVitePlugins = userUniformVitePlugins;
164
+ }
165
+ const respPluginDocs = await pluginDocs(options);
166
+ if (!respPluginDocs) {
167
+ throw new Error("PluginDocs not found");
168
+ }
169
+ if (!respPluginDocs.settings) {
170
+ throw new Error("Settings not found in respPluginDocs");
171
+ }
172
+ respPluginDocs.settings.plugins = [
173
+ ...respPluginDocs.settings?.plugins || [],
174
+ ...preloadSettings.plugins || []
175
+ ];
176
+ globalThis.__xydBasePath = respPluginDocs.basePath;
177
+ globalThis.__xydSettings = respPluginDocs.settings;
178
+ globalThis.__xydPagePathMapping = respPluginDocs.pagePathMapping;
179
+ return {
180
+ respPluginDocs,
181
+ resolvedPlugins
182
+ };
183
+ }
184
+ function virtualComponentsPlugin() {
185
+ return {
186
+ name: "xyd-plugin-virtual-components",
187
+ enforce: "pre",
188
+ config: () => {
189
+ const componentsDist = path.resolve(getHostPath(), "./node_modules/@xyd-js/components/dist");
190
+ return {
191
+ resolve: {
192
+ alias: {
193
+ // TODO: type-safe virtual-components
194
+ "virtual-component:Search": path.resolve(componentsDist, "system.js")
195
+ }
196
+ }
197
+ };
198
+ }
199
+ };
200
+ }
201
+ function virtualProvidersPlugin(settings) {
202
+ return {
203
+ name: "xyd-plugin-virtual-providers",
204
+ enforce: "pre",
205
+ resolveId(id) {
206
+ if (id === "virtual:xyd-analytics-providers") {
207
+ return id;
208
+ }
209
+ },
210
+ async load(id) {
211
+ if (id === "virtual:xyd-analytics-providers") {
212
+ const providers = Object.keys(settings?.integrations?.analytics || {});
213
+ const imports = providers.map(
214
+ (provider) => `import { default as ${provider}Provider } from '@pluganalytics/provider-${provider}'`
215
+ ).join("\n");
216
+ const cases = providers.map(
217
+ (provider) => `case '${provider}': return ${provider}Provider`
218
+ ).join("\n");
219
+ return `
220
+ ${imports}
221
+
222
+ export const loadProvider = async (provider) => {
223
+ switch (provider) {
224
+ ${cases}
225
+ default:
226
+ console.error(\`Provider \${provider} not found\`)
227
+ return null
228
+ }
229
+ }
230
+ `;
231
+ }
232
+ }
233
+ };
234
+ }
235
+ function commonVitePlugins(respPluginDocs, resolvedPlugins) {
236
+ const userVitePlugins = resolvedPlugins.map((p) => p.vite).flat() || [];
237
+ return [
238
+ ...xydContentVitePlugins({
239
+ toc: {
240
+ maxDepth: respPluginDocs.settings.theme?.maxTocDepth || 2
32
241
  },
33
- devDependencies: {}
242
+ settings: respPluginDocs.settings
243
+ }),
244
+ ...respPluginDocs.vitePlugins,
245
+ reactRouter(),
246
+ virtualComponentsPlugin(),
247
+ virtualProvidersPlugin(respPluginDocs.settings),
248
+ pluginIconSet(respPluginDocs.settings),
249
+ ...userVitePlugins
250
+ ];
251
+ }
252
+ function pluginIconSet(settings) {
253
+ const DEFAULT_ICON_SET = "lucide";
254
+ async function fetchIconSet(name, version) {
255
+ if (name.startsWith("http://") || name.startsWith("https://")) {
256
+ try {
257
+ const iconsResp = await fetch(name);
258
+ const iconsData = await iconsResp.json();
259
+ const iconSet = new IconSet(iconsData);
260
+ return { icons: iconsData, iconSet };
261
+ } catch (error) {
262
+ console.warn(`Failed to fetch from URL ${name}:`, error);
263
+ }
264
+ }
265
+ const tryReadFile = (filePath) => {
266
+ try {
267
+ if (!fs.existsSync(filePath)) {
268
+ console.warn(`File does not exist: ${filePath}`);
269
+ return null;
270
+ }
271
+ const fileContent = fs.readFileSync(filePath, "utf-8");
272
+ try {
273
+ const iconsData = JSON.parse(fileContent);
274
+ const iconSet = new IconSet(iconsData);
275
+ return { icons: iconsData, iconSet };
276
+ } catch (parseError) {
277
+ console.warn(`Invalid JSON in file ${filePath}:`, parseError);
278
+ return null;
279
+ }
280
+ } catch (error) {
281
+ console.warn(`Failed to read file ${filePath}:`, error);
282
+ return null;
283
+ }
34
284
  };
35
- if (process.env.XYD_DEV_MODE) {
36
- Object.keys(packageJsonContent.dependencies).forEach((key) => {
37
- if (key.startsWith("@xyd-js/")) {
38
- packageJsonContent.dependencies[key] = "workspace:*";
285
+ if (path.isAbsolute(name)) {
286
+ const result = tryReadFile(name);
287
+ if (result) return result;
288
+ }
289
+ if (name.startsWith(".")) {
290
+ const fullPath = path.join(process.cwd(), name);
291
+ const result = tryReadFile(fullPath);
292
+ if (result) return result;
293
+ }
294
+ const cdnUrl = version ? `https://cdn.jsdelivr.net/npm/@iconify-json/${name}@${version}/icons.json` : `https://cdn.jsdelivr.net/npm/@iconify-json/${name}/icons.json`;
295
+ try {
296
+ const iconsResp = await fetch(cdnUrl);
297
+ const iconsData = await iconsResp.json();
298
+ const iconSet = new IconSet(iconsData);
299
+ return { icons: iconsData, iconSet };
300
+ } catch (error) {
301
+ throw new Error(`Failed to load icon set from any source (file or CDN): ${name}`);
302
+ }
303
+ }
304
+ async function processIconSet(iconSet, icons, noPrefix) {
305
+ const resp = /* @__PURE__ */ new Map();
306
+ for (const icon of Object.keys(icons.icons)) {
307
+ const svg = iconSet.toSVG(icon);
308
+ if (!svg) continue;
309
+ let prefix = noPrefix ? void 0 : iconSet.prefix;
310
+ const iconName = prefix ? `${prefix}:${icon}` : icon;
311
+ resp.set(iconName, { svg: svg.toString() });
312
+ }
313
+ return resp;
314
+ }
315
+ async function addIconsToMap(resp, name, version, noPrefix) {
316
+ const { icons, iconSet } = await fetchIconSet(name, version);
317
+ const newIcons = await processIconSet(iconSet, icons, noPrefix);
318
+ newIcons.forEach((value, key) => resp.set(key, value));
319
+ }
320
+ async function processIconLibrary(library) {
321
+ const resp = /* @__PURE__ */ new Map();
322
+ if (typeof library === "string") {
323
+ await addIconsToMap(resp, library);
324
+ } else if (Array.isArray(library)) {
325
+ for (const item of library) {
326
+ if (typeof item === "string") {
327
+ await addIconsToMap(resp, item);
328
+ } else {
329
+ const { name, version, default: isDefault, noprefix } = item;
330
+ const noPrefix = isDefault || noprefix;
331
+ await addIconsToMap(resp, name, version, noPrefix);
332
+ }
333
+ }
334
+ } else {
335
+ const { name, version, default: isDefault, noprefix } = library;
336
+ const prefix = isDefault || noprefix ? void 0 : name;
337
+ await addIconsToMap(resp, name, version, prefix);
338
+ }
339
+ return resp;
340
+ }
341
+ return {
342
+ name: "xyd-plugin-icon-set",
343
+ enforce: "pre",
344
+ resolveId(id) {
345
+ if (id === "virtual:xyd-icon-set") {
346
+ return id;
347
+ }
348
+ },
349
+ async load(id) {
350
+ if (id === "virtual:xyd-icon-set") {
351
+ let resp;
352
+ if (settings.theme?.icons?.library) {
353
+ resp = await processIconLibrary(settings.theme.icons.library);
354
+ } else {
355
+ resp = await processIconLibrary([
356
+ {
357
+ name: DEFAULT_ICON_SET,
358
+ default: true
359
+ }
360
+ ]);
361
+ }
362
+ return `
363
+ export const iconSet = ${JSON.stringify(Object.fromEntries(resp))};
364
+ `;
365
+ }
366
+ }
367
+ };
368
+ }
369
+ function getHostPath() {
370
+ if (process.env.XYD_DEV_MODE) {
371
+ if (process.env.XYD_HOST) {
372
+ return path.resolve(process.env.XYD_HOST);
373
+ }
374
+ return path.join(__dirname, "../../../", HOST_FOLDER_PATH);
375
+ }
376
+ return path.join(process.cwd(), HOST_FOLDER_PATH);
377
+ }
378
+ function getAppRoot() {
379
+ return getHostPath();
380
+ }
381
+ function getPublicPath() {
382
+ return path.join(process.cwd(), "public");
383
+ }
384
+ function getBuildPath() {
385
+ return path.join(
386
+ process.cwd(),
387
+ BUILD_FOLDER_PATH
388
+ );
389
+ }
390
+ function getDocsPluginBasePath() {
391
+ return path.join(getHostPath(), "./plugins/xyd-plugin-docs");
392
+ }
393
+ async function loadPlugins(settings) {
394
+ const resolvedPlugins = [];
395
+ for (const plugin of settings.plugins || []) {
396
+ let pluginName;
397
+ let pluginArgs = [];
398
+ if (typeof plugin === "string") {
399
+ pluginName = plugin;
400
+ pluginArgs = [];
401
+ } else if (Array.isArray(plugin)) {
402
+ pluginName = plugin[0];
403
+ pluginArgs = plugin.slice(1);
404
+ } else {
405
+ console.error(`Currently only string and array plugins are supported, got: ${plugin}`);
406
+ return [];
407
+ }
408
+ let mod;
409
+ try {
410
+ mod = await import(pluginName);
411
+ } catch (e) {
412
+ pluginName = path.join(process.cwd(), pluginName);
413
+ const pluginPreview = await createServer({
414
+ optimizeDeps: {
415
+ include: []
39
416
  }
40
417
  });
418
+ mod = await pluginPreview.ssrLoadModule(pluginName);
419
+ }
420
+ if (!mod.default) {
421
+ console.error(`Plugin ${plugin} has no default export`);
422
+ continue;
423
+ }
424
+ let pluginInstance = mod.default(...pluginArgs);
425
+ if (typeof pluginInstance === "function") {
426
+ const plug = pluginInstance(settings);
427
+ resolvedPlugins.push(plug);
428
+ continue;
429
+ }
430
+ resolvedPlugins.push(pluginInstance);
431
+ }
432
+ return resolvedPlugins;
433
+ }
434
+ function integrationsToPlugins(integrations) {
435
+ const plugins = [];
436
+ let foundSearchIntegation = 0;
437
+ if (integrations?.search?.orama) {
438
+ if (typeof integrations.search.orama === "boolean") {
439
+ plugins.push("@xyd-js/plugin-orama");
440
+ } else {
441
+ plugins.push(["@xyd-js/plugin-orama", integrations.search.orama]);
442
+ }
443
+ foundSearchIntegation++;
444
+ }
445
+ if (integrations?.search?.algolia) {
446
+ plugins.push(["@xyd-js/plugin-algolia", integrations.search.algolia]);
447
+ foundSearchIntegation++;
448
+ }
449
+ if (foundSearchIntegation > 1) {
450
+ throw new Error("Only one search integration is allowed");
451
+ }
452
+ return plugins;
453
+ }
454
+ async function preWorkspaceSetup(options = {}) {
455
+ await ensureFoldersExist();
456
+ if (!options.force) {
457
+ if (await shouldSkipHostSetup()) {
458
+ return true;
459
+ }
460
+ }
461
+ const hostTemplate = process.env.XYD_DEV_MODE ? path.resolve(__dirname, "../../xyd-host") : path.resolve(__dirname, "../node_modules/@xyd-js/host");
462
+ const hostPath = getHostPath();
463
+ await copyHostTemplate(hostTemplate, hostPath);
464
+ let pluginDocsPath;
465
+ if (process.env.XYD_DEV_MODE) {
466
+ pluginDocsPath = path.resolve(__dirname, "../../xyd-plugin-docs");
467
+ } else {
468
+ pluginDocsPath = path.resolve(__dirname, "../node_modules/@xyd-js/plugin-docs");
469
+ }
470
+ const pagesSourcePath = path.join(pluginDocsPath, "src/pages");
471
+ const pagesTargetPath = path.join(hostPath, "plugins/xyd-plugin-docs/src/pages");
472
+ if (fs.existsSync(pagesSourcePath)) {
473
+ await copyHostTemplate(pagesSourcePath, pagesTargetPath);
474
+ } else {
475
+ console.warn(`Pages source path does not exist: ${pagesSourcePath}`);
476
+ }
477
+ }
478
+ function calculateFolderChecksum(folderPath) {
479
+ const hash = crypto.createHash("sha256");
480
+ const ignorePatterns = [...getGitignorePatterns(folderPath), ".xydchecksum"];
481
+ function processFile(filePath) {
482
+ const relativePath = path.relative(folderPath, filePath);
483
+ const content = fs.readFileSync(filePath);
484
+ hash.update(relativePath);
485
+ hash.update(content);
486
+ }
487
+ function processDirectory(dirPath) {
488
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
489
+ entries.sort((a, b) => a.name.localeCompare(b.name));
490
+ for (const entry of entries) {
491
+ const sourceEntry = path.join(dirPath, entry.name);
492
+ if (shouldIgnoreEntry(entry.name, ignorePatterns)) {
493
+ continue;
494
+ }
495
+ if (entry.name === ".git") {
496
+ continue;
497
+ }
498
+ if (entry.isDirectory()) {
499
+ processDirectory(sourceEntry);
500
+ } else {
501
+ processFile(sourceEntry);
502
+ }
503
+ }
504
+ }
505
+ processDirectory(folderPath);
506
+ return hash.digest("hex");
507
+ }
508
+ function getGitignorePatterns(folderPath) {
509
+ const gitignorePath = path.join(folderPath, ".gitignore");
510
+ if (fs.existsSync(gitignorePath)) {
511
+ const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
512
+ return gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
513
+ }
514
+ return [];
515
+ }
516
+ function shouldIgnoreEntry(entryName, ignorePatterns) {
517
+ return ignorePatterns.some((pattern) => {
518
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
519
+ return regex.test(entryName);
520
+ });
521
+ }
522
+ async function copyHostTemplate(sourcePath, targetPath) {
523
+ if (!fs.existsSync(sourcePath)) {
524
+ throw new Error(`Host template source path does not exist: ${sourcePath}`);
525
+ }
526
+ if (fs.existsSync(targetPath)) {
527
+ fs.rmSync(targetPath, { recursive: true, force: true });
528
+ }
529
+ fs.mkdirSync(targetPath, { recursive: true });
530
+ const ignorePatterns = getGitignorePatterns(sourcePath);
531
+ const entries = fs.readdirSync(sourcePath, { withFileTypes: true });
532
+ for (const entry of entries) {
533
+ const sourceEntry = path.join(sourcePath, entry.name);
534
+ const targetEntry = path.join(targetPath, entry.name);
535
+ if (shouldIgnoreEntry(entry.name, ignorePatterns)) {
536
+ continue;
41
537
  }
42
- if (!fs.existsSync(buildDir)) {
43
- fs.mkdirSync(buildDir, { recursive: true });
538
+ if (entry.name === ".git") {
539
+ continue;
44
540
  }
45
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContent, null, 2), "utf8");
46
- if (process.env.XYD_DEV_MODE) {
47
- execSync("pnpm i", { cwd: buildDir, stdio: "inherit" });
541
+ if (entry.isDirectory()) {
542
+ await copyHostTemplate(sourceEntry, targetEntry);
48
543
  } else {
49
- execSync("npm install", { cwd: buildDir, stdio: "inherit" });
544
+ fs.copyFileSync(sourceEntry, targetEntry);
545
+ if (entry.name === "package.json" && process.env.XYD_DEV_MODE) {
546
+ const packageJsonPath = targetEntry;
547
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
548
+ packageJson.name = "xyd-host-dev";
549
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
550
+ }
551
+ }
552
+ }
553
+ }
554
+ async function ensureFoldersExist() {
555
+ const folders = [CACHE_FOLDER_PATH];
556
+ for (const folder of folders) {
557
+ const fullPath = path.resolve(process.cwd(), folder);
558
+ if (!fs.existsSync(fullPath)) {
559
+ fs.mkdirSync(fullPath, { recursive: true });
560
+ }
561
+ }
562
+ }
563
+ async function postWorkspaceSetup(settings) {
564
+ const spinner = new CLI("dots");
565
+ try {
566
+ spinner.startSpinner("Installing xyd framework...");
567
+ const hostPath = getHostPath();
568
+ const packageJsonPath = path.join(hostPath, "package.json");
569
+ if (!fs.existsSync(packageJsonPath)) {
570
+ console.warn("No package.json found in host path");
571
+ return;
50
572
  }
573
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
574
+ if (!packageJson.dependencies) {
575
+ packageJson.dependencies = {};
576
+ }
577
+ for (const plugin of settings.plugins || []) {
578
+ let pluginName;
579
+ if (typeof plugin === "string") {
580
+ pluginName = plugin;
581
+ } else if (Array.isArray(plugin)) {
582
+ pluginName = plugin[0];
583
+ } else {
584
+ continue;
585
+ }
586
+ if (pluginName.startsWith("@xyd-js/")) {
587
+ continue;
588
+ }
589
+ const isValidNpmPackage = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(pluginName);
590
+ if (isValidNpmPackage) {
591
+ const hostPackageJsonPath = path.join(hostPath, "package.json");
592
+ if (fs.existsSync(hostPackageJsonPath)) {
593
+ const hostPackageJson = JSON.parse(fs.readFileSync(hostPackageJsonPath, "utf-8"));
594
+ const deps = hostPackageJson.dependencies || {};
595
+ const matchingDep = Object.entries(deps).find(([depName]) => {
596
+ return depName === pluginName;
597
+ });
598
+ if (matchingDep) {
599
+ packageJson.dependencies[pluginName] = matchingDep[1];
600
+ } else {
601
+ console.warn(`no matching dependency found for: ${pluginName} in: ${hostPackageJsonPath}`);
602
+ }
603
+ } else {
604
+ console.warn(`no host package.json found in: ${hostPath}`);
605
+ }
606
+ } else if (!pluginName.startsWith(".") && !pluginName.startsWith("/")) {
607
+ console.warn(`invalid plugin name: ${pluginName}`);
608
+ }
609
+ }
610
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
611
+ await nodeInstallPackages(hostPath);
612
+ spinner.stopSpinner();
613
+ spinner.log("\u2714 Local xyd framework installed successfully");
614
+ } catch (error) {
615
+ spinner.stopSpinner();
616
+ spinner.error("\u274C Failed to install xyd framework");
617
+ throw error;
618
+ }
619
+ }
620
+ function nodeInstallPackages(hostPath) {
621
+ const cmd = process.env.XYD_DEV_MODE ? "pnpm i" : "npm i";
622
+ const execOptions = {
623
+ cwd: hostPath,
624
+ env: {
625
+ ...process.env,
626
+ NODE_ENV: ""
627
+ // since 'production' does not install it well
628
+ }
629
+ };
630
+ if (process.env.XYD_VERBOSE) {
631
+ execOptions.stdio = "inherit";
632
+ }
633
+ execSync(cmd, execOptions);
634
+ }
635
+ async function shouldSkipHostSetup() {
636
+ const hostPath = getHostPath();
637
+ if (!fs.existsSync(hostPath)) {
638
+ return false;
639
+ }
640
+ const currentChecksum = calculateFolderChecksum(hostPath);
641
+ const storedChecksum = getStoredChecksum();
642
+ if (!storedChecksum || storedChecksum !== currentChecksum) {
643
+ return false;
644
+ }
645
+ return true;
646
+ }
647
+ function getStoredChecksum() {
648
+ const checksumPath = path.join(getHostPath(), ".xydchecksum");
649
+ if (!fs.existsSync(checksumPath)) {
650
+ return null;
651
+ }
652
+ try {
653
+ return fs.readFileSync(checksumPath, "utf-8").trim();
654
+ } catch (error) {
655
+ console.error("Error reading checksum file:", error);
656
+ return null;
657
+ }
658
+ }
659
+ function storeChecksum(checksum) {
660
+ const checksumPath = path.join(getHostPath(), ".xydchecksum");
661
+ try {
662
+ fs.writeFileSync(checksumPath, checksum);
663
+ } catch (error) {
664
+ console.error("Error writing checksum file:", error);
665
+ }
666
+ }
667
+
668
+ // src/build.ts
669
+ async function build() {
670
+ const skip = await preWorkspaceSetup({
671
+ force: true
672
+ });
673
+ const { respPluginDocs, resolvedPlugins } = await appInit();
674
+ const commonRunVitePlugins = commonVitePlugins(respPluginDocs, resolvedPlugins);
675
+ const appRoot = getAppRoot();
676
+ if (!skip) {
677
+ await postWorkspaceSetup(respPluginDocs.settings);
678
+ const newChecksum = calculateFolderChecksum(getHostPath());
679
+ storeChecksum(newChecksum);
680
+ }
681
+ {
682
+ setupInstallableEnvironmentV2();
51
683
  }
52
684
  try {
53
685
  await viteBuild({
54
- root: process.env.XYD_CLI ? __dirname : process.env.XYD_DOCUMAN_HOST || path.join(__dirname, "../host"),
686
+ mode: "production",
687
+ root: appRoot,
55
688
  plugins: [
56
- ...xydContentVitePlugins({
57
- toc: {
58
- minDepth: 2
59
- }
60
- }),
61
- reactRouter({
62
- outDir: buildDir,
63
- routes: resp.routes
64
- }),
65
- tsconfigPaths(),
66
- ...resp.vitePlugins
689
+ ...commonRunVitePlugins,
690
+ tsconfigPaths()
67
691
  ],
68
692
  optimizeDeps: {
69
693
  include: ["react/jsx-runtime"]
694
+ },
695
+ define: {
696
+ "process.env.NODE_ENV": JSON.stringify("production"),
697
+ "process.env": {}
698
+ },
699
+ resolve: {
700
+ alias: {
701
+ process: "process/browser"
702
+ }
70
703
  }
71
704
  });
72
705
  await viteBuild({
73
- root: process.env.XYD_CLI ? __dirname : process.env.XYD_DOCUMAN_HOST || path.join(__dirname, "../host"),
706
+ mode: "production",
707
+ root: appRoot,
74
708
  build: {
75
709
  ssr: true
76
710
  },
77
711
  plugins: [
78
- ...xydContentVitePlugins({
79
- toc: {
80
- minDepth: 2
81
- }
82
- }),
83
- reactRouter({
84
- outDir: buildDir,
85
- routes: resp.routes
86
- }),
87
- tsconfigPaths(),
88
- ...resp.vitePlugins
712
+ fixManifestPlugin(appRoot),
713
+ ...commonRunVitePlugins
89
714
  ],
90
715
  optimizeDeps: {
91
716
  include: ["react/jsx-runtime"]
717
+ },
718
+ define: {
719
+ "process.env.NODE_ENV": JSON.stringify("production"),
720
+ "process.env": {}
721
+ },
722
+ resolve: {
723
+ alias: {
724
+ process: "process/browser"
725
+ }
92
726
  }
93
727
  });
94
- console.log("Build completed successfully.");
95
728
  } catch (error) {
96
729
  console.error("Build failed:", error);
97
730
  }
98
731
  }
99
-
100
- // src/run/dev.ts
101
- import path2 from "node:path";
102
- import { fileURLToPath as fileURLToPath2 } from "node:url";
103
- import fs2 from "node:fs";
104
- import { createServer, searchForWorkspaceRoot } from "vite";
105
- import { reactRouter as reactRouter2 } from "@xyd-js/react-router-dev/vite";
106
- import { vitePlugins as xydContentVitePlugins2 } from "@xyd-js/content/vite";
107
- import { pluginZero as pluginZero2 } from "@xyd-js/plugin-zero";
108
- var __filename2 = fileURLToPath2(import.meta.url);
109
- var __dirname2 = path2.dirname(__filename2);
110
- var port = process.env.XYD_PORT ? parseInt(process.env.XYD_PORT) : 5175;
111
- async function dev() {
112
- const respPluginZero = await pluginZero2();
113
- if (!respPluginZero) {
114
- throw new Error("PluginZero not found");
732
+ function setupInstallableEnvironmentV2() {
733
+ const buildDir = getBuildPath();
734
+ const packageJsonPath = path2.join(buildDir, "package.json");
735
+ const packageJsonContent = {
736
+ type: "module",
737
+ scripts: {},
738
+ dependencies: {
739
+ // "@react-router/node": "^7.5.0",
740
+ // "isbot": "^5"
741
+ },
742
+ devDependencies: {}
743
+ };
744
+ if (!fs2.existsSync(buildDir)) {
745
+ fs2.mkdirSync(buildDir, { recursive: true });
115
746
  }
747
+ fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJsonContent, null, 2), "utf8");
748
+ }
749
+ function fixManifestPlugin(appRoot) {
750
+ const manifestPath = path2.join(
751
+ getBuildPath(),
752
+ "./server/.vite/manifest.json"
753
+ );
754
+ return {
755
+ name: "xyd-fix-rr-manifest",
756
+ apply: "build",
757
+ // run after manifest is generated
758
+ // 2) after bundle is written, compute prefix and strip it
759
+ writeBundle(_, bundle) {
760
+ const cwdDir = process.cwd();
761
+ let prefix = path2.relative(appRoot, cwdDir).replace(/\\/g, "/");
762
+ if (prefix) prefix += "/";
763
+ const esc = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
764
+ const stripRe = new RegExp(`^${esc}`);
765
+ for (const fileName in bundle) {
766
+ const asset = bundle[fileName];
767
+ if (asset.type !== "asset") continue;
768
+ if (fileName.endsWith("manifest.json")) {
769
+ const manifest = JSON.parse(asset.source.toString());
770
+ const fixed = {};
771
+ for (const key of Object.keys(manifest)) {
772
+ const entry = manifest[key];
773
+ const newKey = key.replace(stripRe, "");
774
+ if (typeof entry.src === "string") {
775
+ entry.src = entry.src.replace(stripRe, "");
776
+ }
777
+ fixed[newKey] = entry;
778
+ }
779
+ asset.source = JSON.stringify(fixed, null, 2);
780
+ fs2.writeFileSync(manifestPath, asset.source, "utf8");
781
+ }
782
+ }
783
+ }
784
+ };
785
+ }
786
+
787
+ // src/dev.ts
788
+ import path3 from "path";
789
+ import fs3 from "fs";
790
+ import { createServer as createServer2, searchForWorkspaceRoot } from "vite";
791
+ if (!process.env.ENABLE_TIMERS) {
792
+ ["time", "timeLog", "timeEnd"].forEach((method) => {
793
+ console[method] = () => {
794
+ };
795
+ });
796
+ }
797
+ async function dev(options) {
798
+ const spinner = new CLI("dots");
799
+ spinner.startSpinner("Preparing local xyd instance... \n");
800
+ const skip = await preWorkspaceSetup();
801
+ const { respPluginDocs, resolvedPlugins } = await appInit();
116
802
  const allowCwd = searchForWorkspaceRoot(process.cwd());
117
- const preview = await createServer({
118
- root: process.env.XYD_CLI ? __dirname2 : process.env.XYD_DOCUMAN_HOST || path2.join(__dirname2, "../host"),
119
- // TODO: bundler?
803
+ const appRoot = getAppRoot();
804
+ const commonRunVitePlugins = commonVitePlugins(respPluginDocs, resolvedPlugins);
805
+ spinner.stopSpinner();
806
+ if (!skip) {
807
+ await postWorkspaceSetup(respPluginDocs.settings);
808
+ const newChecksum = calculateFolderChecksum(getHostPath());
809
+ storeChecksum(newChecksum);
810
+ }
811
+ spinner.log("\u2714 Local xyd instance is ready");
812
+ let server = null;
813
+ const port = options?.port ?? parseInt(process.env.XYD_PORT ?? "5175");
814
+ const preview = await createServer2({
815
+ root: appRoot,
816
+ publicDir: "/public",
120
817
  server: {
818
+ allowedHosts: [],
121
819
  port,
122
820
  fs: {
123
821
  allow: [
124
822
  allowCwd,
125
- process.env.XYD_CLI ? __dirname2 : path2.join(__dirname2, "../host")
823
+ appRoot
126
824
  ]
127
825
  }
128
826
  },
827
+ define: {
828
+ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
829
+ "process.env": {}
830
+ },
831
+ resolve: {
832
+ alias: {
833
+ process: "process/browser"
834
+ }
835
+ },
129
836
  build: {
130
837
  rollupOptions: {
131
- // Exclude package `B` from the client-side bundle
132
- external: ["@xyd-js/uniform", "@xyd-js/uniform/content", "@xyd-js/plugin-zero"]
838
+ external: []
133
839
  }
134
840
  },
135
841
  ssr: {
136
- external: ["@xyd-js/uniform", "@xyd-js/uniform/content", "@xyd-js/plugin-zero"]
842
+ external: []
137
843
  },
138
844
  optimizeDeps: {
139
845
  include: ["react/jsx-runtime"]
140
846
  },
141
847
  plugins: [
142
- // TODO: fix plugin ts
143
- ...xydContentVitePlugins2({
144
- toc: {
145
- minDepth: 2
848
+ ...commonRunVitePlugins,
849
+ {
850
+ name: "xyd-configureServer",
851
+ configureServer(s) {
852
+ server = s;
146
853
  }
147
- }),
148
- reactRouter2({
149
- routes: respPluginZero.routes
150
- }),
151
- ...respPluginZero.vitePlugins
854
+ }
152
855
  ]
153
856
  });
154
- const watcher = fs2.watch(allowCwd, { recursive: true }, (eventType, filename) => {
857
+ const watcher = fs3.watch(allowCwd, { recursive: true }, async (eventType, filename) => {
155
858
  if (!filename) {
156
859
  console.log("[xyd:dev] Received empty filename");
157
860
  return;
158
861
  }
159
- const filePath = path2.join(allowCwd, filename);
160
- if (filePath.endsWith(".md") || filePath.endsWith(".mdx")) {
161
- const relativePath = path2.relative(allowCwd, filePath);
862
+ const filePath = path3.join(allowCwd, filename);
863
+ if (filePath.includes(CACHE_FOLDER_PATH)) {
864
+ return;
865
+ }
866
+ let apiPaths = {};
867
+ if (respPluginDocs?.settings?.api) {
868
+ apiPaths = resolveApiFilePaths(process.cwd(), respPluginDocs.settings.api);
869
+ }
870
+ const apiChanged = !!apiPaths[filePath];
871
+ if (filePath.includes(getPublicPath())) {
872
+ const relativePath = path3.relative(allowCwd, filePath);
162
873
  const urlPath = "/" + relativePath.replace(/\\/g, "/");
163
874
  preview.ws.send({
164
875
  type: "full-reload",
165
876
  path: urlPath
166
877
  });
878
+ return;
879
+ }
880
+ if (SUPPORTED_WATCH_FILES.some((ext) => filePath.endsWith(ext)) || apiChanged) {
881
+ const relativePath = path3.relative(allowCwd, filePath);
882
+ const urlPath = "/" + relativePath.replace(/\\/g, "/");
883
+ if (respPluginDocs?.settings.engine?.uniform?.store) {
884
+ await appInit({
885
+ disableFSWrite: true
886
+ });
887
+ } else {
888
+ await appInit();
889
+ }
890
+ if (server) {
891
+ const docsPluginBasePath = getDocsPluginBasePath();
892
+ const layoutPath = path3.join(docsPluginBasePath, "./src/pages/layout.tsx");
893
+ const layoutModule = server.moduleGraph.getModuleById(layoutPath);
894
+ const pagePath = path3.join(docsPluginBasePath, "./src/pages/page.tsx");
895
+ const pageModule = server.moduleGraph.getModuleById(pagePath);
896
+ if (layoutModule && pageModule) {
897
+ server.moduleGraph.invalidateModule(layoutModule);
898
+ server.moduleGraph.invalidateModule(pageModule);
899
+ try {
900
+ const hostPath = getHostPath();
901
+ const hostReactRouterConfig = path3.join(hostPath, "react-router.config.ts");
902
+ await fs3.promises.utimes(hostReactRouterConfig, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
903
+ } catch (e) {
904
+ console.error("config update error");
905
+ console.error(e);
906
+ }
907
+ server.ws.send({ type: "full-reload" });
908
+ return;
909
+ }
910
+ }
911
+ preview.ws.send({
912
+ type: "full-reload",
913
+ path: urlPath
914
+ });
167
915
  }
168
916
  });
169
917
  watcher.on("error", (error) => {
@@ -176,93 +924,58 @@ async function dev() {
176
924
  watcher.close();
177
925
  });
178
926
  }
179
-
180
- // src/run/serve.ts
181
- import fs3 from "node:fs";
182
- import os from "node:os";
183
- import path3 from "node:path";
184
- import url from "node:url";
185
- import { createRequestHandler } from "@react-router/express";
186
- import compression from "compression";
187
- import express from "express";
188
- import morgan from "morgan";
189
- import sourceMapSupport from "source-map-support";
190
- import getPort from "get-port";
191
- function parseNumber(raw) {
192
- if (raw === void 0) return void 0;
193
- let maybe = Number(raw);
194
- if (Number.isNaN(maybe)) return void 0;
195
- return maybe;
196
- }
197
- async function serve() {
198
- process.env.NODE_ENV = process.env.NODE_ENV ?? "production";
199
- sourceMapSupport.install({
200
- retrieveSourceMap: function(source) {
201
- let match = source.startsWith("file://");
202
- if (match) {
203
- let filePath = url.fileURLToPath(source);
204
- let sourceMapPath = `${filePath}.map`;
205
- if (fs3.existsSync(sourceMapPath)) {
206
- return {
207
- url: source,
208
- map: fs3.readFileSync(sourceMapPath, "utf8")
209
- };
210
- }
211
- }
212
- return null;
213
- }
927
+ function resolveApiFilePaths(basePath, api) {
928
+ const result = {};
929
+ const apis = [api.openapi, api.graphql, api.sources].filter((s) => s !== void 0);
930
+ apis.forEach((section) => {
931
+ flattenApiFile(section).forEach((p) => {
932
+ const apiAbsPath = path3.resolve(basePath, p);
933
+ result[apiAbsPath] = true;
934
+ });
214
935
  });
215
- let port2 = parseNumber(process.env.PORT) ?? await getPort({ port: 3e3 });
216
- let buildPathArg = path3.join(process.cwd(), ".xyd/build/server/index.js");
217
- if (!buildPathArg) {
218
- console.error(`
219
- Usage: react-router-serve <server-build-path> - e.g. react-router-serve build/server/index.js`);
220
- process.exit(1);
221
- }
222
- let buildPath = path3.resolve(buildPathArg);
223
- let build2 = await import(url.pathToFileURL(buildPath).href);
224
- let onListen = () => {
225
- let address = process.env.HOST || Object.values(os.networkInterfaces()).flat().find((ip) => String(ip?.family).includes("4") && !ip?.internal)?.address;
226
- if (!address) {
227
- console.log(`[xyd-serve] http://localhost:${port2}`);
228
- } else {
229
- console.log(
230
- `[xyd-serve] http://localhost:${port2} (http://${address}:${port2})`
231
- );
936
+ return result;
937
+ }
938
+ function flattenApiFile(file) {
939
+ if (!file) return [];
940
+ if (typeof file === "string") {
941
+ return [file];
942
+ }
943
+ if (Array.isArray(file)) {
944
+ return file.flatMap(flattenApiFile);
945
+ }
946
+ if (typeof file === "object") {
947
+ const obj = file;
948
+ if (typeof obj.source === "string") {
949
+ return [obj.source];
232
950
  }
233
- };
234
- build2 = {
235
- ...build2,
236
- assetsBuildDirectory: path3.join(process.cwd(), ".xyd/build/client")
237
- };
238
- let app = express();
239
- app.disable("x-powered-by");
240
- app.use(compression());
241
- app.use(
242
- path3.posix.join(build2.publicPath, "assets"),
243
- express.static(path3.join(build2.assetsBuildDirectory, "assets"), {
244
- immutable: true,
245
- maxAge: "1y"
246
- })
247
- );
248
- app.use(build2.publicPath, express.static(build2.assetsBuildDirectory));
249
- app.use(express.static("public", { maxAge: "1h" }));
250
- app.use(morgan("tiny"));
251
- app.all(
252
- "*",
253
- createRequestHandler({
254
- build: build2,
255
- mode: process.env.NODE_ENV
256
- })
257
- );
258
- let server = process.env.HOST ? app.listen(port2, process.env.HOST, onListen) : app.listen(port2, onListen);
259
- ["SIGTERM", "SIGINT"].forEach((signal) => {
260
- process.once(signal, () => server?.close(console.error));
951
+ return Object.values(obj).flatMap(flattenApiFile);
952
+ }
953
+ return [];
954
+ }
955
+
956
+ // src/install.ts
957
+ import fs4 from "fs";
958
+ import { readSettings as readSettings2 } from "@xyd-js/plugin-docs";
959
+ async function install() {
960
+ const settings = await readSettings2();
961
+ if (!settings) {
962
+ throw new Error("cannot preload settings");
963
+ }
964
+ if (typeof settings === "string") {
965
+ throw new Error("install does not support string settings");
966
+ }
967
+ const hostPath = getHostPath();
968
+ if (fs4.existsSync(hostPath)) {
969
+ fs4.rmSync(hostPath, { recursive: true, force: true });
970
+ }
971
+ await preWorkspaceSetup({
972
+ force: true
261
973
  });
974
+ await postWorkspaceSetup(settings);
262
975
  }
263
976
  export {
264
977
  build,
265
978
  dev,
266
- serve
979
+ install
267
980
  };
268
981
  //# sourceMappingURL=index.js.map