create-z3 0.0.11 → 0.0.14
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/dist/index.js +325 -54
- package/package.json +4 -2
- package/templates/nextjs/README.md +242 -18
- package/templates/nextjs/components.json +24 -0
- package/templates/nextjs/convex/_generated/api.d.ts +65 -0
- package/templates/nextjs/convex/_generated/api.js +23 -0
- package/templates/nextjs/convex/_generated/dataModel.d.ts +60 -0
- package/templates/nextjs/convex/_generated/server.d.ts +143 -0
- package/templates/nextjs/convex/_generated/server.js +93 -0
- package/templates/nextjs/convex/auth/adapter/index.ts +222 -0
- package/templates/nextjs/convex/auth/adapter/utils.ts +597 -0
- package/templates/nextjs/convex/auth/api.ts +8 -0
- package/templates/nextjs/convex/auth/config.ts +10 -0
- package/templates/nextjs/convex/auth/db.ts +299 -0
- package/templates/nextjs/convex/auth/index.ts +54 -0
- package/templates/nextjs/convex/auth/plugins/index.ts +9 -0
- package/templates/nextjs/convex/auth/sessions.ts +60 -0
- package/templates/nextjs/convex/convex.config.ts +5 -0
- package/templates/nextjs/convex/http.ts +28 -0
- package/templates/nextjs/convex/schema.ts +69 -0
- package/templates/nextjs/eslint.config.mjs +157 -0
- package/templates/nextjs/next.config.ts +10 -0
- package/templates/nextjs/package.json +43 -11
- package/templates/nextjs/pnpm-workspace.yaml +5 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/favicons/favicon.ico +0 -0
- package/templates/nextjs/public/file.svg +1 -0
- package/templates/nextjs/public/globe.svg +1 -0
- package/templates/nextjs/public/next.svg +1 -0
- package/templates/nextjs/public/vercel.svg +1 -0
- package/templates/nextjs/public/window.svg +1 -0
- package/templates/nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +13 -0
- package/templates/nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
- package/templates/nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
- package/templates/nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
- package/templates/nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +13 -0
- package/templates/nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
- package/templates/nextjs/src/app/(frontend)/globals.css +65 -0
- package/templates/nextjs/src/app/(frontend)/layout.tsx +53 -0
- package/templates/nextjs/src/app/(frontend)/page.tsx +5 -0
- package/templates/nextjs/src/app/api/auth/[...all]/route.ts +6 -0
- package/templates/nextjs/src/app/favicon.ico +0 -0
- package/templates/nextjs/src/auth/client.tsx +50 -0
- package/templates/nextjs/src/auth/server.ts +54 -0
- package/templates/nextjs/src/auth/types.ts +3 -0
- package/templates/nextjs/src/auth/utils.ts +33 -0
- package/templates/nextjs/src/components/component-example.tsx +501 -0
- package/templates/nextjs/src/components/example.tsx +54 -0
- package/templates/nextjs/src/components/providers/client.tsx +13 -0
- package/templates/nextjs/src/components/providers/convex.tsx +34 -0
- package/templates/nextjs/src/components/providers/server.tsx +14 -0
- package/templates/nextjs/src/components/ui/alert-dialog.tsx +175 -0
- package/templates/nextjs/src/components/ui/badge.tsx +48 -0
- package/templates/nextjs/src/components/ui/button.tsx +53 -0
- package/templates/nextjs/src/components/ui/card.tsx +94 -0
- package/templates/nextjs/src/components/ui/combobox.tsx +292 -0
- package/templates/nextjs/src/components/ui/dialog.tsx +151 -0
- package/templates/nextjs/src/components/ui/dropdown-menu.tsx +254 -0
- package/templates/nextjs/src/components/ui/field.tsx +227 -0
- package/templates/nextjs/src/components/ui/input-group.tsx +149 -0
- package/templates/nextjs/src/components/ui/input.tsx +20 -0
- package/templates/nextjs/src/components/ui/label.tsx +20 -0
- package/templates/nextjs/src/components/ui/select.tsx +192 -0
- package/templates/nextjs/src/components/ui/separator.tsx +25 -0
- package/templates/nextjs/src/components/ui/textarea.tsx +18 -0
- package/templates/nextjs/src/db/constants/auth.ts +6 -0
- package/templates/nextjs/src/db/constants/index.ts +45 -0
- package/templates/nextjs/src/env.mjs +48 -0
- package/templates/nextjs/src/lib/auth/server.ts +15 -0
- package/templates/nextjs/src/lib/utils.ts +6 -0
- package/templates/nextjs/src/proxy.ts +21 -0
- package/templates/nextjs/tsconfig.json +42 -0
- package/templates/tanstack-start/convex/auth/adapter/index.ts +1 -1
- package/templates/tanstack-start/convex/auth/index.ts +1 -1
- package/templates/tanstack-start/package.json +1 -0
- package/templates/nextjs/src/.gitkeep +0 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { select, input, checkbox, confirm, Separator } from "@inquirer/prompts";
|
|
|
6
6
|
import chalk2 from "chalk";
|
|
7
7
|
import { readFileSync } from "fs";
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
|
-
import { dirname as dirname2, join as
|
|
9
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
10
10
|
|
|
11
11
|
// src/utils/validation.ts
|
|
12
12
|
import validateNpmPackageName from "validate-npm-package-name";
|
|
@@ -72,7 +72,10 @@ async function copyTemplate(framework, targetPath) {
|
|
|
72
72
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
73
73
|
const __dirname2 = dirname(__filename2);
|
|
74
74
|
const templateDir = framework === "tanstack" ? "tanstack-start" : "nextjs";
|
|
75
|
-
|
|
75
|
+
let templatePath = join(__dirname2, "../../templates", templateDir);
|
|
76
|
+
if (!fs2.existsSync(templatePath)) {
|
|
77
|
+
templatePath = join(__dirname2, "../templates", templateDir);
|
|
78
|
+
}
|
|
76
79
|
await fs2.copy(templatePath, targetPath, {
|
|
77
80
|
overwrite: false,
|
|
78
81
|
errorOnExist: false
|
|
@@ -1725,25 +1728,25 @@ function getAdditionalProviders() {
|
|
|
1725
1728
|
|
|
1726
1729
|
// src/installers/string-utils.ts
|
|
1727
1730
|
import fs3 from "fs-extra";
|
|
1728
|
-
var DEFAULT_THEME = `--background: 0 0
|
|
1729
|
-
--foreground:
|
|
1730
|
-
--card: 0 0
|
|
1731
|
-
--card-foreground:
|
|
1732
|
-
--popover: 0 0
|
|
1733
|
-
--popover-foreground:
|
|
1734
|
-
--primary:
|
|
1735
|
-
--primary-foreground: 0 0
|
|
1736
|
-
--secondary:
|
|
1737
|
-
--secondary-foreground:
|
|
1738
|
-
--muted:
|
|
1739
|
-
--muted-foreground:
|
|
1740
|
-
--accent:
|
|
1741
|
-
--accent-foreground:
|
|
1742
|
-
--destructive:
|
|
1743
|
-
--destructive-foreground: 0 0
|
|
1744
|
-
--border:
|
|
1745
|
-
--input:
|
|
1746
|
-
--ring:
|
|
1731
|
+
var DEFAULT_THEME = `--background: 100% 0.000 0;
|
|
1732
|
+
--foreground: 3.9% 0.006 240;
|
|
1733
|
+
--card: 100% 0.000 0;
|
|
1734
|
+
--card-foreground: 3.9% 0.006 240;
|
|
1735
|
+
--popover: 100% 0.000 0;
|
|
1736
|
+
--popover-foreground: 3.9% 0.006 240;
|
|
1737
|
+
--primary: 10% 0.003 240;
|
|
1738
|
+
--primary-foreground: 98% 0.000 0;
|
|
1739
|
+
--secondary: 95.9% 0.002 240;
|
|
1740
|
+
--secondary-foreground: 10% 0.003 240;
|
|
1741
|
+
--muted: 95.9% 0.002 240;
|
|
1742
|
+
--muted-foreground: 46.1% 0.002 240;
|
|
1743
|
+
--accent: 95.9% 0.002 240;
|
|
1744
|
+
--accent-foreground: 10% 0.003 240;
|
|
1745
|
+
--destructive: 60.2% 0.168 0;
|
|
1746
|
+
--destructive-foreground: 98% 0.000 0;
|
|
1747
|
+
--border: 90% 0.003 240;
|
|
1748
|
+
--input: 90% 0.003 240;
|
|
1749
|
+
--ring: 10% 0.003 240;
|
|
1747
1750
|
--radius: 0.5rem;`;
|
|
1748
1751
|
function detectIndentation(line) {
|
|
1749
1752
|
const match = line.match(/^(\s*)/);
|
|
@@ -1927,6 +1930,129 @@ import { join as join2 } from "path";
|
|
|
1927
1930
|
import { execa } from "execa";
|
|
1928
1931
|
import ora from "ora";
|
|
1929
1932
|
import crypto from "crypto";
|
|
1933
|
+
|
|
1934
|
+
// src/utils/tweakcn-converter.ts
|
|
1935
|
+
import fs4 from "fs-extra";
|
|
1936
|
+
import convert from "color-convert";
|
|
1937
|
+
async function convertTweakCNToOKLCH(options) {
|
|
1938
|
+
const { source, format = "oklch" } = options;
|
|
1939
|
+
const cssContent = await fetchOrReadCSS(source);
|
|
1940
|
+
const colors = parseColorsFromCSS(cssContent);
|
|
1941
|
+
const convertedColors = colors.map((color) => ({
|
|
1942
|
+
...color,
|
|
1943
|
+
oklch: convertColorToOKLCH(color.value)
|
|
1944
|
+
}));
|
|
1945
|
+
return generateCSSOutput(convertedColors, format);
|
|
1946
|
+
}
|
|
1947
|
+
async function fetchOrReadCSS(source) {
|
|
1948
|
+
if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
1949
|
+
try {
|
|
1950
|
+
const response = await fetch(source);
|
|
1951
|
+
if (!response.ok) {
|
|
1952
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
1953
|
+
}
|
|
1954
|
+
return await response.text();
|
|
1955
|
+
} catch (error) {
|
|
1956
|
+
throw new Error(
|
|
1957
|
+
`Failed to fetch CSS from URL: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
try {
|
|
1962
|
+
return await fs4.readFile(source, "utf-8");
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
throw new Error(
|
|
1965
|
+
`Failed to read CSS from file: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
function parseColorsFromCSS(css) {
|
|
1970
|
+
const colors = [];
|
|
1971
|
+
const cssVarRegex = /--([\w-]+)\s*:\s*([^;]+);/g;
|
|
1972
|
+
let match;
|
|
1973
|
+
while ((match = cssVarRegex.exec(css)) !== null) {
|
|
1974
|
+
const name = `--${match[1]}`;
|
|
1975
|
+
const value = match[2].trim();
|
|
1976
|
+
if (isColorValue(value)) {
|
|
1977
|
+
colors.push({ name, value });
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return colors;
|
|
1981
|
+
}
|
|
1982
|
+
function isColorValue(value) {
|
|
1983
|
+
if (value.startsWith("#")) {
|
|
1984
|
+
return true;
|
|
1985
|
+
}
|
|
1986
|
+
if (value.startsWith("rgb(") || value.startsWith("rgba(")) {
|
|
1987
|
+
return true;
|
|
1988
|
+
}
|
|
1989
|
+
if (value.startsWith("hsl(") || value.startsWith("hsla(")) {
|
|
1990
|
+
return true;
|
|
1991
|
+
}
|
|
1992
|
+
if (value.startsWith("oklch(")) {
|
|
1993
|
+
return true;
|
|
1994
|
+
}
|
|
1995
|
+
if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(value)) {
|
|
1996
|
+
return true;
|
|
1997
|
+
}
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
2000
|
+
function convertColorToOKLCH(colorValue) {
|
|
2001
|
+
const trimmed = colorValue.trim();
|
|
2002
|
+
if (trimmed.startsWith("oklch(")) {
|
|
2003
|
+
const match = trimmed.match(/oklch\(([\d.]+%?)\s+([\d.]+)\s+([\d.]+)\)/);
|
|
2004
|
+
if (match) {
|
|
2005
|
+
return `${match[1]} ${match[2]} ${match[3]}`;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
let rgb;
|
|
2009
|
+
try {
|
|
2010
|
+
if (trimmed.startsWith("#")) {
|
|
2011
|
+
rgb = convert.hex.rgb(trimmed.slice(1));
|
|
2012
|
+
} else if (trimmed.startsWith("rgb(") || trimmed.startsWith("rgba(")) {
|
|
2013
|
+
const match = trimmed.match(/rgba?\((\d+),?\s*(\d+),?\s*(\d+)/);
|
|
2014
|
+
if (!match) throw new Error("Invalid RGB format");
|
|
2015
|
+
rgb = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
|
|
2016
|
+
} else if (trimmed.startsWith("hsl(") || trimmed.startsWith("hsla(")) {
|
|
2017
|
+
const match = trimmed.match(/hsla?\((\d+),?\s*(\d+)%?,?\s*(\d+)%?/);
|
|
2018
|
+
if (!match) throw new Error("Invalid HSL format");
|
|
2019
|
+
const hsl2 = [
|
|
2020
|
+
parseInt(match[1]),
|
|
2021
|
+
parseInt(match[2]),
|
|
2022
|
+
parseInt(match[3])
|
|
2023
|
+
];
|
|
2024
|
+
rgb = convert.hsl.rgb(hsl2);
|
|
2025
|
+
} else if (/^\d+(\.\d+)?\s+\d+(\.\d+)?%\s+\d+(\.\d+)?%$/.test(trimmed)) {
|
|
2026
|
+
const parts = trimmed.split(/\s+/);
|
|
2027
|
+
const h2 = parseFloat(parts[0]);
|
|
2028
|
+
const s = parseFloat(parts[1].replace("%", ""));
|
|
2029
|
+
const l2 = parseFloat(parts[2].replace("%", ""));
|
|
2030
|
+
rgb = convert.hsl.rgb([h2, s, l2]);
|
|
2031
|
+
} else {
|
|
2032
|
+
return trimmed;
|
|
2033
|
+
}
|
|
2034
|
+
const hsl = convert.rgb.hsl(rgb);
|
|
2035
|
+
const l = hsl[2];
|
|
2036
|
+
const c = hsl[1] / 100 * 0.4;
|
|
2037
|
+
const h = hsl[0];
|
|
2038
|
+
return `${l}% ${c.toFixed(3)} ${h}`;
|
|
2039
|
+
} catch (error) {
|
|
2040
|
+
console.warn(`Failed to convert color "${colorValue}": ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2041
|
+
return trimmed;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
function generateCSSOutput(colors, format) {
|
|
2045
|
+
if (colors.length === 0) {
|
|
2046
|
+
return "";
|
|
2047
|
+
}
|
|
2048
|
+
const declarations = colors.map((color) => {
|
|
2049
|
+
const value = format === "oklch" && color.oklch ? color.oklch : color.value;
|
|
2050
|
+
return `${color.name}: ${value};`;
|
|
2051
|
+
}).join("\n");
|
|
2052
|
+
return declarations;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
// src/installers/base.ts
|
|
1930
2056
|
var FrameworkInstaller = class {
|
|
1931
2057
|
/**
|
|
1932
2058
|
* Constructor for FrameworkInstaller
|
|
@@ -1947,24 +2073,22 @@ var FrameworkInstaller = class {
|
|
|
1947
2073
|
}
|
|
1948
2074
|
/**
|
|
1949
2075
|
* Detect the package manager used to invoke the CLI
|
|
1950
|
-
* Checks
|
|
2076
|
+
* Checks environment variables set by package managers
|
|
1951
2077
|
*
|
|
1952
|
-
* @returns Detected package manager,
|
|
2078
|
+
* @returns Detected package manager ('npm', 'yarn', 'pnpm', or 'bun')
|
|
1953
2079
|
*/
|
|
1954
2080
|
detectPackageManager() {
|
|
1955
|
-
const userAgent = process.env.npm_config_user_agent
|
|
1956
|
-
if (userAgent
|
|
1957
|
-
return "pnpm";
|
|
1958
|
-
|
|
1959
|
-
return "
|
|
1960
|
-
} else if (userAgent.includes("bun")) {
|
|
1961
|
-
return "bun";
|
|
2081
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
2082
|
+
if (userAgent) {
|
|
2083
|
+
if (userAgent.includes("pnpm")) return "pnpm";
|
|
2084
|
+
if (userAgent.includes("yarn")) return "yarn";
|
|
2085
|
+
if (userAgent.includes("bun")) return "bun";
|
|
1962
2086
|
}
|
|
1963
2087
|
return "npm";
|
|
1964
2088
|
}
|
|
1965
2089
|
/**
|
|
1966
2090
|
* Install project dependencies using detected package manager
|
|
1967
|
-
* Shows progress
|
|
2091
|
+
* Shows progress spinner during installation
|
|
1968
2092
|
*/
|
|
1969
2093
|
async installDependencies() {
|
|
1970
2094
|
const packageManager = this.detectPackageManager();
|
|
@@ -1974,21 +2098,21 @@ var FrameworkInstaller = class {
|
|
|
1974
2098
|
cwd: this.targetPath,
|
|
1975
2099
|
stdio: "pipe"
|
|
1976
2100
|
});
|
|
1977
|
-
spinner.succeed(
|
|
2101
|
+
spinner.succeed("Dependencies installed successfully");
|
|
1978
2102
|
} catch (error) {
|
|
1979
|
-
spinner.fail(
|
|
2103
|
+
spinner.fail("Failed to install dependencies");
|
|
1980
2104
|
throw new Error(
|
|
1981
2105
|
`Dependency installation failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1982
2106
|
);
|
|
1983
2107
|
}
|
|
1984
2108
|
}
|
|
1985
2109
|
/**
|
|
1986
|
-
* Format
|
|
1987
|
-
*
|
|
2110
|
+
* Format code using project's formatter (Prettier via package.json script)
|
|
2111
|
+
* Non-blocking - continues even if formatting fails
|
|
1988
2112
|
*/
|
|
1989
2113
|
async formatCode() {
|
|
1990
2114
|
const packageManager = this.detectPackageManager();
|
|
1991
|
-
const spinner = ora("Formatting
|
|
2115
|
+
const spinner = ora("Formatting code...").start();
|
|
1992
2116
|
try {
|
|
1993
2117
|
await execa(packageManager, ["run", "format"], {
|
|
1994
2118
|
cwd: this.targetPath,
|
|
@@ -2072,13 +2196,15 @@ Make sure you are authenticated with GitHub CLI (run "gh auth login")`
|
|
|
2072
2196
|
return crypto.randomBytes(32).toString("hex");
|
|
2073
2197
|
}
|
|
2074
2198
|
/**
|
|
2075
|
-
* Fetch TweakCN theme CSS from URL
|
|
2199
|
+
* Fetch TweakCN theme CSS from URL and convert to OKLCH format
|
|
2076
2200
|
* Handles network errors and provides retry suggestions
|
|
2201
|
+
* Optionally converts fetched theme through OKLCH converter for consistent color format
|
|
2077
2202
|
*
|
|
2078
2203
|
* @param url - URL to fetch theme from
|
|
2079
|
-
* @
|
|
2204
|
+
* @param convertToOklch - Whether to convert theme colors to OKLCH format (default: true)
|
|
2205
|
+
* @returns Theme CSS content in OKLCH format (if conversion enabled)
|
|
2080
2206
|
*/
|
|
2081
|
-
async fetchThemeFromUrl(url) {
|
|
2207
|
+
async fetchThemeFromUrl(url, convertToOklch = true) {
|
|
2082
2208
|
const spinner = ora("Fetching TweakCN theme...").start();
|
|
2083
2209
|
try {
|
|
2084
2210
|
const response = await fetch(url);
|
|
@@ -2087,6 +2213,23 @@ Make sure you are authenticated with GitHub CLI (run "gh auth login")`
|
|
|
2087
2213
|
}
|
|
2088
2214
|
const content = await response.text();
|
|
2089
2215
|
spinner.succeed("TweakCN theme fetched");
|
|
2216
|
+
if (convertToOklch) {
|
|
2217
|
+
const convertSpinner = ora("Converting theme to OKLCH format...").start();
|
|
2218
|
+
try {
|
|
2219
|
+
const convertedContent = await convertTweakCNToOKLCH({
|
|
2220
|
+
source: url,
|
|
2221
|
+
format: "oklch"
|
|
2222
|
+
});
|
|
2223
|
+
convertSpinner.succeed("Theme converted to OKLCH format");
|
|
2224
|
+
return convertedContent;
|
|
2225
|
+
} catch (convertError) {
|
|
2226
|
+
convertSpinner.warn("OKLCH conversion failed, using raw theme CSS");
|
|
2227
|
+
console.warn(
|
|
2228
|
+
`Warning: Failed to convert theme to OKLCH: ${convertError instanceof Error ? convertError.message : "Unknown error"}`
|
|
2229
|
+
);
|
|
2230
|
+
return content;
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2090
2233
|
return content;
|
|
2091
2234
|
} catch (error) {
|
|
2092
2235
|
spinner.fail("Failed to fetch TweakCN theme");
|
|
@@ -2111,15 +2254,17 @@ Please check the URL and your internet connection, then try again.`
|
|
|
2111
2254
|
copySpinner.fail("Failed to copy template files");
|
|
2112
2255
|
throw error;
|
|
2113
2256
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2257
|
+
const authSpinner = ora("Configuring authentication...").start();
|
|
2258
|
+
try {
|
|
2259
|
+
await this.updateOAuthConfig(options.oauthProviders, options.emailPasswordAuth);
|
|
2260
|
+
if (options.emailPasswordAuth || options.oauthProviders.length > 0) {
|
|
2118
2261
|
authSpinner.succeed("Authentication configuration updated");
|
|
2119
|
-
}
|
|
2120
|
-
authSpinner.
|
|
2121
|
-
throw error;
|
|
2262
|
+
} else {
|
|
2263
|
+
authSpinner.succeed("Authentication placeholders cleaned up");
|
|
2122
2264
|
}
|
|
2265
|
+
} catch (error) {
|
|
2266
|
+
authSpinner.fail("Failed to configure authentication");
|
|
2267
|
+
throw error;
|
|
2123
2268
|
}
|
|
2124
2269
|
const oauthUISpinner = ora("Configuring OAuth UI...").start();
|
|
2125
2270
|
try {
|
|
@@ -2175,16 +2320,17 @@ Please check the URL and your internet connection, then try again.`
|
|
|
2175
2320
|
if (options.tweakcnTheme) {
|
|
2176
2321
|
if (options.tweakcnTheme.type === "url") {
|
|
2177
2322
|
themeContent = await this.fetchThemeFromUrl(options.tweakcnTheme.content);
|
|
2323
|
+
} else if (options.tweakcnTheme.type === "css") {
|
|
2324
|
+
themeContent = options.tweakcnTheme.content;
|
|
2178
2325
|
} else {
|
|
2179
2326
|
themeContent = options.tweakcnTheme.content;
|
|
2180
2327
|
}
|
|
2328
|
+
await this.applyTweakCNTheme(themeContent);
|
|
2329
|
+
themeSpinner.succeed("TweakCN theme applied");
|
|
2181
2330
|
} else {
|
|
2182
|
-
|
|
2331
|
+
await this.applyTweakCNTheme(DEFAULT_THEME);
|
|
2332
|
+
themeSpinner.succeed("Default theme applied");
|
|
2183
2333
|
}
|
|
2184
|
-
await this.applyTweakCNTheme(themeContent);
|
|
2185
|
-
themeSpinner.succeed(
|
|
2186
|
-
options.tweakcnTheme ? "TweakCN theme applied" : "Default theme applied"
|
|
2187
|
-
);
|
|
2188
2334
|
} catch (error) {
|
|
2189
2335
|
themeSpinner.fail("Failed to apply theme");
|
|
2190
2336
|
throw error;
|
|
@@ -2194,7 +2340,6 @@ Please check the URL and your internet connection, then try again.`
|
|
|
2194
2340
|
}
|
|
2195
2341
|
if (options.installDependencies) {
|
|
2196
2342
|
await this.installDependencies();
|
|
2197
|
-
await this.formatCode();
|
|
2198
2343
|
}
|
|
2199
2344
|
}
|
|
2200
2345
|
};
|
|
@@ -2328,11 +2473,137 @@ var TanStackInstaller = class extends FrameworkInstaller {
|
|
|
2328
2473
|
}
|
|
2329
2474
|
};
|
|
2330
2475
|
|
|
2476
|
+
// src/installers/nextjs.ts
|
|
2477
|
+
import { join as join3 } from "path";
|
|
2478
|
+
var NextJSInstaller = class extends FrameworkInstaller {
|
|
2479
|
+
/**
|
|
2480
|
+
* Framework identifier for Next.js
|
|
2481
|
+
*/
|
|
2482
|
+
get frameworkName() {
|
|
2483
|
+
return "nextjs";
|
|
2484
|
+
}
|
|
2485
|
+
/**
|
|
2486
|
+
* Update OAuth configuration in Convex auth file
|
|
2487
|
+
* Target file: convex/auth/index.ts (SAME as TanStack)
|
|
2488
|
+
* Placeholders: // {{EMAIL_PASSWORD_AUTH}} and // {{OAUTH_PROVIDERS}}
|
|
2489
|
+
*
|
|
2490
|
+
* @param selectedProviders - Array of provider IDs to configure
|
|
2491
|
+
* @param emailPasswordEnabled - Whether email/password authentication is enabled
|
|
2492
|
+
*/
|
|
2493
|
+
async updateOAuthConfig(selectedProviders, emailPasswordEnabled) {
|
|
2494
|
+
const authFilePath = join3(this.targetPath, "convex/auth/index.ts");
|
|
2495
|
+
const authProvidersBlock = generateAuthProvidersBlock(
|
|
2496
|
+
selectedProviders,
|
|
2497
|
+
emailPasswordEnabled
|
|
2498
|
+
);
|
|
2499
|
+
await replacePlaceholder(
|
|
2500
|
+
authFilePath,
|
|
2501
|
+
"// {{OAUTH_PROVIDERS}}",
|
|
2502
|
+
authProvidersBlock
|
|
2503
|
+
);
|
|
2504
|
+
await replacePlaceholder(
|
|
2505
|
+
authFilePath,
|
|
2506
|
+
"// {{EMAIL_PASSWORD_AUTH}}",
|
|
2507
|
+
"",
|
|
2508
|
+
{ graceful: true }
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Update OAuth UI configuration in auth client file
|
|
2513
|
+
* Target file: src/auth/client.tsx (DIFFERENT from TanStack: src/providers.tsx)
|
|
2514
|
+
* Placeholder: // {{OAUTH_UI_PROVIDERS}}
|
|
2515
|
+
*
|
|
2516
|
+
* @param selectedProviders - Array of provider IDs to configure
|
|
2517
|
+
*/
|
|
2518
|
+
async updateOAuthUIConfig(selectedProviders) {
|
|
2519
|
+
const providersFilePath = join3(this.targetPath, "src/auth/client.tsx");
|
|
2520
|
+
const uiConfigBlock = generateOAuthUIProvidersBlock(selectedProviders);
|
|
2521
|
+
await replacePlaceholder(
|
|
2522
|
+
providersFilePath,
|
|
2523
|
+
"// {{OAUTH_UI_PROVIDERS}}",
|
|
2524
|
+
uiConfigBlock
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
/**
|
|
2528
|
+
* Update .env.example with OAuth environment variables
|
|
2529
|
+
* Target file: .env.example (SAME as TanStack)
|
|
2530
|
+
* Placeholder: # {{ENV_OAUTH_VARS}}
|
|
2531
|
+
* Applies NEXT_PUBLIC_ prefix for client-side variables (DIFFERENT parameter from TanStack)
|
|
2532
|
+
*
|
|
2533
|
+
* @param selectedProviders - Array of provider IDs to configure
|
|
2534
|
+
*/
|
|
2535
|
+
async updateEnvExample(selectedProviders) {
|
|
2536
|
+
const envFilePath = join3(this.targetPath, ".env.example");
|
|
2537
|
+
const envVarsBlock = generateEnvVarsBlock(selectedProviders, "nextjs");
|
|
2538
|
+
await replacePlaceholder(
|
|
2539
|
+
envFilePath,
|
|
2540
|
+
"# {{ENV_OAUTH_VARS}}",
|
|
2541
|
+
envVarsBlock
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Update README with OAuth provider setup guides
|
|
2546
|
+
* Target file: README.md (SAME as TanStack)
|
|
2547
|
+
* Placeholder: <!-- {{OAUTH_SETUP_GUIDE}} -->
|
|
2548
|
+
* Handles missing placeholder gracefully with warning
|
|
2549
|
+
*
|
|
2550
|
+
* @param selectedProviders - Array of provider IDs to configure
|
|
2551
|
+
*/
|
|
2552
|
+
async updateReadme(selectedProviders) {
|
|
2553
|
+
const readmeFilePath = join3(this.targetPath, "README.md");
|
|
2554
|
+
const readmeSection = generateReadmeSection(selectedProviders);
|
|
2555
|
+
await replacePlaceholder(
|
|
2556
|
+
readmeFilePath,
|
|
2557
|
+
"<!-- {{OAUTH_SETUP_GUIDE}} -->",
|
|
2558
|
+
readmeSection,
|
|
2559
|
+
{ graceful: true }
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Apply TweakCN theme to global CSS file
|
|
2564
|
+
* Target file: src/app/(frontend)/globals.css (DIFFERENT from TanStack: src/styles/globals.css)
|
|
2565
|
+
* Placeholder: CSS comment with TWEAKCN_THEME variable
|
|
2566
|
+
*
|
|
2567
|
+
* @param themeContent - CSS content to apply
|
|
2568
|
+
*/
|
|
2569
|
+
async applyTweakCNTheme(themeContent) {
|
|
2570
|
+
const cssFilePath = join3(this.targetPath, "src/app/(frontend)/globals.css");
|
|
2571
|
+
await replacePlaceholder(
|
|
2572
|
+
cssFilePath,
|
|
2573
|
+
"/* {{TWEAKCN_THEME}} */",
|
|
2574
|
+
themeContent
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* Update env.mjs with OAuth provider environment variables
|
|
2579
|
+
* Target file: src/env.mjs (DIFFERENT from TanStack: src/env.ts - Next.js uses env.mjs)
|
|
2580
|
+
* Placeholders: // {{OAUTH_ENV_SERVER_SCHEMA}} and // {{OAUTH_ENV_RUNTIME_MAPPING}}
|
|
2581
|
+
* Adds zod schema validation and runtime mappings for OAuth credentials
|
|
2582
|
+
*
|
|
2583
|
+
* @param selectedProviders - Array of provider IDs to configure
|
|
2584
|
+
*/
|
|
2585
|
+
async updateEnvTs(selectedProviders) {
|
|
2586
|
+
const envFilePath = join3(this.targetPath, "src/env.mjs");
|
|
2587
|
+
const serverSchema = generateEnvTsServerSchema(selectedProviders);
|
|
2588
|
+
await replacePlaceholder(
|
|
2589
|
+
envFilePath,
|
|
2590
|
+
"// {{OAUTH_ENV_SERVER_SCHEMA}}",
|
|
2591
|
+
serverSchema
|
|
2592
|
+
);
|
|
2593
|
+
const runtimeMapping = generateEnvTsRuntimeMapping(selectedProviders);
|
|
2594
|
+
await replacePlaceholder(
|
|
2595
|
+
envFilePath,
|
|
2596
|
+
"// {{OAUTH_ENV_RUNTIME_MAPPING}}",
|
|
2597
|
+
runtimeMapping
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
|
|
2331
2602
|
// src/index.ts
|
|
2332
2603
|
var __filename = fileURLToPath2(import.meta.url);
|
|
2333
2604
|
var __dirname = dirname2(__filename);
|
|
2334
2605
|
var packageJson = JSON.parse(
|
|
2335
|
-
readFileSync(
|
|
2606
|
+
readFileSync(join4(__dirname, "../package.json"), "utf-8")
|
|
2336
2607
|
);
|
|
2337
2608
|
var program = new Command();
|
|
2338
2609
|
async function promptOAuthProviders() {
|
|
@@ -2488,7 +2759,7 @@ program.name("create-z3").version(packageJson.version).description("CLI for scaf
|
|
|
2488
2759
|
if (framework === "tanstack") {
|
|
2489
2760
|
installer = new TanStackInstaller(createdPath, projectName);
|
|
2490
2761
|
} else {
|
|
2491
|
-
|
|
2762
|
+
installer = new NextJSInstaller(createdPath, projectName);
|
|
2492
2763
|
}
|
|
2493
2764
|
try {
|
|
2494
2765
|
await installer.initProject(projectOptions);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-z3",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CLI for scaffolding Z3 Stack applications (TanStack/Next.js + Convex + Better Auth)",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"ora": "^8.1.0",
|
|
26
26
|
"execa": "^9.5.2",
|
|
27
27
|
"sort-package-json": "^2.10.0",
|
|
28
|
-
"validate-npm-package-name": "^5.0.0"
|
|
28
|
+
"validate-npm-package-name": "^5.0.0",
|
|
29
|
+
"color-convert": "^2.0.1"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"typescript": "^5.7.0",
|
|
@@ -33,6 +34,7 @@
|
|
|
33
34
|
"@types/node": "^22.0.0",
|
|
34
35
|
"@types/fs-extra": "^11.0.0",
|
|
35
36
|
"@types/validate-npm-package-name": "^4.0.0",
|
|
37
|
+
"@types/color-convert": "^2.0.3",
|
|
36
38
|
"vitest": "^2.0.0"
|
|
37
39
|
},
|
|
38
40
|
"engines": {
|