* fix(lint): resolve module-boundary and prefer-inject errors Retag workspace-shell-util as type:data-access to match its injectable services that depend on @iptvnator/services, and convert RemoteControlService to inject(HttpClient). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(lint): enforce max-lines 400 with generated baseline Add a max-lines ESLint error (hard cap 400 raw lines per TypeScript file) per the repo file-size rule. The 134 pre-existing offenders are baselined in tools/eslint/max-lines-baseline.mjs, regenerable via generate-max-lines-baseline.mjs; the list should only shrink. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ci): enforce lint on PRs and guard coverage policy drift - Add a Lint job to ci.yml running nx run-many -t lint --all, so module-boundary tags, legacy-alias bans, and max-lines gate merges. - Fix the root lint script (was linting only electron-backend). - Add tools/coverage/check-coverage-policy.mjs: fails CI when a project with a test target is missing from coverage-policy.json; wired into coverage:ci as coverage:policy:check. - Run Tier B/C unit tests in CI without coverage (list derived from the policy), so website/packaging/remote-control tests run on PRs. - Replace the hand-picked 16-project test:unit:ci list with --all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: document CI lint enforcement and coverage policy guard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ci): address bot review feedback on policy guard and baseline generator - Drive Tier B/C validation from each policy entry's validationCommand (falling back to nx test), skipping projects with an e2e target since the E2E workflow already runs them (Codex). - Fail when a Tier A entry has no test target (Greptile, adapted: checking all entries against test targets would false-positive on the intentionally spec-less e2e/mock-server tiers). - Guard against missing JSON array in nx show projects output (Greptile). - Scan .tsx files in the max-lines baseline generator to match the ESLint rule's file patterns (Greptile). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
3.5 KiB
Nx Workspace Boundaries
This document records the current monorepo boundary conventions for IPTVnator.
Fresh Worktree Bootstrap
Install dependencies before using Nx discovery or targets:
pnpm install --frozen-lockfile
pnpm nx show projects
pnpm nx show projects depends on the workspace-local Nx packages under
node_modules. In a fresh worktree without dependencies it will fail before it
can inspect project metadata.
Project Tags
Every Nx project should carry three tag families in project.json:
scope:*- ownership area, for examplescope:portal,scope:workspace,scope:shared,scope:electron,scope:e2e, orscope:dev-tools.domain:*- product/runtime domain, for exampledomain:xtream,domain:stalker,domain:m3u,domain:playback,domain:web, ordomain:shared-runtime.type:*- architectural role, for exampletype:app,type:e2e,type:dev-app,type:feature,type:ui,type:data-access,type:util,type:tool, ortype:website.
eslint.config.mjs uses these tags with @nx/enforce-module-boundaries.
When adding a project, choose tags before adding imports so dependency direction
is clear from the start.
Import Aliases
Use scoped @iptvnator/* aliases from tsconfig.base.json.
Examples:
import { SettingsStore } from '@iptvnator/services';
import { Playlist } from '@iptvnator/shared/interfaces';
import { DialogService } from '@iptvnator/ui/components';
Do not introduce legacy bare aliases such as:
componentsm3u-statem3u-utilsservicesshared-interfacesshared-portalsremote-controldatabasedatabase-schemadatabase-path-utilsworkspace-dashboard-featureworkspace-dashboard-data-access
The lint config blocks these aliases so new code uses the same visible namespace and ownership convention.
Buildable libraries that have a local package.json should use the same public
name as their scoped alias. Nx uses package.json.name when it rewrites
buildable dependency paths to dist/ during @nx/js:tsc builds.
Dependency Direction
type:featuremay usetype:feature,type:ui,type:data-access, andtype:util.type:uimay usetype:ui,type:data-access, andtype:util.type:data-accessmay usetype:data-accessandtype:util.type:utilmay use onlytype:util.
If a change needs a dependency in the opposite direction, move the shared contract into a lower-level library instead of weakening boundaries.
Portal collection orchestration that reads/writes favorites, recent items, live
playback, or EPG data belongs in libs/portal/shared/data-access, not
libs/portal/shared/util. That keeps pure collection helpers importable by
Xtream/Stalker data-access libraries while allowing shared UI to use
provider-specific collection services without creating cycles.
Note: workspace-shell-util (libs/workspace/shell/util) is tagged
type:data-access despite its path. It exports injectable services such as
WorkspaceStartupPreferencesService that depend on @iptvnator/services, and
it must stay eagerly importable from apps/web/src/app/app.routes.ts without
pulling the lazy-loaded workspace shell feature bundle into the initial chunk.
CI Enforcement
The Lint job in .github/workflows/ci.yml runs
pnpm nx run-many --target=lint --all on every PR, so
@nx/enforce-module-boundaries violations, legacy bare-alias imports, and
max-lines violations fail CI. Run pnpm run lint locally before pushing.