@schandlergarcia/sf-web-components 1.9.39 → 1.9.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schandlergarcia/sf-web-components",
3
- "version": "1.9.39",
3
+ "version": "1.9.41",
4
4
  "description": "Reusable Salesforce web components library with Tailwind CSS v4 and shadcn/ui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -67,6 +67,7 @@
67
67
  "dependencies": {
68
68
  "class-variance-authority": "^0.7.1",
69
69
  "clsx": "^2.1.1",
70
+ "glob": "^11.0.0",
70
71
  "tailwind-merge": "^3.5.0"
71
72
  },
72
73
  "devDependencies": {
@@ -1,167 +1,368 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Post-install script for @schandlergarcia/sf-web-components
4
+ * Post-install script
5
5
  *
6
- * Automatically copies to the consuming project:
7
- * - Component library source (src/components/library/)
8
- * - Sample data (src/data/)
9
- * - Skills (.a4drules/)
10
- * - Scripts (scripts/)
6
+ * Automatically runs after installing @schandlergarcia/sf-web-components
7
+ * Copies UI components into the consuming project so Tailwind can properly scan them
11
8
  */
12
9
 
13
10
  import fs from 'fs';
14
11
  import path from 'path';
15
12
  import { fileURLToPath } from 'url';
13
+ import { glob } from 'glob';
16
14
 
17
15
  const __filename = fileURLToPath(import.meta.url);
18
16
  const __dirname = path.dirname(__filename);
19
17
 
20
- // Get the package root (one level up from scripts/)
21
- const PACKAGE_ROOT = path.resolve(__dirname, '..');
18
+ // Only run if we're in a consuming project (not the package itself)
19
+ const cwd = process.cwd();
20
+ const isPackageItself = cwd.includes('sf-web-components') && fs.existsSync(path.join(cwd, 'scripts/postinstall.mjs'));
22
21
 
23
- // Get the project root (where the consuming project installed this package)
24
- // Go up from node_modules/@schandlergarcia/sf-web-components
25
- const PROJECT_ROOT = path.resolve(PACKAGE_ROOT, '../../..');
26
-
27
- console.log('\nšŸ“¦ @schandlergarcia/sf-web-components post-install\n');
28
-
29
- // Check if we're in a node_modules context (not during package development)
30
- if (!PACKAGE_ROOT.includes('node_modules')) {
31
- console.log(' ā„¹ļø Running in development mode, skipping setup\n');
22
+ if (isPackageItself) {
23
+ console.log('Skipping postinstall - running in package directory');
32
24
  process.exit(0);
33
25
  }
34
26
 
35
- // Find webapp directory
36
- function findWebappDir() {
37
- // Try direct src/
38
- if (fs.existsSync(path.join(PROJECT_ROOT, 'src'))) {
39
- return PROJECT_ROOT;
27
+ console.log('šŸ”„ Running @schandlergarcia/sf-web-components setup...\n');
28
+
29
+ const PACKAGE_NAME = '@schandlergarcia/sf-web-components';
30
+ let filesUpdated = 0;
31
+ let componentsCopied = 0;
32
+
33
+ // Copy entire library directory
34
+ const packageRoot = path.join(cwd, 'node_modules', PACKAGE_NAME);
35
+ const sourceLibraryDir = path.join(packageRoot, 'src/components/library');
36
+ const targetLibraryDir = path.join(cwd, 'src/components/library');
37
+
38
+ console.log('šŸ“¦ Copying component library...\n');
39
+
40
+ function copyDirectoryRecursive(source, target) {
41
+ if (!fs.existsSync(source)) return 0;
42
+
43
+ let count = 0;
44
+
45
+ if (!fs.existsSync(target)) {
46
+ fs.mkdirSync(target, { recursive: true });
40
47
  }
41
48
 
42
- // Try force-app structure
43
- const webappRoot = path.join(PROJECT_ROOT, 'force-app/main/default/webapplications');
44
- if (fs.existsSync(webappRoot)) {
45
- const webapps = fs.readdirSync(webappRoot, { withFileTypes: true });
46
- for (const webapp of webapps) {
47
- if (webapp.isDirectory()) {
48
- const webappDir = path.join(webappRoot, webapp.name);
49
- if (fs.existsSync(path.join(webappDir, 'src'))) {
50
- return webappDir;
51
- }
49
+ const items = fs.readdirSync(source);
50
+
51
+ for (const item of items) {
52
+ // Skip hidden directories and files (like .sfdx, .git, etc.)
53
+ if (item.startsWith('.')) {
54
+ continue;
55
+ }
56
+
57
+ const sourcePath = path.join(source, item);
58
+ const targetPath = path.join(target, item);
59
+ const stat = fs.statSync(sourcePath);
60
+
61
+ if (stat.isDirectory()) {
62
+ count += copyDirectoryRecursive(sourcePath, targetPath);
63
+ } else if (item.match(/\.(jsx|tsx|js|ts|css|md)$/)) {
64
+ try {
65
+ fs.copyFileSync(sourcePath, targetPath);
66
+ count++;
67
+ } catch (error) {
68
+ console.error(` āœ— Failed to copy ${item}: ${error.message}`);
52
69
  }
53
70
  }
54
71
  }
55
72
 
56
- return null;
73
+ return count;
57
74
  }
58
75
 
59
- const WEBAPP_DIR = findWebappDir();
76
+ if (fs.existsSync(sourceLibraryDir)) {
77
+ componentsCopied = copyDirectoryRecursive(sourceLibraryDir, targetLibraryDir);
78
+ console.log(` āœ“ Copied ${componentsCopied} component files\n`);
79
+ }
60
80
 
61
- if (!WEBAPP_DIR) {
62
- console.log(' āš ļø Could not find webapp directory with src/');
63
- console.log(' ā„¹ļø Skipping automated setup\n');
64
- process.exit(0);
81
+ // Also copy lib directory (utils, etc.)
82
+ const sourceLibDir = path.join(packageRoot, 'src/lib');
83
+ const targetLibDir = path.join(cwd, 'src/lib');
84
+
85
+ if (fs.existsSync(sourceLibDir)) {
86
+ const libFilesCopied = copyDirectoryRecursive(sourceLibDir, targetLibDir);
87
+ console.log(` āœ“ Copied ${libFilesCopied} lib files\n`);
88
+ }
89
+
90
+ // Copy types directory
91
+ const sourceTypesDir = path.join(packageRoot, 'src/types');
92
+ const targetTypesDir = path.join(cwd, 'src/types');
93
+
94
+ if (fs.existsSync(sourceTypesDir)) {
95
+ const typesFilesCopied = copyDirectoryRecursive(sourceTypesDir, targetTypesDir);
96
+ console.log(` āœ“ Copied ${typesFilesCopied} type files\n`);
65
97
  }
66
98
 
67
- let copiedCount = 0;
99
+ // Copy workspace directory (ComponentRegistry, etc.)
100
+ const sourceWorkspaceDir = path.join(packageRoot, 'src/components/workspace');
101
+ const targetWorkspaceDir = path.join(cwd, 'src/components/workspace');
68
102
 
69
- // Helper: Copy directory recursively
70
- function copyRecursive(src, dest) {
71
- if (fs.statSync(src).isDirectory()) {
72
- if (!fs.existsSync(dest)) {
73
- fs.mkdirSync(dest, { recursive: true });
103
+ if (fs.existsSync(sourceWorkspaceDir)) {
104
+ const workspaceFilesCopied = copyDirectoryRecursive(sourceWorkspaceDir, targetWorkspaceDir);
105
+ console.log(` āœ“ Copied ${workspaceFilesCopied} workspace files\n`);
106
+ }
107
+
108
+ // Copy workspace templates (CommandCenter, etc.)
109
+ const workspaceTemplatesDir = path.join(packageRoot, 'src/templates/workspace');
110
+ if (fs.existsSync(workspaceTemplatesDir)) {
111
+ if (!fs.existsSync(targetWorkspaceDir)) {
112
+ fs.mkdirSync(targetWorkspaceDir, { recursive: true });
113
+ }
114
+
115
+ const workspaceTemplates = fs.readdirSync(workspaceTemplatesDir).filter(f => f.endsWith('.template'));
116
+ let workspaceTemplatesCopied = 0;
117
+
118
+ for (const template of workspaceTemplates) {
119
+ const sourcePath = path.join(workspaceTemplatesDir, template);
120
+ const targetFileName = template.replace('.template', '');
121
+ const targetPath = path.join(targetWorkspaceDir, targetFileName);
122
+
123
+ try {
124
+ const content = fs.readFileSync(sourcePath, 'utf-8');
125
+ fs.writeFileSync(targetPath, content, 'utf-8');
126
+ workspaceTemplatesCopied++;
127
+ } catch (error) {
128
+ console.error(` āœ— Failed to copy ${targetFileName}: ${error.message}`);
74
129
  }
75
- fs.readdirSync(src).forEach(file => {
76
- copyRecursive(path.join(src, file), path.join(dest, file));
77
- });
78
- } else {
79
- fs.copyFileSync(src, dest);
130
+ }
131
+
132
+ if (workspaceTemplatesCopied > 0) {
133
+ console.log(` āœ“ Copied ${workspaceTemplatesCopied} workspace template files\n`);
80
134
  }
81
135
  }
82
136
 
83
- // 1. Copy component library source
84
- try {
85
- const targetComponentsDir = path.join(WEBAPP_DIR, 'src/components');
86
- const sourceLibraryDir = path.join(PACKAGE_ROOT, 'src/components/library');
137
+ // Copy pages directory (sample pages, etc.)
138
+ const sourcePagesDir = path.join(packageRoot, 'src/components/pages');
139
+ const targetComponentPagesDir = path.join(cwd, 'src/components/pages');
87
140
 
88
- if (fs.existsSync(sourceLibraryDir)) {
89
- const targetLibraryDir = path.join(targetComponentsDir, 'library');
141
+ if (fs.existsSync(sourcePagesDir)) {
142
+ const pagesFilesCopied = copyDirectoryRecursive(sourcePagesDir, targetComponentPagesDir);
143
+ console.log(` āœ“ Copied ${pagesFilesCopied} component pages\n`);
144
+ }
90
145
 
91
- if (!fs.existsSync(targetLibraryDir)) {
92
- copyRecursive(sourceLibraryDir, targetLibraryDir);
93
- console.log(' āœ… Component library → src/components/library/');
94
- copiedCount++;
146
+ // Update imports in existing files to use local library
147
+ const files = glob.sync('src/**/*.{ts,tsx,js,jsx}', {
148
+ cwd,
149
+ absolute: true,
150
+ ignore: ['**/node_modules/**', '**/dist/**', '**/components/library/**']
151
+ });
152
+
153
+ console.log(`\nšŸ”„ Updating imports in ${files.length} files...\n`);
154
+
155
+ for (const file of files) {
156
+ try {
157
+ let content = fs.readFileSync(file, 'utf-8');
158
+ let modified = false;
159
+
160
+ // Replace package imports with local library imports
161
+ // Match: import { UIButton, Card, MetricCard } from '@schandlergarcia/sf-web-components';
162
+ // Replace: import { UIButton, Card, MetricCard } from '@/components/library';
163
+ const packageImportRegex = new RegExp(
164
+ `from\\s+['"]${PACKAGE_NAME}['"]`,
165
+ 'g'
166
+ );
167
+
168
+ if (packageImportRegex.test(content)) {
169
+ content = content.replace(packageImportRegex, `from '@/components/library'`);
170
+ modified = true;
95
171
  }
172
+
173
+ // Also replace specific subpath imports
174
+ const subpathRegex = new RegExp(
175
+ `from\\s+['"]${PACKAGE_NAME}/lib['"]`,
176
+ 'g'
177
+ );
178
+
179
+ if (subpathRegex.test(content)) {
180
+ content = content.replace(subpathRegex, `from '@/lib'`);
181
+ modified = true;
182
+ }
183
+
184
+ if (modified) {
185
+ fs.writeFileSync(file, content, 'utf-8');
186
+ filesUpdated++;
187
+ console.log(` āœ“ ${path.relative(cwd, file)}`);
188
+ }
189
+ } catch (error) {
190
+ console.error(` āœ— Error updating ${file}: ${error.message}`);
96
191
  }
97
- } catch (err) {
98
- console.log(' āš ļø Could not copy component library:', err.message);
99
192
  }
100
193
 
101
- // 2. Copy sample data
102
- try {
103
- const dataDir = path.join(WEBAPP_DIR, 'src/data');
104
- if (!fs.existsSync(dataDir)) {
105
- fs.mkdirSync(dataDir, { recursive: true });
194
+ // Copy page templates
195
+ const templatesDir = path.join(path.dirname(__dirname), 'src/templates/pages');
196
+ const targetPagesDir = path.join(cwd, 'src/pages');
197
+
198
+ console.log('\nšŸ“„ Installing page templates...\n');
199
+
200
+ let templatesInstalled = 0;
201
+ const installedTemplates = [];
202
+
203
+ if (fs.existsSync(templatesDir)) {
204
+ // Create target pages directory if it doesn't exist
205
+ if (!fs.existsSync(targetPagesDir)) {
206
+ fs.mkdirSync(targetPagesDir, { recursive: true });
106
207
  }
107
208
 
108
- const sourceFile = path.join(PACKAGE_ROOT, 'data/engine-sample-data.js');
109
- const targetFile = path.join(dataDir, 'engine-sample-data.js');
209
+ const templates = fs.readdirSync(templatesDir).filter(f => f.endsWith('.template'));
210
+
211
+ for (const template of templates) {
212
+ const sourcePath = path.join(templatesDir, template);
213
+ const targetFileName = template.replace('.template', '');
214
+ const targetPath = path.join(targetPagesDir, targetFileName);
215
+ const pageName = targetFileName.replace('.tsx', '');
110
216
 
111
- if (!fs.existsSync(targetFile)) {
112
- fs.copyFileSync(sourceFile, targetFile);
113
- console.log(' āœ… Sample data → src/data/engine-sample-data.js');
114
- copiedCount++;
217
+ // Always overwrite to ensure latest templates
218
+ try {
219
+ const content = fs.readFileSync(sourcePath, 'utf-8');
220
+ fs.writeFileSync(targetPath, content, 'utf-8');
221
+ console.log(` āœ“ Installed ${targetFileName}`);
222
+ templatesInstalled++;
223
+ installedTemplates.push(pageName);
224
+ } catch (error) {
225
+ console.error(` āœ— Failed to install ${targetFileName}: ${error.message}`);
226
+ }
115
227
  }
116
- } catch (err) {
117
- console.log(' āš ļø Could not copy sample data:', err.message);
118
228
  }
119
229
 
120
- // 3. Copy .a4drules (skills)
121
- try {
122
- const targetRulesDir = path.join(WEBAPP_DIR, '.a4drules');
123
- const sourceRulesDir = path.join(PACKAGE_ROOT, '.a4drules');
230
+ // Copy routes.tsx template with full configuration
231
+ const routesTemplatePath = path.join(packageRoot, 'src/templates/config/routes.tsx.template');
232
+ const routesPath = path.join(cwd, 'src/routes.tsx');
233
+
234
+ if (fs.existsSync(routesTemplatePath) && fs.existsSync(path.join(cwd, 'src'))) {
235
+ console.log('\nšŸ”„ Installing routes configuration...\n');
124
236
 
125
- if (!fs.existsSync(targetRulesDir) && fs.existsSync(sourceRulesDir)) {
126
- copyRecursive(sourceRulesDir, targetRulesDir);
127
- console.log(' āœ… Skills → .a4drules/');
128
- copiedCount++;
237
+ try {
238
+ const routesContent = fs.readFileSync(routesTemplatePath, 'utf-8');
239
+ fs.writeFileSync(routesPath, routesContent, 'utf-8');
240
+ console.log(' āœ“ Installed complete routes.tsx with Account search and detail pages');
241
+ } catch (error) {
242
+ console.error(` āœ— Failed to install routes.tsx: ${error.message}`);
129
243
  }
130
- } catch (err) {
131
- console.log(' āš ļø Could not copy skills:', err.message);
132
244
  }
133
245
 
134
- // 4. Copy scripts
135
- try {
136
- const scriptsDir = path.join(WEBAPP_DIR, 'scripts');
137
- if (!fs.existsSync(scriptsDir)) {
138
- fs.mkdirSync(scriptsDir, { recursive: true });
139
- }
246
+ // Add reset:command-center script to package.json
247
+ const packageJsonPath = path.join(cwd, 'package.json');
248
+ if (fs.existsSync(packageJsonPath)) {
249
+ console.log('\nšŸ”§ Adding reset script to package.json...\n');
140
250
 
141
- const scriptsToCopy = ['reset-command-center.sh', 'validate-dashboard.sh'];
142
- for (const script of scriptsToCopy) {
143
- const sourceFile = path.join(PACKAGE_ROOT, 'scripts', script);
144
- const targetFile = path.join(scriptsDir, script);
251
+ try {
252
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
145
253
 
146
- if (fs.existsSync(sourceFile) && !fs.existsSync(targetFile)) {
147
- fs.copyFileSync(sourceFile, targetFile);
148
- fs.chmodSync(targetFile, 0o755); // Make executable
149
- console.log(` āœ… Script → scripts/${script}`);
150
- copiedCount++;
254
+ if (!packageJson.scripts) {
255
+ packageJson.scripts = {};
151
256
  }
257
+
258
+ // Add the reset:command-center script if it doesn't exist
259
+ let scriptsAdded = [];
260
+ if (!packageJson.scripts['reset:command-center']) {
261
+ packageJson.scripts['reset:command-center'] = 'bash node_modules/@schandlergarcia/sf-web-components/scripts/reset-command-center.sh';
262
+ scriptsAdded.push('reset:command-center');
263
+ }
264
+
265
+ // Add the validate:dashboard script if it doesn't exist
266
+ if (!packageJson.scripts['validate:dashboard']) {
267
+ packageJson.scripts['validate:dashboard'] = 'bash node_modules/@schandlergarcia/sf-web-components/scripts/validate-dashboard.sh';
268
+ scriptsAdded.push('validate:dashboard');
269
+ }
270
+
271
+ if (scriptsAdded.length > 0) {
272
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf-8');
273
+ scriptsAdded.forEach(script => {
274
+ console.log(` āœ“ Added "${script}" script to package.json`);
275
+ });
276
+ } else {
277
+ console.log(' ℹ Scripts already exist');
278
+ }
279
+ } catch (error) {
280
+ console.error(` āœ— Failed to update package.json: ${error.message}`);
281
+ }
282
+ }
283
+
284
+ // Copy .a4drules from package to project root (so AI assistants can discover them)
285
+ const packageA4dRules = path.join(packageRoot, '.a4drules');
286
+ const projectRootA4dRules = path.join(cwd, '../../../../../.a4drules'); // Go up from webapp to project root
287
+
288
+ if (fs.existsSync(packageA4dRules)) {
289
+ console.log('\nšŸ“‹ Copying AI assistant rules to project root...\n');
290
+
291
+ // Resolve to absolute path
292
+ const projectRootPath = path.resolve(cwd, '../../../../../');
293
+ const targetA4dRules = path.join(projectRootPath, '.a4drules');
294
+
295
+ // Create .a4drules if it doesn't exist
296
+ if (!fs.existsSync(targetA4dRules)) {
297
+ fs.mkdirSync(targetA4dRules, { recursive: true });
298
+ }
299
+
300
+ // Copy skills directory
301
+ const skillsSource = path.join(packageA4dRules, 'skills');
302
+ const skillsTarget = path.join(targetA4dRules, 'skills');
303
+ if (fs.existsSync(skillsSource)) {
304
+ const skillsCopied = copyDirectoryRecursive(skillsSource, skillsTarget);
305
+ console.log(` āœ“ Copied ${skillsCopied} skill files`);
306
+ }
307
+
308
+ // Copy features directory
309
+ const featuresSource = path.join(packageA4dRules, 'features');
310
+ const featuresTarget = path.join(targetA4dRules, 'features');
311
+ if (fs.existsSync(featuresSource)) {
312
+ const featuresCopied = copyDirectoryRecursive(featuresSource, featuresTarget);
313
+ console.log(` āœ“ Copied ${featuresCopied} feature rule files`);
152
314
  }
153
- } catch (err) {
154
- console.log(' āš ļø Could not copy scripts:', err.message);
155
315
  }
156
316
 
157
- // Summary
158
- if (copiedCount > 0) {
159
- console.log(`\n šŸŽ‰ Setup complete! ${copiedCount} items copied\n`);
160
- console.log(' Next steps:\n');
161
- console.log(' npm run reset:command-center # Reset to baseline state');
162
- console.log(' npm run dev # Start dev server\n');
163
- } else {
164
- console.log(' ā„¹ļø All files already exist (not overwriting)\n');
317
+ // Migrate any dashboards from old location (src/components/pages/) to new location (src/pages/)
318
+ const oldPagesDir = path.join(cwd, 'src/components/pages');
319
+ const newPagesDir = path.join(cwd, 'src/pages');
320
+ let migratedFiles = 0;
321
+
322
+ if (fs.existsSync(oldPagesDir)) {
323
+ console.log('\nšŸ”„ Migrating dashboards from old location...\n');
324
+
325
+ const oldFiles = fs.readdirSync(oldPagesDir).filter(f =>
326
+ (f.endsWith('.jsx') || f.endsWith('.tsx')) &&
327
+ f.includes('Dashboard') &&
328
+ f !== 'BlankDashboard.jsx' &&
329
+ f !== 'BlankDashboard.tsx'
330
+ );
331
+
332
+ for (const file of oldFiles) {
333
+ const oldPath = path.join(oldPagesDir, file);
334
+ const newFile = file.replace('.jsx', '.tsx'); // Also convert .jsx to .tsx
335
+ const newPath = path.join(newPagesDir, newFile);
336
+
337
+ try {
338
+ fs.renameSync(oldPath, newPath);
339
+ console.log(` āœ“ Migrated ${file} → src/pages/${newFile}`);
340
+ migratedFiles++;
341
+ } catch (error) {
342
+ console.error(` āœ— Failed to migrate ${file}: ${error.message}`);
343
+ }
344
+ }
345
+
346
+ // Try to remove old directory if empty
347
+ try {
348
+ const remaining = fs.readdirSync(oldPagesDir);
349
+ if (remaining.length === 0) {
350
+ fs.rmdirSync(oldPagesDir);
351
+ console.log(' āœ“ Removed empty src/components/pages directory');
352
+ }
353
+ } catch {
354
+ // Ignore errors
355
+ }
165
356
  }
166
357
 
167
- process.exit(0);
358
+ console.log('\nšŸ“Š Summary:');
359
+ console.log(` - Copied ${componentsCopied} UI components`);
360
+ console.log(` - Updated ${filesUpdated} files`);
361
+ console.log(` - Installed ${templatesInstalled} page templates`);
362
+ console.log(` - Installed CommandCenter.tsx for dashboard management`);
363
+ console.log(` - Added "npm run reset:command-center" script`);
364
+ console.log(` - Installed AI assistant rules for command center building`);
365
+ if (migratedFiles > 0) {
366
+ console.log(` - Migrated ${migratedFiles} dashboard files to correct location`);
367
+ }
368
+ console.log('\nāœ… Setup complete! UI components are now local for optimal Tailwind scanning\n');