From c1dad3a0273792830f6766cef761526b5eab4d62 Mon Sep 17 00:00:00 2001 From: Bart Ledoux Date: Mon, 28 Jan 2019 14:42:12 -0600 Subject: [PATCH 1/5] add alias parameter to ParseOptions --- src/main.ts | 4 +-- src/parse-script.ts | 26 +++++++-------- src/parse.ts | 33 ++++++++++--------- .../__tests__/extendsHandler.ts | 22 ++++++++++--- .../__tests__/mixinsHandler.ts | 9 +++-- src/script-handlers/extendsHandler.ts | 12 ++++--- src/script-handlers/mixinsHandler.ts | 20 ++++++++--- 7 files changed, 78 insertions(+), 48 deletions(-) diff --git a/src/main.ts b/src/main.ts index 41bff0e..3a74d8f 100755 --- a/src/main.ts +++ b/src/main.ts @@ -5,12 +5,12 @@ export { ComponentDoc } export function parse(filePath: string): ComponentDoc { const doc = new Documentation() - parseFile(filePath, doc) + parseFile(doc, { filePath }) return doc.toObject() } export function parseSource(source: string, filePath: string): ComponentDoc { const doc = new Documentation() - parseSourceLocal(source, filePath, doc) + parseSourceLocal(doc, source, { filePath }) return doc.toObject() } diff --git a/src/parse-script.ts b/src/parse-script.ts index 03ba55a..ba8b420 100644 --- a/src/parse-script.ts +++ b/src/parse-script.ts @@ -9,22 +9,22 @@ import resolveExportedComponent from './utils/resolveExportedComponent' // tslint:disable-next-line:no-var-requires import recast = require('recast') +import { ParseOptions } from './parse' const ERROR_MISSING_DEFINITION = 'No suitable component definition found' -interface ParseScriptOptions { - lang: 'ts' | 'js' - filePath: string - nameFilter?: string[] -} +type Handler = ( + doc: Documentation, + componentDefinition: NodePath, + ast: bt.File, + opt: ParseOptions, +) => void export default function parseScript( source: string, documentation: Documentation, - handlers: Array< - (doc: Documentation, componentDefinition: NodePath, ast: bt.File, filePath: string) => void - >, - options: ParseScriptOptions, + handlers: Handler[], + options: ParseOptions, ) { const plugins: ParserPlugin[] = options.lang === 'ts' ? ['typescript'] : ['flow'] @@ -43,17 +43,15 @@ export default function parseScript( } function executeHandlers( - localHandlers: Array< - (doc: Documentation, componentDefinition: NodePath, ast: bt.File, filePath: string) => void - >, + localHandlers: Handler[], componentDefinitions: Map, documentation: Documentation, ast: bt.File, - opt: ParseScriptOptions, + opt: ParseOptions, ) { return componentDefinitions.forEach((compDef, name) => { if (compDef && name && (!opt.nameFilter || opt.nameFilter.indexOf(name) > -1)) { - localHandlers.forEach(handler => handler(documentation, compDef, ast, opt.filePath)) + localHandlers.forEach(handler => handler(documentation, compDef, ast, opt)) } }) } diff --git a/src/parse.ts b/src/parse.ts index d2d8816..bde5e99 100755 --- a/src/parse.ts +++ b/src/parse.ts @@ -10,17 +10,24 @@ import cacher from './utils/cacher' const ERROR_EMPTY_DOCUMENT = 'The passed source is empty' +export interface ParseOptions { + filePath: string + lang?: 'ts' | 'js' + nameFilter?: string[] + aliases?: { [alias: string]: string } +} + /** * parses the source and returns the doc * @param {string} source code whose documentation is parsed * @param {string} filePath path of the current file against whom to resolve the mixins * @returns {object} documentation object */ -export function parseFile(filePath: string, documentation: Documentation, nameFilter?: string[]) { - const source = fs.readFileSync(filePath, { +export function parseFile(documentation: Documentation, opt: ParseOptions) { + const source = fs.readFileSync(opt.filePath, { encoding: 'utf-8', }) - return parseSource(source, filePath, documentation, nameFilter) + return parseSource(documentation, source, opt) } /** @@ -29,13 +36,8 @@ export function parseFile(filePath: string, documentation: Documentation, nameFi * @param {string} filePath path of the current file against whom to resolve the mixins * @returns {object} documentation object */ -export function parseSource( - source: string, - filePath: string, - documentation: Documentation, - nameFilter?: string[], -) { - const singleFileComponent = /\.vue$/i.test(path.extname(filePath)) +export function parseSource(documentation: Documentation, source: string, opt: ParseOptions) { + const singleFileComponent = /\.vue$/i.test(path.extname(opt.filePath)) let parts: SFCDescriptor | null = null if (source === '') { @@ -48,21 +50,22 @@ export function parseSource( const scriptSource = parts ? (parts.script ? parts.script.content : undefined) : source if (scriptSource) { - const lang = + opt.lang = (parts && parts.script && parts.script.attrs && parts.script.attrs.lang === 'ts') || - /\.tsx?$/i.test(path.extname(filePath)) + /\.tsx?$/i.test(path.extname(opt.filePath)) ? 'ts' : 'js' - parseScript(scriptSource, documentation, handlers, { lang, filePath, nameFilter }) + + parseScript(scriptSource, documentation, handlers, opt) } // get slots from template if (parts && parts.template) { - parseTemplate(parts.template, documentation, templateHandlers, filePath) + parseTemplate(parts.template, documentation, templateHandlers, opt.filePath) } if (!documentation.get('displayName')) { // a component should always have a display name - documentation.set('displayName', path.basename(filePath).replace(/\.\w+$/, '')) + documentation.set('displayName', path.basename(opt.filePath).replace(/\.\w+$/, '')) } } diff --git a/src/script-handlers/__tests__/extendsHandler.ts b/src/script-handlers/__tests__/extendsHandler.ts index 0c7d0e9..f71abb1 100644 --- a/src/script-handlers/__tests__/extendsHandler.ts +++ b/src/script-handlers/__tests__/extendsHandler.ts @@ -34,7 +34,7 @@ describe('extendsHandler', () => { const ast = babylon().parse(src) const path = resolveExportedComponent(ast).get('default') if (path) { - extendsHandler(doc, path, ast, '') + extendsHandler(doc, path, ast, { filePath: '' }) } } @@ -46,7 +46,10 @@ describe('extendsHandler', () => { '}', ].join('\n') parseItExtends(src) - expect(parseFile).toHaveBeenCalledWith('./component/full/path', doc, ['default']) + expect(parseFile).toHaveBeenCalledWith(doc, { + filePath: './component/full/path', + nameFilter: ['default'], + }) }) it('should resolve extended modules variables in require', () => { @@ -57,7 +60,10 @@ describe('extendsHandler', () => { '}', ].join('\n') parseItExtends(src) - expect(parseFile).toHaveBeenCalledWith('./component/full/path', doc, ['default']) + expect(parseFile).toHaveBeenCalledWith(doc, { + filePath: './component/full/path', + nameFilter: ['default'], + }) }) it('should resolve extended modules variables in import', () => { @@ -68,7 +74,10 @@ describe('extendsHandler', () => { '}', ].join('\n') parseItExtends(src) - expect(parseFile).toHaveBeenCalledWith('./component/full/path', doc, ['default']) + expect(parseFile).toHaveBeenCalledWith(doc, { + filePath: './component/full/path', + nameFilter: ['default'], + }) }) it('should resolve extended modules variables in class style components', () => { @@ -79,6 +88,9 @@ describe('extendsHandler', () => { '}', ].join('\n') parseItExtends(src) - expect(parseFile).toHaveBeenCalledWith('./component/full/path', doc, ['default']) + expect(parseFile).toHaveBeenCalledWith(doc, { + filePath: './component/full/path', + nameFilter: ['default'], + }) }) }) diff --git a/src/script-handlers/__tests__/mixinsHandler.ts b/src/script-handlers/__tests__/mixinsHandler.ts index 4cb900b..c5d8d3b 100644 --- a/src/script-handlers/__tests__/mixinsHandler.ts +++ b/src/script-handlers/__tests__/mixinsHandler.ts @@ -24,7 +24,7 @@ describe('mixinsHandler', () => { }) mockResolvePathFrom = resolvePathFrom as jest.Mock<(path: string, from: string) => string> - mockResolvePathFrom.mockReturnValue('component/full/path') + mockResolvePathFrom.mockReturnValue('./component/full/path') mockParse = parseFile as jest.Mock mockParse.mockReturnValue({ component: 'documentation' }) @@ -53,8 +53,11 @@ describe('mixinsHandler', () => { const ast = babelParser().parse(src) const path = resolveExportedComponent(ast).get('default') if (path) { - mixinsHandler(doc, path, ast, '') + mixinsHandler(doc, path, ast, { filePath: '' }) } - expect(parseFile).toHaveBeenCalledWith('component/full/path', doc, ['default']) + expect(parseFile).toHaveBeenCalledWith(doc, { + filePath: './component/full/path', + nameFilter: ['default'], + }) }) }) diff --git a/src/script-handlers/extendsHandler.ts b/src/script-handlers/extendsHandler.ts index 713a2b0..fcf799a 100644 --- a/src/script-handlers/extendsHandler.ts +++ b/src/script-handlers/extendsHandler.ts @@ -2,7 +2,7 @@ import * as bt from '@babel/types' import { NodePath } from 'ast-types' import * as path from 'path' import { Documentation } from '../Documentation' -import { parseFile } from '../parse' +import { parseFile, ParseOptions } from '../parse' import resolvePathFrom from '../utils/resolvePathFrom' import resolveRequired from '../utils/resolveRequired' @@ -16,7 +16,7 @@ export default function extendsHandler( documentation: Documentation, componentDefinition: NodePath, astPath: bt.File, - originalFilePath: string, + opt: ParseOptions, ) { const extendsVariableName = getExtendsVariableName(componentDefinition) @@ -28,7 +28,7 @@ export default function extendsHandler( // get all require / import statements const extendsFilePath = resolveRequired(astPath, [extendsVariableName]) - const originalDirName = path.dirname(originalFilePath) + const originalDirName = path.dirname(opt.filePath) // only look for documentation in the current project not in node_modules if (/^\./.test(extendsFilePath[extendsVariableName].filePath)) { @@ -36,7 +36,11 @@ export default function extendsHandler( extendsFilePath[extendsVariableName].filePath, originalDirName, ) - parseFile(fullFilePath, documentation, [extendsFilePath[extendsVariableName].exportName]) + parseFile(documentation, { + ...opt, + filePath: fullFilePath, + nameFilter: [extendsFilePath[extendsVariableName].exportName], + }) } } diff --git a/src/script-handlers/mixinsHandler.ts b/src/script-handlers/mixinsHandler.ts index 743c29d..c8b2079 100644 --- a/src/script-handlers/mixinsHandler.ts +++ b/src/script-handlers/mixinsHandler.ts @@ -1,8 +1,9 @@ import * as bt from '@babel/types' import { NodePath } from 'ast-types' import * as path from 'path' +import Map from 'ts-map' import { Documentation } from '../Documentation' -import { parseFile } from '../parse' +import { parseFile, ParseOptions } from '../parse' import resolvePathFrom from '../utils/resolvePathFrom' import resolveRequired from '../utils/resolveRequired' @@ -15,9 +16,10 @@ export default function mixinsHandler( documentation: Documentation, componentDefinition: NodePath, astPath: bt.File, - originalFilePath: string, + opt: ParseOptions, ) { - const originalDirName = path.dirname(originalFilePath) + const originalDirName = path.dirname(opt.filePath) + // filter only mixins const mixinVariableNames = getMixinsVariableNames(componentDefinition) @@ -29,12 +31,20 @@ export default function mixinsHandler( const mixinVarToFilePath = resolveRequired(astPath, mixinVariableNames) // get each doc for each mixin using parse + const files = new Map() for (const varName of Object.keys(mixinVarToFilePath)) { - // TODO: consolidate variables accessing the same file const { filePath, exportName } = mixinVarToFilePath[varName] const fullFilePath = resolvePathFrom(filePath, originalDirName) - parseFile(fullFilePath, documentation, [exportName]) + const vars = files.get(fullFilePath) || [] + vars.push(exportName) + files.set(fullFilePath, vars) } + + files.forEach((vars, fullFilePath) => { + if (fullFilePath && vars) { + parseFile(documentation, { ...opt, filePath: fullFilePath, nameFilter: vars }) + } + }) } function getMixinsVariableNames(compDef: NodePath): string[] | undefined { From 17309bd59ba955ad74f151de2aa4add6368eb19b Mon Sep 17 00:00:00 2001 From: Bart Ledoux Date: Mon, 28 Jan 2019 14:43:28 -0600 Subject: [PATCH 2/5] add it to the main --- src/main.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3a74d8f..29f65f1 100755 --- a/src/main.ts +++ b/src/main.ts @@ -3,14 +3,18 @@ import { parseFile, parseSource as parseSourceLocal } from './parse' export { ComponentDoc } -export function parse(filePath: string): ComponentDoc { +export function parse(filePath: string, aliases?: { [alias: string]: string }): ComponentDoc { const doc = new Documentation() - parseFile(doc, { filePath }) + parseFile(doc, { filePath, aliases }) return doc.toObject() } -export function parseSource(source: string, filePath: string): ComponentDoc { +export function parseSource( + source: string, + filePath: string, + aliases?: { [alias: string]: string }, +): ComponentDoc { const doc = new Documentation() - parseSourceLocal(doc, source, { filePath }) + parseSourceLocal(doc, source, { filePath, aliases }) return doc.toObject() } From 1eb1002869f29a4142e8fe34e7eb65f2c789a300 Mon Sep 17 00:00:00 2001 From: Bart Ledoux Date: Mon, 28 Jan 2019 15:55:11 -0600 Subject: [PATCH 3/5] create the alias resolver --- src/utils/__tests__/resolveAliases.ts | 11 +++++++++++ src/utils/resolveAliases.ts | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/utils/__tests__/resolveAliases.ts create mode 100644 src/utils/resolveAliases.ts diff --git a/src/utils/__tests__/resolveAliases.ts b/src/utils/__tests__/resolveAliases.ts new file mode 100644 index 0000000..c3fd3b8 --- /dev/null +++ b/src/utils/__tests__/resolveAliases.ts @@ -0,0 +1,11 @@ +import resolveAliases from '../resolveAliases' + +describe('resolveAliases', () => { + it('should resolve aliased from a path', () => { + expect( + resolveAliases('myPath/somethingNice/mixinFile.js', { + myPath: './replacementPath/src/mixins', + }), + ).toEqual('./replacementPath/src/mixins/somethingNice/mixinFile.js') + }) +}) diff --git a/src/utils/resolveAliases.ts b/src/utils/resolveAliases.ts new file mode 100644 index 0000000..4ff696c --- /dev/null +++ b/src/utils/resolveAliases.ts @@ -0,0 +1,17 @@ +export default function resolveAliases( + filePath: string, + aliases: { [alias: string]: string }, +): string { + const aliasKeys = Object.keys(aliases) + let i = aliasKeys.length + let aliasFound = false + if (!aliasKeys.length) { + return filePath + } + while (!aliasFound && i--) { + aliasFound = filePath.substring(0, aliasKeys[i].length) === aliasKeys[i] + } + return `${aliases[aliasKeys[i]].replace(/\/$/, '')}/${filePath + .substring(aliasKeys[i].length + 1) + .replace(/^\//, '')}` +} From 40d1f32c72d4f715c320e4ad369e8aa40b2e76db Mon Sep 17 00:00:00 2001 From: Bart Ledoux Date: Mon, 28 Jan 2019 15:55:25 -0600 Subject: [PATCH 4/5] resove components using aliases --- src/script-handlers/extendsHandler.ts | 8 +++++--- src/script-handlers/mixinsHandler.ts | 6 +++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/script-handlers/extendsHandler.ts b/src/script-handlers/extendsHandler.ts index fcf799a..321b448 100644 --- a/src/script-handlers/extendsHandler.ts +++ b/src/script-handlers/extendsHandler.ts @@ -3,6 +3,7 @@ import { NodePath } from 'ast-types' import * as path from 'path' import { Documentation } from '../Documentation' import { parseFile, ParseOptions } from '../parse' +import resolveAliases from '../utils/resolveAliases' import resolvePathFrom from '../utils/resolvePathFrom' import resolveRequired from '../utils/resolveRequired' @@ -32,10 +33,11 @@ export default function extendsHandler( // only look for documentation in the current project not in node_modules if (/^\./.test(extendsFilePath[extendsVariableName].filePath)) { - const fullFilePath = resolvePathFrom( - extendsFilePath[extendsVariableName].filePath, - originalDirName, + const fullFilePath = resolveAliases( + resolvePathFrom(extendsFilePath[extendsVariableName].filePath, originalDirName), + opt.aliases || {}, ) + parseFile(documentation, { ...opt, filePath: fullFilePath, diff --git a/src/script-handlers/mixinsHandler.ts b/src/script-handlers/mixinsHandler.ts index c8b2079..ae5703a 100644 --- a/src/script-handlers/mixinsHandler.ts +++ b/src/script-handlers/mixinsHandler.ts @@ -4,6 +4,7 @@ import * as path from 'path' import Map from 'ts-map' import { Documentation } from '../Documentation' import { parseFile, ParseOptions } from '../parse' +import resolveAliases from '../utils/resolveAliases' import resolvePathFrom from '../utils/resolvePathFrom' import resolveRequired from '../utils/resolveRequired' @@ -34,7 +35,10 @@ export default function mixinsHandler( const files = new Map() for (const varName of Object.keys(mixinVarToFilePath)) { const { filePath, exportName } = mixinVarToFilePath[varName] - const fullFilePath = resolvePathFrom(filePath, originalDirName) + const fullFilePath = resolveAliases( + resolvePathFrom(filePath, originalDirName), + opt.aliases || {}, + ) const vars = files.get(fullFilePath) || [] vars.push(exportName) files.set(fullFilePath, vars) From f64d7c9b526fb9369b586d550721193821b95e99 Mon Sep 17 00:00:00 2001 From: Bart Ledoux Date: Mon, 28 Jan 2019 16:57:05 -0600 Subject: [PATCH 5/5] add e2e testing --- src/script-handlers/extendsHandler.ts | 6 +++--- src/script-handlers/mixinsHandler.ts | 6 +++--- src/utils/__tests__/resolveAliases.ts | 3 ++- src/utils/resolveAliases.ts | 9 ++++++--- tests/components/button/Button.vue | 10 +++++----- tests/components/button/button.test.ts | 5 ++++- 6 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/script-handlers/extendsHandler.ts b/src/script-handlers/extendsHandler.ts index 321b448..5ba1618 100644 --- a/src/script-handlers/extendsHandler.ts +++ b/src/script-handlers/extendsHandler.ts @@ -33,9 +33,9 @@ export default function extendsHandler( // only look for documentation in the current project not in node_modules if (/^\./.test(extendsFilePath[extendsVariableName].filePath)) { - const fullFilePath = resolveAliases( - resolvePathFrom(extendsFilePath[extendsVariableName].filePath, originalDirName), - opt.aliases || {}, + const fullFilePath = resolvePathFrom( + resolveAliases(extendsFilePath[extendsVariableName].filePath, opt.aliases || {}), + originalDirName, ) parseFile(documentation, { diff --git a/src/script-handlers/mixinsHandler.ts b/src/script-handlers/mixinsHandler.ts index ae5703a..57889f7 100644 --- a/src/script-handlers/mixinsHandler.ts +++ b/src/script-handlers/mixinsHandler.ts @@ -35,9 +35,9 @@ export default function mixinsHandler( const files = new Map() for (const varName of Object.keys(mixinVarToFilePath)) { const { filePath, exportName } = mixinVarToFilePath[varName] - const fullFilePath = resolveAliases( - resolvePathFrom(filePath, originalDirName), - opt.aliases || {}, + const fullFilePath = resolvePathFrom( + resolveAliases(filePath, opt.aliases || {}), + originalDirName, ) const vars = files.get(fullFilePath) || [] vars.push(exportName) diff --git a/src/utils/__tests__/resolveAliases.ts b/src/utils/__tests__/resolveAliases.ts index c3fd3b8..8b6b82d 100644 --- a/src/utils/__tests__/resolveAliases.ts +++ b/src/utils/__tests__/resolveAliases.ts @@ -1,3 +1,4 @@ +import * as path from 'path' import resolveAliases from '../resolveAliases' describe('resolveAliases', () => { @@ -6,6 +7,6 @@ describe('resolveAliases', () => { resolveAliases('myPath/somethingNice/mixinFile.js', { myPath: './replacementPath/src/mixins', }), - ).toEqual('./replacementPath/src/mixins/somethingNice/mixinFile.js') + ).toEqual(path.join('./replacementPath/src/mixins', 'somethingNice/mixinFile.js')) }) }) diff --git a/src/utils/resolveAliases.ts b/src/utils/resolveAliases.ts index 4ff696c..bde0551 100644 --- a/src/utils/resolveAliases.ts +++ b/src/utils/resolveAliases.ts @@ -1,3 +1,5 @@ +import * as path from 'path' + export default function resolveAliases( filePath: string, aliases: { [alias: string]: string }, @@ -11,7 +13,8 @@ export default function resolveAliases( while (!aliasFound && i--) { aliasFound = filePath.substring(0, aliasKeys[i].length) === aliasKeys[i] } - return `${aliases[aliasKeys[i]].replace(/\/$/, '')}/${filePath - .substring(aliasKeys[i].length + 1) - .replace(/^\//, '')}` + if (!aliasFound) { + return filePath + } + return path.join(aliases[aliasKeys[i]], filePath.substring(aliasKeys[i].length + 1)) } diff --git a/tests/components/button/Button.vue b/tests/components/button/Button.vue index 4eb4360..2ae3659 100644 --- a/tests/components/button/Button.vue +++ b/tests/components/button/Button.vue @@ -8,13 +8,13 @@