stackkit 0.2.0 → 0.2.2
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 +4 -7
- package/dist/cli/add.js +84 -120
- package/dist/cli/create.js +28 -15
- package/dist/cli/doctor.js +203 -21
- package/dist/cli/list.js +37 -1
- package/dist/index.js +104 -6
- package/dist/lib/discovery/module-discovery.d.ts +0 -7
- package/dist/lib/discovery/module-discovery.js +26 -23
- package/dist/lib/discovery/shared.d.ts +6 -0
- package/dist/lib/discovery/shared.js +50 -0
- package/dist/lib/framework/framework-utils.js +43 -3
- package/dist/lib/generation/code-generator.js +18 -0
- package/dist/lib/pm/package-manager.js +58 -31
- package/modules/auth/authjs/generator.json +1 -1
- package/modules/auth/better-auth/files/lib/auth.ts +5 -1
- package/package.json +1 -1
- package/templates/express/README.md +4 -9
- package/templates/express/template.json +9 -1
- package/templates/nextjs/.env.example +1 -0
- package/templates/nextjs/README.md +2 -5
- package/templates/nextjs/template.json +2 -0
- package/templates/react/README.md +2 -5
- package/templates/react/src/lib/queryClient.ts +2 -2
- package/templates/react/src/utils/utils.ts +3 -0
- package/templates/react/template.json +2 -0
- package/templates/react/src/config/constants.ts +0 -5
- package/templates/react/src/hooks/index.ts +0 -64
- package/templates/react/src/utils/helpers.ts +0 -51
|
@@ -45,15 +45,55 @@ class FrameworkUtils {
|
|
|
45
45
|
this.frameworkConfigs.set(frameworkName, config);
|
|
46
46
|
return config;
|
|
47
47
|
}
|
|
48
|
-
//
|
|
48
|
+
// Derive compatibility dynamically from available modules if possible
|
|
49
49
|
const defaultConfig = {
|
|
50
50
|
name: frameworkName,
|
|
51
51
|
displayName: frameworkName.charAt(0).toUpperCase() + frameworkName.slice(1),
|
|
52
52
|
compatibility: {
|
|
53
|
-
databases: [
|
|
54
|
-
auth: [
|
|
53
|
+
databases: [],
|
|
54
|
+
auth: [],
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
|
+
try {
|
|
58
|
+
const modulesDir = path.join(templatesDir, "..", "modules");
|
|
59
|
+
const dbDir = path.join(modulesDir, "database");
|
|
60
|
+
const authDir = path.join(modulesDir, "auth");
|
|
61
|
+
if (await fs.pathExists(dbDir)) {
|
|
62
|
+
const dbs = await fs.readdir(dbDir);
|
|
63
|
+
for (const d of dbs) {
|
|
64
|
+
const moduleJson = path.join(dbDir, d, "module.json");
|
|
65
|
+
if (await fs.pathExists(moduleJson)) {
|
|
66
|
+
try {
|
|
67
|
+
const m = await fs.readJson(moduleJson);
|
|
68
|
+
if (m && m.name)
|
|
69
|
+
defaultConfig.compatibility.databases.push(m.name);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// ignore malformed
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (await fs.pathExists(authDir)) {
|
|
78
|
+
const auths = await fs.readdir(authDir);
|
|
79
|
+
for (const a of auths) {
|
|
80
|
+
const moduleJson = path.join(authDir, a, "module.json");
|
|
81
|
+
if (await fs.pathExists(moduleJson)) {
|
|
82
|
+
try {
|
|
83
|
+
const m = await fs.readJson(moduleJson);
|
|
84
|
+
if (m && m.name)
|
|
85
|
+
defaultConfig.compatibility.auth.push(m.name);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// ignore malformed
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// ignore discovery errors and leave empty lists
|
|
96
|
+
}
|
|
57
97
|
this.frameworkConfigs.set(frameworkName, defaultConfig);
|
|
58
98
|
return defaultConfig;
|
|
59
99
|
}
|
|
@@ -390,6 +390,24 @@ class AdvancedCodeGenerator {
|
|
|
390
390
|
catch {
|
|
391
391
|
// ignore failures here — not critical
|
|
392
392
|
}
|
|
393
|
+
// Ensure gitignore is present in target even if template authors
|
|
394
|
+
// renamed it to avoid npm/package issues (e.g. 'gitignore' or '_gitignore')
|
|
395
|
+
try {
|
|
396
|
+
const gitCandidates = [".gitignore", "gitignore", "_gitignore"];
|
|
397
|
+
for (const g of gitCandidates) {
|
|
398
|
+
const src = path.join(templatePath, g);
|
|
399
|
+
if (await fs.pathExists(src)) {
|
|
400
|
+
const dest = path.join(outputPath, ".gitignore");
|
|
401
|
+
if (!(await fs.pathExists(dest))) {
|
|
402
|
+
await fs.copy(src, dest);
|
|
403
|
+
}
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
// ignore
|
|
410
|
+
}
|
|
393
411
|
}
|
|
394
412
|
}
|
|
395
413
|
processOperationTemplates(operation, context) {
|
|
@@ -20,41 +20,32 @@ async function detectPackageManager(cwd) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
async function installDependencies(cwd, pm) {
|
|
23
|
-
const args = [];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
else if (pm === "yarn") {
|
|
28
|
-
args.push("install");
|
|
29
|
-
}
|
|
30
|
-
else if (pm === "pnpm") {
|
|
31
|
-
args.push("install");
|
|
32
|
-
}
|
|
33
|
-
else if (pm === "bun") {
|
|
34
|
-
args.push("install");
|
|
35
|
-
}
|
|
36
|
-
await (0, execa_1.default)(pm, args, { cwd, stdio: "pipe" });
|
|
23
|
+
const args = ["install"];
|
|
24
|
+
const stdio = "pipe";
|
|
25
|
+
await (0, execa_1.default)(pm, args, { cwd, stdio });
|
|
37
26
|
}
|
|
38
27
|
async function addDependencies(cwd, pm, packages, dev = false) {
|
|
39
28
|
if (packages.length === 0)
|
|
40
29
|
return;
|
|
41
30
|
const spinner = logger_1.logger.startSpinner(`Adding ${dev ? "dev " : ""}dependencies: ${packages.join(", ")}...`);
|
|
42
31
|
try {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
const stdio = "pipe";
|
|
33
|
+
let args = [];
|
|
34
|
+
switch (pm) {
|
|
35
|
+
case "npm":
|
|
36
|
+
args = ["install", ...(dev ? ["--save-dev"] : []), ...packages];
|
|
37
|
+
break;
|
|
38
|
+
case "yarn":
|
|
39
|
+
args = ["add", ...(dev ? ["--dev"] : []), ...packages];
|
|
40
|
+
break;
|
|
41
|
+
case "pnpm":
|
|
42
|
+
args = ["add", ...(dev ? ["-D"] : []), ...packages];
|
|
43
|
+
break;
|
|
44
|
+
case "bun":
|
|
45
|
+
args = ["add", ...(dev ? ["-d"] : []), ...packages];
|
|
46
|
+
break;
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
args.push("add", dev ? "--dev" : "", ...packages);
|
|
49
|
-
}
|
|
50
|
-
else if (pm === "pnpm") {
|
|
51
|
-
args.push("add", dev ? "-D" : "", ...packages);
|
|
52
|
-
}
|
|
53
|
-
else if (pm === "bun") {
|
|
54
|
-
// bun uses `bun add` and `-d` for dev dependencies
|
|
55
|
-
args.push("add", ...(dev ? ["-d"] : []), ...packages);
|
|
56
|
-
}
|
|
57
|
-
await (0, execa_1.default)(pm, args.filter(Boolean), { cwd, stdio: "pipe" });
|
|
48
|
+
await (0, execa_1.default)(pm, args, { cwd, stdio });
|
|
58
49
|
spinner.succeed(`Dependencies added successfully`);
|
|
59
50
|
}
|
|
60
51
|
catch (error) {
|
|
@@ -63,7 +54,43 @@ async function addDependencies(cwd, pm, packages, dev = false) {
|
|
|
63
54
|
}
|
|
64
55
|
}
|
|
65
56
|
async function initGit(cwd) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
const spinner = logger_1.logger.startSpinner("Initializing git repository...");
|
|
58
|
+
const run = async (stdio) => {
|
|
59
|
+
await (0, execa_1.default)("git", ["init"], { cwd, stdio });
|
|
60
|
+
await (0, execa_1.default)("git", ["add", "."], { cwd, stdio });
|
|
61
|
+
await (0, execa_1.default)("git", ["commit", "-m", "Initial commit from StackKit"], { cwd, stdio });
|
|
62
|
+
};
|
|
63
|
+
try {
|
|
64
|
+
await run("pipe");
|
|
65
|
+
spinner.succeed("Git repository initialized");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const err = error;
|
|
70
|
+
spinner.fail(`Git init failed: ${err.message}`);
|
|
71
|
+
const isENOBUFS = (e) => {
|
|
72
|
+
if (!e || typeof e !== "object")
|
|
73
|
+
return false;
|
|
74
|
+
const obj = e;
|
|
75
|
+
return (obj.code === "ENOBUFS" ||
|
|
76
|
+
obj.errno === "ENOBUFS" ||
|
|
77
|
+
String(obj.message ?? "").includes("ENOBUFS"));
|
|
78
|
+
};
|
|
79
|
+
if (isENOBUFS(err)) {
|
|
80
|
+
logger_1.logger.warn("ENOBUFS detected; skipping git initialization.");
|
|
81
|
+
logger_1.logger.info("Skipped git initialization due to system resource limits.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
await run("inherit");
|
|
86
|
+
spinner.succeed("Git repository initialized (fallback)");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
catch (fallbackErr) {
|
|
90
|
+
const fe = fallbackErr;
|
|
91
|
+
logger_1.logger.warn(`Git init fallback failed: ${fe.message}`);
|
|
92
|
+
spinner.fail("Git initialization skipped");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
69
96
|
}
|
|
@@ -30,7 +30,9 @@ return betterAuth({
|
|
|
30
30
|
database: mongodbAdapter(db, { client }),
|
|
31
31
|
{{/case}}
|
|
32
32
|
{{/switch}}
|
|
33
|
-
|
|
33
|
+
baseURL: process.env.BETTER_AUTH_URL,
|
|
34
|
+
secret: process.env.BETTER_AUTH_SECRET,
|
|
35
|
+
trustedOrigins: [process.env.APP_URL],
|
|
34
36
|
user: {
|
|
35
37
|
additionalFields: {
|
|
36
38
|
role: {
|
|
@@ -84,9 +86,11 @@ return betterAuth({
|
|
|
84
86
|
session: {
|
|
85
87
|
cookieCache: {
|
|
86
88
|
enabled: true,
|
|
89
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
87
90
|
},
|
|
88
91
|
expiresIn: 60 * 60 * 24 * 7,
|
|
89
92
|
updateAge: 60 * 60 * 24,
|
|
93
|
+
cookieName: "better-auth.session_token",
|
|
90
94
|
}
|
|
91
95
|
})
|
|
92
96
|
};
|
package/package.json
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Production-ready Express (TypeScript) starter for building REST APIs and backend services.
|
|
4
4
|
|
|
5
|
-
Requirements
|
|
6
|
-
------------
|
|
5
|
+
## Requirements
|
|
7
6
|
|
|
8
7
|
- Node.js 18+ (LTS recommended)
|
|
9
8
|
- pnpm or npm
|
|
10
9
|
|
|
11
|
-
Quick Start
|
|
12
|
-
-----------
|
|
10
|
+
## Quick Start
|
|
13
11
|
|
|
14
12
|
Install dependencies and start the development server:
|
|
15
13
|
|
|
@@ -23,15 +21,13 @@ npm install
|
|
|
23
21
|
npm run dev
|
|
24
22
|
```
|
|
25
23
|
|
|
26
|
-
Scripts
|
|
27
|
-
-------
|
|
24
|
+
## Scripts
|
|
28
25
|
|
|
29
26
|
- `npm run dev` - Start development server
|
|
30
27
|
- `npm run build` - Build TypeScript
|
|
31
28
|
- `npm start` - Start production server
|
|
32
29
|
|
|
33
|
-
Environment
|
|
34
|
-
-----------
|
|
30
|
+
## Environment
|
|
35
31
|
|
|
36
32
|
Use a `.env` file or environment variables for configuration. See `.env.example` for available keys.
|
|
37
33
|
|
|
@@ -45,4 +41,3 @@ This project was scaffolded using **StackKit** — a CLI toolkit for building pr
|
|
|
45
41
|
|
|
46
42
|
Learn more about StackKit:
|
|
47
43
|
https://github.com/tariqul420/stackkit
|
|
48
|
-
|
|
@@ -7,7 +7,15 @@
|
|
|
7
7
|
"databases": ["prisma", "mongoose"],
|
|
8
8
|
"auth": ["better-auth"]
|
|
9
9
|
},
|
|
10
|
-
"files": [
|
|
10
|
+
"files": [
|
|
11
|
+
"src/",
|
|
12
|
+
".env.example",
|
|
13
|
+
".gitignore",
|
|
14
|
+
"eslint.config.cjs",
|
|
15
|
+
"package.json",
|
|
16
|
+
"README.md",
|
|
17
|
+
"tsconfig.json"
|
|
18
|
+
],
|
|
11
19
|
"scripts": {
|
|
12
20
|
"dev": "tsx watch src/server.ts",
|
|
13
21
|
"build": "tsc",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Production-ready Next.js starter with TypeScript and App Router.
|
|
4
4
|
|
|
5
|
-
Requirements
|
|
6
|
-
------------
|
|
5
|
+
## Requirements
|
|
7
6
|
|
|
8
7
|
- Node.js 18+ (LTS recommended)
|
|
9
8
|
- pnpm or npm
|
|
10
9
|
|
|
11
|
-
Quick Start
|
|
12
|
-
-----------
|
|
10
|
+
## Quick Start
|
|
13
11
|
|
|
14
12
|
Install dependencies and start a development server:
|
|
15
13
|
|
|
@@ -75,4 +73,3 @@ This project was scaffolded using **StackKit** — a CLI toolkit for building pr
|
|
|
75
73
|
|
|
76
74
|
Learn more about StackKit:
|
|
77
75
|
https://github.com/tariqul420/stackkit
|
|
78
|
-
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Production-ready React starter with TypeScript, Vite, and essential libraries.
|
|
4
4
|
|
|
5
|
-
Requirements
|
|
6
|
-
------------
|
|
5
|
+
## Requirements
|
|
7
6
|
|
|
8
7
|
- Node.js 18+ (LTS recommended)
|
|
9
8
|
- pnpm (recommended) or npm
|
|
10
9
|
|
|
11
|
-
Quick Start
|
|
12
|
-
-----------
|
|
10
|
+
## Quick Start
|
|
13
11
|
|
|
14
12
|
Install dependencies and run the dev server:
|
|
15
13
|
|
|
@@ -89,4 +87,3 @@ This project was scaffolded using **StackKit** — a CLI toolkit for building pr
|
|
|
89
87
|
|
|
90
88
|
Learn more about StackKit:
|
|
91
89
|
https://github.com/tariqul420/stackkit
|
|
92
|
-
|
|
@@ -3,8 +3,8 @@ import { QueryClient } from "@tanstack/react-query";
|
|
|
3
3
|
export const queryClient = new QueryClient({
|
|
4
4
|
defaultOptions: {
|
|
5
5
|
queries: {
|
|
6
|
-
staleTime: 1000 * 60 * 5,
|
|
7
|
-
gcTime: 1000 * 60 * 10,
|
|
6
|
+
staleTime: 1000 * 60 * 5,
|
|
7
|
+
gcTime: 1000 * 60 * 10,
|
|
8
8
|
retry: 1,
|
|
9
9
|
refetchOnWindowFocus: false,
|
|
10
10
|
},
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export const APP_NAME = import.meta.env.VITE_APP_NAME || "React App";
|
|
2
|
-
export const APP_VERSION = import.meta.env.VITE_APP_VERSION || "1.0.0";
|
|
3
|
-
export const API_URL = import.meta.env.VITE_API_URL || "http://localhost:3000/api";
|
|
4
|
-
export const IS_DEV = import.meta.env.DEV;
|
|
5
|
-
export const IS_PROD = import.meta.env.PROD;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
|
|
3
|
-
export function useDebounce<T>(value: T, delay: number): T {
|
|
4
|
-
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
5
|
-
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
const handler = setTimeout(() => {
|
|
8
|
-
setDebouncedValue(value);
|
|
9
|
-
}, delay);
|
|
10
|
-
|
|
11
|
-
return () => {
|
|
12
|
-
clearTimeout(handler);
|
|
13
|
-
};
|
|
14
|
-
}, [value, delay]);
|
|
15
|
-
|
|
16
|
-
return debouncedValue;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function useLocalStorage<T>(
|
|
20
|
-
key: string,
|
|
21
|
-
initialValue: T,
|
|
22
|
-
): [T, (value: T | ((val: T) => T)) => void] {
|
|
23
|
-
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
24
|
-
try {
|
|
25
|
-
const item = window.localStorage.getItem(key);
|
|
26
|
-
return item ? JSON.parse(item) : initialValue;
|
|
27
|
-
} catch (error) {
|
|
28
|
-
console.error(error);
|
|
29
|
-
return initialValue;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const setValue = (value: T | ((val: T) => T)) => {
|
|
34
|
-
try {
|
|
35
|
-
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
36
|
-
setStoredValue(valueToStore);
|
|
37
|
-
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(error);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return [storedValue, setValue];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function useMediaQuery(query: string): boolean {
|
|
47
|
-
const [matches, setMatches] = useState(() => {
|
|
48
|
-
if (typeof window !== "undefined") {
|
|
49
|
-
return window.matchMedia(query).matches;
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
const media = window.matchMedia(query);
|
|
56
|
-
|
|
57
|
-
const listener = () => setMatches(media.matches);
|
|
58
|
-
media.addEventListener("change", listener);
|
|
59
|
-
|
|
60
|
-
return () => media.removeEventListener("change", listener);
|
|
61
|
-
}, [query]);
|
|
62
|
-
|
|
63
|
-
return matches;
|
|
64
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
export function cn(...classes: (string | boolean | undefined | null)[]): string {
|
|
2
|
-
return classes.filter(Boolean).join(" ");
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function formatDate(date: Date | string): string {
|
|
6
|
-
const d = typeof date === "string" ? new Date(date) : date;
|
|
7
|
-
return d.toLocaleDateString("en-US", {
|
|
8
|
-
year: "numeric",
|
|
9
|
-
month: "long",
|
|
10
|
-
day: "numeric",
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function truncate(str: string, maxLength: number): string {
|
|
15
|
-
if (str.length <= maxLength) return str;
|
|
16
|
-
return str.slice(0, maxLength) + "...";
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function delay(ms: number): Promise<void> {
|
|
20
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function debounce<T extends (...args: never[]) => unknown>(
|
|
24
|
-
func: T,
|
|
25
|
-
wait: number,
|
|
26
|
-
): (...args: Parameters<T>) => void {
|
|
27
|
-
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
28
|
-
|
|
29
|
-
return function executedFunction(...args: Parameters<T>) {
|
|
30
|
-
const later = () => {
|
|
31
|
-
timeout = null;
|
|
32
|
-
func(...args);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
if (timeout) clearTimeout(timeout);
|
|
36
|
-
timeout = setTimeout(later, wait);
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function capitalize(str: string): string {
|
|
41
|
-
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function slugify(str: string): string {
|
|
45
|
-
return str
|
|
46
|
-
.toLowerCase()
|
|
47
|
-
.trim()
|
|
48
|
-
.replace(/[^\w\s-]/g, "")
|
|
49
|
-
.replace(/[\s_-]+/g, "-")
|
|
50
|
-
.replace(/^-+|-+$/g, "");
|
|
51
|
-
}
|