1 关键字

SDK;API文件名称;@ohos;

2 问题描述

问题现象:SDK中的API文件名称@ohos无法更改自定义名称,只能以@ohos@system为标识,才可正确识别为API文件和调用。

3 问题原因

(1)更改API文件名称为以其他标识开头的文件名称,例如:将@ohos.power.d.ts改为@tvos.power.d.ts.

(2)在页面中引入API模块,并调用接口。

import Power from '@tvos.power'
​
@Entry
@Component
export default struct Test {
  build() {
    Column() {
      Button('重启')
        .onClick(() => {
          Power_tv.rebootDevice('')
        })
    }
    .height('100%')
    .width('100%')
  }
}

(3)编译代码。

3.1 正常机制

编译后API引入代码:

var _power_1  = globalThis.requireNapi('power') || (isSystemplugin('power', 'ohos') ? globalThis.ohosplugin.power : isSystemplugin('power', 'system') ? globalThis.systemplugin.power : undefined);

3.2 异常机制

编译后API引入代码如下,未识别为API进行正确的引入。

const _power_1 = __importDefault(__webpack_require__(/*! @tvos.power */ "../../api/@tvos.power.d.ts"));

4 解决方案

修改OpenHarmony应用编译仓(ace_ets2bundle)的源码,使应用编译工具支持自定义的API文件标识。

(1)修改文件developtools/ace_ets2bundle/compiler/src/pre_define.ts,增加新标识。

export const SYSTEM_PLUGIN: string = 'system';  // 原有标识
export const OHOS_PLUGIN: string = 'ohos';  // 原有标识
export const TVOS_PLUGIN: string = 'tvos';  // 增加新标识

(2)修改文件developtools/ace_ets2bundle/compiler/src/validate_ui_syntax.ts,增加判断和编译逻辑。

···
import {
    ···
    TVOS_PLUGIN, // 引入新增的标识
    ···
} from './pre_define';
···
function replaceSystemApi(item: string, systemValue: string, moduleType: string, systemKey: string): string {
    ···
    } else if (moduleType === TVOS_PLUGIN) {   // 增加新标识的编译逻辑
        item = `var ${systemValue} = globalThis.requireNapi('${systemKey}') || ` +
            `(isSystemplugin('${systemKey}', '${OHOS_PLUGIN}') ? ` +
            `globalThis.ohosplugin.${systemKey} : isSystemplugin('${systemKey}', '${SYSTEM_PLUGIN}') ` +
            `? globalThis.systemplugin.${systemKey} : undefined)`;
    }
    return item;
}
···
export function processSystemApi(content: string, isProcessAllowList: boolean = false,
  sourcePath: string = null, isSystemModule: boolean = false): string {
  if (isProcessAllowList && projectConfig.compileMode === ESMODULE) {
    const REG_UNUSED_SYSTEM_IMPORT: RegExp = /import(?:\s*)['"]@(system|ohos|tvos)\.(\S+)['"]/g;  // 增加移除未使用API时的标识
    content = content.replace(REG_UNUSED_SYSTEM_IMPORT, '');
  }
​
  const REG_IMPORT_DECL: RegExp = isProcessAllowList ? projectConfig.compileMode === ESMODULE ?  // 增加新标识
    /import\s+(.+)\s+from\s+['"]@(system|ohos|tvos)\.(\S+)['"]/g :  
    /(import|const)\s+(.+)\s*=\s*(\_\_importDefault\()?require\(\s*['"]@(system|ohos|tvos)\.(\S+)['"]\s*\)(\))?/g :
    /(import|export)\s+(?:(.+)|\{([\s\S]+)\})\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g;
​
  const systemValueCollection: Set<string> = new Set();
  const processedContent: string = content.replace(REG_IMPORT_DECL, (item, item1, item2, item3, item4, item5, item6) => {
    ···
    if (isOhmUrl(moduleRequest)) {
      return replaceOhmUrl(isSystemModule, item, importValue, moduleRequest, sourcePath);
    } else if (/^@(system|ohos|tvos)\./.test(moduleRequest)) {  // 增加新标识
      if (!isSystemModule) {
        return item;
      }
      const result: RegExpMatchArray = moduleRequest.match(/^@(system|ohos|tvos)\.(\S+)$/); // 增加新标识
      const moduleType: string = result[1];
      const apiName: string = result[2];
      return replaceSystemApi(item, importValue, moduleType, apiName);
    }
    ···
    return item;
  });
  return processInnerModule(processedContent, systemValueCollection);
}
···
function collectSourcemapNames(sourcePath: string, changedName: string, originalName: string): void {
  ···
  for (let entry of originalImportNamesMap.entries()) {
    const key: string = entry[0];
    const value: string = entry[1];
    if (value === '@ohos.' + originalName || value === '@system.' + originalName || value === '@tvos.' + originalName) { // 增加新标识
      map.set(changedName.trim(), key);
      sourcemapNamesCollection.set(cleanSourcePath, map);
      originalImportNamesMap.delete(key);
      break;
    }
  }
}

5 定位过程

(1)在页面中引入@ohos标识的API模块,查看编译后生成的js文件,发现API被requireNapi方法进行加载。

var _power_1  = globalThis.requireNapi('power') || (isSystemplugin('power', 'ohos') ? globalThis.ohosplugin.power : isSystemplugin('power', 'system') ? globalThis.systemplugin.power : undefined);

(2)在应用编译仓(ace_ets2bundle)源码中搜索requireNapi方法,发现在developtools/ace_ets2bundle/compiler/src/validate_ui_syntax.ts文件中对API的加载进行了处理。

(3)逆向查找replaceSystemApi方法调用位置,依次修改判断条件和逻辑,增加新标识。

6 知识分享

修改、编译和替换SDK步骤:

(1)修改源码 ace_ets2bundle 项目代码。项目仓地址:https://gitee.com/openharmony/developtools_ace_ets2bundle。仓库在OpenHarmony源码的位置developtools/ace_ets2bundle 如下图:

(2)执行SDK编译指令:./build.sh --product-name ohos-sdk

(3)编译成功后,在out/sdk/packages/ohos-sdk中生成相应SDK的压缩包。

(4)解压后更改目录名称为SDK版本名称,放入SDK的ets目录中。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐