hvigorfile.ts 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import { appTasks } from '@ohos/hvigor-ohos-plugin';
  2. import { HvigorPlugin, HvigorNode } from '@ohos/hvigor';
  3. import fs from 'fs';
  4. import path from 'path';
  5. function customPlugin(): HvigorPlugin {
  6. return {
  7. pluginId: 'customPlugin',
  8. apply(node: HvigorNode) {
  9. try {
  10. // console.log('node', JSON.stringify(node))
  11. // 根据node对象结构获取项目路径
  12. const projectPath = node.nodeDir.filePath;
  13. console.log('目标路径:', projectPath);
  14. // 获取所有子模块
  15. const modules = node.allSubNodes.filter(subNode => subNode.node.classKind === 'module');
  16. console.log('模块数量:', modules.length);
  17. // 打印每个模块的路径
  18. const modulePaths = [];
  19. modules.forEach((module, index) => {
  20. try {
  21. // 从module.node._nodePath获取模块路径
  22. const modulePath = module.node._nodePath;
  23. if (modulePath) {
  24. console.log(`模块 ${index + 1} 路径:`, modulePath);
  25. modulePaths.push(modulePath);
  26. } else {
  27. console.log(`模块 ${index + 1} 路径: 无法获取路径`);
  28. }
  29. } catch (error) {
  30. console.error(`获取模块 ${index + 1} 路径时出错:`, error);
  31. }
  32. });
  33. // 遍历并处理所有模块
  34. modulePaths.forEach((modulePath, index) => {
  35. console.log(`处理模块 ${index + 1}:`, modulePath);
  36. processModule(modulePath, projectPath);
  37. });
  38. } catch (error) {
  39. console.error('Error in custom plugin:', error);
  40. }
  41. }
  42. }
  43. function processModule(modulePath: string, projectPath: string) {
  44. try {
  45. // 构建pages目录路径 (基于模块路径)
  46. const pagesPath = path.join(modulePath, 'src', 'main', 'ets', 'pages');
  47. console.log('pages路径:', pagesPath);
  48. // 构建profile目录路径 (基于模块路径)
  49. const rawfilePath = path.join(modulePath, 'src', 'main', 'resources', 'base', 'profile');
  50. console.log('profile路径:', rawfilePath);
  51. // 确保profile目录存在
  52. if (!fs.existsSync(rawfilePath)) {
  53. fs.mkdirSync(rawfilePath, { recursive: true });
  54. }
  55. // router_map.json文件路径
  56. const routerMapPath = path.join(rawfilePath, 'router_map.json');
  57. // module.json5文件路径
  58. const moduleJsonPath = path.join(modulePath, 'src', 'main', 'module.json5');
  59. // 初始化routerMap
  60. let routerMap = {
  61. routerMap: []
  62. };
  63. // 如果router_map.json文件已存在,则读取现有内容
  64. if (fs.existsSync(routerMapPath)) {
  65. try {
  66. const existingContent = fs.readFileSync(routerMapPath, 'utf8');
  67. routerMap = JSON.parse(existingContent);
  68. console.log('读取现有的router_map.json文件');
  69. } catch (error) {
  70. console.error('读取router_map.json文件出错:', error);
  71. }
  72. }
  73. // 检查pages目录是否存在
  74. if (fs.existsSync(pagesPath)) {
  75. // 读取目录内容
  76. const files = fs.readdirSync(pagesPath);
  77. console.log(`Found ${files.length} files/directories in pages folder:`);
  78. // 遍历并处理每个文件
  79. files.forEach((file, index) => {
  80. const fullPath = path.join(pagesPath, file);
  81. const stat = fs.statSync(fullPath);
  82. const type = stat.isDirectory() ? '[DIR]' : '[FILE]';
  83. console.log(`${index + 1}. ${type} ${file}`);
  84. // 如果是文件,读取文件内容
  85. if (!stat.isDirectory() && file.endsWith('.ets')) {
  86. try {
  87. const content = fs.readFileSync(fullPath, 'utf8');
  88. // console.log(`Content of ${file}:`);
  89. // console.log(content);
  90. // 检查是否有@RouterPage修饰器
  91. if (content.includes('@RouterPage')) {
  92. // 生成Builder函数名(去掉Page后缀,加上Builder后缀)
  93. const fileNameWithoutExtension = file.replace('.ets', '');
  94. const builderName = fileNameWithoutExtension.replace(/Page$/, '') + 'Builder';
  95. // 检查是否已经存在Builder函数
  96. const builderRegex = new RegExp(`@Builder\\s+function\\s+${builderName}\\s*\\(\\)`, 'g');
  97. if (!builderRegex.test(content)) {
  98. // 构造新的内容,在文件末尾添加Builder函数
  99. const structName = fileNameWithoutExtension; // 假设struct名称与文件名相同
  100. const builderFunction = `\n@Builder\nfunction ${builderName}() {\n ${structName}()\n}\n`;
  101. const newContent = content + builderFunction;
  102. // 写入文件
  103. fs.writeFileSync(fullPath, newContent, 'utf8');
  104. console.log(`Added ${builderName} function to ${file}`);
  105. } else {
  106. console.log(`Builder function ${builderName} already exists in ${file}`);
  107. }
  108. // 添加到routerMap中
  109. const pageEntry = {
  110. name: fileNameWithoutExtension,
  111. pageSourceFile: `src/main/ets/pages/${file}`,
  112. buildFunction: builderName
  113. };
  114. // 检查是否已存在该条目
  115. const existingIndex = routerMap.routerMap.findIndex(item => item.name === fileNameWithoutExtension);
  116. if (existingIndex >= 0) {
  117. // 更新现有条目
  118. routerMap.routerMap[existingIndex] = pageEntry;
  119. } else {
  120. // 添加新条目
  121. routerMap.routerMap.push(pageEntry);
  122. }
  123. }
  124. console.log('----------------------------------------');
  125. } catch (readError) {
  126. console.error(`Error reading file ${file}:`, readError);
  127. }
  128. }
  129. });
  130. // 将routerMap写入router_map.json文件
  131. try {
  132. fs.writeFileSync(routerMapPath, JSON.stringify(routerMap, null, 2), 'utf8');
  133. console.log('router_map.json文件已更新');
  134. } catch (writeError) {
  135. console.error('写入router_map.json文件出错:', writeError);
  136. }
  137. // 更新module.json5文件,添加routerMap字段
  138. try {
  139. if (fs.existsSync(moduleJsonPath)) {
  140. const moduleJsonContent = fs.readFileSync(moduleJsonPath, 'utf8');
  141. // 使用正则表达式在"module": {后添加routerMap字段
  142. if (moduleJsonContent.includes('"module"')) {
  143. // 检查是否已经存在routerMap字段
  144. if (!moduleJsonContent.includes('"routerMap"')) {
  145. // 在"module": {后添加routerMap字段
  146. const updatedContent = moduleJsonContent.replace(
  147. /("module"\s*:\s*{)/,
  148. '$1\n "routerMap": "$profile:router_map",'
  149. );
  150. fs.writeFileSync(moduleJsonPath, updatedContent, 'utf8');
  151. console.log('module.json5文件已更新,添加了routerMap字段');
  152. } else {
  153. console.log('module.json5中已存在routerMap字段');
  154. }
  155. } else {
  156. console.log('module.json5中未找到"module"字段');
  157. }
  158. } else {
  159. console.log('module.json5文件不存在');
  160. }
  161. } catch (error) {
  162. console.error('更新module.json5文件出错:', error);
  163. }
  164. } else {
  165. console.log('Pages directory not found');
  166. }
  167. } catch (error) {
  168. console.error(`处理模块 ${modulePath} 时出错:`, error);
  169. }
  170. }
  171. }
  172. function rDBPlugin(): HvigorPlugin {
  173. return {
  174. pluginId: 'customPlugin',
  175. apply(node: HvigorNode) {
  176. try {
  177. // 根据node对象结构获取项目路径
  178. const projectPath = node.nodeDir.filePath;
  179. console.log('目标路径:', projectPath);
  180. // 获取所有子模块
  181. const modules = node.allSubNodes.filter(subNode => subNode.node.classKind === 'module');
  182. console.log('模块数量:', modules.length);
  183. // 打印每个模块的路径
  184. const modulePaths = [];
  185. modules.forEach((module, index) => {
  186. try {
  187. // 从module.node._nodePath获取模块路径
  188. const modulePath = module.node._nodePath;
  189. if (modulePath) {
  190. console.log(`模块 ${index + 1} 路径:`, modulePath);
  191. modulePaths.push(modulePath);
  192. if (modulePath.includes('basic')) {
  193. processTables(modulePath)
  194. }
  195. } else {
  196. console.log(`模块 ${index + 1} 路径: 无法获取路径`);
  197. }
  198. } catch (error) {
  199. console.error(`获取模块 ${index + 1} 路径时出错:`, error);
  200. }
  201. });
  202. // 遍历并处理所有模块
  203. // modulePaths.forEach((modulePath, index) => {
  204. // console.log(`处理模块 ${index + 1}:`, modulePath);
  205. // processTables(); // 处理tables目录的逻辑
  206. // });
  207. // processTables(modulePaths[])
  208. } catch (error) {
  209. console.error('Error in custom plugin:', error);
  210. }
  211. }
  212. }
  213. function processTables(currentDir:string) {
  214. try {
  215. // 构建tables目录路径
  216. const tablesPath = path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'tables');
  217. console.log('tables路径:', tablesPath);
  218. // 检查tables目录是否存在
  219. if (fs.existsSync(tablesPath)) {
  220. // 读取目录内容
  221. const files = fs.readdirSync(tablesPath);
  222. console.log(`Found ${files.length} files/directories in tables folder:`);
  223. const tableNames: string[] = [];
  224. const sqlStatements: string[] = [];
  225. // 替换原有的字段处理逻辑
  226. files.forEach((file, index) => {
  227. const fullPath = path.join(tablesPath, file);
  228. const stat = fs.statSync(fullPath);
  229. const type = stat.isDirectory() ? '[DIR]' : '[FILE]';
  230. console.log(`${index + 1}. ${type} ${file}`);
  231. // 如果是文件,读取文件内容
  232. if (!stat.isDirectory() && file.endsWith('.ets')) {
  233. try {
  234. const content = fs.readFileSync(fullPath, 'utf8');
  235. // 使用正则表达式匹配interface名称
  236. const interfaceRegex = /export\s+interface\s+(\w+)/g;
  237. let match;
  238. while ((match = interfaceRegex.exec(content)) !== null) {
  239. const interfaceName = match[1];
  240. tableNames.push(interfaceName);
  241. // 解析接口属性并生成字段
  242. const fieldDefinitions = parseInterfaceFields(content, interfaceName);
  243. // 生成CREATE TABLE SQL语句(包含字段)
  244. const sqlStatement =
  245. `static readonly CREATE_${interfaceName.toUpperCase()}_TABLE = 'CREATE TABLE IF NOT EXISTS ${interfaceName} (${fieldDefinitions})';`;
  246. sqlStatements.push(sqlStatement);
  247. console.log(`Found interface: ${interfaceName}`);
  248. }
  249. } catch (readError) {
  250. console.error(`Error reading file ${file}:`, readError);
  251. }
  252. }
  253. });
  254. // 添加解析接口字段的辅助函数
  255. function parseInterfaceFields(content: string, interfaceName: string): string {
  256. // 查找接口定义区域
  257. const interfaceStart = content.indexOf(`export interface ${interfaceName}`);
  258. if (interfaceStart === -1) {
  259. return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
  260. }
  261. const openBrace = content.indexOf('{', interfaceStart);
  262. const closeBrace = content.indexOf('}', openBrace);
  263. if (openBrace === -1 || closeBrace === -1) {
  264. return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
  265. }
  266. const interfaceBody = content.substring(openBrace + 1, closeBrace);
  267. const lines = interfaceBody.split('\n');
  268. const fields: string[] = [];
  269. let hasPrimaryKey = false;
  270. lines.forEach(line => {
  271. const fieldMatch = line.trim().match(/^(\w+)\??\s*:\s*(.+?);?$/);
  272. if (fieldMatch) {
  273. const fieldName = fieldMatch[1];
  274. const fieldType = fieldMatch[2].trim();
  275. // 根据TypeScript类型映射到SQLite类型
  276. let sqliteType = 'TEXT';
  277. if (fieldType.includes('number') || fieldType.includes('Number')) {
  278. sqliteType = 'INTEGER';
  279. } else if (fieldType.includes('boolean') || fieldType.includes('Boolean')) {
  280. sqliteType = 'INTEGER';
  281. } else if (fieldType.includes('Date')) {
  282. sqliteType = 'TEXT';
  283. }
  284. // 检查是否为主键字段(约定以id命名的字段作为主键)
  285. if (fieldName.toLowerCase() === 'id') {
  286. fields.push(`${fieldName} INTEGER PRIMARY KEY AUTOINCREMENT`);
  287. hasPrimaryKey = true;
  288. } else {
  289. // 处理可选字段
  290. const nullable = line.trim().includes('?') ? '' : ' NOT NULL';
  291. fields.push(`${fieldName} ${sqliteType}${nullable}`);
  292. }
  293. }
  294. });
  295. // 如果没有定义主键,则添加默认主键
  296. if (!hasPrimaryKey) {
  297. fields.unshift('ID INTEGER PRIMARY KEY AUTOINCREMENT');
  298. }
  299. return fields.join(', ');
  300. }
  301. // 更新RelationalStoreUtils.ets文件中的tables数组
  302. const relationalStorePath =
  303. path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'utils', 'RelationalStoreUtis.ets');
  304. if (fs.existsSync(relationalStorePath)) {
  305. let content = fs.readFileSync(relationalStorePath, 'utf8');
  306. // 更新tables数组
  307. if (tableNames.length > 0) {
  308. const tablesArray = `private static tables: string[] = [${tableNames.map(name => `'${name}'`).join(', ')}]`;
  309. const updatedContent = content.replace(/private static tables: string\[\] = \[[^\]]*\]/, tablesArray);
  310. fs.writeFileSync(relationalStorePath, updatedContent, 'utf8');
  311. console.log('RelationalStoreUtis.ets文件已更新');
  312. }
  313. }
  314. // 替换原有的更新RDBSql.ets文件的代码块
  315. // 更新RDBSql.ts文件
  316. const rdbSqlPath = path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'sqls', 'RDBSql.ts');
  317. if (fs.existsSync(rdbSqlPath)) {
  318. let content = fs.readFileSync(rdbSqlPath, 'utf8');
  319. // 构建SQL语句对象属性
  320. if (sqlStatements.length > 0) {
  321. // 提取属性名和值
  322. const sqlProperties: string[] = [];
  323. sqlStatements.forEach(statement => {
  324. // 从 static readonly CREATE_USER_TABLE = 'CREATE TABLE...' 提取属性
  325. const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
  326. const propertyValue = statement.split('=')[1].trim().slice(1, -2); // 去掉引号和分号
  327. sqlProperties.push(` ${propertyName}: '${propertyValue}'`);
  328. });
  329. // 构建新的对象内容
  330. const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
  331. fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
  332. console.log('RDBSql.ts文件已更新为对象形式');
  333. }
  334. } else {
  335. // 如果文件不存在,创建新文件
  336. if (sqlStatements.length > 0) {
  337. const sqlProperties: string[] = [];
  338. sqlStatements.forEach(statement => {
  339. const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
  340. const propertyValue = statement.split('=')[1].trim().slice(1, -2);
  341. sqlProperties.push(` ${propertyName}: '${propertyValue}'`);
  342. });
  343. const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
  344. // 确保目录存在
  345. const dirPath = path.dirname(rdbSqlPath);
  346. if (!fs.existsSync(dirPath)) {
  347. fs.mkdirSync(dirPath, { recursive: true });
  348. }
  349. fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
  350. console.log('RDBSql.ts文件已创建并初始化为对象形式');
  351. }
  352. }
  353. console.log('----------------------------------------');
  354. } else {
  355. console.log('Tables directory not found');
  356. }
  357. } catch (error) {
  358. console.error(`处理tables目录时出错:`, error);
  359. }
  360. }
  361. }
  362. export default {
  363. system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  364. plugins: [
  365. customPlugin(), // 应用自定义Plugin
  366. rDBPlugin()
  367. ] /* Custom plugin to extend the functionality of Hvigor. */
  368. }