@bagelink/workspace 1.11.0 → 1.11.3

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/workspace.ts CHANGED
@@ -3,6 +3,18 @@ import { resolve } from 'node:path'
3
3
  import process from 'node:process'
4
4
  import prompts from 'prompts'
5
5
 
6
+ async function resolveLatestVersion(pkg: string): Promise<string> {
7
+ try {
8
+ const res = await fetch(`https://registry.npmjs.org/${pkg}/latest`)
9
+ if (!res.ok) return 'latest'
10
+ const data = await res.json() as { version: string }
11
+ return `^${data.version}`
12
+ }
13
+ catch {
14
+ return 'latest'
15
+ }
16
+ }
17
+
6
18
  /**
7
19
  * Initialize a new workspace with flat structure
8
20
  */
@@ -45,8 +57,27 @@ export async function initWorkspace(root: string = process.cwd()): Promise<void>
45
57
 
46
58
  const workspaceDir = resolve(root, workspaceName)
47
59
 
60
+ // Resolve actual latest versions from npm before writing package.json
61
+ console.log('Resolving package versions...')
62
+ const allPkgs = [
63
+ '@bagelink/auth',
64
+ '@bagelink/sdk',
65
+ '@bagelink/vue',
66
+ 'pinia',
67
+ 'vue',
68
+ 'vue-router',
69
+ '@bagelink/lint-config',
70
+ '@bagelink/workspace',
71
+ '@vitejs/plugin-vue',
72
+ 'eslint',
73
+ 'vite',
74
+ ]
75
+ const versions = Object.fromEntries(
76
+ await Promise.all(allPkgs.map(async pkg => [pkg, await resolveLatestVersion(pkg)])),
77
+ )
78
+
48
79
  // Create workspace structure
49
- createWorkspaceRoot(root, workspaceName, projectId)
80
+ createWorkspaceRoot(root, workspaceName, projectId, versions)
50
81
 
51
82
  // Create shared package
52
83
  createSharedPackage(workspaceDir)
@@ -71,7 +102,7 @@ export async function initWorkspace(root: string = process.cwd()): Promise<void>
71
102
  /**
72
103
  * Create workspace root files
73
104
  */
74
- function createWorkspaceRoot(root: string, name: string, projectId: string): void {
105
+ function createWorkspaceRoot(root: string, name: string, projectId: string, versions: Record<string, string>): void {
75
106
  const workspaceDir = resolve(root, name)
76
107
 
77
108
  if (existsSync(workspaceDir)) {
@@ -87,15 +118,29 @@ function createWorkspaceRoot(root: string, name: string, projectId: string): voi
87
118
  private: true,
88
119
  workspaces: ['*', '!node_modules'],
89
120
  scripts: {
90
- dev: 'bun scripts/dev.ts',
91
- 'dev:verbose': 'bun run --filter \'./[!shared]*\' dev',
92
- build: 'bun run --filter \'./[!shared]*\' build',
93
- typecheck: 'tsc --noEmit',
121
+ 'dev': 'bgl dev',
122
+ 'dev:local': 'bgl dev --mode localhost',
123
+ 'dev:verbose': 'bun run --filter \'./!shared*\' dev',
124
+ 'build': 'bgl build',
125
+ 'typecheck': 'tsc --noEmit',
126
+ 'lint': 'eslint . --cache',
127
+ 'lint:fix': 'eslint . --cache --fix',
128
+ },
129
+ dependencies: {
130
+ '@bagelink/auth': versions['@bagelink/auth'],
131
+ '@bagelink/sdk': versions['@bagelink/sdk'],
132
+ '@bagelink/vue': versions['@bagelink/vue'],
133
+ 'pinia': versions.pinia,
134
+ 'vue': versions.vue,
135
+ 'vue-router': versions['vue-router'],
94
136
  },
95
137
  devDependencies: {
96
- '@bagelink/workspace': 'latest',
138
+ '@bagelink/lint-config': versions['@bagelink/lint-config'],
139
+ '@bagelink/workspace': versions['@bagelink/workspace'],
140
+ '@vitejs/plugin-vue': versions['@vitejs/plugin-vue'],
141
+ 'eslint': versions.eslint,
97
142
  'typescript': '^5.0.0',
98
- 'vite': 'latest',
143
+ 'vite': versions.vite,
99
144
  },
100
145
  }
101
146
 
@@ -107,10 +152,15 @@ function createWorkspaceRoot(root: string, name: string, projectId: string): voi
107
152
  // bgl.config.ts (shared)
108
153
  const bglConfig = `import { defineWorkspace } from '@bagelink/workspace'
109
154
 
155
+ /**
156
+ * Define your workspace environments
157
+ * You can add as many custom environments as needed (e.g., 'staging', 'preview')
158
+ * Use --mode flag to switch: bgl dev --mode <env_name>
159
+ */
110
160
  export default defineWorkspace({
111
161
  localhost: {
112
162
  host: 'http://localhost:8000',
113
- proxy: '/api',
163
+ proxy: '/api', // Optional: remove to skip proxy setup
114
164
  openapi_url: 'http://localhost:8000/openapi.json',
115
165
  },
116
166
  development: {
@@ -306,6 +356,12 @@ export async function addProject(
306
356
 
307
357
  console.log(`\n📦 Creating project: ${name}\n`)
308
358
 
359
+ console.log('Resolving package versions...')
360
+ const projectPkgs = ['@vitejs/plugin-vue', 'vite', 'vue', 'pinia', 'vue-router']
361
+ const versions = Object.fromEntries(
362
+ await Promise.all(projectPkgs.map(async pkg => [pkg, await resolveLatestVersion(pkg)])),
363
+ )
364
+
309
365
  mkdirSync(projectDir, { recursive: true })
310
366
 
311
367
  // Determine if this is part of a workspace
@@ -320,11 +376,14 @@ export async function addProject(
320
376
  build: 'vite build',
321
377
  preview: 'vite preview',
322
378
  },
323
- dependencies: {} as Record<string, string>,
379
+ dependencies: {
380
+ 'pinia': versions.pinia,
381
+ 'vue': versions.vue,
382
+ 'vue-router': versions['vue-router'],
383
+ } as Record<string, string>,
324
384
  devDependencies: {
325
- '@vitejs/plugin-vue': 'latest',
326
- 'vite': 'latest',
327
- 'vue': 'latest',
385
+ '@vitejs/plugin-vue': versions['@vitejs/plugin-vue'],
386
+ 'vite': versions.vite,
328
387
  },
329
388
  }
330
389
 
@@ -370,25 +429,44 @@ export default defineWorkspace({
370
429
  writeFileSync(resolve(projectDir, 'bgl.config.ts'), bglConfigContent)
371
430
 
372
431
  // vite.config.ts
373
- const viteConfig = `import { defineConfig } from 'vite'
432
+ const viteConfig = `import { resolve } from 'node:path'
433
+ import { defineConfig } from 'vite'
374
434
  import vue from '@vitejs/plugin-vue'
375
- import { createViteProxy } from '@bagelink/workspace'
435
+ import { bagelink } from '@bagelink/workspace/vite'
376
436
  import workspace from './bgl.config'
377
437
 
378
- export default defineConfig(({ mode }) => {
379
- const config = workspace(mode as 'localhost' | 'development' | 'production')
380
-
381
- return {
382
- plugins: [vue()],
383
- server: {
384
- proxy: createViteProxy(config),
438
+ export default defineConfig({
439
+ resolve: {
440
+ alias: {
441
+ '@': resolve(__dirname, 'src'),
385
442
  },
386
- }
443
+ },
444
+ plugins: [
445
+ vue(),
446
+ bagelink({ workspace }),
447
+ ],
387
448
  })
388
449
  `
389
450
 
390
451
  writeFileSync(resolve(projectDir, 'vite.config.ts'), viteConfig)
391
452
 
453
+ // tsconfig.json
454
+ const tsconfigJson = {
455
+ extends: '../tsconfig.app.json',
456
+ compilerOptions: {
457
+ types: ['vite/client'],
458
+ paths: {
459
+ '@/*': ['./src/*'],
460
+ },
461
+ },
462
+ include: ['src/**/*'],
463
+ }
464
+
465
+ writeFileSync(
466
+ resolve(projectDir, 'tsconfig.json'),
467
+ `${JSON.stringify(tsconfigJson, null, 2)}\n`,
468
+ )
469
+
392
470
  // Create basic src structure
393
471
  const srcDir = resolve(projectDir, 'src')
394
472
  mkdirSync(srcDir, { recursive: true })
@@ -410,28 +488,60 @@ export default defineConfig(({ mode }) => {
410
488
 
411
489
  writeFileSync(resolve(projectDir, 'index.html'), indexHtml)
412
490
 
491
+ // src/router/index.ts
492
+ const routerDir = resolve(srcDir, 'router')
493
+ mkdirSync(routerDir, { recursive: true })
494
+ const routerTs = `import { createRouter, createWebHistory } from 'vue-router'
495
+ import HomeView from '../views/HomeView.vue'
496
+
497
+ export default createRouter({
498
+ history: createWebHistory(import.meta.env.BASE_URL),
499
+ routes: [
500
+ { path: '/', component: HomeView },
501
+ ],
502
+ })
503
+ `
504
+ writeFileSync(resolve(routerDir, 'index.ts'), routerTs)
505
+
506
+ // src/views/HomeView.vue
507
+ const viewsDir = resolve(srcDir, 'views')
508
+ mkdirSync(viewsDir, { recursive: true })
509
+ const homeView = `<script setup lang="ts">
510
+ ${isWorkspace ? 'import { formatDate } from \'shared/utils\'\n' : ''}
511
+ </script>
512
+
513
+ <template>
514
+ <main>
515
+ <h1>${name}</h1>
516
+ ${isWorkspace ? '<p>{{ formatDate(new Date()) }}</p>' : ''}
517
+ </main>
518
+ </template>
519
+ `
520
+ writeFileSync(resolve(viewsDir, 'HomeView.vue'), homeView)
521
+
413
522
  // main.ts
414
523
  const mainTs = `import { createApp } from 'vue'
524
+ import { createPinia } from 'pinia'
525
+ import { BagelVue } from '@bagelink/vue'
526
+ import router from './router'
415
527
  import App from './App.vue'
416
528
 
417
- createApp(App).mount('#app')
529
+ createApp(App)
530
+ .use(createPinia())
531
+ .use(router)
532
+ .use(BagelVue)
533
+ .mount('#app')
418
534
  `
419
535
 
420
536
  writeFileSync(resolve(srcDir, 'main.ts'), mainTs)
421
537
 
422
538
  // App.vue
423
539
  const appVue = `<script setup lang="ts">
424
- import { ref } from 'vue'
425
- ${isWorkspace ? 'import { formatDate } from \'shared/utils\'\n' : ''}
426
- const count = ref(0)
540
+ import { RouterView } from 'vue-router'
427
541
  </script>
428
542
 
429
543
  <template>
430
- <div>
431
- <h1>${name}</h1>
432
- <button @click="count++">Count: {{ count }}</button>
433
- ${isWorkspace ? '<p>{{ formatDate(new Date()) }}</p>' : ''}
434
- </div>
544
+ <RouterView />
435
545
  </template>
436
546
  `
437
547
 
@@ -484,7 +594,7 @@ function updateWorkspaceScripts(root: string, projectName: string): void {
484
594
  }
485
595
 
486
596
  /**
487
- * List all projects in workspace
597
+ * List all projects in workspace (sorted alphabetically for consistent ordering)
488
598
  */
489
599
  export function listProjects(root: string = process.cwd()): string[] {
490
600
  try {
@@ -494,10 +604,25 @@ export function listProjects(root: string = process.cwd()): string[] {
494
604
  item => item.isDirectory()
495
605
  && item.name !== 'node_modules'
496
606
  && item.name !== 'shared'
607
+ && item.name !== 'scripts'
497
608
  && item.name !== '.git'
498
609
  && !item.name.startsWith('.'),
499
610
  )
611
+ .filter((item) => {
612
+ // Only include directories with package.json that has a dev script
613
+ const packageJsonPath = resolve(root, item.name, 'package.json')
614
+ if (!existsSync(packageJsonPath)) return false
615
+
616
+ try {
617
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
618
+ return packageJson.scripts?.dev !== undefined
619
+ }
620
+ catch {
621
+ return false
622
+ }
623
+ })
500
624
  .map(item => item.name)
625
+ .sort() // Sort alphabetically for consistent port assignment
501
626
  }
502
627
  catch {
503
628
  return []
@@ -0,0 +1,23 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
6
+ "baseUrl": ".",
7
+ "paths": {
8
+ "@/*": [
9
+ "./src/*"
10
+ ],
11
+ "@shared/*": [
12
+ "../shared/*"
13
+ ]
14
+ }
15
+ },
16
+ "include": [
17
+ "src/**/*",
18
+ "src/**/*.vue"
19
+ ],
20
+ "exclude": [
21
+ "node_modules"
22
+ ]
23
+ }