npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@antmjs/rapper

v2.4.0

Published

rapper, use http api as a function

Downloads

87

Readme

Rapper 是什么?

注:可以使用@antmjs/api 代替

Rapper 是 TypeScript 的最佳拍档,它可以帮你生成具有类型定义的请求方案。

  • 无需自行书写请求代码,把 HTTP 接口当做函数调用
  • 请求参数/返回数据类型化,静态校验、自动补全快到飞起

@antmjs/rapper 是什么?

基于 Rapper 开发,使配置更灵活,同时增加本地类型同步远程文档重要功能

  • ++++
  • 本地接口类型上传到 rapper 远程文档,本地编码驱动远程文档
  • 自定义请求函数模板,满足不同编程规范

快速开始

  1. package.json scripts 中 添加 { "rap" : "npx rapper"}

  2. 配置 antm.config.js

{
  rapper: {
    // 拉取接口地址
    apiUrl?: string;
    /** rap 前端地址,默认是 http://rap2.taobao.org */
    rapUrl?: string;
    // 生成的文件目录地址
    rapperPath?: string;
    // rap登录cookie
    tokenCookie?: string;
    // rap项目id
    repositoryId?: number;
  }
}
  1. 开始写你的 ts 接口类型, 然后执行 npm run rap

rapper 名称对应 接口 ts 类型介绍

ts 接口类型需要配合 rapper 使用

  • 暂时不支持 url 带 path 参数

    rapper 接口字段名称对应

interface XY {
  x: number
  y: number
  z: number
}
export type IUserInfo = {
  request: {
    // (1)
    age?: string // (2)
  }
  response: {
    // (3)
    /**
     *
     * @value true
     */
    success: boolean
    data: {
      /**
       * 数组演示 // (4)
       * @rule 123 // (6)
       */
      array: {
        /**
         * 名称
         * @value #cname // (5)
         */
        name: string
        /**
         * 支持泛型以及接口引用
         */
        other: XY
      }[]
    }
  }
}

以下是 rapper 中含义

  • 1 [request] 请求参数定义;
  • 2 [age?: string] 入参定义字段名称,必选,类型;
  • 3 [response] 返回数据定义;
  • 4 [jsDoc 描述: 数组演示] 字段简介
  • 5 [jsDoc 关键字:@value #cname] 字段初始值
  • 6 [jsDoc 关键字:@rule 123] 字段生成规则

注意 jsDoc 关键字的值中【@】符号由于转义问题需要替换成【#】或者【\@】或者【/@】

命令函入参会和 config 合并(命令行优先级更高)

  • --u 上传
  • --d 下载
  • --m xx 指定 moduleId,不传默认提交更改的模块

rapper 配置 config 有三种方案

方案一(推荐)

通过 antm.config.js 配置 config

// <!-- antm.config.js文件 -->
const antmRapper = require('@antmjs/rapper')
// <!--  使用antm 提供 defineConfig 会有类型提示 -->
export.default = antmRapper.defineConfig({
  upload: { xx: xx }, // 本地上传 配置
})
方案二

通过 命令行参数执行 config 路径

  $ npx rapper --config  ./config/index.js
// <!-- ./config/index.js文件 -->
const antmRapper = require('@antmjs/rapper')
// <!--  antmRapper 提供 defineConfig 会有类型提示 -->
export.default = antmRapper.defineConfig({
  upload: { xx: xx }, // 本地上传 配置
  download: { xx: xx } // 远程下载 配置
})
方案三

通过 package.json 配置 antm.rapper

<!--package.json  文件  -->
{
  'antm': {
  'rapper': {
    'upload': { xx: xx }, // 本地上传 配置
  }
}
}

本地代码类型同步到远程 raper 文档

  • 解析本地文件
  • fetch 方法追加注释 (接口 id 接口模块 id)
  • 格式化 类型
  • 调用 rapper 接口

增量更新实现

  • 每次更新会给文件头部 加一个 MD5 值
  • 初始化会检查合法的文件(符合 formatFunc 结构的文件) MD5 值 对不住
  • 去解析当前文件以及 依赖当前文件的文件
  • 提交变更的模块接口(文件级检查),做不到方法级检查

config 接口类型

interface IConfig {
  // 下载配置
  download: {
    /**
     *
     * @param params   rap上填入接口的信息
     * @returns
     * reqTypeName: request类型名称;
     * resTypeName: response类型名称;
     * funcMain: 请求函数体;
     */
    requestFunc?: (params: {
      funcDescription: string
      repositoryId: number
      moduleId: number
      interfaceId: number
      requestUrl: string
      requestMethod: string
      rapUrl: string
    }) => {
      reqTypeName: string
      resTypeName: string
      funcMain: string
    }
    /**
     *
     * @param params   rap 上填入的module信息
     * @returns
     * fileName: 模块的文件名称;
     * moduleHeader: 模块头部的banner;
     */
    requestModule?: (params: {
      repositoryId: number
      moduleId: number
      moduleRapUrl: string
      moduleDescription: string
    }) => {
      fileName: string
      moduleHeader: string
    }
    // 自定下载的module
    moduleId?: number
  }
  rapper: {
    // 拉取接口地址
    apiUrl?: string
    /** rap 前端地址,默认是 http://rap2.taobao.org */
    rapUrl?: string
    // 生成的文件目录地址
    rapperPath?: string
    // rap登录cookie
    tokenCookie?: string
    // rap项目id
    repositoryId?: number
  }
  upload: {
    //  模式 type 文件扫描入口是type(需要编译生成fetch)
    //  fetch 文件扫描入口是fetch请求函数(不需要编译)
    mode?: 'type' | 'fetch'
    // 需要解析的文件名称正则
    fileRegex?: string
    /**
     *
     * @param params  函数信息
     * @returns
     *  resTypeName: request 类型名称;
     * reqTypeName: response  类型名称;
     * reqUrl: 请求 url;
     * reqMethod: 请求method;
     * interfaceId: 接口id;
     */
    formatFunc?: (params: {
      funcName: string
      body: string
      comment: string
      // 三种函数 定义 会被选中到导出
      funcType: 'CallExpression' | 'FunctionDeclaration' | 'ArrowFunction'
    }) => {
      resTypeName: string
      reqTypeName: string
      reqUrl: string
      reqMethod: string
      interfaceId: number
    } | null
    // 指定下载的 模块id
    moduleId?: number
    // webpack 别名
    alias?: Record<string, string>
  }
  // 内部标识使用 不用管
  __completion?: boolean
  // 是不是上传
  isUpload: boolean
}

export type IOptions = Partial<IConfig>

defaultConfig 会和传进来的 config 合并补全

   const defaultOptions = {
    download: {
      //请求 function 模板
      requestFunc(params) {
        function getFnName(url: string): null | string {
          const fnName = url.match(/\/([.a-z0-9_-]+)\/([a-z0-9_-]+$)/i);
          if (fnName && fnName.length === 3) {
            if (/^\d+\.\d+$/.test(fnName[1])) {
              return fnName[2];
            }
            return fnName[1] + fnName[2].charAt(0).toUpperCase() + fnName[2].slice(1);
          }
          return null;
        }
        const fnName = getFnName(params.requestUrl);
        if (!fnName) {
          throw new TypeError('接口路径不对,请修改合规');
        }
        const camelCaseName = `${fnName.charAt(0).toUpperCase()}${fnName.slice(1)}`;
        const reqTypeName = `IReq${camelCaseName}`;
        const resTypeName = `IRes${camelCaseName}`;
        return {
          reqTypeName,
          resTypeName,
          funcMain: `
              /**
               * 接口名:${params.funcDescription}
               * Rap 地址: ${params.rapUrl}?id=${params.repositoryId}&mod=${params.moduleId}&itf=${params.interfaceId}
               */
              export const ${fnName} = createFetch<${reqTypeName}, ${resTypeName}>('${params.requestUrl}', '${params.requestMethod}')
              `,
        };
      },
      //请求 函数共工头(用于引入函数
      requestModule(params) {
        return {
          fileName: params.moduleDescription,
          moduleHeader: `
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck

import instance from '@/utils/request'

function createFetch<REQ extends Record<string, unknown>, RES extends {data: any}> (url: string, method: string) {
  return  <T extends boolean = false>(
    data: REQ,
    options?: {
      proxy?: T
      pageError?: boolean
    }
  ): Promise<T extends true ? RES['data'] : RES> => {
    return instance(
      {
        url,
        method,
        data,
      },
      options
    )
  }
}
`,
        };
      },
    },
    rapper: {
      // 拉取接口地址
      apiUrl:
        'http://rap2api.taobao.org/repository/get?id=284428&token=TTDNJ7gvXgy9R-9axC-7_mbi4ZxEPlp6',
      /** rap 前端地址,默认是 http://rap2.taobao.org */
      rapUrl: 'http://rap2.taobao.org',

      rapperPath: './src/actions',
      tokenCookie:
        'aliyungf_tc=f3a5915db08fc3b6de3ec5df0d0b3a5dc07c0b701e44cf4bf98a855799570bfe; koa.sid=2I353u8TTwtrHSdPXdJ9t8Mx5lTOeQFV; koa.sid.sig=D4vYLNcryQ8vcU4GkJJknTi_Fm8',
      repositoryId: 284428,
    },
    upload: {
      mode: 'type' as const,
      // fileRegex 将尝试使用绝对文件路径检测测试文件
      // (/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$
      fileRegex: './src/actions/types/.*(js|jsx|ts|tsx)',

      formatFunc(params) {
        // createFetch<IReqGoodsQbf, IResGoodsQbf>('/c/api/1.0/approve/goods/qbf', 'GET')
        // export const goodsQbf = createFetch<IGoodsQbf['request'], IGoodsQbf['response']>("/c/api/1.0/approve/goods/qbf", "GET");
        const [_, reqTypeName, resTypeName, reqUrl, reqMethod] =
          params.body.match(
            /createFetch<([\w\[\]'"]+),\s+([\w\[\]'"]+)>\(['"]([\s\S]+)['"], ['"]([a-zA-Z]+)['"]\)/,
          ) || [];
        if (!reqTypeName || !resTypeName) {
          return null;
        }
        const matchInterfaceId = params.comment.match(/http:\/\/rap2\.tao[\s\S]+&itf=(\d+)/);
        return {
          resTypeName,
          reqTypeName,
          // 如果返回 null '' undefined 0 等 就会被认为是新的接口,会触发上rap操作
          interfaceId: matchInterfaceId ? Number(matchInterfaceId[1]) : null,
          reqUrl: reqUrl,
          reqMethod: reqMethod,
        };
      },
      // webpack 别名 alias 绝对路径
      alias: {
        '@': './src',
      },
    },
    isUpload: true,
  };
    ```