Curated, product-agnostic snapshot of the post-story-04 tree: demo content deleted, auth-only reference feature, web-next shell, all gates green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor library traces, and product naming are curated out; generic template repairs (coverage provider devDeps, root test:coverage script, live lint fixes, root-only release-please) are kept. See TEMPLATE.md for provenance, curation list, and usage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
50 lines
1.7 KiB
JavaScript
50 lines
1.7 KiB
JavaScript
/**
|
|
* Feature test files import from `src/` through the `@/` alias, never via
|
|
* `../` parent traversal (CLAUDE.md "Key Conventions"). A `../` import in a
|
|
* test file is always reaching across `src/` directories — `@/` keeps those
|
|
* imports stable under file moves and makes the test's dependencies legible.
|
|
*
|
|
* Scoped to feature packages (`packages/<name>/src/`, excluding `core-*`):
|
|
* the convention is part of the feature template's contract. Core packages
|
|
* are generated and governed by their own templates, and tooling packages
|
|
* (turbo/generators, scripts) legitimately use relative paths.
|
|
*/
|
|
function isFeatureSrcTestFile(filename) {
|
|
const normalized = filename.replace(/\\/g, "/");
|
|
if (!normalized.endsWith(".test.ts") && !normalized.endsWith(".test.tsx")) {
|
|
return false;
|
|
}
|
|
return /\/packages\/(?!core-)[^/]+\/src\//.test(normalized);
|
|
}
|
|
|
|
/** @type {import("eslint").Rule.RuleModule} */
|
|
export default {
|
|
meta: {
|
|
type: "problem",
|
|
docs: {
|
|
description:
|
|
"Feature test files must import src modules via the @/ alias, not ../ parent paths.",
|
|
},
|
|
schema: [],
|
|
messages: {
|
|
relativeParentImport:
|
|
'Test file imports "{{source}}" with a ../ parent path. Use the "@/" alias for src imports (e.g. "@/application/...").',
|
|
},
|
|
},
|
|
create(context) {
|
|
if (!isFeatureSrcTestFile(context.filename)) return {};
|
|
return {
|
|
ImportDeclaration(node) {
|
|
const source = node.source.value;
|
|
if (typeof source === "string" && source.startsWith("../")) {
|
|
context.report({
|
|
node: node.source,
|
|
messageId: "relativeParentImport",
|
|
data: { source },
|
|
});
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|