Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1b20d16
feat(oxlint-config-smarthr): add sharable oxlint config package
s-sasaki-0529 Apr 13, 2026
9cddd5e
chore(oxlint-config-smarthr): enable release-please for publishing
s-sasaki-0529 Apr 13, 2026
8255a21
chore(oxlint-config-smarthr): require oxlint >= 1.0.0
s-sasaki-0529 Apr 13, 2026
26c1e5d
chore(oxlint-config-smarthr): set initial-version to 0.1.0
s-sasaki-0529 Apr 13, 2026
43b4a9b
chore(oxlint-config-smarthr): revert manual manifest change
s-sasaki-0529 Apr 13, 2026
d4c804f
fix(oxlint-config-smarthr): remove unnecessary categories.correctness…
s-sasaki-0529 Apr 13, 2026
9bc490f
fix(oxlint-config-smarthr): remove typeAware from shared config
s-sasaki-0529 Apr 13, 2026
64abf91
docs(oxlint-config-smarthr): update type-aware linting description
s-sasaki-0529 Apr 13, 2026
748b46c
fix(oxlint-config-smarthr): enable 5 commented-out smarthr rules
s-sasaki-0529 Apr 13, 2026
793bafe
chore(oxlint-config-smarthr): set version to 0.1.0
s-sasaki-0529 Apr 13, 2026
f7c78f1
docs(oxlint-config-smarthr): unify README language to Japanese
s-sasaki-0529 Apr 13, 2026
2c5ed99
Apply suggestion from @s-sasaki-0529
s-sasaki-0529 Apr 13, 2026
53a08eb
Apply suggestion from @s-sasaki-0529
s-sasaki-0529 Apr 13, 2026
9054176
docs: readme が途切れているのを修正
s-sasaki-0529 Apr 13, 2026
c4e5427
fix: pnpm ロックファイルを更新
s-sasaki-0529 Apr 13, 2026
59c326c
ci: add pkg-pr-new preview release workflow
s-sasaki-0529 Apr 13, 2026
ce10db4
fix(ci): add pnpm and node setup to preview release workflow
s-sasaki-0529 Apr 13, 2026
8374ed9
fix(oxlint-config-smarthr): address review feedback
s-sasaki-0529 Apr 13, 2026
f09351a
fix(oxlint-config-smarthr): restore typescript/dot-notation rule
s-sasaki-0529 Apr 13, 2026
54a6f30
fix(oxlint-config-smarthr): address review feedback (round 2)
s-sasaki-0529 Apr 13, 2026
8249f1c
fix(oxlint-config-smarthr): revert override comment change
s-sasaki-0529 Apr 13, 2026
77f168d
test(oxlint-config-smarthr): add smoke test
s-sasaki-0529 Apr 13, 2026
a6141aa
test(oxlint-config-smarthr): replace vitest with oxlint --print-config
s-sasaki-0529 Apr 13, 2026
82d326b
Apply suggestion from @s-sasaki-0529
s-sasaki-0529 Apr 13, 2026
25c0d16
fix(oxlint-config-smarthr): remove unnecessary TypeScript overrides
s-sasaki-0529 Apr 13, 2026
cb33d87
fix(oxlint-config-smarthr): add missing fix option to spread-syntax rule
s-sasaki-0529 Apr 13, 2026
3da0f18
docs: add cross-reference comments between eslint/oxlint configs
s-sasaki-0529 Apr 13, 2026
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
22 changes: 22 additions & 0 deletions .github/workflows/previewRelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Preview Release

on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- 'packages/oxlint-config-smarthr/**'

jobs:
previewRelease:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
with:
package_json_file: 'package.json'
run_install: false
- uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- run: pnpx pkg-pr-new publish packages/oxlint-config-smarthr
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 smarthr-ui パッケージで昨年から導入しているプレリリースの仕組みを、tamatebako でも使いたく導入しています。とりあえず影響範囲を最小にするために本パッケージでのみにしてますが、トリガーのオプションを調整することで任意のパッケージで使用できるはずです。

9 changes: 9 additions & 0 deletions packages/oxlint-config-smarthr/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright 2026 SmartHR

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
58 changes: 58 additions & 0 deletions packages/oxlint-config-smarthr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# oxlint-config-smarthr

[![npm version](https://badge.fury.io/js/oxlint-config-smarthr.svg)](https://badge.fury.io/js/oxlint-config-smarthr)

SmartHR 全社共通の oxlint 設定です。
React + TypeScript プロジェクトでの利用を想定しています。

## インストール

```sh
pnpm add --dev oxlint eslint-plugin-smarthr # peerDependencies
pnpm add --dev oxlint-config-smarthr
```

## 使い方

`oxlint.config.ts` で設定をインポートし、`extends` に追加してください。

```ts
import { defineConfig } from 'oxlint'
import smarthrConfig from 'oxlint-config-smarthr'

export default defineConfig({
extends: [smarthrConfig],
// NOTE: plugins / jsPlugins は extends によって上書きされるため、
// 共有設定分も含めて必要なプラグインをすべて再宣言する必要があります。
plugins: ['typescript', 'import', 'react', 'jsx-a11y'],
jsPlugins: ['eslint-plugin-smarthr'],
rules: {
// プロダクト固有のルール
},
})
```

`oxlint` コマンドで実行可能です。 eslint で使用できる大半のコマンドを利用できます。

```sh
pnpm oxlint
```

## Type-aware linting

この共有設定には `options.typeAware` は含まれていません。
TypeScript の型情報を利用するルール(`typescript/dot-notation` など)を有効にするには、追加パッケージをインストールし、設定ファイルを調整してください。
Comment on lines +43 to +44
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 ここの扱いはちょっと迷ったけど、tsgo に依存する必要があり、まだ必須とするには速い気がしてるのでこの形にしてます。 TypeScript 7 が出たらもうこのパッケージに入れちゃって良いかも。


```bash
pnpm add -D oxlint-tsgolint
```

```ts
export default defineConfig({
extends: [smarthrConfig],
options: {
typeAware: true,
},
// ...
})
```
250 changes: 250 additions & 0 deletions packages/oxlint-config-smarthr/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
/**
* SmartHR 全社共通の oxlint 設定
*
* eslint-config-smarthr の oxlint 版。
* プロダクト横断で共有するルールセットを定義する。
*
* @type {import('oxlint').OxlintConfig}
*/
const config = {
plugins: ['typescript', 'import', 'react', 'jsx-a11y'],
jsPlugins: ['eslint-plugin-smarthr'],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 名前の通り、plugins は Rust で書かれたビルトインのプラグインで、jsPlugins は ESLint プラグインです。

eslint-config-smarthr が扱っているカスタムルールのうち、eslint-plugin-smarthr だけは組み込まれていない(それはそう) ので、JSのまま読み込む形になります。

env: {
builtin: true,
browser: true,
commonjs: true,
es2016: true,
},
rules: {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint-config-smarthr 側と限りなく同じです。ビルトイン化されたプラグインの詳細設定まで完璧に同じとは言い切れないですが‥…。
https://github.com/kufu/tamatebako/blob/748b46c97c3ea2b15e59c1b90e53fa3769960ca3/packages/oxlint-config-smarthr/index.js


// === ESLint recommended ===
'constructor-super': 'error',
'for-direction': 'error',
'no-async-promise-executor': 'error',
'no-case-declarations': 'error',
'no-class-assign': 'error',
'no-compare-neg-zero': 'error',
'no-cond-assign': 'error',
'no-const-assign': 'error',
'no-constant-binary-expression': 'error',
'no-constant-condition': 'error',
'no-control-regex': 'error',
'no-debugger': 'error',
'no-delete-var': 'error',
'no-dupe-class-members': 'error',
'no-dupe-else-if': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-empty': 'error',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-empty-static-block': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
'no-fallthrough': 'error',
'no-func-assign': 'error',
'no-global-assign': 'error',
'no-import-assign': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-loss-of-precision': 'error',
'no-misleading-character-class': 'error',
'no-new-native-nonconstructor': 'error',
'no-nonoctal-decimal-escape': 'error',
'no-obj-calls': 'error',
'no-prototype-builtins': 'error',
'no-redeclare': 'error',
'no-regex-spaces': 'error',
'no-self-assign': 'error',
'no-setter-return': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-this-before-super': 'error',
'no-unsafe-finally': 'error',
'no-unsafe-negation': 'error',
'no-unsafe-optional-chaining': 'error',
'no-unused-labels': 'error',
'no-unused-private-class-members': 'error',
'no-useless-backreference': 'error',
'no-useless-catch': 'error',
'no-useless-escape': 'error',
'no-with': 'error',
'require-yield': 'error',
'use-isnan': 'error',
'valid-typeof': ['error', { requireStringLiterals: true }],

// === ESLint best practices ===
'array-callback-return': 'warn',
'block-scoped-var': 'warn',
'default-param-last': 'error',
eqeqeq: 'error',
'import/no-duplicates': 'error',
'no-caller': 'error',
'no-div-regex': 'warn',
'no-eval': 'error',
'no-extend-native': 'error',
'no-inner-declarations': 'warn',
'no-iterator': 'error',
'no-label-var': 'error',
'no-lone-blocks': 'error',
'no-loop-func': 'warn',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-proto': 'error',
'no-return-assign': 'error',
'no-script-url': 'warn',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-shadow': 'error',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'warn',
'no-unused-expressions': 'error',
'no-useless-call': 'warn',
'no-useless-computed-key': 'error',
'no-useless-concat': 'error',
'no-useless-rename': 'error',
'no-var': 'error',
'no-void': 'error',
'prefer-const': ['warn', { destructuring: 'all', ignoreReadBeforeAssign: true }],
'prefer-numeric-literals': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'warn',
radix: 'error',
'symbol-description': 'error',
'vars-on-top': 'warn',
'no-array-constructor': 'error',

// === React recommended ===
'react/jsx-key': 'error',
'react/jsx-no-comment-textnodes': 'error',
'react/jsx-no-duplicate-props': 'error',
'react/jsx-no-target-blank': 'error',
'react/jsx-no-undef': 'error',
'react/no-children-prop': 'warn',
'react/no-danger-with-children': 'error',
'react/no-direct-mutation-state': 'error',
'react/no-find-dom-node': 'error',
'react/no-is-mounted': 'error',
'react/no-render-return-value': 'error',
'react/no-string-refs': 'error',
'react/no-unescaped-entities': 'warn',
'react/no-unknown-property': 'error',

// === React hooks ===
'react/exhaustive-deps': 'error',
'react/rules-of-hooks': 'error',

// === jsx-a11y ===
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/anchor-has-content': 'warn',
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
'jsx-a11y/aria-props': 'warn',
'jsx-a11y/aria-role': 'warn',
'jsx-a11y/aria-unsupported-elements': 'warn',
'jsx-a11y/heading-has-content': 'warn',
'jsx-a11y/html-has-lang': 'error',
'jsx-a11y/iframe-has-title': 'warn',
'jsx-a11y/label-has-associated-control': 'warn',
'jsx-a11y/lang': 'error',
'jsx-a11y/media-has-caption': 'warn',
'jsx-a11y/mouse-events-have-key-events': 'warn',
'jsx-a11y/no-access-key': 'warn',
'jsx-a11y/no-autofocus': 'warn',
'jsx-a11y/no-distracting-elements': 'warn',
'jsx-a11y/no-noninteractive-tabindex': 'warn',
'jsx-a11y/no-redundant-roles': 'warn',
'jsx-a11y/role-has-required-aria-props': 'warn',
'jsx-a11y/role-supports-aria-props': 'warn',
'jsx-a11y/scope': 'warn',
'jsx-a11y/tabindex-no-positive': 'warn',

// === React additional rules ===
'react/jsx-curly-brace-presence': 'error',
'react/jsx-filename-extension': ['error', { extensions: ['.tsx', '.jsx'] }],
'react/jsx-fragments': ['error', 'syntax'],
'react/jsx-no-useless-fragment': 'error',
'react/jsx-pascal-case': ['warn', { allowAllCaps: true }],
'react/no-did-mount-set-state': 'error',
'react/no-redundant-should-component-update': 'error',
'react/no-this-in-sfc': 'error',
'react/no-will-update-set-state': 'error',
'react/prefer-es6-class': 'error',
'react/style-prop-object': 'error',
'react/void-dom-elements-no-children': 'error',

// === TypeScript ===
'typescript/dot-notation': 'error',
'typescript/no-duplicate-enum-values': 'error',
'typescript/no-empty-object-type': 'error',
'typescript/no-extra-non-null-assertion': 'error',
'typescript/no-misused-new': 'error',
'typescript/no-namespace': 'error',
'typescript/no-non-null-asserted-optional-chain': 'error',
'typescript/no-require-imports': 'error',
'typescript/no-this-alias': 'error',
'typescript/no-unnecessary-type-constraint': 'error',
'typescript/no-unsafe-declaration-merging': 'error',
'typescript/no-unsafe-function-type': 'error',
'typescript/no-wrapper-object-types': 'error',
'typescript/prefer-as-const': 'error',
'typescript/triple-slash-reference': 'error',
'typescript/array-type': ['error', { default: 'array-simple' }],
'typescript/unified-signatures': 'warn',
'typescript/consistent-type-imports': ['error', { fixStyle: 'inline-type-imports' }],
'typescript/no-import-type-side-effects': 'error',

// === eslint-plugin-smarthr ===
'eslint-plugin-smarthr/a11y-anchor-has-href-attribute': 'error',
'eslint-plugin-smarthr/a11y-aria-labelledby': 'warn',
'eslint-plugin-smarthr/a11y-clickable-element-has-text': 'error',
'eslint-plugin-smarthr/a11y-form-control-in-form': 'error',
'eslint-plugin-smarthr/a11y-heading-in-sectioning-content': 'error',
'eslint-plugin-smarthr/a11y-help-link-with-support-href': 'error',
'eslint-plugin-smarthr/a11y-image-has-alt-attribute': 'error',
'eslint-plugin-smarthr/a11y-input-has-name-attribute': 'error',
'eslint-plugin-smarthr/a11y-input-in-form-control': 'error',
'eslint-plugin-smarthr/a11y-numbered-text-within-ol': 'error',
'eslint-plugin-smarthr/a11y-prohibit-checkbox-or-radio-in-table-cell': 'error',
'eslint-plugin-smarthr/a11y-prohibit-input-maxlength-attribute': 'error',
'eslint-plugin-smarthr/a11y-prohibit-input-placeholder': 'error',
'eslint-plugin-smarthr/a11y-prohibit-overflow-hidden': 'warn',
'eslint-plugin-smarthr/a11y-prohibit-sectioning-content-in-form': 'error',
'eslint-plugin-smarthr/a11y-prohibit-useless-sectioning-fragment': 'error',
'eslint-plugin-smarthr/a11y-scroller-has-tabindex': 'warn',
'eslint-plugin-smarthr/a11y-trigger-has-button': 'error',
'eslint-plugin-smarthr/best-practice-for-async-current-target': 'error',
'eslint-plugin-smarthr/best-practice-for-button-element': 'error',
'eslint-plugin-smarthr/best-practice-for-date': 'error',
'eslint-plugin-smarthr/best-practice-for-data-test-attribute': 'off',
'eslint-plugin-smarthr/best-practice-for-interactive-element': 'error',
'eslint-plugin-smarthr/best-practice-for-layouts': 'error',
'eslint-plugin-smarthr/best-practice-for-nested-attributes-array-index': 'error',
'eslint-plugin-smarthr/best-practice-for-optional-chaining': 'error',
'eslint-plugin-smarthr/best-practice-for-prohibit-import-smarthr-ui-local': 'error',
'eslint-plugin-smarthr/best-practice-for-remote-trigger-dialog': 'error',
'eslint-plugin-smarthr/best-practice-for-rest-parameters': 'off',
'eslint-plugin-smarthr/best-practice-for-spread-syntax': ['error', { fix: true }],
'eslint-plugin-smarthr/best-practice-for-tailwind-prohibit-root-margin': 'off',
'eslint-plugin-smarthr/best-practice-for-tailwind-variants': 'off',
'eslint-plugin-smarthr/best-practice-for-text-component': 'warn',
'eslint-plugin-smarthr/best-practice-for-unnesessary-early-return': 'off',
'eslint-plugin-smarthr/component-name': 'error',
'eslint-plugin-smarthr/design-system-guideline-prohibit-dialog-button-icon': 'warn',
'eslint-plugin-smarthr/design-system-guideline-prohibit-double-icons': 'off',
'eslint-plugin-smarthr/format-import-path': 'off',
'eslint-plugin-smarthr/format-translate-component': 'off',
'eslint-plugin-smarthr/no-import-other-domain': 'off',
'eslint-plugin-smarthr/prohibit-export-array-type': 'error',
'eslint-plugin-smarthr/prohibit-file-name': 'off',
'eslint-plugin-smarthr/prohibit-import': 'off',
'eslint-plugin-smarthr/prohibit-path-within-template-literal': 'off',
'eslint-plugin-smarthr/require-barrel-import': 'error',
'eslint-plugin-smarthr/require-declaration': 'off',
'eslint-plugin-smarthr/require-export': 'off',
'eslint-plugin-smarthr/require-i18n-text': 'warn',
'eslint-plugin-smarthr/require-import': 'off',
'eslint-plugin-smarthr/trim-props': 'error',
},
}

export default config
35 changes: 35 additions & 0 deletions packages/oxlint-config-smarthr/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "oxlint-config-smarthr",
"version": "0.1.0",
"description": "A sharable oxlint config for SmartHR",
"type": "module",
"exports": {
".": {
"import": "./index.js"
}
},
"repository": {
"type": "git",
"url": "git+git@github.com:kufu/tamatebako.git",
"directory": "packages/oxlint-config-smarthr"
},
"scripts": {
"test": "oxlint --config index.js --print-config > /dev/null"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 単体テスト的なものですが、eslint-config-smarthr 側でやっているスナップショットの取得は、Oxlint がバイナリで動いてる都合再現できなかったので、設定ファイルの出力ができるかどうかだけを見ています。

少なくとも構文ミスや、存在しないプラグインの参照といったミスがある場合は失敗するようになってます。

},
"keywords": [
"oxlint"
],
"author": "SmartHR",
"license": "MIT",
"bugs": {
"url": "https://github.com/kufu/tamatebako/issues"
},
"homepage": "https://github.com/kufu/tamatebako/tree/master/packages/oxlint-config-smarthr",
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"peerDependencies": {
"oxlint": ">=1.0.0",
"eslint-plugin-smarthr": ">=6.0.0"
}
}
7 changes: 7 additions & 0 deletions packages/oxlint-config-smarthr/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
Loading
Loading