kaddidlehopper 0.5.0 → 0.7.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/cli.js +123 -206
- package/dist/index.js +1 -31
- package/dist/types/cli.d.ts +1 -8
- package/dist/types/types.d.ts +7 -12
- package/package.json +1 -2
- package/src/cli.ts +152 -269
- package/src/index.ts +1 -47
- package/src/types.ts +7 -13
- package/CONTEXT.md +0 -139
- package/add-ons/ai/README.md +0 -34
- package/add-ons/ai/assets/AGENTS.md.append +0 -24
- package/add-ons/ai/assets/_dot_env.local.append +0 -13
- package/add-ons/ai/assets/src/components/AIAssistant.tsx +0 -149
- package/add-ons/ai/assets/src/lib/ai-hook.ts +0 -21
- package/add-ons/ai/assets/src/lib/weather-tools.ts +0 -30
- package/add-ons/ai/assets/src/routes/api.chat.ts +0 -94
- package/add-ons/ai/assets/src/routes/chat.css +0 -175
- package/add-ons/ai/assets/src/routes/chat.tsx +0 -141
- package/add-ons/ai/info.json +0 -22
- package/add-ons/ai/package.json +0 -17
- package/add-ons/ai/small-logo.svg +0 -8
- package/add-ons/db/assets/AGENTS.md.append +0 -22
- package/add-ons/db/assets/DB-SETUP.md +0 -65
- package/add-ons/db/assets/_dot_env.local.append +0 -2
- package/add-ons/db/assets/drizzle.config.ts +0 -10
- package/add-ons/db/assets/src/db/index.ts +0 -8
- package/add-ons/db/assets/src/db/schema.ts +0 -8
- package/add-ons/db/assets/src/routes/db-example.tsx +0 -118
- package/add-ons/db/assets/src/server/guestbook.functions.ts +0 -23
- package/add-ons/db/info.json +0 -22
- package/add-ons/db/package.json +0 -10
- package/add-ons/forms/assets/AGENTS.md.append +0 -13
- package/add-ons/forms/assets/public/form-example.html +0 -14
- package/add-ons/forms/assets/src/routes/form-example.tsx +0 -76
- package/add-ons/forms/info.json +0 -18
- package/add-ons/forms/package.json +0 -3
- package/examples/ai-chat/README.md +0 -36
- package/examples/ai-chat/assets/AGENTS.md.append +0 -24
- package/examples/ai-chat/assets/src/lib/ai-hook.ts +0 -21
- package/examples/ai-chat/assets/src/lib/weather-tools.ts +0 -30
- package/examples/ai-chat/assets/src/routes/__root.tsx +0 -57
- package/examples/ai-chat/assets/src/routes/api.chat.ts +0 -94
- package/examples/ai-chat/assets/src/routes/index.tsx +0 -141
- package/examples/ai-chat/assets/src/styles.css +0 -165
- package/examples/ai-chat/info.json +0 -25
- package/examples/ai-chat/package.json +0 -14
- package/examples/blog/README.md +0 -60
- package/examples/blog/assets/AGENTS.md.append +0 -18
- package/examples/blog/assets/content/posts/beach.md +0 -12
- package/examples/blog/assets/content/posts/jungle.md.ejs +0 -12
- package/examples/blog/assets/content/posts/mountains.md.ejs +0 -12
- package/examples/blog/assets/content/posts/snorkeling.md.ejs +0 -12
- package/examples/blog/assets/content/posts/waterfall.md.ejs +0 -12
- package/examples/blog/assets/content-collections.ts +0 -30
- package/examples/blog/assets/public/beach.jpg +0 -0
- package/examples/blog/assets/public/jungle.jpg +0 -0
- package/examples/blog/assets/public/mountains.jpg +0 -0
- package/examples/blog/assets/public/snorkeling.jpg +0 -0
- package/examples/blog/assets/public/waterfall.jpg +0 -0
- package/examples/blog/assets/src/components/Header.tsx +0 -52
- package/examples/blog/assets/src/components/VacayAssistant.tsx +0 -205
- package/examples/blog/assets/src/components/blog-posts.tsx +0 -78
- package/examples/blog/assets/src/components/ui/card.tsx +0 -92
- package/examples/blog/assets/src/lib/blog-ai-hook.ts +0 -25
- package/examples/blog/assets/src/lib/blog-tools.ts +0 -111
- package/examples/blog/assets/src/lib/utils.ts +0 -6
- package/examples/blog/assets/src/routes/__root.tsx +0 -57
- package/examples/blog/assets/src/routes/api.blog-chat.ts +0 -117
- package/examples/blog/assets/src/routes/category.$category.tsx +0 -19
- package/examples/blog/assets/src/routes/index.tsx +0 -19
- package/examples/blog/assets/src/routes/posts.$slug.tsx +0 -63
- package/examples/blog/assets/src/styles.css +0 -138
- package/examples/blog/info.json +0 -40
- package/examples/blog/package.json +0 -23
- package/examples/calculator/README.md +0 -16
- package/examples/calculator/assets/AGENTS.md.append +0 -9
- package/examples/calculator/assets/src/components/Calculator.tsx +0 -238
- package/examples/calculator/assets/src/components/Header.tsx +0 -17
- package/examples/calculator/assets/src/routes/__root.tsx +0 -57
- package/examples/calculator/assets/src/routes/index.tsx +0 -14
- package/examples/calculator/info.json +0 -20
- package/examples/calculator/package.json +0 -1
- package/examples/dashboard/README.md +0 -17
- package/examples/dashboard/assets/AGENTS.md.append +0 -12
- package/examples/dashboard/assets/src/components/Header.tsx +0 -17
- package/examples/dashboard/assets/src/routes/__root.tsx +0 -57
- package/examples/dashboard/assets/src/routes/index.tsx +0 -158
- package/examples/dashboard/info.json +0 -19
- package/examples/dashboard/package.json +0 -6
- package/examples/ecommerce/README.md +0 -48
- package/examples/ecommerce/assets/AGENTS.md.append +0 -22
- package/examples/ecommerce/assets/public/logo.png +0 -0
- package/examples/ecommerce/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/ecommerce/assets/src/components/BuyButton.tsx +0 -35
- package/examples/ecommerce/assets/src/components/Header.tsx +0 -36
- package/examples/ecommerce/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
- package/examples/ecommerce/assets/src/components/MotorcycleRecommendation.tsx +0 -53
- package/examples/ecommerce/assets/src/data/motorcycles.ts +0 -27
- package/examples/ecommerce/assets/src/lib/motorcycle-ai-hook.ts +0 -24
- package/examples/ecommerce/assets/src/lib/motorcycle-tools.ts +0 -42
- package/examples/ecommerce/assets/src/lib/stripe.server.ts +0 -39
- package/examples/ecommerce/assets/src/routes/__root.tsx +0 -57
- package/examples/ecommerce/assets/src/routes/api.motorcycle-chat.ts +0 -78
- package/examples/ecommerce/assets/src/routes/checkout/cancel.tsx +0 -25
- package/examples/ecommerce/assets/src/routes/checkout/success.tsx +0 -25
- package/examples/ecommerce/assets/src/routes/index.tsx +0 -76
- package/examples/ecommerce/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -55
- package/examples/ecommerce/assets/src/store/motorcycle-assistant.ts +0 -3
- package/examples/ecommerce/assets/src/styles.css +0 -212
- package/examples/ecommerce/info.json +0 -38
- package/examples/ecommerce/package.json +0 -13
- package/examples/events/README.md +0 -110
- package/examples/events/assets/AGENTS.md.append +0 -21
- package/examples/events/assets/content/speakers/andre-costa.md +0 -22
- package/examples/events/assets/content/speakers/hans-mueller.md.ejs +0 -22
- package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +0 -22
- package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +0 -22
- package/examples/events/assets/content/speakers/marie-dubois.md.ejs +0 -20
- package/examples/events/assets/content/speakers/priya-sharma.md.ejs +0 -22
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +0 -39
- package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +0 -39
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +0 -39
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +0 -39
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +0 -36
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +0 -32
- package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +0 -39
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +0 -39
- package/examples/events/assets/content-collections.ts +0 -56
- package/examples/events/assets/public/background-1.jpg +0 -0
- package/examples/events/assets/public/background-2.jpg +0 -0
- package/examples/events/assets/public/background-3.jpg +0 -0
- package/examples/events/assets/public/background-4.jpg +0 -0
- package/examples/events/assets/public/conference-logo.png +0 -0
- package/examples/events/assets/public/favicon.ico +0 -0
- package/examples/events/assets/public/speakers/andre-costa.jpg +0 -0
- package/examples/events/assets/public/speakers/hans-mueller.jpg +0 -0
- package/examples/events/assets/public/speakers/isabella-martinez.jpg +0 -0
- package/examples/events/assets/public/speakers/kenji-nakamura.jpg +0 -0
- package/examples/events/assets/public/speakers/marie-dubois.jpg +0 -0
- package/examples/events/assets/public/speakers/priya-sharma.jpg +0 -0
- package/examples/events/assets/public/talks/croissant-lamination-secrets.jpg +0 -0
- package/examples/events/assets/public/talks/french-macaron-mastery.jpg +0 -0
- package/examples/events/assets/public/talks/neapolitan-pizza-tradition-meets-innovation.jpg +0 -0
- package/examples/events/assets/public/talks/savory-breads-of-the-mediterranean.jpg +0 -0
- package/examples/events/assets/public/talks/sourdough-from-starter-to-masterpiece.jpg +0 -0
- package/examples/events/assets/public/talks/the-art-of-the-perfect-tart.jpg +0 -0
- package/examples/events/assets/public/talks/the-science-of-sugar.jpg +0 -0
- package/examples/events/assets/public/talks/umami-in-pastry-east-meets-west.jpg +0 -0
- package/examples/events/assets/public/tanstack-circle-logo.png +0 -0
- package/examples/events/assets/public/tanstack-word-logo-white.svg +0 -1
- package/examples/events/assets/src/components/Header.tsx +0 -59
- package/examples/events/assets/src/components/HeaderNav.tsx +0 -67
- package/examples/events/assets/src/components/HeroCarousel.tsx +0 -61
- package/examples/events/assets/src/components/RemyAssistant.tsx +0 -207
- package/examples/events/assets/src/components/SpeakerCard.tsx +0 -67
- package/examples/events/assets/src/components/TalkCard.tsx +0 -77
- package/examples/events/assets/src/components/ui/card.tsx +0 -92
- package/examples/events/assets/src/lib/conference-ai-hook.ts +0 -26
- package/examples/events/assets/src/lib/conference-tools.ts +0 -210
- package/examples/events/assets/src/lib/model-selection.ts +0 -1
- package/examples/events/assets/src/lib/utils.ts +0 -6
- package/examples/events/assets/src/routes/__root.tsx +0 -70
- package/examples/events/assets/src/routes/api.remy-chat.ts +0 -119
- package/examples/events/assets/src/routes/index.tsx +0 -192
- package/examples/events/assets/src/routes/schedule.index.tsx +0 -274
- package/examples/events/assets/src/routes/speakers.$slug.tsx +0 -122
- package/examples/events/assets/src/routes/speakers.index.tsx +0 -40
- package/examples/events/assets/src/routes/talks.$slug.tsx +0 -116
- package/examples/events/assets/src/routes/talks.index.tsx +0 -40
- package/examples/events/assets/src/styles.css +0 -182
- package/examples/events/info.json +0 -61
- package/examples/events/package.json +0 -23
- package/examples/marketing/README.md +0 -60
- package/examples/marketing/assets/AGENTS.md.append +0 -15
- package/examples/marketing/assets/public/logo.png +0 -0
- package/examples/marketing/assets/public/motorcycle-adventure.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-cruiser.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-scooter.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-sport.jpg +0 -0
- package/examples/marketing/assets/public/motorcycle-supersport.jpg +0 -0
- package/examples/marketing/assets/src/components/Header.tsx +0 -36
- package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +0 -162
- package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +0 -53
- package/examples/marketing/assets/src/data/motorcycles.ts.ejs +0 -77
- package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +0 -24
- package/examples/marketing/assets/src/lib/motorcycle-tools.ts +0 -42
- package/examples/marketing/assets/src/routes/__root.tsx +0 -57
- package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +0 -78
- package/examples/marketing/assets/src/routes/index.tsx +0 -72
- package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +0 -56
- package/examples/marketing/assets/src/store/motorcycle-assistant.ts +0 -3
- package/examples/marketing/assets/src/styles.css +0 -212
- package/examples/marketing/info.json +0 -33
- package/examples/marketing/package.json +0 -14
- package/examples/portfolio/README.md +0 -49
- package/examples/portfolio/assets/AGENTS.md.append +0 -21
- package/examples/portfolio/assets/content/blog/getting-started-with-tanstack.md +0 -53
- package/examples/portfolio/assets/content/blog/react-19-features.md +0 -78
- package/examples/portfolio/assets/content/blog/tailwind-css-v4-guide.md +0 -60
- package/examples/portfolio/assets/content/education/code-school.md +0 -17
- package/examples/portfolio/assets/content/jobs/initech-junior.md +0 -20
- package/examples/portfolio/assets/content/projects/portfolio-site.md +0 -15
- package/examples/portfolio/assets/content/projects/task-manager.md +0 -15
- package/examples/portfolio/assets/content-collections.ts +0 -65
- package/examples/portfolio/assets/public/contact.html +0 -6
- package/examples/portfolio/assets/public/headshot-on-white.jpg +0 -0
- package/examples/portfolio/assets/src/components/Header.tsx +0 -33
- package/examples/portfolio/assets/src/components/ResumeAssistant.tsx +0 -193
- package/examples/portfolio/assets/src/components/ui/badge.tsx +0 -46
- package/examples/portfolio/assets/src/components/ui/card.tsx +0 -92
- package/examples/portfolio/assets/src/components/ui/checkbox.tsx +0 -30
- package/examples/portfolio/assets/src/components/ui/hover-card.tsx +0 -44
- package/examples/portfolio/assets/src/components/ui/separator.tsx +0 -26
- package/examples/portfolio/assets/src/lib/resume-ai-hook.ts +0 -21
- package/examples/portfolio/assets/src/lib/resume-tools.ts +0 -165
- package/examples/portfolio/assets/src/lib/utils.ts +0 -6
- package/examples/portfolio/assets/src/routes/__root.tsx +0 -57
- package/examples/portfolio/assets/src/routes/api.resume-chat.ts +0 -116
- package/examples/portfolio/assets/src/routes/blog/$slug.tsx +0 -73
- package/examples/portfolio/assets/src/routes/contact.tsx +0 -121
- package/examples/portfolio/assets/src/routes/index.tsx +0 -55
- package/examples/portfolio/assets/src/routes/projects.tsx +0 -62
- package/examples/portfolio/assets/src/routes/resume.tsx +0 -220
- package/examples/portfolio/assets/src/styles.css +0 -138
- package/examples/portfolio/info.json +0 -50
- package/examples/portfolio/package.json +0 -26
- package/examples/resume/README.md +0 -82
- package/examples/resume/assets/AGENTS.md.append +0 -19
- package/examples/resume/assets/content/education/code-school.md +0 -17
- package/examples/resume/assets/content/jobs/freelance.md.ejs +0 -13
- package/examples/resume/assets/content/jobs/initech-junior.md +0 -20
- package/examples/resume/assets/content/jobs/initech-lead.md.ejs +0 -29
- package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +0 -28
- package/examples/resume/assets/content-collections.ts +0 -36
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/Header.tsx +0 -33
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +0 -193
- package/examples/resume/assets/src/components/ui/badge.tsx +0 -46
- package/examples/resume/assets/src/components/ui/card.tsx +0 -92
- package/examples/resume/assets/src/components/ui/checkbox.tsx +0 -30
- package/examples/resume/assets/src/components/ui/hover-card.tsx +0 -44
- package/examples/resume/assets/src/components/ui/separator.tsx +0 -26
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +0 -21
- package/examples/resume/assets/src/lib/resume-tools.ts +0 -165
- package/examples/resume/assets/src/lib/utils.ts +0 -6
- package/examples/resume/assets/src/routes/api.resume-chat.ts +0 -110
- package/examples/resume/assets/src/routes/index.tsx +0 -220
- package/examples/resume/assets/src/styles.css +0 -138
- package/examples/resume/info.json +0 -35
- package/examples/resume/package.json +0 -26
- package/examples/saas/README.md +0 -16
- package/examples/saas/assets/AGENTS.md.append +0 -9
- package/examples/saas/assets/src/components/Header.tsx +0 -17
- package/examples/saas/assets/src/routes/__root.tsx +0 -57
- package/examples/saas/assets/src/routes/faq.tsx +0 -94
- package/examples/saas/assets/src/routes/index.tsx +0 -197
- package/examples/saas/info.json +0 -23
- package/examples/saas/package.json +0 -1
- package/examples/survey/README.md +0 -20
- package/examples/survey/assets/AGENTS.md.append +0 -9
- package/examples/survey/assets/public/form-survey.html +0 -15
- package/examples/survey/assets/src/components/SurveyForm.tsx +0 -128
- package/examples/survey/assets/src/routes/index.tsx +0 -14
- package/examples/survey/info.json +0 -19
- package/examples/survey/package.json +0 -1
- package/project/base/AGENTS.md +0 -86
- package/project/base/_dot_claude/skills/content-collections/SKILL.md +0 -505
- package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +0 -410
- package/project/base/_dot_claude/skills/netlify-db/SKILL.md +0 -424
- package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +0 -419
- package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +0 -243
- package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +0 -372
- package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +0 -421
- package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +0 -426
- package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +0 -493
- package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +0 -430
- package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +0 -445
- package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +0 -494
- package/project/base/_dot_gitignore +0 -8
- package/project/base/netlify.toml +0 -7
- package/project/base/package.json +0 -33
- package/project/base/public/favicon.ico +0 -0
- package/project/base/public/tanstack-circle-logo.png +0 -0
- package/project/base/public/tanstack-word-logo-white.svg +0 -1
- package/project/base/src/components/Header.tsx +0 -17
- package/project/base/src/components/HeaderNav.tsx.ejs +0 -179
- package/project/base/src/router.tsx +0 -15
- package/project/base/src/routes/__root.tsx +0 -57
- package/project/base/src/routes/index.tsx +0 -48
- package/project/base/src/styles.css +0 -15
- package/project/base/tsconfig.json +0 -28
- package/project/base/vite.config.ts.ejs +0 -25
- package/project/info.json +0 -17
- package/project/packages.json +0 -22
- package/scripts/check-outdated-packages.js +0 -421
- package/src/agents-md.ts +0 -139
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { resolve, basename
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { resolve, basename } from 'node:path';
|
|
2
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { cp, rm, mkdtemp, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import { Command } from 'commander';
|
|
4
8
|
import chalk from 'chalk';
|
|
5
9
|
import validatePackageName from 'validate-npm-package-name';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// Utility functions
|
|
10
|
+
const GITHUB_REPO = 'https://github.com/netlify/swar-templates.git';
|
|
11
|
+
const MANIFEST_URL = 'https://raw.githubusercontent.com/netlify/swar-templates/main/manifest.json';
|
|
9
12
|
function sanitizePackageName(name) {
|
|
10
13
|
return name
|
|
11
14
|
.toLowerCase()
|
|
@@ -28,230 +31,144 @@ function validateProjectName(name) {
|
|
|
28
31
|
'Project name does not meet npm package naming requirements',
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (title && message) {
|
|
39
|
-
console.log(chalk.blue(`${title}: ${message}`));
|
|
40
|
-
}
|
|
41
|
-
else if (title) {
|
|
42
|
-
console.log(chalk.blue(title));
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
env.error = (title, message) => {
|
|
46
|
-
if (title && message) {
|
|
47
|
-
console.error(chalk.red(`${title}: ${message}`));
|
|
48
|
-
}
|
|
49
|
-
else if (title) {
|
|
50
|
-
console.error(chalk.red(title));
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
env.warn = (title, message) => {
|
|
54
|
-
if (title && message) {
|
|
55
|
-
console.warn(chalk.yellow(`${title}: ${message}`));
|
|
56
|
-
}
|
|
57
|
-
else if (title) {
|
|
58
|
-
console.warn(chalk.yellow(title));
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
env.spinner = () => ({
|
|
62
|
-
start: (message) => console.log(chalk.gray(`⟳ ${message}`)),
|
|
63
|
-
stop: (message) => console.log(chalk.green(`✓ ${message}`)),
|
|
64
|
-
});
|
|
65
|
-
return env;
|
|
34
|
+
function getPackageManagerFromUserAgent() {
|
|
35
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
36
|
+
if (!userAgent)
|
|
37
|
+
return undefined;
|
|
38
|
+
const pmSpec = userAgent.split(' ')[0];
|
|
39
|
+
const separatorPos = pmSpec.lastIndexOf('/');
|
|
40
|
+
return separatorPos !== -1 ? pmSpec.substring(0, separatorPos) : pmSpec;
|
|
66
41
|
}
|
|
67
|
-
export function cli(
|
|
68
|
-
const environment = createEnvironment(appName);
|
|
42
|
+
export function cli() {
|
|
69
43
|
const program = new Command();
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
program
|
|
45
|
+
.name('netlify-cta')
|
|
46
|
+
.description('CLI to create a new Netlify TanStack Start application');
|
|
72
47
|
program.argument('[project-name]', 'name of the project');
|
|
73
48
|
program
|
|
74
49
|
.option('--no-install', 'skip installing dependencies')
|
|
75
|
-
.option(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
return value;
|
|
80
|
-
})
|
|
81
|
-
.option('--add-ons [...add-ons]', 'pick from a list of available add-ons (comma separated list)', (value) => {
|
|
82
|
-
let addOns = !!value;
|
|
83
|
-
if (typeof value === 'string') {
|
|
84
|
-
addOns = value.split(',').map((addon) => addon.trim());
|
|
85
|
-
}
|
|
86
|
-
return addOns;
|
|
87
|
-
})
|
|
88
|
-
.option('--list-add-ons', 'list all available add-ons', false)
|
|
89
|
-
.option('--list-addons-json', 'list all available add-ons as JSON', false)
|
|
90
|
-
.option('--addon-details <addon-id>', 'show detailed information about a specific add-on')
|
|
50
|
+
.option('--package-manager <pm>', 'explicitly tell the CLI to use this package manager')
|
|
51
|
+
.option('--add-ons [id]', 'starter ID to use from the template repo')
|
|
52
|
+
.option('--list-addons-json', 'list all available starters as JSON', false)
|
|
91
53
|
.option('--no-git', 'do not create a git repository')
|
|
92
|
-
.option('--target-dir <path>', 'the target directory for the application root'
|
|
93
|
-
.option('-f, --force', 'force project creation even if the target directory is not empty'
|
|
94
|
-
.option('--no-force', 'refuse to create if the target directory is not empty')
|
|
95
|
-
.option('--bare-bones', 'create minimal scaffolding for LLM modification', true)
|
|
96
|
-
.option('--no-bare-bones', 'create full scaffolding (not minimal)');
|
|
54
|
+
.option('--target-dir <path>', 'the target directory for the application root')
|
|
55
|
+
.option('-f, --force', 'force project creation even if the target directory is not empty');
|
|
97
56
|
program.action(async (projectName, options) => {
|
|
98
|
-
const framework = getFrameworkByName(defaultFramework);
|
|
99
|
-
if (!framework) {
|
|
100
|
-
console.error(`Framework '${defaultFramework}' not found`);
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
57
|
// Handle --list-addons-json
|
|
104
58
|
if (options.listAddonsJson) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return;
|
|
59
|
+
const manifest = await fetch(MANIFEST_URL).then((r) => r.json());
|
|
60
|
+
console.log(JSON.stringify(manifest.starters, null, 2));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
// Resolve starter ID
|
|
64
|
+
const starterId = typeof options.addOns === 'string' ? options.addOns : 'basic';
|
|
65
|
+
// Resolve target directory — default to CWD
|
|
66
|
+
const targetDir = options.targetDir ? resolve(options.targetDir) : resolve(process.cwd());
|
|
67
|
+
// Resolve project name for package.json
|
|
68
|
+
let resolvedProjectName;
|
|
69
|
+
if (projectName && projectName !== '.') {
|
|
70
|
+
resolvedProjectName = sanitizePackageName(projectName);
|
|
118
71
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const addOns = await getAllAddOns(framework, defaultMode);
|
|
122
|
-
let hasConfigurableAddOns = false;
|
|
123
|
-
for (const addOn of addOns.filter((a) => !forcedAddOns.includes(a.id))) {
|
|
124
|
-
const hasOptions = addOn.options && Object.keys(addOn.options).length > 0;
|
|
125
|
-
const optionMarker = hasOptions ? '*' : ' ';
|
|
126
|
-
if (hasOptions)
|
|
127
|
-
hasConfigurableAddOns = true;
|
|
128
|
-
console.log(`${optionMarker} ${chalk.bold(addOn.id)}: ${addOn.description}`);
|
|
129
|
-
}
|
|
130
|
-
if (hasConfigurableAddOns) {
|
|
131
|
-
console.log('\n* = has configuration options');
|
|
132
|
-
}
|
|
133
|
-
return;
|
|
72
|
+
else {
|
|
73
|
+
resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
|
|
134
74
|
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
75
|
+
const { valid, error } = validateProjectName(resolvedProjectName);
|
|
76
|
+
if (!valid) {
|
|
77
|
+
console.error(chalk.red(error));
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
// Check if target directory exists and is non-empty
|
|
81
|
+
if (existsSync(targetDir)) {
|
|
82
|
+
const contents = readdirSync(targetDir);
|
|
83
|
+
if (contents.length > 0 && !options.force) {
|
|
84
|
+
console.error(chalk.red(`Target directory "${targetDir}" is not empty. Use --force to overwrite.`));
|
|
141
85
|
process.exit(1);
|
|
142
86
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
87
|
+
}
|
|
88
|
+
// Delete index.html if it exists in the target directory
|
|
89
|
+
const indexHtmlPath = join(targetDir, 'index.html');
|
|
90
|
+
if (existsSync(indexHtmlPath)) {
|
|
91
|
+
await rm(indexHtmlPath);
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.bold.cyan(`Creating a new Netlify TanStack Start app in ${chalk.white(targetDir)}...`));
|
|
94
|
+
console.log(chalk.gray(`Using starter: ${starterId}`));
|
|
95
|
+
const manifest = await fetch(MANIFEST_URL).then((r) => r.json());
|
|
96
|
+
const starterEntry = manifest.starters.find((s) => s.id === starterId);
|
|
97
|
+
const frameworkId = starterEntry?.framework;
|
|
98
|
+
// Sparse clone the template repo into a temp directory
|
|
99
|
+
const tmpDir = await mkdtemp(join(tmpdir(), 'netlify-cta-'));
|
|
100
|
+
try {
|
|
101
|
+
console.log(chalk.gray('⟳ Fetching template...'));
|
|
102
|
+
const sparsePaths = [`starters/${starterId}`, ...(frameworkId ? [`frameworks/${frameworkId}`] : [])];
|
|
103
|
+
execSync(`git clone --depth=1 --filter=blob:none --sparse ${GITHUB_REPO} ${tmpDir}`, { stdio: 'pipe' });
|
|
104
|
+
execSync(`git -C ${tmpDir} sparse-checkout set ${sparsePaths.join(' ')}`, { stdio: 'pipe' });
|
|
105
|
+
const starterPath = join(tmpDir, 'starters', starterId);
|
|
106
|
+
if (!existsSync(starterPath)) {
|
|
107
|
+
console.error(chalk.red(`Starter "${starterId}" not found in the template repo. Run --list-addons-json to see available starters.`));
|
|
108
|
+
process.exit(1);
|
|
151
109
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (opt.type === 'select' && opt.options) {
|
|
165
|
-
console.log(` Available values:`);
|
|
166
|
-
for (const choice of opt.options) {
|
|
167
|
-
console.log(` - ${choice.value}: ${choice.label}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
110
|
+
// Copy starter files to target directory
|
|
111
|
+
await cp(starterPath, targetDir, { recursive: true });
|
|
112
|
+
// Copy framework overlay files if they exist
|
|
113
|
+
if (frameworkId) {
|
|
114
|
+
const frameworkPath = join(tmpDir, 'frameworks', frameworkId);
|
|
115
|
+
if (existsSync(frameworkPath)) {
|
|
116
|
+
console.log(chalk.gray(`⟳ Applying framework overlay (${frameworkId})...`));
|
|
117
|
+
await cp(frameworkPath, targetDir, { recursive: true });
|
|
118
|
+
console.log(chalk.green(`✓ Framework overlay applied (${frameworkId})`));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.log(chalk.yellow(`⚠ Framework overlay "${frameworkId}" not found in repo, skipping`));
|
|
171
122
|
}
|
|
172
123
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
console.log(` File: ${route.path}`);
|
|
124
|
+
// Update package.json name if a project name was provided
|
|
125
|
+
if (projectName && projectName !== '.') {
|
|
126
|
+
const pkgPath = join(targetDir, 'package.json');
|
|
127
|
+
if (existsSync(pkgPath)) {
|
|
128
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
|
|
129
|
+
pkg.name = resolvedProjectName;
|
|
130
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
181
131
|
}
|
|
182
132
|
}
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
// Handle project creation: target-dir defaults to current directory
|
|
186
|
-
const targetDir = resolve(options.targetDir ?? '.');
|
|
187
|
-
const isCurrentDir = targetDir === resolve(process.cwd());
|
|
188
|
-
let resolvedProjectName;
|
|
189
|
-
if (projectName) {
|
|
190
|
-
resolvedProjectName =
|
|
191
|
-
projectName === '.'
|
|
192
|
-
? sanitizePackageName(getCurrentDirectoryName())
|
|
193
|
-
: sanitizePackageName(projectName);
|
|
194
|
-
}
|
|
195
|
-
else if (isCurrentDir) {
|
|
196
|
-
resolvedProjectName = sanitizePackageName(getCurrentDirectoryName());
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
console.error('Project name is required when --target-dir is not the current directory');
|
|
200
|
-
program.help();
|
|
201
|
-
return;
|
|
133
|
+
console.log(chalk.green(`✓ Template copied`));
|
|
202
134
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
console.error(error);
|
|
206
|
-
process.exit(1);
|
|
135
|
+
finally {
|
|
136
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
207
137
|
}
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
138
|
+
// Initialize git repository
|
|
139
|
+
if (options.git !== false) {
|
|
140
|
+
try {
|
|
141
|
+
execSync('git init', { cwd: targetDir, stdio: 'pipe' });
|
|
142
|
+
console.log(chalk.green('✓ Initialized git repository'));
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
console.warn(chalk.yellow('⚠ Could not initialize git repository'));
|
|
213
146
|
}
|
|
214
147
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
getPackageManager() ||
|
|
225
|
-
DEFAULT_PACKAGE_MANAGER,
|
|
226
|
-
git: options.git !== false,
|
|
227
|
-
install: options.install !== false,
|
|
228
|
-
chosenAddOns,
|
|
229
|
-
addOnOptions: {
|
|
230
|
-
...populateAddOnOptionsDefaults(chosenAddOns),
|
|
231
|
-
project: { bareBones: options.bareBones ?? true },
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
environment.intro(`Creating a new ${appName} app in ${resolvedProjectName}...`);
|
|
235
|
-
await createApp(environment, finalOptions);
|
|
236
|
-
// Delete files specified in bareBones.deleteFiles for each add-on when in bare-bones mode
|
|
237
|
-
if (options.bareBones !== false) {
|
|
238
|
-
for (const addOn of chosenAddOns) {
|
|
239
|
-
const addOnWithBareBones = addOn;
|
|
240
|
-
if (addOnWithBareBones.bareBones?.deleteFiles) {
|
|
241
|
-
for (const file of addOnWithBareBones.bareBones.deleteFiles) {
|
|
242
|
-
const filePath = join(targetDir, file);
|
|
243
|
-
try {
|
|
244
|
-
await unlink(filePath);
|
|
245
|
-
}
|
|
246
|
-
catch {
|
|
247
|
-
// File may not exist, ignore errors
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
148
|
+
// Install dependencies
|
|
149
|
+
if (options.install !== false) {
|
|
150
|
+
const pm = options.packageManager ??
|
|
151
|
+
getPackageManagerFromUserAgent() ??
|
|
152
|
+
'pnpm';
|
|
153
|
+
console.log(chalk.gray(`⟳ Installing dependencies with ${pm}...`));
|
|
154
|
+
try {
|
|
155
|
+
execSync(`${pm} install`, { cwd: targetDir, stdio: 'inherit' });
|
|
156
|
+
console.log(chalk.green('✓ Dependencies installed'));
|
|
251
157
|
}
|
|
158
|
+
catch {
|
|
159
|
+
console.error(chalk.red(`Failed to install dependencies. Run \`${pm} install\` manually.`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log(chalk.bold.green('\nDone! Your project is ready.'));
|
|
163
|
+
console.log(chalk.white('\nNext steps:'));
|
|
164
|
+
console.log(chalk.cyan(` cd ${basename(targetDir)}`));
|
|
165
|
+
if (options.install === false) {
|
|
166
|
+
const pm = options.packageManager ??
|
|
167
|
+
getPackageManagerFromUserAgent() ??
|
|
168
|
+
'pnpm';
|
|
169
|
+
console.log(chalk.cyan(` ${pm} install`));
|
|
252
170
|
}
|
|
253
|
-
|
|
254
|
-
await generateAgentsMd(targetDir, resolvedProjectName, chosenAddOns);
|
|
171
|
+
console.log(chalk.cyan(' pnpm dev'));
|
|
255
172
|
});
|
|
256
173
|
program.parse();
|
|
257
174
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,33 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { dirname, join } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { registerFramework, scanAddOnDirectories, scanProjectDirectory, } from '@tanstack/cta-engine';
|
|
5
2
|
import { cli } from './cli.js';
|
|
6
|
-
|
|
7
|
-
const addOns = scanAddOnDirectories([
|
|
8
|
-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
|
|
9
|
-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'examples'),
|
|
10
|
-
]);
|
|
11
|
-
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(projectDirectory, join(dirname(dirname(fileURLToPath(import.meta.url))), 'project', 'base'));
|
|
12
|
-
registerFramework({
|
|
13
|
-
id: 'netlify-start',
|
|
14
|
-
name: 'Netlify TanStack Start',
|
|
15
|
-
description: 'TanStack Start applications for Netlify deployment',
|
|
16
|
-
version: '0.1.0',
|
|
17
|
-
base: files,
|
|
18
|
-
addOns,
|
|
19
|
-
basePackageJSON,
|
|
20
|
-
optionalPackages,
|
|
21
|
-
supportedModes: {
|
|
22
|
-
'file-router': {
|
|
23
|
-
displayName: 'File Router',
|
|
24
|
-
description: 'File-based routing with TanStack Start',
|
|
25
|
-
forceTypescript: true,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
cli({
|
|
30
|
-
name: 'netlify-cta',
|
|
31
|
-
appName: 'Netlify TanStack Start',
|
|
32
|
-
defaultFramework: 'Netlify TanStack Start',
|
|
33
|
-
});
|
|
3
|
+
cli();
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,8 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
name: string;
|
|
3
|
-
appName: string;
|
|
4
|
-
defaultFramework: string;
|
|
5
|
-
forcedMode?: string;
|
|
6
|
-
forcedAddOns?: Array<string>;
|
|
7
|
-
}
|
|
8
|
-
export declare function cli({ name, appName, defaultFramework, forcedMode, forcedAddOns, }: CliConfig): void;
|
|
1
|
+
export declare function cli(): void;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import type { PackageManager } from '@tanstack/cta-engine';
|
|
2
1
|
export interface CliOptions {
|
|
3
|
-
|
|
4
|
-
packageManager
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
targetDir?: string;
|
|
11
|
-
install?: boolean;
|
|
12
|
-
force?: boolean;
|
|
13
|
-
bareBones?: boolean;
|
|
2
|
+
install: boolean;
|
|
3
|
+
packageManager: string | undefined;
|
|
4
|
+
addOns: string | boolean | undefined;
|
|
5
|
+
listAddonsJson: boolean;
|
|
6
|
+
git: boolean;
|
|
7
|
+
force: boolean | undefined;
|
|
8
|
+
targetDir: string | undefined;
|
|
14
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaddidlehopper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Create TanStack Start applications for Netlify",
|
|
5
5
|
"bin": "./dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"author": "Jack Herrington <jherr@pobox.com>",
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@tanstack/cta-engine": "^0.48.0",
|
|
22
21
|
"chalk": "^5.4.1",
|
|
23
22
|
"commander": "^13.1.0",
|
|
24
23
|
"validate-npm-package-name": "^7.0.0"
|