From cd69de5e796d151b65c1fa64eeae7788b3d0c48f Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Thu, 13 Nov 2025 15:54:45 +0000 Subject: [PATCH 1/4] feat(shadcn): add build workflow --- packages/shadcn/build.ts | 22 ++++++++-- packages/shadcn/cli.ts | 79 ++++++++++++++++++++++++++++++++++ packages/shadcn/package.json | 12 ++++-- packages/shadcn/tsup.config.ts | 35 +++++++++++++++ packages/shadcn/vite.config.ts | 2 +- pnpm-lock.yaml | 13 +++--- pnpm-workspace.yaml | 2 +- 7 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 packages/shadcn/cli.ts create mode 100644 packages/shadcn/tsup.config.ts diff --git a/packages/shadcn/build.ts b/packages/shadcn/build.ts index 758bc36a8..5e669cb0e 100644 --- a/packages/shadcn/build.ts +++ b/packages/shadcn/build.ts @@ -1,3 +1,19 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import parser from "yargs-parser"; import fs from "fs"; import path from "path"; @@ -5,7 +21,7 @@ import { execSync } from "child_process"; const args = parser(process.argv.slice(2)); const domain = String(args.domain); -const publicDir = args.publicDir ? String(args.publicDir) : "public/r"; +const outDir = args.outDir ? String(args.outDir) : "dist/registry"; const isDev = !!args.dev; if (!domain) { @@ -24,14 +40,14 @@ replaced = replaced.replace(/{{\s*DEP\s*\|\s*([^}]+)\s*}}/g, (_, packageName) => }); fs.writeFileSync("registry.json", replaced, "utf8"); -const publicRDir = path.resolve(publicDir); +const publicRDir = path.resolve(outDir); if (fs.existsSync(publicRDir)) { execSync("rm -rf " + publicRDir, { stdio: "inherit" }); } try { try { - execSync(`./node_modules/.bin/shadcn build -o ${publicDir}`, { stdio: "inherit" }); + execSync(`./node_modules/.bin/shadcn build -o ${outDir}`, { stdio: "inherit" }); } catch (error) { console.error("shadcn build failed:", error); process.exit(1); diff --git a/packages/shadcn/cli.ts b/packages/shadcn/cli.ts new file mode 100644 index 000000000..ba01090f9 --- /dev/null +++ b/packages/shadcn/cli.ts @@ -0,0 +1,79 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { parseArgs } from "node:util"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const { values, positionals } = parseArgs({ + options: { + outDir: { + type: "string", + default: "./public-dev/r", + }, + }, + allowPositionals: true, +}); + +const command = positionals[0]; +const outputPath = positionals[1] || values.outDir; + +// Registry is at dist/registry, CLI is at dist/bin, so go up one level +const sourceDir = "../registry"; + +if (command === "copy") { + const sourcePath = path.resolve(__dirname, sourceDir); + + // Output path should be relative to where the user runs the command from + const destPath = path.resolve(process.cwd(), outputPath); + + // Check if source directory exists + if (!fs.existsSync(sourcePath)) { + console.error(`Error: Source directory "${sourcePath}" does not exist`); + process.exit(1); + } + + // Create destination directory if it doesn't exist + if (!fs.existsSync(destPath)) { + fs.mkdirSync(destPath, { recursive: true }); + } + + // Copy files from source to destination + const files = fs.readdirSync(sourcePath); + let copiedCount = 0; + + for (const file of files) { + const sourceFile = path.join(sourcePath, file); + const destFile = path.join(destPath, file); + + const stat = fs.statSync(sourceFile); + if (stat.isFile()) { + fs.copyFileSync(sourceFile, destFile); + copiedCount++; + console.log(`Copied: ${file}`); + } + } + + console.log(`\nSuccessfully copied ${copiedCount} item(s) from "${sourcePath}" to "${destPath}"`); +} else { + console.error(`Unknown command: ${command || "none"}`); + console.error("Usage: copy "); + process.exit(1); +} diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json index b6363de10..efe7ff08a 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -1,11 +1,14 @@ { "name": "@invertase/firebaseui-shadcn", - "private": true, - "version": "0.0.0", + "version": "0.0.1", "type": "module", + "bin": "./dist/bin/cli.js", + "files": [ + "dist" + ], "scripts": { "dev": "vite", - "build": "tsx build.ts --domain https://fir-ui-shadcn-registry.web.app", + "build": "tsup && tsx build.ts --domain https://fir-ui-shadcn-registry.web.app", "preview": "vite preview", "test": "vitest run" }, @@ -24,6 +27,7 @@ "react-dom": "catalog:", "shadcn": "2.9.3-canary.0", "tailwindcss": "catalog:", + "tsup": "catalog:", "tsx": "^4.20.6", "tw-animate-css": "^1.4.0", "typescript": "catalog:", @@ -33,9 +37,9 @@ "yargs-parser": "^22.0.0" }, "dependencies": { + "@hookform/resolvers": "^5.2.2", "@invertase/firebaseui-core": "workspace:*", "@invertase/firebaseui-react": "workspace:*", - "@hookform/resolvers": "^5.2.2", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.7", diff --git a/packages/shadcn/tsup.config.ts b/packages/shadcn/tsup.config.ts new file mode 100644 index 000000000..b10a3c616 --- /dev/null +++ b/packages/shadcn/tsup.config.ts @@ -0,0 +1,35 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["cli.ts"], + format: ["esm"], + outDir: "dist/bin", + sourcemap: true, + clean: true, + treeshake: true, + minify: false, + banner: { + js: "#!/usr/bin/env node", + }, + outExtension() { + return { + js: `.js`, + }; + }, +}); diff --git a/packages/shadcn/vite.config.ts b/packages/shadcn/vite.config.ts index f14c3ef93..12b2f6140 100644 --- a/packages/shadcn/vite.config.ts +++ b/packages/shadcn/vite.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ { name: "build registry", pattern: ["src/components/**/*.tsx", "registry-spec.json"], - run: ["tsx", "build.ts", "--domain", "http://localhost:5177", "--publicDir", "public-dev/r", "--dev"], + run: ["tsx", "build.ts", "--domain", "http://localhost:5177", "--outDir", "public-dev/r", "--dev"], }, ]), ], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a623a709..1be27c105 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,7 +91,7 @@ catalogs: specifier: ^4.1.13 version: 4.1.16 tsup: - specifier: ^8.5.0 + specifier: 8.5.0 version: 8.5.0 typescript: specifier: ^5.9.2 @@ -980,6 +980,9 @@ importers: tailwindcss: specifier: 'catalog:' version: 4.1.16 + tsup: + specifier: 'catalog:' + version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -20369,11 +20372,11 @@ snapshots: vite@7.1.11(@types/node@20.19.24)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.6): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.3 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 20.19.24 @@ -20387,11 +20390,11 @@ snapshots: vite@7.1.11(@types/node@24.9.2)(jiti@2.6.1)(less@4.4.0)(lightningcss@1.30.2)(sass@1.90.0)(terser@5.43.1)(tsx@4.20.6): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.11 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.3 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.9.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6ab8df87e..86e5aba51 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -32,7 +32,7 @@ catalog: rimraf: ^6.0.1 rxjs: ^7.8.2 tailwindcss: ^4.1.13 - tsup: ^8.5.0 + tsup: 8.5.0 typescript: ^5.9.2 vite: ^7.1.5 vitest: ^3.2.4 From 81b525609b70bce162165a6bda8cade2878870a7 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Thu, 13 Nov 2025 16:00:17 +0000 Subject: [PATCH 2/4] chore: add shadcn prepare script --- packages/shadcn/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json index efe7ff08a..dc13b5901 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -7,6 +7,7 @@ "dist" ], "scripts": { + "prepare": "pnpm run build", "dev": "vite", "build": "tsup && tsx build.ts --domain https://fir-ui-shadcn-registry.web.app", "preview": "vite preview", From 7ca8664080a886047ba28e047982a3599994acb5 Mon Sep 17 00:00:00 2001 From: Elliot Hesp Date: Thu, 13 Nov 2025 16:15:45 +0000 Subject: [PATCH 3/4] feat: add version to registry items --- packages/shadcn/build.ts | 4 ++ packages/shadcn/registry-spec.json | 87 ++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/packages/shadcn/build.ts b/packages/shadcn/build.ts index 5e669cb0e..8edef679d 100644 --- a/packages/shadcn/build.ts +++ b/packages/shadcn/build.ts @@ -18,6 +18,7 @@ import parser from "yargs-parser"; import fs from "fs"; import path from "path"; import { execSync } from "child_process"; +import pkgJson from "./package.json"; const args = parser(process.argv.slice(2)); const domain = String(args.domain); @@ -34,6 +35,9 @@ const registryRaw = fs.readFileSync(registryPath, "utf8"); let replaced = registryRaw.replace(/{{\s*DOMAIN\s*}}/g, domain); +// Replace version placeholder +replaced = replaced.replace(/{{\s*VERSION\s*}}/g, pkgJson.version); + // Replace dependency placeholder based on dev flag replaced = replaced.replace(/{{\s*DEP\s*\|\s*([^}]+)\s*}}/g, (_, packageName) => { return isDev ? `${packageName.trim()}@workspace:*` : packageName.trim(); diff --git a/packages/shadcn/registry-spec.json b/packages/shadcn/registry-spec.json index 6f98e3b1c..271bfcec4 100644 --- a/packages/shadcn/registry-spec.json +++ b/packages/shadcn/registry-spec.json @@ -5,6 +5,9 @@ "items": [ { "name": "apple-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Apple Sign In Button", "description": "A button component for Apple OAuth authentication.", @@ -35,6 +38,9 @@ }, { "name": "country-selector", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Country Selector", "description": "A country selector component for phone number input with country codes and flags.", @@ -49,6 +55,9 @@ }, { "name": "email-link-auth-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Email Link Auth Form", "description": "A form allowing users to sign in via email link.", @@ -63,6 +72,9 @@ }, { "name": "email-link-auth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Email Link Auth Screen", "description": "A screen allowing users to sign in via email link.", @@ -82,6 +94,9 @@ }, { "name": "facebook-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Facebook Sign In Button", "description": "A button component for Facebook OAuth authentication.", @@ -105,6 +120,9 @@ }, { "name": "forgot-password-auth-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Forgot Password Auth Form", "description": "A form allowing users to reset their password via email.", @@ -119,6 +137,9 @@ }, { "name": "forgot-password-auth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Forgot Password Auth Screen", "description": "A screen allowing users to reset their password via email.", @@ -133,6 +154,9 @@ }, { "name": "github-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "GitHub Sign In Button", "description": "A button component for GitHub OAuth authentication.", @@ -163,6 +187,9 @@ }, { "name": "google-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Google Sign In Button", "description": "A button component for Google OAuth authentication.", @@ -198,6 +225,9 @@ }, { "name": "microsoft-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Microsoft Sign In Button", "description": "A button component for Microsoft OAuth authentication.", @@ -228,6 +258,9 @@ }, { "name": "multi-factor-auth-assertion-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Multi-Factor Auth Assertion Form", "description": "A form allowing users to complete multi-factor authentication during sign-in with TOTP or SMS options.", @@ -246,6 +279,9 @@ }, { "name": "multi-factor-auth-assertion-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Multi-Factor Auth Assertion Screen", "description": "A screen allowing users to complete multi-factor authentication during sign-in with TOTP or SMS options.", @@ -260,6 +296,9 @@ }, { "name": "multi-factor-auth-enrollment-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Multi-Factor Auth Enrollment Form", "description": "A form allowing users to select and configure multi-factor authentication methods.", @@ -278,6 +317,9 @@ }, { "name": "multi-factor-auth-enrollment-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Multi-Factor Auth Enrollment Screen", "description": "A screen allowing users to set up multi-factor authentication with TOTP or SMS options.", @@ -292,6 +334,9 @@ }, { "name": "oauth-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "OAuth Button", "description": "A button component for OAuth authentication providers.", @@ -306,6 +351,9 @@ }, { "name": "oauth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "OAuth Screen", "description": "A screen allowing users to sign in with OAuth providers.", @@ -325,6 +373,9 @@ }, { "name": "phone-auth-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Phone Auth Form", "description": "A form allowing users to authenticate using their phone number with SMS verification.", @@ -345,6 +396,9 @@ }, { "name": "phone-auth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Phone Auth Screen", "description": "A screen allowing users to authenticate using their phone number with SMS verification.", @@ -365,6 +419,9 @@ }, { "name": "policies", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Policies", "description": "A component allowing users to navigate to the terms of service and privacy policy.", @@ -378,6 +435,9 @@ }, { "name": "redirect-error", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Redirect Error", "description": "A component that displays redirect errors from Firebase UI authentication flow.", @@ -391,6 +451,9 @@ }, { "name": "sign-in-auth-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Sign In Auth Form", "description": "A form allowing users to sign in with email and password.", @@ -405,6 +468,9 @@ }, { "name": "sign-in-auth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Sign In Auth Screen", "description": "A screen allowing users to sign in with email and password.", @@ -424,6 +490,9 @@ }, { "name": "sign-up-auth-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Sign Up Auth Form", "description": "A form allowing users to sign up with email and password.", @@ -438,6 +507,9 @@ }, { "name": "sign-up-auth-screen", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Sign Up Auth Screen", "description": "A screen allowing users to sign up with email and password.", @@ -457,6 +529,9 @@ }, { "name": "sms-multi-factor-assertion-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "SMS Multi-Factor Assertion Form", "description": "A form allowing users to complete SMS-based multi-factor authentication during sign-in.", @@ -471,6 +546,9 @@ }, { "name": "sms-multi-factor-enrollment-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "SMS Multi-Factor Enrollment Form", "description": "A form allowing users to enroll SMS-based multi-factor authentication.", @@ -485,6 +563,9 @@ }, { "name": "totp-multi-factor-assertion-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "TOTP Multi-Factor Assertion Form", "description": "A form allowing users to complete TOTP-based multi-factor authentication during sign-in.", @@ -499,6 +580,9 @@ }, { "name": "totp-multi-factor-enrollment-form", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "TOTP Multi-Factor Enrollment Form", "description": "A form allowing users to enroll TOTP-based multi-factor authentication with QR code generation.", @@ -513,6 +597,9 @@ }, { "name": "twitter-sign-in-button", + "meta": { + "version": "{{ VERSION }}" + }, "type": "registry:block", "title": "Twitter Sign In Button", "description": "A button component for Twitter OAuth authentication.", From e2bd78d3f17353de3914489e393dc9aa4fd8c0e2 Mon Sep 17 00:00:00 2001 From: Darren Ackers Date: Thu, 13 Nov 2025 16:17:45 +0000 Subject: [PATCH 4/4] chore(shadcn): release package registry --- packages/shadcn/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json index dc13b5901..e3dbf9e79 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -1,6 +1,6 @@ { "name": "@invertase/firebaseui-shadcn", - "version": "0.0.1", + "version": "0.0.2", "type": "module", "bin": "./dist/bin/cli.js", "files": [