Skip to content

Commit b35c845

Browse files
committed
INCOMPLETE
1. Add tests (for desired features added to docs) -> implement 2. (apparent problem in eslint itself with our use of Program:exit): 3. Could also use this approach for checking consistency between function calls and jsdoc argument signature types; compile to WebAssembly test(`no-undefined-types`): issues gajus#507
1 parent 355da58 commit b35c845

File tree

5 files changed

+213
-6
lines changed

5 files changed

+213
-6
lines changed

.README/rules/no-undefined-types.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,38 @@ array's items will be considered as defined for the purposes of that tag.
4545

4646
#### Options
4747

48-
An option object may have the following key:
48+
An option object may have the following keys, helping indicate types or
49+
file sources of types:
4950

5051
- `definedTypes` - This array can be populated to indicate other types which
5152
are automatically considered as defined (in addition to globals, etc.).
5253
Defaults to an empty array.
5354

55+
- `entryFiles` - Array of entry files objects indicating JavaScript or HTML
56+
files whose `import` or `require` statements should be resolved recursively
57+
and be analyzed for `@typedef`'s, globals, etc. (see `typeSources`) to treat
58+
as "defined" for the purposes of this rule. Each object should have a
59+
`file` array and with an optional `node` boolean property to indicate whether
60+
to use the Node Resolution Algorithm (e.g., for Node.js) and/or a `cjs`
61+
boolean property (if following `require`) properties. Set one of the `file`
62+
items to `<main>`, `<exports>`, `<exports.imports>`, or `<exports.require>`
63+
to use the file referenced in the correpsonding property in `package.json`.
64+
65+
- `jsdocConfig` - Object with:
66+
- `file` string pointing to a path for a
67+
[jsdoc config file](https://jsdoc.app/about-configuring-jsdoc.html)
68+
which will be parsed for [input files](https://jsdoc.app/about-configuring-jsdoc.html#specifying-input-files),
69+
including `include`, `exclude`, `includePattern`, and `excludePattern`
70+
properties within the file as well as `opts.recurse`. See `entryFiles`
71+
on how the (JavaScript) files will be treated (with
72+
`sourceType: 'module'` in the jsdoc config file causing "cjs" to be
73+
set to `false`).
74+
75+
- `typeSources` - Array with `globals`, `exports`, and/or `locals` indicating
76+
the source types that will be treated as valid types when found in the
77+
current file or any entry files (`locals` will only apply to the
78+
current file). Defaults to `['typedefs', 'globals', 'exports', 'locals']`.
79+
5480
|||
5581
|---|---|
5682
|Context|everywhere|

README.md

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8569,12 +8569,38 @@ array's items will be considered as defined for the purposes of that tag.
85698569
<a name="eslint-plugin-jsdoc-rules-no-undefined-types-options-20"></a>
85708570
#### Options
85718571

8572-
An option object may have the following key:
8572+
An option object may have the following keys, helping indicate types or
8573+
file sources of types:
85738574

85748575
- `definedTypes` - This array can be populated to indicate other types which
85758576
are automatically considered as defined (in addition to globals, etc.).
85768577
Defaults to an empty array.
85778578

8579+
- `entryFiles` - Array of entry files objects indicating JavaScript or HTML
8580+
files whose `import` or `require` statements should be resolved recursively
8581+
and be analyzed for `@typedef`'s, globals, etc. (see `typeSources`) to treat
8582+
as "defined" for the purposes of this rule. Each object should have a
8583+
`file` array and with an optional `node` boolean property to indicate whether
8584+
to use the Node Resolution Algorithm (e.g., for Node.js) and/or a `cjs`
8585+
boolean property (if following `require`) properties. Set one of the `file`
8586+
items to `<main>`, `<exports>`, `<exports.imports>`, or `<exports.require>`
8587+
to use the file referenced in the correpsonding property in `package.json`.
8588+
8589+
- `jsdocConfig` - Object with:
8590+
- `file` string pointing to a path for a
8591+
[jsdoc config file](https://jsdoc.app/about-configuring-jsdoc.html)
8592+
which will be parsed for [input files](https://jsdoc.app/about-configuring-jsdoc.html#specifying-input-files),
8593+
including `include`, `exclude`, `includePattern`, and `excludePattern`
8594+
properties within the file as well as `opts.recurse`. See `entryFiles`
8595+
on how the (JavaScript) files will be treated (with
8596+
`sourceType: 'module'` in the jsdoc config file causing "cjs" to be
8597+
set to `false`).
8598+
8599+
- `typeSources` - Array with `globals`, `exports`, and/or `locals` indicating
8600+
the source types that will be treated as valid types when found in the
8601+
current file or any entry files (`locals` will only apply to the
8602+
current file). Defaults to `['typedefs', 'globals', 'exports', 'locals']`.
8603+
85788604
|||
85798605
|---|---|
85808606
|Context|everywhere|
@@ -9113,6 +9139,31 @@ class Test {
91139139
return this;
91149140
}
91159141
}
9142+
9143+
import {myTypesA} from '../internal/file.js'; // ERROR
9144+
import {myTypesB} from '../internal/file.js'; // NO ERROR
9145+
9146+
/**
9147+
* @typedef newType
9148+
* @property {myTypesA.someType} someProp - Some prop.
9149+
*/
9150+
9151+
/**
9152+
* @param {newType} arg - Arg.
9153+
*/
9154+
function myFunctionA(arg) {
9155+
return arg;
9156+
}
9157+
9158+
/**
9159+
* @param {myTypesB.someType} arg - Arg.
9160+
*/
9161+
function myFunctionB(arg) {
9162+
return arg;
9163+
}
9164+
9165+
export {myFunctionA, myFunctionB};
9166+
// "jsdoc/no-undefined-types": ["error"|"warn", {"entryFiles":[]}]
91169167
````
91179168

91189169

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@es-joy/jsdoccomment": "^0.6.0",
99
"comment-parser": "1.1.5",
1010
"debug": "^4.3.1",
11+
"es-file-traverse": "^0.10.0",
1112
"esquery": "^1.4.0",
1213
"jsdoctypeparser": "^9.0.0",
1314
"lodash": "^4.17.21",

src/rules/noUndefinedTypes.js

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import {
22
getJSDocComment,
33
} from '@es-joy/jsdoccomment';
4+
import {
5+
traverse as esFileTraverse,
6+
} from 'es-file-traverse';
47
import {
58
parse as parseType, traverse,
69
} from 'jsdoctypeparser';
@@ -23,9 +26,9 @@ const stripPseudoTypes = (str) => {
2326
return str && str.replace(/(?:\.|<>|\.<>|\[\])$/u, '');
2427
};
2528

26-
export default iterateJsdoc(({
29+
export default iterateJsdoc(async ({
2730
context,
28-
node,
31+
node: jsdocNode,
2932
report,
3033
settings,
3134
sourceCode,
@@ -34,7 +37,57 @@ export default iterateJsdoc(({
3437
const {scopeManager} = sourceCode;
3538
const {globalScope} = scopeManager;
3639

37-
const {definedTypes = []} = context.options[0] || {};
40+
const {
41+
definedTypes = [],
42+
entryFiles = [
43+
// {file, cjs, node}
44+
],
45+
jsdocConfig: {file: jsdocConfigFile},
46+
typeSources = ['typedefs', 'globals', 'exports', 'locals'],
47+
} = context.options[0] || {};
48+
49+
// eslint-disable-next-line no-console
50+
console.log('entryFiles', entryFiles, jsdocConfigFile, typeSources);
51+
52+
// No async rules yet per ESLint Discord
53+
// chat response from nzakas
54+
await Promise.all(entryFiles.map(({file, node, cjs}) => {
55+
if ([
56+
'<main>', '<exports>', '<exports.imports>', '<exports.require>',
57+
].includes(file)) {
58+
// Todo: Replace `file` with `package.json`-pointed value
59+
}
60+
61+
return esFileTraverse({
62+
/**
63+
* @callback PromiseReturner
64+
* @returns {Promise<void>}
65+
*/
66+
/**
67+
* @typedef {PlainObject} ESFileTraverseInfo
68+
* @property {string} fullPath
69+
* @property {string} text
70+
* @property {AST} ast
71+
* @property {"esm"|"cjs"|"amd"} [type]
72+
* @property {PromiseReturner[]} [promMethods]
73+
* @property {Set<string>} [resolvedSet]
74+
*/
75+
/**
76+
* @param {"enter"|"exit"} state
77+
* @param {ESFileTraverseInfo} info
78+
* @returns {void}
79+
*/
80+
callback (state, info) {
81+
// Todo: Handle
82+
// eslint-disable-next-line no-console
83+
console.log('state', state, info);
84+
},
85+
86+
cjs,
87+
file,
88+
node,
89+
});
90+
}));
3891

3992
let definedPreferredTypes = [];
4093
const {preferredTypes, structuredTags, mode} = settings;
@@ -77,7 +130,7 @@ export default iterateJsdoc(({
77130
.value();
78131

79132
const ancestorNodes = [];
80-
let currentScope = scopeManager.acquire(node);
133+
let currentScope = scopeManager.acquire(jsdocNode);
81134

82135
while (currentScope && currentScope.block.type !== 'Program') {
83136
ancestorNodes.push(currentScope.block);
@@ -182,6 +235,44 @@ export default iterateJsdoc(({
182235
},
183236
type: 'array',
184237
},
238+
entryFiles: {
239+
items: {
240+
properties: {
241+
cjs: {
242+
type: 'boolean',
243+
},
244+
file: {
245+
items: {
246+
type: 'string',
247+
},
248+
type: 'array',
249+
},
250+
node: {
251+
type: 'boolean',
252+
},
253+
},
254+
require: ['file'],
255+
type: 'object',
256+
},
257+
type: 'array',
258+
},
259+
jsdocConfig: {
260+
properties: {
261+
file: {
262+
type: 'string',
263+
},
264+
},
265+
type: 'object',
266+
},
267+
typeSources: {
268+
items: {
269+
enum: [
270+
'globals', 'exports', 'locals',
271+
],
272+
type: 'string',
273+
},
274+
type: 'array',
275+
},
185276
},
186277
type: 'object',
187278
},

test/rules/assertions/noUndefinedTypes.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,5 +997,43 @@ export default {
997997
`,
998998
parser: require.resolve('@typescript-eslint/parser'),
999999
},
1000+
{
1001+
code: `
1002+
import {myTypesA} from '../internal/file.js'; // ERROR
1003+
import {myTypesB} from '../internal/file.js'; // NO ERROR
1004+
1005+
/**
1006+
* @typedef newType
1007+
* @property {myTypesA.someType} someProp - Some prop.
1008+
*/
1009+
1010+
/**
1011+
* @param {newType} arg - Arg.
1012+
*/
1013+
function myFunctionA(arg) {
1014+
return arg;
1015+
}
1016+
1017+
/**
1018+
* @param {myTypesB.someType} arg - Arg.
1019+
*/
1020+
function myFunctionB(arg) {
1021+
return arg;
1022+
}
1023+
1024+
export {myFunctionA, myFunctionB};
1025+
`,
1026+
options: [
1027+
{
1028+
entryFiles: [],
1029+
},
1030+
],
1031+
parserOptions: {
1032+
sourceType: 'module',
1033+
},
1034+
rules: {
1035+
'no-unused-vars': ['error'],
1036+
},
1037+
},
10001038
],
10011039
};

0 commit comments

Comments
 (0)