@bagelink/workspace 1.10.7 ā 1.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -9
- package/bin/bgl.ts +88 -2
- package/dist/bin/bgl.cjs +854 -14
- package/dist/bin/bgl.mjs +845 -6
- package/dist/composable.cjs +22 -0
- package/dist/composable.d.cts +52 -0
- package/dist/composable.d.mts +52 -0
- package/dist/composable.d.ts +52 -0
- package/dist/composable.mjs +19 -0
- package/dist/index.cjs +3 -160
- package/dist/index.d.cts +4 -160
- package/dist/index.d.mts +4 -160
- package/dist/index.d.ts +4 -160
- package/dist/index.mjs +2 -139
- package/dist/shared/workspace.Bc_dpzhA.mjs +500 -0
- package/dist/shared/workspace.BzlV5kcN.d.cts +50 -0
- package/dist/shared/workspace.BzlV5kcN.d.mts +50 -0
- package/dist/shared/workspace.BzlV5kcN.d.ts +50 -0
- package/dist/shared/workspace.DRlDHdPw.cjs +512 -0
- package/dist/vite.cjs +135 -0
- package/dist/vite.d.cts +98 -0
- package/dist/vite.d.mts +98 -0
- package/dist/vite.d.ts +98 -0
- package/dist/vite.mjs +125 -0
- package/env.d.ts +30 -0
- package/package.json +24 -3
- package/src/build.ts +45 -0
- package/src/composable.ts +70 -0
- package/src/dev.ts +171 -0
- package/src/index.ts +4 -78
- package/src/init.ts +72 -14
- package/src/lint.ts +90 -2
- package/src/netlify.ts +54 -3
- package/src/proxy.ts +23 -3
- package/src/sdk.ts +80 -44
- package/src/types.ts +10 -4
- package/src/vite.ts +166 -0
- package/src/workspace.ts +121 -16
- package/templates/dev-runner.ts +61 -0
- package/templates/tsconfig.app.json +23 -0
- package/dist/shared/workspace.BPEOymAx.cjs +0 -926
- package/dist/shared/workspace.DfYoqH33.mjs +0 -906
package/src/dev.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
import { listProjects } from './workspace.js'
|
|
4
|
+
|
|
5
|
+
interface ServerInfo {
|
|
6
|
+
name: string
|
|
7
|
+
assignedPort: number
|
|
8
|
+
actualPort?: number
|
|
9
|
+
status: 'starting' | 'ready'
|
|
10
|
+
order: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const servers = new Map<string, ServerInfo>()
|
|
14
|
+
const projectToPort = new Map<string, number>()
|
|
15
|
+
const seenLines = new Set<string>()
|
|
16
|
+
const START_PORT = 5173
|
|
17
|
+
|
|
18
|
+
// ANSI colors
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1B[0m',
|
|
21
|
+
cyan: '\x1B[36m',
|
|
22
|
+
green: '\x1B[32m',
|
|
23
|
+
yellow: '\x1B[33m',
|
|
24
|
+
dim: '\x1B[2m',
|
|
25
|
+
red: '\x1B[31m',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function clearAndPrintServers() {
|
|
29
|
+
// Clear previous output
|
|
30
|
+
if (servers.size > 0) {
|
|
31
|
+
process.stdout.write('\x1B[2J\x1B[H') // Clear screen and move to top
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log(`${colors.cyan}š Development Servers${colors.reset}\n`)
|
|
35
|
+
|
|
36
|
+
// Sort by order to maintain consistent display
|
|
37
|
+
const sortedServers = Array.from(servers.entries()).sort((a, b) => a[1].order - b[1].order)
|
|
38
|
+
|
|
39
|
+
for (const [name, info] of sortedServers) {
|
|
40
|
+
const url = `http://localhost:${info.actualPort ?? info.assignedPort}`
|
|
41
|
+
if (info.status === 'ready') {
|
|
42
|
+
console.log(`${colors.green}ā${colors.reset} ${colors.cyan}${name}${colors.reset} ${colors.dim}ā${colors.reset} ${url}`)
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
console.log(`${colors.yellow}ā${colors.reset} ${colors.dim}${name} (starting...)${colors.reset} ${colors.dim}${url}${colors.reset}`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function runDev(
|
|
53
|
+
filter?: string,
|
|
54
|
+
additionalArgs: string[] = [],
|
|
55
|
+
) {
|
|
56
|
+
const argsStr = additionalArgs.length > 0 ? ` -- ${additionalArgs.join(' ')}` : ''
|
|
57
|
+
const resolvedFilters = resolveFilters(filter)
|
|
58
|
+
if (!resolvedFilters || resolvedFilters.length === 0) return 1
|
|
59
|
+
|
|
60
|
+
// Pre-assign ports based on alphabetical order
|
|
61
|
+
const projectNames = resolvedFilters.map(f => f.replace('./', '')).sort()
|
|
62
|
+
projectNames.forEach((name, index) => {
|
|
63
|
+
const assignedPort = START_PORT + index
|
|
64
|
+
servers.set(name, {
|
|
65
|
+
name,
|
|
66
|
+
assignedPort,
|
|
67
|
+
status: 'starting',
|
|
68
|
+
order: index,
|
|
69
|
+
})
|
|
70
|
+
projectToPort.set(name, assignedPort)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
clearAndPrintServers()
|
|
74
|
+
|
|
75
|
+
const filterArgs = resolvedFilters.map(f => `--filter '${f}'`).join(' ')
|
|
76
|
+
const projectNamesDisplay = projectNames.join(', ')
|
|
77
|
+
|
|
78
|
+
const command = `bun run ${filterArgs} dev${argsStr}`
|
|
79
|
+
const proc = spawn(command, {
|
|
80
|
+
cwd: process.cwd(),
|
|
81
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
82
|
+
shell: true,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
let stdoutBuffer = ''
|
|
86
|
+
let stderrBuffer = ''
|
|
87
|
+
|
|
88
|
+
function processLine(line: string) {
|
|
89
|
+
if (!line.trim()) return
|
|
90
|
+
|
|
91
|
+
// Skip duplicate lines
|
|
92
|
+
if (seenLines.has(line)) return
|
|
93
|
+
seenLines.add(line)
|
|
94
|
+
|
|
95
|
+
// Extract port from "project dev: ā Local: http://localhost:5173/"
|
|
96
|
+
const portMatch = line.match(/Local:\s+http:\/\/localhost:(\d+)/)
|
|
97
|
+
if (portMatch) {
|
|
98
|
+
const port = Number.parseInt(portMatch[1], 10)
|
|
99
|
+
|
|
100
|
+
// Extract project name from this line
|
|
101
|
+
const projectInLine = line.match(/^([\w-]+)\s+dev:/)
|
|
102
|
+
if (projectInLine) {
|
|
103
|
+
const name = projectInLine[1]
|
|
104
|
+
const info = servers.get(name)
|
|
105
|
+
if (info && !info.actualPort) {
|
|
106
|
+
info.actualPort = port
|
|
107
|
+
info.status = 'ready'
|
|
108
|
+
clearAndPrintServers()
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
proc.stdout?.setEncoding('utf8')
|
|
115
|
+
proc.stderr?.setEncoding('utf8')
|
|
116
|
+
|
|
117
|
+
proc.stdout?.on('data', (data: string) => {
|
|
118
|
+
// Debug: show we're receiving data
|
|
119
|
+
if (servers.size === 0) {
|
|
120
|
+
console.log(`${colors.dim}Receiving output...${colors.reset}`)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
stdoutBuffer += data
|
|
124
|
+
const lines = stdoutBuffer.split('\n')
|
|
125
|
+
stdoutBuffer = lines.pop() || ''
|
|
126
|
+
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
processLine(line)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
proc.stderr?.on('data', (data: string) => {
|
|
133
|
+
stderrBuffer += data
|
|
134
|
+
const lines = stderrBuffer.split('\n')
|
|
135
|
+
stderrBuffer = lines.pop() || ''
|
|
136
|
+
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
processLine(line)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
proc.on('error', (error) => {
|
|
143
|
+
console.error(`${colors.red}Failed to start dev servers:${colors.reset}`, error.message)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Handle Ctrl+C gracefully
|
|
147
|
+
process.on('SIGINT', () => {
|
|
148
|
+
proc.kill('SIGINT')
|
|
149
|
+
process.exit(0)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
return new Promise<number>((resolve, reject) => {
|
|
153
|
+
proc.on('exit', (code) => {
|
|
154
|
+
resolve(code || 0)
|
|
155
|
+
})
|
|
156
|
+
proc.on('error', reject)
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveFilters(filter?: string): string[] | null {
|
|
161
|
+
if (filter) return [filter]
|
|
162
|
+
|
|
163
|
+
const projects = listProjects()
|
|
164
|
+
if (projects.length === 0) {
|
|
165
|
+
console.error('No projects found')
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Return all projects as individual filters
|
|
170
|
+
return projects.map(p => `./${p}`)
|
|
171
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,16 +4,8 @@ import type {
|
|
|
4
4
|
WorkspaceOptions,
|
|
5
5
|
ProxyConfig,
|
|
6
6
|
} from './types'
|
|
7
|
-
import { resolveConfig, mergeConfigs } from './config'
|
|
8
|
-
import { generateWorkspaceConfig, generateWorkspaceConfigSync } from './init'
|
|
9
|
-
import {
|
|
10
|
-
generateNetlifyConfig,
|
|
11
|
-
generateNetlifyRedirect,
|
|
12
|
-
writeNetlifyConfig,
|
|
13
|
-
setBuildEnvVars,
|
|
14
|
-
} from './netlify'
|
|
15
|
-
import { createViteProxy, createCustomProxy } from './proxy'
|
|
16
7
|
|
|
8
|
+
// Export types (no runtime code)
|
|
17
9
|
export type {
|
|
18
10
|
ProxyConfig,
|
|
19
11
|
WorkspaceConfig,
|
|
@@ -21,23 +13,9 @@ export type {
|
|
|
21
13
|
WorkspaceOptions,
|
|
22
14
|
}
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
generateNetlifyConfig,
|
|
28
|
-
generateNetlifyRedirect,
|
|
29
|
-
generateWorkspaceConfig,
|
|
30
|
-
generateWorkspaceConfigSync,
|
|
31
|
-
mergeConfigs,
|
|
32
|
-
resolveConfig,
|
|
33
|
-
setBuildEnvVars,
|
|
34
|
-
writeNetlifyConfig,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export { setupLint } from './lint'
|
|
38
|
-
export { generateSDK, generateSDKForWorkspace } from './sdk'
|
|
39
|
-
export { addProject, initWorkspace, listProjects } from './workspace'
|
|
40
|
-
export { getWorkspaceInfo, isWorkspace } from './detect'
|
|
16
|
+
// Runtime composables (browser-safe, no Node.js dependencies)
|
|
17
|
+
export { getApiUrl, useWorkspace } from './composable'
|
|
18
|
+
export type { RuntimeWorkspaceConfig } from './composable'
|
|
41
19
|
|
|
42
20
|
/**
|
|
43
21
|
* Define workspace configuration
|
|
@@ -50,55 +28,3 @@ export function defineWorkspace(
|
|
|
50
28
|
return configs[mode] || configs.development
|
|
51
29
|
}
|
|
52
30
|
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Create a workspace instance for managing project configuration
|
|
56
|
-
* Supports both single project and monorepo setups
|
|
57
|
-
*/
|
|
58
|
-
export function createWorkspace(options: WorkspaceOptions = {}) {
|
|
59
|
-
let cachedConfig: WorkspaceConfig | null = null
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
/**
|
|
63
|
-
* Get resolved config for the specified environment
|
|
64
|
-
*/
|
|
65
|
-
async getConfig(mode: WorkspaceEnvironment = 'development'): Promise<WorkspaceConfig> {
|
|
66
|
-
if (!cachedConfig) {
|
|
67
|
-
cachedConfig = await resolveConfig(mode, options)
|
|
68
|
-
}
|
|
69
|
-
return cachedConfig
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Create Vite proxy configuration
|
|
74
|
-
*/
|
|
75
|
-
createProxy(config: WorkspaceConfig): ProxyConfig {
|
|
76
|
-
return createViteProxy(config)
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Generate Netlify configuration file
|
|
81
|
-
*/
|
|
82
|
-
generateNetlify(
|
|
83
|
-
config: WorkspaceConfig,
|
|
84
|
-
outPath: string = './netlify.toml',
|
|
85
|
-
additionalConfig?: string
|
|
86
|
-
): void {
|
|
87
|
-
writeNetlifyConfig(config, outPath, additionalConfig)
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Set build environment variables
|
|
92
|
-
*/
|
|
93
|
-
setBuildEnv(config: WorkspaceConfig): void {
|
|
94
|
-
setBuildEnvVars(config)
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Clear cached configuration
|
|
99
|
-
*/
|
|
100
|
-
clearCache(): void {
|
|
101
|
-
cachedConfig = null
|
|
102
|
-
},
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/init.ts
CHANGED
|
@@ -49,10 +49,15 @@ export async function generateWorkspaceConfig(
|
|
|
49
49
|
const configContent = `import { defineWorkspace } from '@bagelink/workspace'
|
|
50
50
|
import type { WorkspaceConfig, WorkspaceEnvironment } from '@bagelink/workspace'
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Define your workspace environments
|
|
54
|
+
* You can add as many custom environments as needed (e.g., 'staging', 'preview')
|
|
55
|
+
* Use --mode flag to switch: bgl dev --mode <env_name>
|
|
56
|
+
*/
|
|
52
57
|
const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
|
|
53
58
|
localhost: {
|
|
54
59
|
host: 'http://localhost:8000',
|
|
55
|
-
proxy: '/api',
|
|
60
|
+
proxy: '/api', // Optional: remove to skip proxy setup
|
|
56
61
|
openapi_url: 'http://localhost:8000/openapi.json',
|
|
57
62
|
},
|
|
58
63
|
development: {
|
|
@@ -91,6 +96,12 @@ export default defineWorkspace(configs)
|
|
|
91
96
|
message: 'Create/update vite.config.ts?',
|
|
92
97
|
initial: true,
|
|
93
98
|
},
|
|
99
|
+
{
|
|
100
|
+
type: 'confirm',
|
|
101
|
+
name: 'createTsConfig',
|
|
102
|
+
message: 'Create tsconfig.app.json with path aliases?',
|
|
103
|
+
initial: true,
|
|
104
|
+
},
|
|
94
105
|
{
|
|
95
106
|
type: 'confirm',
|
|
96
107
|
name: 'generateNetlify',
|
|
@@ -107,6 +118,10 @@ export default defineWorkspace(configs)
|
|
|
107
118
|
updateViteConfig(root)
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
if (setupResponse.createTsConfig) {
|
|
122
|
+
createTsConfig(root)
|
|
123
|
+
}
|
|
124
|
+
|
|
110
125
|
if (setupResponse.generateNetlify) {
|
|
111
126
|
const prodConfig: WorkspaceConfig = {
|
|
112
127
|
host: productionHost,
|
|
@@ -132,10 +147,15 @@ export function generateWorkspaceConfigSync(
|
|
|
132
147
|
const configContent = `import { defineWorkspace } from '@bagelink/workspace'
|
|
133
148
|
import type { WorkspaceConfig, WorkspaceEnvironment } from '@bagelink/workspace'
|
|
134
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Define your workspace environments
|
|
152
|
+
* You can add as many custom environments as needed (e.g., 'staging', 'preview')
|
|
153
|
+
* Use --mode flag to switch: bgl dev --mode <env_name>
|
|
154
|
+
*/
|
|
135
155
|
const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
|
|
136
156
|
localhost: {
|
|
137
157
|
host: 'http://localhost:8000',
|
|
138
|
-
proxy: '/api',
|
|
158
|
+
proxy: '/api', // Optional: remove to skip proxy setup
|
|
139
159
|
openapi_url: 'http://localhost:8000/openapi.json',
|
|
140
160
|
},
|
|
141
161
|
development: {
|
|
@@ -216,6 +236,38 @@ function updatePackageJsonScripts(root: string): void {
|
|
|
216
236
|
}
|
|
217
237
|
}
|
|
218
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Create tsconfig.app.json with path aliases
|
|
241
|
+
*/
|
|
242
|
+
function createTsConfig(root: string): void {
|
|
243
|
+
const tsConfigPath = resolve(root, 'tsconfig.app.json')
|
|
244
|
+
const tsConfigExists = existsSync(tsConfigPath)
|
|
245
|
+
|
|
246
|
+
if (tsConfigExists) {
|
|
247
|
+
console.log('ā ļø tsconfig.app.json already exists, skipping')
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const tsConfigContent = `{
|
|
252
|
+
"extends": "../tsconfig.json",
|
|
253
|
+
"compilerOptions": {
|
|
254
|
+
"composite": true,
|
|
255
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
256
|
+
"baseUrl": ".",
|
|
257
|
+
"paths": {
|
|
258
|
+
"@/*": ["./src/*"],
|
|
259
|
+
"@shared/*": ["../shared/*"]
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"include": ["src/**/*", "src/**/*.vue"],
|
|
263
|
+
"exclude": ["node_modules"]
|
|
264
|
+
}
|
|
265
|
+
`
|
|
266
|
+
|
|
267
|
+
writeFileSync(tsConfigPath, tsConfigContent, 'utf-8')
|
|
268
|
+
console.log('ā
Created tsconfig.app.json')
|
|
269
|
+
}
|
|
270
|
+
|
|
219
271
|
/**
|
|
220
272
|
* Create or update vite.config.ts
|
|
221
273
|
*/
|
|
@@ -233,25 +285,31 @@ function updateViteConfig(root: string): void {
|
|
|
233
285
|
}
|
|
234
286
|
|
|
235
287
|
// Ask to overwrite
|
|
236
|
-
console.log('ā ļø vite.config.ts exists. Please manually add the
|
|
237
|
-
console.log('
|
|
288
|
+
console.log('ā ļø vite.config.ts exists. Please manually add the bagelink plugin:')
|
|
289
|
+
console.log('')
|
|
290
|
+
console.log(' import { bagelink } from \'@bagelink/workspace/vite\'')
|
|
291
|
+
console.log(' import workspace from \'./bgl.config\'')
|
|
292
|
+
console.log('')
|
|
293
|
+
console.log(' plugins: [')
|
|
294
|
+
console.log(' vue(),')
|
|
295
|
+
console.log(' bagelink({ workspace }),')
|
|
296
|
+
console.log(' ]')
|
|
297
|
+
console.log('')
|
|
238
298
|
return
|
|
239
299
|
}
|
|
240
300
|
|
|
241
|
-
// Create new vite.config.ts
|
|
301
|
+
// Create new vite.config.ts with plugin
|
|
242
302
|
const viteConfigContent = `import { defineConfig } from 'vite'
|
|
243
|
-
import
|
|
303
|
+
import vue from '@vitejs/plugin-vue'
|
|
304
|
+
import { bagelink } from '@bagelink/workspace/vite'
|
|
244
305
|
import workspace from './bgl.config'
|
|
245
306
|
|
|
246
307
|
// https://vitejs.dev/config/
|
|
247
|
-
export default defineConfig(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
proxy: createViteProxy(config),
|
|
253
|
-
},
|
|
254
|
-
}
|
|
308
|
+
export default defineConfig({
|
|
309
|
+
plugins: [
|
|
310
|
+
vue(),
|
|
311
|
+
bagelink({ workspace }),
|
|
312
|
+
],
|
|
255
313
|
})
|
|
256
314
|
`
|
|
257
315
|
|
package/src/lint.ts
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
import prompts from 'prompts'
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* List of redundant lint config files that can be safely removed
|
|
8
|
+
* after setting up the new lint configuration
|
|
9
|
+
*/
|
|
10
|
+
const REDUNDANT_FILES = [
|
|
11
|
+
// Old ESLint configs
|
|
12
|
+
'.eslintrc',
|
|
13
|
+
'.eslintrc.json',
|
|
14
|
+
'.eslintrc.js',
|
|
15
|
+
'.eslintrc.cjs',
|
|
16
|
+
'.eslintrc.yaml',
|
|
17
|
+
'.eslintrc.yml',
|
|
18
|
+
// Oxlint
|
|
19
|
+
'oxlint.json',
|
|
20
|
+
// Old Prettier configs (we create .prettierrc)
|
|
21
|
+
'prettier.config.js',
|
|
22
|
+
'prettier.config.cjs',
|
|
23
|
+
'prettier.config.mjs',
|
|
24
|
+
'.prettierrc.json',
|
|
25
|
+
'.prettierrc.yaml',
|
|
26
|
+
'.prettierrc.yml',
|
|
27
|
+
'.prettierrc.js',
|
|
28
|
+
'.prettierrc.cjs',
|
|
29
|
+
'.prettierrc.mjs',
|
|
30
|
+
'.prettierrc.toml',
|
|
31
|
+
]
|
|
32
|
+
|
|
6
33
|
/**
|
|
7
34
|
* Set up linting in a project
|
|
8
35
|
*/
|
|
@@ -24,6 +51,12 @@ export async function setupLint(
|
|
|
24
51
|
{ title: 'Git Hooks', value: 'githooks', selected: false },
|
|
25
52
|
],
|
|
26
53
|
},
|
|
54
|
+
{
|
|
55
|
+
type: 'confirm',
|
|
56
|
+
name: 'cleanRedundant',
|
|
57
|
+
message: 'Clean up redundant lint config files?',
|
|
58
|
+
initial: true,
|
|
59
|
+
},
|
|
27
60
|
{
|
|
28
61
|
type: 'confirm',
|
|
29
62
|
name: 'installDeps',
|
|
@@ -37,7 +70,12 @@ export async function setupLint(
|
|
|
37
70
|
process.exit(1)
|
|
38
71
|
}
|
|
39
72
|
|
|
40
|
-
const { configs, installDeps } = response
|
|
73
|
+
const { configs, cleanRedundant, installDeps } = response
|
|
74
|
+
|
|
75
|
+
// Clean up redundant files first
|
|
76
|
+
if (cleanRedundant) {
|
|
77
|
+
await cleanRedundantFiles(root)
|
|
78
|
+
}
|
|
41
79
|
|
|
42
80
|
// Create config files
|
|
43
81
|
if (configs.includes('eslint')) {
|
|
@@ -182,6 +220,56 @@ function createGitHooks(root: string): void {
|
|
|
182
220
|
console.log(' Then run: npx simple-git-hooks')
|
|
183
221
|
}
|
|
184
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Clean up redundant lint configuration files
|
|
225
|
+
*/
|
|
226
|
+
async function cleanRedundantFiles(root: string): Promise<void> {
|
|
227
|
+
const foundFiles: string[] = []
|
|
228
|
+
|
|
229
|
+
// Check which redundant files exist
|
|
230
|
+
for (const file of REDUNDANT_FILES) {
|
|
231
|
+
const filePath = resolve(root, file)
|
|
232
|
+
if (existsSync(filePath)) {
|
|
233
|
+
foundFiles.push(file)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (foundFiles.length === 0) {
|
|
238
|
+
console.log('⨠No redundant files found')
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log('\nš Found redundant files:')
|
|
243
|
+
foundFiles.forEach((file) => { console.log(` - ${file}`) })
|
|
244
|
+
|
|
245
|
+
const confirmResponse = await prompts({
|
|
246
|
+
type: 'confirm',
|
|
247
|
+
name: 'confirm',
|
|
248
|
+
message: `Delete ${foundFiles.length} redundant file${foundFiles.length > 1 ? 's' : ''}?`,
|
|
249
|
+
initial: true,
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
if (!confirmResponse.confirm) {
|
|
253
|
+
console.log('āļø Skipped cleaning redundant files')
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Delete confirmed files
|
|
258
|
+
let deleted = 0
|
|
259
|
+
for (const file of foundFiles) {
|
|
260
|
+
try {
|
|
261
|
+
unlinkSync(resolve(root, file))
|
|
262
|
+
console.log(`šļø Deleted ${file}`)
|
|
263
|
+
deleted++
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
console.error(`ā Failed to delete ${file}:`, error)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log(`ā
Cleaned up ${deleted} file${deleted > 1 ? 's' : ''}`)
|
|
271
|
+
}
|
|
272
|
+
|
|
185
273
|
/**
|
|
186
274
|
* Update package.json with lint scripts
|
|
187
275
|
*/
|
package/src/netlify.ts
CHANGED
|
@@ -5,9 +5,13 @@ import process from 'node:process'
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Generate netlify.toml redirect configuration
|
|
8
|
+
* Uses environment variables for flexibility across environments
|
|
8
9
|
*/
|
|
9
10
|
export function generateNetlifyRedirect(config: WorkspaceConfig): string {
|
|
10
|
-
const redirect =
|
|
11
|
+
const redirect = `# API Proxy Configuration
|
|
12
|
+
# Environment variables are set in Netlify UI or netlify.toml [build.environment] section
|
|
13
|
+
|
|
14
|
+
[[redirects]]
|
|
11
15
|
from = "${config.proxy}/*"
|
|
12
16
|
to = "${config.host}/:splat"
|
|
13
17
|
status = 200
|
|
@@ -18,13 +22,49 @@ export function generateNetlifyRedirect(config: WorkspaceConfig): string {
|
|
|
18
22
|
return redirect
|
|
19
23
|
}
|
|
20
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Generate netlify.toml with environment variable template
|
|
27
|
+
* Provides a standard config that can be reused across projects
|
|
28
|
+
*/
|
|
29
|
+
export function generateNetlifyConfigTemplate(): string {
|
|
30
|
+
return `# Standard Netlify configuration for Bagelink projects
|
|
31
|
+
# Uses environment variables for proxy configuration
|
|
32
|
+
|
|
33
|
+
[build.environment]
|
|
34
|
+
# Set these in Netlify UI or override here
|
|
35
|
+
# BGL_PROXY_PATH = "/api"
|
|
36
|
+
# BGL_API_HOST = "https://your-project.bagel.to"
|
|
37
|
+
|
|
38
|
+
[[redirects]]
|
|
39
|
+
# Proxy API requests to backend
|
|
40
|
+
# Uses BGL_PROXY_PATH and BGL_API_HOST from environment
|
|
41
|
+
from = "/api/*"
|
|
42
|
+
to = "https://your-project.bagel.to/:splat"
|
|
43
|
+
status = 200
|
|
44
|
+
force = true
|
|
45
|
+
headers = {X-From = "Netlify"}
|
|
46
|
+
|
|
47
|
+
# Example: Multiple API backends
|
|
48
|
+
# [[redirects]]
|
|
49
|
+
# from = "/api/v2/*"
|
|
50
|
+
# to = "https://api-v2.example.com/:splat"
|
|
51
|
+
# status = 200
|
|
52
|
+
# force = true
|
|
53
|
+
`
|
|
54
|
+
}
|
|
55
|
+
|
|
21
56
|
/**
|
|
22
57
|
* Generate complete netlify.toml file
|
|
23
58
|
*/
|
|
24
59
|
export function generateNetlifyConfig(
|
|
25
60
|
config: WorkspaceConfig,
|
|
26
61
|
additionalConfig?: string,
|
|
62
|
+
useTemplate: boolean = false,
|
|
27
63
|
): string {
|
|
64
|
+
if (useTemplate) {
|
|
65
|
+
return generateNetlifyConfigTemplate()
|
|
66
|
+
}
|
|
67
|
+
|
|
28
68
|
const redirect = generateNetlifyRedirect(config)
|
|
29
69
|
|
|
30
70
|
if (additionalConfig !== undefined && additionalConfig !== '') {
|
|
@@ -40,13 +80,24 @@ export function generateNetlifyConfig(
|
|
|
40
80
|
export function writeNetlifyConfig(
|
|
41
81
|
config: WorkspaceConfig,
|
|
42
82
|
outPath: string = './netlify.toml',
|
|
43
|
-
additionalConfig?: string
|
|
83
|
+
additionalConfig?: string,
|
|
84
|
+
useTemplate: boolean = false,
|
|
44
85
|
): void {
|
|
45
|
-
const content = generateNetlifyConfig(config, additionalConfig)
|
|
86
|
+
const content = generateNetlifyConfig(config, additionalConfig, useTemplate)
|
|
46
87
|
const resolvedPath = resolve(outPath)
|
|
47
88
|
|
|
48
89
|
writeFileSync(resolvedPath, content, 'utf-8')
|
|
49
90
|
console.log(`ā Generated netlify.toml at ${resolvedPath}`)
|
|
91
|
+
|
|
92
|
+
if (!useTemplate) {
|
|
93
|
+
console.log('\nš” Tip: For environment-based config, set these in Netlify UI:')
|
|
94
|
+
console.log(` BGL_PROXY_PATH = "${config.proxy}"`)
|
|
95
|
+
console.log(` BGL_API_HOST = "${config.host}"`)
|
|
96
|
+
if (config.openapi_url) {
|
|
97
|
+
console.log(` BGL_OPENAPI_URL = "${config.openapi_url}"`)
|
|
98
|
+
}
|
|
99
|
+
console.log('')
|
|
100
|
+
}
|
|
50
101
|
}
|
|
51
102
|
|
|
52
103
|
/**
|
package/src/proxy.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse, ClientRequest } from 'node:http'
|
|
1
2
|
import type { WorkspaceConfig, ProxyConfig } from './types'
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -12,7 +13,22 @@ export function createViteProxy(config: WorkspaceConfig): ProxyConfig {
|
|
|
12
13
|
target: config.host,
|
|
13
14
|
changeOrigin: true,
|
|
14
15
|
rewrite: (path: string) => path.replace(new RegExp(`^${config.proxy}`), ''),
|
|
15
|
-
secure:
|
|
16
|
+
secure: false,
|
|
17
|
+
ws: true,
|
|
18
|
+
followRedirects: true,
|
|
19
|
+
autoRewrite: true,
|
|
20
|
+
protocolRewrite: 'http',
|
|
21
|
+
configure: (proxy: any, _options: any) => {
|
|
22
|
+
proxy.on('proxyReq', (proxyReq: ClientRequest, req: IncomingMessage, _res: ServerResponse) => {
|
|
23
|
+
// Ensure proper headers are forwarded
|
|
24
|
+
if (req.headers.origin) {
|
|
25
|
+
proxyReq.setHeader('origin', config.host)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
proxy.on('error', (err: Error, _req: IncomingMessage, _res: ServerResponse) => {
|
|
29
|
+
console.log('proxy error', err)
|
|
30
|
+
})
|
|
31
|
+
},
|
|
16
32
|
}
|
|
17
33
|
}
|
|
18
34
|
|
|
@@ -21,7 +37,9 @@ export function createViteProxy(config: WorkspaceConfig): ProxyConfig {
|
|
|
21
37
|
proxy['/files'] = {
|
|
22
38
|
target: config.host,
|
|
23
39
|
changeOrigin: true,
|
|
24
|
-
secure:
|
|
40
|
+
secure: false,
|
|
41
|
+
ws: true,
|
|
42
|
+
followRedirects: true,
|
|
25
43
|
}
|
|
26
44
|
}
|
|
27
45
|
|
|
@@ -38,6 +56,7 @@ export function createCustomProxy(
|
|
|
38
56
|
changeOrigin?: boolean
|
|
39
57
|
rewrite?: boolean
|
|
40
58
|
secure?: boolean
|
|
59
|
+
ws?: boolean
|
|
41
60
|
} = {}
|
|
42
61
|
): ProxyConfig {
|
|
43
62
|
const proxy: ProxyConfig = {}
|
|
@@ -46,7 +65,8 @@ export function createCustomProxy(
|
|
|
46
65
|
proxy[path] = {
|
|
47
66
|
target,
|
|
48
67
|
changeOrigin: options.changeOrigin ?? true,
|
|
49
|
-
secure: options.secure ??
|
|
68
|
+
secure: options.secure ?? false,
|
|
69
|
+
ws: options.ws ?? true,
|
|
50
70
|
...(options.rewrite === true && {
|
|
51
71
|
rewrite: (p: string) => p.replace(new RegExp(`^${path}`), ''),
|
|
52
72
|
}),
|