Skip to content

Commit 684d08e

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): test(`no-undefined-types`): issues gajus#507
1 parent 3cca564 commit 684d08e

File tree

5 files changed

+212
-6
lines changed

5 files changed

+212
-6
lines changed

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

+27-1
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,38 @@ reporting on use of that namepath elsewhere) and/or that a tag's `type` is
4343

4444
#### Options
4545

46-
An option object may have the following key:
46+
An option object may have the following keys, helping indicate types or
47+
file sources of types:
4748

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

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

README.md

+51-1
Original file line numberDiff line numberDiff line change
@@ -6848,12 +6848,38 @@ reporting on use of that namepath elsewhere) and/or that a tag's `type` is
68486848
<a name="eslint-plugin-jsdoc-rules-no-undefined-types-options-16"></a>
68496849
#### Options
68506850
6851-
An option object may have the following key:
6851+
An option object may have the following keys, helping indicate types or
6852+
file sources of types:
68526853
68536854
- `definedTypes` - This array can be populated to indicate other types which
68546855
are automatically considered as defined (in addition to globals, etc.).
68556856
Defaults to an empty array.
68566857
6858+
- `entryFiles` - Array of entry files objects indicating JavaScript or HTML
6859+
files whose `import` or `require` statements should be resolved recursively
6860+
and be analyzed for `@typedef`'s or globals to treat as "defined" for the
6861+
purposes of this rule. Each object should have a `file` array and with an
6862+
optional `node` property to indicate whether to use the Node Resolution
6863+
Algorithm (e.g., for Node.js) and/or `cjs` (if following `require`)
6864+
properties. Set one of the `file` items to `<main>`, `<exports>`,
6865+
`<exports.imports>`, or `<exports.require>` to use the file referenced in
6866+
the correpsonding property in `package.json`.
6867+
6868+
- `jsdocConfig` - Object with:
6869+
- `file` string pointing to a path for a
6870+
[jsdoc config file](https://jsdoc.app/about-configuring-jsdoc.html)
6871+
which will be parsed for [input files](https://jsdoc.app/about-configuring-jsdoc.html#specifying-input-files),
6872+
including `include`, `exclude`, `includePattern`, and `excludePattern`
6873+
properties within the file as well as `opts.recurse`. See `entryFiles`
6874+
on how the (JavaScript) files will be treated (with
6875+
`sourceType: 'module'` in the jsdoc config file causing "cjs" to be
6876+
set to `false`).
6877+
6878+
- `typeSources` - Array with `globals`, `exports`, and/or `locals` indicating
6879+
the source types that will be treated as valid types when found in the
6880+
current file or any entry files (`locals` will only apply to the
6881+
current file).
6882+
68576883
|||
68586884
|---|---|
68596885
|Context|everywhere|
@@ -7345,6 +7371,30 @@ function quux () {}
73457371
* @type {SomeType}
73467372
*/
73477373
// Settings: {"jsdoc":{"structuredTags":{"namepathDefiner":{"name":"namepath-defining"}}}}
7374+
7375+
import {myTypesA} from '../internal/file.js'; // ERROR
7376+
import {myTypesB} from '../internal/file.js'; // NO ERROR
7377+
7378+
/**
7379+
* @typedef newType
7380+
* @property {myTypesA.someType} someProp - Some prop.
7381+
*/
7382+
7383+
/**
7384+
* @param {newType} arg - Arg.
7385+
*/
7386+
function myFunctionA(arg) {
7387+
return arg;
7388+
}
7389+
7390+
/**
7391+
* @param {myTypesB.someType} arg - Arg.
7392+
*/
7393+
function myFunctionB(arg) {
7394+
return arg;
7395+
}
7396+
7397+
export {myFunctionA, myFunctionB};
73487398
````
73497399
73507400

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dependencies": {
88
"comment-parser": "^0.7.6",
99
"debug": "^4.1.1",
10+
"es-file-traverse": "^0.1.2",
1011
"jsdoctypeparser": "^9.0.0",
1112
"lodash": "^4.17.20",
1213
"regextras": "^0.7.1",

src/rules/noUndefinedTypes.js

+95-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import {
2+
traverse as esFileTraverse,
3+
} from 'es-file-traverse';
14
import {
25
parse as parseType, traverse,
36
} from 'jsdoctypeparser';
@@ -22,9 +25,9 @@ const stripPseudoTypes = (str) => {
2225
return str && str.replace(/(?:\.|<>|\.<>|\[\])$/u, '');
2326
};
2427

25-
export default iterateJsdoc(({
28+
export default iterateJsdoc(async ({
2629
context,
27-
node,
30+
node: jsdocNode,
2831
report,
2932
settings,
3033
sourceCode,
@@ -33,7 +36,57 @@ export default iterateJsdoc(({
3336
const {scopeManager} = sourceCode;
3437
const {globalScope} = scopeManager;
3538

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

3891
let definedPreferredTypes = [];
3992
const {preferredTypes, 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);
@@ -179,6 +232,44 @@ export default iterateJsdoc(({
179232
},
180233
type: 'array',
181234
},
235+
entryFiles: {
236+
items: {
237+
properties: {
238+
cjs: {
239+
type: 'boolean',
240+
},
241+
file: {
242+
items: {
243+
type: 'string',
244+
},
245+
type: 'array',
246+
},
247+
node: {
248+
type: 'boolean',
249+
},
250+
},
251+
require: ['file'],
252+
type: 'object',
253+
},
254+
type: 'array',
255+
},
256+
jsdocConfig: {
257+
properties: {
258+
file: {
259+
type: 'string',
260+
},
261+
},
262+
type: 'object',
263+
},
264+
typeSources: {
265+
items: {
266+
enum: [
267+
'globals', 'exports', 'locals',
268+
],
269+
type: 'string',
270+
},
271+
type: 'array',
272+
},
182273
},
183274
type: 'object',
184275
},

test/rules/assertions/noUndefinedTypes.js

+38
Original file line numberDiff line numberDiff line change
@@ -897,5 +897,43 @@ export default {
897897
},
898898
},
899899
},
900+
{
901+
code: `
902+
import {myTypesA} from '../internal/file.js'; // ERROR
903+
import {myTypesB} from '../internal/file.js'; // NO ERROR
904+
905+
/**
906+
* @typedef newType
907+
* @property {myTypesA.someType} someProp - Some prop.
908+
*/
909+
910+
/**
911+
* @param {newType} arg - Arg.
912+
*/
913+
function myFunctionA(arg) {
914+
return arg;
915+
}
916+
917+
/**
918+
* @param {myTypesB.someType} arg - Arg.
919+
*/
920+
function myFunctionB(arg) {
921+
return arg;
922+
}
923+
924+
export {myFunctionA, myFunctionB};
925+
`,
926+
options: [
927+
{
928+
entryFiles: [],
929+
},
930+
],
931+
parserOptions: {
932+
sourceType: 'module',
933+
},
934+
rules: {
935+
'no-unused-vars': ['error'],
936+
},
937+
},
900938
],
901939
};

0 commit comments

Comments
 (0)