Skip to content
This repository was archived by the owner on May 1, 2019. It is now read-only.

Commit d87f1dd

Browse files
committed
Analyze @global functions, and respect @function name override.
1 parent f23ad40 commit d87f1dd

File tree

4 files changed

+89
-29
lines changed

4 files changed

+89
-29
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
## Unreleased
99
<!-- Add new, unreleased changes here. -->
1010

11+
* Functions will now be scanned if they have a `@global` annotation. Previously they would only be scanned if they had a `@memberof` annotation. One of these annotations is required because otherwise a lot of functions that aren't really public are included in the analysis (e.g. because they are hidden due to their scoping).
12+
* Function names can now be overridden with e.g. `@function MyNewName`.
13+
1114
## [3.0.0-pre.1] - 2017-11-29
1215

1316
* [BREAKING] Switched the underlying parser/AST for JavaScript from `espree/estree` to `babylon/babel-types`. This was needed to support parsing of important platform features such as dynamic imports and moves us closer to supporting TypeScript.

src/javascript/function-scanner.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -121,24 +121,38 @@ class FunctionVisitor implements Visitor {
121121
private _initFunction(
122122
node: babel.Node, analyzedName?: string, _fn?: babel.Function) {
123123
const comment = getAttachedComment(node);
124-
125-
// Quickly filter down to potential candidates.
126-
if (!comment || comment.indexOf('@memberof') === -1) {
124+
if (!comment) {
127125
return;
128126
}
127+
const docs = jsdoc.parseJsdoc(comment);
128+
129+
// The @function annotation can override the name.
130+
const functionTag = jsdoc.getTag(docs, 'function');
131+
if (functionTag && functionTag.name) {
132+
analyzedName = functionTag.name;
133+
}
129134

130135
if (!analyzedName) {
131136
// TODO(fks): Propagate a warning if name could not be determined
132137
return;
133138
}
134139

135-
const docs = jsdoc.parseJsdoc(comment);
140+
if (!jsdoc.hasTag(docs, 'global') && !jsdoc.hasTag(docs, 'memberof')) {
141+
// Without this check we would emit a lot of functions not worthy of
142+
// inclusion. Since we don't do scope analysis, we can't tell when a
143+
// function is actually part of an exposed API. Only include functions
144+
// that are explicitly @global, or declared as part of some namespace
145+
// with @memberof.
146+
return;
147+
}
148+
136149
// TODO(justinfagnani): remove polymerMixin support
137150
if (jsdoc.hasTag(docs, 'mixinFunction') ||
138151
jsdoc.hasTag(docs, 'polymerMixin')) {
139152
// This is a mixin, not a normal function.
140153
return;
141154
}
155+
142156
const functionName = getNamespacedIdentifier(analyzedName, docs);
143157
const sourceRange = this.document.sourceRangeForNode(node)!;
144158
const returnTag = jsdoc.getTag(docs, 'return');

src/test/javascript/function-scanner_test.ts

+45-25
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ suite('FunctionScanner', () => {
2929
const urlLoader = new FSUrlLoader(testFilesDir);
3030
const underliner = new CodeUnderliner(urlLoader);
3131

32-
async function getNamespaceFunctions(filename: string): Promise<any[]> {
32+
async function getNamespaceFunctions(filename: string):
33+
Promise<ScannedFunction[]> {
3334
const file = await urlLoader.load(filename);
3435
const parser = new JavaScriptParser();
3536
const document = parser.parse(file, filename as ResolvedUrl);
@@ -42,20 +43,20 @@ suite('FunctionScanner', () => {
4243
};
4344

4445

45-
async function getTestProps(fn: ScannedFunction):
46-
Promise<any> {
47-
return {
48-
name: fn.name,
49-
description: fn.description,
50-
summary: fn.summary,
51-
warnings: fn.warnings,
52-
params: fn.params, return: fn.return,
53-
codeSnippet: await underliner.underline(fn.sourceRange),
54-
privacy: fn.privacy
55-
};
56-
}
57-
58-
test('scans', async() => {
46+
async function getTestProps(fn: ScannedFunction): Promise<any> {
47+
return {
48+
name: fn.name,
49+
description: fn.description,
50+
summary: fn.summary,
51+
warnings: fn.warnings,
52+
params: fn.params,
53+
return: fn.return,
54+
codeSnippet: await underliner.underline(fn.sourceRange),
55+
privacy: fn.privacy
56+
};
57+
}
58+
59+
test('handles @memberof annotation', async () => {
5960
const namespaceFunctions =
6061
await getNamespaceFunctions('memberof-functions.js');
6162
const functionData =
@@ -71,7 +72,8 @@ suite('FunctionScanner', () => {
7172
name: 'a',
7273
type: 'Number',
7374
}],
74-
privacy: 'public', return: undefined,
75+
privacy: 'public',
76+
return: undefined,
7577
codeSnippet: `
7678
function aaa(a) {
7779
~~~~~~~~~~~~~~~~~
@@ -85,7 +87,8 @@ function aaa(a) {
8587
description: 'bbb',
8688
summary: '',
8789
warnings: [],
88-
params: [], return: undefined,
90+
params: [],
91+
return: undefined,
8992
privacy: 'public',
9093
codeSnippet: `
9194
Polymer.bbb = function bbb() {
@@ -100,7 +103,8 @@ Polymer.bbb = function bbb() {
100103
description: 'ccc',
101104
summary: '',
102105
warnings: [],
103-
params: [], return: undefined,
106+
params: [],
107+
return: undefined,
104108
privacy: 'protected',
105109
codeSnippet: `
106110
function ccc() {
@@ -114,7 +118,8 @@ Polymer.bbb = function bbb() {
114118
summary: '',
115119
warnings: [],
116120
privacy: 'protected',
117-
params: [], return: undefined,
121+
params: [],
122+
return: undefined,
118123
codeSnippet: `
119124
_ddd: function() {
120125
~~~~~~~~~~~~~~~~~~
@@ -128,7 +133,8 @@ Polymer.bbb = function bbb() {
128133
description: 'eee',
129134
summary: '',
130135
warnings: [],
131-
params: [], return: undefined,
136+
params: [],
137+
return: undefined,
132138
privacy: 'private',
133139
codeSnippet: `
134140
eee: () => {},
@@ -139,7 +145,8 @@ Polymer.bbb = function bbb() {
139145
description: 'fff',
140146
summary: '',
141147
warnings: [],
142-
params: [], return: undefined,
148+
params: [],
149+
return: undefined,
143150
privacy: 'public',
144151
codeSnippet: `
145152
fff() {
@@ -154,7 +161,8 @@ Polymer.bbb = function bbb() {
154161
description: 'ggg',
155162
summary: '',
156163
warnings: [],
157-
params: [], return: undefined,
164+
params: [],
165+
return: undefined,
158166
privacy: 'public',
159167
codeSnippet: `
160168
ggg: someFunction,
@@ -165,7 +173,8 @@ Polymer.bbb = function bbb() {
165173
description: 'hhh_ should be private',
166174
summary: '',
167175
warnings: [],
168-
params: [], return: undefined,
176+
params: [],
177+
return: undefined,
169178
privacy: 'private',
170179
codeSnippet: `
171180
hhh_: someOtherFunc,
@@ -176,7 +185,8 @@ Polymer.bbb = function bbb() {
176185
description: '__iii should be private too',
177186
summary: '',
178187
warnings: [],
179-
params: [], return: undefined,
188+
params: [],
189+
return: undefined,
180190
privacy: 'private',
181191
codeSnippet: `
182192
__iii() { },
@@ -187,7 +197,8 @@ Polymer.bbb = function bbb() {
187197
description: 'jjj',
188198
summary: '',
189199
warnings: [],
190-
params: [], return: undefined,
200+
params: [],
201+
return: undefined,
191202
privacy: 'public',
192203
codeSnippet: `
193204
var jjj = function() {
@@ -199,4 +210,13 @@ var jjj = function() {
199210
},
200211
]);
201212
});
213+
214+
test('handles @global, @memberof, @function annotations', async () => {
215+
const functions = await getNamespaceFunctions('annotated-functions.js');
216+
assert.deepEqual(functions.map((fn) => fn.name), [
217+
'globalFn',
218+
'SomeNamespace.memberofFn',
219+
'overrideNameFn',
220+
]);
221+
});
202222
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Ignore me because I have no annotation to hint that I'm in a public API.
3+
*/
4+
function ignoreFn() { }
5+
6+
/**
7+
* I'm explicitly global.
8+
* @global
9+
*/
10+
function globalFn() { }
11+
12+
/**
13+
* I'm in a namespace, so I'm probably part of a public API.
14+
* @memberof SomeNamespace
15+
*/
16+
function memberofFn() { }
17+
18+
/**
19+
* My @function annotation overrides my name.
20+
* @global
21+
* @function overrideNameFn
22+
*/
23+
function wrongNameFn() { }

0 commit comments

Comments
 (0)