hadars 1.0.0 → 1.0.2
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/dist/{chunk-NYLXE7T7.js → chunk-LDVJ26Q3.js} +1 -1
- package/dist/{chunk-2TMQUXFL.js → chunk-QOTDCUE5.js} +58 -18
- package/dist/cli.js +387 -206
- package/dist/cloudflare.cjs +61 -34
- package/dist/cloudflare.js +2 -2
- package/dist/lambda.cjs +61 -34
- package/dist/lambda.js +2 -2
- package/dist/slim-react/index.cjs +87 -45
- package/dist/slim-react/index.d.cts +26 -2
- package/dist/slim-react/index.d.ts +26 -2
- package/dist/slim-react/index.js +5 -1
- package/dist/ssr-render-worker.js +65 -37
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -147,24 +147,6 @@ 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
|
|
169
151
|
var MAP_KEY = "__slimReactContextMap";
|
|
170
152
|
var _g = globalThis;
|
|
@@ -189,8 +171,7 @@ function getContextValue(context) {
|
|
|
189
171
|
const map = _g[MAP_KEY];
|
|
190
172
|
if (map && map.has(context))
|
|
191
173
|
return map.get(context);
|
|
192
|
-
|
|
193
|
-
return "_defaultValue" in c ? c._defaultValue : c._currentValue;
|
|
174
|
+
return context._currentValue;
|
|
194
175
|
}
|
|
195
176
|
function pushContextValue(context, value) {
|
|
196
177
|
let map = _g[MAP_KEY];
|
|
@@ -198,8 +179,7 @@ function pushContextValue(context, value) {
|
|
|
198
179
|
map = new Map;
|
|
199
180
|
_g[MAP_KEY] = map;
|
|
200
181
|
}
|
|
201
|
-
const
|
|
202
|
-
const prev = map.has(context) ? map.get(context) : ("_defaultValue" in c) ? c._defaultValue : c._currentValue;
|
|
182
|
+
const prev = map.has(context) ? map.get(context) : context._currentValue;
|
|
203
183
|
map.set(context, value);
|
|
204
184
|
return prev;
|
|
205
185
|
}
|
|
@@ -304,14 +284,35 @@ function getTreeId() {
|
|
|
304
284
|
const stripped = (id & ~(1 << 31 - Math.clz32(id))).toString(32);
|
|
305
285
|
return stripped + overflow;
|
|
306
286
|
}
|
|
287
|
+
var REACT_MAJOR = typeof __HADARS_REACT_MAJOR__ !== "undefined" ? parseInt(String(__HADARS_REACT_MAJOR__), 10) : 19;
|
|
288
|
+
var REACT_VERSION = REACT_MAJOR < 19 ? "18.3.1" : "19.1.1";
|
|
307
289
|
function makeId() {
|
|
308
290
|
const st = s();
|
|
309
291
|
const treeId = getTreeId();
|
|
310
292
|
const n = st.localIdCounter++;
|
|
311
|
-
|
|
312
|
-
if (
|
|
313
|
-
|
|
314
|
-
|
|
293
|
+
const suffix = n > 0 ? "H" + n.toString(32) : "";
|
|
294
|
+
if (REACT_MAJOR < 19) {
|
|
295
|
+
return ":" + st.idPrefix + "R" + treeId + suffix + ":";
|
|
296
|
+
}
|
|
297
|
+
return "_R_" + st.idPrefix + treeId + suffix + "_";
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// src/slim-react/jsx.ts
|
|
301
|
+
function createElement(type, props, ...children) {
|
|
302
|
+
const normalizedProps = { ...props || {} };
|
|
303
|
+
if (children.length === 1) {
|
|
304
|
+
normalizedProps.children = children[0];
|
|
305
|
+
} else if (children.length > 1) {
|
|
306
|
+
normalizedProps.children = children;
|
|
307
|
+
}
|
|
308
|
+
const key = normalizedProps.key ?? null;
|
|
309
|
+
delete normalizedProps.key;
|
|
310
|
+
return {
|
|
311
|
+
$$typeof: SLIM_ELEMENT,
|
|
312
|
+
type,
|
|
313
|
+
props: normalizedProps,
|
|
314
|
+
key
|
|
315
|
+
};
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
// src/slim-react/hooks.ts
|
|
@@ -364,8 +365,24 @@ function use(usable) {
|
|
|
364
365
|
}
|
|
365
366
|
// src/slim-react/dispatcher.ts
|
|
366
367
|
import * as ReactNS from "react";
|
|
367
|
-
var
|
|
368
|
-
var
|
|
368
|
+
var _r19;
|
|
369
|
+
var _r18;
|
|
370
|
+
var _detected = false;
|
|
371
|
+
var _k19 = "__CLIENT_INTERNALS_DO_NOT_USE" + "_OR_WARN_USERS_THEY_CANNOT_UPGRADE";
|
|
372
|
+
var _k18 = "__SECRET_INTERNALS_DO_NOT_USE" + "_OR_YOU_WILL_BE_FIRED";
|
|
373
|
+
function _detect() {
|
|
374
|
+
if (_detected)
|
|
375
|
+
return;
|
|
376
|
+
_detected = true;
|
|
377
|
+
const r19 = ReactNS[_k19];
|
|
378
|
+
if (r19) {
|
|
379
|
+
_r19 = r19;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const raw = ReactNS[_k18];
|
|
383
|
+
if (raw?.ReactCurrentDispatcher)
|
|
384
|
+
_r18 = raw;
|
|
385
|
+
}
|
|
369
386
|
var slimDispatcher = {
|
|
370
387
|
useId: makeId,
|
|
371
388
|
readContext: (ctx) => getContextValue(ctx),
|
|
@@ -391,15 +408,25 @@ var slimDispatcher = {
|
|
|
391
408
|
useHostTransitionStatus: () => false
|
|
392
409
|
};
|
|
393
410
|
function installDispatcher() {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
411
|
+
_detect();
|
|
412
|
+
if (_r19) {
|
|
413
|
+
const prev = _r19.H;
|
|
414
|
+
_r19.H = slimDispatcher;
|
|
415
|
+
return prev;
|
|
416
|
+
}
|
|
417
|
+
if (_r18) {
|
|
418
|
+
const prev = _r18.ReactCurrentDispatcher.current;
|
|
419
|
+
_r18.ReactCurrentDispatcher.current = slimDispatcher;
|
|
420
|
+
return prev;
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
399
423
|
}
|
|
400
424
|
function restoreDispatcher(prev) {
|
|
401
|
-
|
|
402
|
-
|
|
425
|
+
_detect();
|
|
426
|
+
if (_r19)
|
|
427
|
+
_r19.H = prev;
|
|
428
|
+
else if (_r18)
|
|
429
|
+
_r18.ReactCurrentDispatcher.current = prev;
|
|
403
430
|
}
|
|
404
431
|
|
|
405
432
|
// src/slim-react/render.ts
|
|
@@ -660,7 +687,7 @@ function writeAttributes(writer, props, isSvg, skip) {
|
|
|
660
687
|
if (isSvg && key in SVG_ATTR_MAP) {
|
|
661
688
|
attrName = SVG_ATTR_MAP[key];
|
|
662
689
|
} else {
|
|
663
|
-
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key === "defaultValue" ? "value" : key === "defaultChecked" ? "checked" : key;
|
|
690
|
+
attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key === "tabIndex" ? "tabindex" : key === "defaultValue" ? "value" : key === "defaultChecked" ? "checked" : key === "contentEditable" && REACT_MAJOR < 19 ? "contenteditable" : key;
|
|
664
691
|
}
|
|
665
692
|
if (value === false || value == null) {
|
|
666
693
|
if (value === false && (attrName.charCodeAt(0) === 97 && attrName.startsWith("aria-") || attrName.charCodeAt(0) === 100 && attrName.startsWith("data-"))) {
|
|
@@ -871,7 +898,8 @@ function renderComponent(type, props, writer, isSvg, _suspenseRetries = 0) {
|
|
|
871
898
|
const LazyComp = resolved?.default ?? resolved;
|
|
872
899
|
return renderComponent(LazyComp, props, writer, isSvg);
|
|
873
900
|
}
|
|
874
|
-
|
|
901
|
+
const isConsumer = typeOf === REACT_CONSUMER || typeOf === REACT_CONTEXT && "_context" in type && !("value" in props);
|
|
902
|
+
if (isConsumer) {
|
|
875
903
|
const ctx2 = type._context;
|
|
876
904
|
const value = ctx2 ? getContextValue(ctx2) : undefined;
|
|
877
905
|
const result2 = typeof props.children === "function" ? props.children(value) : null;
|
|
@@ -2448,6 +2476,20 @@ var HadarsFolder = "./.hadars";
|
|
|
2448
2476
|
var StaticPath = `${HadarsFolder}/static`;
|
|
2449
2477
|
var HADARS_TMP_DIR = pathMod2.join(os.tmpdir(), "hadars");
|
|
2450
2478
|
var ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
|
|
2479
|
+
var readReactMajor = async () => {
|
|
2480
|
+
let dir = process.cwd();
|
|
2481
|
+
while (true) {
|
|
2482
|
+
try {
|
|
2483
|
+
const pkgPath = pathMod2.join(dir, "node_modules", "react", "package.json");
|
|
2484
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
2485
|
+
return parseInt(pkg.version.split(".")[0], 10);
|
|
2486
|
+
} catch {}
|
|
2487
|
+
const parent = pathMod2.dirname(dir);
|
|
2488
|
+
if (parent === dir)
|
|
2489
|
+
return 19;
|
|
2490
|
+
dir = parent;
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2451
2493
|
var validateOptions = (options) => {
|
|
2452
2494
|
if (!options.entry) {
|
|
2453
2495
|
throw new Error("Entry file is required");
|
|
@@ -2567,6 +2609,8 @@ var dev = async (options) => {
|
|
|
2567
2609
|
});
|
|
2568
2610
|
const workerCmd = resolveWorkerCmd(packageDir2);
|
|
2569
2611
|
console.log("Spawning SSR worker:", workerCmd.join(" "), "entry:", entry);
|
|
2612
|
+
const reactMajor = await readReactMajor();
|
|
2613
|
+
const ssrDefine = { __HADARS_REACT_MAJOR__: String(reactMajor), ...options.define };
|
|
2570
2614
|
const child = spawn(workerCmd[0], [
|
|
2571
2615
|
...workerCmd.slice(1),
|
|
2572
2616
|
`--entry=${entry}`,
|
|
@@ -2574,7 +2618,7 @@ var dev = async (options) => {
|
|
|
2574
2618
|
`--outFile=${SSR_FILENAME}`,
|
|
2575
2619
|
`--base=${baseURL}`,
|
|
2576
2620
|
...options.swcPlugins ? [`--swcPlugins=${JSON.stringify(options.swcPlugins)}`] : [],
|
|
2577
|
-
|
|
2621
|
+
`--define=${JSON.stringify(ssrDefine)}`,
|
|
2578
2622
|
...options.moduleRules ? [`--moduleRules=${JSON.stringify(options.moduleRules, (_k, v) => v instanceof RegExp ? { __re: v.source, __flags: v.flags } : v)}`] : []
|
|
2579
2623
|
], { stdio: "pipe" });
|
|
2580
2624
|
child.stdin?.end();
|
|
@@ -2748,6 +2792,7 @@ var build = async (options) => {
|
|
|
2748
2792
|
const tmpFilePath = pathMod2.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
|
|
2749
2793
|
await fs.writeFile(tmpFilePath, clientScript);
|
|
2750
2794
|
const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod2.resolve(__dirname3, options.htmlTemplate)) : undefined;
|
|
2795
|
+
const reactMajor = await readReactMajor();
|
|
2751
2796
|
console.log("Building client and server bundles in parallel...");
|
|
2752
2797
|
await Promise.all([
|
|
2753
2798
|
compileEntry(tmpFilePath, {
|
|
@@ -2779,7 +2824,7 @@ var build = async (options) => {
|
|
|
2779
2824
|
target: "node",
|
|
2780
2825
|
mode: "production",
|
|
2781
2826
|
swcPlugins: options.swcPlugins,
|
|
2782
|
-
define: options.define,
|
|
2827
|
+
define: { __HADARS_REACT_MAJOR__: String(reactMajor), ...options.define },
|
|
2783
2828
|
moduleRules: options.moduleRules,
|
|
2784
2829
|
plugins: options.plugins,
|
|
2785
2830
|
postcssPlugins: options.postcssPlugins
|
|
@@ -3184,8 +3229,166 @@ Deploy instructions:`);
|
|
|
3184
3229
|
await unlink(shimPath).catch(() => {});
|
|
3185
3230
|
}
|
|
3186
3231
|
}
|
|
3187
|
-
var
|
|
3188
|
-
|
|
3232
|
+
var _R = "\x1B[0m";
|
|
3233
|
+
var _B = "\x1B[1m";
|
|
3234
|
+
var _D = "\x1B[2m";
|
|
3235
|
+
var _C = "\x1B[36m";
|
|
3236
|
+
var _G = "\x1B[32m";
|
|
3237
|
+
var _UP_KEY = "\x1B[A";
|
|
3238
|
+
var _DOWN_KEY = "\x1B[B";
|
|
3239
|
+
var _HIDE = "\x1B[?25l";
|
|
3240
|
+
var _SHOW = "\x1B[?25h";
|
|
3241
|
+
var _cl = () => "\r\x1B[2K";
|
|
3242
|
+
var _up = (n) => n > 0 ? `\x1B[${n}A` : "";
|
|
3243
|
+
function _readKeys(handler) {
|
|
3244
|
+
return new Promise((resolve2) => {
|
|
3245
|
+
const { stdin } = process;
|
|
3246
|
+
const wasRaw = stdin.isTTY && stdin.isRaw;
|
|
3247
|
+
if (stdin.isTTY)
|
|
3248
|
+
stdin.setRawMode(true);
|
|
3249
|
+
stdin.resume();
|
|
3250
|
+
stdin.setEncoding("utf-8");
|
|
3251
|
+
const onData = (key) => {
|
|
3252
|
+
if (key === "\x03") {
|
|
3253
|
+
cleanup();
|
|
3254
|
+
process.stdout.write(_SHOW);
|
|
3255
|
+
process.exit(130);
|
|
3256
|
+
}
|
|
3257
|
+
if (handler(key))
|
|
3258
|
+
cleanup();
|
|
3259
|
+
};
|
|
3260
|
+
const cleanup = () => {
|
|
3261
|
+
stdin.removeListener("data", onData);
|
|
3262
|
+
if (stdin.isTTY && !wasRaw)
|
|
3263
|
+
stdin.setRawMode(false);
|
|
3264
|
+
stdin.pause();
|
|
3265
|
+
resolve2();
|
|
3266
|
+
};
|
|
3267
|
+
stdin.on("data", onData);
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
3270
|
+
async function promptRadio(question, options) {
|
|
3271
|
+
const out = process.stdout;
|
|
3272
|
+
let cursor = 0;
|
|
3273
|
+
const total = 1 + options.length;
|
|
3274
|
+
const render = (redraw) => {
|
|
3275
|
+
if (redraw)
|
|
3276
|
+
out.write(_up(total));
|
|
3277
|
+
out.write(`${_cl()} ${_B}${question}${_R}
|
|
3278
|
+
`);
|
|
3279
|
+
for (let i = 0;i < options.length; i++) {
|
|
3280
|
+
const arrow = i === cursor ? `${_C}❯${_R}` : " ";
|
|
3281
|
+
const text = i === cursor ? `${_B}${options[i]}${_R}` : `${_D}${options[i]}${_R}`;
|
|
3282
|
+
out.write(`${_cl()} ${arrow} ${text}
|
|
3283
|
+
`);
|
|
3284
|
+
}
|
|
3285
|
+
};
|
|
3286
|
+
out.write(_HIDE);
|
|
3287
|
+
render(false);
|
|
3288
|
+
await _readKeys((key) => {
|
|
3289
|
+
if (key === _UP_KEY && cursor > 0) {
|
|
3290
|
+
cursor--;
|
|
3291
|
+
render(true);
|
|
3292
|
+
} else if (key === _DOWN_KEY && cursor < options.length - 1) {
|
|
3293
|
+
cursor++;
|
|
3294
|
+
render(true);
|
|
3295
|
+
} else if (key === "\r") {
|
|
3296
|
+
return true;
|
|
3297
|
+
}
|
|
3298
|
+
return false;
|
|
3299
|
+
});
|
|
3300
|
+
out.write(_up(total));
|
|
3301
|
+
out.write(`${_cl()} ${question} ${_C}${_B}${options[cursor]}${_R}
|
|
3302
|
+
`);
|
|
3303
|
+
for (let i = 0;i < options.length; i++)
|
|
3304
|
+
out.write(`${_cl()}
|
|
3305
|
+
`);
|
|
3306
|
+
out.write(_up(options.length));
|
|
3307
|
+
out.write(_SHOW);
|
|
3308
|
+
return cursor;
|
|
3309
|
+
}
|
|
3310
|
+
async function promptMultiSelect(question, options) {
|
|
3311
|
+
const out = process.stdout;
|
|
3312
|
+
let cursor = 0;
|
|
3313
|
+
const selected = new Set;
|
|
3314
|
+
const total = 2 + options.length;
|
|
3315
|
+
const render = (redraw) => {
|
|
3316
|
+
if (redraw)
|
|
3317
|
+
out.write(_up(total));
|
|
3318
|
+
out.write(`${_cl()} ${_B}${question}${_R}
|
|
3319
|
+
`);
|
|
3320
|
+
out.write(`${_cl()} ${_D}↑↓ navigate · Space select · Enter confirm${_R}
|
|
3321
|
+
`);
|
|
3322
|
+
for (let i = 0;i < options.length; i++) {
|
|
3323
|
+
const arrow = i === cursor ? `${_C}❯${_R}` : " ";
|
|
3324
|
+
const box = selected.has(i) ? `${_G}●${_R}` : `${_D}○${_R}`;
|
|
3325
|
+
const text = i === cursor ? `${_B}${options[i]}${_R}` : `${_D}${options[i]}${_R}`;
|
|
3326
|
+
out.write(`${_cl()} ${arrow} ${box} ${text}
|
|
3327
|
+
`);
|
|
3328
|
+
}
|
|
3329
|
+
};
|
|
3330
|
+
out.write(_HIDE);
|
|
3331
|
+
render(false);
|
|
3332
|
+
await _readKeys((key) => {
|
|
3333
|
+
if (key === _UP_KEY && cursor > 0) {
|
|
3334
|
+
cursor--;
|
|
3335
|
+
render(true);
|
|
3336
|
+
} else if (key === _DOWN_KEY && cursor < options.length - 1) {
|
|
3337
|
+
cursor++;
|
|
3338
|
+
render(true);
|
|
3339
|
+
} else if (key === " ") {
|
|
3340
|
+
selected.has(cursor) ? selected.delete(cursor) : selected.add(cursor);
|
|
3341
|
+
render(true);
|
|
3342
|
+
} else if (key === "\r") {
|
|
3343
|
+
return true;
|
|
3344
|
+
}
|
|
3345
|
+
return false;
|
|
3346
|
+
});
|
|
3347
|
+
const picked = [...selected].sort((a, b) => a - b);
|
|
3348
|
+
const summary = picked.length > 0 ? picked.map((i) => options[i]).join(", ") : "none";
|
|
3349
|
+
out.write(_up(total));
|
|
3350
|
+
out.write(`${_cl()} ${question} ${_C}${_B}${summary}${_R}
|
|
3351
|
+
`);
|
|
3352
|
+
for (let i = 0;i < total - 1; i++)
|
|
3353
|
+
out.write(`${_cl()}
|
|
3354
|
+
`);
|
|
3355
|
+
out.write(_up(total - 1));
|
|
3356
|
+
out.write(_SHOW);
|
|
3357
|
+
return picked;
|
|
3358
|
+
}
|
|
3359
|
+
var PLUGINS = [
|
|
3360
|
+
{ pkg: "@swc/plugin-emotion", version: "12.0.0", label: "Emotion (CSS-in-JS)" },
|
|
3361
|
+
{ pkg: "@swc/plugin-styled-components", version: "10.0.0", label: "styled-components" },
|
|
3362
|
+
{ pkg: "@swc/plugin-relay", version: "10.0.0", label: "Relay (GraphQL)" },
|
|
3363
|
+
{ pkg: "@swc/plugin-styled-jsx", version: "11.0.0", label: "styled-jsx" },
|
|
3364
|
+
{ pkg: "@swc/plugin-transform-imports", version: "10.0.0", label: "transform-imports" },
|
|
3365
|
+
{ pkg: "@swc/plugin-loadable-components", version: "9.0.0", label: "Loadable Components" },
|
|
3366
|
+
{ pkg: "@swc/plugin-formatjs", version: "7.0.0", label: "FormatJS (i18n)" }
|
|
3367
|
+
];
|
|
3368
|
+
function renderSwcPluginsConfig(plugins) {
|
|
3369
|
+
if (plugins.length === 0)
|
|
3370
|
+
return "";
|
|
3371
|
+
const lines = plugins.map((p) => {
|
|
3372
|
+
if (p.pkg === "@swc/plugin-relay") {
|
|
3373
|
+
return ` ['${p.pkg}', { rootDir: process.cwd(), artifactDirectory: 'src/__generated__' }],`;
|
|
3374
|
+
}
|
|
3375
|
+
return ` ['${p.pkg}', {}],`;
|
|
3376
|
+
});
|
|
3377
|
+
return `
|
|
3378
|
+
swcPlugins: [
|
|
3379
|
+
${lines.join(`
|
|
3380
|
+
`)}
|
|
3381
|
+
],`;
|
|
3382
|
+
}
|
|
3383
|
+
function buildTemplates(name, opts) {
|
|
3384
|
+
const { useTypeScript, plugins } = opts;
|
|
3385
|
+
const appExt = useTypeScript ? "tsx" : "jsx";
|
|
3386
|
+
const cfgExt = useTypeScript ? "ts" : "js";
|
|
3387
|
+
const tsOrJs = useTypeScript ? "tsconfig.json" : "jsconfig.json";
|
|
3388
|
+
const pluginDeps = {};
|
|
3389
|
+
for (const p of plugins)
|
|
3390
|
+
pluginDeps[p.pkg] = p.version;
|
|
3391
|
+
const packageJson = JSON.stringify({
|
|
3189
3392
|
name,
|
|
3190
3393
|
version: "0.1.0",
|
|
3191
3394
|
type: "module",
|
|
@@ -3199,19 +3402,28 @@ var TEMPLATES = {
|
|
|
3199
3402
|
hadars: "latest",
|
|
3200
3403
|
react: "^19.0.0",
|
|
3201
3404
|
"react-dom": "^19.0.0"
|
|
3202
|
-
}
|
|
3405
|
+
},
|
|
3406
|
+
...Object.keys(pluginDeps).length > 0 ? { devDependencies: pluginDeps } : {}
|
|
3203
3407
|
}, null, 2) + `
|
|
3204
|
-
|
|
3205
|
-
|
|
3408
|
+
`;
|
|
3409
|
+
const swcSection = renderSwcPluginsConfig(plugins);
|
|
3410
|
+
const hadarsConfig = useTypeScript ? `import type { HadarsOptions } from 'hadars';
|
|
3206
3411
|
|
|
3207
3412
|
const config: HadarsOptions = {
|
|
3208
3413
|
entry: 'src/App.tsx',
|
|
3209
|
-
port: 3000
|
|
3414
|
+
port: 3000,${swcSection}
|
|
3210
3415
|
};
|
|
3211
3416
|
|
|
3212
3417
|
export default config;
|
|
3213
|
-
|
|
3214
|
-
|
|
3418
|
+
` : `/** @type {import('hadars').HadarsOptions} */
|
|
3419
|
+
const config = {
|
|
3420
|
+
entry: 'src/App.jsx',
|
|
3421
|
+
port: 3000,${swcSection}
|
|
3422
|
+
};
|
|
3423
|
+
|
|
3424
|
+
export default config;
|
|
3425
|
+
`;
|
|
3426
|
+
const tsConfigContent = useTypeScript ? JSON.stringify({
|
|
3215
3427
|
compilerOptions: {
|
|
3216
3428
|
lib: ["ESNext", "DOM"],
|
|
3217
3429
|
target: "ESNext",
|
|
@@ -3226,215 +3438,184 @@ export default config;
|
|
|
3226
3438
|
skipLibCheck: true
|
|
3227
3439
|
}
|
|
3228
3440
|
}, null, 2) + `
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3441
|
+
` : JSON.stringify({
|
|
3442
|
+
compilerOptions: {
|
|
3443
|
+
lib: ["ESNext", "DOM"],
|
|
3444
|
+
target: "ESNext",
|
|
3445
|
+
module: "ESNext",
|
|
3446
|
+
moduleResolution: "bundler",
|
|
3447
|
+
jsx: "react-jsx",
|
|
3448
|
+
checkJs: true,
|
|
3449
|
+
noEmit: true,
|
|
3450
|
+
skipLibCheck: true
|
|
3451
|
+
}
|
|
3452
|
+
}, null, 2) + `
|
|
3453
|
+
`;
|
|
3454
|
+
const appImports = useTypeScript ? `import React from 'react';
|
|
3455
|
+
import { HadarsHead, type HadarsApp } from 'hadars';` : `import React from 'react';
|
|
3456
|
+
import { HadarsHead } from 'hadars';`;
|
|
3457
|
+
const appSignature = useTypeScript ? `const App: HadarsApp<{}> = () => {` : `const App = () => {`;
|
|
3458
|
+
const appContent = `${appImports}
|
|
3236
3459
|
|
|
3237
3460
|
const css = \`
|
|
3238
|
-
*, *::before, *::after { box-sizing: border-box;
|
|
3461
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
3239
3462
|
|
|
3240
|
-
|
|
3241
|
-
font-family: -
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3463
|
+
:root {
|
|
3464
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
3465
|
+
line-height: 1.5;
|
|
3466
|
+
font-weight: 400;
|
|
3467
|
+
color-scheme: light dark;
|
|
3468
|
+
color: rgba(255, 255, 255, 0.87);
|
|
3469
|
+
background-color: #242424;
|
|
3470
|
+
font-synthesis: none;
|
|
3471
|
+
text-rendering: optimizeLegibility;
|
|
3472
|
+
-webkit-font-smoothing: antialiased;
|
|
3245
3473
|
}
|
|
3246
3474
|
|
|
3247
|
-
|
|
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; }
|
|
3475
|
+
body { margin: 0; }
|
|
3258
3476
|
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
padding: 5rem 1rem 4rem;
|
|
3262
|
-
max-width: 680px;
|
|
3477
|
+
#app {
|
|
3478
|
+
max-width: 1280px;
|
|
3263
3479
|
margin: 0 auto;
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
display:
|
|
3267
|
-
|
|
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;
|
|
3480
|
+
padding: 2rem;
|
|
3481
|
+
text-align: center;
|
|
3482
|
+
display: flex;
|
|
3483
|
+
flex-direction: column;
|
|
3295
3484
|
align-items: center;
|
|
3296
|
-
|
|
3297
|
-
|
|
3485
|
+
justify-content: center;
|
|
3486
|
+
min-height: 100vh;
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
.logos { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-bottom: 1.5rem; }
|
|
3490
|
+
|
|
3491
|
+
.logo {
|
|
3492
|
+
height: 6em;
|
|
3493
|
+
padding: 1.5em;
|
|
3494
|
+
will-change: filter;
|
|
3495
|
+
transition: filter 300ms;
|
|
3496
|
+
}
|
|
3497
|
+
.logo-react { animation: spin 20s linear infinite; }
|
|
3498
|
+
.logo-react:hover { filter: drop-shadow(0 0 2em #61dafbaa); }
|
|
3499
|
+
|
|
3500
|
+
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
|
3501
|
+
@media (prefers-reduced-motion: reduce) { .logo-react { animation: none; } }
|
|
3502
|
+
|
|
3503
|
+
h1 { font-size: 3.2em; line-height: 1.1; }
|
|
3504
|
+
|
|
3505
|
+
.card { padding: 2em; }
|
|
3506
|
+
.card p { color: #aaa; }
|
|
3507
|
+
|
|
3508
|
+
button {
|
|
3298
3509
|
border-radius: 8px;
|
|
3299
|
-
|
|
3300
|
-
|
|
3510
|
+
border: 1px solid transparent;
|
|
3511
|
+
padding: 0.6em 1.2em;
|
|
3512
|
+
font-size: 1em;
|
|
3513
|
+
font-weight: 500;
|
|
3514
|
+
font-family: inherit;
|
|
3515
|
+
background-color: #1a1a1a;
|
|
3301
3516
|
cursor: pointer;
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
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; }
|
|
3517
|
+
transition: border-color 0.25s;
|
|
3518
|
+
}
|
|
3519
|
+
button:hover { border-color: #a78bfa; }
|
|
3520
|
+
button:focus-visible { outline: 4px auto -webkit-focus-ring-color; }
|
|
3310
3521
|
|
|
3311
|
-
.
|
|
3312
|
-
|
|
3313
|
-
|
|
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; }
|
|
3522
|
+
.hint { color: #555; font-size: 0.9em; }
|
|
3523
|
+
.hint a { color: inherit; }
|
|
3524
|
+
.hint a:hover { color: #a78bfa; }
|
|
3328
3525
|
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3526
|
+
@media (prefers-color-scheme: light) {
|
|
3527
|
+
:root { color: #213547; background-color: #ffffff; }
|
|
3528
|
+
button { background-color: #f9f9f9; }
|
|
3529
|
+
.card p { color: #666; }
|
|
3530
|
+
.hint { color: #999; }
|
|
3334
3531
|
}
|
|
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
3532
|
\`;
|
|
3346
3533
|
|
|
3347
|
-
|
|
3534
|
+
${appSignature}
|
|
3348
3535
|
const [count, setCount] = React.useState(0);
|
|
3349
3536
|
|
|
3350
3537
|
return (
|
|
3351
3538
|
<>
|
|
3352
3539
|
<HadarsHead status={200}>
|
|
3353
|
-
<title
|
|
3540
|
+
<title>${name}</title>
|
|
3354
3541
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
3355
3542
|
<style data-id="app-styles" dangerouslySetInnerHTML={{ __html: css }} />
|
|
3356
3543
|
</HadarsHead>
|
|
3357
3544
|
|
|
3358
|
-
<
|
|
3359
|
-
<
|
|
3360
|
-
|
|
3361
|
-
|
|
3545
|
+
<div id="app">
|
|
3546
|
+
<div className="logos">
|
|
3547
|
+
<a href="https://react.dev" target="_blank" rel="noopener noreferrer">
|
|
3548
|
+
<svg className="logo logo-react" xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.232 23 20.463" aria-label="React">
|
|
3549
|
+
<circle cx="0" cy="0" r="2.05" fill="#61dafb" />
|
|
3550
|
+
<g stroke="#61dafb" strokeWidth="1" fill="none">
|
|
3551
|
+
<ellipse rx="11" ry="4.2" />
|
|
3552
|
+
<ellipse rx="11" ry="4.2" transform="rotate(60)" />
|
|
3553
|
+
<ellipse rx="11" ry="4.2" transform="rotate(120)" />
|
|
3554
|
+
</g>
|
|
3555
|
+
</svg>
|
|
3556
|
+
</a>
|
|
3362
3557
|
</div>
|
|
3363
|
-
</nav>
|
|
3364
3558
|
|
|
3365
|
-
|
|
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.
|
|
3371
|
-
</p>
|
|
3372
|
-
<div className="hero-actions">
|
|
3373
|
-
<button className="btn btn-primary" onClick={() => setCount(c => c + 1)}>
|
|
3374
|
-
Try the counter ↓
|
|
3375
|
-
</button>
|
|
3376
|
-
</div>
|
|
3377
|
-
</section>
|
|
3559
|
+
<h1>React + hadars</h1>
|
|
3378
3560
|
|
|
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
3561
|
<div className="card">
|
|
3386
|
-
<
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
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>
|
|
3562
|
+
<button onClick={() => setCount(c => c + 1)}>
|
|
3563
|
+
count is {count}
|
|
3564
|
+
</button>
|
|
3565
|
+
<p>
|
|
3566
|
+
Edit <code>src/App.${appExt}</code> and save to test HMR
|
|
3567
|
+
</p>
|
|
3399
3568
|
</div>
|
|
3400
|
-
</div>
|
|
3401
3569
|
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
<
|
|
3406
|
-
|
|
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>
|
|
3570
|
+
<p className="hint">
|
|
3571
|
+
<a href="https://hadars.xyz" target="_blank" rel="noopener noreferrer">hadars docs</a>
|
|
3572
|
+
·
|
|
3573
|
+
<a href="https://react.dev" target="_blank" rel="noopener noreferrer">react docs</a>
|
|
3574
|
+
</p>
|
|
3411
3575
|
</div>
|
|
3412
|
-
|
|
3413
3576
|
</>
|
|
3414
3577
|
);
|
|
3415
3578
|
};
|
|
3416
3579
|
|
|
3417
3580
|
export default App;
|
|
3418
|
-
|
|
3419
|
-
|
|
3581
|
+
`;
|
|
3582
|
+
return {
|
|
3583
|
+
"package.json": packageJson,
|
|
3584
|
+
[`hadars.config.${cfgExt}`]: hadarsConfig,
|
|
3585
|
+
[tsOrJs]: tsConfigContent,
|
|
3586
|
+
".gitignore": `node_modules/
|
|
3587
|
+
.hadars/
|
|
3588
|
+
dist/
|
|
3589
|
+
`,
|
|
3590
|
+
[`src/App.${appExt}`]: appContent
|
|
3591
|
+
};
|
|
3592
|
+
}
|
|
3420
3593
|
async function createProject(name, cwd) {
|
|
3421
3594
|
const dir = resolve(cwd, name);
|
|
3422
3595
|
if (existsSync3(dir)) {
|
|
3423
3596
|
console.error(`Directory already exists: ${dir}`);
|
|
3424
3597
|
process.exit(1);
|
|
3425
3598
|
}
|
|
3426
|
-
|
|
3599
|
+
const useTypeScript = await promptRadio("Language?", ["TypeScript", "JavaScript"]) === 0;
|
|
3600
|
+
const selectedIndices = await promptMultiSelect("Select SWC plugins to enable (optional):", PLUGINS.map((p) => p.label));
|
|
3601
|
+
const plugins = selectedIndices.map((i) => PLUGINS[i]);
|
|
3602
|
+
console.log(`
|
|
3603
|
+
Creating hadars project in ${dir}`);
|
|
3427
3604
|
await mkdir2(join2(dir, "src"), { recursive: true });
|
|
3428
|
-
|
|
3429
|
-
|
|
3605
|
+
const files = buildTemplates(name, { useTypeScript, plugins });
|
|
3606
|
+
for (const [file, content] of Object.entries(files)) {
|
|
3430
3607
|
await writeFile2(join2(dir, file), content, "utf-8");
|
|
3431
3608
|
console.log(` created ${file}`);
|
|
3432
3609
|
}
|
|
3610
|
+
const installHint = plugins.length > 0 ? `
|
|
3611
|
+
# Also install selected SWC plugins:
|
|
3612
|
+
npm install -D ${plugins.map((p) => `${p.pkg}@${p.version}`).join(" ")}
|
|
3613
|
+
` : "";
|
|
3433
3614
|
console.log(`
|
|
3434
3615
|
Done! Next steps:
|
|
3435
3616
|
|
|
3436
3617
|
cd ${name}
|
|
3437
|
-
npm install # or: bun install / pnpm install
|
|
3618
|
+
npm install # or: bun install / pnpm install${installHint}
|
|
3438
3619
|
npm run dev # or: bun run dev
|
|
3439
3620
|
`);
|
|
3440
3621
|
}
|