diff --git a/CHANGELOG.md b/CHANGELOG.md index 23c02a2..57f9eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## UNRELEASED +## [3.0.1] 17.08.2020 + +- [Fixed] Solve issue #26, support `export { variables as var }` statement. +- [Added] now interface `SvelteDataItem` provides a new property `localName` with information about internal name of component property. + ## [3.0.0] 08.08.2020 - [Fixed] Solve vulnerability issues: diff --git a/README.md b/README.md index f1bfdda..7b0f76f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,11 @@ Generate a JSON documentation for a Svelte file ## Changelog +### [3.0.1] 17.08.2020 + +- [Fixed] Solve issue #26, support `export { variables as var }` statement. +- [Added] now interface `SvelteDataItem` provides a new property `localName` with information about internal name of component property. + ### [3.0.0] 08.08.2020 - [Fixed] Solve vulnerability issues: diff --git a/index.js b/index.js index e52445f..939c621 100644 --- a/index.js +++ b/index.js @@ -74,24 +74,32 @@ function convertVisibilityToLevel(visibility) { return 0; } -function mergeItems(itemType, currentItem, newItem) { +function mergeItems(itemType, currentItem, newItem, ignoreLocations) { if (convertVisibilityToLevel(currentItem.visibility) < convertVisibilityToLevel(newItem.visibility)) { currentItem.visibility = newItem.visibility; } if (!currentItem.description && newItem.description) { - currentItem.description = newItem; + currentItem.description = newItem.description; + } + + if (!currentItem.type || currentItem.type.type === 'any') { + if (newItem.type && newItem.type.type !== 'any') { + currentItem.type = newItem.type; + } } if (!currentItem.keywords && newItem.keywords) { currentItem.keywords = newItem.keywords; } - if (newItem.locations && newItem.locations.length > 0) { - if (currentItem.locations) { - currentItem.locations.push(...newItem.locations); - } else { - currentItem.locations = [...newItem.locations]; + if (!ignoreLocations) { + if (newItem.locations && newItem.locations.length > 0) { + if (currentItem.locations) { + currentItem.locations.push(...newItem.locations); + } else { + currentItem.locations = [...newItem.locations]; + } } } @@ -134,6 +142,14 @@ function subscribeOnParserEvents(parser, options, version, resolve, reject) { parser.on(eventName, (value) => { const itemIndex = component[feature].findIndex(item => item.name === value.name); + if (value.localName) { + const localItem = component[feature].find(item => item.name === value.localName); + + if (localItem) { + value = mergeItems(feature, value, localItem, true); + } + } + if (itemIndex < 0) { component[feature].push(value); } else { diff --git a/lib/v3/parser.js b/lib/v3/parser.js index 9cf1ae1..3b25d80 100644 --- a/lib/v3/parser.js +++ b/lib/v3/parser.js @@ -121,6 +121,7 @@ class Parser extends EventEmitter { type: utils.inferTypeFromVariableDeclaration(variable), importPath: variable.importPath, originalName: variable.originalName, + localName: variable.localName }); if (variable.declarator && variable.declarator.init) { @@ -330,6 +331,7 @@ class Parser extends EventEmitter { if (node.type === 'ExportNamedDeclaration' && level === 0 && parseContext.scopeType !== SCOPE_MARKUP) { const declaration = node.declaration; + const specifiers = node.specifiers; if (declaration) { const exportNodeComment = utils.getCommentFromSourceCode(node, parseContext.sourceCode, { defaultVisibility: 'public', useLeading: true, useTrailing: false }); @@ -352,6 +354,27 @@ class Parser extends EventEmitter { return; } } + + if (specifiers) { + specifiers.forEach(specifier => { + if (specifier.type === 'ExportSpecifier') { + const exportedOrLocalName = specifier.exported + ? specifier.exported.name + : specifier.local.name; + + this.emitDataItem({ + node: specifier, + name: exportedOrLocalName, + localName: specifier.local.name, + kind: 'const', + location: { + start: specifier.exported ? specifier.exported.start : specifier.local.start, + end: specifier.exported ? specifier.exported.end : specifier.local.end + } + }, parseContext, 'public'); + } + }); + } } if (node.type === 'LabeledStatement' && level === 0 && parseContext.scopeType !== SCOPE_MARKUP) { diff --git a/package.json b/package.json index 8d22a7c..596d5f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sveltedoc-parser", - "version": "3.0.0", + "version": "3.0.1", "description": "Generate a JSON documentation for a Svelte file", "main": "index.js", "scripts": { diff --git a/test/svelte3/integration/data/data.export.aliace.svelte b/test/svelte3/integration/data/data.export.aliace.svelte new file mode 100644 index 0000000..0e3fc1f --- /dev/null +++ b/test/svelte3/integration/data/data.export.aliace.svelte @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/test/svelte3/integration/data/data.export.many.svelte b/test/svelte3/integration/data/data.export.many.svelte new file mode 100644 index 0000000..9d21030 --- /dev/null +++ b/test/svelte3/integration/data/data.export.many.svelte @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/test/svelte3/integration/data/data.spec.js b/test/svelte3/integration/data/data.spec.js index bd0c5cc..54ea4c8 100644 --- a/test/svelte3/integration/data/data.spec.js +++ b/test/svelte3/integration/data/data.spec.js @@ -247,4 +247,78 @@ describe('SvelteDoc v3 - Props', () => { done(e); }); }); + + it('Export object statement with multiple variables should be parsed as public props', done => { + parser.parse({ + version: 3, + filename: path.resolve(__dirname, 'data.export.many.svelte'), + features: ['data'], + includeSourceLocations: true, + ignoredVisibilities: [] + }).then((doc) => { + expect(doc, 'Document should be provided').to.exist; + expect(doc.data, 'Document data should be parsed').to.exist; + + expect(doc.data.length).to.equal(2); + + const prop1 = doc.data.find(d => d.name === 'a'); + + expect(prop1.name).to.equal('a'); + expect(prop1.visibility).to.equal('public'); + expect(prop1.static).to.be.false; + expect(prop1.description).to.be.equal('The `a` variable description'); + expect(prop1.type).to.eql({ kind: 'type', type: 'number', text: 'number' }); + + expect(prop1.locations, 'Code location should be parsed').to.be.exist; + expect(prop1.locations[0]).is.deep.equals({ start: 58, end: 59 }); + expect(prop1.locations[1]).is.deep.equals({ start: 102, end: 103 }); + + const prop2 = doc.data.find(d => d.name === 'b'); + + expect(prop2.name).to.equal('b'); + expect(prop2.visibility).to.equal('public'); + expect(prop2.static).to.be.false; + expect(prop2.type).to.eql({ kind: 'type', type: 'string', text: 'string' }); + + done(); + }).catch(e => { + done(e); + }); + }); + + it('Export object statement with variable and aliace for that should be parsed as public prop', done => { + parser.parse({ + version: 3, + filename: path.resolve(__dirname, 'data.export.aliace.svelte'), + features: ['data'], + includeSourceLocations: true, + ignoredVisibilities: [] + }).then((doc) => { + expect(doc, 'Document should be provided').to.exist; + expect(doc.data, 'Document data should be parsed').to.exist; + + expect(doc.data.length).to.equal(2); + + const prop = doc.data.find(d => d.name === 'class'); + + expect(prop).to.exist; + expect(prop.name, 'Aliace name must be exposed instead of original name').to.equal('class'); + expect(prop.localName, 'Local name must be stored').to.equal('classes'); + expect(prop.visibility).to.equal('public'); + expect(prop.static).to.be.false; + expect(prop.description).to.be.equal('Description for variable that must be exported later'); + expect(prop.type).to.eql({ kind: 'type', type: 'Array', text: 'Array' }); + + expect(prop.locations, 'Code location should be parsed').to.be.exist; + expect(prop.locations[0]).is.deep.equals({ start: 189, end: 194 }); + + const localProp = doc.data.find(d => d.name === 'classes'); + + expect(localProp, 'Local prop definition also must be provided').to.exist; + + done(); + }).catch(e => { + done(e); + }); + }); }); diff --git a/typings.d.ts b/typings.d.ts index 65c1422..2486d71 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -137,6 +137,20 @@ export interface SvelteDataItem extends ISvelteItem { * @since {2.2.0} */ originalName?: string; + + /** + * The local name of the prop that was exported with aliace statement + * @example + * ```js + * const local = 1; + * export { local as public }; + * // `name` of this item will be `'public'` + * // `localName` of this item will be `'local'` + * ``` + * @since {3.0.1} + */ + localName?: string; + /** * The relative path of importing of this object. * When not defined, so variable is not provided.