Skip to content

Commit 90ed573

Browse files
author
Michael Kaiser
committed
Cleaup and build
1 parent 2e5e83c commit 90ed573

File tree

11 files changed

+1435
-0
lines changed

11 files changed

+1435
-0
lines changed

scripts/build-all-typescript.js

Lines changed: 461 additions & 0 deletions
Large diffs are not rendered by default.

scripts/update-cdk-packages.ts

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/usr/bin/env tsx
2+
3+
import { readFile, writeFile } from 'node:fs/promises';
4+
import { opendir } from 'node:fs/promises';
5+
import { resolve, join } from 'node:path';
6+
import { setTimeout as sleep } from 'node:timers/promises';
7+
8+
interface PackageJson {
9+
dependencies?: Record<string, string>;
10+
devDependencies?: Record<string, string>;
11+
[key: string]: unknown;
12+
}
13+
14+
interface CdkVersion {
15+
version: string;
16+
}
17+
18+
type DependencyUpdates = {
19+
dependencies: Record<string, string>;
20+
devDependencies: Record<string, string>;
21+
};
22+
23+
class PackageUpdateError extends Error {
24+
override cause?: Error;
25+
constructor(message: string, options?: { cause: Error }) {
26+
super(message);
27+
this.name = 'PackageUpdateError';
28+
this.cause = options?.cause;
29+
}
30+
}
31+
32+
// Constants for URLs
33+
const URLS = {
34+
templatePackage: 'https://raw.githubusercontent.com/aws/aws-cdk-cli/refs/heads/main/packages/aws-cdk/lib/init-templates/app/typescript/package.json',
35+
cdkVersion: 'https://raw.githubusercontent.com/aws/aws-cdk/main/version.v2.json',
36+
constructsPackage: 'https://raw.githubusercontent.com/aws/aws-cdk-cli/refs/heads/main/packages/aws-cdk/package.json'
37+
} as const;
38+
39+
// Utility function to get CDK CLI version
40+
async function getCdkCliVersion(): Promise<string> {
41+
try {
42+
const { exec } = require('child_process');
43+
return new Promise((resolve, reject) => {
44+
exec('npx cdk --version', (error: Error | null, stdout: string) => {
45+
if (error) {
46+
reject(new PackageUpdateError('Failed to get CDK version', { cause: error }));
47+
return;
48+
}
49+
// Extract just the version number from the output (e.g., "2.1004.0" from "2.1004.0 (build f0ad96e)")
50+
const version = stdout.trim().split(' ')[0];
51+
resolve(version);
52+
});
53+
});
54+
} catch (error) {
55+
throw new PackageUpdateError(
56+
'Failed to get CDK CLI version',
57+
{ cause: error instanceof Error ? error : new Error(String(error)) }
58+
);
59+
}
60+
}
61+
62+
// Utility function to fetch and parse JSON with retries
63+
async function fetchJson<T>(url: string, retries = 3): Promise<T> {
64+
for (let attempt = 1; attempt <= retries; attempt++) {
65+
try {
66+
const controller = new AbortController();
67+
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
68+
69+
const response = await fetch(url, {
70+
signal: controller.signal,
71+
headers: {
72+
'Accept': 'application/json',
73+
'User-Agent': 'CDK-Package-Updater'
74+
}
75+
});
76+
clearTimeout(timeoutId);
77+
78+
if (!response.ok) {
79+
throw new Error(`HTTP error! status: ${response.status}`);
80+
}
81+
82+
return await response.json() as T;
83+
} catch (error) {
84+
if (attempt === retries) {
85+
throw new PackageUpdateError(
86+
`Failed to fetch from ${url} after ${retries} attempts`,
87+
{ cause: error instanceof Error ? error : new Error(String(error)) }
88+
);
89+
}
90+
console.warn(`Attempt ${attempt} failed, retrying after ${attempt * 1000}ms...`);
91+
await sleep(attempt * 1000); // Exponential backoff
92+
}
93+
}
94+
throw new PackageUpdateError('Unreachable code path');
95+
}
96+
97+
// Utility function to find all package.json files using async iterator
98+
async function findPackageJsonFiles(startPath: string): Promise<string[]> {
99+
const results: string[] = [];
100+
101+
async function* walk(dir: string): AsyncGenerator<string> {
102+
try {
103+
for await (const entry of await opendir(dir)) {
104+
const fullPath = join(dir, entry.name);
105+
if (entry.isDirectory() && entry.name !== 'node_modules') {
106+
yield* walk(fullPath);
107+
} else if (entry.isFile() && entry.name === 'package.json') {
108+
yield fullPath;
109+
}
110+
}
111+
} catch (error) {
112+
throw new PackageUpdateError(
113+
`Error walking directory ${dir}`,
114+
{ cause: error instanceof Error ? error : new Error(String(error)) }
115+
);
116+
}
117+
}
118+
119+
for await (const file of walk(startPath)) {
120+
results.push(file);
121+
}
122+
123+
return results;
124+
}
125+
126+
// Function to update a single package.json file
127+
async function updatePackageJson(
128+
filePath: string,
129+
updates: DependencyUpdates
130+
): Promise<void> {
131+
try {
132+
const fileContent = await readFile(filePath, { encoding: 'utf8' });
133+
const content = JSON.parse(fileContent) as PackageJson;
134+
let updated = false;
135+
136+
// Helper function to update dependencies
137+
const updateDeps = (
138+
depType: 'dependencies' | 'devDependencies',
139+
updates: Record<string, string>
140+
) => {
141+
if (!content[depType]) return;
142+
143+
for (const [pkg, version] of Object.entries(updates)) {
144+
if (content[depType]![pkg] && content[depType]![pkg] !== version) {
145+
content[depType]![pkg] = version;
146+
console.log(`Updated ${pkg} to ${version} in ${filePath}`);
147+
updated = true;
148+
}
149+
}
150+
};
151+
152+
// Update both types of dependencies
153+
updateDeps('dependencies', updates.dependencies);
154+
updateDeps('devDependencies', updates.devDependencies);
155+
156+
if (updated) {
157+
await writeFile(filePath, JSON.stringify(content, null, 2) + '\n');
158+
}
159+
} catch (error) {
160+
throw new PackageUpdateError(
161+
`Error updating ${filePath}`,
162+
{ cause: error instanceof Error ? error : new Error(String(error)) }
163+
);
164+
}
165+
}
166+
167+
// Main function
168+
async function updatePackages(): Promise<void> {
169+
console.log('Fetching latest versions from AWS CDK repositories...');
170+
171+
try {
172+
// Fetch all required data in parallel with AbortController
173+
const controller = new AbortController();
174+
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15 second timeout
175+
176+
const [templatePackage, cdkVersion, constructsPackage] = await Promise.all([
177+
fetchJson<PackageJson>(URLS.templatePackage),
178+
fetchJson<CdkVersion>(URLS.cdkVersion),
179+
fetchJson<PackageJson>(URLS.constructsPackage)
180+
]);
181+
182+
clearTimeout(timeoutId);
183+
184+
// Get the CDK version by running the CLI command
185+
const cdkCliVersion = await getCdkCliVersion();
186+
console.log(`Detected CDK CLI version: ${cdkCliVersion}`);
187+
188+
// Define the dependencies to update
189+
const updates: DependencyUpdates = {
190+
devDependencies: {
191+
'@types/jest': templatePackage.devDependencies?.['@types/jest'] ?? '',
192+
'@types/node': templatePackage.devDependencies?.['@types/node'] ?? '',
193+
'jest': templatePackage.devDependencies?.jest ?? '',
194+
'ts-jest': templatePackage.devDependencies?.['ts-jest'] ?? '',
195+
'ts-node': templatePackage.devDependencies?.['ts-node'] ?? '',
196+
'typescript': templatePackage.devDependencies?.typescript ?? '',
197+
'aws-cdk': cdkCliVersion
198+
},
199+
dependencies: {
200+
'aws-cdk-lib': cdkVersion.version,
201+
'constructs': constructsPackage.devDependencies?.constructs ?? ''
202+
}
203+
};
204+
205+
// Validate that we got all the versions we need
206+
const missingVersions = [
207+
...Object.entries(updates.dependencies),
208+
...Object.entries(updates.devDependencies)
209+
].filter(([, version]) => !version);
210+
211+
if (missingVersions.length > 0) {
212+
throw new PackageUpdateError(
213+
`Missing versions for: ${missingVersions.map(([pkg]) => pkg).join(', ')}`
214+
);
215+
}
216+
217+
// Find all package.json files
218+
const typescriptDir = resolve(__dirname, '../typescript');
219+
console.log(`Searching for package.json files in ${typescriptDir}...`);
220+
const packageFiles = await findPackageJsonFiles(typescriptDir);
221+
222+
console.log(`Found ${packageFiles.length} package.json files to update.`);
223+
224+
// Update all package.json files in parallel with concurrency limit
225+
const concurrencyLimit = 5;
226+
for (let i = 0; i < packageFiles.length; i += concurrencyLimit) {
227+
const batch = packageFiles.slice(i, i + concurrencyLimit);
228+
await Promise.all(
229+
batch.map(filePath => updatePackageJson(filePath, updates))
230+
);
231+
}
232+
233+
console.log('Package updates completed successfully! 🎉');
234+
} catch (error) {
235+
if (error instanceof PackageUpdateError) {
236+
console.error('Error updating packages:', error.message);
237+
if (error.cause) {
238+
console.error('Caused by:', error.cause);
239+
}
240+
} else {
241+
console.error('Unexpected error:', error);
242+
}
243+
process.exit(1);
244+
}
245+
}
246+
247+
// Run the update
248+
updatePackages();
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"ecmaVersion": 2022,
5+
"sourceType": "module",
6+
"project": "./tsconfig.json"
7+
},
8+
"plugins": ["@typescript-eslint"],
9+
"extends": [
10+
"eslint:recommended",
11+
"plugin:@typescript-eslint/recommended"
12+
],
13+
"rules": {
14+
"@typescript-eslint/explicit-function-return-type": "warn",
15+
"@typescript-eslint/no-explicit-any": "warn",
16+
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
17+
},
18+
"env": {
19+
"node": true,
20+
"es2022": true
21+
}
22+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Dependency directories
2+
node_modules/
3+
4+
# Build output
5+
dist/
6+
build/
7+
8+
# Coverage directory
9+
coverage/
10+
11+
# Logs
12+
logs
13+
*.log
14+
npm-debug.log*
15+
yarn-debug.log*
16+
yarn-error.log*
17+
18+
# Environment variables
19+
.env
20+
.env.local
21+
.env.development.local
22+
.env.test.local
23+
.env.production.local
24+
25+
# IDE files
26+
.idea/
27+
.vscode/
28+
*.swp
29+
*.swo
30+
31+
# OS files
32+
.DS_Store
33+
Thumbs.db
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# update-packagejson
2+
3+
A utility for updating package.json files in AWS CDK projects to keep dependencies in sync with the latest versions.
4+
5+
## Prerequisites
6+
7+
- Node.js (LTS version, 18.x or later)
8+
- npm or yarn
9+
10+
## Installation
11+
12+
```bash
13+
npm install
14+
```
15+
16+
## Usage
17+
18+
1. Build the project:
19+
20+
```bash
21+
npm run build
22+
```
23+
24+
2. Run the utility with the repository root path:
25+
26+
```bash
27+
node dist/index.js /path/to/repository
28+
```
29+
30+
For example:
31+
32+
```bash
33+
node dist/index.js ../my-cdk-project
34+
```
35+
36+
The utility will:
37+
- Fetch the latest versions of AWS CDK dependencies from official sources
38+
- Find all package.json files in the specified directory
39+
- Update the dependencies to match the latest versions
40+
- Only update files that need changes
41+
42+
## Development
43+
44+
- `npm run build` - Transpile TypeScript to JavaScript
45+
- `npm run clean` - Remove build artifacts
46+
- `npm run lint` - Run ESLint
47+
- `npm test` - Run tests
48+
49+
## License
50+
51+
ISC
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
collectCoverage: true,
6+
coverageDirectory: 'coverage',
7+
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
8+
testMatch: ['**/*.test.ts'],
9+
verbose: true
10+
};

0 commit comments

Comments
 (0)