@stachelock/ui 0.1.9 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,255 @@
1
+ /* eslint-env node */
2
+ /* eslint-disable no-undef */
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // Function to convert to camelCase
7
+ function toCamelCase(str) {
8
+ return str.replace(/[_-](.)/g, (_, group1) => group1.toUpperCase());
9
+ }
10
+
11
+ // Function to convert to PascalCase
12
+ function toPascalCase(str) {
13
+ const camelCase = toCamelCase(str);
14
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
15
+ }
16
+
17
+ // Function to generate export name for components
18
+ function generateComponentExportName(fileName, directoryName) {
19
+ const cleanFileName = fileName.replace(/^Ui/, '').replace(/^Sl/, '');
20
+ const pascalFileName = toPascalCase(cleanFileName);
21
+
22
+ // For base components, just return the clean name
23
+ if (directoryName === 'components') {
24
+ return pascalFileName;
25
+ }
26
+
27
+ // For subdirectories, use a more intuitive naming
28
+ if (['layouts', 'inputs', 'calendars', 'forms'].includes(directoryName)) {
29
+ // Remove the 's' from the end and capitalize
30
+ const singularDirName = directoryName.slice(0, -1);
31
+ const pascalDirName = toPascalCase(singularDirName);
32
+
33
+ // Special cases for better naming
34
+ if (singularDirName === 'layout') {
35
+ return pascalFileName; // Just use the component name for layouts
36
+ }
37
+ if (singularDirName === 'input') {
38
+ return pascalFileName; // Just use the component name for inputs
39
+ }
40
+ if (singularDirName === 'calendar') {
41
+ return pascalFileName; // Just use the component name for calendars
42
+ }
43
+ if (singularDirName === 'form') {
44
+ // Handle naming conflicts with types
45
+ if (fileName === 'DynamicFormField') {
46
+ return 'FormField'; // Avoid conflict with type
47
+ }
48
+ return pascalFileName; // Just use the component name for forms
49
+ }
50
+
51
+ return `${pascalDirName}${pascalFileName}`;
52
+ }
53
+
54
+ return pascalFileName;
55
+ }
56
+
57
+ // Function to generate index file for a directory
58
+ function generateIndexFile(directoryPath, baseDirectoryPath = directoryPath) {
59
+ const files = fs.readdirSync(directoryPath, { withFileTypes: true });
60
+ let exportStatements = [];
61
+ let hasExports = false;
62
+
63
+ for (const file of files) {
64
+ if (file.isDirectory()) {
65
+ // Skip certain directories
66
+ if (['tests', 'test', '__tests__', 'node_modules', '.git'].includes(file.name.toLowerCase())) {
67
+ continue;
68
+ }
69
+
70
+ // Recursively handle subdirectories
71
+ const subDirectoryPath = path.join(directoryPath, file.name);
72
+ if (generateIndexFile(subDirectoryPath, baseDirectoryPath)) {
73
+ exportStatements.push(`export * from './${file.name}';`);
74
+ hasExports = true;
75
+ }
76
+ } else if (file.name.endsWith('.vue') && file.name !== 'index.vue') {
77
+ const fileName = file.name.replace('.vue', '');
78
+ const directoryName = path.basename(directoryPath);
79
+ const exportName = generateComponentExportName(fileName, directoryName);
80
+
81
+ exportStatements.push(`export { default as ${exportName} } from './${fileName}.vue';`);
82
+ hasExports = true;
83
+ } else if (file.name.endsWith('.ts') && file.name !== 'index.ts' && !file.name.endsWith('.d.ts')) {
84
+ const fileName = file.name.replace('.ts', '');
85
+ // For types directory, export all type files
86
+ if (path.basename(baseDirectoryPath) === 'types') {
87
+ exportStatements.push(`export * from './${fileName}';`);
88
+ } else {
89
+ exportStatements.push(`export * from './${fileName}';`);
90
+ }
91
+ hasExports = true;
92
+ }
93
+ }
94
+
95
+ if (hasExports) {
96
+ const indexPath = path.join(directoryPath, 'index.ts');
97
+ const content = exportStatements.join('\n') + '\n';
98
+
99
+ // Only write if content is different to avoid unnecessary file changes
100
+ if (!fs.existsSync(indexPath) || fs.readFileSync(indexPath, 'utf8') !== content) {
101
+ fs.writeFileSync(indexPath, content);
102
+ console.log(`✅ Generated index.ts for ${path.relative(baseDirectoryPath, directoryPath)}`);
103
+ }
104
+ return true;
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ // Function to generate main index file
111
+ function generateMainIndexFile(srcDirectoryPath) {
112
+ const mainIndexPath = path.join(srcDirectoryPath, 'index.ts');
113
+
114
+ // Generate new content - clean, non-prefixed exports only
115
+ const exportStatements = [
116
+ "// Types",
117
+ "export * from './types';",
118
+ "",
119
+ "// Utils",
120
+ "export * from './utils/id';",
121
+ "export * from './utils/component-registry';",
122
+ "",
123
+ "// Configuration System",
124
+ "export * from './config/design-tokens';",
125
+ "export * from './config/css-variables';",
126
+ "",
127
+ "// Base UI Components",
128
+ "export * from './components';",
129
+ "",
130
+ "// Layout Components",
131
+ "export * from './components/layouts';",
132
+ "",
133
+ "// Input Components",
134
+ "export * from './components/inputs';",
135
+ "",
136
+ "// Calendar Components",
137
+ "export * from './components/calendars';",
138
+ "",
139
+ "// Form Components",
140
+ "export * from './components/forms';",
141
+ "",
142
+ "// Plugin interface and Vue plugin",
143
+ "export { StachelockUI, type StachelockUIOptions } from './plugin';",
144
+ "export { StachelockUI as default } from './plugin';"
145
+ ];
146
+
147
+ const newContent = exportStatements.join('\n') + '\n';
148
+
149
+ // Always write to ensure consistency
150
+ fs.writeFileSync(mainIndexPath, newContent);
151
+ console.log(`✅ Generated main index.ts`);
152
+ }
153
+
154
+ // Function to generate plugin file
155
+ function generatePluginFile(srcDirectoryPath) {
156
+ const pluginPath = path.join(srcDirectoryPath, 'plugin.ts');
157
+
158
+ const pluginContent = `import type { App } from 'vue';
159
+ import * as components from './components';
160
+ import * as layouts from './components/layouts';
161
+ import * as inputs from './components/inputs';
162
+ import * as calendars from './components/calendars';
163
+ import * as forms from './components/forms';
164
+
165
+ // Combine all components
166
+ const allComponents = {
167
+ ...components,
168
+ ...layouts,
169
+ ...inputs,
170
+ ...calendars,
171
+ ...forms,
172
+ };
173
+
174
+ // Plugin interface
175
+ export interface StachelockUIOptions {
176
+ /**
177
+ * Component name prefix (default: 'Sl')
178
+ */
179
+ prefix?: string;
180
+ /**
181
+ * Array of component names to install globally
182
+ */
183
+ components?: string[];
184
+ /**
185
+ * Whether to install all components globally
186
+ */
187
+ installAll?: boolean;
188
+ }
189
+
190
+ // Vue plugin
191
+ export const StachelockUI = {
192
+ install(app: App, options: StachelockUIOptions = {}) {
193
+ const { prefix = 'Sl', installAll = false, components: selectedComponents } = options;
194
+
195
+ // Register components
196
+ Object.entries(allComponents).forEach(([name, component]) => {
197
+ const shouldInstall = installAll ||
198
+ (selectedComponents && selectedComponents.includes(name)) ||
199
+ (!selectedComponents && !installAll);
200
+
201
+ if (shouldInstall) {
202
+ // Register non-prefixed
203
+ app.component(name, component);
204
+ // Also register prefixed alias for back-compat
205
+ const prefixedName = name.startsWith(prefix) ? name : \`\${prefix}\${name}\`;
206
+ app.component(prefixedName, component);
207
+ }
208
+ });
209
+ }
210
+ };
211
+
212
+ export default StachelockUI;
213
+ `;
214
+
215
+ // Only write if content is different
216
+ if (!fs.existsSync(pluginPath) || fs.readFileSync(pluginPath, 'utf8') !== pluginContent) {
217
+ fs.writeFileSync(pluginPath, pluginContent);
218
+ console.log(`✅ Generated plugin.ts`);
219
+ }
220
+ }
221
+
222
+ // Main execution
223
+ function main() {
224
+ console.log('🚀 Generating recursive index files for stachelock-ui...\n');
225
+
226
+ const srcDirectoryPath = path.join(__dirname, '../src');
227
+
228
+ if (!fs.existsSync(srcDirectoryPath)) {
229
+ console.error('❌ src directory not found!');
230
+ process.exit(1);
231
+ }
232
+
233
+ try {
234
+ // Generate index files for all subdirectories
235
+ generateIndexFile(srcDirectoryPath);
236
+
237
+ // Generate main index file
238
+ generateMainIndexFile(srcDirectoryPath);
239
+
240
+ // Don't generate plugin file - we have a custom one
241
+ // generatePluginFile(srcDirectoryPath);
242
+
243
+ console.log('\n✨ All index files generated successfully!');
244
+ } catch (error) {
245
+ console.error('❌ Error generating index files:', error);
246
+ process.exit(1);
247
+ }
248
+ }
249
+
250
+ // Run if called directly
251
+ if (require.main === module) {
252
+ main();
253
+ }
254
+
255
+ module.exports = { generateIndexFile, generateMainIndexFile, generatePluginFile };