@pure-ds/core 0.3.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 (129) hide show
  1. package/CSS-INTELLISENSE-LIMITATION.md +98 -0
  2. package/CSS-INTELLISENSE-QUICK-REF.md +238 -0
  3. package/INTELLISENSE.md +384 -0
  4. package/LICENSE +15 -0
  5. package/custom-elements-manifest.config.js +30 -0
  6. package/custom-elements.json +2003 -0
  7. package/dist/types/index.d.ts +2 -0
  8. package/dist/types/packages/pds-configurator/src/figma-export.d.ts +13 -0
  9. package/dist/types/packages/pds-configurator/src/figma-export.d.ts.map +1 -0
  10. package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts +2 -0
  11. package/dist/types/packages/pds-configurator/src/pds-config-form.d.ts.map +1 -0
  12. package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts +2 -0
  13. package/dist/types/packages/pds-configurator/src/pds-configurator.d.ts.map +1 -0
  14. package/dist/types/packages/pds-configurator/src/pds-demo.d.ts +2 -0
  15. package/dist/types/packages/pds-configurator/src/pds-demo.d.ts.map +1 -0
  16. package/dist/types/pds.config.d.ts +13 -0
  17. package/dist/types/pds.config.d.ts.map +1 -0
  18. package/dist/types/pds.d.ts +408 -0
  19. package/dist/types/public/assets/js/app.d.ts +2 -0
  20. package/dist/types/public/assets/js/app.d.ts.map +1 -0
  21. package/dist/types/public/assets/js/pds.d.ts +23 -0
  22. package/dist/types/public/assets/js/pds.d.ts.map +1 -0
  23. package/dist/types/public/assets/pds/components/pds-calendar.d.ts +23 -0
  24. package/dist/types/public/assets/pds/components/pds-calendar.d.ts.map +1 -0
  25. package/dist/types/public/assets/pds/components/pds-drawer.d.ts +2 -0
  26. package/dist/types/public/assets/pds/components/pds-drawer.d.ts.map +1 -0
  27. package/dist/types/public/assets/pds/components/pds-icon.d.ts +53 -0
  28. package/dist/types/public/assets/pds/components/pds-icon.d.ts.map +1 -0
  29. package/dist/types/public/assets/pds/components/pds-jsonform.d.ts +104 -0
  30. package/dist/types/public/assets/pds/components/pds-jsonform.d.ts.map +1 -0
  31. package/dist/types/public/assets/pds/components/pds-richtext.d.ts +121 -0
  32. package/dist/types/public/assets/pds/components/pds-richtext.d.ts.map +1 -0
  33. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts +61 -0
  34. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts.map +1 -0
  35. package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts +1 -0
  36. package/dist/types/public/assets/pds/components/pds-splitpanel.d.ts.map +1 -0
  37. package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts +39 -0
  38. package/dist/types/public/assets/pds/components/pds-tabstrip.d.ts.map +1 -0
  39. package/dist/types/public/assets/pds/components/pds-toaster.d.ts +111 -0
  40. package/dist/types/public/assets/pds/components/pds-toaster.d.ts.map +1 -0
  41. package/dist/types/public/assets/pds/components/pds-upload.d.ts +83 -0
  42. package/dist/types/public/assets/pds/components/pds-upload.d.ts.map +1 -0
  43. package/dist/types/src/js/app.d.ts +2 -0
  44. package/dist/types/src/js/app.d.ts.map +1 -0
  45. package/dist/types/src/js/common/ask.d.ts +22 -0
  46. package/dist/types/src/js/common/ask.d.ts.map +1 -0
  47. package/dist/types/src/js/common/common.d.ts +3 -0
  48. package/dist/types/src/js/common/common.d.ts.map +1 -0
  49. package/dist/types/src/js/common/font-loader.d.ts +24 -0
  50. package/dist/types/src/js/common/font-loader.d.ts.map +1 -0
  51. package/dist/types/src/js/common/msg.d.ts +3 -0
  52. package/dist/types/src/js/common/msg.d.ts.map +1 -0
  53. package/dist/types/src/js/lit.d.ts +25 -0
  54. package/dist/types/src/js/lit.d.ts.map +1 -0
  55. package/dist/types/src/js/pds-configurator/figma-export.d.ts +13 -0
  56. package/dist/types/src/js/pds-configurator/figma-export.d.ts.map +1 -0
  57. package/dist/types/src/js/pds-configurator/pds-config-form.d.ts +2 -0
  58. package/dist/types/src/js/pds-configurator/pds-config-form.d.ts.map +1 -0
  59. package/dist/types/src/js/pds-configurator/pds-configurator.d.ts +2 -0
  60. package/dist/types/src/js/pds-configurator/pds-configurator.d.ts.map +1 -0
  61. package/dist/types/src/js/pds-configurator/pds-demo.d.ts +2 -0
  62. package/dist/types/src/js/pds-configurator/pds-demo.d.ts.map +1 -0
  63. package/dist/types/src/js/pds-core/pds-config.d.ts +758 -0
  64. package/dist/types/src/js/pds-core/pds-config.d.ts.map +1 -0
  65. package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts +6 -0
  66. package/dist/types/src/js/pds-core/pds-enhancer-metadata.d.ts.map +1 -0
  67. package/dist/types/src/js/pds-core/pds-enhancers.d.ts +14 -0
  68. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -0
  69. package/dist/types/src/js/pds-core/pds-enums.d.ts +87 -0
  70. package/dist/types/src/js/pds-core/pds-enums.d.ts.map +1 -0
  71. package/dist/types/src/js/pds-core/pds-generator.d.ts +741 -0
  72. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -0
  73. package/dist/types/src/js/pds-core/pds-ontology.d.ts +48 -0
  74. package/dist/types/src/js/pds-core/pds-ontology.d.ts.map +1 -0
  75. package/dist/types/src/js/pds-core/pds-paths.d.ts +37 -0
  76. package/dist/types/src/js/pds-core/pds-paths.d.ts.map +1 -0
  77. package/dist/types/src/js/pds-core/pds-query.d.ts +102 -0
  78. package/dist/types/src/js/pds-core/pds-query.d.ts.map +1 -0
  79. package/dist/types/src/js/pds-core/pds-registry.d.ts +40 -0
  80. package/dist/types/src/js/pds-core/pds-registry.d.ts.map +1 -0
  81. package/dist/types/src/js/pds.d.ts +109 -0
  82. package/dist/types/src/js/pds.d.ts.map +1 -0
  83. package/dist/types/src/pds-core/pds-api.d.ts +31 -0
  84. package/dist/types/src/pds-core/pds-api.d.ts.map +1 -0
  85. package/package.json +104 -0
  86. package/packages/pds-cli/README.md +15 -0
  87. package/packages/pds-cli/bin/generate-css-data.js +565 -0
  88. package/packages/pds-cli/bin/generate-manifest.js +352 -0
  89. package/packages/pds-cli/bin/pds-build-icons.js +152 -0
  90. package/packages/pds-cli/bin/pds-dx.js +114 -0
  91. package/packages/pds-cli/bin/pds-static.js +556 -0
  92. package/packages/pds-cli/bin/pds.js +127 -0
  93. package/packages/pds-cli/bin/postinstall.js +380 -0
  94. package/packages/pds-cli/bin/sync-assets.js +252 -0
  95. package/packages/pds-cli/lib/asset-roots.js +47 -0
  96. package/packages/pds-cli/lib/fs-writer.js +75 -0
  97. package/pds.css-data.json +5 -0
  98. package/pds.html-data.json +5 -0
  99. package/public/assets/js/app.js +5719 -0
  100. package/public/assets/js/lit.js +131 -0
  101. package/public/assets/js/pds.js +3423 -0
  102. package/public/assets/pds/components/pds-calendar.js +837 -0
  103. package/public/assets/pds/components/pds-drawer.js +857 -0
  104. package/public/assets/pds/components/pds-icon.js +338 -0
  105. package/public/assets/pds/components/pds-jsonform.js +1775 -0
  106. package/public/assets/pds/components/pds-richtext.js +1035 -0
  107. package/public/assets/pds/components/pds-scrollrow.js +331 -0
  108. package/public/assets/pds/components/pds-splitpanel.js +401 -0
  109. package/public/assets/pds/components/pds-tabstrip.js +251 -0
  110. package/public/assets/pds/components/pds-toaster.js +446 -0
  111. package/public/assets/pds/components/pds-upload.js +657 -0
  112. package/public/assets/pds/custom-elements.json +2003 -0
  113. package/public/assets/pds/icons/pds-icons.svg +498 -0
  114. package/public/assets/pds/pds-css-complete.json +1861 -0
  115. package/public/assets/pds/pds.css-data.json +2152 -0
  116. package/public/assets/pds/vscode-custom-data.json +824 -0
  117. package/readme.md +1870 -0
  118. package/src/js/pds-core/pds-config.js +1162 -0
  119. package/src/js/pds-core/pds-enhancer-metadata.js +75 -0
  120. package/src/js/pds-core/pds-enhancers.js +357 -0
  121. package/src/js/pds-core/pds-enums.js +86 -0
  122. package/src/js/pds-core/pds-generator.js +5317 -0
  123. package/src/js/pds-core/pds-ontology.js +256 -0
  124. package/src/js/pds-core/pds-paths.js +109 -0
  125. package/src/js/pds-core/pds-query.js +571 -0
  126. package/src/js/pds-core/pds-registry.js +129 -0
  127. package/src/js/pds-core/pds.d.ts +129 -0
  128. package/src/js/pds.d.ts +408 -0
  129. package/src/js/pds.js +1579 -0
@@ -0,0 +1,556 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PDS Static Export CLI
5
+ *
6
+ * Generates a static package of Pure Design System assets for consumer apps:
7
+ * - Discovers the web root (reuses discoverWebRoot from postinstall)
8
+ * - Reads pds.config.js (source of truth)
9
+ * - Copies auto-define web components into [public.root]/components/
10
+ * - Generates CSS layers into [public.root]/styles/ (and .css.js modules)
11
+
12
+ * Default target when no public/static root provided: <webroot>/assets/pds
13
+ *
14
+ * Usage:
15
+ * node packages/pds-cli/bin/pds-static.js
16
+ * (or via npm script: npm run pds:static)
17
+ */
18
+
19
+ import { readFile, writeFile, mkdir, readdir, copyFile, stat } from 'fs/promises';
20
+ import { existsSync } from 'fs';
21
+ import path from 'path';
22
+ import { fileURLToPath, pathToFileURL } from 'url';
23
+
24
+ import { discoverWebRoot } from './postinstall.js';
25
+ import { runPdsBuildIcons } from './pds-build-icons.js';
26
+ import { generateManifest } from './generate-manifest.js';
27
+ import { generateCSSData } from './generate-css-data.js';
28
+ import {
29
+ resolvePublicAssetDirectory,
30
+ resolvePublicAssetURL,
31
+ isUrlLike,
32
+ getPublicRootCandidate,
33
+ ensurePdsPath,
34
+ } from '../lib/asset-roots.js';
35
+
36
+ const __filename = fileURLToPath(import.meta.url);
37
+ const __dirname = path.dirname(__filename);
38
+ const repoRoot = path.resolve(__dirname, '../../../');
39
+
40
+ // Lazy imports to avoid circular/packaging issues
41
+ async function loadGenerator() {
42
+ const genPath = path.join(repoRoot, 'src/js/pds-core/pds-generator.js');
43
+ return import(pathToFileURL(genPath).href);
44
+ }
45
+
46
+ let cachedPdsConfigModule = null;
47
+ async function loadPdsConfigModule() {
48
+ if (!cachedPdsConfigModule) {
49
+ const cfgPath = path.join(repoRoot, 'src/js/pds-core/pds-config.js');
50
+ cachedPdsConfigModule = await import(pathToFileURL(cfgPath).href);
51
+ }
52
+ return cachedPdsConfigModule;
53
+ }
54
+
55
+ const DESIGN_KEYS = new Set([
56
+ 'colors',
57
+ 'typography',
58
+ 'spatialRhythm',
59
+ 'shape',
60
+ 'behavior',
61
+ 'layout',
62
+ 'advanced',
63
+ 'a11y',
64
+ 'components',
65
+ 'icons',
66
+ ]);
67
+
68
+ const clone = (value) => {
69
+ if (value === undefined || value === null) return value;
70
+ if (typeof value !== 'object') return value;
71
+ if (typeof globalThis.structuredClone === 'function') {
72
+ return globalThis.structuredClone(value);
73
+ }
74
+ return JSON.parse(JSON.stringify(value));
75
+ };
76
+
77
+ const deepMerge = (target = {}, source = {}) => {
78
+ const out = Array.isArray(target) ? [...target] : { ...target };
79
+ if (!source || typeof source !== 'object') return out;
80
+ for (const [key, value] of Object.entries(source)) {
81
+ if (Array.isArray(value)) {
82
+ out[key] = value.map((item) => clone(item));
83
+ } else if (value && typeof value === 'object') {
84
+ const base = out[key] && typeof out[key] === 'object' ? out[key] : {};
85
+ out[key] = deepMerge(base, value);
86
+ } else {
87
+ out[key] = value;
88
+ }
89
+ }
90
+ return out;
91
+ };
92
+
93
+ const slugify = (str = '') =>
94
+ String(str)
95
+ .toLowerCase()
96
+ .replace(/&/g, ' and ')
97
+ .replace(/[^a-z0-9]+/g, '-')
98
+ .replace(/^-+|-+$/g, '');
99
+
100
+ const stripFunctions = (obj) => {
101
+ if (obj === null || obj === undefined) return obj;
102
+ if (typeof obj === 'function') return undefined;
103
+ if (typeof obj !== 'object') return obj;
104
+
105
+ if (Array.isArray(obj)) {
106
+ return obj
107
+ .map((item) => stripFunctions(item))
108
+ .filter((item) => item !== undefined);
109
+ }
110
+
111
+ const result = {};
112
+ for (const key of Object.keys(obj)) {
113
+ const value = obj[key];
114
+ if (typeof value === 'function') continue;
115
+ const stripped = stripFunctions(value);
116
+ if (stripped !== undefined) {
117
+ result[key] = stripped;
118
+ }
119
+ }
120
+ return result;
121
+ };
122
+
123
+ const looksLikeDesignConfig = (config) => {
124
+ if (!config || typeof config !== 'object') return false;
125
+ return Array.from(DESIGN_KEYS).some((key) => key in config);
126
+ };
127
+
128
+ async function resolveGeneratorOptions(rawConfig = {}) {
129
+ const module = await loadPdsConfigModule();
130
+ const presets = module?.presets || {};
131
+ const defaultLog = module?.defaultLog || ((level, message, ...data) => {
132
+ const method = console[level] || console.log;
133
+ method(message, ...data);
134
+ });
135
+
136
+ const config = rawConfig && typeof rawConfig === 'object' ? rawConfig : {};
137
+ const hasNewShape =
138
+ 'preset' in config || 'design' in config || 'enhancers' in config;
139
+
140
+ if (hasNewShape) {
141
+ const presetId = config?.preset;
142
+ const effectivePreset = String(presetId || 'default').toLowerCase();
143
+ const presetFromMap = presets[effectivePreset];
144
+ const presetList = Object.values(presets || {});
145
+ const matchedPreset =
146
+ presetFromMap ||
147
+ presetList.find((preset) => {
148
+ const idMatch = String(preset?.id || '').toLowerCase() === effectivePreset;
149
+ const nameMatch = slugify(preset?.name || '') === effectivePreset;
150
+ return idMatch || nameMatch;
151
+ });
152
+
153
+ if (!matchedPreset) {
154
+ throw new Error(`PDS preset not found: "${presetId || 'default'}"`);
155
+ }
156
+
157
+ let mergedDesign = clone(matchedPreset);
158
+ if (config.design && typeof config.design === 'object') {
159
+ const overrides = clone(stripFunctions(config.design));
160
+ mergedDesign = deepMerge(mergedDesign, overrides);
161
+ }
162
+
163
+ const {
164
+ mode,
165
+ autoDefine,
166
+ applyGlobalStyles,
167
+ manageTheme,
168
+ themeStorageKey,
169
+ preloadStyles,
170
+ criticalLayers,
171
+ preset: _preset,
172
+ design: _design,
173
+ enhancers: _enhancers,
174
+ log: userLog,
175
+ ...otherProps
176
+ } = config;
177
+
178
+ return {
179
+ ...otherProps,
180
+ design: mergedDesign,
181
+ preset: matchedPreset.name || matchedPreset.id || 'default',
182
+ log: userLog || defaultLog,
183
+ };
184
+ }
185
+
186
+ if (looksLikeDesignConfig(config)) {
187
+ const { log: userLog, ...designConfig } = config;
188
+ const cleanDesign = stripFunctions(designConfig);
189
+ return {
190
+ design: clone(cleanDesign),
191
+ log: userLog || defaultLog,
192
+ };
193
+ }
194
+
195
+ const fallbackPreset = presets?.default || Object.values(presets || {})[0] || {};
196
+ return {
197
+ design: clone(fallbackPreset),
198
+ preset: fallbackPreset.name || fallbackPreset.id || 'default',
199
+ log: defaultLog,
200
+ };
201
+ }
202
+
203
+ // Colors for terminal output
204
+ const COLORS = {
205
+ reset: '\x1b[0m',
206
+ bold: '\x1b[1m',
207
+ green: '\x1b[32m',
208
+ yellow: '\x1b[33m',
209
+ blue: '\x1b[34m',
210
+ magenta: '\x1b[35m',
211
+ red: '\x1b[31m',
212
+ };
213
+
214
+ const shouldLogToStderr = () => process.env.PDS_LOG_STREAM === 'stderr' || process.env.PDS_POSTINSTALL === '1';
215
+ const log = (msg, color = 'reset') => {
216
+ const colorCode = COLORS[color] || '';
217
+ const text = `${colorCode}${msg}${COLORS.reset}`;
218
+ if (shouldLogToStderr()) {
219
+ process.stderr.write(`${text}\n`);
220
+ } else {
221
+ console.log(text);
222
+ }
223
+ };
224
+
225
+ // (Icons generation removed for streamlined static export)
226
+
227
+ // Copy entire directory recursively (conservative: overwrite same-name files only)
228
+ async function copyDirectory(srcDir, dstDir) {
229
+ await mkdir(dstDir, { recursive: true });
230
+ const entries = await readdir(srcDir, { withFileTypes: true });
231
+ for (const entry of entries) {
232
+ const src = path.join(srcDir, entry.name);
233
+ const dst = path.join(dstDir, entry.name);
234
+ if (entry.isDirectory()) {
235
+ await copyDirectory(src, dst);
236
+ } else {
237
+ await mkdir(path.dirname(dst), { recursive: true });
238
+ await copyFile(src, dst);
239
+ log(` • ${path.relative(dstDir, dst)}`, 'blue');
240
+ }
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Update or create .vscode/settings.json with IntelliSense paths
246
+ */
247
+ async function updateVSCodeSettings(targetDir) {
248
+ try {
249
+ const cwd = process.cwd();
250
+ const vscodeDir = path.join(cwd, '.vscode');
251
+ const settingsPath = path.join(vscodeDir, 'settings.json');
252
+
253
+ // Calculate relative paths from workspace root to the generated files
254
+ const htmlCustomDataPath = path.relative(cwd, path.join(targetDir, 'vscode-custom-data.json')).replace(/\\/g, '/');
255
+ const cssCustomDataPath = path.relative(cwd, path.join(targetDir, 'pds.css-data.json')).replace(/\\/g, '/');
256
+
257
+ let settings = {};
258
+
259
+ // Read existing settings if file exists
260
+ if (existsSync(settingsPath)) {
261
+ try {
262
+ const content = await readFile(settingsPath, 'utf-8');
263
+ // Remove comments from JSON (simple approach for // style comments)
264
+ const cleanContent = content.split('\n')
265
+ .filter(line => !line.trim().startsWith('//'))
266
+ .join('\n');
267
+ settings = JSON.parse(cleanContent);
268
+ } catch (e) {
269
+ log(`⚠️ Could not parse existing .vscode/settings.json: ${e.message}`, 'yellow');
270
+ log(' Creating new settings file...', 'yellow');
271
+ }
272
+ }
273
+
274
+ // Update or add the custom data paths
275
+ if (!settings['html.customData']) {
276
+ settings['html.customData'] = [];
277
+ }
278
+ if (!settings['css.customData']) {
279
+ settings['css.customData'] = [];
280
+ }
281
+
282
+ // Remove old PDS paths and add the new one
283
+ settings['html.customData'] = settings['html.customData'].filter(
284
+ p => !p.includes('vscode-custom-data.json') || !p.includes('/pds/')
285
+ );
286
+ settings['css.customData'] = settings['css.customData'].filter(
287
+ p => !p.includes('pds.css-data.json') || !p.includes('/pds/')
288
+ );
289
+
290
+ if (!settings['html.customData'].includes(htmlCustomDataPath)) {
291
+ settings['html.customData'].push(htmlCustomDataPath);
292
+ }
293
+ if (!settings['css.customData'].includes(cssCustomDataPath)) {
294
+ settings['css.customData'].push(cssCustomDataPath);
295
+ }
296
+
297
+ // Ensure .vscode directory exists
298
+ await mkdir(vscodeDir, { recursive: true });
299
+
300
+ // Write settings with proper formatting
301
+ await writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
302
+
303
+ log(`✅ Updated .vscode/settings.json with IntelliSense paths`, 'green');
304
+ log(` • html.customData: ${htmlCustomDataPath}`, 'blue');
305
+ log(` • css.customData: ${cssCustomDataPath}`, 'blue');
306
+ } catch (e) {
307
+ log(`⚠️ Failed to update .vscode/settings.json: ${e?.message || e}`, 'yellow');
308
+ log(' You may need to manually add the IntelliSense paths to your settings.', 'yellow');
309
+ }
310
+ }
311
+
312
+ async function loadConsumerConfig() {
313
+ const cwd = process.cwd();
314
+
315
+ // 1) Prefer repository-level pds.config.js (source of truth for both live and static)
316
+ const repoConfigPath = path.join(repoRoot, 'pds.config.js');
317
+ if (existsSync(repoConfigPath)) {
318
+ log(`📋 Using repo config: ${path.relative(process.cwd(), repoConfigPath)}`,'blue');
319
+ const mod = await import(pathToFileURL(repoConfigPath).href);
320
+ if (mod.config) return mod.config;
321
+ if (mod.default) return mod.default;
322
+ }
323
+
324
+ // 2) Fallbacks for consumer apps: pds.config.js or legacy pds-config.js in CWD
325
+ const candidates = ['pds.config.js', 'pds-config.js'];
326
+ for (const fname of candidates) {
327
+ const configPath = path.join(cwd, fname);
328
+ if (existsSync(configPath)) {
329
+ log(`📋 Using consumer config: ${path.relative(cwd, configPath)}`,'blue');
330
+ try {
331
+ const mod = await import(pathToFileURL(configPath).href);
332
+ if (mod.config) return mod.config;
333
+ if (mod.default) return mod.default;
334
+ if (mod.presets?.default) return mod.presets.default;
335
+ log(`⚠️ Could not resolve config from ${fname}; trying next/fallback`, 'yellow');
336
+ } catch (e) {
337
+ const msg = e?.message || String(e);
338
+ // Helpful guidance when consumer config references browser globals
339
+ if (/PDS is not defined/i.test(msg)) {
340
+ log('❌ Failed to evaluate pds.config.js: PDS is not defined', 'red');
341
+ log(' This usually means your config references the browser global "PDS".', 'yellow');
342
+ log(' The static exporter runs in Node, so please import presets instead of using window.PDS.', 'yellow');
343
+ log(' Example fix for pds.config.js:', 'yellow');
344
+ log(" import { presets } from 'pure-ds/src/js/pds-core/pds-config.js'", 'blue');
345
+ log(' export const config = { ...presets.default, static: { root: "public/assets/pds/" } }', 'blue');
346
+ }
347
+ throw e;
348
+ }
349
+ }
350
+ }
351
+
352
+ // 3) Final fallback to internal default preset
353
+ log('⚠️ No pds.config found; using internal default preset', 'yellow');
354
+ const internalConfigPath = path.join(repoRoot, 'src/js/pds-core/pds-config.js');
355
+ const internal = await import(pathToFileURL(internalConfigPath).href);
356
+ return internal.presets.default;
357
+ }
358
+
359
+ async function main(options = {}) {
360
+ const desiredCwd = options.cwd || process.env.PDS_CONSUMER_ROOT || null;
361
+ const originalCwd = process.cwd();
362
+ let cwdChanged = false;
363
+
364
+ if (desiredCwd && desiredCwd !== originalCwd) {
365
+ try {
366
+ process.chdir(desiredCwd);
367
+ cwdChanged = true;
368
+ } catch (err) {
369
+ log(`⚠️ Could not change working directory to ${desiredCwd}: ${err?.message || err}`, 'yellow');
370
+ }
371
+ }
372
+
373
+ try {
374
+ log('\nPDS Static Export • starting...', 'bold');
375
+
376
+ // 1) Discover web root (used if no explicit static.root is provided)
377
+ const webRoot = await discoverWebRoot();
378
+
379
+ // 2) Load config
380
+ const config = await loadConsumerConfig();
381
+
382
+ // 3) Resolve target directory using normalized public/static root
383
+ const rootCandidate = getPublicRootCandidate(config);
384
+ let targetDir;
385
+ let assetUrlRoot;
386
+
387
+ if (!rootCandidate && config?.staticBase) {
388
+ const baseFolderName = String(config.staticBase || '').trim().replace(/^\/+|\/+$/g, '') || 'assets';
389
+ targetDir = ensurePdsPath(path.join(webRoot.path, baseFolderName));
390
+ assetUrlRoot = resolvePublicAssetURL({ public: { root: baseFolderName } });
391
+ } else {
392
+ targetDir = resolvePublicAssetDirectory(config, { webRootPath: webRoot.path });
393
+ assetUrlRoot = resolvePublicAssetURL(config);
394
+ }
395
+
396
+ log(`📂 Web root: ${webRoot.relative}/`,'blue');
397
+ const relativeTarget = path.relative(process.cwd(), targetDir) || '.';
398
+ log(`📦 Target folder: ${relativeTarget}`,'blue');
399
+ log(`🌐 Public URL: ${assetUrlRoot}`,'blue');
400
+
401
+ if (rootCandidate && isUrlLike(rootCandidate)) {
402
+ log('⚠️ public/static root looks like a URL. Exporting assets under the discovered web root instead.', 'yellow');
403
+ }
404
+
405
+ await mkdir(targetDir, { recursive: true });
406
+
407
+ // 4) Copy component modules into target/components
408
+ // Prefer new location (public/pds/components); fallback to legacy (public/auto-define)
409
+ const compSrcCandidates = [
410
+ path.join(repoRoot, 'public/assets/pds/components'),
411
+ path.join(repoRoot, 'public/pds/components'),
412
+ path.join(repoRoot, 'public/auto-define'),
413
+ ];
414
+ let componentsSource = compSrcCandidates.find(p => existsSync(p));
415
+ if (!componentsSource) {
416
+ log(`⚠️ No components source found (looked for: ${compSrcCandidates.map(p=>path.relative(repoRoot,p)).join(', ')})`, 'yellow');
417
+ log(` Skipping components copy. You can still use generated styles.`, 'yellow');
418
+ } else {
419
+ const componentsDir = path.join(targetDir, 'components');
420
+ log(`📁 Copying components → ${path.relative(process.cwd(), componentsDir)}`,'bold');
421
+ await copyDirectory(componentsSource, componentsDir);
422
+ }
423
+
424
+ // 5) Generate CSS layers into target/styles
425
+ log('🧬 Generating styles...', 'bold');
426
+ const { Generator } = await loadGenerator();
427
+ const generatorOptions = await resolveGeneratorOptions(config);
428
+ const designer = new Generator(generatorOptions);
429
+ const stylesDir = path.join(targetDir, 'styles');
430
+ await mkdir(stylesDir, { recursive: true });
431
+ await writeFile(path.join(stylesDir, 'pds-tokens.css'), designer.tokensCSS, 'utf-8');
432
+ await writeFile(path.join(stylesDir, 'pds-primitives.css'), designer.primitivesCSS, 'utf-8');
433
+ await writeFile(path.join(stylesDir, 'pds-components.css'), designer.componentsCSS, 'utf-8');
434
+ await writeFile(path.join(stylesDir, 'pds-utilities.css'), designer.utilitiesCSS, 'utf-8');
435
+ await writeFile(path.join(stylesDir, 'pds-styles.css'), designer.layeredCSS, 'utf-8');
436
+
437
+ // Also emit constructable stylesheet modules for PDS.start({ mode: 'static' })
438
+ const modules = designer.getCSSModules();
439
+ for (const [filename, content] of Object.entries(modules)) {
440
+ await writeFile(path.join(stylesDir, filename), content, 'utf-8');
441
+ }
442
+ log(`✅ Styles written to ${path.relative(process.cwd(), stylesDir)} (and CSS.js modules)`, 'green');
443
+
444
+ // 5b) Build custom icon sprite when consumer config overrides defaults; otherwise copy stock sprite
445
+ try {
446
+ const internalConfigPath = path.join(repoRoot, 'src/js/pds-core/pds-config.js');
447
+ const internal = await import(pathToFileURL(internalConfigPath).href);
448
+ const defaultIcons = internal.presets?.default?.icons || {};
449
+ const consumerIcons = config?.icons || null;
450
+
451
+ const isDifferent = () => {
452
+ if (!consumerIcons) return false;
453
+ const setDiff = consumerIcons.set && consumerIcons.set !== defaultIcons.set;
454
+ const weightDiff = consumerIcons.weight && consumerIcons.weight !== defaultIcons.weight;
455
+ const includeDiff = JSON.stringify(consumerIcons.include || {}) !== JSON.stringify(defaultIcons.include || {});
456
+ return Boolean(setDiff || weightDiff || includeDiff);
457
+ };
458
+
459
+ const iconsDir = path.join(targetDir, 'icons');
460
+ await mkdir(iconsDir, { recursive: true });
461
+ const iconTarget = path.join(iconsDir, 'pds-icons.svg');
462
+
463
+ if (isDifferent()) {
464
+ log('🛠️ Detected custom icons in consumer config — building sprite...', 'bold');
465
+ // Pass the resolved targetDir to the builder via env override
466
+ const prev = process.env.PDS_STATIC_ROOT;
467
+ process.env.PDS_STATIC_ROOT = targetDir;
468
+ try {
469
+ await runPdsBuildIcons();
470
+ log(`✅ Icons sprite built at ${path.relative(process.cwd(), iconTarget)}`, 'green');
471
+ } finally {
472
+ // restore previous env var (if any)
473
+ if (prev === undefined) delete process.env.PDS_STATIC_ROOT; else process.env.PDS_STATIC_ROOT = prev;
474
+ }
475
+ } else {
476
+ // Copy stock sprite shipped with the package
477
+ const iconSource = path.join(repoRoot, 'public/assets/pds/icons/pds-icons.svg');
478
+ await copyFile(iconSource, iconTarget);
479
+ log(`✅ Icons sprite copied to ${path.relative(process.cwd(), iconTarget)}`, 'green');
480
+ }
481
+ } catch (e) {
482
+ log(`⚠️ Icon sprite step encountered an issue: ${e?.message || e}`, 'yellow');
483
+ }
484
+
485
+ // 6) Generate Custom Elements Manifest (HTML IntelliSense)
486
+ try {
487
+ await generateManifest(targetDir);
488
+ } catch (e) {
489
+ log(`⚠️ Custom Elements Manifest generation failed: ${e?.message || e}`, 'yellow');
490
+ }
491
+
492
+ // 7) Generate CSS Custom Data (CSS IntelliSense)
493
+ try {
494
+ await generateCSSData(targetDir);
495
+ } catch (e) {
496
+ log(`⚠️ CSS custom data generation failed: ${e?.message || e}`, 'yellow');
497
+ }
498
+
499
+ // 8) Update .vscode/settings.json with IntelliSense paths
500
+ await updateVSCodeSettings(targetDir);
501
+
502
+ // 9) Write runtime config helper for auto-discovery
503
+ try {
504
+ const runtimeConfig = {
505
+ exportedAt: new Date().toISOString(),
506
+ staticRoot: assetUrlRoot,
507
+ paths: {
508
+ tokens: `${assetUrlRoot}styles/pds-tokens.css.js`,
509
+ primitives: `${assetUrlRoot}styles/pds-primitives.css.js`,
510
+ components: `${assetUrlRoot}styles/pds-components.css.js`,
511
+ utilities: `${assetUrlRoot}styles/pds-utilities.css.js`,
512
+ styles: `${assetUrlRoot}styles/pds-styles.css.js`,
513
+ }
514
+ };
515
+
516
+ const runtimeConfigPath = path.join(targetDir, 'pds-runtime-config.json');
517
+ await writeFile(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2), 'utf-8');
518
+ log(`✅ Runtime config written to ${path.relative(process.cwd(), runtimeConfigPath)}`, 'green');
519
+ log(`💡 Use this in PDS.start(): static: { root: "${assetUrlRoot}" }`, 'blue');
520
+ } catch (e) {
521
+ log(`⚠️ Failed to write runtime config: ${e?.message || e}`, 'yellow');
522
+ }
523
+
524
+ // 10) Summary
525
+ log('\n────────────────────────────────────────────');
526
+ log('✅ PDS static assets ready', 'green');
527
+ log(`📍 Location: ${path.relative(process.cwd(), targetDir)}`);
528
+ log('• components → components/*.js');
529
+ log('• styles → styles/pds-*.css (+ .css.js modules)');
530
+ log('• intellisense → custom-elements.json, vscode-custom-data.json');
531
+ log('• intellisense → pds.css-data.json, pds-css-complete.json');
532
+ log('────────────────────────────────────────────\n');
533
+
534
+ } catch (err) {
535
+ console.error('❌ pds:static failed:', err?.message || err);
536
+ process.exit(1);
537
+ } finally {
538
+ if (cwdChanged) {
539
+ try {
540
+ process.chdir(originalCwd);
541
+ } catch {}
542
+ }
543
+ }
544
+ }
545
+
546
+ // Execute when run directly from Node or via bin entry (including symlinks on macOS/Linux)
547
+ if (process.argv[1]) {
548
+ const scriptPath = fileURLToPath(import.meta.url);
549
+ const argPath = process.argv[1];
550
+ // Check if this is the main module being executed
551
+ if (argPath === scriptPath || argPath.endsWith('pds-static.js') || argPath.endsWith('pds-export')) {
552
+ main();
553
+ }
554
+ }
555
+
556
+ export { main as runPdsStatic };
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import { generateFromFile, generate, watch as watchConfig } from '../../../src/pds-core/pds-api.js';
3
+ import { writeFilesAtomic } from '../lib/fs-writer.js';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ function help() {
8
+ console.log(`pds-cli — generate and watch PDS artifacts
9
+
10
+ Usage:
11
+ pds-cli generate --config ./pds.config.json --out ./public/pds
12
+ pds-cli watch --config ./pds.config.json --out ./public/pds
13
+
14
+ Options:
15
+ --config <path> Path to pds.config.json (required)
16
+ --out <dir> Output directory to write generated files (required)
17
+ --clean Remove stale files from the output directory
18
+ --help Show this help
19
+ `);
20
+ }
21
+
22
+ function parseArgs(argv) {
23
+ const args = { _: [] };
24
+ for (let i = 2; i < argv.length; i++) {
25
+ const a = argv[i];
26
+ if (a.startsWith('--')) {
27
+ const key = a.slice(2);
28
+ const next = argv[i + 1];
29
+ if (!next || next.startsWith('--')) {
30
+ args[key] = true;
31
+ } else {
32
+ args[key] = next;
33
+ i++;
34
+ }
35
+ } else {
36
+ args._.push(a);
37
+ }
38
+ }
39
+ return args;
40
+ }
41
+
42
+ async function runGenerate(configPath, outDir, opts = {}) {
43
+ if (!configPath || !outDir) {
44
+ console.error('generate requires --config and --out');
45
+ process.exitCode = 2;
46
+ return;
47
+ }
48
+
49
+ try {
50
+ const { modules, meta } = await generateFromFile(configPath);
51
+ // write modules to outDir (atomically). If clean is set, stale files will be removed.
52
+ const res = await writeFilesAtomic(outDir, modules, { clean: !!opts.clean });
53
+ console.log('PDS: generated', Object.keys(modules).length, 'files to', outDir);
54
+ if (res && Array.isArray(res.removed) && res.removed.length) {
55
+ console.log(`PDS: removed ${res.removed.length} stale files`);
56
+ if (opts.verbose) {
57
+ for (const f of res.removed) console.log(' -', f);
58
+ }
59
+ }
60
+ console.log('meta:', meta);
61
+ } catch (err) {
62
+ console.error('PDS generate failed:', err.message || err);
63
+ process.exitCode = 1;
64
+ }
65
+ }
66
+
67
+ async function runWatch(configPath, outDir, opts = {}) {
68
+ if (!configPath || !outDir) {
69
+ console.error('watch requires --config and --out');
70
+ process.exitCode = 2;
71
+ return;
72
+ }
73
+
74
+ console.log('PDS watch starting — config:', configPath, 'out:', outDir);
75
+
76
+ const watcher = watchConfig(configPath, outDir, {
77
+ onWrite: async (modules, out, meta) => {
78
+ try {
79
+ const res = await writeFilesAtomic(outDir, modules, { clean: !!opts.clean });
80
+ console.log('PDS: wrote', Object.keys(modules).length, 'files at', new Date().toISOString());
81
+ if (res && Array.isArray(res.removed) && res.removed.length) {
82
+ console.log(`PDS: removed ${res.removed.length} stale files`);
83
+ if (opts.verbose) for (const f of res.removed) console.log(' -', f);
84
+ }
85
+ } catch (err) {
86
+ console.error('PDS write failed', err);
87
+ }
88
+ },
89
+ onError: (err) => {
90
+ console.error('PDS watch error', err);
91
+ }
92
+ });
93
+
94
+ process.on('SIGINT', () => {
95
+ watcher.close();
96
+ console.log('PDS watch stopped');
97
+ process.exit(0);
98
+ });
99
+ }
100
+
101
+ async function main() {
102
+ const args = parseArgs(process.argv);
103
+ const cmd = args._[0];
104
+ if (!cmd || args.help) {
105
+ help();
106
+ return;
107
+ }
108
+
109
+ const configPath = args.config || args.c;
110
+ const outDir = args.out || args.o;
111
+
112
+ if (cmd === 'generate') {
113
+ await runGenerate(configPath, outDir, { clean: args.clean });
114
+ return;
115
+ }
116
+
117
+ if (cmd === 'watch') {
118
+ await runWatch(configPath, outDir, { clean: args.clean });
119
+ return;
120
+ }
121
+
122
+ console.error('Unknown command:', cmd);
123
+ help();
124
+ process.exitCode = 2;
125
+ }
126
+
127
+ main();