Skip to content

Commit a7f5bc7

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 a7f5bc7

17 files changed

+269
-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",
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/__tests__/starWarsData.js

+4
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ function getCharacter(id) {
122122

123123
/**
124124
* Allows us to query for a character's friends.
125+
* @internal
125126
*/
126127
export function getFriends(character: Character): Array<Promise<Character>> {
127128
// Notice that GraphQL accepts Arrays of Promises.
@@ -130,6 +131,7 @@ export function getFriends(character: Character): Array<Promise<Character>> {
130131

131132
/**
132133
* Allows us to fetch the undisputed hero of the Star Wars trilogy, R2-D2.
134+
* @internal
133135
*/
134136
export function getHero(episode: number): Character {
135137
if (episode === 5) {
@@ -142,13 +144,15 @@ export function getHero(episode: number): Character {
142144

143145
/**
144146
* Allows us to query for the human with the given id.
147+
* @internal
145148
*/
146149
export function getHuman(id: string): Human {
147150
return humanData[id];
148151
}
149152

150153
/**
151154
* Allows us to query for the droid with the given id.
155+
* @internal
152156
*/
153157
export function getDroid(id: string): Droid {
154158
return droidData[id];

src/jsutils/Path.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type Path = {|
77

88
/**
99
* Given a Path and a key, return a new Path containing the new key.
10+
* @internal
1011
*/
1112
export function addPath(
1213
prev: $ReadOnly<Path> | void,

src/language/__tests__/parser-benchmark.js

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { kitchenSinkQuery } from '../../__fixtures__/index';
66

77
export const name = 'Parse kitchen sink';
88
export const count = 1000;
9+
/**
10+
* @internal
11+
*/
912
export function measure() {
1013
parse(kitchenSinkQuery);
1114
}

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/utilities/__tests__/buildASTSchema-benchmark.js

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const schemaAST = parse(bigSchemaSDL);
1010

1111
export const name = 'Build Schema from AST';
1212
export const count = 10;
13+
/**
14+
* @internal
15+
*/
1316
export function measure() {
1417
buildASTSchema(schemaAST, { assumeValid: true });
1518
}

src/utilities/__tests__/buildClientSchema-benchmark.js

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { bigSchemaIntrospectionResult } from '../../__fixtures__/index';
66

77
export const name = 'Build Schema from Introspection';
88
export const count = 10;
9+
/**
10+
* @internal
11+
*/
912
export function measure() {
1013
buildClientSchema(bigSchemaIntrospectionResult.data, { assumeValid: true });
1114
}

src/utilities/__tests__/introspectionFromSchema-benchmark.js

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const document = parse(getIntrospectionQuery());
1313

1414
export const name = 'Execute Introspection Query';
1515
export const count = 10;
16+
/**
17+
* @internal
18+
*/
1619
export function measure() {
1720
execute({ schema, document });
1821
}

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

src/validation/__tests__/harness.js

+9
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ export const testSchema = new GraphQLSchema({
397397
],
398398
});
399399

400+
/**
401+
* @internal
402+
*/
400403
export function expectValidationErrorsWithSchema(
401404
schema: GraphQLSchema,
402405
rule: ValidationRule,
@@ -407,10 +410,16 @@ export function expectValidationErrorsWithSchema(
407410
return expect(errors);
408411
}
409412

413+
/**
414+
* @internal
415+
*/
410416
export function expectValidationErrors(rule: ValidationRule, queryStr: string) {
411417
return expectValidationErrorsWithSchema(testSchema, rule, queryStr);
412418
}
413419

420+
/**
421+
* @internal
422+
*/
414423
export function expectSDLValidationErrors(
415424
schema: ?GraphQLSchema,
416425
rule: SDLValidationRule,

src/validation/__tests__/validateGQL-benchmark.js

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const queryAST = parse(getIntrospectionQuery());
1313

1414
export const name = 'Validate Introspection Query';
1515
export const count = 50;
16+
/**
17+
* @internal
18+
*/
1619
export function measure() {
1720
validate(schema, queryAST);
1821
}

0 commit comments

Comments
 (0)