* Implementation of abstract method from FeatureProcessor * Load and parse rulesync rule files from .rulesync/rules/ directory
()
| 1233 | * Load and parse rulesync rule files from .rulesync/rules/ directory |
| 1234 | */ |
| 1235 | async loadRulesyncFiles(): Promise<RulesyncFile[]> { |
| 1236 | const rulesyncOutputRoot = join(this.inputRoot, RULESYNC_RULES_RELATIVE_DIR_PATH); |
| 1237 | const files = await findFilesByGlobs(join(rulesyncOutputRoot, "**", "*.md")); |
| 1238 | this.logger.debug(`Found ${files.length} rulesync files`); |
| 1239 | const rulesyncRules = await Promise.all( |
| 1240 | files.map((file) => { |
| 1241 | const relativeFilePath = relative(rulesyncOutputRoot, file); |
| 1242 | checkPathTraversal({ |
| 1243 | relativePath: relativeFilePath, |
| 1244 | intendedRootDir: rulesyncOutputRoot, |
| 1245 | }); |
| 1246 | return RulesyncRule.fromFile({ |
| 1247 | outputRoot: this.inputRoot, |
| 1248 | relativeFilePath, |
| 1249 | }); |
| 1250 | }), |
| 1251 | ); |
| 1252 | |
| 1253 | const factory = this.getFactory(this.toolTarget); |
| 1254 | |
| 1255 | const rootRules = rulesyncRules.filter((rule) => rule.getFrontmatter().root); |
| 1256 | |
| 1257 | // Filter roots to those targeting this tool |
| 1258 | const targetedRootRules = rootRules.filter((rule) => |
| 1259 | factory.class.isTargetedByRulesyncRule(rule), |
| 1260 | ); |
| 1261 | |
| 1262 | if (targetedRootRules.length > 1) { |
| 1263 | throw new Error( |
| 1264 | `Multiple root rulesync rules found for target '${this.toolTarget}': ${formatRulePaths(targetedRootRules)}`, |
| 1265 | ); |
| 1266 | } |
| 1267 | |
| 1268 | if (targetedRootRules.length === 0 && rulesyncRules.length > 0) { |
| 1269 | this.logger.warn( |
| 1270 | `No root rulesync rule file found for target '${this.toolTarget}'. Consider adding 'root: true' to one of your rule files in ${RULESYNC_RULES_RELATIVE_DIR_PATH}.`, |
| 1271 | ); |
| 1272 | } |
| 1273 | |
| 1274 | // Validation for localRoot — scoped to this tool's target |
| 1275 | const localRootRules = rulesyncRules.filter((rule) => rule.getFrontmatter().localRoot); |
| 1276 | const targetedLocalRootRules = localRootRules.filter((rule) => |
| 1277 | factory.class.isTargetedByRulesyncRule(rule), |
| 1278 | ); |
| 1279 | |
| 1280 | if (targetedLocalRootRules.length > 1) { |
| 1281 | throw new Error( |
| 1282 | `Multiple localRoot rules found for target '${this.toolTarget}': ${formatRulePaths(targetedLocalRootRules)}. Only one rule can have localRoot: true`, |
| 1283 | ); |
| 1284 | } |
| 1285 | |
| 1286 | if (targetedLocalRootRules.length > 0 && targetedRootRules.length === 0) { |
| 1287 | throw new Error( |
| 1288 | `localRoot: true requires a root: true rule to exist for target '${this.toolTarget}' (found in ${formatRulePaths(targetedLocalRootRules)})`, |
| 1289 | ); |
| 1290 | } |
| 1291 | |
| 1292 | // In global mode, return root rule + non-root rules if the target supports global nonRoot |
no test coverage detected