Skip to content

Commit

Permalink
feat(mini-runner): 生成快应用页面及组件
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyadam committed Dec 31, 2019
1 parent d469c84 commit 4fd3152
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 88 deletions.
182 changes: 99 additions & 83 deletions packages/taro-mini-runner/src/plugins/MiniPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import { merge, defaults, kebabCase } from 'lodash'
import * as t from 'babel-types'
import traverse from 'babel-traverse'
import { Config as IConfig } from '@tarojs/taro'
import * as _ from 'lodash'

import { REG_TYPESCRIPT, BUILD_TYPES, PARSE_AST_TYPE, MINI_APP_FILES, NODE_MODULES_REG, CONFIG_MAP, taroJsFramework } from '../utils/constants'
import { REG_TYPESCRIPT, BUILD_TYPES, PARSE_AST_TYPE, MINI_APP_FILES, NODE_MODULES_REG, CONFIG_MAP, taroJsFramework, taroJsComponents, QUICKAPP_SPECIAL_COMPONENTS, taroJsQuickAppComponents } from '../utils/constants'
import { IComponentObj } from '../utils/types'
import { traverseObjectNode, resolveScriptPath, buildUsingComponents, isNpmPkg, resolveNpmSync, isEmptyObject } from '../utils'
import { resolveScriptPath, buildUsingComponents, isNpmPkg, resolveNpmSync, isEmptyObject, promoteRelativePath } from '../utils'
import TaroSingleEntryDependency from '../dependencies/TaroSingleEntryDependency'
import { getTaroJsQuickAppComponentsPath, generateQuickAppUx, getImportTaroSelfComponents } from '../utils/helper'
import parseAst from '../utils/parseAst'

import TaroLoadChunksPlugin from './TaroLoadChunksPlugin'
import TaroNormalModulesPlugin from './TaroNormalModulesPlugin'
Expand All @@ -26,6 +29,9 @@ import VirtualModulePlugin from './VirtualModulePlugin/VirtualModulePlugin'
interface IMiniPluginOptions {
appEntry?: string,
buildAdapter: BUILD_TYPES,
nodeModulesPath: string,
sourceDir: string,
outputDir: string,
commonChunks: string[]
}

Expand All @@ -34,7 +40,11 @@ export interface ITaroFileInfo {
type: PARSE_AST_TYPE,
config: IConfig,
template?: string,
code?: string
code?: string,
taroSelfComponents?: Set<{
name: string,
path: string
}>
}
}

Expand Down Expand Up @@ -120,14 +130,20 @@ export default class MiniPlugin {
pages: Set<IComponent>
components: Set<IComponent>
sourceDir: string
outputDir: string
context: string
appConfig: IConfig

constructor (options = {}) {
this.options = defaults(options || {}, {
buildAdapter: BUILD_TYPES.WEAPP,
nodeModulesPath: '',
sourceDir: '',
outputDir: '',
commonChunks: ['runtime', 'vendors']
})
this.sourceDir = this.options.sourceDir
this.outputDir = this.options.outputDir

this.pages = new Set()
this.components = new Set()
Expand Down Expand Up @@ -192,80 +208,10 @@ export default class MiniPlugin {
return app
}
const appEntryPath = getEntryPath(entry)
this.sourceDir = path.dirname(appEntryPath)
compiler.options.entry = {}
return appEntryPath
}

parseAst (
ast: t.File,
buildAdapter: BUILD_TYPES
): {
configObj: IConfig,
hasEnablePageScroll: boolean
} {
let configObj = {}
let hasEnablePageScroll
traverse(ast, {
ClassDeclaration (astPath) {
const node = astPath.node
let hasCreateData = false
if (node.superClass) {
astPath.traverse({
ClassMethod (astPath) {
if (astPath.get('key').isIdentifier({ name: '_createData' })) {
hasCreateData = true
}
}
})
if (hasCreateData) {
astPath.traverse({
ClassMethod (astPath) {
const node = astPath.node
if (node.kind === 'constructor') {
astPath.traverse({
ExpressionStatement (astPath) {
const node = astPath.node
if (node.expression &&
node.expression.type === 'AssignmentExpression' &&
node.expression.operator === '=') {
const left = node.expression.left
if (left.type === 'MemberExpression' &&
left.object.type === 'ThisExpression' &&
left.property.type === 'Identifier' &&
left.property.name === 'config') {
configObj = traverseObjectNode(node.expression.right, buildAdapter)
}
}
}
})
}
}
})
}
}
},
ClassMethod (astPath) {
const keyName = (astPath.get('key').node as t.Identifier).name
if (keyName === 'onPageScroll' || keyName === 'onReachBottom') {
hasEnablePageScroll = true
}
},
ClassProperty (astPath) {
const node = astPath.node
const keyName = node.key.name
if (keyName === 'config') {
configObj = traverseObjectNode(node, buildAdapter)
}
}
})

return {
configObj,
hasEnablePageScroll
}
}

getNpmComponentRealPath (code: string, component: IComponentObj, adapter: BUILD_TYPES): string | null {
let componentRealPath: string | null = null
let importExportName
Expand Down Expand Up @@ -420,7 +366,7 @@ export default class MiniPlugin {
isApp: true,
adapter: buildAdapter
})
const { configObj } = this.parseAst(transformResult.ast, buildAdapter)
const { configObj } = parseAst(transformResult.ast, buildAdapter)
const appPages = configObj.pages
this.appConfig = configObj
if (!appPages || appPages.length === 0) {
Expand Down Expand Up @@ -448,6 +394,18 @@ export default class MiniPlugin {
return fs.existsSync(templatePath) && jsContent.indexOf(taroJsFramework) < 0
}

getComponentName (componentPath) {
let componentName
if (NODE_MODULES_REG.test(componentPath)) {
componentName = componentPath.replace(this.context, '').replace(/\\/g, '/').replace(path.extname(componentPath), '')
componentName = componentName.replace(/node_modules/gi, 'npm')
} else {
componentName = componentPath.replace(this.sourceDir, '').replace(/\\/g, '/').replace(path.extname(componentPath), '')
}

return componentName
}

getComponents (fileList: Set<IComponent>, isRoot: boolean) {
const { buildAdapter } = this.options
const isQuickApp = buildAdapter === BUILD_TYPES.QUICKAPP
Expand All @@ -456,6 +414,7 @@ export default class MiniPlugin {
const isComponentConfig = isRoot ? {} : { component: true }

let configObj
let taroSelfComponents
let depComponents
let template
let code = fs.readFileSync(file.path).toString()
Expand Down Expand Up @@ -484,7 +443,7 @@ export default class MiniPlugin {
isTyped: REG_TYPESCRIPT.test(file.path),
adapter: buildAdapter
})
const res = this.parseAst(aheadTransformResult.ast, buildAdapter)
const res = parseAst(aheadTransformResult.ast, buildAdapter)
if (res.configObj.enablePullDownRefresh || (this.appConfig.window && this.appConfig.window.enablePullDownRefresh)) {
rootProps.enablePullDownRefresh = true
}
Expand All @@ -504,7 +463,9 @@ export default class MiniPlugin {
rootProps: isEmptyObject(rootProps) || rootProps,
adapter: buildAdapter
})
configObj = this.parseAst(transformResult.ast, buildAdapter).configObj
let parseAstRes = parseAst(transformResult.ast, buildAdapter)
configObj = parseAstRes.configObj
taroSelfComponents = parseAstRes.taroSelfComponents
const usingComponents = configObj.usingComponents
if (usingComponents) {
Object.keys(usingComponents).forEach(item => {
Expand All @@ -514,30 +475,68 @@ export default class MiniPlugin {
})
})
}
if (isRoot) {
taroSelfComponents.add('taro-page')
}
depComponents = transformResult.components
template = transformResult.template
code = transformResult.code
}
depComponents = depComponents.filter(item => !/^plugin:\/\//.test(item.path))
this.transfromComponentsPath(depComponents)
if (isQuickApp) {
const scriptPath = file.path
const outputScriptPath = scriptPath.replace(this.sourceDir, this.outputDir).replace(path.extname(scriptPath), MINI_APP_FILES[buildAdapter].SCRIPT)
const stylePath = outputScriptPath.replace(path.extname(outputScriptPath), MINI_APP_FILES[buildAdapter].STYLE)
const templPath = outputScriptPath.replace(path.extname(outputScriptPath), MINI_APP_FILES[buildAdapter].TEMPL)
const styleRelativePath = promoteRelativePath(path.relative(outputScriptPath, stylePath))
const scriptRelativePath = promoteRelativePath(path.relative(templPath, outputScriptPath))
const importTaroSelfComponents = getImportTaroSelfComponents(outputScriptPath, this.options.nodeModulesPath, taroSelfComponents)
const usingComponents = configObj.usingComponents
let importUsingComponent: any = new Set([])
if (usingComponents) {
importUsingComponent = new Set(Object.keys(usingComponents).map(item => {
return {
name: item,
path: usingComponents[item]
}
}))
}
const importCustomComponents = new Set(depComponents.map(item => {
return {
path: item.path,
name: item.name as string
}
}))
template = generateQuickAppUx({
template,
script: scriptRelativePath,
style: styleRelativePath,
imports: new Set([...importTaroSelfComponents, ...importUsingComponent, ...importCustomComponents])
})
}
taroFileTypeMap[file.path] = {
type: isRoot ? PARSE_AST_TYPE.PAGE : PARSE_AST_TYPE.COMPONENT,
config: merge({}, isComponentConfig, buildUsingComponents(file.path, this.sourceDir, {}, depComponents), configObj),
template,
code
}
if (taroSelfComponents) {
taroFileTypeMap[file.path].taroSelfComponents = new Set(Array.from(taroSelfComponents).map(item => {
const taroJsQuickAppComponentsPath = getTaroJsQuickAppComponentsPath(this.options.nodeModulesPath)
const componentPath = path.join(taroJsQuickAppComponentsPath, item as string, `index${MINI_APP_FILES[buildAdapter].TEMPL}`)
return {
name: item as string,
path: componentPath
}
}))
}

if (depComponents && depComponents.length) {
depComponents.forEach(item => {
const componentPath = resolveScriptPath(path.resolve(path.dirname(file.path), item.path))
if (fs.existsSync(componentPath) && !Array.from(this.components).some(item => item.path === componentPath)) {
let componentName
if (NODE_MODULES_REG.test(componentPath)) {
componentName = componentPath.replace(this.context, '').replace(/\\/g, '/').replace(path.extname(componentPath), '')
componentName = componentName.replace(/node_modules/gi, 'npm')
} else {
componentName = componentPath.replace(this.sourceDir, '').replace(/\\/g, '/').replace(path.extname(componentPath), '')
}
const componentName = this.getComponentName(componentPath)
const componentTempPath = this.getTemplatePath(componentPath)
const isNative = this.isNativePageORComponent(componentTempPath, fs.readFileSync(componentPath).toString())
const componentObj = { name: componentName, path: componentPath, isNative }
Expand Down Expand Up @@ -601,6 +600,23 @@ export default class MiniPlugin {
source: () => jsonStr
}
}
if (itemInfo.taroSelfComponents) {
itemInfo.taroSelfComponents.forEach(item => {
if (fs.existsSync(item.path)) {
const content = fs.readFileSync(item.path).toString()
let relativePath
if (NODE_MODULES_REG.test(item.path)) {
relativePath = item.path.replace(this.context, '').replace(/node_modules/gi, 'npm')
} else {
relativePath = item.path.replace(this.sourceDir, '')
}
compilation.assets[relativePath] = {
size: () => content.length,
source: () => content
}
}
})
}
})
}

Expand Down
67 changes: 66 additions & 1 deletion packages/taro-mini-runner/src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as path from 'path'
import * as fs from 'fs'
import { isEmptyObject } from '.'
import { isEmptyObject, getInstalledNpmPkgPath, promoteRelativePath } from '.'
import { taroJsQuickAppComponents, REG_STYLE, REG_SCRIPT } from './constants'

let quickappConfig = {}

Expand All @@ -16,3 +17,67 @@ export function getQuickappConfig (appPath) {
}
return quickappConfig
}

export function getTaroJsQuickAppComponentsPath (nodeModulesPath: string): string {
const taroJsQuickAppComponentsPkg = getInstalledNpmPkgPath(taroJsQuickAppComponents, nodeModulesPath)
if (!taroJsQuickAppComponentsPkg) {
// printLog(processTypeEnum.ERROR, '包安装', `缺少包 ${taroJsQuickAppComponents},请安装!`)
process.exit(0)
}
return path.join(path.dirname(taroJsQuickAppComponentsPkg as string), 'src/components')
}

export function getImportTaroSelfComponents (filePath, nodeModulesPath, taroSelfComponents) {
const importTaroSelfComponents = new Set<{ path: string, name: string }>()
const taroJsQuickAppComponentsPath = getTaroJsQuickAppComponentsPath(nodeModulesPath)
taroSelfComponents.forEach(c => {
const cPath = path.join(taroJsQuickAppComponentsPath, c)
const cMainPath = path.join(cPath, 'index')
const cRelativePath = promoteRelativePath(path.relative(filePath, cMainPath.replace(nodeModulesPath, 'npm')))
importTaroSelfComponents.add({
path: cRelativePath,
name: c
})
})
return importTaroSelfComponents
}

export function generateQuickAppUx ({
script,
template,
style,
imports
}: {
script?: string,
template?: string,
style?: string,
imports?: Set<{
path: string,
name: string
}>
}) {
let uxTxt = ''
if (imports && imports.size) {
imports.forEach(item => {
uxTxt += `<import src='${item.path}' name='${item.name}'></import>\n`
})
}
if (style) {
if (REG_STYLE.test(style)) {
uxTxt += `<style src="${style}"></style>\n`
} else {
uxTxt += `<style>\n${style}\n</style>\n`
}
}
if (template) {
uxTxt += `<template>\n${template}\n</template>\n`
}
if (script) {
if (REG_SCRIPT.test(script)) {
uxTxt += `<script src="${script}"></script>\n`
} else {
uxTxt += `<script>\n${script}\n</script>\n`
}
}
return uxTxt
}
9 changes: 9 additions & 0 deletions packages/taro-mini-runner/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,12 @@ export function recursiveMerge (src, ...args) {
}
})
}

export function getInstalledNpmPkgPath (pkgName: string, basedir: string): string | null {
const resolvePath = require('resolve')
try {
return resolvePath.sync(`${pkgName}/package.json`, { basedir })
} catch (err) {
return null
}
}
Loading

0 comments on commit 4fd3152

Please sign in to comment.