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.
Files changed (175) hide show
  1. package/README.md +6 -14
  2. package/bin/index.mjs +403 -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 +653 -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,13 @@
1
+ # robots.txt for nativeCore Framework
2
+
3
+ User-agent: *
4
+ Allow: /
5
+
6
+ # Disallow admin/private areas if any
7
+ # Disallow: /admin/
8
+
9
+ # Sitemap location
10
+ Sitemap: https://yourdomain.com/sitemap.xml
11
+
12
+ # Crawl-delay (optional, adjust as needed)
13
+ Crawl-delay: 1
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
3
+ <url>
4
+ <loc>https://yourdomain.com/</loc>
5
+ <lastmod>2026-01-22</lastmod>
6
+ <changefreq>weekly</changefreq>
7
+ <priority>1.0</priority>
8
+ </url>
9
+ <url>
10
+ <loc>https://yourdomain.com/about</loc>
11
+ <lastmod>2026-01-22</lastmod>
12
+ <changefreq>monthly</changefreq>
13
+ <priority>0.8</priority>
14
+ </url>
15
+ <url>
16
+ <loc>https://yourdomain.com/components</loc>
17
+ <lastmod>2026-01-22</lastmod>
18
+ <changefreq>weekly</changefreq>
19
+ <priority>0.8</priority>
20
+ </url>
21
+ <url>
22
+ <loc>https://yourdomain.com/login</loc>
23
+ <lastmod>2026-01-22</lastmod>
24
+ <changefreq>monthly</changefreq>
25
+ <priority>0.5</priority>
26
+ </url>
27
+ </urlset>
@@ -0,0 +1,121 @@
1
+ import puppeteer from 'puppeteer';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const distPath = path.join(__dirname, '../dist');
8
+ const botDistPath = path.join(distPath, 'bot');
9
+
10
+ // Routes to pre-render for bots. Keep this to publicly reachable marketing/docs pages.
11
+ const routes = [
12
+ '/',
13
+ '/docs',
14
+ '/components',
15
+ '/login'
16
+ ];
17
+
18
+ function sanitizeBotHtml(html) {
19
+ return html
20
+ .replace(/<style>\s*html\[data-public-route="pending"\] body \{[\s\S]*?<\/style>/, '<style>html[data-public-route] body { visibility: visible; opacity: 1; }</style>')
21
+ .replace(/data-public-route="pending"/g, 'data-public-route="ready"')
22
+ .replace(/<script>\s*\(function\(\) \{[\s\S]*?document\.documentElement\.setAttribute\('data-public-route', 'ready'\);\s*\}\)\(\);\s*<\/script>/, '')
23
+ .replace(/<script>\s*\(function\(\) \{[\s\S]*?document\.write\(`<link rel="stylesheet" href="\/src\/styles\/main\.css\?v=\$\{version\}">`\);\s*\}\)\(\);\s*<\/script>/, '')
24
+ .replace(/<script type="module">\s*\(function\(\) \{[\s\S]*?import\(`\/dist\/app\.js\?v=\$\{version\}`\);\s*\}\)\(\);\s*<\/script>/, '')
25
+ .replace(/<script defer="" src="https:\/\/static\.cloudflareinsights\.com\/beacon[^>]*><\/script>/g, '');
26
+ }
27
+
28
+ async function buildForBots() {
29
+ console.log('🤖 Building bot-optimized HTML...\n');
30
+
31
+ fs.rmSync(botDistPath, { recursive: true, force: true });
32
+ fs.mkdirSync(botDistPath, { recursive: true });
33
+
34
+ let browser;
35
+
36
+ try {
37
+ // Launch headless browser
38
+ console.log('🚀 Starting headless browser...');
39
+ browser = await puppeteer.launch({
40
+ headless: 'new',
41
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
42
+ });
43
+
44
+ const page = await browser.newPage();
45
+
46
+ // Process each route
47
+ for (const route of routes) {
48
+ console.log(`📄 Rendering ${route}...`);
49
+
50
+ try {
51
+ // Navigate to the page
52
+ await page.goto(`http://localhost:8000${route}`, {
53
+ waitUntil: 'networkidle0',
54
+ timeout: 30000
55
+ });
56
+
57
+ // Wait a bit for any animations/dynamic content
58
+ await new Promise(resolve => setTimeout(resolve, 1000));
59
+
60
+ // Get the rendered HTML
61
+ const html = sanitizeBotHtml(await page.content());
62
+
63
+ // Determine output path
64
+ let outputPath;
65
+ if (route === '/') {
66
+ outputPath = path.join(botDistPath, 'index.html');
67
+ } else {
68
+ // Create directory for route
69
+ const routeDir = path.join(botDistPath, route);
70
+ if (!fs.existsSync(routeDir)) {
71
+ fs.mkdirSync(routeDir, { recursive: true });
72
+ }
73
+ outputPath = path.join(routeDir, 'index.html');
74
+ }
75
+
76
+ // Write HTML file
77
+ fs.writeFileSync(outputPath, html);
78
+
79
+ console.log(` ✅ Saved to ${outputPath.replace(distPath, 'dist')}`);
80
+ } catch (error) {
81
+ console.error(` ❌ Failed to render ${route}:`, error.message);
82
+ }
83
+ }
84
+
85
+ console.log('\n✨ Bot build complete!');
86
+ console.log(`📦 Bot-optimized files in: dist/bot/`);
87
+ console.log(`📦 Publishable bot files will be copied into: _deploy/bot/ during prepare-static-assets`);
88
+ console.log(`🔍 Search engines will now see pre-rendered HTML\n`);
89
+
90
+ } catch (error) {
91
+ console.error('❌ Bot build failed:', error);
92
+ process.exit(1);
93
+ } finally {
94
+ if (browser) {
95
+ await browser.close();
96
+ }
97
+ }
98
+ }
99
+
100
+ // Check if dev server is running
101
+ async function checkDevServer() {
102
+ try {
103
+ const response = await fetch('http://localhost:8000');
104
+ return response.ok;
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ // Main execution
111
+ (async () => {
112
+ const serverRunning = await checkDevServer();
113
+
114
+ if (!serverRunning) {
115
+ console.error('❌ Dev server is not running on http://localhost:8000');
116
+ console.error('💡 Please run `npm start` in another terminal first\n');
117
+ process.exit(1);
118
+ }
119
+
120
+ await buildForBots();
121
+ })();
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * TypeScript Conversion Helper
4
+ * Converts all remaining .js files to .ts with basic type annotations
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ const srcDir = path.join(__dirname, '..', 'src');
15
+
16
+ // Files to skip (already converted or special cases)
17
+ const skipFiles = new Set([
18
+ 'state.ts', 'component.ts', 'router.ts', 'http.ts',
19
+ 'auth.service.ts', 'api.service.ts', 'storage.service.ts'
20
+ ]);
21
+
22
+ // Basic type inference rules
23
+ const typeInference = {
24
+ 'const.*=.*\\[\\]': 'any[]',
25
+ 'const.*=.*\\{\\}': 'Record<string, any>',
26
+ 'const.*=.*useState\\(': 'State',
27
+ 'const.*=.*computed\\(': 'ComputedState',
28
+ };
29
+
30
+ function convertJsToTs(content) {
31
+ let tsContent = content;
32
+
33
+ // Remove .js extensions from imports
34
+ tsContent = tsContent.replace(/from ['"](.*)\.js['"]/g, "from '$1'");
35
+
36
+ // Add basic type annotations to function parameters
37
+ tsContent = tsContent.replace(
38
+ /function\s+(\w+)\s*\(([^)]*)\)/g,
39
+ (match, funcName, params) => {
40
+ const typedParams = params
41
+ .split(',')
42
+ .map(p => p.trim())
43
+ .filter(p => p)
44
+ .map(p => {
45
+ if (p.includes('=')) {
46
+ const [name, defaultValue] = p.split('=').map(s => s.trim());
47
+ return `${name}: any = ${defaultValue}`;
48
+ }
49
+ return `${p}: any`;
50
+ })
51
+ .join(', ');
52
+ return `function ${funcName}(${typedParams})`;
53
+ }
54
+ );
55
+
56
+ // Add void return types to functions without returns
57
+ tsContent = tsContent.replace(
58
+ /(function\s+\w+\([^)]*\))\s*\{/g,
59
+ '$1: void {'
60
+ );
61
+
62
+ return tsContent;
63
+ }
64
+
65
+ function processFile(filePath) {
66
+ const ext = path.extname(filePath);
67
+ const basename = path.basename(filePath);
68
+
69
+ if (ext !== '.js' || skipFiles.has(basename)) {
70
+ return;
71
+ }
72
+
73
+ const content = fs.readFileSync(filePath, 'utf-8');
74
+ const tsContent = convertJsToTs(content);
75
+ const tsPath = filePath.replace(/\.js$/, '.ts');
76
+
77
+ fs.writeFileSync(tsPath, tsContent, 'utf-8');
78
+ console.log(`✅ Converted: ${path.relative(srcDir, filePath)} → ${path.basename(tsPath)}`);
79
+
80
+ // Delete old .js file
81
+ fs.unlinkSync(filePath);
82
+ console.log(`🗑️ Deleted: ${path.relative(srcDir, filePath)}`);
83
+ }
84
+
85
+ function walkDir(dir) {
86
+ const files = fs.readdirSync(dir);
87
+
88
+ for (const file of files) {
89
+ const filePath = path.join(dir, file);
90
+ const stat = fs.statSync(filePath);
91
+
92
+ if (stat.isDirectory()) {
93
+ walkDir(filePath);
94
+ } else if (stat.isFile()) {
95
+ processFile(filePath);
96
+ }
97
+ }
98
+ }
99
+
100
+ console.log('🚀 Starting TypeScript conversion...\n');
101
+ walkDir(srcDir);
102
+ console.log('\n✨ Conversion complete!');
103
+ console.log('\n📝 Next steps:');
104
+ console.log(' 1. Run: npm run typecheck');
105
+ console.log(' 2. Fix any type errors');
106
+ console.log(' 3. Add proper type annotations to converted files');
@@ -0,0 +1,38 @@
1
+ import { readFileSync, writeFileSync } from 'fs';
2
+
3
+ // Map: corrupted string → correct character/entity
4
+ // These are Windows-1252 bytes re-interpreted as UTF-8 mojibake
5
+ const fixes = [
6
+ // Special HTML chars — use entities so files stay ASCII-safe
7
+ ['src/components/ui/nc-alert.ts', '\u00c3\u00b7', '&times;'],
8
+ ['src/components/ui/nc-modal.ts', '\u00c3\u00b7', '&times;'],
9
+ ['src/components/ui/nc-pagination.ts', '\u00c2\u00ab', '&laquo;'],
10
+ ['src/components/ui/nc-pagination.ts', '\u00e2\u20ac\u00b9','&lsaquo;'],
11
+ ['src/components/ui/nc-pagination.ts', '\u00e2\u20ac\u00a6','&hellip;'],
12
+ ['src/components/ui/nc-pagination.ts', '\u00e2\u20ac\u00ba','&rsaquo;'],
13
+ ['src/components/ui/nc-pagination.ts', '\u00c2\u00bb', '&raquo;'],
14
+ // em dash in comment
15
+ ['src/components/core/app-sidebar.ts', '\u00e2\u20ac\u201c', '\u2014'],
16
+ // Dashboard emojis — restore real UTF-8 emoji codepoints
17
+ ['src/views/protected/dashboard.html', '\u00f0\u0178\u201c\u201c', '\uD83D\uDD04'], // 🔄
18
+ ['src/views/protected/dashboard.html', '\u00e2\u017e\u2022', '\u2795'], // ➕
19
+ ['src/views/protected/dashboard.html', '\u00f0\u0178\u201c\u0160', '\uD83D\uDCCA'], // 📊
20
+ ['src/views/protected/dashboard.html', '\u00e2\u0161\u2122\u00ef\u00b8\u008f', '\u2699\uFE0F'], // ⚙️
21
+ ['src/views/protected/dashboard.html', '\u00f0\u0178\u2019\u00a5', '\uD83D\uDC65'], // 👥
22
+ ['src/views/protected/dashboard.html', '\u00f0\u0178\u201c\u02c6', '\uD83D\uDCC8'], // 📈
23
+ ['src/views/protected/dashboard.html', '\u00e2\u0153\u2026', '\u2705'], // ✅
24
+ // Components page emojis
25
+ ['src/views/public/components.html', '\u00f0\u0178\u201c\u00a7', '\uD83D\uDD27'], // 🔧
26
+ ['src/views/public/components.html', '\u00e2\u00ad\u00ad', '\u2B50'], // ⭐
27
+ ['src/views/public/components.html', '\u00f0\u0178\u201c\u00a4', '\uD83D\uDCE4'], // 📤
28
+ ['src/views/public/components.html', '\u00f0\u0178\u2014\u2018\u00ef\u00b8\u008f', '\uD83D\uDDD1\uFE0F'], // 🗑️
29
+ ];
30
+
31
+ for (const [file, bad, good] of fixes) {
32
+ const content = readFileSync(file, 'utf8');
33
+ if (content.includes(bad)) {
34
+ writeFileSync(file, content.split(bad).join(good), 'utf8');
35
+ console.log(`Fixed ${file} ${JSON.stringify(bad)} -> ${JSON.stringify(good)}`);
36
+ }
37
+ }
38
+ console.log('Encoding fix complete.');
@@ -0,0 +1,32 @@
1
+ import { readFileSync, writeFileSync } from 'fs';
2
+
3
+ const files = [
4
+ 'src/components/ui/nc-fab.ts',
5
+ 'src/components/ui/nc-file-upload.ts',
6
+ 'src/components/ui/nc-icon-button.ts',
7
+ 'src/components/ui/nc-icon.ts',
8
+ 'src/components/ui/nc-menu-item.ts',
9
+ 'src/dev/outline-panel.ts',
10
+ ];
11
+
12
+ const svgCtxMarkers = [' d=', '<path', '<polyline', '<polygon', '<svg', '"M', "'M"];
13
+
14
+ for (const file of files) {
15
+ const orig = readFileSync(file, 'utf8');
16
+ let c = orig;
17
+
18
+ // Replace digit/space + nc- + digit back to v- only in SVG path contexts
19
+ c = c.replace(/([0-9 ])nc-([0-9])/g, (match, before, after, offset) => {
20
+ const ctx = orig.substring(Math.max(0, offset - 80), offset + 80);
21
+ const inSvg = svgCtxMarkers.some(m => ctx.includes(m));
22
+ return inSvg ? before + 'v-' + after : match;
23
+ });
24
+
25
+ if (c !== orig) {
26
+ writeFileSync(file, c, 'utf8');
27
+ console.log('Fixed:', file);
28
+ } else {
29
+ console.log('No change:', file);
30
+ }
31
+ }
32
+ console.log('Done');
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+
10
+ const routesPath = path.resolve(__dirname, '..', 'src', 'routes', 'routes.ts');
11
+ const redirectsPath = path.resolve(__dirname, '..', 'public', '_redirects');
12
+
13
+ export function extractProtectedRoutes(routesSource) {
14
+ const match = routesSource.match(/export const protectedRoutes = \[(.*?)\];/s);
15
+
16
+ if (!match) {
17
+ throw new Error('Could not find protectedRoutes export in src/routes/routes.ts');
18
+ }
19
+
20
+ return Array.from(match[1].matchAll(/['"]([^'"]+)['"]/g), routeMatch => routeMatch[1]);
21
+ }
22
+
23
+ export function extractRegisteredRoutes(routesSource) {
24
+ return Array.from(routesSource.matchAll(/\.register\(\s*['"]([^'"]+)['"]/g), match => match[1]);
25
+ }
26
+
27
+ export function renderCfRouter(protectedRoutes, registeredRoutes) {
28
+ const routeRules = registeredRoutes
29
+ .filter(route => route !== '/')
30
+ .flatMap(route => {
31
+ const variants = route.endsWith('/') ? [route] : [route, `${route}/`];
32
+ return variants.map(routeVariant => `${routeVariant} / 200`);
33
+ });
34
+
35
+ return [
36
+ ...routeRules
37
+ ].join('\n') + '\n';
38
+ }
39
+
40
+ export function generateCfRouter() {
41
+ const routesSource = fs.readFileSync(routesPath, 'utf8');
42
+ const protectedRoutes = extractProtectedRoutes(routesSource);
43
+ const registeredRoutes = extractRegisteredRoutes(routesSource);
44
+ const output = renderCfRouter(protectedRoutes, registeredRoutes);
45
+
46
+ fs.writeFileSync(redirectsPath, output);
47
+ console.log(`Updated: ${path.relative(path.resolve(__dirname, '..'), redirectsPath).replace(/\\/g, '/')}`);
48
+ }
49
+
50
+ if (process.argv[1] && path.resolve(process.argv[1]) === __filename) {
51
+ generateCfRouter();
52
+ }
@@ -0,0 +1,41 @@
1
+ // scripts/inject-denc-tools.mjs
2
+ // Inject denc-tools script into HTML files for development
3
+ import { readFile, writeFile } from 'fs/promises';
4
+ import { resolve } from 'path';
5
+
6
+ const files = ['index.html'];
7
+ const devScripts = ` <!-- DEnc-ONLY-START -->
8
+ <!-- Hot Module Replacement (HMR) - Development Only -->
9
+ <script type="module" src="/src/dev/hmr.js"></script>
10
+ <script type="module" src="/dist/dev/denc-tools.js"></script>
11
+ <!-- DEnc-ONLY-END -->`;
12
+
13
+ async function injectDevTools(file) {
14
+ try {
15
+ const path = resolve(process.cwd(), file);
16
+ let html = await readFile(path, 'utf8');
17
+
18
+ // Check if denc-tools already injected
19
+ if (html.includes('dist/dev/denc-tools.js')) {
20
+ console.log(`✓ Dev scripts already injected in ${file}`);
21
+ return;
22
+ }
23
+
24
+ // Find the injection marker
25
+ const marker = '<!--- inject here --->';
26
+ if (html.includes(marker)) {
27
+ html = html.replace(marker, devScripts);
28
+ await writeFile(path, html, 'utf8');
29
+ console.log(`✓ Injected dev scripts (HMR + denc-tools) into ${file}`);
30
+ } else {
31
+ console.warn(`⚠️ No injection marker found in ${file}`);
32
+ }
33
+ } catch (error) {
34
+ console.error(`✗ Failed to inject denc-tools into ${file}:`, error.message);
35
+ }
36
+ }
37
+
38
+ (async () => {
39
+ console.log('🔧 Injecting dev scripts (HMR + denc-tools) for development...\n');
40
+ await Promise.all(files.map(injectDevTools));
41
+ })();
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Version Injection Script
3
+ * Automatically injects package.json version into cache buster
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ // Read package.json
13
+ const packageJson = JSON.parse(
14
+ fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8')
15
+ );
16
+
17
+ const packageVersion = packageJson.version;
18
+ const githubRunId = process.env.GITHUB_RUN_ID;
19
+ const githubRunAttempt = process.env.GITHUB_RUN_ATTEMPT;
20
+ const gitSha = process.env.GITHUB_SHA?.slice(0, 8);
21
+ const timestamp = new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14);
22
+ const deployVersion = githubRunId
23
+ ? `${packageVersion}-${githubRunId}-${githubRunAttempt || '1'}`
24
+ : gitSha
25
+ ? `${packageVersion}-${gitSha}`
26
+ : `${packageVersion}-${timestamp}`;
27
+
28
+ function replaceOrThrow(content, pattern, replacement, fileLabel) {
29
+ const matched = content.match(pattern);
30
+
31
+ if (!matched) {
32
+ throw new Error(`Failed to inject deploy version into ${fileLabel}`);
33
+ }
34
+
35
+ return content.replace(pattern, replacement);
36
+ }
37
+
38
+ // Update cacheBuster.ts
39
+ const cacheBusterPath = path.join(__dirname, '../src/utils/cacheBuster.ts');
40
+ let cacheBusterContent = fs.readFileSync(cacheBusterPath, 'utf-8');
41
+
42
+ cacheBusterContent = replaceOrThrow(
43
+ cacheBusterContent,
44
+ /export const cacheVersion = isDevelopment[\s\S]*?;/,
45
+ `export const cacheVersion = isDevelopment\n ? Date.now()\n : '${deployVersion}';`,
46
+ 'src/utils/cacheBuster.ts'
47
+ );
48
+
49
+ fs.writeFileSync(cacheBusterPath, cacheBusterContent);
50
+
51
+ for (const htmlFileName of ['index.html']) {
52
+ const htmlPath = path.join(__dirname, `../${htmlFileName}`);
53
+ let htmlContent = fs.readFileSync(htmlPath, 'utf-8');
54
+
55
+ htmlContent = replaceOrThrow(
56
+ htmlContent,
57
+ /const version = isDev \? Date\.now\(\) : '[^']+';/g,
58
+ `const version = isDev ? Date.now() : '${deployVersion}';`,
59
+ htmlFileName
60
+ );
61
+
62
+ fs.writeFileSync(htmlPath, htmlContent);
63
+ }
64
+
65
+ console.log(`Deploy version ${deployVersion} injected successfully!`);