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
package/bin/index.mjs CHANGED
@@ -3,11 +3,15 @@
3
3
  import { spawn } from 'child_process';
4
4
  import fs from 'fs/promises';
5
5
  import path from 'path';
6
+ import { fileURLToPath } from 'url';
6
7
  import { createInterface } from 'readline/promises';
7
8
  import { stdin as input, stdout as output } from 'process';
8
9
 
9
10
  const cliArgs = process.argv.slice(2);
10
11
  const rl = createInterface({ input, output });
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+ const templateDir = path.resolve(__dirname, '../template');
11
15
 
12
16
  function hasFlag(flag) {
13
17
  return cliArgs.includes(flag);
@@ -51,12 +55,18 @@ async function writeFile(filePath, content) {
51
55
  await fs.writeFile(filePath, content, 'utf8');
52
56
  }
53
57
 
54
- async function installDependencies(targetDir) {
55
- const command = 'npm';
56
- const args = ['install'];
58
+ async function removeIfExists(targetPath) {
59
+ await fs.rm(targetPath, { recursive: true, force: true });
60
+ }
57
61
 
62
+ async function replaceInFile(filePath, transform) {
63
+ const existing = await fs.readFile(filePath, 'utf8');
64
+ await fs.writeFile(filePath, transform(existing), 'utf8');
65
+ }
66
+
67
+ async function installDependencies(targetDir) {
58
68
  await new Promise((resolve, reject) => {
59
- const child = spawn(command, args, {
69
+ const child = spawn('npm', ['install'], {
60
70
  cwd: targetDir,
61
71
  stdio: 'inherit',
62
72
  shell: process.platform === 'win32'
@@ -74,462 +84,461 @@ async function installDependencies(targetDir) {
74
84
  });
75
85
  }
76
86
 
77
- function scriptBlock(config) {
78
- const scripts = {
79
- dev: 'npm run compile && node server.js',
80
- start: 'node server.js',
81
- compile: 'tsc',
82
- typecheck: 'tsc --noEmit'
83
- };
84
-
85
- return scripts;
86
- }
87
-
88
87
  function packageJsonTemplate(config) {
89
88
  return JSON.stringify({
90
89
  name: config.projectName,
91
90
  version: '0.1.0',
92
- private: true,
91
+ description: `${config.projectTitle} built with NativeCore`,
93
92
  type: 'module',
94
- scripts: scriptBlock(config),
95
- dependencies: {
96
- nativecorejs: config.frameworkDependency
93
+ main: 'server.js',
94
+ scripts: {
95
+ prestart: 'npm run compile && node scripts/inject-version.mjs',
96
+ start: 'node server.js',
97
+ validate: 'npm run typecheck && npm run build:client && npm run test -- --run',
98
+ dev: 'npm run compile && node scripts/inject-version.mjs && concurrently --kill-others --names "watch,server" -c "blue,green" "node scripts/watch-compile.mjs" "node server.js"',
99
+ 'dev:watch': 'node scripts/watch-compile.mjs',
100
+ clean: 'node -e "const fs=require(\'fs\'); fs.rmSync(\'dist\',{recursive:true,force:true}); fs.rmSync(\'_deploy\',{recursive:true,force:true})"',
101
+ prebuild: 'npm run clean && npm run lint && npm run typecheck',
102
+ build: 'node scripts/inject-version.mjs && npm run compile:prod && node scripts/minify.mjs && node scripts/prepare-static-assets.mjs && node scripts/strip-dev-blocks.mjs && node scripts/remove-dev.mjs',
103
+ 'build:client': 'node scripts/inject-version.mjs && npm run compile:prod && node scripts/minify.mjs && node scripts/prepare-static-assets.mjs',
104
+ compile: 'tsc && tsc-alias',
105
+ 'compile:prod': 'tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && node scripts/remove-dev.mjs',
106
+ typecheck: 'tsc --noEmit',
107
+ 'make:component': 'node scripts/make-component.mjs',
108
+ 'make:core-component': 'node scripts/make-core-component.mjs',
109
+ 'make:controller': 'node scripts/make-controller.mjs',
110
+ 'remove:component': 'node scripts/remove-component.mjs',
111
+ 'remove:core-component': 'node scripts/remove-core-component.mjs',
112
+ 'make:view': 'node scripts/make-view.mjs',
113
+ 'remove:view': 'node scripts/remove-view.mjs',
114
+ test: 'vitest',
115
+ 'test:ui': 'vitest --ui',
116
+ 'test:coverage': 'vitest --coverage',
117
+ lint: 'eslint src/**/*.ts && htmlhint "**/*.html" --config .htmlhintrc',
118
+ 'lint:fix': 'eslint src/**/*.ts --fix'
97
119
  },
120
+ keywords: ['nativecore', 'spa', 'web-components', 'typescript'],
121
+ license: 'MIT',
98
122
  devDependencies: {
99
- typescript: '^5.6.3',
100
- '@types/node': '^22.0.0'
123
+ '@eslint/js': '^9.39.2',
124
+ '@types/node': '^20.11.0',
125
+ 'concurrently': '^9.2.1',
126
+ 'eslint': '^9.39.2',
127
+ 'globals': '^17.0.0',
128
+ 'happy-dom': '^20.8.9',
129
+ 'htmlhint': '^1.1.4',
130
+ 'puppeteer': '^24.36.0',
131
+ 'terser': '^5.46.0',
132
+ 'tsc-alias': '^1.8.16',
133
+ 'typescript': '^5.3.3',
134
+ 'typescript-eslint': '^8.53.1',
135
+ 'vitest': '^4.1.4',
136
+ 'ws': '^8.19.0'
101
137
  }
102
138
  }, null, 2) + '\n';
103
139
  }
104
140
 
105
- function tsconfigTemplate() {
106
- return `{
107
- "compilerOptions": {
108
- "target": "ES2022",
109
- "module": "ESNext",
110
- "moduleResolution": "Bundler",
111
- "outDir": "dist",
112
- "strict": true,
113
- "skipLibCheck": true,
114
- "baseUrl": "."
115
- },
116
- "include": ["src/**/*.ts"]
117
- }
118
- `;
119
- }
120
-
121
- function serverTemplate() {
122
- return `import http from 'http';
123
- import fs from 'fs';
124
- import path from 'path';
125
- import { fileURLToPath } from 'url';
126
-
127
- const __filename = fileURLToPath(import.meta.url);
128
- const __dirname = path.dirname(__filename);
129
- const PORT = process.env.PORT || 8000;
130
-
131
- const mimeTypes = {
132
- '.html': 'text/html',
133
- '.js': 'text/javascript',
134
- '.css': 'text/css',
135
- '.json': 'application/json',
136
- '.svg': 'image/svg+xml'
137
- };
138
-
139
- http.createServer((req, res) => {
140
- const requestPath = req.url?.split('?')[0] || '/';
141
- let filePath = path.join(__dirname, requestPath === '/' ? 'index.html' : requestPath);
142
-
143
- if (!path.extname(requestPath) && !fs.existsSync(filePath)) {
144
- filePath = path.join(__dirname, 'index.html');
145
- }
146
-
147
- fs.readFile(filePath, (error, content) => {
148
- if (error) {
149
- res.writeHead(404, { 'Content-Type': 'text/plain' });
150
- res.end('Not found');
151
- return;
152
- }
153
-
154
- const contentType = mimeTypes[path.extname(filePath)] || 'text/plain';
155
- res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': 'no-cache' });
156
- res.end(content);
157
- });
158
- }).listen(PORT, () => {
159
- console.log('NativeCore starter running at http://localhost:' + PORT);
160
- });
161
- `;
162
- }
163
-
164
- function shellHtmlTemplate(config, shell) {
165
- const entryScript = './dist/app.js';
166
- const shellName = shell === 'app' ? 'protected' : 'public';
167
-
168
- return `<!DOCTYPE html>
169
- <html lang="en">
170
- <head>
171
- <meta charset="UTF-8">
172
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
173
- <meta name="app-shell" content="${shellName}">
174
- <title>${config.projectTitle}</title>
175
- <link rel="stylesheet" href="./node_modules/nativecorejs/src/styles/base.css">
176
- <link rel="stylesheet" href="./src/styles/main.css">
177
- <script type="importmap">
178
- {
179
- "imports": {
180
- "nativecorejs": "./node_modules/nativecorejs/dist/index.js",
181
- "nativecorejs/components": "./node_modules/nativecorejs/dist/components/index.js"
141
+ function nativecoreConfigTemplate(config) {
142
+ return JSON.stringify({
143
+ appName: config.projectTitle,
144
+ packageManager: 'npm',
145
+ useTypeScript: true,
146
+ features: {
147
+ auth: config.includeAuth,
148
+ dashboard: config.includeDashboard,
149
+ devTools: true,
150
+ hmr: true,
151
+ mockApi: true
182
152
  }
183
- }
184
- </script>
185
- </head>
186
- <body>
187
- <div id="main-content"></div>
188
- <script type="module" src="${entryScript}"></script>
189
- </body>
190
- </html>
191
- `;
192
- }
193
-
194
- function appEntryTemplate(config) {
195
- return `import { registerBuiltinComponents, Router } from 'nativecorejs';
196
- import { registerRoutes } from './config/routes.js';
197
-
198
- registerBuiltinComponents();
199
-
200
- const router = new Router();
201
-
202
- registerRoutes(router);
203
- router.start();
204
- `;
153
+ }, null, 2) + '\n';
205
154
  }
206
155
 
207
156
  function routesTemplate(config) {
208
- const typeImport = "import type { ControllerFunction, Router } from 'nativecorejs';\n";
209
157
  const loginRoute = config.includeAuth
210
- ? " .register('/login', 'src/views/pages/public/login.html', lazyController('loginController', '../controllers/login.controller.js'))\n"
158
+ ? " .register('/login', 'src/views/public/login.html', lazyController('loginController', '../controllers/login.controller.js'))\n"
211
159
  : '';
212
160
  const dashboardRoute = config.includeDashboard
213
- ? " .register('/dashboard', 'src/views/pages/protected/dashboard.html', lazyController('dashboardController', '../controllers/dashboard.controller.js'))\n"
161
+ ? " .register('/dashboard', 'src/views/protected/dashboard.html', lazyController('dashboardController', '../controllers/dashboard.controller.js'))\n"
214
162
  : '';
215
- const protectedRoutes = config.includeDashboard ? "export const protectedRoutes = ['/dashboard'];\n" : "export const protectedRoutes = [];\n";
216
-
217
- return `${typeImport}function lazyController(controllerName: string, controllerPath: string): ControllerFunction {
218
- return async (...args: Parameters<ControllerFunction>) => {
219
- const module = await import(controllerPath);
220
- const controller = module[controllerName] as ControllerFunction;
221
- return controller(...args);
163
+ const protectedRoutes = config.includeAuth && config.includeDashboard ? "export const protectedRoutes = ['/dashboard'];\n" : "export const protectedRoutes = [];\n";
164
+
165
+ return `/**
166
+ * Route Configuration
167
+ */
168
+ import { bustCache } from '../utils/cacheBuster.js';
169
+ import type { ControllerFunction, Router } from '../core/router.js';
170
+
171
+ function lazyController(controllerName: string, controllerPath: string): ControllerFunction {
172
+ return async (...args: any[]) => {
173
+ const module = await import(bustCache(controllerPath));
174
+ return module[controllerName](...args);
222
175
  };
223
176
  }
224
177
 
225
178
  export function registerRoutes(router: Router): void {
226
179
  router
227
- .register('/', 'src/views/pages/public/home.html', lazyController('homeController', '../controllers/home.controller.js'))
180
+ .register('/', 'src/views/public/home.html', lazyController('homeController', '../controllers/home.controller.js'))
228
181
  ${loginRoute}${dashboardRoute}}
229
182
 
230
183
  ${protectedRoutes}`;
231
184
  }
232
185
 
233
- function controllerTemplate(name, body, config) {
234
- return `import { trackEvents, trackSubscriptions } from 'nativecorejs';
235
-
236
- export async function ${name}(params: Record<string, string> = {}): Promise<() => void> {
237
- const events = trackEvents();
238
- const subs = trackSubscriptions();
239
-
240
- ${body}
241
-
242
- return () => {
243
- events.cleanup();
244
- subs.cleanup();
245
- };
246
- }
247
- `;
248
- }
249
-
250
- function homeControllerBody(config) {
251
- return ` void params;
252
- const cta = document.querySelector('[data-action="launch-dashboard"]');
253
-
254
- if (cta) {
255
- events.onClick('[data-action="launch-dashboard"]', () => {
256
- window.history.pushState({}, '', '${config.includeDashboard ? '/dashboard' : '/'}');
257
- window.dispatchEvent(new PopStateEvent('popstate'));
258
- });
259
- }`;
260
- }
186
+ function appTsTemplate(config) {
187
+ const authImports = config.includeAuth
188
+ ? "import auth from './services/auth.service.js';\nimport type { User } from './services/auth.service.js';\nimport api from './services/api.service.js';\nimport { authMiddleware } from './middleware/auth.middleware.js';\n"
189
+ : "";
190
+ const authVerify = config.includeAuth
191
+ ? `async function verifyExistingSession(): Promise<void> {
192
+ if (!auth.getToken()) {
193
+ return;
194
+ }
261
195
 
262
- function loginControllerBody() {
263
- return ` void params;
264
- events.onSubmit('[data-form="login"]', (event: Event) => {
265
- event.preventDefault();
266
- });`;
267
- }
196
+ try {
197
+ const response = await api.get<{ authenticated: boolean; user?: User }>('/auth/verify');
198
+ if (!response?.authenticated || !response.user) {
199
+ auth.logout();
200
+ return;
201
+ }
268
202
 
269
- function dashboardControllerBody() {
270
- return ` void params;
271
- const items = document.querySelectorAll('[data-metric-card]');
272
- items.forEach(item => item.classList.add('is-ready'));`;
203
+ auth.setUser(response.user);
204
+ } catch {
205
+ auth.logout();
206
+ }
273
207
  }
274
208
 
275
- function publicViewTemplate(config) {
276
- const authLink = config.includeAuth ? '<a href="/login">Login</a>' : '';
277
- const dashboardButton = config.includeDashboard ? '<button type="button" data-action="launch-dashboard">Open dashboard shell</button>' : '';
278
-
279
- return `<section class="hero">
280
- <p class="eyebrow">NativeCore</p>
281
- <h1>${config.projectTitle}</h1>
282
- <p class="lede">A clean starter generated by create-nativecore. This shell is app-level only and excludes demo API endpoints or deployment-specific backend assets.</p>
283
- <div class="hero-actions">
284
- ${dashboardButton}
285
- ${authLink}
286
- </div>
287
- </section>
288
- `;
289
- }
209
+ `
210
+ : '';
211
+ const authMiddlewareSetup = config.includeAuth ? ' router.use(authMiddleware);\n' : '';
212
+ const authChangeHandler = config.includeAuth ? ` window.addEventListener('auth-change', () => {
213
+ const isAuth = auth.isAuthenticated();
214
+ if (!isAuth) {
215
+ document.body.classList.remove('sidebar-enabled');
216
+ document.getElementById('app')?.classList.remove('sidebar-collapsed');
217
+ document.getElementById('app')?.classList.add('no-sidebar');
218
+ } else {
219
+ updateSidebarVisibility();
220
+ }
221
+ });
290
222
 
291
- function loginViewTemplate() {
292
- return `<section class="page-section auth-page">
293
- <h1>Sign in</h1>
294
- <form data-form="login" class="auth-form">
295
- <label>
296
- <span>Email</span>
297
- <input type="email" name="email" placeholder="you@example.com">
298
- </label>
299
- <label>
300
- <span>Password</span>
301
- <input type="password" name="password" placeholder="Enter your password">
302
- </label>
303
- <button type="submit">Sign in</button>
304
- </form>
305
- </section>
306
- `;
223
+ ` : '';
224
+ const authVerificationCall = config.includeAuth ? ' await verifyExistingSession();\n' : '';
225
+
226
+ return `/**
227
+ * Main Application Entry Point
228
+ */
229
+ import router from './core/router.js';
230
+ ${authImports}import { registerRoutes, protectedRoutes } from './routes/routes.js';
231
+ import { initSidebar } from './utils/sidebar.js';
232
+ import { initLazyComponents } from './core/lazyComponents.js';
233
+ import './utils/dom.js';
234
+ import './components/registry.js';
235
+
236
+ function isLocalhost(): boolean {
237
+ const hostname = window.location.hostname;
238
+ return hostname === 'localhost' ||
239
+ hostname === '127.0.0.1' ||
240
+ hostname.startsWith('192.168.') ||
241
+ hostname.endsWith('.local');
242
+ }
243
+
244
+ function updateSidebarVisibility() {
245
+ ${config.includeAuth ? ` const isAuthenticated = auth.isAuthenticated();` : ' const isAuthenticated = false;'}
246
+ const currentPath = window.location.pathname;
247
+ const isProtectedRoute = protectedRoutes.some(route => currentPath.startsWith(route));
248
+ const app = document.getElementById('app');
249
+
250
+ if (isAuthenticated && isProtectedRoute) {
251
+ document.body.classList.add('sidebar-enabled');
252
+ app?.classList.remove('no-sidebar');
253
+ } else {
254
+ document.body.classList.remove('sidebar-enabled');
255
+ app?.classList.add('no-sidebar');
256
+ }
307
257
  }
308
258
 
309
- function dashboardViewTemplate() {
310
- return `<section class="page-section dashboard-grid">
311
- <article data-metric-card>
312
- <h2>Users</h2>
313
- <p>1,284</p>
314
- </article>
315
- <article data-metric-card>
316
- <h2>Revenue</h2>
317
- <p>$48,900</p>
318
- </article>
319
- <article data-metric-card>
320
- <h2>Errors</h2>
321
- <p>2</p>
322
- </article>
323
- </section>
324
- `;
325
- }
259
+ ${authVerify}async function init() {
260
+ ${authVerificationCall} await initLazyComponents();
326
261
 
327
- function stylesTemplate() {
328
- return `:root {
329
- --background: #f5efe5;
330
- --surface: rgba(255, 255, 255, 0.78);
331
- --surface-strong: #fffaf2;
332
- --text: #1f2937;
333
- --muted: #5b6470;
334
- --accent: #0f766e;
335
- --accent-strong: #115e59;
336
- --border: rgba(31, 41, 55, 0.12);
337
- --shadow: 0 24px 60px rgba(15, 23, 42, 0.10);
338
- font-family: 'Space Grotesk', 'Segoe UI', sans-serif;
339
- }
262
+ window.router = router;
340
263
 
341
- * {
342
- box-sizing: border-box;
343
- }
264
+ ${authMiddlewareSetup} registerRoutes(router);
265
+ router.start();
344
266
 
345
- body {
346
- margin: 0;
347
- min-height: 100vh;
348
- color: var(--text);
349
- background:
350
- radial-gradient(circle at top left, rgba(15, 118, 110, 0.18), transparent 32%),
351
- radial-gradient(circle at bottom right, rgba(217, 119, 6, 0.16), transparent 24%),
352
- linear-gradient(180deg, #f8f5ee 0%, #efe6d8 100%);
353
- }
267
+ initSidebar();
354
268
 
355
- body::before {
356
- content: '';
357
- position: fixed;
358
- inset: 0;
359
- background-image: linear-gradient(rgba(255, 255, 255, 0.18) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.18) 1px, transparent 1px);
360
- background-size: 28px 28px;
361
- pointer-events: none;
362
- opacity: 0.5;
363
- }
269
+ ${authChangeHandler} window.addEventListener('pageloaded', () => {
270
+ updateSidebarVisibility();
271
+ });
364
272
 
365
- #main-content {
366
- position: relative;
367
- z-index: 1;
368
- width: min(1100px, calc(100vw - 2rem));
369
- margin: 0 auto;
370
- padding: 4rem 0 5rem;
273
+ updateSidebarVisibility();
274
+ initDevTools();
371
275
  }
372
276
 
373
- .hero,
374
- .page-section {
375
- background: var(--surface);
376
- backdrop-filter: blur(14px);
377
- border: 1px solid var(--border);
378
- border-radius: 28px;
379
- box-shadow: var(--shadow);
380
- }
277
+ function initDevTools(): void {
278
+ if (!isLocalhost()) {
279
+ return;
280
+ }
381
281
 
382
- .hero {
383
- padding: 4rem;
282
+ Promise.all([
283
+ import('./dev/hmr.js'),
284
+ import('./dev/denc-tools.js')
285
+ ])
286
+ .then(() => {
287
+ window.__NATIVECORE_DEV__ = true;
288
+ })
289
+ .catch(() => {
290
+ // Dev tools not available.
291
+ });
384
292
  }
385
293
 
386
- .eyebrow {
387
- margin: 0 0 1rem;
388
- text-transform: uppercase;
389
- letter-spacing: 0.18em;
390
- color: var(--accent-strong);
391
- font-size: 0.82rem;
294
+ init();
295
+ `;
392
296
  }
393
297
 
394
- h1,
395
- h2,
396
- p {
397
- margin-top: 0;
398
- }
298
+ function homeControllerTemplate(config) {
299
+ const authenticatedHref = config.includeDashboard ? '/dashboard' : '/';
300
+ const unauthenticatedHref = config.includeAuth ? '/login' : authenticatedHref;
301
+ const unauthenticatedLabel = config.includeAuth
302
+ ? 'Sign In'
303
+ : config.includeDashboard
304
+ ? 'Open Dashboard'
305
+ : 'Get Started';
306
+
307
+ return `/**
308
+ * Home Controller
309
+ * Updates the primary landing CTA based on authentication status.
310
+ */
311
+ import auth from '../services/auth.service.js';
312
+
313
+ export async function homeController(): Promise<() => void> {
314
+ const getStartedBtn = document.getElementById('get-started-btn') as HTMLAnchorElement | null;
315
+
316
+ if (getStartedBtn) {
317
+ if (auth.isAuthenticated()) {
318
+ getStartedBtn.href = '${authenticatedHref}';
319
+ getStartedBtn.textContent = 'Go to Dashboard';
320
+ } else {
321
+ getStartedBtn.href = '${unauthenticatedHref}';
322
+ getStartedBtn.textContent = '${unauthenticatedLabel}';
323
+ }
324
+ }
399
325
 
400
- h1 {
401
- font-size: clamp(2.5rem, 6vw, 5rem);
402
- line-height: 0.95;
403
- margin-bottom: 1rem;
326
+ return () => {};
404
327
  }
405
-
406
- .lede,
407
- .page-section p {
408
- max-width: 42rem;
409
- font-size: 1.05rem;
410
- line-height: 1.7;
411
- color: var(--muted);
328
+ `;
412
329
  }
413
330
 
414
- .hero-actions {
415
- display: flex;
416
- flex-wrap: wrap;
417
- gap: 0.9rem;
418
- margin-top: 2rem;
419
- }
331
+ function homeViewTemplate(config) {
332
+ const secondaryAuth = config.includeAuth ? '<nc-a variant="hero-ghost" href="/login">Sign In</nc-a>' : '';
333
+ const secondaryDashboard = config.includeDashboard ? '<nc-a variant="hero-ghost" href="/dashboard">Dashboard</nc-a>' : '';
420
334
 
421
- button,
422
- a {
423
- display: inline-flex;
424
- align-items: center;
425
- justify-content: center;
426
- min-height: 2.9rem;
427
- padding: 0.75rem 1.1rem;
428
- border-radius: 999px;
429
- border: 1px solid transparent;
430
- text-decoration: none;
431
- font: inherit;
335
+ return `<section class="hero">
336
+ <div class="hero-inner">
337
+ <div class="hero-badge">TypeScript-first. Web Components. Dev tools included.</div>
338
+
339
+ <h1>${config.projectTitle}</h1>
340
+
341
+ <p class="hero-tagline">
342
+ This project was scaffolded with NativeCore and includes the full project structure,
343
+ dev tooling, HMR, mock API flow, stores, services, middleware, scripts, and component registries.
344
+ </p>
345
+
346
+ <div class="hero-actions">
347
+ <nc-a variant="hero-primary" href="${config.includeAuth ? '/login' : config.includeDashboard ? '/dashboard' : '/'}" id="get-started-btn">${config.includeAuth ? 'Sign In' : config.includeDashboard ? 'Open Dashboard' : 'Get Started'}</nc-a>
348
+ ${secondaryDashboard}
349
+ ${secondaryAuth}
350
+ </div>
351
+
352
+ <div class="hero-stats">
353
+ <div class="stat-item">
354
+ <span class="stat-number">Full</span>
355
+ <span class="stat-label">Project Template</span>
356
+ </div>
357
+ <div class="stat-item">
358
+ <span class="stat-number">Built-in</span>
359
+ <span class="stat-label">Dev Tools</span>
360
+ </div>
361
+ <div class="stat-item">
362
+ <span class="stat-number">Local</span>
363
+ <span class="stat-label">Mock API</span>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </section>
368
+ `;
432
369
  }
433
370
 
434
- button {
435
- background: var(--accent);
436
- color: #fff;
437
- cursor: pointer;
371
+ function loginViewTemplate() {
372
+ return `<div class="login-experience">
373
+ <div class="login-shell">
374
+ <section class="login-showcase" aria-label="Starter access overview">
375
+ <div class="login-showcase__eyebrow">Starter Auth Flow</div>
376
+ <h1 class="login-showcase__title">Sign in.</h1>
377
+ <p class="login-showcase__copy">
378
+ This starter includes a local mock authentication flow, protected routes, and dashboard handoff.
379
+ </p>
380
+
381
+ <div class="login-showcase__grid">
382
+ <article class="login-showcase__card">
383
+ <h2>What is included</h2>
384
+ <ul class="login-showcase__list">
385
+ <li>Protected route gating with dashboard handoff</li>
386
+ <li>Mock API-backed authentication for local development</li>
387
+ <li>Component-driven UI built from NativeCore primitives</li>
388
+ </ul>
389
+ </article>
390
+
391
+ <article class="login-showcase__card login-showcase__card--accent">
392
+ <h2>Starter defaults</h2>
393
+ <div class="login-showcase__metrics">
394
+ <div>
395
+ <strong>Demo email</strong>
396
+ <span>demo@example.com</span>
397
+ </div>
398
+ <div>
399
+ <strong>Demo password</strong>
400
+ <span>pa$$w0rd</span>
401
+ </div>
402
+ <div>
403
+ <strong>Target route</strong>
404
+ <span>/dashboard</span>
405
+ </div>
406
+ </div>
407
+ </article>
408
+ </div>
409
+ </section>
410
+
411
+ <section class="login-panel" aria-label="Sign in form">
412
+ <div class="login-panel__header">
413
+ <p class="login-panel__eyebrow">Starter Access</p>
414
+ <h2>Access the dashboard</h2>
415
+ <p>Use the local demo credentials below.</p>
416
+ </div>
417
+
418
+ <div class="login-demo-credentials" aria-label="Demo credentials">
419
+ <div class="login-demo-credentials__item">
420
+ <span>Demo email</span>
421
+ <strong>demo@example.com</strong>
422
+ </div>
423
+ <div class="login-demo-credentials__item">
424
+ <span>Demo password</span>
425
+ <strong>pa$$w0rd</strong>
426
+ </div>
427
+ </div>
428
+
429
+ <div id="login-error" class="login-alert alert alert-error" hidden aria-live="polite"></div>
430
+
431
+ <nc-form id="loginForm" class="login-form">
432
+ <nc-field class="login-field" label="Work Email" for="email" required>
433
+ <nc-input
434
+ id="email"
435
+ name="email"
436
+ type="email"
437
+ autocomplete="username email"
438
+ placeholder="demo@example.com"
439
+ value="demo@example.com"
440
+ required
441
+ ></nc-input>
442
+ </nc-field>
443
+
444
+ <nc-field class="login-field" label="Password" for="password" required>
445
+ <nc-input
446
+ id="password"
447
+ name="password"
448
+ type="password"
449
+ autocomplete="current-password"
450
+ placeholder="Enter your password"
451
+ value="pa$$w0rd"
452
+ required
453
+ minlength="8"
454
+ show-password-toggle
455
+ ></nc-input>
456
+ </nc-field>
457
+
458
+ <div class="login-form__utility">
459
+ <nc-checkbox id="rememberMe" name="rememberMe" label="Remember demo email" checked></nc-checkbox>
460
+ <a href="/" data-link class="login-form__utility-link">Return home</a>
461
+ </div>
462
+
463
+ <nc-button id="loginBtn" type="submit" variant="primary" size="lg" full-width>
464
+ Access Dashboard
465
+ </nc-button>
466
+ </nc-form>
467
+ </section>
468
+ </div>
469
+ </div>
470
+ `;
438
471
  }
439
472
 
440
- button:hover {
441
- background: var(--accent-strong);
442
- }
473
+ function controllersIndexTemplate(config) {
474
+ const lines = [
475
+ '/**',
476
+ ' * Controller Registry',
477
+ ' */',
478
+ '',
479
+ "export { homeController } from './home.controller.js';"
480
+ ];
443
481
 
444
- a {
445
- color: var(--text);
446
- background: rgba(255, 255, 255, 0.65);
447
- border-color: var(--border);
448
- }
482
+ if (config.includeAuth) {
483
+ lines.push("export { loginController } from './login.controller.js';");
484
+ }
449
485
 
450
- .page-section {
451
- padding: 2rem;
452
- }
486
+ if (config.includeDashboard) {
487
+ lines.push("export { dashboardController } from './dashboard.controller.js';");
488
+ }
453
489
 
454
- .auth-page {
455
- max-width: 34rem;
490
+ return `${lines.join('\n')}\n`;
456
491
  }
457
492
 
458
- .auth-form {
459
- display: grid;
460
- gap: 1rem;
493
+ async function copyTemplate(targetDir) {
494
+ await fs.cp(templateDir, targetDir, { recursive: true, force: true });
461
495
  }
462
496
 
463
- .auth-form label {
464
- display: grid;
465
- gap: 0.45rem;
466
- }
497
+ async function customizeProject(targetDir, config) {
498
+ await writeFile(path.join(targetDir, 'package.json'), packageJsonTemplate(config));
499
+ await writeFile(path.join(targetDir, 'nativecore.config.json'), nativecoreConfigTemplate(config));
500
+ await writeFile(path.join(targetDir, 'src/app.ts'), appTsTemplate(config));
501
+ await writeFile(path.join(targetDir, 'src/routes/routes.ts'), routesTemplate(config));
502
+ await writeFile(path.join(targetDir, 'src/controllers/index.ts'), controllersIndexTemplate(config));
503
+ await writeFile(path.join(targetDir, 'src/controllers/home.controller.ts'), homeControllerTemplate(config));
504
+ await writeFile(path.join(targetDir, 'src/views/public/home.html'), homeViewTemplate(config));
467
505
 
468
- .auth-form input {
469
- min-height: 3rem;
470
- border: 1px solid var(--border);
471
- border-radius: 16px;
472
- padding: 0.8rem 0.95rem;
473
- font: inherit;
474
- background: rgba(255, 255, 255, 0.85);
475
- }
506
+ if (config.includeAuth) {
507
+ await writeFile(path.join(targetDir, 'src/views/public/login.html'), loginViewTemplate());
508
+ } else {
509
+ await removeIfExists(path.join(targetDir, 'src/controllers/login.controller.ts'));
510
+ await removeIfExists(path.join(targetDir, 'src/views/public/login.html'));
511
+ }
476
512
 
477
- .dashboard-grid {
478
- display: grid;
479
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
480
- gap: 1rem;
481
- }
513
+ if (!config.includeDashboard) {
514
+ await removeIfExists(path.join(targetDir, 'src/controllers/dashboard.controller.ts'));
515
+ await removeIfExists(path.join(targetDir, 'src/views/protected/dashboard.html'));
516
+ }
482
517
 
483
- .dashboard-grid article {
484
- padding: 1.5rem;
485
- border-radius: 20px;
486
- background: var(--surface-strong);
487
- border: 1px solid var(--border);
488
- transform: translateY(8px);
489
- opacity: 0;
490
- transition: transform 160ms ease, opacity 160ms ease;
491
- }
518
+ await replaceInFile(path.join(targetDir, 'src/services/api.service.ts'), content => content.replace(" return 'https://api.nativecorejs.com';", " return '/api';"));
492
519
 
493
- .dashboard-grid article.is-ready {
494
- transform: translateY(0);
495
- opacity: 1;
496
- }
520
+ await replaceInFile(path.join(targetDir, 'src/components/core/app-header.ts'), content => content
521
+ .replace(/<a href="\/docs" data-link class="nanc-link">Docs<\/a>\s*/g, '')
522
+ .replace(/<a href="\/components" data-link class="nanc-link">Components<\/a>\s*/g, '')
523
+ .replace(/<a href="\/docs" data-link class="login-form__utility-link">Review the docs<\/a>/g, '<a href="/" data-link class="login-form__utility-link">Return home</a>'));
497
524
 
498
- @media (max-width: 720px) {
499
- #main-content {
500
- width: min(100vw - 1rem, 100%);
501
- padding-top: 1rem;
502
- }
525
+ await replaceInFile(path.join(targetDir, 'index.html'), content => content
526
+ .replaceAll('NativeCore | Modern Reactive JavaScript Framework', `${config.projectTitle} | Built with NativeCore`)
527
+ .replaceAll('NativeCore Framework', config.projectTitle)
528
+ .replaceAll('https://nativecorejs.com/', '/')
529
+ .replaceAll('https://nativecorejs.com', '/')
530
+ .replaceAll('@nativecorejs', '')
531
+ .replaceAll('A modern, lightweight reactive framework using vanilla JavaScript, Web Components, reactive signals, and zero dependencies.', `${config.projectTitle} built with NativeCore.`));
503
532
 
504
- .hero,
505
- .page-section {
506
- border-radius: 22px;
507
- padding: 1.4rem;
508
- }
509
- }
510
- `;
511
- }
533
+ await replaceInFile(path.join(targetDir, 'manifest.json'), content => content
534
+ .replace(/"name"\s*:\s*"[^"]+"/, `"name": "${config.projectTitle}"`)
535
+ .replace(/"short_name"\s*:\s*"[^"]+"/, `"short_name": "${config.projectTitle}"`));
512
536
 
513
- function nativecoreConfigTemplate(config) {
514
- return JSON.stringify({
515
- appName: config.projectTitle,
516
- packageManager: 'npm',
517
- useTypeScript: true,
518
- frameworkDependency: config.frameworkDependency,
519
- features: {
520
- authShell: config.includeAuth,
521
- dashboard: config.includeDashboard
522
- }
523
- }, null, 2) + '\n';
524
- }
537
+ await replaceInFile(path.join(targetDir, 'public/_headers'), content => content
538
+ .replace(/https:\/\/api\.nativecorejs\.com\s*/g, '')
539
+ .replace(/Access-Control-Allow-Origin: .*\n/g, ''));
525
540
 
526
- async function supportsLocalWorkspace() {
527
- try {
528
- await fs.access(path.join(process.cwd(), 'packages', 'nativecorejs', 'package.json'));
529
- return true;
530
- } catch {
531
- return false;
532
- }
541
+ await replaceInFile(path.join(targetDir, '.env.example'), content => content.replace('APP_NAME=MyApp', `APP_NAME=${config.projectTitle}`));
533
542
  }
534
543
 
535
544
  async function buildProject(config) {
@@ -542,37 +551,9 @@ async function buildProject(config) {
542
551
  if (error.code !== 'ENOENT') throw error;
543
552
  }
544
553
 
545
- const sourceExtension = 'ts';
546
-
547
554
  await ensureDir(targetDir);
548
- await ensureDir(path.join(targetDir, 'src/config'));
549
- await ensureDir(path.join(targetDir, 'src/controllers'));
550
- await ensureDir(path.join(targetDir, 'src/views/pages/public'));
551
- await ensureDir(path.join(targetDir, 'src/views/pages/protected'));
552
- await ensureDir(path.join(targetDir, 'src/styles'));
553
-
554
- await writeFile(path.join(targetDir, 'package.json'), packageJsonTemplate(config));
555
- await writeFile(path.join(targetDir, 'server.js'), serverTemplate());
556
- await writeFile(path.join(targetDir, 'index.html'), shellHtmlTemplate(config, 'index'));
557
- await writeFile(path.join(targetDir, 'nativecore.config.json'), nativecoreConfigTemplate(config));
558
- await writeFile(path.join(targetDir, `src/app.${sourceExtension}`), appEntryTemplate(config));
559
- await writeFile(path.join(targetDir, `src/config/routes.${sourceExtension}`), routesTemplate(config));
560
- await writeFile(path.join(targetDir, `src/controllers/home.controller.${sourceExtension}`), controllerTemplate('homeController', homeControllerBody(config), config));
561
- await writeFile(path.join(targetDir, 'src/views/pages/public/home.html'), publicViewTemplate(config));
562
- await writeFile(path.join(targetDir, 'src/styles/main.css'), stylesTemplate());
563
-
564
- await writeFile(path.join(targetDir, 'tsconfig.json'), tsconfigTemplate());
565
-
566
- if (config.includeAuth) {
567
- await writeFile(path.join(targetDir, 'app.html'), shellHtmlTemplate(config, 'app'));
568
- await writeFile(path.join(targetDir, `src/controllers/login.controller.${sourceExtension}`), controllerTemplate('loginController', loginControllerBody(), config));
569
- await writeFile(path.join(targetDir, 'src/views/pages/public/login.html'), loginViewTemplate());
570
- }
571
-
572
- if (config.includeDashboard) {
573
- await writeFile(path.join(targetDir, `src/controllers/dashboard.controller.${sourceExtension}`), controllerTemplate('dashboardController', dashboardControllerBody(), config));
574
- await writeFile(path.join(targetDir, 'src/views/pages/protected/dashboard.html'), dashboardViewTemplate());
575
- }
555
+ await copyTemplate(targetDir);
556
+ await customizeProject(targetDir, config);
576
557
 
577
558
  return targetDir;
578
559
  }
@@ -586,18 +567,11 @@ async function main() {
586
567
  const projectTitle = toTitleCase(projectName);
587
568
  const useDefaults = hasFlag('--defaults');
588
569
 
589
- const wantsLocalFramework = hasFlag('--local');
590
- const canUseLocalFramework = wantsLocalFramework ? await supportsLocalWorkspace() : false;
591
-
592
- if (wantsLocalFramework && !canUseLocalFramework) {
593
- throw new Error('--local can only be used from the nativecorejs monorepo root where ./packages/nativecorejs exists.');
594
- }
595
-
596
570
  const includeAuth = hasFlag('--no-auth')
597
571
  ? false
598
572
  : useDefaults
599
573
  ? true
600
- : await askYesNo('Include auth shell?', true);
574
+ : await askYesNo('Include auth flow?', true);
601
575
  const includeDashboard = hasFlag('--no-dashboard')
602
576
  ? false
603
577
  : useDefaults
@@ -612,11 +586,8 @@ async function main() {
612
586
  const config = {
613
587
  projectName,
614
588
  projectTitle,
615
- useTypeScript: true,
616
- frameworkDependency: wantsLocalFramework ? 'file:../packages/nativecorejs' : '^0.1.0',
617
589
  includeAuth,
618
590
  includeDashboard,
619
- packageManager: 'npm',
620
591
  shouldInstall
621
592
  };
622
593
 
@@ -651,7 +622,7 @@ async function main() {
651
622
  }
652
623
 
653
624
  console.log('npm run dev');
654
- console.log('\nThis starter expects nativecorejs to provide prebuilt dist files and the base stylesheet from node_modules/nativecorejs.');
625
+ console.log('\nThis scaffold now ships a full NativeCore-style project structure with scripts, dev tools, HMR, services, stores, middleware, mock API, and source folders included.');
655
626
 
656
627
  rl.close();
657
628
  }
@@ -661,4 +632,4 @@ main().catch(error => {
661
632
  console.error(error.message);
662
633
  rl.close();
663
634
  process.exit(1);
664
- });
635
+ });