kaddidlehopper 0.1.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/CONTEXT.md +139 -0
- package/README.md +47 -0
- package/add-ons/ai/README.md +34 -0
- package/add-ons/ai/assets/_dot_env.local.append +13 -0
- package/add-ons/ai/assets/src/components/AIAssistant.tsx +149 -0
- package/add-ons/ai/assets/src/lib/ai-hook.ts +21 -0
- package/add-ons/ai/assets/src/lib/weather-tools.ts +30 -0
- package/add-ons/ai/assets/src/routes/api.chat.ts +94 -0
- package/add-ons/ai/assets/src/routes/chat.css +175 -0
- package/add-ons/ai/assets/src/routes/chat.tsx +141 -0
- package/add-ons/ai/info.json +27 -0
- package/add-ons/ai/package.json +17 -0
- package/add-ons/ai/small-logo.svg +8 -0
- package/dist/cli.js +251 -0
- package/dist/index.js +33 -0
- package/dist/types/cli.d.ts +8 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/types.d.ts +14 -0
- package/dist/types.js +1 -0
- package/examples/blog/README.md +60 -0
- package/examples/blog/assets/content/posts/beach.md +12 -0
- package/examples/blog/assets/content/posts/jungle.md.ejs +12 -0
- package/examples/blog/assets/content/posts/mountains.md.ejs +12 -0
- package/examples/blog/assets/content/posts/snorkeling.md.ejs +12 -0
- package/examples/blog/assets/content/posts/waterfall.md.ejs +12 -0
- package/examples/blog/assets/content-collections.ts +30 -0
- 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 +52 -0
- package/examples/blog/assets/src/components/VacayAssistant.tsx +205 -0
- package/examples/blog/assets/src/components/blog-posts.tsx +78 -0
- package/examples/blog/assets/src/components/ui/card.tsx +92 -0
- package/examples/blog/assets/src/lib/blog-ai-hook.ts +25 -0
- package/examples/blog/assets/src/lib/blog-tools.ts +111 -0
- package/examples/blog/assets/src/lib/utils.ts +6 -0
- package/examples/blog/assets/src/routes/__root.tsx +57 -0
- package/examples/blog/assets/src/routes/api.blog-chat.ts +117 -0
- package/examples/blog/assets/src/routes/category.$category.tsx +19 -0
- package/examples/blog/assets/src/routes/index.tsx +19 -0
- package/examples/blog/assets/src/routes/posts.$slug.tsx +63 -0
- package/examples/blog/assets/src/styles.css +138 -0
- package/examples/blog/info.json +43 -0
- package/examples/blog/package.json +23 -0
- package/examples/events/README.md +110 -0
- package/examples/events/assets/content/speakers/andre-costa.md +22 -0
- package/examples/events/assets/content/speakers/hans-mueller.md.ejs +22 -0
- package/examples/events/assets/content/speakers/isabella-martinez.md.ejs +22 -0
- package/examples/events/assets/content/speakers/kenji-nakamura.md.ejs +22 -0
- package/examples/events/assets/content/speakers/marie-dubois.md.ejs +20 -0
- package/examples/events/assets/content/speakers/priya-sharma.md.ejs +22 -0
- package/examples/events/assets/content/talks/croissant-lamination-secrets.md +39 -0
- package/examples/events/assets/content/talks/french-macaron-mastery.md.ejs +39 -0
- package/examples/events/assets/content/talks/neapolitan-pizza-tradition-meets-innovation.md.ejs +39 -0
- package/examples/events/assets/content/talks/savory-breads-of-the-mediterranean.md.ejs +39 -0
- package/examples/events/assets/content/talks/sourdough-from-starter-to-masterpiece.md.ejs +36 -0
- package/examples/events/assets/content/talks/the-art-of-the-perfect-tart.md.ejs +32 -0
- package/examples/events/assets/content/talks/the-science-of-sugar.md.ejs +39 -0
- package/examples/events/assets/content/talks/umami-in-pastry-east-meets-west.md.ejs +39 -0
- package/examples/events/assets/content-collections.ts +56 -0
- 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 +1 -0
- package/examples/events/assets/src/components/Header.tsx +59 -0
- package/examples/events/assets/src/components/HeaderNav.tsx +67 -0
- package/examples/events/assets/src/components/HeroCarousel.tsx +61 -0
- package/examples/events/assets/src/components/RemyAssistant.tsx +207 -0
- package/examples/events/assets/src/components/SpeakerCard.tsx +67 -0
- package/examples/events/assets/src/components/TalkCard.tsx +77 -0
- package/examples/events/assets/src/components/ui/card.tsx +92 -0
- package/examples/events/assets/src/lib/conference-ai-hook.ts +26 -0
- package/examples/events/assets/src/lib/conference-tools.ts +210 -0
- package/examples/events/assets/src/lib/model-selection.ts +1 -0
- package/examples/events/assets/src/lib/utils.ts +6 -0
- package/examples/events/assets/src/routes/__root.tsx +70 -0
- package/examples/events/assets/src/routes/api.remy-chat.ts +119 -0
- package/examples/events/assets/src/routes/index.tsx +192 -0
- package/examples/events/assets/src/routes/schedule.index.tsx +274 -0
- package/examples/events/assets/src/routes/speakers.$slug.tsx +122 -0
- package/examples/events/assets/src/routes/speakers.index.tsx +40 -0
- package/examples/events/assets/src/routes/talks.$slug.tsx +116 -0
- package/examples/events/assets/src/routes/talks.index.tsx +40 -0
- package/examples/events/assets/src/styles.css +182 -0
- package/examples/events/info.json +74 -0
- package/examples/events/package.json +23 -0
- package/examples/marketing/README.md +60 -0
- 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 +36 -0
- package/examples/marketing/assets/src/components/MotorcycleAIAssistant.tsx +162 -0
- package/examples/marketing/assets/src/components/MotorcycleRecommendation.tsx +53 -0
- package/examples/marketing/assets/src/data/motorcycles.ts.ejs +77 -0
- package/examples/marketing/assets/src/lib/motorcycle-ai-hook.ts +24 -0
- package/examples/marketing/assets/src/lib/motorcycle-tools.ts +42 -0
- package/examples/marketing/assets/src/routes/__root.tsx +57 -0
- package/examples/marketing/assets/src/routes/api.motorcycle-chat.ts +78 -0
- package/examples/marketing/assets/src/routes/index.tsx +72 -0
- package/examples/marketing/assets/src/routes/motorcycles/$motorcycleId.tsx +56 -0
- package/examples/marketing/assets/src/store/motorcycle-assistant.ts +3 -0
- package/examples/marketing/assets/src/styles.css +212 -0
- package/examples/marketing/info.json +38 -0
- package/examples/marketing/package.json +14 -0
- package/examples/resume/README.md +82 -0
- package/examples/resume/assets/content/education/code-school.md +17 -0
- package/examples/resume/assets/content/jobs/freelance.md.ejs +13 -0
- package/examples/resume/assets/content/jobs/initech-junior.md +20 -0
- package/examples/resume/assets/content/jobs/initech-lead.md.ejs +29 -0
- package/examples/resume/assets/content/jobs/initrode-senior.md.ejs +28 -0
- package/examples/resume/assets/content-collections.ts +36 -0
- package/examples/resume/assets/public/headshot-on-white.jpg +0 -0
- package/examples/resume/assets/src/components/Header.tsx +33 -0
- package/examples/resume/assets/src/components/ResumeAssistant.tsx +193 -0
- package/examples/resume/assets/src/components/ui/badge.tsx +46 -0
- package/examples/resume/assets/src/components/ui/card.tsx +92 -0
- package/examples/resume/assets/src/components/ui/checkbox.tsx +30 -0
- package/examples/resume/assets/src/components/ui/hover-card.tsx +44 -0
- package/examples/resume/assets/src/components/ui/separator.tsx +26 -0
- package/examples/resume/assets/src/lib/resume-ai-hook.ts +21 -0
- package/examples/resume/assets/src/lib/resume-tools.ts +165 -0
- package/examples/resume/assets/src/lib/utils.ts +6 -0
- package/examples/resume/assets/src/routes/api.resume-chat.ts +110 -0
- package/examples/resume/assets/src/routes/index.tsx +220 -0
- package/examples/resume/assets/src/styles.css +138 -0
- package/examples/resume/info.json +25 -0
- package/examples/resume/package.json +26 -0
- package/package.json +39 -0
- package/project/base/_dot_claude/skills/content-collections/SKILL.md +505 -0
- package/project/base/_dot_claude/skills/netlify-blobs/SKILL.md +410 -0
- package/project/base/_dot_claude/skills/netlify-db/SKILL.md +424 -0
- package/project/base/_dot_claude/skills/netlify-debugging/SKILL.md +419 -0
- package/project/base/_dot_claude/skills/netlify-forms/SKILL.md +243 -0
- package/project/base/_dot_claude/skills/netlify-functions/SKILL.md +372 -0
- package/project/base/_dot_claude/skills/tanstack-start-api-routes/SKILL.md +421 -0
- package/project/base/_dot_claude/skills/tanstack-start-loaders/SKILL.md +426 -0
- package/project/base/_dot_claude/skills/tanstack-start-project-setup/SKILL.md +493 -0
- package/project/base/_dot_claude/skills/tanstack-start-routes/SKILL.md +430 -0
- package/project/base/_dot_claude/skills/tanstack-start-server-functions/SKILL.md +445 -0
- package/project/base/_dot_claude/skills/tanstack-start-typesafe-routing/SKILL.md +494 -0
- package/project/base/_dot_gitignore +8 -0
- package/project/base/netlify.toml +7 -0
- package/project/base/package.json +33 -0
- 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 +1 -0
- package/project/base/src/components/Header.tsx +17 -0
- package/project/base/src/components/HeaderNav.tsx.ejs +179 -0
- package/project/base/src/router.tsx +15 -0
- package/project/base/src/routes/__root.tsx +57 -0
- package/project/base/src/routes/index.tsx +48 -0
- package/project/base/src/styles.css +15 -0
- package/project/base/tsconfig.json +28 -0
- package/project/base/vite.config.ts.ejs +25 -0
- package/project/packages.json +22 -0
- package/scripts/check-outdated-packages.js +421 -0
- package/src/cli.ts +343 -0
- package/src/index.ts +49 -0
- package/src/types.ts +15 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from 'fs'
|
|
4
|
+
import { join, dirname, relative } from 'path'
|
|
5
|
+
import { fileURLToPath } from 'url'
|
|
6
|
+
import semver from 'semver'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = dirname(__filename)
|
|
10
|
+
|
|
11
|
+
// Parse command line arguments
|
|
12
|
+
const args = process.argv.slice(2)
|
|
13
|
+
const shouldUpdate = args.includes('--update')
|
|
14
|
+
|
|
15
|
+
// Colors for console output
|
|
16
|
+
const colors = {
|
|
17
|
+
red: '\x1b[31m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
yellow: '\x1b[33m',
|
|
20
|
+
blue: '\x1b[34m',
|
|
21
|
+
reset: '\x1b[0m',
|
|
22
|
+
bold: '\x1b[1m',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Recursively find all package.json files in a directory
|
|
27
|
+
*/
|
|
28
|
+
function findPackageJsonFiles(dir, files = []) {
|
|
29
|
+
const items = readdirSync(dir)
|
|
30
|
+
|
|
31
|
+
for (const item of items) {
|
|
32
|
+
const fullPath = join(dir, item)
|
|
33
|
+
const stat = statSync(fullPath)
|
|
34
|
+
|
|
35
|
+
if (stat.isDirectory() && item !== 'node_modules' && item !== 'dist') {
|
|
36
|
+
findPackageJsonFiles(fullPath, files)
|
|
37
|
+
} else if (item === 'package.json') {
|
|
38
|
+
files.push(fullPath)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return files
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Cache for package versions to avoid duplicate API calls
|
|
46
|
+
const versionCache = new Map()
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the latest version of a package from npm with caching and timeout
|
|
50
|
+
*/
|
|
51
|
+
async function getLatestVersion(packageName) {
|
|
52
|
+
// Check cache first
|
|
53
|
+
if (versionCache.has(packageName)) {
|
|
54
|
+
return versionCache.get(packageName)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
// Add timeout to prevent hanging
|
|
59
|
+
const controller = new AbortController()
|
|
60
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000) // 10 second timeout
|
|
61
|
+
|
|
62
|
+
const response = await fetch(
|
|
63
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
64
|
+
{
|
|
65
|
+
signal: controller.signal,
|
|
66
|
+
headers: {
|
|
67
|
+
'User-Agent': 'netlify-cta-outdated-checker',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
clearTimeout(timeoutId)
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
throw new Error(`HTTP ${response.status}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = await response.json()
|
|
79
|
+
const version = data.version
|
|
80
|
+
|
|
81
|
+
// Cache the result
|
|
82
|
+
versionCache.set(packageName, version)
|
|
83
|
+
return version
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error.name === 'AbortError') {
|
|
86
|
+
console.warn(
|
|
87
|
+
`${colors.yellow}Warning: Timeout fetching latest version for ${packageName}${colors.reset}`,
|
|
88
|
+
)
|
|
89
|
+
} else {
|
|
90
|
+
console.warn(
|
|
91
|
+
`${colors.yellow}Warning: Could not fetch latest version for ${packageName}: ${error.message}${colors.reset}`,
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Cache null result to avoid retrying
|
|
96
|
+
versionCache.set(packageName, null)
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if a version range satisfies the latest version
|
|
103
|
+
*/
|
|
104
|
+
function isVersionRangeOutdated(versionRange, latestVersion) {
|
|
105
|
+
try {
|
|
106
|
+
// Clean the version range (remove any npm-specific prefixes)
|
|
107
|
+
const cleanRange = versionRange.replace(/^npm:/, '').split('@').pop()
|
|
108
|
+
|
|
109
|
+
// Check if the range satisfies the latest version
|
|
110
|
+
return !semver.satisfies(latestVersion, cleanRange)
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.warn(
|
|
113
|
+
`${colors.yellow}Warning: Could not parse version range "${versionRange}": ${error.message}${colors.reset}`,
|
|
114
|
+
)
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Update package.json file with new versions
|
|
121
|
+
*/
|
|
122
|
+
function updatePackageJson(filePath, outdatedPackages) {
|
|
123
|
+
try {
|
|
124
|
+
const content = readFileSync(filePath, 'utf8')
|
|
125
|
+
const packageJson = JSON.parse(content)
|
|
126
|
+
|
|
127
|
+
let updated = false
|
|
128
|
+
|
|
129
|
+
for (const pkg of outdatedPackages) {
|
|
130
|
+
// Check and update in dependencies
|
|
131
|
+
if (packageJson.dependencies && packageJson.dependencies[pkg.name]) {
|
|
132
|
+
const currentRange = packageJson.dependencies[pkg.name]
|
|
133
|
+
const newRange = updateVersionRange(currentRange, pkg.latest)
|
|
134
|
+
packageJson.dependencies[pkg.name] = newRange
|
|
135
|
+
updated = true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check and update in devDependencies
|
|
139
|
+
if (
|
|
140
|
+
packageJson.devDependencies &&
|
|
141
|
+
packageJson.devDependencies[pkg.name]
|
|
142
|
+
) {
|
|
143
|
+
const currentRange = packageJson.devDependencies[pkg.name]
|
|
144
|
+
const newRange = updateVersionRange(currentRange, pkg.latest)
|
|
145
|
+
packageJson.devDependencies[pkg.name] = newRange
|
|
146
|
+
updated = true
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check and update in peerDependencies
|
|
150
|
+
if (
|
|
151
|
+
packageJson.peerDependencies &&
|
|
152
|
+
packageJson.peerDependencies[pkg.name]
|
|
153
|
+
) {
|
|
154
|
+
const currentRange = packageJson.peerDependencies[pkg.name]
|
|
155
|
+
const newRange = updateVersionRange(currentRange, pkg.latest)
|
|
156
|
+
packageJson.peerDependencies[pkg.name] = newRange
|
|
157
|
+
updated = true
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (updated) {
|
|
162
|
+
// Write back with proper formatting
|
|
163
|
+
writeFileSync(filePath, JSON.stringify(packageJson, null, 2) + '\n')
|
|
164
|
+
return true
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return false
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error(
|
|
170
|
+
`${colors.red}Error updating ${filePath}: ${error.message}${colors.reset}`,
|
|
171
|
+
)
|
|
172
|
+
return false
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Update version range while preserving the range type (^, ~, etc.)
|
|
178
|
+
*/
|
|
179
|
+
function updateVersionRange(currentRange, latestVersion) {
|
|
180
|
+
// Preserve the prefix (^, ~, >=, etc.)
|
|
181
|
+
if (currentRange.startsWith('^')) {
|
|
182
|
+
return `^${latestVersion}`
|
|
183
|
+
} else if (currentRange.startsWith('~')) {
|
|
184
|
+
return `~${latestVersion}`
|
|
185
|
+
} else if (currentRange.startsWith('>=')) {
|
|
186
|
+
return `>=${latestVersion}`
|
|
187
|
+
} else if (currentRange.startsWith('>')) {
|
|
188
|
+
return `>${latestVersion}`
|
|
189
|
+
} else if (currentRange.startsWith('<=')) {
|
|
190
|
+
return `<=${latestVersion}`
|
|
191
|
+
} else if (currentRange.startsWith('<')) {
|
|
192
|
+
return `<${latestVersion}`
|
|
193
|
+
} else {
|
|
194
|
+
// Exact version or other format, just use the latest version
|
|
195
|
+
return latestVersion
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get relative path for display
|
|
201
|
+
*/
|
|
202
|
+
function getDisplayPath(filePath, rootDir) {
|
|
203
|
+
const relativePath = relative(rootDir, filePath)
|
|
204
|
+
// Remove the package.json part
|
|
205
|
+
return relativePath.replace('/package.json', '')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Check dependencies in a package.json file
|
|
210
|
+
*/
|
|
211
|
+
async function checkPackageJson(filePath) {
|
|
212
|
+
try {
|
|
213
|
+
const content = readFileSync(filePath, 'utf8')
|
|
214
|
+
const packageJson = JSON.parse(content)
|
|
215
|
+
|
|
216
|
+
const outdatedPackages = []
|
|
217
|
+
const allDependencies = {
|
|
218
|
+
...packageJson.dependencies,
|
|
219
|
+
...packageJson.devDependencies,
|
|
220
|
+
...packageJson.peerDependencies,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (Object.keys(allDependencies).length === 0) {
|
|
224
|
+
return { filePath, outdatedPackages: [], totalDependencies: 0 }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const packageNames = Object.keys(allDependencies).filter((name) => {
|
|
228
|
+
const versionRange = allDependencies[name]
|
|
229
|
+
// Skip workspace dependencies and local packages
|
|
230
|
+
return !(
|
|
231
|
+
versionRange.startsWith('workspace:') ||
|
|
232
|
+
versionRange.startsWith('file:') ||
|
|
233
|
+
versionRange.startsWith('link:')
|
|
234
|
+
)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// Process packages in batches to avoid overwhelming the API
|
|
238
|
+
const batchSize = 5
|
|
239
|
+
for (let i = 0; i < packageNames.length; i += batchSize) {
|
|
240
|
+
const batch = packageNames.slice(i, i + batchSize)
|
|
241
|
+
|
|
242
|
+
// Process batch in parallel
|
|
243
|
+
const promises = batch.map(async (packageName) => {
|
|
244
|
+
const versionRange = allDependencies[packageName]
|
|
245
|
+
const latestVersion = await getLatestVersion(packageName)
|
|
246
|
+
|
|
247
|
+
if (!latestVersion) {
|
|
248
|
+
return null
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isVersionRangeOutdated(versionRange, latestVersion)) {
|
|
252
|
+
return {
|
|
253
|
+
name: packageName,
|
|
254
|
+
current: versionRange,
|
|
255
|
+
latest: latestVersion,
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return null
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const results = await Promise.all(promises)
|
|
263
|
+
outdatedPackages.push(...results.filter(Boolean))
|
|
264
|
+
|
|
265
|
+
// Small delay between batches
|
|
266
|
+
if (i + batchSize < packageNames.length) {
|
|
267
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
filePath,
|
|
273
|
+
outdatedPackages,
|
|
274
|
+
totalDependencies: Object.keys(allDependencies).length,
|
|
275
|
+
}
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error(
|
|
278
|
+
`${colors.red}Error processing ${filePath}: ${error.message}${colors.reset}`,
|
|
279
|
+
)
|
|
280
|
+
return {
|
|
281
|
+
filePath,
|
|
282
|
+
outdatedPackages: [],
|
|
283
|
+
totalDependencies: 0,
|
|
284
|
+
error: error.message,
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Main function
|
|
291
|
+
*/
|
|
292
|
+
async function main() {
|
|
293
|
+
const actionText = shouldUpdate ? 'Updating' : 'Checking for'
|
|
294
|
+
console.log(
|
|
295
|
+
`${colors.bold}${colors.blue}${actionText} outdated packages in netlify-cta...${colors.reset}\n`,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
const rootDir = join(__dirname, '..')
|
|
299
|
+
|
|
300
|
+
// Directories to scan for package.json files
|
|
301
|
+
const dirsToScan = [
|
|
302
|
+
join(rootDir, 'add-ons'),
|
|
303
|
+
join(rootDir, 'examples'),
|
|
304
|
+
join(rootDir, 'project'),
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
// Collect all package.json files from all directories
|
|
308
|
+
const packageJsonFiles = []
|
|
309
|
+
for (const dir of dirsToScan) {
|
|
310
|
+
try {
|
|
311
|
+
const files = findPackageJsonFiles(dir)
|
|
312
|
+
packageJsonFiles.push(...files)
|
|
313
|
+
console.log(
|
|
314
|
+
`${colors.blue}Found ${files.length} package.json files in ${relative(rootDir, dir)}${colors.reset}`,
|
|
315
|
+
)
|
|
316
|
+
} catch (error) {
|
|
317
|
+
console.warn(
|
|
318
|
+
`${colors.yellow}Warning: Could not scan ${dir}: ${error.message}${colors.reset}`,
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
console.log(`\nTotal: ${packageJsonFiles.length} package.json files\n`)
|
|
324
|
+
|
|
325
|
+
let totalOutdated = 0
|
|
326
|
+
let totalChecked = 0
|
|
327
|
+
let totalUpdated = 0
|
|
328
|
+
const results = []
|
|
329
|
+
|
|
330
|
+
for (let i = 0; i < packageJsonFiles.length; i++) {
|
|
331
|
+
const filePath = packageJsonFiles[i]
|
|
332
|
+
const displayPath = getDisplayPath(filePath, rootDir)
|
|
333
|
+
|
|
334
|
+
// Show progress
|
|
335
|
+
process.stdout.write(
|
|
336
|
+
`\r${colors.blue}Processing ${i + 1}/${packageJsonFiles.length}: ${displayPath}...${colors.reset}`,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
const result = await checkPackageJson(filePath)
|
|
340
|
+
results.push(result)
|
|
341
|
+
|
|
342
|
+
if (result.error) {
|
|
343
|
+
console.log(`\n${colors.red}Error: ${result.error}${colors.reset}`)
|
|
344
|
+
continue
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
totalChecked += result.totalDependencies
|
|
348
|
+
totalOutdated += result.outdatedPackages.length
|
|
349
|
+
|
|
350
|
+
if (result.outdatedPackages.length > 0) {
|
|
351
|
+
console.log(
|
|
352
|
+
`\n${colors.red}${colors.bold}Found ${result.outdatedPackages.length} outdated packages in ${displayPath}:${colors.reset}`,
|
|
353
|
+
)
|
|
354
|
+
for (const pkg of result.outdatedPackages) {
|
|
355
|
+
console.log(
|
|
356
|
+
` ${colors.red}${pkg.name}${colors.reset}: ${pkg.current} → ${colors.green}${pkg.latest}${colors.reset}`,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (shouldUpdate) {
|
|
361
|
+
const updated = updatePackageJson(filePath, result.outdatedPackages)
|
|
362
|
+
if (updated) {
|
|
363
|
+
totalUpdated++
|
|
364
|
+
console.log(
|
|
365
|
+
` ${colors.green}✓ Updated ${displayPath}${colors.reset}`,
|
|
366
|
+
)
|
|
367
|
+
} else {
|
|
368
|
+
console.log(
|
|
369
|
+
` ${colors.yellow}⚠ Failed to update ${displayPath}${colors.reset}`,
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
console.log()
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Clear progress line and add spacing
|
|
379
|
+
console.log('\n')
|
|
380
|
+
|
|
381
|
+
// Summary
|
|
382
|
+
console.log(`${colors.bold}${colors.blue}Summary:${colors.reset}`)
|
|
383
|
+
console.log(`Total dependencies checked: ${totalChecked}`)
|
|
384
|
+
console.log(
|
|
385
|
+
`Total outdated packages: ${colors.red}${totalOutdated}${colors.reset}`,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if (shouldUpdate) {
|
|
389
|
+
console.log(
|
|
390
|
+
`Total files updated: ${colors.green}${totalUpdated}${colors.reset}`,
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (totalOutdated === 0) {
|
|
395
|
+
console.log(
|
|
396
|
+
`${colors.green}${colors.bold}🎉 All packages are up to date!${colors.reset}`,
|
|
397
|
+
)
|
|
398
|
+
} else if (shouldUpdate && totalUpdated > 0) {
|
|
399
|
+
console.log(
|
|
400
|
+
`${colors.green}${colors.bold}✅ Successfully updated ${totalUpdated} files with outdated packages!${colors.reset}`,
|
|
401
|
+
)
|
|
402
|
+
} else if (shouldUpdate && totalUpdated === 0) {
|
|
403
|
+
console.log(
|
|
404
|
+
`${colors.yellow}${colors.bold}⚠️ No files were updated. Check for errors above.${colors.reset}`,
|
|
405
|
+
)
|
|
406
|
+
} else {
|
|
407
|
+
console.log(
|
|
408
|
+
`${colors.yellow}${colors.bold}⚠️ Found outdated packages that need attention. Use --update to update them.${colors.reset}`,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Exit with error code if outdated packages found and not updating
|
|
413
|
+
process.exit(totalOutdated > 0 && !shouldUpdate ? 1 : 0)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
main().catch((error) => {
|
|
417
|
+
console.error(
|
|
418
|
+
`${colors.red}${colors.bold}Fatal error: ${error.message}${colors.reset}`,
|
|
419
|
+
)
|
|
420
|
+
process.exit(1)
|
|
421
|
+
})
|