Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/auto-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
run: |
# Configure npm to use GitHub Packages registry
echo "@PrabothCharith:registry=https://npm.pkg.github.com" > .npmrc
echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
# Temporarily change the package name to the scoped version for GPR
npm pkg set name="@PrabothCharith/nxt-gen-cli"
# Publish using the GH_TOKEN
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nxt-gen-cli",
"version": "2.1.1",
"version": "2.1.2",
"description": "The ultimate Next.js scaffold CLI generator. Customize your stack with Prisma, React Query, Shadcn, HeroUI, and more in seconds.",
"main": "dist/index.js",
"type": "module",
Expand Down
30 changes: 30 additions & 0 deletions src/lib/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,33 @@ export async function configureTailwindForHeroUI(projectPath: string) {

await sourceFile.save();
}

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function configureGlobalCssForHeroUI lacks documentation. Adding a JSDoc comment would help explain its purpose, parameters, return value, and any assumptions it makes (such as the expected structure of globals.css or the relationship between hero.ts location and CSS file location).

Suggested change
/**
* Configures the global Tailwind CSS setup for HeroUI in a Next.js app.
*
* This function locates `src/app/globals.css` under the given project root,
* and ensures that it uses the Tailwind CSS v4-style `@import` syntax and the
* HeroUI-specific `@plugin` and `@source` directives. If the HeroUI plugin
* directive (`@plugin './hero.ts';`) is already present, the file is left
* unchanged to avoid duplicate configuration.
*
* Assumptions:
* - `globals.css` is located at `src/app/globals.css` relative to
* `projectPath`.
* - The HeroUI plugin entry file `hero.ts` is in the same directory as
* `globals.css`, hence the `./hero.ts` plugin path.
* - The HeroUI theme files are available at
* `node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}` relative to the
* project root.
*
* If `globals.css` does not exist, the function exits without making changes.
*
* @param projectPath Absolute or relative path to the root of the Next.js project.
* @returns A promise that resolves when `globals.css` has been updated (or no-op).
*/

Copilot uses AI. Check for mistakes.
export async function configureGlobalCssForHeroUI(projectPath: string) {
const cssPath = path.join(projectPath, "src/app/globals.css");
if (!fs.existsSync(cssPath)) return;

let content = await fs.readFile(cssPath, "utf-8");

// Check if it's already configured to avoid duplication
if (content.includes("@plugin './hero.ts';")) return;

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duplication check only looks for the exact string @plugin './hero.ts'; but doesn't check for other variations that might indicate the file has already been configured, such as @plugin "./hero.ts" (double quotes) or @plugin './hero.ts' (with different spacing). This could lead to duplicate configuration being added if the existing configuration uses slightly different formatting.

Suggested change
if (content.includes("@plugin './hero.ts';")) return;
const heroPluginPattern = /@plugin\s+["']\.\/hero\.ts["']\s*;?/;
if (heroPluginPattern.test(content)) return;

Copilot uses AI. Check for mistakes.

// Replace default tailwind import with v4 setup
// We assume standard create-next-app output which usually starts with directives
// Or just prepend/replace the top part.

const v4Setup = `@import "tailwindcss";
@plugin './hero.ts';
Comment on lines +187 to +194

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @source directive uses a relative path that starts from src/app/globals.css and navigates up to node_modules. However, if the hero.ts file is created at the project root (as done in scaffold.ts line 618), the path relationship may be inconsistent. The @plugin directive references './hero.ts' which is relative to the CSS file location, but hero.ts is created at the project root, not at src/app/hero.ts. This path mismatch will cause the plugin to not be found at runtime.

Suggested change
if (content.includes("@plugin './hero.ts';")) return;
// Replace default tailwind import with v4 setup
// We assume standard create-next-app output which usually starts with directives
// Or just prepend/replace the top part.
const v4Setup = `@import "tailwindcss";
@plugin './hero.ts';
if (content.includes("@plugin '../../hero.ts';")) return;
// Replace default tailwind import with v4 setup
// We assume standard create-next-app output which usually starts with directives
// Or just prepend/replace the top part.
const v4Setup = `@import "tailwindcss";
@plugin '../../hero.ts';

Copilot uses AI. Check for mistakes.
@source '../../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
@custom-variant dark (&:is(.dark *));

`;

if (content.includes('@import "tailwindcss";')) {
content = content.replace('@import "tailwindcss";', v4Setup);

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The String.prototype.replace() method only replaces the first occurrence by default. If the globals.css file contains multiple instances of @import "tailwindcss"; (which could happen in edge cases or user-modified files), only the first one would be replaced, potentially leaving duplicate or conflicting imports in the file.

Suggested change
content = content.replace('@import "tailwindcss";', v4Setup);
let replaced = false;
content = content.replace(/@import "tailwindcss";/g, () => {
if (!replaced) {
replaced = true;
return v4Setup;
}
return "";
});

Copilot uses AI. Check for mistakes.
} else {
// Fallback: Prepend if no standard import found (though unlikely in fresh v4 app)
content = v4Setup + content;

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the fallback case where no standard Tailwind import is found, the v4 setup is prepended to the entire existing content. This could result in the new directives being inserted in the middle of an existing CSS rule or comment block, potentially breaking the CSS. Consider adding validation to ensure the content structure is appropriate before prepending, or add a newline separator to reduce the risk of CSS syntax errors.

Suggested change
content = v4Setup + content;
const separator =
content.startsWith("\n") || content.startsWith("\r\n") ? "" : "\n";
content = v4Setup + separator + content;

Copilot uses AI. Check for mistakes.
}

await fs.writeFile(cssPath, content);
}
40 changes: 21 additions & 19 deletions src/scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ import {
getDlxCommand,
PackageManager,
} from "./lib/pm.js";
import { addProviderToLayout, configureTailwindForHeroUI } from "./lib/ast.js";
import {
addProviderToLayout,
configureTailwindForHeroUI,
configureGlobalCssForHeroUI,
} from "./lib/ast.js";
import { DependencyCollector } from "./lib/deps.js";
import prompts from "prompts";

Expand Down Expand Up @@ -244,6 +248,13 @@ export const scaffoldProject = async (
} catch (error) {
spinner.fail("Installation failed");
console.log(chalk.red("\nError:"), error);

console.log(chalk.yellow("\nTroubleshooting Tips:"));
console.log(chalk.white(" • If you see 'EACCES' or permission errors, try running:"));
console.log(chalk.cyan(" sudo chown -R $(whoami) ~/.npm"));
console.log(chalk.white(" • If you see 'EEXIST' or cache errors, try:"));
console.log(chalk.cyan(" npm cache clean --force"));
Comment on lines +252 to +256

Copilot AI Jan 3, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The troubleshooting tips only mention npm-specific commands (npm cache clean --force) but the error handler is used regardless of the package manager. Users who encounter errors with yarn, pnpm, or bun will see npm-specific advice that may not apply to their package manager. Consider making the troubleshooting tips package-manager aware using the pm variable.

Suggested change
console.log(chalk.yellow("\nTroubleshooting Tips:"));
console.log(chalk.white(" • If you see 'EACCES' or permission errors, try running:"));
console.log(chalk.cyan(" sudo chown -R $(whoami) ~/.npm"));
console.log(chalk.white(" • If you see 'EEXIST' or cache errors, try:"));
console.log(chalk.cyan(" npm cache clean --force"));
const ownershipDir =
pm === "yarn"
? "~/.cache/yarn"
: pm === "pnpm"
? "~/.pnpm-store"
: pm === "bun"
? "~/.bun"
: "~/.npm";
const cacheCommand =
pm === "yarn"
? "yarn cache clean"
: pm === "pnpm"
? "pnpm store prune"
: pm === "bun"
? "bun install --force"
: "npm cache clean --force";
console.log(chalk.yellow("\nTroubleshooting Tips:"));
console.log(
chalk.white(" • If you see 'EACCES' or permission errors, try running:")
);
console.log(
chalk.cyan(` sudo chown -R $(whoami) ${ownershipDir}`)
);
console.log(
chalk.white(" • If you see 'EEXIST' or cache errors, try:")
);
console.log(chalk.cyan(` ${cacheCommand}`));

Copilot uses AI. Check for mistakes.

console.log(chalk.yellow("\nYou can install dependencies manually:"));
console.log(chalk.cyan(` cd ${projectName}`));
console.log(chalk.cyan(` ${pm} install`));
Expand Down Expand Up @@ -601,26 +612,17 @@ export function cn(...inputs: ClassValue[]) {
const configExists = await fs.pathExists(tailwindConfigPath);

if (!configExists) {
// Create a fresh config compatible with HeroUI
// Assuming Tailwind v4 if config is missing
// 1. Create hero.ts
await fs.writeFile(
tailwindConfigPath,
`
import type { Config } from "tailwindcss";
import {heroui} from '@heroui/react';

const config: Config = {
content: [
"./src/**/*.{js,ts,jsx,tsx,mdx}",
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}"
],
theme: {
extend: {},
},
plugins: [heroui()],
};
export default config;
`
path.join(projectPath, "hero.ts"),
`import { heroui } from "@heroui/react";

export default heroui();`
);

// 2. Configure globals.css for v4
await configureGlobalCssForHeroUI(projectPath);
} else {
await configureTailwindForHeroUI(projectPath);
}
Expand Down