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.
- package/README.md +6 -14
- package/bin/index.mjs +402 -431
- package/package.json +3 -2
- package/template/.env.example +28 -0
- package/template/.htmlhintrc +14 -0
- package/template/api/data/dashboard.json +11 -0
- package/template/api/data/users.json +18 -0
- package/template/api/mockApi.js +161 -0
- package/template/assets/icon.svg +13 -0
- package/template/assets/logo.svg +25 -0
- package/template/eslint.config.js +94 -0
- package/template/index.html +137 -0
- package/template/manifest.json +19 -0
- package/template/public/.well-known/security.txt +9 -0
- package/template/public/_headers +24 -0
- package/template/public/_redirects +14 -0
- package/template/public/assets/icon.svg +13 -0
- package/template/public/assets/logo.svg +25 -0
- package/template/public/manifest.json +19 -0
- package/template/public/robots.txt +13 -0
- package/template/public/sitemap.xml +27 -0
- package/template/scripts/build-for-bots.mjs +121 -0
- package/template/scripts/convert-to-ts.mjs +106 -0
- package/template/scripts/fix-encoding.mjs +38 -0
- package/template/scripts/fix-svg-paths.mjs +32 -0
- package/template/scripts/generate-cf-router.mjs +52 -0
- package/template/scripts/inject-dev-tools.mjs +41 -0
- package/template/scripts/inject-version.mjs +65 -0
- package/template/scripts/make-component.mjs +445 -0
- package/template/scripts/make-component.mjs.backup +432 -0
- package/template/scripts/make-controller.mjs +119 -0
- package/template/scripts/make-core-component.mjs +303 -0
- package/template/scripts/make-view.mjs +346 -0
- package/template/scripts/minify.mjs +71 -0
- package/template/scripts/prepare-static-assets.mjs +141 -0
- package/template/scripts/prompt-bot-build.mjs +223 -0
- package/template/scripts/remove-component.mjs +170 -0
- package/template/scripts/remove-core-component.mjs +156 -0
- package/template/scripts/remove-dev.mjs +13 -0
- package/template/scripts/remove-view.mjs +200 -0
- package/template/scripts/strip-dev-blocks.mjs +30 -0
- package/template/scripts/watch-compile.mjs +69 -0
- package/template/server.js +1066 -0
- package/template/src/app.ts +115 -0
- package/template/src/components/appRegistry.ts +8 -0
- package/template/src/components/core/app-footer.ts +27 -0
- package/template/src/components/core/app-header.ts +175 -0
- package/template/src/components/core/app-sidebar.ts +238 -0
- package/template/src/components/core/loading-spinner.ts +25 -0
- package/template/src/components/core/nc-a.ts +313 -0
- package/template/src/components/core/nc-accordion.ts +186 -0
- package/template/src/components/core/nc-alert.ts +153 -0
- package/template/src/components/core/nc-animation.ts +1150 -0
- package/template/src/components/core/nc-autocomplete.ts +271 -0
- package/template/src/components/core/nc-avatar-group.ts +113 -0
- package/template/src/components/core/nc-avatar.ts +148 -0
- package/template/src/components/core/nc-badge.ts +86 -0
- package/template/src/components/core/nc-bottom-nav.ts +214 -0
- package/template/src/components/core/nc-breadcrumb.ts +96 -0
- package/template/src/components/core/nc-button.ts +307 -0
- package/template/src/components/core/nc-card.ts +160 -0
- package/template/src/components/core/nc-checkbox.ts +282 -0
- package/template/src/components/core/nc-chip.ts +115 -0
- package/template/src/components/core/nc-code.ts +314 -0
- package/template/src/components/core/nc-collapsible.ts +154 -0
- package/template/src/components/core/nc-color-picker.ts +268 -0
- package/template/src/components/core/nc-copy-button.ts +119 -0
- package/template/src/components/core/nc-date-picker.ts +443 -0
- package/template/src/components/core/nc-div.ts +280 -0
- package/template/src/components/core/nc-divider.ts +81 -0
- package/template/src/components/core/nc-drawer.ts +230 -0
- package/template/src/components/core/nc-dropdown.ts +178 -0
- package/template/src/components/core/nc-empty-state.ts +134 -0
- package/template/src/components/core/nc-file-upload.ts +354 -0
- package/template/src/components/core/nc-form.ts +312 -0
- package/template/src/components/core/nc-image.ts +184 -0
- package/template/src/components/core/nc-input.ts +383 -0
- package/template/src/components/core/nc-kbd.ts +48 -0
- package/template/src/components/core/nc-menu-item.ts +193 -0
- package/template/src/components/core/nc-menu.ts +376 -0
- package/template/src/components/core/nc-modal.ts +238 -0
- package/template/src/components/core/nc-nav-item.ts +151 -0
- package/template/src/components/core/nc-number-input.ts +350 -0
- package/template/src/components/core/nc-otp-input.ts +235 -0
- package/template/src/components/core/nc-pagination.ts +178 -0
- package/template/src/components/core/nc-popover.ts +260 -0
- package/template/src/components/core/nc-progress-circular.ts +119 -0
- package/template/src/components/core/nc-progress.ts +134 -0
- package/template/src/components/core/nc-radio.ts +235 -0
- package/template/src/components/core/nc-rating.ts +266 -0
- package/template/src/components/core/nc-rich-text.ts +283 -0
- package/template/src/components/core/nc-scroll-top.ts +116 -0
- package/template/src/components/core/nc-select.ts +452 -0
- package/template/src/components/core/nc-skeleton.ts +107 -0
- package/template/src/components/core/nc-slider.ts +285 -0
- package/template/src/components/core/nc-snackbar.ts +230 -0
- package/template/src/components/core/nc-splash.ts +343 -0
- package/template/src/components/core/nc-stepper.ts +247 -0
- package/template/src/components/core/nc-switch.ts +281 -0
- package/template/src/components/core/nc-tab-item.ts +138 -0
- package/template/src/components/core/nc-table.ts +279 -0
- package/template/src/components/core/nc-tabs.ts +554 -0
- package/template/src/components/core/nc-tag-input.ts +279 -0
- package/template/src/components/core/nc-textarea.ts +216 -0
- package/template/src/components/core/nc-time-picker.ts +438 -0
- package/template/src/components/core/nc-timeline.ts +186 -0
- package/template/src/components/core/nc-tooltip.ts +143 -0
- package/template/src/components/frameworkRegistry.ts +68 -0
- package/template/src/components/preloadRegistry.ts +28 -0
- package/template/src/components/registry.ts +8 -0
- package/template/src/components/ui/dashboard-signal-lab.ts +284 -0
- package/template/src/constants/apiEndpoints.ts +27 -0
- package/template/src/constants/errorMessages.ts +23 -0
- package/template/src/constants/index.ts +8 -0
- package/template/src/constants/routePaths.ts +15 -0
- package/template/src/constants/storageKeys.ts +18 -0
- package/template/src/controllers/dashboard.controller.ts +200 -0
- package/template/src/controllers/home.controller.ts +21 -0
- package/template/src/controllers/index.ts +11 -0
- package/template/src/controllers/login.controller.ts +131 -0
- package/template/src/core/component.ts +354 -0
- package/template/src/core/errorHandler.ts +85 -0
- package/template/src/core/gpu-animation.ts +604 -0
- package/template/src/core/http.ts +173 -0
- package/template/src/core/lazyComponents.ts +90 -0
- package/template/src/core/router.ts +642 -0
- package/template/src/core/signals.ts +146 -0
- package/template/src/core/state.ts +248 -0
- package/template/src/dev/component-editor.ts +1363 -0
- package/template/src/dev/component-overlay.ts +278 -0
- package/template/src/dev/context-menu.ts +223 -0
- package/template/src/dev/denc-tools.ts +250 -0
- package/template/src/dev/hmr.ts +189 -0
- package/template/src/dev/nfbs.code-workspace +27 -0
- package/template/src/dev/outline-panel.ts +1247 -0
- package/template/src/middleware/auth.middleware.ts +23 -0
- package/template/src/routes/routes.ts +38 -0
- package/template/src/services/api.service.ts +394 -0
- package/template/src/services/auth.service.ts +176 -0
- package/template/src/services/index.ts +8 -0
- package/template/src/services/logger.service.ts +74 -0
- package/template/src/services/storage.service.ts +88 -0
- package/template/src/stores/appStore.ts +57 -0
- package/template/src/stores/uiStore.ts +36 -0
- package/template/src/styles/core-variables.css +219 -0
- package/template/src/styles/core.css +710 -0
- package/template/src/styles/main.css +3164 -0
- package/template/src/styles/variables.css +152 -0
- package/template/src/types/global.d.ts +47 -0
- package/template/src/utils/cacheBuster.ts +20 -0
- package/template/src/utils/dom.ts +149 -0
- package/template/src/utils/events.ts +203 -0
- package/template/src/utils/form.ts +176 -0
- package/template/src/utils/formatters.ts +169 -0
- package/template/src/utils/helpers.ts +195 -0
- package/template/src/utils/markdown.ts +307 -0
- package/template/src/utils/sidebar.ts +96 -0
- package/template/src/utils/smoothScroll.ts +85 -0
- package/template/src/utils/templates.ts +23 -0
- package/template/src/utils/validation.ts +73 -0
- package/template/src/views/protected/dashboard.html +293 -0
- package/template/src/views/public/home.html +150 -0
- package/template/src/views/public/login.html +102 -0
- package/template/tests/unit/component.test.ts +87 -0
- package/template/tests/unit/computed.test.ts +79 -0
- package/template/tests/unit/form.test.ts +68 -0
- package/template/tests/unit/formatters.test.ts +49 -0
- package/template/tests/unit/lazy-components.test.ts +59 -0
- package/template/tests/unit/markdown.test.ts +62 -0
- package/template/tests/unit/router.test.ts +112 -0
- package/template/tests/unit/signals.test.ts +54 -0
- package/template/tests/unit/validation.test.ts +50 -0
- package/template/tsconfig.build.json +21 -0
- package/template/tsconfig.json +51 -0
- package/template/vitest.config.ts +36 -0
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
|
|
55
|
-
|
|
56
|
-
|
|
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(
|
|
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
|
-
|
|
91
|
+
description: `${config.projectTitle} built with NativeCore`,
|
|
93
92
|
type: 'module',
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
100
|
-
'@types/node': '^
|
|
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
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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/
|
|
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/
|
|
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
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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/
|
|
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
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
203
|
+
auth.setUser(response.user);
|
|
204
|
+
} catch {
|
|
205
|
+
auth.logout();
|
|
206
|
+
}
|
|
273
207
|
}
|
|
274
208
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
343
|
-
}
|
|
264
|
+
${authMiddlewareSetup} registerRoutes(router);
|
|
265
|
+
router.start();
|
|
344
266
|
|
|
345
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
-
.
|
|
383
|
-
|
|
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
|
-
|
|
387
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
441
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
border-color: var(--border);
|
|
448
|
-
}
|
|
482
|
+
if (config.includeAuth) {
|
|
483
|
+
lines.push("export { loginController } from './login.controller.js';");
|
|
484
|
+
}
|
|
449
485
|
|
|
450
|
-
.
|
|
451
|
-
|
|
452
|
-
}
|
|
486
|
+
if (config.includeDashboard) {
|
|
487
|
+
lines.push("export { dashboardController } from './dashboard.controller.js';");
|
|
488
|
+
}
|
|
453
489
|
|
|
454
|
-
|
|
455
|
-
max-width: 34rem;
|
|
490
|
+
return `${lines.join('\n')}\n`;
|
|
456
491
|
}
|
|
457
492
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
gap: 1rem;
|
|
493
|
+
async function copyTemplate(targetDir) {
|
|
494
|
+
await fs.cp(templateDir, targetDir, { recursive: true, force: true });
|
|
461
495
|
}
|
|
462
496
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
.
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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
|
-
|
|
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
|
|
549
|
-
await
|
|
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
|
|
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
|
|
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
|
+
});
|