├── .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 | [1mVerification Report[0m
3 |
4 | [32mNo issues found. Well done![0m
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 |
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 | Id |
6 | FirstName |
7 | Name |
8 | Bonus Miles |
9 | Status |
10 |
11 |
12 |
13 | {{p.id}} |
14 | {{p.firstName}} |
15 | {{p.name}} |
16 | {{p.bonusMiles}} |
17 | {{p.passengerStatus}} |
18 |
19 |
--------------------------------------------------------------------------------
/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 | [1mVerification Report[0m
3 |
4 | [32mNo issues found. Well done![0m
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 |
--------------------------------------------------------------------------------