buner 1.0.5 → 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 +40 -34
- 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 -12
- package/cli/README.md +0 -1
- package/cli/buner.ts +0 -264
- 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,18 +13,18 @@ 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$1 = { "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
|
-
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", "
|
|
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" };
|
|
28
28
|
const peerDependencies = { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "react-router-dom": "^6.0.0 || ^7.0.0" };
|
|
29
29
|
const devDependencies = { "@eslint/compat": "^2.0.0", "@next/eslint-plugin-next": "^16.0.8", "@types/css": "^0.0.38", "@types/debounce": "^1.2.4", "@types/express": "^5.0.6", "@types/js-beautify": "^1.14.3", "@types/lodash": "^4.17.24", "@types/node": "^24.11.0", "@types/prettier": "^3.0.0", "@types/prompts": "^2.4.9", "@types/react": "^19.2.14", "@types/react-dom": "^19.1.7", "@types/validate-npm-package-name": "^4.0.2", "@typescript-eslint/eslint-plugin": "^8.50.0", "@typescript-eslint/parser": "^8.50.0", "css": "^3.0.0", "eslint": "^9.39.2", "eslint-config-next": "^16.0.8", "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", "eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react": "7.37.5", "eslint-plugin-react-hooks": "7.0.1", "eslint-plugin-react-refresh": "^0.5.2", "eslint-plugin-unused-imports": "^4.4.1", "globals": "^16.3.0", "msw": "^2.12.10", "postcss-scss": "^4.0.9", "prettier": "^3.6.2", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^7.13.1", "rollup": "^4.59.0", "svgo": "^4.0.0" };
|
|
30
30
|
const msw = { "workerDirectory": "public" };
|
|
@@ -430,11 +430,12 @@ 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
435
|
const bin = (name2) => {
|
|
436
436
|
const ext = process.platform === "win32" ? ".cmd" : "";
|
|
437
|
-
const
|
|
437
|
+
const pkgRoot = path.resolve(packageDir, "..");
|
|
438
|
+
const local = path.join(pkgRoot, "node_modules", ".bin", name2 + ext);
|
|
438
439
|
try {
|
|
439
440
|
if (require("fs").existsSync(local)) return local;
|
|
440
441
|
} catch {
|
|
@@ -529,57 +530,62 @@ For example:
|
|
|
529
530
|
await notifyUpdate();
|
|
530
531
|
});
|
|
531
532
|
program.command("dev").description("Start development mode with all watchers").action(async () => {
|
|
533
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
532
534
|
await run(bin("concurrently"), [
|
|
533
535
|
"--kill-others",
|
|
534
|
-
`"
|
|
535
|
-
`"
|
|
536
|
-
`"${bin("cross-env")} scriptOnly=true ${bin("vite")} build --config ${
|
|
537
|
-
`"
|
|
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"`
|
|
538
540
|
]);
|
|
539
541
|
});
|
|
540
542
|
program.command("serve").description("Start the SSR dev server").option("--mode <mode>", "server mode", "development").action(async (opts) => {
|
|
541
|
-
await run(
|
|
543
|
+
await run("node", [pkg("server.js"), "--mode", opts.mode]);
|
|
542
544
|
});
|
|
543
545
|
program.command("build").description("Build the project (static + SSR)").action(async () => {
|
|
544
|
-
|
|
545
|
-
runSync(`${bin("vite")} build --config ${
|
|
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`);
|
|
546
549
|
});
|
|
547
550
|
program.command("generate").description("Full static site generation (states + styles + build + prerender)").option("--mode <mode>", "build mode", "production").action(async (opts) => {
|
|
548
|
-
|
|
549
|
-
runSync(
|
|
551
|
+
const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
|
|
552
|
+
runSync(`node ${pkg("states.js")}`);
|
|
553
|
+
runSync(`node ${pkg("styles.js")}`);
|
|
550
554
|
if (opts.mode === "production") {
|
|
551
|
-
runSync(`${bin("vite")} build --config ${
|
|
552
|
-
runSync(`${bin("vite")} build --config ${
|
|
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`);
|
|
553
557
|
} else {
|
|
554
|
-
runSync(`${bin("vite")} build --config ${
|
|
555
|
-
runSync(`${bin("vite")} build --config ${
|
|
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}`);
|
|
556
560
|
}
|
|
557
|
-
runSync(
|
|
561
|
+
runSync(`node ${pkg("prerender.js")} --add-hash --mode ${opts.mode}`);
|
|
558
562
|
});
|
|
559
563
|
program.command("eshn").description("Generate with --mode eshn").action(async () => {
|
|
560
|
-
|
|
561
|
-
runSync(
|
|
562
|
-
runSync(
|
|
563
|
-
runSync(`${bin("vite")} build --config ${
|
|
564
|
-
runSync(`${bin("
|
|
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`);
|
|
565
570
|
});
|
|
566
571
|
program.command("inte").description("Build and integrate with backend (styles + build + prerender + integration)").action(async () => {
|
|
567
|
-
|
|
568
|
-
runSync(
|
|
569
|
-
runSync(`${bin("vite")} build --config ${
|
|
570
|
-
runSync(`${bin("
|
|
571
|
-
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")}`);
|
|
572
578
|
});
|
|
573
579
|
program.command("styles").description("Compile SCSS").option("--watch", "Watch for changes").action(async (opts) => {
|
|
574
|
-
const args = [pkg("styles.
|
|
580
|
+
const args = [pkg("styles.js")];
|
|
575
581
|
if (opts.watch) args.push("--watch");
|
|
576
|
-
await run(
|
|
582
|
+
await run("node", args);
|
|
577
583
|
});
|
|
578
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) => {
|
|
579
|
-
const args = [pkg("prerender.
|
|
585
|
+
const args = [pkg("prerender.js")];
|
|
580
586
|
if (opts.addHash) args.push("--add-hash");
|
|
581
587
|
args.push("--mode", opts.mode);
|
|
582
|
-
await run(
|
|
588
|
+
await run("node", args);
|
|
583
589
|
});
|
|
584
590
|
program.parseAsync(process.argv).catch(async (error) => {
|
|
585
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();
|