| // Copyright 2014 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'package:path/path.dart' as path; | |
| import 'package:skills_lint/skills_lint.dart'; | |
| /// A rule that checks that paths inside backticks are relative to the skill file, | |
| /// not relative to the repository root. | |
| class CheckBackticksRelativePathsRule extends SkillRule { | |
| CheckBackticksRelativePathsRule(this.valid2SegmentPaths, this.repoRootPath); | |
| final Set<String> valid2SegmentPaths; | |
| final String repoRootPath; | |
| @override | |
| String get name => 'check-backticks-relative-paths'; | |
| @override | |
| AnalysisSeverity get severity => AnalysisSeverity.error; | |
| @override | |
| Future<List<ValidationError>> validate(SkillContext context) async { | |
| final errors = <ValidationError>[]; | |
| final String content = context.rawContent; | |
| // Calculate relative path to root | |
| final String relativePathToRoot = path.relative(repoRootPath, from: context.directory.path); | |
| final backtickRegex = RegExp(r'`([^`]+)`'); | |
| final Iterable<RegExpMatch> matches = backtickRegex.allMatches(content); | |
| for (final match in matches) { | |
| final String textInBackticks = match.group(1)!; | |
| final String normalizedText = textInBackticks.replaceAll(r'\', '/'); | |
| for (final String validPath in valid2SegmentPaths) { | |
| if (normalizedText.startsWith('$validPath/')) { | |
| final fullPath = textInBackticks; | |
| final String correctedPath = path | |
| .join(relativePathToRoot, fullPath) | |
| .replaceAll(r'\', '/'); | |
| errors.add( | |
| ValidationError( | |
| ruleId: name, | |
| file: 'SKILL.md', | |
| message: | |
| 'Found root-relative path "$fullPath" in backticks. Suggested fix: [${path.basename(normalizedText)}]($correctedPath)', | |
| severity: severity, | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| return errors; | |
| } | |
| } |