@bagelink/workspace 1.8.0 ā 1.8.5
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 +290 -7
- package/bin/bgl.ts +160 -11
- package/dist/bin/bgl.cjs +1283 -18
- package/dist/bin/bgl.mjs +1279 -15
- package/dist/composable.cjs +21 -0
- package/dist/composable.d.cts +50 -0
- package/dist/composable.d.mts +50 -0
- package/dist/composable.d.ts +50 -0
- package/dist/composable.mjs +18 -0
- package/dist/index.cjs +3 -155
- package/dist/index.d.cts +4 -133
- package/dist/index.d.mts +4 -133
- package/dist/index.d.ts +4 -133
- package/dist/index.mjs +2 -139
- package/dist/shared/workspace.CefUteAh.d.cts +49 -0
- package/dist/shared/workspace.CefUteAh.d.mts +49 -0
- package/dist/shared/workspace.CefUteAh.d.ts +49 -0
- package/dist/shared/workspace.Dx9_TIij.cjs +86 -0
- package/dist/shared/workspace.Twuo1PFw.mjs +78 -0
- package/dist/vite.cjs +110 -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 +99 -0
- package/env.d.ts +29 -0
- package/package.json +28 -5
- package/src/build.ts +45 -0
- package/src/composable.ts +65 -0
- package/src/detect.ts +90 -0
- package/src/dev.ts +162 -0
- package/src/index.ts +11 -71
- package/src/init.ts +60 -12
- package/src/lint.ts +323 -0
- package/src/netlify.ts +54 -3
- package/src/proxy.ts +23 -3
- package/src/sdk.ts +196 -0
- package/src/types.ts +5 -0
- package/src/vite.ts +139 -0
- package/src/workspace.ts +107 -23
- package/templates/dev-runner.ts +61 -0
- package/templates/tsconfig.app.json +23 -0
- package/dist/shared/workspace.Bwsdwbt-.cjs +0 -575
- package/dist/shared/workspace.Dq-27S1f.mjs +0 -560
package/src/lint.ts
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import prompts from 'prompts'
|
|
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
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Set up linting in a project
|
|
35
|
+
*/
|
|
36
|
+
export async function setupLint(
|
|
37
|
+
root: string = process.cwd(),
|
|
38
|
+
isWorkspace: boolean = false,
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
console.log('\nš Setting up linting...\n')
|
|
41
|
+
|
|
42
|
+
const response = await prompts([
|
|
43
|
+
{
|
|
44
|
+
type: 'multiselect',
|
|
45
|
+
name: 'configs',
|
|
46
|
+
message: 'Select configurations to set up:',
|
|
47
|
+
choices: [
|
|
48
|
+
{ title: 'ESLint', value: 'eslint', selected: true },
|
|
49
|
+
{ title: 'Prettier', value: 'prettier', selected: true },
|
|
50
|
+
{ title: 'EditorConfig', value: 'editorconfig', selected: true },
|
|
51
|
+
{ title: 'Git Hooks', value: 'githooks', selected: false },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'confirm',
|
|
56
|
+
name: 'cleanRedundant',
|
|
57
|
+
message: 'Clean up redundant lint config files?',
|
|
58
|
+
initial: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
type: 'confirm',
|
|
62
|
+
name: 'installDeps',
|
|
63
|
+
message: 'Install dependencies?',
|
|
64
|
+
initial: true,
|
|
65
|
+
},
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
if (!response || !response.configs) {
|
|
69
|
+
console.log('\nā Setup cancelled.\n')
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { configs, cleanRedundant, installDeps } = response
|
|
74
|
+
|
|
75
|
+
// Clean up redundant files first
|
|
76
|
+
if (cleanRedundant) {
|
|
77
|
+
await cleanRedundantFiles(root)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Create config files
|
|
81
|
+
if (configs.includes('eslint')) {
|
|
82
|
+
createEslintConfig(root, isWorkspace)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (configs.includes('prettier')) {
|
|
86
|
+
createPrettierConfig(root)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (configs.includes('editorconfig')) {
|
|
90
|
+
createEditorConfig(root)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (configs.includes('githooks')) {
|
|
94
|
+
createGitHooks(root)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Update package.json
|
|
98
|
+
updatePackageJsonLint(root, configs)
|
|
99
|
+
|
|
100
|
+
if (installDeps) {
|
|
101
|
+
console.log('\nš¦ Installing dependencies...')
|
|
102
|
+
console.log('Run: bun add -D @bagelink/lint-config eslint prettier typescript')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
console.log('\nā
Linting setup complete!')
|
|
106
|
+
console.log('\nAvailable commands:')
|
|
107
|
+
console.log(' bun run lint - Run linter')
|
|
108
|
+
console.log(' bun run lint:fix - Fix linting issues')
|
|
109
|
+
console.log(' bun run format - Format code with Prettier')
|
|
110
|
+
console.log('')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create ESLint config
|
|
115
|
+
*/
|
|
116
|
+
function createEslintConfig(root: string, isWorkspace: boolean): void {
|
|
117
|
+
const configPath = resolve(root, 'eslint.config.js')
|
|
118
|
+
|
|
119
|
+
const config = isWorkspace
|
|
120
|
+
? `import { defineConfig } from '@bagelink/lint-config/eslint'
|
|
121
|
+
|
|
122
|
+
export default defineConfig({
|
|
123
|
+
// Workspace-level ESLint config
|
|
124
|
+
ignores: ['**/dist/**', '**/node_modules/**', '**/.bun-cache/**'],
|
|
125
|
+
})
|
|
126
|
+
`
|
|
127
|
+
: `import vue3Config from '@bagelink/lint-config/eslint/vue3'
|
|
128
|
+
|
|
129
|
+
export default vue3Config
|
|
130
|
+
`
|
|
131
|
+
|
|
132
|
+
writeFileSync(configPath, config)
|
|
133
|
+
console.log('ā
Created eslint.config.js')
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Create Prettier config
|
|
138
|
+
*/
|
|
139
|
+
function createPrettierConfig(root: string): void {
|
|
140
|
+
const configPath = resolve(root, '.prettierrc')
|
|
141
|
+
|
|
142
|
+
const config = {
|
|
143
|
+
semi: false,
|
|
144
|
+
singleQuote: true,
|
|
145
|
+
tabWidth: 2,
|
|
146
|
+
useTabs: true,
|
|
147
|
+
trailingComma: 'all',
|
|
148
|
+
printWidth: 100,
|
|
149
|
+
arrowParens: 'avoid',
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`)
|
|
153
|
+
console.log('ā
Created .prettierrc')
|
|
154
|
+
|
|
155
|
+
// .prettierignore
|
|
156
|
+
const ignorePath = resolve(root, '.prettierignore')
|
|
157
|
+
const ignore = `dist
|
|
158
|
+
node_modules
|
|
159
|
+
.bun-cache
|
|
160
|
+
*.min.js
|
|
161
|
+
*.min.css
|
|
162
|
+
`
|
|
163
|
+
|
|
164
|
+
writeFileSync(ignorePath, ignore)
|
|
165
|
+
console.log('ā
Created .prettierignore')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Create EditorConfig
|
|
170
|
+
*/
|
|
171
|
+
function createEditorConfig(root: string): void {
|
|
172
|
+
const configPath = resolve(root, '.editorconfig')
|
|
173
|
+
|
|
174
|
+
const config = `root = true
|
|
175
|
+
|
|
176
|
+
[*]
|
|
177
|
+
charset = utf-8
|
|
178
|
+
indent_style = tab
|
|
179
|
+
indent_size = 2
|
|
180
|
+
end_of_line = lf
|
|
181
|
+
insert_final_newline = true
|
|
182
|
+
trim_trailing_whitespace = true
|
|
183
|
+
|
|
184
|
+
[*.md]
|
|
185
|
+
trim_trailing_whitespace = false
|
|
186
|
+
|
|
187
|
+
[*.{json,yml,yaml}]
|
|
188
|
+
indent_style = space
|
|
189
|
+
indent_size = 2
|
|
190
|
+
`
|
|
191
|
+
|
|
192
|
+
writeFileSync(configPath, config)
|
|
193
|
+
console.log('ā
Created .editorconfig')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Create Git Hooks
|
|
198
|
+
*/
|
|
199
|
+
function createGitHooks(root: string): void {
|
|
200
|
+
const packageJsonPath = resolve(root, 'package.json')
|
|
201
|
+
|
|
202
|
+
if (!existsSync(packageJsonPath)) {
|
|
203
|
+
console.warn('ā ļø No package.json found, skipping git hooks')
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// .lintstagedrc
|
|
208
|
+
const lintStagedConfig = {
|
|
209
|
+
'*.{js,jsx,ts,tsx,vue}': ['eslint --fix'],
|
|
210
|
+
'*.{json,md,yml,yaml}': ['prettier --write'],
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
writeFileSync(
|
|
214
|
+
resolve(root, '.lintstagedrc'),
|
|
215
|
+
`${JSON.stringify(lintStagedConfig, null, 2)}\n`,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
console.log('ā
Created .lintstagedrc')
|
|
219
|
+
console.log('ā¹ļø Add simple-git-hooks and lint-staged to devDependencies')
|
|
220
|
+
console.log(' Then run: npx simple-git-hooks')
|
|
221
|
+
}
|
|
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
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Update package.json with lint scripts
|
|
275
|
+
*/
|
|
276
|
+
function updatePackageJsonLint(root: string, configs: string[]): void {
|
|
277
|
+
const packageJsonPath = resolve(root, 'package.json')
|
|
278
|
+
|
|
279
|
+
if (!existsSync(packageJsonPath)) {
|
|
280
|
+
console.warn('ā ļø No package.json found')
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const packageJson = JSON.parse(
|
|
286
|
+
readFileSync(packageJsonPath, 'utf-8'),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
if (!packageJson.scripts) {
|
|
290
|
+
packageJson.scripts = {}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Add lint scripts
|
|
294
|
+
if (configs.includes('eslint')) {
|
|
295
|
+
if (!packageJson.scripts.lint) {
|
|
296
|
+
packageJson.scripts.lint = 'eslint .'
|
|
297
|
+
}
|
|
298
|
+
if (!packageJson.scripts['lint:fix']) {
|
|
299
|
+
packageJson.scripts['lint:fix'] = 'eslint . --fix'
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Add format scripts
|
|
304
|
+
if (configs.includes('prettier')) {
|
|
305
|
+
if (!packageJson.scripts.format) {
|
|
306
|
+
packageJson.scripts.format = 'prettier --write .'
|
|
307
|
+
}
|
|
308
|
+
if (!packageJson.scripts['format:check']) {
|
|
309
|
+
packageJson.scripts['format:check'] = 'prettier --check .'
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
writeFileSync(
|
|
314
|
+
packageJsonPath,
|
|
315
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
console.log('ā
Updated package.json with lint scripts')
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
console.error('ā Failed to update package.json:', error)
|
|
322
|
+
}
|
|
323
|
+
}
|
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
|
}),
|
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { WorkspaceConfig } from './types'
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import process from 'node:process'
|
|
5
|
+
import prompts from 'prompts'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate SDK from OpenAPI spec
|
|
9
|
+
*/
|
|
10
|
+
export async function generateSDK(
|
|
11
|
+
root: string = process.cwd(),
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
console.log('\nš§ Generating SDK from OpenAPI...\n')
|
|
14
|
+
|
|
15
|
+
// Try to load config
|
|
16
|
+
let config: WorkspaceConfig | null = null
|
|
17
|
+
let openApiUrl: string | undefined
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const configPath = resolve(root, 'bgl.config.ts')
|
|
21
|
+
if (existsSync(configPath)) {
|
|
22
|
+
const module = await import(`file://${configPath}`)
|
|
23
|
+
const workspace = module.default
|
|
24
|
+
if (typeof workspace === 'function') {
|
|
25
|
+
config = workspace('development')
|
|
26
|
+
if (config?.openapi_url) {
|
|
27
|
+
openApiUrl = config.openapi_url
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Ignore config load errors
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Prompt for missing info
|
|
37
|
+
const response = await prompts([
|
|
38
|
+
{
|
|
39
|
+
type: openApiUrl !== undefined ? null : 'text',
|
|
40
|
+
name: 'openApiUrl',
|
|
41
|
+
message: 'OpenAPI spec URL:',
|
|
42
|
+
initial: openApiUrl ?? 'http://localhost:8000/openapi.json',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'text',
|
|
46
|
+
name: 'outputDir',
|
|
47
|
+
message: 'Output directory:',
|
|
48
|
+
initial: './src/api',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'confirm',
|
|
52
|
+
name: 'splitFiles',
|
|
53
|
+
message: 'Split into organized files?',
|
|
54
|
+
initial: true,
|
|
55
|
+
},
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
if (!response) {
|
|
59
|
+
console.log('\nā SDK generation cancelled.\n')
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const finalUrl = openApiUrl ?? response.openApiUrl
|
|
64
|
+
const { outputDir, splitFiles } = response
|
|
65
|
+
|
|
66
|
+
console.log(`\nš” Fetching OpenAPI spec from: ${finalUrl}`)
|
|
67
|
+
console.log(`š Output directory: ${outputDir}\n`)
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// Dynamic import of @bagelink/sdk
|
|
71
|
+
const { openAPI } = await import('@bagelink/sdk')
|
|
72
|
+
const { readFileSync } = await import('node:fs')
|
|
73
|
+
const { dirname, join } = await import('node:path')
|
|
74
|
+
const { fileURLToPath } = await import('node:url')
|
|
75
|
+
|
|
76
|
+
const { types, code } = await openAPI(finalUrl, '/api')
|
|
77
|
+
|
|
78
|
+
const outputPath = resolve(root, outputDir)
|
|
79
|
+
if (!existsSync(outputPath)) {
|
|
80
|
+
mkdirSync(outputPath, { recursive: true })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Write types
|
|
84
|
+
const typesPath = resolve(outputPath, 'types.d.ts')
|
|
85
|
+
writeFileSync(typesPath, types)
|
|
86
|
+
console.log('ā
Generated types.d.ts')
|
|
87
|
+
|
|
88
|
+
// Write API client
|
|
89
|
+
const apiPath = resolve(outputPath, 'api.ts')
|
|
90
|
+
writeFileSync(apiPath, code)
|
|
91
|
+
console.log('ā
Generated api.ts')
|
|
92
|
+
|
|
93
|
+
// Copy streamClient.ts from @bagelink/sdk
|
|
94
|
+
try {
|
|
95
|
+
const sdkModule = await import('@bagelink/sdk')
|
|
96
|
+
const sdkPath = fileURLToPath(import.meta.resolve('@bagelink/sdk'))
|
|
97
|
+
const sdkDir = dirname(sdkPath)
|
|
98
|
+
const streamClientSource = join(sdkDir, 'openAPITools', 'streamClient.ts')
|
|
99
|
+
|
|
100
|
+
if (existsSync(streamClientSource)) {
|
|
101
|
+
const streamClientCode = readFileSync(streamClientSource, 'utf-8')
|
|
102
|
+
const streamClientPath = resolve(outputPath, 'streamClient.ts')
|
|
103
|
+
writeFileSync(streamClientPath, streamClientCode)
|
|
104
|
+
console.log('ā
Generated streamClient.ts')
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Stream client is optional, don't fail if not found
|
|
108
|
+
console.log('ā¹ļø streamClient.ts not found (optional)')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Write index
|
|
112
|
+
const indexPath = resolve(outputPath, 'index.ts')
|
|
113
|
+
writeFileSync(
|
|
114
|
+
indexPath,
|
|
115
|
+
'export * from \'./api\'\nexport * from \'./types.d\'\n',
|
|
116
|
+
)
|
|
117
|
+
console.log('ā
Generated index.ts')
|
|
118
|
+
|
|
119
|
+
if (splitFiles) {
|
|
120
|
+
console.log('\nš Splitting into organized files...')
|
|
121
|
+
console.log('ā¹ļø File splitting requires @bagelink/sdk bin scripts')
|
|
122
|
+
console.log(' Keeping monolithic structure for now')
|
|
123
|
+
// Note: File splitting requires unpublished bin scripts
|
|
124
|
+
// Users can manually run: bunx bagelink generate
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log('\nā
SDK generated successfully!')
|
|
128
|
+
console.log(`\nImport it in your code:`)
|
|
129
|
+
console.log(` import { api } from '${outputDir.replace('./src/', './')}'`)
|
|
130
|
+
console.log('')
|
|
131
|
+
}
|
|
132
|
+
catch (error: unknown) {
|
|
133
|
+
console.error('\nā Failed to generate SDK:')
|
|
134
|
+
if (error instanceof Error) {
|
|
135
|
+
console.error(error.message)
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
console.error(error)
|
|
139
|
+
}
|
|
140
|
+
console.log('\nMake sure:')
|
|
141
|
+
console.log(' 1. @bagelink/sdk is installed: bun add -D @bagelink/sdk')
|
|
142
|
+
console.log(' 2. OpenAPI URL is accessible')
|
|
143
|
+
console.log(' 3. API server is running (if using localhost)')
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generate SDK for all projects in workspace
|
|
150
|
+
*/
|
|
151
|
+
export async function generateSDKForWorkspace(root: string = process.cwd()): Promise<void> {
|
|
152
|
+
console.log('\nš¢ Generating SDK for workspace projects...\n')
|
|
153
|
+
|
|
154
|
+
// Find all projects
|
|
155
|
+
const fs = await import('node:fs')
|
|
156
|
+
const items = fs.readdirSync(root, { withFileTypes: true })
|
|
157
|
+
const projects = items
|
|
158
|
+
.filter(
|
|
159
|
+
item => item.isDirectory()
|
|
160
|
+
&& item.name !== 'node_modules'
|
|
161
|
+
&& item.name !== 'shared'
|
|
162
|
+
&& item.name !== '.git'
|
|
163
|
+
&& !item.name.startsWith('.'),
|
|
164
|
+
)
|
|
165
|
+
.map(item => item.name)
|
|
166
|
+
|
|
167
|
+
if (projects.length === 0) {
|
|
168
|
+
console.log('No projects found in workspace')
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const response = await prompts({
|
|
173
|
+
type: 'multiselect',
|
|
174
|
+
name: 'selectedProjects',
|
|
175
|
+
message: 'Select projects to generate SDK for:',
|
|
176
|
+
choices: projects.map(p => ({ title: p, value: p, selected: true })),
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
if (!response || !response.selectedProjects || response.selectedProjects.length === 0) {
|
|
180
|
+
console.log('\nā No projects selected.\n')
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
for (const project of response.selectedProjects) {
|
|
185
|
+
console.log(`\nš¦ Generating SDK for: ${project}`)
|
|
186
|
+
const projectPath = resolve(root, project)
|
|
187
|
+
try {
|
|
188
|
+
await generateSDK(projectPath)
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
console.error(`Failed to generate SDK for ${project}`)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log('\nā
All SDKs generated!')
|
|
196
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -45,5 +45,10 @@ export interface ProxyConfig {
|
|
|
45
45
|
changeOrigin: boolean
|
|
46
46
|
rewrite?: (path: string) => string
|
|
47
47
|
secure: boolean
|
|
48
|
+
ws?: boolean
|
|
49
|
+
followRedirects?: boolean
|
|
50
|
+
autoRewrite?: boolean
|
|
51
|
+
protocolRewrite?: string
|
|
52
|
+
configure?: (proxy: any, options: any) => void
|
|
48
53
|
}
|
|
49
54
|
}
|