create-nativecore 0.1.1 → 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.
- package/README.md +6 -14
- package/bin/index.mjs +403 -431
- package/package.json +3 -2
- package/template/.env.example +28 -0
- package/template/.htmlhintrc +14 -0
- package/template/api/data/dashboard.json +11 -0
- package/template/api/data/users.json +18 -0
- package/template/api/mockApi.js +161 -0
- package/template/assets/icon.svg +13 -0
- package/template/assets/logo.svg +25 -0
- package/template/eslint.config.js +94 -0
- package/template/index.html +137 -0
- package/template/manifest.json +19 -0
- package/template/public/.well-known/security.txt +9 -0
- package/template/public/_headers +24 -0
- package/template/public/_redirects +14 -0
- package/template/public/assets/icon.svg +13 -0
- package/template/public/assets/logo.svg +25 -0
- package/template/public/manifest.json +19 -0
- package/template/public/robots.txt +13 -0
- package/template/public/sitemap.xml +27 -0
- package/template/scripts/build-for-bots.mjs +121 -0
- package/template/scripts/convert-to-ts.mjs +106 -0
- package/template/scripts/fix-encoding.mjs +38 -0
- package/template/scripts/fix-svg-paths.mjs +32 -0
- package/template/scripts/generate-cf-router.mjs +52 -0
- package/template/scripts/inject-dev-tools.mjs +41 -0
- package/template/scripts/inject-version.mjs +65 -0
- package/template/scripts/make-component.mjs +445 -0
- package/template/scripts/make-component.mjs.backup +432 -0
- package/template/scripts/make-controller.mjs +119 -0
- package/template/scripts/make-core-component.mjs +303 -0
- package/template/scripts/make-view.mjs +346 -0
- package/template/scripts/minify.mjs +71 -0
- package/template/scripts/prepare-static-assets.mjs +141 -0
- package/template/scripts/prompt-bot-build.mjs +223 -0
- package/template/scripts/remove-component.mjs +170 -0
- package/template/scripts/remove-core-component.mjs +156 -0
- package/template/scripts/remove-dev.mjs +13 -0
- package/template/scripts/remove-view.mjs +200 -0
- package/template/scripts/strip-dev-blocks.mjs +30 -0
- package/template/scripts/watch-compile.mjs +69 -0
- package/template/server.js +1066 -0
- package/template/src/app.ts +115 -0
- package/template/src/components/appRegistry.ts +8 -0
- package/template/src/components/core/app-footer.ts +27 -0
- package/template/src/components/core/app-header.ts +175 -0
- package/template/src/components/core/app-sidebar.ts +238 -0
- package/template/src/components/core/loading-spinner.ts +25 -0
- package/template/src/components/core/nc-a.ts +313 -0
- package/template/src/components/core/nc-accordion.ts +186 -0
- package/template/src/components/core/nc-alert.ts +153 -0
- package/template/src/components/core/nc-animation.ts +1150 -0
- package/template/src/components/core/nc-autocomplete.ts +271 -0
- package/template/src/components/core/nc-avatar-group.ts +113 -0
- package/template/src/components/core/nc-avatar.ts +148 -0
- package/template/src/components/core/nc-badge.ts +86 -0
- package/template/src/components/core/nc-bottom-nav.ts +214 -0
- package/template/src/components/core/nc-breadcrumb.ts +96 -0
- package/template/src/components/core/nc-button.ts +307 -0
- package/template/src/components/core/nc-card.ts +160 -0
- package/template/src/components/core/nc-checkbox.ts +282 -0
- package/template/src/components/core/nc-chip.ts +115 -0
- package/template/src/components/core/nc-code.ts +314 -0
- package/template/src/components/core/nc-collapsible.ts +154 -0
- package/template/src/components/core/nc-color-picker.ts +268 -0
- package/template/src/components/core/nc-copy-button.ts +119 -0
- package/template/src/components/core/nc-date-picker.ts +443 -0
- package/template/src/components/core/nc-div.ts +280 -0
- package/template/src/components/core/nc-divider.ts +81 -0
- package/template/src/components/core/nc-drawer.ts +230 -0
- package/template/src/components/core/nc-dropdown.ts +178 -0
- package/template/src/components/core/nc-empty-state.ts +134 -0
- package/template/src/components/core/nc-file-upload.ts +354 -0
- package/template/src/components/core/nc-form.ts +312 -0
- package/template/src/components/core/nc-image.ts +184 -0
- package/template/src/components/core/nc-input.ts +383 -0
- package/template/src/components/core/nc-kbd.ts +48 -0
- package/template/src/components/core/nc-menu-item.ts +193 -0
- package/template/src/components/core/nc-menu.ts +376 -0
- package/template/src/components/core/nc-modal.ts +238 -0
- package/template/src/components/core/nc-nav-item.ts +151 -0
- package/template/src/components/core/nc-number-input.ts +350 -0
- package/template/src/components/core/nc-otp-input.ts +235 -0
- package/template/src/components/core/nc-pagination.ts +178 -0
- package/template/src/components/core/nc-popover.ts +260 -0
- package/template/src/components/core/nc-progress-circular.ts +119 -0
- package/template/src/components/core/nc-progress.ts +134 -0
- package/template/src/components/core/nc-radio.ts +235 -0
- package/template/src/components/core/nc-rating.ts +266 -0
- package/template/src/components/core/nc-rich-text.ts +283 -0
- package/template/src/components/core/nc-scroll-top.ts +116 -0
- package/template/src/components/core/nc-select.ts +452 -0
- package/template/src/components/core/nc-skeleton.ts +107 -0
- package/template/src/components/core/nc-slider.ts +285 -0
- package/template/src/components/core/nc-snackbar.ts +230 -0
- package/template/src/components/core/nc-splash.ts +343 -0
- package/template/src/components/core/nc-stepper.ts +247 -0
- package/template/src/components/core/nc-switch.ts +281 -0
- package/template/src/components/core/nc-tab-item.ts +138 -0
- package/template/src/components/core/nc-table.ts +279 -0
- package/template/src/components/core/nc-tabs.ts +554 -0
- package/template/src/components/core/nc-tag-input.ts +279 -0
- package/template/src/components/core/nc-textarea.ts +216 -0
- package/template/src/components/core/nc-time-picker.ts +438 -0
- package/template/src/components/core/nc-timeline.ts +186 -0
- package/template/src/components/core/nc-tooltip.ts +143 -0
- package/template/src/components/frameworkRegistry.ts +68 -0
- package/template/src/components/preloadRegistry.ts +28 -0
- package/template/src/components/registry.ts +8 -0
- package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
- package/template/src/constants/apiEndpoints.ts +27 -0
- package/template/src/constants/errorMessages.ts +23 -0
- package/template/src/constants/index.ts +8 -0
- package/template/src/constants/routePaths.ts +15 -0
- package/template/src/constants/storageKeys.ts +18 -0
- package/template/src/controllers/dashboard.controller.ts +200 -0
- package/template/src/controllers/home.controller.ts +21 -0
- package/template/src/controllers/index.ts +11 -0
- package/template/src/controllers/login.controller.ts +131 -0
- package/template/src/core/component.ts +354 -0
- package/template/src/core/errorHandler.ts +85 -0
- package/template/src/core/gpu-animation.ts +604 -0
- package/template/src/core/http.ts +173 -0
- package/template/src/core/lazyComponents.ts +90 -0
- package/template/src/core/router.ts +653 -0
- package/template/src/core/signals.ts +146 -0
- package/template/src/core/state.ts +248 -0
- package/template/src/dev/component-editor.ts +1363 -0
- package/template/src/dev/component-overlay.ts +278 -0
- package/template/src/dev/context-menu.ts +223 -0
- package/template/src/dev/denc-tools.ts +250 -0
- package/template/src/dev/hmr.ts +189 -0
- package/template/src/dev/nfbs.code-workspace +27 -0
- package/template/src/dev/outline-panel.ts +1247 -0
- package/template/src/middleware/auth.middleware.ts +23 -0
- package/template/src/routes/routes.ts +38 -0
- package/template/src/services/api.service.ts +394 -0
- package/template/src/services/auth.service.ts +176 -0
- package/template/src/services/index.ts +8 -0
- package/template/src/services/logger.service.ts +74 -0
- package/template/src/services/storage.service.ts +88 -0
- package/template/src/stores/appStore.ts +57 -0
- package/template/src/stores/uiStore.ts +36 -0
- package/template/src/styles/core-variables.css +219 -0
- package/template/src/styles/core.css +710 -0
- package/template/src/styles/main.css +3164 -0
- package/template/src/styles/variables.css +152 -0
- package/template/src/types/global.d.ts +47 -0
- package/template/src/utils/cacheBuster.ts +20 -0
- package/template/src/utils/dom.ts +149 -0
- package/template/src/utils/events.ts +203 -0
- package/template/src/utils/form.ts +176 -0
- package/template/src/utils/formatters.ts +169 -0
- package/template/src/utils/helpers.ts +195 -0
- package/template/src/utils/markdown.ts +307 -0
- package/template/src/utils/sidebar.ts +96 -0
- package/template/src/utils/smoothScroll.ts +85 -0
- package/template/src/utils/templates.ts +23 -0
- package/template/src/utils/validation.ts +73 -0
- package/template/src/views/protected/dashboard.html +293 -0
- package/template/src/views/public/home.html +150 -0
- package/template/src/views/public/login.html +102 -0
- package/template/tests/unit/component.test.ts +87 -0
- package/template/tests/unit/computed.test.ts +79 -0
- package/template/tests/unit/form.test.ts +68 -0
- package/template/tests/unit/formatters.test.ts +49 -0
- package/template/tests/unit/lazy-components.test.ts +59 -0
- package/template/tests/unit/markdown.test.ts +62 -0
- package/template/tests/unit/router.test.ts +112 -0
- package/template/tests/unit/signals.test.ts +54 -0
- package/template/tests/unit/validation.test.ts +50 -0
- package/template/tsconfig.build.json +21 -0
- package/template/tsconfig.json +51 -0
- package/template/vitest.config.ts +36 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Component Generator Script
|
|
5
|
+
* Like Laravel Artisan for vanilla JS components
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node scripts/make-component.mjs counter
|
|
9
|
+
* npm run make:component counter
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import readline from 'readline';
|
|
16
|
+
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// Get component name from command line
|
|
21
|
+
const componentName = process.argv[2];
|
|
22
|
+
|
|
23
|
+
if (!componentName) {
|
|
24
|
+
console.error('Error: Component name is required');
|
|
25
|
+
console.log('\nUsage:');
|
|
26
|
+
console.log(' npm run make:component <name>');
|
|
27
|
+
console.log('\nExample:');
|
|
28
|
+
console.log(' npm run make:component my-card');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validate component name (kebab-case with required hyphen)
|
|
33
|
+
if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(componentName)) {
|
|
34
|
+
console.error('Error: Component name must be in kebab-case with at least one hyphen');
|
|
35
|
+
console.error(' Custom elements require a hyphen to avoid conflicts with native HTML elements');
|
|
36
|
+
console.error('\nValid examples:');
|
|
37
|
+
console.error(' - my-card');
|
|
38
|
+
console.error(' - user-profile');
|
|
39
|
+
console.error(' - sample-component');
|
|
40
|
+
console.error('\nInvalid examples:');
|
|
41
|
+
console.error(' - card (no hyphen)');
|
|
42
|
+
console.error(' - MyCard (not kebab-case)');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Generate class name (PascalCase)
|
|
47
|
+
const className = componentName
|
|
48
|
+
.split('-')
|
|
49
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
50
|
+
.join('');
|
|
51
|
+
|
|
52
|
+
// Component file path (in components/ui folder by default)
|
|
53
|
+
const componentsDir = path.resolve(__dirname, '..', 'src', 'components');
|
|
54
|
+
const uiDir = path.join(componentsDir, 'ui');
|
|
55
|
+
const componentFile = path.join(uiDir, `${componentName}.ts`);
|
|
56
|
+
const registryFile = path.join(componentsDir, 'registry.ts');
|
|
57
|
+
|
|
58
|
+
// Ensure ui directory exists
|
|
59
|
+
if (!fs.existsSync(uiDir)) {
|
|
60
|
+
fs.mkdirSync(uiDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check if component already exists
|
|
64
|
+
if (fs.existsSync(componentFile)) {
|
|
65
|
+
console.error(`Error: Component "${componentName}.ts" already exists`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Template files
|
|
70
|
+
const jsTemplate = `/**
|
|
71
|
+
* ${className} Component
|
|
72
|
+
* Generated on ${new Date().toLocaleDateString()}
|
|
73
|
+
*
|
|
74
|
+
* DEV MODE INTEGRATION:
|
|
75
|
+
* - Define variant/size CSS classes for automatic dropdown detection
|
|
76
|
+
* - Add attributes to observedAttributes for live editing
|
|
77
|
+
* - Implement attributeChangedCallback for instant preview updates
|
|
78
|
+
* - Changes can be saved to instance (HTML) or globally (component file)
|
|
79
|
+
*
|
|
80
|
+
* REGISTRATION:
|
|
81
|
+
* This component is automatically registered in src/components/registry.ts
|
|
82
|
+
* Usage: <${componentName}></${componentName}>
|
|
83
|
+
*
|
|
84
|
+
* PERFORMANCE:
|
|
85
|
+
* - Lazy-loaded by default (loads on first use)
|
|
86
|
+
* - For critical components: Add to src/components/preloadRegistry.ts
|
|
87
|
+
*/
|
|
88
|
+
import { Component, defineComponent } from '@core/component.js';
|
|
89
|
+
import { useState, computed } from '@core/state.js';
|
|
90
|
+
import type { State, ComputedState } from '@core/state.js';
|
|
91
|
+
import { html } from '@utils/templates.js';
|
|
92
|
+
|
|
93
|
+
export class ${className} extends Component {
|
|
94
|
+
// ========== Shadow DOM ==========
|
|
95
|
+
// Enable for CSS encapsulation and <slot> support
|
|
96
|
+
static useShadowDOM = true;
|
|
97
|
+
|
|
98
|
+
// ========== Dev Tools: Attribute Options ==========
|
|
99
|
+
// Defines the dropdown values shown in the dev tools panel for each attribute.
|
|
100
|
+
// This takes priority over automatic CSS class detection.
|
|
101
|
+
// Add one entry per observed attribute that has a fixed set of valid values.
|
|
102
|
+
// The variant values here must match your CSS class names below (e.g. .primary { ... })
|
|
103
|
+
static attributeOptions = {
|
|
104
|
+
variant: ['primary', 'secondary', 'success'],
|
|
105
|
+
size: ['small', 'medium', 'large'],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ========== Observed Attributes (Dev Tools Integration) ==========
|
|
109
|
+
// Attributes listed here:
|
|
110
|
+
// - Show up in dev tools sidebar
|
|
111
|
+
// - Trigger attributeChangedCallback when changed
|
|
112
|
+
static get observedAttributes() {
|
|
113
|
+
return ['variant', 'size', 'disabled'];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ========== Local State ==========
|
|
117
|
+
private count?: State<number>;
|
|
118
|
+
private name?: State<string>;
|
|
119
|
+
|
|
120
|
+
// ========== Computed State ==========
|
|
121
|
+
private doubleCount?: ComputedState<number>;
|
|
122
|
+
private greeting?: ComputedState<string>;
|
|
123
|
+
|
|
124
|
+
// ========== Watcher unsubscribes (call in onUnmount) ==========
|
|
125
|
+
private _unwatchCount?: () => void;
|
|
126
|
+
private _unwatchName?: () => void;
|
|
127
|
+
|
|
128
|
+
constructor() {
|
|
129
|
+
super();
|
|
130
|
+
|
|
131
|
+
// ========== Initialize Local State ==========
|
|
132
|
+
// this.count = useState(0);
|
|
133
|
+
// this.name = useState('World');
|
|
134
|
+
|
|
135
|
+
// ========== Initialize Computed Values ==========
|
|
136
|
+
// Auto-updates when dependencies change
|
|
137
|
+
// this.doubleCount = computed(() => this.count!.value * 2);
|
|
138
|
+
// this.greeting = computed(() => \`Hello, \${this.name!.value}!\`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
template() {
|
|
142
|
+
// ========== Get Attributes ==========
|
|
143
|
+
const variant = this.attr('variant', 'primary');
|
|
144
|
+
const size = this.attr('size', 'medium');
|
|
145
|
+
const disabled = this.hasAttribute('disabled');
|
|
146
|
+
|
|
147
|
+
// TIP: Keep attributeOptions.variant in sync with the CSS class names below.
|
|
148
|
+
// Each value in the array needs a matching CSS rule (e.g. .primary { ... })
|
|
149
|
+
//
|
|
150
|
+
// CSS variables used below (var(--primary), var(--spacing-md), etc.) are
|
|
151
|
+
// defined in src/styles/variables.css — edit that file to change tokens globally.
|
|
152
|
+
|
|
153
|
+
return html\`
|
|
154
|
+
<style>
|
|
155
|
+
:host {
|
|
156
|
+
display: block;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.${componentName} {
|
|
160
|
+
padding: var(--spacing-md);
|
|
161
|
+
background: var(--bg-primary);
|
|
162
|
+
border-radius: var(--radius-md);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ========== Variant Examples ========== */
|
|
166
|
+
/* Dev tools will detect these as dropdown options */
|
|
167
|
+
.primary {
|
|
168
|
+
background: var(--primary);
|
|
169
|
+
color: white;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.secondary {
|
|
173
|
+
background: var(--secondary);
|
|
174
|
+
color: white;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.success {
|
|
178
|
+
background: var(--success);
|
|
179
|
+
color: white;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ========== Size Examples ========== */
|
|
183
|
+
.small {
|
|
184
|
+
padding: var(--spacing-sm);
|
|
185
|
+
font-size: 0.875rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.medium {
|
|
189
|
+
padding: var(--spacing-md);
|
|
190
|
+
font-size: 1rem;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.large {
|
|
194
|
+
padding: var(--spacing-lg);
|
|
195
|
+
font-size: 1.125rem;
|
|
196
|
+
}
|
|
197
|
+
</style>
|
|
198
|
+
|
|
199
|
+
<div class="${componentName} \${variant} \${size}" \${disabled ? 'disabled' : ''}>
|
|
200
|
+
<h3>${className}</h3>
|
|
201
|
+
<p>Your component content here</p>
|
|
202
|
+
<slot></slot>
|
|
203
|
+
</div>
|
|
204
|
+
\`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Live attribute updates (Dev Tools Integration)
|
|
209
|
+
* Called automatically when observed attributes change
|
|
210
|
+
* Enables instant preview without full re-render
|
|
211
|
+
*/
|
|
212
|
+
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
|
|
213
|
+
if (!this._mounted) return;
|
|
214
|
+
|
|
215
|
+
const container = this.$('.${componentName}') as HTMLElement;
|
|
216
|
+
if (!container) return;
|
|
217
|
+
|
|
218
|
+
switch (name) {
|
|
219
|
+
case 'variant':
|
|
220
|
+
case 'size':
|
|
221
|
+
// Update classes instantly
|
|
222
|
+
this.updateClasses(container);
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case 'disabled':
|
|
226
|
+
// Update disabled state
|
|
227
|
+
container.toggleAttribute('disabled', this.hasAttribute('disabled'));
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Helper: Update element classes based on current attributes
|
|
234
|
+
*/
|
|
235
|
+
private updateClasses(element: HTMLElement): void {
|
|
236
|
+
const variant = this.attr('variant', 'primary');
|
|
237
|
+
const size = this.attr('size', 'medium');
|
|
238
|
+
element.className = \`${componentName} \${variant} \${size}\`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
onMount() {
|
|
242
|
+
// ========== Event Delegation (Recommended Pattern) ==========
|
|
243
|
+
// Handles clicks even after re-renders
|
|
244
|
+
this.shadowRoot.addEventListener('click', (e) => {
|
|
245
|
+
const target = e.target as HTMLElement;
|
|
246
|
+
|
|
247
|
+
// if (target.matches('.btn-increment')) {
|
|
248
|
+
// this.handleIncrement();
|
|
249
|
+
// }
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ========== State Watchers ==========
|
|
253
|
+
// Store the returned unsubscribe — call it in onUnmount
|
|
254
|
+
// this._unwatchCount = this.count?.watch(value => {
|
|
255
|
+
// const display = this.shadowRoot.querySelector('#count-display');
|
|
256
|
+
// if (display) display.textContent = value.toString();
|
|
257
|
+
// });
|
|
258
|
+
|
|
259
|
+
// this._unwatchName = this.name?.watch(value => {
|
|
260
|
+
// const display = this.shadowRoot.querySelector('#greeting');
|
|
261
|
+
// if (display) display.textContent = value;
|
|
262
|
+
// });
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ========== Event Handlers ==========
|
|
266
|
+
// private handleIncrement(): void {
|
|
267
|
+
// if (this.count) {
|
|
268
|
+
// this.count.value++;
|
|
269
|
+
// }
|
|
270
|
+
//
|
|
271
|
+
// // Emit event to parent
|
|
272
|
+
// this.emitEvent('increment', { value: this.count?.value });
|
|
273
|
+
// }
|
|
274
|
+
|
|
275
|
+
// private handleDecrement(): void {
|
|
276
|
+
// if (this.count) {
|
|
277
|
+
// this.count.value--;
|
|
278
|
+
// }
|
|
279
|
+
//
|
|
280
|
+
// // Emit event to parent
|
|
281
|
+
// this.emitEvent('decrement', { value: this.count?.value });
|
|
282
|
+
// }
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Emit custom events to parent components
|
|
286
|
+
*
|
|
287
|
+
* Usage in parent:
|
|
288
|
+
* <${componentName}></${componentName}>
|
|
289
|
+
*
|
|
290
|
+
* // In controller:
|
|
291
|
+
* document.querySelector('${componentName}')
|
|
292
|
+
* .addEventListener('custom-event', (e) => {
|
|
293
|
+
* console.log(e.detail); // Your data here
|
|
294
|
+
* });
|
|
295
|
+
*/
|
|
296
|
+
// emitEvent(name: string, detail: any = {}, options = {}) {
|
|
297
|
+
// this.dispatchEvent(new CustomEvent(name, {
|
|
298
|
+
// detail,
|
|
299
|
+
// bubbles: true,
|
|
300
|
+
// composed: true,
|
|
301
|
+
// ...options
|
|
302
|
+
// }));
|
|
303
|
+
// }
|
|
304
|
+
|
|
305
|
+
onUnmount() {
|
|
306
|
+
// Unsubscribe all state watchers
|
|
307
|
+
this._unwatchCount?.();
|
|
308
|
+
this._unwatchName?.();
|
|
309
|
+
|
|
310
|
+
// Dispose computed values to release dependency subscriptions
|
|
311
|
+
this.doubleCount?.dispose();
|
|
312
|
+
this.greeting?.dispose();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
defineComponent('${componentName}', ${className});
|
|
317
|
+
`;
|
|
318
|
+
|
|
319
|
+
const readmeTemplate = `# ${className}
|
|
320
|
+
|
|
321
|
+
Custom element: \`<${componentName}>\`
|
|
322
|
+
|
|
323
|
+
## Usage
|
|
324
|
+
|
|
325
|
+
\`\`\`html
|
|
326
|
+
<${componentName}></${componentName}>
|
|
327
|
+
\`\`\`
|
|
328
|
+
|
|
329
|
+
## Properties
|
|
330
|
+
|
|
331
|
+
| Property | Type | Default | Description |
|
|
332
|
+
|----------|------|---------|-------------|
|
|
333
|
+
| - | - | - | - |
|
|
334
|
+
|
|
335
|
+
## Events
|
|
336
|
+
|
|
337
|
+
| Event | Description |
|
|
338
|
+
|-------|-------------|
|
|
339
|
+
| - | - |
|
|
340
|
+
|
|
341
|
+
## CSS Variables
|
|
342
|
+
|
|
343
|
+
| Variable | Description |
|
|
344
|
+
|----------|-------------|
|
|
345
|
+
| - | - |
|
|
346
|
+
|
|
347
|
+
## Example
|
|
348
|
+
|
|
349
|
+
\`\`\`html
|
|
350
|
+
<${componentName}></${componentName}>
|
|
351
|
+
\`\`\`
|
|
352
|
+
`;
|
|
353
|
+
|
|
354
|
+
// Write component file
|
|
355
|
+
fs.writeFileSync(componentFile, jsTemplate.trim());
|
|
356
|
+
|
|
357
|
+
// Prompt for prefetch
|
|
358
|
+
const rl = readline.createInterface({
|
|
359
|
+
input: process.stdin,
|
|
360
|
+
output: process.stdout
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
console.log('\nPerformance optimization:');
|
|
364
|
+
console.log(' All components are lazy-loaded by default (loads on first use).');
|
|
365
|
+
console.log(' For critical layout components (header, sidebar, footer), prefetching improves performance.\n');
|
|
366
|
+
|
|
367
|
+
rl.question('Would you like to prefetch this component? (y/N): ', (answer) => {
|
|
368
|
+
const shouldPreload = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
369
|
+
|
|
370
|
+
if (shouldPreload) {
|
|
371
|
+
// Add import to preloadRegistry.ts
|
|
372
|
+
const preloadFile = path.resolve(__dirname, '..', 'src', 'components', 'preloadRegistry.ts');
|
|
373
|
+
|
|
374
|
+
if (fs.existsSync(preloadFile)) {
|
|
375
|
+
let preloadContent = fs.readFileSync(preloadFile, 'utf-8');
|
|
376
|
+
const importStatement = `import './ui/${componentName}.js';`;
|
|
377
|
+
|
|
378
|
+
// Check if not already imported
|
|
379
|
+
if (!preloadContent.includes(importStatement)) {
|
|
380
|
+
// Find the last import line and add after it
|
|
381
|
+
const lines = preloadContent.split('\n');
|
|
382
|
+
const lastImportIndex = lines.findLastIndex(line =>
|
|
383
|
+
line.trim().startsWith("import './") && line.endsWith(".js';")
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
if (lastImportIndex !== -1) {
|
|
387
|
+
// Add after last import
|
|
388
|
+
lines.splice(lastImportIndex + 1, 0, importStatement);
|
|
389
|
+
} else {
|
|
390
|
+
// Add at the end of the file
|
|
391
|
+
lines.push(importStatement);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
preloadContent = lines.join('\n');
|
|
395
|
+
fs.writeFileSync(preloadFile, preloadContent);
|
|
396
|
+
console.log('Component will be preloaded (added to preloadRegistry.ts)\n');
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
console.warn('preloadRegistry.ts not found - component will still be lazy-loaded\n');
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
console.log('Component will be lazy-loaded (default behavior)\n');
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Add to registry.ts for lazy loading registration
|
|
406
|
+
if (fs.existsSync(registryFile)) {
|
|
407
|
+
let registryContent = fs.readFileSync(registryFile, 'utf-8');
|
|
408
|
+
const registrationStatement = `componentRegistry.register('${componentName}', './ui/${componentName}.js');`;
|
|
409
|
+
|
|
410
|
+
// Check if already registered
|
|
411
|
+
if (!registryContent.includes(registrationStatement)) {
|
|
412
|
+
// Find the last registration line and add after it
|
|
413
|
+
const lines = registryContent.split('\n');
|
|
414
|
+
const lastRegisterIndex = lines.findLastIndex(line => line.includes('componentRegistry.register'));
|
|
415
|
+
|
|
416
|
+
if (lastRegisterIndex !== -1) {
|
|
417
|
+
// Add after the last registration
|
|
418
|
+
lines.splice(lastRegisterIndex + 1, 0, registrationStatement);
|
|
419
|
+
registryContent = lines.join('\n');
|
|
420
|
+
} else {
|
|
421
|
+
// Add at the end if no registrations found
|
|
422
|
+
registryContent = registryContent.trimEnd() + '\n' + registrationStatement + '\n';
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
fs.writeFileSync(registryFile, registryContent);
|
|
426
|
+
console.log('Component registered in component registry\n');
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
console.log('Note: src/components/registry.ts not found. Manual registration required.\n');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Success message
|
|
433
|
+
console.log(`Location: src/components/ui/${componentName}.ts`);
|
|
434
|
+
console.log(`Registered in: src/components/registry.ts`);
|
|
435
|
+
if (shouldPreload) {
|
|
436
|
+
console.log(`Preloaded in: src/components/preloadRegistry.ts`);
|
|
437
|
+
}
|
|
438
|
+
console.log('\nNext steps:');
|
|
439
|
+
console.log(` 1. Edit src/components/ui/${componentName}.ts`);
|
|
440
|
+
console.log(` 2. Use in your HTML: <${componentName}></${componentName}>`);
|
|
441
|
+
console.log('\nComponent class:', className);
|
|
442
|
+
console.log('Custom element:', `<${componentName}>`);
|
|
443
|
+
|
|
444
|
+
rl.close();
|
|
445
|
+
});
|