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
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,462 @@ 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 primaryHref = config.includeAuth ? '/login' : config.includeDashboard ? '/dashboard' : '/';
333
+ const primaryLabel = config.includeAuth ? 'Sign In' : config.includeDashboard ? 'Open Dashboard' : 'Get Started';
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>NativeCoreJS</h1>
340
+
341
+ <p class="hero-tagline">
342
+ Build modern applications with a browser-native architecture that leans on web standards,
343
+ not proprietary runtimes. NativeCoreJS keeps you close to the platform with Web Components,
344
+ TypeScript, reactive state, and high-performance patterns that stay durable as the web evolves.
345
+ </p>
346
+
347
+ <div class="hero-actions">
348
+ <nc-a variant="hero-primary" href="${primaryHref}" id="get-started-btn">${primaryLabel}</nc-a>
349
+ <nc-a variant="hero-ghost" href="https://nativecorejs.com/docs" target="_blank" rel="noopener noreferrer">Read the Docs</nc-a>
350
+ <nc-a variant="hero-ghost" href="https://nativecorejs.com/components" target="_blank" rel="noopener noreferrer">Component Library</nc-a>
351
+ </div>
352
+
353
+ <div class="hero-stats">
354
+ <div class="stat-item">
355
+ <span class="stat-number">Full</span>
356
+ <span class="stat-label">Project Template</span>
357
+ </div>
358
+ <div class="stat-item">
359
+ <span class="stat-number">Built-in</span>
360
+ <span class="stat-label">Dev Tools</span>
361
+ </div>
362
+ <div class="stat-item">
363
+ <span class="stat-number">Local</span>
364
+ <span class="stat-label">Mock API</span>
365
+ </div>
366
+ </div>
367
+ </div>
368
+ </section>
369
+ `;
432
370
  }
433
371
 
434
- button {
435
- background: var(--accent);
436
- color: #fff;
437
- cursor: pointer;
372
+ function loginViewTemplate() {
373
+ return `<div class="login-experience">
374
+ <div class="login-shell">
375
+ <section class="login-showcase" aria-label="Starter access overview">
376
+ <div class="login-showcase__eyebrow">Starter Auth Flow</div>
377
+ <h1 class="login-showcase__title">Sign in.</h1>
378
+ <p class="login-showcase__copy">
379
+ This starter includes a local mock authentication flow, protected routes, and dashboard handoff.
380
+ </p>
381
+
382
+ <div class="login-showcase__grid">
383
+ <article class="login-showcase__card">
384
+ <h2>What is included</h2>
385
+ <ul class="login-showcase__list">
386
+ <li>Protected route gating with dashboard handoff</li>
387
+ <li>Mock API-backed authentication for local development</li>
388
+ <li>Component-driven UI built from NativeCore primitives</li>
389
+ </ul>
390
+ </article>
391
+
392
+ <article class="login-showcase__card login-showcase__card--accent">
393
+ <h2>Starter defaults</h2>
394
+ <div class="login-showcase__metrics">
395
+ <div>
396
+ <strong>Demo email</strong>
397
+ <span>demo@example.com</span>
398
+ </div>
399
+ <div>
400
+ <strong>Demo password</strong>
401
+ <span>pa$$w0rd</span>
402
+ </div>
403
+ <div>
404
+ <strong>Target route</strong>
405
+ <span>/dashboard</span>
406
+ </div>
407
+ </div>
408
+ </article>
409
+ </div>
410
+ </section>
411
+
412
+ <section class="login-panel" aria-label="Sign in form">
413
+ <div class="login-panel__header">
414
+ <p class="login-panel__eyebrow">Starter Access</p>
415
+ <h2>Access the dashboard</h2>
416
+ <p>Use the local demo credentials below.</p>
417
+ </div>
418
+
419
+ <div class="login-demo-credentials" aria-label="Demo credentials">
420
+ <div class="login-demo-credentials__item">
421
+ <span>Demo email</span>
422
+ <strong>demo@example.com</strong>
423
+ </div>
424
+ <div class="login-demo-credentials__item">
425
+ <span>Demo password</span>
426
+ <strong>pa$$w0rd</strong>
427
+ </div>
428
+ </div>
429
+
430
+ <div id="login-error" class="login-alert alert alert-error" hidden aria-live="polite"></div>
431
+
432
+ <nc-form id="loginForm" class="login-form">
433
+ <nc-field class="login-field" label="Work Email" for="email" required>
434
+ <nc-input
435
+ id="email"
436
+ name="email"
437
+ type="email"
438
+ autocomplete="username email"
439
+ placeholder="demo@example.com"
440
+ value="demo@example.com"
441
+ required
442
+ ></nc-input>
443
+ </nc-field>
444
+
445
+ <nc-field class="login-field" label="Password" for="password" required>
446
+ <nc-input
447
+ id="password"
448
+ name="password"
449
+ type="password"
450
+ autocomplete="current-password"
451
+ placeholder="Enter your password"
452
+ value="pa$$w0rd"
453
+ required
454
+ minlength="8"
455
+ show-password-toggle
456
+ ></nc-input>
457
+ </nc-field>
458
+
459
+ <div class="login-form__utility">
460
+ <nc-checkbox id="rememberMe" name="rememberMe" label="Remember demo email" checked></nc-checkbox>
461
+ <a href="/" data-link class="login-form__utility-link">Return home</a>
462
+ </div>
463
+
464
+ <nc-button id="loginBtn" type="submit" variant="primary" size="lg" full-width>
465
+ Access Dashboard
466
+ </nc-button>
467
+ </nc-form>
468
+ </section>
469
+ </div>
470
+ </div>
471
+ `;
438
472
  }
439
473
 
440
- button:hover {
441
- background: var(--accent-strong);
442
- }
474
+ function controllersIndexTemplate(config) {
475
+ const lines = [
476
+ '/**',
477
+ ' * Controller Registry',
478
+ ' */',
479
+ '',
480
+ "export { homeController } from './home.controller.js';"
481
+ ];
443
482
 
444
- a {
445
- color: var(--text);
446
- background: rgba(255, 255, 255, 0.65);
447
- border-color: var(--border);
448
- }
483
+ if (config.includeAuth) {
484
+ lines.push("export { loginController } from './login.controller.js';");
485
+ }
449
486
 
450
- .page-section {
451
- padding: 2rem;
452
- }
487
+ if (config.includeDashboard) {
488
+ lines.push("export { dashboardController } from './dashboard.controller.js';");
489
+ }
453
490
 
454
- .auth-page {
455
- max-width: 34rem;
491
+ return `${lines.join('\n')}\n`;
456
492
  }
457
493
 
458
- .auth-form {
459
- display: grid;
460
- gap: 1rem;
494
+ async function copyTemplate(targetDir) {
495
+ await fs.cp(templateDir, targetDir, { recursive: true, force: true });
461
496
  }
462
497
 
463
- .auth-form label {
464
- display: grid;
465
- gap: 0.45rem;
466
- }
498
+ async function customizeProject(targetDir, config) {
499
+ await writeFile(path.join(targetDir, 'package.json'), packageJsonTemplate(config));
500
+ await writeFile(path.join(targetDir, 'nativecore.config.json'), nativecoreConfigTemplate(config));
501
+ await writeFile(path.join(targetDir, 'src/app.ts'), appTsTemplate(config));
502
+ await writeFile(path.join(targetDir, 'src/routes/routes.ts'), routesTemplate(config));
503
+ await writeFile(path.join(targetDir, 'src/controllers/index.ts'), controllersIndexTemplate(config));
504
+ await writeFile(path.join(targetDir, 'src/controllers/home.controller.ts'), homeControllerTemplate(config));
505
+ await writeFile(path.join(targetDir, 'src/views/public/home.html'), homeViewTemplate(config));
467
506
 
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
- }
507
+ if (config.includeAuth) {
508
+ await writeFile(path.join(targetDir, 'src/views/public/login.html'), loginViewTemplate());
509
+ } else {
510
+ await removeIfExists(path.join(targetDir, 'src/controllers/login.controller.ts'));
511
+ await removeIfExists(path.join(targetDir, 'src/views/public/login.html'));
512
+ }
476
513
 
477
- .dashboard-grid {
478
- display: grid;
479
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
480
- gap: 1rem;
481
- }
514
+ if (!config.includeDashboard) {
515
+ await removeIfExists(path.join(targetDir, 'src/controllers/dashboard.controller.ts'));
516
+ await removeIfExists(path.join(targetDir, 'src/views/protected/dashboard.html'));
517
+ }
482
518
 
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
- }
519
+ await replaceInFile(path.join(targetDir, 'src/services/api.service.ts'), content => content.replace(" return 'https://api.nativecorejs.com';", " return '/api';"));
492
520
 
493
- .dashboard-grid article.is-ready {
494
- transform: translateY(0);
495
- opacity: 1;
496
- }
521
+ await replaceInFile(path.join(targetDir, 'src/components/core/app-header.ts'), content => content
522
+ .replace(/<a href="\/docs" data-link class="nanc-link">Docs<\/a>\s*/g, '')
523
+ .replace(/<a href="\/components" data-link class="nanc-link">Components<\/a>\s*/g, '')
524
+ .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
525
 
498
- @media (max-width: 720px) {
499
- #main-content {
500
- width: min(100vw - 1rem, 100%);
501
- padding-top: 1rem;
502
- }
526
+ await replaceInFile(path.join(targetDir, 'index.html'), content => content
527
+ .replaceAll('NativeCore | Modern Reactive JavaScript Framework', 'NativeCoreJS | Built with NativeCore')
528
+ .replaceAll('NativeCore Framework', 'NativeCoreJS')
529
+ .replaceAll('https://nativecorejs.com/', '/')
530
+ .replaceAll('https://nativecorejs.com', '/')
531
+ .replaceAll('@nativecorejs', '')
532
+ .replaceAll('A modern, lightweight reactive framework using vanilla JavaScript, Web Components, reactive signals, and zero dependencies.', 'A NativeCoreJS starter focused on web standards, browser-native architecture, and durable performance.'));
503
533
 
504
- .hero,
505
- .page-section {
506
- border-radius: 22px;
507
- padding: 1.4rem;
508
- }
509
- }
510
- `;
511
- }
534
+ await replaceInFile(path.join(targetDir, 'manifest.json'), content => content
535
+ .replace(/"name"\s*:\s*"[^"]+"/, '"name": "NativeCoreJS"')
536
+ .replace(/"short_name"\s*:\s*"[^"]+"/, '"short_name": "NativeCoreJS"'));
512
537
 
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
- }
538
+ await replaceInFile(path.join(targetDir, 'public/_headers'), content => content
539
+ .replace(/https:\/\/api\.nativecorejs\.com\s*/g, '')
540
+ .replace(/Access-Control-Allow-Origin: .*\n/g, ''));
525
541
 
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
- }
542
+ await replaceInFile(path.join(targetDir, '.env.example'), content => content.replace('APP_NAME=MyApp', `APP_NAME=${config.projectTitle}`));
533
543
  }
534
544
 
535
545
  async function buildProject(config) {
@@ -542,37 +552,9 @@ async function buildProject(config) {
542
552
  if (error.code !== 'ENOENT') throw error;
543
553
  }
544
554
 
545
- const sourceExtension = 'ts';
546
-
547
555
  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
- }
556
+ await copyTemplate(targetDir);
557
+ await customizeProject(targetDir, config);
576
558
 
577
559
  return targetDir;
578
560
  }
@@ -586,18 +568,11 @@ async function main() {
586
568
  const projectTitle = toTitleCase(projectName);
587
569
  const useDefaults = hasFlag('--defaults');
588
570
 
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
571
  const includeAuth = hasFlag('--no-auth')
597
572
  ? false
598
573
  : useDefaults
599
574
  ? true
600
- : await askYesNo('Include auth shell?', true);
575
+ : await askYesNo('Include auth flow?', true);
601
576
  const includeDashboard = hasFlag('--no-dashboard')
602
577
  ? false
603
578
  : useDefaults
@@ -612,11 +587,8 @@ async function main() {
612
587
  const config = {
613
588
  projectName,
614
589
  projectTitle,
615
- useTypeScript: true,
616
- frameworkDependency: wantsLocalFramework ? 'file:../packages/nativecorejs' : '^0.1.0',
617
590
  includeAuth,
618
591
  includeDashboard,
619
- packageManager: 'npm',
620
592
  shouldInstall
621
593
  };
622
594
 
@@ -651,7 +623,7 @@ async function main() {
651
623
  }
652
624
 
653
625
  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.');
626
+ 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
627
 
656
628
  rl.close();
657
629
  }
@@ -661,4 +633,4 @@ main().catch(error => {
661
633
  console.error(error.message);
662
634
  rl.close();
663
635
  process.exit(1);
664
- });
636
+ });