@brightbase/blocks 0.1.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.
Files changed (3) hide show
  1. package/README.md +51 -0
  2. package/index.mjs +195 -0
  3. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # @brightbase/blocks
2
+
3
+ CLI for installing blocks from the bbai-blocks registry into a consumer bbai project.
4
+
5
+ ## Install (once)
6
+
7
+ The package is **private** on npm. You need:
8
+ 1. An npm Pro/Teams account on the `@brightbase` org
9
+ 2. To be logged in: `npm login`
10
+
11
+ Then in your consumer project, blocks install with:
12
+
13
+ ```bash
14
+ BBAI_BLOCKS_TOKEN=<your-token> npx @brightbase/blocks add <slug>
15
+ ```
16
+
17
+ The token also gates the catalog UI at https://bbai-blocks.vercel.app — same value for both.
18
+
19
+ Tip: put `BBAI_BLOCKS_TOKEN` in your shell rc or `.env.local` and source it so you don't have to type it every install.
20
+
21
+ ## What it does
22
+
23
+ ```
24
+ npx @brightbase/blocks add bbase-hero47
25
+ ```
26
+
27
+ 1. Fetches metadata + source from `bbai-blocks.vercel.app/r/<slug>` (with auth)
28
+ 2. Runs `npx shadcn@latest add` to write the component file and any `_shared/` deps
29
+ 3. Appends a lazy loader to `src/components/preview/preview-registry.ts`
30
+ 4. Upserts the slug into the `custom` category of `design/block-manifest.json`
31
+
32
+ After install, the block appears under "Custom" in the Studio block library.
33
+
34
+ ## Publish (maintainers only)
35
+
36
+ ```bash
37
+ cd cli
38
+ npm login # if not already
39
+ npm publish # publishes as restricted (requires npm Pro)
40
+ ```
41
+
42
+ To cut a new version, bump `version` in `package.json` and re-publish.
43
+
44
+ ## Options
45
+
46
+ ```
47
+ npx @brightbase/blocks add <slug>
48
+ npx @brightbase/blocks add <slug> --registry <url> # use a different registry origin
49
+ npx @brightbase/blocks --help
50
+ npx @brightbase/blocks --version
51
+ ```
package/index.mjs ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bbai-blocks — install blocks from the bbai-blocks registry.
4
+ *
5
+ * Wraps `npx shadcn@latest add` with post-install wiring:
6
+ * 1. Fetches metadata from the registry
7
+ * 2. Runs shadcn to write component + _shared deps
8
+ * 3. Appends lazy loader to preview-registry.ts
9
+ * 4. Upserts slug into the 'custom' category of block-manifest.json
10
+ *
11
+ * Usage:
12
+ * npx bbai-blocks add <slug>
13
+ * npx bbai-blocks add <slug> --registry <url>
14
+ */
15
+
16
+ import { readFile, writeFile } from 'fs/promises'
17
+ import { execSync } from 'child_process'
18
+ import { join } from 'path'
19
+
20
+ const REGISTRY_DEFAULT = 'https://bbai-blocks.vercel.app'
21
+ const PREVIEW_REGISTRY_PATH = 'src/components/preview/preview-registry.ts'
22
+ const MANIFEST_PATH = 'design/block-manifest.json'
23
+
24
+ const VERSION = '0.1.0'
25
+
26
+ // ── Arg parsing ──────────────────────────────────────────────────────────────
27
+
28
+ const argv = process.argv.slice(2)
29
+
30
+ if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
31
+ printHelp()
32
+ process.exit(0)
33
+ }
34
+
35
+ if (argv.includes('--version') || argv.includes('-v')) {
36
+ console.log(VERSION)
37
+ process.exit(0)
38
+ }
39
+
40
+ const command = argv[0]
41
+ const rest = argv.slice(1)
42
+
43
+ if (command !== 'add') {
44
+ console.error(`Unknown command: ${command}`)
45
+ printHelp()
46
+ process.exit(1)
47
+ }
48
+
49
+ const slug = rest.find((a) => !a.startsWith('--'))
50
+ const registryFlag = rest.indexOf('--registry')
51
+ const registryBase = registryFlag !== -1 ? rest[registryFlag + 1] : REGISTRY_DEFAULT
52
+ // --auth=<token> overrides BBAI_BLOCKS_TOKEN env var
53
+ const authArg = rest.find((a) => a.startsWith('--auth='))
54
+ const authToken = authArg ? authArg.slice('--auth='.length) : undefined
55
+
56
+ if (!slug) {
57
+ console.error('Missing slug. Usage: bbai-blocks add <slug>')
58
+ process.exit(1)
59
+ }
60
+
61
+ if (!/^[a-z0-9-]+$/.test(slug)) {
62
+ console.error(`Invalid slug: ${slug}`)
63
+ process.exit(1)
64
+ }
65
+
66
+ await runAdd(slug, registryBase, authToken)
67
+
68
+ // ── Commands ─────────────────────────────────────────────────────────────────
69
+
70
+ async function runAdd(slug, registryBase, authToken) {
71
+ // --auth flag wins; falls back to BBAI_BLOCKS_TOKEN env var
72
+ const token = authToken || process.env.BBAI_BLOCKS_TOKEN
73
+ const baseUrl = `${registryBase}/r/${slug}`
74
+ // Token in query param so shadcn's plain fetch can authenticate
75
+ const registryUrl = token ? `${baseUrl}?token=${encodeURIComponent(token)}` : baseUrl
76
+
77
+ console.log(`\nFetching ${baseUrl}${token ? ' (with token)' : ''}…`)
78
+ let meta
79
+ try {
80
+ const res = await fetch(baseUrl, {
81
+ headers: token ? { Authorization: `Bearer ${token}` } : {},
82
+ })
83
+ if (res.status === 401) {
84
+ console.error('Unauthorized. Pass --auth=<token> or set BBAI_BLOCKS_TOKEN.')
85
+ process.exit(1)
86
+ }
87
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
88
+ meta = await res.json()
89
+ } catch (err) {
90
+ console.error(`Failed to fetch ${baseUrl}: ${err.message}`)
91
+ process.exit(1)
92
+ }
93
+
94
+ const label = meta.title ?? slug
95
+ const mainFile = meta.files?.[0]
96
+ const exportName = mainFile?.content ? parseExportName(mainFile.content, slug) : toExportName(slug)
97
+ console.log(` label: ${label}`)
98
+ console.log(` exportName: ${exportName}`)
99
+
100
+ console.log(`\nInstalling via shadcn…`)
101
+ try {
102
+ execSync(`npx shadcn@latest add "${registryUrl}" --overwrite`, { stdio: 'inherit' })
103
+ } catch {
104
+ // shadcn exits non-zero on some prompts even when successful — continue
105
+ }
106
+
107
+ await wirePreviewRegistry(slug, exportName, mainFile?.path)
108
+ await wireBlockManifest(slug, label)
109
+
110
+ console.log(`\nDone. ${slug} is installed and wired.\n`)
111
+ }
112
+
113
+ async function wirePreviewRegistry(slug, exportName, installPath) {
114
+ console.log(`\nUpdating ${PREVIEW_REGISTRY_PATH}…`)
115
+ const path = join(process.cwd(), PREVIEW_REGISTRY_PATH)
116
+ let source = await readFile(path, 'utf-8')
117
+
118
+ if (source.includes(`'${slug}'`)) {
119
+ console.log(` already present — skipping`)
120
+ return
121
+ }
122
+
123
+ const filePath = installPath ?? `src/components/_preview/_bbase/${slug}.tsx`
124
+ const importPath = filePath
125
+ .replace(/^src\/components\//, '../')
126
+ .replace(/\.tsx$/, '')
127
+
128
+ const entry = ` '${slug}': () => import('${importPath}').then(m => m.${exportName}),\n`
129
+
130
+ const closingBrace = source.lastIndexOf('}')
131
+ if (closingBrace === -1) {
132
+ console.error(' Could not find closing brace — add entry manually')
133
+ return
134
+ }
135
+
136
+ source = source.slice(0, closingBrace) + entry + source.slice(closingBrace)
137
+ await writeFile(path, source, 'utf-8')
138
+ console.log(` added: ${entry.trim()}`)
139
+ }
140
+
141
+ async function wireBlockManifest(slug, label) {
142
+ console.log(`\nUpdating ${MANIFEST_PATH}…`)
143
+ const path = join(process.cwd(), MANIFEST_PATH)
144
+ const manifest = JSON.parse(await readFile(path, 'utf-8'))
145
+
146
+ let customCat = manifest.categories?.find((c) => c.name === 'custom')
147
+ if (!customCat) {
148
+ customCat = { name: 'custom', label: 'Custom', description: 'Project-specific blocks', blocks: [] }
149
+ manifest.categories = manifest.categories ?? []
150
+ manifest.categories.push(customCat)
151
+ }
152
+
153
+ if (customCat.blocks.some((b) => b.slug === slug)) {
154
+ console.log(` already present in custom category — skipping`)
155
+ return
156
+ }
157
+
158
+ customCat.blocks.push({ slug, label, free: true, source: 'bbase' })
159
+ await writeFile(path, JSON.stringify(manifest, null, 2) + '\n', 'utf-8')
160
+ console.log(` added ${slug} to custom category`)
161
+ }
162
+
163
+ // ── Helpers ──────────────────────────────────────────────────────────────────
164
+
165
+ function toExportName(s) {
166
+ return s.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('')
167
+ }
168
+
169
+ function parseExportName(source, slug) {
170
+ const m = source.match(/export\s+(?:default\s+)?function\s+([A-Z][A-Za-z0-9]+)/)
171
+ return m ? m[1] : toExportName(slug)
172
+ }
173
+
174
+ function printHelp() {
175
+ console.log(`bbai-blocks v${VERSION}
176
+
177
+ Install blocks from the bbai-blocks registry.
178
+
179
+ Usage:
180
+ npx bbai-blocks add <slug>
181
+ npx bbai-blocks add <slug> --registry <url>
182
+
183
+ Options:
184
+ --auth=<token> Auth token. Overrides BBAI_BLOCKS_TOKEN env var.
185
+ Common shell-expansion form: --auth=$BBAI_BLOCKS_TOKEN
186
+ --registry <url> Override the registry origin (default: ${REGISTRY_DEFAULT})
187
+ --version, -v Print version
188
+ --help, -h Print this help
189
+
190
+ Env vars:
191
+ BBAI_BLOCKS_TOKEN Used when --auth is not passed. Sent as Authorization:
192
+ Bearer header (direct fetch) and ?token= query param
193
+ (shadcn's fetch).
194
+ `)
195
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@brightbase/blocks",
3
+ "version": "0.1.0",
4
+ "description": "CLI for installing blocks from the bbai-blocks registry into a bbai project",
5
+ "type": "module",
6
+ "bin": {
7
+ "bbai-blocks": "./index.mjs"
8
+ },
9
+ "files": [
10
+ "index.mjs",
11
+ "README.md"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "keywords": [
17
+ "bbai",
18
+ "shadcn",
19
+ "blocks",
20
+ "registry"
21
+ ],
22
+ "license": "UNLICENSED",
23
+ "private": false,
24
+ "publishConfig": {
25
+ "access": "restricted"
26
+ }
27
+ }