Skip to content

Commit aaf194e

Browse files
committed
feat: add sofie-code-preset-setup bin script
Adds a new `sofie-code-preset-setup` CLI command that automates setting up or updating a project to use this preset. It: - Errors if the project does not use yarn - Sets the `prettier` config key in package.json - Adds/updates lint, lint:fix, lint:eslint, lint:prettier, and license-validate scripts - Adds a prepare script for husky if not already present - Sets up lint-staged config in package.json - Creates eslint.config.mjs if it does not already exist - Copies the .editorconfig from the preset - Creates .husky/pre-commit if it does not already exist Also adds .editorconfig to the published files list.
1 parent 2a2cb00 commit aaf194e

2 files changed

Lines changed: 153 additions & 0 deletions

File tree

bin/setup.mjs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env node
2+
'use strict'
3+
4+
import { existsSync } from 'fs'
5+
import { copyFile, mkdir, readFile, writeFile } from 'fs/promises'
6+
import path from 'path'
7+
import { fileURLToPath } from 'url'
8+
9+
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
10+
const projectDir = process.cwd()
11+
12+
// ── 1. Find and parse the project's package.json ──────────────────────────────
13+
14+
const pkgPath = path.join(projectDir, 'package.json')
15+
if (!existsSync(pkgPath)) {
16+
console.error('Error: No package.json found in the current directory.')
17+
process.exit(1)
18+
}
19+
20+
let pkgText
21+
try {
22+
pkgText = await readFile(pkgPath, 'utf-8')
23+
} catch (e) {
24+
console.error(`Error reading package.json: ${e.message}`)
25+
process.exit(1)
26+
}
27+
28+
let pkg
29+
try {
30+
pkg = JSON.parse(pkgText)
31+
} catch (e) {
32+
console.error(`Error parsing package.json: ${e.message}`)
33+
process.exit(1)
34+
}
35+
36+
// ── 2. Require yarn ────────────────────────────────────────────────────────────
37+
38+
const pmField = pkg.packageManager ?? ''
39+
if (pmField && !pmField.startsWith('yarn')) {
40+
console.error(`Error: package.json declares packageManager "${pmField}". This tool requires yarn.`)
41+
process.exit(1)
42+
}
43+
if (!pmField) {
44+
if (existsSync(path.join(projectDir, 'package-lock.json'))) {
45+
console.error('Error: Found a package-lock.json. This tool requires yarn.')
46+
process.exit(1)
47+
}
48+
if (existsSync(path.join(projectDir, 'pnpm-lock.yaml'))) {
49+
console.error('Error: Found a pnpm-lock.yaml. This tool requires yarn.')
50+
process.exit(1)
51+
}
52+
}
53+
54+
// ── 3. Update package.json ────────────────────────────────────────────────────
55+
56+
let pkgChanged = false
57+
// Preserve the original indentation style
58+
const indent = pkgText.match(/^\t/m) ? '\t' : ' '
59+
60+
function markChanged(label) {
61+
pkgChanged = true
62+
console.log(` \u2714 ${label}`)
63+
}
64+
65+
// prettier config
66+
const prettierValue = '@sofie-automation/code-standard-preset/prettier.config.mjs'
67+
if (pkg.prettier !== prettierValue) {
68+
pkg.prettier = prettierValue
69+
markChanged('Set prettier config')
70+
}
71+
72+
// lint scripts — always update preset-owned scripts; add "prepare" only if absent
73+
pkg.scripts ??= {}
74+
const presetScripts = {
75+
'lint:eslint': 'eslint .',
76+
'lint:prettier': 'prettier --check .',
77+
lint: 'yarn lint:eslint && yarn lint:prettier',
78+
'lint:fix': 'yarn lint:eslint --fix && yarn lint:prettier --write',
79+
'license-validate': 'sofie-licensecheck',
80+
}
81+
for (const [name, cmd] of Object.entries(presetScripts)) {
82+
if (pkg.scripts[name] !== cmd) {
83+
pkg.scripts[name] = cmd
84+
markChanged(`Set script "${name}"`)
85+
}
86+
}
87+
if (!pkg.scripts.prepare) {
88+
pkg.scripts.prepare = 'husky'
89+
markChanged('Set script "prepare" (husky)')
90+
}
91+
92+
// lint-staged
93+
const targetLintStaged = {
94+
'*.{css,json,md,scss}': ['prettier --write'],
95+
'*.{ts,tsx,js,jsx,mjs,cjs}': ['eslint --fix'],
96+
}
97+
if (JSON.stringify(pkg['lint-staged']) !== JSON.stringify(targetLintStaged)) {
98+
pkg['lint-staged'] = targetLintStaged
99+
markChanged('Set lint-staged config')
100+
}
101+
102+
if (pkgChanged) {
103+
await writeFile(pkgPath, JSON.stringify(pkg, null, indent) + '\n', 'utf-8')
104+
console.log(' \u2714 Wrote package.json')
105+
} else {
106+
console.log(' - package.json already up to date')
107+
}
108+
109+
// ── 4. Create eslint.config.mjs if missing ────────────────────────────────────
110+
111+
const eslintConfigPath = path.join(projectDir, 'eslint.config.mjs')
112+
if (!existsSync(eslintConfigPath)) {
113+
await writeFile(
114+
eslintConfigPath,
115+
[
116+
"import { generateEslintConfig } from '@sofie-automation/code-standard-preset/eslint/main.mjs'",
117+
'',
118+
'export default await generateEslintConfig({})',
119+
'',
120+
].join('\n'),
121+
'utf-8'
122+
)
123+
console.log(' \u2714 Created eslint.config.mjs')
124+
} else {
125+
console.log(' - eslint.config.mjs already exists, skipping')
126+
}
127+
128+
// ── 5. Copy .editorconfig ─────────────────────────────────────────────────────
129+
130+
const srcEditorconfig = path.join(scriptDir, '..', '.editorconfig')
131+
const destEditorconfig = path.join(projectDir, '.editorconfig')
132+
await copyFile(srcEditorconfig, destEditorconfig)
133+
console.log(' \u2714 Copied .editorconfig')
134+
135+
// ── 6. Create .husky/pre-commit if missing ────────────────────────────────────
136+
137+
const preCommitPath = path.join(projectDir, '.husky', 'pre-commit')
138+
if (!existsSync(preCommitPath)) {
139+
await mkdir(path.join(projectDir, '.husky'), { recursive: true })
140+
await writeFile(preCommitPath, 'lint-staged\n', { encoding: 'utf-8', mode: 0o755 })
141+
console.log(' \u2714 Created .husky/pre-commit')
142+
} else {
143+
console.log(' - .husky/pre-commit already exists, skipping')
144+
}
145+
146+
// ── Done ──────────────────────────────────────────────────────────────────────
147+
148+
console.log('\nDone. Next steps:')
149+
console.log(' 1. yarn add --dev eslint typescript husky lint-staged prettier')
150+
console.log(' 2. yarn install (to initialize husky via the prepare script)')
151+
console.log(' 3. Review and commit the changes')

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"node": ">= 20"
2929
},
3030
"bin": {
31+
"sofie-code-preset-setup": "./bin/setup.mjs",
3132
"sofie-licensecheck": "./bin/checkLicenses.mjs",
3233
"sofie-version": "./bin/updateVersion.mjs"
3334
},
@@ -46,6 +47,7 @@
4647
"files": [
4748
"/CHANGELOG.md",
4849
"/README.md",
50+
"/.editorconfig",
4951
"/ts",
5052
"/bin",
5153
"/eslint",

0 commit comments

Comments
 (0)