Skip to content

Commit

Permalink
Merge pull request #62 from alexprey/47-fix-template-comment-parsing
Browse files Browse the repository at this point in the history
#47 Fix template comment parsing
  • Loading branch information
alexprey authored Feb 16, 2021
2 parents 91d0e85 + f2e4afe commit 62d0fab
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 13 deletions.
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ function subscribeOnParserEvents(parser, ignoredVisibilities, version, resolve,
/**
* Main parse function.
* @param {SvelteParserOptions} options
* @return {Promise<import('./typings').SvelteComponentDoc>}
* @example
* const { parse } = require('sveltedoc-parser');
* // basic usage only requires 'filename' to be set.
Expand Down
39 changes: 39 additions & 0 deletions lib/jsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,51 @@ function parseReturnKeyword(text) {
return output;
}

/**
* @param {string} comment
* @return {string}
*/
const convertToJsDocComment = (comment) => {
if (!comment) {
return '';
}

comment = comment.trim();

// Comment content already is JSDoc format
if (comment.startsWith('/**') && comment.endsWith('*/')) {
return comment;
}

const lines = comment.split('\n');

return lines.map((line, lineNumber) => {
line = line.trim();

if (lineNumber === 0) {
if (!line.startsWith('/**')) {
line = `/**\n * ${line}`;
}
}

if (lineNumber === lines.length - 1) {
if (!line.endsWith('*/')) {
line = `${line}\n */`;
}
}

return line.trimEnd();
}).join('\n * ');
};

module.exports = {
parseType,
parseParamKeyword,
parseReturnKeyword,
parseTypeKeyword,
parseJSTypeFromValueNode,

convertToJsDocComment,

DEFAULT_TYPE
};
5 changes: 5 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const getVisibility = (keywords, defaultVisibility) => {
return defaultVisibility;
};

/**
* @param {string} text
* @param {JSVisibilityScope} defaultVisibility
* @return {import('../typings').IScopedCommentItem}
*/
const parseComment = (text, defaultVisibility = DEFAULT_VISIBILITY) => {
const result = {
keywords: [],
Expand Down
35 changes: 22 additions & 13 deletions lib/v3/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { Parser: HtmlParser } = require('htmlparser2-svelte');
const { parseAndMergeKeywords } = require('./v3-utils');
const { parseComment, hasOwnProperty } = require('../utils');
const { TemplateEvent } = require('./events');
const jsdoc = require('../jsdoc');

/**
* @typedef {import('../../typings').Svelte3Feature} Svelte3Feature
Expand Down Expand Up @@ -55,6 +56,17 @@ class TemplateParser extends EventEmitter {
let lastTagName = null;
let parser = null;

const parseAndConsumeLastComment = (defaultVisibility) => {
const parsedComment = parseComment(
jsdoc.convertToJsDocComment(lastComment),
defaultVisibility
);

lastComment = null;

return parsedComment;
};

return {
onparserinit: (parserInstance) => {
parser = parserInstance;
Expand Down Expand Up @@ -84,7 +96,10 @@ class TemplateParser extends EventEmitter {
if (name.length > 3 && name.indexOf('on:') === 0 && !value) {
const nameWithModificators = name.substr(3).split('|');

const parsedComment = parseAndConsumeLastComment();

const baseEvent = {
...parsedComment,
name: nameWithModificators[0],
parent: lastTagName,
modificators: nameWithModificators.slice(1),
Expand All @@ -93,20 +108,10 @@ class TemplateParser extends EventEmitter {
: null
};

if (lastComment) {
lastComment = `/** ${lastComment} */`;
}

const comment = parseComment(lastComment || '');

baseEvent.visibility = comment.visibility;
baseEvent.description = comment.description || '';
baseEvent.keywords = comment.keywords;

if (!hasOwnProperty(this.eventsEmitted, baseEvent.name)) {
this.eventsEmitted[baseEvent.name] = baseEvent;

parseAndMergeKeywords(comment.keywords, baseEvent);
parseAndMergeKeywords(parsedComment.keywords, baseEvent);

this.emit(TemplateEvent.EVENT, baseEvent);
}
Expand All @@ -132,7 +137,7 @@ class TemplateParser extends EventEmitter {

if (isTopLevelElement && isNotStyleOrScript) {
if (lastComment && rootElementIndex === 0) {
const parsedComment = parseComment(lastComment);
const parsedComment = parseAndConsumeLastComment();

this.emit(TemplateEvent.GLOBAL_COMMENT, parsedComment);
}
Expand All @@ -149,9 +154,13 @@ class TemplateParser extends EventEmitter {
visibility: 'public'
}));

const parsedComment = parseAndConsumeLastComment('public');

// TODO parse parameters description and types for slot

const slot = {
...parsedComment,
name: attrs.name || 'default',
description: lastComment,
visibility: 'public',
parameters: exposedParameters
};
Expand Down
11 changes: 11 additions & 0 deletions test/svelte3/integration/slots/slot.comments.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div>
<!-- The first slot description. -->
<slot name="first" />
<div>
<slot />

<button on:click />
</div>
<!-- The second slot description. -->
<slot name="second" />
</div>
32 changes: 32 additions & 0 deletions test/svelte3/integration/slots/slots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,36 @@ describe('SvelteDoc v3 - Slots', () => {
done(e);
});
});

it('Slot comments should be correctly parsed', done => {
parser.parse({
version: 3,
filename: path.resolve(__dirname, 'slot.comments.svelte'),
features: ['slots', 'events'],
ignoredVisibilities: []
}).then(doc => {
expect(doc, 'Document should be provided').to.exist;
expect(doc.slots, 'Document slots should be parsed').to.exist;

expect(doc.slots).to.have.length(3);

const firstSlot = doc.slots.find(slot => slot.name === 'first');
const defaultSlot = doc.slots.find(slot => slot.name === 'default');
const secondSlot = doc.slots.find(slot => slot.name === 'second');

expect(firstSlot.description).to.equal('The first slot description.');
expect(defaultSlot.description).to.empty;
expect(secondSlot.description).to.equal('The second slot description.');

expect(doc.events).to.have.length(1);

const event = doc.events[0];

expect(event.description).to.be.empty;

done();
}).catch(e => {
done(e);
});
});
});
33 changes: 33 additions & 0 deletions test/unit/jsdoc/jsdoc.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,37 @@ describe('JSDoc parser module tests', () => {
expect(returns.type.type).to.equal('any');
});
});

describe('convertToJsDocComment', () => {
it('when single line text then should be wrapped', () => {
const output = jsdoc.convertToJsDocComment(' The simple component description. ');

expect(output).to.equal(`/**
* The simple component description.
*/`
);
});

it('when comment is already jsdoc then should be w/o any changes', () => {
const output = jsdoc.convertToJsDocComment('/** The JSDoc comment **/');

expect(output).to.equal('/** The JSDoc comment **/');
});

it('when multiline comment then should be correcly wrapped', () => {
const output = jsdoc.convertToJsDocComment(`
The simple component description.
@author Alexey
@param {string} title
`);

expect(output).to.equal(`/**
* The simple component description.
*
* @author Alexey
* @param {string} title
*/`);
});
});
});

0 comments on commit 62d0fab

Please sign in to comment.