buner 1.0.4 → 1.0.6
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/{bin → dist}/buner.js +54 -35
- package/{integration.ts → dist/integration.js} +51 -100
- package/dist/migrate-scss.js +33 -0
- package/dist/prerender.js +158 -0
- package/dist/scripts.js +36 -0
- package/dist/server.js +171 -0
- package/dist/states.js +41 -0
- package/dist/styles.js +165 -0
- package/package.json +3 -11
- package/cli/README.md +0 -1
- package/cli/buner.ts +0 -241
- package/cli/cli.ts +0 -125
- package/cli/create-app.ts +0 -59
- package/cli/helpers/copy.ts +0 -61
- package/cli/helpers/format-files.ts +0 -197
- package/cli/helpers/git.ts +0 -77
- package/cli/helpers/install.ts +0 -26
- package/cli/helpers/is-folder-empty.ts +0 -40
- package/cli/helpers/is-writeable.ts +0 -14
- package/cli/helpers/make-dir.ts +0 -7
- package/cli/helpers/validate-pkg.ts +0 -17
- package/cli/install-template.ts +0 -72
- package/migrate-scss.ts +0 -42
- package/prerender.ts +0 -229
- package/scripts.ts +0 -56
- package/server.ts +0 -29
- package/states.ts +0 -63
- package/styles.ts +0 -232
package/{bin → dist}/buner.js
RENAMED
|
@@ -13,15 +13,15 @@ import { globby } from "globby";
|
|
|
13
13
|
import { exec } from "node:child_process";
|
|
14
14
|
import validateProjectName from "validate-npm-package-name";
|
|
15
15
|
const name = "buner";
|
|
16
|
-
const version = "1.0.
|
|
16
|
+
const version = "1.0.6";
|
|
17
17
|
const description = "Frontend build toolkit for Vite + React SSR projects — SCSS pipeline, prerender, SSR dev server, and backend integration.";
|
|
18
18
|
const type = "module";
|
|
19
19
|
const license = "MIT";
|
|
20
20
|
const repository = { "type": "git", "url": "https://github.com/precise-alloy/buner.git" };
|
|
21
21
|
const homepage = "https://www.npmjs.com/package/buner";
|
|
22
22
|
const keywords = ["vite", "react", "ssr", "scss", "prerender", "frontend", "build-tool", "cli"];
|
|
23
|
-
const bin = { "buner": "./
|
|
24
|
-
const files = ["
|
|
23
|
+
const bin$1 = { "buner": "./dist/buner.js" };
|
|
24
|
+
const files = ["dist", "xpack", "public", "vite.config.ts", "index.html", "tsconfig.json", "eslint.config.mjs", "types.d.ts", ".env", ".env.development", ".env.eshn", "README.md"];
|
|
25
25
|
const scripts = { "start": "bun run dev", "predev": "bun upgrade", "dev": 'concurrently --kill-others "bun styles.ts --watch" "bun states.ts --watch" "cross-env scriptOnly=true vite build --mode development --watch" "bun server.ts --mode development"', "serve": "bun server.ts", "watch": "bun server.ts", "build": "bun run build:static && bun run build:server", "build:eshn": "bun run build:static:eshn && bun run build:server:eshn", "preview": "vite preview", "build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server", "build:server:eshn": "vite build --ssr src/entry-server.tsx --outDir dist/server --mode eshn", "build:static": "vite build --outDir dist/static", "build:static:eshn": "vite build --outDir dist/static --mode eshn", "generate": "bun states.ts && bun run styles && bun run build && bun prerender.ts --add-hash", "eshn": "bun states.ts && bun run styles && bun run build:eshn && bun prerender.ts --add-hash --mode eshn", "inte": "bun run styles && bun run build && bun prerender.ts && bun integration.ts", "styles": "bun styles.ts", "format": "prettier --write .", "lint": "eslint --fix", "cli:start": "vite build -c vite.cli.config.ts --watch", "cli:build": "vite build -c vite.cli.config.ts" };
|
|
26
26
|
const engines = { "node": ">=20.0.0" };
|
|
27
27
|
const dependencies = { "@vitejs/plugin-react": "^5.1.4", "autoprefixer": "^10.4.27", "chalk": "^5.5.0", "cheerio": "^1.2.0", "chokidar": "^4.0.3", "commander": "^14.0.3", "concurrently": "^9.2.0", "cross-env": "^10.1.0", "cssnano": "^7.1.0", "debounce": "^3.0.0", "express": "^5.2.1", "glob": "^13.0.6", "globby": "^16.1.1", "js-beautify": "^1.15.4", "lodash": "^4.17.21", "magic-string": "^0.30.17", "node-fetch": "^3.3.2", "postcss": "^8.5.8", "prompts": "^2.4.2", "sass": "^1.89.2", "slash": "^5.1.0", "validate-npm-package-name": "^7.0.0", "vite": "^7.0.6" };
|
|
@@ -37,7 +37,7 @@ const packageJson = {
|
|
|
37
37
|
repository,
|
|
38
38
|
homepage,
|
|
39
39
|
keywords,
|
|
40
|
-
bin,
|
|
40
|
+
bin: bin$1,
|
|
41
41
|
files,
|
|
42
42
|
scripts,
|
|
43
43
|
engines,
|
|
@@ -430,8 +430,23 @@ function validateNpmName(name2) {
|
|
|
430
430
|
}
|
|
431
431
|
const { green, yellow, bold, cyan, red } = chalk;
|
|
432
432
|
const packageName = "buner";
|
|
433
|
-
const packageDir = path.
|
|
433
|
+
const packageDir = path.dirname(fileURLToPath(import.meta.url));
|
|
434
434
|
const pkg = (file) => path.join(packageDir, file);
|
|
435
|
+
const bin = (name2) => {
|
|
436
|
+
const ext = process.platform === "win32" ? ".cmd" : "";
|
|
437
|
+
const pkgRoot = path.resolve(packageDir, "..");
|
|
438
|
+
const local = path.join(pkgRoot, "node_modules", ".bin", name2 + ext);
|
|
439
|
+
try {
|
|
440
|
+
if (require("fs").existsSync(local)) return local;
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
const consumerBin = path.join(process.cwd(), "node_modules", ".bin", name2 + ext);
|
|
444
|
+
try {
|
|
445
|
+
if (require("fs").existsSync(consumerBin)) return consumerBin;
|
|
446
|
+
} catch {
|
|
447
|
+
}
|
|
448
|
+
return name2;
|
|
449
|
+
};
|
|
435
450
|
const run = (cmd, args = [], options = {}) => {
|
|
436
451
|
return new Promise((resolve, reject) => {
|
|
437
452
|
const child = spawn(cmd, args, {
|
|
@@ -515,58 +530,62 @@ For example:
|
|
|
515
530
|
await notifyUpdate();
|
|
516
531
|
});
|
|
517
532
|
program.command("dev").description("Start development mode with all watchers").action(async () => {
|
|
518
|
-
|
|
519
|
-
|
|
533
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
534
|
+
await run(bin("concurrently"), [
|
|
520
535
|
"--kill-others",
|
|
521
|
-
`"
|
|
522
|
-
`"
|
|
523
|
-
`"cross-env scriptOnly=true
|
|
524
|
-
`"
|
|
536
|
+
`"node ${pkg("styles.js")} --watch"`,
|
|
537
|
+
`"node ${pkg("states.js")} --watch"`,
|
|
538
|
+
`"${bin("cross-env")} scriptOnly=true ${bin("vite")} build --config ${viteConfig} --mode development --watch"`,
|
|
539
|
+
`"node ${pkg("server.js")} --mode development"`
|
|
525
540
|
]);
|
|
526
541
|
});
|
|
527
542
|
program.command("serve").description("Start the SSR dev server").option("--mode <mode>", "server mode", "development").action(async (opts) => {
|
|
528
|
-
await run("
|
|
543
|
+
await run("node", [pkg("server.js"), "--mode", opts.mode]);
|
|
529
544
|
});
|
|
530
545
|
program.command("build").description("Build the project (static + SSR)").action(async () => {
|
|
531
|
-
|
|
532
|
-
runSync(
|
|
546
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
547
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --outDir dist/static`);
|
|
548
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --ssr src/entry-server.tsx --outDir dist/server`);
|
|
533
549
|
});
|
|
534
550
|
program.command("generate").description("Full static site generation (states + styles + build + prerender)").option("--mode <mode>", "build mode", "production").action(async (opts) => {
|
|
535
|
-
|
|
536
|
-
runSync(`
|
|
551
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
552
|
+
runSync(`node ${pkg("states.js")}`);
|
|
553
|
+
runSync(`node ${pkg("styles.js")}`);
|
|
537
554
|
if (opts.mode === "production") {
|
|
538
|
-
runSync(
|
|
539
|
-
runSync(
|
|
555
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --outDir dist/static`);
|
|
556
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --ssr src/entry-server.tsx --outDir dist/server`);
|
|
540
557
|
} else {
|
|
541
|
-
runSync(
|
|
542
|
-
runSync(
|
|
558
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --outDir dist/static --mode ${opts.mode}`);
|
|
559
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
|
|
543
560
|
}
|
|
544
|
-
runSync(`
|
|
561
|
+
runSync(`node ${pkg("prerender.js")} --add-hash --mode ${opts.mode}`);
|
|
545
562
|
});
|
|
546
563
|
program.command("eshn").description("Generate with --mode eshn").action(async () => {
|
|
547
|
-
|
|
548
|
-
runSync(`
|
|
549
|
-
runSync(`
|
|
550
|
-
runSync(
|
|
551
|
-
runSync(
|
|
564
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
565
|
+
runSync(`node ${pkg("states.js")}`);
|
|
566
|
+
runSync(`node ${pkg("styles.js")}`);
|
|
567
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --outDir dist/static --mode eshn`);
|
|
568
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --ssr src/entry-server.tsx --outDir dist/server --mode eshn`);
|
|
569
|
+
runSync(`node ${pkg("prerender.js")} --add-hash --mode eshn`);
|
|
552
570
|
});
|
|
553
571
|
program.command("inte").description("Build and integrate with backend (styles + build + prerender + integration)").action(async () => {
|
|
554
|
-
|
|
555
|
-
runSync(`
|
|
556
|
-
runSync(
|
|
557
|
-
runSync(
|
|
558
|
-
runSync(`
|
|
572
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
573
|
+
runSync(`node ${pkg("styles.js")}`);
|
|
574
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --outDir dist/static`);
|
|
575
|
+
runSync(`${bin("vite")} build --config ${viteConfig} --ssr src/entry-server.tsx --outDir dist/server`);
|
|
576
|
+
runSync(`node ${pkg("prerender.js")}`);
|
|
577
|
+
runSync(`node ${pkg("integration.js")}`);
|
|
559
578
|
});
|
|
560
579
|
program.command("styles").description("Compile SCSS").option("--watch", "Watch for changes").action(async (opts) => {
|
|
561
|
-
const args = [pkg("styles.
|
|
580
|
+
const args = [pkg("styles.js")];
|
|
562
581
|
if (opts.watch) args.push("--watch");
|
|
563
|
-
await run("
|
|
582
|
+
await run("node", args);
|
|
564
583
|
});
|
|
565
584
|
program.command("prerender").description("Pre-render HTML files").option("--add-hash", "Add content hashes to asset URLs").option("--mode <mode>", "build mode", "production").action(async (opts) => {
|
|
566
|
-
const args = [pkg("prerender.
|
|
585
|
+
const args = [pkg("prerender.js")];
|
|
567
586
|
if (opts.addHash) args.push("--add-hash");
|
|
568
587
|
args.push("--mode", opts.mode);
|
|
569
|
-
await run("
|
|
588
|
+
await run("node", args);
|
|
570
589
|
});
|
|
571
590
|
program.parseAsync(process.argv).catch(async (error) => {
|
|
572
591
|
console.log(red(error));
|
|
@@ -1,179 +1,130 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
interface CopyItem {
|
|
13
|
-
from: string;
|
|
14
|
-
to?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface FileExistCheck {
|
|
18
|
-
folder?: string;
|
|
19
|
-
fileName: string | RegExp;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const argvModeIndex = process.argv.indexOf('--mode');
|
|
23
|
-
const mode =
|
|
24
|
-
argvModeIndex >= 0 && argvModeIndex < process.argv.length - 1 && !process.argv[argvModeIndex + 1].startsWith('-')
|
|
25
|
-
? process.argv[argvModeIndex + 1]
|
|
26
|
-
: 'production';
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path, { resolve } from "path";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import fs$1 from "node:fs";
|
|
5
|
+
import slash from "slash";
|
|
6
|
+
import { glob } from "glob";
|
|
7
|
+
import { loadEnv } from "vite";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
const argvModeIndex = process.argv.indexOf("--mode");
|
|
10
|
+
const mode = argvModeIndex >= 0 && argvModeIndex < process.argv.length - 1 && !process.argv[argvModeIndex + 1].startsWith("-") ? process.argv[argvModeIndex + 1] : "production";
|
|
27
11
|
const projectRoot = process.cwd();
|
|
28
12
|
const xpackEnv = loadEnv(mode, projectRoot);
|
|
29
|
-
const toAbsolute = (p
|
|
13
|
+
const toAbsolute = (p) => slash(path.resolve(projectRoot, p));
|
|
30
14
|
const log = console.log.bind(console);
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const srcBasePath = toAbsolute('dist/static/assets');
|
|
15
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
16
|
+
const staticBasePath = toAbsolute("dist/static");
|
|
17
|
+
const srcBasePath = toAbsolute("dist/static/assets");
|
|
35
18
|
const destBasePath = toAbsolute(xpackEnv.VITE_INTE_ASSET_DIR);
|
|
36
|
-
const patternPath = xpackEnv.VITE_INTE_PATTERN_DIR ? toAbsolute(xpackEnv.VITE_INTE_PATTERN_DIR) :
|
|
37
|
-
|
|
19
|
+
const patternPath = xpackEnv.VITE_INTE_PATTERN_DIR ? toAbsolute(xpackEnv.VITE_INTE_PATTERN_DIR) : void 0;
|
|
38
20
|
if (patternPath && fs.existsSync(patternPath)) {
|
|
39
21
|
fs.rmSync(patternPath, { recursive: true, force: true });
|
|
40
|
-
|
|
41
22
|
fs.mkdirSync(patternPath, { recursive: true });
|
|
42
23
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{ from:
|
|
46
|
-
{ from:
|
|
47
|
-
{ from:
|
|
48
|
-
{ from:
|
|
49
|
-
{ from:
|
|
50
|
-
{ from:
|
|
51
|
-
{ from: 'pages', to: patternPath },
|
|
24
|
+
const copyItems = [
|
|
25
|
+
{ from: "css" },
|
|
26
|
+
{ from: "fonts" },
|
|
27
|
+
{ from: "images" },
|
|
28
|
+
{ from: "js" },
|
|
29
|
+
{ from: "vendors" },
|
|
30
|
+
{ from: "hashes.json" },
|
|
31
|
+
{ from: "pages", to: patternPath }
|
|
52
32
|
];
|
|
53
|
-
const hashItems
|
|
54
|
-
|
|
33
|
+
const hashItems = ["css", "images", "js"];
|
|
55
34
|
copyItems.forEach((item) => {
|
|
56
35
|
const srcPath = slash(path.join(srcBasePath, item.from));
|
|
57
36
|
const destPath = slash(item.to ?? path.join(destBasePath, item.from));
|
|
58
|
-
|
|
59
37
|
if (!fs.existsSync(srcPath)) {
|
|
60
38
|
return;
|
|
61
39
|
}
|
|
62
|
-
|
|
63
40
|
log(`Copy file ${srcPath} to ${destPath}`);
|
|
64
|
-
|
|
65
41
|
if (fs.statSync(srcPath).isDirectory()) {
|
|
66
42
|
if (fs.existsSync(destPath)) {
|
|
67
43
|
fs.rmSync(destPath, { recursive: true, force: true });
|
|
68
44
|
}
|
|
69
|
-
|
|
70
45
|
fs.mkdirSync(destPath, { recursive: true });
|
|
71
|
-
|
|
72
46
|
fs.cpSync(srcPath, destPath, { recursive: true, force: true });
|
|
73
47
|
} else {
|
|
74
48
|
const destDirPath = path.dirname(destPath);
|
|
75
|
-
|
|
76
49
|
if (!fs.existsSync(destDirPath)) {
|
|
77
50
|
fs.mkdirSync(destDirPath);
|
|
78
51
|
}
|
|
79
|
-
|
|
80
52
|
fs.copyFileSync(srcPath, destPath);
|
|
81
53
|
}
|
|
82
54
|
});
|
|
83
|
-
|
|
84
55
|
hashItems.forEach((item) => {
|
|
85
56
|
const srcPath = slash(path.join(srcBasePath, item));
|
|
86
|
-
const files = glob.sync(srcPath +
|
|
87
|
-
|
|
57
|
+
const files = glob.sync(srcPath + "/**/*.{css,js,svg}");
|
|
88
58
|
files.forEach((file) => {
|
|
89
59
|
const relativePath = slash(file.substring(staticBasePath.length));
|
|
90
|
-
|
|
91
60
|
if (!/\.0x[a-z0-9_-]{8,12}\.\w+$/gi.test(file)) {
|
|
92
61
|
const content = fs.readFileSync(file);
|
|
93
|
-
const sha1Hash = crypto.createHash(
|
|
94
|
-
|
|
62
|
+
const sha1Hash = crypto.createHash("sha1");
|
|
95
63
|
sha1Hash.update(content);
|
|
96
|
-
const hash = sha1Hash.digest(
|
|
97
|
-
|
|
64
|
+
const hash = sha1Hash.digest("base64url").substring(0, 10);
|
|
98
65
|
hashes.set(relativePath, hash);
|
|
99
66
|
} else {
|
|
100
|
-
hashes.set(relativePath,
|
|
67
|
+
hashes.set(relativePath, "");
|
|
101
68
|
}
|
|
102
69
|
});
|
|
103
70
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
},
|
|
113
|
-
{} as { [key: string]: string }
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
fs.writeFileSync(path.join(destBasePath, 'hashes.json'), JSON.stringify(sortedHashes, null, ' '));
|
|
117
|
-
|
|
71
|
+
const sortedHashes = Array.from(hashes).sort((a, b) => a[0].localeCompare(b[0])).reduce(
|
|
72
|
+
(obj, [key, value]) => {
|
|
73
|
+
obj[key] = value;
|
|
74
|
+
return obj;
|
|
75
|
+
},
|
|
76
|
+
{}
|
|
77
|
+
);
|
|
78
|
+
fs.writeFileSync(path.join(destBasePath, "hashes.json"), JSON.stringify(sortedHashes, null, " "));
|
|
118
79
|
if (patternPath) {
|
|
119
80
|
fs.mkdirSync(patternPath, { recursive: true });
|
|
120
|
-
glob.sync(
|
|
121
|
-
let basename =
|
|
122
|
-
const segments = slash(p).split(
|
|
123
|
-
|
|
81
|
+
glob.sync("./dist/static/{atoms,molecules,organisms,templates,pages}/**/*.*").forEach((p) => {
|
|
82
|
+
let basename = "";
|
|
83
|
+
const segments = slash(p).split("/");
|
|
124
84
|
if (segments.length < 4) return;
|
|
125
85
|
switch (segments.length) {
|
|
126
86
|
case 4:
|
|
127
|
-
basename = path.basename(slash(p).replaceAll(/(atoms|molecules|organisms|templates|pages)\/([\w._-]+)$/gi,
|
|
87
|
+
basename = path.basename(slash(p).replaceAll(/(atoms|molecules|organisms|templates|pages)\/([\w._-]+)$/gi, "$1-$2"));
|
|
128
88
|
fs.copyFileSync(p, resolve(patternPath, basename));
|
|
129
89
|
break;
|
|
130
90
|
default:
|
|
131
91
|
segments.splice(0, 2);
|
|
132
|
-
|
|
92
|
+
fs$1.cpSync(p, resolve(patternPath, segments.join("-")), { recursive: true });
|
|
133
93
|
break;
|
|
134
94
|
}
|
|
135
95
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
const newText = text
|
|
140
|
-
.replaceAll(/react-loader\.0x[a-z0-9_-]{8,12}\.js/gi, 'react-loader.0x00000000.js')
|
|
141
|
-
.replaceAll(/\.svg\?v=[a-z0-9_-]+/gi, '.svg');
|
|
142
|
-
|
|
96
|
+
glob.sync(slash(path.resolve(patternPath + "/**/*.{htm,html}"))).forEach((p) => {
|
|
97
|
+
const text = fs.readFileSync(p, "utf-8");
|
|
98
|
+
const newText = text.replaceAll(/react-loader\.0x[a-z0-9_-]{8,12}\.js/gi, "react-loader.0x00000000.js").replaceAll(/\.svg\?v=[a-z0-9_-]+/gi, ".svg");
|
|
143
99
|
if (text !== newText) {
|
|
144
100
|
fs.writeFileSync(p, newText);
|
|
145
101
|
}
|
|
146
102
|
});
|
|
147
103
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{ fileName:
|
|
151
|
-
{ fileName:
|
|
152
|
-
{ fileName: 'main.js', folder: 'js' },
|
|
104
|
+
const checkExistFileList = [
|
|
105
|
+
{ fileName: "hashes.json" },
|
|
106
|
+
{ fileName: /react-loader\.0x[a-z0-9_-]{8,12}\.js/gi, folder: "js" },
|
|
107
|
+
{ fileName: "main.js", folder: "js" }
|
|
153
108
|
];
|
|
154
109
|
let isAllExist = true;
|
|
155
|
-
|
|
156
110
|
checkExistFileList.forEach((file) => {
|
|
157
|
-
if (typeof file.fileName ===
|
|
158
|
-
const destPath = slash(path.join(destBasePath, file.folder ??
|
|
159
|
-
|
|
111
|
+
if (typeof file.fileName === "string") {
|
|
112
|
+
const destPath = slash(path.join(destBasePath, file.folder ?? "", file.fileName.toString()));
|
|
160
113
|
if (!fs.existsSync(destPath)) {
|
|
161
114
|
log(chalk.yellow(`Cannot find: ${destPath}`));
|
|
162
115
|
isAllExist = false;
|
|
163
116
|
}
|
|
164
117
|
} else {
|
|
165
118
|
const fileName = file.fileName;
|
|
166
|
-
const folderFiles = slash(path.join(destBasePath, file.folder ??
|
|
119
|
+
const folderFiles = slash(path.join(destBasePath, file.folder ?? ""));
|
|
167
120
|
const files = fs.readdirSync(folderFiles);
|
|
168
121
|
const found = files.find((f) => fileName.test(f));
|
|
169
|
-
|
|
170
122
|
if (!found) {
|
|
171
123
|
log(chalk.yellow(`Cannot find: ${slash(path.join(folderFiles, fileName.toString()))}`));
|
|
172
124
|
isAllExist = false;
|
|
173
125
|
}
|
|
174
126
|
}
|
|
175
127
|
});
|
|
176
|
-
|
|
177
128
|
if (!isAllExist) {
|
|
178
129
|
process.exit(1);
|
|
179
130
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { glob } from "glob";
|
|
3
|
+
const migration = async () => {
|
|
4
|
+
const usableFiles = ["./xpack/styles/root.scss"];
|
|
5
|
+
const forwardableFiles = [
|
|
6
|
+
"./src/assets/styles/style-base.scss",
|
|
7
|
+
"./src/assets/styles/style.all.scss",
|
|
8
|
+
"./src/assets/styles/00-abstracts/_abstracts.scss",
|
|
9
|
+
"./src/assets/styles/01-mixins/_mixins.scss",
|
|
10
|
+
"./src/assets/styles/02-base/_base.scss"
|
|
11
|
+
];
|
|
12
|
+
for (const file of forwardableFiles) {
|
|
13
|
+
try {
|
|
14
|
+
console.log(`Migrating ${file}`);
|
|
15
|
+
execSync(`bunx sass-migrator --migrate-deps module --forward=all ${file}`, { stdio: "inherit" });
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.error(err.message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const componentFiles = await glob(["./src/atoms/**/*.scss", "./src/molecules/**/*.scss", "./src/organisms/**/*.scss"], { nodir: true });
|
|
21
|
+
const allFiles = [...usableFiles, ...componentFiles];
|
|
22
|
+
for (const file of allFiles) {
|
|
23
|
+
try {
|
|
24
|
+
console.log(`Migrating ${file}`);
|
|
25
|
+
execSync(`bunx sass-migrator module --migrate-deps ${file}`, { stdio: "inherit" });
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(err.message);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
migration().catch((err) => {
|
|
32
|
+
console.error("An error occurred during migration:", err.message);
|
|
33
|
+
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { pathToFileURL } from "url";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
import jsBeautify from "js-beautify";
|
|
6
|
+
import * as cheerio from "cheerio";
|
|
7
|
+
import slash from "slash";
|
|
8
|
+
import _ from "lodash";
|
|
9
|
+
import { loadEnv } from "vite";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
const argvModeIndex = process.argv.indexOf("--mode");
|
|
12
|
+
const mode = argvModeIndex >= 0 && argvModeIndex < process.argv.length - 1 && !process.argv[argvModeIndex + 1].startsWith("-") ? process.argv[argvModeIndex + 1] : "production";
|
|
13
|
+
const projectRoot = process.cwd();
|
|
14
|
+
const xpackEnv = loadEnv(mode, projectRoot);
|
|
15
|
+
const toAbsolute = (p) => path.resolve(projectRoot, p);
|
|
16
|
+
const log = console.log.bind(console);
|
|
17
|
+
const template = fs.readFileSync(toAbsolute(process.env.VITE_TEMPLATE ?? "dist/static/index.html"), "utf-8");
|
|
18
|
+
const { render, routesToPrerender } = await import(pathToFileURL(toAbsolute("./dist/server/entry-server.js")).href);
|
|
19
|
+
const beautifyOptions = {
|
|
20
|
+
indent_size: 2,
|
|
21
|
+
indent_char: " ",
|
|
22
|
+
keep_array_indentation: false,
|
|
23
|
+
break_chained_methods: false,
|
|
24
|
+
indent_scripts: "normal",
|
|
25
|
+
brace_style: "collapse",
|
|
26
|
+
space_before_conditional: true,
|
|
27
|
+
unescape_strings: false,
|
|
28
|
+
jslint_happy: false,
|
|
29
|
+
end_with_newline: false,
|
|
30
|
+
wrap_line_length: 0,
|
|
31
|
+
indent_inner_html: false,
|
|
32
|
+
comma_first: false,
|
|
33
|
+
e4x: false,
|
|
34
|
+
indent_empty_lines: false,
|
|
35
|
+
wrap_attributes: "force",
|
|
36
|
+
max_preserve_newlines: 5,
|
|
37
|
+
preserve_newlines: true
|
|
38
|
+
};
|
|
39
|
+
const updateResourcePath = ($, tagName, attr, addHash) => {
|
|
40
|
+
$(tagName).each((_2, el) => {
|
|
41
|
+
const href = $(el).attr(attr);
|
|
42
|
+
if (href && href.startsWith("/")) {
|
|
43
|
+
let newPath = href;
|
|
44
|
+
if (process.env.VITE_DOMAIN) {
|
|
45
|
+
newPath = process.env.VITE_DOMAIN + newPath;
|
|
46
|
+
}
|
|
47
|
+
if (href.startsWith(xpackEnv.VITE_BASE_URL) && !href.startsWith(xpackEnv.VITE_BASE_URL + "assets/vendors/") && [".css", ".ico", ".js", ".webmanifest", ".svg"].includes(path.extname(href).toLowerCase()) && !/\.0x[a-z0-9]{8}\.\w+$/gi.test(href)) {
|
|
48
|
+
const path2 = toAbsolute("dist/static/" + href.substring(xpackEnv.VITE_BASE_URL.length));
|
|
49
|
+
if (fs.existsSync(path2)) {
|
|
50
|
+
const content = fs.readFileSync(path2);
|
|
51
|
+
const sha1Hash = crypto.createHash("sha1");
|
|
52
|
+
sha1Hash.update(content);
|
|
53
|
+
const hash = sha1Hash.digest("base64url").substring(0, 10);
|
|
54
|
+
if (addHash) {
|
|
55
|
+
newPath += "?v=" + hash;
|
|
56
|
+
}
|
|
57
|
+
} else if (path2.endsWith("mock-api.js")) ;
|
|
58
|
+
else {
|
|
59
|
+
log(chalk.yellow("Cannot find:", path2));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (newPath != href) {
|
|
63
|
+
$(el).attr(attr, newPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const removeStyleBase = ($) => {
|
|
69
|
+
$('link[rel="stylesheet"]').each((_2, el) => {
|
|
70
|
+
const href = $(el).attr("href");
|
|
71
|
+
if (href?.includes("style-base")) {
|
|
72
|
+
$(el).remove();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const removeDuplicateAssets = ($, selector, attr, paths) => {
|
|
77
|
+
$(selector).each((_2, el) => {
|
|
78
|
+
if ($(el).attr("data-pl-inplace") === "true") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const path2 = $(el).attr(attr);
|
|
82
|
+
if (!path2) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const index = $(el).index();
|
|
86
|
+
const parent = $(el).parent().clone();
|
|
87
|
+
const child = parent.children()[index];
|
|
88
|
+
parent.empty();
|
|
89
|
+
parent.append(child);
|
|
90
|
+
const html = parent.html();
|
|
91
|
+
$(el).after("\n<!-- " + html + " -->");
|
|
92
|
+
if (paths.includes(path2)) {
|
|
93
|
+
$(el).remove();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
paths.push(path2);
|
|
97
|
+
$(el).removeAttr("data-pl-require");
|
|
98
|
+
if ($(el).attr("type") === "module") {
|
|
99
|
+
const deferValue = $(el).attr("defer");
|
|
100
|
+
if ($(el).attr("defer") === "" || deferValue === "defer" || deferValue === "true") {
|
|
101
|
+
$(el).removeAttr("defer");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
$("head").append(el);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
const viteAbsoluteUrl = (remain, addExtension = false) => {
|
|
108
|
+
const baseUrl = xpackEnv.VITE_BASE_URL;
|
|
109
|
+
const normalizedRemain = (remain?.startsWith("/") ? remain : "/" + remain) + (addExtension && !remain.endsWith("/") ? xpackEnv.VITE_PATH_EXTENSION ?? "" : "");
|
|
110
|
+
if (!baseUrl) {
|
|
111
|
+
return normalizedRemain;
|
|
112
|
+
}
|
|
113
|
+
if (!baseUrl.endsWith("/")) {
|
|
114
|
+
return baseUrl + normalizedRemain;
|
|
115
|
+
}
|
|
116
|
+
const len = baseUrl.length;
|
|
117
|
+
return baseUrl.substring(0, len - 1) + normalizedRemain;
|
|
118
|
+
};
|
|
119
|
+
const renderPage = async (renderedPages, addHash) => {
|
|
120
|
+
for (const route of routesToPrerender) {
|
|
121
|
+
const output = await render(viteAbsoluteUrl(route.route, true));
|
|
122
|
+
const destLocalizedFolderPath = toAbsolute("dist/static");
|
|
123
|
+
let html = template.replace("<!--app-html-->", output.html ?? "").replace("@style.scss", "/assets/css/" + route.name + ".css");
|
|
124
|
+
const $ = cheerio.load(html);
|
|
125
|
+
const paths = [];
|
|
126
|
+
removeDuplicateAssets($, "link[data-pl-require][href]", "href", paths);
|
|
127
|
+
removeDuplicateAssets($, "script[data-pl-require][src]", "src", paths);
|
|
128
|
+
updateResourcePath($, "link", "href", addHash);
|
|
129
|
+
updateResourcePath($, "script", "src", addHash);
|
|
130
|
+
updateResourcePath($, "img", "src", addHash);
|
|
131
|
+
if (route.route === "/") {
|
|
132
|
+
removeStyleBase($);
|
|
133
|
+
}
|
|
134
|
+
$("head title").text(route.name);
|
|
135
|
+
const fileName = (route.route === "/" ? "/index" : route.route) + ".html";
|
|
136
|
+
const filePath = `${destLocalizedFolderPath}${fileName}`;
|
|
137
|
+
if (!fs.existsSync(path.dirname(filePath))) {
|
|
138
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
139
|
+
}
|
|
140
|
+
html = $.html();
|
|
141
|
+
html = jsBeautify.html_beautify(html, beautifyOptions);
|
|
142
|
+
html = html.replace("/* app-styles */", output.styles);
|
|
143
|
+
fs.writeFileSync(toAbsolute(filePath), html);
|
|
144
|
+
log("pre-rendered:", slash(filePath));
|
|
145
|
+
renderedPages.push({
|
|
146
|
+
name: _.kebabCase(fileName.replaceAll(/\.\w+$/gi, "")),
|
|
147
|
+
url: `${process.env.VITE_DOMAIN ?? ""}${fileName}`,
|
|
148
|
+
fileName
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
(async () => {
|
|
153
|
+
const renderedPages = [];
|
|
154
|
+
const pool = [];
|
|
155
|
+
const addHash = !!process.argv.includes("--add-hash");
|
|
156
|
+
pool.push(renderPage(renderedPages, addHash));
|
|
157
|
+
await Promise.all(pool);
|
|
158
|
+
})();
|
package/dist/scripts.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import chokidar from "chokidar";
|
|
4
|
+
import { glob } from "glob";
|
|
5
|
+
import slash from "slash";
|
|
6
|
+
import { transformWithEsbuild } from "vite";
|
|
7
|
+
const log = console.log.bind(console);
|
|
8
|
+
const scriptCompile = async (inputPath) => {
|
|
9
|
+
console.log("compile:", slash(inputPath));
|
|
10
|
+
const code = fs.readFileSync(inputPath, "utf8");
|
|
11
|
+
return transformWithEsbuild(code, inputPath, {
|
|
12
|
+
minify: true,
|
|
13
|
+
format: "esm",
|
|
14
|
+
sourcemap: path.basename(inputPath).includes("critical") ? false : "external"
|
|
15
|
+
}).then((result) => {
|
|
16
|
+
const savePath = path.resolve("public/assets/js/" + path.parse(inputPath).name + ".js");
|
|
17
|
+
const saveDir = path.dirname(savePath);
|
|
18
|
+
if (!fs.existsSync(saveDir)) {
|
|
19
|
+
fs.mkdirSync(saveDir);
|
|
20
|
+
}
|
|
21
|
+
fs.writeFileSync(savePath, result.code);
|
|
22
|
+
}).catch((error) => {
|
|
23
|
+
log(error);
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
const run = async () => {
|
|
27
|
+
if (process.argv.includes("--watch")) {
|
|
28
|
+
const watcher = chokidar.watch("src/assets/scripts/**/*.{js,jsx,ts,tsx}");
|
|
29
|
+
watcher.on("add", scriptCompile).on("change", scriptCompile).on("unlink", (path2) => log(`File ${path2} has been removed`));
|
|
30
|
+
} else {
|
|
31
|
+
const pool = [];
|
|
32
|
+
glob.sync("src/assets/scripts/**/*.{js,jsx,ts,tsx}").forEach((p) => pool.push(scriptCompile(p)));
|
|
33
|
+
await Promise.all(pool);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
await run();
|