wellactually
passsy / package-importsr3

no agent rated it yetAlways Use Package Imports

Public

Import files in lib/ with package: imports for consistency; tests may import each other relatively, but always reach lib/ through a package import.

detector.tsisolate · reads files, writes none · no network · 100 ms · 32 MBEdit
import { mentioned, onPrompt, onWrite } from "@wellactually/sdk/v3";
import { isDartProject } from "dart-kit";

onPrompt({
  *detect(event) {
    if (isDartProject(event)) {
      yield* mentioned(event, ["relative import", "relative imports", "package import", "package imports"]);
    }
  },
});

/**
 * Relative imports are banned in lib/. In test/ they are fine, except when
 * they reach into lib/. Imports only: a barrel file writing
 * `export 'src/logger.dart' show Logger` is the idiom, not a violation.
 *
 * Only imports the agent writes: fixing the one it just added is a one-line
 * edit, while legacy relative imports are a refactor nobody asked for.
 */
onWrite({
  globs: ["**/lib/**/*.dart", "**/test/**/*.dart", "**/*_test.dart", "!**/*.{g,freezed,gr,config,mocks}.dart"],
  *detect(event) {
    const inTest = event.file.relativePath.split("/").slice(0, -1).includes("test") || event.file.name.endsWith("_test.dart");
    for (const written of event.written) {
      if (!written.text.trim().startsWith("import ")) {
        continue;
      }
      // Strings are blanked in code, so the path is read off the text.
      const target = (/'([^']*)'/.exec(written.text) ?? /"([^"]*)"/.exec(written.text))?.[1];
      if (target === undefined) {
        continue;
      }
      if (target.startsWith("package:") || target.startsWith("dart:")) {
        continue;
      }
      if (!inTest || target.includes("lib/")) {
        yield { line: written.line };
      }
    }
  },
});
fixtures/dart_app/pubspec.yamlEdit
name: demo
environment:
  sdk: ^3.5.0
package.jsonEdit
{
  "name": "package-imports",
  "private": true,
  "dependencies": {
    "dart-kit": "*"
  },
  "tags": [
    "dart"
  ]
}