Example #1
0
 files.forEach(file => {
   if (fs.existsSync(file)) {
     const code = fs.readFileSync(file).toString()
     const transformResult = wxTransformer({
       code,
       sourcePath: file,
       outputPath: file,
       isNormal: true,
       isTyped: REG_TYPESCRIPT.test(file)
     })
     const {
       styleFiles,
       scriptFiles,
       jsonFiles,
       mediaFiles
     } = parseAst(PARSE_AST_TYPE.NORMAL, transformResult.ast, [], file, file, true)
     const resFiles = styleFiles.concat(scriptFiles, jsonFiles, mediaFiles)
     if (resFiles.length) {
       resFiles.forEach(item => {
         copyFileToDist(item, sourceDir, outputDir)
       })
     }
     if (scriptFiles.length) {
       analyzeFiles(scriptFiles, sourceDir, outputDir)
     }
     if (styleFiles.length) {
       analyzeStyleFilesImport(styleFiles, sourceDir, outputDir)
     }
   }
 })
Example #2
0
export function isFileToBeTaroComponent (
  code: string,
  sourcePath: string,
  outputPath: string
) {
  const {
    buildAdapter,
    sourceDir,
    constantsReplaceList,
    jsxAttributeNameReplace
  } = getBuildData()
  const transformResult: IWxTransformResult = wxTransformer({
    code,
    sourcePath: sourcePath,
    sourceDir,
    outputPath: outputPath,
    isNormal: true,
    isTyped: REG_TYPESCRIPT.test(sourcePath),
    adapter: buildAdapter,
    env: constantsReplaceList,
    jsxAttributeNameReplace
  })
  const { ast }: IWxTransformResult = transformResult
  let isTaroComponent = false

  traverse(ast, {
    ClassDeclaration (astPath) {
      astPath.traverse({
        ClassMethod (astPath) {
          if (astPath.get('key').isIdentifier({ name: 'render' })) {
            astPath.traverse({
              JSXElement () {
                isTaroComponent = true
              }
            })
          }
        }
      })
    },

    ClassExpression (astPath) {
      astPath.traverse({
        ClassMethod (astPath) {
          if (astPath.get('key').isIdentifier({ name: 'render' })) {
            astPath.traverse({
              JSXElement () {
                isTaroComponent = true
              }
            })
          }
        }
      })
    }
  })

  return {
    isTaroComponent,
    transformResult
  }
}
Example #3
0
async function buildH5Lib () {
  try {
    const { appPath, outputDirName, tempPath } = buildData
    const outputDir = path.join(appPath, outputDirName, h5OutputName)
    const tempEntryFilePath = resolveScriptPath(path.join(tempPath, 'index'))
    const outputEntryFilePath = path.join(outputDir, path.basename(tempEntryFilePath))
    const code = fs.readFileSync(tempEntryFilePath).toString()
    const transformResult = wxTransformer({
      code,
      sourcePath: tempEntryFilePath,
      outputPath: outputEntryFilePath,
      isNormal: true,
      isTyped: REG_TYPESCRIPT.test(tempEntryFilePath)
    })
    const { styleFiles, components, code: generateCode } = parseEntryAst(transformResult.ast, tempEntryFilePath)
    const relativePath = path.relative(appPath, tempEntryFilePath)
    printLog(processTypeEnum.COPY, '发现文件', relativePath)
    fs.ensureDirSync(path.dirname(outputEntryFilePath))
    fs.writeFileSync(outputEntryFilePath, generateCode)
    if (components.length) {
      components.forEach(item => {
        copyFileToDist(item.path as string, tempPath, outputDir)
      })
      analyzeFiles(components.map(item => item.path as string), tempPath, outputDir)
    }
    if (styleFiles.length) {
      styleFiles.forEach(item => {
        copyFileToDist(item, tempPath, path.join(appPath, outputDirName))
      })
      analyzeStyleFilesImport(styleFiles, tempPath, path.join(appPath, outputDirName))
    }
  } catch (err) {
    console.log(err)
  }
}
Example #4
0
 files.forEach(file => {
   if (!fs.existsSync(file) || this.hadBeenCopyedFiles.has(file)) {
     return
   }
   const code = fs.readFileSync(file).toString()
   let outputFilePath = file.replace(this.root, this.convertDir)
   const extname = path.extname(outputFilePath)
   if (/\.wxs/.test(extname)) {
     outputFilePath += '.js'
   }
   const transformResult = wxTransformer({
     code,
     sourcePath: file,
     outputPath: outputFilePath,
     isNormal: true,
     isTyped: REG_TYPESCRIPT.test(file)
   })
   const { ast, scriptFiles } = this.parseAst({
     ast: transformResult.ast,
     outputFilePath,
     sourceFilePath: file
   })
   const jsCode = generateMinimalEscapeCode(ast)
   this.writeFileToTaro(outputFilePath, prettier.format(jsCode, prettierJSConfig))
   printLog(processTypeEnum.COPY, 'JS 文件', this.generateShowPath(outputFilePath))
   this.hadBeenCopyedFiles.add(file)
   this.generateScriptFiles(scriptFiles)
 })
Example #5
0
function getJSAst (code, filePath) {
  return wxTransformer({
    code,
    sourcePath: filePath,
    isNormal: true,
    isTyped: REG_TYPESCRIPT.test(filePath),
    adapter: 'rn'
  }).ast
}
Example #6
0
async function buildForWeapp () {
  const { appPath, entryFilePath, outputDirName, entryFileName, sourceDir } = buildData
  console.log()
  console.log(chalk.green('开始编译小程序端组件库!'))
  if (!fs.existsSync(entryFilePath)) {
    console.log(chalk.red('入口文件不存在,请检查!'))
    return
  }
  try {
    const outputDir = path.join(appPath, outputDirName, weappOutputName)
    const outputEntryFilePath = path.join(outputDir, entryFileName)
    const code = fs.readFileSync(entryFilePath).toString()
    const transformResult = wxTransformer({
      code,
      sourcePath: entryFilePath,
      outputPath: outputEntryFilePath,
      isNormal: true,
      isTyped: REG_TYPESCRIPT.test(entryFilePath)
    })
    const { styleFiles, components } = parseEntryAst(transformResult.ast, entryFilePath)
    if (styleFiles.length) {
      const outputStylePath = path.join(outputDir, 'css', 'index.css')
      await compileDepStyles(outputStylePath, styleFiles)
    }
    const relativePath = path.relative(appPath, entryFilePath)
    printLog(processTypeEnum.COPY, '发现文件', relativePath)
    fs.ensureDirSync(path.dirname(outputEntryFilePath))
    fs.copyFileSync(entryFilePath, path.format({
      dir: path.dirname(outputEntryFilePath),
      base: path.basename(outputEntryFilePath)
    }))
    if (components.length) {
      components.forEach(item => {
        copyFileToDist(item.path as string, sourceDir, outputDir)
      })
      analyzeFiles(components.map(item => item.path as string), sourceDir, outputDir)
    }
  } catch (err) {
    console.log(err)
  }
}
Example #7
0
 copyFilesFromSrcToOutput(cFiles, (sourceFilePath, outputFilePath) => {
   if (fs.existsSync(sourceFilePath)) {
     const fileContent = fs.readFileSync(sourceFilePath).toString()
     const match = SCRIPT_CONTENT_REG.exec(fileContent)
     if (match) {
       const scriptContent = match[1]
       const transformResult: IWxTransformResult = wxTransformer({
         code: scriptContent,
         sourcePath: sourceFilePath,
         sourceDir: getBuildData().sourceDir,
         outputPath: outputFilePath,
         isNormal: true,
         isTyped: false,
         adapter: BUILD_TYPES.QUICKAPP
       })
       const res = parseAst(PARSE_AST_TYPE.NORMAL, transformResult.ast, [], sourceFilePath, outputFilePath)
       const newFileContent = fileContent.replace(SCRIPT_CONTENT_REG, `<script>${res.code}</script>`)
       fs.ensureDirSync(path.dirname(outputFilePath))
       fs.writeFileSync(outputFilePath, newFileContent)
     }
   }
 })
Example #8
0
export async function compileScriptFile (
  content: string,
  sourceFilePath: string,
  outputFilePath: string,
  adapter: BUILD_TYPES
): Promise<string> {
  const {
    appPath,
    sourceDir,
    constantsReplaceList,
    jsxAttributeNameReplace,
    projectConfig
  } = getBuildData()
  if (NODE_MODULES_REG.test(sourceFilePath) && fs.existsSync(outputFilePath)) {
    return fs.readFileSync(outputFilePath).toString()
  }
  const babelConfig = getBabelConfig(projectConfig!.plugins!.babel)
  const compileScriptRes = await callPlugin('babel', content, sourceFilePath, babelConfig, appPath)
  const code = compileScriptRes.code
  if (!shouldTransformAgain()) {
    return code
  }
  const transformResult: IWxTransformResult = wxTransformer({
    code,
    sourcePath: sourceFilePath,
    sourceDir,
    outputPath: outputFilePath,
    isNormal: true,
    isTyped: false,
    adapter,
    env: constantsReplaceList,
    jsxAttributeNameReplace
  })
  const res = parseAst(PARSE_AST_TYPE.NORMAL, transformResult.ast, [], sourceFilePath, outputFilePath)
  return res.code
}
Example #9
0
export async function buildSingleComponent (
  componentObj: IComponentObj,
  buildConfig: IComponentBuildConfig = {}
): Promise<IBuildResult> {
  const componentsBuildResult = getComponentsBuildResult()
  if (isComponentHasBeenBuilt(componentObj.path as string) && componentsBuildResult.get(componentObj.path as string)) {
    return componentsBuildResult.get(componentObj.path as string) as IBuildResult
  }
  const {
    appPath,
    buildAdapter,
    constantsReplaceList,
    sourceDir,
    outputDir,
    sourceDirName,
    outputDirName,
    npmOutputDir,
    nodeModulesPath,
    outputFilesTypes,
    isProduction,
    jsxAttributeNameReplace,
    projectConfig
  } = getBuildData()
  const isQuickApp = buildAdapter === BUILD_TYPES.QUICKAPP

  if (componentObj.path) {
    componentsNamedMap.set(componentObj.path, {
      name: componentObj.name,
      type: componentObj.type
    })
  }
  const component = componentObj.path
  if (!component) {
    printLog(processTypeEnum.ERROR, '组件错误', `组件${_.upperFirst(_.camelCase(componentObj.name))}路径错误,请检查!(可能原因是导出的组件名不正确)`)
    return {
      js: '',
      wxss: '',
      wxml: ''
    }
  }
  let componentShowPath = component.replace(appPath + path.sep, '')
  componentShowPath = componentShowPath.split(path.sep).join('/')
  let isComponentFromNodeModules = false
  let sourceDirPath = sourceDir
  let buildOutputDir = outputDir
  // 来自 node_modules 的组件
  if (NODE_MODULES_REG.test(componentShowPath)) {
    isComponentFromNodeModules = true
    sourceDirPath = nodeModulesPath
    buildOutputDir = npmOutputDir
  }
  let outputComponentShowPath = componentShowPath.replace(isComponentFromNodeModules ? NODE_MODULES : sourceDirName, buildConfig.outputDirName || outputDirName)
  outputComponentShowPath = outputComponentShowPath.replace(path.extname(outputComponentShowPath), '')
  printLog(processTypeEnum.COMPILE, '组件文件', componentShowPath)
  const componentContent = fs.readFileSync(component).toString()
  const outputComponentJSPath = component.replace(sourceDirPath, buildConfig.outputDir || buildOutputDir).replace(path.extname(component), outputFilesTypes.SCRIPT)
  const outputComponentWXMLPath = outputComponentJSPath.replace(path.extname(outputComponentJSPath), outputFilesTypes.TEMPL)
  const outputComponentWXSSPath = outputComponentJSPath.replace(path.extname(outputComponentJSPath), outputFilesTypes.STYLE)
  const outputComponentJSONPath = outputComponentJSPath.replace(path.extname(outputComponentJSPath), outputFilesTypes.CONFIG)
  if (!isComponentHasBeenBuilt(component)) {
    setHasBeenBuiltComponents(component)
  }
  try {
    const isTaroComponentRes = isFileToBeTaroComponent(componentContent, component, outputComponentJSPath)
    const componentExportsMap = getComponentExportsMap()
    if (!isTaroComponentRes.isTaroComponent) {
      const transformResult = isTaroComponentRes.transformResult
      const componentRealPath = parseComponentExportAst(transformResult.ast, componentObj.name as string, component, componentObj.type as string)
      const realComponentObj: IComponentObj = {
        path: componentRealPath,
        name: componentObj.name,
        type: componentObj.type
      }
      let isInMap = false
      notTaroComponents.add(component)
      if (componentExportsMap.size) {
        componentExportsMap.forEach(componentExports => {
          componentExports.forEach(item => {
            if (item.path === component) {
              isInMap = true
              item.path = componentRealPath
            }
          })
        })
      }
      if (!isInMap) {
        const componentExportsMapItem = componentExportsMap.get(component) || []
        componentExportsMapItem.push(realComponentObj)
        setComponentExportsMap(component, componentExportsMapItem)
      }
      return await buildSingleComponent(realComponentObj, buildConfig)
    }
    const transformResult: IWxTransformResult = wxTransformer({
      code: componentContent,
      sourcePath: component,
      sourceDir,
      outputPath: outputComponentJSPath,
      isRoot: false,
      isTyped: REG_TYPESCRIPT.test(component),
      isNormal: false,
      adapter: buildAdapter,
      env: constantsReplaceList,
      jsxAttributeNameReplace
    })
    const componentWXMLContent = isProduction ? transformResult.compressedTemplate : transformResult.template
    const componentDepComponents = transformResult.components
    const res = parseAst(PARSE_AST_TYPE.COMPONENT, transformResult.ast, componentDepComponents, component, outputComponentJSPath, buildConfig.npmSkip)
    let resCode = res.code
    fs.ensureDirSync(path.dirname(outputComponentJSPath))
    // 解析原生组件
    const { usingComponents = {} }: IConfig = res.configObj
    if (usingComponents && !isEmptyObject(usingComponents)) {
      const keys = Object.keys(usingComponents)
      keys.forEach(item => {
        componentDepComponents.forEach(component => {
          if (_.camelCase(item) === _.camelCase(component.name)) {
            delete usingComponents[item]
          }
        })
      })
      transfromNativeComponents(outputComponentJSONPath.replace(buildConfig.outputDir || buildOutputDir, sourceDirPath), res.configObj)
    }
    if (!isQuickApp) {
      resCode = await compileScriptFile(resCode, component, outputComponentJSPath, buildAdapter)
      if (isProduction) {
        uglifyJS(resCode, component, appPath, projectConfig!.plugins!.uglify as TogglableOptions)
      }
    } else {
      // 快应用编译,搜集创建组件 ux 文件
      const importTaroSelfComponents = getImportTaroSelfComponents(outputComponentJSPath, res.taroSelfComponents)
      const importCustomComponents = new Set(componentDepComponents.map(item => {
        delete item.type
        return item
      }))
      const styleRelativePath = promoteRelativePath(path.relative(outputComponentJSPath, outputComponentWXSSPath))
      const uxTxt = generateQuickAppUx({
        script: resCode,
        style: styleRelativePath,
        imports: new Set([...importTaroSelfComponents, ...importCustomComponents]),
        template: componentWXMLContent
      })
      fs.writeFileSync(outputComponentWXMLPath, uxTxt)
      printLog(processTypeEnum.GENERATE, '组件文件', `${outputDirName}/${componentObj.name}${outputFilesTypes.TEMPL}`)
    }

    const dependencyTree = getDependencyTree()
    const fileDep = dependencyTree.get(component) || {
      style: [],
      script: [],
      json: [],
      media: []
    }
    // 编译依赖的组件文件
    let realComponentsPathList: IComponentObj[] = []
    if (componentDepComponents.length) {
      realComponentsPathList = getRealComponentsPathList(component, componentDepComponents)
      res.scriptFiles = res.scriptFiles.map(item => {
        for (let i = 0; i < realComponentsPathList.length; i++) {
          const componentObj = realComponentsPathList[i]
          const componentPath = componentObj.path
          if (item === componentPath) {
            return ''
          }
        }
        return item
      }).filter(item => item)
      realComponentsPathList = realComponentsPathList.filter(item => !isComponentHasBeenBuilt(item.path as string) || notTaroComponents.has(item.path as string))
      await buildDepComponents(realComponentsPathList)
    }
    if (componentExportsMap.size && realComponentsPathList.length) {
      realComponentsPathList.forEach(componentObj => {
        if (componentExportsMap.has(componentObj.path as string)) {
          const componentMap = componentExportsMap.get(componentObj.path as string)
          componentMap && componentMap.forEach(componentObj => {
            componentDepComponents.forEach(depComponent => {
              if (depComponent.name === componentObj.name) {
                let componentPath = componentObj.path
                let realPath
                if (NODE_MODULES_REG.test(componentPath as string)) {
                  componentPath = (componentPath as string).replace(nodeModulesPath, npmOutputDir)
                  realPath = promoteRelativePath(path.relative(outputComponentJSPath, componentPath))
                } else {
                  realPath = promoteRelativePath(path.relative(component, (componentPath as string)))
                }
                depComponent.path = realPath.replace(path.extname(realPath), '')
              }
            })
          })
        }
      })
    }
    if (!isQuickApp) {
      fs.writeFileSync(outputComponentJSONPath, JSON.stringify(_.merge({}, buildUsingComponents(component, componentDepComponents, true), res.configObj), null, 2))
      printLog(processTypeEnum.GENERATE, '组件配置', `${outputDirName}/${outputComponentShowPath}${outputFilesTypes.CONFIG}`)
      fs.writeFileSync(outputComponentJSPath, resCode)
      printLog(processTypeEnum.GENERATE, '组件逻辑', `${outputDirName}/${outputComponentShowPath}${outputFilesTypes.SCRIPT}`)
      fs.writeFileSync(outputComponentWXMLPath, componentWXMLContent)
      processNativeWxml(outputComponentWXMLPath.replace(outputDir, sourceDir), componentWXMLContent, outputComponentWXMLPath)
      printLog(processTypeEnum.GENERATE, '组件模板', `${outputDirName}/${outputComponentShowPath}${outputFilesTypes.TEMPL}`)
    }
    // 编译依赖的脚本文件
    if (isDifferentArray(fileDep['script'], res.scriptFiles)) {
      await compileDepScripts(res.scriptFiles, !isQuickApp)
    }
    const depComponents = getDepComponents()
    // 编译样式文件
    if (isDifferentArray(fileDep['style'], res.styleFiles) || isDifferentArray(depComponents.get(component) || [], componentDepComponents)) {
      printLog(processTypeEnum.GENERATE, '组件样式', `${outputDirName}/${outputComponentShowPath}${outputFilesTypes.STYLE}`)
      await compileDepStyles(outputComponentWXSSPath, res.styleFiles)
    }
    // 拷贝依赖文件
    if (isDifferentArray(fileDep['json'], res.jsonFiles)) {
      copyFilesFromSrcToOutput(res.jsonFiles)
    }
    if (isDifferentArray(fileDep['media'], res.mediaFiles)) {
      copyFilesFromSrcToOutput(res.mediaFiles)
    }
    fileDep['style'] = res.styleFiles
    fileDep['script'] = res.scriptFiles
    fileDep['json'] = res.jsonFiles
    fileDep['media'] = res.mediaFiles
    dependencyTree.set(component, fileDep)
    depComponents.set(component, componentDepComponents)
    const buildResult = {
      js: outputComponentJSPath,
      wxss: outputComponentWXSSPath,
      wxml: outputComponentWXMLPath
    }
    componentsBuildResult.set(component, buildResult)
    return buildResult
  } catch (err) {
    printLog(processTypeEnum.ERROR, '组件编译', `组件${componentShowPath}编译失败!`)
    console.log(err)
    return {
      js: '',
      wxss: '',
      wxml: ''
    }
  }
}
Example #10
0
 return scriptFiles.map(async item => {
   if (path.isAbsolute(item)) {
     let outputItem
     if (NODE_MODULES_REG.test(item)) {
       outputItem = item.replace(nodeModulesPath, npmOutputDir).replace(path.extname(item), '.js')
     } else {
       outputItem = item.replace(path.join(sourceDir), path.join(outputDir)).replace(path.extname(item), '.js')
     }
     const weappConf = Object.assign({}, projectConfig.weapp)
     const useCompileConf = Object.assign({}, weappConf.compile)
     const compileExclude = useCompileConf.exclude || []
     let isInCompileExclude = false
     compileExclude.forEach(excludeItem => {
       if (item.indexOf(path.join(appPath, excludeItem)) >= 0) {
         isInCompileExclude = true
       }
     })
     if (isInCompileExclude) {
       copyFileSync(item, outputItem)
       return
     }
     if (!isBuildingScripts.get(outputItem)) {
       isBuildingScripts.set(outputItem, true)
       try {
         const code = fs.readFileSync(item).toString()
         const transformResult = wxTransformer({
           code,
           sourcePath: item,
           sourceDir,
           outputPath: outputItem,
           isNormal: true,
           isTyped: REG_TYPESCRIPT.test(item),
           adapter: buildAdapter,
           env: constantsReplaceList,
           jsxAttributeNameReplace
         })
         const ast = transformResult.ast
         const res = parseAst(PARSE_AST_TYPE.NORMAL, ast, [], item, outputItem)
         const fileDep = dependencyTree.get(item) || {} as IDependency
         let resCode = res.code
         if (needUseBabel) {
           resCode = await compileScriptFile(res.code, item, outputItem, buildAdapter)
         }
         fs.ensureDirSync(path.dirname(outputItem))
         if (isProduction && needUseBabel) {
           uglifyJS(resCode, item, appPath, projectConfig!.plugins!.uglify as TogglableOptions)
         }
         if (NODE_MODULES_REG.test(item)) {
           resCode = npmCodeHack(outputItem, resCode, buildAdapter)
         }
         fs.writeFileSync(outputItem, resCode)
         let modifyOutput = outputItem.replace(appPath + path.sep, '')
         modifyOutput = modifyOutput.split(path.sep).join('/')
         printLog(processTypeEnum.GENERATE, '依赖文件', modifyOutput)
         // 编译依赖的脚本文件
         if (isDifferentArray(fileDep['script'], res.scriptFiles)) {
           if (buildDepSync) {
             await Promise.all(compileDepScripts(res.scriptFiles, needUseBabel, buildDepSync))
           } else {
             compileDepScripts(res.scriptFiles, needUseBabel, buildDepSync)
           }
         }
         // 拷贝依赖文件
         if (isDifferentArray(fileDep['json'], res.jsonFiles)) {
           copyFilesFromSrcToOutput(res.jsonFiles)
         }
         if (isDifferentArray(fileDep['media'], res.mediaFiles)) {
           copyFilesFromSrcToOutput(res.mediaFiles)
         }
         fileDep['script'] = res.scriptFiles
         fileDep['json'] = res.jsonFiles
         fileDep['media'] = res.mediaFiles
         dependencyTree.set(item, fileDep)
       } catch (err) {
         printLog(processTypeEnum.ERROR, '编译失败', item.replace(appPath + path.sep, ''))
         console.log(err)
       }
     }
   }
 })