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.
- package/README.md +6 -14
- package/bin/index.mjs +403 -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 +653 -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,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
|
-
|
|
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 primaryHref = config.includeAuth ? '/login' : config.includeDashboard ? '/dashboard' : '/';
|
|
333
|
+
const primaryLabel = config.includeAuth ? 'Sign In' : config.includeDashboard ? 'Open Dashboard' : 'Get Started';
|
|
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>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
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
441
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
border-color: var(--border);
|
|
448
|
-
}
|
|
483
|
+
if (config.includeAuth) {
|
|
484
|
+
lines.push("export { loginController } from './login.controller.js';");
|
|
485
|
+
}
|
|
449
486
|
|
|
450
|
-
.
|
|
451
|
-
|
|
452
|
-
}
|
|
487
|
+
if (config.includeDashboard) {
|
|
488
|
+
lines.push("export { dashboardController } from './dashboard.controller.js';");
|
|
489
|
+
}
|
|
453
490
|
|
|
454
|
-
|
|
455
|
-
max-width: 34rem;
|
|
491
|
+
return `${lines.join('\n')}\n`;
|
|
456
492
|
}
|
|
457
493
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
gap: 1rem;
|
|
494
|
+
async function copyTemplate(targetDir) {
|
|
495
|
+
await fs.cp(templateDir, targetDir, { recursive: true, force: true });
|
|
461
496
|
}
|
|
462
497
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
.
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
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
|
-
}
|
|
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
|
|
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
|
|
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
|
+
});
|