@jayrdeaton/scripts 1.1.1 → 1.1.2

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/README.md CHANGED
@@ -69,6 +69,22 @@ Example: `jrd check-domains ??fu.com`
69
69
 
70
70
  ---
71
71
 
72
+ ### `jrd check-scripts`
73
+
74
+ Compare `package.json` scripts across projects for consistency. Highlights scripts whose values differ from the most common value (or a reference project).
75
+
76
+ ```
77
+ jrd check-scripts [scripts...] [options]
78
+
79
+ Options:
80
+ -d, --dir <dir> Root directory to scan (default: ~/Developer)
81
+ -r, --ref <ref> Reference project name to compare against
82
+ -a, --all Show all scripts, including matching ones
83
+ -f, --flat Show one line per project instead of grouping by value
84
+ ```
85
+
86
+ ---
87
+
72
88
  ### `jrd clean-builds`
73
89
 
74
90
  Delete build artifacts (`build`, `dist`, `ios`, `android`) across one or more repos. Dry run by default.
@@ -118,6 +134,21 @@ Options:
118
134
 
119
135
  ---
120
136
 
137
+ ### `jrd find-dep`
138
+
139
+ Find projects in a directory that use any of the given dependencies (searches `dependencies`, `devDependencies`, and `peerDependencies`).
140
+
141
+ ```
142
+ jrd find-dep <deps...> [options]
143
+
144
+ Options:
145
+ -d, --dir <dir> Root directory to scan (default: ~/Developer)
146
+ ```
147
+
148
+ Example: `jrd find-dep react-native expo`
149
+
150
+ ---
151
+
121
152
  ### `jrd focus`
122
153
 
123
154
  Bring an application to the front using AppleScript.
@@ -140,6 +171,21 @@ jrd folder-sizes [dir]
140
171
 
141
172
  ---
142
173
 
174
+ ### `jrd new-expo-project`
175
+
176
+ Bootstrap a new Expo project from the boilerplate repo. Clones or updates the boilerplate, copies it to `~/Developer/<Name>`, rewrites `package.json` and `app.json` with the derived name/slug/bundle identifiers, and creates an initial git commit.
177
+
178
+ ```
179
+ jrd new-expo-project [options]
180
+
181
+ Options:
182
+ -n, --name <name> Project name (required)
183
+ ```
184
+
185
+ Example: `jrd new-expo-project --name MyApp`
186
+
187
+ ---
188
+
143
189
  ### `jrd npm-downloads`
144
190
 
145
191
  List all your npm packages sorted by total downloads.
@@ -188,6 +234,16 @@ jrd repo-status [dir]
188
234
 
189
235
  ---
190
236
 
237
+ ### `jrd update-boilerplate`
238
+
239
+ Update the Expo boilerplate repo — clones it if absent, runs `jrd update-deps`, lint-fixes, type-checks, tests, then commits and pushes the result.
240
+
241
+ ```
242
+ jrd update-boilerplate
243
+ ```
244
+
245
+ ---
246
+
191
247
  ### `jrd update-deps`
192
248
 
193
249
  Update all npm dependencies to `@latest`. Automatically runs `npx expo install --fix` if the project uses Expo.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayrdeaton/scripts",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "private": false,
5
5
  "description": "Personal dev scripts",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync } from 'node:fs'
2
1
  import { execSync } from 'node:child_process'
2
+ import { existsSync, readFileSync } from 'node:fs'
3
3
 
4
4
  import { Color, Program } from 'termkit'
5
5
 
@@ -41,5 +41,5 @@ export const command = Program.command('base64')
41
41
  .action(({ value, copy }) => {
42
42
  const result = Buffer.from(value, 'base64').toString('utf8')
43
43
  output(result, copy)
44
- }),
44
+ })
45
45
  ])
@@ -1,5 +1,5 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
1
  import { execSync } from 'node:child_process'
2
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
3
3
 
4
4
  import { Color, Program } from 'termkit'
5
5
 
@@ -34,5 +34,5 @@ export const command = Program.command('binary')
34
34
  const buf = Buffer.from(JSON.parse(readFileSync(file, 'utf8')), 'binary')
35
35
  writeFileSync(destination, buf)
36
36
  console.log(`${Color.green('Success:')} Restored ${file} to ${destination}`)
37
- }),
37
+ })
38
38
  ])
@@ -0,0 +1,149 @@
1
+ import { readdirSync, readFileSync, statSync } from 'node:fs'
2
+ import { homedir } from 'node:os'
3
+ import { join, resolve } from 'node:path'
4
+
5
+ import { Color, Program, Spinner } from 'termkit'
6
+
7
+ function loadProject(pkgPath) {
8
+ try {
9
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
10
+ return { name: pkg.name, scripts: pkg.scripts ?? {} }
11
+ } catch {
12
+ return null
13
+ }
14
+ }
15
+
16
+ function mostCommon(values) {
17
+ const freq = {}
18
+ for (const v of values) freq[v] = (freq[v] ?? 0) + 1
19
+ return Object.entries(freq).sort((a, b) => b[1] - a[1])[0][0]
20
+ }
21
+
22
+ export const command = Program.command('check-scripts', '[scripts...]')
23
+ .description('Compare package.json scripts across projects for consistency')
24
+ .option('d', 'dir', '[dir]', 'Root directory to scan (default: ~/Developer)')
25
+ .option('r', 'ref', '[ref]', 'Reference project name to compare against')
26
+ .option('a', 'all', null, 'Show all scripts, including matching ones')
27
+ .option('f', 'flat', null, 'Show one line per project instead of grouping by value')
28
+ .action(async (options) => {
29
+ const root = resolve(options.dir ?? join(homedir(), 'Developer'))
30
+ const filterScripts = options.scripts ?? []
31
+
32
+ let entries
33
+ try {
34
+ entries = readdirSync(root)
35
+ } catch {
36
+ console.error(Color.red(`Could not read directory: ${root}`))
37
+ process.exit(1)
38
+ }
39
+
40
+ const spinner = new Spinner({ text: 'Scanning projects...' })
41
+ spinner.start()
42
+
43
+ const projects = []
44
+
45
+ for (const entry of entries) {
46
+ const dir = join(root, entry)
47
+ try {
48
+ if (!statSync(dir).isDirectory()) continue
49
+ } catch {
50
+ continue
51
+ }
52
+ spinner.message(entry)
53
+ const result = loadProject(join(dir, 'package.json'))
54
+ if (result) projects.push({ dir: entry, ...result })
55
+ }
56
+
57
+ spinner.stop()
58
+
59
+ if (!projects.length) {
60
+ console.log(Color.yellow('No projects with package.json found.'))
61
+ return
62
+ }
63
+
64
+ let refProject = null
65
+ if (options.ref) {
66
+ refProject = projects.find((p) => p.dir === options.ref || p.name === options.ref)
67
+ if (!refProject) {
68
+ console.error(Color.red(`Reference project not found: ${options.ref}`))
69
+ process.exit(1)
70
+ }
71
+ }
72
+
73
+ const allScriptNames = new Set()
74
+ for (const p of projects) {
75
+ for (const key of Object.keys(p.scripts)) {
76
+ if (!filterScripts.length || filterScripts.includes(key)) {
77
+ allScriptNames.add(key)
78
+ }
79
+ }
80
+ }
81
+
82
+ let printed = 0
83
+
84
+ for (const scriptName of [...allScriptNames].sort()) {
85
+ const withScript = projects.filter((p) => p.scripts[scriptName] !== undefined)
86
+ if (withScript.length < 2 && !refProject) continue
87
+
88
+ const expectedValue = refProject
89
+ ? refProject.scripts[scriptName]
90
+ : mostCommon(withScript.map((p) => p.scripts[scriptName]))
91
+
92
+ const allMatch =
93
+ withScript.every((p) => p.scripts[scriptName] === expectedValue) &&
94
+ (!refProject || projects.every((p) => p.scripts[scriptName] !== undefined))
95
+
96
+ if (!options.all && allMatch) continue
97
+
98
+ console.log(`\n${Color.bold(scriptName)}`)
99
+
100
+ if (options.flat) {
101
+ // Per-project lines
102
+ const allRelevant = refProject ? projects : withScript
103
+ for (const p of allRelevant) {
104
+ const value = p.scripts[scriptName]
105
+ const missing = value === undefined
106
+ const matches = !missing && value === expectedValue
107
+ const icon = matches ? Color.green('✓') : Color.red('✗')
108
+ const label = matches ? Color.faint(p.dir) : Color.bold(p.dir)
109
+ const display = missing ? Color.faint('(missing)') : Color.faint(value)
110
+ console.log(` ${icon} ${label} ${display}`)
111
+ }
112
+ } else {
113
+ // Grouped by value
114
+ const groups = new Map()
115
+ const allRelevant = refProject ? projects : withScript
116
+
117
+ for (const p of allRelevant) {
118
+ const value = p.scripts[scriptName] ?? null
119
+ if (!groups.has(value)) groups.set(value, [])
120
+ groups.get(value).push(p.dir)
121
+ }
122
+
123
+ // Sort: expected value first, then others, missing last
124
+ const sorted = [...groups.entries()].sort(([a], [b]) => {
125
+ if (a === expectedValue) return -1
126
+ if (b === expectedValue) return 1
127
+ if (a === null) return 1
128
+ if (b === null) return -1
129
+ return 0
130
+ })
131
+
132
+ for (const [value, dirs] of sorted) {
133
+ const matches = value === expectedValue
134
+ const icon = matches ? Color.green('✓') : Color.red('✗')
135
+ const label = matches ? Color.faint(dirs.join(', ')) : Color.bold(dirs.join(', '))
136
+ const display = value === null ? Color.faint('(missing)') : Color.faint(value)
137
+ console.log(` ${icon} ${label} ${display}`)
138
+ }
139
+ }
140
+
141
+ printed++
142
+ }
143
+
144
+ if (!printed) {
145
+ console.log(Color.green('\nAll scripts are consistent across projects.'))
146
+ } else {
147
+ console.log()
148
+ }
149
+ })
@@ -1,7 +1,7 @@
1
- import { extname, basename } from 'node:path'
2
1
  import { readdirSync, rmSync, statSync } from 'node:fs'
3
- import { createInterface } from 'node:readline'
2
+ import { basename, extname } from 'node:path'
4
3
  import { join, resolve } from 'node:path'
4
+ import { createInterface } from 'node:readline'
5
5
 
6
6
  import { Color, Program, Spinner } from 'termkit'
7
7
 
@@ -1,4 +1,4 @@
1
- import { readFileSync, readdirSync, statSync } from 'node:fs'
1
+ import { readdirSync, readFileSync, statSync } from 'node:fs'
2
2
  import { homedir } from 'node:os'
3
3
  import { join, resolve } from 'node:path'
4
4
 
@@ -77,16 +77,12 @@ export const command = Program.command('find-dep', '[deps...]')
77
77
  console.log(Color.bold(`\nFound ${results.length} project${results.length !== 1 ? 's' : ''}:\n`))
78
78
 
79
79
  for (const result of results) {
80
- const label = result.projectName && result.projectName !== result.dir
81
- ? `${Color.bold(result.dir)} ${Color.faint(`(${result.projectName})`)}`
82
- : Color.bold(result.dir)
80
+ const label = result.projectName && result.projectName !== result.dir ? `${Color.bold(result.dir)} ${Color.faint(`(${result.projectName})`)}` : Color.bold(result.dir)
83
81
 
84
82
  console.log(` ${label}`)
85
83
 
86
84
  for (const dep of result.found) {
87
- const fieldLabel = dep.field === 'dependencies' ? 'dep'
88
- : dep.field === 'devDependencies' ? 'dev'
89
- : 'peer'
85
+ const fieldLabel = dep.field === 'dependencies' ? 'dep' : dep.field === 'devDependencies' ? 'dev' : 'peer'
90
86
  console.log(` ${Color.cyan(dep.name)} ${Color.faint(`${dep.version} [${fieldLabel}]`)}`)
91
87
  }
92
88
  }
@@ -3,7 +3,7 @@ import { cpSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs
3
3
  import { homedir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
 
6
- import { Color, Program, log } from 'termkit'
6
+ import { Color, log, Program } from 'termkit'
7
7
 
8
8
  const BOILERPLATE_REPO = 'git@github.com:jayrdeaton/Expo-Boilerplate.git'
9
9
  const BOILERPLATE_DIR = join(homedir(), 'Developer', 'Expo-Boilerplate')
@@ -49,7 +49,7 @@ export const command = Program.command('new-expo-project')
49
49
  log.info(`Creating ${displayName}...`)
50
50
  cpSync(BOILERPLATE_DIR, targetDir, {
51
51
  recursive: true,
52
- filter: (src) => !src.includes('/node_modules/'),
52
+ filter: (src) => !src.includes('/node_modules/')
53
53
  })
54
54
 
55
55
  rmSync(join(targetDir, '.git'), { recursive: true, force: true })
@@ -1,7 +1,7 @@
1
1
  import { readdirSync, renameSync, statSync } from 'node:fs'
2
2
  import { extname, join } from 'node:path'
3
3
 
4
- import { Color, Program, log } from 'termkit'
4
+ import { Color, log, Program } from 'termkit'
5
5
 
6
6
  export const command = Program.command('rename-season')
7
7
  .description('Rename files in a directory to SxEE format for TV library pickup')
@@ -3,7 +3,7 @@ import { existsSync } from 'node:fs'
3
3
  import { homedir } from 'node:os'
4
4
  import { join } from 'node:path'
5
5
 
6
- import { Color, Program, log } from 'termkit'
6
+ import { Color, log, Program } from 'termkit'
7
7
 
8
8
  const BOILERPLATE_REPO = 'git@github.com:jayrdeaton/Expo-Boilerplate.git'
9
9
  const BOILERPLATE_DIR = join(homedir(), 'Developer', 'Expo-Boilerplate')
@@ -2,7 +2,7 @@ import { execSync } from 'node:child_process'
2
2
  import { readFileSync } from 'node:fs'
3
3
  import { resolve } from 'node:path'
4
4
 
5
- import { Color, Program, log } from 'termkit'
5
+ import { Color, log, Program } from 'termkit'
6
6
 
7
7
  function exec(cmd) {
8
8
  console.log(Color.faint(`$ ${cmd}`))