blumenjs 0.1.2 → 0.1.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/dist/cli/blumen.js +112 -31
- package/dist/cli/commands/build.js +24 -1
- package/dist/cli/commands/create.js +106 -18
- package/dist/cli/commands/dev.js +26 -9
- package/dist/cli/commands/start.js +26 -3
- package/dist/cli/utils.js +25 -1
- package/dist/templates/app/pages/BlumenStarter.tsx +3 -5
- package/package.json +1 -1
package/dist/cli/blumen.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
import { spawn, execSync } from "child_process";
|
|
4
4
|
|
|
5
5
|
// cli/utils.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
6
9
|
var c = {
|
|
7
10
|
reset: "\x1B[0m",
|
|
8
11
|
bold: "\x1B[1m",
|
|
@@ -24,10 +27,30 @@ var log = {
|
|
|
24
27
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
25
28
|
blank: () => console.log("")
|
|
26
29
|
};
|
|
30
|
+
function getVersion() {
|
|
31
|
+
try {
|
|
32
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
33
|
+
let dir = path.dirname(thisFile);
|
|
34
|
+
for (let i = 0; i < 5; i++) {
|
|
35
|
+
const pkgFile = path.join(dir, "package.json");
|
|
36
|
+
if (fs.existsSync(pkgFile)) {
|
|
37
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
38
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
39
|
+
return pkg.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
dir = path.dirname(dir);
|
|
43
|
+
}
|
|
44
|
+
return "0.0.0";
|
|
45
|
+
} catch {
|
|
46
|
+
return "0.0.0";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
27
49
|
function banner() {
|
|
50
|
+
const version = getVersion();
|
|
28
51
|
console.log("");
|
|
29
52
|
console.log(
|
|
30
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
53
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
31
54
|
);
|
|
32
55
|
console.log(
|
|
33
56
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -147,17 +170,11 @@ async function dev() {
|
|
|
147
170
|
log.success(`${c.bold}Ready!${c.reset} All services are running.`);
|
|
148
171
|
log.blank();
|
|
149
172
|
console.log(
|
|
150
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}App${c.reset}:
|
|
151
|
-
);
|
|
152
|
-
console.log(
|
|
153
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}SSR${c.reset}: ${c.dim}http://localhost:4000${c.reset}`
|
|
154
|
-
);
|
|
155
|
-
console.log(
|
|
156
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}HMR${c.reset}: ${c.dim}http://localhost:3100${c.reset}`
|
|
173
|
+
` ${c.dim}\u279C${c.reset} ${c.bold}App${c.reset}: ${c.cyan}http://localhost:3000${c.reset}`
|
|
157
174
|
);
|
|
158
175
|
log.blank();
|
|
159
176
|
console.log(
|
|
160
|
-
` ${c.dim}Press ${c.bold}Ctrl+C${c.reset}${c.dim} to stop
|
|
177
|
+
` ${c.dim}Press ${c.bold}Ctrl+C${c.reset}${c.dim} to stop.${c.reset}`
|
|
161
178
|
);
|
|
162
179
|
log.blank();
|
|
163
180
|
divider();
|
|
@@ -231,10 +248,10 @@ async function build() {
|
|
|
231
248
|
|
|
232
249
|
// cli/commands/start.ts
|
|
233
250
|
import { spawn as spawn2 } from "child_process";
|
|
234
|
-
import * as
|
|
251
|
+
import * as fs2 from "fs";
|
|
235
252
|
async function start() {
|
|
236
253
|
banner();
|
|
237
|
-
if (!
|
|
254
|
+
if (!fs2.existsSync("dist/ssr-server.js")) {
|
|
238
255
|
log.error("Production build not found.");
|
|
239
256
|
log.info(
|
|
240
257
|
`Run ${c.bold}blumen build${c.reset} first to create a production build.`
|
|
@@ -319,23 +336,23 @@ async function start() {
|
|
|
319
336
|
}
|
|
320
337
|
|
|
321
338
|
// cli/commands/create.ts
|
|
322
|
-
import * as
|
|
323
|
-
import * as
|
|
339
|
+
import * as fs3 from "fs";
|
|
340
|
+
import * as path2 from "path";
|
|
324
341
|
import { execSync as execSync3 } from "child_process";
|
|
325
342
|
function getFrameworkRoot() {
|
|
326
|
-
const cliEntry =
|
|
327
|
-
const cliDir =
|
|
328
|
-
return
|
|
343
|
+
const cliEntry = fs3.realpathSync(process.argv[1]);
|
|
344
|
+
const cliDir = path2.dirname(cliEntry);
|
|
345
|
+
return path2.resolve(cliDir, "..");
|
|
329
346
|
}
|
|
330
347
|
function readProjectFile(relativePath) {
|
|
331
348
|
const root = getFrameworkRoot();
|
|
332
|
-
const bundledPath =
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
349
|
+
const bundledPath = path2.join(root, "templates", relativePath);
|
|
350
|
+
if (fs3.existsSync(bundledPath)) {
|
|
351
|
+
return fs3.readFileSync(bundledPath, "utf-8");
|
|
335
352
|
}
|
|
336
|
-
const sourcePath =
|
|
337
|
-
if (
|
|
338
|
-
return
|
|
353
|
+
const sourcePath = path2.join(root, relativePath);
|
|
354
|
+
if (fs3.existsSync(sourcePath)) {
|
|
355
|
+
return fs3.readFileSync(sourcePath, "utf-8");
|
|
339
356
|
}
|
|
340
357
|
throw new Error(`Template file not found: ${relativePath}`);
|
|
341
358
|
}
|
|
@@ -538,10 +555,71 @@ export function Link({ href, children, onClick, target, ...rest }: LinkProps) {
|
|
|
538
555
|
|
|
539
556
|
export default Link;
|
|
540
557
|
`;
|
|
558
|
+
var DOCKERFILE = `# \u2500\u2500 Stage 1: Build Go server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
559
|
+
FROM golang:1.22-alpine AS go-builder
|
|
560
|
+
WORKDIR /app
|
|
561
|
+
COPY go-server/ go-server/
|
|
562
|
+
WORKDIR /app/go-server
|
|
563
|
+
RUN go build -o /app/blumen-server main.go
|
|
564
|
+
|
|
565
|
+
# \u2500\u2500 Stage 2: Build Node SSR + client bundle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
566
|
+
FROM node:20-alpine AS node-builder
|
|
567
|
+
WORKDIR /app
|
|
568
|
+
COPY package.json tsconfig.json webpack.config.js ./
|
|
569
|
+
COPY app/ app/
|
|
570
|
+
COPY node-ssr/ node-ssr/
|
|
571
|
+
COPY scripts/ scripts/
|
|
572
|
+
RUN npm install --production=false
|
|
573
|
+
RUN npx tsx scripts/generate-routes.ts
|
|
574
|
+
RUN npx webpack --mode production
|
|
575
|
+
RUN npx esbuild node-ssr/server.ts --bundle --platform=node --format=esm \\
|
|
576
|
+
--outfile=dist/ssr-server.js --external:react --external:react-dom
|
|
577
|
+
|
|
578
|
+
# \u2500\u2500 Stage 3: Production image \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
579
|
+
FROM node:20-alpine
|
|
580
|
+
RUN apk add --no-cache libc6-compat
|
|
581
|
+
WORKDIR /app
|
|
582
|
+
|
|
583
|
+
# Copy Go binary
|
|
584
|
+
COPY --from=go-builder /app/blumen-server ./blumen-server
|
|
585
|
+
|
|
586
|
+
# Copy Node SSR server + production deps
|
|
587
|
+
COPY --from=node-builder /app/dist/ ./dist/
|
|
588
|
+
COPY --from=node-builder /app/static/ ./static/
|
|
589
|
+
COPY --from=node-builder /app/node_modules/ ./node_modules/
|
|
590
|
+
COPY --from=node-builder /app/package.json ./
|
|
591
|
+
|
|
592
|
+
EXPOSE 3000 4000
|
|
593
|
+
|
|
594
|
+
# Start both services (Go on :3000, Node SSR on :4000)
|
|
595
|
+
CMD sh -c './blumen-server & NODE_ENV=production node dist/ssr-server.js'
|
|
596
|
+
`;
|
|
597
|
+
var DOCKER_COMPOSE = `services:
|
|
598
|
+
app:
|
|
599
|
+
build: .
|
|
600
|
+
ports:
|
|
601
|
+
- "3000:3000"
|
|
602
|
+
environment:
|
|
603
|
+
- NODE_ENV=production
|
|
604
|
+
restart: unless-stopped
|
|
605
|
+
healthcheck:
|
|
606
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000"]
|
|
607
|
+
interval: 30s
|
|
608
|
+
timeout: 5s
|
|
609
|
+
retries: 3
|
|
610
|
+
start_period: 10s
|
|
611
|
+
`;
|
|
612
|
+
var DOCKERIGNORE = `node_modules
|
|
613
|
+
dist
|
|
614
|
+
static/js/bundle.js
|
|
615
|
+
.git
|
|
616
|
+
*.log
|
|
617
|
+
.DS_Store
|
|
618
|
+
`;
|
|
541
619
|
function writeFile(base, relPath, content) {
|
|
542
|
-
const fullPath =
|
|
543
|
-
|
|
544
|
-
|
|
620
|
+
const fullPath = path2.join(base, relPath);
|
|
621
|
+
fs3.mkdirSync(path2.dirname(fullPath), { recursive: true });
|
|
622
|
+
fs3.writeFileSync(fullPath, content, "utf-8");
|
|
545
623
|
}
|
|
546
624
|
function getTemplateFiles(projectName) {
|
|
547
625
|
return [
|
|
@@ -563,7 +641,11 @@ function getTemplateFiles(projectName) {
|
|
|
563
641
|
["go-server/main.go", readProjectFile("go-server/main.go")],
|
|
564
642
|
["scripts/generate-routes.ts", readProjectFile("scripts/generate-routes.ts")],
|
|
565
643
|
// Placeholder
|
|
566
|
-
["static/js/.gitkeep", ""]
|
|
644
|
+
["static/js/.gitkeep", ""],
|
|
645
|
+
// Docker support (production deployment)
|
|
646
|
+
["Dockerfile", DOCKERFILE],
|
|
647
|
+
["docker-compose.yml", DOCKER_COMPOSE],
|
|
648
|
+
[".dockerignore", DOCKERIGNORE]
|
|
567
649
|
];
|
|
568
650
|
}
|
|
569
651
|
async function create(projectName) {
|
|
@@ -578,8 +660,8 @@ async function create(projectName) {
|
|
|
578
660
|
);
|
|
579
661
|
process.exit(1);
|
|
580
662
|
}
|
|
581
|
-
const projectDir =
|
|
582
|
-
if (
|
|
663
|
+
const projectDir = path2.resolve(process.cwd(), projectName);
|
|
664
|
+
if (fs3.existsSync(projectDir)) {
|
|
583
665
|
log.error(`Directory ${c.bold}${projectName}${c.reset} already exists.`);
|
|
584
666
|
process.exit(1);
|
|
585
667
|
}
|
|
@@ -628,7 +710,6 @@ async function create(projectName) {
|
|
|
628
710
|
}
|
|
629
711
|
|
|
630
712
|
// cli/blumen.ts
|
|
631
|
-
var VERSION = "0.1.0";
|
|
632
713
|
async function main() {
|
|
633
714
|
const command = process.argv[2];
|
|
634
715
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -653,8 +734,8 @@ async function main() {
|
|
|
653
734
|
console.log("");
|
|
654
735
|
return;
|
|
655
736
|
}
|
|
656
|
-
if (command === "--version" || command === "-v") {
|
|
657
|
-
console.log(`blumen v${
|
|
737
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
738
|
+
console.log(`blumen v${getVersion()}`);
|
|
658
739
|
return;
|
|
659
740
|
}
|
|
660
741
|
switch (command) {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
3
|
|
|
4
4
|
// cli/utils.ts
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
5
8
|
var c = {
|
|
6
9
|
reset: "\x1B[0m",
|
|
7
10
|
bold: "\x1B[1m",
|
|
@@ -23,10 +26,30 @@ var log = {
|
|
|
23
26
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
24
27
|
blank: () => console.log("")
|
|
25
28
|
};
|
|
29
|
+
function getVersion() {
|
|
30
|
+
try {
|
|
31
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
32
|
+
let dir = path.dirname(thisFile);
|
|
33
|
+
for (let i = 0; i < 5; i++) {
|
|
34
|
+
const pkgFile = path.join(dir, "package.json");
|
|
35
|
+
if (fs.existsSync(pkgFile)) {
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
37
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
38
|
+
return pkg.version;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
dir = path.dirname(dir);
|
|
42
|
+
}
|
|
43
|
+
return "0.0.0";
|
|
44
|
+
} catch {
|
|
45
|
+
return "0.0.0";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
26
48
|
function banner() {
|
|
49
|
+
const version = getVersion();
|
|
27
50
|
console.log("");
|
|
28
51
|
console.log(
|
|
29
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
52
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
30
53
|
);
|
|
31
54
|
console.log(
|
|
32
55
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// cli/commands/create.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as fs2 from "fs";
|
|
3
|
+
import * as path2 from "path";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
5
|
|
|
6
6
|
// cli/utils.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
7
10
|
var c = {
|
|
8
11
|
reset: "\x1B[0m",
|
|
9
12
|
bold: "\x1B[1m",
|
|
@@ -25,10 +28,30 @@ var log = {
|
|
|
25
28
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
26
29
|
blank: () => console.log("")
|
|
27
30
|
};
|
|
31
|
+
function getVersion() {
|
|
32
|
+
try {
|
|
33
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
34
|
+
let dir = path.dirname(thisFile);
|
|
35
|
+
for (let i = 0; i < 5; i++) {
|
|
36
|
+
const pkgFile = path.join(dir, "package.json");
|
|
37
|
+
if (fs.existsSync(pkgFile)) {
|
|
38
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
39
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
40
|
+
return pkg.version;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
dir = path.dirname(dir);
|
|
44
|
+
}
|
|
45
|
+
return "0.0.0";
|
|
46
|
+
} catch {
|
|
47
|
+
return "0.0.0";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
28
50
|
function banner() {
|
|
51
|
+
const version = getVersion();
|
|
29
52
|
console.log("");
|
|
30
53
|
console.log(
|
|
31
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
54
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
32
55
|
);
|
|
33
56
|
console.log(
|
|
34
57
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -65,19 +88,19 @@ async function select(question, options) {
|
|
|
65
88
|
|
|
66
89
|
// cli/commands/create.ts
|
|
67
90
|
function getFrameworkRoot() {
|
|
68
|
-
const cliEntry =
|
|
69
|
-
const cliDir =
|
|
70
|
-
return
|
|
91
|
+
const cliEntry = fs2.realpathSync(process.argv[1]);
|
|
92
|
+
const cliDir = path2.dirname(cliEntry);
|
|
93
|
+
return path2.resolve(cliDir, "..");
|
|
71
94
|
}
|
|
72
95
|
function readProjectFile(relativePath) {
|
|
73
96
|
const root = getFrameworkRoot();
|
|
74
|
-
const bundledPath =
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
97
|
+
const bundledPath = path2.join(root, "templates", relativePath);
|
|
98
|
+
if (fs2.existsSync(bundledPath)) {
|
|
99
|
+
return fs2.readFileSync(bundledPath, "utf-8");
|
|
77
100
|
}
|
|
78
|
-
const sourcePath =
|
|
79
|
-
if (
|
|
80
|
-
return
|
|
101
|
+
const sourcePath = path2.join(root, relativePath);
|
|
102
|
+
if (fs2.existsSync(sourcePath)) {
|
|
103
|
+
return fs2.readFileSync(sourcePath, "utf-8");
|
|
81
104
|
}
|
|
82
105
|
throw new Error(`Template file not found: ${relativePath}`);
|
|
83
106
|
}
|
|
@@ -280,10 +303,71 @@ export function Link({ href, children, onClick, target, ...rest }: LinkProps) {
|
|
|
280
303
|
|
|
281
304
|
export default Link;
|
|
282
305
|
`;
|
|
306
|
+
var DOCKERFILE = `# \u2500\u2500 Stage 1: Build Go server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
307
|
+
FROM golang:1.22-alpine AS go-builder
|
|
308
|
+
WORKDIR /app
|
|
309
|
+
COPY go-server/ go-server/
|
|
310
|
+
WORKDIR /app/go-server
|
|
311
|
+
RUN go build -o /app/blumen-server main.go
|
|
312
|
+
|
|
313
|
+
# \u2500\u2500 Stage 2: Build Node SSR + client bundle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
314
|
+
FROM node:20-alpine AS node-builder
|
|
315
|
+
WORKDIR /app
|
|
316
|
+
COPY package.json tsconfig.json webpack.config.js ./
|
|
317
|
+
COPY app/ app/
|
|
318
|
+
COPY node-ssr/ node-ssr/
|
|
319
|
+
COPY scripts/ scripts/
|
|
320
|
+
RUN npm install --production=false
|
|
321
|
+
RUN npx tsx scripts/generate-routes.ts
|
|
322
|
+
RUN npx webpack --mode production
|
|
323
|
+
RUN npx esbuild node-ssr/server.ts --bundle --platform=node --format=esm \\
|
|
324
|
+
--outfile=dist/ssr-server.js --external:react --external:react-dom
|
|
325
|
+
|
|
326
|
+
# \u2500\u2500 Stage 3: Production image \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
327
|
+
FROM node:20-alpine
|
|
328
|
+
RUN apk add --no-cache libc6-compat
|
|
329
|
+
WORKDIR /app
|
|
330
|
+
|
|
331
|
+
# Copy Go binary
|
|
332
|
+
COPY --from=go-builder /app/blumen-server ./blumen-server
|
|
333
|
+
|
|
334
|
+
# Copy Node SSR server + production deps
|
|
335
|
+
COPY --from=node-builder /app/dist/ ./dist/
|
|
336
|
+
COPY --from=node-builder /app/static/ ./static/
|
|
337
|
+
COPY --from=node-builder /app/node_modules/ ./node_modules/
|
|
338
|
+
COPY --from=node-builder /app/package.json ./
|
|
339
|
+
|
|
340
|
+
EXPOSE 3000 4000
|
|
341
|
+
|
|
342
|
+
# Start both services (Go on :3000, Node SSR on :4000)
|
|
343
|
+
CMD sh -c './blumen-server & NODE_ENV=production node dist/ssr-server.js'
|
|
344
|
+
`;
|
|
345
|
+
var DOCKER_COMPOSE = `services:
|
|
346
|
+
app:
|
|
347
|
+
build: .
|
|
348
|
+
ports:
|
|
349
|
+
- "3000:3000"
|
|
350
|
+
environment:
|
|
351
|
+
- NODE_ENV=production
|
|
352
|
+
restart: unless-stopped
|
|
353
|
+
healthcheck:
|
|
354
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000"]
|
|
355
|
+
interval: 30s
|
|
356
|
+
timeout: 5s
|
|
357
|
+
retries: 3
|
|
358
|
+
start_period: 10s
|
|
359
|
+
`;
|
|
360
|
+
var DOCKERIGNORE = `node_modules
|
|
361
|
+
dist
|
|
362
|
+
static/js/bundle.js
|
|
363
|
+
.git
|
|
364
|
+
*.log
|
|
365
|
+
.DS_Store
|
|
366
|
+
`;
|
|
283
367
|
function writeFile(base, relPath, content) {
|
|
284
|
-
const fullPath =
|
|
285
|
-
|
|
286
|
-
|
|
368
|
+
const fullPath = path2.join(base, relPath);
|
|
369
|
+
fs2.mkdirSync(path2.dirname(fullPath), { recursive: true });
|
|
370
|
+
fs2.writeFileSync(fullPath, content, "utf-8");
|
|
287
371
|
}
|
|
288
372
|
function getTemplateFiles(projectName) {
|
|
289
373
|
return [
|
|
@@ -305,7 +389,11 @@ function getTemplateFiles(projectName) {
|
|
|
305
389
|
["go-server/main.go", readProjectFile("go-server/main.go")],
|
|
306
390
|
["scripts/generate-routes.ts", readProjectFile("scripts/generate-routes.ts")],
|
|
307
391
|
// Placeholder
|
|
308
|
-
["static/js/.gitkeep", ""]
|
|
392
|
+
["static/js/.gitkeep", ""],
|
|
393
|
+
// Docker support (production deployment)
|
|
394
|
+
["Dockerfile", DOCKERFILE],
|
|
395
|
+
["docker-compose.yml", DOCKER_COMPOSE],
|
|
396
|
+
[".dockerignore", DOCKERIGNORE]
|
|
309
397
|
];
|
|
310
398
|
}
|
|
311
399
|
async function create(projectName) {
|
|
@@ -320,8 +408,8 @@ async function create(projectName) {
|
|
|
320
408
|
);
|
|
321
409
|
process.exit(1);
|
|
322
410
|
}
|
|
323
|
-
const projectDir =
|
|
324
|
-
if (
|
|
411
|
+
const projectDir = path2.resolve(process.cwd(), projectName);
|
|
412
|
+
if (fs2.existsSync(projectDir)) {
|
|
325
413
|
log.error(`Directory ${c.bold}${projectName}${c.reset} already exists.`);
|
|
326
414
|
process.exit(1);
|
|
327
415
|
}
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { spawn, execSync } from "child_process";
|
|
3
3
|
|
|
4
4
|
// cli/utils.ts
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
5
8
|
var c = {
|
|
6
9
|
reset: "\x1B[0m",
|
|
7
10
|
bold: "\x1B[1m",
|
|
@@ -23,10 +26,30 @@ var log = {
|
|
|
23
26
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
24
27
|
blank: () => console.log("")
|
|
25
28
|
};
|
|
29
|
+
function getVersion() {
|
|
30
|
+
try {
|
|
31
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
32
|
+
let dir = path.dirname(thisFile);
|
|
33
|
+
for (let i = 0; i < 5; i++) {
|
|
34
|
+
const pkgFile = path.join(dir, "package.json");
|
|
35
|
+
if (fs.existsSync(pkgFile)) {
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
37
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
38
|
+
return pkg.version;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
dir = path.dirname(dir);
|
|
42
|
+
}
|
|
43
|
+
return "0.0.0";
|
|
44
|
+
} catch {
|
|
45
|
+
return "0.0.0";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
26
48
|
function banner() {
|
|
49
|
+
const version = getVersion();
|
|
27
50
|
console.log("");
|
|
28
51
|
console.log(
|
|
29
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
52
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
30
53
|
);
|
|
31
54
|
console.log(
|
|
32
55
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -122,17 +145,11 @@ async function dev() {
|
|
|
122
145
|
log.success(`${c.bold}Ready!${c.reset} All services are running.`);
|
|
123
146
|
log.blank();
|
|
124
147
|
console.log(
|
|
125
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}App${c.reset}:
|
|
126
|
-
);
|
|
127
|
-
console.log(
|
|
128
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}SSR${c.reset}: ${c.dim}http://localhost:4000${c.reset}`
|
|
129
|
-
);
|
|
130
|
-
console.log(
|
|
131
|
-
` ${c.dim}\u279C${c.reset} ${c.bold}HMR${c.reset}: ${c.dim}http://localhost:3100${c.reset}`
|
|
148
|
+
` ${c.dim}\u279C${c.reset} ${c.bold}App${c.reset}: ${c.cyan}http://localhost:3000${c.reset}`
|
|
132
149
|
);
|
|
133
150
|
log.blank();
|
|
134
151
|
console.log(
|
|
135
|
-
` ${c.dim}Press ${c.bold}Ctrl+C${c.reset}${c.dim} to stop
|
|
152
|
+
` ${c.dim}Press ${c.bold}Ctrl+C${c.reset}${c.dim} to stop.${c.reset}`
|
|
136
153
|
);
|
|
137
154
|
log.blank();
|
|
138
155
|
divider();
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// cli/commands/start.ts
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
|
-
import * as
|
|
3
|
+
import * as fs2 from "fs";
|
|
4
4
|
|
|
5
5
|
// cli/utils.ts
|
|
6
|
+
import * as fs from "fs";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
6
9
|
var c = {
|
|
7
10
|
reset: "\x1B[0m",
|
|
8
11
|
bold: "\x1B[1m",
|
|
@@ -24,10 +27,30 @@ var log = {
|
|
|
24
27
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
25
28
|
blank: () => console.log("")
|
|
26
29
|
};
|
|
30
|
+
function getVersion() {
|
|
31
|
+
try {
|
|
32
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
33
|
+
let dir = path.dirname(thisFile);
|
|
34
|
+
for (let i = 0; i < 5; i++) {
|
|
35
|
+
const pkgFile = path.join(dir, "package.json");
|
|
36
|
+
if (fs.existsSync(pkgFile)) {
|
|
37
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
38
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
39
|
+
return pkg.version;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
dir = path.dirname(dir);
|
|
43
|
+
}
|
|
44
|
+
return "0.0.0";
|
|
45
|
+
} catch {
|
|
46
|
+
return "0.0.0";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
27
49
|
function banner() {
|
|
50
|
+
const version = getVersion();
|
|
28
51
|
console.log("");
|
|
29
52
|
console.log(
|
|
30
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
53
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
31
54
|
);
|
|
32
55
|
console.log(
|
|
33
56
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -41,7 +64,7 @@ function divider() {
|
|
|
41
64
|
// cli/commands/start.ts
|
|
42
65
|
async function start() {
|
|
43
66
|
banner();
|
|
44
|
-
if (!
|
|
67
|
+
if (!fs2.existsSync("dist/ssr-server.js")) {
|
|
45
68
|
log.error("Production build not found.");
|
|
46
69
|
log.info(
|
|
47
70
|
`Run ${c.bold}blumen build${c.reset} first to create a production build.`
|
package/dist/cli/utils.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
// cli/utils.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
2
5
|
var c = {
|
|
3
6
|
reset: "\x1B[0m",
|
|
4
7
|
bold: "\x1B[1m",
|
|
@@ -20,10 +23,30 @@ var log = {
|
|
|
20
23
|
step: (msg) => console.log(` ${c.dim}\u2192${c.reset} ${msg}`),
|
|
21
24
|
blank: () => console.log("")
|
|
22
25
|
};
|
|
26
|
+
function getVersion() {
|
|
27
|
+
try {
|
|
28
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
29
|
+
let dir = path.dirname(thisFile);
|
|
30
|
+
for (let i = 0; i < 5; i++) {
|
|
31
|
+
const pkgFile = path.join(dir, "package.json");
|
|
32
|
+
if (fs.existsSync(pkgFile)) {
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(pkgFile, "utf-8"));
|
|
34
|
+
if (pkg.name === "blumenjs" || pkg.name === "go-react-ssr") {
|
|
35
|
+
return pkg.version;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
dir = path.dirname(dir);
|
|
39
|
+
}
|
|
40
|
+
return "0.0.0";
|
|
41
|
+
} catch {
|
|
42
|
+
return "0.0.0";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
23
45
|
function banner() {
|
|
46
|
+
const version = getVersion();
|
|
24
47
|
console.log("");
|
|
25
48
|
console.log(
|
|
26
|
-
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}
|
|
49
|
+
` ${c.magenta}${c.bold}\u{1F338} Blumen${c.reset} ${c.dim}v${version}${c.reset}`
|
|
27
50
|
);
|
|
28
51
|
console.log(
|
|
29
52
|
` ${c.dim}The React framework powered by Go${c.reset}`
|
|
@@ -80,6 +103,7 @@ export {
|
|
|
80
103
|
c,
|
|
81
104
|
confirm,
|
|
82
105
|
divider,
|
|
106
|
+
getVersion,
|
|
83
107
|
log,
|
|
84
108
|
select
|
|
85
109
|
};
|
|
@@ -7,7 +7,7 @@ interface HomeProps {
|
|
|
7
7
|
|
|
8
8
|
const HomePage: React.FC<HomeProps> = () => {
|
|
9
9
|
return (
|
|
10
|
-
|
|
10
|
+
<div className="blumen-home">
|
|
11
11
|
<style
|
|
12
12
|
dangerouslySetInnerHTML={{
|
|
13
13
|
__html: `
|
|
@@ -305,8 +305,7 @@ const HomePage: React.FC<HomeProps> = () => {
|
|
|
305
305
|
`,
|
|
306
306
|
}}
|
|
307
307
|
/>
|
|
308
|
-
|
|
309
|
-
{/* Background effects */}
|
|
308
|
+
{/* Background effects */}
|
|
310
309
|
<div className="blumen-bg" />
|
|
311
310
|
<div className="blumen-orb blumen-orb-1" />
|
|
312
311
|
<div className="blumen-orb blumen-orb-2" />
|
|
@@ -390,8 +389,7 @@ const HomePage: React.FC<HomeProps> = () => {
|
|
|
390
389
|
</p>
|
|
391
390
|
</div>
|
|
392
391
|
</div>
|
|
393
|
-
|
|
394
|
-
</>
|
|
392
|
+
</div>
|
|
395
393
|
);
|
|
396
394
|
};
|
|
397
395
|
|