buner 0.0.2 → 1.0.0
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/.env +15 -0
- package/.env.development +6 -0
- package/.env.eshn +5 -0
- package/README.md +141 -5
- package/bin/buner.js +566 -0
- package/cli/README.md +1 -0
- package/cli/buner.ts +234 -0
- package/cli/cli.ts +125 -0
- package/cli/create-app.ts +59 -0
- package/cli/helpers/copy.ts +62 -0
- package/cli/helpers/format-files.ts +189 -0
- package/cli/helpers/git.ts +77 -0
- package/cli/helpers/install.ts +26 -0
- package/cli/helpers/is-folder-empty.ts +40 -0
- package/cli/helpers/is-writeable.ts +14 -0
- package/cli/helpers/make-dir.ts +7 -0
- package/cli/helpers/validate-pkg.ts +17 -0
- package/cli/install-template.ts +77 -0
- package/eslint.config.mjs +187 -0
- package/index.html +44 -0
- package/integration.ts +179 -0
- package/migrate-scss.ts +42 -0
- package/package.json +135 -7
- package/prerender.ts +229 -0
- package/public/.nojekyll +1 -0
- package/public/400.html +1 -0
- package/public/401.html +21 -0
- package/public/403.html +252 -0
- package/public/404.css +51 -0
- package/public/404.html +29 -0
- package/public/__images__/awww.jpeg +0 -0
- package/public/__images__/bat-body.png +0 -0
- package/public/__images__/bat-wing.png +0 -0
- package/public/__images__/haunted-house-background.png +0 -0
- package/public/__images__/haunted-house-foreground.png +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Bold.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-BoldItalic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Italic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-Regular.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-SemiBold.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText-SemiBoldItalic.ttf +0 -0
- package/public/assets/fonts/crimson-text/CrimsonText.woff2 +0 -0
- package/public/assets/fonts/crimson-text/OFL.txt +93 -0
- package/public/assets/fonts/work-sans/OFL.txt +93 -0
- package/public/assets/fonts/work-sans/README.txt +81 -0
- package/public/assets/fonts/work-sans/WorkSans-Italic-VariableFont_wght.ttf +0 -0
- package/public/assets/fonts/work-sans/WorkSans-VariableFont_wght.ttf +0 -0
- package/public/assets/fonts/work-sans/WorkSans.woff2 +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Black.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-BlackItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Bold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-BoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraBold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraBoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraLight.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ExtraLightItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Italic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Light.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-LightItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Medium.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-MediumItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Regular.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-SemiBold.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-SemiBoldItalic.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-Thin.ttf +0 -0
- package/public/assets/fonts/work-sans/static/WorkSans-ThinItalic.ttf +0 -0
- package/public/assets/images/icons.svg +67 -0
- package/public/assets/images/logo.svg +14 -0
- package/public/assets/images/root.svg +49 -0
- package/public/assets/vendors/axios@0.24.0/axios.js +2275 -0
- package/public/assets/vendors/axios@0.24.0/axios.map +1 -0
- package/public/assets/vendors/axios@0.24.0/axios.min.js +2 -0
- package/public/assets/vendors/axios@0.24.0/axios.min.map +1 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +3 -0
- package/public/icon-128.png +0 -0
- package/public/icon-16.png +0 -0
- package/public/icon-192.png +0 -0
- package/public/icon-48.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/json/avatar.json +42 -0
- package/public/manifest.webmanifest +29 -0
- package/public/mockServiceWorker.js +349 -0
- package/public/pl-states.svg +4 -0
- package/public/samples/01.svg +1 -0
- package/public/samples/Airbnb.svg +3 -0
- package/public/samples/Facebook.svg +3 -0
- package/public/samples/Google.svg +8 -0
- package/public/samples/Microsoft.svg +7 -0
- package/public/samples/Spotify.svg +3 -0
- package/public/samples/alexandra-stolz.svg +35 -0
- package/public/samples/browserconfig.xml +9 -0
- package/public/samples/cliff-curtis.jpg +0 -0
- package/public/samples/emilia-clarke.jpg +0 -0
- package/public/samples/favicon.ico +0 -0
- package/public/samples/icons/android-chrome-192x192.png +0 -0
- package/public/samples/icons/apple-touch-icon.png +0 -0
- package/public/samples/icons/favicon-144x144.png +0 -0
- package/public/samples/icons/favicon-150x150.png +0 -0
- package/public/samples/icons/favicon-16x16.png +0 -0
- package/public/samples/icons/favicon-32x32.png +0 -0
- package/public/samples/icons/favicon-48x48.png +0 -0
- package/public/samples/icons/favicon-70x70.png +0 -0
- package/public/samples/icons/favicon.ico +0 -0
- package/public/samples/image-1.svg +166 -0
- package/public/samples/image-2.svg +110 -0
- package/public/samples/image-3.svg +113 -0
- package/public/samples/janet-bray.svg +36 -0
- package/public/samples/kate-winslet.jpg +0 -0
- package/public/samples/manifest.json +19 -0
- package/public/samples/michelle-yeoh.jpg +0 -0
- package/public/samples/peg-legge.svg +37 -0
- package/public/samples/richard-guerra.svg +42 -0
- package/public/samples/rose-leslie.jpg +0 -0
- package/public/samples/sample-1.svg +365 -0
- package/public/samples/sample-2.svg +129 -0
- package/public/samples/sample-3.svg +93 -0
- package/public/samples/sample-4.svg +168 -0
- package/public/samples/sample-5.svg +155 -0
- package/public/samples/sample-6.svg +445 -0
- package/public/samples/sample-7.svg +404 -0
- package/public/samples/sample-8.png +0 -0
- package/public/staticwebapp.config.json +138 -0
- package/scripts.ts +56 -0
- package/server.ts +29 -0
- package/states.ts +63 -0
- package/styles.ts +232 -0
- package/tsconfig.json +71 -25
- package/types.d.ts +54 -0
- package/vite.config.ts +3 -0
- package/xpack/alias.ts +21 -0
- package/xpack/config.ts +59 -0
- package/xpack/create-server.ts +68 -0
- package/xpack/create-vite-dev-server.ts +33 -0
- package/xpack/deploy/deploy-inte.ts +3 -0
- package/xpack/filename.ts +43 -0
- package/xpack/hooks/build-start.ts +17 -0
- package/xpack/hooks/close-bundle.ts +19 -0
- package/xpack/hooks/handle-hot-update.ts +22 -0
- package/xpack/hooks/options.ts +55 -0
- package/xpack/hooks/resolve-dynamic-import.ts +18 -0
- package/xpack/hooks/transform-index-html.ts +18 -0
- package/xpack/hooks/transform.ts +72 -0
- package/xpack/hooks/write-bundle.ts +16 -0
- package/xpack/manual-chunk.ts +56 -0
- package/xpack/paths.ts +30 -0
- package/xpack/renderer.ts +141 -0
- package/xpack/root/active-item-options.tsx +98 -0
- package/xpack/root/frame-controls.tsx +139 -0
- package/xpack/root/index.tsx +107 -0
- package/xpack/root/rendered-item.tsx +25 -0
- package/xpack/root/root-context.ts +22 -0
- package/xpack/root/root-nav.tsx +162 -0
- package/xpack/root/state-animation-html.tsx +18 -0
- package/xpack/root/template.tsx +23 -0
- package/xpack/root/use-click-outside.ts +37 -0
- package/xpack/scripts/color-mode.entry.ts +28 -0
- package/xpack/scripts/mock-api.entry.ts +11 -0
- package/xpack/scripts/pl-states.entry.ts +321 -0
- package/xpack/scripts/root.entry.ts +135 -0
- package/xpack/scripts/theme-critical.entry.ts +20 -0
- package/xpack/states.schema.json +61 -0
- package/xpack/styles/_border.scss +22 -0
- package/xpack/styles/_breakpoint.scss +117 -0
- package/xpack/styles/_form.scss +23 -0
- package/xpack/styles/_px2rem.scss +5 -0
- package/xpack/styles/_reset.scss +134 -0
- package/xpack/styles/_state-toggle.scss +121 -0
- package/xpack/styles/_theme.scss +68 -0
- package/xpack/styles/_top-panel.scss +87 -0
- package/xpack/styles/_xpack-root.scss +322 -0
- package/xpack/styles/pl-states.scss +308 -0
- package/xpack/styles/root.scss +129 -0
- package/.github/workflows/deploy.yaml +0 -32
- package/index.ts +0 -1
package/bin/buner.js
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execSync, spawn } from "child_process";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import fetch from "node-fetch";
|
|
7
|
+
import prompts from "prompts";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import fs$1 from "fs/promises";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import { globby } from "globby";
|
|
13
|
+
import { exec } from "node:child_process";
|
|
14
|
+
import validateProjectName from "validate-npm-package-name";
|
|
15
|
+
const name = "buner";
|
|
16
|
+
const version = "1.0.0";
|
|
17
|
+
const description = "Frontend build toolkit for Vite + React SSR projects — SCSS pipeline, prerender, SSR dev server, and backend integration.";
|
|
18
|
+
const type = "module";
|
|
19
|
+
const license = "MIT";
|
|
20
|
+
const repository = { "type": "git", "url": "https://github.com/precise-alloy/buner.git" };
|
|
21
|
+
const homepage = "https://www.npmjs.com/package/buner";
|
|
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"];
|
|
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
|
+
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", "validate-npm-package-name": "^7.0.0", "vite": "^7.0.6" };
|
|
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
|
+
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
|
+
const msw = { "workerDirectory": "public" };
|
|
31
|
+
const packageJson = {
|
|
32
|
+
name,
|
|
33
|
+
version,
|
|
34
|
+
description,
|
|
35
|
+
type,
|
|
36
|
+
license,
|
|
37
|
+
repository,
|
|
38
|
+
homepage,
|
|
39
|
+
keywords,
|
|
40
|
+
bin,
|
|
41
|
+
files,
|
|
42
|
+
scripts,
|
|
43
|
+
engines,
|
|
44
|
+
dependencies,
|
|
45
|
+
peerDependencies,
|
|
46
|
+
devDependencies,
|
|
47
|
+
msw
|
|
48
|
+
};
|
|
49
|
+
const isWriteable = async (directory) => {
|
|
50
|
+
try {
|
|
51
|
+
await fs.promises.access(directory, fs.constants.W_OK);
|
|
52
|
+
return true;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const makeDir = (root, options = { recursive: true }) => {
|
|
58
|
+
return fs.promises.mkdir(root, options);
|
|
59
|
+
};
|
|
60
|
+
const isFolderEmpty = (root) => {
|
|
61
|
+
const validFiles = [
|
|
62
|
+
".DS_Store",
|
|
63
|
+
".git",
|
|
64
|
+
".gitattributes",
|
|
65
|
+
".gitignore",
|
|
66
|
+
".gitlab-ci.yml",
|
|
67
|
+
".hg",
|
|
68
|
+
".hgcheck",
|
|
69
|
+
".hgignore",
|
|
70
|
+
".idea",
|
|
71
|
+
".npmignore",
|
|
72
|
+
".travis.yml",
|
|
73
|
+
"LICENSE",
|
|
74
|
+
"Thumbs.db",
|
|
75
|
+
"docs",
|
|
76
|
+
"mkdocs.yml",
|
|
77
|
+
"npm-debug.log",
|
|
78
|
+
"yarn-debug.log",
|
|
79
|
+
"yarn-error.log",
|
|
80
|
+
"yarnrc.yml",
|
|
81
|
+
".yarn"
|
|
82
|
+
];
|
|
83
|
+
const conflicts = fs.readdirSync(root).filter((file) => !validFiles.includes(file)).filter((file) => !/\.iml$/.test(file));
|
|
84
|
+
if (conflicts.length > 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
};
|
|
89
|
+
function isInGitRepository() {
|
|
90
|
+
try {
|
|
91
|
+
execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
|
|
92
|
+
return true;
|
|
93
|
+
} catch (_) {
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
function isInMercurialRepository() {
|
|
98
|
+
try {
|
|
99
|
+
execSync("hg --cwd . root", { stdio: "ignore" });
|
|
100
|
+
return true;
|
|
101
|
+
} catch (_) {
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
function isDefaultBranchSet() {
|
|
106
|
+
try {
|
|
107
|
+
execSync("git config init.defaultBranch", { stdio: "ignore" });
|
|
108
|
+
return true;
|
|
109
|
+
} catch (_) {
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const tryGitInit = (root) => {
|
|
114
|
+
let didInit = false;
|
|
115
|
+
try {
|
|
116
|
+
execSync("git --version", { stdio: "ignore" });
|
|
117
|
+
if (isInGitRepository() || isInMercurialRepository()) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
execSync("git init", { stdio: "ignore" });
|
|
121
|
+
didInit = true;
|
|
122
|
+
if (!isDefaultBranchSet()) {
|
|
123
|
+
execSync("git checkout -b main", { stdio: "ignore" });
|
|
124
|
+
}
|
|
125
|
+
execSync("git add -A", { stdio: "ignore" });
|
|
126
|
+
execSync('git commit -m "Initial commit from Create App"', {
|
|
127
|
+
stdio: "ignore"
|
|
128
|
+
});
|
|
129
|
+
return true;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
if (didInit) {
|
|
132
|
+
try {
|
|
133
|
+
fs.rmSync(path.join(root, ".git"), { recursive: true, force: true });
|
|
134
|
+
} catch (_) {
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const excludeFiles = [
|
|
141
|
+
".git",
|
|
142
|
+
"bin",
|
|
143
|
+
".vscode",
|
|
144
|
+
".build",
|
|
145
|
+
"public/samples",
|
|
146
|
+
"public/assets/vendors",
|
|
147
|
+
"src/_api/!(_base.ts)",
|
|
148
|
+
"src/_data",
|
|
149
|
+
"src/assets/scripts/!(color-mode|main|mock-api|pl-states|root|theme-critical).entry.ts",
|
|
150
|
+
"src/atoms",
|
|
151
|
+
"src/mocks/avatar",
|
|
152
|
+
"src/mocks/user",
|
|
153
|
+
"src/molecules",
|
|
154
|
+
"src/organisms/!(root|header|footer)/*",
|
|
155
|
+
"src/pages/!(Root|Home).tsx",
|
|
156
|
+
"src/templates/!(root|home)/*",
|
|
157
|
+
"!cli",
|
|
158
|
+
"!vite.cli.config.ts"
|
|
159
|
+
];
|
|
160
|
+
const copy = async (src, dest, { cwd }) => {
|
|
161
|
+
const sourceFiles = await globby(src, {
|
|
162
|
+
cwd,
|
|
163
|
+
dot: true,
|
|
164
|
+
absolute: false,
|
|
165
|
+
gitignore: true,
|
|
166
|
+
ignore: excludeFiles
|
|
167
|
+
});
|
|
168
|
+
const destRelativeToCwd = path.resolve(dest);
|
|
169
|
+
await fs$1.mkdir(path.join(destRelativeToCwd, "src/atoms"), { recursive: true });
|
|
170
|
+
await fs$1.mkdir(path.join(destRelativeToCwd, "src/molecules"), { recursive: true });
|
|
171
|
+
await fs$1.mkdir(path.join(destRelativeToCwd, "src/mocks/example"), { recursive: true });
|
|
172
|
+
return Promise.all(
|
|
173
|
+
sourceFiles.map(async (p) => {
|
|
174
|
+
const dirname2 = path.dirname(p);
|
|
175
|
+
const basename = path.basename(p);
|
|
176
|
+
const from = path.resolve(cwd, p);
|
|
177
|
+
const filePath = path.join(destRelativeToCwd, dirname2, basename);
|
|
178
|
+
await fs$1.mkdir(path.dirname(filePath), { recursive: true });
|
|
179
|
+
return fs$1.copyFile(from, filePath);
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
const { red: red$1 } = chalk;
|
|
184
|
+
const install = async () => {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
exec("npm install", (error, stdout) => {
|
|
187
|
+
if (error) {
|
|
188
|
+
reject(error);
|
|
189
|
+
console.log(`${red$1(error)}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
console.log(stdout);
|
|
193
|
+
resolve(stdout);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
const formatFiles = async (root) => {
|
|
198
|
+
let filePath, data;
|
|
199
|
+
filePath = path.join(root, "src/mocks/handlers.ts");
|
|
200
|
+
data = "import { handlers } from './consts';\n\nexport { handlers };";
|
|
201
|
+
await fs$1.writeFile(filePath, data + os.EOL);
|
|
202
|
+
filePath = path.join(root, "src/mocks/example/index.ts");
|
|
203
|
+
data = `
|
|
204
|
+
import { handlers } from '@mocks/handlers';
|
|
205
|
+
import { rest } from 'msw';
|
|
206
|
+
|
|
207
|
+
handlers.push(
|
|
208
|
+
rest.get('/api/example', (req, res, ctx) => {
|
|
209
|
+
return res(ctx.status(200), ctx.json({test: 'test'}));
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
`;
|
|
213
|
+
await fs$1.writeFile(filePath, data + os.EOL);
|
|
214
|
+
filePath = path.join(root, "src/react-loader.tsx");
|
|
215
|
+
data = await fs$1.readFile(filePath, "utf8");
|
|
216
|
+
data = data.replace(
|
|
217
|
+
/const blocks((?!};).)*};/s,
|
|
218
|
+
"const blocks: { [name: string]: any } = {\n root: lazy(() => import('./organisms/root/Root')),\n};"
|
|
219
|
+
);
|
|
220
|
+
await fs$1.writeFile(filePath, data);
|
|
221
|
+
filePath = path.join(root, "src/_types/atoms.d.ts");
|
|
222
|
+
await fs$1.writeFile(
|
|
223
|
+
filePath,
|
|
224
|
+
`
|
|
225
|
+
import { BasedAtomicModel } from "./_general";
|
|
226
|
+
`
|
|
227
|
+
);
|
|
228
|
+
filePath = path.join(root, "src/_types/molecules.d.ts");
|
|
229
|
+
await fs$1.writeFile(
|
|
230
|
+
filePath,
|
|
231
|
+
`
|
|
232
|
+
import { BasedAtomicModel } from "./_general";
|
|
233
|
+
`
|
|
234
|
+
);
|
|
235
|
+
filePath = path.join(root, "src/_types/organisms.d.ts");
|
|
236
|
+
await fs$1.writeFile(
|
|
237
|
+
filePath,
|
|
238
|
+
`
|
|
239
|
+
import { BasedAtomicModel } from "./_general";
|
|
240
|
+
|
|
241
|
+
interface FooterModel extends BasedAtomicModel {}
|
|
242
|
+
|
|
243
|
+
interface HeaderModel extends BasedAtomicModel {}
|
|
244
|
+
`
|
|
245
|
+
);
|
|
246
|
+
filePath = path.join(root, "src/pages/Home.tsx");
|
|
247
|
+
await fs$1.writeFile(
|
|
248
|
+
filePath,
|
|
249
|
+
`
|
|
250
|
+
import Template from '@templates/home/Home';
|
|
251
|
+
|
|
252
|
+
const Home = () => {
|
|
253
|
+
return <Template footer={footer} header={header} />;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export default Home;
|
|
257
|
+
`
|
|
258
|
+
);
|
|
259
|
+
filePath = path.join(root, "src/templates/home/Home.tsx");
|
|
260
|
+
await fs$1.writeFile(
|
|
261
|
+
filePath,
|
|
262
|
+
`
|
|
263
|
+
import { FooterModel, HeaderModel } from '@_types/organisms';
|
|
264
|
+
import Footer from '@organisms/footer/Footer';
|
|
265
|
+
import Header from '@organisms/header/Header';
|
|
266
|
+
|
|
267
|
+
interface Props {
|
|
268
|
+
header?: HeaderModel;
|
|
269
|
+
footer?: FooterModel;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const Home = (model: Props) => {
|
|
273
|
+
const { header, footer } = model;
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<>
|
|
277
|
+
{header && <Header {...header} />}
|
|
278
|
+
|
|
279
|
+
<main>
|
|
280
|
+
// write components here
|
|
281
|
+
</main>
|
|
282
|
+
|
|
283
|
+
<Footer {...footer} />
|
|
284
|
+
</>
|
|
285
|
+
);
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export default Home;
|
|
289
|
+
`
|
|
290
|
+
);
|
|
291
|
+
filePath = path.join(root, "src/organisms/header/Header.tsx");
|
|
292
|
+
await fs$1.writeFile(
|
|
293
|
+
filePath,
|
|
294
|
+
`
|
|
295
|
+
import { getModifiers } from '@helpers/functions';
|
|
296
|
+
import RequireCss from '@helpers/RequireCss';
|
|
297
|
+
import { HeaderModel } from '@_types/organisms';
|
|
298
|
+
|
|
299
|
+
const Header = (model: HeaderModel) => {
|
|
300
|
+
const modifiers = getModifiers(model, 'zzz-o-header');
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
<header className={modifiers}>
|
|
304
|
+
<h2>Header</h2>
|
|
305
|
+
<RequireCss path="b-header" />
|
|
306
|
+
</header>
|
|
307
|
+
);
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export default Header;
|
|
311
|
+
`
|
|
312
|
+
);
|
|
313
|
+
filePath = path.join(root, "src/organisms/header/Header.scss");
|
|
314
|
+
await fs$1.writeFile(filePath, "");
|
|
315
|
+
filePath = path.join(root, "src/organisms/footer/Footer.tsx");
|
|
316
|
+
await fs$1.writeFile(
|
|
317
|
+
filePath,
|
|
318
|
+
`
|
|
319
|
+
import { getModifiers } from '@helpers/functions';
|
|
320
|
+
import RequireCss from '@helpers/RequireCss';
|
|
321
|
+
import { FooterModel } from '@_types/organisms';
|
|
322
|
+
|
|
323
|
+
const Footer = (model: FooterModel) => {
|
|
324
|
+
const modifiers = getModifiers(model, 'zzz-o-footer');
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<footer className={modifiers}>
|
|
328
|
+
<h2>Footer</h2>
|
|
329
|
+
<RequireCss path="b-footer" />
|
|
330
|
+
</footer>
|
|
331
|
+
);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
export default Footer;
|
|
335
|
+
`
|
|
336
|
+
);
|
|
337
|
+
filePath = path.join(root, "src/organisms/footer/Footer.scss");
|
|
338
|
+
await fs$1.writeFile(filePath, "");
|
|
339
|
+
return ["xxx"];
|
|
340
|
+
};
|
|
341
|
+
const filename = fileURLToPath(import.meta.url);
|
|
342
|
+
const dirname = path.dirname(filename);
|
|
343
|
+
const { cyan: cyan$1 } = chalk;
|
|
344
|
+
const installTemplate = async (model) => {
|
|
345
|
+
const { appName, root } = model;
|
|
346
|
+
console.log("\nInitializing project");
|
|
347
|
+
const copySource = ["**"];
|
|
348
|
+
await copy(copySource, root, {
|
|
349
|
+
cwd: path.join(dirname, "..")
|
|
350
|
+
});
|
|
351
|
+
await formatFiles(root);
|
|
352
|
+
const packageJson2 = JSON.parse(await fs$1.readFile("package.json", "utf8"));
|
|
353
|
+
packageJson2.name = appName;
|
|
354
|
+
packageJson2.description = "";
|
|
355
|
+
packageJson2.version = "0.1.0";
|
|
356
|
+
delete packageJson2.bin;
|
|
357
|
+
packageJson2.scripts && Object.keys(packageJson2.scripts).map((scriptName) => {
|
|
358
|
+
if (scriptName.startsWith("cli")) {
|
|
359
|
+
delete packageJson2.scripts[scriptName];
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
packageJson2.dependencies && Object.keys(packageJson2.dependencies).map((dependency) => {
|
|
363
|
+
if (!dependency.startsWith("react")) {
|
|
364
|
+
delete packageJson2.dependencies[dependency];
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
const devDeps = Object.keys(packageJson2.devDependencies).length;
|
|
368
|
+
if (!devDeps) delete packageJson2.devDependencies;
|
|
369
|
+
await fs$1.writeFile(path.join(root, "package.json"), JSON.stringify(packageJson2, null, 2) + os.EOL);
|
|
370
|
+
console.log("\nInstalling dependencies:");
|
|
371
|
+
for (const dependency in packageJson2.dependencies) {
|
|
372
|
+
console.log(`- ${cyan$1(dependency)}`);
|
|
373
|
+
}
|
|
374
|
+
if (devDeps) {
|
|
375
|
+
console.log("\nInstalling devDependencies:");
|
|
376
|
+
for (const dependency in packageJson2.devDependencies) console.log(`- ${cyan$1(dependency)}`);
|
|
377
|
+
}
|
|
378
|
+
await install();
|
|
379
|
+
};
|
|
380
|
+
const { green: green$1 } = chalk;
|
|
381
|
+
const createApp = async (model) => {
|
|
382
|
+
const { appPath } = model;
|
|
383
|
+
const root = path.resolve(appPath);
|
|
384
|
+
if (!await isWriteable(path.dirname(root))) {
|
|
385
|
+
console.error("The application path is not writable, please check folder permissions and try again.");
|
|
386
|
+
console.error("It is likely you do not have write permissions for this folder.");
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
const appName = path.basename(root);
|
|
390
|
+
await makeDir(root);
|
|
391
|
+
if (!isFolderEmpty(root)) {
|
|
392
|
+
console.log(`
|
|
393
|
+
The directory ${green$1(appName)} contains files that could conflict or not empty`);
|
|
394
|
+
console.log("\nEither try using a new directory name, or remove these files.");
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
console.log(`
|
|
398
|
+
Creating a new app in ${green$1(root)}.`);
|
|
399
|
+
process.chdir(root);
|
|
400
|
+
await installTemplate({
|
|
401
|
+
appName,
|
|
402
|
+
root
|
|
403
|
+
});
|
|
404
|
+
console.log("\nInitializing a git repository.");
|
|
405
|
+
if (tryGitInit(root)) {
|
|
406
|
+
console.log("\nInitialized a git repository.");
|
|
407
|
+
}
|
|
408
|
+
console.log(`
|
|
409
|
+
${green$1("Success!")} Created ${appName} at ${appPath}`);
|
|
410
|
+
};
|
|
411
|
+
function validateNpmName(name2) {
|
|
412
|
+
const nameValidation = validateProjectName(name2);
|
|
413
|
+
if (nameValidation.validForNewPackages && !name2.startsWith("-")) {
|
|
414
|
+
return { valid: true };
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
valid: false,
|
|
418
|
+
problems: [...nameValidation.errors || [], ...nameValidation.warnings || []]
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
const { green, yellow, bold, cyan, red } = chalk;
|
|
422
|
+
const packageName = "buner";
|
|
423
|
+
const run = (cmd, args = [], options = {}) => {
|
|
424
|
+
return new Promise((resolve, reject) => {
|
|
425
|
+
const child = spawn(cmd, args, {
|
|
426
|
+
stdio: "inherit",
|
|
427
|
+
shell: true,
|
|
428
|
+
cwd: process.cwd(),
|
|
429
|
+
...options
|
|
430
|
+
});
|
|
431
|
+
child.on("close", (code) => {
|
|
432
|
+
if (code !== 0) {
|
|
433
|
+
reject(new Error(`Command "${cmd} ${args.join(" ")}" exited with code ${code}`));
|
|
434
|
+
} else {
|
|
435
|
+
resolve();
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
child.on("error", reject);
|
|
439
|
+
});
|
|
440
|
+
};
|
|
441
|
+
const runSync = (cmd) => {
|
|
442
|
+
execSync(cmd, { stdio: "inherit", cwd: process.cwd() });
|
|
443
|
+
};
|
|
444
|
+
const onPromptState = (state) => {
|
|
445
|
+
if (state?.aborted) {
|
|
446
|
+
process.stdout.write("\x1B[?25h");
|
|
447
|
+
process.stdout.write("\n");
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const parseVersion = (version2) => {
|
|
452
|
+
return parseInt(version2.replaceAll(".", ""));
|
|
453
|
+
};
|
|
454
|
+
const update = fetch(`https://registry.npmjs.org/${packageJson.name}/latest`).then((res) => res.json()).catch(() => null);
|
|
455
|
+
async function notifyUpdate() {
|
|
456
|
+
try {
|
|
457
|
+
const data = await update;
|
|
458
|
+
if (data.version && parseVersion(data.version) !== parseVersion(packageJson.version)) {
|
|
459
|
+
const updateMessage = `npm update -g ${packageName}`;
|
|
460
|
+
console.log(
|
|
461
|
+
yellow(bold(`A new version of '${packageName}' is available!`)) + "\nYou can update by running: " + cyan(updateMessage) + "\n"
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
} catch {
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const program = new Command();
|
|
468
|
+
program.name(packageName).description("Frontend build toolkit for Vite + React SSR projects").version(packageJson.version);
|
|
469
|
+
program.command("create").argument("[project-directory]", "the project name", "").description("Scaffold a new frontend project").action(async (projectPath) => {
|
|
470
|
+
if (!projectPath) {
|
|
471
|
+
validateNpmName("my-app");
|
|
472
|
+
const res = await prompts({
|
|
473
|
+
onState: onPromptState,
|
|
474
|
+
type: "text",
|
|
475
|
+
name: "path",
|
|
476
|
+
message: "What is your project named?",
|
|
477
|
+
initial: "my-app",
|
|
478
|
+
validate: (name2) => {
|
|
479
|
+
const validation2 = validateNpmName(path.basename(path.resolve(name2)));
|
|
480
|
+
if (validation2.valid) {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
return `Invalid project name ${validation2?.problems?.[0] ? validation2?.problems?.[0] : ""}`;
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
if (typeof res.path === "string") {
|
|
487
|
+
projectPath = res.path.trim();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (!projectPath) {
|
|
491
|
+
console.log(
|
|
492
|
+
`
|
|
493
|
+
Please specify the project directory:
|
|
494
|
+
${cyan("buner create")} ${green("<project-directory>")}
|
|
495
|
+
For example:
|
|
496
|
+
${cyan("buner create")} ${green("my-app")}
|
|
497
|
+
`
|
|
498
|
+
);
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
const resolvedProjectPath = path.resolve(projectPath);
|
|
502
|
+
await createApp({ appPath: resolvedProjectPath });
|
|
503
|
+
await notifyUpdate();
|
|
504
|
+
});
|
|
505
|
+
program.command("dev").description("Start development mode with all watchers").action(async () => {
|
|
506
|
+
await run("npx", [
|
|
507
|
+
"concurrently",
|
|
508
|
+
"--kill-others",
|
|
509
|
+
'"bun styles.ts --watch"',
|
|
510
|
+
'"bun states.ts --watch"',
|
|
511
|
+
'"cross-env scriptOnly=true npx vite build --mode development --watch"',
|
|
512
|
+
'"bun server.ts --mode development"'
|
|
513
|
+
]);
|
|
514
|
+
});
|
|
515
|
+
program.command("serve").description("Start the SSR dev server").option("--mode <mode>", "server mode", "development").action(async (opts) => {
|
|
516
|
+
await run("bun", ["server.ts", "--mode", opts.mode]);
|
|
517
|
+
});
|
|
518
|
+
program.command("build").description("Build the project (static + SSR)").action(async () => {
|
|
519
|
+
runSync("npx vite build --outDir dist/static");
|
|
520
|
+
runSync("npx vite build --ssr src/entry-server.tsx --outDir dist/server");
|
|
521
|
+
});
|
|
522
|
+
program.command("generate").description("Full static site generation (states + styles + build + prerender)").option("--mode <mode>", "build mode", "production").action(async (opts) => {
|
|
523
|
+
runSync("bun states.ts");
|
|
524
|
+
runSync("bun styles.ts");
|
|
525
|
+
if (opts.mode === "production") {
|
|
526
|
+
runSync("npx vite build --outDir dist/static");
|
|
527
|
+
runSync("npx vite build --ssr src/entry-server.tsx --outDir dist/server");
|
|
528
|
+
} else {
|
|
529
|
+
runSync(`npx vite build --outDir dist/static --mode ${opts.mode}`);
|
|
530
|
+
runSync(`npx vite build --ssr src/entry-server.tsx --outDir dist/server --mode ${opts.mode}`);
|
|
531
|
+
}
|
|
532
|
+
runSync(`bun prerender.ts --add-hash --mode ${opts.mode}`);
|
|
533
|
+
});
|
|
534
|
+
program.command("eshn").description("Generate with --mode eshn").action(async () => {
|
|
535
|
+
runSync("bun states.ts");
|
|
536
|
+
runSync("bun styles.ts");
|
|
537
|
+
runSync("npx vite build --outDir dist/static --mode eshn");
|
|
538
|
+
runSync("npx vite build --ssr src/entry-server.tsx --outDir dist/server --mode eshn");
|
|
539
|
+
runSync("bun prerender.ts --add-hash --mode eshn");
|
|
540
|
+
});
|
|
541
|
+
program.command("inte").description("Build and integrate with backend (styles + build + prerender + integration)").action(async () => {
|
|
542
|
+
runSync("bun styles.ts");
|
|
543
|
+
runSync("npx vite build --outDir dist/static");
|
|
544
|
+
runSync("npx vite build --ssr src/entry-server.tsx --outDir dist/server");
|
|
545
|
+
runSync("bun prerender.ts");
|
|
546
|
+
runSync("bun integration.ts");
|
|
547
|
+
});
|
|
548
|
+
program.command("styles").description("Compile SCSS").option("--watch", "Watch for changes").action(async (opts) => {
|
|
549
|
+
const args = ["styles.ts"];
|
|
550
|
+
if (opts.watch) args.push("--watch");
|
|
551
|
+
await run("bun", args);
|
|
552
|
+
});
|
|
553
|
+
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) => {
|
|
554
|
+
const args = ["prerender.ts"];
|
|
555
|
+
if (opts.addHash) args.push("--add-hash");
|
|
556
|
+
args.push("--mode", opts.mode);
|
|
557
|
+
await run("bun", args);
|
|
558
|
+
});
|
|
559
|
+
program.parseAsync(process.argv).catch(async (error) => {
|
|
560
|
+
console.log(red(error));
|
|
561
|
+
await notifyUpdate();
|
|
562
|
+
process.exit(1);
|
|
563
|
+
});
|
|
564
|
+
export {
|
|
565
|
+
packageName
|
|
566
|
+
};
|
package/cli/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[](https://episerver-es-emea.visualstudio.com/Alloy-Template/_build/latest?definitionId=28&branchName=fe-release)
|