hvigorfile.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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 typeRegex = /export\s+(interface|class)\s+(\w+)/g;
  237. let match;
  238. while ((match = typeRegex.exec(content)) !== null) {
  239. const typeKind = match[1]; // 'interface' 或 'class'
  240. const typeName = match[2]; // 类型名称
  241. tableNames.push(typeName);
  242. // 解析字段定义
  243. const fieldDefinitions = parseTypeFields(content, typeName);
  244. // 生成CREATE TABLE SQL语句
  245. const sqlStatement =
  246. `static readonly CREATE_${typeName.toUpperCase()}_TABLE = 'CREATE TABLE IF NOT EXISTS ${typeName} (${fieldDefinitions})';`;
  247. sqlStatements.push(sqlStatement);
  248. console.log(`Found ${typeKind}: ${typeName}`);
  249. }
  250. } catch (readError) {
  251. console.error(`Error reading file ${file}:`, readError);
  252. }
  253. }
  254. });
  255. // 添加解析接口字段的辅助函数
  256. function parseTypeFields(content: string, typeName: string): string {
  257. // 使用正则表达式查找 interface 或 class 定义,允许中间有多个空格
  258. const classRegex = new RegExp(`export\\s+class\\s+${typeName}`, 'g');
  259. const interfaceRegex = new RegExp(`export\\s+interface\\s+${typeName}`, 'g');
  260. const classMatch = classRegex.exec(content);
  261. const interfaceMatch = interfaceRegex.exec(content);
  262. let typeStart = -1;
  263. if (classMatch) {
  264. typeStart = classMatch.index;
  265. } else if (interfaceMatch) {
  266. typeStart = interfaceMatch.index;
  267. }
  268. console.log('开始解析类型体内容:');
  269. console.log('typeStart内容:', typeStart);
  270. if (typeStart === -1) {
  271. return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
  272. }
  273. // 后续逻辑保持不变...
  274. const openBrace = content.indexOf('{', typeStart);
  275. const closeBrace = content.indexOf('}', openBrace);
  276. if (openBrace === -1 || closeBrace === -1) {
  277. console.log('openBrace和closeBrace内容:', openBrace,closeBrace);
  278. return 'ID INTEGER PRIMARY KEY AUTOINCREMENT';
  279. }
  280. const typeBody = content.substring(openBrace + 1, closeBrace);
  281. const lines = typeBody.split('\n');
  282. const fields: string[] = [];
  283. let hasPrimaryKey = false;
  284. // 改进字段解析逻辑
  285. lines.forEach(line => {
  286. const trimmedLine = line.trim();
  287. // 使用更宽松的正则表达式,支持多种格式
  288. // 匹配格式: fieldName?: type; 或 fieldName: type; (允许前后空格)
  289. const fieldMatch = trimmedLine.match(/^(\w+)(\??)\s*:\s*([^;]+?)\s*(?:;|$)/);
  290. if (fieldMatch) {
  291. const fieldName = fieldMatch[1];
  292. const isOptional = fieldMatch[2] === '?';
  293. const fieldType = fieldMatch[3].trim();
  294. console.log(`解析字段: ${fieldName}, 可选: ${isOptional}, 类型: ${fieldType}`); // 调试信息
  295. // 根据TypeScript类型映射到SQLite类型
  296. let sqliteType = 'TEXT';
  297. const normalizedFieldType = fieldType.toLowerCase();
  298. if (normalizedFieldType.includes('number') || normalizedFieldType.includes('int') || normalizedFieldType.includes('float')) {
  299. sqliteType = 'INTEGER';
  300. } else if (normalizedFieldType.includes('boolean')) {
  301. sqliteType = 'INTEGER';
  302. } else if (normalizedFieldType.includes('date')) {
  303. sqliteType = 'TEXT';
  304. }
  305. // 检查是否为主键字段
  306. if (fieldName.toLowerCase() === 'id') {
  307. fields.push(`${fieldName} INTEGER PRIMARY KEY AUTOINCREMENT`);
  308. hasPrimaryKey = true;
  309. } else {
  310. // 处理可选字段
  311. const nullable = isOptional ? '' : ' NOT NULL';
  312. fields.push(`${fieldName} ${sqliteType}${nullable}`);
  313. }
  314. }
  315. });
  316. // 如果没有定义主键,则添加默认主键
  317. if (!hasPrimaryKey) {
  318. fields.unshift('ID INTEGER PRIMARY KEY AUTOINCREMENT');
  319. }
  320. return fields.join(', ');
  321. }
  322. // 更新RelationalStoreUtils.ets文件中的tables数组
  323. const relationalStorePath =
  324. path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'utils', 'RelationalStoreUtis.ets');
  325. if (fs.existsSync(relationalStorePath)) {
  326. let content = fs.readFileSync(relationalStorePath, 'utf8');
  327. // 更新tables数组
  328. if (tableNames.length > 0) {
  329. const tablesArray = `private static tables: string[] = [${tableNames.map(name => `'${name}'`).join(', ')}]`;
  330. const updatedContent = content.replace(/private static tables: string\[\] = \[[^\]]*\]/, tablesArray);
  331. fs.writeFileSync(relationalStorePath, updatedContent, 'utf8');
  332. console.log('RelationalStoreUtis.ets文件已更新');
  333. }
  334. }
  335. // 替换原有的更新RDBSql.ets文件的代码块
  336. // 更新RDBSql.ts文件
  337. // 更新RDBSql.ts文件的改进逻辑
  338. const rdbSqlPath = path.join(currentDir, 'src', 'main', 'ets', 'rdb', 'sqls', 'RDBSql.ts');
  339. if (fs.existsSync(rdbSqlPath)) {
  340. let content = fs.readFileSync(rdbSqlPath, 'utf8');
  341. // 解析现有rDbSql对象中的属性
  342. const existingProperties: Map<string, string> = new Map();
  343. const propertyRegex = /(\w+):\s*'([^']+)'/g;
  344. let match;
  345. while ((match = propertyRegex.exec(content)) !== null) {
  346. existingProperties.set(match[1], match[2]);
  347. }
  348. // 构建SQL语句对象属性
  349. if (sqlStatements.length > 0) {
  350. const sqlProperties: string[] = [];
  351. // 首先保留现有的属性(用于手动添加的SQL)
  352. existingProperties.forEach((value, key) => {
  353. // 检查是否是自动生成的CREATE_*_TABLE属性
  354. if (!key.startsWith('CREATE_') || !key.endsWith('_TABLE')) {
  355. sqlProperties.push(` ${key}: '${value}'`);
  356. }
  357. });
  358. // 添加或更新基于类定义生成的SQL属性
  359. sqlStatements.forEach(statement => {
  360. const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
  361. const propertyValue = statement.split('=')[1].trim().slice(1, -2);
  362. sqlProperties.push(` ${propertyName}: '${propertyValue}'`);
  363. });
  364. // 构建新的对象内容
  365. const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
  366. fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
  367. console.log('RDBSql.ts文件已更新,保留了手动添加的SQL语句');
  368. }
  369. } else {
  370. // 如果文件不存在,创建新文件(原有逻辑保持不变)
  371. if (sqlStatements.length > 0) {
  372. const sqlProperties: string[] = [];
  373. sqlStatements.forEach(statement => {
  374. const propertyName = statement.split('=')[0].trim().replace('static readonly ', '');
  375. const propertyValue = statement.split('=')[1].trim().slice(1, -2);
  376. sqlProperties.push(` ${propertyName}: '${propertyValue}'`);
  377. });
  378. const newContent = `export const rDbSql = {\n${sqlProperties.join(',\n')}\n}`;
  379. // 确保目录存在
  380. const dirPath = path.dirname(rdbSqlPath);
  381. if (!fs.existsSync(dirPath)) {
  382. fs.mkdirSync(dirPath, { recursive: true });
  383. }
  384. fs.writeFileSync(rdbSqlPath, newContent, 'utf8');
  385. console.log('RDBSql.ts文件已创建并初始化为对象形式');
  386. }
  387. }
  388. console.log('----------------------------------------');
  389. } else {
  390. console.log('Tables directory not found');
  391. }
  392. } catch (error) {
  393. console.error(`处理tables目录时出错:`, error);
  394. }
  395. }
  396. }
  397. export default {
  398. system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  399. plugins: [
  400. customPlugin(), // 应用自定义Plugin
  401. rDBPlugin()
  402. ] /* Custom plugin to extend the functionality of Hvigor. */
  403. }