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.
@@ -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.4";
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": "./bin/buner.js" };
24
- const files = ["bin", "cli", "xpack", "public", "server.ts", "prerender.ts", "integration.ts", "styles.ts", "scripts.ts", "states.ts", "migrate-scss.ts", "vite.config.ts", "index.html", "tsconfig.json", "eslint.config.mjs", "types.d.ts", ".env", ".env.development", ".env.eshn", "README.md"];
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.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
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
- await run("npx", [
519
- "concurrently",
533
+ const viteConfig = path.resolve(packageDir, "..", "vite.config.ts");
534
+ await run(bin("concurrently"), [
520
535
  "--kill-others",
521
- `"bun ${pkg("styles.ts")} --watch"`,
522
- `"bun ${pkg("states.ts")} --watch"`,
523
- `"cross-env scriptOnly=true npx vite build --config ${pkg("vite.config.ts")} --mode development --watch"`,
524
- `"bun ${pkg("server.ts")} --mode development"`
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("bun", [pkg("server.ts"), "--mode", opts.mode]);
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
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --outDir dist/static`);
532
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --ssr src/entry-server.tsx --outDir dist/server`);
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
- runSync(`bun ${pkg("states.ts")}`);
536
- runSync(`bun ${pkg("styles.ts")}`);
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(`npx vite build --config ${pkg("vite.config.ts")} --outDir dist/static`);
539
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --ssr src/entry-server.tsx --outDir dist/server`);
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(`npx vite build --config ${pkg("vite.config.ts")} --outDir dist/static --mode ${opts.mode}`);
542
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
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(`bun ${pkg("prerender.ts")} --add-hash --mode ${opts.mode}`);
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
- runSync(`bun ${pkg("states.ts")}`);
548
- runSync(`bun ${pkg("styles.ts")}`);
549
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --outDir dist/static --mode eshn`);
550
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --ssr src/entry-server.tsx --outDir dist/server --mode eshn`);
551
- runSync(`bun ${pkg("prerender.ts")} --add-hash --mode eshn`);
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
- runSync(`bun ${pkg("styles.ts")}`);
555
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --outDir dist/static`);
556
- runSync(`npx vite build --config ${pkg("vite.config.ts")} --ssr src/entry-server.tsx --outDir dist/server`);
557
- runSync(`bun ${pkg("prerender.ts")}`);
558
- runSync(`bun ${pkg("integration.ts")}`);
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.ts")];
580
+ const args = [pkg("styles.js")];
562
581
  if (opts.watch) args.push("--watch");
563
- await run("bun", args);
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.ts")];
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("bun", args);
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
- /* eslint-disable no-console */
2
- import fs from 'fs';
3
- import path, { resolve } from 'path';
4
- import crypto from 'node:crypto';
5
- import nodeFs from 'node:fs';
6
-
7
- import slash from 'slash';
8
- import { glob } from 'glob';
9
- import { loadEnv } from 'vite';
10
- import chalk from 'chalk';
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: string) => slash(path.resolve(projectRoot, p));
13
+ const toAbsolute = (p) => slash(path.resolve(projectRoot, p));
30
14
  const log = console.log.bind(console);
31
-
32
- const hashes: Map<string, string> = new Map();
33
- const staticBasePath = toAbsolute('dist/static');
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) : undefined;
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
- const copyItems: CopyItem[] = [
45
- { from: 'css' },
46
- { from: 'fonts' },
47
- { from: 'images' },
48
- { from: 'js' },
49
- { from: 'vendors' },
50
- { from: 'hashes.json' },
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: string[] = ['css', 'images', 'js'];
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 + '/**/*.{css,js,svg}');
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('sha1');
94
-
62
+ const sha1Hash = crypto.createHash("sha1");
95
63
  sha1Hash.update(content);
96
- const hash = sha1Hash.digest('base64url').substring(0, 10);
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
- const sortedHashes = Array.from(hashes)
106
- .sort((a, b) => a[0].localeCompare(b[0]))
107
- .reduce(
108
- (obj, [key, value]) => {
109
- obj[key] = value;
110
-
111
- return obj;
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('./dist/static/{atoms,molecules,organisms,templates,pages}/**/*.*').forEach((p) => {
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, '$1-$2'));
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
- nodeFs.cpSync(p, resolve(patternPath, segments.join('-')), { recursive: true });
92
+ fs$1.cpSync(p, resolve(patternPath, segments.join("-")), { recursive: true });
133
93
  break;
134
94
  }
135
95
  });
136
-
137
- glob.sync(slash(path.resolve(patternPath + '/**/*.{htm,html}'))).forEach((p) => {
138
- const text = fs.readFileSync(p, 'utf-8');
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
- const checkExistFileList: FileExistCheck[] = [
150
- { fileName: 'hashes.json' },
151
- { fileName: /react-loader\.0x[a-z0-9_-]{8,12}\.js/gi, folder: 'js' },
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 === 'string') {
158
- const destPath = slash(path.join(destBasePath, file.folder ?? '', file.fileName.toString()));
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
+ })();
@@ -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();