@lastbrain/app 0.1.8 → 0.1.10
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/scripts/init-app.js +7 -10
- package/package.json +3 -2
- package/src/app-shell/(admin)/layout.tsx +13 -0
- package/src/app-shell/(auth)/layout.tsx +13 -0
- package/src/app-shell/(public)/page.tsx +11 -0
- package/src/app-shell/layout.tsx +5 -0
- package/src/app-shell/not-found.tsx +28 -0
- package/src/auth/authHelpers.ts +24 -0
- package/src/auth/useAuthSession.ts +54 -0
- package/src/cli.ts +96 -0
- package/src/index.ts +21 -0
- package/src/layouts/AdminLayout.tsx +7 -0
- package/src/layouts/AppProviders.tsx +61 -0
- package/src/layouts/AuthLayout.tsx +7 -0
- package/src/layouts/PublicLayout.tsx +7 -0
- package/src/layouts/RootLayout.tsx +27 -0
- package/src/modules/module-loader.ts +14 -0
- package/src/scripts/README.md +262 -0
- package/src/scripts/db-init.ts +338 -0
- package/src/scripts/db-migrations-sync.ts +86 -0
- package/src/scripts/dev-sync.ts +218 -0
- package/src/scripts/init-app.ts +1076 -0
- package/src/scripts/module-add.ts +242 -0
- package/src/scripts/module-build.ts +502 -0
- package/src/scripts/module-create.ts +809 -0
- package/src/scripts/module-list.ts +37 -0
- package/src/scripts/module-remove.ts +367 -0
- package/src/scripts/readme-build.ts +60 -0
- package/src/styles.css +3 -0
- package/src/templates/AuthGuidePage.tsx +68 -0
- package/src/templates/DefaultDoc.tsx +462 -0
- package/src/templates/DocPage.tsx +381 -0
- package/src/templates/DocsPageWithModules.tsx +22 -0
- package/src/templates/MigrationsGuidePage.tsx +61 -0
- package/src/templates/ModuleGuidePage.tsx +71 -0
- package/src/templates/SimpleDocPage.tsx +587 -0
- package/src/templates/SimpleHomePage.tsx +385 -0
- package/src/templates/env.example/.env.example +6 -0
- package/src/templates/migrations/20201010100000_app_base.sql +228 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const GENERATED_HEADER = "// GENERATED BY LASTBRAIN APP-SHELL\n";
|
|
6
|
+
|
|
7
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = path.join(scriptDir, "..", "..");
|
|
9
|
+
const projectRoot = path.join(packageRoot, "..", "..");
|
|
10
|
+
const srcAppShell = path.join(projectRoot, "packages/app/src/app-shell");
|
|
11
|
+
const destAppShell = path.join(projectRoot, "apps/web/app");
|
|
12
|
+
const webPackage = path.join(projectRoot, "apps/web/package.json");
|
|
13
|
+
|
|
14
|
+
const scriptsToEnsure = {
|
|
15
|
+
dev: "next dev",
|
|
16
|
+
build: "next build",
|
|
17
|
+
"build:modules": "pnpm --filter @lastbrain/app module:build",
|
|
18
|
+
"db:migrations:sync": "pnpm --filter @lastbrain/app db:migrations:sync",
|
|
19
|
+
"db:init": "pnpm --filter @lastbrain/app db:init",
|
|
20
|
+
"readme:create": "pnpm --filter @lastbrain/app readme:create"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const dependenciesToEnsure = {
|
|
24
|
+
"@lastbrain/app": "workspace:*",
|
|
25
|
+
"@lastbrain/core": "workspace:*",
|
|
26
|
+
"@lastbrain/ui": "workspace:*"
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const gitignoreTemplate = path.join(projectRoot, "packages/app/src/templates/gitignore/.gitignore");
|
|
30
|
+
const consumerGitignore = path.join(projectRoot, "apps/web/.gitignore");
|
|
31
|
+
const envTemplate = path.join(projectRoot, "packages/app/src/templates/env.example/.env.example");
|
|
32
|
+
const consumerEnvExample = path.join(projectRoot, "apps/web/.env.example");
|
|
33
|
+
const consumerEnvLocal = path.join(projectRoot, "apps/web/.env.local");
|
|
34
|
+
|
|
35
|
+
function ensureDirectory(dir: string) {
|
|
36
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeContent(data: string) {
|
|
40
|
+
const trimmed = data.startsWith(GENERATED_HEADER) ? data.slice(GENERATED_HEADER.length) : data;
|
|
41
|
+
const sanitized = trimmed.trimStart();
|
|
42
|
+
return `${GENERATED_HEADER}\n${sanitized}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function syncFile(src: string, dest: string) {
|
|
46
|
+
const relativePath = path.relative(destAppShell, dest);
|
|
47
|
+
let content: string;
|
|
48
|
+
|
|
49
|
+
if (relativePath === "layout.tsx") {
|
|
50
|
+
content = `${GENERATED_HEADER}\nimport { RootLayout } from "@lastbrain/app";\n\nexport default RootLayout;\n`;
|
|
51
|
+
} else {
|
|
52
|
+
const data = fs.readFileSync(src, "utf-8");
|
|
53
|
+
content = normalizeContent(data);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ensureDirectory(path.dirname(dest));
|
|
57
|
+
fs.writeFileSync(dest, content);
|
|
58
|
+
return dest;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function copyDirectory(srcDir: string, destDir: string) {
|
|
62
|
+
const stats = fs.statSync(srcDir);
|
|
63
|
+
if (stats.isDirectory()) {
|
|
64
|
+
ensureDirectory(destDir);
|
|
65
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
copyDirectory(path.join(srcDir, entry.name), path.join(destDir, entry.name));
|
|
68
|
+
}
|
|
69
|
+
} else if (stats.isFile()) {
|
|
70
|
+
syncFile(srcDir, destDir);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function syncAppShell() {
|
|
75
|
+
if (!fs.existsSync(srcAppShell)) {
|
|
76
|
+
throw new Error(`Source app-shell not found at ${srcAppShell}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const copied: string[] = [];
|
|
80
|
+
const recurse = (src: string, dest: string) => {
|
|
81
|
+
const stats = fs.statSync(src);
|
|
82
|
+
if (stats.isDirectory()) {
|
|
83
|
+
ensureDirectory(dest);
|
|
84
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
recurse(path.join(src, entry.name), path.join(dest, entry.name));
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
syncFile(src, dest);
|
|
90
|
+
copied.push(dest);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
recurse(srcAppShell, destAppShell);
|
|
95
|
+
return copied;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function cleanupStaleGroupFiles() {
|
|
99
|
+
const staleFiles = [
|
|
100
|
+
path.join(destAppShell, "(auth)", "page.tsx"),
|
|
101
|
+
path.join(destAppShell, "(admin)", "page.tsx")
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
staleFiles.forEach((file) => {
|
|
105
|
+
if (fs.existsSync(file)) {
|
|
106
|
+
fs.unlinkSync(file);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const staleDirs = [
|
|
111
|
+
path.join(destAppShell, "(auth)", "dashboard"),
|
|
112
|
+
path.join(destAppShell, "(admin)", "dashboard")
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
staleDirs.forEach((dir) => {
|
|
116
|
+
if (fs.existsSync(dir)) {
|
|
117
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function mergeScripts(base: Record<string, string>, additions: Record<string, string>) {
|
|
123
|
+
return { ...base, ...additions };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function syncPackageJson() {
|
|
127
|
+
ensureDirectory(path.dirname(webPackage));
|
|
128
|
+
const defaultPkg = {
|
|
129
|
+
name: "web",
|
|
130
|
+
private: true,
|
|
131
|
+
version: "0.1.0",
|
|
132
|
+
type: "module"
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const pkg = fs.existsSync(webPackage) ? JSON.parse(fs.readFileSync(webPackage, "utf-8")) : defaultPkg;
|
|
136
|
+
pkg.scripts = mergeScripts(pkg.scripts ?? {}, scriptsToEnsure);
|
|
137
|
+
pkg.dependencies = { ...(pkg.dependencies ?? {}), ...dependenciesToEnsure };
|
|
138
|
+
|
|
139
|
+
fs.writeFileSync(webPackage, JSON.stringify(pkg, null, 2) + "\n");
|
|
140
|
+
return { scripts: Object.keys(scriptsToEnsure), dependencies: Object.keys(dependenciesToEnsure) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function syncEnvExample() {
|
|
144
|
+
if (!fs.existsSync(envTemplate)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const shouldOverwrite =
|
|
149
|
+
!fs.existsSync(consumerEnvExample) ||
|
|
150
|
+
fs.readFileSync(consumerEnvExample, "utf-8").includes("GENERATED BY LASTBRAIN");
|
|
151
|
+
|
|
152
|
+
if (!shouldOverwrite) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fs.copyFileSync(envTemplate, consumerEnvExample);
|
|
157
|
+
return consumerEnvExample;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function ensureEnvLocal() {
|
|
161
|
+
if (fs.existsSync(consumerEnvLocal)) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!fs.existsSync(envTemplate)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fs.copyFileSync(envTemplate, consumerEnvLocal);
|
|
170
|
+
return consumerEnvLocal;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function syncGitignore() {
|
|
174
|
+
if (!fs.existsSync(gitignoreTemplate)) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const shouldOverwrite =
|
|
179
|
+
!fs.existsSync(consumerGitignore) ||
|
|
180
|
+
fs.readFileSync(consumerGitignore, "utf-8").includes("GENERATED BY LASTBRAIN");
|
|
181
|
+
|
|
182
|
+
if (!shouldOverwrite) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fs.copyFileSync(gitignoreTemplate, consumerGitignore);
|
|
187
|
+
return consumerGitignore;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function main() {
|
|
191
|
+
ensureDirectory(destAppShell);
|
|
192
|
+
const copiedFiles = syncAppShell();
|
|
193
|
+
cleanupStaleGroupFiles();
|
|
194
|
+
const changes = syncPackageJson();
|
|
195
|
+
|
|
196
|
+
const gitignoreSynced = syncGitignore();
|
|
197
|
+
const envExampleSynced = syncEnvExample();
|
|
198
|
+
const envLocalCreated = ensureEnvLocal();
|
|
199
|
+
|
|
200
|
+
console.log("✅ apps/web synced with @lastbrain/app");
|
|
201
|
+
console.log(`Copied or updated files (${copiedFiles.length}):`);
|
|
202
|
+
copiedFiles.forEach((file) => console.log(` • ${path.relative(projectRoot, file)}`));
|
|
203
|
+
console.log("Scripts ensured in apps/web/package.json:");
|
|
204
|
+
changes.scripts.forEach((script) => console.log(` • ${script}`));
|
|
205
|
+
console.log("Dependencies ensured in apps/web/package.json:");
|
|
206
|
+
changes.dependencies.forEach((dep) => console.log(` • ${dep}`));
|
|
207
|
+
if (gitignoreSynced) {
|
|
208
|
+
console.log(`.gitignore ensured at ${path.relative(projectRoot, gitignoreSynced)}`);
|
|
209
|
+
}
|
|
210
|
+
if (envExampleSynced) {
|
|
211
|
+
console.log(`.env.example ensured at ${path.relative(projectRoot, envExampleSynced)}`);
|
|
212
|
+
}
|
|
213
|
+
if (envLocalCreated) {
|
|
214
|
+
console.log(`.env.local ensured at ${path.relative(projectRoot, envLocalCreated)}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main();
|