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