Skip to content

Commit b220ff5

Browse files
committed
feat: add new lint command
1 parent 2c78857 commit b220ff5

File tree

11 files changed

+2672
-305
lines changed

11 files changed

+2672
-305
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.type-coverage
12
.yarn/*
23
!.yarn/patches
34
!.yarn/plugins

doom.config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,11 @@ reference:
2020
sidebar:
2121
collapsed: false
2222
editRepoBaseUrl: alauda/doom/tree/main/docs
23+
lint:
24+
cspellOptions:
25+
cspell:
26+
words:
27+
- artifactcleanupruns
28+
- katanomi
29+
- testplans
30+
- testmodules

eslint.config.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,28 @@ export default config(
1212
eslint.configs.recommended,
1313
importX.flatConfigs.recommended,
1414
importX.flatConfigs.typescript,
15-
configs.eslintRecommended,
16-
...configs.strictTypeChecked,
1715
{
16+
files: ['**/*.{ts,tsx}'],
17+
extends: [configs.eslintRecommended, configs.strictTypeChecked],
1818
languageOptions: {
1919
parserOptions: {
2020
projectService: true,
2121
},
2222
},
23-
settings: {
24-
'import-x/resolver-next': [createTypeScriptImportResolver()],
25-
},
2623
rules: {
2724
'@typescript-eslint/no-misused-promises': 'off',
2825
'@typescript-eslint/no-non-null-assertion': 'off',
2926
'@typescript-eslint/restrict-template-expressions': [
3027
'error',
3128
{ allowNumber: true },
3229
],
30+
},
31+
},
32+
{
33+
settings: {
34+
'import-x/resolver-next': createTypeScriptImportResolver(),
35+
},
36+
rules: {
3337
'import-x/default': 'off',
3438
'import-x/first': 'error',
3539
'import-x/newline-after-import': 'error',
@@ -64,8 +68,4 @@ export default config(
6468
'prefer-const': ['error', { destructuring: 'all' }],
6569
},
6670
},
67-
{
68-
files: ['**/*.js'],
69-
...configs.disableTypeChecked,
70-
},
7171
)

fixture-docs/doom.config.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,18 @@ internalRoutes:
4646
- '*/internal/*.mdx'
4747
- '*/concepts/**'
4848
editRepoBaseUrl: alauda/doom/tree/main/fixture-docs
49+
lint:
50+
cspellOptions:
51+
cspell:
52+
words:
53+
- gatewayview
54+
- netrelationship
55+
- openshiftpipelinesascodes
56+
- tekton
57+
- tektonchains
58+
- tektonconfigs
59+
- tektonhubs
60+
- tektoninstallersets
61+
- tektonpipelines
62+
- tektonresults
63+
- tektontriggers

package.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"fixture": "yarn dev fixture-docs",
4646
"format": "prettier --write .",
4747
"lint": "run-p 'lint:*'",
48+
"lint:doom": "yarn doom lint",
4849
"lint:es": "eslint . --cache",
4950
"lint:tsc": "tsc -b",
5051
"prepare": "simple-git-hooks && patch-package && yarn-berry-deduplicate || exit 0",
@@ -56,19 +57,24 @@
5657
"version": "changeset version && yarn --no-immutable"
5758
},
5859
"dependencies": {
60+
"@cspell/eslint-plugin": "^9.0.1",
61+
"@eslint-react/eslint-plugin": "^1.49.0",
5962
"@inquirer/prompts": "^7.5.1",
6063
"@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0",
6164
"@playwright/browser-chromium": "^1.52.0",
6265
"@rsbuild/plugin-yaml": "^1.0.2",
63-
"@rspress/core": "1.43.13",
64-
"@shikijs/transformers": "^3.4.0",
66+
"@rspress/core": "1.44.0",
67+
"@shikijs/transformers": "^3.4.2",
6568
"chokidar": "^4.0.3",
6669
"cli-progress": "^3.12.0",
6770
"clsx": "^2.1.1",
6871
"commander": "^13.1.0",
6972
"ejs": "^3.1.10",
7073
"es-toolkit": "^1.37.2",
74+
"eslint": "^9.26.0",
75+
"eslint-plugin-mdx": "^3.4.2",
7176
"html-tag-names": "^2.1.0",
77+
"mdast-util-mdx": "^2.0.1",
7278
"mermaid": "^11.6.0",
7379
"openai": "^5.0.0-beta.0",
7480
"openapi-types": "^12.1.3",
@@ -82,18 +88,20 @@
8288
"remark-directive": "^2.0.1",
8389
"remark-frontmatter": "^4.0.1",
8490
"remark-gfm": "^3.0.1",
85-
"shiki": "^3.4.0",
91+
"remark-stringify": "^10.0.3",
92+
"shiki": "^3.4.2",
8693
"simple-git": "^3.27.0",
8794
"swagger2openapi": "^7.0.8",
8895
"tinyglobby": "^0.2.13",
8996
"type-fest": "^4.41.0",
97+
"typescript-eslint": "^8.32.1",
9098
"x-fetch": "^0.2.6",
91-
"yaml": "^2.7.1",
99+
"yaml": "^2.8.0",
92100
"yoctocolors": "^2.1.1"
93101
},
94102
"devDependencies": {
95103
"@changesets/changelog-github": "^0.5.1",
96-
"@changesets/cli": "^2.29.3",
104+
"@changesets/cli": "^2.29.4",
97105
"@eslint/js": "^9.26.0",
98106
"@swc-node/register": "^1.10.10",
99107
"@swc/core": "1.11.24",
@@ -107,7 +115,6 @@
107115
"@types/swagger2openapi": "^7.0.4",
108116
"@unts/patch-package": "^8.1.1",
109117
"clean-pkg-json": "^1.3.0",
110-
"eslint": "^9.26.0",
111118
"eslint-import-resolver-typescript": "^4.3.4",
112119
"eslint-plugin-import-x": "^4.11.1",
113120
"nano-staged": "^0.8.0",
@@ -118,7 +125,6 @@
118125
"simple-git-hooks": "^2.13.0",
119126
"type-coverage": "^2.29.7",
120127
"typescript": "^5.8.3",
121-
"typescript-eslint": "^8.32.0",
122128
"yarn-berry-deduplicate": "^6.1.3"
123129
},
124130
"publishConfig": {

src/cli/helpers.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import fs from 'node:fs/promises'
22

33
import { glob } from 'tinyglobby'
4+
import { xfetch } from 'x-fetch'
5+
import { parse } from 'yaml'
46

57
import { FALSY_VALUES } from '../shared/index.js'
8+
import type { NormalizedTermItem } from '../terms.js'
69

710
export const parseBoolean = (value: string) =>
811
!!value && !FALSY_VALUES.has(value)
@@ -48,3 +51,15 @@ export const defaultGitHubUrl = (url: string) =>
4851
/^https?:\/\//.test(url)
4952
? url
5053
: `https://github.com/${url.replace(/^(\/*github.com)?\/+/i, '')}`
54+
55+
const parseTerms_ = async () => {
56+
const terms = await xfetch(
57+
'https://gitlab-ce.alauda.cn/alauda-public/product-doc-guide/-/raw/main/terms.yaml',
58+
{ type: 'text' },
59+
)
60+
return parse(terms) as NormalizedTermItem[]
61+
}
62+
63+
let parsedTermsCache: Promise<NormalizedTermItem[]> | undefined
64+
65+
export const parseTerms = () => (parsedTermsCache ??= parseTerms_())

src/cli/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { setNodeEnv } from '../utils/index.js'
2020
import { CWD, DEFAULT_CONFIGS, I18N_FILE, SITES_FILE } from './constants.js'
2121
import { exportCommand } from './export.js'
2222
import { parseBoolean } from './helpers.js'
23+
import { lintCommand } from './lint.js'
2324
import { loadConfig } from './load-config.js'
2425
import { newCommand } from './new.js'
2526
import { translateCommand } from './translate.js'
@@ -253,6 +254,7 @@ program
253254
program.addCommand(newCommand)
254255
program.addCommand(translateCommand)
255256
program.addCommand(exportCommand)
257+
program.addCommand(lintCommand)
256258

257259
program.parseAsync().catch((err: unknown) => {
258260
if (err instanceof Error && err.name === 'ExitPromptError') {

src/cli/lint.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type { Options } from '@cspell/eslint-plugin'
2+
import cspellRecommended from '@cspell/eslint-plugin/recommended'
3+
import js from '@eslint/js'
4+
import react from '@eslint-react/eslint-plugin'
5+
import { Command } from 'commander'
6+
import { merge } from 'es-toolkit/compat'
7+
import { ESLint } from 'eslint'
8+
import * as mdx from 'eslint-plugin-mdx'
9+
import tseslint from 'typescript-eslint'
10+
11+
import type { GlobalCliOptions } from '../types.js'
12+
13+
import { parseTerms } from './helpers.js'
14+
import { loadConfig } from './load-config.js'
15+
16+
export const lintCommand = new Command('lint')
17+
.description('Lint the documentation')
18+
.argument('[root]', 'Root directory of the documentation')
19+
.action(async function (root?: string) {
20+
const globalOptions = this.optsWithGlobals<GlobalCliOptions>()
21+
22+
const { config } = await loadConfig(root, globalOptions)
23+
24+
const docsDir = config.root!
25+
26+
const parsedTerms = await parseTerms()
27+
28+
const eslint = new ESLint({
29+
cwd: docsDir,
30+
// @ts-expect-error -- stronger types
31+
baseConfig: tseslint.config([
32+
{
33+
extends: [
34+
js.configs.recommended,
35+
react.configs.recommended,
36+
mdx.configs.flat,
37+
],
38+
},
39+
{
40+
files: ['**/docs/en/**/*.{js,jsx,md,mdx,ts,tsx}'],
41+
extends: [cspellRecommended],
42+
rules: {
43+
'@cspell/spellchecker': [
44+
'error',
45+
merge(
46+
{
47+
autoFix: true,
48+
cspell: {
49+
words: parsedTerms.map((it) => it.en),
50+
flagWords: parsedTerms.flatMap(
51+
({ badCases }) => badCases?.en ?? [],
52+
),
53+
},
54+
} satisfies Partial<Options>,
55+
config.lint?.cspellOptions,
56+
),
57+
],
58+
},
59+
},
60+
// https://github.com/eslint/eslint/issues/19722
61+
// {
62+
// files: ['**/*.{ts,tsx}'],
63+
// extends: [
64+
// tseslint.configs.recommendedTypeChecked,
65+
// react.configs['recommended-typescript'],
66+
// ],
67+
// rules: {
68+
// '@typescript-eslint/no-misused-promises': 'off',
69+
// '@typescript-eslint/no-non-null-assertion': 'off',
70+
// '@typescript-eslint/restrict-template-expressions': [
71+
// 'error',
72+
// { allowNumber: true },
73+
// ],
74+
// 'prefer-const': ['error', { destructuring: 'all' }],
75+
// },
76+
// languageOptions: {
77+
// parser: tseslint.parser,
78+
// parserOptions: {
79+
// projectService: true,
80+
// },
81+
// },
82+
// },
83+
]),
84+
})
85+
86+
const results = await eslint.lintFiles('**/*.{js,jsx,ts,tsx,md,mdx}')
87+
88+
if (!results.length) {
89+
return
90+
}
91+
92+
process.exitCode = 1
93+
94+
const formatter = await eslint.loadFormatter('stylish')
95+
96+
process.stderr.write(await formatter.format(results))
97+
})

src/cli/translate.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import matter from 'gray-matter'
1313
import { AzureOpenAI, RateLimitError } from 'openai'
1414
import { pRateLimit } from 'p-ratelimit'
1515
import { glob } from 'tinyglobby'
16-
import { xfetch } from 'x-fetch'
17-
import { parse } from 'yaml'
1816
import { cyan, red } from 'yoctocolors'
1917

2018
import {
@@ -24,14 +22,14 @@ import {
2422
type NormalizeImgSrcOptions,
2523
} from '../plugins/index.js'
2624
import { Language, SUPPORTED_LANGUAGES } from '../shared/index.js'
27-
import type { NormalizedTermItem } from '../terms.js'
2825
import type { GlobalCliOptions, TranslateOptions } from '../types.js'
2926
import { pathExists } from '../utils/index.js'
3027

3128
import {
3229
escapeMarkdownHeadingIds,
3330
getMatchedDocFilePaths,
3431
parseBoolean,
32+
parseTerms,
3533
} from './helpers.js'
3634
import { loadConfig } from './load-config.js'
3735

@@ -98,11 +96,7 @@ export interface InternalTranslateOptions extends TranslateOptions {
9896
}
9997

10098
const resolveTerms_ = async () => {
101-
const terms = await xfetch(
102-
'https://gitlab-ce.alauda.cn/alauda-public/product-doc-guide/-/raw/main/terms.yaml',
103-
{ type: 'text' },
104-
)
105-
const parsedTerms = parse(terms) as NormalizedTermItem[]
99+
const parsedTerms = await parseTerms()
106100
return (
107101
'- 以下是常见的相关术语词汇对应表(English <-> 中文)\n' +
108102
parsedTerms.map(({ en, zh = en }) => ` * ${en} <-> ${zh}`).join('\n')

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Options } from '@cspell/eslint-plugin'
12
import type { serve } from '@rspress/core'
23

34
import type {
@@ -37,6 +38,10 @@ export interface TranslateOptions {
3738
userPrompt?: string
3839
}
3940

41+
export interface LintOptions {
42+
cspellOptions?: Partial<Options>
43+
}
44+
4045
declare module '@rspress/shared' {
4146
interface UserConfig {
4247
prefix?: string
@@ -51,6 +56,7 @@ declare module '@rspress/shared' {
5156
translate?: TranslateOptions
5257
shiki?: PluginShikiOptions
5358
editRepoBaseUrl?: string
59+
lint?: LintOptions
5460
}
5561

5662
interface SiteData {

0 commit comments

Comments
 (0)