Skip to content

Initial scaffold CLI project and templates - #1

Merged
lasalasa merged 3 commits into
mainfrom
feature/initialTemplates
Mar 22, 2026
Merged

lasalasa merged 3 commits into
mainfrom
feature/initialTemplates

Conversation

@lasalasa

Copy link
Copy Markdown
Member

No description provided.

Bootstrap the scaffold CLI project: add CLI entry, command handlers, generators, TypeScript types, utility helpers, and a comprehensive set of Handlebars templates for React, Next.js, Angular, Express, and FastAPI. Include project configuration and developer tooling (ESLint, Prettier, tsup, vitest, tsconfig), docs (CONTRIBUTING, PLUGIN_GUIDE, TEMPLATE_GUIDE), tests, and GitHub metadata (issue/PR templates and workflows for CI, release, and template testing). Also update README with usage and features and add a basic changesets config and package.json to support publishing.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR scaffolds an initial scaffold CLI project generator, including core CLI commands, generator framework, built-in templates (React/Next.js/Angular/Express/FastAPI + variants), and a test/docs/CI baseline.

Changes:

  • Added CLI commands (create, list, doctor) plus prompt flows and generator implementations.
  • Added a large set of Handlebars templates for multiple frameworks, shared configs, and optional features (CI/Docker/etc.).
  • Added Vitest-based unit/integration tests and GitHub Actions workflows (CI, release, template smoke tests), plus contributor docs.

Reviewed changes

Copilot reviewed 103 out of 105 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
vitest.config.ts Adds Vitest configuration for the repo test suite.
tsup.config.ts Adds tsup build configuration for bundling the CLI.
tsconfig.json Adds TypeScript compiler configuration for the CLI source.
tests/unit/prompts.test.ts Unit tests for validation utilities.
tests/unit/plugins.test.ts Unit tests for plugin registry behavior.
tests/unit/generators.test.ts Unit tests for generator selection + file emission checks.
tests/integration/scaffold.test.ts Integration tests executing the built CLI.
tests/fixtures/test-config.json Fixture config for “create from config” scenarios.
src/utils/validator.ts Project name, directory, and Node version validation utilities.
src/utils/logger.ts Logging/spinner helpers for CLI UX.
src/utils/index.ts Barrel exports for utility modules.
src/utils/file.ts File IO + template rendering + templates directory resolution.
src/types/plugin.ts Plugin interface/types.
src/types/index.ts Barrel exports for type definitions and defaults.
src/types/generator.ts Generator result type definition.
src/types/config.ts Config model/types and default config/feature flags.
src/templates/shared/tsconfig.base.json.hbs Shared base TS config template.
src/templates/shared/readme.md.hbs Shared README template for generated projects.
src/templates/shared/prettier.config.js.hbs Shared Prettier config template.
src/templates/shared/license.hbs Shared LICENSE template.
src/templates/shared/gitignore.hbs Shared .gitignore template.
src/templates/shared/github-ci.yml.hbs Shared GitHub Actions CI template for generated projects.
src/templates/shared/eslint.config.js.hbs Shared ESLint flat config template.
src/templates/shared/docker/docker-compose.yml.hbs Shared docker-compose template for generated projects.
src/templates/shared/docker/Dockerfile.hbs Shared Dockerfile template for generated projects.
src/templates/react/with-tailwind/tailwind.config.ts.hbs React Tailwind variant config template.
src/templates/react/with-tailwind/src/index.css.hbs React Tailwind variant CSS template.
src/templates/react/with-tailwind/postcss.config.js.hbs React Tailwind variant PostCSS config template.
src/templates/react/with-shadcn/src/lib/utils.ts.hbs React shadcn variant utility template.
src/templates/react/with-shadcn/components.json.hbs React shadcn variant components config template.
src/templates/react/base/vite.config.ts.hbs React base Vite config template (optionally with Vitest).
src/templates/react/base/tsconfig.json.hbs React base TS config template.
src/templates/react/base/tests/App.test.tsx.hbs React base test template.
src/templates/react/base/src/main.tsx.hbs React base entrypoint template.
src/templates/react/base/src/index.css.hbs React base CSS template.
src/templates/react/base/src/App.tsx.hbs React base App template.
src/templates/react/base/package.json.hbs React base package.json template.
src/templates/react/base/index.html.hbs React base HTML template.
src/templates/nextjs/with-prisma/src/lib/db.ts.hbs Next.js Prisma variant DB client template.
src/templates/nextjs/with-prisma/prisma/seed.ts.hbs Next.js Prisma seed template.
src/templates/nextjs/with-prisma/prisma/schema.prisma.hbs Next.js Prisma schema template.
src/templates/nextjs/with-auth/src/lib/auth.ts.hbs Next.js NextAuth variant auth config template.
src/templates/nextjs/with-auth/src/app/api/auth/[...nextauth]/route.ts.hbs Next.js NextAuth route handler template.
src/templates/nextjs/base/tsconfig.json.hbs Next.js base TS config template.
src/templates/nextjs/base/src/app/page.tsx.hbs Next.js base page template.
src/templates/nextjs/base/src/app/layout.tsx.hbs Next.js base layout template.
src/templates/nextjs/base/src/app/globals.css.hbs Next.js base global styles template (optionally Tailwind).
src/templates/nextjs/base/package.json.hbs Next.js base package.json template.
src/templates/nextjs/base/next.config.ts.hbs Next.js base config template.
src/templates/fastapi/base/requirements.txt.hbs FastAPI requirements template.
src/templates/fastapi/base/pyproject.toml.hbs FastAPI pyproject template.
src/templates/fastapi/base/app/routes/init.py.hbs FastAPI routes package marker.
src/templates/fastapi/base/app/models/init.py.hbs FastAPI models package marker.
src/templates/fastapi/base/app/main.py.hbs FastAPI app template.
src/templates/express/with-prisma/src/lib/db.ts.hbs Express Prisma variant DB client template.
src/templates/express/with-prisma/prisma/seed.ts.hbs Express Prisma seed template.
src/templates/express/with-prisma/prisma/schema.prisma.hbs Express Prisma schema template.
src/templates/express/base/tsconfig.json.hbs Express base TS config template.
src/templates/express/base/src/routes/index.ts.hbs Express base routes template.
src/templates/express/base/src/middleware/errorHandler.ts.hbs Express base error middleware template.
src/templates/express/base/src/index.ts.hbs Express base server entrypoint template.
src/templates/express/base/package.json.hbs Express base package.json template.
src/templates/angular/base/tsconfig.json.hbs Angular base TS config template.
src/templates/angular/base/src/main.ts.hbs Angular base bootstrap template.
src/templates/angular/base/src/app/app.config.ts.hbs Angular base application config template.
src/templates/angular/base/src/app/app.component.ts.hbs Angular base component template.
src/templates/angular/base/package.json.hbs Angular base package.json template.
src/templates/angular/base/angular.json.hbs Angular base angular.json template.
src/prompts/index.ts Orchestrates interactive prompts into a ProjectConfig.
src/prompts/framework.ts Prompts for project type + framework selection.
src/prompts/features.ts Prompts for feature flag selection.
src/prompts/config.ts Prompts for variant/package manager/git init.
src/plugins/types.ts Re-exports plugin types for plugin API consumers.
src/plugins/index.ts Plugin registry + plugin loading helpers.
src/index.ts CLI entrypoint that parses Commander program.
src/generators/react.ts React generator implementation.
src/generators/nextjs.ts Next.js generator implementation.
src/generators/index.ts Generator factory + base export.
src/generators/fullstack.ts Full-stack monorepo generator implementation.
src/generators/fastapi.ts FastAPI generator implementation (custom shared config behavior).
src/generators/express.ts Express generator implementation.
src/generators/base.ts Base generator pipeline (base/variant/shared/features/git).
src/generators/angular.ts Angular generator implementation.
src/constants.ts Framework metadata + variant display names.
src/commands/list.ts scaffold list command implementation.
src/commands/index.ts Commands barrel export.
src/commands/doctor.ts scaffold doctor dependency check command.
src/commands/create.ts scaffold create command implementation.
src/cli.ts Commander program wiring and global flags.
package.json Defines CLI package metadata, scripts, deps, and engine constraints.
eslint.config.js ESLint flat config for the repo.
docs/TEMPLATE_GUIDE.md Documentation for adding templates/variants.
docs/PLUGIN_GUIDE.md Documentation for plugin system usage/authoring.
docs/CONTRIBUTING.md Contributor setup + workflow documentation.
README.md Project README with usage and supported templates overview.
.prettierrc Repo Prettier configuration.
.github/workflows/template-test.yml Scheduled workflow to generate templates and smoke-check them.
.github/workflows/release.yml Tag-based npm publish + GitHub release workflow.
.github/workflows/ci.yml CI workflow (typecheck/lint/format/build/test).
.github/PULL_REQUEST_TEMPLATE.md PR template for contributors.
.github/ISSUE_TEMPLATE/new_template.yml Issue template for new template requests.
.github/ISSUE_TEMPLATE/feature_request.yml Issue template for feature requests.
.github/ISSUE_TEMPLATE/bug_report.yml Issue template for bug reports.
.changeset/config.json Changesets configuration for versioning/releases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/prompts/features.ts
Comment on lines +17 to +39
const choices: FeatureChoice[] = [
{
name: 'TypeScript',
value: 'typescript',
checked: true,
disabled: isPython ? 'N/A for Python' : false,
},
{ name: 'ESLint + Prettier', value: 'eslint', checked: !isPython, disabled: isPython ? 'N/A for Python' : false },
{ name: 'Tailwind CSS', value: 'tailwind', checked: false },
{ name: 'shadcn/ui components', value: 'shadcn', checked: false },
{ name: isPython ? 'Pytest testing' : 'Vitest testing', value: 'testing', checked: true },
{ name: 'GitHub Actions CI/CD', value: 'githubActions', checked: false },
{ name: 'Docker setup', value: 'docker', checked: false },
{ name: 'Husky pre-commit hooks', value: 'husky', checked: false, disabled: isPython ? 'N/A for Python' : false },
{ name: '.env example file', value: 'envExample', checked: false },
];

// Filter out frontend-only features for backend projects
const filteredChoices =
projectType === 'backend'
? choices.filter((c) => !['tailwind', 'shadcn'].includes(c.value))
: choices;

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

tailwind/shadcn are being collected as feature flags here, but the actual file overlays live under template variants (with-tailwind, with-shadcn) and are selected separately via promptVariant(). This can easily generate broken projects (e.g. base variant + features.tailwind=true => Tailwind deps but no config/CSS; or with-tailwind variant + features.tailwind=false => config files but missing deps). Consider removing these from the feature checklist and treating them purely as variants, or automatically derive features.tailwind/features.shadcn from the chosen variant (and/or constrain available variants based on chosen features).

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +41
{{else}}
FROM node:20-alpine AS base

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

{{#if (eq framework "nextjs")}}
FROM base AS builder
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]
{{else}}
RUN npm run build

EXPOSE 3000
CMD ["node", "dist/index.js"]
{{/if}}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The non-FastAPI/non-Next.js branch assumes every Node template builds a runnable dist/index.js (CMD ["node", "dist/index.js"]). That’s not true for frontend templates like React+Vite (output is static assets) or Angular, so enabling the docker feature will generate a Dockerfile that can’t start. Consider making Docker templates framework-specific (React/Angular should serve built static files) or restricting docker to frameworks where this Dockerfile is valid (e.g., Express).

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +46
const frontendConfig: ProjectConfig = {
...this.config,
type: 'frontend',
gitInit: false,
};

const frontendGen = this.createFrontendGenerator(frontendConfig, webDir);
await frontendGen.generate();

// Generate backend in apps/api
const backendConfig: ProjectConfig = {
...this.config,
type: 'backend',
framework: 'express',
gitInit: false,
};

const backendGen = new ExpressGenerator(backendConfig, apiDir);
await backendGen.generate();

// Write root package.json for workspaces
const rootPkgPath = path.join(this.targetDir, 'package.json');

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

FullstackGenerator calls generateBase() directly on the nested frontend/backend generators. That bypasses their generate() pipeline (variant overlay, shared configs like README/.gitignore/LICENSE, feature application like GitHub Actions/Docker, and git init). The resulting monorepo apps will be missing those expected files/configs. Consider invoking generate() for each sub-generator (with gitInit: false) or refactoring shared steps so fullstack generation applies the same stages.

Copilot uses AI. Check for mistakes.
Comment thread docs/PLUGIN_GUIDE.md
Comment on lines +73 to +79
## Loading Local Plugins

Use the `--plugin` flag to load a plugin from a local path:

```bash
scaffold create my-app --plugin ./my-local-plugin.js
```

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

This guide documents a --plugin flag for scaffold create, but the current CLI implementation does not define or handle any --plugin option, and plugin loading isn’t invoked from the command flow. Either implement the flag + plugin loading path, or update the docs to match the actual supported behavior.

Copilot uses AI. Check for mistakes.
Comment thread src/utils/validator.ts
Comment on lines +10 to +12
if (!VALID_PROJECT_NAME.test(name)) {
return 'Project name must be a valid npm package name (lowercase, no spaces, can use hyphens and dots).';
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The error message says project names "can use hyphens and dots", but VALID_PROJECT_NAME also allows _ and ~ (and scoped names). Either tighten the regex to match the documented rules, or update the message to reflect what’s actually accepted so users aren’t misled.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

This shadcn variant introduces imports from clsx and tailwind-merge, but the React base package.json.hbs does not include these dependencies under any feature/variant condition. The generated project will fail to compile. Add the required deps (and ensure Tailwind is enabled/installed for this variant) via a package.json.hbs overlay or conditional dependencies based on variant/feature flags.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +9
import { spinner, success, createDir, writeFile } from '../utils/index.js';
import type { ProjectConfig } from '../types/index.js';

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

There are unused imports here (renderAndWrite, getTemplatesDir) which will be flagged by TypeScript/ESLint depending on settings and add noise. Remove them or use them as intended.

Copilot uses AI. Check for mistakes.
Comment thread src/plugins/index.ts
Comment on lines +15 to +32
export async function loadPluginsFromNodeModules(): Promise<void> {
// Scan node_modules for scaffold-plugin-* packages
try {
const fs = await import('fs-extra');
const path = await import('node:path');
const nodeModulesDir = path.join(process.cwd(), 'node_modules');

if (!(await fs.pathExists(nodeModulesDir))) return;

const entries = await fs.readdir(nodeModulesDir);
const pluginDirs = entries.filter((e) => e.startsWith('scaffold-plugin-'));

for (const dir of pluginDirs) {
try {
const pluginPath = path.join(nodeModulesDir, dir);
const pluginModule = await import(pluginPath);
const plugin: ScaffoldPlugin = pluginModule.default || pluginModule;
registerPlugin(plugin);

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

loadPluginsFromNodeModules() builds an absolute directory path (.../node_modules/<pkg>) and passes it to import(). In Node ESM, importing a directory path typically throws ERR_UNSUPPORTED_DIR_IMPORT unless you resolve to a file (or use the package specifier). Additionally, this scan misses scoped packages (node_modules/@scope/scaffold-plugin-*). Prefer await import(dir) using the package name, and/or handle @scope/* directories when scanning.

Copilot uses AI. Check for mistakes.
Comment thread src/generators/base.ts
Comment on lines +138 to +140
} catch {
s.stop();
debug('Git initialization skipped (git not available)');

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The catch here treats any git failure as “git not available”. In practice git commit commonly fails due to missing user.name/user.email or an empty index, and silently skipping leaves users thinking git init worked. Consider catching errors separately (or logging the actual error in verbose mode) and adding a warning to result.warnings when git init/commit fails.

Suggested change
} catch {
s.stop();
debug('Git initialization skipped (git not available)');
} catch (error: unknown) {
s.stop();
const message =
error instanceof Error && error.message
? error.message
: String(error);
debug(`Git initialization failed: ${message}`);
this.result.warnings.push(
'Git initialization failed. The repository may not have been fully initialized. Please ensure git is installed and configured (e.g., user.name and user.email) and try again.',
);

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +9
import type { NextAuthOptions } from 'next-auth';
import GithubProvider from 'next-auth/providers/github';

export const authOptions: NextAuthOptions = {
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID ?? '',
clientSecret: process.env.GITHUB_SECRET ?? '',
}),

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

This template imports next-auth, but the generated project’s package.json (base) does not add next-auth to dependencies/devDependencies when the with-auth variant is selected. As-is, the generated Next.js project will fail to install/build. Add a package.json.hbs overlay in this variant (or conditionalize base package.json.hbs on variant) to include the required dependencies and any required scripts/env docs.

Copilot uses AI. Check for mistakes.
@lasalasa
lasalasa merged commit 43f5f39 into main Mar 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants