├── .detective ├── config.json ├── hash └── log ├── .editorconfig ├── .env ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── issue.md └── workflows │ ├── build.yml │ ├── deploy-docs.yml │ ├── release-please.yml │ └── test-deploy-docs.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-push ├── .prettierignore ├── .prettierrc ├── .vscode └── extensions.json ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── cli.md │ ├── dependency-rules.md │ ├── installation.md │ ├── integration.md │ ├── introduction.md │ ├── module_boundaries.md │ ├── release-notes │ │ ├── 0.16.md │ │ ├── 0.17.md │ │ └── 0.18.md │ └── roadmap.md ├── docusaurus.config.ts ├── package.json ├── sidebars.ts ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.tsx │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.module.css │ │ ├── index.tsx │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── dependency-rules-1.png │ │ ├── docusaurus.png │ │ ├── favicon.ico │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── modularity-dark.svg │ │ ├── modularity.svg │ │ ├── module-boundaries-barrel-file.png │ │ ├── module-boundaries-barrel-invalid.png │ │ ├── module-boundaries-barrel-less-invalid.png │ │ ├── module-boundaries-barrel-less-valid.png │ │ ├── module-boundaries-barrel-valid.png │ │ ├── simple.svg │ │ ├── simplicity-dark.svg │ │ ├── simplicity.ai │ │ ├── simplicity.svg │ │ ├── social-card.png │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ ├── undraw_docusaurus_tree.svg │ │ ├── zero-dependencies-dark.svg │ │ └── zero-dependencies.svg ├── tsconfig.json └── yarn.lock ├── eslint.config.js ├── logo.png ├── migrations.json ├── nx.json ├── package.json ├── packages ├── .gitkeep ├── core │ ├── README.md │ ├── package.json │ ├── project.json │ ├── src │ │ ├── bin │ │ │ └── main.ts │ │ ├── index.ts │ │ ├── lib │ │ │ ├── api │ │ │ │ └── get-project-data.ts │ │ │ ├── checks │ │ │ │ ├── any-tag.ts │ │ │ │ ├── check-for-circular.ts │ │ │ │ ├── check-for-dependency-rule-violation.ts │ │ │ │ ├── get-afi.ts │ │ │ │ ├── has-encapsulation-violations.ts │ │ │ │ ├── is-dependency-allowed.ts │ │ │ │ ├── no-dependencies.ts │ │ │ │ ├── same-tag.ts │ │ │ │ ├── tests │ │ │ │ │ ├── check-for-dependency-rule-violation.spec.ts │ │ │ │ │ ├── encapsulation-barrel-less.spec.ts │ │ │ │ │ ├── encapsulation-with-exclude-root.spec.ts │ │ │ │ │ ├── has-encapsulation-violations.spec.ts │ │ │ │ │ └── is-dependency-allowed.spec.ts │ │ │ │ └── unused-files.ts │ │ │ ├── cli │ │ │ │ ├── cli.ts │ │ │ │ ├── export-data.ts │ │ │ │ ├── init.ts │ │ │ │ ├── internal │ │ │ │ │ ├── get-entry-from-cli-or-config.ts │ │ │ │ │ ├── handle-error.ts │ │ │ │ │ └── log-info-for-missing-sheriff-config.ts │ │ │ │ ├── list.ts │ │ │ │ ├── main.ts │ │ │ │ ├── tests │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ ├── export-data.spec.ts.snap │ │ │ │ │ │ ├── init.spec.ts.snap │ │ │ │ │ │ ├── list.spec.ts.snap │ │ │ │ │ │ └── verify.spec.ts.snap │ │ │ │ │ ├── export-data.spec.ts │ │ │ │ │ ├── get-entry-from-cli-or-file.spec.ts │ │ │ │ │ ├── handle-error.spec.ts │ │ │ │ │ ├── helpers │ │ │ │ │ │ └── mock-cli.ts │ │ │ │ │ ├── init.spec.ts │ │ │ │ │ ├── list.spec.ts │ │ │ │ │ ├── main.spec.ts │ │ │ │ │ ├── verify-cli-wrapper.ts │ │ │ │ │ ├── verify.spec.ts │ │ │ │ │ └── version.spec.ts │ │ │ │ ├── verify.ts │ │ │ │ └── version.ts │ │ │ ├── config │ │ │ │ ├── configuration.ts │ │ │ │ ├── default-config.ts │ │ │ │ ├── dependency-rules-config.ts │ │ │ │ ├── find-config.ts │ │ │ │ ├── module-config.ts │ │ │ │ ├── parse-config.ts │ │ │ │ ├── tests │ │ │ │ │ ├── __snapshots__ │ │ │ │ │ │ └── parse-config.spec.ts.snap │ │ │ │ │ └── parse-config.spec.ts │ │ │ │ └── user-sheriff-config.ts │ │ │ ├── error │ │ │ │ └── user-error.ts │ │ │ ├── eslint │ │ │ │ ├── tests │ │ │ │ │ ├── eslint.spec.ts │ │ │ │ │ ├── violates-dependency-rule.spec.ts │ │ │ │ │ └── violates-encapsulation-rule.spec.ts │ │ │ │ ├── violates-dependency-rule.ts │ │ │ │ └── violates-encapsulation-rule.ts │ │ │ ├── extend.extensions.d.ts │ │ │ ├── file-info │ │ │ │ ├── fix-path-separators.ts │ │ │ │ ├── format-file-info.ts │ │ │ │ ├── fs-path.ts │ │ │ │ ├── generate-ts-data.ts │ │ │ │ ├── generate-unassigned-file-info.ts │ │ │ │ ├── get-ts-config-context.ts │ │ │ │ ├── resolve-potential-ts-path.ts │ │ │ │ ├── tests │ │ │ │ │ ├── fs-path.spec.ts │ │ │ │ │ ├── generate-ts-data.spec.ts │ │ │ │ │ ├── generate-unassigned-file-info.spec.ts │ │ │ │ │ ├── get-ts-config-context.spec.ts │ │ │ │ │ ├── resolve-potential-ts-path.spec.ts │ │ │ │ │ ├── traverse-filesystem.spec.ts │ │ │ │ │ └── unassigned-file-info.spec.ts │ │ │ │ ├── traverse-filesystem.ts │ │ │ │ ├── traverse-unassigned-file-info.ts │ │ │ │ ├── ts-config.ts │ │ │ │ ├── ts-data.ts │ │ │ │ └── unassigned-file-info.ts │ │ │ ├── fs │ │ │ │ ├── default-fs.spec.ts │ │ │ │ ├── default-fs.ts │ │ │ │ ├── find-files │ │ │ │ │ ├── test1 │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── test2 │ │ │ │ │ │ └── customers │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── test3 │ │ │ │ │ │ ├── admin │ │ │ │ │ │ │ └── booking │ │ │ │ │ │ │ │ ├── data │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ └── feature │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── customers │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── holidays │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── test4 │ │ │ │ │ │ └── .gitkeep │ │ │ │ ├── find-nearest │ │ │ │ │ ├── test1 │ │ │ │ │ │ └── customers │ │ │ │ │ │ │ ├── admin │ │ │ │ │ │ │ └── core │ │ │ │ │ │ │ │ └── feature │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ ├── test2 │ │ │ │ │ │ └── customers │ │ │ │ │ │ │ ├── admin │ │ │ │ │ │ │ └── core │ │ │ │ │ │ │ │ ├── feature │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ │ │ └── tsconfig.json │ │ │ │ │ └── test3 │ │ │ │ │ │ └── customers │ │ │ │ │ │ └── admin │ │ │ │ │ │ └── core │ │ │ │ │ │ └── feature │ │ │ │ │ │ └── index.ts │ │ │ │ ├── fs.ts │ │ │ │ ├── getFs.ts │ │ │ │ ├── virtual-fs.spec.ts │ │ │ │ └── virtual-fs.ts │ │ │ ├── log │ │ │ │ ├── index.ts │ │ │ │ ├── log-level.ts │ │ │ │ ├── log.module.spec.ts │ │ │ │ ├── log.ts │ │ │ │ └── logger.ts │ │ │ ├── main │ │ │ │ ├── after-init.ts │ │ │ │ ├── callback.ts │ │ │ │ ├── init.spec.ts │ │ │ │ ├── init.ts │ │ │ │ ├── internal │ │ │ │ │ ├── callback.ts │ │ │ │ │ └── initialized.ts │ │ │ │ └── parse-project.ts │ │ │ ├── modules │ │ │ │ ├── create-modules.ts │ │ │ │ ├── file.info.ts │ │ │ │ ├── fill-file-info-map.ts │ │ │ │ ├── find-module-paths.ts │ │ │ │ ├── format-modules.ts │ │ │ │ ├── get-project-dirs-from-file-info.ts │ │ │ │ ├── internal │ │ │ │ │ ├── create-module-path-patterns-tree.ts │ │ │ │ │ ├── find-module-paths-with-barrel.ts │ │ │ │ │ ├── find-module-paths-without-barrel.ts │ │ │ │ │ └── flatten-modules.ts │ │ │ │ ├── module.ts │ │ │ │ ├── tests │ │ │ │ │ ├── create-module-path-patterns-tree.spec.ts │ │ │ │ │ ├── create-module.spec.ts │ │ │ │ │ ├── find-module-paths-with-barrel.spec.ts │ │ │ │ │ ├── find-module-paths-without-barrel.spec.ts │ │ │ │ │ ├── flatten-tagging.spec.ts │ │ │ │ │ └── get-project-dirs-from-file-info.spec.ts │ │ │ │ └── traverse-file-info.ts │ │ │ ├── tags │ │ │ │ ├── calc-tags-for-module.spec.ts │ │ │ │ └── calc-tags-for-module.ts │ │ │ ├── test │ │ │ │ ├── build-file-info.ts │ │ │ │ ├── expect.extensions.ts │ │ │ │ ├── find-assigned-file-info.ts │ │ │ │ ├── find-file-info.ts │ │ │ │ ├── fixtures │ │ │ │ │ └── ts-config.ts │ │ │ │ ├── in-vfs.ts │ │ │ │ ├── project-configurator.ts │ │ │ │ ├── project-creator.ts │ │ │ │ ├── sheriff.config.ts │ │ │ │ ├── test-init.ts │ │ │ │ └── traverse-project.ts │ │ │ └── util │ │ │ │ ├── assert-not-null.ts │ │ │ │ ├── get.ts │ │ │ │ ├── throw-if-null.spec.ts │ │ │ │ ├── throw-if-null.ts │ │ │ │ ├── typed-object-functions.ts │ │ │ │ └── wildcard-to-regex.ts │ │ └── sheriff.full-spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json └── eslint-plugin │ ├── README.md │ ├── package.json │ ├── project.json │ ├── src │ ├── index.ts │ └── lib │ │ ├── configs │ │ ├── all.ts │ │ └── legacy.ts │ │ └── rules │ │ ├── create-rule.ts │ │ ├── deep-import.ts │ │ ├── dependency-rule.ts │ │ ├── encapsulation.ts │ │ ├── executor.ts │ │ ├── index.ts │ │ └── tests │ │ ├── create-rule.spec.ts │ │ ├── deep-import.spec.ts │ │ ├── dependency-rule.spec.ts │ │ └── encapsulation.spec.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── tsconfig.spec.json ├── release-please-config.json ├── run-integration-tests.sh ├── sonar-project.properties ├── test-projects ├── angular-i │ ├── .editorconfig │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── integration-test.sh │ ├── package.json │ ├── sheriff.config.ts │ ├── sheriff.config.ts.original │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.ts │ │ │ ├── app.routes.ts │ │ │ ├── bookings │ │ │ │ ├── +state │ │ │ │ │ ├── bookings.actions.ts │ │ │ │ │ ├── bookings.effects.ts │ │ │ │ │ ├── bookings.reducer.ts │ │ │ │ │ └── bookings.selectors.ts │ │ │ │ ├── bookings.routes.ts │ │ │ │ ├── index.ts │ │ │ │ └── overview │ │ │ │ │ ├── overview.component.html │ │ │ │ │ └── overview.component.ts │ │ │ ├── customers │ │ │ │ ├── api │ │ │ │ │ └── index.ts │ │ │ │ ├── data │ │ │ │ │ ├── customers-repository.service.ts │ │ │ │ │ ├── customers.actions.ts │ │ │ │ │ ├── customers.effects.ts │ │ │ │ │ ├── customers.json │ │ │ │ │ ├── customers.reducer.ts │ │ │ │ │ ├── customers.selectors.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── provide-customers.ts │ │ │ │ ├── feature │ │ │ │ │ ├── components │ │ │ │ │ │ ├── add-customer.component.ts │ │ │ │ │ │ ├── customers-container.component.ts │ │ │ │ │ │ ├── customers-root │ │ │ │ │ │ │ ├── customers-root.component.html │ │ │ │ │ │ │ └── customers-root.component.ts │ │ │ │ │ │ └── edit-customer.component.ts │ │ │ │ │ ├── customers.routes.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── services │ │ │ │ │ │ ├── data.guard.ts │ │ │ │ │ │ └── data.ts │ │ │ │ ├── model │ │ │ │ │ ├── customer.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui │ │ │ │ │ ├── customer.pipe.ts │ │ │ │ │ ├── customer │ │ │ │ │ ├── customer.component.html │ │ │ │ │ ├── customer.component.scss │ │ │ │ │ └── customer.component.ts │ │ │ │ │ ├── customers │ │ │ │ │ ├── customers.component.html │ │ │ │ │ └── customers.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── holidays │ │ │ │ ├── feature │ │ │ │ │ ├── +state │ │ │ │ │ │ ├── holidays.actions.ts │ │ │ │ │ │ ├── holidays.effects.ts │ │ │ │ │ │ ├── holidays.reducer.ts │ │ │ │ │ │ └── holidays.selectors.ts │ │ │ │ │ ├── address-lookuper.service.ts │ │ │ │ │ ├── address.ts │ │ │ │ │ ├── holidays.routes.ts │ │ │ │ │ ├── holidays │ │ │ │ │ │ ├── holidays.component.spec.ts │ │ │ │ │ │ └── holidays.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── parse-address.spec.ts │ │ │ │ │ ├── parse-address.ts │ │ │ │ │ └── request-info │ │ │ │ │ │ ├── request-info.component.harness.ts │ │ │ │ │ │ ├── request-info.component.html │ │ │ │ │ │ └── request-info.component.ts │ │ │ │ ├── model │ │ │ │ │ ├── holiday.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui │ │ │ │ │ ├── holiday-card │ │ │ │ │ ├── holiday-card.component.html │ │ │ │ │ ├── holiday-card.component.scss │ │ │ │ │ └── holiday-card.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ ├── config │ │ │ │ │ ├── configuration.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── form │ │ │ │ │ ├── form-errors.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── options.ts │ │ │ │ ├── http │ │ │ │ │ ├── base-url.interceptor.ts │ │ │ │ │ ├── error-message.context.ts │ │ │ │ │ ├── error.interceptor.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── with-error-message-context.ts │ │ │ │ ├── master-data │ │ │ │ │ ├── +state │ │ │ │ │ │ └── master.reducer.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── shared-master-data.provider.ts │ │ │ │ ├── ngrx-utils │ │ │ │ │ ├── deep-clone.ts │ │ │ │ │ ├── filter-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── load-status.ts │ │ │ │ │ ├── noop.action.ts │ │ │ │ │ └── safe-concat-map.ts │ │ │ │ ├── security │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── security.actions.ts │ │ │ │ │ ├── security.effects.ts │ │ │ │ │ ├── security.provider.ts │ │ │ │ │ ├── security.reducer.ts │ │ │ │ │ ├── security.selectors.ts │ │ │ │ │ └── security.service.ts │ │ │ │ ├── testing │ │ │ │ │ ├── assert-type.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── mock-inject.ts │ │ │ │ ├── ui-messaging │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loader │ │ │ │ │ │ ├── loader.component.ts │ │ │ │ │ │ ├── loading.interceptor.ts │ │ │ │ │ │ ├── loading.service.ts │ │ │ │ │ │ ├── silent-load.context.ts │ │ │ │ │ │ └── with-silent-load-context.ts │ │ │ │ │ ├── message │ │ │ │ │ │ ├── confirmation.component.ts │ │ │ │ │ │ ├── message.component.html │ │ │ │ │ │ ├── message.component.ts │ │ │ │ │ │ ├── message.service.ts │ │ │ │ │ │ ├── message.store.ts │ │ │ │ │ │ └── message.ts │ │ │ │ │ └── shared-ui-messaging.provider.ts │ │ │ │ ├── ui │ │ │ │ │ ├── blinker.directive.ts │ │ │ │ │ └── index.ts │ │ │ │ └── util │ │ │ │ │ ├── assert-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── is-defined.ts │ │ │ │ │ └── safe-assign.ts │ │ │ └── shell │ │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.scss │ │ │ │ └── header.component.ts │ │ │ │ ├── home.component.ts │ │ │ │ ├── services │ │ │ │ ├── error-handler.service.ts │ │ │ │ └── user-loader.guard.ts │ │ │ │ └── sidemenu │ │ │ │ ├── sidemenu.component.html │ │ │ │ ├── sidemenu.component.scss │ │ │ │ └── sidemenu.component.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── copenhagen.jpg │ │ │ ├── darmstadt-small.jpg │ │ │ ├── darmstadt.jpg │ │ │ ├── detroit.jpg │ │ │ ├── firenze.jpg │ │ │ ├── granada.jpg │ │ │ ├── large-bg.jpg │ │ │ ├── logo.png │ │ │ ├── london.jpg │ │ │ ├── luebeck.jpg │ │ │ ├── reykjavík.jpg │ │ │ ├── shanghai.jpg │ │ │ ├── vienna-small.jpg │ │ │ └── vienna.jpg │ │ ├── environments │ │ │ ├── environment.development.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.scss │ ├── tailwind.config.js │ ├── tests │ │ ├── actual │ │ │ └── .gitkeep │ │ ├── auto-tagging.config.ts │ │ ├── customer-api-re-exports.index.ts │ │ ├── customer.dependency-rule.component.ts │ │ ├── customers-container.deep-import.component.ts │ │ ├── dynamic-import-sheriff.config.ts │ │ ├── empty-sheriff.config.ts │ │ ├── expected │ │ │ ├── auto-tagging-lint.json │ │ │ ├── cli-export.txt │ │ │ ├── cli-list.txt │ │ │ ├── cli-verify-failure.txt │ │ │ ├── cli-verify-success.txt │ │ │ ├── dependency-rule-lint.json │ │ │ ├── dynamic-import-lint.json │ │ │ ├── encapsulation-lint.json │ │ │ ├── re-exports-lint.json │ │ │ └── user-error-lint.json │ │ └── sheriff.config-failure.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── yarn.lock ├── angular-ii │ ├── .editorconfig │ ├── .eslintrc.json │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── README.md │ ├── angular.json │ ├── package-lock.json │ ├── package.json │ ├── sheriff.config.ts │ ├── src │ │ ├── app │ │ │ ├── +state │ │ │ │ └── index.ts │ │ │ ├── about │ │ │ │ ├── about.component.ts │ │ │ │ └── lazy │ │ │ │ │ └── lazy.component.ts │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.ts │ │ │ ├── app.routes.ts │ │ │ ├── domains │ │ │ │ ├── checkin │ │ │ │ │ ├── data │ │ │ │ │ │ ├── checkin.service.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── feature-manage │ │ │ │ │ │ ├── feature-manage.component.css │ │ │ │ │ │ ├── feature-manage.component.html │ │ │ │ │ │ ├── feature-manage.component.ts │ │ │ │ │ │ └── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── util-auth │ │ │ │ │ │ ├── auth.interceptor.ts │ │ │ │ │ │ ├── auth.service.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── util-common │ │ │ │ │ │ ├── city.pipe.ts │ │ │ │ │ │ ├── city.validator.ts │ │ │ │ │ │ ├── combine-environment-providers.ts │ │ │ │ │ │ ├── custom-appender.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── legacy.interceptor.ts │ │ │ │ │ └── util-logger │ │ │ │ │ │ ├── color-config.ts │ │ │ │ │ │ ├── color.service.ts │ │ │ │ │ │ ├── features.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── log-appender.ts │ │ │ │ │ │ ├── log-formatter.ts │ │ │ │ │ │ ├── log-level.ts │ │ │ │ │ │ ├── logger-config.ts │ │ │ │ │ │ ├── logger-module.ts │ │ │ │ │ │ ├── logger.ts │ │ │ │ │ │ └── providers.ts │ │ │ │ └── ticketing │ │ │ │ │ ├── data │ │ │ │ │ ├── +state │ │ │ │ │ │ ├── actions.ts │ │ │ │ │ │ ├── effects.ts │ │ │ │ │ │ ├── reducers.ts │ │ │ │ │ │ └── selectors.ts │ │ │ │ │ ├── flight.service.ts │ │ │ │ │ ├── flight.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── passenger.ts │ │ │ │ │ ├── feature-booking │ │ │ │ │ ├── flight-booking.component.html │ │ │ │ │ ├── flight-booking.component.ts │ │ │ │ │ ├── flight-booking.routes.ts │ │ │ │ │ ├── flight-edit │ │ │ │ │ │ ├── flight-edit.component.css │ │ │ │ │ │ ├── flight-edit.component.html │ │ │ │ │ │ └── flight-edit.component.ts │ │ │ │ │ ├── flight-search │ │ │ │ │ │ ├── flight-search.component.css │ │ │ │ │ │ ├── flight-search.component.html │ │ │ │ │ │ └── flight-search.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── passenger-search │ │ │ │ │ │ ├── passenger-search.component.css │ │ │ │ │ │ ├── passenger-search.component.html │ │ │ │ │ │ └── passenger-search.component.ts │ │ │ │ │ ├── provider.ts │ │ │ │ │ └── utils │ │ │ │ │ │ └── booking.interceptor.ts │ │ │ │ │ ├── feature-my-tickets │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── my-tickets.component.ts │ │ │ │ │ ├── ticket.service.ts │ │ │ │ │ └── tickets.module.ts │ │ │ │ │ ├── feature-next-flight │ │ │ │ │ ├── index.ts │ │ │ │ │ └── next-flight.component.ts │ │ │ │ │ └── ui-common │ │ │ │ │ ├── flight-card │ │ │ │ │ ├── flight-card.component.css │ │ │ │ │ ├── flight-card.component.html │ │ │ │ │ └── flight-card.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── home │ │ │ │ ├── home.component.css │ │ │ │ ├── home.component.html │ │ │ │ └── home.component.ts │ │ │ ├── init.service.ts │ │ │ ├── logger.config.ts │ │ │ └── shell │ │ │ │ ├── index.ts │ │ │ │ ├── navbar │ │ │ │ ├── navbar.component.html │ │ │ │ └── navbar.component.ts │ │ │ │ └── sidebar │ │ │ │ ├── sidebar.component.css │ │ │ │ ├── sidebar.component.html │ │ │ │ └── sidebar.component.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.css │ ├── tsconfig.app.json │ ├── tsconfig.json │ └── tsconfig.spec.json ├── angular-iii │ ├── .editorconfig │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── package.json │ ├── sheriff.config.ts │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.ts │ │ │ ├── app.routes.ts │ │ │ ├── customers │ │ │ │ ├── api │ │ │ │ │ └── index.ts │ │ │ │ ├── data │ │ │ │ │ ├── customers-repository.service.ts │ │ │ │ │ ├── customers.actions.ts │ │ │ │ │ ├── customers.effects.ts │ │ │ │ │ ├── customers.reducer.ts │ │ │ │ │ ├── customers.selectors.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── provide-customers.ts │ │ │ │ ├── feature │ │ │ │ │ ├── components │ │ │ │ │ │ ├── add-customer.component.ts │ │ │ │ │ │ ├── customers-container.component.ts │ │ │ │ │ │ ├── customers-root │ │ │ │ │ │ │ ├── customers-root.component.html │ │ │ │ │ │ │ └── customers-root.component.ts │ │ │ │ │ │ └── edit-customer.component.ts │ │ │ │ │ ├── customers.routes.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── services │ │ │ │ │ │ ├── data.guard.ts │ │ │ │ │ │ └── data.ts │ │ │ │ ├── model │ │ │ │ │ ├── customer.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui │ │ │ │ │ ├── customer.pipe.ts │ │ │ │ │ ├── customer │ │ │ │ │ ├── customer.component.html │ │ │ │ │ ├── customer.component.scss │ │ │ │ │ └── customer.component.ts │ │ │ │ │ ├── customers │ │ │ │ │ ├── customers.component.html │ │ │ │ │ └── customers.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ ├── config │ │ │ │ │ ├── configuration.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── form │ │ │ │ │ ├── form-errors.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── options.ts │ │ │ │ ├── http │ │ │ │ │ ├── base-url.interceptor.ts │ │ │ │ │ ├── error-message.context.ts │ │ │ │ │ ├── error.interceptor.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── with-error-message-context.ts │ │ │ │ ├── master-data │ │ │ │ │ ├── +state │ │ │ │ │ │ └── master.reducer.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── shared-master-data.provider.ts │ │ │ │ ├── ngrx-utils │ │ │ │ │ ├── deep-clone.ts │ │ │ │ │ ├── filter-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── load-status.ts │ │ │ │ │ ├── noop.action.ts │ │ │ │ │ └── safe-concat-map.ts │ │ │ │ ├── security │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── security.actions.ts │ │ │ │ │ ├── security.effects.ts │ │ │ │ │ ├── security.provider.ts │ │ │ │ │ ├── security.reducer.ts │ │ │ │ │ ├── security.selectors.ts │ │ │ │ │ └── security.service.ts │ │ │ │ ├── testing │ │ │ │ │ ├── assert-type.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── mock-inject.ts │ │ │ │ ├── ui-messaging │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loader │ │ │ │ │ │ ├── loader.component.ts │ │ │ │ │ │ ├── loading.interceptor.ts │ │ │ │ │ │ ├── loading.service.ts │ │ │ │ │ │ ├── silent-load.context.ts │ │ │ │ │ │ └── with-silent-load-context.ts │ │ │ │ │ ├── message │ │ │ │ │ │ ├── confirmation.component.ts │ │ │ │ │ │ ├── message.component.html │ │ │ │ │ │ ├── message.component.ts │ │ │ │ │ │ ├── message.service.ts │ │ │ │ │ │ ├── message.store.ts │ │ │ │ │ │ └── message.ts │ │ │ │ │ └── shared-ui-messaging.provider.ts │ │ │ │ ├── ui │ │ │ │ │ ├── blinker.directive.ts │ │ │ │ │ └── index.ts │ │ │ │ └── util │ │ │ │ │ ├── assert-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── is-defined.ts │ │ │ │ │ └── safe-assign.ts │ │ │ └── shell │ │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.scss │ │ │ │ └── header.component.ts │ │ │ │ ├── home.component.ts │ │ │ │ ├── services │ │ │ │ ├── error-handler.service.ts │ │ │ │ └── user-loader.guard.ts │ │ │ │ └── sidemenu │ │ │ │ ├── sidemenu.component.html │ │ │ │ ├── sidemenu.component.scss │ │ │ │ └── sidemenu.component.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── copenhagen.jpg │ │ │ ├── darmstadt-small.jpg │ │ │ ├── darmstadt.jpg │ │ │ ├── detroit.jpg │ │ │ ├── firenze.jpg │ │ │ ├── granada.jpg │ │ │ ├── large-bg.jpg │ │ │ ├── logo.png │ │ │ ├── london.jpg │ │ │ ├── luebeck.jpg │ │ │ ├── reykjavík.jpg │ │ │ ├── shanghai.jpg │ │ │ ├── vienna-small.jpg │ │ │ └── vienna.jpg │ │ ├── environments │ │ │ ├── environment.development.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.scss │ ├── tailwind.config.js │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── yarn.lock ├── angular-iv │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── eslint.config.js │ ├── integration-test.sh │ ├── package.json │ ├── sheriff.config.ts │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.ts │ │ │ ├── app.routes.ts │ │ │ ├── bookings │ │ │ │ ├── +state │ │ │ │ │ ├── bookings.actions.ts │ │ │ │ │ ├── bookings.effects.ts │ │ │ │ │ ├── bookings.reducer.ts │ │ │ │ │ └── bookings.selectors.ts │ │ │ │ ├── bookings.routes.ts │ │ │ │ ├── index.ts │ │ │ │ └── overview │ │ │ │ │ ├── overview.component.html │ │ │ │ │ └── overview.component.ts │ │ │ ├── customers │ │ │ │ ├── api │ │ │ │ │ └── index.ts │ │ │ │ ├── data │ │ │ │ │ ├── customers-repository.service.ts │ │ │ │ │ ├── customers.actions.ts │ │ │ │ │ ├── customers.effects.ts │ │ │ │ │ ├── customers.json │ │ │ │ │ ├── customers.reducer.ts │ │ │ │ │ ├── customers.selectors.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── provide-customers.ts │ │ │ │ ├── feature │ │ │ │ │ ├── components │ │ │ │ │ │ ├── add-customer.component.ts │ │ │ │ │ │ ├── customers-container.component.ts │ │ │ │ │ │ ├── customers-root │ │ │ │ │ │ │ ├── customers-root.component.html │ │ │ │ │ │ │ └── customers-root.component.ts │ │ │ │ │ │ └── edit-customer.component.ts │ │ │ │ │ ├── customers.routes.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── services │ │ │ │ │ │ ├── data.guard.ts │ │ │ │ │ │ └── data.ts │ │ │ │ ├── model │ │ │ │ │ ├── customer.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui │ │ │ │ │ ├── customer.pipe.ts │ │ │ │ │ ├── customer │ │ │ │ │ ├── customer.component.html │ │ │ │ │ ├── customer.component.scss │ │ │ │ │ └── customer.component.ts │ │ │ │ │ ├── customers │ │ │ │ │ ├── customers.component.html │ │ │ │ │ └── customers.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── holidays │ │ │ │ ├── feature │ │ │ │ │ ├── +state │ │ │ │ │ │ ├── holidays.actions.ts │ │ │ │ │ │ ├── holidays.effects.ts │ │ │ │ │ │ ├── holidays.reducer.ts │ │ │ │ │ │ └── holidays.selectors.ts │ │ │ │ │ ├── address-lookuper.service.ts │ │ │ │ │ ├── address.ts │ │ │ │ │ ├── holidays.routes.ts │ │ │ │ │ ├── holidays │ │ │ │ │ │ ├── holidays.component.spec.ts │ │ │ │ │ │ └── holidays.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── parse-address.spec.ts │ │ │ │ │ ├── parse-address.ts │ │ │ │ │ └── request-info │ │ │ │ │ │ ├── request-info.component.harness.ts │ │ │ │ │ │ ├── request-info.component.html │ │ │ │ │ │ └── request-info.component.ts │ │ │ │ ├── model │ │ │ │ │ ├── holiday.ts │ │ │ │ │ └── index.ts │ │ │ │ └── ui │ │ │ │ │ ├── holiday-card │ │ │ │ │ ├── holiday-card.component.html │ │ │ │ │ ├── holiday-card.component.scss │ │ │ │ │ └── holiday-card.component.ts │ │ │ │ │ └── index.ts │ │ │ ├── shared │ │ │ │ ├── config │ │ │ │ │ ├── configuration.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── form │ │ │ │ │ ├── form-errors.component.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── options.ts │ │ │ │ ├── http │ │ │ │ │ ├── base-url.interceptor.ts │ │ │ │ │ ├── error-message.context.ts │ │ │ │ │ ├── error.interceptor.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── with-error-message-context.ts │ │ │ │ ├── master-data │ │ │ │ │ ├── +state │ │ │ │ │ │ └── master.reducer.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── shared-master-data.provider.ts │ │ │ │ ├── ngrx-utils │ │ │ │ │ ├── deep-clone.ts │ │ │ │ │ ├── filter-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── load-status.ts │ │ │ │ │ ├── noop.action.ts │ │ │ │ │ └── safe-concat-map.ts │ │ │ │ ├── security │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── security.actions.ts │ │ │ │ │ ├── security.effects.ts │ │ │ │ │ ├── security.provider.ts │ │ │ │ │ ├── security.reducer.ts │ │ │ │ │ ├── security.selectors.ts │ │ │ │ │ └── security.service.ts │ │ │ │ ├── testing │ │ │ │ │ ├── assert-type.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── mock-inject.ts │ │ │ │ ├── ui-messaging │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loader │ │ │ │ │ │ ├── loader.component.ts │ │ │ │ │ │ ├── loading.interceptor.ts │ │ │ │ │ │ ├── loading.service.ts │ │ │ │ │ │ ├── silent-load.context.ts │ │ │ │ │ │ └── with-silent-load-context.ts │ │ │ │ │ ├── message │ │ │ │ │ │ ├── confirmation.component.ts │ │ │ │ │ │ ├── message.component.html │ │ │ │ │ │ ├── message.component.ts │ │ │ │ │ │ ├── message.service.ts │ │ │ │ │ │ ├── message.store.ts │ │ │ │ │ │ └── message.ts │ │ │ │ │ └── shared-ui-messaging.provider.ts │ │ │ │ ├── ui │ │ │ │ │ ├── blinker.directive.ts │ │ │ │ │ └── index.ts │ │ │ │ └── util │ │ │ │ │ ├── assert-defined.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── is-defined.ts │ │ │ │ │ └── safe-assign.ts │ │ │ └── shell │ │ │ │ ├── header │ │ │ │ ├── header.component.html │ │ │ │ ├── header.component.scss │ │ │ │ └── header.component.ts │ │ │ │ ├── home.component.ts │ │ │ │ ├── services │ │ │ │ ├── error-handler.service.ts │ │ │ │ └── user-loader.guard.ts │ │ │ │ └── sidemenu │ │ │ │ ├── sidemenu.component.html │ │ │ │ ├── sidemenu.component.scss │ │ │ │ └── sidemenu.component.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── copenhagen.jpg │ │ │ ├── darmstadt-small.jpg │ │ │ ├── darmstadt.jpg │ │ │ ├── detroit.jpg │ │ │ ├── firenze.jpg │ │ │ ├── granada.jpg │ │ │ ├── large-bg.jpg │ │ │ ├── logo.png │ │ │ ├── london.jpg │ │ │ ├── luebeck.jpg │ │ │ ├── reykjavík.jpg │ │ │ ├── shanghai.jpg │ │ │ ├── vienna-small.jpg │ │ │ └── vienna.jpg │ │ ├── environments │ │ │ ├── environment.development.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ └── styles.scss │ ├── tailwind.config.js │ ├── tests │ │ ├── actual │ │ │ └── .gitkeep │ │ ├── auto-tagging.config.ts │ │ ├── customer-api-re-exports.index.ts │ │ ├── customer.dependency-rule.component.ts │ │ ├── customers-container.deep-import.component.ts │ │ ├── dynamic-import-sheriff.config.ts │ │ ├── empty-sheriff.config.ts │ │ ├── expected │ │ │ ├── auto-tagging-lint.json │ │ │ ├── cli-export.txt │ │ │ ├── cli-list.txt │ │ │ ├── cli-verify-failure.txt │ │ │ ├── cli-verify-success.txt │ │ │ ├── dependency-rule-lint.json │ │ │ ├── dynamic-import-lint.json │ │ │ ├── encapsulation-lint.json │ │ │ ├── re-exports-lint.json │ │ │ └── user-error-lint.json │ │ └── sheriff.config-failure.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── yarn.lock ├── nextjs-i │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── eslint.config.mjs │ ├── next.config.ts │ ├── package.json │ ├── pnpm-lock.yaml │ ├── postcss.config.mjs │ ├── public │ │ ├── file.svg │ │ ├── globe.svg │ │ ├── next.svg │ │ ├── vercel.svg │ │ └── window.svg │ ├── shared │ │ └── ui │ │ │ ├── icon-link.tsx │ │ │ ├── icon-link.variants.ts │ │ │ └── index.ts │ ├── shell │ │ ├── footer.tsx │ │ └── index.ts │ └── tsconfig.json ├── remove-paths.mjs ├── tsconfig.json └── typescript-i │ ├── .gitignore │ ├── configs │ ├── .eslintrc.json │ └── eslint.config.js │ ├── integration-test.sh │ ├── package-lock.json │ ├── package.json │ ├── sheriff.config.ts │ ├── src │ ├── data │ │ ├── credentials.ts │ │ ├── db.service.ts │ │ └── index.ts │ ├── logic │ │ ├── checkout.ts │ │ └── index.ts │ ├── main.ts │ └── web │ │ ├── checkout-controller.ts │ │ └── index.ts │ ├── tests │ └── expected-lint.json │ └── tsconfig.json ├── tools ├── scripts │ └── publish.mjs └── tsconfig.tools.json ├── tsconfig.base.json ├── tsconfig.spec.json ├── video-preview.jpg ├── vitest.config.ci.ts ├── vitest.config.ts └── yarn.lock /.detective/hash: -------------------------------------------------------------------------------- 1 | 687b14f18979daf3cb871b8c11921168672abe82, v1.2.1 -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = 120 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # Nx 18 enables using plugins to infer targets by default 2 | # This is disabled for existing workspaces to maintain compatibility 3 | # For more info, see: https://nx.dev/concepts/inferred-tasks 4 | NX_ADD_PLUGINS=false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: Asking for help, report a bug, or request a feature 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | If you have a question or need to report a bug, here are a few ways you can help us: 11 | 12 | - Try reproducing the issue in **StackBlitz**. You can use our starter project here: 13 | [Sheriff StackBlitz Starter](https://stackblitz.com/github/softarc-consulting/sheriff-stackblitz-starter). 14 | - If the issue is related to your setup and you're allowed to share your application's structure (only import statements and filenames), please attach your `sheriff.config.ts` along with the output of: 15 | 16 | ```sh 17 | sheriff export 18 | ``` 19 | This will help us diagnose and resolve issues more efficiently. 🚀 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: '18' 17 | cache: 'yarn' 18 | - run: npm install -g yarn yalc 19 | - run: yarn 20 | - run: yarn lint:all 21 | - run: yarn link:sheriff 22 | - run: yarn test:ci 23 | - run: cd test-projects/angular-i && yarn && npx ng lint 24 | - run: cd test-projects/angular-ii && yarn && npx ng lint 25 | - run: cd test-projects/angular-iii && yarn && npx ng lint 26 | - run: ./run-integration-tests.sh 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | dist 5 | tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | .nx 42 | .yalc 43 | yalc.lock 44 | .test-projects 45 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run lint:all 5 | npx --no -- commitlint --edit ${1} 6 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx vitest run 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | 6 | /.nx/cache 7 | /.nx/workspace-data -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "dbaeumer.vscode-eslint", 6 | "firsttris.vscode-jest-runner" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'scope-enum': [ 5 | 2, 6 | 'optional', 7 | ['core', 'eslint-plugin', 'docs', 'test-projects'], 8 | ], 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 1rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | color: white; 12 | background: #2d77bb; 13 | } 14 | 15 | .header { 16 | margin: 0; 17 | } 18 | 19 | .logo { 20 | max-width: 250px; 21 | } 22 | 23 | @media screen and (max-width: 996px) { 24 | .heroBanner { 25 | padding: 2rem; 26 | } 27 | } 28 | 29 | .buttons { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | } 34 | 35 | .getStarted { 36 | text-align: center; 37 | margin-bottom: 2em; 38 | } 39 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/dependency-rules-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/dependency-rules-1.png -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/logo.png -------------------------------------------------------------------------------- /docs/static/img/module-boundaries-barrel-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/module-boundaries-barrel-file.png -------------------------------------------------------------------------------- /docs/static/img/module-boundaries-barrel-invalid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/module-boundaries-barrel-invalid.png -------------------------------------------------------------------------------- /docs/static/img/module-boundaries-barrel-less-invalid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/module-boundaries-barrel-less-invalid.png -------------------------------------------------------------------------------- /docs/static/img/module-boundaries-barrel-less-valid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/module-boundaries-barrel-less-valid.png -------------------------------------------------------------------------------- /docs/static/img/module-boundaries-barrel-valid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/module-boundaries-barrel-valid.png -------------------------------------------------------------------------------- /docs/static/img/simplicity.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/simplicity.ai -------------------------------------------------------------------------------- /docs/static/img/social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/docs/static/img/social-card.png -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/logo.png -------------------------------------------------------------------------------- /packages/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/.gitkeep -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | Sheriff enforces module boundaries and dependency rules in TypeScript. 6 | 7 | This is the core package. You should download it together with the eslint-plugin. 8 | 9 | For more information, please go to https://github.com/softarc-consulting/sheriff. 10 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@softarc/sheriff-core", 3 | "version": "0.18.0", 4 | "homepage": "https://github.com/softarc-consulting/sheriff", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Rainer Hahnekamp", 8 | "email": "rainer.hahnekamp@angulararchitects.io", 9 | "url": "https://www.angulararchitects.io/" 10 | }, 11 | "bin": { 12 | "sheriff": "./src/bin/main.js" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/softarc-consulting/sheriff" 17 | }, 18 | "type": "commonjs", 19 | "peerDependencies": { 20 | "typescript": ">=4.8" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/bin/main.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * All processing, except accessing the arguments from CLI 5 | * is done by `main()`. 6 | * 7 | * This is due to better testability. 8 | */ 9 | 10 | import { main } from '../lib/cli/main'; 11 | 12 | main.apply(this, process.argv.slice(2)); 13 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { violatesEncapsulationRule } from './lib/eslint/violates-encapsulation-rule'; 2 | export { violatesDependencyRule } from './lib/eslint/violates-dependency-rule'; 3 | export { anyTag } from './lib/checks/any-tag'; 4 | export { sameTag } from './lib/checks/same-tag'; 5 | export { noDependencies } from './lib/checks/no-dependencies'; 6 | export { UserSheriffConfig as SheriffConfig } from './lib/config/user-sheriff-config'; 7 | export { UserError } from './lib/error/user-error'; 8 | export { getProjectData } from './lib/api/get-project-data'; 9 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/any-tag.ts: -------------------------------------------------------------------------------- 1 | import { RuleMatcherFn } from '../config/dependency-rules-config'; 2 | 3 | /** 4 | * access all modules 5 | */ 6 | export const anyTag: RuleMatcherFn = () => true; 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/check-for-circular.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * We use the BSF algorithm to check for circular dependencies. 3 | * ESLint also uses it https://github.com/import-js/eslint-plugin-import/blob/main/src/rules/no-cycle.js 4 | * It is well described on https://en.wikipedia.org/wiki/Breadth-first_search 5 | */ 6 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/get-afi.ts: -------------------------------------------------------------------------------- 1 | import { FsPath } from '../file-info/fs-path'; 2 | import throwIfNull from '../util/throw-if-null'; 3 | import { FileInfo } from '../modules/file.info'; 4 | 5 | export function getAfi( 6 | path: FsPath, 7 | assignedFileInfoMap: Map, 8 | ) { 9 | return throwIfNull( 10 | assignedFileInfoMap.get(path), 11 | `cannot find AssignedFileInfo for ${path}`, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/no-dependencies.ts: -------------------------------------------------------------------------------- 1 | import { RuleMatcherFn } from '../config/dependency-rules-config'; 2 | 3 | /** 4 | * Restrict access to any other module 5 | */ 6 | export const noDependencies: RuleMatcherFn[] = []; 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/same-tag.ts: -------------------------------------------------------------------------------- 1 | import { RuleMatcherFn } from '../config/dependency-rules-config'; 2 | 3 | /** 4 | * Useful for wildcard rules. 5 | * 6 | * Instead of 7 | * ```typescript 8 | * { 9 | * depRules: { 10 | * 'domain:customers': 'domain:customers', 11 | * 'domain:holidays': 'domain:holidays', 12 | * 'domain:accuonting': 'domain:accounting', 13 | * } 14 | * } 15 | * ``` 16 | * 17 | * use `sameTag`: 18 | * 19 | * ```typescript 20 | * { 21 | * depRules: { 22 | * 'domain:*': sameTag 23 | * } 24 | * } 25 | * ``` 26 | */ 27 | export const sameTag: RuleMatcherFn = ({ from, to }) => from === to; 28 | -------------------------------------------------------------------------------- /packages/core/src/lib/checks/unused-files.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/checks/unused-files.ts -------------------------------------------------------------------------------- /packages/core/src/lib/cli/cli.ts: -------------------------------------------------------------------------------- 1 | export const cli = { 2 | endProcessOk: () => process.exit(0), 3 | endProcessError: () => process.exit(1), 4 | log: (message: string) => console.log(message), 5 | logError: (message: string) => console.error(message), 6 | bold: (text: string) => `\u001b[1m${text}\u001b[0m`, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/export-data.ts: -------------------------------------------------------------------------------- 1 | import { getEntryFromCliOrConfig } from './internal/get-entry-from-cli-or-config'; 2 | import { cli } from './cli'; 3 | import { getProjectData } from '../api/get-project-data'; 4 | import getFs from '../fs/getFs'; 5 | 6 | export function exportData(...args: string[]): void { 7 | const fs = getFs(); 8 | const entryFile = getEntryFromCliOrConfig(args[0], false); 9 | 10 | const data = getProjectData(entryFile, fs.cwd(), { includeExternalLibraries: true }); 11 | cli.log(JSON.stringify(data, null, ' ')); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/internal/handle-error.ts: -------------------------------------------------------------------------------- 1 | import { UserError } from '../../error/user-error'; 2 | import { cli } from '../cli'; 3 | 4 | /** 5 | * Catches Error for the CLI and prints `UserError` in 6 | * UI-friendly way. Everything else as it is. 7 | * 8 | * @param fn which should be 'error-handled' 9 | */ 10 | export function handleError(fn: () => void) { 11 | try { 12 | fn(); 13 | cli.endProcessOk(); 14 | } catch (error) { 15 | if (error instanceof UserError) { 16 | cli.logError(error.message); 17 | } else if (error instanceof Error) { 18 | cli.logError(error.message); 19 | } else { 20 | cli.logError(String(error)); 21 | } 22 | cli.endProcessError(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/internal/log-info-for-missing-sheriff-config.ts: -------------------------------------------------------------------------------- 1 | import { ProjectInfo } from '../../main/init'; 2 | import { cli } from '../cli'; 3 | 4 | export function logInfoForMissingSheriffConfig(projectInfo: ProjectInfo) { 5 | if (projectInfo.config.isConfigFileMissing) { 6 | cli.log( 7 | 'Default settings applied. For more control, run "npx sheriff init" to create a sheriff.config.ts.', 8 | ); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/tests/main.spec.ts: -------------------------------------------------------------------------------- 1 | import { it, expect } from 'vitest'; 2 | import { mockCli } from './helpers/mock-cli'; 3 | import { main } from '../main'; 4 | import {version} from '../../../../package.json'; 5 | 6 | it('should include the version in main', () => { 7 | const { allLogs } = mockCli(); 8 | main(); 9 | expect(allLogs()).toContain(`Sheriff (${version})`); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/tests/version.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, it } from 'vitest'; 2 | import { mockCli } from './helpers/mock-cli'; 3 | import { main } from '../main'; 4 | import { version } from '../../../../package.json'; 5 | 6 | it('should print out the current version according to the package.json', () => { 7 | const { allLogs } = mockCli(); 8 | main('version'); 9 | 10 | expect(allLogs()).toBe(version); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/core/src/lib/cli/version.ts: -------------------------------------------------------------------------------- 1 | import { cli } from '../cli/cli'; 2 | import { version as packageVersion } from '../../../package.json'; 3 | 4 | /** 5 | * Although the version is already shown in the CLI, 6 | * we still provide a separate command to simplify 7 | * certain CI runs, scripts which need to get the 8 | * version. 9 | */ 10 | export function version() { 11 | cli.log(packageVersion); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/config/configuration.ts: -------------------------------------------------------------------------------- 1 | import { UserSheriffConfig } from './user-sheriff-config'; 2 | 3 | export type Configuration = Required< 4 | Omit< 5 | UserSheriffConfig, 6 | | 'tagging' 7 | | 'showWarningOnBarrelCollision' 8 | | 'encapsulatedFolderNameForBarrelLess' 9 | > 10 | > & { 11 | // dependency rules will skip if `isConfigFileMissing` is true 12 | isConfigFileMissing: boolean; 13 | }; 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/config/default-config.ts: -------------------------------------------------------------------------------- 1 | import { Configuration } from './configuration'; 2 | 3 | export const defaultConfig: Configuration = { 4 | version: 1, 5 | autoTagging: true, 6 | modules: {}, 7 | depRules: {}, 8 | excludeRoot: false, 9 | enableBarrelLess: false, 10 | encapsulationPattern: 'internal', 11 | log: false, 12 | entryFile: '', 13 | isConfigFileMissing: false, 14 | barrelFileName: 'index.ts' 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/lib/config/dependency-rules-config.ts: -------------------------------------------------------------------------------- 1 | import { FsPath } from '../file-info/fs-path'; 2 | 3 | export interface DependencyCheckContext { 4 | fromModulePath: FsPath; 5 | toModulePath: FsPath; 6 | fromFilePath: FsPath; 7 | toFilePath: FsPath; 8 | } 9 | 10 | export type RuleMatcherFn = ( 11 | context: { 12 | from: string; 13 | to: string; 14 | } & DependencyCheckContext, 15 | ) => boolean; 16 | export type RuleMatcher = string | null | RuleMatcherFn; 17 | export type DependencyRulesConfig = Record; 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/config/find-config.ts: -------------------------------------------------------------------------------- 1 | import getFs from '../fs/getFs'; 2 | import { FsPath } from '../file-info/fs-path'; 3 | 4 | export const findConfig = (rootDir: FsPath): FsPath | undefined => { 5 | const fs = getFs(); 6 | const configFilePath = fs.join(rootDir, 'sheriff.config.ts'); 7 | if (fs.exists(configFilePath)) { 8 | return configFilePath; 9 | } 10 | 11 | return undefined; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/config/tests/__snapshots__/parse-config.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`parse Config > should read value 1`] = ` 4 | ""use strict"; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.a = void 0; 7 | exports.a = 1; 8 | " 9 | `; 10 | -------------------------------------------------------------------------------- /packages/core/src/lib/extend.extensions.d.ts: -------------------------------------------------------------------------------- 1 | import { UserError } from '@softarc/sheriff-core'; 2 | 3 | declare module 'vitest' { 4 | interface Assertion { 5 | 6 | toBeVfsFile(expected: string): T; 7 | 8 | toBeVfsFiles(expected: string[]): T; 9 | 10 | toThrowUserError(userError: UserError): T; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/fix-path-separators.ts: -------------------------------------------------------------------------------- 1 | import { FsPath, toFsPath } from './fs-path'; 2 | import getFs from '../fs/getFs'; 3 | 4 | /** 5 | * Ensures that `FsPath` uses the separator from the OS and not always '/' 6 | * 7 | * @param path 8 | */ 9 | export function fixPathSeparators(path: string): FsPath { 10 | const fs = getFs(); 11 | 12 | if (fs.pathSeparator !== '/') { 13 | return toFsPath(path.replace(/\//g, fs.pathSeparator)); 14 | } 15 | 16 | return toFsPath(path); 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/format-file-info.ts: -------------------------------------------------------------------------------- 1 | import { UnassignedFileInfo } from './unassigned-file-info'; 2 | import traverseUnassignedFileInfo from './traverse-unassigned-file-info'; 3 | import { EOL } from 'os'; 4 | 5 | export const formatFileInfo = ( 6 | fileInfo: UnassignedFileInfo, 7 | indent = 2, 8 | ): string => { 9 | const output: string[] = []; 10 | for (const entry of traverseUnassignedFileInfo(fileInfo)) { 11 | const prefix = ' '.repeat(indent * entry.level); 12 | output.push(`${prefix}${entry.fileInfo.path}`); 13 | } 14 | 15 | return output.join(EOL); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/tests/resolve-potential-ts-path.spec.ts: -------------------------------------------------------------------------------- 1 | import { ResolveFn } from '../traverse-filesystem'; 2 | import { resolvePotentialTsPath } from '../resolve-potential-ts-path'; 3 | import { FsPath } from '../fs-path'; 4 | import { it, expect } from 'vitest'; 5 | 6 | it('should return undefined if TS resolving does not work', () => { 7 | const resolveFn: ResolveFn = () => ({ 8 | resolvedModule: undefined, 9 | }); 10 | 11 | expect( 12 | resolvePotentialTsPath( 13 | '@customers', 14 | { 15 | '@customers': '/project/src/app/customers/index.ts' as FsPath, 16 | }, 17 | resolveFn, 18 | ), 19 | ).toBeUndefined(); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/traverse-unassigned-file-info.ts: -------------------------------------------------------------------------------- 1 | import { UnassignedFileInfo } from './unassigned-file-info'; 2 | 3 | export function* traverseUnassignedFileInfo(fileInfo: UnassignedFileInfo) { 4 | const traversed = new Set(); 5 | 6 | function* traverse( 7 | fileInfo: UnassignedFileInfo, 8 | level = 1, 9 | ): Generator<{ fileInfo: UnassignedFileInfo; level: number }, void> { 10 | if (traversed.has(fileInfo.path)) { 11 | return; 12 | } 13 | 14 | traversed.add(fileInfo.path); 15 | yield { fileInfo, level }; 16 | 17 | for (const child of fileInfo.imports) { 18 | yield* traverse(child, level + 1); 19 | } 20 | } 21 | 22 | yield* traverse(fileInfo); 23 | } 24 | 25 | export default traverseUnassignedFileInfo; 26 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/ts-config.ts: -------------------------------------------------------------------------------- 1 | import { CompilerOptions } from 'typescript'; 2 | 3 | export interface TsConfig { 4 | compilerOptions?: CompilerOptions; 5 | extends?: string; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/file-info/ts-data.ts: -------------------------------------------------------------------------------- 1 | import type * as ts from 'typescript'; 2 | import { FsPath } from './fs-path'; 3 | import { TsConfigContext } from "./get-ts-config-context"; 4 | 5 | export type TsPaths = Record; 6 | 7 | /** 8 | * Contains data needed by `traverseFilesystem`. 9 | */ 10 | export type TsData = TsConfigContext & { 11 | configObject: ReturnType; 12 | cwd: string; 13 | sys: typeof ts.sys; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test1/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test1/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test2/customers/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test2/customers/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test3/admin/booking/data/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test3/admin/booking/data/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test3/admin/booking/feature/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test3/admin/booking/feature/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test3/customers/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test3/customers/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test3/holidays/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test3/holidays/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-files/test4/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-files/test4/.gitkeep -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test1/customers/admin/core/feature/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test1/customers/tsconfig.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test1/customers/tsconfig.json -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test2/customers/admin/core/feature/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test2/customers/admin/core/tsconfig.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test2/customers/admin/core/tsconfig.json -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test2/customers/tsconfig.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test2/customers/tsconfig.json -------------------------------------------------------------------------------- /packages/core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/packages/core/src/lib/fs/find-nearest/test3/customers/admin/core/feature/index.ts -------------------------------------------------------------------------------- /packages/core/src/lib/fs/getFs.ts: -------------------------------------------------------------------------------- 1 | import defaultFs from './default-fs'; 2 | import virtualFs from './virtual-fs'; 3 | import { Fs } from './fs'; 4 | 5 | let fsImplementation: 'default' | 'virtual' = 'default'; 6 | 7 | export function useDefaultFs() { 8 | fsImplementation = 'default'; 9 | return defaultFs 10 | } 11 | 12 | export function useVirtualFs() { 13 | fsImplementation = 'virtual' 14 | return virtualFs; 15 | } 16 | 17 | const getFs = (): Fs => 18 | fsImplementation === 'default' ? defaultFs : virtualFs; 19 | 20 | export const isFsVirtualised = () => fsImplementation === 'virtual'; 21 | 22 | export default getFs; 23 | -------------------------------------------------------------------------------- /packages/core/src/lib/log/index.ts: -------------------------------------------------------------------------------- 1 | export { logger } from './logger'; 2 | -------------------------------------------------------------------------------- /packages/core/src/lib/log/log-level.ts: -------------------------------------------------------------------------------- 1 | export type LogLevel = 'info' | 'debug' | 'warn'; 2 | -------------------------------------------------------------------------------- /packages/core/src/lib/log/logger.ts: -------------------------------------------------------------------------------- 1 | import { log } from './log'; 2 | 3 | export function logger(scope: string) { 4 | return { 5 | info(message: string) { 6 | log(message, scope, 'info'); 7 | }, 8 | debug(message: string) { 9 | log(message, scope, 'debug'); 10 | }, 11 | level(message: string) { 12 | log(message, scope, 'warn'); 13 | }, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/lib/main/after-init.ts: -------------------------------------------------------------------------------- 1 | import { Configuration } from '../config/configuration'; 2 | import { initialized } from './internal/initialized'; 3 | import { callbacks } from './internal/callback'; 4 | 5 | export function afterInit( 6 | callback: (config: Configuration | undefined) => void, 7 | ) { 8 | if (initialized.status) { 9 | callback(initialized.config); 10 | } else { 11 | callbacks.push(callback); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/main/callback.ts: -------------------------------------------------------------------------------- 1 | import { Configuration } from '../config/configuration'; 2 | 3 | export type Callback = (config: Configuration | undefined) => void; 4 | -------------------------------------------------------------------------------- /packages/core/src/lib/main/internal/callback.ts: -------------------------------------------------------------------------------- 1 | import { Callback } from '../callback'; 2 | 3 | export const callbacks: Callback[] = []; 4 | -------------------------------------------------------------------------------- /packages/core/src/lib/main/internal/initialized.ts: -------------------------------------------------------------------------------- 1 | import { Configuration } from '../../config/configuration'; 2 | 3 | export const initialized: { 4 | status: boolean; 5 | config: Configuration | undefined; 6 | } = { status: false, config: undefined }; 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/modules/fill-file-info-map.ts: -------------------------------------------------------------------------------- 1 | import { Module } from './module'; 2 | import { FsPath } from '../file-info/fs-path'; 3 | import { FileInfo } from './file.info'; 4 | import { logger } from '../log'; 5 | 6 | const log = logger('core.modules.assigned-file-info-map'); 7 | 8 | export const fillFileInfoMap = ( 9 | fileInfoMap: Map, 10 | moduleInfos: Module[], 11 | ): void => { 12 | for (const moduleInfo of moduleInfos) { 13 | for (const assignedFileInfo of moduleInfo.fileInfos) { 14 | fileInfoMap.set(assignedFileInfo.path, assignedFileInfo); 15 | } 16 | } 17 | 18 | log.info(Array.from(fileInfoMap).join(', ')); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/core/src/lib/modules/format-modules.ts: -------------------------------------------------------------------------------- 1 | import { Module } from './module'; 2 | import { EOL } from 'os'; 3 | 4 | export const formatModules = (modules: Module[]): string => { 5 | const output: string[] = []; 6 | for (const module of modules) { 7 | output.push( 8 | `${module.path}: ${module.fileInfos.map((afi) => afi.path).join(', ')}`, 9 | ); 10 | } 11 | return output.join(EOL); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/modules/internal/find-module-paths-with-barrel.ts: -------------------------------------------------------------------------------- 1 | import { FsPath, toFsPath } from '../../file-info/fs-path'; 2 | import getFs from '../../fs/getFs'; 3 | 4 | export function findModulePathsWithBarrel( 5 | projectDirs: FsPath[], 6 | barrelFileName: string, 7 | ): FsPath[] { 8 | return projectDirs.flatMap((projectDir) => 9 | getFs() 10 | .findFiles(projectDir, barrelFileName) 11 | .map((path) => path.slice(0, -(barrelFileName.length + 1))) 12 | .map(toFsPath), 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/lib/modules/traverse-file-info.ts: -------------------------------------------------------------------------------- 1 | import { FileInfo } from './file.info'; 2 | 3 | export function* traverseFileInfo(fileInfo: FileInfo) { 4 | const traversed = new Set(); 5 | 6 | function* traverse( 7 | fileInfo: FileInfo, 8 | level = 1, 9 | ): Generator<{ fileInfo: FileInfo; level: number }, void> { 10 | if (traversed.has(fileInfo.path)) { 11 | return; 12 | } 13 | 14 | traversed.add(fileInfo.path); 15 | yield { fileInfo, level }; 16 | 17 | for (const child of fileInfo.imports) { 18 | yield* traverse(child, level + 1); 19 | } 20 | } 21 | 22 | yield* traverse(fileInfo); 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/lib/test/find-assigned-file-info.ts: -------------------------------------------------------------------------------- 1 | import { FileInfo } from '../modules/file.info'; 2 | import { Module } from '../modules/module'; 3 | 4 | export const findAssignedFileInfo = ( 5 | moduleInfos: Module[], 6 | path: string, 7 | ): FileInfo => { 8 | for (const moduleInfo of moduleInfos) { 9 | for (const assignedFileInfo of moduleInfo.fileInfos) { 10 | if (assignedFileInfo.path === path) { 11 | return assignedFileInfo; 12 | } 13 | } 14 | } 15 | 16 | throw new Error(`cannot find AssignedFileInfo of ${path}`); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/test/find-file-info.ts: -------------------------------------------------------------------------------- 1 | import { UnassignedFileInfo} from '../file-info/unassigned-file-info'; 2 | import traverseUnassignedFileInfo from '../file-info/traverse-unassigned-file-info'; 3 | 4 | export default ( 5 | fi: UnassignedFileInfo, 6 | path: string, 7 | ): UnassignedFileInfo | undefined => { 8 | for (const { fileInfo } of traverseUnassignedFileInfo(fi)) { 9 | if (fileInfo.path === path) { 10 | return fileInfo; 11 | } 12 | } 13 | 14 | return undefined; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/lib/test/in-vfs.ts: -------------------------------------------------------------------------------- 1 | import { FsPath, toFsPath } from '../file-info/fs-path'; 2 | import getFs from '../fs/getFs'; 3 | 4 | export const inVfs = (path: string): FsPath => { 5 | return toFsPath(getFs().join('/project', path)); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/assert-not-null.ts: -------------------------------------------------------------------------------- 1 | export function assertNotNull( 2 | value: T, 3 | prefix = '', 4 | ): asserts value is NonNullable { 5 | if (value === undefined || value === null) { 6 | throw new Error(`${prefix} value cannot be null`); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/get.ts: -------------------------------------------------------------------------------- 1 | import throwIfNull from './throw-if-null'; 2 | 3 | export default ( 4 | map: Map, 5 | key: Key, 6 | ): Value => throwIfNull(map.get(key), `${key} does not exist in passed Map`); 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/throw-if-null.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | import throwIfNull from './throw-if-null'; 3 | 4 | describe('throw if null', () => { 5 | const colours = new Map([ 6 | ['red', 'f00'], 7 | ['green', '0f0'], 8 | ['blue', '00f'], 9 | ['black', ''], 10 | ['virtual', null], 11 | ]); 12 | it('should throw an error', () => { 13 | expect(() => 14 | throwIfNull(colours.get('yellow'), 'yellow does not exist'), 15 | ).toThrow('yellow does not exist'); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/throw-if-null.ts: -------------------------------------------------------------------------------- 1 | export default (value: T, prefix = ''): NonNullable => { 2 | if (value === undefined || value === null) { 3 | throw new Error(`${prefix} value cannot be null`); 4 | } 5 | 6 | return value; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/typed-object-functions.ts: -------------------------------------------------------------------------------- 1 | type PropertyKey = string | number | symbol; 2 | 3 | export function keys(value: Record): Key[] { 4 | return Object.keys(value) as Key[]; 5 | } 6 | 7 | export function entries(value: Record): [Key, Value][] { 8 | return Object.entries(value) as [Key, Value][]; 9 | } 10 | 11 | export function fromEntries( 12 | entries: Iterable<[Key, Value]>, 13 | ): Record { 14 | return Object.fromEntries(entries) as Record; 15 | } 16 | 17 | export function values(value: Record): Value[] { 18 | return Object.values(value); 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/wildcard-to-regex.ts: -------------------------------------------------------------------------------- 1 | // https://www.delftstack.com/howto/javascript/wildcard-string-comparison-in-javascript 2 | 3 | export const wildcardToRegex = (wildcardRule: string): RegExp => { 4 | const escapeRegex = (str: string) => 5 | str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'); 6 | const regexpString = `^${wildcardRule 7 | .split('*') 8 | .map(escapeRegex) 9 | .join('.*')}$`; 10 | return new RegExp(regexpString); 11 | }; 12 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, { 18 | "path": "./tsconfig.spec.json" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "resolveJsonModule": true, 7 | "esModuleInterop": true, 8 | "types": ["node"] 9 | }, 10 | "include": ["src/**/*.ts"], 11 | "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["vitest", "node"], 7 | "resolveJsonModule": true, 8 | "esModuleInterop": true 9 | }, 10 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/eslint-plugin/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | Sheriff enforces module boundaries and dependency rules in TypeScript. 6 | 7 | This is the package for ESLint. You should download it together with the core package. 8 | 9 | For more information, please go to https://github.com/softarc-consulting/sheriff. 10 | -------------------------------------------------------------------------------- /packages/eslint-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@softarc/eslint-plugin-sheriff", 3 | "version": "0.18.0", 4 | "homepage": "https://github.com/softarc-consulting/sheriff", 5 | "license": "MIT", 6 | "author": { 7 | "name": "Rainer Hahnekamp", 8 | "email": "rainer.hahnekamp@angulararchitects.io", 9 | "url": "https://www.angulararchitects.io/" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/softarc-consulting/sheriff" 14 | }, 15 | "type": "commonjs", 16 | "dependencies": {}, 17 | "peerDependencies": { 18 | "@softarc/sheriff-core": "0.18.0", 19 | "eslint": "^8.0.0 || ^9.0.0", 20 | "@typescript-eslint/utils": "^5.48.2 || ^6.0.0 || ^7.0.0 || ^8.0.0-alpha.20" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import rules from './lib/rules'; 2 | import { legacy, legacyBarrelModulesOnly } from "./lib/configs/legacy"; 3 | import { all, barrelModulesOnly } from "./lib/configs/all"; 4 | import { 5 | name as packageName, 6 | version as packageVersion, 7 | } from '../package.json'; 8 | 9 | const meta = { name: packageName, version: packageVersion }; 10 | 11 | const configs = { 12 | legacy, 13 | legacyBarrelModulesOnly, 14 | barrelModulesOnly, 15 | all 16 | }; 17 | 18 | export {configs, rules, meta}; 19 | 20 | export default { configs, rules, meta }; 21 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/configs/legacy.ts: -------------------------------------------------------------------------------- 1 | import { ESLint } from 'eslint'; 2 | 3 | export const legacyBarrelModulesOnly: ESLint.ConfigData = { 4 | parser: '@typescript-eslint/parser', 5 | plugins: ['@softarc/sheriff'], 6 | rules: { 7 | '@softarc/sheriff/dependency-rule': 'error', 8 | '@softarc/sheriff/deep-import': 'error', 9 | }, 10 | }; 11 | 12 | export const legacy: ESLint.ConfigData = { 13 | parser: '@typescript-eslint/parser', 14 | plugins: ['@softarc/sheriff'], 15 | rules: { 16 | '@softarc/sheriff/dependency-rule': 'error', 17 | '@softarc/sheriff/encapsulation': 'error', 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/rules/deep-import.ts: -------------------------------------------------------------------------------- 1 | import { violatesEncapsulationRule } from '@softarc/sheriff-core'; 2 | import { createRule } from './create-rule'; 3 | 4 | export const deepImport = createRule( 5 | 'Deep Import', 6 | (context, node, isFirstRun, filename, sourceCode) => { 7 | const importValue = (node.source as { value: string }).value; 8 | const message = violatesEncapsulationRule( 9 | filename, 10 | importValue, 11 | isFirstRun, 12 | sourceCode, 13 | true 14 | ); 15 | if (message) { 16 | context.report({ 17 | message, 18 | node, 19 | }); 20 | } 21 | }, 22 | ); 23 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/rules/dependency-rule.ts: -------------------------------------------------------------------------------- 1 | import { violatesDependencyRule } from '@softarc/sheriff-core'; 2 | import { createRule } from './create-rule'; 3 | 4 | export const dependencyRule = createRule( 5 | 'Dependency Rule', 6 | (context, node, isFirstRun, filename, sourceCode) => { 7 | const importValue = (node.source as { value: string }).value; 8 | const message = violatesDependencyRule( 9 | filename, 10 | importValue, 11 | isFirstRun, 12 | sourceCode, 13 | ); 14 | if (message) { 15 | context.report({ 16 | message, 17 | node, 18 | }); 19 | } 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/rules/encapsulation.ts: -------------------------------------------------------------------------------- 1 | import { violatesEncapsulationRule } from '@softarc/sheriff-core'; 2 | import { createRule } from './create-rule'; 3 | 4 | export const encapsulation = createRule( 5 | 'Encapsulation', 6 | (context, node, isFirstRun, filename, sourceCode) => { 7 | const importValue = (node.source as { value: string }).value; 8 | const message = violatesEncapsulationRule( 9 | filename, 10 | importValue, 11 | isFirstRun, 12 | sourceCode, 13 | false 14 | ); 15 | if (message) { 16 | context.report({ 17 | message, 18 | node, 19 | }); 20 | } 21 | }, 22 | ); 23 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/rules/executor.ts: -------------------------------------------------------------------------------- 1 | import { Rule } from 'eslint'; 2 | import {ExportAllDeclaration, ExportNamedDeclaration, ImportDeclaration, ImportExpression} from 'estree'; 3 | 4 | export type ExecutorNode = ImportExpression | ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration; 5 | 6 | export type Executor = ( 7 | context: Rule.RuleContext, 8 | node: ExecutorNode, 9 | isFirstRun: boolean, 10 | filename: string, 11 | sourceCode: string, 12 | ) => void; 13 | -------------------------------------------------------------------------------- /packages/eslint-plugin/src/lib/rules/index.ts: -------------------------------------------------------------------------------- 1 | import { deepImport } from './deep-import'; 2 | import { dependencyRule } from './dependency-rule'; 3 | import { encapsulation } from "./encapsulation"; 4 | 5 | export default { 6 | 'deep-import': deepImport, 7 | 'dependency-rule': dependencyRule, 8 | 'encapsulation': encapsulation, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/eslint-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /packages/eslint-plugin/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "resolveJsonModule": true, 7 | "esModuleInterop": true, 8 | "types": ["node"] 9 | }, 10 | "include": ["src/**/*.ts"], 11 | "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/eslint-plugin/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["vitest", "node"] 7 | }, 8 | "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 3 | "release-type": "node", 4 | "packages": { 5 | "packages/core": { 6 | "component": "core" 7 | }, 8 | "packages/eslint-plugin": { 9 | "component": "eslint-plugin" 10 | } 11 | }, 12 | "plugins": [ 13 | { 14 | "type": "linked-versions", 15 | "groupName": "sheriff", 16 | "components": [ 17 | "core", 18 | "eslint-plugin" 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.javascript.lcov.reportPaths=./coverage/lcov.info 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | div.main { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: stretch; 7 | 8 | mat-toolbar { 9 | mat-toolbar-row { 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | } 14 | 15 | mat-drawer-container { 16 | flex: 1 1 auto; 17 | min-height: 25em; 18 | 19 | mat-drawer { 20 | background: #fafafa; 21 | } 22 | 23 | mat-drawer-content { 24 | padding: 1em; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/bookings/+state/bookings.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 2 | import { Booking } from './bookings.reducer'; 3 | 4 | export const bookingsActions = createActionGroup({ 5 | source: 'Customer Bookings', 6 | events: { 7 | Load: emptyProps(), 8 | Loaded: props<{ bookings: Booking[] }>(), 9 | Reset: emptyProps(), 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/bookings/+state/bookings.selectors.ts: -------------------------------------------------------------------------------- 1 | import { bookingsFeature } from './bookings.reducer'; 2 | 3 | const { selectBookings, selectLoaded } = bookingsFeature; 4 | 5 | export const fromBookings = { selectBookings, selectLoaded }; 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/bookings/bookings.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { provideEffects } from '@ngrx/effects'; 3 | import { provideState } from '@ngrx/store'; 4 | import { bookingsFeature } from './+state/bookings.reducer'; 5 | import { OverviewComponent } from './overview/overview.component'; 6 | import { BookingsEffects } from './+state/bookings.effects'; 7 | 8 | export const bookingsRoutes: Routes = [ 9 | { 10 | path: '', 11 | providers: [ 12 | provideState(bookingsFeature), 13 | provideEffects([BookingsEffects]), 14 | ], 15 | component: OverviewComponent, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/bookings/index.ts: -------------------------------------------------------------------------------- 1 | export { bookingsRoutes } from './bookings.routes'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/api/index.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CustomersRepository } from '../data'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class CustomersApi { 6 | repo = inject(CustomersRepository); 7 | get selectedCustomer$() { 8 | return this.repo.selectedCustomer$; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/data/customers.actions.ts: -------------------------------------------------------------------------------- 1 | import { Customer } from '@eternal/customers/model'; 2 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 3 | 4 | export const customersActions = createActionGroup({ 5 | source: 'Customers', 6 | events: { 7 | Load: props<{ page: number }>(), 8 | Loaded: props<{ customers: Customer[]; total: number; page: number }>(), 9 | Add: props<{ customer: Customer }>(), 10 | Added: props<{ customers: Customer[] }>(), 11 | Update: props<{ customer: Customer }>(), 12 | Updated: props<{ customers: Customer[] }>(), 13 | Remove: props<{ customer: Customer }>(), 14 | Removed: props<{ customers: Customer[] }>(), 15 | Select: props<{ id: number }>(), 16 | Unselect: emptyProps(), 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/data/customers.json: -------------------------------------------------------------------------------- 1 | { 2 | "person": { 3 | "id": 1, 4 | "firstname": "Volker", 5 | "lastname":"Gruber" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/data/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomersRepository } from './customers-repository.service'; 2 | export { provideCustomers } from './provide-customers'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/data/provide-customers.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { customersFeature } from './customers.reducer'; 3 | import { provideEffects } from '@ngrx/effects'; 4 | import { CustomersEffects } from './customers.effects'; 5 | import "./customers.json" 6 | 7 | export const provideCustomers = [ 8 | provideState(customersFeature), 9 | provideEffects([CustomersEffects]), 10 | ]; 11 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/feature/components/customers-root/customers-root.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/feature/components/customers-root/customers-root.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterOutlet } from '@angular/router'; 3 | 4 | @Component({ 5 | templateUrl: './customers-root.component.html', 6 | standalone: true, 7 | imports: [RouterOutlet], 8 | }) 9 | export class CustomersRootComponent {} 10 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/feature/index.ts: -------------------------------------------------------------------------------- 1 | export { customersRoutes } from './customers.routes'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/feature/services/data.guard.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CanActivate } from '@angular/router'; 3 | import { CustomersRepository } from '../../data'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class DataGuard implements CanActivate { 9 | #customersRepository = inject(CustomersRepository); 10 | 11 | canActivate(): boolean { 12 | this.#customersRepository.load(1); 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/model/customer.ts: -------------------------------------------------------------------------------- 1 | export interface Customer { 2 | id: number; 3 | firstname: string; 4 | name: string; 5 | country: string; 6 | birthdate: string; 7 | } 8 | 9 | let id = 1; 10 | 11 | export function createCustomer(customer: Partial = {}): Customer { 12 | return { 13 | ...{ 14 | id: id++, 15 | firstname: 'Jessica', 16 | name: 'Trabner', 17 | country: 'AT', 18 | birthdate: '2001-09-02', 19 | }, 20 | ...customer, 21 | }; 22 | } 23 | 24 | export function createCustomers(...customers: Partial[]) { 25 | return customers.map(createCustomer); 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/model/index.ts: -------------------------------------------------------------------------------- 1 | export { Customer, createCustomer, createCustomers } from './customer'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/ui/customer.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { Customer } from '@eternal/customers/model'; 3 | 4 | @Pipe({ 5 | name: 'customer', 6 | standalone: true, 7 | }) 8 | export class CustomerPipe implements PipeTransform { 9 | transform(customer: Customer): string { 10 | if (!customer.name && !customer.firstname) { 11 | return '-'; 12 | } 13 | return `${customer.name}, ${customer.firstname}`; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/ui/customer/customer.component.scss: -------------------------------------------------------------------------------- 1 | form { 2 | width: 20em; 3 | 4 | .buttons { 5 | display: flex; 6 | justify-content: space-evenly; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/customers/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomerComponent } from './customer/customer.component'; 2 | export { 3 | CustomersComponent, 4 | CustomersViewModel, 5 | } from './customers/customers.component'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/feature/+state/holidays.actions.ts: -------------------------------------------------------------------------------- 1 | import { Holiday } from '@eternal/holidays/model'; 2 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 3 | 4 | export const holidaysActions = createActionGroup({ 5 | source: 'Holidays', 6 | events: { 7 | Load: emptyProps(), 8 | Loaded: props<{ holidays: Holiday[] }>(), 9 | 'Add Favourite': props<{ id: number }>(), 10 | 'Favourite Added': props<{ id: number }>(), 11 | 'Remove Favourite': props<{ id: number }>(), 12 | 'Favourite Removed': props<{ id: number }>(), 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/feature/+state/holidays.selectors.ts: -------------------------------------------------------------------------------- 1 | import { holidaysFeature } from './holidays.reducer'; 2 | import { createSelector } from '@ngrx/store'; 3 | 4 | const selectHolidaysWithFavourite = createSelector( 5 | holidaysFeature.selectHolidays, 6 | holidaysFeature.selectFavouriteIds, 7 | (holidays, favouriteIds) => 8 | holidays.map((holiday) => ({ 9 | ...holiday, 10 | isFavourite: favouriteIds.includes(holiday.id), 11 | })) 12 | ); 13 | 14 | export const fromHolidays = { 15 | get: holidaysFeature.selectHolidays, 16 | selectHolidaysWithFavourite, 17 | }; 18 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/feature/address.ts: -------------------------------------------------------------------------------- 1 | export interface Address { 2 | street: string; 3 | streetNumber: string; 4 | zip?: string; 5 | city?: string; 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/feature/index.ts: -------------------------------------------------------------------------------- 1 | export { holidaysRoutes } from './holidays.routes'; 2 | export { AddressLookuper } from './address-lookuper.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/feature/parse-address.ts: -------------------------------------------------------------------------------- 1 | import { Address } from './address'; 2 | 3 | export function parseAddress(query: string): Address { 4 | const shortPattern = /^([\w\s]+)\s(\d+)$/; 5 | const longPattern = /^([\w\s]+)\s(\d+),\s(\d+)\s([\w]+)$/; 6 | let match: string[] | null = query.match(shortPattern); 7 | 8 | if (match) { 9 | const [, street, streetNumber] = match; 10 | return { street, streetNumber }; 11 | } else { 12 | match = query.match(longPattern); 13 | if (match) { 14 | const [, street, streetNumber, zip, city] = match; 15 | return { street, streetNumber, zip, city }; 16 | } 17 | } 18 | 19 | throw new Error('Could not parse address. Invalid format.'); 20 | } 21 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/model/index.ts: -------------------------------------------------------------------------------- 1 | export { Holiday, createHoliday, createHolidays } from './holiday'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/holidays/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { HolidayCardComponent } from './holiday-card/holiday-card.component'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/config/configuration.ts: -------------------------------------------------------------------------------- 1 | export class Configuration { 2 | constructor(public baseUrl: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/config/index.ts: -------------------------------------------------------------------------------- 1 | export { Configuration } from './configuration'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/form/form-errors.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { JsonPipe, NgIf } from '@angular/common'; 4 | import { MatInputModule } from '@angular/material/input'; 5 | 6 | @Component({ 7 | selector: 'eternal-form-errors', 8 | template: ` 9 | This field is mandatory 10 | `, 11 | standalone: true, 12 | imports: [NgIf, JsonPipe, MatInputModule], 13 | }) 14 | export class FormErrorsComponent { 15 | @Input() control: FormControl | undefined; 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/form/index.ts: -------------------------------------------------------------------------------- 1 | export { Options } from './options'; 2 | export { FormErrorsComponent } from './form-errors.component'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/form/options.ts: -------------------------------------------------------------------------------- 1 | export interface Option { 2 | label: string; 3 | value: string; 4 | } 5 | 6 | export type Options = Option[]; 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/http/error-message.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const ERROR_MESSAGE_CONTEXT = new HttpContextToken( 4 | () => 'Sorry, something went wrong on our side.' 5 | ); 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/http/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorInterceptor } from './error.interceptor'; 2 | export { BaseUrlInterceptor } from './base-url.interceptor'; 3 | export { withErrorMessageContext } from './with-error-message-context'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/http/with-error-message-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { ERROR_MESSAGE_CONTEXT } from './error-message.context'; 3 | 4 | export function withErrorMessageContext(message: string) { 5 | return new HttpContext().set(ERROR_MESSAGE_CONTEXT, message); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/master-data/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedMasterDataProvider } from './shared-master-data.provider'; 2 | export { selectCountries, masterFeature } from './+state/master.reducer'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/master-data/shared-master-data.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { masterFeature } from './+state/master.reducer'; 3 | 4 | export const sharedMasterDataProvider = provideState(masterFeature); 5 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/deep-clone.ts: -------------------------------------------------------------------------------- 1 | import { map, Observable } from 'rxjs'; 2 | 3 | export function deepClone(source$: Observable): Observable { 4 | return source$.pipe(map((object) => structuredClone(object))); 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/filter-defined.ts: -------------------------------------------------------------------------------- 1 | import { filter, Observable } from 'rxjs'; 2 | import { isDefined } from '@eternal/shared/util'; 3 | 4 | export function filterDefined( 5 | source$: Observable 6 | ): Observable> { 7 | return source$.pipe(filter(isDefined)); 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { noopAction } from './noop.action'; 2 | export { safeConcatMap } from './safe-concat-map'; 3 | export { filterDefined } from './filter-defined'; 4 | export { deepClone } from './deep-clone'; 5 | export { LoadStatus } from './load-status'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/load-status.ts: -------------------------------------------------------------------------------- 1 | export type LoadStatus = 'not loaded' | 'loading' | 'loaded'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/noop.action.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@ngrx/store'; 2 | 3 | export const noopAction = createAction('[Util] NOOP'); 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ngrx-utils/safe-concat-map.ts: -------------------------------------------------------------------------------- 1 | import { TypedAction } from '@ngrx/store/src/models'; 2 | import { catchError, concatMap, Observable, of, OperatorFunction } from 'rxjs'; 3 | import { noopAction } from './noop.action'; 4 | 5 | export function safeConcatMap( 6 | project: (value: S) => Observable> 7 | ): OperatorFunction> { 8 | return (source$: Observable): Observable> => 9 | source$.pipe( 10 | concatMap((value) => 11 | project(value).pipe(catchError(() => of(noopAction()))) 12 | ) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/security/index.ts: -------------------------------------------------------------------------------- 1 | export { securityProvider } from './security.provider'; 2 | export { SecurityService } from './security.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/security/security.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 2 | import { User } from './security.reducer'; 3 | 4 | export const securityActions = createActionGroup({ 5 | source: 'Security', 6 | events: { 7 | 'Load User': emptyProps(), 8 | 'Load User Success': props<{ user: User }>(), 9 | 'Sign In User': props<{ email: string; password: string }>(), 10 | 'Sign In User Success': props<{ user: User }>(), 11 | 'Sign Out User': emptyProps(), 12 | 'Sign Out User Success': props<{ user: User }>(), 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/security/security.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideEffects } from '@ngrx/effects'; 2 | import { provideState } from '@ngrx/store'; 3 | import { SecurityEffects } from './security.effects'; 4 | import { securityFeature } from './security.reducer'; 5 | 6 | export const securityProvider = [ 7 | provideState(securityFeature), 8 | provideEffects([SecurityEffects]), 9 | ]; 10 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/security/security.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | import { securityFeature } from './security.reducer'; 3 | 4 | const { selectUser, selectLoaded } = securityFeature; 5 | 6 | const selectSignedIn = createSelector( 7 | selectUser, 8 | selectLoaded, 9 | (user, loaded) => loaded && !user?.anonymous 10 | ); 11 | 12 | export const fromSecurity = { 13 | selectUser, 14 | selectLoaded, 15 | selectSignedIn, 16 | }; 17 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/testing/assert-type.ts: -------------------------------------------------------------------------------- 1 | export function assertType(obj: unknown = null): T { 2 | return obj as T; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/testing/index.ts: -------------------------------------------------------------------------------- 1 | export { assertType } from './assert-type'; 2 | export * from './mock-inject'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedUiMessagingProvider } from './shared-ui-messaging.provider'; 2 | export { LoaderComponent } from './loader/loader.component'; 3 | export { LoadingService } from './loader/loading.service'; 4 | export { LoadingInterceptor } from './loader/loading.interceptor'; 5 | export { MessageService } from './message/message.service'; 6 | export { MessageComponent } from './message/message.component'; 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/loader/loader.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe, NgStyle } from '@angular/common'; 2 | import { Component, inject } from '@angular/core'; 3 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 4 | import { LoadingService } from './loading.service'; 5 | 6 | @Component({ 7 | selector: 'eternal-loader', 8 | template: ``, 14 | standalone: true, 15 | imports: [MatProgressBarModule, NgStyle, AsyncPipe], 16 | }) 17 | export class LoaderComponent { 18 | loadingService = inject(LoadingService); 19 | } 20 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/loader/loading.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class LoadingService { 6 | #loading$ = new BehaviorSubject(false); 7 | loading$ = this.#loading$.asObservable(); 8 | 9 | start() { 10 | this.#loading$.next(true); 11 | } 12 | 13 | stop() { 14 | this.#loading$.next(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/loader/silent-load.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const SILENT_LOAD_CONTEXT = new HttpContextToken(() => false); 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/loader/with-silent-load-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { SILENT_LOAD_CONTEXT } from './silent-load.context'; 3 | 4 | export function withSilentLoadContext() { 5 | return new HttpContext().set(SILENT_LOAD_CONTEXT, true); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/message/message.component.html: -------------------------------------------------------------------------------- 1 |
8 | check_circle 9 | warning 10 |

11 | {{ message.text }} 12 |

13 |
14 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/message/message.store.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { Message } from './message'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class MessageStore { 7 | #messages$ = new Subject(); 8 | 9 | messages$ = this.#messages$.asObservable(); 10 | 11 | add(message: Message) { 12 | this.#messages$.next(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/message/message.ts: -------------------------------------------------------------------------------- 1 | export interface Message { 2 | text: string; 3 | type: 'error' | 'info'; 4 | } 5 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui-messaging/shared-ui-messaging.provider.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | import { LoadingInterceptor } from './loader/loading.interceptor'; 3 | import { importProvidersFrom } from '@angular/core'; 4 | import { MatDialogModule } from '@angular/material/dialog'; 5 | 6 | export const sharedUiMessagingProvider = [ 7 | { 8 | provide: HTTP_INTERCEPTORS, 9 | multi: true, 10 | useClass: LoadingInterceptor, 11 | }, 12 | importProvidersFrom(MatDialogModule), 13 | ]; 14 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { BlinkerDirective } from './blinker.directive'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/util/assert-defined.ts: -------------------------------------------------------------------------------- 1 | export function assertDefined(value: T | undefined): asserts value is T { 2 | if (value === undefined) { 3 | throw new Error('value is undefined'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/util/index.ts: -------------------------------------------------------------------------------- 1 | export { assertDefined } from './assert-defined'; 2 | export { isDefined } from './is-defined'; 3 | export { safeAssign } from './safe-assign'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/util/is-defined.ts: -------------------------------------------------------------------------------- 1 | export function isDefined(value: T): value is NonNullable { 2 | return value !== undefined; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shared/util/safe-assign.ts: -------------------------------------------------------------------------------- 1 | export function safeAssign>( 2 | object: T, 3 | changes: Partial = {} 4 | ): void { 5 | Object.assign(object, changes); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shell/header/header.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 100%; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | } 7 | 8 | a.home { 9 | text-decoration: none; 10 | color: inherit; 11 | 12 | h1 { 13 | display: flex; 14 | align-items: center; 15 | 16 | img { 17 | width: 4em; 18 | height: auto; 19 | padding-right: 1em; 20 | } 21 | } 22 | } 23 | 24 | div.security a { 25 | margin: 0 0.5em 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shell/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'eternal-home', 5 | template: `

6 | Eternal is an imaginary travel agency and is used as training application 7 | for Angular developers. 8 |

9 |

10 | You can click around, do whatever you want but don't expect to be able to 11 | book a real holiday 😉. 12 |

`, 13 | standalone: true, 14 | }) 15 | export class HomeComponent {} 16 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shell/services/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler, Injectable, Injector } from '@angular/core'; 2 | import { MessageService } from '@eternal/shared/ui-messaging'; 3 | 4 | @Injectable() 5 | export class ErrorHandlerService implements ErrorHandler { 6 | constructor(private injector: Injector) {} 7 | 8 | handleError(error: unknown): void { 9 | const messageService = this.injector.get(MessageService); 10 | messageService.error('We are sorry. An error happened.'); 11 | console.error(error); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shell/sidemenu/sidemenu.component.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/app/shell/sidemenu/sidemenu.component.scss: -------------------------------------------------------------------------------- 1 | 2 | ul { 3 | padding: 0; 4 | text-align: center; 5 | 6 | li { 7 | list-style-type: none; 8 | padding: 1em; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/copenhagen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/copenhagen.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/darmstadt-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/darmstadt-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/darmstadt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/darmstadt.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/detroit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/detroit.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/firenze.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/firenze.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/granada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/granada.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/large-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/large-bg.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/logo.png -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/london.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/london.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/luebeck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/luebeck.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/reykjavík.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/reykjavík.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/shanghai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/shanghai.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/vienna-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/vienna-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/assets/vienna.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/assets/vienna.jpg -------------------------------------------------------------------------------- /test-projects/angular-i/src/environments/environment.development.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-i/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/src/favicon.ico -------------------------------------------------------------------------------- /test-projects/angular-i/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test-projects/angular-i/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,ts}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-i/tests/actual/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-i/tests/actual/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-i/tests/auto-tagging.config.ts: -------------------------------------------------------------------------------- 1 | import { SheriffConfig } from "@softarc/sheriff-core"; 2 | 3 | export const config: SheriffConfig = { 4 | depRules: { 5 | 'root': 'noTag', 6 | 'noTag': 'noTag' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-i/tests/customer-api-re-exports.index.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CustomersRepository } from '../data'; 3 | export { bookingsRoutes } from '../../bookings'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class CustomersApi { 7 | repo = inject(CustomersRepository); 8 | get selectedCustomer$() { 9 | return this.repo.selectedCustomer$; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test-projects/angular-i/tests/empty-sheriff.config.ts: -------------------------------------------------------------------------------- 1 | import { SheriffConfig } from "@softarc/sheriff-core"; 2 | 3 | export const config: SheriffConfig = { 4 | autoTagging: false, 5 | tagging: {}, 6 | depRules: {} 7 | }; 8 | -------------------------------------------------------------------------------- /test-projects/angular-i/tests/expected/auto-tagging-lint.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test-projects/angular-i/tests/expected/cli-verify-success.txt: -------------------------------------------------------------------------------- 1 | 2 | Verification Report 3 | 4 | No issues found. Well done! 5 | -------------------------------------------------------------------------------- /test-projects/angular-i/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-i/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-ii/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /test-projects/angular-ii/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /test-projects/angular-ii/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /test-projects/angular-ii/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "pwa-chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/+state/index.ts: -------------------------------------------------------------------------------- 1 | import { createReducer } from "@ngrx/store" 2 | 3 | export interface AppState { 4 | userName: string; 5 | } 6 | 7 | export const initState: AppState = { 8 | userName: 'Jane Doe' 9 | } 10 | 11 | export const reducer = createReducer( 12 | initState 13 | ); -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit, ViewChild, ViewContainerRef} from "@angular/core"; 2 | 3 | @Component({ 4 | standalone: true, 5 | selector: 'app-about', 6 | template: ` 7 |

About

8 | 9 | ` 10 | }) 11 | export class AboutComponent implements OnInit{ 12 | title = 'Standalone Demo'; 13 | 14 | @ViewChild('container', {read: ViewContainerRef}) 15 | viewContainer!: ViewContainerRef; 16 | 17 | async ngOnInit() { 18 | const esm = await import('./lazy/lazy.component'); 19 | const ref = this.viewContainer.createComponent(esm.LazyComponent) 20 | ref.instance.title = `Lazy Sub Component !!`; 21 | } 22 | } 23 | 24 | export default AboutComponent; 25 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/about/lazy/lazy.component.ts: -------------------------------------------------------------------------------- 1 | import { CommonModule } from "@angular/common"; 2 | import { Component } from "@angular/core"; 3 | 4 | @Component({ 5 | standalone: true, 6 | selector: 'app-lazy', 7 | imports: [CommonModule], 8 | template: `

{{ title }}

` 9 | }) 10 | export class LazyComponent { 11 | title = 'Standalone Demo'; 12 | visible = true; 13 | } 14 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | } 8 | 9 | .sidenav .mat-toolbar { 10 | background: inherit; 11 | } 12 | 13 | .mat-toolbar.mat-primary { 14 | position: sticky; 15 | top: 0; 16 | z-index: 1; 17 | } 18 | 19 | .app-container { 20 | padding: 20px; 21 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 | 9 |
-------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/data/checkin.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class CheckinService { 7 | 8 | checkin(ticketNumber: string): void { 9 | console.log('checking in', ticketNumber); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from './checkin.service'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/feature-manage/feature-manage.component.css: -------------------------------------------------------------------------------- 1 | .form-control { 2 | max-width: 300px; 3 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/feature-manage/feature-manage.component.html: -------------------------------------------------------------------------------- 1 |

Checkin

2 | 3 |
4 | Ticket Number: 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/feature-manage/feature-manage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { CheckinService } from '../data'; 5 | 6 | @Component({ 7 | selector: 'app-feature-manage', 8 | standalone: true, 9 | imports: [CommonModule, FormsModule], 10 | templateUrl: './feature-manage.component.html', 11 | styleUrls: ['./feature-manage.component.css'], 12 | }) 13 | export class FeatureManageComponent { 14 | #service = inject(CheckinService); 15 | 16 | ticketNumber = ''; 17 | 18 | checkin() { 19 | this.#service.checkin(this.ticketNumber); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/checkin/feature-manage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './feature-manage.component'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-auth/auth.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpInterceptorFn } from "@angular/common/http"; 2 | import { tap } from "rxjs"; 3 | 4 | export const authInterceptor: HttpInterceptorFn = (req, next) => { 5 | 6 | console.log('authInterceptor (root scope)'); 7 | 8 | if (req.url.startsWith('https://demo.angulararchitects.io/api/')) { 9 | // Setting a dummy token for demonstration 10 | const headers = req.headers.set('Authorization', 'Bearer Auth-1234567'); 11 | req = req.clone({headers}); 12 | } 13 | 14 | return next(req).pipe( 15 | tap(resp => console.log('response', resp)) 16 | ); 17 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({providedIn: 'root'}) 4 | export class AuthService { 5 | isAuthenticated(): boolean { 6 | // Just for demo purposes 7 | return true; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth.interceptor'; 2 | export * from './auth.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-common/combine-environment-providers.ts: -------------------------------------------------------------------------------- 1 | import { EnvironmentProviders, makeEnvironmentProviders, Provider } from "@angular/core"; 2 | 3 | export function combineEnvironmentProviders(envProviders: EnvironmentProviders[]): EnvironmentProviders { 4 | const providers = envProviders as unknown as Provider[][]; 5 | return makeEnvironmentProviders(providers.flat()); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-common/custom-appender.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { LogAppender } from "../util-logger"; 3 | import { LogLevel } from "../util-logger"; 4 | 5 | @Injectable() 6 | export class CustomAppender implements LogAppender { 7 | logs: string[] = []; 8 | 9 | append(level: LogLevel, category: string, msg: string): void { 10 | this.logs.push(msg); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './city.pipe'; 2 | export * from './city.validator'; 3 | export * from './legacy.interceptor' 4 | export * from './custom-appender' 5 | export * from './combine-environment-providers' 6 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-logger/color-config.ts: -------------------------------------------------------------------------------- 1 | export abstract class ColorConfig { 2 | abstract debug: number; 3 | abstract info: number; 4 | abstract error: number; 5 | } 6 | 7 | // Color Code from https://en.m.wikipedia.org/wiki/ANSI_escape_code#Colors 8 | export const defaultColorConfig: ColorConfig = { 9 | debug: 32, 10 | info: 34, 11 | error: 31 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-logger/index.ts: -------------------------------------------------------------------------------- 1 | export * from './color-config'; 2 | export * from './color.service'; 3 | export * from './features'; 4 | export * from './log-appender'; 5 | export * from './log-formatter'; 6 | export * from './log-level'; 7 | export * from './logger-config'; 8 | export * from './logger-module'; 9 | export * from './logger'; 10 | export * from './providers'; 11 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-logger/log-formatter.ts: -------------------------------------------------------------------------------- 1 | import {InjectionToken} from "@angular/core"; 2 | import {LogLevel} from "./log-level"; 3 | 4 | export const LOG_FORMATTER = new InjectionToken('LOG_FORMATTER'); 5 | 6 | export type LogFormatFn = (level: LogLevel, category: string, msg: string) => string; 7 | 8 | export const defaultLogFormatFn: LogFormatFn = (level, category, msg) => { 9 | const levelString = LogLevel[level].padEnd(5); 10 | return `[${levelString}] ${category.toUpperCase()} ${msg}`; 11 | } 12 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-logger/log-level.ts: -------------------------------------------------------------------------------- 1 | export enum LogLevel { 2 | DEBUG = 0, 3 | INFO = 1, 4 | ERROR = 2 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/shared/util-logger/logger-config.ts: -------------------------------------------------------------------------------- 1 | import { Type } from "@angular/core"; 2 | import { DefaultLogAppender, LogAppender } from "./log-appender"; 3 | import { defaultLogFormatFn, LogFormatFn } from "./log-formatter"; 4 | import { LogLevel } from "./log-level"; 5 | 6 | export abstract class LoggerConfig { 7 | abstract level: LogLevel; 8 | abstract chaining: boolean; 9 | abstract formatter: LogFormatFn; 10 | abstract appenders: Type[]; 11 | } 12 | 13 | export const defaultConfig: LoggerConfig = { 14 | level: LogLevel.DEBUG, 15 | chaining: false, 16 | formatter: defaultLogFormatFn, 17 | appenders: [DefaultLogAppender] 18 | } 19 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/+state/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction, props } from "@ngrx/store"; 2 | import { Flight } from "../flight"; 3 | 4 | export const loadFlights = createAction( 5 | "[booking] loadFlights", 6 | props<{from: string, to: string}>() 7 | ); 8 | 9 | export const delayFlight = createAction( 10 | "[booking] delayFlight", 11 | props<{id: number}>() 12 | ); 13 | 14 | export const loadFlightsSuccess = createAction( 15 | "[booking] loadFlightsSuccess", 16 | props<{flights: Flight[]}>() 17 | ); -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/+state/effects.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@angular/core"; 2 | import { Actions, createEffect, ofType } from "@ngrx/effects"; 3 | import { map, switchMap } from "rxjs"; 4 | import { FlightService } from "../flight.service"; 5 | import { loadFlights, loadFlightsSuccess } from "./actions"; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class BookingEffects { 11 | 12 | loadFlights$ = createEffect(() => this.actions$.pipe( 13 | ofType(loadFlights), 14 | switchMap(a => this.flightService.find(a.from, a.to)), 15 | map(flights => loadFlightsSuccess({flights})) 16 | )); 17 | 18 | constructor(private actions$: Actions, 19 | private flightService: FlightService) { } 20 | } 21 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/+state/selectors.ts: -------------------------------------------------------------------------------- 1 | import { createFeatureSelector, createSelector } from "@ngrx/store"; 2 | import { BookingState, BOOKING_FEATURE_KEY } from "./reducers"; 3 | 4 | export const selectBooking = createFeatureSelector(BOOKING_FEATURE_KEY); 5 | 6 | export const selectFlights = createSelector( 7 | selectBooking, 8 | booking => booking.flights 9 | ); 10 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/flight.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface Flight { 3 | id: number; 4 | from: string; 5 | to: string; 6 | date: string; 7 | delayed: boolean; 8 | } 9 | 10 | export const initFlight: Flight = { 11 | id: 0, 12 | from: '', 13 | to: '', 14 | date: '', 15 | delayed: false 16 | }; 17 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/index.ts: -------------------------------------------------------------------------------- 1 | export * from './+state/actions'; 2 | export * from './+state/effects'; 3 | export * from './+state/reducers'; 4 | export * from './+state/selectors'; 5 | export * from './flight.service'; 6 | export * from './flight'; 7 | export * from './passenger'; 8 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/data/passenger.ts: -------------------------------------------------------------------------------- 1 | export interface Passenger { 2 | id: number; 3 | firstName: string, 4 | name: string; 5 | bonusMiles: number; 6 | passengerStatus: string; 7 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-booking.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-booking.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from "@angular/core"; 2 | import { RouterLink, RouterOutlet } from "@angular/router"; 3 | import { LoggerService } from "../../shared/util-logger"; 4 | 5 | @Component({ 6 | standalone: true, 7 | selector: 'app-flight-booking', 8 | imports: [ 9 | RouterOutlet, 10 | RouterLink, 11 | ], 12 | templateUrl: './flight-booking.component.html' 13 | }) 14 | export class FlightBookingComponent { 15 | logger = inject(LoggerService); 16 | 17 | constructor() { 18 | this.logger.info('booking', 'Hello from Booking'); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-edit/flight-edit.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-edit/flight-edit.component.css -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-edit/flight-edit.component.html: -------------------------------------------------------------------------------- 1 |

Flight Edit

2 | 3 |

Id: {{id}}

4 |

ShowDetails: {{showDetails}}

5 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-search/flight-search.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/app/domains/ticketing/feature-booking/flight-search/flight-search.component.css -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/index.ts: -------------------------------------------------------------------------------- 1 | export * from './flight-booking.routes'; -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/passenger-search/passenger-search.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/app/domains/ticketing/feature-booking/passenger-search/passenger-search.component.css -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/passenger-search/passenger-search.component.html: -------------------------------------------------------------------------------- 1 |

Passengers

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
IdFirstNameNameBonus MilesStatus
{{p.id}}{{p.firstName}}{{p.name}}{{p.bonusMiles}}{{p.passengerStatus}}
-------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-booking/utils/booking.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpInterceptorFn } from "@angular/common/http"; 2 | import { tap } from "rxjs"; 3 | 4 | export const bookingInterceptor: HttpInterceptorFn = (req, next) => { 5 | 6 | console.log('bookingInterceptor (lazy scope)'); 7 | 8 | if (req.url.startsWith('https://demo.angulararchitects.io/api/')) { 9 | // Setting a dummy token for demonstration 10 | const headers = req.headers.set('Authorization', 'Bearer Booking-ABCDEFG'); 11 | req = req.clone({headers}); 12 | } 13 | 14 | return next(req).pipe( 15 | tap(resp => console.log('response', resp)) 16 | ); 17 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-my-tickets/index.ts: -------------------------------------------------------------------------------- 1 | export * from './my-tickets.component'; 2 | export * from './ticket.service'; 3 | export * from './tickets.module'; -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-my-tickets/ticket.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Flight } from '../data'; 3 | 4 | @Injectable() 5 | export class TicketService { 6 | 7 | readonly tickets: Flight[] = [ 8 | { id: 4711, from: 'Graz', to: 'Düsseldorf', delayed: false, date: new Date().toISOString()}, 9 | { id: 4712, from: 'Graz', to: 'Paderborn', delayed: false, date: new Date().toISOString()} 10 | ]; 11 | 12 | constructor() { 13 | console.debug('creating ticket service'); 14 | } 15 | 16 | get(limit = -1) { 17 | if (limit === -1) { 18 | return this.tickets; 19 | } 20 | return this.tickets.slice(0, limit); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-next-flight/index.ts: -------------------------------------------------------------------------------- 1 | export * from './next-flight.component'; -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/feature-next-flight/next-flight.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | import {TicketsModule} from '../feature-my-tickets'; 3 | 4 | @Component({ 5 | selector: 'app-next-flight', 6 | standalone: true, 7 | template: ` 8 | 9 | `, 10 | imports: [ 11 | TicketsModule 12 | ] 13 | }) 14 | export class NextFlightComponent { 15 | } 16 | 17 | export default NextFlightComponent; 18 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/ui-common/flight-card/flight-card.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/app/domains/ticketing/ui-common/flight-card/flight-card.component.css -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/domains/ticketing/ui-common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './flight-card/flight-card.component'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/home/home.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/app/home/home.component.css -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |

Welcome!

-------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from "@angular/core"; 2 | import { LoggerService } from "../domains/shared/util-logger"; 3 | 4 | @Component({ 5 | standalone: true, 6 | selector: 'app-home', 7 | templateUrl: './home.component.html' 8 | }) 9 | export class HomeComponent { 10 | logger = inject(LoggerService); 11 | 12 | constructor() { 13 | this.logger.debug('home', 'My Debug Message'); 14 | this.logger.info('home', 'My Info Message'); 15 | this.logger.error('home', 'My Error Message'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/init.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({providedIn: 'root'}) 4 | export class InitService { 5 | init(): void { 6 | console.debug('Initializing stuff ...'); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/logger.config.ts: -------------------------------------------------------------------------------- 1 | import { CustomAppender } from "./domains/shared/util-common"; 2 | import { DefaultLogAppender, defaultLogFormatFn, LoggerConfig, LogLevel } from "./domains/shared/util-logger"; 3 | 4 | export const loggerConfig: Partial = { 5 | level: LogLevel.DEBUG, 6 | appenders: [CustomAppender, DefaultLogAppender], 7 | formatter: defaultLogFormatFn, 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/shell/index.ts: -------------------------------------------------------------------------------- 1 | // import { NavbarComponent } from './navbar/navbar.component'; 2 | // import { SidebarComponent } from './sidebar/sidebar.component'; 3 | 4 | export { NavbarComponent } from './navbar/navbar.component'; 5 | export { SidebarComponent } from './sidebar/sidebar.component'; 6 | 7 | 8 | // export const SHELL = [ 9 | // NavbarComponent, 10 | // SidebarComponent 11 | // ]; -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/shell/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | Flighs42 10 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/app/shell/sidebar/sidebar.component.css: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 200px; 7 | } 8 | 9 | .sidenav .mat-toolbar { 10 | background: inherit; 11 | } 12 | 13 | .mat-toolbar.mat-primary { 14 | position: sticky; 15 | top: 0; 16 | z-index: 1; 17 | } 18 | 19 | .app-container { 20 | padding: 20px; 21 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/assets/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-ii/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-ii/src/favicon.ico -------------------------------------------------------------------------------- /test-projects/angular-ii/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StandaloneCli 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /test-projects/angular-ii/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | body { 3 | background-color:#fafafa; 4 | } -------------------------------------------------------------------------------- /test-projects/angular-ii/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-ii/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iii/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /test-projects/angular-iii/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /test-projects/angular-iii/README.md: -------------------------------------------------------------------------------- 1 | This Angular application uses relative paths. 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | div.main { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: stretch; 7 | 8 | mat-toolbar { 9 | mat-toolbar-row { 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | } 14 | 15 | mat-drawer-container { 16 | flex: 1 1 auto; 17 | min-height: 25em; 18 | 19 | mat-drawer { 20 | background: #fafafa; 21 | } 22 | 23 | mat-drawer-content { 24 | padding: 1em; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { UserLoaderGuard } from './shell/services/user-loader.guard'; 3 | import { HomeComponent } from './shell/home.component'; 4 | 5 | export const appRoutes: Routes = [ 6 | { 7 | path: '', 8 | canActivate: [UserLoaderGuard], 9 | children: [ 10 | { 11 | path: '', 12 | component: HomeComponent, 13 | }, 14 | { 15 | path: 'customers', 16 | loadChildren: () => 17 | import('@eternal/customers/feature').then((m) => m.customersRoutes), 18 | }, 19 | ], 20 | }, 21 | ]; 22 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/api/index.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CustomersRepository } from '../data'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class CustomersApi { 6 | repo = inject(CustomersRepository); 7 | get selectedCustomer$() { 8 | return this.repo.selectedCustomer$; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/data/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomersRepository } from './customers-repository.service'; 2 | export { provideCustomers } from './provide-customers'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/data/provide-customers.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { customersFeature } from './customers.reducer'; 3 | import { provideEffects } from '@ngrx/effects'; 4 | import { CustomersEffects } from './customers.effects'; 5 | 6 | export const provideCustomers = [ 7 | provideState(customersFeature), 8 | provideEffects([CustomersEffects]), 9 | ]; 10 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/feature/components/customers-root/customers-root.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/feature/components/customers-root/customers-root.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterOutlet } from '@angular/router'; 3 | 4 | @Component({ 5 | templateUrl: './customers-root.component.html', 6 | standalone: true, 7 | imports: [RouterOutlet], 8 | }) 9 | export class CustomersRootComponent {} 10 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/feature/index.ts: -------------------------------------------------------------------------------- 1 | export { customersRoutes } from './customers.routes'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/feature/services/data.guard.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CanActivate } from '@angular/router'; 3 | import { CustomersRepository } from '../../data'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class DataGuard implements CanActivate { 9 | #customersRepository = inject(CustomersRepository); 10 | 11 | canActivate(): boolean { 12 | this.#customersRepository.load(1); 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/model/customer.ts: -------------------------------------------------------------------------------- 1 | export interface Customer { 2 | id: number; 3 | firstname: string; 4 | name: string; 5 | country: string; 6 | birthdate: string; 7 | } 8 | 9 | let id = 1; 10 | 11 | export function createCustomer(customer: Partial = {}): Customer { 12 | return { 13 | ...{ 14 | id: id++, 15 | firstname: 'Jessica', 16 | name: 'Trabner', 17 | country: 'AT', 18 | birthdate: '2001-09-02', 19 | }, 20 | ...customer, 21 | }; 22 | } 23 | 24 | export function createCustomers(...customers: Partial[]) { 25 | return customers.map(createCustomer); 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/model/index.ts: -------------------------------------------------------------------------------- 1 | export { Customer, createCustomer, createCustomers } from './customer'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/ui/customer.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { Customer } from '@eternal/customers/model'; 3 | 4 | @Pipe({ 5 | name: 'customer', 6 | standalone: true, 7 | }) 8 | export class CustomerPipe implements PipeTransform { 9 | transform(customer: Customer): string { 10 | if (!customer.name && !customer.firstname) { 11 | return '-'; 12 | } 13 | return `${customer.name}, ${customer.firstname}`; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/ui/customer/customer.component.scss: -------------------------------------------------------------------------------- 1 | form { 2 | width: 20em; 3 | 4 | .buttons { 5 | display: flex; 6 | justify-content: space-evenly; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/customers/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomerComponent } from './customer/customer.component'; 2 | export { 3 | CustomersComponent, 4 | CustomersViewModel, 5 | } from './customers/customers.component'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/config/configuration.ts: -------------------------------------------------------------------------------- 1 | export class Configuration { 2 | constructor(public baseUrl: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/config/index.ts: -------------------------------------------------------------------------------- 1 | export { Configuration } from './configuration'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/form/form-errors.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { JsonPipe, NgIf } from '@angular/common'; 4 | import { MatInputModule } from '@angular/material/input'; 5 | 6 | @Component({ 7 | selector: 'eternal-form-errors', 8 | template: ` 9 | This field is mandatory 10 | `, 11 | standalone: true, 12 | imports: [NgIf, JsonPipe, MatInputModule], 13 | }) 14 | export class FormErrorsComponent { 15 | @Input() control: FormControl | undefined; 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/form/index.ts: -------------------------------------------------------------------------------- 1 | export { Options } from './options'; 2 | export { FormErrorsComponent } from './form-errors.component'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/form/options.ts: -------------------------------------------------------------------------------- 1 | export interface Option { 2 | label: string; 3 | value: string; 4 | } 5 | 6 | export type Options = Option[]; 7 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/http/error-message.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const ERROR_MESSAGE_CONTEXT = new HttpContextToken( 4 | () => 'Sorry, something went wrong on our side.' 5 | ); 6 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/http/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorInterceptor } from './error.interceptor'; 2 | export { BaseUrlInterceptor } from './base-url.interceptor'; 3 | export { withErrorMessageContext } from './with-error-message-context'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/http/with-error-message-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { ERROR_MESSAGE_CONTEXT } from './error-message.context'; 3 | 4 | export function withErrorMessageContext(message: string) { 5 | return new HttpContext().set(ERROR_MESSAGE_CONTEXT, message); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/master-data/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedMasterDataProvider } from './shared-master-data.provider'; 2 | export { selectCountries, masterFeature } from './+state/master.reducer'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/master-data/shared-master-data.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { masterFeature } from './+state/master.reducer'; 3 | 4 | export const sharedMasterDataProvider = provideState(masterFeature); 5 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/deep-clone.ts: -------------------------------------------------------------------------------- 1 | import { map, Observable } from 'rxjs'; 2 | 3 | export function deepClone(source$: Observable): Observable { 4 | return source$.pipe(map((object) => structuredClone(object))); 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/filter-defined.ts: -------------------------------------------------------------------------------- 1 | import { filter, Observable } from 'rxjs'; 2 | import { isDefined } from '@eternal/shared/util'; 3 | 4 | export function filterDefined( 5 | source$: Observable 6 | ): Observable> { 7 | return source$.pipe(filter(isDefined)); 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { noopAction } from './noop.action'; 2 | export { safeConcatMap } from './safe-concat-map'; 3 | export { filterDefined } from './filter-defined'; 4 | export { deepClone } from './deep-clone'; 5 | export { LoadStatus } from './load-status'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/load-status.ts: -------------------------------------------------------------------------------- 1 | export type LoadStatus = 'not loaded' | 'loading' | 'loaded'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/noop.action.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@ngrx/store'; 2 | 3 | export const noopAction = createAction('[Util] NOOP'); 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ngrx-utils/safe-concat-map.ts: -------------------------------------------------------------------------------- 1 | import { TypedAction } from '@ngrx/store/src/models'; 2 | import { catchError, concatMap, Observable, of, OperatorFunction } from 'rxjs'; 3 | import { noopAction } from './noop.action'; 4 | 5 | export function safeConcatMap( 6 | project: (value: S) => Observable> 7 | ): OperatorFunction> { 8 | return (source$: Observable): Observable> => 9 | source$.pipe( 10 | concatMap((value) => 11 | project(value).pipe(catchError(() => of(noopAction()))) 12 | ) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/security/index.ts: -------------------------------------------------------------------------------- 1 | export { securityProvider } from './security.provider'; 2 | export { SecurityService } from './security.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/security/security.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 2 | import { User } from './security.reducer'; 3 | 4 | export const securityActions = createActionGroup({ 5 | source: 'Security', 6 | events: { 7 | 'Load User': emptyProps(), 8 | 'Load User Success': props<{ user: User }>(), 9 | 'Sign In User': props<{ email: string; password: string }>(), 10 | 'Sign In User Success': props<{ user: User }>(), 11 | 'Sign Out User': emptyProps(), 12 | 'Sign Out User Success': props<{ user: User }>(), 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/security/security.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideEffects } from '@ngrx/effects'; 2 | import { provideState } from '@ngrx/store'; 3 | import { SecurityEffects } from './security.effects'; 4 | import { securityFeature } from './security.reducer'; 5 | 6 | export const securityProvider = [ 7 | provideState(securityFeature), 8 | provideEffects([SecurityEffects]), 9 | ]; 10 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/security/security.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | import { securityFeature } from './security.reducer'; 3 | 4 | const { selectUser, selectLoaded } = securityFeature; 5 | 6 | const selectSignedIn = createSelector( 7 | selectUser, 8 | selectLoaded, 9 | (user, loaded) => loaded && !user?.anonymous 10 | ); 11 | 12 | export const fromSecurity = { 13 | selectUser, 14 | selectLoaded, 15 | selectSignedIn, 16 | }; 17 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/testing/assert-type.ts: -------------------------------------------------------------------------------- 1 | export function assertType(obj: unknown = null): T { 2 | return obj as T; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/testing/index.ts: -------------------------------------------------------------------------------- 1 | export { assertType } from './assert-type'; 2 | export * from './mock-inject'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedUiMessagingProvider } from './shared-ui-messaging.provider'; 2 | export { LoaderComponent } from './loader/loader.component'; 3 | export { LoadingService } from './loader/loading.service'; 4 | export { LoadingInterceptor } from './loader/loading.interceptor'; 5 | export { MessageService } from './message/message.service'; 6 | export { MessageComponent } from './message/message.component'; 7 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/loader/loader.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe, NgStyle } from '@angular/common'; 2 | import { Component, inject } from '@angular/core'; 3 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 4 | import { LoadingService } from './loading.service'; 5 | 6 | @Component({ 7 | selector: 'eternal-loader', 8 | template: ``, 14 | standalone: true, 15 | imports: [MatProgressBarModule, NgStyle, AsyncPipe], 16 | }) 17 | export class LoaderComponent { 18 | loadingService = inject(LoadingService); 19 | } 20 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/loader/loading.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class LoadingService { 6 | #loading$ = new BehaviorSubject(false); 7 | loading$ = this.#loading$.asObservable(); 8 | 9 | start() { 10 | this.#loading$.next(true); 11 | } 12 | 13 | stop() { 14 | this.#loading$.next(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/loader/silent-load.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const SILENT_LOAD_CONTEXT = new HttpContextToken(() => false); 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/loader/with-silent-load-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { SILENT_LOAD_CONTEXT } from './silent-load.context'; 3 | 4 | export function withSilentLoadContext() { 5 | return new HttpContext().set(SILENT_LOAD_CONTEXT, true); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/message/message.component.html: -------------------------------------------------------------------------------- 1 |
8 | check_circle 9 | warning 10 |

11 | {{ message.text }} 12 |

13 |
14 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/message/message.store.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { Message } from './message'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class MessageStore { 7 | #messages$ = new Subject(); 8 | 9 | messages$ = this.#messages$.asObservable(); 10 | 11 | add(message: Message) { 12 | this.#messages$.next(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/message/message.ts: -------------------------------------------------------------------------------- 1 | export interface Message { 2 | text: string; 3 | type: 'error' | 'info'; 4 | } 5 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui-messaging/shared-ui-messaging.provider.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | import { LoadingInterceptor } from './loader/loading.interceptor'; 3 | import { importProvidersFrom } from '@angular/core'; 4 | import { MatDialogModule } from '@angular/material/dialog'; 5 | 6 | export const sharedUiMessagingProvider = [ 7 | { 8 | provide: HTTP_INTERCEPTORS, 9 | multi: true, 10 | useClass: LoadingInterceptor, 11 | }, 12 | importProvidersFrom(MatDialogModule), 13 | ]; 14 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { BlinkerDirective } from './blinker.directive'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/util/assert-defined.ts: -------------------------------------------------------------------------------- 1 | export function assertDefined(value: T | undefined): asserts value is T { 2 | if (value === undefined) { 3 | throw new Error('value is undefined'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/util/index.ts: -------------------------------------------------------------------------------- 1 | export { assertDefined } from './assert-defined'; 2 | export { isDefined } from './is-defined'; 3 | export { safeAssign } from './safe-assign'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/util/is-defined.ts: -------------------------------------------------------------------------------- 1 | export function isDefined(value: T): value is NonNullable { 2 | return value !== undefined; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shared/util/safe-assign.ts: -------------------------------------------------------------------------------- 1 | export function safeAssign>( 2 | object: T, 3 | changes: Partial = {} 4 | ): void { 5 | Object.assign(object, changes); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shell/header/header.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 100%; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | } 7 | 8 | a.home { 9 | text-decoration: none; 10 | color: inherit; 11 | 12 | h1 { 13 | display: flex; 14 | align-items: center; 15 | 16 | img { 17 | width: 4em; 18 | height: auto; 19 | padding-right: 1em; 20 | } 21 | } 22 | } 23 | 24 | div.security a { 25 | margin: 0 0.5em 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shell/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'eternal-home', 5 | template: `

6 | Eternal is an imaginary travel agency and is used as training application 7 | for Angular developers. 8 |

9 |

10 | You can click around, do whatever you want but don't expect to be able to 11 | book a real holiday 😉. 12 |

`, 13 | standalone: true, 14 | }) 15 | export class HomeComponent {} 16 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shell/services/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler, Injectable, Injector } from '@angular/core'; 2 | import { MessageService } from '@eternal/shared/ui-messaging'; 3 | 4 | @Injectable() 5 | export class ErrorHandlerService implements ErrorHandler { 6 | constructor(private injector: Injector) {} 7 | 8 | handleError(error: unknown): void { 9 | const messageService = this.injector.get(MessageService); 10 | messageService.error('We are sorry. An error happened.'); 11 | console.error(error); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shell/sidemenu/sidemenu.component.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/app/shell/sidemenu/sidemenu.component.scss: -------------------------------------------------------------------------------- 1 | 2 | ul { 3 | padding: 0; 4 | text-align: center; 5 | 6 | li { 7 | list-style-type: none; 8 | padding: 1em; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/copenhagen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/copenhagen.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/darmstadt-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/darmstadt-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/darmstadt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/darmstadt.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/detroit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/detroit.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/firenze.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/firenze.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/granada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/granada.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/large-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/large-bg.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/logo.png -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/london.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/london.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/luebeck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/luebeck.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/reykjavík.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/reykjavík.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/shanghai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/shanghai.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/vienna-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/vienna-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/assets/vienna.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/assets/vienna.jpg -------------------------------------------------------------------------------- /test-projects/angular-iii/src/environments/environment.development.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iii/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iii/src/favicon.ico -------------------------------------------------------------------------------- /test-projects/angular-iii/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test-projects/angular-iii/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,ts}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-iii/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iii/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | div.main { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: stretch; 7 | 8 | mat-toolbar { 9 | mat-toolbar-row { 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | } 14 | 15 | mat-drawer-container { 16 | flex: 1 1 auto; 17 | min-height: 25em; 18 | 19 | mat-drawer { 20 | background: #fafafa; 21 | } 22 | 23 | mat-drawer-content { 24 | padding: 1em; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/bookings/+state/bookings.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 2 | import { Booking } from './bookings.reducer'; 3 | 4 | export const bookingsActions = createActionGroup({ 5 | source: 'Customer Bookings', 6 | events: { 7 | Load: emptyProps(), 8 | Loaded: props<{ bookings: Booking[] }>(), 9 | Reset: emptyProps(), 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/bookings/+state/bookings.selectors.ts: -------------------------------------------------------------------------------- 1 | import { bookingsFeature } from './bookings.reducer'; 2 | 3 | const { selectBookings, selectLoaded } = bookingsFeature; 4 | 5 | export const fromBookings = { selectBookings, selectLoaded }; 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/bookings/bookings.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { provideEffects } from '@ngrx/effects'; 3 | import { provideState } from '@ngrx/store'; 4 | import { bookingsFeature } from './+state/bookings.reducer'; 5 | import { OverviewComponent } from './overview/overview.component'; 6 | import { BookingsEffects } from './+state/bookings.effects'; 7 | 8 | export const bookingsRoutes: Routes = [ 9 | { 10 | path: '', 11 | providers: [ 12 | provideState(bookingsFeature), 13 | provideEffects([BookingsEffects]), 14 | ], 15 | component: OverviewComponent, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/bookings/index.ts: -------------------------------------------------------------------------------- 1 | export { bookingsRoutes } from './bookings.routes'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/api/index.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CustomersRepository } from '../data'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class CustomersApi { 6 | repo = inject(CustomersRepository); 7 | get selectedCustomer$() { 8 | return this.repo.selectedCustomer$; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/data/customers.json: -------------------------------------------------------------------------------- 1 | { 2 | "person": { 3 | "id": 1, 4 | "firstname": "Volker", 5 | "lastname":"Gruber" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/data/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomersRepository } from './customers-repository.service'; 2 | export { provideCustomers } from './provide-customers'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/data/provide-customers.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { customersFeature } from './customers.reducer'; 3 | import { provideEffects } from '@ngrx/effects'; 4 | import { CustomersEffects } from './customers.effects'; 5 | 6 | export const provideCustomers = [ 7 | provideState(customersFeature), 8 | provideEffects([CustomersEffects]), 9 | ]; 10 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/feature/components/customers-root/customers-root.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/feature/components/customers-root/customers-root.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterOutlet } from '@angular/router'; 3 | 4 | @Component({ 5 | templateUrl: './customers-root.component.html', 6 | standalone: true, 7 | imports: [RouterOutlet], 8 | }) 9 | export class CustomersRootComponent {} 10 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/feature/index.ts: -------------------------------------------------------------------------------- 1 | export { customersRoutes } from './customers.routes'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/feature/services/data.guard.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | 3 | import { CustomersRepository } from '../../data'; 4 | 5 | @Injectable({ 6 | providedIn: 'root', 7 | }) 8 | export class DataGuard { 9 | #customersRepository = inject(CustomersRepository); 10 | 11 | canActivate(): boolean { 12 | this.#customersRepository.load(1); 13 | return true; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/model/customer.ts: -------------------------------------------------------------------------------- 1 | export interface Customer { 2 | id: number; 3 | firstname: string; 4 | name: string; 5 | country: string; 6 | birthdate: string; 7 | } 8 | 9 | let id = 1; 10 | 11 | export function createCustomer(customer: Partial = {}): Customer { 12 | return { 13 | ...{ 14 | id: id++, 15 | firstname: 'Jessica', 16 | name: 'Trabner', 17 | country: 'AT', 18 | birthdate: '2001-09-02', 19 | }, 20 | ...customer, 21 | }; 22 | } 23 | 24 | export function createCustomers(...customers: Partial[]) { 25 | return customers.map(createCustomer); 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/model/index.ts: -------------------------------------------------------------------------------- 1 | export { Customer, createCustomer, createCustomers } from './customer'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/ui/customer.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { Customer } from '@eternal/customers/model'; 3 | 4 | @Pipe({ 5 | name: 'customer', 6 | standalone: true, 7 | }) 8 | export class CustomerPipe implements PipeTransform { 9 | transform(customer: Customer): string { 10 | if (!customer.name && !customer.firstname) { 11 | return '-'; 12 | } 13 | return `${customer.name}, ${customer.firstname}`; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/ui/customer/customer.component.scss: -------------------------------------------------------------------------------- 1 | form { 2 | width: 20em; 3 | 4 | .buttons { 5 | display: flex; 6 | justify-content: space-evenly; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/customers/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { CustomerComponent } from './customer/customer.component'; 2 | export { 3 | CustomersComponent, 4 | CustomersViewModel, 5 | } from './customers/customers.component'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/feature/+state/holidays.actions.ts: -------------------------------------------------------------------------------- 1 | import { Holiday } from '@eternal/holidays/model'; 2 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 3 | 4 | export const holidaysActions = createActionGroup({ 5 | source: 'Holidays', 6 | events: { 7 | Load: emptyProps(), 8 | Loaded: props<{ holidays: Holiday[] }>(), 9 | 'Add Favourite': props<{ id: number }>(), 10 | 'Favourite Added': props<{ id: number }>(), 11 | 'Remove Favourite': props<{ id: number }>(), 12 | 'Favourite Removed': props<{ id: number }>(), 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/feature/+state/holidays.selectors.ts: -------------------------------------------------------------------------------- 1 | import { holidaysFeature } from './holidays.reducer'; 2 | import { createSelector } from '@ngrx/store'; 3 | 4 | const selectHolidaysWithFavourite = createSelector( 5 | holidaysFeature.selectHolidays, 6 | holidaysFeature.selectFavouriteIds, 7 | (holidays, favouriteIds) => 8 | holidays.map((holiday) => ({ 9 | ...holiday, 10 | isFavourite: favouriteIds.includes(holiday.id), 11 | })) 12 | ); 13 | 14 | export const fromHolidays = { 15 | get: holidaysFeature.selectHolidays, 16 | selectHolidaysWithFavourite, 17 | }; 18 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/feature/address.ts: -------------------------------------------------------------------------------- 1 | export interface Address { 2 | street: string; 3 | streetNumber: string; 4 | zip?: string; 5 | city?: string; 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/feature/index.ts: -------------------------------------------------------------------------------- 1 | export { holidaysRoutes } from './holidays.routes'; 2 | export { AddressLookuper } from './address-lookuper.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/feature/parse-address.ts: -------------------------------------------------------------------------------- 1 | import { Address } from './address'; 2 | 3 | export function parseAddress(query: string): Address { 4 | const shortPattern = /^([\w\s]+)\s(\d+)$/; 5 | const longPattern = /^([\w\s]+)\s(\d+),\s(\d+)\s([\w]+)$/; 6 | let match: string[] | null = query.match(shortPattern); 7 | 8 | if (match) { 9 | const [, street, streetNumber] = match; 10 | return { street, streetNumber }; 11 | } else { 12 | match = query.match(longPattern); 13 | if (match) { 14 | const [, street, streetNumber, zip, city] = match; 15 | return { street, streetNumber, zip, city }; 16 | } 17 | } 18 | 19 | throw new Error('Could not parse address. Invalid format.'); 20 | } 21 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/model/index.ts: -------------------------------------------------------------------------------- 1 | export { Holiday, createHoliday, createHolidays } from './holiday'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/holidays/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { HolidayCardComponent } from './holiday-card/holiday-card.component'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/config/configuration.ts: -------------------------------------------------------------------------------- 1 | export class Configuration { 2 | constructor(public baseUrl: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/config/index.ts: -------------------------------------------------------------------------------- 1 | export { Configuration } from './configuration'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/form/form-errors.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { FormControl } from '@angular/forms'; 3 | import { JsonPipe, NgIf } from '@angular/common'; 4 | import { MatInputModule } from '@angular/material/input'; 5 | 6 | @Component({ 7 | selector: 'eternal-form-errors', 8 | template: ` 9 | This field is mandatory 10 | `, 11 | standalone: true, 12 | imports: [NgIf, JsonPipe, MatInputModule], 13 | }) 14 | export class FormErrorsComponent { 15 | @Input() control: FormControl | undefined; 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/form/index.ts: -------------------------------------------------------------------------------- 1 | export { Options } from './options'; 2 | export { FormErrorsComponent } from './form-errors.component'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/form/options.ts: -------------------------------------------------------------------------------- 1 | export interface Option { 2 | label: string; 3 | value: string; 4 | } 5 | 6 | export type Options = Option[]; 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/http/error-message.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const ERROR_MESSAGE_CONTEXT = new HttpContextToken( 4 | () => 'Sorry, something went wrong on our side.' 5 | ); 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/http/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorInterceptor } from './error.interceptor'; 2 | export { BaseUrlInterceptor } from './base-url.interceptor'; 3 | export { withErrorMessageContext } from './with-error-message-context'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/http/with-error-message-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { ERROR_MESSAGE_CONTEXT } from './error-message.context'; 3 | 4 | export function withErrorMessageContext(message: string) { 5 | return new HttpContext().set(ERROR_MESSAGE_CONTEXT, message); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/master-data/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedMasterDataProvider } from './shared-master-data.provider'; 2 | export { selectCountries, masterFeature } from './+state/master.reducer'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/master-data/shared-master-data.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideState } from '@ngrx/store'; 2 | import { masterFeature } from './+state/master.reducer'; 3 | 4 | export const sharedMasterDataProvider = provideState(masterFeature); 5 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/deep-clone.ts: -------------------------------------------------------------------------------- 1 | import { map, Observable } from 'rxjs'; 2 | 3 | export function deepClone(source$: Observable): Observable { 4 | return source$.pipe(map((object) => structuredClone(object))); 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/filter-defined.ts: -------------------------------------------------------------------------------- 1 | import { filter, Observable } from 'rxjs'; 2 | import { isDefined } from '@eternal/shared/util'; 3 | 4 | export function filterDefined( 5 | source$: Observable 6 | ): Observable> { 7 | return source$.pipe(filter(isDefined)); 8 | } 9 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { noopAction } from './noop.action'; 2 | export { safeConcatMap } from './safe-concat-map'; 3 | export { filterDefined } from './filter-defined'; 4 | export { deepClone } from './deep-clone'; 5 | export { LoadStatus } from './load-status'; 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/load-status.ts: -------------------------------------------------------------------------------- 1 | export type LoadStatus = 'not loaded' | 'loading' | 'loaded'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/noop.action.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from '@ngrx/store'; 2 | 3 | export const noopAction = createAction('[Util] NOOP'); 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ngrx-utils/safe-concat-map.ts: -------------------------------------------------------------------------------- 1 | import { Action } from '@ngrx/store'; 2 | import { catchError, concatMap, Observable, of, OperatorFunction } from 'rxjs'; 3 | import { noopAction } from './noop.action'; 4 | 5 | export function safeConcatMap( 6 | project: (value: S) => Observable> 7 | ): OperatorFunction> { 8 | return (source$: Observable): Observable> => 9 | source$.pipe( 10 | concatMap((value) => 11 | project(value).pipe(catchError(() => of(noopAction()))) 12 | ) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/security/index.ts: -------------------------------------------------------------------------------- 1 | export { securityProvider } from './security.provider'; 2 | export { SecurityService } from './security.service'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/security/security.actions.ts: -------------------------------------------------------------------------------- 1 | import { createActionGroup, emptyProps, props } from '@ngrx/store'; 2 | import { User } from './security.reducer'; 3 | 4 | export const securityActions = createActionGroup({ 5 | source: 'Security', 6 | events: { 7 | 'Load User': emptyProps(), 8 | 'Load User Success': props<{ user: User }>(), 9 | 'Sign In User': props<{ email: string; password: string }>(), 10 | 'Sign In User Success': props<{ user: User }>(), 11 | 'Sign Out User': emptyProps(), 12 | 'Sign Out User Success': props<{ user: User }>(), 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/security/security.provider.ts: -------------------------------------------------------------------------------- 1 | import { provideEffects } from '@ngrx/effects'; 2 | import { provideState } from '@ngrx/store'; 3 | import { SecurityEffects } from './security.effects'; 4 | import { securityFeature } from './security.reducer'; 5 | 6 | export const securityProvider = [ 7 | provideState(securityFeature), 8 | provideEffects([SecurityEffects]), 9 | ]; 10 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/security/security.selectors.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@ngrx/store'; 2 | import { securityFeature } from './security.reducer'; 3 | 4 | const { selectUser, selectLoaded } = securityFeature; 5 | 6 | const selectSignedIn = createSelector( 7 | selectUser, 8 | selectLoaded, 9 | (user, loaded) => loaded && !user?.anonymous 10 | ); 11 | 12 | export const fromSecurity = { 13 | selectUser, 14 | selectLoaded, 15 | selectSignedIn, 16 | }; 17 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/testing/assert-type.ts: -------------------------------------------------------------------------------- 1 | export function assertType(obj: unknown = null): T { 2 | return obj as T; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/testing/index.ts: -------------------------------------------------------------------------------- 1 | export { assertType } from './assert-type'; 2 | export * from './mock-inject'; 3 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/index.ts: -------------------------------------------------------------------------------- 1 | export { sharedUiMessagingProvider } from './shared-ui-messaging.provider'; 2 | export { LoaderComponent } from './loader/loader.component'; 3 | export { LoadingService } from './loader/loading.service'; 4 | export { LoadingInterceptor } from './loader/loading.interceptor'; 5 | export { MessageService } from './message/message.service'; 6 | export { MessageComponent } from './message/message.component'; 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/loader/loader.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe, NgStyle } from '@angular/common'; 2 | import { Component, inject } from '@angular/core'; 3 | import { MatProgressBarModule } from '@angular/material/progress-bar'; 4 | import { LoadingService } from './loading.service'; 5 | 6 | @Component({ 7 | selector: 'eternal-loader', 8 | template: ``, 14 | standalone: true, 15 | imports: [MatProgressBarModule, NgStyle, AsyncPipe], 16 | }) 17 | export class LoaderComponent { 18 | loadingService = inject(LoadingService); 19 | } 20 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/loader/loading.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | 4 | @Injectable({ providedIn: 'root' }) 5 | export class LoadingService { 6 | #loading$ = new BehaviorSubject(false); 7 | loading$ = this.#loading$.asObservable(); 8 | 9 | start() { 10 | this.#loading$.next(true); 11 | } 12 | 13 | stop() { 14 | this.#loading$.next(false); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/loader/silent-load.context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContextToken } from '@angular/common/http'; 2 | 3 | export const SILENT_LOAD_CONTEXT = new HttpContextToken(() => false); 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/loader/with-silent-load-context.ts: -------------------------------------------------------------------------------- 1 | import { HttpContext } from '@angular/common/http'; 2 | import { SILENT_LOAD_CONTEXT } from './silent-load.context'; 3 | 4 | export function withSilentLoadContext() { 5 | return new HttpContext().set(SILENT_LOAD_CONTEXT, true); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/message/message.component.html: -------------------------------------------------------------------------------- 1 |
8 | check_circle 9 | warning 10 |

11 | {{ message.text }} 12 |

13 |
14 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/message/message.store.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs'; 3 | import { Message } from './message'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class MessageStore { 7 | #messages$ = new Subject(); 8 | 9 | messages$ = this.#messages$.asObservable(); 10 | 11 | add(message: Message) { 12 | this.#messages$.next(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/message/message.ts: -------------------------------------------------------------------------------- 1 | export interface Message { 2 | text: string; 3 | type: 'error' | 'info'; 4 | } 5 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui-messaging/shared-ui-messaging.provider.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | import { LoadingInterceptor } from './loader/loading.interceptor'; 3 | import { importProvidersFrom } from '@angular/core'; 4 | import { MatDialogModule } from '@angular/material/dialog'; 5 | 6 | export const sharedUiMessagingProvider = [ 7 | { 8 | provide: HTTP_INTERCEPTORS, 9 | multi: true, 10 | useClass: LoadingInterceptor, 11 | }, 12 | importProvidersFrom(MatDialogModule), 13 | ]; 14 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { BlinkerDirective } from './blinker.directive'; 2 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/util/assert-defined.ts: -------------------------------------------------------------------------------- 1 | export function assertDefined(value: T | undefined): asserts value is T { 2 | if (value === undefined) { 3 | throw new Error('value is undefined'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/util/index.ts: -------------------------------------------------------------------------------- 1 | export { assertDefined } from './assert-defined'; 2 | export { isDefined } from './is-defined'; 3 | export { safeAssign } from './safe-assign'; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/util/is-defined.ts: -------------------------------------------------------------------------------- 1 | export function isDefined(value: T): value is NonNullable { 2 | return value !== undefined; 3 | } 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shared/util/safe-assign.ts: -------------------------------------------------------------------------------- 1 | export function safeAssign>( 2 | object: T, 3 | changes: Partial = {} 4 | ): void { 5 | Object.assign(object, changes); 6 | } 7 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/header/header.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | width: 100%; 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | } 7 | 8 | a.home { 9 | text-decoration: none; 10 | color: inherit; 11 | 12 | h1 { 13 | display: flex; 14 | align-items: center; 15 | 16 | img { 17 | width: 4em; 18 | height: auto; 19 | padding-right: 1em; 20 | } 21 | } 22 | } 23 | 24 | div.security a { 25 | margin: 0 0.5em 26 | } 27 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'eternal-home', 5 | template: `

6 | Eternal is an imaginary travel agency and is used as training application 7 | for Angular developers. 8 |

9 |

10 | You can click around, do whatever you want but don't expect to be able to 11 | book a real holiday 😉. 12 |

`, 13 | standalone: true, 14 | }) 15 | export class HomeComponent {} 16 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/services/error-handler.service.ts: -------------------------------------------------------------------------------- 1 | import { ErrorHandler, Injectable, Injector } from '@angular/core'; 2 | import { MessageService } from '@eternal/shared/ui-messaging'; 3 | 4 | @Injectable() 5 | export class ErrorHandlerService implements ErrorHandler { 6 | constructor(private injector: Injector) {} 7 | 8 | handleError(error: unknown): void { 9 | const messageService = this.injector.get(MessageService); 10 | messageService.error('We are sorry. An error happened.'); 11 | console.error(error); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/services/user-loader.guard.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | 3 | import { SecurityService } from '@eternal/shared/security'; 4 | import { Observable } from 'rxjs'; 5 | import { filter, map } from 'rxjs/operators'; 6 | 7 | @Injectable({ providedIn: 'root' }) 8 | export class UserLoaderGuard { 9 | #securityService = inject(SecurityService); 10 | 11 | canActivate(): Observable | boolean { 12 | return this.#securityService.getLoaded$().pipe( 13 | map((loaded) => { 14 | if (!loaded) { 15 | this.#securityService.load(); 16 | } 17 | return loaded; 18 | }), 19 | filter((loaded) => loaded) 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/sidemenu/sidemenu.component.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/app/shell/sidemenu/sidemenu.component.scss: -------------------------------------------------------------------------------- 1 | 2 | ul { 3 | padding: 0; 4 | text-align: center; 5 | 6 | li { 7 | list-style-type: none; 8 | padding: 1em; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/copenhagen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/copenhagen.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/darmstadt-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/darmstadt-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/darmstadt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/darmstadt.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/detroit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/detroit.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/firenze.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/firenze.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/granada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/granada.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/large-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/large-bg.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/logo.png -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/london.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/london.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/luebeck.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/luebeck.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/reykjavík.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/reykjavík.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/shanghai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/shanghai.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/vienna-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/vienna-small.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/assets/vienna.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/assets/vienna.jpg -------------------------------------------------------------------------------- /test-projects/angular-iv/src/environments/environment.development.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | baseUrl: 'https://api.eternal-holidays.net', 3 | }; 4 | -------------------------------------------------------------------------------- /test-projects/angular-iv/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/src/favicon.ico -------------------------------------------------------------------------------- /test-projects/angular-iv/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular 6 | 7 | 8 | 9 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{html,ts}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/actual/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/angular-iv/tests/actual/.gitkeep -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/auto-tagging.config.ts: -------------------------------------------------------------------------------- 1 | import { SheriffConfig } from "@softarc/sheriff-core"; 2 | 3 | export const config: SheriffConfig = { 4 | depRules: { 5 | 'root': 'noTag', 6 | 'noTag': 'noTag' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/customer-api-re-exports.index.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core'; 2 | import { CustomersRepository } from '../data'; 3 | export { bookingsRoutes } from '../../bookings'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class CustomersApi { 7 | repo = inject(CustomersRepository); 8 | get selectedCustomer$() { 9 | return this.repo.selectedCustomer$; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/empty-sheriff.config.ts: -------------------------------------------------------------------------------- 1 | import { SheriffConfig } from "@softarc/sheriff-core"; 2 | 3 | export const config: SheriffConfig = { 4 | autoTagging: false, 5 | tagging: {}, 6 | depRules: {} 7 | }; 8 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/expected/auto-tagging-lint.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test-projects/angular-iv/tests/expected/cli-verify-success.txt: -------------------------------------------------------------------------------- 1 | 2 | Verification Report 3 | 4 | No issues found. Well done! 5 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/angular-iv/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/test-projects/nextjs-i/app/favicon.ico -------------------------------------------------------------------------------- /test-projects/nextjs-i/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | @theme { 4 | --font-sans: var(--font-geist-sans); 5 | --font-mono: var(--font-geist-mono); 6 | } 7 | 8 | :root { 9 | --background: #ffffff; 10 | --foreground: #171717; 11 | } 12 | 13 | @media (prefers-color-scheme: dark) { 14 | :root { 15 | --background: #0a0a0a; 16 | --foreground: #ededed; 17 | } 18 | } 19 | 20 | body { 21 | color: var(--foreground); 22 | background: var(--background); 23 | font-family: Arial, Helvetica, sans-serif; 24 | } 25 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/shared/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { IconLink } from './icon-link'; 2 | -------------------------------------------------------------------------------- /test-projects/nextjs-i/shell/index.ts: -------------------------------------------------------------------------------- 1 | export { Footer } from './footer'; 2 | -------------------------------------------------------------------------------- /test-projects/remove-paths.mjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import * as fs from "fs"; 4 | 5 | const file = process.argv[2]; 6 | 7 | const tmpDir = process.env['TMP_DIR'] 8 | const lastSegment = tmpDir.split('/').pop() 9 | 10 | let content = fs.readFileSync(file, { 11 | encoding: "utf-8" 12 | }); 13 | const regExp = new RegExp(`"[^"]+/${lastSegment}/[^/]+`, 'g') 14 | const cleanedContent = content.replace(regExp, '".') 15 | const formattedContent = JSON.stringify(JSON.parse(cleanedContent), null, 2); 16 | fs.writeFileSync(file, formattedContent, { encoding: "utf-8" }); 17 | -------------------------------------------------------------------------------- /test-projects/typescript-i/.gitignore: -------------------------------------------------------------------------------- 1 | /lint.json 2 | -------------------------------------------------------------------------------- /test-projects/typescript-i/configs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { "warnOnUnsupportedTypeScriptVersion": true }, 5 | "env": { 6 | "browser": false 7 | }, 8 | "overrides": [ 9 | { 10 | "files": ["*.ts"], 11 | "extends": ["plugin:@softarc/sheriff/legacy"] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /test-projects/typescript-i/configs/eslint.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const tseslint = require('typescript-eslint'); 3 | const sheriff = require('@softarc/eslint-plugin-sheriff'); 4 | 5 | module.exports = tseslint.config({ 6 | files: ['**/*.ts'], 7 | extends: [sheriff.configs.all], 8 | }); 9 | -------------------------------------------------------------------------------- /test-projects/typescript-i/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-i", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@typescript-eslint/eslint-plugin": "^5.62.0", 14 | "eslint": "^8.45.0", 15 | "eslint-config-standard-with-typescript": "^37.0.0", 16 | "eslint-plugin-import": "^2.28.0", 17 | "eslint-plugin-n": "^16.0.1", 18 | "eslint-plugin-promise": "^6.1.1", 19 | "typescript": "4.8", 20 | "typescript-eslint": "^8.1.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-projects/typescript-i/sheriff.config.ts: -------------------------------------------------------------------------------- 1 | import { SheriffConfig } from '@softarc/sheriff-core'; 2 | 3 | export const sheriffConfig: SheriffConfig = { 4 | version: 1, 5 | tagging: { 'src/': '' }, 6 | depRules: { 7 | root: 'web', 8 | data: '', 9 | logic: 'data', 10 | web: 'logic', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/data/credentials.ts: -------------------------------------------------------------------------------- 1 | type Credentials = { name: string; password: string }; 2 | 3 | export const credentials: Credentials = { 4 | name: 'sa', 5 | password: 'oracle', 6 | }; 7 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/data/db.service.ts: -------------------------------------------------------------------------------- 1 | import { credentials } from './credentials'; 2 | 3 | export class DbService { 4 | init() { 5 | console.log(`connecting with ${credentials.name}`); 6 | } 7 | 8 | select(sql: string): void { 9 | console.log(sql); 10 | } 11 | 12 | exec(sql: string): void { 13 | console.log(sql); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/data/index.ts: -------------------------------------------------------------------------------- 1 | export { DbService } from './db.service'; 2 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/logic/checkout.ts: -------------------------------------------------------------------------------- 1 | import { DbService } from '../data'; 2 | 3 | export class Checkout { 4 | constructor() { 5 | const dbService = new DbService(); 6 | dbService.init(); 7 | } 8 | 9 | checkout(basket: number) { 10 | return basket; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/logic/index.ts: -------------------------------------------------------------------------------- 1 | export { Checkout } from './checkout'; 2 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/main.ts: -------------------------------------------------------------------------------- 1 | import { CheckoutController } from '@app/web/checkout-controller'; 2 | 3 | const controller = new CheckoutController(); 4 | controller.init(); 5 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/web/checkout-controller.ts: -------------------------------------------------------------------------------- 1 | import { Checkout } from '../logic'; 2 | import { DbService } from '@app/data'; 3 | 4 | export class CheckoutController { 5 | constructor() { 6 | const dbService = new DbService(); 7 | console.log(`attempting to connect to db...`); 8 | } 9 | 10 | init() { 11 | const checkout = new Checkout(); 12 | checkout.checkout(5); 13 | console.log('starting'); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test-projects/typescript-i/src/web/index.ts: -------------------------------------------------------------------------------- 1 | export { CheckoutController } from './checkout-controller'; 2 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": false, 11 | "target": "es2015", 12 | "module": "esnext", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "strict": true, 17 | "baseUrl": ".", 18 | "paths": { 19 | "@softarc/eslint-plugin-sheriff": ["packages/eslint-plugin/src/index.ts"], 20 | "@softarc/sheriff-core": ["packages/core/src/index.ts"] 21 | } 22 | }, 23 | "exclude": ["node_modules", "tmp"] 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["**/*.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /video-preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/softarc-consulting/sheriff/757a38d099a42dae3f23db8cb61b7644a1f4a01e/video-preview.jpg -------------------------------------------------------------------------------- /vitest.config.ci.ts: -------------------------------------------------------------------------------- 1 | import defaultConfig from './vitest.config'; 2 | import { defineConfig, mergeConfig } from 'vitest/config'; 3 | 4 | export default mergeConfig( 5 | defaultConfig, 6 | defineConfig({ 7 | test: { 8 | include: ['packages/**/*.spec.ts', 'packages/**/*.full-spec.ts'], 9 | coverage: { enabled: true }, 10 | }, 11 | }), 12 | ); 13 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | test: { 6 | coverage: { 7 | provider: 'v8', 8 | reporter: ['text', 'html', 'lcov'], 9 | include: ['packages/*/src/lib/**/*.ts'], 10 | }, 11 | include: ['packages/**/*.spec.ts'], 12 | setupFiles: ['packages/core/src/lib/test/expect.extensions.ts'], 13 | alias: { 14 | '@softarc/eslint-plugin-sheriff': resolve( 15 | './packages/eslint-plugin/src/index.ts', 16 | ), 17 | '@softarc/sheriff-core': resolve('./packages/core/src/index.ts'), 18 | }, 19 | }, 20 | }); 21 | --------------------------------------------------------------------------------