tempest-react-sdk 0.5.1 → 0.8.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/README.md +206 -35
- package/bin/create-tempest-app.mjs +248 -0
- package/bin/tempest.mjs +282 -0
- package/dist/styles.css +1 -1
- package/dist/sw.cjs +2 -0
- package/dist/sw.cjs.map +1 -0
- package/dist/sw.d.ts +103 -0
- package/dist/sw.js +95 -0
- package/dist/sw.js.map +1 -0
- package/dist/tempest-react-sdk.cjs +4 -4
- package/dist/tempest-react-sdk.cjs.map +1 -1
- package/dist/tempest-react-sdk.d.ts +1697 -7
- package/dist/tempest-react-sdk.js +4485 -2989
- package/dist/tempest-react-sdk.js.map +1 -1
- package/dist/vite.cjs +2 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.ts +62 -0
- package/dist/vite.js +46 -0
- package/dist/vite.js.map +1 -0
- package/package.json +34 -2
- package/template/README.md +37 -0
- package/template/_env.example +2 -0
- package/template/_gitignore +9 -0
- package/template/_prettierrc.json +9 -0
- package/template/eslint.config.js +44 -0
- package/template/index.html +12 -0
- package/template/package.json +38 -0
- package/template/src/App.tsx +15 -0
- package/template/src/layouts/RootLayout.tsx +28 -0
- package/template/src/lib/api.ts +17 -0
- package/template/src/main.tsx +10 -0
- package/template/src/pages/Dashboard.tsx +19 -0
- package/template/src/pages/Home.tsx +16 -0
- package/template/src/pages/Login.tsx +27 -0
- package/template/src/routes.tsx +27 -0
- package/template/src/stores/auth.ts +15 -0
- package/template/src/vite-env.d.ts +1 -0
- package/template/tsconfig.json +26 -0
- package/template/vite.config.ts +7 -0
- package/template-pwa/README.md +64 -0
- package/template-pwa/_env.example +7 -0
- package/template-pwa/index.html +22 -0
- package/template-pwa/package.json +6 -0
- package/template-pwa/public/icon-maskable.svg +4 -0
- package/template-pwa/public/icon.svg +4 -0
- package/template-pwa/public/manifest.webmanifest +35 -0
- package/template-pwa/src/main.tsx +36 -0
- package/template-pwa/src/pages/Dashboard.tsx +73 -0
- package/template-pwa/src/sw.ts +35 -0
- package/template-pwa/src/vite-env.d.ts +12 -0
- package/template-pwa/vite.sw.config.ts +27 -0
package/bin/tempest.mjs
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// tempest — project CLI shipped inside tempest-react-sdk.
|
|
3
|
+
//
|
|
4
|
+
// tempest doctor health-check the current project (à la flutter doctor)
|
|
5
|
+
// tempest lint [paths…] run ESLint (report only)
|
|
6
|
+
// tempest fix [paths…] ESLint --fix (sort imports, drop unused, tidy whitespace) + Prettier
|
|
7
|
+
// tempest format [paths…] Prettier --write
|
|
8
|
+
// tempest --help | --version
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const ROOT = process.cwd();
|
|
15
|
+
const SELF_DIR = resolve(fileURLToPath(import.meta.url), "..");
|
|
16
|
+
|
|
17
|
+
const c = {
|
|
18
|
+
reset: "\x1b[0m",
|
|
19
|
+
bold: "\x1b[1m",
|
|
20
|
+
dim: "\x1b[2m",
|
|
21
|
+
green: "\x1b[32m",
|
|
22
|
+
yellow: "\x1b[33m",
|
|
23
|
+
red: "\x1b[31m",
|
|
24
|
+
cyan: "\x1b[36m",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function selfVersion() {
|
|
28
|
+
try {
|
|
29
|
+
return (
|
|
30
|
+
JSON.parse(readFileSync(join(SELF_DIR, "..", "package.json"), "utf8")).version ?? "?"
|
|
31
|
+
);
|
|
32
|
+
} catch {
|
|
33
|
+
return "?";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Resolve a project-local CLI binary (e.g. eslint, prettier). */
|
|
38
|
+
function localBin(name) {
|
|
39
|
+
const p = join(ROOT, "node_modules", ".bin", name);
|
|
40
|
+
return existsSync(p) ? p : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function run(bin, args) {
|
|
44
|
+
const res = spawnSync(bin, args, { stdio: "inherit", cwd: ROOT });
|
|
45
|
+
return res.status ?? 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readJSON(path) {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ---------------------------------------------------------------- doctor ----
|
|
57
|
+
|
|
58
|
+
function fmt(status, label, detail) {
|
|
59
|
+
const mark =
|
|
60
|
+
status === "ok"
|
|
61
|
+
? `${c.green}✓${c.reset}`
|
|
62
|
+
: status === "warn"
|
|
63
|
+
? `${c.yellow}!${c.reset}`
|
|
64
|
+
: `${c.red}✗${c.reset}`;
|
|
65
|
+
const tail = detail ? ` ${c.dim}— ${detail}${c.reset}` : "";
|
|
66
|
+
return ` [${mark}] ${label}${tail}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function fileIncludes(path, needle) {
|
|
70
|
+
try {
|
|
71
|
+
return readFileSync(path, "utf8").includes(needle);
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function firstExisting(paths) {
|
|
78
|
+
return paths.find((p) => existsSync(join(ROOT, p))) ?? null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function doctor() {
|
|
82
|
+
const checks = [];
|
|
83
|
+
const pkg = readJSON(join(ROOT, "package.json"));
|
|
84
|
+
|
|
85
|
+
// Node
|
|
86
|
+
const [maj, min] = process.versions.node.split(".").map(Number);
|
|
87
|
+
const nodeOk = maj > 20 || (maj === 20 && min >= 19);
|
|
88
|
+
checks.push([
|
|
89
|
+
nodeOk ? "ok" : "fail",
|
|
90
|
+
`Node ${process.versions.node}`,
|
|
91
|
+
nodeOk ? "" : "requires >= 20.19",
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
// package.json
|
|
95
|
+
if (!pkg) {
|
|
96
|
+
checks.push(["fail", "package.json", "not found — run inside your project root"]);
|
|
97
|
+
return report(checks);
|
|
98
|
+
}
|
|
99
|
+
checks.push(["ok", "package.json found"]);
|
|
100
|
+
|
|
101
|
+
// SDK dependency + installed
|
|
102
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
103
|
+
checks.push(
|
|
104
|
+
deps["tempest-react-sdk"]
|
|
105
|
+
? ["ok", "tempest-react-sdk in dependencies", deps["tempest-react-sdk"]]
|
|
106
|
+
: [
|
|
107
|
+
"fail",
|
|
108
|
+
"tempest-react-sdk in dependencies",
|
|
109
|
+
"add it: npm install tempest-react-sdk",
|
|
110
|
+
],
|
|
111
|
+
);
|
|
112
|
+
checks.push(
|
|
113
|
+
existsSync(join(ROOT, "node_modules", "tempest-react-sdk"))
|
|
114
|
+
? ["ok", "tempest-react-sdk installed"]
|
|
115
|
+
: ["fail", "tempest-react-sdk installed", "run npm install"],
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// React peers
|
|
119
|
+
const hasReact = deps.react && deps["react-dom"];
|
|
120
|
+
checks.push(
|
|
121
|
+
hasReact
|
|
122
|
+
? ["ok", "react + react-dom present"]
|
|
123
|
+
: ["fail", "react + react-dom present", "install react react-dom"],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Vite config + createViteConfig
|
|
127
|
+
const viteCfg = firstExisting(["vite.config.ts", "vite.config.js", "vite.config.mjs"]);
|
|
128
|
+
if (!viteCfg) {
|
|
129
|
+
checks.push(["warn", "vite config", "no vite.config.* found"]);
|
|
130
|
+
} else {
|
|
131
|
+
checks.push(
|
|
132
|
+
fileIncludes(join(ROOT, viteCfg), "createViteConfig")
|
|
133
|
+
? ["ok", `${viteCfg} uses createViteConfig`]
|
|
134
|
+
: ["warn", `${viteCfg}`, "not using createViteConfig from tempest-react-sdk/vite"],
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// tsconfig @ alias
|
|
139
|
+
const tsc = readJSON(join(ROOT, "tsconfig.json"));
|
|
140
|
+
const paths = tsc?.compilerOptions?.paths ?? {};
|
|
141
|
+
checks.push(
|
|
142
|
+
paths["@/*"]
|
|
143
|
+
? ["ok", 'tsconfig "@/*" alias']
|
|
144
|
+
: ["warn", 'tsconfig "@/*" alias', 'add "paths": { "@/*": ["./src/*"] }'],
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// styles.css imported at entry
|
|
148
|
+
const entry = firstExisting(["src/main.tsx", "src/main.ts", "src/index.tsx", "src/index.ts"]);
|
|
149
|
+
if (entry) {
|
|
150
|
+
checks.push(
|
|
151
|
+
fileIncludes(join(ROOT, entry), "tempest-react-sdk/styles.css")
|
|
152
|
+
? ["ok", `${entry} imports styles.css`]
|
|
153
|
+
: ["warn", `${entry}`, 'add import "tempest-react-sdk/styles.css"'],
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
checks.push(["warn", "app entry", "no src/main.tsx found"]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// tooling
|
|
160
|
+
checks.push(
|
|
161
|
+
firstExisting(["eslint.config.js", "eslint.config.mjs", ".eslintrc.cjs", ".eslintrc.json"])
|
|
162
|
+
? ["ok", "ESLint config present"]
|
|
163
|
+
: ["warn", "ESLint config", "no eslint config — `tempest fix` needs it"],
|
|
164
|
+
);
|
|
165
|
+
checks.push(
|
|
166
|
+
localBin("eslint")
|
|
167
|
+
? ["ok", "eslint installed"]
|
|
168
|
+
: ["warn", "eslint installed", "npm i -D eslint"],
|
|
169
|
+
);
|
|
170
|
+
checks.push(
|
|
171
|
+
localBin("prettier")
|
|
172
|
+
? ["ok", "prettier installed"]
|
|
173
|
+
: ["warn", "prettier installed", "npm i -D prettier"],
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// .env
|
|
177
|
+
if (existsSync(join(ROOT, ".env"))) checks.push(["ok", ".env present"]);
|
|
178
|
+
else if (existsSync(join(ROOT, ".env.example")))
|
|
179
|
+
checks.push(["warn", ".env", "only .env.example — copy it: cp .env.example .env"]);
|
|
180
|
+
|
|
181
|
+
return report(checks);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function report(checks) {
|
|
185
|
+
console.log(`\n${c.bold}${c.cyan}tempest doctor${c.reset} ${c.dim}(${ROOT})${c.reset}\n`);
|
|
186
|
+
for (const [status, label, detail] of checks) console.log(fmt(status, label, detail));
|
|
187
|
+
const fails = checks.filter((x) => x[0] === "fail").length;
|
|
188
|
+
const warns = checks.filter((x) => x[0] === "warn").length;
|
|
189
|
+
console.log("");
|
|
190
|
+
if (fails)
|
|
191
|
+
console.log(
|
|
192
|
+
`${c.red}✗ ${fails} problem(s)${c.reset}${warns ? `, ${c.yellow}${warns} warning(s)${c.reset}` : ""}.`,
|
|
193
|
+
);
|
|
194
|
+
else if (warns)
|
|
195
|
+
console.log(`${c.yellow}! ${warns} warning(s)${c.reset} — usable, but worth fixing.`);
|
|
196
|
+
else console.log(`${c.green}✓ No issues found.${c.reset}`);
|
|
197
|
+
console.log("");
|
|
198
|
+
return fails ? 1 : 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ------------------------------------------------------- lint / fix / fmt ----
|
|
202
|
+
|
|
203
|
+
function requireBin(name) {
|
|
204
|
+
const bin = localBin(name);
|
|
205
|
+
if (!bin) {
|
|
206
|
+
console.error(
|
|
207
|
+
`${c.red}✗ ${name} not found in node_modules.${c.reset} Install it: ${c.bold}npm i -D ${name}${c.reset}`,
|
|
208
|
+
);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
return bin;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function lint(paths) {
|
|
215
|
+
return run(requireBin("eslint"), paths.length ? paths : ["."]);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function fix(paths) {
|
|
219
|
+
const targets = paths.length ? paths : ["."];
|
|
220
|
+
console.log(`${c.dim}→ eslint --fix (sort imports · drop unused · tidy whitespace)${c.reset}`);
|
|
221
|
+
const eslintStatus = run(requireBin("eslint"), [...targets, "--fix"]);
|
|
222
|
+
const prettier = localBin("prettier");
|
|
223
|
+
let prettierStatus = 0;
|
|
224
|
+
if (prettier) {
|
|
225
|
+
console.log(`${c.dim}→ prettier --write${c.reset}`);
|
|
226
|
+
prettierStatus = run(prettier, ["--write", ...targets]);
|
|
227
|
+
} else {
|
|
228
|
+
console.log(`${c.yellow}! prettier not installed — skipping format pass${c.reset}`);
|
|
229
|
+
}
|
|
230
|
+
return eslintStatus || prettierStatus;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function format(paths) {
|
|
234
|
+
return run(requireBin("prettier"), ["--write", ...(paths.length ? paths : ["."])]);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ------------------------------------------------------------------ main ----
|
|
238
|
+
|
|
239
|
+
function usage() {
|
|
240
|
+
console.log(`
|
|
241
|
+
${c.bold}${c.cyan}tempest${c.reset} ${c.dim}v${selfVersion()}${c.reset} — project CLI for tempest-react-sdk apps
|
|
242
|
+
|
|
243
|
+
${c.bold}Usage${c.reset}
|
|
244
|
+
tempest <command> [paths…]
|
|
245
|
+
|
|
246
|
+
${c.bold}Commands${c.reset}
|
|
247
|
+
${c.bold}doctor${c.reset} Health-check the current project
|
|
248
|
+
${c.bold}lint${c.reset} [paths] Run ESLint (report only)
|
|
249
|
+
${c.bold}fix${c.reset} [paths] ESLint --fix (sort imports, remove unused, tidy whitespace) + Prettier
|
|
250
|
+
${c.bold}format${c.reset} [paths] Prettier --write
|
|
251
|
+
|
|
252
|
+
${c.bold}Options${c.reset}
|
|
253
|
+
-h, --help Show this help
|
|
254
|
+
-v, --version Show version
|
|
255
|
+
`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
259
|
+
|
|
260
|
+
if (cmd === "-v" || cmd === "--version") {
|
|
261
|
+
console.log(selfVersion());
|
|
262
|
+
process.exit(0);
|
|
263
|
+
}
|
|
264
|
+
if (!cmd || cmd === "-h" || cmd === "--help" || cmd === "help") {
|
|
265
|
+
usage();
|
|
266
|
+
process.exit(0);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const commands = {
|
|
270
|
+
doctor: () => doctor(),
|
|
271
|
+
lint: () => lint(rest),
|
|
272
|
+
fix: () => fix(rest),
|
|
273
|
+
format: () => format(rest),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (!commands[cmd]) {
|
|
277
|
+
console.error(`${c.red}✗ Unknown command: ${cmd}${c.reset}`);
|
|
278
|
+
usage();
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
process.exit(commands[cmd]());
|