Simba
Simba
TARO

Taro插件:defineConstants自动生成TS声明文件

Taro插件:defineConstants自动生成TS声明文件
7 min read
#Taro

场景

通常我们会使用Typescript来开发Taro项目 并使用Taro约定的config选项defineConstants来定义我们项目中的全局变量(如:cdn地址)

image-20230301133005914

但是当我们在使用这些全局变量时,会发现编辑器中(如:Webstorm)会报以下错误:

image-20230301134003070

这是为什么呢 我们明明在全局已经定义过了叫cdnURL的变量 为啥说找不到呢🤔

分析

简单介绍下我编写这个插件的心路历程嗷:

  1. 首先分析错误提示信息 🧐

    • 前置错误类型提示TS2304 说明是Typescript在对变量进行静态校验的时候找不到cdnUrl这个变量
  2. 思考变量存在哪里 🤔

    • 通过官网defineConstants的介绍了解到 他是基于Webpack DefinePlugin来实现的声明全局变量的功能 它大概是个什么东西呢🤔

      //webpack.config.js
      const webpack = require('webpack');
       
      module.exports = {
          entry: {
              index: "index.js"
          },
          output: {
              path: "dist/",
              filename: "[name].js",
          },
          plugins: [
              new webpack.DefinePlugin({
                  simba: 'sweeeet'
              })
          ]
      };
       
      //index.js
      console.log(simba);
       
      // 编译结果
      function(module, exports, __webpack_require__) {
         console.log((sweeeet));
      }

      说明defineConstants的变量是通过打包生成的变量再给到Taro进行内部的处理 所以Typescript自然也没办法解析到我们内部的变量📦

  3. 查阅官方issues 有无相关内容

    • 翻阅了接近6页的issues 找到了1条相关并给出解决方案的issues

      • defineConstants在webstorm报错

        image-20230301140330312.png

        这样确实可以解决TS报错的问题 但是如果有几十个这样的全局变量 然后全部还要自己手动去声明一次 感觉有点不太聪明的亚子 既然提供了这样的配置选项 感觉可以做的更好一点吧

于是乎我就开搞了!😎

开搞

首先先了解了下Taro插件机制和Wepack类似 提供了主要的三个Hook:

// plugin.js
 
export default (ctx, options) => {
  // plugin 主体
  ctx.onBuildStart(() => {
    console.log('编译开始!')
  })
  ctx.onBuildFinish(() => {
    console.log('Webpack 编译结束!')
  })
  ctx.onBuildComplete(() => {
    console.log('Taro 构建完成!')
  })
}

然后再项目配置中如何引入(因为是独立的功能,这里暂时先不考虑使用presets插件集的方式)

// config/index.js
 
const config = {
  plugins: [
    // 引入 npm 安装的插件
    '@tarojs/plugin-mock',
    // 引入 npm 安装的插件,并传入插件参数
    [
      '@tarojs/plugin-mock',
      {
        mocks: {
          '/api/user/1': {
            name: 'judy',
            desc: 'Mental guy',
          },
        },
      },
    ],
    // 从本地绝对路径引入插件,同样如果需要传入参数也是如上
    '/absulute/path/plugin/filename',
  ],
}

(!!!)Plugin支持本地绝对路径 所以可以在项目本地开发 但是如果没有使用打包工具(如:Rollup),在项目中开发上述plugin.js的示例代码要改为es5的模块

// plugin.js
 
module.exports = (ctx, options) => {
  // plugin 主体
  ctx.onBuildStart(() => {
    console.log('编译开始!')
  })
  ctx.onBuildFinish(() => {
    console.log('Webpack 编译结束!')
  })
  ctx.onBuildComplete(() => {
    console.log('Taro 构建完成!')
  })
}

(我习惯自己开发npm包了 所以就不采用这种方式 愿意折腾的可以继续往下看0.0)

OK 知己知彼百战百胜 开始搭建我们的npm包环境

  1. npm init创建我们项目的package.json

  2. 创建打包相关的目录(如:src/dist/lib) 配置打包使用的依赖 我这边使用的是Rollup/Typescript来进行开发

    (详细打包相关的不展开说明了 可以去看下我的github)

  3. 核心逻辑是generateDeclare函数

    image-20230301143044168.png

    • 递归读取我们defineConstants中的对象 通过JSON.parse & typeof value来确定我们最终定义的类型
    • (!!!)特殊情况说明:这里考虑到大家总是对defineConstants中定义字符串出错不敏感 因为如果要定义字符串必须通过JSON.stringify / '"xxx"'来定义 特别是后者这种模式我感觉是很反人类的 所以我在开发这块儿的时候给这里做了特殊处理 详见代码
  4. 写入我们的类型声明文件

    image-20230301143628580.png

    • 插件主体这块我考虑过后只使用了onBuildStart钩子来处理逻辑
    • 这里考虑到如果是打包成生产环境(即production)不需要声明文件 所以直接逻辑会直接中断
    • 写一个提示语告诉用户开始编译
    • 调用node fs模块中的writeFile异步写入声明文件
      • 这块对比了下options中使用追加写入 or 覆盖写入的差别 认为不需要再额外写特殊的diff逻辑 直接覆盖也不会有很大的性能开销 所以还是选择了覆盖写入
    • 写一个编译结束时的结束语
  5. 编写单测(还没整

  6. package.json中的version字段改为beta版本

    • version: 0.0.1-beta
  7. 添加.npmrc文件写入自己的token 执行npm publish发布

食用方法 🔨

#安装依赖
 
npm install -D taro-plugin-auto-declare-define-constants
 
#or yarn
 
yarn add -D taro-plugin-auto-declare-define-constants
// config/index.js
 
const config = {
  //...
  plugins: [
    'taro-plugin-auto-declare-define-constants'
  ]
  //...
}
#最后将项目跑起来 看下项目根目录是否出现「defineConstants.d.ts」
 
npm run dev:weapp
 
#or yarn
 
yarn dev:weapp

image-20230301145055217.png

image-20230301145127164.png

这样我们就对defineConstants进行全局的类型声明 解决了TS的报错问题 编辑器也不会报错啦 🎉

  • 如果有其他的更好的建议 欢迎给我提issues thx 🤓