Skip to content

Commit 588b521

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 ea32116 commit 588b521

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
@@ -7823,12 +7823,38 @@ array's items will be considered as defined for the purposes of that tag.
78237823
<a name="eslint-plugin-jsdoc-rules-no-undefined-types-options-18"></a>
78247824
#### Options
78257825

7826-
An option object may have the following key:
7826+
An option object may have the following keys, helping indicate types or
7827+
file sources of types:
78277828

78287829
- `definedTypes` - This array can be populated to indicate other types which
78297830
are automatically considered as defined (in addition to globals, etc.).
78307831
Defaults to an empty array.
78317832

7833+
- `entryFiles` - Array of entry files objects indicating JavaScript or HTML
7834+
files whose `import` or `require` statements should be resolved recursively
7835+
and be analyzed for `@typedef`'s, globals, etc. (see `typeSources`) to treat
7836+
as "defined" for the purposes of this rule. Each object should have a
7837+
`file` array and with an optional `node` boolean property to indicate whether
7838+
to use the Node Resolution Algorithm (e.g., for Node.js) and/or a `cjs`
7839+
boolean property (if following `require`) properties. Set one of the `file`
7840+
items to `<main>`, `<exports>`, `<exports.imports>`, or `<exports.require>`
7841+
to use the file referenced in the correpsonding property in `package.json`.
7842+
7843+
- `jsdocConfig` - Object with:
7844+
- `file` string pointing to a path for a
7845+
[jsdoc config file](https://jsdoc.app/about-configuring-jsdoc.html)
7846+
which will be parsed for [input files](https://jsdoc.app/about-configuring-jsdoc.html#specifying-input-files),
7847+
including `include`, `exclude`, `includePattern`, and `excludePattern`
7848+
properties within the file as well as `opts.recurse`. See `entryFiles`
7849+
on how the (JavaScript) files will be treated (with
7850+
`sourceType: 'module'` in the jsdoc config file causing "cjs" to be
7851+
set to `false`).
7852+
7853+
- `typeSources` - Array with `globals`, `exports`, and/or `locals` indicating
7854+
the source types that will be treated as valid types when found in the
7855+
current file or any entry files (`locals` will only apply to the
7856+
current file). Defaults to `['typedefs', 'globals', 'exports', 'locals']`.
7857+
78327858
|||
78337859
|---|---|
78347860
|Context|everywhere|
@@ -8367,6 +8393,31 @@ class Test {
83678393
return this;
83688394
}
83698395
}
8396+
8397+
import {myTypesA} from '../internal/file.js'; // ERROR
8398+
import {myTypesB} from '../internal/file.js'; // NO ERROR
8399+
8400+
/**
8401+
* @typedef newType
8402+
* @property {myTypesA.someType} someProp - Some prop.
8403+
*/
8404+
8405+
/**
8406+
* @param {newType} arg - Arg.
8407+
*/
8408+
function myFunctionA(arg) {
8409+
return arg;
8410+
}
8411+
8412+
/**
8413+
* @param {myTypesB.someType} arg - Arg.
8414+
*/
8415+
function myFunctionB(arg) {
8416+
return arg;
8417+
}
8418+
8419+
export {myFunctionA, myFunctionB};
8420+
// "jsdoc/no-undefined-types": ["error"|"warn", {"entryFiles":[]}]
83708421
````
83718422

83728423

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.4.3",
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
@@ -993,5 +993,43 @@ export default {
993993
`,
994994
parser: require.resolve('@typescript-eslint/parser'),
995995
},
996+
{
997+
code: `
998+
import {myTypesA} from '../internal/file.js'; // ERROR
999+
import {myTypesB} from '../internal/file.js'; // NO ERROR
1000+
1001+
/**
1002+
* @typedef newType
1003+
* @property {myTypesA.someType} someProp - Some prop.
1004+
*/
1005+
1006+
/**
1007+
* @param {newType} arg - Arg.
1008+
*/
1009+
function myFunctionA(arg) {
1010+
return arg;
1011+
}
1012+
1013+
/**
1014+
* @param {myTypesB.someType} arg - Arg.
1015+
*/
1016+
function myFunctionB(arg) {
1017+
return arg;
1018+
}
1019+
1020+
export {myFunctionA, myFunctionB};
1021+
`,
1022+
options: [
1023+
{
1024+
entryFiles: [],
1025+
},
1026+
],
1027+
parserOptions: {
1028+
sourceType: 'module',
1029+
},
1030+
rules: {
1031+
'no-unused-vars': ['error'],
1032+
},
1033+
},
9961034
],
9971035
};

0 commit comments

Comments
 (0)