Skip to content

Commit

Permalink
Merge branch 'dev' of https://gitee.com/lookforwhat/vue-devui into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
unfound committed Aug 31, 2021
2 parents dcffb07 + 8aa5440 commit 98a8235
Show file tree
Hide file tree
Showing 176 changed files with 17,457 additions and 990 deletions.
83 changes: 63 additions & 20 deletions devui-cli/commands/create.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const logger = require('../shared/logger')
const { bigCamelCase, resolveDirFilesInfo, parseExportByFileInfo } = require('../shared/utils')
const { bigCamelCase, resolveDirFilesInfo, parseExportByFileInfo, parseComponentInfo } = require('../shared/utils')
const fs = require('fs-extra')
const { resolve } = require('path')
const {
Expand All @@ -10,7 +10,13 @@ const {
INDEX_FILE_NAME,
VUE_DEVUI_FILE,
VUE_DEVUI_IGNORE_DIRS,
VUE_DEVUI_FILE_NAME
VUE_DEVUI_FILE_NAME,
CREATE_SUPPORT_TYPES,
CREATE_UNFINISHED_TYPES,
CREATE_SUPPORT_TYPE_MAP,
SITES_COMPONENTS_DIR,
VITEPRESS_SIDEBAR_FILE,
VITEPRESS_SIDEBAR_FILE_NAME
} = require('../shared/constant')
const { isEmpty, kebabCase } = require('lodash')
const inquirer = require('inquirer')
Expand All @@ -27,42 +33,48 @@ const {
} = require('../templates/component')
const { createVueDevuiTemplate } = require('../templates/vue-devui')
const ora = require('ora')
const { createVitepressSidebarTemplate } = require('../templates/vitepress-sidebar')

exports.validateCreateType = (type) => {
const flag = /^(component|(vue-devui)|(vitepress\/sidebar))$/.test(type)
const re = new RegExp('^(' + CREATE_SUPPORT_TYPES.map((t) => `(${t})`).join('|') + ')$')
const flag = re.test(type)

!flag && logger.error('类型错误,可选类型为:component, vue-devui, vitepress/sidebar')
!flag && logger.error(`类型错误,可选类型为:${CREATE_SUPPORT_TYPES.join(', ')}`)

return flag ? type : null
}

// TODO: 待优化代码结构
exports.create = async (cwd) => {
let { type, ignoreParseError } = cwd
let { type } = cwd

if (isEmpty(type)) {
const result = await inquirer.prompt([selectCreateType()])
type = result.type
}

if (['vitepress/sidebar'].includes(type)) {
if (CREATE_UNFINISHED_TYPES.includes(type)) {
logger.info('抱歉,该功能暂未完成!')
return process.exit(0)
process.exit(0)
}

let params = {}

try {
switch (type) {
case 'component':
const result = await inquirer.prompt([typeName(), typeTitle(), selectCategory(), selectParts()])
result.hasComponent = result.parts.includes(COMPONENT_PARTS_MAP.get('component'))
result.hasDirective = result.parts.includes(COMPONENT_PARTS_MAP.get('directive'))
result.hasService = result.parts.includes(COMPONENT_PARTS_MAP.get('service'))
case CREATE_SUPPORT_TYPE_MAP.component:
params = await inquirer.prompt([typeName(), typeTitle(), selectCategory(), selectParts()])
params.hasComponent = params.parts.includes(COMPONENT_PARTS_MAP.get('component'))
params.hasDirective = params.parts.includes(COMPONENT_PARTS_MAP.get('directive'))
params.hasService = params.parts.includes(COMPONENT_PARTS_MAP.get('service'))

await createComponent(result)
await createComponent(params, cwd)
break
case 'vue-devui':
await createVueDevui(ignoreParseError)
case CREATE_SUPPORT_TYPE_MAP['vue-devui']:
await createVueDevui(params, cwd)
break
case 'vitepress/sidebar':
case CREATE_SUPPORT_TYPE_MAP['vitepress/sidebar']:
await createVitepressSidebar(params, cwd)
break
default:
break
Expand All @@ -76,8 +88,6 @@ exports.create = async (cwd) => {
async function createComponent(params = {}) {
let { name, hasComponent, hasDirective, hasService } = params

name = name.replace(new RegExp(`^${DEVUI_NAMESPACE}`, 'i'), '')

const componentName = kebabCase(name)
const styleName = kebabCase(name)
const typesName = kebabCase(name) + '-types'
Expand All @@ -99,6 +109,7 @@ async function createComponent(params = {}) {
const directiveTemplate = createDirectiveTemplate(_params)
const serviceTemplate = createServiceTemplate(_params)
const indexTemplate = createIndexTemplate(_params)
// TODO: 增加测试模板
const testsTemplate = createTestsTemplate(_params)

const componentDir = resolve(DEVUI_DIR, componentName)
Expand All @@ -107,7 +118,7 @@ async function createComponent(params = {}) {

if (fs.pathExistsSync(componentDir)) {
logger.error(`${bigCamelCase(componentName)} 组件目录已存在!`)
return process.exit(1)
process.exit(1)
}

let spinner = ora(`创建组件 ${bigCamelCase(componentName)} 开始...`).start()
Expand Down Expand Up @@ -146,7 +157,7 @@ async function createComponent(params = {}) {
}
}

async function createVueDevui(ignoreParseError) {
async function createVueDevui(params, { ignoreParseError }) {
const fileInfo = resolveDirFilesInfo(DEVUI_DIR, VUE_DEVUI_IGNORE_DIRS)
const exportModules = []

Expand All @@ -172,3 +183,35 @@ async function createVueDevui(ignoreParseError) {
process.exit(1)
}
}

async function createVitepressSidebar(params, { cover }) {
if (fs.pathExistsSync(VITEPRESS_SIDEBAR_FILE) && !cover) {
logger.warning(`${VITEPRESS_SIDEBAR_FILE_NAME} 文件已存在!`)
process.exit(0)
}

const fileInfo = resolveDirFilesInfo(DEVUI_DIR, VUE_DEVUI_IGNORE_DIRS)
const componentsInfo = []

fileInfo.forEach((f) => {
const info = parseComponentInfo(f.dirname)

if (isEmpty(info)) return

componentsInfo.push(info)
})

const template = createVitepressSidebarTemplate(componentsInfo)

let spinner = ora(`创建 ${VITEPRESS_SIDEBAR_FILE_NAME} 文件开始...`).start()

try {
await fs.writeFile(VITEPRESS_SIDEBAR_FILE, template, { encoding: 'utf-8' })

spinner.succeed(`创建 ${VITEPRESS_SIDEBAR_FILE_NAME} 文件成功!`)
logger.info(`文件地址:${VITEPRESS_SIDEBAR_FILE}`)
} catch (e) {
spinner.fail(e.toString())
process.exit(1)
}
}
5 changes: 3 additions & 2 deletions devui-cli/index.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#!/usr/bin/env node
const { Command } = require('commander')
const { create, validateCreateType } = require('./commands/create')
const { VERSION } = require('./shared/constant')
const { VERSION, CREATE_SUPPORT_TYPES } = require('./shared/constant')

const program = new Command()

program
.command('create')
.description('创建一个组件模板或配置文件')
.option('-t --type <type>', '创建类型,可选值:component, vue-devui, vitepress/sidebar', validateCreateType)
.option('-t --type <type>', `创建类型,可选值:${CREATE_SUPPORT_TYPES.join(', ')}`, validateCreateType)
.option('--ignore-parse-error', '忽略解析错误', false)
.option('--cover', '覆盖原文件', false)
.action(create)

program.parse().version(VERSION)
4 changes: 2 additions & 2 deletions devui-cli/inquirers/component.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { COMPONENT_PARTS_MAP } = require('../shared/constant')
const { COMPONENT_PARTS_MAP, VITEPRESS_SIDEBAR_CATEGORY } = require('../shared/constant')

exports.typeName = () => ({
name: 'name',
Expand Down Expand Up @@ -28,7 +28,7 @@ exports.selectCategory = () => ({
name: 'category',
type: 'list',
message: '(必填)请选择组件分类,将用作文档列表分类:',
choices: ['通用', '导航', '反馈', '扩展服务', '数据录入', '数据展示', '布局'],
choices: VITEPRESS_SIDEBAR_CATEGORY,
default: 0
})

Expand Down
4 changes: 3 additions & 1 deletion devui-cli/inquirers/create.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const { CREATE_SUPPORT_TYPES } = require('../shared/constant')

exports.selectCreateType = () => ({
name: 'type',
type: 'list',
message: '(必填)请选择创建类型:',
choices: ['component', 'vue-devui', 'vitepress/sidebar'],
choices: CREATE_SUPPORT_TYPES,
default: 0
})
17 changes: 17 additions & 0 deletions devui-cli/shared/constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,26 @@ exports.INDEX_FILE_NAME = 'index.ts'
exports.VUE_DEVUI_IGNORE_DIRS = ['shared', 'style']
exports.VUE_DEVUI_FILE_NAME = 'vue-devui.ts'
exports.VUE_DEVUI_FILE = resolve(this.DEVUI_DIR, this.VUE_DEVUI_FILE_NAME)
exports.SITES_DIR = resolve(this.CWD, 'sites')
exports.SITES_COMPONENTS_DIR_NAME = 'components'
exports.SITES_COMPONENTS_DIR = resolve(this.SITES_DIR, this.SITES_COMPONENTS_DIR_NAME)
exports.VITEPRESS_DIR = resolve(this.SITES_DIR, '.vitepress')
exports.VITEPRESS_SIDEBAR_FILE_NAME = 'sidebar.ts'
exports.VITEPRESS_SIDEBAR_FILE = resolve(this.VITEPRESS_DIR, `config/${this.VITEPRESS_SIDEBAR_FILE_NAME}`)

// 这里的分类顺序将会影响最终生成的页面侧边栏顺序
exports.VITEPRESS_SIDEBAR_CATEGORY = ['通用', '导航', '反馈', '数据录入', '数据展示', '布局']

exports.COMPONENT_PARTS_MAP = new Map([
['component', 'component(组件)'],
['directive', 'directive(指令)'],
['service', 'service(服务)']
])

exports.CREATE_SUPPORT_TYPE_MAP = Object.freeze({
component: 'component',
'vue-devui': 'vue-devui',
'vitepress/sidebar': 'vitepress/sidebar'
})
exports.CREATE_SUPPORT_TYPES = Object.keys(this.CREATE_SUPPORT_TYPE_MAP)
exports.CREATE_UNFINISHED_TYPES = []
54 changes: 48 additions & 6 deletions devui-cli/shared/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { camelCase, upperFirst } = require('lodash')
const { INDEX_FILE_NAME } = require('./constant')
const { INDEX_FILE_NAME, DEVUI_DIR } = require('./constant')
const { resolve } = require('path')
const logger = require('./logger')
const fs = require('fs-extra')
Expand Down Expand Up @@ -57,17 +57,14 @@ exports.parseExportByFileInfo = (fileInfo, ignoreParseError) => {

while (searchContent.search(partRe) !== -1) {
const reStartIndex = indexContent.search(partRe)
const partStartIndex = indexContent.indexOf('{', reStartIndex) + 1
const partEndIndex = indexContent.indexOf('}', partStartIndex) - 1

const partContent = indexContent.slice(partStartIndex, partEndIndex)
const partContent = this.extractStr(indexContent, '{', '}', reStartIndex)

partContent
.replace(/(\s|\r|\n|\t)/g, '')
.split(',')
.forEach((p) => parts.push(p))

searchContent = indexContent.slice(partEndIndex)
searchContent = indexContent.slice(reStartIndex + partContent.length)
}

exportModule.default = fileInfo.name + 'Install'
Expand All @@ -76,3 +73,48 @@ exports.parseExportByFileInfo = (fileInfo, ignoreParseError) => {

return exportModule
}

exports.parseComponentInfo = (name) => {
const componentInfo = {}
const indexContent = fs.readFileSync(resolve(DEVUI_DIR, name, INDEX_FILE_NAME), { encoding: 'utf-8' })

const defaultRe = /export default/

if (!defaultRe.test(indexContent)) {
logger.warning(`${this.bigCamelCase(name)} must have "export default" and component info.`)
} else {
const reStartIndex = indexContent.indexOf('export default {')
const extraRe = /((^['"])|(['"]$))/g

componentInfo.title = this.extractStr(indexContent, 'title:', ',', reStartIndex).trim().replace(extraRe, '')
componentInfo.category = this.extractStr(indexContent, 'category:', ',', reStartIndex).trim().replace(extraRe, '')
componentInfo.status = this.transformNullishStr(
this.extractStr(indexContent, 'status:', ',', reStartIndex).trim().replace(extraRe, '')
)
}

componentInfo.name = this.bigCamelCase(name)

return componentInfo
}

exports.transformNullishStr = (str) => {
console.log(str)
switch (str) {
case 'null':
return null
case 'undefined':
return undefined
default:
return str
}
}

exports.extractStr = (content = '', startKeywords = '', endKeywords = '', startIndex = 0) => {
const keywordsStartIndex = content.indexOf(startKeywords, startIndex) + startKeywords.length
const keywordsEndIndex = content.indexOf(endKeywords, keywordsStartIndex)

if ([keywordsStartIndex - startIndex, keywordsEndIndex].some((index) => index < 0)) return ''

return content.slice(keywordsStartIndex, keywordsEndIndex)
}
11 changes: 6 additions & 5 deletions devui-cli/templates/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ exports.createIndexTemplate = ({
const importDirectiveStr = `\nimport ${bigCamelCase(directiveName)} from './src/${directiveName}'`
const importServiceStr = `\nimport ${bigCamelCase(serviceName)} from './src/${serviceName}'`

const installComponentStr = `\n\t\tapp.use(${bigCamelCase(componentName)} as any)`
const installDirectiveStr = `\n\t\tapp.directive('${bigCamelCase(componentName)}', ${bigCamelCase(directiveName)})`
const installServiceStr = `\n\t\tapp.config.globalProperties.$${camelCase(serviceName)} = ${bigCamelCase(
const installComponentStr = `\n app.use(${bigCamelCase(componentName)} as any)`
const installDirectiveStr = `\n app.directive('${bigCamelCase(componentName)}', ${bigCamelCase(directiveName)})`
const installServiceStr = `\n app.config.globalProperties.$${camelCase(serviceName)} = ${bigCamelCase(
serviceName
)}`

Expand All @@ -101,7 +101,7 @@ import type { App } from 'vue'\
${importStr}
${
hasComponent
? `\n${bigCamelCase(componentName)}.install = function(app: App) {
? `\n${bigCamelCase(componentName)}.install = function(app: App): void {
app.component(${bigCamelCase(componentName)}.name, ${bigCamelCase(componentName)})
}\n`
: ''
Expand All @@ -117,7 +117,8 @@ export { ${[
export default {
title: '${bigCamelCase(componentName)} ${title}',
category: '${category}',
install(app: App) {\
status: undefined, \/\/ TODO: 组件若开发完成则填入"已完成",并删除该注释
install(app: App): void {
${installStr}
}
}
Expand Down
35 changes: 35 additions & 0 deletions devui-cli/templates/vitepress-sidebar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const { kebabCase } = require('lodash')
const { SITES_COMPONENTS_DIR_NAME, VITEPRESS_SIDEBAR_CATEGORY } = require('../shared/constant')
const logger = require('../shared/logger')

function buildComponentOptions(text, name, status) {
return { text, link: `/${SITES_COMPONENTS_DIR_NAME}/${kebabCase(name)}/`, status }
}

function buildCategoryOptions(text, children = []) {
return { text, children }
}

exports.createVitepressSidebarTemplate = (componentsInfo) => {
const rootNav = { text: '快速开始', link: '/' }
const categoryMap = VITEPRESS_SIDEBAR_CATEGORY.reduce((map, cate) => map.set(cate, []), new Map())

;(componentsInfo || []).forEach((info) => {
if (categoryMap.has(info.category)) {
categoryMap.get(info.category).push(buildComponentOptions(info.title, info.name, info.status))
} else {
logger.warning(`组件 ${info.name} 的分类 ${info.category} 不存在!`)
}
})

const sidebar = [].concat(
rootNav,
Array.from(categoryMap).map(([k, v]) => buildCategoryOptions(k, v))
)

return `\
export default {
'/': ${JSON.stringify(sidebar, null, 2).replace(/\n/g, '\n\t')}
}
`
}
4 changes: 2 additions & 2 deletions devui/accordion/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { Accordion }
export default {
title: 'Accordion 手风琴',
category: '导航',
install(app: App): void {
app.use(Accordion as any)
install(app: App): void {
app.use(Accordion as any)
}
}
4 changes: 2 additions & 2 deletions devui/alert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { Alert }
export default {
title: 'Alert 警告',
category: '反馈',
install(app: App): void {
app.use(Alert as any)
install(app: App): void {
app.use(Alert as any)
}
}
Loading

0 comments on commit 98a8235

Please sign in to comment.