@bagelink/workspace 1.10.6 → 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 CHANGED
@@ -57,10 +57,15 @@ Create `bgl.config.ts`:
57
57
  import { defineWorkspace } from '@bagelink/workspace'
58
58
  import type { WorkspaceConfig, WorkspaceEnvironment } from '@bagelink/workspace'
59
59
 
60
+ /**
61
+ * Define your workspace environments
62
+ * You can add as many custom environments as needed (e.g., 'staging', 'preview')
63
+ * Use --mode flag to switch: bgl dev --mode <env_name>
64
+ */
60
65
  const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
61
66
  localhost: {
62
67
  host: 'http://localhost:8000',
63
- proxy: '/api',
68
+ proxy: '/api', // Optional: remove to skip proxy setup
64
69
  openapi_url: 'http://localhost:8000/openapi.json',
65
70
  },
66
71
  development: {
@@ -72,6 +77,11 @@ const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
72
77
  host: 'https://project.bagel.to',
73
78
  proxy: '/api',
74
79
  },
80
+ // Add your own custom environments
81
+ staging: {
82
+ host: 'https://staging.project.bagel.to',
83
+ proxy: '/api',
84
+ },
75
85
  }
76
86
 
77
87
  export default defineWorkspace(configs)
@@ -79,7 +89,66 @@ export default defineWorkspace(configs)
79
89
 
80
90
  ## Usage
81
91
 
82
- ### 1. Configure Vite (`vite.config.ts`)
92
+ ### 1. Add Type Definitions
93
+
94
+ Add to your project's `env.d.ts`:
95
+
96
+ ```typescript
97
+ /// <reference types="@bagelink/workspace/env" />
98
+ ```
99
+
100
+ This provides TypeScript types for injected environment variables.
101
+
102
+ ### 2. Configure Vite (`vite.config.ts`)
103
+
104
+ **Recommended: Use the Vite plugin (keeps your config clean and standard)**
105
+
106
+ ```typescript
107
+ import { defineConfig } from 'vite'
108
+ import vue from '@vitejs/plugin-vue'
109
+ import { bagelink } from '@bagelink/workspace/vite'
110
+ import workspace from './bgl.config'
111
+
112
+ export default defineConfig({
113
+ plugins: [
114
+ vue(),
115
+ bagelink({ workspace }),
116
+ ],
117
+ })
118
+ ```
119
+
120
+ The plugin automatically:
121
+ - Configures proxy based on `bgl.config.ts`
122
+ - Sets up `@` alias pointing to `./src`
123
+ - Sets up `@shared` alias (in monorepos)
124
+ - Keeps your vite.config.ts clean and extensible
125
+
126
+ **Advanced: Custom options**
127
+
128
+ ```typescript
129
+ import { defineConfig } from 'vite'
130
+ import vue from '@vitejs/plugin-vue'
131
+ import { bagelink } from '@bagelink/workspace/vite'
132
+ import { fileURLToPath } from 'node:url'
133
+ import workspace from './bgl.config'
134
+
135
+ export default defineConfig({
136
+ plugins: [
137
+ vue(),
138
+ bagelink({
139
+ workspace,
140
+ config: {
141
+ sharedPath: '../packages/shared',
142
+ additionalAliases: {
143
+ '@utils': fileURLToPath(new URL('./src/utils', import.meta.url))
144
+ }
145
+ }
146
+ }),
147
+ ],
148
+ })
149
+ ```
150
+
151
+ **Legacy: Manual proxy setup**
83
152
 
84
153
  ```typescript
85
154
  import { defineConfig } from 'vite'
@@ -98,19 +167,104 @@ export default defineConfig(({ mode }) => {
98
167
  })
99
168
  ```
100
169
 
101
- ### 2. Add scripts to `package.json`
170
+ ### 3. Use Configuration in Your App
171
+
172
+ **Access config at runtime with `useWorkspace()`:**
173
+
174
+ ```typescript
175
+ import { useWorkspace, getApiUrl } from '@bagelink/workspace'
176
+
177
+ // In your app setup
178
+ const { baseURL, proxy, host, mode } = useWorkspace()
179
+ const auth = createAuth({ baseURL }) // Uses proxy if available, otherwise host
180
+
181
+ // Or get full API URL
182
+ const apiUrl = getApiUrl() // 'https://project.bagel.to/api'
183
+ ```
184
+
185
+ **Vue component example:**
186
+
187
+ ```vue
188
+ <script setup lang="ts">
189
+ import { useWorkspace } from '@bagelink/workspace'
190
+ import { createAuth } from '@bagelink/auth'
191
+
192
+ const { baseURL, proxy, host, mode } = useWorkspace()
193
+ const auth = createAuth({ baseURL }) // Uses proxy if available, otherwise host
194
+
195
+ console.log('API Host:', host)
196
+ console.log('Environment:', mode)
197
+ </script>
198
+ ```
199
+
200
+ **Access raw environment variables:**
201
+
202
+ ```typescript
203
+ // These are injected at build time by the bagelink plugin
204
+ const proxy = import.meta.env.VITE_BGL_PROXY // '/api'
205
+ const host = import.meta.env.VITE_BGL_HOST // 'https://project.bagel.to'
206
+ const openapi = import.meta.env.VITE_BGL_OPENAPI_URL // optional
207
+ ```
208
+
209
+ ### 4. Add scripts to `package.json`
102
210
 
103
211
  ```json
104
212
  {
105
213
  "scripts": {
106
214
  "dev": "vite",
107
215
  "dev:local": "vite --mode localhost",
108
- "build": "vite build"
216
+ "dev:staging": "vite --mode staging",
217
+ "build": "vite build",
218
+ "build:staging": "vite build --mode staging"
109
219
  }
110
220
  }
111
221
  ```
112
222
 
113
- ### 3. Generate Netlify Config
223
+ ### 5. Custom Environments
224
+
225
+ You can define as many custom environments as you need. The environment name is completely flexible:
226
+
227
+ ```typescript
228
+ const configs: Record<WorkspaceEnvironment, WorkspaceConfig> = {
229
+ localhost: {
230
+ host: 'http://localhost:8000',
231
+ proxy: '/api',
232
+ },
233
+ development: {
234
+ host: 'https://dev.project.bagel.to',
235
+ proxy: '/api',
236
+ },
237
+ staging: {
238
+ host: 'https://staging.project.bagel.to',
239
+ proxy: '/api',
240
+ },
241
+ preview: {
242
+ host: 'https://preview.project.bagel.to',
243
+ // No proxy - will use host directly
244
+ },
245
+ production: {
246
+ host: 'https://project.bagel.to',
247
+ proxy: '/api',
248
+ },
249
+ }
250
+ ```
251
+
252
+ Use the `--mode` flag to switch environments:
253
+
254
+ ```bash
255
+ # Development
256
+ bgl dev --mode development
257
+
258
+ # Staging
259
+ bgl dev --mode staging
260
+
261
+ # Production build
262
+ bgl build --mode production
263
+ ```
264
+
265
+ **Note:** The `proxy` field is optional. If omitted, no proxy will be configured for that environment, and `baseURL` from `useWorkspace()` will return the `host` value instead.
266
+
267
+ ### 6. Generate Netlify Config
114
268
 
115
269
  ```typescript
116
270
  import { writeNetlifyConfig } from '@bagelink/workspace'
@@ -409,7 +563,42 @@ Show CLI help.
409
563
 
410
564
  ## API
411
565
 
412
- ### `defineWorkspace(configs)`
566
+ ### Runtime Functions
567
+
568
+ #### `useWorkspace()`
569
+
570
+ Get workspace configuration at runtime. Config is injected as environment variables during build.
571
+
572
+ ```typescript
573
+ import { useWorkspace } from '@bagelink/workspace'
574
+
575
+ const { baseURL, proxy, host, openapiUrl, mode } = useWorkspace()
576
+ ```
577
+
578
+ **Returns:**
579
+ ```typescript
580
+ interface RuntimeWorkspaceConfig {
581
+ baseURL: string // proxy if available, otherwise host
582
+ proxy?: string // '/api' (optional)
583
+ host: string // 'https://project.bagel.to'
584
+ openapiUrl?: string // optional
585
+ mode: string // current environment (e.g., 'localhost', 'development', 'staging')
586
+ }
587
+ ```
588
+
589
+ #### `getApiUrl()`
590
+
591
+ Get the full API URL by combining host and proxy.
592
+
593
+ ```typescript
594
+ import { getApiUrl } from '@bagelink/workspace'
595
+
596
+ const apiUrl = getApiUrl() // 'https://project.bagel.to/api'
597
+ ```
598
+
599
+ ### Configuration Functions
600
+
601
+ #### `defineWorkspace(configs)`
413
602
 
414
603
  Define workspace configuration for all environments.
415
604
 
@@ -448,21 +637,37 @@ const proxy = workspace.createProxy(config)
448
637
  workspace.generateNetlify(config, './netlify.toml')
449
638
  ```
450
639
 
451
- ### `createViteProxy(config)`
640
+ ### Build-Time Functions
641
+
642
+ These are available from `@bagelink/workspace/vite`:
643
+
644
+ #### `createViteProxy(config)`
452
645
 
453
646
  Generate Vite proxy configuration.
454
647
 
455
- ### `writeNetlifyConfig(config, outPath?, additionalConfig?)`
648
+ ```typescript
649
+ import { createViteProxy } from '@bagelink/workspace/vite'
650
+ ```
651
+
652
+ #### `writeNetlifyConfig(config, outPath?, additionalConfig?)`
456
653
 
457
654
  Generate and write netlify.toml file.
458
655
 
459
- ### `setBuildEnvVars(config)`
656
+ ```typescript
657
+ import { writeNetlifyConfig } from '@bagelink/workspace/vite'
658
+ ```
659
+
660
+ #### `setBuildEnvVars(config)`
460
661
 
461
662
  Set environment variables for build process:
462
663
  - `BGL_PROXY_PATH` - Proxy path (e.g., `/api`)
463
664
  - `BGL_API_HOST` - API host URL
464
665
  - `BGL_OPENAPI_URL` - OpenAPI specification URL
465
666
 
667
+ ```typescript
668
+ import { setBuildEnvVars } from '@bagelink/workspace/vite'
669
+ ```
670
+
466
671
  ## License
467
672
 
468
673
  MIT © Bagel Studio
package/bin/bgl.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { resolve } from 'node:path'
2
3
  import process from 'node:process'
4
+ import { runBuild } from '../src/build.js'
3
5
  import { isWorkspace } from '../src/detect.js'
6
+ import { runDev } from '../src/dev.js'
4
7
  import { generateWorkspaceConfig } from '../src/init.js'
5
8
  import { setupLint } from '../src/lint.js'
6
9
  import { generateSDK, generateSDKForWorkspace } from '../src/sdk.js'
@@ -10,16 +13,29 @@ const [,, command, subcommand, ...args] = process.argv
10
13
 
11
14
  async function main() {
12
15
  if (command === 'init') {
13
- const createWorkspace = args.includes('--workspace') || args.includes('-w')
16
+ // Check both subcommand and args for --workspace flag
17
+ const createWorkspace
18
+ = subcommand === '--workspace'
19
+ || subcommand === '-w'
20
+ || args.includes('--workspace')
21
+ || args.includes('-w')
22
+
23
+ // Support 'bgl init .' or 'bgl init <path>'
24
+ let targetPath = process.cwd()
25
+ if (subcommand && !subcommand.startsWith('-')) {
26
+ // If subcommand is not a flag, treat it as a path
27
+ targetPath = subcommand === '.' ? process.cwd() : resolve(process.cwd(), subcommand)
28
+ }
29
+
14
30
  if (createWorkspace) {
15
31
  await initWorkspace()
16
32
  }
17
33
  else {
18
- await generateWorkspaceConfig()
34
+ await generateWorkspaceConfig(targetPath)
19
35
  }
20
36
  }
21
37
  else if (command === 'add') {
22
- const projectName = args[0]
38
+ const projectName = subcommand // 'bgl add admin' -> subcommand is 'admin'
23
39
  if (!projectName) {
24
40
  console.error('Error: Project name is required')
25
41
  console.log('Usage: bgl add <project-name>')
@@ -92,21 +108,48 @@ SDK Commands:
92
108
  process.exit(1)
93
109
  }
94
110
  }
111
+ else if (command === 'dev') {
112
+ const { filter, additionalArgs } = parseFilterArgs(
113
+ undefined,
114
+ subcommand,
115
+ args,
116
+ )
117
+ const exitCode = await runDev(filter, additionalArgs)
118
+ process.exit(exitCode)
119
+ }
120
+ else if (command === 'build') {
121
+ const { filter, additionalArgs } = parseFilterArgs(
122
+ undefined,
123
+ subcommand,
124
+ args,
125
+ )
126
+ const exitCode = await runBuild(filter, additionalArgs)
127
+ process.exit(exitCode)
128
+ }
95
129
  else {
96
130
  console.log(`
97
131
  Bagel Workspace CLI
98
132
 
99
133
  Usage:
100
- bgl init Generate bgl.config.ts for single project
134
+ bgl init [path] Generate bgl.config.ts for single project
135
+ Examples: bgl init, bgl init ., bgl init ./my-app
101
136
  bgl init --workspace Create a new workspace with multiple projects
102
137
  bgl add <name> Add a new project to workspace
103
138
  bgl list List all projects in workspace
139
+ bgl dev [filter] [...args] Run dev servers with clean output (default: './!shared*')
140
+ Examples:
141
+ bgl dev --mode localhost
142
+ bgl dev --mode staging
143
+ bgl dev admin --mode production
144
+ bgl build [project] [...args] Build project by directory (default: all projects)
145
+ Example: bgl build --mode production
104
146
  bgl lint init Set up linting (auto-detects workspace)
105
147
  bgl sdk generate Generate SDK (auto-detects workspace)
106
148
 
107
149
  Options:
108
150
  --workspace, -w Force workspace mode
109
151
  --project, -p Force single project mode
152
+ --mode <env> Set environment (matches bgl.config.ts keys)
110
153
  --help, -h Show this help message
111
154
 
112
155
  Note: Commands auto-detect workspace mode based on directory structure
@@ -115,6 +158,54 @@ Note: Commands auto-detect workspace mode based on directory structure
115
158
  }
116
159
  }
117
160
 
161
+ function normalizeFilter(input: string): string {
162
+ // Keep glob patterns as-is, otherwise ensure ./ prefix
163
+ if (input.startsWith('.') || input.includes('*') || input.includes('[')) {
164
+ return input
165
+ }
166
+ return `./${input}`
167
+ }
168
+
169
+ function parseFilterArgs(
170
+ defaultFilter: string | undefined,
171
+ subcommandArg?: string,
172
+ argsList: string[] = [],
173
+ ): { filter?: string, additionalArgs: string[] } {
174
+ const tokens = [subcommandArg, ...argsList].filter(Boolean) as string[]
175
+
176
+ // Flags that take values
177
+ const flagsWithValues = new Set(['--mode', '--host', '--port'])
178
+
179
+ // Find non-flag tokens (excluding flag values)
180
+ const nonFlagIndexes: number[] = []
181
+ for (let i = 0; i < tokens.length; i++) {
182
+ const token = tokens[i]
183
+ if (token.startsWith('-')) {
184
+ // If this flag takes a value, skip the next token
185
+ if (flagsWithValues.has(token)) {
186
+ i++ // Skip next token (the value)
187
+ }
188
+ } else {
189
+ // Not a flag and not a flag value
190
+ const prevToken = i > 0 ? tokens[i - 1] : null
191
+ if (!prevToken || !flagsWithValues.has(prevToken)) {
192
+ nonFlagIndexes.push(i)
193
+ }
194
+ }
195
+ }
196
+
197
+ const filterIndex = nonFlagIndexes.length > 0
198
+ ? nonFlagIndexes[nonFlagIndexes.length - 1]
199
+ : -1
200
+ const filter = filterIndex >= 0
201
+ ? normalizeFilter(tokens[filterIndex])
202
+ : defaultFilter
203
+ const additionalArgs = filterIndex >= 0
204
+ ? tokens.filter((_, index) => index !== filterIndex)
205
+ : tokens
206
+ return { filter, additionalArgs }
207
+ }
208
+
118
209
  main().catch((error: unknown) => {
119
210
  console.error('Error:', error)
120
211
  process.exit(1)