Lei Zhang

Lei Zhang

2022,三餐四季温素有趣。

gulp-enhance-your-work-flow

整个流程非常的简单,通过加载 component folderpage folder

动态的迁移对应 template file 到指定的目录中。

script => args => load folder => output folder

在创建 page 时,由于小程序 app.json 的特殊性质,我们需要区分主包分包

并且在迁移时,还要自动的更新 app.json,将新增的主包 分包 页面 等维护在配置文件中。

可以依据项目实际情况,将一切你认为需要初始化的东西全部写在模板装

Component

component/tmp.js
/**
 * // @echo FILENAME
 *
 * @desc      Component
 * @author    // @echo AUTHOR
 * @date      // @echo DATE
 */

Component({
  behaviors: [],

  options: {
    multipleSlots: true
  },

  externalClasses: [],

  /**
   * 组件的属性列表
   */
  properties: {},

  /**
   * 组件的初始数据
   */
  data: {},

  /**
   * 组件的方法列表
   */
  methods: {}
})
component/tmp.json
{
  "component": true
}
component/tmp.markdown
# 组件文档

## classes

| 名称 | 说明 |
| --- | --- |
| N/A | N/A |

## props

| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| N/A | N/A | N/A | N/A |

## events

| 名称 | 说明 | 参数 |
| --- | --- | --- |
| N/A | N/A | N/A |

## slots

| 名称 | 说明 |
| --- | --- |
| N/A | N/A |

## 示例

### 基本用法

N/A
component/tmp.scss
@import '@/_shared/styles/themes.scss';

.container {
  position: relative;
}
component/tmp.wxml
<view class="container">
  <!-- @echo FILENAME -->
</view>

Page

page/tmp.js
/**
 * // @echo FILENAME
 *
 * @desc      Page
 * @author    // @echo AUTHOR
 * @date      // @echo DATE
 */

Page({
  /**
   * 页面的初始数据
   */
  data: {},

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {},

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {},

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {},

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {},

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {},

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {},

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {}
})
page/tmp.json
{
  "navigationBarTitleText": "wechat",
  "backgroundColor": "#f6f6f6",
  "navigationBarTextStyle": "black",
  "navigationStyle": "custom",
  "enablePullDownRefresh": false,
  "usingComponents": {}
}
page/tmp.scss
@import '@/_shared/styles/themes.scss';

.container {
  position: relative;
}
page/tmp.wxml
<navbar title="<!-- @echo FILENAME -->" />

<view class="container">
  <!-- @echo FILENAME -->
</view>

SCRIPTS

index.js
const { execSync } = require('child_process')
const path = require('path')
const through2 = require('through2')
const gulp = require('gulp')
const $ = require('gulp-load-plugins')({ DEBUG: false, lazy: true })
const yargs = require('yargs/yargs')

const { dateFormatter, migration } = require('../lib/helper')
const logger = require('../lib/logger')

const args = yargs(yargs.hideBin(process.argv)).argv

const exec = command => {
  try {
    const result = execSync(command, { stdio: 'pipe' })

    return result.toString().replace(/(\r\n|\n|\r)/gm, '')
  } catch (error) {
    return null
  }
}

const userName = exec('git config user.name')
const userEmail = exec('git config user.email')

// 作者
const author = userName
  ? userEmail
    ? `${userName}<${userEmail}>`
    : userName
  : 'mp-generator'

// 组件名
const component = args.component || args.c

// 页面名
const page = args.page || args.p

// 分包名,例:"home" "customer/order" 等
// 若不指定
// 组件则会创建至 "./src/_shared/components/" 中
// 页面则会创建至 "./src/home/pages/" 中
const _system = args.system || args.s
// 排除 "home" 目录,"home" 为主包
const system = _system === 'home' && page ? undefined : _system

// 模式,用于日志字符串拼接
const mode = page ? 'page' : 'component'

// 上下文变量
const processContext = {
  FILENAME: component || page,
  AUTHOR: author,
  DATE: dateFormatter(new Date(), 'yyyy-MM-dd')
}

// 文件名称
let fileName
// 输出目录
let output

if (mode === 'component') {
  const targetFolder = system
    ? `./src/${system}/components`
    : `./src/_shared/components`

  fileName = component
  output = path.resolve(process.cwd(), targetFolder, fileName)
}

if (mode === 'page') {
  const targetFolder = system ? `./src/${system}/pages` : `./src/home/pages`

  fileName = page

  output = path.resolve(process.cwd(), targetFolder, fileName)
}

validate()

run()

// 参数校验
function validate() {
  if (!component && !page) {
    logger.fatal(new Error('必须指定页面或组件名称'))
  }

  if (component && page) {
    logger.fatal(new Error('不能同时创建页面和组件'))
  }

  if (!output) {
    logger.fatal(new Error('无法解析出 output'))
  }
}

async function run() {
  try {
    await migrateJs()
    await migrateScss()
    await migrateOthers()
    await generateAppJson()

    logger.success(
      `${mode} has created @ ${path.resolve(process.cwd(), output)}`
    )
  } catch (error) {
    logger.fatal(error)
  }
}

// 迁移 js
function migrateJs() {
  return new Promise((resolve, reject) => {
    const stream = gulp
      .src(path.resolve(__dirname, `tpl/${mode}/tpl.js`))
      .pipe($.preprocess({ context: processContext, type: 'js' }))
      .pipe($.rename({ basename: fileName, extname: '.js' }))
      .pipe(migration())
      .pipe(gulp.dest(output))

    stream.on('end', () => resolve())

    stream.on('error', err => reject(err))
  })
}

// 迁移 scss
function migrateScss() {
  return new Promise((resolve, reject) => {
    const stream = gulp
      .src(path.resolve(__dirname, `tpl/${mode}/tpl.scss`))
      .pipe($.preprocess({ context: processContext, type: 'css' }))
      .pipe($.rename({ basename: fileName, extname: '.scss' }))
      .pipe(migration())
      .pipe(gulp.dest(output))

    stream.on('end', () => resolve())

    stream.on('error', err => reject(err))
  })
}

// 迁移 wxml json
function migrateOthers() {
  return new Promise((resolve, reject) => {
    const stream = gulp
      .src(path.resolve(__dirname, `tpl/${mode}/tpl.!(js|scss)`))
      .pipe($.preprocess({ context: processContext, type: 'html' }))
      .pipe(
        $.rename(p => {
          p.basename = fileName
        })
      )
      .pipe(migration())
      .pipe(gulp.dest(output))

    stream.on('end', () => resolve())

    stream.on('error', err => reject(err))
  })
}

// 更新 page route
function generateAppJson() {
  return new Promise((resolve, reject) => {
    if (mode !== 'page') return resolve()

    const stream = gulp
      .src(path.resolve(process.cwd(), './src/app.json'))
      .pipe(
        through2.obj(function (chunk, enc, callback) {
          const { contents } = chunk
          const data = JSON.parse(contents.toString())

          // 主包,则直接添加路由
          // 分包,则添加至分包的路由中
          if (!system) {
            const { pages = [] } = data

            const route = `home/pages/${fileName}/${fileName}`

            const found = pages.find(item => item === route)

            if (found === undefined) {
              pages.push(route)
            }
          } else {
            const { subPackages } = data

            const route = `pages/${fileName}/${fileName}`

            if (!subPackages || subPackages.length === 0) {
              data.subPackages = []
              data.subPackages.push({
                root: system,
                pages: [route]
              })
            } else {
              const index = subPackages.findIndex(item => item.root === system)

              if (index > -1) {
                const isExist = subPackages[index].pages.findIndex(
                  item => item === route
                )

                if (isExist === -1) subPackages[index].pages.push(route)
              } else {
                subPackages.push({
                  root: system,
                  pages: [route]
                })
              }
            }
          }

          chunk.contents = Buffer.from(JSON.stringify(data), 'utf8')

          this.push(chunk)

          callback()
        })
      )
      .pipe($.jsonFormat(2))
      .pipe(migration('generate'))
      .pipe(gulp.dest(path.resolve(process.cwd(), './src/')))

    stream.on('end', () => resolve())

    stream.on('error', err => reject(err))
  })
}
编辑此页
最后更新于113 天之前
icon©2022