chore: tighten architecture boundaries
This commit is contained in:
parent
d2c3b1bce8
commit
042271f519
|
|
@ -93,7 +93,8 @@ The app editor session (`src/app/editor/session/create.ts`) is a thin Vue wrappe
|
|||
|
||||
## Commands
|
||||
|
||||
- `bun run check` — type-aware lint + typecheck via oxlint + tsgo (run before committing)
|
||||
- `bun run check` — type-aware lint + typecheck via oxlint + tsgo + architecture checks (run before committing)
|
||||
- `bun run check:arch` — Steiger architecture lint for project-specific import boundaries
|
||||
- `bun run check:vue` — vue-tsc type-check for .vue files (has pre-existing errors, fix progressively)
|
||||
- `bun run test:dupes` — jscpd copy-paste detection across all TS sources
|
||||
- `bun run format` — oxfmt with import sorting
|
||||
|
|
@ -237,6 +238,7 @@ Release commits are the exception: keep using `Release v0.x.y`.
|
|||
## Code conventions
|
||||
|
||||
- Do not place code or tests ad hoc. Before adding or moving files, inspect the existing folder structure and nearby patterns, then put changes in the established domain-specific location. If no proper location exists, create one deliberately and update docs/conventions as needed.
|
||||
- Architecture boundaries are enforced by Steiger (`bun run check:arch`). App code must use public workspace package exports, workspace packages must not import app `src/` code, package-local aliases (`#core`, `#vue`, `#cli`, `#mcp`) are only for their owning package, core must stay framework-agnostic, and canvas/editor overlay code must not import property-panel internals.
|
||||
- Test placement is strict: E2E tests live under `tests/e2e/**` and use `*.spec.ts`; engine/unit tests live under `tests/engine/**` and use `*.test.ts`. Do not put store-only/internal-state assertions in E2E. If a test drives the UI like a user and verifies visible behavior, it can be E2E; if it creates nodes through internals and asserts graph state, it belongs in engine/unit coverage.
|
||||
|
||||
### File and folder naming
|
||||
|
|
|
|||
|
|
@ -29,6 +29,13 @@ const PACKAGE_ALIASES: Record<string, string> = {
|
|||
'#mcp/': 'packages/mcp/src/'
|
||||
}
|
||||
|
||||
const PACKAGE_ALIAS_OWNERS: Record<string, string> = {
|
||||
'#core/': 'packages/core/src/',
|
||||
'#vue/': 'packages/vue/src/',
|
||||
'#cli/': 'packages/cli/src/',
|
||||
'#mcp/': 'packages/mcp/src/'
|
||||
}
|
||||
|
||||
function normalizePath(filePath: string) {
|
||||
return filePath.split(path.sep).join('/')
|
||||
}
|
||||
|
|
@ -134,6 +141,33 @@ const noAppImportsInWorkspacePackages = createImportRule(
|
|||
}
|
||||
)
|
||||
|
||||
const noPackageInternalsInApp = createImportRule(
|
||||
'open-pencil/no-package-internals-in-app',
|
||||
(sourceRel, specifier, resolved) => {
|
||||
if (!sourceRel.startsWith('src/')) return null
|
||||
if (specifier in PACKAGE_ALIASES || Object.keys(PACKAGE_ALIASES).some((alias) => specifier.startsWith(alias))) {
|
||||
return 'App code must use package public exports such as @open-pencil/core or @open-pencil/vue, not package-local aliases.'
|
||||
}
|
||||
if (resolved?.startsWith('packages/')) {
|
||||
return 'App code must not import workspace package internals. Use package public exports instead.'
|
||||
}
|
||||
return null
|
||||
}
|
||||
)
|
||||
|
||||
const noForeignPackageLocalAliases = createImportRule(
|
||||
'open-pencil/no-foreign-package-local-aliases',
|
||||
(sourceRel, specifier) => {
|
||||
if (sourceRel.startsWith('scripts/')) return null
|
||||
for (const [alias, owner] of Object.entries(PACKAGE_ALIAS_OWNERS)) {
|
||||
if (specifier.startsWith(alias) && !sourceRel.startsWith(owner)) {
|
||||
return `Package-local alias ${alias} can only be used inside ${owner}. Use a public package export across package boundaries.`
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
)
|
||||
|
||||
const noUiImportsInCore = createImportRule(
|
||||
'open-pencil/no-ui-imports-in-core',
|
||||
(sourceRel, specifier) => {
|
||||
|
|
@ -156,6 +190,8 @@ export const openPencilArchitecturePlugin = {
|
|||
ruleDefinitions: [
|
||||
noPropertyPanelImportsInCanvas,
|
||||
noAppImportsInWorkspacePackages,
|
||||
noPackageInternalsInApp,
|
||||
noForeignPackageLocalAliases,
|
||||
noUiImportsInCore
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ export default defineConfig([
|
|||
rules: {
|
||||
'open-pencil/no-property-panel-imports-in-canvas': 'error',
|
||||
'open-pencil/no-app-imports-in-workspace-packages': 'error',
|
||||
'open-pencil/no-package-internals-in-app': 'error',
|
||||
'open-pencil/no-foreign-package-local-aliases': 'error',
|
||||
'open-pencil/no-ui-imports-in-core': 'error'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue