@hadss/hmrouter-plugin
Version:
HMRouter Compiler Plugin
345 lines (344 loc) • 15.9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskManager = void 0;
const micromatch_1 = __importDefault(require("micromatch"));
const constants_1 = require("../../hmrouter_extension/constants");
const PluginError_1 = require("../../hmrouter_extension/error/PluginError");
const extension_1 = require("../extension");
const utils_1 = require("../utils");
const TaskStage_1 = require("./TaskStage");
const annotationConfig = {
[constants_1.AnnotationConstants.ANIMATOR_ANNOTATION]: {
prefix: constants_1.PrefixConstants.ANIMATOR_PREFIX,
propertyName: 'animatorName',
resultType: 'HMAnimatorResult',
},
[constants_1.AnnotationConstants.INTERCEPTOR_ANNOTATION]: {
prefix: constants_1.PrefixConstants.INTERCEPTOR_PREFIX,
propertyName: 'interceptorName',
resultType: 'HMInterceptorResult',
},
[constants_1.AnnotationConstants.LIFECYCLE_ANNOTATION]: {
prefix: constants_1.PrefixConstants.LIFECYCLE_PREFIX,
propertyName: 'lifecycleName',
resultType: 'HMLifecycleResult',
},
[constants_1.AnnotationConstants.SERVICE_ANNOTATION]: {
prefix: constants_1.PrefixConstants.SERVICE_PREFIX,
propertyName: 'serviceName',
resultType: 'HMServiceResult',
},
[constants_1.AnnotationConstants.SERVICE_PROVIDE_ANNOTATION]: {
prefix: constants_1.PrefixConstants.SERVICE_PROVIDE_PREFIX,
propertyName: 'serviceName',
resultType: 'HMServiceResult',
},
};
class TaskManager {
constructor(context, moduleExtensions) {
this.extensionManager = extension_1.ExtensionManager.getInstance();
this.moduleExtensions = moduleExtensions || [];
this.context = context;
}
executeStage(stage) {
switch (stage) {
case TaskStage_1.TaskStage.AFTER_ANNOTATION_ANALYSIS:
this.executeAnnotationAnalysis();
break;
default:
this.getExtensions().forEach((extension) => {
const methodName = stage.toString();
const extensionMethods = {
'afterInitialize': extension.afterInitialize?.bind(extension),
'afterCodeGeneration': extension.afterCodeGeneration?.bind(extension),
'afterRouterMapBuilding': extension.afterRouterMapBuilding?.bind(extension),
'afterConfigUpdate': extension.afterConfigUpdate?.bind(extension),
'afterObfuscationProcess': extension.afterObfuscationProcess?.bind(extension),
'afterResourceProcess': extension.afterResourceProcess?.bind(extension),
'afterCompletion': extension.afterCompletion?.bind(extension)
};
const method = extensionMethods[methodName];
if (method) {
method(this.context);
}
});
break;
}
}
executeAnnotationAnalysis() {
this.routerContext = this.context;
const extensions = this.getExtensions();
this.routerContext.scanFiles.forEach((file) => {
this.routerContext.setCurrentFilePath(file);
const sourceFile = utils_1.TsAstUtil.getSourceFile(file);
extensions.forEach((extension) => {
if (extension.afterAnnotationAnalysis) {
extension.afterAnnotationAnalysis(sourceFile, file, this.routerContext);
}
});
});
this.routerContext.setCurrentFilePath('');
this.generateRouterFilePath();
this.buildRouterMap();
this.validateTemplateData();
}
getExtensions() {
const globalExtensions = this.extensionManager.getExtensions();
const allExtensions = globalExtensions.concat(this.moduleExtensions);
return allExtensions.sort((a, b) => {
const aIsHMRouter = a.name?.includes('HMRouter') || false;
const bIsHMRouter = b.name?.includes('HMRouter') || false;
if (aIsHMRouter && !bIsHMRouter) {
return -1;
}
if (!aIsHMRouter && bIsHMRouter) {
return 1;
}
return 0;
});
}
validateTemplateData() {
const componentNames = new Set();
this.routerContext.getAnalyzeResults().forEach((result) => {
if (result.name) {
componentNames.add(result.name);
}
});
const templateComponentNames = new Set(this.routerContext.getTemplateDataMap().keys());
templateComponentNames.forEach((name) => {
if (!componentNames.has(name)) {
utils_1.Logger.warn(this.routerContext.node.getNodeName(), `Component ${name} has template data but was not found in analyze results.`);
}
});
}
generateRouterFilePath() {
this.routerContext.generatedPaths = new Map();
this.routerContext.getAnalyzeResults().forEach((analyzeResult) => {
if (analyzeResult.annotation === constants_1.AnnotationConstants.ROUTER_ANNOTATION) {
const relativeShortPath = this.routerContext.config.getRelativeShortSourcePath(analyzeResult);
const generatedFilePath = this.routerContext.config
.getBuilderFilePath(relativeShortPath)
.replaceAll(constants_1.FilePathConstants.FILE_SEPARATOR, constants_1.FilePathConstants.DELIMITER);
this.buildRouterTemplatePath(analyzeResult);
const uniqueKey = `${analyzeResult.name}#${analyzeResult.pageUrl}`;
this.routerContext.generatedPaths.set(uniqueKey, generatedFilePath);
}
});
}
buildRouterMap() {
this.routerContext.getAnalyzeResults().forEach((result) => {
if (!result.sourceFilePath) {
throw PluginError_1.PluginError.create(PluginError_1.ErrorCode.ERROR_PAGE_SOURCE_FILE, this.routerContext.config.moduleName, result.sourceFilePath);
}
let pageSourceFile = this.routerContext.config
.getRelativeSourcePath(result.sourceFilePath)
.replaceAll(constants_1.FilePathConstants.FILE_SEPARATOR, constants_1.FilePathConstants.DELIMITER);
if (result.annotation === constants_1.AnnotationConstants.ROUTER_ANNOTATION) {
let pageUrl = result.pageUrl;
let uniqueKey = `${result.name}#${pageUrl}`;
let builderPath = this.routerContext.generatedPaths.get(uniqueKey) || '';
this.routerContext.routerMap.push({
name: pageUrl,
pageSourceFile: builderPath,
buildFunction: result.name + 'Builder',
customData: result,
});
return;
}
this.useAnnotationConfig(result, pageSourceFile, this.routerContext);
});
this.readExistingRouterMap(this.routerContext);
}
useAnnotationConfig(result, pageSourceFile, context) {
const config = annotationConfig[result.annotation];
let nameValue;
switch (result.annotation) {
case constants_1.AnnotationConstants.ANIMATOR_ANNOTATION:
nameValue = result.animatorName;
break;
case constants_1.AnnotationConstants.INTERCEPTOR_ANNOTATION:
nameValue = result.interceptorName;
break;
case constants_1.AnnotationConstants.LIFECYCLE_ANNOTATION:
nameValue = result.lifecycleName;
break;
case constants_1.AnnotationConstants.SERVICE_ANNOTATION:
case constants_1.AnnotationConstants.SERVICE_PROVIDE_ANNOTATION:
nameValue = result.serviceName;
break;
default:
nameValue = result.name;
break;
}
const fullName = config.prefix + nameValue;
context.routerMap.push({
name: fullName,
pageSourceFile: pageSourceFile,
buildFunction: '',
customData: result,
});
}
buildRouterTemplatePath(result) {
let pageSourceFile = this.routerContext.config
.getRelativeSourcePath(result.sourceFilePath)
.replaceAll(constants_1.FilePathConstants.FILE_SEPARATOR, constants_1.FilePathConstants.DELIMITER);
let tempFilePath = this.matchedPath(pageSourceFile, this.routerContext.config.customPageTemplate, this.routerContext.config.getDefaultTplFilePath());
tempFilePath = this.determineTemplatePath(result, tempFilePath);
result.templatePath = tempFilePath;
}
matchedPath(filePath, customPageTemplate, defaultTplFilePath) {
for (const template of customPageTemplate) {
if (micromatch_1.default.isMatch(filePath, template.srcPath)) {
utils_1.Logger.debug(this.routerContext.node.getNodeName(), `${filePath} detected matching template: ${template.templatePath}`);
return utils_1.PluginFileUtil.pathResolve(this.routerContext.config.configDir, template.templatePath);
}
}
return defaultTplFilePath;
}
readExistingRouterMap(context) {
const moduleJsonOpt = context.moduleContext.getModuleJsonOpt();
if (!moduleJsonOpt?.module.routerMap) {
utils_1.Logger.info(context.node.getNodeName(), `No routerMap found in the module configuration`);
return;
}
const routerMapFileName = moduleJsonOpt.module.routerMap.split(':')[1];
if (routerMapFileName !== 'hm_router_map') {
const routerMapFilePath = context.config.getModuleRouterMapFilePath(routerMapFileName);
const routerMapObj = utils_1.PluginFileUtil.readJson5(routerMapFilePath);
const systemRouterMap = routerMapObj[constants_1.RouterMapConstants.ROUTER_MAP_KEY];
if (systemRouterMap && systemRouterMap.length > 0) {
context.routerMap.unshift(...systemRouterMap);
utils_1.Logger.info(context.node.getNodeName(), `System routerMap added to the current routerMap`);
}
}
}
determineTemplatePath(analyzeResult, defaultTemplatePath) {
const libraryVersion = this.detectLibraryVersion();
utils_1.Logger.debug(this.routerContext.node.getNodeName(), `Library version: ${libraryVersion}`);
if (analyzeResult.useNavDst === true) {
return utils_1.PluginFileUtil.pathResolve(__dirname, constants_1.FilePathConstants.PARENT_DELIMITER.repeat(3), constants_1.TemplateConstants.CUSTOM_BUILDER_TEMPLATE);
}
const templateType = this.determineTemplateType(libraryVersion);
if (templateType !== 'latest' && libraryVersion) {
utils_1.Logger.warn(this.routerContext.node.getNodeName(), `[HMRouter Version Compatibility] Detected library version ${libraryVersion}, ` +
`which is below the minimum recommended version ${constants_1.VersionConstants.HMROUTER_MIN_VERSION_FOR_SDK_API}. ` +
`Automatically switched to compatible template (${templateType}). ` +
`Please upgrade /hmrouter to ${constants_1.VersionConstants.HMROUTER_MIN_VERSION_FOR_SDK_API} or higher for full feature support.`);
}
switch (templateType) {
case 'v1':
return utils_1.PluginFileUtil.pathResolve(__dirname, constants_1.FilePathConstants.PARENT_DELIMITER.repeat(3) + constants_1.TemplateConstants.V1_TEMPLATE);
case 'v1_1':
return utils_1.PluginFileUtil.pathResolve(__dirname, constants_1.FilePathConstants.PARENT_DELIMITER.repeat(3) + constants_1.TemplateConstants.V1_1_TEMPLATE);
case 'latest':
default:
return defaultTemplatePath;
}
}
determineTemplateType(libraryVersion) {
if (!libraryVersion) {
return 'v1';
}
const currentVersion = this.parseVersion(libraryVersion);
const v1_1Version = this.parseVersion(constants_1.VersionConstants.HMROUTER_MIN_VERSION_FOR_NAV_HELPER);
const v1_2Version = this.parseVersion(constants_1.VersionConstants.HMROUTER_MIN_VERSION_FOR_SDK_API);
if (!currentVersion || !v1_1Version || !v1_2Version) {
return 'v1';
}
if (this.compareVersions(currentVersion, v1_2Version) >= 0) {
return 'latest';
}
if (this.compareVersions(currentVersion, v1_1Version) >= 0) {
return 'v1_1';
}
return 'v1';
}
detectLibraryVersion() {
let libraryVersion = '';
const possiblePaths = this.getLibraryPossiblePaths();
for (const packagePath of possiblePaths) {
libraryVersion = this.detectLibraryVersion2(packagePath);
if (libraryVersion) {
break;
}
}
return libraryVersion;
}
detectLibraryVersion2(lockFilePath) {
try {
if (!utils_1.PluginFileUtil.exist(lockFilePath)) {
return '';
}
const lockFileData = utils_1.PluginFileUtil.readJson5(lockFilePath);
return this.extractHMRouterVersion(lockFileData.packages);
}
catch (error) {
return '';
}
}
extractHMRouterVersion(packages) {
if (!packages) {
return '';
}
const hmrouterPrefix = `${constants_1.VersionConstants.HMROUTER_ORGANIZATION}/${constants_1.VersionConstants.HMROUTER_LIB_NAME}@`;
for (const key of Object.keys(packages)) {
if (!key.startsWith(hmrouterPrefix)) {
continue;
}
const packageInfo = packages[key];
if (packageInfo && packageInfo[constants_1.VersionConstants.HMROUTER_VERSION_KEY]) {
return packageInfo[constants_1.VersionConstants.HMROUTER_VERSION_KEY] ?? '';
}
}
return '';
}
getLibraryPossiblePaths() {
const paths = [];
paths.push(utils_1.PluginFileUtil.pathResolve(this.routerContext.config.modulePath, constants_1.FilePathConstants.OH_PACKAGE_LOCK_FILE_NAME));
const projectRoot = utils_1.PluginStore.getInstance().get('projectFilePath');
if (projectRoot && projectRoot !== this.routerContext.config.modulePath) {
paths.push(utils_1.PluginFileUtil.pathResolve(projectRoot, constants_1.FilePathConstants.OH_PACKAGE_LOCK_FILE_NAME));
}
return paths;
}
parseVersion(version) {
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(-.*)?/);
if (!match) {
return null;
}
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
preRelease: match[4] || '',
};
}
compareVersions(v1, v2) {
if (v1.major !== v2.major) {
return v1.major < v2.major ? -1 : 1;
}
if (v1.minor !== v2.minor) {
return v1.minor < v2.minor ? -1 : 1;
}
if (v1.patch !== v2.patch) {
return v1.patch < v2.patch ? -1 : 1;
}
if (v1.preRelease && !v2.preRelease) {
return -1;
}
if (!v1.preRelease && v2.preRelease) {
return 1;
}
if (v1.preRelease < v2.preRelease) {
return -1;
}
if (v1.preRelease > v2.preRelease) {
return 1;
}
return 0;
}
}
exports.TaskManager = TaskManager;