@yousxlfs/next-arch 0.1.1 → 0.2.0
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 +581 -84
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/app/package.json +25 -4
- package/templates/packages/better-auth/examples/src/features/_examples/with-better-auth/lib/auth-placeholder.ts +14 -0
- package/templates/packages/env/core/src/shared/config/env.ts +22 -0
- package/templates/packages/jotai/core/src/shared/providers/JotaiProvider.tsx +19 -0
- package/templates/packages/jotai/examples/src/features/_examples/with-jotai/README.md +3 -0
- package/templates/packages/jotai/examples/src/features/_examples/with-jotai/model/example.atoms.ts +19 -0
- package/templates/packages/motion/core/src/shared/lib/motion.ts +17 -0
- package/templates/packages/motion/examples/src/features/_examples/with-motion/components/ExampleMotionCard.tsx +19 -0
- package/templates/packages/next-intl/core/src/shared/config/i18n.ts +10 -0
- package/templates/packages/nuqs/examples/src/features/_examples/with-nuqs/README.md +3 -0
- package/templates/packages/nuqs/examples/src/features/_examples/with-nuqs/hooks/use-example-params.ts +19 -0
- package/templates/packages/react-hook-form/examples/src/features/_examples/with-react-hook-form/README.md +1 -0
- package/templates/packages/react-hook-form/examples/src/features/_examples/with-react-hook-form/components/ExampleRhfForm.tsx +40 -0
- package/templates/packages/redux/core/src/app/providers/redux-store.ts +15 -0
- package/templates/packages/redux/core/src/shared/providers/ReduxProvider.tsx +24 -0
- package/templates/packages/redux/examples/src/app/providers/redux-store.ts +18 -0
- package/templates/packages/redux/examples/src/features/_examples/with-redux/README.md +4 -0
- package/templates/packages/redux/examples/src/features/_examples/with-redux/model/example.slice.ts +36 -0
- package/templates/packages/sentry/core/src/shared/config/sentry.ts +9 -0
- package/templates/packages/sonner/examples/src/features/_examples/with-sonner/lib/toast.ts +7 -0
- package/templates/packages/sonner-provider/core/src/shared/providers/SonnerToaster.tsx +13 -0
- package/templates/packages/tanstack-form/examples/src/features/_examples/with-tanstack-form/README.md +3 -0
- package/templates/packages/tanstack-form/examples/src/features/_examples/with-tanstack-form/components/ExampleForm.tsx +60 -0
- package/templates/packages/tanstack-query/core/src/shared/lib/query-client.ts +24 -0
- package/templates/packages/tanstack-query/core/src/shared/providers/QueryProvider.tsx +30 -0
- package/templates/packages/tanstack-query/examples/src/features/_examples/with-tanstack-query/README.md +34 -0
- package/templates/packages/tanstack-query/examples/src/features/_examples/with-tanstack-query/actions/example.action.ts +34 -0
- package/templates/packages/tanstack-query/examples/src/features/_examples/with-tanstack-query/queries/use-example.query.ts +49 -0
- package/templates/packages/tanstack-table/examples/src/features/_examples/with-tanstack-table/README.md +3 -0
- package/templates/packages/tanstack-table/examples/src/features/_examples/with-tanstack-table/components/ExampleTable.tsx +66 -0
- package/templates/packages/trpc/core/src/app/api/trpc/router.ts +19 -0
- package/templates/packages/trpc/examples/src/app/providers/trpc-client.ts +13 -0
- package/templates/packages/uploadthing/core/src/app/api/uploadthing/route.ts +10 -0
- package/templates/packages/zustand/core/src/shared/lib/store.ts +13 -0
- package/templates/packages/zustand/examples/src/features/_examples/with-zustand/README.md +13 -0
- package/templates/packages/zustand/examples/src/features/_examples/with-zustand/model/example.store.ts +28 -0
- package/templates/pages/auth/src/app/({{name}})/layout.tsx +7 -0
- package/templates/pages/auth/src/app/({{name}})/login/page.tsx +5 -0
- package/templates/pages/auth/src/app/({{name}})/register/page.tsx +5 -0
- package/templates/pages/auth/src/entities/user/index.ts +2 -0
- package/templates/pages/auth/src/entities/user/lib/user-schema.ts +9 -0
- package/templates/pages/auth/src/entities/user/types/user.types.ts +5 -0
- package/templates/pages/auth/src/features/{{name}}/actions/login.action.ts +7 -0
- package/templates/pages/auth/src/features/{{name}}/actions/logout.action.ts +5 -0
- package/templates/pages/auth/src/features/{{name}}/actions/register.action.ts +7 -0
- package/templates/pages/auth/src/features/{{name}}/components/AuthGuard.tsx +14 -0
- package/templates/pages/auth/src/features/{{name}}/components/LoginForm.tsx +36 -0
- package/templates/pages/auth/src/features/{{name}}/components/RegisterForm.tsx +43 -0
- package/templates/pages/auth/src/features/{{name}}/hooks/use-session.ts +14 -0
- package/templates/pages/auth/src/features/{{name}}/index.ts +9 -0
- package/templates/pages/auth/src/features/{{name}}/lib/auth-helpers.ts +3 -0
- package/templates/pages/auth/src/features/{{name}}/queries/use-user.query.ts +16 -0
- package/templates/pages/auth/src/features/{{name}}/types/auth.types.ts +13 -0
- package/templates/pages/auth/src/views/{{name}}/LoginView.tsx +10 -0
- package/templates/pages/auth/src/views/{{name}}/RegisterView.tsx +10 -0
- package/templates/pages/auth/src/views/{{name}}/index.ts +2 -0
- package/templates/pages/blank/src/app/{{name}}/page.tsx +5 -0
- package/templates/pages/blank/src/features/{{name}}/index.ts +3 -0
- package/templates/pages/blank/src/views/{{name}}/index.ts +1 -0
- package/templates/pages/blank/src/views/{{name}}/{{Name}}View.tsx +8 -0
- package/templates/pages/crud/src/app/{{name}}/[id]/page.tsx +5 -0
- package/templates/pages/crud/src/app/{{name}}/new/page.tsx +5 -0
- package/templates/pages/crud/src/app/{{name}}/page.tsx +5 -0
- package/templates/pages/crud/src/entities/{{name}}/index.ts +2 -0
- package/templates/pages/crud/src/entities/{{name}}/lib/{{name}}-schema.ts +6 -0
- package/templates/pages/crud/src/entities/{{name}}/types/{{name}}.types.ts +4 -0
- package/templates/pages/crud/src/features/{{name}}/actions/create-{{name}}.action.ts +5 -0
- package/templates/pages/crud/src/features/{{name}}/actions/delete-{{name}}.action.ts +5 -0
- package/templates/pages/crud/src/features/{{name}}/actions/update-{{name}}.action.ts +5 -0
- package/templates/pages/crud/src/features/{{name}}/components/ProductCard.tsx +3 -0
- package/templates/pages/crud/src/features/{{name}}/components/ProductForm.tsx +5 -0
- package/templates/pages/crud/src/features/{{name}}/components/ProductsList.tsx +15 -0
- package/templates/pages/crud/src/features/{{name}}/index.ts +8 -0
- package/templates/pages/crud/src/features/{{name}}/queries/use-{{name}}.query.ts +10 -0
- package/templates/pages/crud/src/features/{{name}}/queries/use-{{name}}s.query.ts +10 -0
- package/templates/pages/crud/src/views/{{name}}/index.ts +1 -0
- package/templates/pages/crud/src/views/{{name}}/{{Name}}ListView.tsx +26 -0
- package/templates/pages/dashboard/src/app/{{name}}/layout.tsx +8 -0
- package/templates/pages/dashboard/src/app/{{name}}/page.tsx +5 -0
- package/templates/pages/dashboard/src/features/{{name}}/components/AnalyticsCard.tsx +8 -0
- package/templates/pages/dashboard/src/features/{{name}}/index.ts +1 -0
- package/templates/pages/dashboard/src/views/{{name}}/DashboardView.tsx +10 -0
- package/templates/pages/dashboard/src/views/{{name}}/index.ts +1 -0
- package/templates/pages/profile/src/app/{{name}}/page.tsx +5 -0
- package/templates/pages/profile/src/features/{{name}}/components/ProfileCard.tsx +8 -0
- package/templates/pages/profile/src/features/{{name}}/index.ts +1 -0
- package/templates/pages/profile/src/views/{{name}}/ProfileView.tsx +9 -0
- package/templates/pages/profile/src/views/{{name}}/index.ts +1 -0
- package/templates/pages/settings/src/app/{{name}}/page.tsx +5 -0
- package/templates/pages/settings/src/features/{{name}}/components/SettingsTabs.tsx +18 -0
- package/templates/pages/settings/src/features/{{name}}/index.ts +1 -0
- package/templates/pages/settings/src/views/{{name}}/SettingsView.tsx +10 -0
- package/templates/pages/settings/src/views/{{name}}/index.ts +1 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/lib/naming.ts","../src/lib/paths.ts","../src/commands/init.ts","../src/lib/copy.ts"],"sourcesContent":["import { program } from 'commander';\nimport { cancel, log, outro } from '@clack/prompts';\nimport chalk from 'chalk';\nimport path from 'path';\nimport { generateCommand } from './commands/generate.js';\nimport { initCommand } from './commands/init.js';\n\nconsole.log(chalk.blue('Next Architecture CLI'));\n\nprogram\n .name('next-arch')\n .description('CLI for Next.js Feature-Sliced Architecture')\n .version('0.1.1');\n\nprogram\n .command('init <projectName>')\n .description('Create a new project with Next Architecture')\n .option('-C, --cwd <path>', 'directory where the project folder will be created')\n .action(async (projectName: string, options: { cwd?: string }) => {\n try {\n await initCommand(projectName, { cwd: options.cwd });\n } catch (error) {\n cancel(error instanceof Error ? error.message : 'Init failed');\n process.exit(1);\n }\n });\n\nprogram\n .command('generate <type> <name>')\n .alias('g')\n .description('Generate feature, widget, entity, or view')\n .option('-C, --cwd <path>', 'path to Next.js project root (default: current directory)')\n .option('-f, --force', 'overwrite existing slice')\n .action(async (type: string, name: string, options: { cwd?: string; force?: boolean }) => {\n try {\n const projectRoot = options.cwd ? path.resolve(options.cwd) : process.cwd();\n await generateCommand(type, name, projectRoot, { force: options.force });\n outro('Done!');\n } catch (error) {\n cancel(error instanceof Error ? error.message : 'Generate failed');\n process.exit(1);\n }\n });\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n log.error(error instanceof Error ? error.message : 'Unexpected error');\n process.exit(1);\n});\n","import { log } from '@clack/prompts';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { assertValidSliceName, toKebabCase, toPascalCase } from '../lib/naming.js';\nimport { resolveTemplatesDir } from '../lib/paths.js';\n\nconst SLICE_TYPES = ['feature', 'view', 'widget', 'entity'] as const;\ntype SliceType = (typeof SLICE_TYPES)[number];\n\nconst TARGET_DIRS: Record<SliceType, string> = {\n feature: 'features',\n view: 'views',\n widget: 'widgets',\n entity: 'entities',\n};\n\nfunction isSliceType(value: string): value is SliceType {\n return SLICE_TYPES.includes(value as SliceType);\n}\n\nfunction assertNextProject(cwd: string): void {\n const packageJson = path.join(cwd, 'package.json');\n const srcDir = path.join(cwd, 'src');\n\n if (!fs.existsSync(packageJson) || !fs.existsSync(srcDir)) {\n throw new Error('Run this command from the root of a Next Architecture project.');\n }\n}\n\nasync function renderTemplateDir(\n templateDir: string,\n targetDir: string,\n replacements: Record<string, string>,\n): Promise<string[]> {\n const created: string[] = [];\n\n if (!(await fs.pathExists(templateDir))) {\n throw new Error(`Template \"${path.basename(templateDir)}\" not found.`);\n }\n\n const entries = await fs.readdir(templateDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const sourcePath = path.join(templateDir, entry.name);\n const renderedName = Object.entries(replacements).reduce(\n (name, [from, to]) => name.replaceAll(from, to),\n entry.name,\n );\n const targetPath = path.join(targetDir, renderedName);\n\n if (entry.isDirectory()) {\n await fs.ensureDir(targetPath);\n created.push(...(await renderTemplateDir(sourcePath, targetPath, replacements)));\n continue;\n }\n\n await fs.ensureDir(path.dirname(targetPath));\n let content = await fs.readFile(sourcePath, 'utf8');\n for (const [from, to] of Object.entries(replacements)) {\n content = content.replaceAll(from, to);\n }\n await fs.writeFile(targetPath, content);\n created.push(path.relative(process.cwd(), targetPath));\n }\n\n return created;\n}\n\nexport async function generateCommand(\n type: string,\n name: string,\n projectRoot = process.cwd(),\n options: { force?: boolean } = {},\n): Promise<void> {\n const root = path.resolve(projectRoot);\n assertNextProject(root);\n assertValidSliceName(name);\n\n if (!isSliceType(type)) {\n throw new Error(`Unknown type \"${type}\". Use: ${SLICE_TYPES.join(', ')}`);\n }\n\n const pascalName = toPascalCase(name);\n const kebabName = toKebabCase(name);\n const templatesDir = resolveTemplatesDir();\n const templateDir = path.join(templatesDir, type);\n const targetDir = path.join(root, 'src', TARGET_DIRS[type], kebabName);\n\n const previousCwd = process.cwd();\n process.chdir(root);\n\n try {\n if (await fs.pathExists(targetDir)) {\n if (!options.force) {\n throw new Error(\n `\"${type}\" \"${kebabName}\" already exists at ${targetDir}. Use --force to overwrite.`,\n );\n }\n await fs.remove(targetDir);\n }\n\n const replacements = {\n '{{Name}}': pascalName,\n '{{name}}': kebabName,\n };\n\n const created = await renderTemplateDir(templateDir, targetDir, replacements);\n\n log.success(`Created ${type} \"${kebabName}\" in ${root}`);\n for (const file of created) {\n log.info(` ${file}`);\n }\n\n if (type === 'view') {\n log.info(`Add to a route: import { ${pascalName} } from '@/views/${kebabName}';`);\n }\n } finally {\n process.chdir(previousCwd);\n }\n}\n","export function toPascalCase(value: string): string {\n return value\n .replace(/[-_/]+/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .split(' ')\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join('');\n}\n\nexport function toKebabCase(value: string): string {\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n\nexport function assertValidSliceName(name: string): void {\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {\n throw new Error(\n 'Name must start with a letter and contain only letters, numbers, hyphens, or underscores.',\n );\n }\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport fs from 'fs-extra';\n\nconst PACKAGE_NAMES = new Set(['next-arch', '@yousxlfs/next-arch']);\n\nfunction isNextArchPackage(pkg: { name?: string }): boolean {\n return typeof pkg.name === 'string' && PACKAGE_NAMES.has(pkg.name);\n}\n\nfunction findPackageRoot(startDir: string): string {\n let current = startDir;\n\n while (current !== path.dirname(current)) {\n const packageJsonPath = path.join(current, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as { name?: string };\n if (isNextArchPackage(pkg)) {\n return current;\n }\n }\n current = path.dirname(current);\n }\n\n throw new Error('next-arch package root not found.');\n}\n\nexport function getPackageRoot(): string {\n return findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));\n}\n\nexport function resolveTemplatesDir(): string {\n const templatesDir = path.join(getPackageRoot(), 'templates');\n if (!fs.existsSync(templatesDir)) {\n throw new Error('Templates directory not found. Reinstall next-arch.');\n }\n return templatesDir;\n}\n\nexport function resolveAppTemplateDir(): string {\n const appTemplate = path.join(resolveTemplatesDir(), 'app');\n if (!fs.existsSync(path.join(appTemplate, 'package.json'))) {\n throw new Error('App template not found. Reinstall next-arch.');\n }\n return appTemplate;\n}\n","import { confirm, intro, log, outro } from '@clack/prompts';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { copyProjectTemplate } from '../lib/copy.js';\nimport { getPackageRoot, resolveAppTemplateDir } from '../lib/paths.js';\n\nasync function resolveEslintPluginSource(): Promise<string> {\n const packageRoot = getPackageRoot();\n const candidates = [\n path.join(packageRoot, 'vendor', 'eslint-plugin-next-arch'),\n path.resolve(packageRoot, '..', '..', 'packages', 'eslint-plugin-next-arch'),\n ];\n\n for (const candidate of candidates) {\n if (await fs.pathExists(path.join(candidate, 'dist', 'index.js'))) {\n return candidate;\n }\n }\n\n throw new Error(\n 'eslint-plugin-next-arch is not available. Reinstall next-arch or run \"pnpm build\" in the monorepo.',\n );\n}\n\nasync function bundleEslintPlugin(targetDir: string): Promise<void> {\n const pluginSource = await resolveEslintPluginSource();\n const pluginTarget = path.join(targetDir, 'vendor', 'eslint-plugin-next-arch');\n\n await fs.ensureDir(pluginTarget);\n await fs.copy(path.join(pluginSource, 'dist'), path.join(pluginTarget, 'dist'));\n await fs.copy(path.join(pluginSource, 'package.json'), path.join(pluginTarget, 'package.json'));\n}\n\nasync function patchPackageJson(targetDir: string, projectName: string): Promise<void> {\n const packageJsonPath = path.join(targetDir, 'package.json');\n const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as {\n name: string;\n scripts?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n pkg.name = projectName;\n delete pkg.scripts?.arch;\n if (pkg.devDependencies) {\n pkg.devDependencies['eslint-plugin-next-arch'] = 'file:./vendor/eslint-plugin-next-arch';\n }\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nexport async function initCommand(\n projectName: string,\n options: { cwd?: string } = {},\n): Promise<void> {\n const baseDir = path.resolve(options.cwd ?? process.cwd());\n intro('Creating new Next Architecture project...');\n\n const targetDir = path.join(baseDir, projectName);\n const templateDir = resolveAppTemplateDir();\n\n if (await fs.pathExists(targetDir)) {\n const shouldContinue = await confirm({\n message: 'Directory already exists. Continue and merge files?',\n });\n\n if (!shouldContinue) {\n outro('Cancelled');\n return;\n }\n }\n\n log.info(`Copying template from ${path.basename(templateDir)}...`);\n await copyProjectTemplate(templateDir, targetDir);\n await bundleEslintPlugin(targetDir);\n await patchPackageJson(targetDir, projectName);\n await fs.writeFile(path.join(targetDir, '.npmrc'), 'ignore-workspace=true\\n');\n\n log.success(`Project \"${projectName}\" created`);\n log.info(` cd ${projectName}`);\n log.info(' pnpm install');\n log.info(' pnpm dev');\n outro('Done!');\n}\n","import fs from 'fs-extra';\nimport path from 'path';\n\nconst EXCLUDED_DIRS = new Set(['node_modules', '.next', '.turbo', 'dist']);\n\nexport async function copyProjectTemplate(\n sourceDir: string,\n targetDir: string,\n): Promise<void> {\n await fs.copy(sourceDir, targetDir, {\n filter(src) {\n const relative = path.relative(sourceDir, src);\n if (!relative) return true;\n\n return !relative.split(path.sep).some((part) => EXCLUDED_DIRS.has(part));\n },\n });\n}\n\nexport async function replaceInFile(\n filePath: string,\n replacements: Record<string, string>,\n): Promise<void> {\n if (!(await fs.pathExists(filePath))) return;\n\n let content = await fs.readFile(filePath, 'utf8');\n for (const [from, to] of Object.entries(replacements)) {\n content = content.replaceAll(from, to);\n }\n await fs.writeFile(filePath, content);\n}\n"],"mappings":"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","names":["log","outro","path","fs","path","path","fs","log","fs","path","fs","path","path","fs","log","path","outro","log"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/commands/page.ts","../src/lib/naming.ts","../src/lib/page-prompts.ts","../src/lib/page-presets.ts","../src/lib/paths.ts","../src/lib/template.ts","../src/commands/init.ts","../src/lib/apply-packages.ts","../src/lib/packages.ts","../src/lib/copy.ts","../src/lib/init-prompts.ts"],"sourcesContent":["import { program } from 'commander';\nimport { cancel, log, outro } from '@clack/prompts';\nimport chalk from 'chalk';\nimport path from 'path';\nimport { generateCommand } from './commands/generate.js';\nimport { initCommand } from './commands/init.js';\nimport { pageCommand } from './commands/page.js';\nimport type { PagePreset } from './lib/page-presets.js';\n\nconsole.log(chalk.blue('Next Architecture CLI'));\n\nprogram\n .name('next-arch')\n .description('CLI for Next.js Feature-Sliced Architecture')\n .version('0.2.0');\n\nprogram\n .command('init <projectName>')\n .description('Create a new project with Next Architecture')\n .option('-C, --cwd <path>', 'directory where the project folder will be created')\n .option('-y, --yes', 'use default package selections without prompts')\n .option('--no-examples', 'skip generating example files')\n .action(\n async (\n projectName: string,\n options: { cwd?: string; yes?: boolean; noExamples?: boolean; examples?: boolean },\n ) => {\n try {\n await initCommand(projectName, {\n cwd: options.cwd,\n yes: options.yes,\n noExamples: options.examples === false,\n });\n } catch (error) {\n cancel(error instanceof Error ? error.message : 'Init failed');\n process.exit(1);\n }\n });\n\nprogram\n .command('page <name>')\n .description('Generate a full FSD page (view + feature + routes)')\n .option('-C, --cwd <path>', 'path to Next.js project root (default: current directory)')\n .option('-f, --force', 'overwrite existing page paths')\n .option('-y, --yes', 'use blank preset without prompts')\n .option('--preset <preset>', 'page preset: auth, dashboard, crud, profile, settings, blank')\n .action(\n async (\n name: string,\n options: { cwd?: string; force?: boolean; yes?: boolean; preset?: PagePreset },\n ) => {\n try {\n const projectRoot = options.cwd ? path.resolve(options.cwd) : process.cwd();\n await pageCommand(name, projectRoot, {\n force: options.force,\n yes: options.yes,\n preset: options.preset,\n });\n outro('Done!');\n } catch (error) {\n cancel(error instanceof Error ? error.message : 'Page generation failed');\n process.exit(1);\n }\n },\n );\n\nprogram\n .command('generate <type> <name>')\n .alias('g')\n .description('Generate page, feature, widget, entity, or view')\n .option('-C, --cwd <path>', 'path to Next.js project root (default: current directory)')\n .option('-f, --force', 'overwrite existing slice')\n .option('-y, --yes', 'skip interactive page preset selection')\n .option('--preset <preset>', 'page preset when type is page')\n .action(\n async (\n type: string,\n name: string,\n options: { cwd?: string; force?: boolean; yes?: boolean; preset?: string },\n ) => {\n try {\n const projectRoot = options.cwd ? path.resolve(options.cwd) : process.cwd();\n await generateCommand(type, name, projectRoot, {\n force: options.force,\n yes: options.yes,\n preset: options.preset,\n });\n outro('Done!');\n } catch (error) {\n cancel(error instanceof Error ? error.message : 'Generate failed');\n process.exit(1);\n }\n },\n );\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n log.error(error instanceof Error ? error.message : 'Unexpected error');\n process.exit(1);\n});\n","import { log } from '@clack/prompts';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport type { PagePreset } from '../lib/page-presets.js';\nimport { pageCommand } from './page.js';\nimport { assertValidSliceName, toKebabCase, toPascalCase } from '../lib/naming.js';\nimport { resolveTemplatesDir } from '../lib/paths.js';\nimport { buildReplacements, renderTemplateDir } from '../lib/template.js';\n\nconst SLICE_TYPES = ['feature', 'view', 'widget', 'entity'] as const;\ntype SliceType = (typeof SLICE_TYPES)[number];\n\nconst TARGET_DIRS: Record<SliceType, string> = {\n feature: 'features',\n view: 'views',\n widget: 'widgets',\n entity: 'entities',\n};\n\nfunction isSliceType(value: string): value is SliceType {\n return SLICE_TYPES.includes(value as SliceType);\n}\n\nfunction assertNextProject(cwd: string): void {\n const packageJson = path.join(cwd, 'package.json');\n const srcDir = path.join(cwd, 'src');\n\n if (!fs.existsSync(packageJson) || !fs.existsSync(srcDir)) {\n throw new Error('Run this command from the root of a Next Architecture project.');\n }\n}\n\nexport interface GenerateCommandOptions {\n force?: boolean;\n yes?: boolean;\n preset?: string;\n}\n\nexport async function generateCommand(\n type: string,\n name: string,\n projectRoot = process.cwd(),\n options: GenerateCommandOptions = {},\n): Promise<void> {\n if (type === 'page') {\n await pageCommand(name, projectRoot, {\n force: options.force,\n yes: options.yes,\n preset: options.preset as PagePreset | undefined,\n });\n return;\n }\n\n const root = path.resolve(projectRoot);\n assertNextProject(root);\n assertValidSliceName(name);\n\n if (!isSliceType(type)) {\n throw new Error(`Unknown type \"${type}\". Use: page, ${SLICE_TYPES.join(', ')}`);\n }\n\n const pascalName = toPascalCase(name);\n const kebabName = toKebabCase(name);\n const templatesDir = resolveTemplatesDir();\n const templateDir = path.join(templatesDir, type);\n const targetDir = path.join(root, 'src', TARGET_DIRS[type], kebabName);\n\n const previousCwd = process.cwd();\n process.chdir(root);\n\n try {\n if (await fs.pathExists(targetDir)) {\n if (!options.force) {\n throw new Error(\n `\"${type}\" \"${kebabName}\" already exists at ${targetDir}. Use --force to overwrite.`,\n );\n }\n await fs.remove(targetDir);\n }\n\n const replacements = buildReplacements(name, pascalName, kebabName);\n const created = await renderTemplateDir(templateDir, targetDir, replacements);\n const relativeFiles = created.map((file) => path.relative(root, file));\n\n log.success(`Created ${type} \"${kebabName}\" in ${root}`);\n for (const file of relativeFiles) {\n log.info(` ${file}`);\n }\n\n if (type === 'view') {\n log.info(`Add to a route: import { ${pascalName} } from '@/views/${kebabName}';`);\n }\n } finally {\n process.chdir(previousCwd);\n }\n}\n","import { log } from '@clack/prompts';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { assertValidSliceName, toKebabCase, toPascalCase } from '../lib/naming.js';\nimport { promptPagePreset } from '../lib/page-prompts.js';\nimport type { PagePreset } from '../lib/page-presets.js';\nimport { resolveTemplatesDir } from '../lib/paths.js';\nimport { buildReplacements, renderTemplateDir } from '../lib/template.js';\n\nfunction assertNextProject(cwd: string): void {\n const packageJson = path.join(cwd, 'package.json');\n const srcDir = path.join(cwd, 'src');\n\n if (!fs.existsSync(packageJson) || !fs.existsSync(srcDir)) {\n throw new Error('Run this command from the root of a Next Architecture project.');\n }\n}\n\nasync function pathExistsAny(paths: string[]): Promise<boolean> {\n for (const candidate of paths) {\n if (await fs.pathExists(candidate)) {\n return true;\n }\n }\n return false;\n}\n\nexport interface PageCommandOptions {\n force?: boolean;\n yes?: boolean;\n preset?: PagePreset;\n}\n\nexport async function pageCommand(\n name: string,\n projectRoot = process.cwd(),\n options: PageCommandOptions = {},\n): Promise<void> {\n const root = path.resolve(projectRoot);\n assertNextProject(root);\n assertValidSliceName(name);\n\n const preset = await promptPagePreset({ yes: options.yes, preset: options.preset });\n const pascalName = toPascalCase(name);\n const kebabName = toKebabCase(name);\n const templatesDir = resolveTemplatesDir();\n const templateDir = path.join(templatesDir, 'pages', preset);\n\n if (!(await fs.pathExists(templateDir))) {\n throw new Error(`Page preset \"${preset}\" template not found.`);\n }\n\n const targetDir = path.join(root, 'src');\n const replacements = buildReplacements(name, pascalName, kebabName);\n\n const conflictPaths = [\n path.join(targetDir, 'app', kebabName),\n path.join(targetDir, 'app', `(${kebabName})`),\n path.join(targetDir, 'views', kebabName),\n path.join(targetDir, 'features', kebabName),\n path.join(targetDir, 'entities', kebabName),\n ];\n\n if (!options.force && (await pathExistsAny(conflictPaths))) {\n throw new Error(\n `Page \"${kebabName}\" already exists. Use --force to overwrite conflicting paths.`,\n );\n }\n\n const previousCwd = process.cwd();\n process.chdir(root);\n\n try {\n if (options.force) {\n for (const conflictPath of conflictPaths) {\n if (await fs.pathExists(conflictPath)) {\n await fs.remove(conflictPath);\n }\n }\n }\n\n const created = await renderTemplateDir(templateDir, root, replacements);\n const relativeFiles = created.map((file) => path.relative(root, file));\n\n log.success(`Created ${preset} page \"${kebabName}\"`);\n for (const file of relativeFiles) {\n log.info(` ${file}`);\n }\n\n if (preset === 'blank') {\n log.info(`Route: src/app/${kebabName}/page.tsx`);\n }\n if (preset === 'auth') {\n log.info(`Routes: src/app/(${kebabName})/login and register`);\n }\n if (preset === 'crud') {\n log.info(`Routes: src/app/${kebabName}, ${kebabName}/[id], ${kebabName}/new`);\n }\n } finally {\n process.chdir(previousCwd);\n }\n}\n","export function toPascalCase(value: string): string {\n return value\n .replace(/[-_/]+/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .split(' ')\n .filter(Boolean)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())\n .join('');\n}\n\nexport function toKebabCase(value: string): string {\n return value\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n}\n\nexport function assertValidSliceName(name: string): void {\n if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {\n throw new Error(\n 'Name must start with a letter and contain only letters, numbers, hyphens, or underscores.',\n );\n }\n}\n","import { cancel, isCancel, select } from '@clack/prompts';\nimport {\n DEFAULT_PAGE_PRESET,\n PAGE_PRESET_LABELS,\n PAGE_PRESETS,\n type PagePreset,\n} from './page-presets.js';\n\nfunction exitOnCancel<T>(value: T | symbol): T {\n if (isCancel(value)) {\n cancel('Cancelled');\n process.exit(0);\n }\n return value;\n}\n\nexport async function promptPagePreset(options: {\n yes?: boolean;\n preset?: PagePreset;\n}): Promise<PagePreset> {\n if (options.preset) {\n if (!PAGE_PRESETS.includes(options.preset)) {\n throw new Error(`Unknown preset \"${options.preset}\". Use: ${PAGE_PRESETS.join(', ')}`);\n }\n return options.preset;\n }\n\n if (options.yes) {\n return DEFAULT_PAGE_PRESET;\n }\n\n return exitOnCancel(\n await select<PagePreset>({\n message: 'What page template do you want?',\n options: PAGE_PRESETS.map((preset) => ({\n value: preset,\n label: PAGE_PRESET_LABELS[preset],\n })),\n initialValue: 'blank',\n }),\n );\n}\n","export type PagePreset = 'auth' | 'dashboard' | 'crud' | 'profile' | 'settings' | 'blank';\n\nexport const PAGE_PRESETS: PagePreset[] = [\n 'auth',\n 'dashboard',\n 'crud',\n 'profile',\n 'settings',\n 'blank',\n];\n\nexport const DEFAULT_PAGE_PRESET: PagePreset = 'blank';\n\nexport const PAGE_PRESET_LABELS: Record<PagePreset, string> = {\n auth: 'auth — login/register/logout flow',\n dashboard: 'dashboard — layout with sidebar + analytics',\n crud: 'crud — list + create/edit/delete',\n profile: 'profile — user profile page',\n settings: 'settings — tabbed settings page',\n blank: 'blank — minimal page structure',\n};\n","import path from 'path';\nimport { fileURLToPath } from 'url';\nimport fs from 'fs-extra';\n\nconst PACKAGE_NAMES = new Set(['next-arch', '@yousxlfs/next-arch']);\n\nfunction isNextArchPackage(pkg: { name?: string }): boolean {\n return typeof pkg.name === 'string' && PACKAGE_NAMES.has(pkg.name);\n}\n\nfunction findPackageRoot(startDir: string): string {\n let current = startDir;\n\n while (current !== path.dirname(current)) {\n const packageJsonPath = path.join(current, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as { name?: string };\n if (isNextArchPackage(pkg)) {\n return current;\n }\n }\n current = path.dirname(current);\n }\n\n throw new Error('next-arch package root not found.');\n}\n\nexport function getPackageRoot(): string {\n return findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));\n}\n\nexport function resolveTemplatesDir(): string {\n const templatesDir = path.join(getPackageRoot(), 'templates');\n if (!fs.existsSync(templatesDir)) {\n throw new Error('Templates directory not found. Reinstall next-arch.');\n }\n return templatesDir;\n}\n\nexport function resolveAppTemplateDir(): string {\n const appTemplate = path.join(resolveTemplatesDir(), 'app');\n if (!fs.existsSync(path.join(appTemplate, 'package.json'))) {\n throw new Error('App template not found. Reinstall next-arch.');\n }\n return appTemplate;\n}\n","import fs from 'fs-extra';\nimport path from 'path';\n\nexport async function renderTemplateDir(\n templateDir: string,\n targetDir: string,\n replacements: Record<string, string>,\n): Promise<string[]> {\n const created: string[] = [];\n\n if (!(await fs.pathExists(templateDir))) {\n throw new Error(`Template \"${path.basename(templateDir)}\" not found.`);\n }\n\n const entries = await fs.readdir(templateDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const sourcePath = path.join(templateDir, entry.name);\n const renderedName = Object.entries(replacements).reduce(\n (name, [from, to]) => name.replaceAll(from, to),\n entry.name,\n );\n const targetPath = path.join(targetDir, renderedName);\n\n if (entry.isDirectory()) {\n await fs.ensureDir(targetPath);\n created.push(...(await renderTemplateDir(sourcePath, targetPath, replacements)));\n continue;\n }\n\n await fs.ensureDir(path.dirname(targetPath));\n let content = await fs.readFile(sourcePath, 'utf8');\n for (const [from, to] of Object.entries(replacements)) {\n content = content.replaceAll(from, to);\n }\n await fs.writeFile(targetPath, content);\n created.push(targetPath);\n }\n\n return created;\n}\n\nexport async function copyTemplateTree(\n sourceDir: string,\n targetDir: string,\n): Promise<string[]> {\n const created: string[] = [];\n\n if (!(await fs.pathExists(sourceDir))) {\n return created;\n }\n\n const entries = await fs.readdir(sourceDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const sourcePath = path.join(sourceDir, entry.name);\n const targetPath = path.join(targetDir, entry.name);\n\n if (entry.isDirectory()) {\n await fs.ensureDir(targetPath);\n created.push(...(await copyTemplateTree(sourcePath, targetPath)));\n continue;\n }\n\n await fs.ensureDir(path.dirname(targetPath));\n await fs.copy(sourcePath, targetPath);\n created.push(targetPath);\n }\n\n return created;\n}\n\nexport function buildReplacements(name: string, pascalName: string, kebabName: string): Record<string, string> {\n return {\n '{{Name}}': pascalName,\n '{{name}}': kebabName,\n '{{NAME}}': name.toUpperCase(),\n };\n}\n","import { confirm, intro, log, outro } from '@clack/prompts';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { applyPackageSelections } from '../lib/apply-packages.js';\nimport { copyProjectTemplate } from '../lib/copy.js';\nimport { promptInitSelections } from '../lib/init-prompts.js';\nimport { formatSelectionsSummary } from '../lib/packages.js';\nimport { getPackageRoot, resolveAppTemplateDir } from '../lib/paths.js';\n\nasync function resolveEslintPluginSource(): Promise<string> {\n const packageRoot = getPackageRoot();\n const candidates = [\n path.join(packageRoot, 'vendor', 'eslint-plugin-next-arch'),\n path.resolve(packageRoot, '..', '..', 'packages', 'eslint-plugin-next-arch'),\n ];\n\n for (const candidate of candidates) {\n if (await fs.pathExists(path.join(candidate, 'dist', 'index.js'))) {\n return candidate;\n }\n }\n\n throw new Error(\n 'eslint-plugin-next-arch is not available. Reinstall next-arch or run \"pnpm build\" in the monorepo.',\n );\n}\n\nasync function bundleEslintPlugin(targetDir: string): Promise<void> {\n const pluginSource = await resolveEslintPluginSource();\n const pluginTarget = path.join(targetDir, 'vendor', 'eslint-plugin-next-arch');\n\n await fs.ensureDir(pluginTarget);\n await fs.copy(path.join(pluginSource, 'dist'), path.join(pluginTarget, 'dist'));\n await fs.copy(path.join(pluginSource, 'package.json'), path.join(pluginTarget, 'package.json'));\n}\n\nasync function patchPackageJson(targetDir: string, projectName: string): Promise<void> {\n const packageJsonPath = path.join(targetDir, 'package.json');\n const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as {\n name: string;\n scripts?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n pkg.name = projectName;\n delete pkg.scripts?.arch;\n if (pkg.devDependencies) {\n pkg.devDependencies['eslint-plugin-next-arch'] = 'file:./vendor/eslint-plugin-next-arch';\n }\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nexport interface InitCommandOptions {\n cwd?: string;\n yes?: boolean;\n noExamples?: boolean;\n}\n\nexport async function initCommand(\n projectName: string,\n options: InitCommandOptions = {},\n): Promise<void> {\n const baseDir = path.resolve(options.cwd ?? process.cwd());\n intro('Creating new Next Architecture project...');\n\n const selections = await promptInitSelections({\n yes: options.yes,\n noExamples: options.noExamples,\n });\n\n const targetDir = path.join(baseDir, projectName);\n const templateDir = resolveAppTemplateDir();\n\n if (await fs.pathExists(targetDir)) {\n const shouldContinue = await confirm({\n message: 'Directory already exists. Continue and merge files?',\n });\n\n if (!shouldContinue) {\n outro('Cancelled');\n return;\n }\n }\n\n log.info('Package setup:');\n for (const line of formatSelectionsSummary(selections)) {\n log.info(` ${line}`);\n }\n\n log.info(`Copying template from ${path.basename(templateDir)}...`);\n await copyProjectTemplate(templateDir, targetDir);\n await bundleEslintPlugin(targetDir);\n await patchPackageJson(targetDir, projectName);\n await applyPackageSelections(targetDir, selections);\n await fs.writeFile(path.join(targetDir, '.npmrc'), 'ignore-workspace=true\\n');\n\n log.success(`Project \"${projectName}\" created`);\n log.info(` cd ${projectName}`);\n log.info(' npm install');\n log.info(' npm run dev');\n if (selections.withExamples) {\n log.info(' See src/features/_examples/ for commented package examples');\n }\n outro('Done!');\n}\n","import fs from 'fs-extra';\nimport path from 'path';\nimport {\n getPackageTemplates,\n resolveDependencies,\n type InitSelections,\n} from './packages.js';\nimport { copyTemplateTree } from './template.js';\nimport { resolveTemplatesDir } from './paths.js';\n\nasync function mergePackageJson(\n targetDir: string,\n selections: InitSelections,\n): Promise<void> {\n const packageJsonPath = path.join(targetDir, 'package.json');\n const pkg = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n const { dependencies, devDependencies } = resolveDependencies(selections);\n\n pkg.dependencies = { ...pkg.dependencies, ...dependencies };\n pkg.devDependencies = { ...pkg.devDependencies, ...devDependencies };\n\n await fs.writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nexport async function applyPackageSelections(\n targetDir: string,\n selections: InitSelections,\n): Promise<string[]> {\n const packagesDir = path.join(resolveTemplatesDir(), 'packages');\n const created: string[] = [];\n\n await mergePackageJson(targetDir, selections);\n\n for (const templateId of getPackageTemplates(selections)) {\n const templateRoot = path.join(packagesDir, templateId);\n const coreSrc = path.join(templateRoot, 'core', 'src');\n const examplesSrc = path.join(templateRoot, 'examples', 'src');\n\n if (await fs.pathExists(coreSrc)) {\n const files = await copyTemplateTree(coreSrc, path.join(targetDir, 'src'));\n created.push(...files);\n }\n\n if (selections.withExamples && (await fs.pathExists(examplesSrc))) {\n const files = await copyTemplateTree(examplesSrc, path.join(targetDir, 'src'));\n created.push(...files);\n }\n }\n\n return created;\n}\n","export type StateManager = 'zustand' | 'redux' | 'jotai' | 'none';\nexport type FormLibrary = 'tanstack-form' | 'react-hook-form' | 'none';\n\nexport type OptionalPackage =\n | 'tanstack-table'\n | 'motion'\n | 'nuqs'\n | 'trpc'\n | 'better-auth'\n | 'uploadthing'\n | 'sonner'\n | 'next-intl'\n | 'sentry';\n\nexport interface InitSelections {\n stateManager: StateManager;\n formLibrary: FormLibrary;\n optionalPackages: OptionalPackage[];\n withExamples: boolean;\n}\n\nexport const DEFAULT_INIT_SELECTIONS: InitSelections = {\n stateManager: 'zustand',\n formLibrary: 'tanstack-form',\n optionalPackages: ['tanstack-table'],\n withExamples: true,\n};\n\nexport const PACKAGE_VERSIONS = {\n zustand: '^5.0.14',\n '@reduxjs/toolkit': '^2.8.2',\n 'react-redux': '^9.2.0',\n jotai: '^2.12.5',\n '@tanstack/react-form': '^1.0.0',\n zod: '^4.4.3',\n 'react-hook-form': '^7.56.4',\n '@hookform/resolvers': '^5.0.1',\n '@tanstack/react-query': '^5.101.2',\n '@tanstack/react-query-devtools': '^5.101.2',\n '@tanstack/react-table': '^8.21.3',\n motion: '^12.19.1',\n nuqs: '^2.4.3',\n '@trpc/client': '^11.1.2',\n '@trpc/server': '^11.1.2',\n '@trpc/react-query': '^11.1.2',\n 'better-auth': '^1.2.8',\n uploadthing: '^7.7.2',\n '@uploadthing/react': '^7.3.1',\n sonner: '^2.0.3',\n 'next-intl': '^4.1.0',\n '@sentry/nextjs': '^9.22.0',\n} as const;\n\nexport interface ResolvedDependencies {\n dependencies: Record<string, string>;\n devDependencies: Record<string, string>;\n}\n\nexport function resolveDependencies(selections: InitSelections): ResolvedDependencies {\n const dependencies: Record<string, string> = {};\n const devDependencies: Record<string, string> = {};\n\n dependencies['@tanstack/react-query'] = PACKAGE_VERSIONS['@tanstack/react-query'];\n devDependencies['@tanstack/react-query-devtools'] =\n PACKAGE_VERSIONS['@tanstack/react-query-devtools'];\n\n dependencies.zod = PACKAGE_VERSIONS.zod;\n\n switch (selections.stateManager) {\n case 'zustand':\n dependencies.zustand = PACKAGE_VERSIONS.zustand;\n break;\n case 'redux':\n dependencies['@reduxjs/toolkit'] = PACKAGE_VERSIONS['@reduxjs/toolkit'];\n dependencies['react-redux'] = PACKAGE_VERSIONS['react-redux'];\n break;\n case 'jotai':\n dependencies.jotai = PACKAGE_VERSIONS.jotai;\n break;\n case 'none':\n break;\n }\n\n switch (selections.formLibrary) {\n case 'tanstack-form':\n dependencies['@tanstack/react-form'] = PACKAGE_VERSIONS['@tanstack/react-form'];\n break;\n case 'react-hook-form':\n dependencies['react-hook-form'] = PACKAGE_VERSIONS['react-hook-form'];\n dependencies['@hookform/resolvers'] = PACKAGE_VERSIONS['@hookform/resolvers'];\n break;\n case 'none':\n break;\n }\n\n for (const pkg of selections.optionalPackages) {\n switch (pkg) {\n case 'tanstack-table':\n dependencies['@tanstack/react-table'] = PACKAGE_VERSIONS['@tanstack/react-table'];\n break;\n case 'motion':\n dependencies.motion = PACKAGE_VERSIONS.motion;\n break;\n case 'nuqs':\n dependencies.nuqs = PACKAGE_VERSIONS.nuqs;\n break;\n case 'trpc':\n dependencies['@trpc/client'] = PACKAGE_VERSIONS['@trpc/client'];\n dependencies['@trpc/server'] = PACKAGE_VERSIONS['@trpc/server'];\n dependencies['@trpc/react-query'] = PACKAGE_VERSIONS['@trpc/react-query'];\n break;\n case 'better-auth':\n dependencies['better-auth'] = PACKAGE_VERSIONS['better-auth'];\n break;\n case 'uploadthing':\n dependencies.uploadthing = PACKAGE_VERSIONS.uploadthing;\n dependencies['@uploadthing/react'] = PACKAGE_VERSIONS['@uploadthing/react'];\n break;\n case 'sonner':\n dependencies.sonner = PACKAGE_VERSIONS.sonner;\n break;\n case 'next-intl':\n dependencies['next-intl'] = PACKAGE_VERSIONS['next-intl'];\n break;\n case 'sentry':\n dependencies['@sentry/nextjs'] = PACKAGE_VERSIONS['@sentry/nextjs'];\n break;\n }\n }\n\n return { dependencies, devDependencies };\n}\n\n/** Package template folders to apply (relative to templates/packages/). */\nexport function getPackageTemplates(selections: InitSelections): string[] {\n const templates = new Set<string>(['tanstack-query', 'env']);\n\n switch (selections.stateManager) {\n case 'zustand':\n templates.add('zustand');\n break;\n case 'redux':\n templates.add('redux');\n break;\n case 'jotai':\n templates.add('jotai');\n break;\n case 'none':\n break;\n }\n\n switch (selections.formLibrary) {\n case 'tanstack-form':\n templates.add('tanstack-form');\n break;\n case 'react-hook-form':\n templates.add('react-hook-form');\n break;\n case 'none':\n break;\n }\n\n for (const pkg of selections.optionalPackages) {\n templates.add(pkg);\n }\n\n if (selections.optionalPackages.includes('sonner')) {\n templates.add('sonner-provider');\n }\n\n return [...templates];\n}\n\nexport function formatSelectionsSummary(selections: InitSelections): string[] {\n const lines: string[] = [];\n\n const stateLabels: Record<StateManager, string> = {\n zustand: 'Zustand',\n redux: 'Redux Toolkit',\n jotai: 'Jotai',\n none: 'None',\n };\n\n const formLabels: Record<FormLibrary, string> = {\n 'tanstack-form': 'TanStack Form + zod',\n 'react-hook-form': 'React Hook Form + zod',\n none: 'None',\n };\n\n lines.push(`State: ${stateLabels[selections.stateManager]}`);\n lines.push(`Forms: ${formLabels[selections.formLibrary]}`);\n lines.push('Always: TanStack Query + Devtools');\n\n const optionalLabels: Record<OptionalPackage, string> = {\n 'tanstack-table': 'TanStack Table',\n motion: 'Motion',\n nuqs: 'nuqs',\n trpc: 'tRPC',\n 'better-auth': 'Better Auth',\n uploadthing: 'Uploadthing',\n sonner: 'Sonner',\n 'next-intl': 'next-intl',\n 'sentry': 'Sentry',\n };\n\n if (selections.optionalPackages.length > 0) {\n lines.push(\n `Optional: ${selections.optionalPackages.map((p) => optionalLabels[p]).join(', ')}`,\n );\n } else {\n lines.push('Optional: none');\n }\n\n lines.push(`Examples: ${selections.withExamples ? 'yes' : 'no'}`);\n\n return lines;\n}\n","import fs from 'fs-extra';\nimport path from 'path';\n\nconst EXCLUDED_DIRS = new Set(['node_modules', '.next', '.turbo', 'dist']);\n\nexport async function copyProjectTemplate(\n sourceDir: string,\n targetDir: string,\n): Promise<void> {\n await fs.copy(sourceDir, targetDir, {\n filter(src) {\n const relative = path.relative(sourceDir, src);\n if (!relative) return true;\n\n return !relative.split(path.sep).some((part) => EXCLUDED_DIRS.has(part));\n },\n });\n}\n\nexport async function replaceInFile(\n filePath: string,\n replacements: Record<string, string>,\n): Promise<void> {\n if (!(await fs.pathExists(filePath))) return;\n\n let content = await fs.readFile(filePath, 'utf8');\n for (const [from, to] of Object.entries(replacements)) {\n content = content.replaceAll(from, to);\n }\n await fs.writeFile(filePath, content);\n}\n","import { cancel, confirm, isCancel, multiselect, select } from '@clack/prompts';\nimport {\n DEFAULT_INIT_SELECTIONS,\n type FormLibrary,\n type InitSelections,\n type OptionalPackage,\n type StateManager,\n formatSelectionsSummary,\n} from './packages.js';\n\nexport interface InitPromptOptions {\n yes?: boolean;\n noExamples?: boolean;\n}\n\nfunction exitOnCancel<T>(value: T | symbol): T {\n if (isCancel(value)) {\n cancel('Cancelled');\n process.exit(0);\n }\n return value;\n}\n\nexport async function promptInitSelections(options: InitPromptOptions = {}): Promise<InitSelections> {\n if (options.yes) {\n return {\n ...DEFAULT_INIT_SELECTIONS,\n withExamples: options.noExamples ? false : DEFAULT_INIT_SELECTIONS.withExamples,\n };\n }\n\n const stateManager = exitOnCancel(\n await select<StateManager>({\n message: 'Which state manager do you want?',\n options: [\n { value: 'zustand', label: 'Zustand (recommended)' },\n { value: 'redux', label: 'Redux Toolkit' },\n { value: 'jotai', label: 'Jotai' },\n { value: 'none', label: 'None' },\n ],\n initialValue: 'zustand',\n }),\n );\n\n const formLibrary = exitOnCancel(\n await select<FormLibrary>({\n message: 'Which form library do you want?',\n options: [\n { value: 'tanstack-form', label: 'TanStack Form (recommended)' },\n { value: 'react-hook-form', label: 'React Hook Form' },\n { value: 'none', label: 'None' },\n ],\n initialValue: 'tanstack-form',\n }),\n );\n\n const optionalPackages = exitOnCancel(\n await multiselect<OptionalPackage>({\n message: 'Select additional packages (multi-select)',\n options: [\n { value: 'tanstack-table', label: 'TanStack Table (headless tables)' },\n { value: 'motion', label: 'Motion / Framer Motion (animations)' },\n { value: 'nuqs', label: 'nuqs (URL state management)' },\n { value: 'trpc', label: 'tRPC (end-to-end type safety, if using separate backend)' },\n { value: 'better-auth', label: 'Better Auth (alternative to NextAuth)' },\n { value: 'uploadthing', label: 'Uploadthing (file uploads)' },\n { value: 'sonner', label: 'Sonner (toast notifications)' },\n { value: 'next-intl', label: 'next-intl (i18n)' },\n { value: 'sentry', label: 'Sentry (error tracking)' },\n ],\n initialValues: ['tanstack-table'],\n required: false,\n }),\n );\n\n let withExamples = true;\n if (!options.noExamples) {\n withExamples = exitOnCancel(\n await confirm({\n message: 'Generate example files with comments (Russian)?',\n initialValue: true,\n }),\n );\n } else {\n withExamples = false;\n }\n\n const selections: InitSelections = {\n stateManager,\n formLibrary,\n optionalPackages,\n withExamples,\n };\n\n const proceed = exitOnCancel(\n await confirm({\n message: `Proceed with this setup?\\n${formatSelectionsSummary(selections).map((l) => ` • ${l}`).join('\\n')}`,\n initialValue: true,\n }),\n );\n\n if (!proceed) {\n cancel('Cancelled');\n process.exit(0);\n }\n\n return selections;\n}\n"],"mappings":"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","names":["cancel","log","outro","path","log","fs","path","fs","path","fs","path","path","fs","assertNextProject","path","fs","log","confirm","log","fs","path","fs","path","path","fs","fs","path","cancel","isCancel","select","exitOnCancel","isCancel","cancel","select","path","fs","confirm","log","cancel","path","outro","log"]}
|
package/package.json
CHANGED
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"lint": "eslint"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@tanstack/react-query": "^5.101.2",
|
|
13
12
|
"class-variance-authority": "^0.7.1",
|
|
14
13
|
"clsx": "^2.1.1",
|
|
15
14
|
"lucide-react": "^1.22.0",
|
|
@@ -19,9 +18,7 @@
|
|
|
19
18
|
"react-dom": "19.2.4",
|
|
20
19
|
"shadcn": "^4.12.0",
|
|
21
20
|
"tailwind-merge": "^3.6.0",
|
|
22
|
-
"tw-animate-css": "^1.4.0"
|
|
23
|
-
"zod": "^4.4.3",
|
|
24
|
-
"zustand": "^5.0.14"
|
|
21
|
+
"tw-animate-css": "^1.4.0"
|
|
25
22
|
},
|
|
26
23
|
"devDependencies": {
|
|
27
24
|
"@tailwindcss/postcss": "^4",
|
|
@@ -33,5 +30,29 @@
|
|
|
33
30
|
"eslint-plugin-next-arch": "workspace:*",
|
|
34
31
|
"tailwindcss": "^4",
|
|
35
32
|
"typescript": "^5"
|
|
33
|
+
},
|
|
34
|
+
"_comment_optionalPackages": {
|
|
35
|
+
"zustand": "^5.0.14",
|
|
36
|
+
"@reduxjs/toolkit": "^2.8.2",
|
|
37
|
+
"react-redux": "^9.2.0",
|
|
38
|
+
"jotai": "^2.12.5",
|
|
39
|
+
"@tanstack/react-form": "^1.0.0",
|
|
40
|
+
"zod": "^4.4.3",
|
|
41
|
+
"react-hook-form": "^7.56.4",
|
|
42
|
+
"@hookform/resolvers": "^5.0.1",
|
|
43
|
+
"@tanstack/react-query": "^5.101.2",
|
|
44
|
+
"@tanstack/react-query-devtools": "^5.101.2",
|
|
45
|
+
"@tanstack/react-table": "^8.21.3",
|
|
46
|
+
"motion": "^12.19.1",
|
|
47
|
+
"nuqs": "^2.4.3",
|
|
48
|
+
"@trpc/client": "^11.1.2",
|
|
49
|
+
"@trpc/server": "^11.1.2",
|
|
50
|
+
"@trpc/react-query": "^11.1.2",
|
|
51
|
+
"better-auth": "^1.2.8",
|
|
52
|
+
"uploadthing": "^7.7.2",
|
|
53
|
+
"@uploadthing/react": "^7.3.1",
|
|
54
|
+
"sonner": "^2.0.3",
|
|
55
|
+
"next-intl": "^4.1.0",
|
|
56
|
+
"@sentry/nextjs": "^9.22.0"
|
|
36
57
|
}
|
|
37
58
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ПРИМЕР: Better Auth — точка входа
|
|
3
|
+
*
|
|
4
|
+
* Где живёт: shared/lib/ или features/auth/lib/
|
|
5
|
+
* Для auth-фичи логичнее features/auth/lib/auth.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// import { betterAuth } from 'better-auth';
|
|
9
|
+
// export const auth = betterAuth({ /* config */ });
|
|
10
|
+
|
|
11
|
+
export const AUTH_PLACEHOLDER = 'Настрой Better Auth в features/auth/lib/';
|
|
12
|
+
|
|
13
|
+
// Куда это идёт в архитектуре:
|
|
14
|
+
// features/auth/lib/ — инфраструктура авторизации внутри фичи auth.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ПРИМЕР: типизированные переменные окружения
|
|
3
|
+
*
|
|
4
|
+
* Где живёт: shared/config/
|
|
5
|
+
* Почему здесь: env — конфигурация приложения, не бизнес-логика.
|
|
6
|
+
* Валидируй один раз при старте, используй везде с типами.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
const envSchema = z.object({
|
|
12
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
|
13
|
+
NEXT_PUBLIC_APP_URL: z.string().url().optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const env = envSchema.parse({
|
|
17
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
18
|
+
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Куда это идёт в архитектуре:
|
|
22
|
+
// shared/config/ — доступен всем слоям. Не дублируй process.env в фичах.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Jotai Provider
|
|
5
|
+
*
|
|
6
|
+
* Где живёт: shared/providers/
|
|
7
|
+
* Подключи в app/layout.tsx.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Provider } from 'jotai';
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
12
|
+
|
|
13
|
+
interface JotaiProviderProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function JotaiProvider({ children }: JotaiProviderProps) {
|
|
18
|
+
return <Provider>{children}</Provider>;
|
|
19
|
+
}
|
package/templates/packages/jotai/examples/src/features/_examples/with-jotai/model/example.atoms.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Jotai atoms внутри фичи
|
|
5
|
+
*
|
|
6
|
+
* Где живёт: features/<name>/model/
|
|
7
|
+
* Атомарный стейт — альтернатива Zustand для мелкого гранулярного стейта.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { atom } from 'jotai';
|
|
11
|
+
|
|
12
|
+
export const sidebarOpenAtom = atom(true);
|
|
13
|
+
|
|
14
|
+
export const toggleSidebarAtom = atom(null, (get, set) => {
|
|
15
|
+
set(sidebarOpenAtom, !get(sidebarOpenAtom));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Куда это идёт в архитектуре:
|
|
19
|
+
// features/<name>/model/ — атомы принадлежат фиче.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ПРИМЕР: переиспользуемые Motion variants
|
|
3
|
+
*
|
|
4
|
+
* Где живёт: shared/lib/
|
|
5
|
+
* Почему здесь: variants — дизайн-токены анимации, не бизнес-логика.
|
|
6
|
+
* Компоненты фич импортируют variants отсюда.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const fadeIn = {
|
|
10
|
+
initial: { opacity: 0, y: 8 },
|
|
11
|
+
animate: { opacity: 1, y: 0 },
|
|
12
|
+
exit: { opacity: 0, y: -8 },
|
|
13
|
+
transition: { duration: 0.2 },
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
// Куда это идёт в архитектуре:
|
|
17
|
+
// shared/lib/motion.ts — импорт в features/<name>/components/ через @/shared/lib/motion.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Motion в компоненте фичи
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { motion } from 'motion/react';
|
|
8
|
+
import { fadeIn } from '@/shared/lib/motion';
|
|
9
|
+
|
|
10
|
+
export function ExampleMotionCard() {
|
|
11
|
+
return (
|
|
12
|
+
<motion.div {...fadeIn} className="rounded-xl border p-6">
|
|
13
|
+
Анимированная карточка
|
|
14
|
+
</motion.div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Куда это идёт в архитектуре:
|
|
19
|
+
// features/<name>/components/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ПРИМЕР: next-intl конфиг
|
|
3
|
+
*
|
|
4
|
+
* Где живёт: shared/config/ или корень проекта (i18n.ts)
|
|
5
|
+
* Следуй гайду next-intl для App Router.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const locales = ['ru', 'en'] as const;
|
|
9
|
+
export type Locale = (typeof locales)[number];
|
|
10
|
+
export const defaultLocale: Locale = 'ru';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: nuqs — type-safe searchParams
|
|
5
|
+
*
|
|
6
|
+
* Где живёт: features/<name>/hooks/
|
|
7
|
+
* URL-стейт привязан к фиче (фильтры, пагинация списка).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseAsInteger, useQueryState } from 'nuqs';
|
|
11
|
+
|
|
12
|
+
export function useExampleParams() {
|
|
13
|
+
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(1));
|
|
14
|
+
|
|
15
|
+
return { page, setPage };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Куда это идёт в архитектуре:
|
|
19
|
+
// features/<name>/hooks/ — view использует хук через публичный API фичи.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Пример: React Hook Form + zodResolver
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: React Hook Form + Zod
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
8
|
+
import { useForm } from 'react-hook-form';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
const exampleSchema = z.object({
|
|
12
|
+
email: z.string().email(),
|
|
13
|
+
password: z.string().min(8),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
type ExampleFormValues = z.infer<typeof exampleSchema>;
|
|
17
|
+
|
|
18
|
+
export function ExampleRhfForm() {
|
|
19
|
+
const { register, handleSubmit } = useForm<ExampleFormValues>({
|
|
20
|
+
resolver: zodResolver(exampleSchema),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<form onSubmit={handleSubmit((data) => console.log(data))} className="flex flex-col gap-4 max-w-sm">
|
|
25
|
+
<input className="rounded border px-3 py-2" {...register('email')} placeholder="Email" />
|
|
26
|
+
<input
|
|
27
|
+
className="rounded border px-3 py-2"
|
|
28
|
+
type="password"
|
|
29
|
+
{...register('password')}
|
|
30
|
+
placeholder="Password"
|
|
31
|
+
/>
|
|
32
|
+
<button type="submit" className="rounded bg-primary px-4 py-2 text-primary-foreground">
|
|
33
|
+
Отправить
|
|
34
|
+
</button>
|
|
35
|
+
</form>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Куда это идёт в архитектуре:
|
|
40
|
+
// features/<name>/components/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Сборка Redux store из reducers фич.
|
|
5
|
+
* Добавляй reducers из features/<name>/model/ здесь.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
9
|
+
|
|
10
|
+
export const store = configureStore({
|
|
11
|
+
reducer: {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
15
|
+
export type AppDispatch = typeof store.dispatch;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Redux Provider для Next.js
|
|
5
|
+
*
|
|
6
|
+
* Где живёт: shared/providers/
|
|
7
|
+
* Подключи в app/layout.tsx. Reducers фич регистрируй в app/providers/store.ts
|
|
8
|
+
* (слой app может импортировать features — это разрешено).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Provider } from 'react-redux';
|
|
12
|
+
import type { ReactNode } from 'react';
|
|
13
|
+
import { store } from '@/app/providers/redux-store';
|
|
14
|
+
|
|
15
|
+
interface ReduxProviderProps {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ReduxProvider({ children }: ReduxProviderProps) {
|
|
20
|
+
return <Provider store={store}>{children}</Provider>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Куда это идёт в архитектуре:
|
|
24
|
+
// shared/providers/ → app/layout.tsx
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Redux store с reducer из фичи-примера.
|
|
5
|
+
* Перезаписывает app/providers/redux-store.ts при генерации примеров.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
9
|
+
import { exampleReducer } from '@/features/_examples/with-redux/model/example.slice';
|
|
10
|
+
|
|
11
|
+
export const store = configureStore({
|
|
12
|
+
reducer: {
|
|
13
|
+
example: exampleReducer,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
18
|
+
export type AppDispatch = typeof store.dispatch;
|
package/templates/packages/redux/examples/src/features/_examples/with-redux/model/example.slice.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ПРИМЕР: Redux Toolkit slice внутри фичи
|
|
5
|
+
*
|
|
6
|
+
* Где живёт: features/<name>/model/
|
|
7
|
+
* Почему здесь: Redux slice = стейт конкретной фичи.
|
|
8
|
+
* Глобальный store собирается в shared/lib/store.ts из slices фич.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
12
|
+
|
|
13
|
+
interface ExampleState {
|
|
14
|
+
count: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const initialState: ExampleState = { count: 0 };
|
|
18
|
+
|
|
19
|
+
const exampleSlice = createSlice({
|
|
20
|
+
name: 'example',
|
|
21
|
+
initialState,
|
|
22
|
+
reducers: {
|
|
23
|
+
increment: (state) => {
|
|
24
|
+
state.count += 1;
|
|
25
|
+
},
|
|
26
|
+
setCount: (state, action: PayloadAction<number>) => {
|
|
27
|
+
state.count = action.payload;
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const { increment, setCount } = exampleSlice.actions;
|
|
33
|
+
export const exampleReducer = exampleSlice.reducer;
|
|
34
|
+
|
|
35
|
+
// Куда это идёт в архитектуре:
|
|
36
|
+
// features/<name>/model/ — reducer регистрируется в shared/lib/store.ts.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ПРИМЕР: Sentry — инициализация
|
|
3
|
+
*
|
|
4
|
+
* Запусти: npx @sentry/wizard@latest -i nextjs
|
|
5
|
+
* Этот файл — напоминание о месте в архитектуре.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const SENTRY_NOTE =
|
|
9
|
+
'Sentry настраивается через wizard в корне проекта (sentry.client.config.ts и т.д.)';
|