From a211666fd8f9933963a463606b49f81d5e076b6c Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Fri, 26 Feb 2021 11:24:15 +0100 Subject: [PATCH 1/4] Added generic source option to layer option types --- src/ol/layer/BaseImage.js | 5 +++-- src/ol/layer/BaseTile.js | 5 +++-- src/ol/layer/BaseVector.js | 5 +++-- src/ol/layer/Image.js | 2 +- src/ol/layer/Layer.js | 5 +++-- src/ol/layer/Tile.js | 2 +- src/ol/layer/Vector.js | 2 +- src/ol/layer/VectorImage.js | 5 +++-- src/ol/layer/VectorTile.js | 4 +++- src/ol/layer/WebGLPoints.js | 5 +++-- 10 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/ol/layer/BaseImage.js b/src/ol/layer/BaseImage.js index 798c38c111..8c7e38c376 100644 --- a/src/ol/layer/BaseImage.js +++ b/src/ol/layer/BaseImage.js @@ -4,6 +4,7 @@ import Layer from './Layer.js'; /** + * @template {import("../source/Image.js").default} ImageSourceType * @typedef {Object} Options * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. * @property {number} [opacity=1] Opacity (0, 1). @@ -26,7 +27,7 @@ import Layer from './Layer.js'; * this layer in its layers collection, and the layer will be rendered on top. This is useful for * temporary layers. The standard way to add a layer to a map and have it managed by the map is to * use {@link module:ol/Map#addLayer}. - * @property {import("../source/Image.js").default} [source] Source for this layer. + * @property {ImageSourceType} [source] Source for this layer. * @property {Object} [properties] Arbitrary observable properties. Can be accessed with `#get()` and `#set()`. */ @@ -44,7 +45,7 @@ import Layer from './Layer.js'; */ class BaseImageLayer extends Layer { /** - * @param {Options} [opt_options] Layer options. + * @param {Options} [opt_options] Layer options. */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/layer/BaseTile.js b/src/ol/layer/BaseTile.js index 4d6cede4ac..4207a737df 100644 --- a/src/ol/layer/BaseTile.js +++ b/src/ol/layer/BaseTile.js @@ -6,6 +6,7 @@ import TileProperty from './TileProperty.js'; import {assign} from '../obj.js'; /** + * @template {import("../source/Tile.js").default} TileSourceType * @typedef {Object} Options * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. * @property {number} [opacity=1] Opacity (0, 1). @@ -26,7 +27,7 @@ import {assign} from '../obj.js'; * be visible. * @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0` * means no preloading. - * @property {import("../source/Tile.js").default} [source] Source for this layer. + * @property {TileSourceType} [source] Source for this layer. * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * this layer in its layers collection, and the layer will be rendered on top. This is useful for * temporary layers. The standard way to add a layer to a map and have it managed by the map is to @@ -49,7 +50,7 @@ import {assign} from '../obj.js'; */ class BaseTileLayer extends Layer { /** - * @param {Options} [opt_options] Tile layer options. + * @param {Options} [opt_options] Tile layer options. */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/layer/BaseVector.js b/src/ol/layer/BaseVector.js index b7da84dbd3..8c8ad65f16 100644 --- a/src/ol/layer/BaseVector.js +++ b/src/ol/layer/BaseVector.js @@ -10,6 +10,7 @@ import { } from '../style/Style.js'; /** + * @template {import("../source/Vector.js").default|import("../source/VectorTile.js").default} VectorSourceType * @typedef {Object} Options * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. * @property {number} [opacity=1] Opacity (0, 1). @@ -34,7 +35,7 @@ import { * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the * renderer when getting features from the vector source for the rendering or hit-detection. * Recommended value: the size of the largest symbol, line width or label. - * @property {import("../source/Vector.js").default} [source] Source. + * @property {VectorSourceType} [source] Source. * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * this layer in its layers collection, and the layer will be rendered on top. This is useful for * temporary layers. The standard way to add a layer to a map and have it managed by the map is to @@ -77,7 +78,7 @@ const Property = { */ class BaseVectorLayer extends Layer { /** - * @param {Options} [opt_options] Options. + * @param {Options} [opt_options] Options. */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/layer/Image.js b/src/ol/layer/Image.js index 1d52dbe43f..90c49319c5 100644 --- a/src/ol/layer/Image.js +++ b/src/ol/layer/Image.js @@ -18,7 +18,7 @@ import CanvasImageLayerRenderer from '../renderer/canvas/ImageLayer.js'; */ class ImageLayer extends BaseImageLayer { /** - * @param {import("./BaseImage.js").Options} [opt_options] Layer options. + * @param {import("./BaseImage.js").Options} [opt_options] Layer options. */ constructor(opt_options) { super(opt_options); diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index f5b3cd1219..1dc67905d2 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -16,6 +16,7 @@ import {listen, unlistenByKey} from '../events.js'; */ /** + * @template {import("../source/Source.js").default} SourceType * @typedef {Object} Options * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. * @property {number} [opacity=1] Opacity (0, 1). @@ -34,7 +35,7 @@ import {listen, unlistenByKey} from '../events.js'; * visible. * @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will * be visible. - * @property {import("../source/Source.js").default} [source] Source for this layer. If not provided to the constructor, + * @property {SourceType} [source] Source for this layer. If not provided to the constructor, * the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after * construction. * @property {import("../PluggableMap.js").default} [map] Map. @@ -88,7 +89,7 @@ import {listen, unlistenByKey} from '../events.js'; */ class Layer extends BaseLayer { /** - * @param {Options} options Layer options. + * @param {Options} options Layer options. */ constructor(options) { const baseOptions = assign({}, options); diff --git a/src/ol/layer/Tile.js b/src/ol/layer/Tile.js index 0898da7457..ed9dbd6554 100644 --- a/src/ol/layer/Tile.js +++ b/src/ol/layer/Tile.js @@ -18,7 +18,7 @@ import CanvasTileLayerRenderer from '../renderer/canvas/TileLayer.js'; */ class TileLayer extends BaseTileLayer { /** - * @param {import("./BaseTile.js").Options} [opt_options] Tile layer options. + * @param {import("./BaseTile.js").Options} [opt_options] Tile layer options. */ constructor(opt_options) { super(opt_options); diff --git a/src/ol/layer/Vector.js b/src/ol/layer/Vector.js index 1de881bc95..5658ed3854 100644 --- a/src/ol/layer/Vector.js +++ b/src/ol/layer/Vector.js @@ -17,7 +17,7 @@ import CanvasVectorLayerRenderer from '../renderer/canvas/VectorLayer.js'; */ class VectorLayer extends BaseVectorLayer { /** - * @param {import("./BaseVector.js").Options} [opt_options] Options. + * @param {import("./BaseVector.js").Options} [opt_options] Options. */ constructor(opt_options) { super(opt_options); diff --git a/src/ol/layer/VectorImage.js b/src/ol/layer/VectorImage.js index ecb07c0723..9c01895cd7 100644 --- a/src/ol/layer/VectorImage.js +++ b/src/ol/layer/VectorImage.js @@ -6,6 +6,7 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer. import {assign} from '../obj.js'; /** + * @template {import("../source/Vector.js").default} VectorSourceType * @typedef {Object} Options * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. * @property {number} [opacity=1] Opacity (0, 1). @@ -30,7 +31,7 @@ import {assign} from '../obj.js'; * @property {number} [renderBuffer=100] The buffer in pixels around the viewport extent used by the * renderer when getting features from the vector source for the rendering or hit-detection. * Recommended value: the size of the largest symbol, line width or label. - * @property {import("../source/Vector.js").default} [source] Source. + * @property {VectorSourceType} [source] Source. * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * this layer in its layers collection, and the layer will be rendered on top. This is useful for * temporary layers. The standard way to add a layer to a map and have it managed by the map is to @@ -58,7 +59,7 @@ import {assign} from '../obj.js'; */ class VectorImageLayer extends BaseVectorLayer { /** - * @param {Options} [opt_options] Options. + * @param {Options} [opt_options] Options. */ constructor(opt_options) { const options = opt_options ? opt_options : {}; diff --git a/src/ol/layer/VectorTile.js b/src/ol/layer/VectorTile.js index 538b523a06..ca11b7f2d6 100644 --- a/src/ol/layer/VectorTile.js +++ b/src/ol/layer/VectorTile.js @@ -88,7 +88,9 @@ class VectorTileLayer extends BaseVectorLayer { delete baseOptions.preload; delete baseOptions.useInterimTilesOnError; - super(/** @type {import("./BaseVector.js").Options} */ (baseOptions)); + super( + /** @type {import("./BaseVector.js").Options} */ (baseOptions) + ); if (options.renderMode === VectorTileRenderType.IMAGE) { //FIXME deprecated - remove this check in v7. diff --git a/src/ol/layer/WebGLPoints.js b/src/ol/layer/WebGLPoints.js index 6681d95ad9..0597ebf1d0 100644 --- a/src/ol/layer/WebGLPoints.js +++ b/src/ol/layer/WebGLPoints.js @@ -7,6 +7,7 @@ import {assign} from '../obj.js'; import {parseLiteralStyle} from '../webgl/ShaderBuilder.js'; /** + * @template {import("../source/Vector.js").default} VectorSourceType * @typedef {Object} Options * @property {import('../style/literal.js').LiteralStyle} style Literal style to apply to the layer features. * @property {string} [className='ol-layer'] A CSS class name to set to the layer element. @@ -26,7 +27,7 @@ import {parseLiteralStyle} from '../webgl/ShaderBuilder.js'; * visible. * @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will * be visible. - * @property {import("../source/Vector.js").default} [source] Source. + * @property {VectorSourceType} [source] Source. * @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will * prevent all hit detection on the layer. * @property {Object} [properties] Arbitrary observable properties. Can be accessed with `#get()` and `#set()`. @@ -73,7 +74,7 @@ import {parseLiteralStyle} from '../webgl/ShaderBuilder.js'; */ class WebGLPointsLayer extends Layer { /** - * @param {Options} options Options. + * @param {Options} options Options. */ constructor(options) { const baseOptions = assign({}, options); From a5d0b619473c822e8b1a9d2aa9ec14089f8f9185 Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Fri, 16 Apr 2021 16:13:30 +0200 Subject: [PATCH 2/4] Allow options to be generic in jsdoc --- config/jsdoc/plugins/inline-options.cjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/jsdoc/plugins/inline-options.cjs b/config/jsdoc/plugins/inline-options.cjs index 61bd7e5990..15d7fdeeb9 100644 --- a/config/jsdoc/plugins/inline-options.cjs +++ b/config/jsdoc/plugins/inline-options.cjs @@ -32,7 +32,11 @@ exports.handlers = { for (let j = 0, jj = params.length; j < jj; ++j) { const param = params[j]; if (param.type && param.type.names) { - const type = param.type.names[0]; + let type = param.type.names[0]; + const genericMatches = type.match(/(^.*?)\.?<.*>/); + if (genericMatches) { + type = genericMatches[1]; + } if (type in properties) { param.type.names[0] = type; params.push.apply( From aa99de4a6bd8e1e4119cde5be98f4531d562a72d Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Fri, 16 Apr 2021 20:06:06 +0200 Subject: [PATCH 3/4] parse comment for templates --- config/jsdoc/plugins/inline-options.cjs | 68 +++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/config/jsdoc/plugins/inline-options.cjs b/config/jsdoc/plugins/inline-options.cjs index 15d7fdeeb9..ce2d12f880 100644 --- a/config/jsdoc/plugins/inline-options.cjs +++ b/config/jsdoc/plugins/inline-options.cjs @@ -5,7 +5,48 @@ * Inlines option params from typedefs */ -const properties = {}; +const docletInfos = {}; + +/** + * This parses the comment for `@template` annotations and returns an object with name / type pairs for all template + * values + * @param {string} comment a jsdoc comment to parse + * @return {Object} results + */ +function parseCommentForTemplates(comment) { + let remainingText = comment; + const results = {}; + while (true) { + const templateMatch = remainingText.match(/\* @template\s*([\s\S]*)/); + + if (!templateMatch) { + return results; + } + + remainingText = templateMatch[1]; + + let type = '*'; + + if (remainingText[0] === '{') { + let index = 1; + let openParenthesis = 1; + while (openParenthesis > 0) { + if (remainingText[index] === '{') { + openParenthesis++; + } else if (remainingText[index] === '}') { + openParenthesis--; + } + index++; + } + type = remainingText.slice(1, index - 1); + remainingText = remainingText.slice(index); + } + + const name = remainingText.match(/\s*(\S*)/)[1]; + + results[name] = type; + } +} exports.handlers = { /** @@ -14,7 +55,9 @@ exports.handlers = { */ newDoclet: function (e) { if (e.doclet.kind == 'typedef' && e.doclet.properties) { - properties[e.doclet.longname] = e.doclet.properties; + docletInfos[e.doclet.longname] = { + properties: e.doclet.properties, + }; } }, @@ -25,6 +68,16 @@ exports.handlers = { */ parseComplete: function (e) { const doclets = e.doclets; + + // gather type information + for (let i = 0, ii = doclets.length; i < ii; ++i) { + const doclet = doclets[i]; + if (doclet.longname in docletInfos) { + docletInfos[doclet.longname].type = doclet.type; + } + } + + // inline options for (let i = 0, ii = doclets.length; i < ii; ++i) { const doclet = doclets[i]; if (doclet.params) { @@ -37,13 +90,18 @@ exports.handlers = { if (genericMatches) { type = genericMatches[1]; } - if (type in properties) { - param.type.names[0] = type; + if (type in docletInfos) { + const templateInfo = parseCommentForTemplates(doclet.comment); + param.type = docletInfos[type].type; params.push.apply( params, - properties[type].map((p) => { + docletInfos[type].properties.map((p) => { const property = Object.assign({}, p); property.name = `${param.name}.${property.name}`; + if (property.type.names[0] in templateInfo) { + property.type.names[0] = + templateInfo[property.type.names[0]]; + } return property; }) ); From 9344de5740110e40358a962c21efa84d04434ef6 Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Sat, 17 Apr 2021 09:52:32 +0200 Subject: [PATCH 4/4] remove unneeded code --- config/jsdoc/plugins/inline-options.cjs | 50 ++++++++++--------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/config/jsdoc/plugins/inline-options.cjs b/config/jsdoc/plugins/inline-options.cjs index ce2d12f880..704aa1d585 100644 --- a/config/jsdoc/plugins/inline-options.cjs +++ b/config/jsdoc/plugins/inline-options.cjs @@ -5,7 +5,7 @@ * Inlines option params from typedefs */ -const docletInfos = {}; +const properties = {}; /** * This parses the comment for `@template` annotations and returns an object with name / type pairs for all template @@ -25,23 +25,23 @@ function parseCommentForTemplates(comment) { remainingText = templateMatch[1]; - let type = '*'; - - if (remainingText[0] === '{') { - let index = 1; - let openParenthesis = 1; - while (openParenthesis > 0) { - if (remainingText[index] === '{') { - openParenthesis++; - } else if (remainingText[index] === '}') { - openParenthesis--; - } - index++; - } - type = remainingText.slice(1, index - 1); - remainingText = remainingText.slice(index); + if (remainingText[0] !== '{') { + continue; } + let index = 1; + let openParenthesis = 1; + while (openParenthesis > 0) { + if (remainingText[index] === '{') { + openParenthesis++; + } else if (remainingText[index] === '}') { + openParenthesis--; + } + index++; + } + const type = remainingText.slice(1, index - 1); + remainingText = remainingText.slice(index); + const name = remainingText.match(/\s*(\S*)/)[1]; results[name] = type; @@ -55,9 +55,7 @@ exports.handlers = { */ newDoclet: function (e) { if (e.doclet.kind == 'typedef' && e.doclet.properties) { - docletInfos[e.doclet.longname] = { - properties: e.doclet.properties, - }; + properties[e.doclet.longname] = e.doclet.properties; } }, @@ -69,15 +67,6 @@ exports.handlers = { parseComplete: function (e) { const doclets = e.doclets; - // gather type information - for (let i = 0, ii = doclets.length; i < ii; ++i) { - const doclet = doclets[i]; - if (doclet.longname in docletInfos) { - docletInfos[doclet.longname].type = doclet.type; - } - } - - // inline options for (let i = 0, ii = doclets.length; i < ii; ++i) { const doclet = doclets[i]; if (doclet.params) { @@ -90,12 +79,11 @@ exports.handlers = { if (genericMatches) { type = genericMatches[1]; } - if (type in docletInfos) { + if (type in properties) { const templateInfo = parseCommentForTemplates(doclet.comment); - param.type = docletInfos[type].type; params.push.apply( params, - docletInfos[type].properties.map((p) => { + properties[type].map((p) => { const property = Object.assign({}, p); property.name = `${param.name}.${property.name}`; if (property.type.names[0] in templateInfo) {