From b5fb8b20eea03b384240d7f7c1bf60d5ff0e593e Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 12:00:53 +0100 Subject: [PATCH 01/35] style: fix eslint config --- .eslintrc.cjs | 39 --------------------------------------- eslint.config.mjs | 2 +- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 .eslintrc.cjs diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 9612493..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - root: true, - env: { - browser: true, - node: true, - }, - parserOptions: { - parser: "@typescript-eslint/parser", - ecmaVersion: 2020, - }, - extends: [ - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended", - "@vue/typescript/recommended", - ], - plugins: ["eslint-plugin-tsdoc"], - rules: { - "no-console": ["warn", { allow: ["info", "warn", "error"] }], - "no-debugger": "warn", - "no-undef": "off", - curly: "error", - "prefer-const": "error", - "padding-line-between-statements": [ - "error", - { blankLine: "always", prev: "*", next: "return" }, - ], - "@typescript-eslint/no-unused-vars": [ - "error", - { - argsIgnorePattern: "^_", - varsIgnorePattern: "^_", - }, - ], - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/explicit-module-boundary-types": "error", - "tsdoc/syntax": "warn", - }, -} diff --git a/eslint.config.mjs b/eslint.config.mjs index 9227d36..37d6a6f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -11,9 +11,9 @@ export default tseslint.config( }, eslint.configs.recommended, tseslint.configs.recommended, - prettier, ...pluginVue.configs["flat/essential"], ...vueTsEslintConfig(), + prettier, { plugins: { tsdoc, From c02475bad2790cd76ae2395ca2aa075d24a13546 Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 12:01:21 +0100 Subject: [PATCH 02/35] build: compile vue components --- package-lock.json | 605 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + vite.config.ts | 4 +- 3 files changed, 609 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 43bf886..8b6c886 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "typescript": "^5.7.2", "typescript-eslint": "^8.17.0", "vite": "^6.0.2", + "vite-plugin-dts": "^4.3.0", "vite-plugin-sdk": "^0.1.3", "vitest": "^2.1.8", "vue": "^3.5.13" @@ -1099,6 +1100,120 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.48.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.48.0.tgz", + "integrity": "sha512-FMFgPjoilMUWeZXqYRlJ3gCVRhB7WU/HN88n8OLqEsmsG4zBdX/KQdtJfhq95LQTQ++zfu0Em1LLb73NqRCLYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor-model": "7.30.0", + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.10.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.3", + "@rushstack/ts-command-line": "4.23.1", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.4.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.0.tgz", + "integrity": "sha512-26/LJZBrsWDKAkOWRiQbdVgcfd1F3nyJnAiJzsAgpouPk7LtOIj7PK9aJtBaw/pUXrkotEg27RrT+Jm/q0bbug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.10.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@microsoft/tsdoc": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", @@ -1555,6 +1670,203 @@ "win32" ] }, + "node_modules/@rushstack/node-core-library": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.10.0.tgz", + "integrity": "sha512-2pPLCuS/3x7DCd7liZkqOewGM0OzLyCacdvOe8j6Yrx9LkETGnxul1t7603bIaB8nUAooORcct9fFDOQMbWAgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/node-core-library/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rushstack/node-core-library/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.3.tgz", + "integrity": "sha512-csXbZsAdab/v8DbU1sz7WC2aNaKArcdS/FPmXMOXEj/JBBZMvDK0+1b4Qao0kkG0ciB1Qe86/Mb68GjH6/TnMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/node-core-library": "5.10.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/terminal/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.1.tgz", + "integrity": "sha512-40jTmYoiu/xlIpkkRsVfENtBq4CW3R4azbL0Vmda+fMwHWqss6wwf/Cy/UJmMqIzpfYc2OTnjYP1ZLD3CmyeCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rushstack/terminal": "0.14.3", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@rushstack/ts-command-line/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, "node_modules/@size-limit/esbuild": { "version": "11.1.6", "resolved": "https://registry.npmjs.org/@size-limit/esbuild/-/esbuild-11.1.6.tgz", @@ -1624,6 +1936,13 @@ } } }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -2069,6 +2388,35 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@volar/language-core": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.10.tgz", + "integrity": "sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.10" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.10.tgz", + "integrity": "sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.10.tgz", + "integrity": "sha512-F8ZtBMhSXyYKuBfGpYwqA5rsONnOwAVvjyE7KPYJ7wgZqo2roASqNWUnianOomJX5u1cxeRooHV59N0PhvEOgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.10", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", @@ -2119,6 +2467,17 @@ "@vue/shared": "3.5.13" } }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, "node_modules/@vue/devtools-api": { "version": "6.6.4", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", @@ -2151,6 +2510,31 @@ } } }, + "node_modules/@vue/language-core": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.6.tgz", + "integrity": "sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.1", + "@vue/compiler-dom": "^3.4.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@vue/reactivity": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", @@ -2282,6 +2666,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -2678,6 +3104,20 @@ "dot-prop": "^5.1.0" } }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2701,6 +3141,13 @@ "typedarray": "^0.0.6" } }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -3079,6 +3526,13 @@ "node": "*" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -3876,6 +4330,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4315,6 +4776,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -4438,6 +4909,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4924,6 +5405,13 @@ "node": ">=0.10.0" } }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4984,6 +5472,23 @@ "node": ">=4" } }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5894,6 +6399,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mlly": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", + "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^1.1.2", + "pkg-types": "^1.2.1", + "ufo": "^1.5.4" + } + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -5911,6 +6429,13 @@ "dev": true, "license": "MIT" }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nanoid": { "version": "5.0.9", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz", @@ -6155,6 +6680,13 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6268,6 +6800,18 @@ "node": ">=0.10.0" } }, + "node_modules/pkg-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "pathe": "^1.1.2" + } + }, "node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -6960,6 +7504,13 @@ "readable-stream": "^3.0.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -7091,6 +7642,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -7590,6 +8151,13 @@ } } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -8254,6 +8822,36 @@ } } }, + "node_modules/vite-plugin-dts": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.3.0.tgz", + "integrity": "sha512-LkBJh9IbLwL6/rxh0C1/bOurDrIEmRE7joC+jFdOEEciAFPbpEKOLSAr5nNh5R7CJ45cMbksTrFfy52szzC5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/api-extractor": "^7.47.11", + "@rollup/pluginutils": "^5.1.0", + "@volar/typescript": "^2.4.4", + "@vue/language-core": "2.1.6", + "compare-versions": "^6.1.1", + "debug": "^4.3.6", + "kolorist": "^1.8.0", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.11" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "*", + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vite-plugin-sdk": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/vite-plugin-sdk/-/vite-plugin-sdk-0.1.3.tgz", @@ -8907,6 +9505,13 @@ } } }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" + }, "node_modules/vue": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", diff --git a/package.json b/package.json index 62f5c0a..6b4ac30 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "typescript": "^5.7.2", "typescript-eslint": "^8.17.0", "vite": "^6.0.2", + "vite-plugin-dts": "^4.3.0", "vite-plugin-sdk": "^0.1.3", "vitest": "^2.1.8", "vue": "^3.5.13" diff --git a/vite.config.ts b/vite.config.ts index 044785e..769b470 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,11 @@ /// +import vue from "@vitejs/plugin-vue" import { defineConfig } from "vite" +import dts from "vite-plugin-dts" import sdk from "vite-plugin-sdk" export default defineConfig({ - plugins: [sdk()], + plugins: [sdk(), vue(), dts({ include: "src/**/*.vue" })], // @ts-expect-error Vite 6 issue(?) test: { environment: "jsdom", From e889af1e19a9f9995c1095c6163dec713f8f3736 Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 14:28:39 +0100 Subject: [PATCH 03/35] feat!: remove `isomorphic-unfetch` fallback --- package-lock.json | 59 ----------------------------- package.json | 1 - src/PrismicTest.vue | 9 +++++ src/createPrismic.ts | 15 +------- src/index.ts | 4 ++ test/createPrismic-client.test.ts | 62 +++++++++++++------------------ 6 files changed, 40 insertions(+), 110 deletions(-) create mode 100644 src/PrismicTest.vue diff --git a/package-lock.json b/package-lock.json index 8b6c886..ed7494d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "Apache-2.0", "dependencies": { "@prismicio/client": "^7.12.0", - "isomorphic-unfetch": "^3.1.0", "vue-router": "^4.5.0" }, "devDependencies": { @@ -5073,16 +5072,6 @@ "dev": true, "license": "ISC" }, - "node_modules/isomorphic-unfetch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", - "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.1", - "unfetch": "^4.2.0" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -6479,48 +6468,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", @@ -8179,12 +8126,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unfetch": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", - "license": "MIT" - }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", diff --git a/package.json b/package.json index 6b4ac30..5f2ed18 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,6 @@ }, "dependencies": { "@prismicio/client": "^7.12.0", - "isomorphic-unfetch": "^3.1.0", "vue-router": "^4.5.0" }, "devDependencies": { diff --git a/src/PrismicTest.vue b/src/PrismicTest.vue new file mode 100644 index 0000000..aeded83 --- /dev/null +++ b/src/PrismicTest.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/createPrismic.ts b/src/createPrismic.ts index 140418f..cb10691 100644 --- a/src/createPrismic.ts +++ b/src/createPrismic.ts @@ -1,6 +1,5 @@ import type { Client, - FetchLike, HTMLRichTextFunctionSerializer, HTMLRichTextMapSerializer, LinkResolverFunction, @@ -55,19 +54,7 @@ export const createPrismic = (options: PrismicPluginOptions): PrismicPlugin => { if (options.client) { client = options.client } else { - client = createClient(options.endpoint, { - fetch: async (endpoint, options) => { - let fetchFunction: FetchLike - if (typeof globalThis.fetch === "function") { - fetchFunction = globalThis.fetch - } else { - fetchFunction = (await import("isomorphic-unfetch")).default - } - - return await fetchFunction(endpoint, options) - }, - ...options.clientConfig, - }) + client = createClient(options.endpoint, options.clientConfig) } const prismicClient: PrismicPluginClient = { diff --git a/src/index.ts b/src/index.ts index d1583c3..aa49d43 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,7 @@ +import PrismicTest from "./PrismicTest.vue" + +export { PrismicTest } + export { createPrismic } from "./createPrismic" export { usePrismic } from "./usePrismic" diff --git a/test/createPrismic-client.test.ts b/test/createPrismic-client.test.ts index 461d78c..c82838f 100644 --- a/test/createPrismic-client.test.ts +++ b/test/createPrismic-client.test.ts @@ -2,18 +2,11 @@ import { expect, it, vi } from "vitest" import { createClient, getRepositoryEndpoint } from "@prismicio/client" import { mount } from "@vue/test-utils" -import unfetch from "isomorphic-unfetch" import { WrapperComponent } from "./__fixtures__/WrapperComponent" import { createPrismic } from "../src" -vi.mock("isomorphic-unfetch", () => { - return { - default: vi.fn(), - } -}) - it("creates client from repository name", () => { const prismic = createPrismic({ endpoint: "test" }) @@ -67,6 +60,25 @@ it("uses provided client", () => { expect(wrapper.vm.$prismic.client.endpoint).toBe(client.endpoint) }) +it("uses `globalThis` fetch function by default", () => { + const initialFetch = globalThis.fetch + globalThis.fetch = vi.fn() + + const prismic = createPrismic({ endpoint: "test" }) + + const wrapper = mount(WrapperComponent, { + global: { + plugins: [prismic], + }, + }) + + expect(globalThis.fetch).not.toHaveBeenCalled() + wrapper.vm.$prismic.client.fetchFn("foo", {}) + expect(globalThis.fetch).toHaveBeenCalledOnce() + + globalThis.fetch = initialFetch +}) + it("uses provided fetch function", () => { const spiedFetch = vi.fn() @@ -89,36 +101,14 @@ it("uses provided fetch function", () => { expect(spiedFetch).toHaveBeenCalledOnce() }) -it("uses `globalThis` fetch function when available", () => { - // `globalThis.fetch` does not exists in our Node.js context - const fetchStub = (globalThis.fetch = vi.fn()) - - const prismic = createPrismic({ endpoint: "test" }) - - const wrapper = mount(WrapperComponent, { - global: { - plugins: [prismic], - }, - }) - - expect(fetchStub).not.toHaveBeenCalled() - wrapper.vm.$prismic.client.fetchFn("foo", {}) - expect(fetchStub).toHaveBeenCalledOnce() - - // @ts-expect-error `globalThis.fetch` does not exists in our Node.js context +it.only("throws when `globalThis` fetch function is not available and no fetch function is provided", async () => { + const initialFetch = globalThis.fetch + // @ts-expect-error - We're deleting the global fetch function for testing purposes delete globalThis.fetch -}) -it("uses `isomorphic-unfetch` when `globalThis` fetch function is not available", async () => { - const prismic = createPrismic({ endpoint: "test" }) - - const wrapper = mount(WrapperComponent, { - global: { - plugins: [prismic], - }, - }) + expect(() => createPrismic({ endpoint: "test" })).toThrowError( + /a valid fetch implementation was not provided/i, + ) - expect(unfetch).not.toHaveBeenCalled() - await wrapper.vm.$prismic.client.fetchFn("foo", {}) - expect(unfetch).toHaveBeenCalledOnce() + globalThis.fetch = initialFetch }) From dc33283be9d9355cdff64f66f176935677405e5c Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 17:27:35 +0100 Subject: [PATCH 04/35] refactor: Prismic Vue plugin --- eslint.config.mjs | 1 + package-lock.json | 69 +++++++++++++++++++++++---- package.json | 9 ++-- src/PrismicTest.vue | 9 ---- src/components/index.ts | 15 +++--- src/createPrismic.ts | 6 +-- src/index.ts | 8 ---- src/lib/devMsg.ts | 20 ++++++++ src/types.ts | 27 +++++------ test/__setup__.ts | 14 ++++++ test/createPrismic-client.test.ts | 4 +- test/createPrismic-helpers.test.ts | 76 +++++++++++++++++++++++++++++- vite.config.ts | 3 ++ 13 files changed, 202 insertions(+), 59 deletions(-) delete mode 100644 src/PrismicTest.vue create mode 100644 src/lib/devMsg.ts create mode 100644 test/__setup__.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 37d6a6f..9aae7a3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -44,6 +44,7 @@ export default tseslint.config( "@typescript-eslint/consistent-type-imports": "error", "tsdoc/syntax": "warn", "vue/multi-word-component-names": "off", + "vue/no-v-text-v-html-on-component": "off", }, }, ) diff --git a/package-lock.json b/package-lock.json index ed7494d..d59a9cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,17 +29,18 @@ "eslint-plugin-vue": "^9.32.0", "jsdom": "^25.0.1", "jsdom-global": "^3.0.2", - "prettier": "^3.4.1", + "prettier": "^3.4.2", "prettier-plugin-jsdoc": "^1.3.0", "size-limit": "^11.1.6", "standard-version": "^9.5.0", - "typescript": "^5.7.2", + "typescript": "~5.6.3", "typescript-eslint": "^8.17.0", "vite": "^6.0.2", "vite-plugin-dts": "^4.3.0", "vite-plugin-sdk": "^0.1.3", "vitest": "^2.1.8", - "vue": "^3.5.13" + "vue": "^3.5.13", + "vue-tsc": "^2.1.10" }, "engines": { "node": ">=16.10.0" @@ -2707,6 +2708,13 @@ "dev": true, "license": "MIT" }, + "node_modules/alien-signals": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.2.2.tgz", + "integrity": "sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==", + "dev": true, + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -6830,9 +6838,9 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -8058,9 +8066,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -9556,6 +9564,49 @@ "vue": "^3.2.0" } }, + "node_modules/vue-tsc": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.10.tgz", + "integrity": "sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~2.4.8", + "@vue/language-core": "2.1.10", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vue-tsc/node_modules/@vue/language-core": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.10.tgz", + "integrity": "sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.8", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^0.2.0", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/package.json b/package.json index 5f2ed18..cb9cb87 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "release:beta": "npm run test && standard-version --release-as major --prerelease beta && git push --follow-tags && npm run build && npm publish --tag beta", "release:beta:dry": "standard-version --release-as major --prerelease beta --dry-run", "lint": "eslint", - "types": "vitest --typecheck.only --run && tsc --noEmit", + "types": "vitest --typecheck.only --run && vue-tsc --noEmit", "types:watch": "vitest --typecheck.only", "unit": "vitest run --coverage", "unit:watch": "vitest watch", @@ -77,17 +77,18 @@ "eslint-plugin-vue": "^9.32.0", "jsdom": "^25.0.1", "jsdom-global": "^3.0.2", - "prettier": "^3.4.1", + "prettier": "^3.4.2", "prettier-plugin-jsdoc": "^1.3.0", "size-limit": "^11.1.6", "standard-version": "^9.5.0", - "typescript": "^5.7.2", + "typescript": "~5.6.3", "typescript-eslint": "^8.17.0", "vite": "^6.0.2", "vite-plugin-dts": "^4.3.0", "vite-plugin-sdk": "^0.1.3", "vitest": "^2.1.8", - "vue": "^3.5.13" + "vue": "^3.5.13", + "vue-tsc": "^2.1.10" }, "peerDependencies": { "vue": "^3.0.0" diff --git a/src/PrismicTest.vue b/src/PrismicTest.vue deleted file mode 100644 index aeded83..0000000 --- a/src/PrismicTest.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/components/index.ts b/src/components/index.ts index 97daf4e..8f2ff08 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,15 +1,16 @@ -export { PrismicEmbedImpl, PrismicEmbed } from "./PrismicEmbed" -export type { PrismicEmbedProps } from "./PrismicEmbed" +import PrismicEmbed from "./PrismicEmbed.vue" +import PrismicImage from "./PrismicImage.vue" +import PrismicText from "./PrismicText.vue" -export { usePrismicImage, PrismicImageImpl, PrismicImage } from "./PrismicImage" -export type { UsePrismicImageOptions, PrismicImageProps } from "./PrismicImage" +export type { PrismicEmbedProps } from "./PrismicEmbed.vue" +export type { PrismicImageProps } from "./PrismicImage.vue" +export type { PrismicTextProps } from "./PrismicText.vue" + +export { PrismicEmbed, PrismicImage, PrismicText } export { usePrismicLink, PrismicLinkImpl, PrismicLink } from "./PrismicLink" export type { UsePrismicLinkOptions, PrismicLinkProps } from "./PrismicLink" -export { usePrismicText, PrismicTextImpl, PrismicText } from "./PrismicText" -export type { UsePrismicTextOptions, PrismicTextProps } from "./PrismicText" - export { usePrismicRichText, PrismicRichTextImpl, diff --git a/src/createPrismic.ts b/src/createPrismic.ts index cb10691..65cd00a 100644 --- a/src/createPrismic.ts +++ b/src/createPrismic.ts @@ -131,9 +131,9 @@ export const createPrismic = (options: PrismicPluginOptions): PrismicPlugin => { if (options.injectComponents !== false) { app.component(PrismicLink.name, PrismicLink) - app.component(PrismicEmbed.name, PrismicEmbed) - app.component(PrismicImage.name, PrismicImage) - app.component(PrismicText.name, PrismicText) + app.component(PrismicEmbed.name!, PrismicEmbed) + app.component(PrismicImage.name!, PrismicImage) + app.component(PrismicText.name!, PrismicText) app.component(PrismicRichText.name, PrismicRichText) app.component(SliceZone.name, SliceZone) } diff --git a/src/index.ts b/src/index.ts index aa49d43..0550096 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,9 @@ -import PrismicTest from "./PrismicTest.vue" - -export { PrismicTest } - export { createPrismic } from "./createPrismic" export { usePrismic } from "./usePrismic" export { // Composables - usePrismicImage, usePrismicLink, - usePrismicText, usePrismicRichText, // Components PrismicEmbed, @@ -25,9 +19,7 @@ export { } from "./components" export type { // Composables - UsePrismicImageOptions, UsePrismicLinkOptions, - UsePrismicTextOptions, UsePrismicRichTextOptions, // Components PrismicEmbedProps, diff --git a/src/lib/devMsg.ts b/src/lib/devMsg.ts new file mode 100644 index 0000000..440e497 --- /dev/null +++ b/src/lib/devMsg.ts @@ -0,0 +1,20 @@ +import { version } from "../../package.json" + +/** + * Returns a `prismic.dev/msg` URL for a given message slug. + * + * @example + * + * ```ts + * devMsg("missing-param") + * // => "https://prismic.dev/msg/svelte/v1.2.3/missing-param.md" + * ``` + * + * @param slug - Slug for the message. This corresponds to a Markdown file in + * the Git repository's `/messages` directory. + * + * @returns The `prismic.dev/msg` URL for the given slug. + */ +export const devMsg = (slug: string): string => { + return `https://prismic.dev/msg/vue/v${version}/${slug}` +} diff --git a/src/types.ts b/src/types.ts index 96ff8d1..28fdc7c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,7 +58,7 @@ type PrismicPluginComponentsOptions = { * * @defaultValue {@link RouterLink} */ - linkInternalComponent?: string | ConcreteComponent | Raw + linkInternalComponent?: ComponentOrTagName /** * An HTML tag name, a component, or a functional component used to render @@ -73,20 +73,7 @@ type PrismicPluginComponentsOptions = { * * @defaultValue `"a"` */ - linkExternalComponent?: string | ConcreteComponent | Raw - - /** - * An HTML tag name, a component, or a functional component used to render - * images. - * - * @remarks - * HTML tag names and components will be rendered using the `img` tag - * interface (`src` and `alt` attribute). Components will also receive an - * additional `copyright` props. - * - * @defaultValue `"img"` - */ - imageComponent?: string | ConcreteComponent | Raw + linkExternalComponent?: ComponentOrTagName /** * Default widths to use when rendering an image with `widths="defaults"` @@ -469,3 +456,13 @@ export type VueUseOptions = { export type VueUseParameters = { [K in keyof T]: T extends number ? Ref | T[K] : T[K] } + +/** + * A component or a tag name to be used as props. + * + * @internal + */ +export type ComponentOrTagName = + | string + | ConcreteComponent + | Raw diff --git a/test/__setup__.ts b/test/__setup__.ts new file mode 100644 index 0000000..0bc4f32 --- /dev/null +++ b/test/__setup__.ts @@ -0,0 +1,14 @@ +import { beforeEach } from "vitest" + +import type { MockFactory } from "@prismicio/mock" +import { createMockFactory } from "@prismicio/mock" + +declare module "vitest" { + export interface TestContext { + mock: MockFactory + } +} + +beforeEach((ctx) => { + ctx.mock = createMockFactory({ seed: ctx.task.name }) +}) diff --git a/test/createPrismic-client.test.ts b/test/createPrismic-client.test.ts index c82838f..56d5682 100644 --- a/test/createPrismic-client.test.ts +++ b/test/createPrismic-client.test.ts @@ -60,7 +60,7 @@ it("uses provided client", () => { expect(wrapper.vm.$prismic.client.endpoint).toBe(client.endpoint) }) -it("uses `globalThis` fetch function by default", () => { +it("uses `globalThis.fetch` by default", () => { const initialFetch = globalThis.fetch globalThis.fetch = vi.fn() @@ -101,7 +101,7 @@ it("uses provided fetch function", () => { expect(spiedFetch).toHaveBeenCalledOnce() }) -it.only("throws when `globalThis` fetch function is not available and no fetch function is provided", async () => { +it("throws when `globalThis.fetch` is not available and no fetch function is provided", async () => { const initialFetch = globalThis.fetch // @ts-expect-error - We're deleting the global fetch function for testing purposes delete globalThis.fetch diff --git a/test/createPrismic-helpers.test.ts b/test/createPrismic-helpers.test.ts index 5843df8..1a315c1 100644 --- a/test/createPrismic-helpers.test.ts +++ b/test/createPrismic-helpers.test.ts @@ -68,7 +68,7 @@ it("`asHTML` uses provided default serializer", () => { expect(spiedRichTextSerializer).toHaveBeenCalled() }) -// TODO: Remove in v5 +// TODO: Remove in v5, we prefer `richTextSerializer` now it("`asHTML` uses provided default deprecated serializer", () => { const spiedRichTextSerializer = vi.fn() @@ -88,6 +88,29 @@ it("`asHTML` uses provided default deprecated serializer", () => { expect(spiedRichTextSerializer).toHaveBeenCalled() }) +// TODO: Remove in v5, we prefer `richTextSerializer` now +it("`asHTML` uses provided serializer over default deprecated serializer", () => { + const spiedRichTextSerializer1 = vi.fn() + const spiedRichTextSerializer2 = vi.fn() + + const prismic = createPrismic({ + endpoint: "test", + htmlSerializer: spiedRichTextSerializer1, + }) + + const wrapper = mount(WrapperComponent, { + global: { + plugins: [prismic], + }, + }) + + wrapper.vm.$prismic.asHTML(richTextFixture.en, { + serializer: spiedRichTextSerializer2, + }) + + expect(spiedRichTextSerializer2).toHaveBeenCalled() +}) + it("`asHTML` uses provided serializer over default provided", () => { const spiedRichTextSerializer1 = vi.fn() const spiedRichTextSerializer2 = vi.fn() @@ -111,7 +134,7 @@ it("`asHTML` uses provided serializer over default provided", () => { expect(spiedRichTextSerializer2).toHaveBeenCalled() }) -// TODO: Remove in v5 +// TODO: Remove when switching to client v8, we prefer a config object now it("`asHTML` uses provided deprecated serializer over default provided", () => { const spiedRichTextSerializer1 = vi.fn() const spiedRichTextSerializer2 = vi.fn() @@ -181,3 +204,52 @@ it("`asLink` uses provided link resolver over default provided", (ctx) => { expect(spiedLinkResolver1).not.toHaveBeenCalled() expect(spiedLinkResolver2).toHaveBeenCalledOnce() }) + +it("`asLinkAttrs` uses provided default link resolver", (ctx) => { + const spiedLinkResolver = vi.fn() + + const prismic = createPrismic({ + endpoint: "test", + linkResolver: spiedLinkResolver, + }) + + const wrapper = mount(WrapperComponent, { + global: { + plugins: [prismic], + }, + }) + + wrapper.vm.$prismic.asLinkAttrs({ + ...mock.value.link({ type: LinkType.Document, seed: ctx.task.name }), + url: undefined, + }) + + expect(spiedLinkResolver).toHaveBeenCalledOnce() +}) + +it("`asLinkAttrs` uses provided link resolver over default provided", (ctx) => { + const spiedLinkResolver1 = vi.fn() + const spiedLinkResolver2 = vi.fn() + + const prismic = createPrismic({ + endpoint: "test", + linkResolver: spiedLinkResolver1, + }) + + const wrapper = mount(WrapperComponent, { + global: { + plugins: [prismic], + }, + }) + + wrapper.vm.$prismic.asLinkAttrs( + { + ...mock.value.link({ type: LinkType.Document, seed: ctx.task.name }), + url: undefined, + }, + { linkResolver: spiedLinkResolver2 }, + ) + + expect(spiedLinkResolver1).not.toHaveBeenCalled() + expect(spiedLinkResolver2).toHaveBeenCalledOnce() +}) diff --git a/vite.config.ts b/vite.config.ts index 769b470..2f718e6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,9 @@ export default defineConfig({ environment: "jsdom", coverage: { reporter: ["lcovonly", "text"], + include: ["src"], }, + setupFiles: ["./test/__setup__"], + typecheck: { checker: "vue-tsc" }, }, }) From a9a43a3029aee945af922468a2880647a41363d9 Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 17:27:49 +0100 Subject: [PATCH 05/35] refactor!: --- src/components/PrismicEmbed.ts | 90 -------------------------- src/components/PrismicEmbed.vue | 43 +++++++++++++ test/components-PrismicEmbed.test.ts | 95 ++++++++++++++++------------ 3 files changed, 98 insertions(+), 130 deletions(-) delete mode 100644 src/components/PrismicEmbed.ts create mode 100644 src/components/PrismicEmbed.vue diff --git a/src/components/PrismicEmbed.ts b/src/components/PrismicEmbed.ts deleted file mode 100644 index 988ffce..0000000 --- a/src/components/PrismicEmbed.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { EmbedField } from "@prismicio/client" -import type { - AllowedComponentProps, - ComponentCustomProps, - ConcreteComponent, - DefineComponent, - PropType, - Raw, - VNodeProps, -} from "vue" -import { defineComponent, h } from "vue" - -import { simplyResolveComponent } from "../lib/simplyResolveComponent" - -/** - * The default component rendered to wrap the embed. - */ -const defaultWrapper = "div" - -/** - * Props for ``. - */ -export type PrismicEmbedProps = { - /** - * The Prismic embed field to render. - */ - field: EmbedField - - /** - * An HTML tag name, a component, or a functional component used to wrap the - * output. - * - * @defaultValue `"div"` - */ - wrapper?: string | ConcreteComponent | Raw -} - -/** - * `` implementation. - * - * @internal - */ -export const PrismicEmbedImpl = /*#__PURE__*/ defineComponent({ - name: "PrismicEmbed", - props: { - field: { - type: Object as PropType, - required: true, - }, - wrapper: { - type: [String, Object, Function] as PropType< - string | ConcreteComponent | Raw - >, - default: undefined, - required: false, - }, - }, - setup(props) { - // Prevent fatal if user didn't check for field, throws `Invalid prop` warn - if (!props.field) { - return () => null - } - - return () => { - return h(simplyResolveComponent(props.wrapper || defaultWrapper), { - "data-oembed": props.field.embed_url, - "data-oembed-type": props.field.type, - "data-oembed-provider": props.field.provider_name, - innerHTML: props.field.html || null, - }) - } - }, -}) - -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to render a Prismic embed field. - * - * @see Component props {@link PrismicEmbedProps} - * @see Templating embed fields {@link https://prismic.io/docs/technologies/vue-template-content#embeds} - */ -export const PrismicEmbed = PrismicEmbedImpl as unknown as { - new (): { - $props: AllowedComponentProps & - ComponentCustomProps & - VNodeProps & - PrismicEmbedProps - } -} diff --git a/src/components/PrismicEmbed.vue b/src/components/PrismicEmbed.vue new file mode 100644 index 0000000..0811145 --- /dev/null +++ b/src/components/PrismicEmbed.vue @@ -0,0 +1,43 @@ + + + diff --git a/test/components-PrismicEmbed.test.ts b/test/components-PrismicEmbed.test.ts index 220eb12..0b20312 100644 --- a/test/components-PrismicEmbed.test.ts +++ b/test/components-PrismicEmbed.test.ts @@ -1,93 +1,108 @@ -import { expect, it, vi } from "vitest" +import { expect, it } from "vitest" -import * as mock from "@prismicio/mock" import type { EmbedField } from "@prismicio/client" import { mount } from "@vue/test-utils" import { markRaw } from "vue" import { WrapperComponent } from "./__fixtures__/WrapperComponent" -import { PrismicEmbedImpl } from "../src/components" +import { PrismicEmbed } from "../src/components" -it("renders embed field", () => { - const wrapper = mount(PrismicEmbedImpl, { - props: { field: mock.value.embed({ seed: 1 }) }, +it("renders nothing when passed an empty field", () => { + const wrapper = mount(PrismicEmbed, { + props: { field: {} }, + }) + + expect(wrapper.html()).toBe("") +}) + +it("renders embed field", (ctx) => { + const wrapper = mount(PrismicEmbed, { + props: { field: ctx.mock.value.embed() }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, + ` + "
+ + +
" + `, ) }) -it("renders embed field with no HTML", () => { - const wrapper = mount(PrismicEmbedImpl, { +it("renders embed field with no HTML", (ctx) => { + const wrapper = mount(PrismicEmbed, { props: { field: { - ...mock.value.embed({ seed: 2 }), + ...ctx.mock.value.embed(), html: null, } as EmbedField, }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, + `"
"`, ) }) -it("uses provided wrapper tag", () => { - const wrapper = mount(PrismicEmbedImpl, { - props: { field: mock.value.embed({ seed: 3 }), wrapper: "section" }, +it("uses provided wrapper tag", (ctx) => { + const wrapper = mount(PrismicEmbed, { + props: { field: ctx.mock.value.embed(), wrapper: "section" }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, + ` + "
+ + +
" + `, ) }) -it("uses provided wrapper component", () => { - const wrapper = mount(PrismicEmbedImpl, { +it("uses provided wrapper component", (ctx) => { + const wrapper = mount(PrismicEmbed, { props: { - field: mock.value.embed({ seed: 4 }), + field: ctx.mock.value.embed(), wrapper: markRaw(WrapperComponent), }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, - ) -}) - -it("renders nothing when invalid", () => { - vi.stubGlobal("console", { warn: vi.fn() }) - - const wrapper = mount(PrismicEmbedImpl, { - props: { field: null as unknown as EmbedField }, - }) - - expect(wrapper.html()).toBe("") - expect(console.warn).toHaveBeenCalledOnce() - expect(vi.mocked(console.warn).mock.calls[0][0]).toMatch( - /Invalid prop: type check failed for prop/i, + `"
"`, ) - - vi.resetAllMocks() }) -it("reacts to changes properly", async () => { - const wrapper = mount(PrismicEmbedImpl, { - props: { field: mock.value.embed({ seed: 5 }) }, +it("reacts to changes properly", async (ctx) => { + const wrapper = mount(PrismicEmbed, { + props: { field: ctx.mock.value.embed() }, }) const firstRender = wrapper.html() await wrapper.setProps({ - field: mock.value.embed({ seed: 1 }), + field: { + ...ctx.mock.value.embed(), + embed_url: "https://www.youtube.com/embed/c-ATzcy6VkI", + }, }) const secondRender = wrapper.html() expect(secondRender).not.toBe(firstRender) expect(secondRender).toMatchInlineSnapshot( - `"
"`, + ` + "
+ + +
" + `, ) }) From 34f121a96e6db9a16fa637a0f96ac1d3b431e4ab Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 17:27:58 +0100 Subject: [PATCH 06/35] refactor!: --- src/components/PrismicImage.ts | 307 ------------------------- src/components/PrismicImage.vue | 160 +++++++++++++ test/components-PrismicImage.test.ts | 332 +++++++++++++++------------ 3 files changed, 341 insertions(+), 458 deletions(-) delete mode 100644 src/components/PrismicImage.ts create mode 100644 src/components/PrismicImage.vue diff --git a/src/components/PrismicImage.ts b/src/components/PrismicImage.ts deleted file mode 100644 index b741364..0000000 --- a/src/components/PrismicImage.ts +++ /dev/null @@ -1,307 +0,0 @@ -import type { ImageField } from "@prismicio/client" -import { - asImagePixelDensitySrcSet, - asImageSrc, - asImageWidthSrcSet, - isFilled, -} from "@prismicio/client" -import type { - AllowedComponentProps, - ComponentCustomProps, - ComputedRef, - ConcreteComponent, - DefineComponent, - PropType, - Raw, - VNodeProps, -} from "vue" -import { computed, defineComponent, h, unref } from "vue" - -import { __PRODUCTION__ } from "../lib/__PRODUCTION__" -import { simplyResolveComponent } from "../lib/simplyResolveComponent" - -import type { VueUseOptions } from "../types" - -import { usePrismic } from "../usePrismic" - -/** - * The default component rendered for images. - */ -const defaultImageComponent = "img" - -/** - * Props for ``. - */ -export type PrismicImageProps = { - /** - * The Prismic image field to render. - */ - field: ImageField | ImageField - - /** - * An HTML tag name, a component, or a functional component used to render - * images. - * - * @remarks - * HTML tag names and components will be rendered using the `img` tag - * interface (`src`, `srcset`, and `alt` attribute). Components will also - * receive an additional `copyright` props. - * - * @defaultValue The one provided to `@prismicio/vue` plugin if configured, `"img"` otherwise. - */ - imageComponent?: string | ConcreteComponent | Raw - - /** - * An object of Imgix URL API parameters. - * - * @see Imgix URL parameters reference: https://docs.imgix.com/apis/rendering - */ - imgixParams?: Parameters[1] - - /** - * Adds an additional `srcset` attribute to the image following given widths. - * - * @remarks - * A special value of `"thumbnails"` is accepted to automatically use image - * widths coming from the API. - * @remarks - * A special value of `"defaults"` is accepted to automatically use image - * widths coming from the plugin configuration. - * @remarks - * This prop is not compatible with the `pixelDensities` prop. When both are - * used the `pixelDensities` prop will be ignored. - */ - widths?: - | NonNullable[1]>["widths"] - | "thumbnails" - | "defaults" - - /** - * Adds an additional `srcset` attribute to the image following giving pixel - * densities. - * - * @remarks - * A special value of `"defaults"` is accepted to automatically use image - * pixel densities coming from the plugin configuration. - * @remarks - * This prop is not compatible with the `widths` prop. When both are used, the - * `pixelDensities` prop will be ignored. - */ - pixelDensities?: - | NonNullable< - Parameters[1] - >["pixelDensities"] - | "defaults" -} - -/** - * Options for {@link usePrismicImage}. - */ -export type UsePrismicImageOptions = VueUseOptions< - Omit -> - -/** - * Return type of {@link usePrismicImage}. - */ -export type UsePrismicImageReturnType = { - /** - * Resolved image `src` value. - */ - src: ComputedRef - - /** - * Resolved image `srcset` value. - */ - srcset: ComputedRef - - /** - * Resolved image `alt` value. - */ - alt: ComputedRef - - /** - * Resolved image `copyright` value. - */ - copyright: ComputedRef -} - -/** - * A low level composable that returns a resolved information about a Prismic - * image field. - * - * @param props - {@link UsePrismicImageOptions} - * - * @returns - Resolved image information {@link UsePrismicImageReturnType} - */ -export const usePrismicImage = ( - props: UsePrismicImageOptions, -): UsePrismicImageReturnType => { - const { options } = usePrismic() - - const asImage = computed(() => { - const field = unref(props.field) - - if (!isFilled.image(field)) { - return { - src: null, - srcset: null, - } - } - - const imgixParams = unref(props.imgixParams) - const widths = unref(props.widths) - const pixelDensities = unref(props.pixelDensities) - - if (widths) { - if (!__PRODUCTION__ && pixelDensities) { - console.warn( - "[PrismicImage] Only one of `widths` or `pixelDensities` props can be provided. You can resolve this warning by removing either the `widths` or `pixelDensities` prop. `widths` will be used in this case.", - props, - ) - } - - return asImageWidthSrcSet(field, { - ...imgixParams, - widths: - widths === "defaults" - ? options.components?.imageWidthSrcSetDefaults - : widths, - }) - } else if (pixelDensities) { - return asImagePixelDensitySrcSet(field, { - ...imgixParams, - pixelDensities: - pixelDensities === "defaults" - ? options.components?.imagePixelDensitySrcSetDefaults - : pixelDensities, - }) - } else { - return { - src: asImageSrc(field, imgixParams), - srcset: null, - } - } - }) - - const src = computed(() => { - return asImage.value.src - }) - const srcset = computed(() => { - return asImage.value.srcset - }) - const alt = computed(() => { - return unref(props.field).alt || "" - }) - const copyright = computed(() => { - return unref(props.field).copyright || null - }) - - return { - src, - srcset, - alt, - copyright, - } -} - -/** - * `` implementation. - * - * @internal - */ -export const PrismicImageImpl = /*#__PURE__*/ defineComponent({ - name: "PrismicImage", - props: { - field: { - type: Object as PropType>, - required: true, - }, - imageComponent: { - type: [String, Object] as PropType< - string | ConcreteComponent | Raw - >, - default: undefined, - required: false, - }, - imgixParams: { - type: Object as PropType[1]>, - default: undefined, - required: false, - }, - widths: { - type: [String, Object] as PropType< - | NonNullable[1]>["widths"] - | "thumbnails" - | "defaults" - >, - default: undefined, - required: false, - }, - pixelDensities: { - type: [String, Object] as PropType< - | NonNullable< - Parameters[1] - >["pixelDensities"] - | "defaults" - >, - default: undefined, - required: false, - }, - }, - setup(props) { - // Prevent fatal if user didn't check for field, throws `Invalid prop` warn - if (!props.field) { - return () => null - } - - const { options } = usePrismic() - - const type = computed(() => { - return ( - props.imageComponent || - options.components?.imageComponent || - defaultImageComponent - ) - }) - - const { src, srcset, alt, copyright } = usePrismicImage(props) - - return () => { - const attributes = { - src: src.value, - srcset: srcset.value, - alt: alt.value, - } - - switch (type.value) { - case "img": - // Fitting img tag interface - return h("img", attributes) - - default: - return h(simplyResolveComponent(type.value), { - ...attributes, - copyright: copyright.value, - }) - } - } - }, -}) - -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to render a Prismic image field. - * - * @see Component props {@link PrismicImageProps} - * @see Templating image fields {@link https://prismic.io/docs/technologies/vue-template-content#images} - */ -export const PrismicImage = PrismicImageImpl as unknown as { - new (): { - $props: AllowedComponentProps & - ComponentCustomProps & - VNodeProps & - PrismicImageProps - } -} diff --git a/src/components/PrismicImage.vue b/src/components/PrismicImage.vue new file mode 100644 index 0000000..60eed22 --- /dev/null +++ b/src/components/PrismicImage.vue @@ -0,0 +1,160 @@ + + + diff --git a/test/components-PrismicImage.test.ts b/test/components-PrismicImage.test.ts index 26aa5ea..15d5145 100644 --- a/test/components-PrismicImage.test.ts +++ b/test/components-PrismicImage.test.ts @@ -1,86 +1,88 @@ import { expect, it, vi } from "vitest" -import * as mock from "@prismicio/mock" -import type { ImageField } from "@prismicio/client" import { mount } from "@vue/test-utils" -import { markRaw } from "vue" - -import { - WrapperComponent, - createWrapperComponent, -} from "./__fixtures__/WrapperComponent" import { createPrismic } from "../src" -import { PrismicImageImpl } from "../src/components" +import { PrismicImage } from "../src/components" -it("renders image field", () => { - const wrapper = mount(PrismicImageImpl, { - props: { field: mock.value.image({ seed: 1 }) }, +it("renders nothing when passed an empty field", () => { + const wrapper = mount(PrismicImage, { + props: { + field: { url: null, alt: null, copyright: null }, + }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"Maecenas sed enim ut sem viverra aliquet eget sit"`, - ) + expect(wrapper.html()).toBe("") }) -it("renders image field with an accessible default alt value", () => { - const wrapper = mount(PrismicImageImpl, { - props: { field: { ...mock.value.image({ seed: 2 }), alt: null } }, +it("renders an img element with a width-based srcset by default", (ctx) => { + const wrapper = mount(PrismicImage, { + props: { field: ctx.mock.value.image() }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `""`, + `"Vulputate sapien nec sagittis aliquam malesuada"`, ) }) -it("renders image field with provided alt value", () => { - const wrapper = mount(PrismicImageImpl, { - props: { field: { ...mock.value.image({ seed: 2 }), alt: "foo" } }, - attrs: { - alt: "bar", +it("renders a width-based srcset with given widths", (ctx) => { + const wrapper = mount(PrismicImage, { + props: { + field: ctx.mock.value.image(), + imgixParams: { sat: 100 }, + widths: [100, 200, 300], }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"bar"`, + `"At tellus at urna condimentum mattis pellentesque"`, ) }) -it("renders image field with imgix URL parameters", () => { - const wrapper = mount(PrismicImageImpl, { +it("renders a width-based srcset with default widths if widths is `defaults`", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 3 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, + widths: "defaults", }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"Faucibus a pellentesque sit amet porttitor eget dolor morbi non"`, + `"Gravida dictum fusce ut placerat orci nulla pellentesque dignissim"`, ) }) -it("renders image field with width-based `srcset`", () => { - const wrapper = mount(PrismicImageImpl, { +it("renders a width-based srcset with plugin's default widths if widths is `defaults`", (ctx) => { + const prismic = createPrismic({ + endpoint: "test", + components: { + imageWidthSrcSetDefaults: [400, 500, 600], + }, + }) + + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 4 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, - widths: [100, 200, 300], + widths: "defaults", }, + global: { plugins: [prismic] }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"Lobortis feugiat vivamus at augue eget arcu"`, + `"Nisl vel pretium lectus quam id leo in vitae turpis massa sed"`, ) }) -it("renders image field with thumbnails width-based `srcset`", () => { - const wrapper = mount(PrismicImageImpl, { +it("renders a width-based srcset with the field's responsive views if widths is `thumbnails`", (ctx) => { + const wrapper = mount(PrismicImage, { props: { field: { - ...mock.value.image({ seed: 5 }), - foo: mock.value.image({ seed: 6 }), - bar: mock.value.image({ seed: 7 }), - baz: mock.value.image({ seed: 8 }), + ...ctx.mock.value.image(), + foo: ctx.mock.value.image(), + bar: ctx.mock.value.image(), + baz: ctx.mock.value.image(), }, imgixParams: { sat: 100 }, widths: "thumbnails", @@ -88,224 +90,252 @@ it("renders image field with thumbnails width-based `srcset`", () => { }) expect(wrapper.html()).toMatchInlineSnapshot( - `"Diam maecenas sed enim ut sem"`, + `"Facilisis magna etiam tempor orci eu lobortis elementum nibh tellus molestie nunc non blandit"`, ) }) -it("renders image field with defaults width-based `srcset`", () => { - const wrapper = mount(PrismicImageImpl, { +it("renders pixel-density srcset with the given densities", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 9 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, - widths: "defaults", + pixelDensities: [1, 2], + }, + }) + + expect(wrapper.html()).toMatchInlineSnapshot( + `"Risus in hendrerit gravida rutrum quisque non tellus orci ac"`, + ) +}) + +it("renders pixel-density srcset with default densities if pixelDensities is `defaults`", (ctx) => { + const wrapper = mount(PrismicImage, { + props: { + field: ctx.mock.value.image(), + imgixParams: { sat: 100 }, + pixelDensities: "defaults", }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"Sed pulvinar proin gravida hendrerit lectus a molestie lorem ipsum dolor sit"`, + `"Sed augue lacus viverra vitae congue eu"`, ) }) -it("renders image field with plugin defaults width-based `srcset`", () => { +it("renders pixel-density srcset with plugin's default densities if pixelDensities is `defaults`", (ctx) => { const prismic = createPrismic({ endpoint: "test", components: { - imageWidthSrcSetDefaults: [400, 500, 600], + imagePixelDensitySrcSetDefaults: [3, 4], }, }) - const wrapper = mount(PrismicImageImpl, { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 10 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, - widths: "defaults", + pixelDensities: "defaults", }, global: { plugins: [prismic] }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"Ipsum suspendisse ultrices gravida dictum fusce ut placerat orci"`, + `"Amet consectetur adipiscing elit pellentesque habitant morbi tristique"`, ) }) -it("renders image field with pixel-density-based `srcset`", () => { - const wrapper = mount(PrismicImageImpl, { +it("prioritizes widths prop over pixelDensities", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 11 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, - pixelDensities: [1, 2], + widths: "defaults", + // @ts-expect-error - Purposely giving incompatible props. + pixelDensities: "defaults", }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"At erat pellentesque adipiscing commodo elit"`, + `"Aliquet porttitor lacus luctus accumsan tortor posuere ac ut consequat semper viverra"`, ) }) -it("renders image field with defaults pixel-density-based `srcset`", () => { - const wrapper = mount(PrismicImageImpl, { +it("warns if both widths and pixelDensites are given", (ctx) => { + // The warning only logs in "development". + const originalNodeEnv = process.env.NODE_ENV + process.env.NODE_ENV = "development" + const consoleWarnSpy = vi + .spyOn(console, "warn") + .mockImplementation(() => void 0) + + mount(PrismicImage, { props: { - field: mock.value.image({ seed: 12 }), + field: ctx.mock.value.image(), imgixParams: { sat: 100 }, + widths: "defaults", + // @ts-expect-error - Purposely giving incompatible props. pixelDensities: "defaults", }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"Risus nullam eget felis eget nunc lobortis"`, + expect(consoleWarnSpy).toHaveBeenCalledOnce() + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringMatching( + /only one of "widths" or "pixelDensities" props can be provided/i, + ), ) + + consoleWarnSpy.mockRestore() + process.env.NODE_ENV = originalNodeEnv }) -it("renders image field with plugin defaults pixel-density-based `srcset`", () => { - const prismic = createPrismic({ - endpoint: "test", - components: { - imagePixelDensitySrcSetDefaults: [3, 4], - }, +it("uses the field's alt if given", (ctx) => { + const wrapper = mount(PrismicImage, { + props: { field: { ...ctx.mock.value.image(), alt: "foo" } }, }) - const wrapper = mount(PrismicImageImpl, { + expect(wrapper.html()).toContain('alt="foo"') +}) + +it("alt is omitted if the field does not have an alt value", (ctx) => { + const wrapper = mount(PrismicImage, { + props: { field: { ...ctx.mock.value.image(), alt: null } }, + }) + + expect(wrapper.html()).not.toContain('alt="') +}) + +it("supports an explicit decorative fallback alt value if given", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 13 }), - imgixParams: { sat: 100 }, - pixelDensities: "defaults", + field: { ...ctx.mock.value.image(), alt: null }, + fallbackAlt: "", }, - global: { plugins: [prismic] }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"Nunc id cursus metus aliquam eleifend mi in"`, - ) + expect(wrapper.html()).toContain('alt=""') }) -it("renders image field using width-based over pixel-density-based `srcset` and warns user", () => { - vi.stubGlobal("console", { warn: vi.fn() }) +it("warns if a non-decorative fallback alt value is given", (ctx) => { + // The warning only logs in "development". + const originalNodeEnv = process.env.NODE_ENV + process.env.NODE_ENV = "development" + const consoleWarnSpy = vi + .spyOn(console, "warn") + .mockImplementation(() => void 0) - const wrapper = mount(PrismicImageImpl, { + mount(PrismicImage, { props: { - field: mock.value.image({ seed: 14 }), - imgixParams: { sat: 100 }, - widths: "defaults", - pixelDensities: "defaults", + field: ctx.mock.value.image(), + // @ts-expect-error - Purposely giving incompatible props. + fallbackAlt: "non-decorative", }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada"`, - ) - expect(console.warn).toHaveBeenCalledOnce() - expect(vi.mocked(console.warn).mock.calls[0][0]).toMatch( - /\[PrismicImage\] Only one of `widths` or `pixelDensities`/i, + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringMatching(/alt-must-be-an-empty-string/i), ) - vi.resetAllMocks() + consoleWarnSpy.mockRestore() + process.env.NODE_ENV = originalNodeEnv }) -it("renders partial image field", () => { - const wrapper = mount(PrismicImageImpl, { +it("supports an explicit decorative alt when field has an alt value", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: { - ...mock.value.image({ seed: 15 }), - url: null, - alt: null, - copyright: null, - } as unknown as ImageField, + field: { ...ctx.mock.value.image(), alt: "foo" }, + alt: "", }, }) - expect(wrapper.html()).toMatchInlineSnapshot(`""`) + expect(wrapper.html()).toContain('alt=""') }) -it("uses plugin provided image component", () => { - const prismic = createPrismic({ - endpoint: "test", - components: { - imageComponent: WrapperComponent, - }, - }) - - const wrapper = mount(PrismicImageImpl, { +it("supports an explicit decorative alt when field does not have an alt value", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: mock.value.image({ seed: 16 }), + field: { ...ctx.mock.value.image(), alt: null }, + alt: "", }, - global: { plugins: [prismic] }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, - ) + expect(wrapper.html()).toContain('alt=""') }) -it("uses provided image component over plugin provided", () => { - const prismic = createPrismic({ - endpoint: "test", - components: { - imageComponent: createWrapperComponent(1), - }, - }) +it("warns if a non-decorative alt value is given", (ctx) => { + // The warning only logs in "development". + const originalNodeEnv = process.env.NODE_ENV + process.env.NODE_ENV = "development" + const consoleWarnSpy = vi + .spyOn(console, "warn") + .mockImplementation(() => void 0) - const wrapper = mount(PrismicImageImpl, { + mount(PrismicImage, { props: { - field: mock.value.image({ seed: 17 }), - imageComponent: markRaw(createWrapperComponent(2)), + field: { ...ctx.mock.value.image(), alt: null }, + // @ts-expect-error - Purposely giving incompatible props. + alt: "non-decorative", }, - global: { plugins: [prismic] }, }) - expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringMatching(/alt-must-be-an-empty-string/i), ) + + consoleWarnSpy.mockRestore() + process.env.NODE_ENV = originalNodeEnv }) -it("renders partial image field with image component", () => { - const wrapper = mount(PrismicImageImpl, { +it("supports imgix parameters", (ctx) => { + const wrapper = mount(PrismicImage, { props: { - field: { - ...mock.value.image({ seed: 18 }), - url: null, - alt: null, - copyright: null, - } as unknown as ImageField, - imageComponent: markRaw(WrapperComponent), + field: ctx.mock.value.image(), + imgixParams: { sat: 100 }, }, }) expect(wrapper.html()).toMatchInlineSnapshot( - `"
"`, + `"Odio euismod lacinia at quis risus sed vulputate odio ut enim"`, ) }) -it("renders nothing when invalid", () => { - vi.stubGlobal("console", { warn: vi.fn() }) +it("allows removing existing Imgix params via the imgixParams prop", (ctx) => { + const field = ctx.mock.value.image({ + model: ctx.mock.model.image(), + }) + const fieldURL = new URL(field.url) + fieldURL.searchParams.set("auto", "compress,format") + fieldURL.searchParams.set("sat", "-100") + fieldURL.searchParams.set("ar", "1:2") + field.url = fieldURL.toString() - const wrapper = mount(PrismicImageImpl, { - props: { field: null as unknown as ImageField }, + const wrapper = mount(PrismicImage, { + props: { + field, + imgixParams: { auto: undefined, sat: undefined }, + }, }) - expect(wrapper.html()).toBe("") - expect(console.warn).toHaveBeenCalledOnce() - expect(vi.mocked(console.warn).mock.calls[0][0]).toMatch( - /Invalid prop: type check failed for prop/i, + expect(wrapper.html()).toMatchInlineSnapshot( + `"Ac odio tempor orci dapibus ultrices in iaculis nunc sed augue"`, ) - - vi.resetAllMocks() }) -it("reacts to changes properly", async () => { - const wrapper = mount(PrismicImageImpl, { - props: { field: mock.value.image({ seed: 19 }) }, +it("reacts to changes properly", async (ctx) => { + const wrapper = mount(PrismicImage, { + props: { field: ctx.mock.value.image() }, }) const firstRender = wrapper.html() await wrapper.setProps({ - field: mock.value.image({ seed: 20 }), + field: ctx.mock.value.image(), }) const secondRender = wrapper.html() expect(secondRender).not.toBe(firstRender) expect(secondRender).toMatchInlineSnapshot( - `"Eget felis eget nunc lobortis mattis aliquam faucibus purus in massa"`, + `"Cursus eget nunc scelerisque viverra mauris in aliquam sem fringilla"`, ) }) From a4cc1a5b88e993cb770bb5180514fcbd3bbe7381 Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 17:28:06 +0100 Subject: [PATCH 07/35] refactor!: --- src/components/PrismicText.ts | 165 ------------------ src/components/PrismicText.vue | 66 +++++++ .../components-PrismicText.test.ts.snap | 7 - test/components-PrismicText.test.ts | 131 ++++++++++---- 4 files changed, 167 insertions(+), 202 deletions(-) delete mode 100644 src/components/PrismicText.ts create mode 100644 src/components/PrismicText.vue delete mode 100644 test/__snapshots__/components-PrismicText.test.ts.snap diff --git a/src/components/PrismicText.ts b/src/components/PrismicText.ts deleted file mode 100644 index dcd5c9d..0000000 --- a/src/components/PrismicText.ts +++ /dev/null @@ -1,165 +0,0 @@ -import type { RichTextField } from "@prismicio/client" -import { asText, isFilled } from "@prismicio/client" -import type { - AllowedComponentProps, - ComponentCustomProps, - ComputedRef, - ConcreteComponent, - DefineComponent, - PropType, - Raw, - VNode, - VNodeProps, -} from "vue" -import { computed, defineComponent, h, unref } from "vue" - -import { simplyResolveComponent } from "../lib/simplyResolveComponent" - -import type { VueUseOptions } from "../types" - -/** - * The default component rendered to wrap the text output. - */ -const defaultWrapper = "div" -/** - * Props for ``. - */ -export type PrismicTextProps = { - /** - * The Prismic rich text or title field to render. - */ - field: RichTextField | null | undefined - - /** - * Separator used to join each element. - * - * @defaultValue `" "` (a space) - */ - separator?: string - - /** - * An HTML tag name, a component, or a functional component used to wrap the - * output. - * - * @defaultValue `"div"` - */ - wrapper?: string | ConcreteComponent | Raw - - /** - * The string value to be rendered when the field is empty. If a fallback is - * not given, `""` (nothing) will be rendered. - */ - fallback?: string -} - -/** - * Options for {@link usePrismicText}. - */ -export type UsePrismicTextOptions = VueUseOptions< - Omit -> - -/** - * Return type of {@link usePrismicText}. - */ -export type UsePrismicTextReturnType = { - /** - * Serialized rich text field as plain text. - */ - text: ComputedRef -} - -/** - * A low level composable that returns a serialized rich text field as plain - * text. - * - * @param props - {@link UsePrismicTextOptions} - * - * @returns - Serialized rich text field as plain text - * {@link UsePrismicTextReturnType} - */ -export const usePrismicText = ( - props: UsePrismicTextOptions, -): UsePrismicTextReturnType => { - const text = computed(() => { - const field = unref(props.field) - - if (!isFilled.richText(field)) { - return unref(props.fallback) ?? "" - } - - return asText(unref(field), unref(props.separator)) - }) - - return { - text, - } -} - -/** - * `` implementation. - * - * @internal - */ -export const PrismicTextImpl = /*#__PURE__*/ defineComponent({ - name: "PrismicText", - props: { - field: { - type: Array as unknown as PropType, - default: undefined, - required: false, - }, - separator: { - type: String as PropType, - default: undefined, - required: false, - }, - wrapper: { - type: [String, Object, Function] as PropType< - string | ConcreteComponent | Raw - >, - default: undefined, - required: false, - }, - fallback: { - type: String as PropType, - default: undefined, - required: false, - }, - }, - setup(props) { - const { text } = usePrismicText(props) - - return () => { - const parent = simplyResolveComponent(props.wrapper || defaultWrapper) - - // This works but is absurd - // if (typeof parent === "string") { - // return h(parent, null, { default: () => text.value }); - // } else { - // return h(parent, null, { default: () => text.value }); - // } - - return h(parent as VNode, null, { - default: () => text.value, - }) - } - }, -}) - -// export the public type for h/tsx inference -// also to avoid inline import() in generated d.ts files -/** - * Component to render a Prismic rich text field as plain text. - * - * @see Component props {@link PrismicTextProps} - * @see Templating rich text and title fields {@link https://prismic.io/docs/technologies/vue-template-content#rich-text-and-titles} - */ -export const PrismicText = PrismicTextImpl as unknown as { - new (): { - $props: AllowedComponentProps & - ComponentCustomProps & - VNodeProps & - PrismicTextProps - } -} diff --git a/src/components/PrismicText.vue b/src/components/PrismicText.vue new file mode 100644 index 0000000..b83089f --- /dev/null +++ b/src/components/PrismicText.vue @@ -0,0 +1,66 @@ + + + diff --git a/test/__snapshots__/components-PrismicText.test.ts.snap b/test/__snapshots__/components-PrismicText.test.ts.snap deleted file mode 100644 index 39756fa..0000000 --- a/test/__snapshots__/components-PrismicText.test.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`renders rich text field as plain text 1`] = `"
Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"`; - -exports[`uses provided wrapper component 1`] = `"
Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"`; - -exports[`uses provided wrapper tag 1`] = `"

Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

"`; diff --git a/test/components-PrismicText.test.ts b/test/components-PrismicText.test.ts index 28d54a9..87b96aa 100644 --- a/test/components-PrismicText.test.ts +++ b/test/components-PrismicText.test.ts @@ -1,77 +1,148 @@ import { expect, it } from "vitest" -import type { RichTextField } from "@prismicio/client" +import { type RichTextField, RichTextNodeType } from "@prismicio/client" import { mount } from "@vue/test-utils" import { markRaw } from "vue" import { WrapperComponent } from "./__fixtures__/WrapperComponent" -import { richTextFixture } from "./__fixtures__/richText" -import { PrismicTextImpl } from "../src/components" +import { PrismicText } from "../src/components" + +it("renders plain text representation", () => { + const wrapper = mount(PrismicText, { + props: { + field: [ + { + type: RichTextNodeType.heading1, + text: "Heading 1", + spans: [], + }, + ], + }, + }) + + expect(wrapper.html()).toBe("Heading 1") +}) -it("renders rich text field as plain text", () => { - const wrapper = mount(PrismicTextImpl, { - props: { field: richTextFixture.en }, +it("returns null when passed empty field", () => { + const wrapper = mount(PrismicText, { + props: { field: null }, }) - expect(wrapper.html()).toMatchSnapshot() + expect(wrapper.html()).toBe("") }) -it("renders fallback when rich text is empty", () => { - const wrapper1 = mount(PrismicTextImpl, { - props: { field: [] }, +it("returns fallback when passed empty field", () => { + const nullField = mount(PrismicText, { + props: { field: null, fallback: "fallback" }, }) - expect(wrapper1.html()).toBe("
") + const undefinedField = mount(PrismicText, { + props: { field: undefined, fallback: "fallback" }, + }) - const wrapper2 = mount(PrismicTextImpl, { - props: { field: [], fallback: "foo" }, + const emptyField = mount(PrismicText, { + props: { field: [], fallback: "fallback" }, }) - expect(wrapper2.html()).toBe("
foo
") + expect(nullField.html()).toBe("fallback") + expect(undefinedField.html()).toBe("fallback") + expect(emptyField.html()).toBe("fallback") +}) - const wrapper3 = mount(PrismicTextImpl, { - props: { field: null, fallback: "bar" }, +it("returns fallback when passed empty field with wrapper", () => { + const nullField = mount(PrismicText, { + props: { field: null, fallback: "fallback", wrapper: "p" }, }) - expect(wrapper3.html()).toBe("
bar
") + const undefinedField = mount(PrismicText, { + props: { field: undefined, fallback: "fallback", wrapper: "p" }, + }) - const wrapper4 = mount(PrismicTextImpl, { - props: { field: undefined, fallback: "baz" }, + const emptyField = mount(PrismicText, { + props: { field: [], fallback: "fallback", wrapper: "p" }, }) - expect(wrapper4.html()).toBe("
baz
") + expect(nullField.html()).toBe("

fallback

") + expect(undefinedField.html()).toBe("

fallback

") + expect(emptyField.html()).toBe("

fallback

") }) it("uses provided wrapper tag", () => { - const wrapper = mount(PrismicTextImpl, { - props: { field: richTextFixture.en, wrapper: "p" }, + const wrapper = mount(PrismicText, { + props: { + field: [ + { + type: RichTextNodeType.heading1, + text: "Heading 1", + spans: [], + }, + ], + wrapper: "p", + }, }) - expect(wrapper.html()).toMatchSnapshot() + expect(wrapper.html()).toBe("

Heading 1

") }) it("uses provided wrapper component", () => { - const wrapper = mount(PrismicTextImpl, { - props: { field: richTextFixture.en, wrapper: markRaw(WrapperComponent) }, + const wrapper = mount(PrismicText, { + props: { + field: [ + { + type: RichTextNodeType.heading1, + text: "Heading 1", + spans: [], + }, + ], + wrapper: markRaw(WrapperComponent), + }, }) - expect(wrapper.html()).toMatchSnapshot() + expect(wrapper.html()).toBe(`
Heading 1
`) +}) + +it("throws error if passed a string-based field (e.g. Key Text or Select)", () => { + // The error is only thrown in "development". + const originalNodeEnv = process.env.NODE_ENV + process.env.NODE_ENV = "development" + + expect(() => + mount(PrismicText, { + props: { + // @ts-expect-error - We are purposely not providing a correct field. + field: "not a Rich Text field", + wrapper: markRaw(WrapperComponent), + }, + }), + ).toThrowError(/prismictext-works-only-with-rich-text-and-title-fields/i) + + process.env.NODE_ENV = originalNodeEnv }) it("reacts to changes properly", async () => { - const wrapper = mount(PrismicTextImpl, { - props: { field: richTextFixture.en }, + const wrapper = mount(PrismicText, { + props: { + field: [ + { + type: RichTextNodeType.heading1, + text: "Heading 1", + spans: [], + }, + ], + }, }) const firstRender = wrapper.html() await wrapper.setProps({ - field: [{ type: "paragraph", text: "foo", spans: [] }] as RichTextField, + field: [ + { type: RichTextNodeType.paragraph, text: "foo", spans: [] }, + ] as RichTextField, }) const secondRender = wrapper.html() expect(secondRender).not.toBe(firstRender) - expect(secondRender).toBe("
foo
") + expect(secondRender).toBe("foo") }) From 89df916c90a0902d2b131ca77692b214f5f4b25e Mon Sep 17 00:00:00 2001 From: lihbr Date: Wed, 4 Dec 2024 17:32:20 +0100 Subject: [PATCH 08/35] ci: update typescript range --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f581731..f393adf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: os: [ubuntu-latest] node: [22] # Add relevant versions from: https://majors.nullvoxpopuli.com/q?minors=on&packages=typescript - typescript: ["4.9", "5.0", "5.1", "5.2", "5.3", "5.4"] + typescript: ["5.2", "5.3", "5.4", "5.5", "5.6"] steps: From b179f9d9f39269e77834be44de3337b96b0e5253 Mon Sep 17 00:00:00 2001 From: lihbr Date: Thu, 5 Dec 2024 14:56:07 +0100 Subject: [PATCH 09/35] fix: forwards attributes to `` --- src/components/PrismicText.vue | 2 +- test/components-PrismicText.test.ts | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/PrismicText.vue b/src/components/PrismicText.vue index b83089f..3b607e8 100644 --- a/src/components/PrismicText.vue +++ b/src/components/PrismicText.vue @@ -56,7 +56,7 @@ if (typeof process !== "undefined" && process.env.NODE_ENV === "development") {