diff --git a/.DS_Store b/.DS_Store index 695d0da..9c0626f 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.eslintignore b/.eslintignore index 9385391..768fab3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,6 +2,7 @@ /blueprints/*/files/ # compiled output +/declarations/ /dist/ # misc diff --git a/.eslintrc.js b/.eslintrc.js index fa8f035..a2f8a27 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -48,6 +48,10 @@ module.exports = { parser: 'ember-eslint-parser', plugins: ['ember'], extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:ember/recommended-gts'], + rules: { + 'no-unused-vars': 'off', + 'no-undef': 'off', + } }, // node files { @@ -57,7 +61,6 @@ module.exports = { './.stylelintrc.{js,cjs}', './.template-lintrc.{js,cjs}', './ember-cli-build.js', - './playwright.config.ts', './testem.js', './tailwind.config.js', './blueprints/*/index.js', diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b937c91..f534a8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,28 +12,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: wyvox/action-setup-pnpm@v3 with: node-version: 20 - - uses: pnpm/action-setup@v2 - name: Install pnpm - with: - version: 8 - run_install: false - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - name: Install dependencies - run: pnpm install - - name: Install Playwright Browsers - run: npx playwright install --with-deps - name: Run Tests run: pnpm test diff --git a/.husky/_/pre-push b/.husky/_/pre-push new file mode 100755 index 0000000..cf59fe1 --- /dev/null +++ b/.husky/_/pre-push @@ -0,0 +1,60 @@ +#!/bin/sh + +if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then + set -x +fi + +if [ "$LEFTHOOK" = "0" ]; then + exit 0 +fi + +call_lefthook() +{ + if test -n "$LEFTHOOK_BIN" + then + "$LEFTHOOK_BIN" "$@" + elif lefthook -h >/dev/null 2>&1 + then + lefthook "$@" + else + dir="$(git rev-parse --show-toplevel)" + osArch=$(uname | tr '[:upper:]' '[:lower:]') + cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/') + if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" + then + "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@" + elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" + then + "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@" + elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" + then + "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@" + elif test -f "$dir/node_modules/lefthook/bin/index.js" + then + "$dir/node_modules/lefthook/bin/index.js" "$@" + + elif bundle exec lefthook -h >/dev/null 2>&1 + then + bundle exec lefthook "$@" + elif yarn lefthook -h >/dev/null 2>&1 + then + yarn lefthook "$@" + elif pnpm lefthook -h >/dev/null 2>&1 + then + pnpm lefthook "$@" + elif swift package plugin lefthook >/dev/null 2>&1 + then + swift package --disable-sandbox plugin lefthook "$@" + elif command -v mint >/dev/null 2>&1 + then + mint run csjones/lefthook-plugin "$@" + elif command -v npx >/dev/null 2>&1 + then + npx lefthook "$@" + else + echo "Can't find lefthook in PATH" + fi + fi +} + +call_lefthook run "pre-push" "$@" diff --git a/.husky/_/prepare-commit-msg b/.husky/_/prepare-commit-msg new file mode 100755 index 0000000..e8e8dda --- /dev/null +++ b/.husky/_/prepare-commit-msg @@ -0,0 +1,60 @@ +#!/bin/sh + +if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then + set -x +fi + +if [ "$LEFTHOOK" = "0" ]; then + exit 0 +fi + +call_lefthook() +{ + if test -n "$LEFTHOOK_BIN" + then + "$LEFTHOOK_BIN" "$@" + elif lefthook -h >/dev/null 2>&1 + then + lefthook "$@" + else + dir="$(git rev-parse --show-toplevel)" + osArch=$(uname | tr '[:upper:]' '[:lower:]') + cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/') + if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" + then + "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@" + elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" + then + "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@" + elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" + then + "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@" + elif test -f "$dir/node_modules/lefthook/bin/index.js" + then + "$dir/node_modules/lefthook/bin/index.js" "$@" + + elif bundle exec lefthook -h >/dev/null 2>&1 + then + bundle exec lefthook "$@" + elif yarn lefthook -h >/dev/null 2>&1 + then + yarn lefthook "$@" + elif pnpm lefthook -h >/dev/null 2>&1 + then + pnpm lefthook "$@" + elif swift package plugin lefthook >/dev/null 2>&1 + then + swift package --disable-sandbox plugin lefthook "$@" + elif command -v mint >/dev/null 2>&1 + then + mint run csjones/lefthook-plugin "$@" + elif command -v npx >/dev/null 2>&1 + then + npx lefthook "$@" + else + echo "Can't find lefthook in PATH" + fi + fi +} + +call_lefthook run "prepare-commit-msg" "$@" diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 7b08d54..0000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -pnpm run lint -pnpm run test:duplication -pnpm run test:ember --filter '!Acceptance' -# pnpm run verify-coverage 'dry-run' \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..f301fed --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +auto-install-peers=false diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 7820f24..0942536 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["typed-ember.glint-vscode","ms-playwright.playwright","dbaeumer.vscode-eslint","bradlc.vscode-tailwindcss","lifeart.vscode-ember-unstable","redhat.vscode-yaml","mikestead.dotenv","esbenp.prettier-vscode"] + "recommendations": ["typed-ember.glint-vscode","dbaeumer.vscode-eslint","bradlc.vscode-tailwindcss","lifeart.vscode-ember-unstable","redhat.vscode-yaml","mikestead.dotenv","esbenp.prettier-vscode"] } diff --git a/README.md b/README.md index 1a01d7f..ec208a1 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,47 @@ -# ember-boilerplate +# Ember-boilerplate [![Tests](https://github.com/TRIPTYK/ember-boilerplate/actions/workflows/ci.yml/badge.svg)](https://github.com/TRIPTYK/ember-boilerplate/actions/workflows/ci.yml) -## You can use this IF - -- ✅ You use tailwindcss -- ✅ You use typescript -- ✅ You use JWT based authentification -- ✅ You use pnpm -- ✅ You don't bother using TRIPTYK packages +

+ + Ember logo + + + TRIPTYK logo + +

## Preconfigured 5.x Ember project with -### Ember Side - - Embroider - Typescript integration (ember-cli-typescript) - Validations (ember-immer-changeset + yup) - Tailwindcss 3.x, with primary and secondary colors configured - Flash messages (ember-cli-flash) -- Tests (ember-test-selectors, ember-cli-page-object, playwright) +- Tests (ember-test-selectors, ember-cli-page-object) - Sessions,Login,Logout,... (ember-simple-auth,ember-simple-auth-token) -- Base ember adapter, serializer and controller +- Ember data next gen (RequestManager based) - Ember concurrency - Test seeding & parallelization (ember-exam) - dev & test mocking (msw) -- e2e testing (playwright) - Translations (ember-intl) - Authorizations (ember-can) - Pre-made registration flow (login/register/forgot-password) -### VSCODE IDE +## VSCODE IDE - VSCODE Ready, all rules are setup for a great developing experience. -### Additional Tooling +## Additional Tooling - With-backend: `with-backend.js` Starts the ember app with a backend synchronously. -- Code duplication: with jscpd. - Husky: checks linting + code duplication + integration & unit tests before commiting to VSC. -### Docker +## Docker - A docker image can be found in `images`. -### CI +## CI A github workflow CI is provided. @@ -86,7 +83,7 @@ Components are located: `app/components/`. ### API Mocking -Development mocks are in `public/mocks`. +Development mocks are in `app/handlers`. Testing mocks are split in the `tests` folders. ## Installation @@ -99,9 +96,8 @@ Testing mocks are split in the `tests` folders. ## Running / Development -- `ember serve` +- `pnpm start` - Visit your app at [http://localhost:4200](http://localhost:4200). -- Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). ### Running Tests @@ -112,12 +108,7 @@ Testing mocks are split in the `tests` folders. - `pnpm lint` - `pnpm lint:fix` -### Duplication - -- `pnpm test:duplication` - - ### Building -- `ember build` (development) -- `ember build --environment production` (production) +- `pnpm build --mode=dev` (development) +- `pnpm build` (production) diff --git a/app/adapters/application.ts b/app/adapters/application.ts deleted file mode 100644 index 09aa041..0000000 --- a/app/adapters/application.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { service } from '@ember/service'; -import JSONAPIAdapter from '@ember-data/adapter/json-api'; - -import config from '../config/environment'; - -import type SessionService from 'ember-boilerplate/services/session'; -import type FlashMessageService from 'ember-cli-flash/services/flash-messages'; - -export default class ApplicationAdapter extends JSONAPIAdapter { - @service declare session: SessionService; - @service declare flashMessages: FlashMessageService; - - get host() { - return config.host; - } - namespace = config.namespace; - - get headers(): Record { - const headers = { - Accept: 'application/vnd.api+json', - 'Content-Type': 'application/vnd.api+json', - Authorization: '', - }; - - if (this.session.isAuthenticated) { - headers[ - 'Authorization' - ] = `Bearer ${this.session.data.authenticated.accessToken}`; - } - - return headers; - } - - async handleResponse( - status: number, - headers: Record, - payload: Record, - requestData: Record - ) { - if (status === 401 && this.session.isAuthenticated) { - this.flashMessages.danger(`Unauthorized action`); - await this.session.invalidate(); - } else { - if (status > 500) { - // Internal - this.flashMessages.danger(`Fatal error : ${payload}`); - } else if (status > 400) { - // Bad request - } - } - - return super.handleResponse(status, headers, payload, requestData); - } - - urlForQueryRecord( - query: Record, - modelName: string | number - ): string { - const id = query['id'] as string | number; - - delete query['id']; - - return this.buildURL(modelName, id, null, 'findRecord', query); - } -} - -// DO NOT DELETE: this is how TypeScript knows how to look up your adapters. -declare module 'ember-data/types/registries/adapter' { - export default interface AdapterRegistry { - application: ApplicationAdapter; - } -} diff --git a/app/adapters/user.ts b/app/adapters/user.ts deleted file mode 100644 index a4c74de..0000000 --- a/app/adapters/user.ts +++ /dev/null @@ -1,25 +0,0 @@ -import JSONAPIAdapter from '../adapters/application'; - -export default class User extends JSONAPIAdapter { - urlForQueryRecord( - query: Record, - modelName: string | number - ) { - if (query['profile']) { - delete query['profile']; - - const url = `${this.host}/${this.namespace}/users/profile`; - - return url; - } - - return super.urlForQueryRecord(query, modelName); - } -} - -// DO NOT DELETE: this is how TypeScript knows how to look up your adapters. -declare module 'ember-data/types/registries/adapter' { - export default interface AdapterRegistry { - user: User; - } -} diff --git a/app/app.ts b/app/app.ts index 4b3f9d0..1a03bff 100644 --- a/app/app.ts +++ b/app/app.ts @@ -1,7 +1,11 @@ +import { setBuildURLConfig } from '@ember-data/request-utils'; import Application from '@ember/application'; +// @ts-expect-error +import initializer from 'ember-simple-auth/initializers/ember-simple-auth'; +// @ts-expect-error +import initializerJwt from '@triptyk/ember-simple-auth-token/initializers/simple-auth-token'; import config from 'ember-boilerplate/config/environment'; -import loadInitializers from 'ember-load-initializers'; import Resolver from 'ember-resolver'; export default class App extends Application { @@ -10,4 +14,15 @@ export default class App extends Application { Resolver = Resolver; } -loadInitializers(App, config.modulePrefix); +// ember-load-initializers not working anymore, registering manually +function loadInitializers() { + App.instanceInitializer(initializer); + App.instanceInitializer(initializerJwt); +} + +setBuildURLConfig({ + host: config.host, + namespace: config.namespace, +}); + +loadInitializers(); diff --git a/app/components/forms/forgot-password.gts b/app/components/forms/forgot-password.gts index 7ed7fb9..c314f7a 100644 --- a/app/components/forms/forgot-password.gts +++ b/app/components/forms/forgot-password.gts @@ -2,8 +2,7 @@ import { LinkTo } from '@ember/routing'; import InputsValidationComponent from 'ember-boilerplate/components/inputs/input-validation'; import t from 'ember-intl/helpers/t'; - -import YupForm from './yup-form'; +import TpkForm from '@triptyk/ember-input-validation/components/tpk-form'; import type { TOC } from '@ember/component/template-only'; import type { ForgotPasswordChangeset } from 'ember-boilerplate/changesets/forgot-password'; @@ -22,7 +21,7 @@ export interface FormsForgotPasswordSignature { } const FormsForgotPassword: TOC =