Skip to content
This repository was archived by the owner on Mar 8, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(filePath, doc)
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(source, filePath, doc)
parseSourceLocal(doc, source, { filePath, aliases })
return doc.toObject()
}
26 changes: 12 additions & 14 deletions src/parse-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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']

Expand All @@ -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<string, NodePath>,
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))
}
})
}
33 changes: 18 additions & 15 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

/**
Expand All @@ -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 === '') {
Expand All @@ -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+$/, ''))
}
}
22 changes: 17 additions & 5 deletions src/script-handlers/__tests__/extendsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '' })
}
}

Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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', () => {
Expand All @@ -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'],
})
})
})
9 changes: 6 additions & 3 deletions src/script-handlers/__tests__/mixinsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' })
Expand Down Expand Up @@ -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'],
})
})
})
16 changes: 11 additions & 5 deletions src/script-handlers/extendsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ 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 resolveAliases from '../utils/resolveAliases'
import resolvePathFrom from '../utils/resolvePathFrom'
import resolveRequired from '../utils/resolveRequired'

Expand All @@ -16,7 +17,7 @@ export default function extendsHandler(
documentation: Documentation,
componentDefinition: NodePath,
astPath: bt.File,
originalFilePath: string,
opt: ParseOptions,
) {
const extendsVariableName = getExtendsVariableName(componentDefinition)

Expand All @@ -28,15 +29,20 @@ 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)) {
const fullFilePath = resolvePathFrom(
extendsFilePath[extendsVariableName].filePath,
resolveAliases(extendsFilePath[extendsVariableName].filePath, opt.aliases || {}),
originalDirName,
)
parseFile(fullFilePath, documentation, [extendsFilePath[extendsVariableName].exportName])

parseFile(documentation, {
...opt,
filePath: fullFilePath,
nameFilter: [extendsFilePath[extendsVariableName].exportName],
})
}
}

Expand Down
26 changes: 20 additions & 6 deletions src/script-handlers/mixinsHandler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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 resolveAliases from '../utils/resolveAliases'
import resolvePathFrom from '../utils/resolvePathFrom'
import resolveRequired from '../utils/resolveRequired'

Expand All @@ -15,9 +17,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)

Expand All @@ -29,12 +32,23 @@ export default function mixinsHandler(
const mixinVarToFilePath = resolveRequired(astPath, mixinVariableNames)

// get each doc for each mixin using parse
const files = new Map<string, string[]>()
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 fullFilePath = resolvePathFrom(
resolveAliases(filePath, opt.aliases || {}),
originalDirName,
)
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 {
Expand Down
12 changes: 12 additions & 0 deletions src/utils/__tests__/resolveAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as path from 'path'
import resolveAliases from '../resolveAliases'

describe('resolveAliases', () => {
it('should resolve aliased from a path', () => {
expect(
resolveAliases('myPath/somethingNice/mixinFile.js', {
myPath: './replacementPath/src/mixins',
}),
).toEqual(path.join('./replacementPath/src/mixins', 'somethingNice/mixinFile.js'))
})
})
20 changes: 20 additions & 0 deletions src/utils/resolveAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as path from 'path'

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]
}
if (!aliasFound) {
return filePath
}
return path.join(aliases[aliasKeys[i]], filePath.substring(aliasKeys[i].length + 1))
}
10 changes: 5 additions & 5 deletions tests/components/button/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
<script>
import Vue from 'vue'
import { ClientTable } from 'some-plugin'
import another from '../../mixins/another'
import anotherMixin from '../../mixins/anotherMixin'
import model from '../../utils/model.json'
import another from '@mixins/another'
import anotherMixin from '@utils/anotherMixin'
import model from '@utils/model.json'
import genericMixin from './genericMixin'
import colorMixin from './colorMixin'
import review from '../../utils/review.json'
import { multi, hidden } from '../../mixins/multiMixin'
import review from '@utils/review.json'
import { multi, hidden } from '@mixins/multiMixin'

Vue.use(ClientTable)

Expand Down
5 changes: 4 additions & 1 deletion tests/components/button/button.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ let docButton: ComponentDoc

describe('tests button', () => {
beforeAll(done => {
docButton = parse(button)
docButton = parse(button, {
'@mixins': path.resolve(__dirname, '../../mixins'),
'@utils': path.resolve(__dirname, '../../utils'),
})
done()
})

Expand Down