diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..53dafda --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +# Dependencies +node_modules + +# OS +.DS_Store + +# Build output +dist +stats.html + +# Favicon generation output +**/favicons/** +*.auto-generated* diff --git a/astro.config.ts b/astro.config.ts index 0db81c8..16b004c 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -15,5 +15,10 @@ export default defineConfig({ ], vite: { plugins: [visualizer()], + resolve: { + alias: { + 'date-fns/locale': 'date-fns/locale/index.js', + }, + }, }, }); diff --git a/package-lock.json b/package-lock.json index 4dd2875..f6edcb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "astro-compress": "1.1.28", "concurrently": "7.6.0", "date-fns": "2.29.3", + "favicons": "7.0.2", "iconify-icon-names": "1.1.0", "immer": "9.0.18", "locales-ts": "1.0.0", @@ -35,6 +36,7 @@ "puppeteer-report": "3.1.0", "rollup-plugin-visualizer": "5.9.0", "tailwindcss": "3.2.4", + "ts-node": "10.9.1", "type-fest": "3.5.3", "typescript": "4.9.4" }, @@ -610,6 +612,28 @@ "partytown": "bin/partytown.cjs" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@emmetio/abbreviation": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.2.3.tgz", @@ -1109,6 +1133,30 @@ "node": ">=10.13.0" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "node_modules/@types/acorn": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", @@ -2605,6 +2653,12 @@ "node": ">=14" } }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -3445,6 +3499,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -3634,6 +3694,20 @@ "reusify": "^1.0.4" } }, + "node_modules/favicons": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/favicons/-/favicons-7.0.2.tgz", + "integrity": "sha512-M/qE3ERHOBu0+Op+61jx8CdvOnSKrrl2zxUPpoGgsNyfjuGqRsK80zYoA5Uwdxl8QM4egfhBWZp1j7KK3YnOMg==", + "dev": true, + "dependencies": { + "escape-html": "^1.0.3", + "sharp": "^0.31.1", + "xml2js": "^0.4.23" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -4660,9 +4734,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -4864,6 +4938,12 @@ "sourcemap-codec": "^1.4.8" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/markdown-table": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz", @@ -7511,6 +7591,12 @@ "suf-log": "^2.5.3" } }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -8238,6 +8324,73 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-resolver": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tsconfig-resolver/-/tsconfig-resolver-3.0.1.tgz", @@ -8613,6 +8766,12 @@ "node": ">=8" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/vfile": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", @@ -9336,6 +9495,28 @@ } } }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -9447,6 +9628,15 @@ "fd-slicer": "~1.1.0" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -9918,6 +10108,27 @@ "integrity": "sha512-Zbr2Eo0AQ4yzmQr/36/h+6LKjmdVBB3Q5cGzO6rtlIKB/IOpbQVUZW+XAnhpJmJr9sIF97OZjgbhG9k7Sjn4yw==", "dev": true }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, "@emmetio/abbreviation": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.2.3.tgz", @@ -10316,6 +10527,30 @@ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "@types/acorn": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", @@ -11473,6 +11708,12 @@ "path-type": "^4.0.0" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -12002,6 +12243,12 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -12142,6 +12389,17 @@ "reusify": "^1.0.4" } }, + "favicons": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/favicons/-/favicons-7.0.2.tgz", + "integrity": "sha512-M/qE3ERHOBu0+Op+61jx8CdvOnSKrrl2zxUPpoGgsNyfjuGqRsK80zYoA5Uwdxl8QM4egfhBWZp1j7KK3YnOMg==", + "dev": true, + "requires": { + "escape-html": "^1.0.3", + "sharp": "^0.31.1", + "xml2js": "^0.4.23" + } + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -12872,9 +13130,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonc-parser": { @@ -13028,6 +13286,12 @@ "sourcemap-codec": "^1.4.8" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "markdown-table": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.2.tgz", @@ -14847,6 +15111,12 @@ "suf-log": "^2.5.3" } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -15379,6 +15649,47 @@ "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", "dev": true }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, "tsconfig-resolver": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tsconfig-resolver/-/tsconfig-resolver-3.0.1.tgz", @@ -15632,6 +15943,12 @@ "sade": "^1.7.3" } }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "vfile": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.4.tgz", @@ -16051,6 +16368,22 @@ "dev": true, "requires": {} }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -16140,6 +16473,12 @@ "fd-slicer": "~1.1.0" } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index f39746e..f7fe197 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,10 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "generate-cv": "node scripts/generate-cv.cjs", - "prettier:check": "prettier --check --ignore-path .gitignore .", - "prettier:write": "prettier --write --ignore-path .gitignore .", + "generate-cv": "ts-node scripts/generate-cv.ts", + "generate-favicons": "ts-node scripts/generate-favicons.ts", + "prettier:check": "prettier --check .", + "prettier:write": "prettier --write .", "astro:check": "astro check", "ts:check": "tsc --jsx preserve --skipLibCheck", "check": "concurrently npm:*:check" @@ -36,6 +37,7 @@ "astro-compress": "1.1.28", "concurrently": "7.6.0", "date-fns": "2.29.3", + "favicons": "7.0.2", "iconify-icon-names": "1.1.0", "immer": "9.0.18", "locales-ts": "1.0.0", @@ -48,6 +50,7 @@ "puppeteer-report": "3.1.0", "rollup-plugin-visualizer": "5.9.0", "tailwindcss": "3.2.4", + "ts-node": "10.9.1", "type-fest": "3.5.3", "typescript": "4.9.4" } diff --git a/public/favicon.svg b/public/favicon.svg deleted file mode 100644 index 0f39062..0000000 --- a/public/favicon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - diff --git a/public/favicons/android-chrome-192x192.png b/public/favicons/android-chrome-192x192.png new file mode 100644 index 0000000..5b8e6b6 Binary files /dev/null and b/public/favicons/android-chrome-192x192.png differ diff --git a/public/favicons/android-chrome-512x512.png b/public/favicons/android-chrome-512x512.png new file mode 100644 index 0000000..8b65072 Binary files /dev/null and b/public/favicons/android-chrome-512x512.png differ diff --git a/public/favicons/apple-touch-icon.png b/public/favicons/apple-touch-icon.png new file mode 100644 index 0000000..1098e52 Binary files /dev/null and b/public/favicons/apple-touch-icon.png differ diff --git a/public/favicons/favicon-16x16.png b/public/favicons/favicon-16x16.png new file mode 100644 index 0000000..9dbbf4a Binary files /dev/null and b/public/favicons/favicon-16x16.png differ diff --git a/public/favicons/favicon-32x32.png b/public/favicons/favicon-32x32.png new file mode 100644 index 0000000..4d5433f Binary files /dev/null and b/public/favicons/favicon-32x32.png differ diff --git a/public/favicons/favicon.ico b/public/favicons/favicon.ico new file mode 100644 index 0000000..ded715d Binary files /dev/null and b/public/favicons/favicon.ico differ diff --git a/public/favicons/manifest.webmanifest b/public/favicons/manifest.webmanifest new file mode 100644 index 0000000..d0df906 --- /dev/null +++ b/public/favicons/manifest.webmanifest @@ -0,0 +1,26 @@ +{ + "name": "Mark Freeman - Senior React Developer", + "short_name": "Mark Freeman - Senior React Developer", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sodales ac dui at vestibulum. In condimentum metus id dui tincidunt, in blandit mi vehicula.", + "dir": "auto", + "lang": "en-US", + "display": "standalone", + "orientation": "any", + "start_url": ".", + "background_color": "#fff", + "theme_color": "#fff", + "icons": [ + { + "src": "/favicons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/favicons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + } + ] +} \ No newline at end of file diff --git a/scripts/generate-cv.cjs b/scripts/generate-cv.cjs deleted file mode 100644 index 31332ad..0000000 --- a/scripts/generate-cv.cjs +++ /dev/null @@ -1,50 +0,0 @@ -const { exec } = require('node:child_process'); -const path = require('node:path'); -const fs = require('node:fs'); -const puppeteer = require('puppeteer'); -const report = require('puppeteer-report'); - -const waitFor = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); - -const retry = async ({ promise, retries, retryTime }) => { - try { - return await promise(); - } catch (error) { - if (retries <= 0) throw error; - - await waitFor(retryTime); - - return await retry({ promise, retries: retries - 1, retryTime }); - } -}; - -const config = { - path: path.join(__dirname, '..', 'public', 'cv.pdf'), - format: 'A4', - printBackground: true, - margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' }, -}; - -const main = async () => { - const child = exec('npm run dev'); - - const browser = await puppeteer.launch({ headless: true }); - - const page = await browser.newPage(); - - await page.setViewport({ width: 794, height: 1122, deviceScaleFactor: 2 }); - - await retry({ - promise: () => page.goto('http://localhost:3000/pdf', { waitUntil: 'networkidle0' }), - retries: 5, - retryTime: 1000, - }); - - await report.pdfPage(page, config); - - await browser.close(); - - child.kill(); -}; - -main(); diff --git a/scripts/generate-cv.ts b/scripts/generate-cv.ts new file mode 100644 index 0000000..3325dca --- /dev/null +++ b/scripts/generate-cv.ts @@ -0,0 +1,59 @@ +import { exec } from 'node:child_process'; +import * as path from 'node:path'; +import * as puppeteer from 'puppeteer'; +import { pdfPage } from 'puppeteer-report'; + +const waitFor = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +const goTo = async (page: puppeteer.Page, url: string) => { + await page.goto(url, { waitUntil: 'networkidle0' }); +}; + +type GoToReturn = ReturnType; + +interface RetryOptions { + promise: () => GoToReturn; + retries: number; + retryTime: number; +} + +const retry = async ({ promise, retries, retryTime }: RetryOptions): GoToReturn => { + try { + return await promise(); + } catch (error) { + if (retries <= 0) throw error; + + await waitFor(retryTime); + + return await retry({ promise, retries: retries - 1, retryTime }); + } +}; + +const main = async () => { + const child = exec('npm run dev'); + + const browser = await puppeteer.launch({ headless: true }); + + const page = await browser.newPage(); + + await page.setViewport({ width: 794, height: 1122, deviceScaleFactor: 2 }); + + await retry({ + promise: () => goTo(page, 'http://localhost:3000/pdf'), + retries: 5, + retryTime: 1000, + }); + + await pdfPage(page, { + path: path.join(__dirname, '..', 'public', 'cv.pdf'), + format: 'A4', + printBackground: true, + margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' }, + }); + + await browser.close(); + + child.kill(); +}; + +main(); diff --git a/scripts/generate-favicons.ts b/scripts/generate-favicons.ts new file mode 100644 index 0000000..dfc990e --- /dev/null +++ b/scripts/generate-favicons.ts @@ -0,0 +1,53 @@ +import { favicons, config as faviconsConfig, FaviconFile, FaviconImage } from 'favicons'; +import config from '../src/data/config'; +import { mkdir, writeFile, rm } from 'fs/promises'; +import { existsSync } from 'fs'; + +const faviconsDirectory = './public/favicons'; + +const saveFile = async (file: FaviconFile | FaviconImage) => { + await writeFile(`${faviconsDirectory}/${file.name}`, file.contents); + console.log(`${file.name} has been created successfully`); +}; + +(async () => { + const { faviconPath } = config.meta; + + const response = await favicons(`.${faviconPath}`, { + ...faviconsConfig.defaults, + path: '/favicons', + appName: config.meta.title, + appDescription: config.meta.description, + appShortName: config.meta.title, + lang: config.i18n.locale.code, + start_url: '.', + icons: { + android: ['android-chrome-192x192.png', 'android-chrome-512x512.png'], + windows: false, + yandex: false, + appleStartup: false, + appleIcon: ['apple-touch-icon.png'], + favicons: ['favicon-16x16.png', 'favicon-32x32.png', 'favicon.ico'], + }, + }); + + if (existsSync(faviconsDirectory)) { + await rm(faviconsDirectory, { recursive: true }); + } + + await mkdir(faviconsDirectory); + + await Promise.all([...response.images, ...response.files].map(saveFile)); + + const comments = [ + '\n', + '\n', + ]; + + const formattedHtml = response.html.map((line) => line.replace('>', '/>')).join('\n'); + + const pathToFaviconsFile = './src/web/head/favicons.auto-generated.astro'; + + await writeFile(pathToFaviconsFile, [...comments, formattedHtml, '\n']); + console.log(`${pathToFaviconsFile} has been updated successfully`); +})(); diff --git a/src/data/config.ts b/src/data/config.ts index b0816fa..c2fa3a4 100644 --- a/src/data/config.ts +++ b/src/data/config.ts @@ -1,5 +1,5 @@ import type { Config } from '@/types/data'; -import enUS from 'date-fns/locale/en-US/index.js'; +import { enUS } from 'date-fns/locale'; import type { ReadonlyDeep } from 'type-fest'; const config = { @@ -14,7 +14,7 @@ const config = { title: 'Mark Freeman - Senior React Developer', description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sodales ac dui at vestibulum. In condimentum metus id dui tincidunt, in blandit mi vehicula.', - favicon: '/favicon.svg', + faviconPath: '/src/assets/my-image.jpeg', }, pdf: { footer: diff --git a/src/types/config/meta-config.types.ts b/src/types/config/meta-config.types.ts index 5cff761..585f202 100644 --- a/src/types/config/meta-config.types.ts +++ b/src/types/config/meta-config.types.ts @@ -20,11 +20,11 @@ export interface MetaConfig { description: string; /** - * [WEB] URL or path to the page's favicon. + * [WEB] Absolute path to the image used for favicons generation. * * Specified icon will be displayed next to the page title in browser tab. */ - favicon: string; + faviconPath: string; /** * [WEB] Title used in open graph links. diff --git a/src/web/components/head.astro b/src/web/components/head.astro new file mode 100644 index 0000000..b9d8868 --- /dev/null +++ b/src/web/components/head.astro @@ -0,0 +1,19 @@ +--- +import Favicons from '@/web/head/favicons.auto-generated.astro'; +import InitialTheme from '@/web/head/initial-theme.astro'; +import Meta from '@/web/head/meta.astro'; + +import type { MetaConfig } from '@/types/config/meta-config.types'; + +interface Props { + meta: MetaConfig; +} + +const { meta } = Astro.props; +--- + + + + + + diff --git a/src/web/components/layout.astro b/src/web/components/layout.astro index f6fe997..9a9745c 100644 --- a/src/web/components/layout.astro +++ b/src/web/components/layout.astro @@ -1,6 +1,7 @@ --- import type { Data } from '@/types/data'; import Analytics from '@/web/analytics/analytics.astro'; +import Head from './head.astro'; export interface Props extends Pick {} @@ -9,33 +10,7 @@ const { meta, i18n } = Astro.props; - - - - - {meta.title} - - - - - {meta.ogImage && } - - + diff --git a/src/web/head/favicons.auto-generated.astro b/src/web/head/favicons.auto-generated.astro new file mode 100644 index 0000000..4f83c43 --- /dev/null +++ b/src/web/head/favicons.auto-generated.astro @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/web/head/initial-theme.astro b/src/web/head/initial-theme.astro new file mode 100644 index 0000000..1cf2700 --- /dev/null +++ b/src/web/head/initial-theme.astro @@ -0,0 +1,16 @@ + diff --git a/src/web/head/meta.astro b/src/web/head/meta.astro new file mode 100644 index 0000000..89499a1 --- /dev/null +++ b/src/web/head/meta.astro @@ -0,0 +1,18 @@ +--- +import type { MetaConfig } from '@/types/config/meta-config.types'; + +interface Props { + meta: MetaConfig; +} + +const { meta } = Astro.props; +--- + + + + +{meta.title} + + + +{meta.ogImage && } diff --git a/tsconfig.json b/tsconfig.json index 1a1e3ad..20c9449 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,5 +35,11 @@ "@/*": ["src/*"] } }, - "include": ["**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.astro"] + "include": ["**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.astro"], + "ts-node": { + "transpileOnly": true, + "compilerOptions": { + "module": "CommonJS" + } + } }