hadars 0.4.2 → 0.4.3-rc.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 +53 -14
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/{chunk-2TMQUXFL.js → chunk-BNBZ3XYX.js} +78 -18
- package/dist/{chunk-NYLXE7T7.js → chunk-YCCTGINV.js} +1 -1
- package/dist/cli.js +330 -226
- package/dist/cloudflare.cjs +83 -34
- package/dist/cloudflare.js +3 -2
- package/dist/index.js +2 -0
- package/dist/lambda.cjs +83 -34
- package/dist/lambda.js +3 -2
- package/dist/slim-react/index.cjs +103 -45
- package/dist/slim-react/index.d.cts +24 -2
- package/dist/slim-react/index.d.ts +24 -2
- package/dist/slim-react/index.js +4 -1
- package/dist/slim-react/jsx-runtime.js +1 -0
- package/dist/ssr-render-worker.js +86 -37
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -147,25 +147,8 @@ var SLIM_ELEMENT = Symbol.for("react.element");
|
|
|
147
147
|
var REACT19_ELEMENT = Symbol.for("react.transitional.element");
|
|
148
148
|
var FRAGMENT_TYPE = Symbol.for("react.fragment");
|
|
149
149
|
var SUSPENSE_TYPE = Symbol.for("react.suspense");
|
|
150
|
-
// src/slim-react/jsx.ts
|
|
151
|
-
function createElement(type, props, ...children) {
|
|
152
|
-
const normalizedProps = { ...props || {} };
|
|
153
|
-
if (children.length === 1) {
|
|
154
|
-
normalizedProps.children = children[0];
|
|
155
|
-
} else if (children.length > 1) {
|
|
156
|
-
normalizedProps.children = children;
|
|
157
|
-
}
|
|
158
|
-
const key = normalizedProps.key ?? null;
|
|
159
|
-
delete normalizedProps.key;
|
|
160
|
-
return {
|
|
161
|
-
$$typeof: SLIM_ELEMENT,
|
|
162
|
-
type,
|
|
163
|
-
props: normalizedProps,
|
|
164
|
-
key
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
150
|
// src/slim-react/renderContext.ts
|
|
151
|
+
import { createRequire as _nodeCreateRequire } from "node:module";
|
|
169
152
|
var MAP_KEY = "__slimReactContextMap";
|
|
170
153
|
var _g = globalThis;
|
|
171
154
|
if (!("__slimReactContextMap" in _g))
|
|
@@ -189,8 +172,7 @@ function getContextValue(context) {
|
|
|
189
172
|
const map = _g[MAP_KEY];
|
|
190
173
|
if (map && map.has(context))
|
|
191
174
|
return map.get(context);
|
|
192
|
-
|
|
193
|
-
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
175
|
+
return context._currentValue;
|
|
194
176
|
}
|
|
195
177
|
function pushContextValue(context, value) {
|
|
196
178
|
let map = _g[MAP_KEY];
|
|
@@ -198,8 +180,7 @@ function pushContextValue(context, value) {
|
|
|
198
180
|
map = new Map;
|
|
199
181
|
_g[MAP_KEY] = map;
|
|
200
182
|
}
|
|
201
|
-
const
|
|
202
|
-
const prev = map.has(context) ? map.get(context) : ("_defaultValue" in c) ? c._defaultValue : c._currentValue;
|
|
183
|
+
const prev = map.has(context) ? map.get(context) : context._currentValue;
|
|
203
184
|
map.set(context, value);
|
|
204
185
|
return prev;
|
|
205
186
|
}
|
|
@@ -304,14 +285,54 @@ function getTreeId() {
|
|
|
304
285
|
const stripped = (id & ~(1 << 31 - Math.clz32(id))).toString(32);
|
|
305
286
|
return stripped + overflow;
|
|
306
287
|
}
|
|
288
|
+
var _detectReact = () => {
|
|
289
|
+
if (typeof __HADARS_REACT_MAJOR__ !== "undefined") {
|
|
290
|
+
const major = parseInt(String(__HADARS_REACT_MAJOR__), 10);
|
|
291
|
+
return {
|
|
292
|
+
major,
|
|
293
|
+
version: major < 19 ? "18.3.1" : "19.1.1"
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
const parse = (ver) => ({ major: parseInt(ver.split(".")[0], 10), version: ver });
|
|
297
|
+
try {
|
|
298
|
+
return parse(__require("react").version);
|
|
299
|
+
} catch {}
|
|
300
|
+
try {
|
|
301
|
+
const req = _nodeCreateRequire(process.cwd() + "/__hadars__.js");
|
|
302
|
+
return parse(req("react").version);
|
|
303
|
+
} catch {}
|
|
304
|
+
return { major: 19, version: "19.1.1" };
|
|
305
|
+
};
|
|
306
|
+
var _react = _detectReact();
|
|
307
|
+
var REACT_MAJOR = _react.major;
|
|
308
|
+
var REACT_VERSION = _react.version;
|
|
307
309
|
function makeId() {
|
|
308
310
|
const st = s();
|
|
309
311
|
const treeId = getTreeId();
|
|
310
312
|
const n = st.localIdCounter++;
|
|
311
|
-
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
const suffix = n > 0 ? "H" + n.toString(32) : "";
|
|
314
|
+
if (REACT_MAJOR < 19) {
|
|
315
|
+
return ":" + st.idPrefix + "R" + treeId + suffix + ":";
|
|
316
|
+
}
|
|
317
|
+
return "_R_" + st.idPrefix + treeId + suffix + "_";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/slim-react/jsx.ts
|
|
321
|
+
function createElement(type, props, ...children) {
|
|
322
|
+
const normalizedProps = { ...props || {} };
|
|
323
|
+
if (children.length === 1) {
|
|
324
|
+
normalizedProps.children = children[0];
|
|
325
|
+
} else if (children.length > 1) {
|
|
326
|
+
normalizedProps.children = children;
|
|
327
|
+
}
|
|
328
|
+
const key = normalizedProps.key ?? null;
|
|
329
|
+
delete normalizedProps.key;
|
|
330
|
+
return {
|
|
331
|
+
$$typeof: SLIM_ELEMENT,
|
|
332
|
+
type,
|
|
333
|
+
props: normalizedProps,
|
|
334
|
+
key
|
|
335
|
+
};
|
|
315
336
|
}
|
|
316
337
|
|
|
317
338
|
// src/slim-react/hooks.ts
|
|
@@ -364,8 +385,22 @@ function use(usable) {
|
|
|
364
385
|
}
|
|
365
386
|
// src/slim-react/dispatcher.ts
|
|
366
387
|
import * as ReactNS from "react";
|
|
367
|
-
var
|
|
368
|
-
var
|
|
388
|
+
var _r19;
|
|
389
|
+
var _r18;
|
|
390
|
+
var _detected = false;
|
|
391
|
+
function _detect() {
|
|
392
|
+
if (_detected)
|
|
393
|
+
return;
|
|
394
|
+
_detected = true;
|
|
395
|
+
const r19 = ReactNS.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
396
|
+
if (r19) {
|
|
397
|
+
_r19 = r19;
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const raw = ReactNS.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
401
|
+
if (raw?.ReactCurrentDispatcher)
|
|
402
|
+
_r18 = raw;
|
|
403
|
+
}
|
|
369
404
|
var slimDispatcher = {
|
|
370
405
|
useId: makeId,
|
|
371
406
|
readContext: (ctx) => getContextValue(ctx),
|
|
@@ -391,15 +426,25 @@ var slimDispatcher = {
|
|
|
391
426
|
useHostTransitionStatus: () => false
|
|
392
427
|
};
|
|
393
428
|
function installDispatcher() {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
429
|
+
_detect();
|
|
430
|
+
if (_r19) {
|
|
431
|
+
const prev = _r19.H;
|
|
432
|
+
_r19.H = slimDispatcher;
|
|
433
|
+
return prev;
|
|
434
|
+
}
|
|
435
|
+
if (_r18) {
|
|
436
|
+
const prev = _r18.ReactCurrentDispatcher.current;
|
|
437
|
+
_r18.ReactCurrentDispatcher.current = slimDispatcher;
|
|
438
|
+
return prev;
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
399
441
|
}
|
|
400
442
|
function restoreDispatcher(prev) {
|
|
401
|
-
|
|
402
|
-
|
|
443
|
+
_detect();
|
|
444
|
+
if (_r19)
|
|
445
|
+
_r19.H = prev;
|
|
446
|
+
else if (_r18)
|
|
447
|
+
_r18.ReactCurrentDispatcher.current = prev;
|
|
403
448
|
}
|
|
404
449
|
|
|
405
450
|
// src/slim-react/render.ts
|
|
@@ -660,7 +705,7 @@ function writeAttributes(writer, props, isSvg, skip) {
|
|
|
660
705
|
if (isSvg && key in SVG_ATTR_MAP) {
|
|
661
706
|
attrName = SVG_ATTR_MAP[key];
|
|
662
707
|
} else {
|
|
663
|
-
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key === "defaultValue" ? "value" : key === "defaultChecked" ? "checked" : key;
|
|
708
|
+
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key === "defaultValue" ? "value" : key === "defaultChecked" ? "checked" : key === "contentEditable" && REACT_MAJOR < 19 ? "contenteditable" : key;
|
|
664
709
|
}
|
|
665
710
|
if (value === false || value == null) {
|
|
666
711
|
if (value === false && (attrName.charCodeAt(0) === 97 && attrName.startsWith("aria-") || attrName.charCodeAt(0) === 100 && attrName.startsWith("data-"))) {
|
|
@@ -871,7 +916,8 @@ function renderComponent(type, props, writer, isSvg, _suspenseRetries = 0) {
|
|
|
871
916
|
const LazyComp = resolved?.default ?? resolved;
|
|
872
917
|
return renderComponent(LazyComp, props, writer, isSvg);
|
|
873
918
|
}
|
|
874
|
-
|
|
919
|
+
const isConsumer = typeOf === REACT_CONSUMER || typeOf === REACT_CONTEXT && "_context" in type && !("value" in props);
|
|
920
|
+
if (isConsumer) {
|
|
875
921
|
const ctx2 = type._context;
|
|
876
922
|
const value = ctx2 ? getContextValue(ctx2) : undefined;
|
|
877
923
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
@@ -2448,6 +2494,20 @@ var HadarsFolder = "./.hadars";
|
|
|
2448
2494
|
var StaticPath = `${HadarsFolder}/static`;
|
|
2449
2495
|
var HADARS_TMP_DIR = pathMod2.join(os.tmpdir(), "hadars");
|
|
2450
2496
|
var ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
|
|
2497
|
+
var readReactMajor = async () => {
|
|
2498
|
+
let dir = process.cwd();
|
|
2499
|
+
while (true) {
|
|
2500
|
+
try {
|
|
2501
|
+
const pkgPath = pathMod2.join(dir, "node_modules", "react", "package.json");
|
|
2502
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
2503
|
+
return parseInt(pkg.version.split(".")[0], 10);
|
|
2504
|
+
} catch {}
|
|
2505
|
+
const parent = pathMod2.dirname(dir);
|
|
2506
|
+
if (parent === dir)
|
|
2507
|
+
return 19;
|
|
2508
|
+
dir = parent;
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2451
2511
|
var validateOptions = (options) => {
|
|
2452
2512
|
if (!options.entry) {
|
|
2453
2513
|
throw new Error("Entry file is required");
|
|
@@ -2567,6 +2627,8 @@ var dev = async (options) => {
|
|
|
2567
2627
|
});
|
|
2568
2628
|
const workerCmd = resolveWorkerCmd(packageDir2);
|
|
2569
2629
|
console.log("Spawning SSR worker:", workerCmd.join(" "), "entry:", entry);
|
|
2630
|
+
const reactMajor = await readReactMajor();
|
|
2631
|
+
const ssrDefine = { __HADARS_REACT_MAJOR__: String(reactMajor), ...options.define };
|
|
2570
2632
|
const child = spawn(workerCmd[0], [
|
|
2571
2633
|
...workerCmd.slice(1),
|
|
2572
2634
|
`--entry=${entry}`,
|
|
@@ -2574,7 +2636,7 @@ var dev = async (options) => {
|
|
|
2574
2636
|
`--outFile=${SSR_FILENAME}`,
|
|
2575
2637
|
`--base=${baseURL}`,
|
|
2576
2638
|
...options.swcPlugins ? [`--swcPlugins=${JSON.stringify(options.swcPlugins)}`] : [],
|
|
2577
|
-
|
|
2639
|
+
`--define=${JSON.stringify(ssrDefine)}`,
|
|
2578
2640
|
...options.moduleRules ? [`--moduleRules=${JSON.stringify(options.moduleRules, (_k, v) => v instanceof RegExp ? { __re: v.source, __flags: v.flags } : v)}`] : []
|
|
2579
2641
|
], { stdio: "pipe" });
|
|
2580
2642
|
child.stdin?.end();
|
|
@@ -2748,6 +2810,7 @@ var build = async (options) => {
|
|
|
2748
2810
|
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
2749
2811
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
2750
2812
|
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname3, options.htmlTemplate)) : undefined;
|
|
2813
|
+
const reactMajor = await readReactMajor();
|
|
2751
2814
|
console.log("Building client and server bundles in parallel...");
|
|
2752
2815
|
await Promise.all([
|
|
2753
2816
|
compileEntry(tmpFilePath, {
|
|
@@ -2779,7 +2842,7 @@ var build = async (options) => {
|
|
|
2779
2842
|
target: "node",
|
|
2780
2843
|
mode: "production",
|
|
2781
2844
|
swcPlugins: options.swcPlugins,
|
|
2782
|
-
define: options.define,
|
|
2845
|
+
define: { __HADARS_REACT_MAJOR__: String(reactMajor), ...options.define },
|
|
2783
2846
|
moduleRules: options.moduleRules,
|
|
2784
2847
|
plugins: options.plugins,
|
|
2785
2848
|
postcssPlugins: options.postcssPlugins
|
|
@@ -3184,8 +3247,166 @@ Deploy instructions:`);
|
|
|
3184
3247
|
await unlink(shimPath).catch(() => {});
|
|
3185
3248
|
}
|
|
3186
3249
|
}
|
|
3187
|
-
var
|
|
3188
|
-
|
|
3250
|
+
var _R = "\x1B[0m";
|
|
3251
|
+
var _B = "\x1B[1m";
|
|
3252
|
+
var _D = "\x1B[2m";
|
|
3253
|
+
var _C = "\x1B[36m";
|
|
3254
|
+
var _G = "\x1B[32m";
|
|
3255
|
+
var _UP_KEY = "\x1B[A";
|
|
3256
|
+
var _DOWN_KEY = "\x1B[B";
|
|
3257
|
+
var _HIDE = "\x1B[?25l";
|
|
3258
|
+
var _SHOW = "\x1B[?25h";
|
|
3259
|
+
var _cl = () => "\r\x1B[2K";
|
|
3260
|
+
var _up = (n) => n > 0 ? `\x1B[${n}A` : "";
|
|
3261
|
+
function _readKeys(handler) {
|
|
3262
|
+
return new Promise((resolve2) => {
|
|
3263
|
+
const { stdin } = process;
|
|
3264
|
+
const wasRaw = stdin.isTTY && stdin.isRaw;
|
|
3265
|
+
if (stdin.isTTY)
|
|
3266
|
+
stdin.setRawMode(true);
|
|
3267
|
+
stdin.resume();
|
|
3268
|
+
stdin.setEncoding("utf-8");
|
|
3269
|
+
const onData = (key) => {
|
|
3270
|
+
if (key === "\x03") {
|
|
3271
|
+
cleanup();
|
|
3272
|
+
process.stdout.write(_SHOW);
|
|
3273
|
+
process.exit(130);
|
|
3274
|
+
}
|
|
3275
|
+
if (handler(key))
|
|
3276
|
+
cleanup();
|
|
3277
|
+
};
|
|
3278
|
+
const cleanup = () => {
|
|
3279
|
+
stdin.removeListener("data", onData);
|
|
3280
|
+
if (stdin.isTTY && !wasRaw)
|
|
3281
|
+
stdin.setRawMode(false);
|
|
3282
|
+
stdin.pause();
|
|
3283
|
+
resolve2();
|
|
3284
|
+
};
|
|
3285
|
+
stdin.on("data", onData);
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
async function promptRadio(question, options) {
|
|
3289
|
+
const out = process.stdout;
|
|
3290
|
+
let cursor = 0;
|
|
3291
|
+
const total = 1 + options.length;
|
|
3292
|
+
const render = (redraw) => {
|
|
3293
|
+
if (redraw)
|
|
3294
|
+
out.write(_up(total));
|
|
3295
|
+
out.write(`${_cl()} ${_B}${question}${_R}
|
|
3296
|
+
`);
|
|
3297
|
+
for (let i = 0;i < options.length; i++) {
|
|
3298
|
+
const arrow = i === cursor ? `${_C}❯${_R}` : " ";
|
|
3299
|
+
const text = i === cursor ? `${_B}${options[i]}${_R}` : `${_D}${options[i]}${_R}`;
|
|
3300
|
+
out.write(`${_cl()} ${arrow} ${text}
|
|
3301
|
+
`);
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
out.write(_HIDE);
|
|
3305
|
+
render(false);
|
|
3306
|
+
await _readKeys((key) => {
|
|
3307
|
+
if (key === _UP_KEY && cursor > 0) {
|
|
3308
|
+
cursor--;
|
|
3309
|
+
render(true);
|
|
3310
|
+
} else if (key === _DOWN_KEY && cursor < options.length - 1) {
|
|
3311
|
+
cursor++;
|
|
3312
|
+
render(true);
|
|
3313
|
+
} else if (key === "\r") {
|
|
3314
|
+
return true;
|
|
3315
|
+
}
|
|
3316
|
+
return false;
|
|
3317
|
+
});
|
|
3318
|
+
out.write(_up(total));
|
|
3319
|
+
out.write(`${_cl()} ${question} ${_C}${_B}${options[cursor]}${_R}
|
|
3320
|
+
`);
|
|
3321
|
+
for (let i = 0;i < options.length; i++)
|
|
3322
|
+
out.write(`${_cl()}
|
|
3323
|
+
`);
|
|
3324
|
+
out.write(_up(options.length));
|
|
3325
|
+
out.write(_SHOW);
|
|
3326
|
+
return cursor;
|
|
3327
|
+
}
|
|
3328
|
+
async function promptMultiSelect(question, options) {
|
|
3329
|
+
const out = process.stdout;
|
|
3330
|
+
let cursor = 0;
|
|
3331
|
+
const selected = new Set;
|
|
3332
|
+
const total = 2 + options.length;
|
|
3333
|
+
const render = (redraw) => {
|
|
3334
|
+
if (redraw)
|
|
3335
|
+
out.write(_up(total));
|
|
3336
|
+
out.write(`${_cl()} ${_B}${question}${_R}
|
|
3337
|
+
`);
|
|
3338
|
+
out.write(`${_cl()} ${_D}↑↓ navigate · Space select · Enter confirm${_R}
|
|
3339
|
+
`);
|
|
3340
|
+
for (let i = 0;i < options.length; i++) {
|
|
3341
|
+
const arrow = i === cursor ? `${_C}❯${_R}` : " ";
|
|
3342
|
+
const box = selected.has(i) ? `${_G}●${_R}` : `${_D}○${_R}`;
|
|
3343
|
+
const text = i === cursor ? `${_B}${options[i]}${_R}` : `${_D}${options[i]}${_R}`;
|
|
3344
|
+
out.write(`${_cl()} ${arrow} ${box} ${text}
|
|
3345
|
+
`);
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3348
|
+
out.write(_HIDE);
|
|
3349
|
+
render(false);
|
|
3350
|
+
await _readKeys((key) => {
|
|
3351
|
+
if (key === _UP_KEY && cursor > 0) {
|
|
3352
|
+
cursor--;
|
|
3353
|
+
render(true);
|
|
3354
|
+
} else if (key === _DOWN_KEY && cursor < options.length - 1) {
|
|
3355
|
+
cursor++;
|
|
3356
|
+
render(true);
|
|
3357
|
+
} else if (key === " ") {
|
|
3358
|
+
selected.has(cursor) ? selected.delete(cursor) : selected.add(cursor);
|
|
3359
|
+
render(true);
|
|
3360
|
+
} else if (key === "\r") {
|
|
3361
|
+
return true;
|
|
3362
|
+
}
|
|
3363
|
+
return false;
|
|
3364
|
+
});
|
|
3365
|
+
const picked = [...selected].sort((a, b) => a - b);
|
|
3366
|
+
const summary = picked.length > 0 ? picked.map((i) => options[i]).join(", ") : "none";
|
|
3367
|
+
out.write(_up(total));
|
|
3368
|
+
out.write(`${_cl()} ${question} ${_C}${_B}${summary}${_R}
|
|
3369
|
+
`);
|
|
3370
|
+
for (let i = 0;i < total - 1; i++)
|
|
3371
|
+
out.write(`${_cl()}
|
|
3372
|
+
`);
|
|
3373
|
+
out.write(_up(total - 1));
|
|
3374
|
+
out.write(_SHOW);
|
|
3375
|
+
return picked;
|
|
3376
|
+
}
|
|
3377
|
+
var PLUGINS = [
|
|
3378
|
+
{ pkg: "@swc/plugin-emotion", version: "12.0.0", label: "Emotion (CSS-in-JS)" },
|
|
3379
|
+
{ pkg: "@swc/plugin-styled-components", version: "10.0.0", label: "styled-components" },
|
|
3380
|
+
{ pkg: "@swc/plugin-relay", version: "10.0.0", label: "Relay (GraphQL)" },
|
|
3381
|
+
{ pkg: "@swc/plugin-styled-jsx", version: "11.0.0", label: "styled-jsx" },
|
|
3382
|
+
{ pkg: "@swc/plugin-transform-imports", version: "10.0.0", label: "transform-imports" },
|
|
3383
|
+
{ pkg: "@swc/plugin-loadable-components", version: "9.0.0", label: "Loadable Components" },
|
|
3384
|
+
{ pkg: "@swc/plugin-formatjs", version: "7.0.0", label: "FormatJS (i18n)" }
|
|
3385
|
+
];
|
|
3386
|
+
function renderSwcPluginsConfig(plugins) {
|
|
3387
|
+
if (plugins.length === 0)
|
|
3388
|
+
return "";
|
|
3389
|
+
const lines = plugins.map((p) => {
|
|
3390
|
+
if (p.pkg === "@swc/plugin-relay") {
|
|
3391
|
+
return ` ['${p.pkg}', { rootDir: process.cwd(), artifactDirectory: 'src/__generated__' }],`;
|
|
3392
|
+
}
|
|
3393
|
+
return ` ['${p.pkg}', {}],`;
|
|
3394
|
+
});
|
|
3395
|
+
return `
|
|
3396
|
+
swcPlugins: [
|
|
3397
|
+
${lines.join(`
|
|
3398
|
+
`)}
|
|
3399
|
+
],`;
|
|
3400
|
+
}
|
|
3401
|
+
function buildTemplates(name, opts) {
|
|
3402
|
+
const { useTypeScript, plugins } = opts;
|
|
3403
|
+
const appExt = useTypeScript ? "tsx" : "jsx";
|
|
3404
|
+
const cfgExt = useTypeScript ? "ts" : "js";
|
|
3405
|
+
const tsOrJs = useTypeScript ? "tsconfig.json" : "jsconfig.json";
|
|
3406
|
+
const pluginDeps = {};
|
|
3407
|
+
for (const p of plugins)
|
|
3408
|
+
pluginDeps[p.pkg] = p.version;
|
|
3409
|
+
const packageJson = JSON.stringify({
|
|
3189
3410
|
name,
|
|
3190
3411
|
version: "0.1.0",
|
|
3191
3412
|
type: "module",
|
|
@@ -3199,19 +3420,28 @@ var TEMPLATES = {
|
|
|
3199
3420
|
hadars: "latest",
|
|
3200
3421
|
react: "^19.0.0",
|
|
3201
3422
|
"react-dom": "^19.0.0"
|
|
3202
|
-
}
|
|
3423
|
+
},
|
|
3424
|
+
...Object.keys(pluginDeps).length > 0 ? { devDependencies: pluginDeps } : {}
|
|
3203
3425
|
}, null, 2) + `
|
|
3204
|
-
|
|
3205
|
-
|
|
3426
|
+
`;
|
|
3427
|
+
const swcSection = renderSwcPluginsConfig(plugins);
|
|
3428
|
+
const hadarsConfig = useTypeScript ? `import type { HadarsOptions } from 'hadars';
|
|
3206
3429
|
|
|
3207
3430
|
const config: HadarsOptions = {
|
|
3208
3431
|
entry: 'src/App.tsx',
|
|
3209
|
-
port: 3000
|
|
3432
|
+
port: 3000,${swcSection}
|
|
3210
3433
|
};
|
|
3211
3434
|
|
|
3212
3435
|
export default config;
|
|
3213
|
-
|
|
3214
|
-
|
|
3436
|
+
` : `/** @type {import('hadars').HadarsOptions} */
|
|
3437
|
+
const config = {
|
|
3438
|
+
entry: 'src/App.jsx',
|
|
3439
|
+
port: 3000,${swcSection}
|
|
3440
|
+
};
|
|
3441
|
+
|
|
3442
|
+
export default config;
|
|
3443
|
+
`;
|
|
3444
|
+
const tsConfigContent = useTypeScript ? JSON.stringify({
|
|
3215
3445
|
compilerOptions: {
|
|
3216
3446
|
lib: ["ESNext", "DOM"],
|
|
3217
3447
|
target: "ESNext",
|
|
@@ -3226,215 +3456,89 @@ export default config;
|
|
|
3226
3456
|
skipLibCheck: true
|
|
3227
3457
|
}
|
|
3228
3458
|
}, null, 2) + `
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
}
|
|
3246
|
-
|
|
3247
|
-
.nav {
|
|
3248
|
-
display: flex;
|
|
3249
|
-
align-items: center;
|
|
3250
|
-
justify-content: space-between;
|
|
3251
|
-
padding: 1rem 2rem;
|
|
3252
|
-
border-bottom: 1px solid #1e1e2e;
|
|
3253
|
-
}
|
|
3254
|
-
.nav-brand { font-weight: 700; font-size: 1.1rem; color: #a78bfa; letter-spacing: -0.02em; }
|
|
3255
|
-
.nav-links { display: flex; gap: 1.5rem; }
|
|
3256
|
-
.nav-links a { color: #94a3b8; text-decoration: none; font-size: 0.9rem; }
|
|
3257
|
-
.nav-links a:hover { color: #e2e8f0; }
|
|
3258
|
-
|
|
3259
|
-
.hero {
|
|
3260
|
-
text-align: center;
|
|
3261
|
-
padding: 5rem 1rem 4rem;
|
|
3262
|
-
max-width: 680px;
|
|
3263
|
-
margin: 0 auto;
|
|
3264
|
-
}
|
|
3265
|
-
.hero-badge {
|
|
3266
|
-
display: inline-block;
|
|
3267
|
-
background: #1e1a2e;
|
|
3268
|
-
border: 1px solid #4c1d95;
|
|
3269
|
-
color: #a78bfa;
|
|
3270
|
-
font-size: 0.75rem;
|
|
3271
|
-
font-weight: 600;
|
|
3272
|
-
letter-spacing: 0.05em;
|
|
3273
|
-
text-transform: uppercase;
|
|
3274
|
-
padding: 0.3rem 0.8rem;
|
|
3275
|
-
border-radius: 999px;
|
|
3276
|
-
margin-bottom: 1.5rem;
|
|
3277
|
-
}
|
|
3278
|
-
.hero h1 {
|
|
3279
|
-
font-size: clamp(2rem, 5vw, 3.25rem);
|
|
3280
|
-
font-weight: 800;
|
|
3281
|
-
letter-spacing: -0.03em;
|
|
3282
|
-
line-height: 1.15;
|
|
3283
|
-
margin-bottom: 1rem;
|
|
3284
|
-
}
|
|
3285
|
-
.hero h1 span { color: #a78bfa; }
|
|
3286
|
-
.hero p {
|
|
3287
|
-
font-size: 1.1rem;
|
|
3288
|
-
color: #94a3b8;
|
|
3289
|
-
line-height: 1.7;
|
|
3290
|
-
margin-bottom: 2.5rem;
|
|
3291
|
-
}
|
|
3292
|
-
.hero-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }
|
|
3293
|
-
.btn {
|
|
3294
|
-
display: inline-flex;
|
|
3295
|
-
align-items: center;
|
|
3296
|
-
gap: 0.4rem;
|
|
3297
|
-
padding: 0.65rem 1.4rem;
|
|
3298
|
-
border-radius: 8px;
|
|
3299
|
-
font-size: 0.9rem;
|
|
3300
|
-
font-weight: 600;
|
|
3301
|
-
cursor: pointer;
|
|
3302
|
-
border: none;
|
|
3303
|
-
transition: opacity 0.15s, transform 0.1s;
|
|
3304
|
-
text-decoration: none;
|
|
3305
|
-
}
|
|
3306
|
-
.btn:hover { opacity: 0.85; transform: translateY(-1px); }
|
|
3307
|
-
.btn:active { transform: translateY(0); }
|
|
3308
|
-
.btn-primary { background: #7c3aed; color: #fff; }
|
|
3309
|
-
.btn-ghost { background: #1e1e2e; color: #e2e8f0; border: 1px solid #2d2d3e; }
|
|
3310
|
-
|
|
3311
|
-
.features {
|
|
3312
|
-
display: grid;
|
|
3313
|
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
3314
|
-
gap: 1rem;
|
|
3315
|
-
max-width: 900px;
|
|
3316
|
-
margin: 0 auto 4rem;
|
|
3317
|
-
padding: 0 1.5rem;
|
|
3318
|
-
}
|
|
3319
|
-
.card {
|
|
3320
|
-
background: #16161f;
|
|
3321
|
-
border: 1px solid #1e1e2e;
|
|
3322
|
-
border-radius: 12px;
|
|
3323
|
-
padding: 1.5rem;
|
|
3324
|
-
}
|
|
3325
|
-
.card-icon { font-size: 1.5rem; margin-bottom: 0.75rem; }
|
|
3326
|
-
.card h3 { font-size: 0.95rem; font-weight: 700; margin-bottom: 0.4rem; }
|
|
3327
|
-
.card p { font-size: 0.85rem; color: #64748b; line-height: 1.6; }
|
|
3328
|
-
|
|
3329
|
-
.demo {
|
|
3330
|
-
max-width: 480px;
|
|
3331
|
-
margin: 0 auto 4rem;
|
|
3332
|
-
padding: 0 1.5rem;
|
|
3333
|
-
text-align: center;
|
|
3334
|
-
}
|
|
3335
|
-
.demo-box {
|
|
3336
|
-
background: #16161f;
|
|
3337
|
-
border: 1px solid #1e1e2e;
|
|
3338
|
-
border-radius: 12px;
|
|
3339
|
-
padding: 2rem;
|
|
3340
|
-
}
|
|
3341
|
-
.demo-box h2 { font-size: 0.8rem; font-weight: 600; color: #64748b; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 1.25rem; }
|
|
3342
|
-
.counter { font-size: 3.5rem; font-weight: 800; color: #a78bfa; letter-spacing: -0.04em; margin-bottom: 1.25rem; }
|
|
3343
|
-
.demo-actions { display: flex; gap: 0.75rem; justify-content: center; }
|
|
3344
|
-
|
|
3345
|
-
\`;
|
|
3459
|
+
` : JSON.stringify({
|
|
3460
|
+
compilerOptions: {
|
|
3461
|
+
lib: ["ESNext", "DOM"],
|
|
3462
|
+
target: "ESNext",
|
|
3463
|
+
module: "ESNext",
|
|
3464
|
+
moduleResolution: "bundler",
|
|
3465
|
+
jsx: "react-jsx",
|
|
3466
|
+
checkJs: true,
|
|
3467
|
+
noEmit: true,
|
|
3468
|
+
skipLibCheck: true
|
|
3469
|
+
}
|
|
3470
|
+
}, null, 2) + `
|
|
3471
|
+
`;
|
|
3472
|
+
const appImports = useTypeScript ? `import React from 'react';
|
|
3473
|
+
import { HadarsHead, type HadarsApp } from 'hadars';` : `import React from 'react';
|
|
3474
|
+
import { HadarsHead } from 'hadars';`;
|
|
3475
|
+
const appSignature = useTypeScript ? `const App: HadarsApp<{}> = () => {` : `const App = () => {`;
|
|
3476
|
+
const appContent = `${appImports}
|
|
3346
3477
|
|
|
3347
|
-
|
|
3478
|
+
${appSignature}
|
|
3348
3479
|
const [count, setCount] = React.useState(0);
|
|
3349
3480
|
|
|
3350
3481
|
return (
|
|
3351
3482
|
<>
|
|
3352
3483
|
<HadarsHead status={200}>
|
|
3353
|
-
<title
|
|
3484
|
+
<title>${name}</title>
|
|
3354
3485
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3355
|
-
<style data-id="app-styles" dangerouslySetInnerHTML={{ __html: css }} />
|
|
3356
3486
|
</HadarsHead>
|
|
3357
3487
|
|
|
3358
|
-
<
|
|
3359
|
-
<
|
|
3360
|
-
<
|
|
3361
|
-
<
|
|
3362
|
-
</div>
|
|
3363
|
-
</nav>
|
|
3364
|
-
|
|
3365
|
-
<section className="hero">
|
|
3366
|
-
<div className="hero-badge">built with hadars</div>
|
|
3367
|
-
<h1>Ship <span>React apps</span><br />at full speed</h1>
|
|
3368
|
-
<p>
|
|
3369
|
-
SSR out of the box, zero config, instant hot-reload.
|
|
3370
|
-
Edit <code>src/App.tsx</code> to get started.
|
|
3488
|
+
<main style={{ fontFamily: 'sans-serif', maxWidth: 480, margin: '4rem auto', padding: '0 1rem', textAlign: 'center' }}>
|
|
3489
|
+
<h1>${name}</h1>
|
|
3490
|
+
<p style={{ color: '#666', margin: '1rem 0 2rem' }}>
|
|
3491
|
+
Edit <code>src/App.${appExt}</code> to get started.
|
|
3371
3492
|
</p>
|
|
3372
|
-
<
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3493
|
+
<p style={{ fontSize: '3rem', margin: '1rem 0' }}>{count}</p>
|
|
3494
|
+
<div style={{ display: 'flex', gap: '0.5rem', justifyContent: 'center' }}>
|
|
3495
|
+
<button onClick={() => setCount(c => c - 1)}>−</button>
|
|
3496
|
+
<button onClick={() => setCount(c => c + 1)}>+</button>
|
|
3376
3497
|
</div>
|
|
3377
|
-
</
|
|
3378
|
-
|
|
3379
|
-
<div className="features">
|
|
3380
|
-
<div className="card">
|
|
3381
|
-
<div className="card-icon">⚡</div>
|
|
3382
|
-
<h3>Server-side rendering</h3>
|
|
3383
|
-
<p>Pages render on the server and hydrate on the client — great for SEO and first paint.</p>
|
|
3384
|
-
</div>
|
|
3385
|
-
<div className="card">
|
|
3386
|
-
<div className="card-icon">\uD83D\uDD25</div>
|
|
3387
|
-
<h3>Hot module reload</h3>
|
|
3388
|
-
<p>Changes in <code>src/App.tsx</code> reflect instantly in the browser during development.</p>
|
|
3389
|
-
</div>
|
|
3390
|
-
<div className="card">
|
|
3391
|
-
<div className="card-icon">\uD83D\uDCE6</div>
|
|
3392
|
-
<h3>Zero config</h3>
|
|
3393
|
-
<p>One config file. Export a React component, run <code>hadars dev</code>, done.</p>
|
|
3394
|
-
</div>
|
|
3395
|
-
<div className="card">
|
|
3396
|
-
<div className="card-icon">\uD83D\uDDC4️</div>
|
|
3397
|
-
<h3>Server data hooks</h3>
|
|
3398
|
-
<p>Use <code>useServerData</code> to fetch data on the server without extra round-trips.</p>
|
|
3399
|
-
</div>
|
|
3400
|
-
</div>
|
|
3401
|
-
|
|
3402
|
-
<div className="demo">
|
|
3403
|
-
<div className="demo-box">
|
|
3404
|
-
<h2>Client interactivity works</h2>
|
|
3405
|
-
<div className="counter">{count}</div>
|
|
3406
|
-
<div className="demo-actions">
|
|
3407
|
-
<button className="btn btn-ghost" onClick={() => setCount(c => c - 1)}>− dec</button>
|
|
3408
|
-
<button className="btn btn-primary" onClick={() => setCount(c => c + 1)}>+ inc</button>
|
|
3409
|
-
</div>
|
|
3410
|
-
</div>
|
|
3411
|
-
</div>
|
|
3412
|
-
|
|
3498
|
+
</main>
|
|
3413
3499
|
</>
|
|
3414
3500
|
);
|
|
3415
3501
|
};
|
|
3416
3502
|
|
|
3417
3503
|
export default App;
|
|
3418
|
-
|
|
3419
|
-
|
|
3504
|
+
`;
|
|
3505
|
+
return {
|
|
3506
|
+
"package.json": packageJson,
|
|
3507
|
+
[`hadars.config.${cfgExt}`]: hadarsConfig,
|
|
3508
|
+
[tsOrJs]: tsConfigContent,
|
|
3509
|
+
".gitignore": `node_modules/
|
|
3510
|
+
.hadars/
|
|
3511
|
+
dist/
|
|
3512
|
+
`,
|
|
3513
|
+
[`src/App.${appExt}`]: appContent
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3420
3516
|
async function createProject(name, cwd) {
|
|
3421
3517
|
const dir = resolve(cwd, name);
|
|
3422
3518
|
if (existsSync3(dir)) {
|
|
3423
3519
|
console.error(`Directory already exists: ${dir}`);
|
|
3424
3520
|
process.exit(1);
|
|
3425
3521
|
}
|
|
3426
|
-
|
|
3522
|
+
const useTypeScript = await promptRadio("Language?", ["TypeScript", "JavaScript"]) === 0;
|
|
3523
|
+
const selectedIndices = await promptMultiSelect("Select SWC plugins to enable (optional):", PLUGINS.map((p) => p.label));
|
|
3524
|
+
const plugins = selectedIndices.map((i) => PLUGINS[i]);
|
|
3525
|
+
console.log(`
|
|
3526
|
+
Creating hadars project in ${dir}`);
|
|
3427
3527
|
await mkdir2(join2(dir, "src"), { recursive: true });
|
|
3428
|
-
|
|
3429
|
-
|
|
3528
|
+
const files = buildTemplates(name, { useTypeScript, plugins });
|
|
3529
|
+
for (const [file, content] of Object.entries(files)) {
|
|
3430
3530
|
await writeFile2(join2(dir, file), content, "utf-8");
|
|
3431
3531
|
console.log(` created ${file}`);
|
|
3432
3532
|
}
|
|
3533
|
+
const installHint = plugins.length > 0 ? `
|
|
3534
|
+
# Also install selected SWC plugins:
|
|
3535
|
+
npm install -D ${plugins.map((p) => `${p.pkg}@${p.version}`).join(" ")}
|
|
3536
|
+
` : "";
|
|
3433
3537
|
console.log(`
|
|
3434
3538
|
Done! Next steps:
|
|
3435
3539
|
|
|
3436
3540
|
cd ${name}
|
|
3437
|
-
npm install # or: bun install / pnpm install
|
|
3541
|
+
npm install # or: bun install / pnpm install${installHint}
|
|
3438
3542
|
npm run dev # or: bun run dev
|
|
3439
3543
|
`);
|
|
3440
3544
|
}
|