sveltedoc-parser
Advanced tools
Comparing version 2.2.0 to 2.3.0
@@ -6,2 +6,9 @@ # Change Log | ||
## [2.3.0] 02.10.2019 | ||
- [Added] Svelte V3: Implement support of script element locations | ||
- [Fixed] Svelte V3: Fix parsing when component have multiple `<script>` blocks | ||
- [Added] Spec: Property `locations` was added to items and presents the list of item code locations | ||
- [Changed] Spec: Property `loc` for items marked as depricated, see `locations` property instead | ||
## [2.2.0] 15.08.2019 | ||
@@ -8,0 +15,0 @@ |
29
index.js
@@ -77,3 +77,3 @@ const { loadFileStructureFromOptions } = require('./lib/helpers'); | ||
function mergeItems(currentItem, newItem) { | ||
function mergeItems(itemType, currentItem, newItem) { | ||
if (convertVisibilityToLevel(currentItem.visibility) < convertVisibilityToLevel(newItem.visibility)) { | ||
@@ -91,12 +91,18 @@ currentItem.visibility = newItem.visibility; | ||
if (newItem.bind && newItem.bind.length > 0) { | ||
if (currentItem.bind) { | ||
currentItem.bind.push(...newItem.bind); | ||
if (newItem.locations && newItem.locations.length > 0) { | ||
if (currentItem.locations) { | ||
currentItem.locations.push(...newItem.locations); | ||
} else { | ||
currentItem.bind = [...newItem.bind]; | ||
currentItem.locations = [...newItem.locations]; | ||
} | ||
} | ||
if (!currentItem.bind && newItem.bind) { | ||
currentItem.bind = newItem.bind; | ||
if (itemType === 'data') { | ||
if (newItem.bind && newItem.bind.length > 0) { | ||
if (currentItem.bind) { | ||
currentItem.bind.push(...newItem.bind); | ||
} else { | ||
currentItem.bind = [...newItem.bind]; | ||
} | ||
} | ||
} | ||
@@ -136,9 +142,4 @@ | ||
} else { | ||
// Use merge logic of items information for specific features | ||
if (['data'].includes(feature)) { | ||
const currentItem = component[feature][itemIndex]; | ||
component[feature][itemIndex] = mergeItems(currentItem, value); | ||
} else { | ||
component[feature][itemIndex] = value; | ||
} | ||
const currentItem = component[feature][itemIndex]; | ||
component[feature][itemIndex] = mergeItems(feature, currentItem, value); | ||
} | ||
@@ -145,0 +146,0 @@ }); |
@@ -33,22 +33,23 @@ const fs = require('fs'); | ||
function extractHtmlBlock(content, blockName) { | ||
let remainingContent = content; | ||
function extractHtmlBlock(content, blockName, searchStartIndex) { | ||
const blockOuterStartIndex = content.indexOf(`<${blockName}`, searchStartIndex); | ||
if (blockOuterStartIndex >= 0) { | ||
const blockInnerEndIndex = content.indexOf(`</${blockName}>`, blockOuterStartIndex + blockName.length + 1); | ||
const blockStart = remainingContent.indexOf(`<${blockName}`); | ||
if (blockStart >= 0) { | ||
const blockEnd = remainingContent.indexOf(`</${blockName}>`, blockStart + blockName.length + 1); | ||
if (blockInnerEndIndex >= 0) { | ||
const openTagEndIndex = content.indexOf('>', blockOuterStartIndex + blockName.length); | ||
if (blockEnd >= 0) { | ||
const openTagEndIndex = remainingContent.indexOf('>', blockStart + blockName.length); | ||
const attributes = remainingContent.substr(blockStart + blockName.length + 1, openTagEndIndex - blockStart - blockName.length - 1); | ||
const innerBlockContent = remainingContent.substr(openTagEndIndex + 1, blockEnd - openTagEndIndex - 1); | ||
const attributes = content.substr(blockOuterStartIndex + blockName.length + 1, openTagEndIndex - blockOuterStartIndex - blockName.length - 1); | ||
const innerBlockContent = content.substr(openTagEndIndex + 1, blockInnerEndIndex - openTagEndIndex - 1); | ||
const offset = openTagEndIndex + 1; | ||
remainingContent = remainingContent.substr(0, blockStart) + remainingContent.substr(blockEnd + blockName.length + 3); | ||
const blockOuterEndIndex = blockInnerEndIndex + blockName.length + 3; | ||
return { | ||
remainingContent: remainingContent, | ||
block: { | ||
offset: offset, | ||
outerPosition: { | ||
start: blockOuterStartIndex, | ||
end: blockOuterEndIndex | ||
}, | ||
content: innerBlockContent, | ||
@@ -62,5 +63,4 @@ attributes: attributes | ||
return { | ||
remainingContent: content, | ||
block: null | ||
} | ||
}; | ||
} | ||
@@ -72,13 +72,10 @@ | ||
let searchResult = extractHtmlBlock(content, blockName); | ||
if (searchResult.block) { | ||
while (searchResult.block) { | ||
blocks.push(searchResult.block); | ||
const searchStartIndex = searchResult.block.outerPosition.end; | ||
searchResult = extractHtmlBlock(content, blockName, searchStartIndex); | ||
} | ||
// while (searchResult.block !== null) { | ||
// blocks.push(searchResult.block); | ||
// searchResult = extractHtmlBlock(searchResult.remainingContent, blockName); | ||
// } | ||
return { | ||
remainingContent: searchResult.remainingContent, | ||
blocks: blocks | ||
@@ -90,7 +87,7 @@ }; | ||
const scriptBlocksSearchResult = extractAllHtmlBlocks(fileContent, 'script'); | ||
const styleBlocksSearchResult = extractAllHtmlBlocks(scriptBlocksSearchResult.remainingContent, 'style'); | ||
const styleBlocksSearchResult = extractAllHtmlBlocks(fileContent, 'style'); | ||
return { | ||
content: fileContent, | ||
template: styleBlocksSearchResult.remainingContent, | ||
template: fileContent, | ||
scripts: scriptBlocksSearchResult.blocks, | ||
@@ -97,0 +94,0 @@ styles: styleBlocksSearchResult.blocks |
@@ -150,6 +150,7 @@ const EventEmitter = require('events'); | ||
if (p.key && p.key.start >= 0 && p.key.end >= p.key.start) { | ||
entry.loc = { | ||
entry.locations = [{ | ||
start: p.key.start + this.scriptOffset, | ||
end: p.key.end + this.scriptOffset | ||
}; | ||
}]; | ||
entry.loc = entry.locations[0]; | ||
} | ||
@@ -504,6 +505,8 @@ } | ||
if (next.start >= 0 && next.end >= next.start) { | ||
event.loc = { | ||
event.locations = [{ | ||
start: next.start + this.scriptOffset, | ||
end: next.end + this.scriptOffset | ||
} | ||
}]; | ||
// TODO: Deprication - Remove this property with V3.* | ||
event.loc = event.locations[0]; | ||
} | ||
@@ -590,6 +593,8 @@ } | ||
if (this.includeSourceLocations && parser.startIndex >= 0 && parser.endIndex >= parser.startIndex) { | ||
slot.loc = { | ||
slot.locations = [{ | ||
start: parser.startIndex, | ||
end: parser.endIndex | ||
}; | ||
}]; | ||
// TODO: Deprication - Remove this property with V3.* | ||
slot.loc = slot.locations[0]; | ||
} | ||
@@ -606,2 +611,6 @@ | ||
parent: tagName, | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
? [lastAttributeLocations[name]] | ||
: null, | ||
// TODO: Deprication - Remove this property with V3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
@@ -645,2 +654,6 @@ ? lastAttributeLocations[name] | ||
modificators: nameWithModificators.slice(1), | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
? [lastAttributeLocations[name]] | ||
: null, | ||
// TODO: Deprication - Remove this property with V3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
@@ -665,2 +678,6 @@ ? lastAttributeLocations[name] | ||
parent: null, | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
? [lastAttributeLocations[name]] | ||
: null, | ||
// TODO: Deprication - Remove this property with V3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
@@ -667,0 +684,0 @@ ? lastAttributeLocations[name] |
@@ -108,3 +108,3 @@ const EventEmitter = require('events'); | ||
emitDataItem(variable, scopeType, defaultVisibility, parentComment) { | ||
emitDataItem(variable, parseContext, defaultVisibility, parentComment) { | ||
const comment = parentComment || utils.getComment(variable.node, defaultVisibility); | ||
@@ -115,9 +115,19 @@ | ||
kind: variable.kind, | ||
static: scopeType === SCOPE_STATIC, | ||
static: parseContext.scopeType === SCOPE_STATIC, | ||
readonly: variable.kind === 'const', | ||
type: jsdoc.DEFAULT_TYPE, | ||
importPath: variable.importPath, | ||
originalName: variable.originalName | ||
originalName: variable.originalName, | ||
}); | ||
if (this.includeSourceLocations && variable.location) { | ||
item.locations = [{ | ||
start: variable.location.start + parseContext.offset, | ||
end: variable.location.end + parseContext.offset, | ||
}]; | ||
// TODO: Deprication - remove after 3.* | ||
item.loc = item.locations[0]; | ||
} | ||
this.updateType(item); | ||
@@ -128,3 +138,3 @@ | ||
emitMethodItem(method, scopeType, defaultVisibility, parentComment) { | ||
emitMethodItem(method, parseContext, defaultVisibility, parentComment) { | ||
const comment = parentComment || utils.getComment(method.node, defaultVisibility); | ||
@@ -135,15 +145,35 @@ | ||
args: method.args, | ||
static: scopeType === SCOPE_STATIC | ||
static: parseContext.scopeType === SCOPE_STATIC | ||
}); | ||
if (this.includeSourceLocations && method.location) { | ||
item.locations = [{ | ||
start: method.location.start + parseContext.offset, | ||
end: method.location.end + parseContext.offset | ||
}]; | ||
// TODO: Deprication - remove after 3.* | ||
item.loc = item.locations[0]; | ||
} | ||
this.emit('method', item); | ||
} | ||
emitComputedItem(computed, scopeType, defaultVisibility) { | ||
emitComputedItem(computed, parseContext, defaultVisibility) { | ||
const item = Object.assign({}, utils.getComment(computed.node, defaultVisibility), { | ||
name: computed.name, | ||
static: scopeType === SCOPE_STATIC, | ||
static: parseContext.scopeType === SCOPE_STATIC, | ||
type: jsdoc.DEFAULT_TYPE | ||
}); | ||
if (this.includeSourceLocations && computed.location) { | ||
item.locations = [{ | ||
start: computed.location.start + parseContext.offset, | ||
end: computed.location.end + parseContext.offset | ||
}]; | ||
// TODO: Deprication - remove after 3.* | ||
item.loc = item.locations[0]; | ||
} | ||
this.updateType(item); | ||
@@ -154,3 +184,3 @@ | ||
emitEventItem(event) { | ||
emitEventItem(event, parseContext) { | ||
const item = Object.assign({}, utils.getComment(event.node, 'public'), { | ||
@@ -160,2 +190,12 @@ name: event.name | ||
if (this.includeSourceLocations && event.location) { | ||
item.locations = [{ | ||
start: event.location.start + parseContext.offset, | ||
end: event.location.end + parseContext.offset | ||
}]; | ||
// TODO: Deprication - remove after 3.* | ||
item.loc = item.locations[0]; | ||
} | ||
this.emit('event', item); | ||
@@ -172,14 +212,23 @@ } | ||
emitImportedComponentItem(importNode, name, path) { | ||
const item = Object.assign({}, utils.getComment(importNode, 'private'), { | ||
name: name, | ||
importPath: path, | ||
emitImportedComponentItem(component, parseContext) { | ||
const item = Object.assign({}, utils.getComment(component.node, 'private'), { | ||
name: component.name, | ||
importPath: component.path, | ||
// TODO: 3.*, Backward compatibility: Remove this property | ||
value: path | ||
value: component.path | ||
}); | ||
if (this.includeSourceLocations && component.location) { | ||
item.locations = [{ | ||
start: component.location.start + parseContext.offset, | ||
end: component.location.end + parseContext.offset | ||
}]; | ||
// TODO: 3.*, Backward compatibility: Remove this property | ||
item.loc = item.locations[0]; | ||
} | ||
this.emit('component', item); | ||
} | ||
parseBodyRecursively(rootNode, scopeType, level) { | ||
parseBodyRecursively(rootNode, parseContext, level) { | ||
const nodes = rootNode.body | ||
@@ -195,4 +244,4 @@ ? rootNode.body | ||
if (callee.type === 'Identifier' && this.dispatcherNames.indexOf(callee.name) >= 0) { | ||
const eventItem = this.parseEvenDeclaration(expressionNode); | ||
this.emitEventItem(eventItem); | ||
const eventItem = this.parseEventDeclaration(expressionNode); | ||
this.emitEventItem(eventItem, parseContext); | ||
@@ -205,3 +254,3 @@ return; | ||
if (expressionNode.body) { | ||
this.parseBodyRecursively(expressionNode.body, scopeType, level + 1); | ||
this.parseBodyRecursively(expressionNode.body, parseContext, level + 1); | ||
return; | ||
@@ -214,5 +263,5 @@ } | ||
const callee = node.callee; | ||
if (callee.type === 'Identifier' && this.dispatcherNames.indexOf(callee.name) >= 0) { | ||
const eventItem = this.parseEvenDeclaration(node); | ||
this.emitEventItem(eventItem); | ||
if (callee.type === 'Identifier' && this.dispatcherNames.includes(callee.name)) { | ||
const eventItem = this.parseEventDeclaration(node); | ||
this.emitEventItem(eventItem, parseContext); | ||
@@ -223,7 +272,7 @@ return; | ||
if (node.type === 'VariableDeclaration' && scopeType !== SCOPE_MARKUP) { | ||
if (node.type === 'VariableDeclaration' && parseContext.scopeType !== SCOPE_MARKUP) { | ||
const variables = this.parseVariableDeclaration(node); | ||
variables.forEach(variable => { | ||
if (level === 0) { | ||
this.emitDataItem(variable, scopeType, 'private'); | ||
this.emitDataItem(variable, parseContext, 'private'); | ||
} | ||
@@ -235,3 +284,3 @@ | ||
const callee = initNode.callee; | ||
if (callee.type === 'Identifier' && this.dispatcherConstructorNames.indexOf(callee.name) >= 0) { | ||
if (callee.type === 'Identifier' && this.dispatcherConstructorNames.includes(callee.name)) { | ||
this.dispatcherNames.push(variable.name); | ||
@@ -241,3 +290,3 @@ } | ||
if (initNode.body) { | ||
this.parseBodyRecursively(initNode.body, scopeType, level + 1); | ||
this.parseBodyRecursively(initNode.body, parseContext, level + 1); | ||
} | ||
@@ -252,6 +301,6 @@ } | ||
if (node.type === 'FunctionDeclaration') { | ||
this.emitMethodItem(this.parseFunctionDeclaration(node), scopeType, 'private'); | ||
this.emitMethodItem(this.parseFunctionDeclaration(node), parseContext, 'private'); | ||
if (node.body) { | ||
this.parseBodyRecursively(node.body, scopeType, level + 1); | ||
this.parseBodyRecursively(node.body, parseContext, level + 1); | ||
} | ||
@@ -261,3 +310,3 @@ return; | ||
if (node.type === 'ExportNamedDeclaration' && level === 0 && scopeType !== SCOPE_MARKUP) { | ||
if (node.type === 'ExportNamedDeclaration' && level === 0 && parseContext.scopeType !== SCOPE_MARKUP) { | ||
const declaration = node.declaration; | ||
@@ -270,3 +319,3 @@ if (declaration) { | ||
variables.forEach(variable => { | ||
this.emitDataItem(variable, scopeType, 'public', exportNodeComment); | ||
this.emitDataItem(variable, parseContext, 'public', exportNodeComment); | ||
}); | ||
@@ -279,3 +328,3 @@ | ||
const func = this.parseFunctionDeclaration(declaration); | ||
this.emitMethodItem(func, scopeType, 'public', exportNodeComment); | ||
this.emitMethodItem(func, parseContext, 'public', exportNodeComment); | ||
@@ -287,3 +336,3 @@ return; | ||
if (node.type === 'LabeledStatement' && level === 0 && scopeType !== SCOPE_MARKUP) { | ||
if (node.type === 'LabeledStatement' && level === 0 && parseContext.scopeType !== SCOPE_MARKUP) { | ||
const idNode = node.label; | ||
@@ -298,4 +347,8 @@ if (idNode && idNode.type === 'Identifier' && idNode.name === '$') { | ||
name: leftNode.name, | ||
location: { | ||
start: leftNode.start, | ||
end: leftNode.end | ||
}, | ||
node: node | ||
}, scopeType, 'private'); | ||
}, parseContext, 'private'); | ||
@@ -309,3 +362,3 @@ return; | ||
if (node.type === 'ImportDeclaration' && level === 0 && scopeType !== SCOPE_MARKUP) { | ||
if (node.type === 'ImportDeclaration' && level === 0 && parseContext.scopeType !== SCOPE_MARKUP) { | ||
const specifier = node.specifiers[0]; | ||
@@ -327,3 +380,12 @@ const source = node.source; | ||
if (importEntry.identifier[0] === importEntry.identifier[0].toUpperCase()) { | ||
this.emitImportedComponentItem(node, importEntry.identifier, importEntry.sourceFilename); | ||
const component = { | ||
node: node, | ||
name: importEntry.identifier, | ||
path: importEntry.sourceFilename, | ||
location: { | ||
start: specifier.local.start, | ||
end: specifier.local.end | ||
} | ||
}; | ||
this.emitImportedComponentItem(component, parseContext); | ||
return; | ||
@@ -340,4 +402,8 @@ } else { | ||
importPath: importEntry.sourceFilename, | ||
kind: 'const' | ||
}, scopeType, 'private'); | ||
kind: 'const', | ||
location: { | ||
start: specifier.local.start, | ||
end: specifier.local.end | ||
} | ||
}, parseContext, 'private'); | ||
} | ||
@@ -356,4 +422,8 @@ } | ||
importPath: sourceFileName, | ||
kind: 'const' | ||
}, scopeType, 'private'); | ||
kind: 'const', | ||
location: { | ||
start: specifier.local.start, | ||
end: specifier.local.end | ||
} | ||
}, parseContext, 'private'); | ||
} | ||
@@ -363,2 +433,3 @@ }); | ||
// Import svelte API functions | ||
if (sourceFileName === 'svelte') { | ||
@@ -372,7 +443,2 @@ // Dispatcher constructors | ||
} | ||
} | ||
// Import svelte API functions | ||
if (node.source.type === 'Literal' && node.source.value === 'svelte') { | ||
} | ||
@@ -384,3 +450,3 @@ } | ||
if (node.body) { | ||
this.parseBodyRecursively(node.body, scopeType, level + 1); | ||
this.parseBodyRecursively(node.body, parseContext, level + 1); | ||
} | ||
@@ -410,12 +476,13 @@ }); | ||
this.parseBodyRecursively( | ||
ast, | ||
isStaticScope | ||
const scriptParseContext = { | ||
scopeType: isStaticScope | ||
? SCOPE_STATIC | ||
: SCOPE_DEFAULT, | ||
0 | ||
); | ||
: SCOPE_DEFAULT, | ||
offset: scriptBlock.offset | ||
}; | ||
this.parseBodyRecursively(ast, scriptParseContext, 0); | ||
} | ||
parseMarkupExpressionBlock(expression) { | ||
parseMarkupExpressionBlock(expression, offset) { | ||
const ast = espree.parse( | ||
@@ -426,6 +493,11 @@ expression, | ||
this.parseBodyRecursively(ast, SCOPE_MARKUP, 0); | ||
const scriptParseContext = { | ||
scope: SCOPE_MARKUP, | ||
offset: offset | ||
}; | ||
this.parseBodyRecursively(ast, scriptParseContext, 0); | ||
} | ||
parseEvenDeclaration(node) { | ||
parseEventDeclaration(node) { | ||
if (node.type !== 'CallExpression') { | ||
@@ -446,3 +518,7 @@ throw new Error('Node should have a CallExpressionType, but is ' + node.type); | ||
: undefined, | ||
node: node | ||
node: node, | ||
location: { | ||
start: nameNode.start, | ||
end: nameNode.end | ||
} | ||
}; | ||
@@ -465,3 +541,7 @@ } | ||
node: node, | ||
declarator: declarator | ||
declarator: declarator, | ||
location: { | ||
start: idNode.start, | ||
end: idNode.end | ||
} | ||
}); | ||
@@ -476,3 +556,7 @@ } else if (idNode.type === 'ObjectPattern') { | ||
node: node, | ||
declarator: declarator | ||
declarator: declarator, | ||
locations: { | ||
start: propertyIdNode.start, | ||
end: propertyIdNode.end | ||
} | ||
}); | ||
@@ -504,2 +588,6 @@ } | ||
name: node.id.name, | ||
location: { | ||
start: node.id.start, | ||
end: node.id.end | ||
}, | ||
args: args | ||
@@ -590,2 +678,6 @@ }; | ||
modificators: nameWithModificators.slice(1), | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
? [lastAttributeLocations[name]] | ||
: null, | ||
// TODO: Reprication, remove this property with 3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
@@ -640,2 +732,6 @@ ? lastAttributeLocations[name] | ||
parent: tagName, | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
? [lastAttributeLocations[name]] | ||
: null, | ||
// TODO: Reprication, remove this property with 3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty(name) | ||
@@ -655,2 +751,4 @@ ? lastAttributeLocations[name] | ||
}], | ||
locations: bindProperty.locations, | ||
// TODO: Reprication, remove this property with 3.* | ||
loc: bindProperty.loc, | ||
@@ -675,2 +773,6 @@ visibility: 'private', | ||
parent: tagName, | ||
locations: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty('bind:this') | ||
? [lastAttributeLocations['bind:this']] | ||
: null, | ||
// TODO: Reprication, remove this property with 3.* | ||
loc: this.includeSourceLocations && lastAttributeLocations.hasOwnProperty('bind:this') | ||
@@ -677,0 +779,0 @@ ? lastAttributeLocations['bind:this'] |
{ | ||
"name": "sveltedoc-parser", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "Generate a JSON documentation for a Svelte file", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -7,4 +7,11 @@ # The sveltedoc parser | ||
Changelog of release versions can be found [here](/CHANGELOG.md) | ||
## Changelog: [2.3.0] 02.10.2019 | ||
- [Added] Svelte V3: Implement support of script element locations | ||
- [Fixed] Svelte V3: Fix parsing when component have multiple `<script>` blocks | ||
- [Added] Spec: Property `locations` was added to items and presents the list of item code locations | ||
- [Changed] Spec: Property `loc` for items marked as depricated, see `locations` property instead | ||
Full changelog of release versions can be found [here](/CHANGELOG.md) | ||
## Install | ||
@@ -11,0 +18,0 @@ |
@@ -62,2 +62,3 @@ /** | ||
* Provided only if requested by specific option parameter. | ||
* @deprecated This field marked as depricated, please use `locations` instead of this. | ||
*/ | ||
@@ -67,2 +68,8 @@ loc?: SourceLocation; | ||
/** | ||
* The list of source code locations for this item. | ||
* Provided only if requested by specific option parameter. | ||
*/ | ||
locations?: SourceLocation[]; | ||
/** | ||
* The description of the item, provided from related comment. | ||
@@ -69,0 +76,0 @@ */ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117346
13
2250
136