From 45b2f7d9d68fa954aefa36343a49ef5b9b186371 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 22 Jul 2021 10:21:35 +0100 Subject: [PATCH] fix: handle multiline tags, title, description (#12) * fix: handle multiline tags, title, description * feat: allow configurable separator * test: add jsdoc transform tests * fix: check for tags --- src/loader/babel.ts | 31 +++++++++----- test/transform.test.ts | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/src/loader/babel.ts b/src/loader/babel.ts index 7988e5a..ccff743 100644 --- a/src/loader/babel.ts +++ b/src/loader/babel.ts @@ -115,6 +115,19 @@ export default function babelPluginUntyped () { } } +function clumpLines (lines: string[], delimiters = [], separator = ' ') { + const clumps: string[] = [] + while (lines.length) { + const line = lines.shift() + if (line && !delimiters.includes(line[0]) && clumps.length && clumps[clumps.length - 1]) { + clumps[clumps.length - 1] += separator + line + } else { + clumps.push(line) + } + } + return clumps.filter(Boolean) +} + function parseJSDocs (input: string | string[]): Schema { const schema: Schema = { title: '', @@ -125,15 +138,10 @@ function parseJSDocs (input: string | string[]): Schema { const lines = ([] as string[]).concat(input) .map(c => c.split('\n').map(l => l.replace(/^[\s*]+|[\s*]$/, ''))) .flat() - .filter(Boolean) - const comments: string[] = [] - while (lines.length && !lines[0].startsWith('@')) { - const comment = lines.shift() - if (comment) { - comments.push(comment) - } - } + const firstTag = lines.findIndex(l => l.startsWith('@')) + const comments = clumpLines(lines.slice(0, firstTag >= 0 ? firstTag : undefined)) + if (comments.length === 1) { schema.title = comments[0] } else if (comments.length > 1) { @@ -141,9 +149,10 @@ function parseJSDocs (input: string | string[]): Schema { schema.description = comments.splice(1).join('\n') } - if (lines.length) { - for (const line of lines) { - schema.tags.push(line.trim()) + if (firstTag >= 0) { + const tags = clumpLines(lines.slice(firstTag), ['@'], '\n') + for (const tag of tags) { + schema.tags.push(tag.trim()) } } diff --git a/test/transform.test.ts b/test/transform.test.ts index 5c42ea4..d94a441 100644 --- a/test/transform.test.ts +++ b/test/transform.test.ts @@ -89,6 +89,103 @@ describe('transform (functions)', () => { }) }) +describe('transform (jsdoc)', () => { + it('extracts title and description from jsdoc', () => { + const result = transform(` + export default { + /** + * Define the source directory of + * your Nuxt application. + * + * This property can be overwritten (for example, running \`nuxt ./my-app/\` + * will set the \`rootDir\` to the absolute path of \`./my-app/\` from the + * current/working directory. + * + * With more content in description. + */ + srcDir: 'src' + } + `) + expectCodeToMatch(result, /export default ([\s\S]*)$/, { + srcDir: { + $default: 'src', + $schema: { + title: 'Define the source directory of your Nuxt application.', + description: 'This property can be overwritten (for example, running `nuxt ./my-app/` will set the `rootDir` to the absolute path of `./my-app/` from the current/working directory.\nWith more content in description.', + tags: [] + } + } + }) + }) + + it('correctly parses tags', () => { + const result = transform(` + export default { + /** + * Define the source directory of your Nuxt application. + * + * This property can be overwritten. + * @note This is a note. + * that is on two lines + * @example + * \`\`\`js + * export default secretNumber = 42 + * \`\`\` + * + * @see https://nuxtjs.org + */ + srcDir: 'src' + } + `) + expectCodeToMatch(result, /export default ([\s\S]*)$/, { + srcDir: { + $default: 'src', + $schema: { + title: 'Define the source directory of your Nuxt application.', + description: 'This property can be overwritten.', + tags: [ + '@note This is a note.\nthat is on two lines', + '@example\n```js\nexport default secretNumber = 42\n```', + '@see https://nuxtjs.org' + ] + } + } + }) + }) + + it('correctly parses only tags', () => { + const result = transform(` + export default { + /** + * @note This is a note. + * that is on two lines + * @example + * \`\`\`js + * export default secretNumber = 42 + * \`\`\` + * + * @see https://nuxtjs.org + */ + srcDir: 'src' + } + `) + expectCodeToMatch(result, /export default ([\s\S]*)$/, { + srcDir: { + $default: 'src', + $schema: { + title: '', + description: '', + tags: [ + '@note This is a note.\nthat is on two lines', + '@example\n```js\nexport default secretNumber = 42\n```', + '@see https://nuxtjs.org' + ] + } + } + }) + }) +}) + function expectCodeToMatch (code: string, pattern: RegExp, expected: any) { const [, result] = code.match(pattern) expect(result).toBeDefined()