diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json index 8df7338..9316860 100644 --- a/.codesandbox/tasks.json +++ b/.codesandbox/tasks.json @@ -2,8 +2,8 @@ // These tasks will run in order when initializing your CodeSandbox project. "setupTasks": [ { - "command": "npm install", - "name": "Install Dependencies" + "name": "Install Dependencies", + "command": "npm install" } ], diff --git a/.env b/.env index 889cf9e..288da03 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -NEXT_PUBLIC_ENVIRONMENT="production" +NEXT_PUBLIC_ENVIRONMENT=production diff --git a/.env.development b/.env.development index 565e615..b2aba18 100644 --- a/.env.development +++ b/.env.development @@ -1 +1 @@ -NEXT_PUBLIC_ENVIRONMENT="development" +NEXT_PUBLIC_ENVIRONMENT=development diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index b69c58c..2fbf0b2 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -6,10 +6,7 @@ name: Audit # - [ ] coverage drops on: # yamllint disable-line rule:truthy - pull_request: - types: - - synchronize - - opened + - push jobs: bundle-size: @@ -35,3 +32,32 @@ jobs: with: build-script: npm run build bundlewatch-github-token: ${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }} + + performance: + name: Audit the performance + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout repository + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + name: Setup Node.js @v20 + with: + cache: npm + node-version: 20.x + + - run: npm ci + name: Install dependencies + + - run: npm install -g @lhci/cli@0.14.x + name: Install Lighthouse CLI + + - run: npm run build + name: Build project + + - run: lhci autorun + name: Run Lighthouse CI + env: + LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }} diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 0000000..0c33fa2 --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,26 @@ +name: Dependabot auto-merge +on: + - pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + name: Update the dependencies + runs-on: ubuntu-latest + if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'theholocron/nextjs-template' + steps: + - uses: dependabot/fetch-metadata@4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d + name: Fetch Dependabot metadata + id: metadata + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - run: gh pr merge --auto --merge "$PR_URL" + name: Enable auto-merge for Dependabot PRs + if: steps.metadata.outputs.update-type == 'version-update:semver-patch' + env: + PR_URL: ${{github.event.pull_request.html_url}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4c8c60d..34484f2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,10 +36,12 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANNOTATE_ONLY: true # Allow annotations in the PR DISABLE_COMMENTS: false # Enable comments in the PR + IGNORE_GENERATED_FILES: true IGNORE_GITIGNORED_FILES: true LINTER_RULES_PATH: / CSS_FILE_NAME: "stylelint.config.js" EDITORCONFIG_FILE_NAME: ".editorconfig-checker.json" + FILTER_REGEX_EXCLUDE: "^public/.*" FIX_CSS_PRETTIER: true FIX_ENV: true FIX_GRAPHQL_PRETTIER: true diff --git a/.gitignore b/.gitignore index 17fb936..acf4a86 100755 --- a/.gitignore +++ b/.gitignore @@ -88,7 +88,6 @@ reports dist super-linter-output .next -next-env.d.ts out build .vercel diff --git a/app/msw/handlers.ts b/app/msw/handlers.ts index c919b4b..527ff41 100644 --- a/app/msw/handlers.ts +++ b/app/msw/handlers.ts @@ -1,7 +1,4 @@ import { handlers as authHandlers } from "../../src/auth/handlers"; import { handlers as taskHandlers } from "../../src/tasks/handlers"; -export const handlers = [ - ...taskHandlers, - ...authHandlers, -]; +export const handlers = [...taskHandlers, ...authHandlers]; diff --git a/lighthouserc.json b/lighthouserc.json new file mode 100644 index 0000000..8f9e407 --- /dev/null +++ b/lighthouserc.json @@ -0,0 +1,14 @@ +{ + "ci": { + "collect": { + "url": ["http://localhost:3000/"], + "startServerCommand": "npx serve@latest out" + }, + "assert": { + "preset": "lighthouse:recommended" + }, + "upload": { + "target": "temporary-public-storage" + } + } +} diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..40c3d68 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/next.config.mjs b/next.config.mjs index 81aa05b..96e5f6b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,5 +1,15 @@ -/** @type {import('next').NextConfig} */ +/** + * @see https://nextjs.org/docs/app/building-your-application/configuring + * @type {import('next').NextConfig} + */ +import bundleAnalyzer from "@next/bundle-analyzer"; -const nextConfig = {}; +const config = { + output: "export", +}; -export default nextConfig; +const withBundleAnalyzer = bundleAnalyzer({ + enabled: process.env.ANALYZE === "true", +}); + +export default withBundleAnalyzer(config); diff --git a/package-lock.json b/package-lock.json index 308f3e3..953f856 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,10 @@ "name": "@theholocron/nextjs-template", "version": "1.0.1", "license": "GPL-3.0", + "dependencies": { + "@next/bundle-analyzer": "^15.0.3", + "next": "^14" + }, "devDependencies": { "@codesandbox/storybook-addon": "^0.2.0", "@storybook/nextjs": "^8.2.5", @@ -25,7 +29,6 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.2", "alexjs": "^1.0.0", - "next": "^14", "react": "^18", "react-dom": "^18", "vite": "^5.4.9" @@ -2976,6 +2979,15 @@ "ms": "^2.1.1" } }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@dual-bundle/import-meta-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", @@ -5061,11 +5073,19 @@ "node": ">=18" } }, + "node_modules/@next/bundle-analyzer": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-15.0.3.tgz", + "integrity": "sha512-x7ZNvpoQPO0C5ZG//qVp21Qs3v6+C8LBJmdu9DKj4/NmjlnwoQ7dqRZ/nKZcwVhkFT7BHf+Qd5FaeHq9IDJvDQ==", + "license": "MIT", + "dependencies": { + "webpack-bundle-analyzer": "4.10.1" + } + }, "node_modules/@next/env": { "version": "14.2.17", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.17.tgz", "integrity": "sha512-MCgO7VHxXo8sYR/0z+sk9fGyJJU636JyRmkjc7ZJY8Hurl8df35qG5hoAh5KMs75FLjhlEo9bb2LGe89Y/scDA==", - "dev": true, "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { @@ -5075,7 +5095,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5092,7 +5111,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5109,7 +5127,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5126,7 +5143,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5143,7 +5159,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5160,7 +5175,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5177,7 +5191,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5194,7 +5207,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5211,7 +5223,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5527,6 +5538,12 @@ "node": ">=12" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "license": "MIT" + }, "node_modules/@radix-ui/number": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", @@ -9044,14 +9061,12 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/helpers": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", @@ -10696,7 +10711,6 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -10715,6 +10729,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/adjust-sourcemap-loader": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", @@ -13062,7 +13088,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, "dependencies": { "streamsearch": "^1.1.0" }, @@ -13269,7 +13294,6 @@ "version": "1.0.30001677", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", - "dev": true, "funding": [ { "type": "opencollective", @@ -13650,7 +13674,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "dev": true, "license": "MIT" }, "node_modules/cliui": { @@ -14731,6 +14754,12 @@ "dev": true, "license": "MIT" }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -15483,7 +15512,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true, "license": "MIT" }, "node_modules/duplexer3": { @@ -15952,7 +15980,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -18092,7 +18119,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -18112,6 +18138,21 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -18707,7 +18748,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, "license": "MIT" }, "node_modules/html-minifier-terser": { @@ -19922,7 +19962,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21642,7 +21681,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -22821,7 +22859,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -24908,6 +24945,15 @@ "node": ">=4" } }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -25010,7 +25056,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -25043,7 +25088,6 @@ "version": "14.2.17", "resolved": "https://registry.npmjs.org/next/-/next-14.2.17.tgz", "integrity": "sha512-hNo/Zy701DDO3nzKkPmsLRlDfNCtb1OJxFUvjGEl04u7SFa3zwC6hqsOUzMajcaEOEV8ey1GjvByvrg0Qr5AiQ==", - "dev": true, "license": "MIT", "dependencies": { "@next/env": "14.2.17", @@ -25094,7 +25138,6 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -25123,7 +25166,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dev": true, "license": "MIT", "dependencies": { "client-only": "0.0.1" @@ -25990,6 +26032,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -26789,7 +26840,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -27739,7 +27789,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -27824,7 +27873,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", @@ -29343,7 +29391,6 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -29640,6 +29687,20 @@ "license": "MIT", "optional": true }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -29772,7 +29833,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -30311,7 +30371,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, "engines": { "node": ">=10.0.0" } @@ -31787,6 +31846,15 @@ "dev": true, "license": "MIT" }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", @@ -31962,7 +32030,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/tty-browserify": { @@ -34110,6 +34177,63 @@ } } }, + "node_modules/webpack-bundle-analyzer": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", + "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", + "gzip-size": "^6.0.0", + "html-escaper": "^2.0.2", + "is-plain-object": "^5.0.0", + "opener": "^1.5.2", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/webpack-dev-middleware": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.3.tgz", diff --git a/package.json b/package.json index 6ab4676..f3ee884 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,20 @@ { "name": "@theholocron/nextjs-template", + "version": "1.0.1", "description": "", "homepage": "https://github.com/theholocron/nextjs-template#readme", + "bugs": { + "url": "https://github.com/theholocron/nextjs-template/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/theholocron/nextjs-template.git" + }, + "license": "GPL-3.0", "author": { "name": "Newton Koumantzelis" }, - "main": "dist/nextjs-template.cjs.js", - "module": "dist/nextjs-template.es.js", - "style": "dist/style.css", - "types": "dist/index.d.ts", + "type": "module", "exports": { ".": { "import": "./dist/nextjs-template.es.js", @@ -16,49 +22,21 @@ }, "./style": "./dist/style.css" }, + "main": "dist/nextjs-template.cjs.js", + "module": "dist/nextjs-template.es.js", + "types": "dist/index.d.ts", + "style": "dist/style.css", "files": [ "dist/*" ], - "type": "module", - "version": "1.0.1", - "repository": { - "type": "git", - "url": "git+https://github.com/theholocron/nextjs-template.git" - }, - "bugs": { - "url": "https://github.com/theholocron/nextjs-template/issues" - }, - "releases": "https://github.com/theholocron/nextjs-template/releases", - "wiki": "https://github.com/theholocron/nextjs-template/wiki", - "license": "GPL-3.0", - "packageManager": "npm@9.5.0", - "bundlewatch": { - "files": [ - { - "path": "./dist/**/*.css", - "maxSize": "50 kB" - }, - { - "path": "./dist/**/*.js", - "maxSize": "100 kB" - } - ] - }, - "msw": { - "workerDirectory": [ - "public" - ] - }, - "publishConfig": { - "access": "public" - }, "scripts": { - "dev": "next dev", - "dev:vite": "vite", "build": "next build", - "build:vite": "vite build", "build:storybook": "storybook build", + "build:vite": "vite build", + "dev": "next dev", + "dev:vite": "vite", "lint": "docker run -e LOG_LEVEL=DEBUG -e RUN_LOCAL=true -v .:/tmp/lint --rm ghcr.io/super-linter/super-linter:latest", + "prepare": "husky", "preview": "vite preview", "start": "next start", "start:storybook": "storybook dev -p 6006", @@ -66,9 +44,9 @@ "test:cypress": "cypress open", "test:storybook": "test-storybook --coverage" }, - "engines": { - "node": ">=20", - "npm": ">=10" + "dependencies": { + "@next/bundle-analyzer": "^15.0.3", + "next": "^14" }, "devDependencies": { "@codesandbox/storybook-addon": "^0.2.0", @@ -87,7 +65,6 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.2", "alexjs": "^1.0.0", - "next": "^14", "react": "^18", "react-dom": "^18", "vite": "^5.4.9" @@ -96,5 +73,32 @@ "next": "^14", "react": "^18", "react-dom": "^18" - } + }, + "packageManager": "npm@9.5.0", + "engines": { + "node": ">=20", + "npm": ">=10" + }, + "publishConfig": { + "access": "public" + }, + "bundlewatch": { + "files": [ + { + "path": "./.next/static/css/*.css", + "maxSize": "50 kB" + }, + { + "path": "./.next/static/chunks/*.js", + "maxSize": "100 kB" + } + ] + }, + "msw": { + "workerDirectory": [ + "public" + ] + }, + "releases": "https://github.com/theholocron/nextjs-template/releases", + "wiki": "https://github.com/theholocron/nextjs-template/wiki" } diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 6eb8052..d790913 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -8,149 +8,149 @@ * - Please do NOT serve this file on production. */ -const PACKAGE_VERSION = '2.6.0' -const INTEGRITY_CHECKSUM = '07a8241b182f8a246a7cd39894799a9e' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() - -self.addEventListener('install', function () { - self.skipWaiting() -}) - -self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) - -self.addEventListener('message', async function (event) { - const clientId = event.source.id - - if (!clientId || !self.clients) { - return - } - - const client = await self.clients.get(clientId) - - if (!client) { - return - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }) - break - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: { - client: { - id: client.id, - frameType: client.frameType, - }, - }, - }) - break - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister() - } - - break - } - } -}) - -self.addEventListener('fetch', function (event) { - const { request } = event - - // Bypass navigation requests. - if (request.mode === 'navigate') { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return - } - - // Generate unique request ID. - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId)) -}) +const PACKAGE_VERSION = "2.6.0"; +const INTEGRITY_CHECKSUM = "07a8241b182f8a246a7cd39894799a9e"; +const IS_MOCKED_RESPONSE = Symbol("isMockedResponse"); +const activeClientIds = new Set(); + +self.addEventListener("install", function () { + self.skipWaiting(); +}); + +self.addEventListener("activate", function (event) { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("message", async function (event) { + const clientId = event.source.id; + + if (!clientId || !self.clients) { + return; + } + + const client = await self.clients.get(clientId); + + if (!client) { + return; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + switch (event.data) { + case "KEEPALIVE_REQUEST": { + sendToClient(client, { + type: "KEEPALIVE_RESPONSE", + }); + break; + } + + case "INTEGRITY_CHECK_REQUEST": { + sendToClient(client, { + type: "INTEGRITY_CHECK_RESPONSE", + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }); + break; + } + + case "MOCK_ACTIVATE": { + activeClientIds.add(clientId); + + sendToClient(client, { + type: "MOCKING_ENABLED", + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }); + break; + } + + case "MOCK_DEACTIVATE": { + activeClientIds.delete(clientId); + break; + } + + case "CLIENT_CLOSED": { + activeClientIds.delete(clientId); + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId; + }); + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister(); + } + + break; + } + } +}); + +self.addEventListener("fetch", function (event) { + const { request } = event; + + // Bypass navigation requests. + if (request.mode === "navigate") { + return; + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === "only-if-cached" && request.mode !== "same-origin") { + return; + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return; + } + + // Generate unique request ID. + const requestId = crypto.randomUUID(); + event.respondWith(handleRequest(event, requestId)); +}); async function handleRequest(event, requestId) { - const client = await resolveMainClient(event) - const response = await getResponse(event, client, requestId) - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - ;(async function () { - const responseClone = response.clone() - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - requestId, - isMockedResponse: IS_MOCKED_RESPONSE in response, - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - body: responseClone.body, - headers: Object.fromEntries(responseClone.headers.entries()), - }, - }, - [responseClone.body], - ) - })() - } - - return response + const client = await resolveMainClient(event); + const response = await getResponse(event, client, requestId); + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + (async function () { + const responseClone = response.clone(); + + sendToClient( + client, + { + type: "RESPONSE", + payload: { + requestId, + isMockedResponse: IS_MOCKED_RESPONSE in response, + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + body: responseClone.body, + headers: Object.fromEntries(responseClone.headers.entries()), + }, + }, + [responseClone.body] + ); + })(); + } + + return response; } // Resolve the main client for the given event. @@ -158,136 +158,133 @@ async function handleRequest(event, requestId) { // that registered the worker. It's with the latter the worker should // communicate with during the response resolving phase. async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) - - if (activeClientIds.has(event.clientId)) { - return client - } - - if (client?.frameType === 'top-level') { - return client - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible' - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) + const client = await self.clients.get(event.clientId); + + if (activeClientIds.has(event.clientId)) { + return client; + } + + if (client?.frameType === "top-level") { + return client; + } + + const allClients = await self.clients.matchAll({ + type: "window", + }); + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === "visible"; + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id); + }); } async function getResponse(event, client, requestId) { - const { request } = event - - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = request.clone() - - function passthrough() { - const headers = Object.fromEntries(requestClone.headers.entries()) - - // Remove internal MSW request header so the passthrough request - // complies with any potential CORS preflight checks on the server. - // Some servers forbid unknown request headers. - delete headers['x-msw-intention'] - - return fetch(requestClone, { headers }) - } - - // Bypass mocking when the client is not active. - if (!client) { - return passthrough() - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough() - } - - // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer() - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: requestBuffer, - keepalive: request.keepalive, - }, - }, - [requestBuffer], - ) - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) - } - - case 'PASSTHROUGH': { - return passthrough() - } - } - - return passthrough() + const { request } = event; + + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = request.clone(); + + function passthrough() { + const headers = Object.fromEntries(requestClone.headers.entries()); + + // Remove internal MSW request header so the passthrough request + // complies with any potential CORS preflight checks on the server. + // Some servers forbid unknown request headers. + delete headers["x-msw-intention"]; + + return fetch(requestClone, { headers }); + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough(); + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough(); + } + + // Notify the client that a request has been intercepted. + const requestBuffer = await request.arrayBuffer(); + const clientMessage = await sendToClient( + client, + { + type: "REQUEST", + payload: { + id: requestId, + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: requestBuffer, + keepalive: request.keepalive, + }, + }, + [requestBuffer] + ); + + switch (clientMessage.type) { + case "MOCK_RESPONSE": { + return respondWithMock(clientMessage.data); + } + + case "PASSTHROUGH": { + return passthrough(); + } + } + + return passthrough(); } function sendToClient(client, message, transferrables = []) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel() - - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) - } - - resolve(event.data) - } - - client.postMessage( - message, - [channel.port2].concat(transferrables.filter(Boolean)), - ) - }) + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error); + } + + resolve(event.data); + }; + + client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean))); + }); } async function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error() - } - - const mockedResponse = new Response(response.body, response) - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }) - - return mockedResponse + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error(); + } + + const mockedResponse = new Response(response.body, response); + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }); + + return mockedResponse; } diff --git a/src/auth/handlers.ts b/src/auth/handlers.ts index 3cec65f..013d0f8 100644 --- a/src/auth/handlers.ts +++ b/src/auth/handlers.ts @@ -8,9 +8,7 @@ const authenticate = http.post("/authenticate", async () => { }, }; - return HttpResponse.json(user, { status: 201 }) + return HttpResponse.json(user, { status: 201 }); }); -export const handlers = [ - authenticate, -]; +export const handlers = [authenticate]; diff --git a/src/auth/index.ts b/src/auth/index.ts index fb62b74..22a58ff 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -1 +1 @@ -export { useAuth } from "./use-auth"; +export * from "./use-auth"; diff --git a/src/inbox/inbox.tsx b/src/inbox/inbox.tsx index c18e410..71f58ff 100644 --- a/src/inbox/inbox.tsx +++ b/src/inbox/inbox.tsx @@ -6,7 +6,7 @@ export interface InboxProps { error?: string; } -type TaskState = "TASK_PINNED" | "TASK_INBOX"; +type TaskState = "TASK_PINNED" | "TASK_INBOX" | "TASK_ARCHIVED"; export function Inbox (props: InboxProps) { const { @@ -16,8 +16,8 @@ export function Inbox (props: InboxProps) { const [tasks, dispatch] = useTasks(); // Archive or move the task back to inbox - const archiveTask = (archive: boolean, id: string) => { - dispatch({ type: archive ? "ARCHIVE_TASK" : "INBOX_TASK", id }); + const archiveTask = (actionType: "ARCHIVE_TASK" | "INBOX_TASK", id: string) => { + dispatch({ type: actionType, id }); }; // Delete task by id diff --git a/src/login/login.tsx b/src/login/login.tsx index c988982..c92122b 100644 --- a/src/login/login.tsx +++ b/src/login/login.tsx @@ -1,18 +1,14 @@ "use client"; -import * as React from "react"; import type { Credentials } from "../auth"; import "./login.css"; -export interface LoginFormProps extends React.HTMLProps { +export interface LoginFormProps { onSubmit: (formData: Credentials) => void; } -export function LoginForm (props: LoginFormProps) { - const { - onSubmit, - ...rest - } = props; +export function LoginForm(props: LoginFormProps) { + const { onSubmit, ...rest } = props; return (
{ event.preventDefault(); const elementsArray = Array.from(event.currentTarget.elements) as HTMLInputElement[]; - const formData = elementsArray.reduce((acc: { [key: string]: string }, elem: object) => { + const formData = elementsArray.reduce((acc: Partial, elem: HTMLInputElement) => { if (elem.name) { - acc[elem.name] = elem.value; + acc[elem.name as keyof Credentials] = elem.value; } return acc; - }, {}); + }, {} as Partial); // Ensure `formData` includes both `username` and `password` before calling `onSubmit` if (formData.username && formData.password) { @@ -76,7 +72,7 @@ export interface LoginProps { onLogIn: (credentials: Credentials) => void; } -export function Login (props: LoginProps) { +export function Login(props: LoginProps) { return (
diff --git a/src/tasks/handlers.ts b/src/tasks/handlers.ts index fd93790..baff7a7 100644 --- a/src/tasks/handlers.ts +++ b/src/tasks/handlers.ts @@ -4,6 +4,4 @@ import { Default as TaskListDefault } from "./task-list.story"; export const taskListHandler = http.get("/tasks", () => HttpResponse.json(TaskListDefault.args)); export const taskListErrorHandler = http.get("/tasks", () => HttpResponse.json([])); -export const handlers = [ - taskListHandler, -] +export const handlers = [taskListHandler]; diff --git a/src/tasks/task.story.tsx b/src/tasks/task.story.tsx index 444e083..7e35651 100644 --- a/src/tasks/task.story.tsx +++ b/src/tasks/task.story.tsx @@ -1,4 +1,4 @@ -import { Task } from "./task"; +import { Task, type TaskProps } from "./task"; export default { component: Task, @@ -12,7 +12,7 @@ export default { }; export const Default = { - render: (args) => ( + render: (args: TaskProps) => (
@@ -27,7 +27,7 @@ export const Default = { }; export const Pinned = { - render: (args) => ( + render: (args: TaskProps) => (
@@ -42,7 +42,7 @@ export const Pinned = { }; export const Archived = { - render: (args) => ( + render: (args: TaskProps) => (
@@ -59,7 +59,7 @@ export const Archived = { const longTitleString = `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`; export const LongTitle = { - render: (args) => ( + render: (args: TaskProps) => (
diff --git a/src/tasks/use-tasks.ts b/src/tasks/use-tasks.ts index 3d4383f..51db350 100644 --- a/src/tasks/use-tasks.ts +++ b/src/tasks/use-tasks.ts @@ -2,7 +2,7 @@ import * as React from "react"; -export interface Task { +interface Task { id: string; title: string; state: "TASK_INBOX" | "TASK_PINNED" | "TASK_ARCHIVED"; diff --git a/tsconfig.json b/tsconfig.json index 4714bd9..57b3588 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -17,28 +13,16 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, - "types": [ - "cypress", - "node" - ], + "types": ["cypress", "node"], "plugins": [ { "name": "next" } ], "paths": { - "@/*": [ - "./*" - ] + "@/*": ["./*"] } }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts" - ], - "exclude": [ - "node_modules" - ] + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] }