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/dist/server.js ADDED
@@ -0,0 +1,171 @@
1
+ import { createServer as createServer$1, loadEnv } from "vite";
2
+ import fs$1 from "node:fs";
3
+ import path$1 from "node:path";
4
+ import express from "express";
5
+ import serveStatic from "serve-static";
6
+ import chalk from "chalk";
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import * as cheerio from "cheerio";
10
+ import jsBeautify from "js-beautify";
11
+ const createViteDevServer = ({ root: root2, baseUrl, hmrPort, isTest: isTest2 }) => {
12
+ const server = createServer$1({
13
+ root: root2,
14
+ base: baseUrl,
15
+ logLevel: isTest2 ? "error" : "info",
16
+ server: {
17
+ middlewareMode: true,
18
+ watch: {
19
+ // During tests we edit the files too fast and sometimes chokidar
20
+ // misses change events, so enforce polling for consistency
21
+ usePolling: true,
22
+ interval: 200
23
+ },
24
+ hmr: {
25
+ port: hmrPort
26
+ }
27
+ },
28
+ appType: "custom"
29
+ });
30
+ return server;
31
+ };
32
+ const beautifyOptions = {
33
+ indent_size: 2,
34
+ indent_char: " ",
35
+ keep_array_indentation: false,
36
+ break_chained_methods: false,
37
+ indent_scripts: "normal",
38
+ brace_style: "expand",
39
+ space_before_conditional: true,
40
+ unescape_strings: false,
41
+ jslint_happy: false,
42
+ end_with_newline: false,
43
+ wrap_line_length: 0,
44
+ indent_inner_html: false,
45
+ comma_first: false,
46
+ e4x: false,
47
+ indent_empty_lines: false,
48
+ wrap_attributes: "force"
49
+ };
50
+ const updateResourcePath = ($, tagName, attr) => {
51
+ $(tagName).each((_, el) => {
52
+ const href = $(el).attr(attr);
53
+ if (href && href.startsWith("/")) {
54
+ let newPath = href;
55
+ if (process.env.VITE_DOMAIN) {
56
+ newPath = process.env.VITE_DOMAIN + newPath;
57
+ }
58
+ if (href.startsWith("/") && !href.startsWith("/assets/vendors/") && [".css", ".ico", ".js", ".webmanifest", ".svg"].includes(path.extname(href).toLowerCase()) && !/\.0x[a-z0-9]{8}\.\w+$/gi.test(href)) {
59
+ newPath += "?v=" + (/* @__PURE__ */ new Date()).getTime();
60
+ }
61
+ if (newPath != href) {
62
+ $(el).attr(attr, newPath);
63
+ }
64
+ }
65
+ });
66
+ };
67
+ const removeDuplicateAssets = ($, selector, attr, paths) => {
68
+ $(selector).each((_, el) => {
69
+ if ($(el).attr("data-pl-inplace") === "true") {
70
+ return;
71
+ }
72
+ const path2 = $(el).attr(attr);
73
+ if (!path2) {
74
+ return;
75
+ }
76
+ const index = $(el).index();
77
+ const parent = $(el).parent().clone();
78
+ const child = parent.children()[index];
79
+ parent.empty();
80
+ parent.append(child);
81
+ const html = parent.html();
82
+ $(el).after("\n<!-- " + html + " -->");
83
+ if (paths.includes(path2)) {
84
+ $(el).remove();
85
+ return;
86
+ }
87
+ paths.push(path2);
88
+ $("head").append(el);
89
+ });
90
+ };
91
+ const _useRenderer = ({ app, indexProd, isProd: isProd2, viteDevServer, resolve }) => {
92
+ app.use(async (req, res) => {
93
+ try {
94
+ let template, render;
95
+ if (!isProd2) {
96
+ template = fs.readFileSync(resolve("index.html"), "utf-8");
97
+ template = await viteDevServer.transformIndexHtml(req.originalUrl, template);
98
+ render = (await viteDevServer.ssrLoadModule(resolve("src/entry-server.tsx"))).render;
99
+ } else {
100
+ template = indexProd;
101
+ render = (await import(resolve("dist/server/entry-server.js"))).render;
102
+ }
103
+ const context = {};
104
+ const output = render(req.originalUrl);
105
+ if (context.url) {
106
+ return res.redirect(301, context.url);
107
+ }
108
+ const html = template.replace("<!--app-html-->", output.html);
109
+ const $ = cheerio.load(html);
110
+ const paths = [];
111
+ removeDuplicateAssets($, "link[data-pl-require][href]", "href", paths);
112
+ removeDuplicateAssets($, "script[data-pl-require][src]", "src", paths);
113
+ updateResourcePath($, "link", "href");
114
+ updateResourcePath($, "script", "src");
115
+ updateResourcePath($, "img", "src");
116
+ res.status(200).set({ "Content-Type": "text/html" }).end(jsBeautify.html_beautify($.html(), beautifyOptions).replace("/* app-styles */", output.styles));
117
+ } catch (e) {
118
+ !isProd2 && viteDevServer.ssrFixStacktrace(e);
119
+ console.log(e.stack);
120
+ res.status(500).end(e.stack);
121
+ }
122
+ });
123
+ };
124
+ const argvModeIndex$1 = process.argv.indexOf("--mode");
125
+ const mode$1 = argvModeIndex$1 >= 0 && argvModeIndex$1 < process.argv.length - 1 && !process.argv[argvModeIndex$1 + 1].startsWith("-") ? process.argv[argvModeIndex$1 + 1] : "production";
126
+ process.env.MY_CUSTOM_SECRET = "API_KEY_4c2928b5a14b475d94c3579cbea06178";
127
+ const isProd = process.env.NODE_ENV === "production";
128
+ const createServer = async ({ root: root2, hmrPort, baseUrl, isTest: isTest2 }) => {
129
+ const resolve = (p) => path$1.join(root2, p);
130
+ const indexProd = isProd ? fs$1.readFileSync(resolve("index.html"), "utf-8") : "";
131
+ const app = express();
132
+ let viteDevServer;
133
+ if (!isProd) {
134
+ viteDevServer = await createViteDevServer({ root: root2, baseUrl, hmrPort, isTest: isTest2 });
135
+ app.use(viteDevServer.middlewares);
136
+ }
137
+ app.use("/assets/images", serveStatic(resolve("public/assets/images"), { index: false }));
138
+ app.use("/assets/fonts", serveStatic(resolve("public/assets/fonts"), { index: false }));
139
+ app.use("/assets/css", serveStatic(resolve("public/assets/css"), { index: false }));
140
+ app.use("/assets/js", serveStatic(resolve("public/assets/js"), { index: false }));
141
+ app.use("/assets/vendors", serveStatic(resolve("public/assets/vendors"), { index: false }));
142
+ app.use("/assets", serveStatic(resolve("dist/assets"), { index: false }));
143
+ app.use("/samples", serveStatic(resolve("public/samples"), { index: false }));
144
+ _useRenderer({ app, indexProd, isProd, viteDevServer, resolve });
145
+ return { app, viteDevServer };
146
+ };
147
+ const startServer = (props) => {
148
+ createServer(props).then(({ app }) => {
149
+ app.listen(props.port, () => {
150
+ const xpackEnv2 = loadEnv(mode$1, props.root);
151
+ console.log("Running on " + chalk.green("http://localhost:" + props.port + xpackEnv2.VITE_BASE_URL));
152
+ });
153
+ });
154
+ };
155
+ console.log("[INIT] server");
156
+ const argvModeIndex = process.argv.indexOf("--mode");
157
+ const mode = argvModeIndex >= 0 && argvModeIndex < process.argv.length - 1 && !process.argv[argvModeIndex + 1].startsWith("-") ? process.argv[argvModeIndex + 1] : "production";
158
+ const root = process.cwd();
159
+ const xpackEnv = loadEnv(mode, root);
160
+ const isTest = !!xpackEnv.VITE_TEST_BUILD || process.env.NODE_ENV === "test";
161
+ const port = xpackEnv.VITE_PORT ? parseInt(xpackEnv.VITE_PORT) : 5e3;
162
+ if (!isTest) {
163
+ console.log(root);
164
+ startServer({
165
+ root,
166
+ isTest,
167
+ port,
168
+ hmrPort: port + 1,
169
+ baseUrl: xpackEnv.VITE_BASE_URL
170
+ });
171
+ }
package/dist/states.js ADDED
@@ -0,0 +1,41 @@
1
+ import fs from "fs";
2
+ import chokidar from "chokidar";
3
+ import debounce from "debounce";
4
+ import { glob } from "glob";
5
+ const isWatch = process.argv.includes("--watch");
6
+ const log = console.log.bind(console);
7
+ const states = {};
8
+ const buildStates = debounce(() => {
9
+ const output = [];
10
+ const keys = Object.keys(states);
11
+ [].forEach.call(keys, (key) => {
12
+ const state = states[key];
13
+ if (!state) {
14
+ return;
15
+ }
16
+ try {
17
+ output.push(JSON.parse(state));
18
+ } catch (error) {
19
+ console.log(error);
20
+ }
21
+ });
22
+ const json = JSON.stringify(output, null, " ");
23
+ fs.writeFileSync("public/pl-states.json", json);
24
+ }, 500);
25
+ const setStates = (statePath) => {
26
+ const state = fs.readFileSync(statePath, "utf-8");
27
+ states[statePath] = state;
28
+ buildStates();
29
+ };
30
+ const removeStates = (statePath) => {
31
+ delete states[statePath];
32
+ buildStates();
33
+ };
34
+ if (isWatch) {
35
+ const watcher = chokidar.watch("src/**/*.states.json");
36
+ watcher.on("ready", () => {
37
+ log("States are ready!");
38
+ }).on("add", (path) => setStates(path)).on("change", (path) => setStates(path)).on("unlink", (path) => removeStates(path));
39
+ } else {
40
+ glob.sync("src/**/*.states.json").forEach((path) => setStates(path));
41
+ }
package/dist/styles.js ADDED
@@ -0,0 +1,165 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { pathToFileURL, fileURLToPath } from "url";
4
+ import { watch } from "chokidar";
5
+ import * as sass from "sass";
6
+ import slash from "slash";
7
+ import debounce from "debounce";
8
+ import { glob } from "glob";
9
+ import postcss from "postcss";
10
+ import autoprefixer from "autoprefixer";
11
+ import cssnano from "cssnano";
12
+ const isWatch = process.argv.includes("--watch");
13
+ const outDir = "./public/assets/css";
14
+ if (!isWatch && fs.existsSync(outDir)) {
15
+ fs.rmSync(outDir, { force: true, recursive: true });
16
+ }
17
+ if (!fs.existsSync(outDir)) {
18
+ fs.mkdirSync(outDir, { recursive: true });
19
+ }
20
+ const log = console.log.bind(console);
21
+ const prepareCssFileContent = ({
22
+ srcFile,
23
+ includeMixins = true,
24
+ includeAbstracts = true
25
+ }) => {
26
+ return [
27
+ includeAbstracts ? slash(`@use '${path.relative(path.dirname(srcFile), path.resolve("src/assets/styles/00-abstracts/abstracts"))}' as *;
28
+ `) : void 0,
29
+ includeMixins ? slash(`@use '${path.relative(path.dirname(srcFile), path.resolve("src/assets/styles/01-mixins/mixins"))}' as *;
30
+ `) : void 0,
31
+ fs.readFileSync(srcFile, "utf-8")
32
+ ].filter(Boolean);
33
+ };
34
+ const stringOptions = (srcFile) => {
35
+ const options = {
36
+ sourceMap: true,
37
+ sourceMapIncludeSources: true,
38
+ syntax: "scss",
39
+ style: "compressed",
40
+ url: pathToFileURL(path.resolve(srcFile)),
41
+ importer: {
42
+ canonicalize(url) {
43
+ return new URL(url);
44
+ },
45
+ load(canonicalUrl) {
46
+ let filePath = fileURLToPath(canonicalUrl);
47
+ if (!filePath.endsWith(".scss")) {
48
+ const parentDir = path.dirname(filePath);
49
+ const fileName = path.basename(filePath);
50
+ filePath = path.join(parentDir, fileName + ".scss");
51
+ if (!fs.existsSync(filePath)) {
52
+ filePath = path.join(parentDir, "_" + fileName + ".scss");
53
+ }
54
+ }
55
+ if (!fs.existsSync(filePath)) return null;
56
+ if (filePath.includes("abstracts") || filePath.includes("_mixins") || filePath.includes("_base") || filePath.includes("xpack"))
57
+ return {
58
+ contents: fs.readFileSync(filePath, "utf-8"),
59
+ syntax: "scss"
60
+ };
61
+ let content = prepareCssFileContent({ srcFile: filePath });
62
+ if (filePath.includes("mixins")) {
63
+ content = prepareCssFileContent({ srcFile: filePath, includeMixins: false });
64
+ }
65
+ return {
66
+ contents: content.join(""),
67
+ syntax: "scss"
68
+ };
69
+ }
70
+ }
71
+ };
72
+ return options;
73
+ };
74
+ const compile = (srcFile, options) => {
75
+ if (options.isReady) {
76
+ log("compile:", slash(srcFile));
77
+ }
78
+ if (path.basename(srcFile).startsWith("_")) {
79
+ return;
80
+ }
81
+ const name = path.basename(srcFile) === "index.scss" ? path.basename(path.dirname(srcFile)) + ".css" : path.basename(srcFile).replace(/\.scss$/gi, ".css");
82
+ const outFile = (options.prefix ?? "") + name;
83
+ const cssStrings = srcFile.includes("xpack") ? [fs.readFileSync(srcFile, "utf-8")] : prepareCssFileContent({ srcFile });
84
+ if (srcFile.includes("style-base") || srcFile.includes("style-all")) {
85
+ glob.sync("./src/atoms/**/*.scss").forEach((atomPath) => {
86
+ if (!path.basename(atomPath).startsWith("_")) {
87
+ cssStrings.push(sass.compileString(prepareCssFileContent({ srcFile: atomPath }).join(""), stringOptions(atomPath)).css);
88
+ }
89
+ });
90
+ glob.sync("./src/molecules/**/*.scss").forEach((molPath) => {
91
+ if (!path.basename(molPath).startsWith("_")) {
92
+ cssStrings.push(sass.compileString(prepareCssFileContent({ srcFile: molPath }).join(""), stringOptions(molPath)).css);
93
+ }
94
+ });
95
+ }
96
+ sass.compileStringAsync(cssStrings.join(""), stringOptions(srcFile)).then((result) => postcssProcess(result, srcFile, outFile)).catch((error) => {
97
+ log(error);
98
+ });
99
+ };
100
+ const postcssProcess = (result, from, to) => {
101
+ const postcssOptions = { from: pathToFileURL(from).href, to, map: { prev: result.sourceMap, absolute: false } };
102
+ postcss([autoprefixer({ grid: true }), cssnano]).process(result.css, postcssOptions).then((result2) => {
103
+ fs.writeFileSync(path.join(outDir, to), result2.css + (result2.map ? `
104
+ /*# sourceMappingURL=${to}.map */` : ""));
105
+ if (result2.map) {
106
+ fs.writeFileSync(path.join(outDir, to + ".map"), result2.map.toString());
107
+ }
108
+ });
109
+ };
110
+ const styleOrganisms = debounce((isReady) => {
111
+ const paths = glob.sync("src/organisms/**/*.scss", { nodir: true });
112
+ [].forEach.call(paths, (p) => styleOrganism(p, isReady));
113
+ }, 200);
114
+ const styleTemplates = debounce((isReady) => {
115
+ const paths = glob.sync("src/templates/**/*.scss", { nodir: true });
116
+ [].forEach.call(paths, (p) => styleTemplate(p, isReady));
117
+ }, 200);
118
+ const styleBase = debounce((isReady) => compile("src/assets/styles/style-base.scss", { isReady }), 200);
119
+ const stylePlState = debounce((isReady) => compile("xpack/styles/pl-states.scss", { isReady }), 200);
120
+ const styleRoot = debounce((isReady) => compile("xpack/styles/root.scss", { isReady }), 200);
121
+ const styleOrganism = (srcFile, isReady) => compile(srcFile, { prefix: "b-", isReady });
122
+ const styleTemplate = (srcFile, isReady) => compile(srcFile, { prefix: "p-", isReady });
123
+ const sassCompile = (inputPath, isReady) => {
124
+ const p = slash(inputPath);
125
+ if (p.startsWith("src/assets/styles/00-abstracts/") || p.startsWith("src/assets/styles/01-mixins/")) {
126
+ styleBase(isReady);
127
+ styleOrganisms(isReady);
128
+ styleTemplates(isReady);
129
+ stylePlState(isReady);
130
+ }
131
+ if (p.startsWith("src/atoms") || p.startsWith("src/molecules") || p.startsWith("src/assets/styles/02-base")) {
132
+ styleBase(isReady);
133
+ }
134
+ if (p.startsWith("src/organisms")) {
135
+ if (path.basename(p).startsWith("_")) {
136
+ glob.sync(path.dirname(p) + "/*.scss", { nodir: true }).filter((p2) => !path.basename(p2).startsWith("_")).forEach((p2) => styleOrganism(p2, isReady));
137
+ } else {
138
+ styleOrganism(p, isReady);
139
+ }
140
+ }
141
+ if (p.startsWith("src/templates")) {
142
+ if (path.basename(p).startsWith("_")) {
143
+ glob.sync(path.dirname(p) + "/*.scss", { nodir: true }).filter((p2) => !path.basename(p2).startsWith("_")).forEach((p2) => styleTemplate(p2, isReady));
144
+ } else {
145
+ styleTemplate(p, isReady);
146
+ }
147
+ }
148
+ if (p.startsWith("xpack/styles/pl-states")) {
149
+ stylePlState(isReady);
150
+ } else if (p.startsWith("xpack/styles")) {
151
+ styleRoot(isReady);
152
+ }
153
+ };
154
+ if (isWatch) {
155
+ const watcher = watch(["src", "xpack/styles"], { ignored: (path2, stats) => !!stats?.isFile() && !path2.endsWith(".scss") });
156
+ let isReady = false;
157
+ watcher.on("ready", () => {
158
+ log("SCSS ready!");
159
+ isReady = true;
160
+ }).on("add", (path2) => sassCompile(path2, isReady)).on("change", (path2) => sassCompile(path2, isReady)).on("unlink", (path2) => log(`File ${path2} has been removed`));
161
+ } else {
162
+ styleBase(true);
163
+ stylePlState(true);
164
+ glob.sync(["src/{organisms,templates}/**/*.scss", "xpack/styles/**/*.scss"]).filter((p) => !path.basename(p).startsWith("_")).forEach((path2) => sassCompile(path2, true));
165
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buner",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Frontend build toolkit for Vite + React SSR projects — SCSS pipeline, prerender, SSR dev server, and backend integration.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,20 +20,12 @@
20
20
  "cli"
21
21
  ],
22
22
  "bin": {
23
- "buner": "./bin/buner.js"
23
+ "buner": "./dist/buner.js"
24
24
  },
25
25
  "files": [
26
- "bin",
27
- "cli",
26
+ "dist",
28
27
  "xpack",
29
28
  "public",
30
- "server.ts",
31
- "prerender.ts",
32
- "integration.ts",
33
- "styles.ts",
34
- "scripts.ts",
35
- "states.ts",
36
- "migrate-scss.ts",
37
29
  "vite.config.ts",
38
30
  "index.html",
39
31
  "tsconfig.json",
package/cli/README.md DELETED
@@ -1 +0,0 @@
1
- [![Build Status](https://episerver-es-emea.visualstudio.com/Alloy-Template/_apis/build/status%2FFE%20-%20Integration?branchName=fe-release)](https://episerver-es-emea.visualstudio.com/Alloy-Template/_build/latest?definitionId=28&branchName=fe-release)
package/cli/buner.ts DELETED
@@ -1,241 +0,0 @@
1
- /* eslint-disable no-console */
2
-
3
- import path from 'path';
4
- import { execSync, spawn, SpawnOptions } from 'child_process';
5
- import { fileURLToPath } from 'url';
6
-
7
- import { Command } from 'commander';
8
- import chalk from 'chalk';
9
- import fetch from 'node-fetch';
10
- import prompts from 'prompts';
11
-
12
- import packageJson from '../package.json';
13
-
14
- import { createApp } from './create-app.js';
15
- import { validateNpmName } from './helpers/validate-pkg.js';
16
-
17
- const { green, yellow, bold, cyan, red } = chalk;
18
- const packageName = 'buner';
19
-
20
- // Package's own directory (where server.ts, styles.ts, etc. live)
21
- const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
22
-
23
- /** Resolve a file path relative to the buner package directory */
24
- const pkg = (file: string) => path.join(packageDir, file);
25
-
26
- const run = (cmd: string, args: string[] = [], options: SpawnOptions = {}) => {
27
- return new Promise<void>((resolve, reject) => {
28
- const child = spawn(cmd, args, {
29
- stdio: 'inherit',
30
- shell: true,
31
- cwd: process.cwd(),
32
- ...options,
33
- });
34
-
35
- child.on('close', (code) => {
36
- if (code !== 0) {
37
- reject(new Error(`Command "${cmd} ${args.join(' ')}" exited with code ${code}`));
38
- } else {
39
- resolve();
40
- }
41
- });
42
-
43
- child.on('error', reject);
44
- });
45
- };
46
-
47
- const runSync = (cmd: string) => {
48
- execSync(cmd, { stdio: 'inherit', cwd: process.cwd() });
49
- };
50
-
51
- const onPromptState = (state: { value?: string; aborted?: boolean }) => {
52
- if (state?.aborted) {
53
- process.stdout.write('\x1B[?25h');
54
- process.stdout.write('\n');
55
- process.exit(1);
56
- }
57
- };
58
-
59
- const parseVersion = (version: string): number => {
60
- return parseInt(version.replaceAll('.', ''));
61
- };
62
-
63
- const update = fetch(`https://registry.npmjs.org/${packageJson.name}/latest`)
64
- .then((res) => res.json())
65
- .catch(() => null);
66
-
67
- async function notifyUpdate(): Promise<void> {
68
- try {
69
- const data = (await update) as { version: string };
70
-
71
- if (data.version && parseVersion(data.version) !== parseVersion(packageJson.version)) {
72
- const updateMessage = `npm update -g ${packageName}`;
73
-
74
- console.log(
75
- yellow(bold(`A new version of '${packageName}' is available!`)) + '\n' + 'You can update by running: ' + cyan(updateMessage) + '\n'
76
- );
77
- }
78
- } catch {
79
- // ignore error
80
- }
81
- }
82
-
83
- const program = new Command();
84
-
85
- program.name(packageName).description('Frontend build toolkit for Vite + React SSR projects').version(packageJson.version);
86
-
87
- // buner create [dir]
88
- program
89
- .command('create')
90
- .argument('[project-directory]', 'the project name', '')
91
- .description('Scaffold a new frontend project')
92
- .action(async (projectPath: string) => {
93
- if (!projectPath) {
94
- const validation = validateNpmName('my-app');
95
-
96
- const res = await prompts({
97
- onState: onPromptState,
98
- type: 'text',
99
- name: 'path',
100
- message: 'What is your project named?',
101
- initial: 'my-app',
102
- validate: (name) => {
103
- const validation = validateNpmName(path.basename(path.resolve(name)));
104
-
105
- if (validation.valid) {
106
- return true;
107
- }
108
-
109
- return `Invalid project name ${validation?.problems?.[0] ? validation?.problems?.[0] : ''}`;
110
- },
111
- });
112
-
113
- if (typeof res.path === 'string') {
114
- projectPath = res.path.trim();
115
- }
116
- }
117
-
118
- if (!projectPath) {
119
- console.log(
120
- '\nPlease specify the project directory:\n' +
121
- ` ${cyan('buner create')} ${green('<project-directory>')}\n` +
122
- 'For example:\n' +
123
- ` ${cyan('buner create')} ${green('my-app')}\n`
124
- );
125
- process.exit(1);
126
- }
127
-
128
- const resolvedProjectPath = path.resolve(projectPath);
129
-
130
- await createApp({ appPath: resolvedProjectPath });
131
- await notifyUpdate();
132
- });
133
-
134
- // buner dev
135
- program
136
- .command('dev')
137
- .description('Start development mode with all watchers')
138
- .action(async () => {
139
- await run('npx', [
140
- 'concurrently',
141
- '--kill-others',
142
- `"bun ${pkg('styles.ts')} --watch"`,
143
- `"bun ${pkg('states.ts')} --watch"`,
144
- `"cross-env scriptOnly=true npx vite build --config ${pkg('vite.config.ts')} --mode development --watch"`,
145
- `"bun ${pkg('server.ts')} --mode development"`,
146
- ]);
147
- });
148
-
149
- // buner serve
150
- program
151
- .command('serve')
152
- .description('Start the SSR dev server')
153
- .option('--mode <mode>', 'server mode', 'development')
154
- .action(async (opts) => {
155
- await run('bun', [pkg('server.ts'), '--mode', opts.mode]);
156
- });
157
-
158
- // buner build
159
- program
160
- .command('build')
161
- .description('Build the project (static + SSR)')
162
- .action(async () => {
163
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --outDir dist/static`);
164
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
165
- });
166
-
167
- // buner generate
168
- program
169
- .command('generate')
170
- .description('Full static site generation (states + styles + build + prerender)')
171
- .option('--mode <mode>', 'build mode', 'production')
172
- .action(async (opts) => {
173
- runSync(`bun ${pkg('states.ts')}`);
174
- runSync(`bun ${pkg('styles.ts')}`);
175
- if (opts.mode === 'production') {
176
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --outDir dist/static`);
177
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
178
- } else {
179
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --outDir dist/static --mode ${opts.mode}`);
180
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
181
- }
182
- runSync(`bun ${pkg('prerender.ts')} --add-hash --mode ${opts.mode}`);
183
- });
184
-
185
- // buner eshn
186
- program
187
- .command('eshn')
188
- .description('Generate with --mode eshn')
189
- .action(async () => {
190
- runSync(`bun ${pkg('states.ts')}`);
191
- runSync(`bun ${pkg('styles.ts')}`);
192
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --outDir dist/static --mode eshn`);
193
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server --mode eshn`);
194
- runSync(`bun ${pkg('prerender.ts')} --add-hash --mode eshn`);
195
- });
196
-
197
- // buner inte
198
- program
199
- .command('inte')
200
- .description('Build and integrate with backend (styles + build + prerender + integration)')
201
- .action(async () => {
202
- runSync(`bun ${pkg('styles.ts')}`);
203
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --outDir dist/static`);
204
- runSync(`npx vite build --config ${pkg('vite.config.ts')} --ssr src/entry-server.tsx --outDir dist/server`);
205
- runSync(`bun ${pkg('prerender.ts')}`);
206
- runSync(`bun ${pkg('integration.ts')}`);
207
- });
208
-
209
- // buner styles
210
- program
211
- .command('styles')
212
- .description('Compile SCSS')
213
- .option('--watch', 'Watch for changes')
214
- .action(async (opts) => {
215
- const args = [pkg('styles.ts')];
216
-
217
- if (opts.watch) args.push('--watch');
218
- await run('bun', args);
219
- });
220
-
221
- // buner prerender
222
- program
223
- .command('prerender')
224
- .description('Pre-render HTML files')
225
- .option('--add-hash', 'Add content hashes to asset URLs')
226
- .option('--mode <mode>', 'build mode', 'production')
227
- .action(async (opts) => {
228
- const args = [pkg('prerender.ts')];
229
-
230
- if (opts.addHash) args.push('--add-hash');
231
- args.push('--mode', opts.mode);
232
- await run('bun', args);
233
- });
234
-
235
- program.parseAsync(process.argv).catch(async (error) => {
236
- console.log(red(error));
237
- await notifyUpdate();
238
- process.exit(1);
239
- });
240
-
241
- export { packageName };