@mouse_484/oxc-config 0.2.0 → 0.4.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mouse_484/oxc-config",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.4.0",
5
5
  "author": "mouse_484",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/mouse484/config/tree/main/packages/oxc",
@@ -13,13 +13,23 @@
13
13
  "url": "https://github.com/mouse484/config/issues"
14
14
  },
15
15
  "main": "src/index.js",
16
+ "types": "src/index.d.ts",
17
+ "scripts": {
18
+ "typegen": "node scripts/typegen.js"
19
+ },
16
20
  "dependencies": {
17
21
  "@stylistic/eslint-plugin": "^5.10.0",
22
+ "defu": "^6.1.7",
23
+ "eslint-plugin-antfu": "^3.2.3",
24
+ "eslint-plugin-better-tailwindcss": "^4.7.0",
18
25
  "eslint-plugin-perfectionist": "^5.10.0",
19
26
  "oxlint": "^1.76.0",
20
27
  "oxlint-tsgolint": "^7.0.2001"
21
28
  },
22
29
  "publishConfig": {
23
30
  "access": "public"
31
+ },
32
+ "devDependencies": {
33
+ "ts-morph": "^28.0.0"
24
34
  }
25
35
  }
@@ -0,0 +1,84 @@
1
+ import { IndentationText, Project, ts } from 'ts-morph'
2
+
3
+ const project = new Project({
4
+ tsConfigFilePath: 'jsconfig.json',
5
+ skipAddingFilesFromTsConfig: true,
6
+ manipulationSettings: {
7
+ indentationText: IndentationText.TwoSpaces,
8
+ },
9
+ })
10
+
11
+ const sourceFile = project.addSourceFileAtPath('src/configs/index.js')
12
+ const dtsFile = project.addSourceFileAtPath('src/configs/index.d.ts')
13
+ const variable = sourceFile.getVariableDeclarationOrThrow('CONFIGS')
14
+
15
+ const importSpecifiers = /** @type {Map<string, string>} */ (new Map())
16
+ for (const declaration of sourceFile.getImportDeclarations()) {
17
+ const name = declaration.getDefaultImport()?.getText()
18
+ if (name !== undefined) {
19
+ importSpecifiers.set(name, declaration.getModuleSpecifierValue())
20
+ }
21
+ }
22
+
23
+ function getConfigLiteral(/** @type {string} */ importedName) {
24
+ const moduleSpecifier = importSpecifiers.get(importedName)
25
+ if (moduleSpecifier === undefined) {
26
+ return {}
27
+ }
28
+ const file = project.addSourceFileAtPath(`src/configs/${moduleSpecifier.replace(/^\.\//, '')}`)
29
+ const literal = file
30
+ .getDescendants()
31
+ .map(node => node.asKind(ts.SyntaxKind.ObjectLiteralExpression))
32
+ .find(node => node?.getProperty('name') !== undefined)
33
+ if (!literal) {
34
+ return {}
35
+ }
36
+ return {
37
+ defaultEnabled: literal.getProperty('defaultEnabled')
38
+ ?.asKind(ts.SyntaxKind.PropertyAssignment)
39
+ ?.getInitializer()
40
+ ?.getText(),
41
+ options: literal.getProperty('options')
42
+ ?.asKind(ts.SyntaxKind.PropertyAssignment)
43
+ ?.getInitializer()
44
+ ?.getText(),
45
+ }
46
+ }
47
+
48
+ dtsFile.getInterface('Options')?.remove()
49
+
50
+ dtsFile.addInterface({
51
+ name: 'Options',
52
+ isExported: true,
53
+ properties: variable
54
+ .getType()
55
+ .getArrayElementTypeOrThrow()
56
+ .getUnionTypes()
57
+ .map((node) => {
58
+ const name = node.getPropertyOrThrow('name')
59
+ .getTypeAtLocation(variable)
60
+ .getText()
61
+ .replaceAll(/^"|"$/g, '')
62
+ const options = node.getProperty('options')
63
+ ?.getTypeAtLocation(variable)
64
+ .getConstraint()
65
+ const { defaultEnabled, options: defaultOptions } = getConfigLiteral(name)
66
+ const tags = [
67
+ { tagName: 'default', text: defaultEnabled ?? 'true' },
68
+ ]
69
+ if (defaultOptions !== undefined) {
70
+ tags.push({ tagName: 'options', text: defaultOptions })
71
+ }
72
+ return {
73
+ name,
74
+ type: options ? `boolean | ${options.getText()}` : 'boolean',
75
+ hasQuestionToken: true,
76
+ docs: [{
77
+ description: `\`${name}\` option`,
78
+ tags,
79
+ }],
80
+ }
81
+ }),
82
+ })
83
+
84
+ dtsFile.saveSync()
@@ -1,19 +1,24 @@
1
- import { defineConfig } from 'oxlint'
1
+ import { createConfigs } from '../lib/factory.js'
2
2
 
3
- export default defineConfig({
4
- rules: {
5
- 'eslint/id-length': ['off'],
6
- 'eslint/func-style': ['error', 'declaration', { allowArrowFunctions: true }],
7
- 'eslint/no-magic-numbers': ['error', { ignore: [0, 1, -1] }],
8
- 'eslint/no-undefined': ['off'],
9
- 'eslint/no-ternary': ['off'],
10
- 'eslint/no-nested-ternary': ['error'],
11
- 'eslint/no-unneeded-ternary': ['error'],
12
- 'eslint/no-void': ['error', { allowAsStatement: true }],
13
- /**
14
- * Prefer `perfectionist` plugin for sorting
15
- */
16
- 'eslint/sort-imports': ['off'],
17
- 'eslint/sort-keys': ['off'],
18
- },
3
+ export default createConfigs({
4
+ name: 'eslint',
5
+ configs: [
6
+ {
7
+ rules: {
8
+ 'eslint/id-length': 'off',
9
+ 'eslint/func-style': ['error', 'declaration', { allowArrowFunctions: true }],
10
+ 'eslint/no-magic-numbers': ['error', { ignore: [0, 1, -1] }],
11
+ 'eslint/no-undefined': 'off',
12
+ 'eslint/no-ternary': 'off',
13
+ 'eslint/no-nested-ternary': 'error',
14
+ 'eslint/no-unneeded-ternary': 'error',
15
+ 'eslint/no-void': ['error', { allowAsStatement: true }],
16
+ /**
17
+ * Prefer `perfectionist` plugin for sorting
18
+ */
19
+ 'eslint/sort-imports': 'off',
20
+ 'eslint/sort-keys': 'off',
21
+ },
22
+ },
23
+ ],
19
24
  })
@@ -0,0 +1,52 @@
1
+ export declare function buildConfigs(options: Options): OxlintConfig[]
2
+
3
+ export interface Options {
4
+ /**
5
+ * `eslint` option
6
+ * @default true
7
+ */
8
+ eslint?: boolean
9
+ /**
10
+ * `opinionated` option
11
+ * @default true
12
+ */
13
+ opinionated?: boolean
14
+ /**
15
+ * `oxc` option
16
+ * @default true
17
+ */
18
+ oxc?: boolean
19
+ /**
20
+ * `perfectionist` option
21
+ * @default true
22
+ */
23
+ perfectionist?: boolean
24
+ /**
25
+ * `stylistic` option
26
+ * @default true
27
+ */
28
+ stylistic?: boolean
29
+ /**
30
+ * `tailwind` option
31
+ * @default false
32
+ * @options {
33
+ * entryPoint: 'src/global.css',
34
+ * }
35
+ */
36
+ tailwind?: boolean | { entryPoint: string } | undefined
37
+ /**
38
+ * `tanstackRouter` option
39
+ * @default false
40
+ */
41
+ tanstackRouter?: boolean
42
+ /**
43
+ * `typeAware` option
44
+ * @default true
45
+ */
46
+ typeAware?: boolean
47
+ /**
48
+ * `typescript` option
49
+ * @default true
50
+ */
51
+ typescript?: boolean
52
+ }
@@ -1,29 +1,37 @@
1
+ import defu from 'defu'
1
2
  import eslint from './eslint.js'
3
+ import opinionated from './opinionated.js'
2
4
  import oxc from './oxc.js'
3
5
  import perfectionist from './perfectionist.js'
4
6
  import stylistic from './stylistic.js'
7
+ import tailwind from './tailwind.js'
5
8
  import tanstackRouter from './tanstack-router.js'
6
9
  import typeAware from './type-aware.js'
7
10
  import typescript from './typescript.js'
8
11
 
9
- /**
10
- * @satisfies {Record<string, [boolean, import('oxlint').OxlintConfig ]>}
11
- */
12
- const _configs = {
13
- eslint: [true, eslint],
14
- oxc: [true, oxc],
15
- perfectionist: [true, perfectionist],
16
- stylistic: [true, stylistic],
17
- tanstackRouter: [false, tanstackRouter],
18
- typeAware: [true, typeAware],
19
- typescript: [true, typescript],
20
- }
21
-
22
- /** @typedef {Partial<Record<keyof typeof _configs, boolean>>} ConfigOptions */
12
+ const CONFIGS = /** @type {const} */[
13
+ eslint,
14
+ opinionated,
15
+ oxc,
16
+ perfectionist,
17
+ stylistic,
18
+ tailwind,
19
+ tanstackRouter,
20
+ typeAware,
21
+ typescript,
22
+ ]
23
23
 
24
- /** @param {ConfigOptions} [options] */
25
- export default function configs(options = {}) {
26
- return Object.entries(_configs).flatMap(([name, [defaultEnabled, config]]) => {
27
- return (Object.hasOwn(options, name) ? options[/** @type {keyof ConfigOptions} */ (name)] : defaultEnabled) ? [config] : []
24
+ /** @type {import(".").buildConfigs} */
25
+ export default function buildConfigs(options) {
26
+ const configs = CONFIGS.flatMap(({ name, build, defaultEnabled, options: defaultOptions }) => {
27
+ const option = options[name]
28
+ if (option === false || (option === undefined && defaultEnabled === false)) {
29
+ return []
30
+ }
31
+ // @ts-expect-error - build configs with options
32
+ // eslint-disable-next-line unicorn/prefer-minimal-ternary
33
+ return typeof option === 'object' ? build(defu(option, defaultOptions)) : build(defaultOptions)
28
34
  })
35
+
36
+ return configs
29
37
  }
@@ -0,0 +1,17 @@
1
+ import { createConfigs } from '../lib/factory.js'
2
+
3
+ export default createConfigs({
4
+ name: 'opinionated',
5
+ configs: [
6
+ {
7
+ jsPlugins: ['eslint-plugin-antfu'],
8
+ rules: {
9
+ 'antfu/top-level-function': 'error',
10
+ 'antfu/consistent-chaining': 'error',
11
+ 'antfu/import-dedupe': 'error',
12
+ 'antfu/indent-unindent': ['error', { indent: 2 }],
13
+ 'antfu/no-ts-export-equal': 'error',
14
+ },
15
+ },
16
+ ],
17
+ })
@@ -1,8 +1,13 @@
1
- import { defineConfig } from 'oxlint'
1
+ import { createConfigs } from '../lib/factory.js'
2
2
 
3
- export default defineConfig({
4
- rules: {
5
- 'oxc/no-async-await': ['off'],
6
- 'oxc/no-rest-spread-properties': ['off'],
7
- },
3
+ export default createConfigs({
4
+ name: 'oxc',
5
+ configs: [
6
+ {
7
+ rules: {
8
+ 'oxc/no-async-await': 'off',
9
+ 'oxc/no-rest-spread-properties': 'off',
10
+ },
11
+ },
12
+ ],
8
13
  })
@@ -1,9 +1,14 @@
1
1
  import perfectionist from 'eslint-plugin-perfectionist'
2
- import { defineConfig } from 'oxlint'
2
+ import { createConfigs } from '../lib/factory.js'
3
3
 
4
- export default defineConfig({
5
- jsPlugins: ['eslint-plugin-perfectionist'],
6
- rules: {
7
- ...perfectionist.configs['recommended-natural'].rules,
8
- },
4
+ export default createConfigs({
5
+ name: 'perfectionist',
6
+ configs: [
7
+ {
8
+ jsPlugins: ['eslint-plugin-perfectionist'],
9
+ rules: {
10
+ ...perfectionist.configs['recommended-natural'].rules,
11
+ },
12
+ },
13
+ ],
9
14
  })
@@ -1,5 +1,5 @@
1
1
  import stylistic from '@stylistic/eslint-plugin'
2
- import { defineConfig } from 'oxlint'
2
+ import { createConfigs } from '../lib/factory.js'
3
3
 
4
4
  const { rules } = stylistic.configs.customize({
5
5
  indent: 2,
@@ -8,7 +8,12 @@ const { rules } = stylistic.configs.customize({
8
8
  jsx: true,
9
9
  })
10
10
 
11
- export default defineConfig({
12
- jsPlugins: ['@stylistic/eslint-plugin'],
13
- rules,
11
+ export default createConfigs ({
12
+ name: 'stylistic',
13
+ configs: [
14
+ {
15
+ jsPlugins: ['@stylistic/eslint-plugin'],
16
+ rules,
17
+ },
18
+ ],
14
19
  })
@@ -0,0 +1,28 @@
1
+ import eslintPluginBetterTailwindcss from 'eslint-plugin-better-tailwindcss'
2
+ import { createConfigs } from '../lib/factory.js'
3
+
4
+ export default createConfigs({
5
+ name: 'tailwind',
6
+ defaultEnabled: false,
7
+ options: {
8
+ entryPoint: 'src/global.css',
9
+ },
10
+ configs: ({ entryPoint }) => ([
11
+ {
12
+ jsPlugins: [
13
+ 'eslint-plugin-better-tailwindcss',
14
+ ],
15
+ rules: {
16
+ ...eslintPluginBetterTailwindcss.configs.recommended.rules,
17
+ 'better-tailwindcss/no-unknown-classes': ['error', {
18
+ detectComponentClasses: true,
19
+ }],
20
+ },
21
+ settings: {
22
+ 'better-tailwindcss': {
23
+ entryPoint,
24
+ },
25
+ },
26
+ },
27
+ ]),
28
+ })
@@ -1,10 +1,19 @@
1
- import { defineConfig } from 'oxlint'
1
+ import { createConfigs } from '../lib/factory.js'
2
2
 
3
- export default defineConfig({
4
- overrides: [{
5
- files: ['src/routes/**/*.tsx'],
6
- rules: {
7
- 'eslint/no-use-before-define': ['off'],
3
+ export default createConfigs({
4
+ defaultEnabled: false,
5
+ name: 'tanstackRouter',
6
+ configs: [
7
+ {
8
+ ignorePatterns: ['**/src/routeTree.gen.ts'],
9
+ overrides: [
10
+ {
11
+ files: ['src/routes/**/*.tsx'],
12
+ rules: {
13
+ 'eslint/no-use-before-define': 'off',
14
+ },
15
+ },
16
+ ],
8
17
  },
9
- }],
18
+ ],
10
19
  })
@@ -1,11 +1,16 @@
1
- import { defineConfig } from 'oxlint'
1
+ import { createConfigs } from '../lib/factory.js'
2
2
 
3
- export default defineConfig({
4
- options: {
5
- typeAware: true,
6
- typeCheck: true,
7
- },
8
- rules: {
9
- 'typescript/prefer-readonly-parameter-types': ['off'],
10
- },
3
+ export default createConfigs({
4
+ name: 'typeAware',
5
+ configs: [
6
+ {
7
+ options: {
8
+ typeAware: true,
9
+ typeCheck: true,
10
+ },
11
+ rules: {
12
+ 'typescript/prefer-readonly-parameter-types': 'off',
13
+ },
14
+ },
15
+ ],
11
16
  })
@@ -1,8 +1,13 @@
1
- import { defineConfig } from 'oxlint'
1
+ import { createConfigs } from '../lib/factory.js'
2
2
 
3
- export default defineConfig({
4
- rules: {
5
- 'typescript/explicit-function-return-type': ['off'],
6
- 'typescript/explicit-module-boundary-types': ['off'],
7
- },
3
+ export default createConfigs({
4
+ name: 'typescript',
5
+ configs: [
6
+ {
7
+ rules: {
8
+ 'typescript/explicit-function-return-type': 'off',
9
+ 'typescript/explicit-module-boundary-types': 'off',
10
+ },
11
+ },
12
+ ],
8
13
  })
package/src/index.d.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  import type { OxlintConfig } from 'oxlint'
2
- import type { ConfigOptions } from './configs/index.js'
2
+ import type { Options } from './configs'
3
3
 
4
- export type Options = ConfigOptions
4
+ declare function lint(options?: Options, config?: OxlintConfig): OxlintConfig
5
5
 
6
- declare function lint(options?: Options): OxlintConfig
7
-
8
- export {
9
- lint,
10
- }
6
+ export { lint }
7
+ export { type Options } from './configs'
package/src/index.js CHANGED
@@ -1,18 +1,24 @@
1
- import { defineConfig } from 'oxlint'
2
- import configs from './configs/index.js'
1
+ import defu from 'defu'
2
+ import buildConfigs from './configs/index.js'
3
3
 
4
- /** @param {import('.').Options} [options] */
5
- export function lint(options = {}) {
6
- return defineConfig({
7
- categories: {
8
- correctness: 'error',
9
- suspicious: 'warn',
10
- pedantic: 'warn',
11
- perf: 'warn',
12
- style: 'warn',
13
- restriction: 'warn',
14
- nursery: 'warn',
4
+ /** @type {import('.').lint} */
5
+ export function lint(options, config = {}) {
6
+ // @ts-expect-error - deep merge config with default config
7
+ return defu(
8
+ config,
9
+ {
10
+ categories: {
11
+ correctness: 'error',
12
+ suspicious: 'warn',
13
+ pedantic: 'warn',
14
+ perf: 'warn',
15
+ style: 'warn',
16
+ restriction: 'warn',
17
+ nursery: 'warn',
18
+ },
15
19
  },
16
- extends: configs(options),
17
- })
20
+ // @ts-expect-error - buildConfigs returns a Config[]
21
+ // eslint-disable-next-line ts/no-unsafe-argument
22
+ ...buildConfigs(options),
23
+ )
18
24
  }
@@ -0,0 +1,19 @@
1
+ import type { OxlintConfig } from 'oxlint'
2
+
3
+ declare function createConfigs<
4
+ TName extends string,
5
+ TOptions extends Record<string, unknown> | undefined = undefined,
6
+ >(
7
+ parameters: {
8
+ name: TName
9
+ defaultEnabled?: boolean
10
+ options?: TOptions
11
+ configs: OxlintConfig[] | ((options: TOptions) => OxlintConfig[])
12
+ }): {
13
+ name: TName
14
+ defaultEnabled?: boolean
15
+ options?: TOptions
16
+ build: (options?: TOptions) => OxlintConfig[]
17
+ }
18
+
19
+ export { createConfigs }
@@ -0,0 +1,18 @@
1
+ /** @type {import('./factory').createConfigs} */
2
+ export function createConfigs({ name, defaultEnabled, options, configs }) {
3
+ return {
4
+ name,
5
+ defaultEnabled,
6
+ options,
7
+ build: (overriddenOptions) => {
8
+ if (typeof configs === 'function') {
9
+ // @ts-expect-error - build configs
10
+ return configs({
11
+ ...options,
12
+ ...overriddenOptions,
13
+ })
14
+ }
15
+ return configs
16
+ },
17
+ }
18
+ }