From 45c20c7dad1406a611377ef80fb9545d03fe8285 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Thu, 13 Jul 2017 22:52:02 -0700 Subject: [PATCH] [doclint] introduce class members This patch unifies Documentation.Method with Documentation.Property, making it possible to verify sorting order across methods and properties. References #14. --- docs/api.md | 49 +++++++++++--------- utils/doclint/Documentation.js | 57 +++++++++++++++++------- utils/doclint/JSBuilder.js | 14 +++--- utils/doclint/MDBuilder.js | 14 +++--- utils/doclint/lint.js | 23 +++++----- utils/doclint/test/07-sorting/doc.md | 2 + utils/doclint/test/07-sorting/foo.js | 1 + utils/doclint/test/golden/07-sorting.txt | 3 +- 8 files changed, 97 insertions(+), 66 deletions(-) diff --git a/docs/api.md b/docs/api.md index c5aad848..8d9cbc7e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -9,9 +9,9 @@ * [browser.close()](#browserclose) * [browser.closePage(page)](#browserclosepagepage) * [browser.newPage()](#browsernewpage) - * [browser.version()](#browserversion) * [browser.stderr](#browserstderr) * [browser.stdout](#browserstdout) + * [browser.version()](#browserversion) - [class: Page](#class-page) * [page.addScriptTag(url)](#pageaddscripttagurl) * [page.click(selector)](#pageclickselector) @@ -56,25 +56,25 @@ * [frame.url()](#frameurl) * [frame.waitFor(selector)](#framewaitforselector) - [class: Request](#class-request) - * [request.response()](#requestresponse) * [request.headers](#requestheaders) * [request.method](#requestmethod) + * [request.response()](#requestresponse) * [request.url](#requesturl) - [class: Response](#class-response) * [response.headers](#responseheaders) * [response.ok](#responseok) + * [response.request()](#responserequest) * [response.status](#responsestatus) * [response.statusText](#responsestatustext) * [response.url](#responseurl) - * [response.request()](#responserequest) - [class: InterceptedRequest](#class-interceptedrequest) - * [interceptedRequest.headers](#interceptedrequestheaders) - * [interceptedRequest.method](#interceptedrequestmethod) - * [interceptedRequest.url](#interceptedrequesturl) - * [interceptedRequest.postData](#interceptedrequestpostdata) * [interceptedRequest.abort()](#interceptedrequestabort) * [interceptedRequest.continue()](#interceptedrequestcontinue) + * [interceptedRequest.headers](#interceptedrequestheaders) * [interceptedRequest.isHandled()](#interceptedrequestishandled) + * [interceptedRequest.method](#interceptedrequestmethod) + * [interceptedRequest.postData](#interceptedrequestpostdata) + * [interceptedRequest.url](#interceptedrequesturl) - [class: Headers](#class-headers) * [headers.append(name, value)](#headersappendname-value) * [headers.delete(name)](#headersdeletename) @@ -122,10 +122,6 @@ Closes chromium application with all the pages (if any were opened). The browser Create a new page in browser and returns a promise which gets resolved with a Page object. -#### browser.version() -- returns: <[Promise]<[string]>> - - #### browser.stderr - <[stream.Readable]> @@ -136,6 +132,9 @@ A Readable Stream that represents the browser process's stderr. A Readable Stream that represents the browser process's stdout. +#### browser.version() +- returns: <[Promise]<[string]>> + ### class: Page Page provides interface to interact with a tab in a browser. Pages are created by browser: @@ -339,7 +338,6 @@ Dialog's type, could be one of the `alert`, `beforeunload`, `confirm` and `promp ### class: Request -#### request.response() #### request.headers - <[Headers]> @@ -350,6 +348,9 @@ Contains the associated [Headers] object of the request. Contains the request's method (GET, POST, etc.) + +#### request.response() + #### request.url - <[string]> @@ -367,6 +368,7 @@ Contains the [Headers] object associated with the response. Contains a boolean stating whether the response was successful (status in the range 200-299) or not. +#### response.request() #### response.status - <[number]> @@ -378,39 +380,44 @@ Contains the status code of the response (e.g., 200 for a success). Contains the status message corresponding to the status code (e.g., OK for 200). + + + #### response.url - <[string]> Contains the URL of the response. -#### response.request() + ### class: InterceptedRequest +#### interceptedRequest.abort() +#### interceptedRequest.continue() #### interceptedRequest.headers - <[Headers]> Contains the [Headers] object associated with the request. +#### interceptedRequest.isHandled() + #### interceptedRequest.method - <[string]> Contains the request's method (GET, POST, etc.) -#### interceptedRequest.url -- <[string]> - -Contains the URL of the request. - #### interceptedRequest.postData - <[string]> In case of a `POST` request, contains `POST` data. -#### interceptedRequest.abort() -#### interceptedRequest.continue() -#### interceptedRequest.isHandled() +#### interceptedRequest.url +- <[string]> + +Contains the URL of the request. + + ### class: Headers #### headers.append(name, value) diff --git a/utils/doclint/Documentation.js b/utils/doclint/Documentation.js index 1fe1b61f..490a4df3 100644 --- a/utils/doclint/Documentation.js +++ b/utils/doclint/Documentation.js @@ -74,15 +74,15 @@ class Documentation { if (classes.has(cls.name)) errors.push(`Duplicate declaration of class ${cls.name}`); classes.add(cls.name); - let methods = new Set(); - for (let method of cls.methodsArray) { - if (methods.has(method.name)) - errors.push(`Duplicate declaration of method ${cls.name}.${method.name}()`); - methods.add(method.name); + let members = new Set(); + for (let member of cls.membersArray) { + if (members.has(member.name)) + errors.push(`Duplicate declaration of method ${cls.name}.${member.name}()`); + members.add(member.name); let args = new Set(); - for (let arg of method.argsArray) { + for (let arg of member.argsArray) { if (args.has(arg.name)) - errors.push(`Duplicate declaration of argument ${cls.name}.${method.name} "${arg.name}"`); + errors.push(`Duplicate declaration of argument ${cls.name}.${member.name} "${arg.name}"`); args.add(arg.name); } } @@ -94,32 +94,55 @@ class Documentation { Documentation.Class = class { /** * @param {string} name - * @param {!Array} methodsArray - * @param {!Array} propertiesArray + * @param {!Array} membersArray */ - constructor(name, methodsArray, propertiesArray) { + constructor(name, membersArray) { this.name = name; - this.methodsArray = methodsArray; + this.membersArray = membersArray; + this.members = new Map(); + this.properties = new Map(); this.methods = new Map(); - for (let method of methodsArray) - this.methods.set(method.name, method); - this.propertiesArray = propertiesArray; - this.properties = new Set(propertiesArray); + for (let member of membersArray) { + this.members.set(member.name, member); + if (member.type === 'method') + this.methods.set(member.name, member); + else if (member.type === 'property') + this.properties.set(member.name, member); + } } }; -Documentation.Method = class { +Documentation.Member = class { /** + * @param {string} type * @param {string} name * @param {!Array} argsArray */ - constructor(name, argsArray) { + constructor(type, name, argsArray) { + this.type = type; this.name = name; this.argsArray = argsArray; this.args = new Map(); for (let arg of argsArray) this.args.set(arg.name, arg); } + + /** + * @param {string} name + * @param {!Array} argsArray + * @return {!Documentation.Member} + */ + static createMethod(name, argsArray) { + return new Documentation.Member('method', name, argsArray); + } + + /** + * @param {string} name + * @return {!Documentation.Member} + */ + static createProperty(name) { + return new Documentation.Member('property', name, []); + } }; Documentation.Argument = class { diff --git a/utils/doclint/JSBuilder.js b/utils/doclint/JSBuilder.js index 30f818e4..26866171 100644 --- a/utils/doclint/JSBuilder.js +++ b/utils/doclint/JSBuilder.js @@ -8,8 +8,7 @@ class JSOutline { constructor(text) { this.classes = []; this._currentClassName = null; - this._currentClassMethods = []; - this._currentClassProperties = []; + this._currentClassMembers = []; this._text = text; let ast = esprima.parseScript(this._text, {loc: true, range: true}); @@ -35,8 +34,8 @@ class JSOutline { for (let param of node.value.params) args.push(new Documentation.Argument(this._extractText(param))); let methodName = this._extractText(node.key); - let method = new Documentation.Method(methodName, args); - this._currentClassMethods.push(method); + let method = Documentation.Member.createMethod(methodName, args); + this._currentClassMembers.push(method); // Extract properties from constructor. if (node.kind === 'constructor') { let walker = new ESTreeWalker(node => { @@ -46,7 +45,7 @@ class JSOutline { if (node.type === 'MemberExpression' && node.object && node.object.type === 'ThisExpression' && node.property && node.property.type === 'Identifier') - this._currentClassProperties.push(node.property.name); + this._currentClassMembers.push(Documentation.Member.createProperty(node.property.name)); }); walker.walk(node); } @@ -60,11 +59,10 @@ class JSOutline { _flushClassIfNeeded() { if (this._currentClassName === null) return; - let jsClass = new Documentation.Class(this._currentClassName, this._currentClassMethods, this._currentClassProperties); + let jsClass = new Documentation.Class(this._currentClassName, this._currentClassMembers); this.classes.push(jsClass); this._currentClassName = null; - this._currentClassMethods = []; - this._currentClassProperties = []; + this._currentClassMembers = []; } _extractText(node) { diff --git a/utils/doclint/MDBuilder.js b/utils/doclint/MDBuilder.js index 44801ece..045b1cfc 100644 --- a/utils/doclint/MDBuilder.js +++ b/utils/doclint/MDBuilder.js @@ -53,8 +53,7 @@ class MDOutline { const methodRegex = /^(\w+)\.(\w+)\((.*)\)$/; const propertyRegex = /^(\w+)\.(\w+)$/; let currentClassName = null; - let currentClassMethods = []; - let currentClassProperties = []; + let currentClassMembers = []; for (const cls of classes) { let match = cls.name.match(classHeading); if (!match) @@ -84,8 +83,8 @@ class MDOutline { if (parameters !== member.args.join(', ')) this.errors.push(`Heading arguments for "${member.name}" do not match described ones, i.e. "${parameters}" != "${member.args.join(', ')}"`); let args = member.args.map(arg => new Documentation.Argument(arg)); - let method = new Documentation.Method(methodName, args); - currentClassMethods.push(method); + let method = Documentation.Member.createMethod(methodName, args); + currentClassMembers.push(method); } function handleProperty(member, className, propertyName) { @@ -93,16 +92,15 @@ class MDOutline { this.errors.push(`Failed to process header as property: ${member.name}`); return; } - currentClassProperties.push(propertyName); + currentClassMembers.push(Documentation.Member.createProperty(propertyName)); } function flushClassIfNeeded() { if (currentClassName === null) return; - this.classes.push(new Documentation.Class(currentClassName, currentClassMethods, currentClassProperties)); + this.classes.push(new Documentation.Class(currentClassName, currentClassMembers)); currentClassName = null; - currentClassMethods = []; - currentClassProperties = []; + currentClassMembers = []; } } } diff --git a/utils/doclint/lint.js b/utils/doclint/lint.js index 012315de..fb1208b4 100644 --- a/utils/doclint/lint.js +++ b/utils/doclint/lint.js @@ -63,17 +63,19 @@ function lintMarkdown(doc) { const errors = []; // Methods should be sorted alphabetically. for (let cls of doc.classesArray) { - for (let i = 0; i < cls.methodsArray.length - 1; ++i) { + for (let i = 0; i < cls.membersArray.length - 1; ++i) { // Constructor always goes first. - if (cls.methodsArray[i].name === 'constructor') { + if (cls.membersArray[i].name === 'constructor') { if (i > 0) errors.push(`Constructor of ${cls.name} should go before other methods`); continue; } - let method1 = cls.methodsArray[i]; - let method2 = cls.methodsArray[i + 1]; - if (method1.name > method2.name) - errors.push(`${cls.name}.${method1.name} breaks alphabetic methods sorting inside class ${cls.name}`); + let member1 = cls.membersArray[i]; + let member2 = cls.membersArray[i + 1]; + if (member1.name > member2.name) { + let memberName = `${cls.name}.${member1.name}` + (member1.type === 'method' ? '()' : ''); + errors.push(`${memberName} breaks alphabetic member sorting inside class ${cls.name}`); + } } } return errors; @@ -89,13 +91,12 @@ function filterJSDocumentation(jsDocumentation) { for (let cls of jsDocumentation.classesArray) { if (EXCLUDE_CLASSES.has(cls.name)) continue; - let methods = cls.methodsArray.filter(method => { - if (method.name.startsWith('_')) + let members = cls.membersArray.filter(member => { + if (member.name.startsWith('_')) return false; - return !EXCLUDE_METHODS.has(`${cls.name}.${method.name}`); + return !EXCLUDE_METHODS.has(`${cls.name}.${member.name}`); }); - let properties = cls.propertiesArray.filter(property => !property.startsWith('_')); - classes.push(new Documentation.Class(cls.name, methods, properties)); + classes.push(new Documentation.Class(cls.name, members)); } return new Documentation(classes); } diff --git a/utils/doclint/test/07-sorting/doc.md b/utils/doclint/test/07-sorting/doc.md index 5e9c03e7..a38a941a 100644 --- a/utils/doclint/test/07-sorting/doc.md +++ b/utils/doclint/test/07-sorting/doc.md @@ -4,6 +4,8 @@ #### new Foo() +#### foo.ddd + #### foo.ccc() #### foo.bbb() diff --git a/utils/doclint/test/07-sorting/foo.js b/utils/doclint/test/07-sorting/foo.js index b904f3c5..cf442a87 100644 --- a/utils/doclint/test/07-sorting/foo.js +++ b/utils/doclint/test/07-sorting/foo.js @@ -1,5 +1,6 @@ class Foo { constructor() { + this.ddd = 10; } aaa() {} diff --git a/utils/doclint/test/golden/07-sorting.txt b/utils/doclint/test/golden/07-sorting.txt index 567dc2c3..9a41d6df 100644 --- a/utils/doclint/test/golden/07-sorting.txt +++ b/utils/doclint/test/golden/07-sorting.txt @@ -1,2 +1,3 @@ [MarkDown] Constructor of Foo should go before other methods -[MarkDown] Foo.ccc breaks alphabetic methods sorting inside class Foo \ No newline at end of file +[MarkDown] Foo.ddd breaks alphabetic member sorting inside class Foo +[MarkDown] Foo.ccc() breaks alphabetic member sorting inside class Foo \ No newline at end of file