Skip to content

元编程与跨端 API 插件体系

1. 元编程的概念

元编程是一种编程技术,允许程序将其他程序(或自身)作为数据来处理。具体来说,它是编写能够生成、操作或转换其他代码的程序。元编程的主要特点包括:

  • 代码自动生成:根据某些规则或元数据自动生成代码
  • 代码分析与转换:分析现有代码结构并进行转换
  • 运行时自省:程序在运行时检查自身结构(如类型信息、方法签名等)

这个项目是一个跨端 API 插件体系,核心原理是利用 TypeScript 的类型系统和元编程能力,通过解析 TS 格式定义的 API 插件,自动生成多端(小程序、Android、iOS)可用的代码。

2. 元编程实现方式

  • TypeScript 类型解析:使用 TypeScript 的编译器 API 解析插件文件的类型、注释和结构
  • 代码生成:根据解析后的元信息,基于模板生成不同平台的代码
  • 统一插件定义规范:通过装饰器和特定结构定义统一的插件接口

3. 核心实现

3.1 元编程解析 - TS 插件定义分析

3.1.1 插件解析入口 - Parser 类

typescript
// src/parse/parser.ts
public parse(pluginFile: string): { plugin?: PluginEntity; caniuse?: string; task?: tt.TaskMeta; error?: string } {
  const program = ts.createProgram([pluginFile], {});
  const source = program.getSourceFile(pluginFile);
  if (source) {
    const TPlugin =
      require(pluginFile).TPlugin;
    var entity = this.parsePlugin(source, program.getTypeChecker(), TPlugin);

    //若为单一平台,设置所有方法、事件以及属性为单一平台
    if (entity.meta.platform != tt.Platforms.All) {
      entity.methods.forEach((method) => {
        method.meta.platform = entity.meta.platform;
      });
      // ...同样处理events和registerEvents
    }
    var caniusej = JSON.stringify(this.caniuserJson);
    // ...
    return { plugin: entity, caniuse: caniusej, ... };
  }
}

这是元编程解析的核心入口,通过 TypeScript Compiler API 创建程序对象,获取源文件,并进行解析。

3.1.2 插件元数据解析 - parsePlugin 方法

typescript
// src/parse/parser.ts
private parsePlugin(source: ts.SourceFile, typeChecker: ts.TypeChecker, plugin): PluginEntity {
  // 初始化结构
  const entity = { ... } as PluginEntity;
  const methods: Array<MethodEntity> = [];
  const events: Array<EventEntity> = [];
  const registerEvents: Array<RegisterEventEntity> = [];
  const customObjects = {};
  let constants;
  let errorCodes = {};

  // 遍历源代码 AST
  ts.forEachChild(source, (node) => {
    if (ts.isClassDeclaration(node)) {
      // 处理类声明...

      // 遍历类成员
      node.members.forEach((subNode) => {
        if (ts.isMethodDeclaration(subNode)) {
          // 获取方法名
          const nameString = subNode.name.getText();

          // 使用装饰器元数据
          const metaKey = this.getPlugin();
          const meta = Reflect.getMetadata(metaKey, new plugin());

          // 处理方法
          let metaMethod = Reflect.getMetadata(td.metaKeys.method, new plugin(), nameString);
          if (metaMethod) {
            let entity = this.parseMethod(source, typeChecker, subNode, metaMethod, nameString, new plugin());
            methods.push(entity);
            // ...生成caniuse配置
          }

          // 处理事件
          let metaEvent = Reflect.getMetadata(td.metaKeys.event, new plugin(), nameString);
          if (metaEvent) {
            // ...类似处理事件
          }

          // 处理注册事件
          let registerEvent = Reflect.getMetadata(td.metaKeys.registerEvent, new plugin(), nameString);
          if (registerEvent) {
            // ...类似处理注册事件
          }
        }
      });
    }
  });

  return { methods, events, registerEvents, customObjects, ... };
}

这是核心的元编程解析逻辑,通过 TypeScript AST 遍历源代码结构,结合 Reflect API 获取装饰器元数据,解析 API 插件定义。

3.2 代码生成 - 多端代码转换

3.2.1 代码生成入口 - generate 函数

typescript
// src/bin/uni/plugin-cli-generate.ts
function generate() {
  // 初始化解析器
  parser = new Parser({ isRN: false, checkErrorCode: checkErrorCode });

  // 处理 TypeScript 文件格式化
  const pluginTsContent = prettier.format(
    fs.readFileSync(path.join(process.cwd(), 'plugin.ts')).toString(),
    { singleQuote: false, parser: 'typescript' }
  );
  fs.writeFileSync(path.join(process.cwd(), 'plugin.ts'), pluginTsContent);

  // 创建模板
  createOrUpdateTemplate(false);

  // 解析插件定义
  let result = parser.parse(path.join(process.cwd(), 'plugin.ts'));

  // 生成各端代码
  let templatePath = getTemplateDirPath(false);
  templatePath += '/anonymization';

  **generateAndroid({ result, templatePath, options: { platform, enabled } });
  **generateIOS({ result, templatePath, options: { platform } });

  if (!isApp) {
    __generateJS({
      result,
      templatePath,
      options: { isDev, diff, enabled, operator, parser, ai, pluginVersion, validate },
    });
  }
}

这是代码生成流程的总入口,负责解析插件定义并调用各个平台的代码生成函数。

3.2.2 JavaScript 代码生成

typescript
// src/generate/genJs.ts
export function generateJS({
  result,
  templatePath,
  options: {
    parser,
    isDev,
    diff,
    enabled,
    operator,
    ai,
    pluginVersion,
    validate,
  },
}) {
  // 处理普通插件包
  console.log("处理普通插件包...");
  const jsTemplate = path.join(templatePath, "js", "temp");

  // 生成 JS 代码
  genOrUpdateJS(
    jsTemplate,
    process.cwd(),
    genJSWrapData(result.plugin, result.caniuse),
    isDev
  );

  // 生成 RN 代码
  const rnTemplate = path.join(templatePath, "rn", "temp");
  genOrUpdateRN(
    rnTemplate,
    process.cwd(),
    genJSWrapData(result.plugin, result.caniuse),
    isDev
  );

  // 生成文档
  generateDoc(result);

  // 生成差异文件
  if (diff) {
    generateDiffWithBackup(result, parser, {
      pluginVersion: pluginVersion,
      operator,
      ai,
    });
  }

  // 生成 native enabled 文件
  generateNativeEnabledFile(
    { name: result.plugin.name, version: result.plugin.version },
    enabled
  );
}

负责生成 JavaScript 和 React Native 代码,以及相关文档和配置文件。

3.3 类型系统与数据结构

3.3.1 插件实体结构定义

typescript
// src/parse/parser.ts
export interface PluginEntity {
  meta: tt.PluginMeta;
  events: Array<EventEntity>;
  methods: Array<MethodEntity>;
  registerEvents: Array<RegisterEventEntity>;
  customObjects?: {};
  constants?: any;
  iOSTime?: IOSCreatTime;
  task?: Task;
}

export interface MethodEntity {
  meta: tt.MethodMeta;
  parameters: Array<ParameterEntity>;
}

export interface EventEntity {
  meta: tt.EventMeta;
  parameter: ParameterEntity;
}

export interface RegisterEventEntity {
  meta: tt.RegisterEventMeta;
  parameters: Array<ParameterEntity>;
}

export interface ParameterEntity {
  name: string;
  type: string;
  jsType?: string;
  desc: string;
}

这些接口定义了插件的结构模型,包括方法、事件、注册事件和参数等。

3.4 元编程核心技术

3.4.1 TypeScript 编译器 API 使用

typescript
// TypeScript 编译器 API 的使用
const program = ts.createProgram([pluginFile], {});
const source = program.getSourceFile(pluginFile);
const typeChecker = program.getTypeChecker();

// 遍历 AST

// TypeScript 编译器 API 的使用
const program = ts.createProgram([pluginFile], {});
const source = program.getSourceFile(pluginFile);
const typeChecker = program.getTypeChecker();

// 遍历 AST
ts.forEachChild(source, (node) => {
  if (ts.isClassDeclaration(node)) {
    // 处理类声明...
  }
});

// 类型检查与信息获取
function serializeSymbol(symbol: ts.Symbol): DocEntry {
  return {
    name: symbol.getName(),
    documentation: ts.displayPartsToString(
      symbol.getDocumentationComment(typeChecker)
    ),
    type: getStandardType(symbol.valueDeclaration, typeChecker),
  };
}

3.4.2 装饰器反射机制

typescript
// 使用 Reflect 元数据 API 获取装饰器信息
const metaKey = this.getPlugin();
const meta = Reflect.getMetadata(metaKey, new plugin());

// 获取方法元数据
let metaMethod = Reflect.getMetadata(
  td.metaKeys.method,
  new plugin(),
  nameString
);
if (metaMethod) {
  // 处理方法...
}

// 获取事件元数据
let metaEvent = Reflect.getMetadata(
  td.metaKeys.event,
  new plugin(),
  nameString
);
if (metaEvent) {
  // 处理事件...
}

3.5 代码生成与转换流程

3.5.1 代码生成流程

  1. 解析 plugin.ts 文件,提取 API 元数据
  2. 构建中间表示 (PluginEntity),包含所有方法、事件和类型信息
  3. 基于模板生成各平台代码:
    • 生成 JavaScript/React Native 代码
    • 生成 Android 代码
    • 生成 iOS 代码
  4. 生成 API 文档、测试用例和错误码定义
  5. 根据差异生成版本迁移文件

核心架构

从代码结构看,项目主要分为以下几个核心模块:

  1. 解析模块 (parse)
    • parser.ts:核心解析器,负责将 TypeScript 插件定义解析为中间表示形式
    • 使用 TypeScript 编译器 API (ts 模块) 解析源代码
    • 提取方法、事件、注册事件等元数据信息
    • 解析 JSDoc 注释作为 API 文档
  2. 转换模块 (transform)
    • 将解析后的插件元数据转换为各平台可用的数据结构
    • 处理类型映射关系(TypeScript 类型 → JavaScript/Android/iOS 类型)
  3. 生成模块 (generate)
    • genJs.ts:生成 JavaScript 代码
    • genAndroid.ts:生成 Android 代码
    • genOrUpdateRN.ts:生成 React Native 代码
    • genOrUpdateJS.ts:更新 JavaScript 代码
    • generateDoc.ts:生成 API 文档
  4. 模板模块 (template)
    • 存放各平台代码生成模板
    • 提供工具函数处理模板
  5. 包装模块 (wrap)
    • js/js-wrap.ts:JavaScript 代码包装器
    • ios/ios-wrap.ts:iOS 代码包装器
    • js/doc-wrap.ts:文档生成包装器
  6. CLI 工具模块 (bin)
    • 提供命令行工具,便于集成到开发流程和 CI 环境 工作流程
    • 解析阶段:
      • 通过 TypeScript 编译器解析插件定义文件 (plugin.ts)
      • 提取插件元数据、方法、事件、类型定义等
      • 转换阶段:
      • 将解析得到的元数据转换为各平台可理解的格式
      • 处理类型映射和平台特定适配
    • 生成阶段:
      • 根据转换后的数据,基于模板生成各平台代码
      • 生成相应的文档、测试用例和错误码定义
    • 发布流程:
      • 生成的代码自动发布到相应的仓库(npm、maven 等)
      • 与原生应用构建流程集成,实现 API 的版本化依赖

技术亮点

  • 单一来源:通过 TypeScript 定义的统一插件,生成多平台代码,避免手动维护多套实现
  • 类型安全:利用 TypeScript 的类型系统保证 API 的类型安全和一致性
  • 自动化工具链:自动生成代码、文档、测试用例,提高开发效率
  • 版本化管理:结合原生 App 构建流程,实现 API 的版本化依赖
  • 开发体验:提供代码提示等开发时功能,改善开发体验

这个项目的核心价值在于通过元编程技术,实现了"写一次定义,多端自动生成"的目标,大大提高了跨平台 API 开发的效率和一致性。

基于 MIT 许可发布