feat: support inner types for things like Promise<Boolean>

This commit is contained in:
Samuel Attard 2018-09-19 16:44:23 +10:00
Родитель a6444d7682
Коммит 3ba4721268
7 изменённых файлов: 4811 добавлений и 100 удалений

Просмотреть файл

@ -4,20 +4,39 @@ const cheerio = require('cheerio')
const cleanDeep = require('clean-deep')
const entities = require('entities')
const parameterPattern = /<code>((?:...)?\w+?)<\/code>\s*(\(?(?:(?:(?:<a href.*?>)?[a-zA-Z0-9[\]]+(?:<\/a>)?(?: \| )?)+)\)?(?:\[])?)(?: - )?([\s\S]*)/m
const parameterPattern = /<code>((?:...)?\w+?)<\/code>\s*(\(?(?:(?:(?:<a href.*?>)?[a-zA-Z0-9[\]]+(?:<(?:[a-zA-Z[\]]+(?: \| )?)+>)?(?:<\/a>)?(?: \| )?)+)\)?(?:\[])?)(?: - )?([\s\S]*)/m
const methodReturnPattern = /^Returns (<a.+?>)?<code>(.*?)<\/code>(<\/a>)?/g
const multiTypePattern = /(?:<a href.*?>)?([a-zA-Z0-9[\]]+)(?:<\/a>)?(?: \| )?/mg
const multiTypePattern = /(?:<a href.*?>)?([a-zA-Z0-9[\]]+(?:<(?:[a-zA-Z[\]]+(?: \| )?)+>)?)(?:<\/a>)?(?: \| )?/mg
const innerTypePattern = /([a-zA-Z0-9]+)(?:<(.+)>)?/g
const stripArrTags = (type) => {
if (Array.isArray(type)) {
return type.map(typeName => ({
typeName: stripArrTags(typeName),
collection: typeName.endsWith('[]') || (typeof multitypify(typeName) === 'string' && multitypify(typeName).endsWith('[]'))
}))
return type.map(typeName => {
const typeInfo = getInnerType(stripArrTags(typeName))
return {
typeName: typeInfo.outer,
collection: typeName.endsWith('[]') || (typeof multitypify(typeName) === 'string' && multitypify(typeName).endsWith('[]')),
innerType: typeInfo.inner
}
})
} else {
return type.endsWith('[]') ? type.substr(0, type.length - 2) : type
}
}
const getInnerType = (type) => {
if (typeof type === 'string') {
innerTypePattern.lastIndex = 0
const execResult = innerTypePattern.exec(type)
const outer = execResult[1]
if (!outer) throw new Error('Outer type should never be null but it was for: ' + type)
var inner = execResult[2]
if (inner) {
inner = stripArrTags(multitypify(inner))
}
return { outer, inner }
}
return { outer: type, inner: undefined }
}
const multitypify = (fullTypeString) => {
let typeString = fullTypeString
if (typeString.startsWith('(') && typeString.endsWith(')')) {
@ -97,7 +116,7 @@ const getPossibleValues = ($, el, description, api) => {
const generateObjectProps = ($, topUL, api) => {
return topUL.find('> li')
.map((i, el) => {
var match = $(el).html().match(parameterPattern)
var match = entities.decodeHTML($(el).html()).match(parameterPattern)
if (!match) {
console.error('parameter subproperties')
console.error(`parameterPattern: ${parameterPattern}`)
@ -115,10 +134,12 @@ const generateObjectProps = ($, topUL, api) => {
.filter(function () { return this.type === 'text' || (this.type === 'tag' && this.name === 'code') }) // avoid nested ULs
.text()
const typeInfo = getInnerType(stripArrTags(multitypify(match[2])))
const ret = {
name: match[1],
type: stripArrTags(multitypify(match[2])),
type: typeInfo.outer,
collection: match[2].endsWith('[]') || (typeof multitypify(match[2]) === 'string' && multitypify(match[2]).endsWith('[]')),
innerType: typeInfo.inner,
description: sanitizeDescription(description),
required: !description.match(/\(optional\)/i)
}
@ -157,6 +178,7 @@ const generateObjectProps = ($, topUL, api) => {
module.exports = {
stripArrTags,
generateObjectProps,
getInnerType,
// Find all paragraphs in a swath
description: ($, openingHeadingElement) =>
textify($, swath($, openingHeadingElement)
@ -176,7 +198,7 @@ module.exports = {
let properties
if (returnText) {
type = methodReturnPattern.exec(returnHTML)[2]
type = entities.decodeHTML(methodReturnPattern.exec(returnHTML)[2])
description = returnText.split(`Returns ${type}`)[1].replace(/^ - /g, '').replace(/^:/g, '').trim()
}
if (type === 'Object') {
@ -195,9 +217,11 @@ module.exports = {
if (type === 'String') {
possibleValues = getPossibleValues($, returnPara, sanitizeDescription(preserveBackTicks($(returnPara).html())), api)
}
const typeInfo = getInnerType(stripArrTags(multitypify(type)))
const ret = {
type: stripArrTags(multitypify(type)),
type: typeInfo.outer,
collection: type.endsWith('[]') || (typeof multitypify(type) === 'string' && multitypify(type).endsWith('[]')),
innerType: typeInfo.inner,
description,
properties,
possibleValues
@ -229,7 +253,7 @@ module.exports = {
.first()
.find('> li')
.map((i, el) => {
var match = $(el).html()
var match = entities.decodeHTML($(el).html())
.match(parameterPattern)
// Parsing error
@ -249,10 +273,12 @@ module.exports = {
.filter(function () { return this.type === 'text' }) // avoid nested ULs
.text()
const typeInfo = getInnerType(stripArrTags(multitypify(match[2])))
var arg = {
name: match[1],
type: stripArrTags(multitypify(match[2])),
type: typeInfo.outer,
collection: match[2].endsWith('[]') || (typeof multitypify(match[2]) === 'string' && multitypify(match[2]).endsWith('[]')),
innerType: typeInfo.inner,
description: sanitizeDescription(description),
required: !description.match(/\(optional\)/i)
}

Просмотреть файл

@ -4,8 +4,8 @@ const CollectionItem = require('./collection-item')
const parsers = require('./parsers')
const pattern = /<code>.*?\.(?:(\w+)\.)?(\w+)<\/code>/
const typePattern = /An? ([a-zA-Z]+(?:\[])?) ?/
const props = ['name', 'description', 'type', 'collection', '_superObject']
const typePattern = /An? ([a-zA-Z]+(?:<[a-zA-Z[\]]+>)?(?:\[])?) ?/
const props = ['name', 'description', 'type', 'innerType', 'collection', '_superObject']
module.exports = class Property extends CollectionItem {
constructor (api, el) {
@ -18,7 +18,9 @@ module.exports = class Property extends CollectionItem {
this.type = null
const typeMatch = typePattern.exec(this.description)
if (typeMatch) {
this.type = parsers.stripArrTags(typeMatch[1])
const typeInfo = parsers.getInnerType(parsers.stripArrTags(typeMatch[1]))
this.type = typeInfo.outer
this.innerType = typeInfo.inner
this.collection = typeMatch[1].endsWith('[]')
}
}

4782
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

6
test/fixtures/electron/docs/api/app.md поставляемый
Просмотреть файл

@ -517,7 +517,7 @@ The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
Returns `Boolean` - Whether the call succeeded.
Returns `Promise<Boolean | null | Point | Rect[]>` - Whether the call succeeded.
This method checks if the current executable as the default handler for a
protocol (aka URI scheme). If so, it will remove the app as the default handler.
@ -525,11 +525,11 @@ protocol (aka URI scheme). If so, it will remove the app as the default handler.
### `app.isDefaultProtocolClient(protocol[, path, args])` _macOS_ _Windows_
* `protocol` String - The name of your protocol, without `://`.
* `protocol` Promise<String> - The name of your protocol, without `://`.
* `path` String (optional) _Windows_ - Defaults to `process.execPath`
* `args` String[] (optional) _Windows_ - Defaults to an empty array
Returns `Boolean`
Returns `Promise<boolean>`
This method checks if the current executable is the default handler for a protocol
(aka URI scheme). If so, it will return true. Otherwise, it will return false.

Просмотреть файл

@ -573,7 +573,7 @@ events.
#### `win.id`
A `Integer` representing the unique ID of the window.
A `Promise<Integer>` representing the unique ID of the window.
### Instance Methods

2
test/fixtures/electron/docs/api/screen.md поставляемый
Просмотреть файл

@ -93,7 +93,7 @@ The `screen` module has the following methods:
Returns `Object`:
* `x` Integer
* `x` Promise<Integer>
* `y` Integer
The current absolute position of the mouse pointer.

Просмотреть файл

@ -159,6 +159,39 @@ describe('APIs', function () {
expect(method.description).to.exist
})
})
they('can return promises with inner types', function () {
expect(apis.app.methods.isDefaultProtocolClient).to.exist
expect(apis.app.methods.isDefaultProtocolClient.returns.type).to.eq('Promise')
expect(apis.app.methods.isDefaultProtocolClient.returns.innerType).to.eq('boolean')
})
they('can return promises with complex inner types', function () {
expect(apis.app.methods.removeAsDefaultProtocolClient).to.exist
expect(apis.app.methods.removeAsDefaultProtocolClient.returns.type).to.eq('Promise')
expect(apis.app.methods.removeAsDefaultProtocolClient.returns.innerType).to.deep.eq([
{
collection: false,
innerType: undefined,
typeName: 'Boolean'
},
{
collection: false,
innerType: undefined,
typeName: 'null'
},
{
collection: false,
innerType: undefined,
typeName: 'Point'
},
{
collection: true,
innerType: undefined,
typeName: 'Rect'
}
])
})
})
describe('Parameters', function () {
@ -255,9 +288,11 @@ describe('APIs', function () {
expect(param.type).to.be.an('array')
expect(param.type).to.deep.equal([{
typeName: 'String',
innerType: undefined,
collection: false
}, {
typeName: 'Buffer',
innerType: undefined,
collection: false
}])
})
@ -269,6 +304,12 @@ describe('APIs', function () {
expect(param.type[1].typeName).to.equal('null')
expect(param, 'should have function parameters').to.have.property('parameters')
})
they('can be promises with inner types', function () {
var param = apis.app.methods.isDefaultProtocolClient.parameters[0]
expect(param.type).to.eq('Promise')
expect(param.innerType).to.eq('String')
})
})
describe('Static Methods', function () {
@ -360,6 +401,12 @@ describe('APIs', function () {
they('are marked `required` for super objects', function () {
apis.app.properties.forEach(prop => expect(prop.required).to.equal(true))
})
they('can be promises with inner types', function () {
var prop = apis.BrowserWindow.instanceProperties.id
expect(prop.type).to.eq('Promise')
expect(prop.innerType).to.eq('Integer')
})
})
describe('Instance Properties', function () {
@ -393,8 +440,8 @@ describe('APIs', function () {
expect(props[0].description).to.include('object this window owns')
expect(props[0].type).to.equal('WebContents')
expect(props[1].name).to.equal('id')
expect(props[1].description).to.equal('A Integer representing the unique ID of the window.')
expect(props[1].type).to.equal('Integer')
expect(props[1].description).to.equal('A Promise<Integer> representing the unique ID of the window.')
expect(props[1].type).to.equal('Promise')
})
})
@ -500,6 +547,16 @@ describe('APIs', function () {
expect(method.returns.properties[1].type).to.equal('JumpListItem')
expect(method.returns.properties[1].collection).to.equal(true)
})
it('resolve promises in deep objects as return values', function () {
const method = apis.screen.methods.getCursorScreenPoint
expect(method.returns.type).to.equal('Object')
expect(method.returns.properties.length).to.equal(2)
const prop = method.returns.properties[0]
expect(prop.name).to.eq('x')
expect(prop.type).to.eq('Promise')
expect(prop.innerType).to.eq('Integer')
})
})
describe('Returns', function () {