@bagelink/workspace 1.8.0 → 1.10.0

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/src/lint.ts ADDED
@@ -0,0 +1,235 @@
1
+ import { existsSync, writeFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import process from 'node:process'
4
+ import prompts from 'prompts'
5
+
6
+ /**
7
+ * Set up linting in a project
8
+ */
9
+ export async function setupLint(
10
+ root: string = process.cwd(),
11
+ isWorkspace: boolean = false,
12
+ ): Promise<void> {
13
+ console.log('\n🔍 Setting up linting...\n')
14
+
15
+ const response = await prompts([
16
+ {
17
+ type: 'multiselect',
18
+ name: 'configs',
19
+ message: 'Select configurations to set up:',
20
+ choices: [
21
+ { title: 'ESLint', value: 'eslint', selected: true },
22
+ { title: 'Prettier', value: 'prettier', selected: true },
23
+ { title: 'EditorConfig', value: 'editorconfig', selected: true },
24
+ { title: 'Git Hooks', value: 'githooks', selected: false },
25
+ ],
26
+ },
27
+ {
28
+ type: 'confirm',
29
+ name: 'installDeps',
30
+ message: 'Install dependencies?',
31
+ initial: true,
32
+ },
33
+ ])
34
+
35
+ if (!response || !response.configs) {
36
+ console.log('\n❌ Setup cancelled.\n')
37
+ process.exit(1)
38
+ }
39
+
40
+ const { configs, installDeps } = response
41
+
42
+ // Create config files
43
+ if (configs.includes('eslint')) {
44
+ createEslintConfig(root, isWorkspace)
45
+ }
46
+
47
+ if (configs.includes('prettier')) {
48
+ createPrettierConfig(root)
49
+ }
50
+
51
+ if (configs.includes('editorconfig')) {
52
+ createEditorConfig(root)
53
+ }
54
+
55
+ if (configs.includes('githooks')) {
56
+ createGitHooks(root)
57
+ }
58
+
59
+ // Update package.json
60
+ updatePackageJsonLint(root, configs)
61
+
62
+ if (installDeps) {
63
+ console.log('\n📦 Installing dependencies...')
64
+ console.log('Run: bun add -D @bagelink/lint-config eslint prettier typescript')
65
+ }
66
+
67
+ console.log('\n✅ Linting setup complete!')
68
+ console.log('\nAvailable commands:')
69
+ console.log(' bun run lint - Run linter')
70
+ console.log(' bun run lint:fix - Fix linting issues')
71
+ console.log(' bun run format - Format code with Prettier')
72
+ console.log('')
73
+ }
74
+
75
+ /**
76
+ * Create ESLint config
77
+ */
78
+ function createEslintConfig(root: string, isWorkspace: boolean): void {
79
+ const configPath = resolve(root, 'eslint.config.js')
80
+
81
+ const config = isWorkspace
82
+ ? `import { defineConfig } from '@bagelink/lint-config/eslint'
83
+
84
+ export default defineConfig({
85
+ // Workspace-level ESLint config
86
+ ignores: ['**/dist/**', '**/node_modules/**', '**/.bun-cache/**'],
87
+ })
88
+ `
89
+ : `import vue3Config from '@bagelink/lint-config/eslint/vue3'
90
+
91
+ export default vue3Config
92
+ `
93
+
94
+ writeFileSync(configPath, config)
95
+ console.log('✅ Created eslint.config.js')
96
+ }
97
+
98
+ /**
99
+ * Create Prettier config
100
+ */
101
+ function createPrettierConfig(root: string): void {
102
+ const configPath = resolve(root, '.prettierrc')
103
+
104
+ const config = {
105
+ semi: false,
106
+ singleQuote: true,
107
+ tabWidth: 2,
108
+ useTabs: true,
109
+ trailingComma: 'all',
110
+ printWidth: 100,
111
+ arrowParens: 'avoid',
112
+ }
113
+
114
+ writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`)
115
+ console.log('✅ Created .prettierrc')
116
+
117
+ // .prettierignore
118
+ const ignorePath = resolve(root, '.prettierignore')
119
+ const ignore = `dist
120
+ node_modules
121
+ .bun-cache
122
+ *.min.js
123
+ *.min.css
124
+ `
125
+
126
+ writeFileSync(ignorePath, ignore)
127
+ console.log('✅ Created .prettierignore')
128
+ }
129
+
130
+ /**
131
+ * Create EditorConfig
132
+ */
133
+ function createEditorConfig(root: string): void {
134
+ const configPath = resolve(root, '.editorconfig')
135
+
136
+ const config = `root = true
137
+
138
+ [*]
139
+ charset = utf-8
140
+ indent_style = tab
141
+ indent_size = 2
142
+ end_of_line = lf
143
+ insert_final_newline = true
144
+ trim_trailing_whitespace = true
145
+
146
+ [*.md]
147
+ trim_trailing_whitespace = false
148
+
149
+ [*.{json,yml,yaml}]
150
+ indent_style = space
151
+ indent_size = 2
152
+ `
153
+
154
+ writeFileSync(configPath, config)
155
+ console.log('✅ Created .editorconfig')
156
+ }
157
+
158
+ /**
159
+ * Create Git Hooks
160
+ */
161
+ function createGitHooks(root: string): void {
162
+ const packageJsonPath = resolve(root, 'package.json')
163
+
164
+ if (!existsSync(packageJsonPath)) {
165
+ console.warn('⚠️ No package.json found, skipping git hooks')
166
+ return
167
+ }
168
+
169
+ // .lintstagedrc
170
+ const lintStagedConfig = {
171
+ '*.{js,jsx,ts,tsx,vue}': ['eslint --fix'],
172
+ '*.{json,md,yml,yaml}': ['prettier --write'],
173
+ }
174
+
175
+ writeFileSync(
176
+ resolve(root, '.lintstagedrc'),
177
+ `${JSON.stringify(lintStagedConfig, null, 2)}\n`,
178
+ )
179
+
180
+ console.log('✅ Created .lintstagedrc')
181
+ console.log('ℹ️ Add simple-git-hooks and lint-staged to devDependencies')
182
+ console.log(' Then run: npx simple-git-hooks')
183
+ }
184
+
185
+ /**
186
+ * Update package.json with lint scripts
187
+ */
188
+ function updatePackageJsonLint(root: string, configs: string[]): void {
189
+ const packageJsonPath = resolve(root, 'package.json')
190
+
191
+ if (!existsSync(packageJsonPath)) {
192
+ console.warn('⚠️ No package.json found')
193
+ return
194
+ }
195
+
196
+ try {
197
+ const packageJson = JSON.parse(
198
+ require('fs').readFileSync(packageJsonPath, 'utf-8'),
199
+ )
200
+
201
+ if (!packageJson.scripts) {
202
+ packageJson.scripts = {}
203
+ }
204
+
205
+ // Add lint scripts
206
+ if (configs.includes('eslint')) {
207
+ if (!packageJson.scripts.lint) {
208
+ packageJson.scripts.lint = 'eslint .'
209
+ }
210
+ if (!packageJson.scripts['lint:fix']) {
211
+ packageJson.scripts['lint:fix'] = 'eslint . --fix'
212
+ }
213
+ }
214
+
215
+ // Add format scripts
216
+ if (configs.includes('prettier')) {
217
+ if (!packageJson.scripts.format) {
218
+ packageJson.scripts.format = 'prettier --write .'
219
+ }
220
+ if (!packageJson.scripts['format:check']) {
221
+ packageJson.scripts['format:check'] = 'prettier --check .'
222
+ }
223
+ }
224
+
225
+ writeFileSync(
226
+ packageJsonPath,
227
+ `${JSON.stringify(packageJson, null, 2)}\n`,
228
+ )
229
+
230
+ console.log('✅ Updated package.json with lint scripts')
231
+ }
232
+ catch (error) {
233
+ console.error('❌ Failed to update package.json:', error)
234
+ }
235
+ }
package/src/sdk.ts ADDED
@@ -0,0 +1,186 @@
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
+ projectName?: string,
13
+ ): Promise<void> {
14
+ console.log('\n🔧 Generating SDK from OpenAPI...\n')
15
+
16
+ // Try to load config
17
+ let config: WorkspaceConfig | null = null
18
+ let openApiUrl: string | undefined
19
+
20
+ try {
21
+ const configPath = resolve(root, 'bgl.config.ts')
22
+ if (existsSync(configPath)) {
23
+ const module = await import(`file://${configPath}`)
24
+ const workspace = module.default
25
+ if (typeof workspace === 'function') {
26
+ config = workspace('development')
27
+ openApiUrl = config.openapi_url
28
+ }
29
+ }
30
+ }
31
+ catch (error) {
32
+ console.warn('⚠️ Could not load bgl.config.ts')
33
+ }
34
+
35
+ // Prompt for missing info
36
+ const response = await prompts([
37
+ {
38
+ type: openApiUrl ? null : 'text',
39
+ name: 'openApiUrl',
40
+ message: 'OpenAPI spec URL:',
41
+ initial: openApiUrl || 'http://localhost:8000/openapi.json',
42
+ },
43
+ {
44
+ type: 'text',
45
+ name: 'outputDir',
46
+ message: 'Output directory:',
47
+ initial: './src/api',
48
+ },
49
+ {
50
+ type: 'confirm',
51
+ name: 'splitFiles',
52
+ message: 'Split into organized files?',
53
+ initial: true,
54
+ },
55
+ ])
56
+
57
+ if (!response) {
58
+ console.log('\n❌ SDK generation cancelled.\n')
59
+ process.exit(1)
60
+ }
61
+
62
+ const finalUrl = openApiUrl || response.openApiUrl
63
+ const { outputDir, splitFiles } = response
64
+
65
+ console.log(`\n📡 Fetching OpenAPI spec from: ${finalUrl}`)
66
+ console.log(`📁 Output directory: ${outputDir}\n`)
67
+
68
+ try {
69
+ // Dynamic import of @bagelink/sdk
70
+ const { default: openAPI } = await import('@bagelink/sdk')
71
+
72
+ const { types, code } = await openAPI(finalUrl, '/api')
73
+
74
+ const outputPath = resolve(root, outputDir)
75
+ if (!existsSync(outputPath)) {
76
+ mkdirSync(outputPath, { recursive: true })
77
+ }
78
+
79
+ // Write types
80
+ const typesPath = resolve(outputPath, 'types.d.ts')
81
+ writeFileSync(typesPath, types)
82
+ console.log('✅ Generated types.d.ts')
83
+
84
+ // Write API client
85
+ const apiPath = resolve(outputPath, 'api.ts')
86
+ writeFileSync(apiPath, code)
87
+ console.log('✅ Generated api.ts')
88
+
89
+ // Write index
90
+ const indexPath = resolve(outputPath, 'index.ts')
91
+ writeFileSync(
92
+ indexPath,
93
+ 'export * from \'./api\'\nexport * from \'./types.d\'\n',
94
+ )
95
+ console.log('✅ Generated index.ts')
96
+
97
+ if (splitFiles) {
98
+ console.log('\n🔀 Splitting into organized files...')
99
+ try {
100
+ const { splitClientCode } = await import('@bagelink/sdk/bin/splitClientGen')
101
+ await splitClientCode({
102
+ bagelinkDir: outputPath,
103
+ useDirectories: true,
104
+ })
105
+ // Clean up monolithic files
106
+ const fs = await import('node:fs')
107
+ fs.rmSync(apiPath, { force: true })
108
+ fs.rmSync(typesPath, { force: true })
109
+ fs.rmSync(indexPath, { force: true })
110
+ console.log('✅ Files organized into directories')
111
+ }
112
+ catch (error) {
113
+ console.warn('⚠️ Could not split files, keeping monolithic structure')
114
+ }
115
+ }
116
+
117
+ console.log('\n✅ SDK generated successfully!')
118
+ console.log(`\nImport it in your code:`)
119
+ console.log(` import { api } from '${outputDir.replace('./src/', './')}'`)
120
+ console.log('')
121
+ }
122
+ catch (error: unknown) {
123
+ console.error('\n❌ Failed to generate SDK:')
124
+ if (error instanceof Error) {
125
+ console.error(error.message)
126
+ }
127
+ else {
128
+ console.error(error)
129
+ }
130
+ console.log('\nMake sure:')
131
+ console.log(' 1. @bagelink/sdk is installed: bun add -D @bagelink/sdk')
132
+ console.log(' 2. OpenAPI URL is accessible')
133
+ console.log(' 3. API server is running (if using localhost)')
134
+ process.exit(1)
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Generate SDK for all projects in workspace
140
+ */
141
+ export async function generateSDKForWorkspace(root: string = process.cwd()): Promise<void> {
142
+ console.log('\n🏢 Generating SDK for workspace projects...\n')
143
+
144
+ // Find all projects
145
+ const fs = await import('node:fs')
146
+ const items = fs.readdirSync(root, { withFileTypes: true })
147
+ const projects = items
148
+ .filter(
149
+ item => item.isDirectory()
150
+ && item.name !== 'node_modules'
151
+ && item.name !== 'shared'
152
+ && item.name !== '.git'
153
+ && !item.name.startsWith('.'),
154
+ )
155
+ .map(item => item.name)
156
+
157
+ if (projects.length === 0) {
158
+ console.log('No projects found in workspace')
159
+ return
160
+ }
161
+
162
+ const response = await prompts({
163
+ type: 'multiselect',
164
+ name: 'selectedProjects',
165
+ message: 'Select projects to generate SDK for:',
166
+ choices: projects.map(p => ({ title: p, value: p, selected: true })),
167
+ })
168
+
169
+ if (!response || !response.selectedProjects || response.selectedProjects.length === 0) {
170
+ console.log('\n❌ No projects selected.\n')
171
+ return
172
+ }
173
+
174
+ for (const project of response.selectedProjects) {
175
+ console.log(`\n📦 Generating SDK for: ${project}`)
176
+ const projectPath = resolve(root, project)
177
+ try {
178
+ await generateSDK(projectPath)
179
+ }
180
+ catch (error) {
181
+ console.error(`Failed to generate SDK for ${project}:`, error)
182
+ }
183
+ }
184
+
185
+ console.log('\n✅ All SDKs generated!')
186
+ }