Skip to content

Commit adacfa2

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/npm_and_yarn-47c4268404
2 parents 461684d + 935b096 commit adacfa2

5 files changed

Lines changed: 139 additions & 16 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@alauda/doom": patch
3+
---
4+
5+
Declare Doom's MDX global components in the built-in ESLint config so `doom lint` does not report them as undefined JSX identifiers.

packages/doom/src/eslint.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import fs from 'node:fs'
12
import { createRequire } from 'node:module'
3+
import path from 'node:path'
24
import { fileURLToPath } from 'node:url'
35

46
import type { Options } from '@cspell/eslint-plugin'
@@ -13,17 +15,43 @@ import globals from 'globals'
1315
import tseslint from 'typescript-eslint'
1416

1517
import { loadConfig } from './cli/load-config.js'
18+
import { getGlobalComponentNames } from './plugins/global/components.js'
19+
import { pkgResolve } from './utils/index.js'
1620

1721
const cjsRequire = createRequire(import.meta.url)
1822

19-
let remarkConfigPath: string
23+
let remarkConfigPath: string | undefined
24+
25+
const isLoadableRemarkConfigPath = (filepath: string) =>
26+
['.cjs', '.js', '.mjs'].includes(path.extname(filepath))
2027

2128
try {
22-
remarkConfigPath = fileURLToPath(import.meta.resolve('./remarkrc.js'))
29+
const resolvedRemarkConfigPath = fileURLToPath(
30+
import.meta.resolve('./remarkrc.js'),
31+
)
32+
if (isLoadableRemarkConfigPath(resolvedRemarkConfigPath)) {
33+
remarkConfigPath = resolvedRemarkConfigPath
34+
}
2335
} catch {
24-
remarkConfigPath = cjsRequire.resolve('./remarkrc.js')
36+
try {
37+
const resolvedRemarkConfigPath = cjsRequire.resolve('./remarkrc.js')
38+
if (isLoadableRemarkConfigPath(resolvedRemarkConfigPath)) {
39+
remarkConfigPath = resolvedRemarkConfigPath
40+
}
41+
} catch {
42+
remarkConfigPath = undefined
43+
}
44+
}
45+
46+
const builtRemarkConfigPath = pkgResolve('lib/remarkrc.js')
47+
if (!remarkConfigPath && fs.existsSync(builtRemarkConfigPath)) {
48+
remarkConfigPath = builtRemarkConfigPath
2549
}
2650

51+
const globalComponentGlobals = Object.fromEntries(
52+
getGlobalComponentNames().map((name) => [name, 'readonly']),
53+
) as Record<string, 'readonly'>
54+
2755
async function doom(
2856
userConfig?: UserConfig | null,
2957
): Promise<tseslint.ConfigArray>
@@ -64,7 +92,7 @@ async function doom(userConfigOrRoot?: UserConfig | string | null | URL) {
6492
languageOptions: {
6593
globals: globals.browser,
6694
parserOptions: {
67-
remarkConfigPath,
95+
...(remarkConfigPath && { remarkConfigPath }),
6896
},
6997
},
7098
rules: cspellEnabled
@@ -81,6 +109,9 @@ async function doom(userConfigOrRoot?: UserConfig | string | null | URL) {
81109
},
82110
{
83111
files: ['**/*.mdx'],
112+
languageOptions: {
113+
globals: globalComponentGlobals,
114+
},
84115
rules: {
85116
'@eslint-react/jsx-no-children-prop': 'off',
86117
'@eslint-react/rules-of-hooks': 'off',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
4+
import { baseResolve } from '../../utils/index.ts'
5+
6+
const componentsDir = baseResolve('runtime/components')
7+
8+
const isGlobalComponentFile = (file: string) => {
9+
const basename = path.basename(file, path.extname(file))
10+
return (
11+
!basename.startsWith('_') &&
12+
!basename.endsWith('.d') &&
13+
basename !== 'index'
14+
)
15+
}
16+
17+
export const getGlobalComponentFiles = () =>
18+
fs
19+
.readdirSync(componentsDir)
20+
.filter(isGlobalComponentFile)
21+
.map((file) => path.resolve(componentsDir, file))
22+
23+
export const getGlobalComponentNames = () =>
24+
fs
25+
.readdirSync(componentsDir)
26+
.filter(isGlobalComponentFile)
27+
.map((file) => path.basename(file, path.extname(file)))

packages/doom/src/plugins/global/index.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import { ACP_BASE, type DoomSite } from '../../shared/index.ts'
88
import type { ExportItem } from '../../types.ts'
99
import { baseResolve, pkgResolve } from '../../utils/index.ts'
1010

11+
import { getGlobalComponentFiles } from './components.ts'
12+
1113
const globalComponentsDir = baseResolve('global')
12-
const componentsDir = baseResolve('runtime/components')
1314

1415
export interface GlobalPluginOptions {
1516
version?: string
@@ -39,17 +40,7 @@ export const globalPlugin = ({
3940
.readdirSync(globalComponentsDir, 'utf8')
4041
.map((component) => path.resolve(globalComponentsDir, component)),
4142
markdown: {
42-
globalComponents: fs
43-
.readdirSync(componentsDir)
44-
.filter((file) => {
45-
const basename = path.basename(file, path.extname(file))
46-
return (
47-
!basename.startsWith('_') &&
48-
!basename.endsWith('.d') &&
49-
basename !== 'index'
50-
)
51-
})
52-
.map((file) => path.resolve(componentsDir, file)),
43+
globalComponents: getGlobalComponentFiles(),
5344
},
5445
addRuntimeModules(config, isProd) {
5546
return {

packages/doom/test/eslint.spec.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, expect, test } from '@rstest/core'
2+
import { ESLint } from 'eslint'
3+
4+
import doom from '#eslint.ts'
5+
6+
type ESLintOptions = NonNullable<ConstructorParameters<typeof ESLint>[0]>
7+
8+
const lintMdx = async (value: string) => {
9+
const overrideConfig = [
10+
...(await doom(null)),
11+
{
12+
files: ['**/*.mdx'],
13+
languageOptions: {
14+
parserOptions: {
15+
ecmaFeatures: {
16+
jsx: true,
17+
},
18+
ignoreRemarkConfig: true,
19+
},
20+
},
21+
rules: {
22+
'mdx/remark': 'off',
23+
},
24+
},
25+
] as unknown as ESLintOptions['overrideConfig']
26+
27+
const eslint = new ESLint({
28+
cwd: process.cwd(),
29+
overrideConfigFile: true,
30+
overrideConfig,
31+
})
32+
33+
const [result] = await eslint.lintText(value, {
34+
filePath: 'docs/en/test.mdx',
35+
})
36+
37+
return result.messages
38+
}
39+
40+
describe('doom eslint config', () => {
41+
test('allows doom global MDX components without local imports', async () => {
42+
const messages = await lintMdx(
43+
[
44+
'# Title',
45+
'',
46+
'<Overview />',
47+
'<Steps>',
48+
' <Term name="product" />',
49+
'</Steps>',
50+
'',
51+
].join('\n'),
52+
)
53+
54+
expect(messages).not.toContainEqual(
55+
expect.objectContaining({ ruleId: 'no-undef' }),
56+
)
57+
})
58+
59+
test('still reports unknown MDX component references', async () => {
60+
const messages = await lintMdx('# Title\n\n<UnknownComponent />\n')
61+
62+
expect(messages).toContainEqual(
63+
expect.objectContaining({
64+
ruleId: 'no-undef',
65+
message: "'UnknownComponent' is not defined.",
66+
}),
67+
)
68+
})
69+
})

0 commit comments

Comments
 (0)