create-nativecore 0.1.1 → 0.2.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 (175) hide show
  1. package/README.md +6 -14
  2. package/bin/index.mjs +402 -431
  3. package/package.json +3 -2
  4. package/template/.env.example +28 -0
  5. package/template/.htmlhintrc +14 -0
  6. package/template/api/data/dashboard.json +11 -0
  7. package/template/api/data/users.json +18 -0
  8. package/template/api/mockApi.js +161 -0
  9. package/template/assets/icon.svg +13 -0
  10. package/template/assets/logo.svg +25 -0
  11. package/template/eslint.config.js +94 -0
  12. package/template/index.html +137 -0
  13. package/template/manifest.json +19 -0
  14. package/template/public/.well-known/security.txt +9 -0
  15. package/template/public/_headers +24 -0
  16. package/template/public/_redirects +14 -0
  17. package/template/public/assets/icon.svg +13 -0
  18. package/template/public/assets/logo.svg +25 -0
  19. package/template/public/manifest.json +19 -0
  20. package/template/public/robots.txt +13 -0
  21. package/template/public/sitemap.xml +27 -0
  22. package/template/scripts/build-for-bots.mjs +121 -0
  23. package/template/scripts/convert-to-ts.mjs +106 -0
  24. package/template/scripts/fix-encoding.mjs +38 -0
  25. package/template/scripts/fix-svg-paths.mjs +32 -0
  26. package/template/scripts/generate-cf-router.mjs +52 -0
  27. package/template/scripts/inject-dev-tools.mjs +41 -0
  28. package/template/scripts/inject-version.mjs +65 -0
  29. package/template/scripts/make-component.mjs +445 -0
  30. package/template/scripts/make-component.mjs.backup +432 -0
  31. package/template/scripts/make-controller.mjs +119 -0
  32. package/template/scripts/make-core-component.mjs +303 -0
  33. package/template/scripts/make-view.mjs +346 -0
  34. package/template/scripts/minify.mjs +71 -0
  35. package/template/scripts/prepare-static-assets.mjs +141 -0
  36. package/template/scripts/prompt-bot-build.mjs +223 -0
  37. package/template/scripts/remove-component.mjs +170 -0
  38. package/template/scripts/remove-core-component.mjs +156 -0
  39. package/template/scripts/remove-dev.mjs +13 -0
  40. package/template/scripts/remove-view.mjs +200 -0
  41. package/template/scripts/strip-dev-blocks.mjs +30 -0
  42. package/template/scripts/watch-compile.mjs +69 -0
  43. package/template/server.js +1066 -0
  44. package/template/src/app.ts +115 -0
  45. package/template/src/components/appRegistry.ts +8 -0
  46. package/template/src/components/core/app-footer.ts +27 -0
  47. package/template/src/components/core/app-header.ts +175 -0
  48. package/template/src/components/core/app-sidebar.ts +238 -0
  49. package/template/src/components/core/loading-spinner.ts +25 -0
  50. package/template/src/components/core/nc-a.ts +313 -0
  51. package/template/src/components/core/nc-accordion.ts +186 -0
  52. package/template/src/components/core/nc-alert.ts +153 -0
  53. package/template/src/components/core/nc-animation.ts +1150 -0
  54. package/template/src/components/core/nc-autocomplete.ts +271 -0
  55. package/template/src/components/core/nc-avatar-group.ts +113 -0
  56. package/template/src/components/core/nc-avatar.ts +148 -0
  57. package/template/src/components/core/nc-badge.ts +86 -0
  58. package/template/src/components/core/nc-bottom-nav.ts +214 -0
  59. package/template/src/components/core/nc-breadcrumb.ts +96 -0
  60. package/template/src/components/core/nc-button.ts +307 -0
  61. package/template/src/components/core/nc-card.ts +160 -0
  62. package/template/src/components/core/nc-checkbox.ts +282 -0
  63. package/template/src/components/core/nc-chip.ts +115 -0
  64. package/template/src/components/core/nc-code.ts +314 -0
  65. package/template/src/components/core/nc-collapsible.ts +154 -0
  66. package/template/src/components/core/nc-color-picker.ts +268 -0
  67. package/template/src/components/core/nc-copy-button.ts +119 -0
  68. package/template/src/components/core/nc-date-picker.ts +443 -0
  69. package/template/src/components/core/nc-div.ts +280 -0
  70. package/template/src/components/core/nc-divider.ts +81 -0
  71. package/template/src/components/core/nc-drawer.ts +230 -0
  72. package/template/src/components/core/nc-dropdown.ts +178 -0
  73. package/template/src/components/core/nc-empty-state.ts +134 -0
  74. package/template/src/components/core/nc-file-upload.ts +354 -0
  75. package/template/src/components/core/nc-form.ts +312 -0
  76. package/template/src/components/core/nc-image.ts +184 -0
  77. package/template/src/components/core/nc-input.ts +383 -0
  78. package/template/src/components/core/nc-kbd.ts +48 -0
  79. package/template/src/components/core/nc-menu-item.ts +193 -0
  80. package/template/src/components/core/nc-menu.ts +376 -0
  81. package/template/src/components/core/nc-modal.ts +238 -0
  82. package/template/src/components/core/nc-nav-item.ts +151 -0
  83. package/template/src/components/core/nc-number-input.ts +350 -0
  84. package/template/src/components/core/nc-otp-input.ts +235 -0
  85. package/template/src/components/core/nc-pagination.ts +178 -0
  86. package/template/src/components/core/nc-popover.ts +260 -0
  87. package/template/src/components/core/nc-progress-circular.ts +119 -0
  88. package/template/src/components/core/nc-progress.ts +134 -0
  89. package/template/src/components/core/nc-radio.ts +235 -0
  90. package/template/src/components/core/nc-rating.ts +266 -0
  91. package/template/src/components/core/nc-rich-text.ts +283 -0
  92. package/template/src/components/core/nc-scroll-top.ts +116 -0
  93. package/template/src/components/core/nc-select.ts +452 -0
  94. package/template/src/components/core/nc-skeleton.ts +107 -0
  95. package/template/src/components/core/nc-slider.ts +285 -0
  96. package/template/src/components/core/nc-snackbar.ts +230 -0
  97. package/template/src/components/core/nc-splash.ts +343 -0
  98. package/template/src/components/core/nc-stepper.ts +247 -0
  99. package/template/src/components/core/nc-switch.ts +281 -0
  100. package/template/src/components/core/nc-tab-item.ts +138 -0
  101. package/template/src/components/core/nc-table.ts +279 -0
  102. package/template/src/components/core/nc-tabs.ts +554 -0
  103. package/template/src/components/core/nc-tag-input.ts +279 -0
  104. package/template/src/components/core/nc-textarea.ts +216 -0
  105. package/template/src/components/core/nc-time-picker.ts +438 -0
  106. package/template/src/components/core/nc-timeline.ts +186 -0
  107. package/template/src/components/core/nc-tooltip.ts +143 -0
  108. package/template/src/components/frameworkRegistry.ts +68 -0
  109. package/template/src/components/preloadRegistry.ts +28 -0
  110. package/template/src/components/registry.ts +8 -0
  111. package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
  112. package/template/src/constants/apiEndpoints.ts +27 -0
  113. package/template/src/constants/errorMessages.ts +23 -0
  114. package/template/src/constants/index.ts +8 -0
  115. package/template/src/constants/routePaths.ts +15 -0
  116. package/template/src/constants/storageKeys.ts +18 -0
  117. package/template/src/controllers/dashboard.controller.ts +200 -0
  118. package/template/src/controllers/home.controller.ts +21 -0
  119. package/template/src/controllers/index.ts +11 -0
  120. package/template/src/controllers/login.controller.ts +131 -0
  121. package/template/src/core/component.ts +354 -0
  122. package/template/src/core/errorHandler.ts +85 -0
  123. package/template/src/core/gpu-animation.ts +604 -0
  124. package/template/src/core/http.ts +173 -0
  125. package/template/src/core/lazyComponents.ts +90 -0
  126. package/template/src/core/router.ts +642 -0
  127. package/template/src/core/signals.ts +146 -0
  128. package/template/src/core/state.ts +248 -0
  129. package/template/src/dev/component-editor.ts +1363 -0
  130. package/template/src/dev/component-overlay.ts +278 -0
  131. package/template/src/dev/context-menu.ts +223 -0
  132. package/template/src/dev/denc-tools.ts +250 -0
  133. package/template/src/dev/hmr.ts +189 -0
  134. package/template/src/dev/nfbs.code-workspace +27 -0
  135. package/template/src/dev/outline-panel.ts +1247 -0
  136. package/template/src/middleware/auth.middleware.ts +23 -0
  137. package/template/src/routes/routes.ts +38 -0
  138. package/template/src/services/api.service.ts +394 -0
  139. package/template/src/services/auth.service.ts +176 -0
  140. package/template/src/services/index.ts +8 -0
  141. package/template/src/services/logger.service.ts +74 -0
  142. package/template/src/services/storage.service.ts +88 -0
  143. package/template/src/stores/appStore.ts +57 -0
  144. package/template/src/stores/uiStore.ts +36 -0
  145. package/template/src/styles/core-variables.css +219 -0
  146. package/template/src/styles/core.css +710 -0
  147. package/template/src/styles/main.css +3164 -0
  148. package/template/src/styles/variables.css +152 -0
  149. package/template/src/types/global.d.ts +47 -0
  150. package/template/src/utils/cacheBuster.ts +20 -0
  151. package/template/src/utils/dom.ts +149 -0
  152. package/template/src/utils/events.ts +203 -0
  153. package/template/src/utils/form.ts +176 -0
  154. package/template/src/utils/formatters.ts +169 -0
  155. package/template/src/utils/helpers.ts +195 -0
  156. package/template/src/utils/markdown.ts +307 -0
  157. package/template/src/utils/sidebar.ts +96 -0
  158. package/template/src/utils/smoothScroll.ts +85 -0
  159. package/template/src/utils/templates.ts +23 -0
  160. package/template/src/utils/validation.ts +73 -0
  161. package/template/src/views/protected/dashboard.html +293 -0
  162. package/template/src/views/public/home.html +150 -0
  163. package/template/src/views/public/login.html +102 -0
  164. package/template/tests/unit/component.test.ts +87 -0
  165. package/template/tests/unit/computed.test.ts +79 -0
  166. package/template/tests/unit/form.test.ts +68 -0
  167. package/template/tests/unit/formatters.test.ts +49 -0
  168. package/template/tests/unit/lazy-components.test.ts +59 -0
  169. package/template/tests/unit/markdown.test.ts +62 -0
  170. package/template/tests/unit/router.test.ts +112 -0
  171. package/template/tests/unit/signals.test.ts +54 -0
  172. package/template/tests/unit/validation.test.ts +50 -0
  173. package/template/tsconfig.build.json +21 -0
  174. package/template/tsconfig.json +51 -0
  175. package/template/vitest.config.ts +36 -0
@@ -0,0 +1,71 @@
1
+ // scripts/minify.mjs
2
+ // Minify all JavaScript files in dist folder for production
3
+ import { readFile, writeFile } from 'fs/promises';
4
+ import { readdir, stat } from 'fs/promises';
5
+ import { join } from 'path';
6
+ import { minify } from 'terser';
7
+
8
+ async function getAllJSFiles(dir, fileList = []) {
9
+ const files = await readdir(dir);
10
+
11
+ for (const file of files) {
12
+ const filePath = join(dir, file);
13
+ const fileStat = await stat(filePath);
14
+
15
+ if (fileStat.isDirectory()) {
16
+ await getAllJSFiles(filePath, fileList);
17
+ } else if (file.endsWith('.js') && !file.endsWith('.min.js')) {
18
+ fileList.push(filePath);
19
+ }
20
+ }
21
+
22
+ return fileList;
23
+ }
24
+
25
+ async function minifyFile(filePath) {
26
+ try {
27
+ const code = await readFile(filePath, 'utf8');
28
+ const result = await minify(code, {
29
+ compress: {
30
+ dead_code: true,
31
+ drop_console: false, // Keep console.warn/error, remove console.log
32
+ drop_debugger: true,
33
+ passes: 2, // Multiple passes for better compression
34
+ pure_funcs: ['console.log', 'console.debug', 'console.trace'],
35
+ },
36
+ mangle: {
37
+ toplevel: true, // Mangle all variable names for max compression
38
+ properties: false, // Don't mangle property names (safer for web components)
39
+ },
40
+ format: {
41
+ comments: false, // Remove all comments
42
+ beautify: false, // No whitespace/newlines - everything on one line
43
+ },
44
+ });
45
+
46
+ if (result.code) {
47
+ await writeFile(filePath, result.code, 'utf8');
48
+ const originalSize = Buffer.byteLength(code, 'utf8');
49
+ const minifiedSize = Buffer.byteLength(result.code, 'utf8');
50
+ const savings = ((1 - minifiedSize / originalSize) * 100).toFixed(1);
51
+ console.log(`✓ ${filePath.replace(process.cwd(), '.')} - ${savings}% smaller`);
52
+ }
53
+ } catch (error) {
54
+ console.error(`✗ Failed to minify ${filePath}:`, error.message);
55
+ }
56
+ }
57
+
58
+ async function minifyAll() {
59
+ const distPath = join(process.cwd(), 'dist');
60
+ console.log('🗜️ Minifying JavaScript files...\n');
61
+
62
+ const jsFiles = await getAllJSFiles(distPath);
63
+
64
+ for (const file of jsFiles) {
65
+ await minifyFile(file);
66
+ }
67
+
68
+ console.log(`\n✨ Minified ${jsFiles.length} files`);
69
+ }
70
+
71
+ minifyAll().catch(console.error);
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Prepare a deployment directory (_deploy/) that mirrors the static
3
+ * hosting structure expected by Cloudflare Pages and other file-based
4
+ * deploy targets.
5
+ *
6
+ * Expected runtime paths the HTML shells reference:
7
+ * /dist/app.js - compiled JS (module entry)
8
+ * /src/styles/*.css - stylesheets
9
+ * /src/views/**\/*.html - view partials loaded by the router
10
+ * /docs/*.md - markdown consumed by the docs controller
11
+ * /manifest.json, /robots.txt, /sitemap.xml, etc.
12
+ */
13
+ import fs from 'fs';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+ const rootDir = path.resolve(__dirname, '..');
20
+ const distDir = path.join(rootDir, 'dist');
21
+ const deployDir = path.join(rootDir, '_deploy');
22
+
23
+ function ensureDir(dirPath) {
24
+ fs.mkdirSync(dirPath, { recursive: true });
25
+ }
26
+
27
+ function cleanDir(dirPath) {
28
+ if (fs.existsSync(dirPath)) {
29
+ fs.rmSync(dirPath, { recursive: true, force: true });
30
+ }
31
+ ensureDir(dirPath);
32
+ }
33
+
34
+ function copyFile(sourcePath, destinationPath) {
35
+ if (!fs.existsSync(sourcePath)) {
36
+ return;
37
+ }
38
+
39
+ ensureDir(path.dirname(destinationPath));
40
+ fs.copyFileSync(sourcePath, destinationPath);
41
+ }
42
+
43
+ function copyDirectory(sourceDir, destinationDir, filter) {
44
+ if (!fs.existsSync(sourceDir)) {
45
+ return;
46
+ }
47
+
48
+ ensureDir(destinationDir);
49
+
50
+ for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
51
+ const sourcePath = path.join(sourceDir, entry.name);
52
+ const destinationPath = path.join(destinationDir, entry.name);
53
+
54
+ if (entry.isDirectory()) {
55
+ copyDirectory(sourcePath, destinationPath, filter);
56
+ } else if (!filter || filter(entry.name)) {
57
+ copyFile(sourcePath, destinationPath);
58
+ }
59
+ }
60
+ }
61
+
62
+ /** Only compiled JS/JSON output — skip .d.ts, sourcemaps, and build info. */
63
+ function isCompiledJS(name) {
64
+ if (name === '.tsbuildinfo') return false;
65
+ if (name.endsWith('.d.ts')) return false;
66
+ if (name.endsWith('.d.ts.map')) return false;
67
+ if (name.endsWith('.js.map')) return false;
68
+ return true;
69
+ }
70
+
71
+ function prepareDeployDirectory() {
72
+ cleanDir(deployDir);
73
+ const botDistDir = path.join(distDir, 'bot');
74
+
75
+ // ---------------------------------------------------------------
76
+ // 1. HTML shells (version-injected by inject-version.mjs earlier)
77
+ // ---------------------------------------------------------------
78
+ copyFile(path.join(rootDir, 'index.html'), path.join(deployDir, 'index.html'));
79
+ copyFile(path.join(rootDir, 'manifest.json'), path.join(deployDir, 'manifest.json'));
80
+
81
+ // ---------------------------------------------------------------
82
+ // 2. Compiled JS → _deploy/dist/ (matches runtime /dist/app.js)
83
+ // ---------------------------------------------------------------
84
+ const jsDeployDir = path.join(deployDir, 'dist');
85
+
86
+ for (const entry of fs.readdirSync(distDir, { withFileTypes: true })) {
87
+ const sourcePath = path.join(distDir, entry.name);
88
+ const destPath = path.join(jsDeployDir, entry.name);
89
+
90
+ // Skip static assets that were previously mixed into dist/.
91
+ // We only want the TS compiler output here.
92
+ if (['src', 'docs', 'bot', 'index.html', 'manifest.json',
93
+ 'robots.txt', 'sitemap.xml', '.well-known', 'assets'].includes(entry.name)) {
94
+ continue;
95
+ }
96
+
97
+ if (entry.isDirectory()) {
98
+ copyDirectory(sourcePath, destPath, isCompiledJS);
99
+ } else if (isCompiledJS(entry.name)) {
100
+ copyFile(sourcePath, destPath);
101
+ }
102
+ }
103
+
104
+ // ---------------------------------------------------------------
105
+ // 3. CSS → _deploy/src/styles/ (matches runtime /src/styles/)
106
+ // ---------------------------------------------------------------
107
+ copyDirectory(
108
+ path.join(rootDir, 'src', 'styles'),
109
+ path.join(deployDir, 'src', 'styles')
110
+ );
111
+
112
+ // ---------------------------------------------------------------
113
+ // 4. View partials → _deploy/src/views/ (matches runtime /src/views/)
114
+ // ---------------------------------------------------------------
115
+ copyDirectory(
116
+ path.join(rootDir, 'src', 'views'),
117
+ path.join(deployDir, 'src', 'views')
118
+ );
119
+
120
+ // ---------------------------------------------------------------
121
+ // 5. Docs markdown → _deploy/docs/
122
+ // ---------------------------------------------------------------
123
+ copyFile(
124
+ path.join(rootDir, 'docs', 'NATIVECORE_EBOOK.md'),
125
+ path.join(deployDir, 'docs', 'NATIVECORE_EBOOK.md')
126
+ );
127
+
128
+ // ---------------------------------------------------------------
129
+ // 6. Bot-rendered HTML → _deploy/bot/ when available
130
+ // ---------------------------------------------------------------
131
+ copyDirectory(botDistDir, path.join(deployDir, 'bot'));
132
+
133
+ // ---------------------------------------------------------------
134
+ // 7. Public static assets → _deploy/ root (robots.txt, etc.)
135
+ // ---------------------------------------------------------------
136
+ copyDirectory(path.join(rootDir, 'public'), deployDir);
137
+
138
+ console.log('Deployment directory prepared: _deploy/');
139
+ }
140
+
141
+ prepareDeployDirectory();
@@ -0,0 +1,223 @@
1
+ import readline from 'readline';
2
+ import { exec, spawn } from 'child_process';
3
+ import { promisify } from 'util';
4
+
5
+ const execAsync = promisify(exec);
6
+
7
+ const rl = readline.createInterface({
8
+ input: process.stdin,
9
+ output: process.stdout
10
+ });
11
+
12
+ let serverProcess = null;
13
+
14
+ function normalizeBotBuildPreference(value) {
15
+ if (!value) return null;
16
+
17
+ const normalized = value.toLowerCase().trim();
18
+
19
+ if (['y', 'yes', 'true', '1', 'always'].includes(normalized)) {
20
+ return 'always';
21
+ }
22
+
23
+ if (['n', 'no', 'false', '0', 'never', 'skip'].includes(normalized)) {
24
+ return 'never';
25
+ }
26
+
27
+ if (['auto', 'prompt'].includes(normalized)) {
28
+ return 'auto';
29
+ }
30
+
31
+ return null;
32
+ }
33
+
34
+ function resolveBotBuildMode() {
35
+ const cliArgs = new Set(process.argv.slice(2));
36
+
37
+ if (cliArgs.has('--yes') || cliArgs.has('--bots')) {
38
+ return 'always';
39
+ }
40
+
41
+ if (cliArgs.has('--no') || cliArgs.has('--skip-bots')) {
42
+ return 'never';
43
+ }
44
+
45
+ const envMode = normalizeBotBuildPreference(process.env.BOT_BUILD);
46
+ if (envMode) {
47
+ return envMode;
48
+ }
49
+
50
+ if (process.env.CI || !process.stdin.isTTY || !process.stdout.isTTY) {
51
+ return 'never';
52
+ }
53
+
54
+ return 'prompt';
55
+ }
56
+
57
+ function question(prompt) {
58
+ return new Promise((resolve) => {
59
+ rl.question(prompt, (answer) => {
60
+ resolve(answer);
61
+ });
62
+ });
63
+ }
64
+
65
+ async function startServer() {
66
+ return new Promise((resolve, reject) => {
67
+ console.log('🚀 Starting dev server...');
68
+
69
+ // Start server directly without npm (skips prestart compilation)
70
+ serverProcess = spawn('node', ['server.js'], {
71
+ detached: false,
72
+ stdio: 'ignore', // Suppress output to avoid overlap
73
+ shell: true
74
+ });
75
+
76
+ serverProcess.on('error', (error) => {
77
+ reject(new Error(`Failed to start server: ${error.message}`));
78
+ });
79
+
80
+ // Wait for server to be ready
81
+ console.log('⏳ Waiting for server...');
82
+ let attempts = 0;
83
+ const maxAttempts = 30;
84
+
85
+ const checkServer = setInterval(async () => {
86
+ attempts++;
87
+
88
+ try {
89
+ const response = await fetch('http://localhost:8000');
90
+ if (response.ok) {
91
+ clearInterval(checkServer);
92
+ console.log('✅ Server ready!\n');
93
+ resolve();
94
+ }
95
+ } catch (error) {
96
+ if (attempts >= maxAttempts) {
97
+ clearInterval(checkServer);
98
+ reject(new Error('Server failed to start after 30 seconds'));
99
+ }
100
+ }
101
+ }, 1000);
102
+ });
103
+ }
104
+
105
+ function stopServer() {
106
+ if (serverProcess) {
107
+ console.log('\n🛑 Stopping dev server...');
108
+ serverProcess.kill();
109
+ serverProcess = null;
110
+ }
111
+ }
112
+
113
+ async function main() {
114
+ const mode = resolveBotBuildMode();
115
+
116
+ // Wait for any background compilation to finish
117
+ await new Promise(resolve => setTimeout(resolve, 500));
118
+
119
+ // Clear any overlapping output
120
+ console.clear();
121
+
122
+ console.log('\n╔════════════════════════════════════════════════════╗');
123
+ console.log('║ 🤖 SEO-Optimized Build Available ║');
124
+ console.log('╚════════════════════════════════════════════════════╝\n');
125
+
126
+ console.log('Generate bot-optimized HTML for search engines?');
127
+ console.log(' • Improves SEO (Google, Bing, etc.)');
128
+ console.log(' • Enables social media previews');
129
+ console.log(' • Zero impact on user performance');
130
+ console.log('');
131
+
132
+ let shouldBuildBots = false;
133
+
134
+ if (mode === 'always') {
135
+ console.log('BOT_BUILD=always detected. Running bot build without prompt.\n');
136
+ shouldBuildBots = true;
137
+ } else if (mode === 'never') {
138
+ console.log('BOT_BUILD=never detected. Skipping bot build without prompt.\n');
139
+ } else {
140
+ const answer = await question('Build for bots? (y/N): ');
141
+ shouldBuildBots = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
142
+ }
143
+
144
+ if (shouldBuildBots) {
145
+ console.log('\n📦 Starting bot build...\n');
146
+
147
+ let serverWasStarted = false;
148
+
149
+ try {
150
+ // Check if server is already running
151
+ console.log('⚙️ Checking dev server...');
152
+
153
+ try {
154
+ const response = await fetch('http://localhost:8000');
155
+ if (response.ok) {
156
+ console.log('✅ Server already running!\n');
157
+ }
158
+ } catch {
159
+ // Server not running, try to start it
160
+ try {
161
+ await startServer();
162
+ serverWasStarted = true;
163
+ } catch (startError) {
164
+ // If start failed due to port in use, check if it's actually working now
165
+ if (startError.message.includes('address already in use') ||
166
+ startError.message.includes('EADDRINUSE')) {
167
+ console.log('\n⚠️ Port already in use, checking if server is now accessible...');
168
+ try {
169
+ const retryResponse = await fetch('http://localhost:8000');
170
+ if (retryResponse.ok) {
171
+ console.log('✅ Server is accessible!\n');
172
+ } else {
173
+ throw new Error('Server port in use but not responding');
174
+ }
175
+ } catch {
176
+ throw new Error('Port 8000 is in use. Please run: taskkill /F /IM node.exe');
177
+ }
178
+ } else {
179
+ throw startError;
180
+ }
181
+ }
182
+ }
183
+
184
+ // Run bot build
185
+ console.log('🎨 Rendering pages...\n');
186
+ const { stdout, stderr } = await execAsync('node scripts/build-for-bots.mjs');
187
+
188
+ if (stdout) console.log(stdout);
189
+ if (stderr && !stderr.includes('ExperimentalWarning')) {
190
+ console.error(stderr);
191
+ }
192
+
193
+ } catch (error) {
194
+ console.error('\n❌ Bot build failed:', error.message);
195
+ rl.close();
196
+ if (serverWasStarted) stopServer();
197
+ process.exit(1);
198
+ } finally {
199
+ // Clean up server if we started it
200
+ if (serverWasStarted) {
201
+ stopServer();
202
+ }
203
+ }
204
+ } else {
205
+ console.log('\n⏭️ Skipping bot build');
206
+ console.log('💡 Run `npm run build:bots` anytime to generate\n');
207
+ }
208
+
209
+ rl.close();
210
+ }
211
+
212
+ // Handle cleanup on exit
213
+ process.on('exit', stopServer);
214
+ process.on('SIGINT', () => {
215
+ stopServer();
216
+ process.exit(0);
217
+ });
218
+ process.on('SIGTERM', () => {
219
+ stopServer();
220
+ process.exit(0);
221
+ });
222
+
223
+ main();
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Component Removal Script
5
+ * Removes component file, import from registry.ts, and usage from codebase
6
+ *
7
+ * Usage:
8
+ * node scripts/remove-component.mjs sample-component
9
+ * npm run remove:component sample-component
10
+ */
11
+
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ // Get component name from command line
20
+ const componentName = process.argv[2];
21
+
22
+ if (!componentName) {
23
+ console.error('❌ Error: Component name is required');
24
+ console.log('\nUsage:');
25
+ console.log(' npm run clean:component <name>');
26
+ console.log('\nExample:');
27
+ console.log(' npm run clean:component my-card');
28
+ process.exit(1);
29
+ }
30
+
31
+ // Validate component name format
32
+ if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(componentName)) {
33
+ console.error('❌ Error: Component name must be in kebab-case with at least one hyphen');
34
+ process.exit(1);
35
+ }
36
+
37
+ // Paths
38
+ const componentsDir = path.resolve(__dirname, '..', 'src', 'components');
39
+ const uiDir = path.join(componentsDir, 'ui');
40
+ const componentFile = path.join(uiDir, `${componentName}.ts`);
41
+ const registryFile = path.join(componentsDir, 'registry.ts');
42
+
43
+ console.log(`\n🗑️ Removing component: ${componentName}\n`);
44
+
45
+ // Step 1: Check if component file exists
46
+ if (!fs.existsSync(componentFile)) {
47
+ console.error(`❌ Error: Component file "${componentName}.ts" does not exist`);
48
+ console.log(` Expected location: src/components/ui/${componentName}.ts`);
49
+ process.exit(1);
50
+ }
51
+
52
+ // Step 2: Remove from registry.ts (lazy loading registration)
53
+ let removedFromIndex = false;
54
+ if (fs.existsSync(registryFile)) {
55
+ let indexContent = fs.readFileSync(registryFile, 'utf-8');
56
+ const registrationPattern = new RegExp(`componentRegistry\\.register\\('${componentName}'[^\\n]*\\n`, 'g');
57
+
58
+ if (registrationPattern.test(indexContent)) {
59
+ // Remove the registration line
60
+ indexContent = indexContent.replace(registrationPattern, '');
61
+
62
+ fs.writeFileSync(registryFile, indexContent);
63
+ console.log('✅ Removed registration from registry.ts');
64
+ removedFromIndex = true;
65
+ } else {
66
+ console.log('⚠️ Registration not found in registry.ts (may have been manually removed)');
67
+ }
68
+ } else {
69
+ console.log('⚠️ registry.ts not found');
70
+ }
71
+
72
+ // Step 2.5: Remove from preloadRegistry.ts if it exists
73
+ const preloadFile = path.join(componentsDir, 'preloadRegistry.ts');
74
+ let removedFromPreload = false;
75
+
76
+ if (fs.existsSync(preloadFile)) {
77
+ let preloadContent = fs.readFileSync(preloadFile, 'utf-8');
78
+ const preloadPattern = new RegExp(`import\\s+['\"]\\./ui/${componentName}\\.js['\"];?\\s*\\n`, 'g');
79
+
80
+ if (preloadPattern.test(preloadContent)) {
81
+ preloadContent = preloadContent.replace(preloadPattern, '');
82
+ fs.writeFileSync(preloadFile, preloadContent);
83
+ console.log('✅ Removed from preloadRegistry.ts');
84
+ removedFromPreload = true;
85
+ }
86
+ }
87
+
88
+ // Step 3: Find and list usages in codebase
89
+ console.log(`\n🔍 Searching for <${componentName}> usage in codebase...`);
90
+
91
+ const usagePattern = new RegExp(`<${componentName}[^>]*>`, 'g');
92
+ const closingPattern = new RegExp(`</${componentName}>`, 'g');
93
+ const usages = [];
94
+
95
+ // Search in specific directories
96
+ const searchDirs = [
97
+ path.resolve(__dirname, '..', 'src', 'views', 'pages'),
98
+ path.resolve(__dirname, '..', 'src', 'components'),
99
+ path.resolve(__dirname, '..', 'src', 'controllers'),
100
+ ];
101
+
102
+ function searchDirectory(dirPath) {
103
+ if (!fs.existsSync(dirPath)) return;
104
+
105
+ const files = fs.readdirSync(dirPath, { withFileTypes: true });
106
+
107
+ for (const file of files) {
108
+ const fullPath = path.join(dirPath, file.name);
109
+
110
+ if (file.isDirectory()) {
111
+ searchDirectory(fullPath);
112
+ } else if (file.name.endsWith('.html') || file.name.endsWith('.ts')) {
113
+ const content = fs.readFileSync(fullPath, 'utf-8');
114
+ const openingMatches = content.match(usagePattern);
115
+ const closingMatches = content.match(closingPattern);
116
+
117
+ if (openingMatches || closingMatches) {
118
+ const relativePath = path.relative(path.resolve(__dirname, '..'), fullPath);
119
+ usages.push({
120
+ file: relativePath,
121
+ count: (openingMatches?.length || 0) + (closingMatches?.length || 0)
122
+ });
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ searchDirs.forEach(searchDirectory);
129
+
130
+ // Step 4: Remove usages from files
131
+ if (usages.length > 0) {
132
+ console.log(`\n📋 Found usage in ${usages.length} file(s):\n`);
133
+
134
+ for (const usage of usages) {
135
+ console.log(` - ${usage.file} (${usage.count} occurrence${usage.count > 1 ? 's' : ''})`);
136
+ }
137
+
138
+ console.log(`\n🗑️ Removing <${componentName}> tags from files...`);
139
+
140
+ for (const usage of usages) {
141
+ const fullPath = path.resolve(__dirname, '..', usage.file);
142
+ let content = fs.readFileSync(fullPath, 'utf-8');
143
+
144
+ // Remove self-closing tags: <component-name />
145
+ content = content.replace(new RegExp(`<${componentName}\\s*\\/?>`, 'g'), '');
146
+
147
+ // Remove opening and closing tags: <component-name></component-name>
148
+ content = content.replace(new RegExp(`<${componentName}[^>]*>[\\s\\S]*?<\\/${componentName}>`, 'g'), '');
149
+
150
+ fs.writeFileSync(fullPath, content);
151
+ console.log(` ✅ Cleaned ${usage.file}`);
152
+ }
153
+ } else {
154
+ console.log(' No usages found ✨');
155
+ }
156
+
157
+ // Step 5: Delete the component file
158
+ fs.unlinkSync(componentFile);
159
+ console.log(`\n✅ Deleted component file: ${componentName}.ts`);
160
+
161
+ // Summary
162
+ console.log('\n✨ Component removed successfully!\n');
163
+ console.log('📊 Summary:');
164
+ console.log(` 🗑️ Deleted: src/components/ui/${componentName}.ts`);
165
+ console.log(` ${removedFromIndex ? '✅' : '⚠️ '} Registry.ts: ${removedFromIndex ? 'Updated' : 'Not found'}`);
166
+ if (removedFromPreload) {
167
+ console.log(` ✅ PreloadRegistry.ts: Removed`);
168
+ }
169
+ console.log(` 📝 Files cleaned: ${usages.length}`);
170
+ console.log('');