Skip to content

Commit 2c54655

Browse files
ocavueljharb
authored andcommitted
[Fix] types: correct generated type declaration
- Add types/rules/jsx-no-literals.d.ts to avoid error TS2309: An export assignment cannot be used in a module with other exported elements.
1 parent 4ef92b4 commit 2c54655

20 files changed

+144
-89
lines changed

.github/workflows/type-check.yml

+18-2
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,34 @@ jobs:
4444
- name: build types
4545
run: npm run build-types
4646

47+
# Pack the lib into a tarball so that when we install the lib later in the
48+
# test-published-types directory, it's only install `dependencies` of the
49+
# lib.
50+
- name: pack the lib
51+
run: npm pack --pack-destination /tmp/
52+
53+
- name: find the packed lib
54+
run: echo "ESLINT_PLUGIN_REACT_PATH=$(ls /tmp/eslint-plugin-react*.tgz | tail -n 1)" >> $GITHUB_ENV
55+
56+
- name: show the path to the packed lib
57+
run: echo "$ESLINT_PLUGIN_REACT_PATH"
58+
4759
- name: npm install working directory
4860
run: npm install
4961
working-directory: test-published-types
5062

51-
- name: install typescript version ${{ matrix.ts_version }}
52-
run: npm install --no-save typescript@${{ matrix.ts_version }}
63+
- name: install eslint-plugin-react and typescript version ${{ matrix.ts_version }}
64+
run: npm install --no-save "$ESLINT_PLUGIN_REACT_PATH" typescript@${{ matrix.ts_version }}
5365
working-directory: test-published-types
5466

5567
- name: show installed typescript version
5668
run: npm list typescript --depth=0
5769
working-directory: test-published-types
5870

71+
- name: show installed eslint-plugin-react version
72+
run: npm list eslint-plugin-react --depth=0
73+
working-directory: test-published-types
74+
5975
- name: check types with lib "${{ matrix.ts_lib }}"
6076
run: npx tsc --lib ${{ matrix.ts_lib }}
6177
working-directory: test-published-types

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
88

99
### Fixed
1010
* [`no-danger`]: avoid a crash on a nested component name ([#3833][] @ljharb)
11+
* [Fix] types: correct generated type declaration ([#3840][] @ocavue)
1112

1213
### Changed
1314
* [Tests] [`jsx-no-script-url`]: Improve tests ([#3849][] @radu2147)
1415

1516
[#3849]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3849
17+
[#3840]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3840
1618
[#3833]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3833
1719

1820
## [7.37.2] - 2024.10.22

index.js

+29-25
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function filterRules(rules, predicate) {
1111

1212
/**
1313
* @param {object} rules - rules object mapping rule name to rule module
14-
* @returns {Record<string, 2 | 'error'>}
14+
* @returns {Record<string, SEVERITY_ERROR | 'error'>}
1515
*/
1616
function configureAsError(rules) {
1717
return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2]));
@@ -31,6 +31,10 @@ const plugins = [
3131
'react',
3232
];
3333

34+
// TODO: with TS 4.5+, inline this
35+
const SEVERITY_ERROR = /** @type {2} */ (2);
36+
const SEVERITY_OFF = /** @type {0} */ (0);
37+
3438
const configs = {
3539
recommended: {
3640
plugins,
@@ -40,28 +44,28 @@ const configs = {
4044
},
4145
},
4246
rules: {
43-
'react/display-name': 2,
44-
'react/jsx-key': 2,
45-
'react/jsx-no-comment-textnodes': 2,
46-
'react/jsx-no-duplicate-props': 2,
47-
'react/jsx-no-target-blank': 2,
48-
'react/jsx-no-undef': 2,
49-
'react/jsx-uses-react': 2,
50-
'react/jsx-uses-vars': 2,
51-
'react/no-children-prop': 2,
52-
'react/no-danger-with-children': 2,
53-
'react/no-deprecated': 2,
54-
'react/no-direct-mutation-state': 2,
55-
'react/no-find-dom-node': 2,
56-
'react/no-is-mounted': 2,
57-
'react/no-render-return-value': 2,
58-
'react/no-string-refs': 2,
59-
'react/no-unescaped-entities': 2,
60-
'react/no-unknown-property': 2,
61-
'react/no-unsafe': 0,
62-
'react/prop-types': 2,
63-
'react/react-in-jsx-scope': 2,
64-
'react/require-render-return': 2,
47+
'react/display-name': SEVERITY_ERROR,
48+
'react/jsx-key': SEVERITY_ERROR,
49+
'react/jsx-no-comment-textnodes': SEVERITY_ERROR,
50+
'react/jsx-no-duplicate-props': SEVERITY_ERROR,
51+
'react/jsx-no-target-blank': SEVERITY_ERROR,
52+
'react/jsx-no-undef': SEVERITY_ERROR,
53+
'react/jsx-uses-react': SEVERITY_ERROR,
54+
'react/jsx-uses-vars': SEVERITY_ERROR,
55+
'react/no-children-prop': SEVERITY_ERROR,
56+
'react/no-danger-with-children': SEVERITY_ERROR,
57+
'react/no-deprecated': SEVERITY_ERROR,
58+
'react/no-direct-mutation-state': SEVERITY_ERROR,
59+
'react/no-find-dom-node': SEVERITY_ERROR,
60+
'react/no-is-mounted': SEVERITY_ERROR,
61+
'react/no-render-return-value': SEVERITY_ERROR,
62+
'react/no-string-refs': SEVERITY_ERROR,
63+
'react/no-unescaped-entities': SEVERITY_ERROR,
64+
'react/no-unknown-property': SEVERITY_ERROR,
65+
'react/no-unsafe': SEVERITY_OFF,
66+
'react/prop-types': SEVERITY_ERROR,
67+
'react/react-in-jsx-scope': SEVERITY_ERROR,
68+
'react/require-render-return': SEVERITY_ERROR,
6569
},
6670
},
6771
all: {
@@ -82,8 +86,8 @@ const configs = {
8286
jsxPragma: null, // for @typescript/eslint-parser
8387
},
8488
rules: {
85-
'react/react-in-jsx-scope': 0,
86-
'react/jsx-uses-react': 0,
89+
'react/react-in-jsx-scope': SEVERITY_OFF,
90+
'react/jsx-uses-react': SEVERITY_OFF,
8791
},
8892
},
8993
};

lib/rules/forbid-foreign-prop-types.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ module.exports = {
109109
&& !ast.isAssignmentLHS(node)
110110
&& !isAllowedAssignment(node)
111111
)) || (
112-
// @ts-expect-error The JSXText type is not present in the estree type definitions
112+
// @ts-expect-error: Literal is not a valid type
113113
(node.property.type === 'Literal' || node.property.type === 'JSXText')
114114
&& 'value' in node.property
115115
&& node.property.value === 'propTypes'

lib/rules/forbid-prop-types.js

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ module.exports = {
195195
const propTypesSpecifier = node.specifiers.find((specifier) => (
196196
'imported' in specifier
197197
&& specifier.imported
198+
&& 'name' in specifier.imported
198199
&& specifier.imported.name === 'PropTypes'
199200
));
200201
if (propTypesSpecifier) {

lib/rules/forward-ref-uses-ref.js

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const messages = {
4141
removeForwardRef: 'Remove forwardRef wrapper',
4242
};
4343

44+
/** @type {import('eslint').Rule.RuleModule} */
4445
module.exports = {
4546
meta: {
4647
docs: {

lib/rules/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/* eslint global-require: 0 */
44

55
/** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
6-
module.exports = {
6+
const rules = {
77
'boolean-prop-naming': require('./boolean-prop-naming'),
88
'button-has-type': require('./button-has-type'),
99
'checked-requires-onchange-or-readonly': require('./checked-requires-onchange-or-readonly'),
@@ -108,3 +108,5 @@ module.exports = {
108108
'style-prop-object': require('./style-prop-object'),
109109
'void-dom-elements-no-children': require('./void-dom-elements-no-children'),
110110
};
111+
112+
module.exports = rules;

lib/rules/jsx-fragments.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,12 @@ module.exports = {
170170
ImportDeclaration(node) {
171171
if (node.source && node.source.value === 'react') {
172172
node.specifiers.forEach((spec) => {
173-
if ('imported' in spec && spec.imported && spec.imported.name === fragmentPragma) {
173+
if (
174+
'imported' in spec
175+
&& spec.imported
176+
&& 'name' in spec.imported
177+
&& spec.imported.name === fragmentPragma
178+
) {
174179
if (spec.local) {
175180
fragmentNames.add(spec.local.name);
176181
}

lib/rules/jsx-no-literals.js

+18-48
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ const docsUrl = require('../util/docsUrl');
1717
const report = require('../util/report');
1818
const getText = require('../util/eslint').getText;
1919

20+
/** @typedef {import('eslint').Rule.RuleModule} RuleModule */
21+
22+
/** @typedef {import('../../types/rules/jsx-no-literals').Config} Config */
23+
/** @typedef {import('../../types/rules/jsx-no-literals').RawConfig} RawConfig */
24+
/** @typedef {import('../../types/rules/jsx-no-literals').ResolvedConfig} ResolvedConfig */
25+
/** @typedef {import('../../types/rules/jsx-no-literals').OverrideConfig} OverrideConfig */
26+
/** @typedef {import('../../types/rules/jsx-no-literals').ElementConfig} ElementConfig */
27+
2028
// ------------------------------------------------------------------------------
2129
// Rule Definition
2230
// ------------------------------------------------------------------------------
@@ -45,7 +53,7 @@ const messages = {
4553
literalNotInJSXExpressionInElement: 'Missing JSX expression container around literal string: "{{text}}" in {{element}}',
4654
};
4755

48-
/** @type {Exclude<import('eslint').Rule.RuleModule['meta']['schema'], unknown[]>['properties']} */
56+
/** @type {Exclude<RuleModule['meta']['schema'], unknown[] | false>['properties']} */
4957
const commonPropertiesSchema = {
5058
noStrings: {
5159
type: 'boolean',
@@ -65,52 +73,7 @@ const commonPropertiesSchema = {
6573
},
6674
};
6775

68-
/**
69-
* @typedef RawElementConfigProperties
70-
* @property {boolean} [noStrings]
71-
* @property {string[]} [allowedStrings]
72-
* @property {boolean} [ignoreProps]
73-
* @property {boolean} [noAttributeStrings]
74-
*
75-
* @typedef RawOverrideConfigProperties
76-
* @property {boolean} [allowElement]
77-
* @property {boolean} [applyToNestedElements=true]
78-
*
79-
* @typedef {RawElementConfigProperties} RawElementConfig
80-
* @typedef {RawElementConfigProperties & RawElementConfigProperties} RawOverrideConfig
81-
*
82-
* @typedef RawElementOverrides
83-
* @property {Record<string, RawOverrideConfig>} [elementOverrides]
84-
*
85-
* @typedef {RawElementConfig & RawElementOverrides} RawConfig
86-
*
87-
* ----------------------------------------------------------------------
88-
*
89-
* @typedef ElementConfigType
90-
* @property {'element'} type
91-
*
92-
* @typedef ElementConfigProperties
93-
* @property {boolean} noStrings
94-
* @property {Set<string>} allowedStrings
95-
* @property {boolean} ignoreProps
96-
* @property {boolean} noAttributeStrings
97-
*
98-
* @typedef OverrideConfigProperties
99-
* @property {'override'} type
100-
* @property {string} name
101-
* @property {boolean} allowElement
102-
* @property {boolean} applyToNestedElements
103-
*
104-
* @typedef {ElementConfigType & ElementConfigProperties} ElementConfig
105-
* @typedef {OverrideConfigProperties & ElementConfigProperties} OverrideConfig
106-
*
107-
* @typedef ElementOverrides
108-
* @property {Record<string, OverrideConfig>} elementOverrides
109-
*
110-
* @typedef {ElementConfig & ElementOverrides} Config
111-
* @typedef {Config | OverrideConfig} ResolvedConfig
112-
*/
113-
76+
// eslint-disable-next-line valid-jsdoc
11477
/**
11578
* Normalizes the element portion of the config
11679
* @param {RawConfig} config
@@ -128,6 +91,7 @@ function normalizeElementConfig(config) {
12891
};
12992
}
13093

94+
// eslint-disable-next-line valid-jsdoc
13195
/**
13296
* Normalizes the config and applies default values to all config options
13397
* @param {RawConfig} config
@@ -182,8 +146,9 @@ const elementOverrides = {
182146
},
183147
};
184148

149+
/** @type {RuleModule} */
185150
module.exports = {
186-
meta: /** @type {import('eslint').Rule.RuleModule["meta"]} */ ({
151+
meta: /** @type {RuleModule['meta']} */ ({
187152
docs: {
188153
description: 'Disallow usage of string literals in JSX',
189154
category: 'Stylistic Issues',
@@ -339,6 +304,7 @@ module.exports = {
339304
return some(iterFrom([ancestors.parent, ancestors.grandParent]), (parent) => jsxElementTypes.has(parent.type));
340305
}
341306

307+
// eslint-disable-next-line valid-jsdoc
342308
/**
343309
* Determines whether a given node's value and its immediate parent are
344310
* viable text nodes that can/should be reported on
@@ -370,6 +336,7 @@ module.exports = {
370336
return isStandardJSXNode && parent.type !== 'JSXExpressionContainer';
371337
}
372338

339+
// eslint-disable-next-line valid-jsdoc
373340
/**
374341
* Gets an override config for a given node. For any given node, we also
375342
* need to traverse the ancestor tree to determine if an ancestor's config
@@ -408,6 +375,7 @@ module.exports = {
408375
}
409376
}
410377

378+
// eslint-disable-next-line valid-jsdoc
411379
/**
412380
* @param {ResolvedConfig} resolvedConfig
413381
* @returns {boolean}
@@ -416,6 +384,7 @@ module.exports = {
416384
return resolvedConfig.type === 'override' && 'allowElement' in resolvedConfig && !!resolvedConfig.allowElement;
417385
}
418386

387+
// eslint-disable-next-line valid-jsdoc
419388
/**
420389
* @param {boolean} ancestorIsJSXElement
421390
* @param {ResolvedConfig} resolvedConfig
@@ -433,6 +402,7 @@ module.exports = {
433402
return resolvedConfig.type === 'override' ? 'literalNotInJSXExpressionInElement' : 'literalNotInJSXExpression';
434403
}
435404

405+
// eslint-disable-next-line valid-jsdoc
436406
/**
437407
* @param {ASTNode} node
438408
* @param {string} messageId

lib/rules/jsx-props-no-spread-multi.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const messages = {
1616
noMultiSpreading: 'Spreading the same expression multiple times is forbidden',
1717
};
1818

19+
/** @type {import('eslint').Rule.RuleModule} */
1920
module.exports = {
2021
meta: {
2122
docs: {

lib/rules/jsx-space-before-closing.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module.exports = {
5959
const sourceCode = getSourceCode(context);
6060

6161
const leftToken = getTokenBeforeClosingBracket(node);
62-
const closingSlash = /** @type {import("eslint").AST.Token} */ (sourceCode.getTokenAfter(leftToken));
62+
const closingSlash = /** @type {import('eslint').AST.Token} */ (sourceCode.getTokenAfter(leftToken));
6363

6464
if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
6565
return;

lib/rules/no-access-state-in-setstate.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ module.exports = {
116116
&& node.object.type === 'ThisExpression'
117117
&& isClassComponent(node)
118118
) {
119-
/** @type {import("eslint").Rule.Node} */
119+
/** @type {import('eslint').Rule.Node} */
120120
let current = node;
121121
while (current.type !== 'Program') {
122122
// Reporting if this.state is directly within this.setState
@@ -163,7 +163,7 @@ module.exports = {
163163

164164
Identifier(node) {
165165
// Checks if the identifier is a variable within an object
166-
/** @type {import("eslint").Rule.Node} */
166+
/** @type {import('eslint').Rule.Node} */
167167
let current = node;
168168
while (current.parent.type === 'BinaryExpression') {
169169
current = current.parent;

lib/rules/no-deprecated.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ module.exports = {
229229
}
230230
node.specifiers.filter(((s) => 'imported' in s && s.imported)).forEach((specifier) => {
231231
// TODO, semver-major: remove `in` check as part of jsdoc->tsdoc migration
232-
checkDeprecation(node, 'imported' in specifier && `${MODULES[node.source.value][0]}.${specifier.imported.name}`, specifier);
232+
checkDeprecation(node, 'imported' in specifier && 'name' in specifier.imported && `${MODULES[node.source.value][0]}.${specifier.imported.name}`, specifier);
233233
});
234234
},
235235

lib/rules/no-unused-state.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ module.exports = {
468468
&& unwrappedRight.type === 'ObjectExpression'
469469
) {
470470
// Find the nearest function expression containing this assignment.
471-
/** @type {import("eslint").Rule.Node} */
471+
/** @type {import('eslint').Rule.Node} */
472472
let fn = node;
473473
while (fn.type !== 'FunctionExpression' && fn.parent) {
474474
fn = fn.parent;

test-published-types/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
const react = require('eslint-plugin-react');
44

55
/** @type {import('eslint').Linter.Config[]} */
6-
module.exports = [
6+
const config = [
77
{
88
plugins: {
99
react,
1010
},
1111
},
1212
];
13+
14+
module.exports = config;

test-published-types/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"private": true,
44
"version": "0.0.0",
55
"dependencies": {
6-
"eslint": "^9.11.1",
7-
"eslint-plugin-react": "file:.."
6+
"eslint": "^9.11.1"
87
}
98
}

0 commit comments

Comments
 (0)