-
Notifications
You must be signed in to change notification settings - Fork 300
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from beeman/beeman/sync-package-json
feat: add script to sync versions in package.json files
- Loading branch information
Showing
12 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"name": "program-examples", | ||
"version": "1.0.0", | ||
"description": "### :crab: Rust. :snake: Python. :ice_cube: Solidity. :link: All on-chain.", | ||
"scripts": { | ||
"sync-package-json": "ts-node scripts/sync-package-json.ts" | ||
}, | ||
"keywords": [], | ||
"author": "Solana Foundation", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@types/node": "^20.9.0", | ||
"picocolors": "^1.0.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^5.2.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { readFileSync } from 'node:fs' | ||
|
||
export function changePackageVersion(file: string, pkgName: string, pkgVersion: string): [boolean, string] { | ||
const content = JSON.parse(readFileSync(file).toString('utf-8')) | ||
if (content.dependencies && content.dependencies[pkgName] && content.dependencies[pkgName] !== pkgVersion) { | ||
content.dependencies[pkgName] = pkgVersion | ||
return [true, content] | ||
} | ||
if (content.devDependencies && content.devDependencies[pkgName] && content.devDependencies[pkgName] !== pkgVersion) { | ||
content.devDependencies[pkgName] = pkgVersion | ||
return [true, content] | ||
} | ||
return [false, content] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { basename } from 'node:path' | ||
import * as p from 'picocolors' | ||
import { getDepsCount } from './get-deps-count' | ||
import { getRecursiveFileList } from './get-recursive-file-list' | ||
|
||
export function commandCheck(path: string = '.') { | ||
const files = getRecursiveFileList(path).filter((file) => basename(file) === 'package.json') | ||
const depsCounter = getDepsCount(files) | ||
|
||
const single: string[] = [] | ||
const multiple: string[] = [] | ||
|
||
Object.keys(depsCounter) | ||
.sort() | ||
.map((pkg) => { | ||
const versions = depsCounter[pkg] | ||
const versionMap = Object.keys(versions).sort() | ||
const versionsLength = versionMap.length | ||
|
||
if (versionsLength === 1) { | ||
const count = versions[versionMap[0]].length | ||
single.push(`${p.green(`✔`)} ${pkg}@${versionMap[0]} (${count})`) | ||
return | ||
} | ||
|
||
const versionCount: { version: string; count: number }[] = [] | ||
for (const version of versionMap) { | ||
versionCount.push({ version, count: versions[version].length }) | ||
} | ||
versionCount.sort((a, b) => b.count - a.count) | ||
|
||
multiple.push(`${p.yellow(`⚠`)} ${pkg} has ${versionsLength} versions:`) | ||
|
||
for (const { count, version } of versionCount) { | ||
multiple.push(` - ${p.bold(version)} (${count})`) | ||
} | ||
}) | ||
|
||
for (const string of [...single.sort(), ...multiple]) { | ||
console.log(string) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
export function commandHelp() { | ||
console.log(`Usage: yarn sync-package-json <command> [options]`) | ||
console.log(``) | ||
console.log(`Commands:`) | ||
console.log(` check <path> Check package.json files`) | ||
console.log(` help Show this help`) | ||
console.log(` list <path> List package.json files`) | ||
console.log(` set [ver] <path> Set specific version in package.json files`) | ||
console.log(` update <path> <pkgs> Update all versions in package.json files`) | ||
console.log(``) | ||
console.log(`Arguments:`) | ||
console.log(` path Path to directory`) | ||
console.log(``) | ||
console.log(`Examples:`) | ||
console.log(` yarn sync-package-json check`) | ||
console.log(` yarn sync-package-json check basics`) | ||
console.log(` yarn sync-package-json list`) | ||
console.log(` yarn sync-package-json list basics`) | ||
console.log(` yarn sync-package-json help`) | ||
console.log(` yarn sync-package-json set @coral-xyz/[email protected]`) | ||
console.log(` yarn sync-package-json set @coral-xyz/[email protected] basics`) | ||
console.log(` yarn sync-package-json update`) | ||
console.log(` yarn sync-package-json update basics`) | ||
console.log(` yarn sync-package-json update . @solana/web3.js @solana/spl-token`) | ||
process.exit(0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { basename } from 'node:path' | ||
import { getRecursiveFileList } from './get-recursive-file-list' | ||
|
||
export function commandList(path: string) { | ||
const files = getRecursiveFileList(path).filter((file) => basename(file) === 'package.json') | ||
for (const file of files) { | ||
console.log(file) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { writeFileSync } from 'fs' | ||
import { basename } from 'node:path' | ||
import { changePackageVersion } from './change-package-version' | ||
import { getRecursiveFileList } from './get-recursive-file-list' | ||
|
||
export function commandSet(version: string, path: string = '.') { | ||
if (!version) { | ||
console.error(`Version is required`) | ||
process.exit(1) | ||
} | ||
if ( | ||
!version | ||
// Strip first character if it's a `@` | ||
.replace(/^@/, '') | ||
.includes('@') | ||
) { | ||
console.error(`Invalid package version: ${version}. Provide package with version, e.g. @solana/[email protected]`) | ||
process.exit(1) | ||
} | ||
// Take anything after the second `@` as the version, the rest is the package name | ||
const [pkg, ...rest] = version.split('@').reverse() | ||
const pkgName = rest.reverse().join('@') | ||
|
||
// Make sure pkgVersions has a ^ prefix, if not add it | ||
const pkgVersion = pkg.startsWith('^') ? pkg : `^${pkg}` | ||
|
||
console.log(`Setting package ${pkgName} to ${pkgVersion} in ${path}`) | ||
|
||
const files = getRecursiveFileList(path).filter((file) => basename(file) === 'package.json') | ||
let count = 0 | ||
for (const file of files) { | ||
const [changed, content] = changePackageVersion(file, pkgName, pkgVersion) | ||
if (changed) { | ||
writeFileSync(file, JSON.stringify(content, null, 2) + '\n') | ||
count++ | ||
} | ||
} | ||
if (count === 0) { | ||
console.log(`No files updated`) | ||
} else { | ||
console.log(`Updated ${count} files`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { execSync } from 'child_process' | ||
import { writeFileSync } from 'fs' | ||
import { basename } from 'node:path' | ||
import * as p from 'picocolors' | ||
import { changePackageVersion } from './change-package-version' | ||
|
||
import { getDepsCount } from './get-deps-count' | ||
import { getRecursiveFileList } from './get-recursive-file-list' | ||
|
||
export function commandUpdate(path: string = '.', packageNames: string[] = []) { | ||
const files = getRecursiveFileList(path).filter((file) => basename(file) === 'package.json') | ||
const depsCounter = getDepsCount(files) | ||
const pkgNames = Object.keys(depsCounter).sort() | ||
if (packageNames.length > 0) { | ||
console.log(`Updating ${packageNames.join(', ')} in ${files.length} files`) | ||
} | ||
|
||
let total = 0 | ||
for (const pkgName of pkgNames.filter((pkgName) => packageNames.length === 0 || packageNames.includes(pkgName))) { | ||
// Get latest version from npm | ||
const npmVersion = execSync(`npm view ${pkgName} version`).toString().trim() | ||
|
||
let count = 0 | ||
for (const file of files) { | ||
const [changed, content] = changePackageVersion(file, pkgName, `^${npmVersion}`) | ||
if (changed) { | ||
writeFileSync(file, JSON.stringify(content, null, 2) + '\n') | ||
count++ | ||
} | ||
} | ||
total += count | ||
|
||
if (count === 0) { | ||
console.log(p.dim(`Package ${pkgName} is up to date ${npmVersion}`)) | ||
continue | ||
} | ||
console.log(p.green(` -> Updated ${count} files with ${pkgName} ${npmVersion}`)) | ||
} | ||
|
||
if (total === 0) { | ||
console.log(`No files updated`) | ||
} else { | ||
console.log(`Updated ${total} files`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { readFileSync } from 'node:fs' | ||
|
||
export function getDepsCount(files: string[] = []): Record<string, Record<string, string[]>> { | ||
const map: Record<string, JSON> = {} | ||
const depsCounter: Record<string, Record<string, string[]>> = {} | ||
|
||
for (const file of files) { | ||
const content = JSON.parse(readFileSync(file).toString('utf-8')) | ||
map[file] = content | ||
|
||
const deps = content.dependencies ?? {} | ||
const devDeps = content.devDependencies ?? {} | ||
|
||
const merged = { ...deps, ...devDeps } | ||
|
||
Object.keys(merged) | ||
.sort() | ||
.map((pkg) => { | ||
const pkgVersion = merged[pkg] | ||
if (!depsCounter[pkg]) { | ||
depsCounter[pkg] = { [pkgVersion]: [file] } | ||
return | ||
} | ||
if (!depsCounter[pkg][pkgVersion]) { | ||
depsCounter[pkg][pkgVersion] = [file] | ||
return | ||
} | ||
depsCounter[pkg][pkgVersion] = [...depsCounter[pkg][pkgVersion], file] | ||
}) | ||
} | ||
return depsCounter | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Point method at path and return a list of all the files in the directory recursively | ||
import { readdirSync, statSync } from 'node:fs' | ||
|
||
export function getRecursiveFileList(path: string): string[] { | ||
const ignore = ['.git', '.github', '.idea', '.next', '.vercel', '.vscode', 'coverage', 'dist', 'node_modules'] | ||
const files: string[] = [] | ||
|
||
const items = readdirSync(path) | ||
items.forEach((item) => { | ||
if (ignore.includes(item)) { | ||
return | ||
} | ||
// Check out if it's a directory or a file | ||
const isDir = statSync(`${path}/${item}`).isDirectory() | ||
if (isDir) { | ||
// If it's a directory, recursively call the method | ||
files.push(...getRecursiveFileList(`${path}/${item}`)) | ||
} else { | ||
// If it's a file, add it to the array of files | ||
files.push(`${path}/${item}`) | ||
} | ||
}) | ||
|
||
return files.filter((file) => { | ||
// Remove package.json from the root directory | ||
return path === '.' ? file !== './package.json' : true | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export * from './command-check' | ||
export * from './command-help' | ||
export * from './command-list' | ||
export * from './command-set' | ||
export * from './command-update' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { commandCheck, commandHelp, commandList, commandSet, commandUpdate } from './lib' | ||
|
||
const params: string[] = process.argv.slice(3) | ||
|
||
switch (process.argv[2]) { | ||
case 'check': | ||
commandCheck(params[0]) | ||
break | ||
case 'list': | ||
commandList(params[0]) | ||
break | ||
case 'set': | ||
commandSet(params[0], params[1]) | ||
break | ||
case 'update': | ||
commandUpdate(params[0], params.slice(1)) | ||
break | ||
case 'help': | ||
default: | ||
commandHelp() | ||
break | ||
} |
Oops, something went wrong.