@bagelink/workspace 1.7.57 → 1.7.61
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/bin/bgl.ts +42 -3
- package/dist/bin/bgl.cjs +1020 -955
- package/dist/bin/bgl.mjs +1020 -955
- package/package.json +1 -1
- package/src/build.ts +44 -0
- package/src/dev.ts +23 -3
- package/src/workspace.ts +1 -1
package/dist/bin/bgl.mjs
CHANGED
|
@@ -1,548 +1,385 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import process from 'node:process';
|
|
4
|
-
import { existsSync, readFileSync, readdirSync, writeFileSync, unlinkSync, mkdirSync } from 'node:fs';
|
|
5
4
|
import { spawn } from 'node:child_process';
|
|
5
|
+
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
6
6
|
import prompts from 'prompts';
|
|
7
7
|
import { w as writeNetlifyConfig } from '../shared/workspace.Twuo1PFw.mjs';
|
|
8
8
|
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
if (existsSync(packageJsonPath)) {
|
|
12
|
-
try {
|
|
13
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
14
|
-
if (packageJson.workspaces !== void 0) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
} catch {
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
const items = readdirSync(root, { withFileTypes: true });
|
|
22
|
-
const projectDirs = items.filter(
|
|
23
|
-
(item) => item.isDirectory() && item.name !== "node_modules" && item.name !== "shared" && item.name !== ".git" && !item.name.startsWith(".") && existsSync(resolve(root, item.name, "package.json"))
|
|
24
|
-
);
|
|
25
|
-
return projectDirs.length >= 2;
|
|
26
|
-
} catch {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const servers = /* @__PURE__ */ new Map();
|
|
32
|
-
const seenLines = /* @__PURE__ */ new Set();
|
|
33
|
-
const colors = {
|
|
34
|
-
reset: "\x1B[0m",
|
|
35
|
-
cyan: "\x1B[36m",
|
|
36
|
-
green: "\x1B[32m",
|
|
37
|
-
yellow: "\x1B[33m",
|
|
38
|
-
dim: "\x1B[2m",
|
|
39
|
-
red: "\x1B[31m"
|
|
40
|
-
};
|
|
41
|
-
function clearAndPrintServers() {
|
|
42
|
-
if (servers.size > 0) {
|
|
43
|
-
process.stdout.write("\x1B[2J\x1B[H");
|
|
44
|
-
}
|
|
45
|
-
console.log(`${colors.cyan}\u{1F680} Development Servers${colors.reset}
|
|
46
|
-
`);
|
|
47
|
-
for (const [name, info] of servers.entries()) {
|
|
48
|
-
if (info.status === "ready" && info.port) {
|
|
49
|
-
const url = `http://localhost:${info.port}`;
|
|
50
|
-
console.log(`${colors.green}\u25CF${colors.reset} ${colors.cyan}${name}${colors.reset} ${colors.dim}\u2192${colors.reset} ${url}`);
|
|
51
|
-
} else {
|
|
52
|
-
console.log(`${colors.yellow}\u25CB${colors.reset} ${colors.dim}${name} (starting...)${colors.reset}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
console.log("");
|
|
56
|
-
}
|
|
57
|
-
async function runDev(filter = "./!shared*", additionalArgs = []) {
|
|
58
|
-
const argsStr = additionalArgs.length > 0 ? ` -- ${additionalArgs.join(" ")}` : "";
|
|
59
|
-
console.log(`${colors.dim}Starting dev servers with filter: ${filter}${argsStr}${colors.reset}
|
|
60
|
-
`);
|
|
61
|
-
const command = `bun run --filter '${filter}' dev${argsStr}`;
|
|
62
|
-
const proc = spawn(command, {
|
|
63
|
-
cwd: process.cwd(),
|
|
64
|
-
stdio: ["inherit", "pipe", "pipe"],
|
|
65
|
-
shell: true
|
|
66
|
-
});
|
|
67
|
-
let stdoutBuffer = "";
|
|
68
|
-
let stderrBuffer = "";
|
|
69
|
-
function processLine(line) {
|
|
70
|
-
if (!line.trim()) return;
|
|
71
|
-
if (seenLines.has(line)) return;
|
|
72
|
-
seenLines.add(line);
|
|
73
|
-
const projectMatch = line.match(/^(\w+)\s+dev:/);
|
|
74
|
-
if (projectMatch) {
|
|
75
|
-
const name = projectMatch[1];
|
|
76
|
-
if (!servers.has(name)) {
|
|
77
|
-
console.log(`${colors.dim}[DEBUG] Detected project: ${name}${colors.reset}`);
|
|
78
|
-
servers.set(name, { name, status: "starting" });
|
|
79
|
-
clearAndPrintServers();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const portMatch = line.match(/Local:\s+http:\/\/localhost:(\d+)/);
|
|
83
|
-
if (portMatch) {
|
|
84
|
-
const port = Number.parseInt(portMatch[1], 10);
|
|
85
|
-
const projectInLine = line.match(/^(\w+)\s+dev:/);
|
|
86
|
-
if (projectInLine) {
|
|
87
|
-
const name = projectInLine[1];
|
|
88
|
-
const info = servers.get(name);
|
|
89
|
-
console.log(`${colors.dim}[DEBUG] Port ${port} for project: ${name}, exists: ${!!info}, hasPort: ${info?.port}${colors.reset}`);
|
|
90
|
-
if (info && !info.port) {
|
|
91
|
-
info.port = port;
|
|
92
|
-
info.status = "ready";
|
|
93
|
-
clearAndPrintServers();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
proc.stdout?.setEncoding("utf8");
|
|
99
|
-
proc.stderr?.setEncoding("utf8");
|
|
100
|
-
proc.stdout?.on("data", (data) => {
|
|
101
|
-
if (servers.size === 0) {
|
|
102
|
-
console.log(`${colors.dim}Receiving output...${colors.reset}`);
|
|
103
|
-
}
|
|
104
|
-
stdoutBuffer += data;
|
|
105
|
-
const lines = stdoutBuffer.split("\n");
|
|
106
|
-
stdoutBuffer = lines.pop() || "";
|
|
107
|
-
for (const line of lines) {
|
|
108
|
-
processLine(line);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
proc.stderr?.on("data", (data) => {
|
|
112
|
-
stderrBuffer += data;
|
|
113
|
-
const lines = stderrBuffer.split("\n");
|
|
114
|
-
stderrBuffer = lines.pop() || "";
|
|
115
|
-
for (const line of lines) {
|
|
116
|
-
processLine(line);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
proc.on("error", (error) => {
|
|
120
|
-
console.error(`${colors.red}Failed to start dev servers:${colors.reset}`, error.message);
|
|
121
|
-
});
|
|
122
|
-
process.on("SIGINT", () => {
|
|
123
|
-
proc.kill("SIGINT");
|
|
124
|
-
process.exit(0);
|
|
125
|
-
});
|
|
126
|
-
return new Promise((resolve, reject) => {
|
|
127
|
-
proc.on("exit", (code) => {
|
|
128
|
-
resolve(code || 0);
|
|
129
|
-
});
|
|
130
|
-
proc.on("error", reject);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function generateWorkspaceConfig(root = process.cwd(), configFile = "bgl.config.ts") {
|
|
135
|
-
console.log("\n\u{1F527} No bgl.config.ts found. Let's create one!\n");
|
|
9
|
+
async function initWorkspace(root = process.cwd()) {
|
|
10
|
+
console.log("\n\u{1F680} Creating Bagel workspace...\n");
|
|
136
11
|
const response = await prompts([
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
name: "workspaceName",
|
|
15
|
+
message: "Workspace name:",
|
|
16
|
+
initial: "my-workspace"
|
|
17
|
+
},
|
|
137
18
|
{
|
|
138
19
|
type: "text",
|
|
139
20
|
name: "projectId",
|
|
140
|
-
message: "
|
|
141
|
-
initial: "my-project"
|
|
142
|
-
validate: (value) => value.length > 0 ? true : "Project ID is required"
|
|
21
|
+
message: "Bagel project ID:",
|
|
22
|
+
initial: "my-project"
|
|
143
23
|
},
|
|
144
24
|
{
|
|
145
25
|
type: "confirm",
|
|
146
|
-
name: "
|
|
147
|
-
message: "
|
|
148
|
-
initial:
|
|
26
|
+
name: "createFirstProject",
|
|
27
|
+
message: "Create first project?",
|
|
28
|
+
initial: true
|
|
149
29
|
},
|
|
150
30
|
{
|
|
151
31
|
type: (prev) => prev ? "text" : null,
|
|
152
|
-
name: "
|
|
153
|
-
message: "
|
|
154
|
-
initial: "
|
|
32
|
+
name: "firstProjectName",
|
|
33
|
+
message: "First project name:",
|
|
34
|
+
initial: "web"
|
|
155
35
|
}
|
|
156
36
|
]);
|
|
157
|
-
if (!response || !response.
|
|
158
|
-
console.log("\n\u274C
|
|
37
|
+
if (!response || !response.workspaceName) {
|
|
38
|
+
console.log("\n\u274C Workspace creation cancelled.\n");
|
|
159
39
|
process.exit(1);
|
|
160
40
|
}
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
|
|
41
|
+
const { workspaceName, projectId, createFirstProject, firstProjectName } = response;
|
|
42
|
+
const workspaceDir = resolve(root, workspaceName);
|
|
43
|
+
createWorkspaceRoot(root, workspaceName, projectId);
|
|
44
|
+
createSharedPackage(workspaceDir);
|
|
45
|
+
if (createFirstProject && firstProjectName) {
|
|
46
|
+
await addProject(firstProjectName, workspaceDir);
|
|
47
|
+
}
|
|
48
|
+
console.log("\n\u2705 Workspace created successfully!");
|
|
49
|
+
console.log("\nNext steps:");
|
|
50
|
+
console.log(` cd ${workspaceName}`);
|
|
51
|
+
console.log(" bun install");
|
|
52
|
+
if (createFirstProject) {
|
|
53
|
+
console.log(` bun run dev:${firstProjectName}`);
|
|
54
|
+
} else {
|
|
55
|
+
console.log(" bgl add <project-name> # Add a project");
|
|
56
|
+
}
|
|
57
|
+
console.log("");
|
|
58
|
+
}
|
|
59
|
+
function createWorkspaceRoot(root, name, projectId) {
|
|
60
|
+
const workspaceDir = resolve(root, name);
|
|
61
|
+
if (existsSync(workspaceDir)) {
|
|
62
|
+
console.error(`\u274C Directory ${name} already exists`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
66
|
+
const packageJson = {
|
|
67
|
+
name,
|
|
68
|
+
private: true,
|
|
69
|
+
workspaces: ["*", "!node_modules"],
|
|
70
|
+
scripts: {
|
|
71
|
+
"dev": "bgl dev",
|
|
72
|
+
"dev:local": "bgl dev --mode localhost",
|
|
73
|
+
"dev:verbose": "bun run --filter './!shared*' dev",
|
|
74
|
+
"build": "bgl build",
|
|
75
|
+
"typecheck": "tsc --noEmit",
|
|
76
|
+
"lint": "eslint . --cache",
|
|
77
|
+
"lint:fix": "eslint . --cache --fix"
|
|
78
|
+
},
|
|
79
|
+
dependencies: {
|
|
80
|
+
"@bagelink/auth": "latest",
|
|
81
|
+
"@bagelink/sdk": "latest",
|
|
82
|
+
"@bagelink/vue": "latest",
|
|
83
|
+
"pinia": "latest",
|
|
84
|
+
"vue": "latest",
|
|
85
|
+
"vue-router": "latest"
|
|
86
|
+
},
|
|
87
|
+
devDependencies: {
|
|
88
|
+
"@bagelink/lint-config": "latest",
|
|
89
|
+
"@bagelink/workspace": "latest",
|
|
90
|
+
"@vitejs/plugin-vue": "latest",
|
|
91
|
+
"eslint": "latest",
|
|
92
|
+
"typescript": "^5.0.0",
|
|
93
|
+
"vite": "latest"
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
writeFileSync(
|
|
97
|
+
resolve(workspaceDir, "package.json"),
|
|
98
|
+
`${JSON.stringify(packageJson, null, 2)}
|
|
99
|
+
`
|
|
100
|
+
);
|
|
101
|
+
const bglConfig = `import { defineWorkspace } from '@bagelink/workspace'
|
|
164
102
|
|
|
165
|
-
|
|
103
|
+
export default defineWorkspace({
|
|
166
104
|
localhost: {
|
|
167
105
|
host: 'http://localhost:8000',
|
|
168
106
|
proxy: '/api',
|
|
169
107
|
openapi_url: 'http://localhost:8000/openapi.json',
|
|
170
108
|
},
|
|
171
109
|
development: {
|
|
172
|
-
host: '
|
|
110
|
+
host: 'https://${projectId}.bagel.to',
|
|
173
111
|
proxy: '/api',
|
|
174
|
-
openapi_url: '
|
|
112
|
+
openapi_url: 'https://${projectId}.bagel.to/openapi.json',
|
|
175
113
|
},
|
|
176
114
|
production: {
|
|
177
|
-
host: '
|
|
115
|
+
host: 'https://${projectId}.bagel.to',
|
|
178
116
|
proxy: '/api',
|
|
179
|
-
openapi_url: '
|
|
117
|
+
openapi_url: 'https://${projectId}.bagel.to/openapi.json',
|
|
180
118
|
},
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export default defineWorkspace(configs)
|
|
119
|
+
})
|
|
184
120
|
`;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
type: "confirm",
|
|
207
|
-
name: "createTsConfig",
|
|
208
|
-
message: "Create tsconfig.app.json with path aliases?",
|
|
209
|
-
initial: true
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
type: "confirm",
|
|
213
|
-
name: "generateNetlify",
|
|
214
|
-
message: "Generate netlify.toml for deployment?",
|
|
215
|
-
initial: true
|
|
216
|
-
}
|
|
217
|
-
]);
|
|
218
|
-
if (setupResponse.updatePackageJson) {
|
|
219
|
-
updatePackageJsonScripts(root);
|
|
220
|
-
}
|
|
221
|
-
if (setupResponse.updateViteConfig) {
|
|
222
|
-
updateViteConfig(root);
|
|
223
|
-
}
|
|
224
|
-
if (setupResponse.createTsConfig) {
|
|
225
|
-
createTsConfig(root);
|
|
226
|
-
}
|
|
227
|
-
if (setupResponse.generateNetlify) {
|
|
228
|
-
const prodConfig = {
|
|
229
|
-
host: productionHost,
|
|
230
|
-
proxy: "/api"
|
|
231
|
-
};
|
|
232
|
-
writeNetlifyConfig(prodConfig, resolve(root, "netlify.toml"));
|
|
233
|
-
}
|
|
234
|
-
console.log("\n\u{1F4A1} You can edit these files to customize your configuration.\n");
|
|
235
|
-
}
|
|
236
|
-
function updatePackageJsonScripts(root) {
|
|
237
|
-
const packageJsonPath = resolve(root, "package.json");
|
|
238
|
-
if (!existsSync(packageJsonPath)) {
|
|
239
|
-
console.log("\u26A0\uFE0F No package.json found, skipping script update");
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
try {
|
|
243
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
244
|
-
if (!packageJson.scripts) {
|
|
245
|
-
packageJson.scripts = {};
|
|
246
|
-
}
|
|
247
|
-
const scriptsToAdd = {
|
|
248
|
-
"dev": "vite",
|
|
249
|
-
"dev:local": "vite --mode localhost",
|
|
250
|
-
"build": "vite build",
|
|
251
|
-
"preview": "vite preview"
|
|
252
|
-
};
|
|
253
|
-
let modified = false;
|
|
254
|
-
for (const [key, value] of Object.entries(scriptsToAdd)) {
|
|
255
|
-
if (key === "dev" || key === "dev:local") {
|
|
256
|
-
if (packageJson.scripts[key] !== value) {
|
|
257
|
-
packageJson.scripts[key] = value;
|
|
258
|
-
modified = true;
|
|
259
|
-
}
|
|
260
|
-
} else {
|
|
261
|
-
if (!packageJson.scripts[key]) {
|
|
262
|
-
packageJson.scripts[key] = value;
|
|
263
|
-
modified = true;
|
|
264
|
-
}
|
|
121
|
+
writeFileSync(resolve(workspaceDir, "bgl.config.ts"), bglConfig);
|
|
122
|
+
const tsConfig = {
|
|
123
|
+
compilerOptions: {
|
|
124
|
+
target: "ES2020",
|
|
125
|
+
useDefineForClassFields: true,
|
|
126
|
+
module: "ESNext",
|
|
127
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
128
|
+
skipLibCheck: true,
|
|
129
|
+
moduleResolution: "bundler",
|
|
130
|
+
allowImportingTsExtensions: true,
|
|
131
|
+
resolveJsonModule: true,
|
|
132
|
+
isolatedModules: true,
|
|
133
|
+
noEmit: true,
|
|
134
|
+
jsx: "preserve",
|
|
135
|
+
strict: true,
|
|
136
|
+
noUnusedLocals: true,
|
|
137
|
+
noUnusedParameters: true,
|
|
138
|
+
noFallthroughCasesInSwitch: true,
|
|
139
|
+
baseUrl: ".",
|
|
140
|
+
paths: {
|
|
141
|
+
"shared/*": ["./shared/*"]
|
|
265
142
|
}
|
|
266
143
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const tsConfigPath = resolve(root, "tsconfig.app.json");
|
|
280
|
-
const tsConfigExists = existsSync(tsConfigPath);
|
|
281
|
-
if (tsConfigExists) {
|
|
282
|
-
console.log("\u26A0\uFE0F tsconfig.app.json already exists, skipping");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
const tsConfigContent = `{
|
|
286
|
-
"extends": "../tsconfig.json",
|
|
287
|
-
"compilerOptions": {
|
|
288
|
-
"composite": true,
|
|
289
|
-
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
290
|
-
"baseUrl": ".",
|
|
291
|
-
"paths": {
|
|
292
|
-
"@/*": ["./src/*"],
|
|
293
|
-
"@shared/*": ["../shared/*"]
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
"include": ["src/**/*", "src/**/*.vue"],
|
|
297
|
-
"exclude": ["node_modules"]
|
|
298
|
-
}
|
|
144
|
+
};
|
|
145
|
+
writeFileSync(
|
|
146
|
+
resolve(workspaceDir, "tsconfig.json"),
|
|
147
|
+
`${JSON.stringify(tsConfig, null, 2)}
|
|
148
|
+
`
|
|
149
|
+
);
|
|
150
|
+
const gitignore = `node_modules
|
|
151
|
+
dist
|
|
152
|
+
.DS_Store
|
|
153
|
+
*.local
|
|
154
|
+
.env.local
|
|
155
|
+
.vite
|
|
299
156
|
`;
|
|
300
|
-
writeFileSync(
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const existingConfig = readFileSync(viteConfigPath, "utf-8");
|
|
308
|
-
if (existingConfig.includes("@bagelink/workspace")) {
|
|
309
|
-
console.log("\u2139\uFE0F vite.config.ts already configured");
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
console.log("\u26A0\uFE0F vite.config.ts exists. Please manually add the bagelink plugin:");
|
|
313
|
-
console.log("");
|
|
314
|
-
console.log(" import { bagelink } from '@bagelink/workspace/vite'");
|
|
315
|
-
console.log(" import workspace from './bgl.config'");
|
|
316
|
-
console.log("");
|
|
317
|
-
console.log(" plugins: [");
|
|
318
|
-
console.log(" vue(),");
|
|
319
|
-
console.log(" bagelink({ workspace }),");
|
|
320
|
-
console.log(" ]");
|
|
321
|
-
console.log("");
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
const viteConfigContent = `import { defineConfig } from 'vite'
|
|
325
|
-
import vue from '@vitejs/plugin-vue'
|
|
326
|
-
import { bagelink } from '@bagelink/workspace/vite'
|
|
327
|
-
import workspace from './bgl.config'
|
|
157
|
+
writeFileSync(resolve(workspaceDir, ".gitignore"), gitignore);
|
|
158
|
+
const scriptsDir = resolve(workspaceDir, "scripts");
|
|
159
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
160
|
+
const devRunnerContent = `#!/usr/bin/env bun
|
|
161
|
+
import { spawn } from 'bun'
|
|
162
|
+
import { readdir } from 'fs/promises'
|
|
163
|
+
import { resolve } from 'path'
|
|
328
164
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
165
|
+
const projectsRoot = process.cwd()
|
|
166
|
+
const projects = (await readdir(projectsRoot, { withFileTypes: true }))
|
|
167
|
+
.filter(
|
|
168
|
+
item =>
|
|
169
|
+
item.isDirectory()
|
|
170
|
+
&& item.name !== 'node_modules'
|
|
171
|
+
&& item.name !== 'shared'
|
|
172
|
+
&& item.name !== 'scripts'
|
|
173
|
+
&& item.name !== '.git'
|
|
174
|
+
&& !item.name.startsWith('.'),
|
|
175
|
+
)
|
|
176
|
+
.map(item => item.name)
|
|
177
|
+
|
|
178
|
+
console.log(\`\\n\u{1F680} Starting \${projects.length} project\${projects.length > 1 ? 's' : ''}...\\n\`)
|
|
179
|
+
|
|
180
|
+
const urlPattern = /Local:\\s+(http:\\/\\/localhost:\\d+)/
|
|
181
|
+
|
|
182
|
+
projects.forEach((project) => {
|
|
183
|
+
const proc = spawn({
|
|
184
|
+
cmd: ['bun', 'run', 'dev'],
|
|
185
|
+
cwd: resolve(projectsRoot, project),
|
|
186
|
+
stdout: 'pipe',
|
|
187
|
+
stderr: 'pipe',
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const decoder = new TextDecoder()
|
|
191
|
+
|
|
192
|
+
proc.stdout.pipeTo(
|
|
193
|
+
new WritableStream({
|
|
194
|
+
write(chunk) {
|
|
195
|
+
const text = decoder.decode(chunk)
|
|
196
|
+
const match = text.match(urlPattern)
|
|
197
|
+
if (match) {
|
|
198
|
+
console.log(\` \u2713 \${project.padEnd(15)} \u2192 \${match[1]}\`)
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
}),
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
proc.stderr.pipeTo(
|
|
205
|
+
new WritableStream({
|
|
206
|
+
write(chunk) {
|
|
207
|
+
const text = decoder.decode(chunk)
|
|
208
|
+
if (text.includes('error') || text.includes('Error')) {
|
|
209
|
+
console.error(\` \u2717 \${project}: \${text.trim()}\`)
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
}),
|
|
213
|
+
)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
console.log('\\n\u{1F4A1} Press Ctrl+C to stop all servers\\n')
|
|
217
|
+
|
|
218
|
+
process.on('SIGINT', () => {
|
|
219
|
+
console.log('\\n\\n\u{1F44B} Stopping all servers...\\n')
|
|
220
|
+
process.exit()
|
|
335
221
|
})
|
|
336
222
|
`;
|
|
337
|
-
writeFileSync(
|
|
338
|
-
console.log(
|
|
223
|
+
writeFileSync(resolve(scriptsDir, "dev.ts"), devRunnerContent);
|
|
224
|
+
console.log(`\u2705 Created workspace: ${name}`);
|
|
339
225
|
}
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
// Old Prettier configs (we create .prettierrc)
|
|
352
|
-
"prettier.config.js",
|
|
353
|
-
"prettier.config.cjs",
|
|
354
|
-
"prettier.config.mjs",
|
|
355
|
-
".prettierrc.json",
|
|
356
|
-
".prettierrc.yaml",
|
|
357
|
-
".prettierrc.yml",
|
|
358
|
-
".prettierrc.js",
|
|
359
|
-
".prettierrc.cjs",
|
|
360
|
-
".prettierrc.mjs",
|
|
361
|
-
".prettierrc.toml"
|
|
362
|
-
];
|
|
363
|
-
async function setupLint(root = process.cwd(), isWorkspace = false) {
|
|
364
|
-
console.log("\n\u{1F50D} Setting up linting...\n");
|
|
365
|
-
const response = await prompts([
|
|
366
|
-
{
|
|
367
|
-
type: "multiselect",
|
|
368
|
-
name: "configs",
|
|
369
|
-
message: "Select configurations to set up:",
|
|
370
|
-
choices: [
|
|
371
|
-
{ title: "ESLint", value: "eslint", selected: true },
|
|
372
|
-
{ title: "Prettier", value: "prettier", selected: true },
|
|
373
|
-
{ title: "EditorConfig", value: "editorconfig", selected: true },
|
|
374
|
-
{ title: "Git Hooks", value: "githooks", selected: false }
|
|
375
|
-
]
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
type: "confirm",
|
|
379
|
-
name: "cleanRedundant",
|
|
380
|
-
message: "Clean up redundant lint config files?",
|
|
381
|
-
initial: true
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
type: "confirm",
|
|
385
|
-
name: "installDeps",
|
|
386
|
-
message: "Install dependencies?",
|
|
387
|
-
initial: true
|
|
226
|
+
function createSharedPackage(root) {
|
|
227
|
+
const sharedDir = resolve(root, "shared");
|
|
228
|
+
mkdirSync(sharedDir, { recursive: true });
|
|
229
|
+
const packageJson = {
|
|
230
|
+
name: "shared",
|
|
231
|
+
version: "1.0.0",
|
|
232
|
+
type: "module",
|
|
233
|
+
exports: {
|
|
234
|
+
".": "./index.ts",
|
|
235
|
+
"./utils": "./utils/index.ts",
|
|
236
|
+
"./types": "./types/index.ts"
|
|
388
237
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
238
|
+
};
|
|
239
|
+
writeFileSync(
|
|
240
|
+
resolve(sharedDir, "package.json"),
|
|
241
|
+
`${JSON.stringify(packageJson, null, 2)}
|
|
242
|
+
`
|
|
243
|
+
);
|
|
244
|
+
writeFileSync(
|
|
245
|
+
resolve(sharedDir, "index.ts"),
|
|
246
|
+
"// Shared utilities and exports\nexport * from './utils'\n"
|
|
247
|
+
);
|
|
248
|
+
mkdirSync(resolve(sharedDir, "utils"), { recursive: true });
|
|
249
|
+
writeFileSync(
|
|
250
|
+
resolve(sharedDir, "utils", "index.ts"),
|
|
251
|
+
"// Shared utility functions\nexport function formatDate(date: Date): string {\n return date.toISOString()\n}\n"
|
|
252
|
+
);
|
|
253
|
+
mkdirSync(resolve(sharedDir, "types"), { recursive: true });
|
|
254
|
+
writeFileSync(
|
|
255
|
+
resolve(sharedDir, "types", "index.ts"),
|
|
256
|
+
"// Shared types\nexport interface User {\n id: string\n name: string\n}\n"
|
|
257
|
+
);
|
|
258
|
+
console.log("\u2705 Created shared package");
|
|
259
|
+
}
|
|
260
|
+
async function addProject(name, root = process.cwd()) {
|
|
261
|
+
const projectDir = resolve(root, name);
|
|
262
|
+
if (existsSync(projectDir)) {
|
|
263
|
+
console.error(`\u274C Project ${name} already exists`);
|
|
392
264
|
process.exit(1);
|
|
393
265
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
266
|
+
console.log(`
|
|
267
|
+
\u{1F4E6} Creating project: ${name}
|
|
268
|
+
`);
|
|
269
|
+
mkdirSync(projectDir, { recursive: true });
|
|
270
|
+
const isWorkspace = existsSync(resolve(root, "bgl.config.ts"));
|
|
271
|
+
const packageJson = {
|
|
272
|
+
name,
|
|
273
|
+
type: "module",
|
|
274
|
+
scripts: {
|
|
275
|
+
dev: "vite",
|
|
276
|
+
build: "vite build",
|
|
277
|
+
preview: "vite preview"
|
|
278
|
+
},
|
|
279
|
+
dependencies: {},
|
|
280
|
+
devDependencies: {
|
|
281
|
+
"@vitejs/plugin-vue": "latest",
|
|
282
|
+
"vite": "latest",
|
|
283
|
+
"vue": "latest"
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
if (isWorkspace) {
|
|
287
|
+
packageJson.dependencies.shared = "workspace:*";
|
|
414
288
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
289
|
+
writeFileSync(
|
|
290
|
+
resolve(projectDir, "package.json"),
|
|
291
|
+
`${JSON.stringify(packageJson, null, 2)}
|
|
292
|
+
`
|
|
293
|
+
);
|
|
294
|
+
const bglConfigContent = isWorkspace ? `import { defineWorkspace } from '@bagelink/workspace'
|
|
295
|
+
import rootWorkspace from '../bgl.config'
|
|
296
|
+
|
|
297
|
+
export default defineWorkspace({
|
|
298
|
+
localhost: rootWorkspace('localhost'),
|
|
299
|
+
development: rootWorkspace('development'),
|
|
300
|
+
production: rootWorkspace('production'),
|
|
301
|
+
})
|
|
302
|
+
` : `import { defineWorkspace } from '@bagelink/workspace'
|
|
303
|
+
|
|
304
|
+
export default defineWorkspace({
|
|
305
|
+
localhost: {
|
|
306
|
+
host: 'http://localhost:8000',
|
|
307
|
+
proxy: '/api',
|
|
308
|
+
},
|
|
309
|
+
development: {
|
|
310
|
+
host: 'https://my-project.bagel.to',
|
|
311
|
+
proxy: '/api',
|
|
312
|
+
},
|
|
313
|
+
production: {
|
|
314
|
+
host: 'https://my-project.bagel.to',
|
|
315
|
+
proxy: '/api',
|
|
316
|
+
},
|
|
317
|
+
})
|
|
318
|
+
`;
|
|
319
|
+
writeFileSync(resolve(projectDir, "bgl.config.ts"), bglConfigContent);
|
|
320
|
+
const viteConfig = `import { defineConfig } from 'vite'
|
|
321
|
+
import vue from '@vitejs/plugin-vue'
|
|
322
|
+
import { bagelink } from '@bagelink/workspace/vite'
|
|
323
|
+
import workspace from './bgl.config'
|
|
425
324
|
|
|
426
325
|
export default defineConfig({
|
|
427
|
-
|
|
428
|
-
|
|
326
|
+
plugins: [
|
|
327
|
+
vue(),
|
|
328
|
+
bagelink({ workspace }),
|
|
329
|
+
],
|
|
429
330
|
})
|
|
430
|
-
` : `import vue3Config from '@bagelink/lint-config/eslint/vue3'
|
|
431
|
-
|
|
432
|
-
export default vue3Config
|
|
433
331
|
`;
|
|
434
|
-
writeFileSync(
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
`);
|
|
450
|
-
console.log("\u2705 Created .prettierrc");
|
|
451
|
-
const ignorePath = resolve(root, ".prettierignore");
|
|
452
|
-
const ignore = `dist
|
|
453
|
-
node_modules
|
|
454
|
-
.bun-cache
|
|
455
|
-
*.min.js
|
|
456
|
-
*.min.css
|
|
332
|
+
writeFileSync(resolve(projectDir, "vite.config.ts"), viteConfig);
|
|
333
|
+
const srcDir = resolve(projectDir, "src");
|
|
334
|
+
mkdirSync(srcDir, { recursive: true });
|
|
335
|
+
const indexHtml = `<!DOCTYPE html>
|
|
336
|
+
<html lang="en">
|
|
337
|
+
<head>
|
|
338
|
+
<meta charset="UTF-8">
|
|
339
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
340
|
+
<title>${name}</title>
|
|
341
|
+
</head>
|
|
342
|
+
<body>
|
|
343
|
+
<div id="app"></div>
|
|
344
|
+
<script type="module" src="/src/main.ts"><\/script>
|
|
345
|
+
</body>
|
|
346
|
+
</html>
|
|
457
347
|
`;
|
|
458
|
-
writeFileSync(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
function createEditorConfig(root) {
|
|
462
|
-
const configPath = resolve(root, ".editorconfig");
|
|
463
|
-
const config = `root = true
|
|
464
|
-
|
|
465
|
-
[*]
|
|
466
|
-
charset = utf-8
|
|
467
|
-
indent_style = tab
|
|
468
|
-
indent_size = 2
|
|
469
|
-
end_of_line = lf
|
|
470
|
-
insert_final_newline = true
|
|
471
|
-
trim_trailing_whitespace = true
|
|
348
|
+
writeFileSync(resolve(projectDir, "index.html"), indexHtml);
|
|
349
|
+
const mainTs = `import { createApp } from 'vue'
|
|
350
|
+
import App from './App.vue'
|
|
472
351
|
|
|
473
|
-
|
|
474
|
-
|
|
352
|
+
createApp(App).mount('#app')
|
|
353
|
+
`;
|
|
354
|
+
writeFileSync(resolve(srcDir, "main.ts"), mainTs);
|
|
355
|
+
const appVue = `<script setup lang="ts">
|
|
356
|
+
import { ref } from 'vue'
|
|
357
|
+
${isWorkspace ? "import { formatDate } from 'shared/utils'\n" : ""}
|
|
358
|
+
const count = ref(0)
|
|
359
|
+
<\/script>
|
|
475
360
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
361
|
+
<template>
|
|
362
|
+
<div>
|
|
363
|
+
<h1>${name}</h1>
|
|
364
|
+
<button @click="count++">Count: {{ count }}</button>
|
|
365
|
+
${isWorkspace ? "<p>{{ formatDate(new Date()) }}</p>" : ""}
|
|
366
|
+
</div>
|
|
367
|
+
</template>
|
|
479
368
|
`;
|
|
480
|
-
writeFileSync(
|
|
481
|
-
console.log(
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const packageJsonPath = resolve(root, "package.json");
|
|
485
|
-
if (!existsSync(packageJsonPath)) {
|
|
486
|
-
console.warn("\u26A0\uFE0F No package.json found, skipping git hooks");
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
const lintStagedConfig = {
|
|
490
|
-
"*.{js,jsx,ts,tsx,vue}": ["eslint --fix"],
|
|
491
|
-
"*.{json,md,yml,yaml}": ["prettier --write"]
|
|
492
|
-
};
|
|
493
|
-
writeFileSync(
|
|
494
|
-
resolve(root, ".lintstagedrc"),
|
|
495
|
-
`${JSON.stringify(lintStagedConfig, null, 2)}
|
|
496
|
-
`
|
|
497
|
-
);
|
|
498
|
-
console.log("\u2705 Created .lintstagedrc");
|
|
499
|
-
console.log("\u2139\uFE0F Add simple-git-hooks and lint-staged to devDependencies");
|
|
500
|
-
console.log(" Then run: npx simple-git-hooks");
|
|
501
|
-
}
|
|
502
|
-
async function cleanRedundantFiles(root) {
|
|
503
|
-
const foundFiles = [];
|
|
504
|
-
for (const file of REDUNDANT_FILES) {
|
|
505
|
-
const filePath = resolve(root, file);
|
|
506
|
-
if (existsSync(filePath)) {
|
|
507
|
-
foundFiles.push(file);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
if (foundFiles.length === 0) {
|
|
511
|
-
console.log("\u2728 No redundant files found");
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
console.log("\n\u{1F4CB} Found redundant files:");
|
|
515
|
-
foundFiles.forEach((file) => {
|
|
516
|
-
console.log(` - ${file}`);
|
|
517
|
-
});
|
|
518
|
-
const confirmResponse = await prompts({
|
|
519
|
-
type: "confirm",
|
|
520
|
-
name: "confirm",
|
|
521
|
-
message: `Delete ${foundFiles.length} redundant file${foundFiles.length > 1 ? "s" : ""}?`,
|
|
522
|
-
initial: true
|
|
523
|
-
});
|
|
524
|
-
if (!confirmResponse.confirm) {
|
|
525
|
-
console.log("\u23ED\uFE0F Skipped cleaning redundant files");
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
let deleted = 0;
|
|
529
|
-
for (const file of foundFiles) {
|
|
530
|
-
try {
|
|
531
|
-
unlinkSync(resolve(root, file));
|
|
532
|
-
console.log(`\u{1F5D1}\uFE0F Deleted ${file}`);
|
|
533
|
-
deleted++;
|
|
534
|
-
} catch (error) {
|
|
535
|
-
console.error(`\u274C Failed to delete ${file}:`, error);
|
|
536
|
-
}
|
|
369
|
+
writeFileSync(resolve(srcDir, "App.vue"), appVue);
|
|
370
|
+
console.log(`\u2705 Created project: ${name}`);
|
|
371
|
+
if (isWorkspace) {
|
|
372
|
+
updateWorkspaceScripts(root, name);
|
|
537
373
|
}
|
|
538
|
-
console.log(
|
|
374
|
+
console.log("\nNext steps:");
|
|
375
|
+
console.log(` cd ${name}`);
|
|
376
|
+
console.log(" bun install");
|
|
377
|
+
console.log(" bun run dev");
|
|
378
|
+
console.log("");
|
|
539
379
|
}
|
|
540
|
-
function
|
|
380
|
+
function updateWorkspaceScripts(root, projectName) {
|
|
541
381
|
const packageJsonPath = resolve(root, "package.json");
|
|
542
|
-
if (!existsSync(packageJsonPath))
|
|
543
|
-
console.warn("\u26A0\uFE0F No package.json found");
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
382
|
+
if (!existsSync(packageJsonPath)) return;
|
|
546
383
|
try {
|
|
547
384
|
const packageJson = JSON.parse(
|
|
548
385
|
readFileSync(packageJsonPath, "utf-8")
|
|
@@ -550,474 +387,391 @@ function updatePackageJsonLint(root, configs) {
|
|
|
550
387
|
if (!packageJson.scripts) {
|
|
551
388
|
packageJson.scripts = {};
|
|
552
389
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
packageJson.scripts.lint = "eslint .";
|
|
556
|
-
}
|
|
557
|
-
if (!packageJson.scripts["lint:fix"]) {
|
|
558
|
-
packageJson.scripts["lint:fix"] = "eslint . --fix";
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (configs.includes("prettier")) {
|
|
562
|
-
if (!packageJson.scripts.format) {
|
|
563
|
-
packageJson.scripts.format = "prettier --write .";
|
|
564
|
-
}
|
|
565
|
-
if (!packageJson.scripts["format:check"]) {
|
|
566
|
-
packageJson.scripts["format:check"] = "prettier --check .";
|
|
567
|
-
}
|
|
568
|
-
}
|
|
390
|
+
packageJson.scripts[`dev:${projectName}`] = `bun --filter ${projectName} dev`;
|
|
391
|
+
packageJson.scripts[`build:${projectName}`] = `bun --filter ${projectName} build`;
|
|
569
392
|
writeFileSync(
|
|
570
393
|
packageJsonPath,
|
|
571
394
|
`${JSON.stringify(packageJson, null, 2)}
|
|
572
395
|
`
|
|
573
396
|
);
|
|
574
|
-
console.log(
|
|
397
|
+
console.log(`\u2705 Added scripts: dev:${projectName}, build:${projectName}`);
|
|
575
398
|
} catch (error) {
|
|
576
|
-
console.
|
|
399
|
+
console.warn("\u26A0\uFE0F Could not update workspace scripts");
|
|
577
400
|
}
|
|
578
401
|
}
|
|
579
|
-
|
|
580
|
-
async function generateSDK(root = process.cwd()) {
|
|
581
|
-
console.log("\n\u{1F527} Generating SDK from OpenAPI...\n");
|
|
582
|
-
let config = null;
|
|
583
|
-
let openApiUrl;
|
|
402
|
+
function listProjects(root = process.cwd()) {
|
|
584
403
|
try {
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if (typeof workspace === "function") {
|
|
590
|
-
config = workspace("development");
|
|
591
|
-
if (config?.openapi_url) {
|
|
592
|
-
openApiUrl = config.openapi_url;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
404
|
+
const items = readdirSync(root, { withFileTypes: true });
|
|
405
|
+
return items.filter(
|
|
406
|
+
(item) => item.isDirectory() && item.name !== "node_modules" && item.name !== "shared" && item.name !== ".git" && !item.name.startsWith(".")
|
|
407
|
+
).map((item) => item.name);
|
|
596
408
|
} catch {
|
|
409
|
+
return [];
|
|
597
410
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function runBuild(filter, additionalArgs = []) {
|
|
414
|
+
const argsStr = additionalArgs.length > 0 ? ` -- ${additionalArgs.join(" ")}` : "";
|
|
415
|
+
const resolvedFilter = resolveFilter$1(filter);
|
|
416
|
+
if (!resolvedFilter) return 1;
|
|
417
|
+
const command = `bun run --filter '${resolvedFilter}' build${argsStr}`;
|
|
418
|
+
const proc = spawn(command, {
|
|
419
|
+
cwd: process.cwd(),
|
|
420
|
+
stdio: "inherit",
|
|
421
|
+
shell: true
|
|
422
|
+
});
|
|
423
|
+
proc.on("error", (error) => {
|
|
424
|
+
console.error("Failed to start build:", error.message);
|
|
425
|
+
});
|
|
426
|
+
return new Promise((resolve, reject) => {
|
|
427
|
+
proc.on("exit", (code) => {
|
|
428
|
+
resolve(code || 0);
|
|
429
|
+
});
|
|
430
|
+
proc.on("error", reject);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function resolveFilter$1(filter) {
|
|
434
|
+
if (filter) return filter;
|
|
435
|
+
const projects = listProjects();
|
|
436
|
+
if (projects.length === 0) {
|
|
437
|
+
console.error("No projects found");
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
return "./[!shared]*";
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function isWorkspace(root = process.cwd()) {
|
|
444
|
+
const packageJsonPath = resolve(root, "package.json");
|
|
445
|
+
if (existsSync(packageJsonPath)) {
|
|
446
|
+
try {
|
|
447
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
448
|
+
if (packageJson.workspaces !== void 0) {
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
} catch {
|
|
616
452
|
}
|
|
617
|
-
]);
|
|
618
|
-
if (!response) {
|
|
619
|
-
console.log("\n\u274C SDK generation cancelled.\n");
|
|
620
|
-
process.exit(1);
|
|
621
453
|
}
|
|
622
|
-
const finalUrl = openApiUrl ?? response.openApiUrl;
|
|
623
|
-
const { outputDir, splitFiles } = response;
|
|
624
|
-
console.log(`
|
|
625
|
-
\u{1F4E1} Fetching OpenAPI spec from: ${finalUrl}`);
|
|
626
|
-
console.log(`\u{1F4C1} Output directory: ${outputDir}
|
|
627
|
-
`);
|
|
628
454
|
try {
|
|
629
|
-
const
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
if (!existsSync(outputPath)) {
|
|
633
|
-
mkdirSync(outputPath, { recursive: true });
|
|
634
|
-
}
|
|
635
|
-
const typesPath = resolve(outputPath, "types.d.ts");
|
|
636
|
-
writeFileSync(typesPath, types);
|
|
637
|
-
console.log("\u2705 Generated types.d.ts");
|
|
638
|
-
const apiPath = resolve(outputPath, "api.ts");
|
|
639
|
-
writeFileSync(apiPath, code);
|
|
640
|
-
console.log("\u2705 Generated api.ts");
|
|
641
|
-
const indexPath = resolve(outputPath, "index.ts");
|
|
642
|
-
writeFileSync(
|
|
643
|
-
indexPath,
|
|
644
|
-
"export * from './api'\nexport * from './types.d'\n"
|
|
455
|
+
const items = readdirSync(root, { withFileTypes: true });
|
|
456
|
+
const projectDirs = items.filter(
|
|
457
|
+
(item) => item.isDirectory() && item.name !== "node_modules" && item.name !== "shared" && item.name !== ".git" && !item.name.startsWith(".") && existsSync(resolve(root, item.name, "package.json"))
|
|
645
458
|
);
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
459
|
+
return projectDirs.length >= 2;
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const servers = /* @__PURE__ */ new Map();
|
|
466
|
+
const seenLines = /* @__PURE__ */ new Set();
|
|
467
|
+
const colors = {
|
|
468
|
+
reset: "\x1B[0m",
|
|
469
|
+
cyan: "\x1B[36m",
|
|
470
|
+
green: "\x1B[32m",
|
|
471
|
+
yellow: "\x1B[33m",
|
|
472
|
+
dim: "\x1B[2m",
|
|
473
|
+
red: "\x1B[31m"
|
|
474
|
+
};
|
|
475
|
+
function clearAndPrintServers() {
|
|
476
|
+
if (servers.size > 0) {
|
|
477
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
478
|
+
}
|
|
479
|
+
console.log(`${colors.cyan}\u{1F680} Development Servers${colors.reset}
|
|
480
|
+
`);
|
|
481
|
+
for (const [name, info] of servers.entries()) {
|
|
482
|
+
if (info.status === "ready" && info.port) {
|
|
483
|
+
const url = `http://localhost:${info.port}`;
|
|
484
|
+
console.log(`${colors.green}\u25CF${colors.reset} ${colors.cyan}${name}${colors.reset} ${colors.dim}\u2192${colors.reset} ${url}`);
|
|
661
485
|
} else {
|
|
662
|
-
console.
|
|
486
|
+
console.log(`${colors.yellow}\u25CB${colors.reset} ${colors.dim}${name} (starting...)${colors.reset}`);
|
|
663
487
|
}
|
|
664
|
-
console.log("\nMake sure:");
|
|
665
|
-
console.log(" 1. @bagelink/sdk is installed: bun add -D @bagelink/sdk");
|
|
666
|
-
console.log(" 2. OpenAPI URL is accessible");
|
|
667
|
-
console.log(" 3. API server is running (if using localhost)");
|
|
668
|
-
process.exit(1);
|
|
669
488
|
}
|
|
489
|
+
console.log("");
|
|
670
490
|
}
|
|
671
|
-
async function
|
|
672
|
-
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const response = await prompts({
|
|
683
|
-
type: "multiselect",
|
|
684
|
-
name: "selectedProjects",
|
|
685
|
-
message: "Select projects to generate SDK for:",
|
|
686
|
-
choices: projects.map((p) => ({ title: p, value: p, selected: true }))
|
|
491
|
+
async function runDev(filter, additionalArgs = []) {
|
|
492
|
+
const argsStr = additionalArgs.length > 0 ? ` -- ${additionalArgs.join(" ")}` : "";
|
|
493
|
+
const resolvedFilter = resolveFilter(filter);
|
|
494
|
+
if (!resolvedFilter) return 1;
|
|
495
|
+
console.log(`${colors.dim}Starting dev servers with filter: ${resolvedFilter}${argsStr}${colors.reset}
|
|
496
|
+
`);
|
|
497
|
+
const command = `bun run --filter '${resolvedFilter}' dev${argsStr}`;
|
|
498
|
+
const proc = spawn(command, {
|
|
499
|
+
cwd: process.cwd(),
|
|
500
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
501
|
+
shell: true
|
|
687
502
|
});
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
503
|
+
let stdoutBuffer = "";
|
|
504
|
+
let stderrBuffer = "";
|
|
505
|
+
function processLine(line) {
|
|
506
|
+
if (!line.trim()) return;
|
|
507
|
+
if (seenLines.has(line)) return;
|
|
508
|
+
seenLines.add(line);
|
|
509
|
+
const projectMatch = line.match(/^(\w+)\s+dev:/);
|
|
510
|
+
if (projectMatch) {
|
|
511
|
+
const name = projectMatch[1];
|
|
512
|
+
if (!servers.has(name)) {
|
|
513
|
+
console.log(`${colors.dim}[DEBUG] Detected project: ${name}${colors.reset}`);
|
|
514
|
+
servers.set(name, { name, status: "starting" });
|
|
515
|
+
clearAndPrintServers();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const portMatch = line.match(/Local:\s+http:\/\/localhost:(\d+)/);
|
|
519
|
+
if (portMatch) {
|
|
520
|
+
const port = Number.parseInt(portMatch[1], 10);
|
|
521
|
+
const projectInLine = line.match(/^(\w+)\s+dev:/);
|
|
522
|
+
if (projectInLine) {
|
|
523
|
+
const name = projectInLine[1];
|
|
524
|
+
const info = servers.get(name);
|
|
525
|
+
console.log(`${colors.dim}[DEBUG] Port ${port} for project: ${name}, exists: ${!!info}, hasPort: ${info?.port}${colors.reset}`);
|
|
526
|
+
if (info && !info.port) {
|
|
527
|
+
info.port = port;
|
|
528
|
+
info.status = "ready";
|
|
529
|
+
clearAndPrintServers();
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
691
533
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
534
|
+
proc.stdout?.setEncoding("utf8");
|
|
535
|
+
proc.stderr?.setEncoding("utf8");
|
|
536
|
+
proc.stdout?.on("data", (data) => {
|
|
537
|
+
if (servers.size === 0) {
|
|
538
|
+
console.log(`${colors.dim}Receiving output...${colors.reset}`);
|
|
539
|
+
}
|
|
540
|
+
stdoutBuffer += data;
|
|
541
|
+
const lines = stdoutBuffer.split("\n");
|
|
542
|
+
stdoutBuffer = lines.pop() || "";
|
|
543
|
+
for (const line of lines) {
|
|
544
|
+
processLine(line);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
proc.stderr?.on("data", (data) => {
|
|
548
|
+
stderrBuffer += data;
|
|
549
|
+
const lines = stderrBuffer.split("\n");
|
|
550
|
+
stderrBuffer = lines.pop() || "";
|
|
551
|
+
for (const line of lines) {
|
|
552
|
+
processLine(line);
|
|
700
553
|
}
|
|
554
|
+
});
|
|
555
|
+
proc.on("error", (error) => {
|
|
556
|
+
console.error(`${colors.red}Failed to start dev servers:${colors.reset}`, error.message);
|
|
557
|
+
});
|
|
558
|
+
process.on("SIGINT", () => {
|
|
559
|
+
proc.kill("SIGINT");
|
|
560
|
+
process.exit(0);
|
|
561
|
+
});
|
|
562
|
+
return new Promise((resolve, reject) => {
|
|
563
|
+
proc.on("exit", (code) => {
|
|
564
|
+
resolve(code || 0);
|
|
565
|
+
});
|
|
566
|
+
proc.on("error", reject);
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function resolveFilter(filter) {
|
|
570
|
+
if (filter) return filter;
|
|
571
|
+
const projects = listProjects();
|
|
572
|
+
if (projects.length === 0) {
|
|
573
|
+
console.error("No projects found");
|
|
574
|
+
return null;
|
|
701
575
|
}
|
|
702
|
-
|
|
576
|
+
return "./[!shared]*";
|
|
703
577
|
}
|
|
704
578
|
|
|
705
|
-
async function
|
|
706
|
-
console.log("\n\u{
|
|
579
|
+
async function generateWorkspaceConfig(root = process.cwd(), configFile = "bgl.config.ts") {
|
|
580
|
+
console.log("\n\u{1F527} No bgl.config.ts found. Let's create one!\n");
|
|
707
581
|
const response = await prompts([
|
|
708
|
-
{
|
|
709
|
-
type: "text",
|
|
710
|
-
name: "workspaceName",
|
|
711
|
-
message: "Workspace name:",
|
|
712
|
-
initial: "my-workspace"
|
|
713
|
-
},
|
|
714
582
|
{
|
|
715
583
|
type: "text",
|
|
716
584
|
name: "projectId",
|
|
717
|
-
message: "Bagel project ID
|
|
718
|
-
initial: "my-project"
|
|
585
|
+
message: "What is your Bagel project ID?",
|
|
586
|
+
initial: "my-project",
|
|
587
|
+
validate: (value) => value.length > 0 ? true : "Project ID is required"
|
|
719
588
|
},
|
|
720
589
|
{
|
|
721
590
|
type: "confirm",
|
|
722
|
-
name: "
|
|
723
|
-
message: "
|
|
724
|
-
initial:
|
|
725
|
-
},
|
|
726
|
-
{
|
|
727
|
-
type: (prev) => prev ? "text" : null,
|
|
728
|
-
name: "firstProjectName",
|
|
729
|
-
message: "First project name:",
|
|
730
|
-
initial: "web"
|
|
731
|
-
}
|
|
732
|
-
]);
|
|
733
|
-
if (!response || !response.workspaceName) {
|
|
734
|
-
console.log("\n\u274C Workspace creation cancelled.\n");
|
|
735
|
-
process.exit(1);
|
|
736
|
-
}
|
|
737
|
-
const { workspaceName, projectId, createFirstProject, firstProjectName } = response;
|
|
738
|
-
const workspaceDir = resolve(root, workspaceName);
|
|
739
|
-
createWorkspaceRoot(root, workspaceName, projectId);
|
|
740
|
-
createSharedPackage(workspaceDir);
|
|
741
|
-
if (createFirstProject && firstProjectName) {
|
|
742
|
-
await addProject(firstProjectName, workspaceDir);
|
|
743
|
-
}
|
|
744
|
-
console.log("\n\u2705 Workspace created successfully!");
|
|
745
|
-
console.log("\nNext steps:");
|
|
746
|
-
console.log(` cd ${workspaceName}`);
|
|
747
|
-
console.log(" bun install");
|
|
748
|
-
if (createFirstProject) {
|
|
749
|
-
console.log(` bun run dev:${firstProjectName}`);
|
|
750
|
-
} else {
|
|
751
|
-
console.log(" bgl add <project-name> # Add a project");
|
|
752
|
-
}
|
|
753
|
-
console.log("");
|
|
754
|
-
}
|
|
755
|
-
function createWorkspaceRoot(root, name, projectId) {
|
|
756
|
-
const workspaceDir = resolve(root, name);
|
|
757
|
-
if (existsSync(workspaceDir)) {
|
|
758
|
-
console.error(`\u274C Directory ${name} already exists`);
|
|
759
|
-
process.exit(1);
|
|
760
|
-
}
|
|
761
|
-
mkdirSync(workspaceDir, { recursive: true });
|
|
762
|
-
const packageJson = {
|
|
763
|
-
name,
|
|
764
|
-
private: true,
|
|
765
|
-
workspaces: ["*", "!node_modules"],
|
|
766
|
-
scripts: {
|
|
767
|
-
"dev": "bgl dev",
|
|
768
|
-
"dev:local": "bgl dev --mode localhost",
|
|
769
|
-
"dev:verbose": "bun run --filter './!shared*' dev",
|
|
770
|
-
"build": "bun run --filter './!shared*' build",
|
|
771
|
-
"typecheck": "tsc --noEmit",
|
|
772
|
-
"lint": "eslint . --cache",
|
|
773
|
-
"lint:fix": "eslint . --cache --fix"
|
|
774
|
-
},
|
|
775
|
-
dependencies: {
|
|
776
|
-
"@bagelink/auth": "latest",
|
|
777
|
-
"@bagelink/sdk": "latest",
|
|
778
|
-
"@bagelink/vue": "latest",
|
|
779
|
-
"pinia": "latest",
|
|
780
|
-
"vue": "latest",
|
|
781
|
-
"vue-router": "latest"
|
|
591
|
+
name: "useCustomHost",
|
|
592
|
+
message: "Use custom production host?",
|
|
593
|
+
initial: false
|
|
782
594
|
},
|
|
783
|
-
|
|
784
|
-
"
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
"typescript": "^5.0.0",
|
|
789
|
-
"vite": "latest"
|
|
595
|
+
{
|
|
596
|
+
type: (prev) => prev ? "text" : null,
|
|
597
|
+
name: "customHost",
|
|
598
|
+
message: "Enter production host URL:",
|
|
599
|
+
initial: "https://api.example.com"
|
|
790
600
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const
|
|
601
|
+
]);
|
|
602
|
+
if (!response || !response.projectId) {
|
|
603
|
+
console.log("\n\u274C Config generation cancelled.\n");
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
const productionHost = response.useCustomHost === true ? response.customHost : `https://${response.projectId}.bagel.to`;
|
|
607
|
+
const configContent = `import { defineWorkspace } from '@bagelink/workspace'
|
|
608
|
+
import type { WorkspaceConfig, WorkspaceEnvironment } from '@bagelink/workspace'
|
|
798
609
|
|
|
799
|
-
|
|
610
|
+
const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
|
|
800
611
|
localhost: {
|
|
801
612
|
host: 'http://localhost:8000',
|
|
802
613
|
proxy: '/api',
|
|
803
614
|
openapi_url: 'http://localhost:8000/openapi.json',
|
|
804
615
|
},
|
|
805
616
|
development: {
|
|
806
|
-
host: '
|
|
617
|
+
host: '${productionHost}',
|
|
807
618
|
proxy: '/api',
|
|
808
|
-
openapi_url: '
|
|
619
|
+
openapi_url: '${productionHost}/openapi.json',
|
|
809
620
|
},
|
|
810
621
|
production: {
|
|
811
|
-
host: '
|
|
622
|
+
host: '${productionHost}',
|
|
812
623
|
proxy: '/api',
|
|
813
|
-
openapi_url: '
|
|
624
|
+
openapi_url: '${productionHost}/openapi.json',
|
|
814
625
|
},
|
|
815
|
-
}
|
|
816
|
-
`;
|
|
817
|
-
writeFileSync(resolve(workspaceDir, "bgl.config.ts"), bglConfig);
|
|
818
|
-
const tsConfig = {
|
|
819
|
-
compilerOptions: {
|
|
820
|
-
target: "ES2020",
|
|
821
|
-
useDefineForClassFields: true,
|
|
822
|
-
module: "ESNext",
|
|
823
|
-
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
824
|
-
skipLibCheck: true,
|
|
825
|
-
moduleResolution: "bundler",
|
|
826
|
-
allowImportingTsExtensions: true,
|
|
827
|
-
resolveJsonModule: true,
|
|
828
|
-
isolatedModules: true,
|
|
829
|
-
noEmit: true,
|
|
830
|
-
jsx: "preserve",
|
|
831
|
-
strict: true,
|
|
832
|
-
noUnusedLocals: true,
|
|
833
|
-
noUnusedParameters: true,
|
|
834
|
-
noFallthroughCasesInSwitch: true,
|
|
835
|
-
baseUrl: ".",
|
|
836
|
-
paths: {
|
|
837
|
-
"shared/*": ["./shared/*"]
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
writeFileSync(
|
|
842
|
-
resolve(workspaceDir, "tsconfig.json"),
|
|
843
|
-
`${JSON.stringify(tsConfig, null, 2)}
|
|
844
|
-
`
|
|
845
|
-
);
|
|
846
|
-
const gitignore = `node_modules
|
|
847
|
-
dist
|
|
848
|
-
.DS_Store
|
|
849
|
-
*.local
|
|
850
|
-
.env.local
|
|
851
|
-
.vite
|
|
852
|
-
`;
|
|
853
|
-
writeFileSync(resolve(workspaceDir, ".gitignore"), gitignore);
|
|
854
|
-
const scriptsDir = resolve(workspaceDir, "scripts");
|
|
855
|
-
mkdirSync(scriptsDir, { recursive: true });
|
|
856
|
-
const devRunnerContent = `#!/usr/bin/env bun
|
|
857
|
-
import { spawn } from 'bun'
|
|
858
|
-
import { readdir } from 'fs/promises'
|
|
859
|
-
import { resolve } from 'path'
|
|
860
|
-
|
|
861
|
-
const projectsRoot = process.cwd()
|
|
862
|
-
const projects = (await readdir(projectsRoot, { withFileTypes: true }))
|
|
863
|
-
.filter(
|
|
864
|
-
item =>
|
|
865
|
-
item.isDirectory()
|
|
866
|
-
&& item.name !== 'node_modules'
|
|
867
|
-
&& item.name !== 'shared'
|
|
868
|
-
&& item.name !== 'scripts'
|
|
869
|
-
&& item.name !== '.git'
|
|
870
|
-
&& !item.name.startsWith('.'),
|
|
871
|
-
)
|
|
872
|
-
.map(item => item.name)
|
|
873
|
-
|
|
874
|
-
console.log(\`\\n\u{1F680} Starting \${projects.length} project\${projects.length > 1 ? 's' : ''}...\\n\`)
|
|
875
|
-
|
|
876
|
-
const urlPattern = /Local:\\s+(http:\\/\\/localhost:\\d+)/
|
|
877
|
-
|
|
878
|
-
projects.forEach((project) => {
|
|
879
|
-
const proc = spawn({
|
|
880
|
-
cmd: ['bun', 'run', 'dev'],
|
|
881
|
-
cwd: resolve(projectsRoot, project),
|
|
882
|
-
stdout: 'pipe',
|
|
883
|
-
stderr: 'pipe',
|
|
884
|
-
})
|
|
885
|
-
|
|
886
|
-
const decoder = new TextDecoder()
|
|
887
|
-
|
|
888
|
-
proc.stdout.pipeTo(
|
|
889
|
-
new WritableStream({
|
|
890
|
-
write(chunk) {
|
|
891
|
-
const text = decoder.decode(chunk)
|
|
892
|
-
const match = text.match(urlPattern)
|
|
893
|
-
if (match) {
|
|
894
|
-
console.log(\` \u2713 \${project.padEnd(15)} \u2192 \${match[1]}\`)
|
|
895
|
-
}
|
|
896
|
-
},
|
|
897
|
-
}),
|
|
898
|
-
)
|
|
899
|
-
|
|
900
|
-
proc.stderr.pipeTo(
|
|
901
|
-
new WritableStream({
|
|
902
|
-
write(chunk) {
|
|
903
|
-
const text = decoder.decode(chunk)
|
|
904
|
-
if (text.includes('error') || text.includes('Error')) {
|
|
905
|
-
console.error(\` \u2717 \${project}: \${text.trim()}\`)
|
|
906
|
-
}
|
|
907
|
-
},
|
|
908
|
-
}),
|
|
909
|
-
)
|
|
910
|
-
})
|
|
911
|
-
|
|
912
|
-
console.log('\\n\u{1F4A1} Press Ctrl+C to stop all servers\\n')
|
|
626
|
+
}
|
|
913
627
|
|
|
914
|
-
|
|
915
|
-
console.log('\\n\\n\u{1F44B} Stopping all servers...\\n')
|
|
916
|
-
process.exit()
|
|
917
|
-
})
|
|
628
|
+
export default defineWorkspace(configs)
|
|
918
629
|
`;
|
|
919
|
-
|
|
920
|
-
|
|
630
|
+
const configPath = resolve(root, configFile);
|
|
631
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
632
|
+
console.log(`
|
|
633
|
+
\u2705 Created ${configFile}`);
|
|
634
|
+
console.log(` Production host: ${productionHost}`);
|
|
635
|
+
console.log(` Local dev host: http://localhost:8000
|
|
636
|
+
`);
|
|
637
|
+
const setupResponse = await prompts([
|
|
638
|
+
{
|
|
639
|
+
type: "confirm",
|
|
640
|
+
name: "updatePackageJson",
|
|
641
|
+
message: "Add/update dev scripts in package.json?",
|
|
642
|
+
initial: true
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
type: "confirm",
|
|
646
|
+
name: "updateViteConfig",
|
|
647
|
+
message: "Create/update vite.config.ts?",
|
|
648
|
+
initial: true
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
type: "confirm",
|
|
652
|
+
name: "createTsConfig",
|
|
653
|
+
message: "Create tsconfig.app.json with path aliases?",
|
|
654
|
+
initial: true
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
type: "confirm",
|
|
658
|
+
name: "generateNetlify",
|
|
659
|
+
message: "Generate netlify.toml for deployment?",
|
|
660
|
+
initial: true
|
|
661
|
+
}
|
|
662
|
+
]);
|
|
663
|
+
if (setupResponse.updatePackageJson) {
|
|
664
|
+
updatePackageJsonScripts(root);
|
|
665
|
+
}
|
|
666
|
+
if (setupResponse.updateViteConfig) {
|
|
667
|
+
updateViteConfig(root);
|
|
668
|
+
}
|
|
669
|
+
if (setupResponse.createTsConfig) {
|
|
670
|
+
createTsConfig(root);
|
|
671
|
+
}
|
|
672
|
+
if (setupResponse.generateNetlify) {
|
|
673
|
+
const prodConfig = {
|
|
674
|
+
host: productionHost,
|
|
675
|
+
proxy: "/api"
|
|
676
|
+
};
|
|
677
|
+
writeNetlifyConfig(prodConfig, resolve(root, "netlify.toml"));
|
|
678
|
+
}
|
|
679
|
+
console.log("\n\u{1F4A1} You can edit these files to customize your configuration.\n");
|
|
921
680
|
}
|
|
922
|
-
function
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
"./types": "./types/index.ts"
|
|
681
|
+
function updatePackageJsonScripts(root) {
|
|
682
|
+
const packageJsonPath = resolve(root, "package.json");
|
|
683
|
+
if (!existsSync(packageJsonPath)) {
|
|
684
|
+
console.log("\u26A0\uFE0F No package.json found, skipping script update");
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
689
|
+
if (!packageJson.scripts) {
|
|
690
|
+
packageJson.scripts = {};
|
|
933
691
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
692
|
+
const scriptsToAdd = {
|
|
693
|
+
"dev": "vite",
|
|
694
|
+
"dev:local": "vite --mode localhost",
|
|
695
|
+
"build": "vite build",
|
|
696
|
+
"preview": "vite preview"
|
|
697
|
+
};
|
|
698
|
+
let modified = false;
|
|
699
|
+
for (const [key, value] of Object.entries(scriptsToAdd)) {
|
|
700
|
+
if (key === "dev" || key === "dev:local") {
|
|
701
|
+
if (packageJson.scripts[key] !== value) {
|
|
702
|
+
packageJson.scripts[key] = value;
|
|
703
|
+
modified = true;
|
|
704
|
+
}
|
|
705
|
+
} else {
|
|
706
|
+
if (!packageJson.scripts[key]) {
|
|
707
|
+
packageJson.scripts[key] = value;
|
|
708
|
+
modified = true;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (modified) {
|
|
713
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
714
|
+
`, "utf-8");
|
|
715
|
+
console.log("\u2705 Updated package.json with dev scripts");
|
|
716
|
+
} else {
|
|
717
|
+
console.log("\u2139\uFE0F Scripts already up to date in package.json");
|
|
718
|
+
}
|
|
719
|
+
} catch (error) {
|
|
720
|
+
console.error("\u274C Failed to update package.json:", error);
|
|
721
|
+
}
|
|
955
722
|
}
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
723
|
+
function createTsConfig(root) {
|
|
724
|
+
const tsConfigPath = resolve(root, "tsconfig.app.json");
|
|
725
|
+
const tsConfigExists = existsSync(tsConfigPath);
|
|
726
|
+
if (tsConfigExists) {
|
|
727
|
+
console.log("\u26A0\uFE0F tsconfig.app.json already exists, skipping");
|
|
728
|
+
return;
|
|
961
729
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
dev: "vite",
|
|
972
|
-
build: "vite build",
|
|
973
|
-
preview: "vite preview"
|
|
974
|
-
},
|
|
975
|
-
dependencies: {},
|
|
976
|
-
devDependencies: {
|
|
977
|
-
"@vitejs/plugin-vue": "latest",
|
|
978
|
-
"vite": "latest",
|
|
979
|
-
"vue": "latest"
|
|
730
|
+
const tsConfigContent = `{
|
|
731
|
+
"extends": "../tsconfig.json",
|
|
732
|
+
"compilerOptions": {
|
|
733
|
+
"composite": true,
|
|
734
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
735
|
+
"baseUrl": ".",
|
|
736
|
+
"paths": {
|
|
737
|
+
"@/*": ["./src/*"],
|
|
738
|
+
"@shared/*": ["../shared/*"]
|
|
980
739
|
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
writeFileSync(
|
|
986
|
-
resolve(projectDir, "package.json"),
|
|
987
|
-
`${JSON.stringify(packageJson, null, 2)}
|
|
988
|
-
`
|
|
989
|
-
);
|
|
990
|
-
const bglConfigContent = isWorkspace ? `import { defineWorkspace } from '@bagelink/workspace'
|
|
991
|
-
import rootWorkspace from '../bgl.config'
|
|
992
|
-
|
|
993
|
-
export default defineWorkspace({
|
|
994
|
-
localhost: rootWorkspace('localhost'),
|
|
995
|
-
development: rootWorkspace('development'),
|
|
996
|
-
production: rootWorkspace('production'),
|
|
997
|
-
})
|
|
998
|
-
` : `import { defineWorkspace } from '@bagelink/workspace'
|
|
999
|
-
|
|
1000
|
-
export default defineWorkspace({
|
|
1001
|
-
localhost: {
|
|
1002
|
-
host: 'http://localhost:8000',
|
|
1003
|
-
proxy: '/api',
|
|
1004
|
-
},
|
|
1005
|
-
development: {
|
|
1006
|
-
host: 'https://my-project.bagel.to',
|
|
1007
|
-
proxy: '/api',
|
|
1008
|
-
},
|
|
1009
|
-
production: {
|
|
1010
|
-
host: 'https://my-project.bagel.to',
|
|
1011
|
-
proxy: '/api',
|
|
1012
|
-
},
|
|
1013
|
-
})
|
|
740
|
+
},
|
|
741
|
+
"include": ["src/**/*", "src/**/*.vue"],
|
|
742
|
+
"exclude": ["node_modules"]
|
|
743
|
+
}
|
|
1014
744
|
`;
|
|
1015
|
-
writeFileSync(
|
|
1016
|
-
|
|
745
|
+
writeFileSync(tsConfigPath, tsConfigContent, "utf-8");
|
|
746
|
+
console.log("\u2705 Created tsconfig.app.json");
|
|
747
|
+
}
|
|
748
|
+
function updateViteConfig(root) {
|
|
749
|
+
const viteConfigPath = resolve(root, "vite.config.ts");
|
|
750
|
+
const viteConfigExists = existsSync(viteConfigPath);
|
|
751
|
+
if (viteConfigExists) {
|
|
752
|
+
const existingConfig = readFileSync(viteConfigPath, "utf-8");
|
|
753
|
+
if (existingConfig.includes("@bagelink/workspace")) {
|
|
754
|
+
console.log("\u2139\uFE0F vite.config.ts already configured");
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
console.log("\u26A0\uFE0F vite.config.ts exists. Please manually add the bagelink plugin:");
|
|
758
|
+
console.log("");
|
|
759
|
+
console.log(" import { bagelink } from '@bagelink/workspace/vite'");
|
|
760
|
+
console.log(" import workspace from './bgl.config'");
|
|
761
|
+
console.log("");
|
|
762
|
+
console.log(" plugins: [");
|
|
763
|
+
console.log(" vue(),");
|
|
764
|
+
console.log(" bagelink({ workspace }),");
|
|
765
|
+
console.log(" ]");
|
|
766
|
+
console.log("");
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const viteConfigContent = `import { defineConfig } from 'vite'
|
|
1017
770
|
import vue from '@vitejs/plugin-vue'
|
|
1018
771
|
import { bagelink } from '@bagelink/workspace/vite'
|
|
1019
772
|
import workspace from './bgl.config'
|
|
1020
773
|
|
|
774
|
+
// https://vitejs.dev/config/
|
|
1021
775
|
export default defineConfig({
|
|
1022
776
|
plugins: [
|
|
1023
777
|
vue(),
|
|
@@ -1025,57 +779,215 @@ export default defineConfig({
|
|
|
1025
779
|
],
|
|
1026
780
|
})
|
|
1027
781
|
`;
|
|
1028
|
-
writeFileSync(
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
782
|
+
writeFileSync(viteConfigPath, viteConfigContent, "utf-8");
|
|
783
|
+
console.log("\u2705 Created vite.config.ts");
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const REDUNDANT_FILES = [
|
|
787
|
+
// Old ESLint configs
|
|
788
|
+
".eslintrc",
|
|
789
|
+
".eslintrc.json",
|
|
790
|
+
".eslintrc.js",
|
|
791
|
+
".eslintrc.cjs",
|
|
792
|
+
".eslintrc.yaml",
|
|
793
|
+
".eslintrc.yml",
|
|
794
|
+
// Oxlint
|
|
795
|
+
"oxlint.json",
|
|
796
|
+
// Old Prettier configs (we create .prettierrc)
|
|
797
|
+
"prettier.config.js",
|
|
798
|
+
"prettier.config.cjs",
|
|
799
|
+
"prettier.config.mjs",
|
|
800
|
+
".prettierrc.json",
|
|
801
|
+
".prettierrc.yaml",
|
|
802
|
+
".prettierrc.yml",
|
|
803
|
+
".prettierrc.js",
|
|
804
|
+
".prettierrc.cjs",
|
|
805
|
+
".prettierrc.mjs",
|
|
806
|
+
".prettierrc.toml"
|
|
807
|
+
];
|
|
808
|
+
async function setupLint(root = process.cwd(), isWorkspace = false) {
|
|
809
|
+
console.log("\n\u{1F50D} Setting up linting...\n");
|
|
810
|
+
const response = await prompts([
|
|
811
|
+
{
|
|
812
|
+
type: "multiselect",
|
|
813
|
+
name: "configs",
|
|
814
|
+
message: "Select configurations to set up:",
|
|
815
|
+
choices: [
|
|
816
|
+
{ title: "ESLint", value: "eslint", selected: true },
|
|
817
|
+
{ title: "Prettier", value: "prettier", selected: true },
|
|
818
|
+
{ title: "EditorConfig", value: "editorconfig", selected: true },
|
|
819
|
+
{ title: "Git Hooks", value: "githooks", selected: false }
|
|
820
|
+
]
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
type: "confirm",
|
|
824
|
+
name: "cleanRedundant",
|
|
825
|
+
message: "Clean up redundant lint config files?",
|
|
826
|
+
initial: true
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
type: "confirm",
|
|
830
|
+
name: "installDeps",
|
|
831
|
+
message: "Install dependencies?",
|
|
832
|
+
initial: true
|
|
833
|
+
}
|
|
834
|
+
]);
|
|
835
|
+
if (!response || !response.configs) {
|
|
836
|
+
console.log("\n\u274C Setup cancelled.\n");
|
|
837
|
+
process.exit(1);
|
|
838
|
+
}
|
|
839
|
+
const { configs, cleanRedundant, installDeps } = response;
|
|
840
|
+
if (cleanRedundant) {
|
|
841
|
+
await cleanRedundantFiles(root);
|
|
842
|
+
}
|
|
843
|
+
if (configs.includes("eslint")) {
|
|
844
|
+
createEslintConfig(root, isWorkspace);
|
|
845
|
+
}
|
|
846
|
+
if (configs.includes("prettier")) {
|
|
847
|
+
createPrettierConfig(root);
|
|
848
|
+
}
|
|
849
|
+
if (configs.includes("editorconfig")) {
|
|
850
|
+
createEditorConfig(root);
|
|
851
|
+
}
|
|
852
|
+
if (configs.includes("githooks")) {
|
|
853
|
+
createGitHooks(root);
|
|
854
|
+
}
|
|
855
|
+
updatePackageJsonLint(root, configs);
|
|
856
|
+
if (installDeps) {
|
|
857
|
+
console.log("\n\u{1F4E6} Installing dependencies...");
|
|
858
|
+
console.log("Run: bun add -D @bagelink/lint-config eslint prettier typescript");
|
|
859
|
+
}
|
|
860
|
+
console.log("\n\u2705 Linting setup complete!");
|
|
861
|
+
console.log("\nAvailable commands:");
|
|
862
|
+
console.log(" bun run lint - Run linter");
|
|
863
|
+
console.log(" bun run lint:fix - Fix linting issues");
|
|
864
|
+
console.log(" bun run format - Format code with Prettier");
|
|
865
|
+
console.log("");
|
|
866
|
+
}
|
|
867
|
+
function createEslintConfig(root, isWorkspace) {
|
|
868
|
+
const configPath = resolve(root, "eslint.config.js");
|
|
869
|
+
const config = isWorkspace ? `import { defineConfig } from '@bagelink/lint-config/eslint'
|
|
870
|
+
|
|
871
|
+
export default defineConfig({
|
|
872
|
+
// Workspace-level ESLint config
|
|
873
|
+
ignores: ['**/dist/**', '**/node_modules/**', '**/.bun-cache/**'],
|
|
874
|
+
})
|
|
875
|
+
` : `import vue3Config from '@bagelink/lint-config/eslint/vue3'
|
|
876
|
+
|
|
877
|
+
export default vue3Config
|
|
1043
878
|
`;
|
|
1044
|
-
writeFileSync(
|
|
1045
|
-
|
|
1046
|
-
|
|
879
|
+
writeFileSync(configPath, config);
|
|
880
|
+
console.log("\u2705 Created eslint.config.js");
|
|
881
|
+
}
|
|
882
|
+
function createPrettierConfig(root) {
|
|
883
|
+
const configPath = resolve(root, ".prettierrc");
|
|
884
|
+
const config = {
|
|
885
|
+
semi: false,
|
|
886
|
+
singleQuote: true,
|
|
887
|
+
tabWidth: 2,
|
|
888
|
+
useTabs: true,
|
|
889
|
+
trailingComma: "all",
|
|
890
|
+
printWidth: 100,
|
|
891
|
+
arrowParens: "avoid"
|
|
892
|
+
};
|
|
893
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
894
|
+
`);
|
|
895
|
+
console.log("\u2705 Created .prettierrc");
|
|
896
|
+
const ignorePath = resolve(root, ".prettierignore");
|
|
897
|
+
const ignore = `dist
|
|
898
|
+
node_modules
|
|
899
|
+
.bun-cache
|
|
900
|
+
*.min.js
|
|
901
|
+
*.min.css
|
|
902
|
+
`;
|
|
903
|
+
writeFileSync(ignorePath, ignore);
|
|
904
|
+
console.log("\u2705 Created .prettierignore");
|
|
905
|
+
}
|
|
906
|
+
function createEditorConfig(root) {
|
|
907
|
+
const configPath = resolve(root, ".editorconfig");
|
|
908
|
+
const config = `root = true
|
|
1047
909
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
<\/script>
|
|
910
|
+
[*]
|
|
911
|
+
charset = utf-8
|
|
912
|
+
indent_style = tab
|
|
913
|
+
indent_size = 2
|
|
914
|
+
end_of_line = lf
|
|
915
|
+
insert_final_newline = true
|
|
916
|
+
trim_trailing_whitespace = true
|
|
1056
917
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
</template>
|
|
918
|
+
[*.md]
|
|
919
|
+
trim_trailing_whitespace = false
|
|
920
|
+
|
|
921
|
+
[*.{json,yml,yaml}]
|
|
922
|
+
indent_style = space
|
|
923
|
+
indent_size = 2
|
|
1064
924
|
`;
|
|
1065
|
-
writeFileSync(
|
|
1066
|
-
console.log(
|
|
1067
|
-
|
|
1068
|
-
|
|
925
|
+
writeFileSync(configPath, config);
|
|
926
|
+
console.log("\u2705 Created .editorconfig");
|
|
927
|
+
}
|
|
928
|
+
function createGitHooks(root) {
|
|
929
|
+
const packageJsonPath = resolve(root, "package.json");
|
|
930
|
+
if (!existsSync(packageJsonPath)) {
|
|
931
|
+
console.warn("\u26A0\uFE0F No package.json found, skipping git hooks");
|
|
932
|
+
return;
|
|
1069
933
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
934
|
+
const lintStagedConfig = {
|
|
935
|
+
"*.{js,jsx,ts,tsx,vue}": ["eslint --fix"],
|
|
936
|
+
"*.{json,md,yml,yaml}": ["prettier --write"]
|
|
937
|
+
};
|
|
938
|
+
writeFileSync(
|
|
939
|
+
resolve(root, ".lintstagedrc"),
|
|
940
|
+
`${JSON.stringify(lintStagedConfig, null, 2)}
|
|
941
|
+
`
|
|
942
|
+
);
|
|
943
|
+
console.log("\u2705 Created .lintstagedrc");
|
|
944
|
+
console.log("\u2139\uFE0F Add simple-git-hooks and lint-staged to devDependencies");
|
|
945
|
+
console.log(" Then run: npx simple-git-hooks");
|
|
1075
946
|
}
|
|
1076
|
-
function
|
|
947
|
+
async function cleanRedundantFiles(root) {
|
|
948
|
+
const foundFiles = [];
|
|
949
|
+
for (const file of REDUNDANT_FILES) {
|
|
950
|
+
const filePath = resolve(root, file);
|
|
951
|
+
if (existsSync(filePath)) {
|
|
952
|
+
foundFiles.push(file);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (foundFiles.length === 0) {
|
|
956
|
+
console.log("\u2728 No redundant files found");
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
console.log("\n\u{1F4CB} Found redundant files:");
|
|
960
|
+
foundFiles.forEach((file) => {
|
|
961
|
+
console.log(` - ${file}`);
|
|
962
|
+
});
|
|
963
|
+
const confirmResponse = await prompts({
|
|
964
|
+
type: "confirm",
|
|
965
|
+
name: "confirm",
|
|
966
|
+
message: `Delete ${foundFiles.length} redundant file${foundFiles.length > 1 ? "s" : ""}?`,
|
|
967
|
+
initial: true
|
|
968
|
+
});
|
|
969
|
+
if (!confirmResponse.confirm) {
|
|
970
|
+
console.log("\u23ED\uFE0F Skipped cleaning redundant files");
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
let deleted = 0;
|
|
974
|
+
for (const file of foundFiles) {
|
|
975
|
+
try {
|
|
976
|
+
unlinkSync(resolve(root, file));
|
|
977
|
+
console.log(`\u{1F5D1}\uFE0F Deleted ${file}`);
|
|
978
|
+
deleted++;
|
|
979
|
+
} catch (error) {
|
|
980
|
+
console.error(`\u274C Failed to delete ${file}:`, error);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
console.log(`\u2705 Cleaned up ${deleted} file${deleted > 1 ? "s" : ""}`);
|
|
984
|
+
}
|
|
985
|
+
function updatePackageJsonLint(root, configs) {
|
|
1077
986
|
const packageJsonPath = resolve(root, "package.json");
|
|
1078
|
-
if (!existsSync(packageJsonPath))
|
|
987
|
+
if (!existsSync(packageJsonPath)) {
|
|
988
|
+
console.warn("\u26A0\uFE0F No package.json found");
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
1079
991
|
try {
|
|
1080
992
|
const packageJson = JSON.parse(
|
|
1081
993
|
readFileSync(packageJsonPath, "utf-8")
|
|
@@ -1083,27 +995,156 @@ function updateWorkspaceScripts(root, projectName) {
|
|
|
1083
995
|
if (!packageJson.scripts) {
|
|
1084
996
|
packageJson.scripts = {};
|
|
1085
997
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
998
|
+
if (configs.includes("eslint")) {
|
|
999
|
+
if (!packageJson.scripts.lint) {
|
|
1000
|
+
packageJson.scripts.lint = "eslint .";
|
|
1001
|
+
}
|
|
1002
|
+
if (!packageJson.scripts["lint:fix"]) {
|
|
1003
|
+
packageJson.scripts["lint:fix"] = "eslint . --fix";
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (configs.includes("prettier")) {
|
|
1007
|
+
if (!packageJson.scripts.format) {
|
|
1008
|
+
packageJson.scripts.format = "prettier --write .";
|
|
1009
|
+
}
|
|
1010
|
+
if (!packageJson.scripts["format:check"]) {
|
|
1011
|
+
packageJson.scripts["format:check"] = "prettier --check .";
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1088
1014
|
writeFileSync(
|
|
1089
1015
|
packageJsonPath,
|
|
1090
1016
|
`${JSON.stringify(packageJson, null, 2)}
|
|
1091
1017
|
`
|
|
1092
1018
|
);
|
|
1093
|
-
console.log(
|
|
1019
|
+
console.log("\u2705 Updated package.json with lint scripts");
|
|
1094
1020
|
} catch (error) {
|
|
1095
|
-
console.
|
|
1021
|
+
console.error("\u274C Failed to update package.json:", error);
|
|
1096
1022
|
}
|
|
1097
1023
|
}
|
|
1098
|
-
|
|
1024
|
+
|
|
1025
|
+
async function generateSDK(root = process.cwd()) {
|
|
1026
|
+
console.log("\n\u{1F527} Generating SDK from OpenAPI...\n");
|
|
1027
|
+
let config = null;
|
|
1028
|
+
let openApiUrl;
|
|
1099
1029
|
try {
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1030
|
+
const configPath = resolve(root, "bgl.config.ts");
|
|
1031
|
+
if (existsSync(configPath)) {
|
|
1032
|
+
const module = await import(`file://${configPath}`);
|
|
1033
|
+
const workspace = module.default;
|
|
1034
|
+
if (typeof workspace === "function") {
|
|
1035
|
+
config = workspace("development");
|
|
1036
|
+
if (config?.openapi_url) {
|
|
1037
|
+
openApiUrl = config.openapi_url;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1104
1041
|
} catch {
|
|
1105
|
-
return [];
|
|
1106
1042
|
}
|
|
1043
|
+
const response = await prompts([
|
|
1044
|
+
{
|
|
1045
|
+
type: openApiUrl !== void 0 ? null : "text",
|
|
1046
|
+
name: "openApiUrl",
|
|
1047
|
+
message: "OpenAPI spec URL:",
|
|
1048
|
+
initial: openApiUrl ?? "http://localhost:8000/openapi.json"
|
|
1049
|
+
},
|
|
1050
|
+
{
|
|
1051
|
+
type: "text",
|
|
1052
|
+
name: "outputDir",
|
|
1053
|
+
message: "Output directory:",
|
|
1054
|
+
initial: "./src/api"
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
type: "confirm",
|
|
1058
|
+
name: "splitFiles",
|
|
1059
|
+
message: "Split into organized files?",
|
|
1060
|
+
initial: true
|
|
1061
|
+
}
|
|
1062
|
+
]);
|
|
1063
|
+
if (!response) {
|
|
1064
|
+
console.log("\n\u274C SDK generation cancelled.\n");
|
|
1065
|
+
process.exit(1);
|
|
1066
|
+
}
|
|
1067
|
+
const finalUrl = openApiUrl ?? response.openApiUrl;
|
|
1068
|
+
const { outputDir, splitFiles } = response;
|
|
1069
|
+
console.log(`
|
|
1070
|
+
\u{1F4E1} Fetching OpenAPI spec from: ${finalUrl}`);
|
|
1071
|
+
console.log(`\u{1F4C1} Output directory: ${outputDir}
|
|
1072
|
+
`);
|
|
1073
|
+
try {
|
|
1074
|
+
const { openAPI } = await import('@bagelink/sdk');
|
|
1075
|
+
const { types, code } = await openAPI(finalUrl, "/api");
|
|
1076
|
+
const outputPath = resolve(root, outputDir);
|
|
1077
|
+
if (!existsSync(outputPath)) {
|
|
1078
|
+
mkdirSync(outputPath, { recursive: true });
|
|
1079
|
+
}
|
|
1080
|
+
const typesPath = resolve(outputPath, "types.d.ts");
|
|
1081
|
+
writeFileSync(typesPath, types);
|
|
1082
|
+
console.log("\u2705 Generated types.d.ts");
|
|
1083
|
+
const apiPath = resolve(outputPath, "api.ts");
|
|
1084
|
+
writeFileSync(apiPath, code);
|
|
1085
|
+
console.log("\u2705 Generated api.ts");
|
|
1086
|
+
const indexPath = resolve(outputPath, "index.ts");
|
|
1087
|
+
writeFileSync(
|
|
1088
|
+
indexPath,
|
|
1089
|
+
"export * from './api'\nexport * from './types.d'\n"
|
|
1090
|
+
);
|
|
1091
|
+
console.log("\u2705 Generated index.ts");
|
|
1092
|
+
if (splitFiles) {
|
|
1093
|
+
console.log("\n\u{1F500} Splitting into organized files...");
|
|
1094
|
+
console.log("\u2139\uFE0F File splitting requires @bagelink/sdk bin scripts");
|
|
1095
|
+
console.log(" Keeping monolithic structure for now");
|
|
1096
|
+
}
|
|
1097
|
+
console.log("\n\u2705 SDK generated successfully!");
|
|
1098
|
+
console.log(`
|
|
1099
|
+
Import it in your code:`);
|
|
1100
|
+
console.log(` import { api } from '${outputDir.replace("./src/", "./")}'`);
|
|
1101
|
+
console.log("");
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
console.error("\n\u274C Failed to generate SDK:");
|
|
1104
|
+
if (error instanceof Error) {
|
|
1105
|
+
console.error(error.message);
|
|
1106
|
+
} else {
|
|
1107
|
+
console.error(error);
|
|
1108
|
+
}
|
|
1109
|
+
console.log("\nMake sure:");
|
|
1110
|
+
console.log(" 1. @bagelink/sdk is installed: bun add -D @bagelink/sdk");
|
|
1111
|
+
console.log(" 2. OpenAPI URL is accessible");
|
|
1112
|
+
console.log(" 3. API server is running (if using localhost)");
|
|
1113
|
+
process.exit(1);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
async function generateSDKForWorkspace(root = process.cwd()) {
|
|
1117
|
+
console.log("\n\u{1F3E2} Generating SDK for workspace projects...\n");
|
|
1118
|
+
const fs = await import('node:fs');
|
|
1119
|
+
const items = fs.readdirSync(root, { withFileTypes: true });
|
|
1120
|
+
const projects = items.filter(
|
|
1121
|
+
(item) => item.isDirectory() && item.name !== "node_modules" && item.name !== "shared" && item.name !== ".git" && !item.name.startsWith(".")
|
|
1122
|
+
).map((item) => item.name);
|
|
1123
|
+
if (projects.length === 0) {
|
|
1124
|
+
console.log("No projects found in workspace");
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
const response = await prompts({
|
|
1128
|
+
type: "multiselect",
|
|
1129
|
+
name: "selectedProjects",
|
|
1130
|
+
message: "Select projects to generate SDK for:",
|
|
1131
|
+
choices: projects.map((p) => ({ title: p, value: p, selected: true }))
|
|
1132
|
+
});
|
|
1133
|
+
if (!response || !response.selectedProjects || response.selectedProjects.length === 0) {
|
|
1134
|
+
console.log("\n\u274C No projects selected.\n");
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
for (const project of response.selectedProjects) {
|
|
1138
|
+
console.log(`
|
|
1139
|
+
\u{1F4E6} Generating SDK for: ${project}`);
|
|
1140
|
+
const projectPath = resolve(root, project);
|
|
1141
|
+
try {
|
|
1142
|
+
await generateSDK(projectPath);
|
|
1143
|
+
} catch {
|
|
1144
|
+
console.error(`Failed to generate SDK for ${project}`);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
console.log("\n\u2705 All SDKs generated!");
|
|
1107
1148
|
}
|
|
1108
1149
|
|
|
1109
1150
|
const [, , command, subcommand, ...args] = process.argv;
|
|
@@ -1181,10 +1222,21 @@ SDK Commands:
|
|
|
1181
1222
|
process.exit(1);
|
|
1182
1223
|
}
|
|
1183
1224
|
} else if (command === "dev") {
|
|
1184
|
-
const filter
|
|
1185
|
-
|
|
1225
|
+
const { filter, additionalArgs } = parseFilterArgs(
|
|
1226
|
+
void 0,
|
|
1227
|
+
subcommand,
|
|
1228
|
+
args
|
|
1229
|
+
);
|
|
1186
1230
|
const exitCode = await runDev(filter, additionalArgs);
|
|
1187
1231
|
process.exit(exitCode);
|
|
1232
|
+
} else if (command === "build") {
|
|
1233
|
+
const { filter, additionalArgs } = parseFilterArgs(
|
|
1234
|
+
void 0,
|
|
1235
|
+
subcommand,
|
|
1236
|
+
args
|
|
1237
|
+
);
|
|
1238
|
+
const exitCode = await runBuild(filter, additionalArgs);
|
|
1239
|
+
process.exit(exitCode);
|
|
1188
1240
|
} else {
|
|
1189
1241
|
console.log(`
|
|
1190
1242
|
Bagel Workspace CLI
|
|
@@ -1197,6 +1249,7 @@ Usage:
|
|
|
1197
1249
|
bgl list List all projects in workspace
|
|
1198
1250
|
bgl dev [filter] [...args] Run dev servers with clean output (default: './!shared*')
|
|
1199
1251
|
Additional args are passed to vite (e.g., --mode localhost)
|
|
1252
|
+
bgl build [project] [...args] Build project by directory (default: all projects)
|
|
1200
1253
|
bgl lint init Set up linting (auto-detects workspace)
|
|
1201
1254
|
bgl sdk generate Generate SDK (auto-detects workspace)
|
|
1202
1255
|
|
|
@@ -1210,6 +1263,18 @@ Note: Commands auto-detect workspace mode based on directory structure
|
|
|
1210
1263
|
process.exit(command === "--help" || command === "-h" ? 0 : 1);
|
|
1211
1264
|
}
|
|
1212
1265
|
}
|
|
1266
|
+
function normalizeFilter(input) {
|
|
1267
|
+
if (input.startsWith(".")) return input;
|
|
1268
|
+
return `./${input}`;
|
|
1269
|
+
}
|
|
1270
|
+
function parseFilterArgs(defaultFilter, subcommandArg, argsList = []) {
|
|
1271
|
+
const tokens = [subcommandArg, ...argsList].filter(Boolean);
|
|
1272
|
+
const nonFlagIndexes = tokens.map((token, index) => token.startsWith("-") ? -1 : index).filter((index) => index >= 0);
|
|
1273
|
+
const filterIndex = nonFlagIndexes.length > 0 ? nonFlagIndexes[nonFlagIndexes.length - 1] : -1;
|
|
1274
|
+
const filter = filterIndex >= 0 ? normalizeFilter(tokens[filterIndex]) : defaultFilter;
|
|
1275
|
+
const additionalArgs = filterIndex >= 0 ? tokens.filter((_, index) => index !== filterIndex) : tokens;
|
|
1276
|
+
return { filter, additionalArgs };
|
|
1277
|
+
}
|
|
1213
1278
|
main().catch((error) => {
|
|
1214
1279
|
console.error("Error:", error);
|
|
1215
1280
|
process.exit(1);
|