Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/tangy-streets-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@naverpay/code-style-cli": minor
---

[code-style-cli] 패키지 설치 및 설정 파일 생성 CLI 도구 추가

PR: [[code-style-cli] 패키지 설치 및 설정 파일 생성 CLI 도구 추가](https://github.com/NaverPayDev/code-style/pull/139)
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
네이버페이의 일관된 코드 스타일을 유지하기 위한 레포입니다.
해당 레포는 모노레포 구조로 여러 패키지를 포함하며, 필요에 맞게 선택하여 사용하실 수 있습니다.

- [@naverpay/eslint-config](./packages/eslint-config/README.md): ESLint config 를 제공하는 패키지입니다.
- [@naverpay/code-style-cli](./packages/code-style-cli/README.md): 패키지 설치 및 설정 파일 생성을 위한 CLI 도구입니다.
- [@naverpay/eslint-config](./packages/eslint-config/README.md): ESLint config를 제공하는 패키지입니다.
- [@naverpay/eslint-plugin](./packages/eslint-plugin/README.md): ESLint plugin 을 제공하는 패키지입니다.
- [@naverpay/oxlint-config](./packages/oxlint-config/README.md): oxlint config 를 제공하는 패키지입니다.
- [@naverpay/prettier-config](./packages/prettier-config/README.md): Prettier config 를 제공하는 패키지입니다.
- [@naverpay/stylelint-config](./packages/stylelint-config/README.md): Stylelint config 를 제공하는 패키지 입니다.
- [@naverpay/biome-config](./packages/biome-config/README.md): Biome config 를 제공하는 패키지입니다.
- [@naverpay/stylelint-config](./packages/stylelint-config/README.md): Stylelint config 를 제공하는 패키지입니다.
- [@naverpay/editorconfig](./packages/editorconfig/README.md): IDE 일관된 코딩 스타일로 작성할 수 있도록 `.editorconfig` 를 제공하는 패키지입니다.
- [@naverpay/markdown-lint](./packages/markdown-lint/README.md): 마크다운(Markdown) 파일의 스타일을 검사하는 패키지 입니다.
118 changes: 118 additions & 0 deletions packages/code-style-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# @naverpay/code-style-cli

네이버페이 코드 스타일 패키지를 쉽게 설치하고 설정할 수 있는 CLI 도구입니다.

> pnpm v10+에서는 보안상의 이유로 postinstall 스크립트가 기본적으로 실행되지 않습니다. 이 CLI를 사용하면 패키지 설치와 설정 파일 생성을 한 번에 처리할 수 있습니다.

## 사용 방법

프로젝트 루트에서 실행합니다.

```bash
npx @naverpay/code-style-cli
```

1. 패키지 매니저를 자동으로 감지합니다. (npm, yarn, pnpm)
2. 설치할 패키지를 선택합니다.
3. 선택한 패키지가 설치되고 설정 파일이 자동으로 생성됩니다.

## 지원 패키지

| 패키지 | 설정 파일 |
|--------|-----------|
| [@naverpay/eslint-config](../eslint-config/README.md) | - |
| [@naverpay/eslint-plugin](../eslint-plugin/README.md) | - |
| [@naverpay/prettier-config](../prettier-config/README.md) | `.prettierrc` |
| [@naverpay/stylelint-config](../stylelint-config/README.md) | `stylelint.config.mjs` |
| [@naverpay/markdown-lint](../markdown-lint/README.md) | `.markdownlint.jsonc` |
| [@naverpay/editorconfig](../editorconfig/README.md) | `.editorconfig` |
| [@naverpay/oxlint-config](../oxlint-config/README.md) | `.oxlintrc.json` |
| [@naverpay/biome-config](../biome-config/README.md) | `biome.json` |
| [oxfmt](#oxfmt-가이드) | `.oxfmtrc.json` |

> eslint-config, eslint-plugin은 설정이 복잡하여 설정 파일을 자동 생성하지 않습니다.

## 설치 후 설정

CLI는 기본 설정 파일만 생성합니다. 추가 설정이 필요한 경우:

- **설정 변경**: 생성된 설정 파일을 직접 수정하세요.
- **CLI 명령어**: 각 패키지 README의 "CLI" 섹션을 참고하세요.
- **IDE 설정**: 각 패키지 README의 "Integrating with IDE" 섹션을 참고하세요.

## 주의 사항

- 프로젝트 루트에 `package.json`이 있어야 합니다.
- 이미 설정 파일이 존재하는 경우 덮어쓰지 않습니다.

---

## oxfmt 가이드

> oxfmt는 Prettier 호환 포매터로, Rust로 작성되어 빠른 속도를 제공합니다.
>
> **Note:** oxfmt는 현재 **alpha** 단계입니다. VSCode Extension 지원도 experimental 상태입니다.

oxfmt는 현재 `extends` 옵션을 지원하지 않아 별도 config 패키지가 없습니다. CLI에서 네이버페이 권장 설정이 포함된 `.oxfmtrc.json`을 생성합니다.

### 설정

필요에 따라 `ignorePatterns`를 추가합니다.

```json
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"singleQuote": true,
"semi": false,
"useTabs": false,
"tabWidth": 4,
"endOfLine": "lf",
"bracketSpacing": false,
"arrowParens": "always",
"bracketSameLine": false,
"printWidth": 120,
"trailingComma": "all",
"ignorePatterns": ["dist", "pnpm-lock.yaml", ".github"]
}
```

### CLI

package.json에 스크립트를 추가하여 format 검사를 할 수 있습니다.

```json
{
"scripts": {
"format": "oxfmt",
"format:check": "oxfmt --check"
}
}
```

### Integrating with IDE

#### VSCode

> **Warning:** oxfmt의 VSCode 지원은 현재 **experimental** 단계입니다.

1. [oxc Extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode)을 설치합니다.
2. `settings.json`에 아래 설정을 추가합니다.

```json
{
"oxc.enable": true,
"oxc.fmt.experimental": true,
"editor.defaultFormatter": "oxc.oxc-vscode",
"editor.formatOnSave": true,
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
}
}
```

#### WebStorm

[oxc-intellij-plugin](https://plugins.jetbrains.com/plugin/27061-oxc) (v0.0.21 이상)을 설치하여 사용할 수 있습니다.
104 changes: 104 additions & 0 deletions packages/code-style-cli/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env node
/* eslint-disable no-console */

import {execSync} from 'child_process'
import fs from 'fs'

import {checkbox, confirm} from '@inquirer/prompts'

import {PACKAGE_MANAGERS, TOOLS, TOOLS_MAP} from './configs.js'

// 1. package.json 존재 확인
if (!fs.existsSync('package.json')) {
console.error('❌ package.json이 없습니다. 프로젝트 루트에서 실행해주세요.')
process.exit(1)
}

// 2. 패키지 매니저 감지
function detectPackageManager() {
for (const [name, {lockFile}] of Object.entries(PACKAGE_MANAGERS)) {
if (fs.existsSync(lockFile)) return name
}
return 'npm'
}

const pm = detectPackageManager()
console.log(`📦 패키지 매니저: ${pm}`)

// 3. 설치된 패키지 확인
const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
const installedPkgs = {...pkgJson.dependencies, ...pkgJson.devDependencies}

// 4. 패키지 선택 UI
const choices = TOOLS.map((tool) => {
const isInstalled = tool.packages.some((pkg) => installedPkgs[pkg])
return {
...tool,
name: isInstalled ? `${tool.value} (설치됨)` : tool.value,
}
})

const selected = await checkbox({
message: '설치할 패키지를 선택하세요 (이미 설치된 패키지 선택 시 업데이트)',
choices,
pageSize: choices.length,
})

if (selected.length === 0) {
console.log('⚠️ 선택된 패키지가 없습니다.')
process.exit(0)
}

const selectedTools = selected.map((value) => TOOLS_MAP[value])
console.log('✅ 선택된 패키지:', selected)

// 5. 패키지 설치
const packagesToInstall = selectedTools.flatMap((tool) => tool.packages)

if (packagesToInstall.length > 0) {
console.log('\n📥 패키지 설치 중...')
try {
execSync(`${PACKAGE_MANAGERS[pm].installCmd} ${packagesToInstall.join(' ')}`, {stdio: 'inherit'})
console.log('✅ 패키지 설치 완료!')
} catch {
console.error('❌ 패키지 설치 실패. 네트워크 연결을 확인하거나 수동으로 설치해주세요.')
process.exit(1)
}
}

// 6. 설정 파일 생성
for (const tool of selectedTools) {
if (!tool.configFile) continue

if (fs.existsSync(tool.configFile)) {
const overwrite = await confirm({
message: `${tool.configFile} 이미 존재합니다. 덮어쓰시겠습니까?`,
default: false,
})
if (!overwrite) {
console.log(`⏭️ ${tool.configFile} 스킵`)
continue
}
}

if (tool.copyFrom) {
if (!fs.existsSync(tool.copyFrom)) {
console.error(`❌ ${tool.copyFrom} 파일을 찾을 수 없습니다.`)
continue
}
const content = fs.readFileSync(tool.copyFrom, 'utf-8')
fs.writeFileSync(tool.configFile, content)
} else if (tool.getContent) {
const content = tool.getContent()
if (!content) {
console.error(`❌ ${tool.configFile} 생성에 필요한 정보를 찾을 수 없습니다.`)
continue
}
fs.writeFileSync(tool.configFile, content)
} else if (tool.configContent) {
fs.writeFileSync(tool.configFile, tool.configContent)
}
console.log(`✅ ${tool.configFile} 생성 완료`)
}

console.log('\n🎉 완료!')
114 changes: 114 additions & 0 deletions packages/code-style-cli/configs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import fs from 'fs'

// 설정 파일 내용
const PRETTIERRC_CONTENT = '"@naverpay/prettier-config"'

const STYLELINT_CONTENT = `/** @type {import('stylelint').Config} */
const config = {
extends: ['@naverpay/stylelint-config'],
defaultSeverity: 'error',
rules: {},
}

export default config
`

const MARKDOWNLINT_CONTENT = JSON.stringify(
{
extends: '@naverpay/markdown-lint',
},
null,
4,
)

const OXLINTRC_CONTENT = JSON.stringify(
{
$schema: './node_modules/oxlint/configuration_schema.json',
extends: ['./node_modules/@naverpay/oxlint-config/node/.oxlintrc.json'],
},
null,
4,
)

function getBiomeContent() {
const pkgJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
const version = pkgJson.devDependencies?.['@biomejs/biome']?.replace(/[\^~]/, '')
if (!version) return null
return JSON.stringify(
{
$schema: `https://biomejs.dev/schemas/${version}/schema.json`,
extends: ['@naverpay/biome-config'],
},
null,
4,
)
}

const OXFMTRC_CONTENT = JSON.stringify(
{
$schema: './node_modules/oxfmt/configuration_schema.json',
singleQuote: true,
semi: false,
useTabs: false,
tabWidth: 4,
endOfLine: 'lf',
bracketSpacing: false,
arrowParens: 'always',
bracketSameLine: false,
printWidth: 120,
trailingComma: 'all',
},
null,
4,
)

// 패키지 목록
export const TOOLS = [
{value: 'eslint-config', packages: ['@naverpay/eslint-config']},
{value: 'eslint-plugin', packages: ['@naverpay/eslint-plugin']},
{
value: 'prettier-config',
packages: ['@naverpay/prettier-config'],
configFile: '.prettierrc',
configContent: PRETTIERRC_CONTENT,
},
{
value: 'stylelint-config',
packages: ['@naverpay/stylelint-config', 'stylelint'],
configFile: 'stylelint.config.mjs',
configContent: STYLELINT_CONTENT,
},
{
value: 'markdown-lint',
packages: ['@naverpay/markdown-lint'],
configFile: '.markdownlint.jsonc',
configContent: MARKDOWNLINT_CONTENT,
},
{
value: 'editorconfig',
packages: ['@naverpay/editorconfig'],
configFile: '.editorconfig',
copyFrom: 'node_modules/@naverpay/editorconfig/.editorconfig',
},
{
value: 'oxlint-config',
packages: ['@naverpay/oxlint-config', 'oxlint'],
configFile: '.oxlintrc.json',
configContent: OXLINTRC_CONTENT,
},
{
value: 'biome-config',
packages: ['@naverpay/biome-config', '@biomejs/biome'],
configFile: 'biome.json',
getContent: getBiomeContent,
},
{value: 'oxfmt', packages: ['oxfmt'], configFile: '.oxfmtrc.json', configContent: OXFMTRC_CONTENT},
]

export const TOOLS_MAP = Object.fromEntries(TOOLS.map((t) => [t.value, t]))

export const PACKAGE_MANAGERS = {
npm: {lockFile: 'package-lock.json', installCmd: 'npm install -D'},
yarn: {lockFile: 'yarn.lock', installCmd: 'yarn add -D'},
pnpm: {lockFile: 'pnpm-lock.yaml', installCmd: 'pnpm add -D'},
}
25 changes: 25 additions & 0 deletions packages/code-style-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@naverpay/code-style-cli",
"version": "0.0.1",
"description": "NaverPay code-style CLI tool",
"keywords": [
"code-style",
"cli",
"naverpay"
],
"homepage": "https://github.com/NaverPayDev/code-style",
"repository": {
"type": "git",
"url": "https://github.com/NaverPayDev/code-style.git",
"directory": "packages/code-style-cli"
},
"license": "MIT",
"author": "@NaverPayDev/frontend",
"type": "module",
"bin": {
"code-style": "cli.js"
},
"dependencies": {
"@inquirer/prompts": "^8.1.0"
}
}
Loading