jskelet 0.6.3 → 0.6.4
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/AGENTS.md +136 -136
- package/CHANGELOG.md +628 -620
- package/LICENSE +21 -21
- package/README.md +2 -0
- package/bin/jskelet.mjs +130 -130
- package/docs/01-baslangic.md +291 -291
- package/docs/02-mimari.md +310 -310
- package/docs/03-routing.md +515 -515
- package/docs/04-render-ve-sablonlar.md +667 -661
- package/docs/05-islands.md +486 -486
- package/docs/06-cache.md +1467 -1443
- package/docs/07-yapilandirma.md +1208 -1197
- package/docs/08-build.md +429 -429
- package/docs/09-dev-araclari.md +364 -364
- package/docs/10-dagitim.md +348 -338
- package/docs/12-panel-ve-oturum.md +479 -478
- package/docs/README.md +83 -83
- package/docs/en/01-getting-started.md +298 -298
- package/docs/en/02-architecture.md +329 -329
- package/docs/en/03-routing.md +531 -531
- package/docs/en/04-rendering.md +675 -669
- package/docs/en/05-islands.md +497 -497
- package/docs/en/06-caching.md +1476 -1453
- package/docs/en/07-configuration.md +1229 -1219
- package/docs/en/08-build.md +447 -447
- package/docs/en/09-dev-tools.md +373 -373
- package/docs/en/10-deployment.md +351 -340
- package/docs/en/11-migration.md +398 -398
- package/docs/en/12-dashboards-and-sessions.md +489 -488
- package/docs/en/README.md +87 -87
- package/package.json +137 -137
- package/src/build/ensure-build.mjs +19 -19
- package/src/build/paths.mjs +153 -153
- package/src/build/resolve-peer.mjs +36 -36
- package/src/build/tasks/client.mjs +349 -349
- package/src/build/tasks/css.mjs +235 -235
- package/src/build/tasks/fonts.mjs +146 -146
- package/src/build/tasks/icons.mjs +357 -357
- package/src/build/tasks/images.mjs +244 -244
- package/src/build/tasks/precompress.mjs +78 -78
- package/src/build/tasks/templates.mjs +20 -20
- package/src/client/admin/i18n.js +764 -764
- package/src/client/admin/login.html +74 -74
- package/src/client/admin/panel.css +809 -809
- package/src/client/admin/panel.html +495 -495
- package/src/client/admin/panel.js +1251 -1251
- package/src/client/devtools/report.html +185 -185
- package/src/client/devtools/report.js +745 -745
- package/src/client/devtools/seo.js +628 -628
- package/src/client/dom.js +95 -95
- package/src/client/form.js +192 -192
- package/src/client/index.js +45 -45
- package/src/client/registry.js +305 -305
- package/src/client/safe-image.js +91 -91
- package/src/client/shared-cookie.js +225 -225
- package/src/client/store.js +36 -36
- package/src/client/swap.js +188 -188
- package/src/compile/codegen.js +336 -336
- package/src/compile/compile-all.js +149 -149
- package/src/compile/errors.js +66 -66
- package/src/compile/expr.js +409 -409
- package/src/compile/index.js +17 -17
- package/src/compile/parse.js +541 -541
- package/src/compile/resolve.js +211 -211
- package/src/compile/scan-exports.js +51 -51
- package/src/config/defaults.js +541 -534
- package/src/config/index.js +1500 -1469
- package/src/config/pattern.js +107 -107
- package/src/generate.mjs +163 -163
- package/src/http/control-flow.js +71 -71
- package/src/http/cookies-entry.js +21 -21
- package/src/http/cookies.js +277 -277
- package/src/http/request-cache.js +46 -46
- package/src/http/request-context.js +165 -165
- package/src/http/shared-cookie.js +178 -178
- package/src/index.js +101 -101
- package/src/init.mjs +232 -230
- package/src/migrate/apply.mjs +262 -262
- package/src/migrate/babel.mjs +79 -79
- package/src/migrate/classify.mjs +155 -155
- package/src/migrate/config.mjs +126 -126
- package/src/migrate/fs-walk.mjs +191 -191
- package/src/migrate/parse.mjs +26 -26
- package/src/migrate/scan.mjs +177 -177
- package/src/migrate/transform/expr-source.mjs +168 -168
- package/src/migrate/transform/island.mjs +67 -67
- package/src/migrate/transform/jsx-to-component.mjs +302 -302
- package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
- package/src/migrate/transform/page-split.mjs +435 -435
- package/src/migrate/write.mjs +81 -81
- package/src/migrate.mjs +171 -171
- package/src/runtime/alias-hooks.mjs +119 -119
- package/src/runtime/register.mjs +4 -4
- package/src/server/admin/actions.js +229 -229
- package/src/server/admin/auth.js +125 -125
- package/src/server/admin/event-log.js +151 -151
- package/src/server/admin/gate.js +209 -209
- package/src/server/admin/inventory.js +188 -188
- package/src/server/admin/mount.js +56 -56
- package/src/server/admin/router.js +216 -216
- package/src/server/admin/snapshot.js +241 -241
- package/src/server/assets.js +147 -147
- package/src/server/auth/handoff.js +309 -309
- package/src/server/cache-blob.js +70 -70
- package/src/server/cache-control.js +45 -0
- package/src/server/cache-deps.js +42 -42
- package/src/server/cache-vary.js +113 -113
- package/src/server/cloudflare.js +607 -607
- package/src/server/create-app.js +366 -366
- package/src/server/data-cache.js +553 -553
- package/src/server/dev/report.js +485 -485
- package/src/server/dev/socket.js +170 -170
- package/src/server/dev/version-check.mjs +139 -139
- package/src/server/disk-cache.js +233 -233
- package/src/server/ejs-adapter.js +59 -59
- package/src/server/html-cache.js +1196 -1196
- package/src/server/image-optimizer.js +500 -500
- package/src/server/logs/access-middleware.js +66 -66
- package/src/server/logs/file-sink.js +193 -193
- package/src/server/logs/pipeline.js +165 -165
- package/src/server/logs/s3-put.js +214 -214
- package/src/server/logs/s3-sink.js +112 -112
- package/src/server/metadata.js +102 -102
- package/src/server/middleware/compression.js +205 -205
- package/src/server/middleware/csrf.js +134 -134
- package/src/server/middleware/dev-gate.js +75 -75
- package/src/server/middleware/headers.js +37 -37
- package/src/server/middleware/redirects.js +32 -32
- package/src/server/middleware/robots-txt.js +341 -341
- package/src/server/middleware/static-precompressed.js +121 -121
- package/src/server/middleware/trailing-slash.js +53 -53
- package/src/server/middleware/upstream-proxy.js +141 -141
- package/src/server/og-image.js +369 -356
- package/src/server/port-guard.js +255 -255
- package/src/server/prewarm.js +1082 -1082
- package/src/server/redis.js +588 -588
- package/src/server/render.js +910 -910
- package/src/server/router.js +157 -157
- package/src/server/status-page.js +265 -265
- package/src/server/upstream-limiter.js +376 -376
- package/src/server/upstream-tracking.js +166 -166
- package/src/shared/cookie-domain.js +66 -66
- package/src/start.mjs +22 -22
- package/src/templates/layout.ejs +30 -30
- package/src/templates/layout.jsk +30 -30
- package/src/version.mjs +31 -31
- package/src/views/components/loader.js +101 -101
- package/src/views/helpers/html.js +102 -102
- package/src/views/helpers/tags.js +375 -375
- package/types/config/defaults.d.ts +6 -0
- package/types/config/index.d.ts +6 -0
- package/types/server/cache-control.d.ts +28 -0
- package/types/server/og-image.d.ts +5 -0
package/src/init.mjs
CHANGED
|
@@ -1,230 +1,232 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `jskelet init` — bulunduğun dizine çalışan bir minimum iskelet kurar.
|
|
3
|
-
*
|
|
4
|
-
* Varsayılan düzen feature-first'tir: sayfa, bileşen ve island
|
|
5
|
-
* `features/<name>/` altında toplanır; URL kaydı yine açıkça yazılır.
|
|
6
|
-
* Şablonlar `.jsk`. Var olan dosyaların üzerine yazmaz: komutu ikinci kez
|
|
7
|
-
* çalıştırmak yalnızca eksikleri tamamlar. Amaç, "kurulum yaptım ama hiçbir
|
|
8
|
-
* şey çalışmıyor" aşamasını tamamen atlamak — `jskelet dev` hemen ardından
|
|
9
|
-
* çalışır.
|
|
10
|
-
*/
|
|
11
|
-
import fs from "node:fs";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import * as log from "./log.mjs";
|
|
14
|
-
|
|
15
|
-
/** @type {Record<string, string>} */
|
|
16
|
-
const FILES = {
|
|
17
|
-
"jskelet.config.mjs": `/**
|
|
18
|
-
* JSkelet configuration. Every field is optional; the app still runs on
|
|
19
|
-
* defaults if you delete this file.
|
|
20
|
-
*
|
|
21
|
-
* Details: node_modules/jskelet/docs/en/07-configuration.md
|
|
22
|
-
*/
|
|
23
|
-
export default {
|
|
24
|
-
brand: { lang: "en" },
|
|
25
|
-
|
|
26
|
-
/** Third-party origins; emitted as preconnect in \`<head>\`. */
|
|
27
|
-
preconnect: [],
|
|
28
|
-
|
|
29
|
-
async cache() {
|
|
30
|
-
return {
|
|
31
|
-
/** How long a page's HTML stays in the cache (seconds). */
|
|
32
|
-
html: { "/": 60 },
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
@source "../
|
|
166
|
-
@source "../
|
|
167
|
-
@source "../
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
log.
|
|
228
|
-
|
|
229
|
-
log.line("
|
|
230
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `jskelet init` — bulunduğun dizine çalışan bir minimum iskelet kurar.
|
|
3
|
+
*
|
|
4
|
+
* Varsayılan düzen feature-first'tir: sayfa, bileşen ve island
|
|
5
|
+
* `features/<name>/` altında toplanır; URL kaydı yine açıkça yazılır.
|
|
6
|
+
* Şablonlar `.jsk`. Var olan dosyaların üzerine yazmaz: komutu ikinci kez
|
|
7
|
+
* çalıştırmak yalnızca eksikleri tamamlar. Amaç, "kurulum yaptım ama hiçbir
|
|
8
|
+
* şey çalışmıyor" aşamasını tamamen atlamak — `jskelet dev` hemen ardından
|
|
9
|
+
* çalışır.
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import * as log from "./log.mjs";
|
|
14
|
+
|
|
15
|
+
/** @type {Record<string, string>} */
|
|
16
|
+
const FILES = {
|
|
17
|
+
"jskelet.config.mjs": `/**
|
|
18
|
+
* JSkelet configuration. Every field is optional; the app still runs on
|
|
19
|
+
* defaults if you delete this file.
|
|
20
|
+
*
|
|
21
|
+
* Details: node_modules/jskelet/docs/en/07-configuration.md
|
|
22
|
+
*/
|
|
23
|
+
export default {
|
|
24
|
+
brand: { lang: "en" },
|
|
25
|
+
|
|
26
|
+
/** Third-party origins; emitted as preconnect in \`<head>\`. */
|
|
27
|
+
preconnect: [],
|
|
28
|
+
|
|
29
|
+
async cache() {
|
|
30
|
+
return {
|
|
31
|
+
/** How long a page's HTML stays in the cache (seconds). */
|
|
32
|
+
html: { "/": 60 },
|
|
33
|
+
/** Edge stale window after that TTL. 0 omits the directive. */
|
|
34
|
+
staleWhileRevalidate: 60,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
hooks: {
|
|
39
|
+
/** Metadata defaults for every page. */
|
|
40
|
+
metadata() {
|
|
41
|
+
return {
|
|
42
|
+
titleTemplate: "%s | JSkelet",
|
|
43
|
+
description: "A site built with JSkelet.",
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
/** Locals added to the layout on every render. */
|
|
48
|
+
layoutContext() {
|
|
49
|
+
return { bodyClass: "min-h-full" };
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** 404 page. */
|
|
53
|
+
notFound() {
|
|
54
|
+
return {
|
|
55
|
+
view: "pages/not-found",
|
|
56
|
+
metadata: { title: "Page not found", robots: { index: false } },
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
`,
|
|
62
|
+
|
|
63
|
+
"features/home/index.js": `/**
|
|
64
|
+
* Feature route registration. Explicit paths only — no filesystem URL routing.
|
|
65
|
+
* Loaded after \`routes/\` (alphabetically among features).
|
|
66
|
+
*
|
|
67
|
+
* @param {import('express').Express} app
|
|
68
|
+
* @param {{ route: Function }} api
|
|
69
|
+
*/
|
|
70
|
+
export default function register(app, { route }) {
|
|
71
|
+
app.get(
|
|
72
|
+
"/",
|
|
73
|
+
route(
|
|
74
|
+
async () => ({
|
|
75
|
+
view: "pages/home",
|
|
76
|
+
metadata: { title: "Home" },
|
|
77
|
+
data: { message: "JSkelet is running." },
|
|
78
|
+
}),
|
|
79
|
+
{ revalidate: 60 },
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
`,
|
|
84
|
+
|
|
85
|
+
"features/home/views/pages/home.jsk": `<section class="wrapper">
|
|
86
|
+
<h1>{{ metadata.title }}</h1>
|
|
87
|
+
<p>{{ message }}</p>
|
|
88
|
+
<Button text="Example component" />
|
|
89
|
+
<div data-island="counter" data-island-props='{"start":0}'></div>
|
|
90
|
+
</section>
|
|
91
|
+
`,
|
|
92
|
+
|
|
93
|
+
"features/home/views/components/button.js": `import { attrs, esc } from "jskelet/html";
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Named exports under \`views/components/**\` (including feature views) become
|
|
97
|
+
* PascalCase tags in \`.jsk\`: \`<Button text="Save" />\`.
|
|
98
|
+
*
|
|
99
|
+
* @param {{ text: string, href?: string, class?: string }} props
|
|
100
|
+
* @returns {string}
|
|
101
|
+
*/
|
|
102
|
+
export function button({ text, href, class: className }) {
|
|
103
|
+
const tag = href ? "a" : "button";
|
|
104
|
+
return \`<\${tag}\${attrs({ href, class: className })}>\${esc(text)}</\${tag}>\`;
|
|
105
|
+
}
|
|
106
|
+
`,
|
|
107
|
+
|
|
108
|
+
"features/home/client/counter.js": `/**
|
|
109
|
+
* Island contract: a named export called \`mount(element, props)\`.
|
|
110
|
+
* Register it from \`client/entries/main.js\`. The returned function, if any,
|
|
111
|
+
* is reserved for cleanup.
|
|
112
|
+
*
|
|
113
|
+
* @param {HTMLElement} element
|
|
114
|
+
* @param {{ start?: number }} props
|
|
115
|
+
*/
|
|
116
|
+
export function mount(element, props) {
|
|
117
|
+
let value = props.start ?? 0;
|
|
118
|
+
|
|
119
|
+
const button = document.createElement("button");
|
|
120
|
+
button.type = "button";
|
|
121
|
+
|
|
122
|
+
const paint = () => {
|
|
123
|
+
button.textContent = \`Clicks: \${value}\`;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
button.addEventListener("click", () => {
|
|
127
|
+
value += 1;
|
|
128
|
+
paint();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
paint();
|
|
132
|
+
element.append(button);
|
|
133
|
+
}
|
|
134
|
+
`,
|
|
135
|
+
|
|
136
|
+
"features/home/server/.gitkeep": "",
|
|
137
|
+
|
|
138
|
+
"views/pages/not-found.jsk": `<section class="wrapper">
|
|
139
|
+
<h1>404</h1>
|
|
140
|
+
<p>The page you are looking for was not found.</p>
|
|
141
|
+
<p><Link href="/" text="Back to home" /></p>
|
|
142
|
+
</section>
|
|
143
|
+
`,
|
|
144
|
+
|
|
145
|
+
"client/entries/main.js": `import { registerAll, start } from "jskelet/client";
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Island registry. Values are dynamic imports: a module is downloaded only if
|
|
149
|
+
* that island is actually on the page and becomes visible.
|
|
150
|
+
*/
|
|
151
|
+
registerAll({
|
|
152
|
+
counter: () => import("../../features/home/client/counter.js"),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
start();
|
|
156
|
+
`,
|
|
157
|
+
|
|
158
|
+
"styles/globals.css": `@import "tailwindcss" source(none);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Tailwind's class scanning depends on these directives. Automatic detection
|
|
162
|
+
* only scans the directory holding this file; variants used in templates
|
|
163
|
+
* (like data-[active=false]:…) would otherwise be dropped silently.
|
|
164
|
+
*/
|
|
165
|
+
@source "../views";
|
|
166
|
+
@source "../client";
|
|
167
|
+
@source "../routes";
|
|
168
|
+
@source "../features";
|
|
169
|
+
@source "../shared";
|
|
170
|
+
|
|
171
|
+
body {
|
|
172
|
+
margin: 0;
|
|
173
|
+
font-family: system-ui, sans-serif;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.wrapper {
|
|
177
|
+
max-width: 64rem;
|
|
178
|
+
margin-inline: auto;
|
|
179
|
+
padding: 2rem 1rem;
|
|
180
|
+
}
|
|
181
|
+
`,
|
|
182
|
+
|
|
183
|
+
"jsconfig.json": `{
|
|
184
|
+
"compilerOptions": {
|
|
185
|
+
"checkJs": true,
|
|
186
|
+
"module": "esnext",
|
|
187
|
+
"moduleResolution": "bundler",
|
|
188
|
+
"target": "es2022",
|
|
189
|
+
"baseUrl": ".",
|
|
190
|
+
"paths": { "@/*": ["./*"] }
|
|
191
|
+
},
|
|
192
|
+
"exclude": ["node_modules", "public/assets"]
|
|
193
|
+
}
|
|
194
|
+
`,
|
|
195
|
+
|
|
196
|
+
".gitignore": `node_modules/
|
|
197
|
+
.jskelet/
|
|
198
|
+
public/assets/
|
|
199
|
+
.env
|
|
200
|
+
`,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @param {string} root
|
|
205
|
+
* @returns {Promise<void>}
|
|
206
|
+
*/
|
|
207
|
+
export async function init(root) {
|
|
208
|
+
log.section("init");
|
|
209
|
+
|
|
210
|
+
const created = [];
|
|
211
|
+
const skipped = [];
|
|
212
|
+
|
|
213
|
+
for (const [relative, contents] of Object.entries(FILES)) {
|
|
214
|
+
const target = path.join(root, relative);
|
|
215
|
+
|
|
216
|
+
if (fs.existsSync(target)) {
|
|
217
|
+
skipped.push(relative);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
222
|
+
fs.writeFileSync(target, contents);
|
|
223
|
+
created.push(relative);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
for (const file of created) log.line(`+ ${file}`);
|
|
227
|
+
if (skipped.length) log.warn(`${skipped.length} files already existed, skipped`);
|
|
228
|
+
|
|
229
|
+
log.line("");
|
|
230
|
+
log.line("next step: npx jskelet dev");
|
|
231
|
+
log.line("grow with: npx jskelet generate feature <name>");
|
|
232
|
+
}
|