diff --git a/packages/shadcn/build.ts b/packages/shadcn/build.ts index 758bc36a..8edef679 100644 --- a/packages/shadcn/build.ts +++ b/packages/shadcn/build.ts @@ -1,11 +1,28 @@ +/** + * 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"; import { execSync } from "child_process"; +import pkgJson from "./package.json"; 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) { @@ -18,20 +35,23 @@ 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(); }); 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 00000000..ba01090f --- /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 b6363de1..e3dbf9e7 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -1,11 +1,15 @@ { "name": "@invertase/firebaseui-shadcn", - "private": true, - "version": "0.0.0", + "version": "0.0.2", "type": "module", + "bin": "./dist/bin/cli.js", + "files": [ + "dist" + ], "scripts": { + "prepare": "pnpm run build", "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 +28,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 +38,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/registry-spec.json b/packages/shadcn/registry-spec.json index 6f98e3b1..271bfcec 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.", diff --git a/packages/shadcn/tsup.config.ts b/packages/shadcn/tsup.config.ts new file mode 100644 index 00000000..b10a3c61 --- /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 f14c3ef9..12b2f614 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 2a623a70..1be27c10 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 6ab8df87..86e5aba5 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