Skip to content

Commit 42fb05d

Browse files
committed
Added @internal Js comments to internal API functions
Created ESLint rule to check whether internal API functions are properly annotated or not Added support to get list of puvlic API functions (map.json created during linting) Signed-off-by: Naveen Jain <[email protected]>
1 parent fe878ee commit 42fb05d

File tree

7 files changed

+234
-1
lines changed

7 files changed

+234
-1
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
"test:ci": "yarn check --integrity && npm run prettier:check && npm run lint -- --no-cache && npm run check && npm run testonly:cover && npm run check:ts && npm run check:spelling && npm run build",
2929
"testonly": "mocha --full-trace src/**/__tests__/**/*-test.js",
3030
"testonly:cover": "nyc npm run testonly",
31-
"lint": "eslint --rulesdir './resources/eslint-rules' --rule 'no-dir-import: error' --cache --ext .js,.ts src resources",
31+
"prelint": "node resources/generate-exported",
32+
"lint": "eslint --rulesdir './resources/eslint-rules' --rule 'no-dir-import: error' --cache --ext .js,.ts src resources && eslint --rulesdir ./resources/eslint-rules/ --rule 'internal-func: 1' src --ignore-pattern 'src/jsutils/*' --ignore-pattern 'src/polyfills/*' --ignore-pattern 'src/**/__tests__/**' --ignore-pattern 'src/__tests__/*'",
33+
"postlint": "rm resources/babel-plugins/map.json",
3234
"benchmark": "node --noconcurrent_sweeping --expose-gc --predictable ./resources/benchmark.js",
3335
"prettier": "prettier --ignore-path .gitignore --write --list-different \"**/*.{js,ts,md,json,yml}\"",
3436
"prettier:check": "prettier --ignore-path .gitignore --check \"**/*.{js,ts,md,json,yml}\"",
+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// @flow strict
2+
3+
'use strict';
4+
5+
// @noflow
6+
7+
const path = require('path');
8+
const fs = require('fs');
9+
10+
const { HashMap } = require('./helper');
11+
12+
// Create a new Hashmap to store file names and declarations
13+
const mp = new HashMap();
14+
15+
// All the rules that are required by plugin
16+
const ExportNamedDeclaration = {
17+
enter(babelPath, state) {
18+
if (babelPath.node.declaration) {
19+
return handleDeclaration(babelPath, state);
20+
}
21+
22+
if (babelPath.node.specifiers) {
23+
return handleNodeSpecifiers(
24+
babelPath.node.specifiers,
25+
state,
26+
babelPath.node.source ? babelPath.node.source.value : undefined,
27+
);
28+
}
29+
},
30+
};
31+
32+
const ExportAllDeclaration = {
33+
enter(babelPath, state) {
34+
mp.add(
35+
'*',
36+
state,
37+
babelPath.node.source ? babelPath.node.source.value : undefined,
38+
);
39+
},
40+
};
41+
42+
module.exports = function() {
43+
return {
44+
visitor: {
45+
ExportNamedDeclaration,
46+
ExportAllDeclaration,
47+
ExportDefaultDeclaration: ExportNamedDeclaration,
48+
Program: {
49+
exit() {
50+
return writeToJSON();
51+
},
52+
},
53+
},
54+
};
55+
};
56+
57+
// Helper functions for the rules
58+
function handleDeclaration(babelPath, state) {
59+
switch (babelPath.node.declaration.type) {
60+
case 'VariableDeclaration':
61+
return handleVariableDeclarations(
62+
babelPath.node.declaration,
63+
state,
64+
babelPath.node.source ? babelPath.node.source.value : undefined,
65+
);
66+
case 'FunctionDeclaration':
67+
case 'ClassDeclaration':
68+
return handleFunctionDeclarations(
69+
babelPath.node.declaration,
70+
state,
71+
babelPath.node.source ? babelPath.node.source.value : undefined,
72+
);
73+
}
74+
}
75+
76+
function handleNodeSpecifiers(specifiers, state, source) {
77+
return specifiers.forEach(specifier => {
78+
switch (specifier.type) {
79+
case 'ExportSpecifier':
80+
mp.add(specifier.local.name, state, source);
81+
break;
82+
case 'ExportNamespaceSpecifier':
83+
mp.add('*', state, source);
84+
break;
85+
}
86+
});
87+
}
88+
89+
function handleVariableDeclarations(variableDeclaration, state, source) {
90+
variableDeclaration.declarations.forEach(declaration =>
91+
mp.add(declaration.id.name, state, source),
92+
);
93+
}
94+
95+
function handleFunctionDeclarations(declaration, state, source) {
96+
return mp.add(declaration.id.name, state, source);
97+
}
98+
99+
// To write final result to JSON file
100+
function writeToJSON() {
101+
if (!fs.existsSync(path.join(__dirname, '/map.json'))) {
102+
fs.writeFileSync(path.join(__dirname, '/map.json'), JSON.stringify({}));
103+
}
104+
const exportedValues = require(path.join(__dirname, '/map.json'));
105+
for (const key of mp.keys()) {
106+
exportedValues[key] = exportedValues[key] || [];
107+
108+
exportedValues[key] = exportedValues[key].concat(Array.from(mp.get(key)));
109+
110+
exportedValues[key] = Array.from(new Set(exportedValues[key]));
111+
}
112+
113+
fs.writeFileSync(
114+
path.join(__dirname, '/map.json'),
115+
JSON.stringify(exportedValues),
116+
);
117+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @flow strict
2+
3+
'use strict';
4+
const path = require('path');
5+
6+
class HashMap {
7+
constructor() {
8+
this._map = new Map();
9+
}
10+
11+
add(value, state, source) {
12+
let filepath = path.resolve(state.file.opts.filename);
13+
if (source) {
14+
const pathArray = state.file.opts.filename.split('/');
15+
const directoryPath = pathArray.slice(0, pathArray.length - 1).join('/');
16+
filepath = require.resolve(path.resolve(directoryPath, source));
17+
}
18+
if (!this._map.has(filepath)) {
19+
this._map.set(filepath, new Set());
20+
}
21+
this._map.get(filepath).add(value);
22+
}
23+
24+
get(key) {
25+
return this._map.get(key);
26+
}
27+
28+
keys() {
29+
return this._map.keys();
30+
}
31+
}
32+
33+
module.exports = { HashMap };
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// @flow strict
2+
3+
// @noflow
4+
5+
'use strict';
6+
const path = require('path');
7+
8+
const listOfExports = require(path.join(
9+
process.cwd(),
10+
'/resources/babel-plugins/',
11+
'map.json',
12+
));
13+
module.exports = {
14+
create(context) {
15+
function isExportedLocallyOnly(name) {
16+
if (!listOfExports[context.getFilename()]) {
17+
return true;
18+
}
19+
return !listOfExports[context.getFilename()].find(
20+
value => value === name || value === '*',
21+
);
22+
}
23+
24+
const source = context.getSourceCode();
25+
/**
26+
*
27+
*/
28+
return {
29+
'ExportNamedDeclaration > :matches(FunctionDeclaration,ClassDeclaration)'(
30+
node,
31+
) {
32+
if (isExportedLocallyOnly(node.id.name)) {
33+
if (!source.getJSDocComment(node)) {
34+
return context.report({
35+
node,
36+
message: 'Please enter JSDoC internal functions using @internal',
37+
});
38+
}
39+
if (!source.getJSDocComment(node).value.includes('@internal')) {
40+
context.report({
41+
node,
42+
message: 'Please annotate internal functions using @internal',
43+
});
44+
}
45+
}
46+
},
47+
};
48+
},
49+
};

resources/generate-exported.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @noflow
2+
3+
'use strict';
4+
5+
const babel = require('@babel/core');
6+
const flowTypesPlugin = require('@babel/plugin-transform-flow-strip-types');
7+
8+
const extractExportPlugin = require('./babel-plugins/extract-exports');
9+
10+
const directoriesToScan = [
11+
'/src',
12+
'/src/error',
13+
'/src/type',
14+
'/src/language',
15+
'/src/validation',
16+
'/src/utilities',
17+
'/src/execution',
18+
'/src/subscription',
19+
];
20+
21+
directoriesToScan.forEach(path =>
22+
babel.transformFileSync(process.cwd() + path + '/index.js', {
23+
babelrc: false,
24+
plugins: [flowTypesPlugin, extractExportPlugin],
25+
}),
26+
);

src/language/ast.js

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { type TokenKindEnum } from './tokenKind';
88
/**
99
* Contains a range of UTF-8 character offsets and token references that
1010
* identify the region of the source from which the AST derived.
11+
* @internal
1112
*/
1213
export class Location {
1314
/**
@@ -52,6 +53,7 @@ defineToJSON(Location, function() {
5253
/**
5354
* Represents a range of characters represented by a lexical token
5455
* within a Source.
56+
* @internal
5557
*/
5658
export class Token {
5759
/**

src/validation/ValidationContext.js

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type VariableUsage = {|
3838
* An instance of this class is passed as the "this" context to all validators,
3939
* allowing access to commonly useful contextual information from within a
4040
* validation rule.
41+
* @internal
4142
*/
4243
export class ASTValidationContext {
4344
_ast: DocumentNode;
@@ -133,6 +134,9 @@ export class ASTValidationContext {
133134

134135
export type ASTValidationRule = ASTValidationContext => ASTVisitor;
135136

137+
/**
138+
* @internal
139+
*/
136140
export class SDLValidationContext extends ASTValidationContext {
137141
_schema: ?GraphQLSchema;
138142

0 commit comments

Comments
 (0)