From 3b6b5ac4575337aa4f16c81515c3ec4b64562889 Mon Sep 17 00:00:00 2001 From: Matteo Rigoni Date: Fri, 29 May 2020 12:38:50 +0200 Subject: [PATCH] feat(cms): parse vue components in text elements (#744) * feat: (wip) adding capability to parse vue components inside text element in sw cms * feat: (wip) adding capability to parse vue components inside text element in sw cms - refactor with html-to-vue library * feat: (wip) Added entities-parser plugin to work with html-to-vue * feat: (wip) Added entities-parser - moved 'he' dependency from default-theme to nuxt-module * feat: fixed SSR of CmsElementText.vue, added SwButton and SfLink to extraComponentsMap * feat: SfButtons from CmsElementText.vue now contains a proper SfLink. --- .../cms/elements/CmsElementText.vue | 91 ++++++++++++++++--- packages/default-theme/package.json | 2 + packages/nuxt-module/__tests__/module.spec.ts | 34 +++++++ packages/nuxt-module/package.json | 1 + .../entities-parser/entities-parser.csr.js | 8 ++ .../entities-parser/entities-parser.ssr.js | 8 ++ packages/nuxt-module/src/interfaces.ts | 7 +- packages/nuxt-module/src/module.ts | 27 ++++++ yarn.lock | 19 ++++ 9 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 packages/nuxt-module/plugins/entities-parser/entities-parser.csr.js create mode 100644 packages/nuxt-module/plugins/entities-parser/entities-parser.ssr.js diff --git a/packages/default-theme/cms/elements/CmsElementText.vue b/packages/default-theme/cms/elements/CmsElementText.vue index e69f2db74..cd2c9bd13 100644 --- a/packages/default-theme/cms/elements/CmsElementText.vue +++ b/packages/default-theme/cms/elements/CmsElementText.vue @@ -1,35 +1,96 @@ - - - diff --git a/packages/default-theme/package.json b/packages/default-theme/package.json index f9c86f7c4..eb6d86a52 100644 --- a/packages/default-theme/package.json +++ b/packages/default-theme/package.json @@ -21,6 +21,7 @@ "@storefront-ui/vue": "0.7.16", "currency.js": "^1.2.2", "dayjs": "^1.8.25", + "html-to-vue": "^1.2.0", "node-sass": "^4.14.1", "nuxt": "^2.12.2", "sass-loader": "^8.0.2", @@ -40,6 +41,7 @@ "eslint-config-prettier": "^6.11.0", "eslint-plugin-nuxt": ">=0.5.2", "eslint-plugin-prettier": "^3.1.3", + "husky": "^4.2.5", "jest": "^25.4.0", "lint-staged": "^10.1.7", "prettier": "^2.0.5", diff --git a/packages/nuxt-module/__tests__/module.spec.ts b/packages/nuxt-module/__tests__/module.spec.ts index 914704373..aea8a2a22 100644 --- a/packages/nuxt-module/__tests__/module.spec.ts +++ b/packages/nuxt-module/__tests__/module.spec.ts @@ -118,6 +118,40 @@ describe("nuxt-module - ShopwarePWAModule runModule", () => { }); }); + it("should add entities-parser client plugin", () => { + runModule(moduleObject, {}); + const pathForEntitiesParserClientPlugin = path.join( + __dirname, + "..", + "plugins", + "entities-parser", + "entities-parser.csr.js" + ); + expect(moduleObject.addPlugin).toBeCalledWith({ + fileName: "entities-parser.csr.js", + mode: "client", + options: {}, + src: pathForEntitiesParserClientPlugin, + }); + }); + + it("should add entities-parser server plugin", () => { + runModule(moduleObject, {}); + const pathForEntitiesParserServerPlugin = path.join( + __dirname, + "..", + "plugins", + "entities-parser", + "entities-parser.ssr.js" + ); + expect(moduleObject.addPlugin).toBeCalledWith({ + fileName: "entities-parser.ssr.js", + mode: "server", + options: {}, + src: pathForEntitiesParserServerPlugin, + }); + }); + it("should show console error when shopwareEndpoint contains api endpoint instead of just domain", () => { jest.resetAllMocks(); diff --git a/packages/nuxt-module/package.json b/packages/nuxt-module/package.json index 4ee55d86a..94a6b3632 100644 --- a/packages/nuxt-module/package.json +++ b/packages/nuxt-module/package.json @@ -21,6 +21,7 @@ "@nuxt/utils": "^2.12.2", "cookie-universal": "^2.1.3", "fs-jetpack": "^2.2.3", + "he": "^1.2.0", "path": "^0.12.7", "snyk": "^1.316.1", "universal-analytics": "^0.4.20", diff --git a/packages/nuxt-module/plugins/entities-parser/entities-parser.csr.js b/packages/nuxt-module/plugins/entities-parser/entities-parser.csr.js new file mode 100644 index 000000000..0a5264024 --- /dev/null +++ b/packages/nuxt-module/plugins/entities-parser/entities-parser.csr.js @@ -0,0 +1,8 @@ +import Vue from "vue"; + +let decoder; +Vue.prototype.$entitiesDecoder = (text) => { + decoder = decoder || document.createElement("div"); + decoder.innerHTML = text; + return decoder.textContent; +}; diff --git a/packages/nuxt-module/plugins/entities-parser/entities-parser.ssr.js b/packages/nuxt-module/plugins/entities-parser/entities-parser.ssr.js new file mode 100644 index 000000000..7594fe738 --- /dev/null +++ b/packages/nuxt-module/plugins/entities-parser/entities-parser.ssr.js @@ -0,0 +1,8 @@ +import { decode } from "he"; +import Vue from "vue"; + +Vue.prototype.$entitiesDecoder = (html) => decode(html); +export default ({ app }, inject) => { + // Set the function directly on the context.app object + app.$entitiesDecoder = (html) => decode(html); +}; diff --git a/packages/nuxt-module/src/interfaces.ts b/packages/nuxt-module/src/interfaces.ts index fe0397146..56d0fc73d 100644 --- a/packages/nuxt-module/src/interfaces.ts +++ b/packages/nuxt-module/src/interfaces.ts @@ -17,7 +17,12 @@ export interface NuxtModuleOptions extends ModuleThis { }; }; addLayout: (options: { src: string }, templateName: string) => void; - addPlugin: (options: { src: string; fileName: string; options: {} }) => void; + addPlugin: (options: { + src: string; + fileName: string; + mode?: string; + options: {}; + }) => void; extendRoutes: (method: Function) => void; extendBuild: (method: Function) => void; nuxt: any; diff --git a/packages/nuxt-module/src/module.ts b/packages/nuxt-module/src/module.ts index eb6fc0225..43cebb857 100644 --- a/packages/nuxt-module/src/module.ts +++ b/packages/nuxt-module/src/module.ts @@ -57,11 +57,38 @@ export function runModule(moduleObject: NuxtModuleOptions, moduleOptions: {}) { options: moduleOptions, }); + moduleObject.addPlugin({ + src: path.join( + __dirname, + "..", + "plugins", + "entities-parser", + "entities-parser.csr.js" + ), + fileName: "entities-parser.csr.js", + mode: "client", + options: moduleOptions, + }); + + moduleObject.addPlugin({ + src: path.join( + __dirname, + "..", + "plugins", + "entities-parser", + "entities-parser.ssr.js" + ), + fileName: "entities-parser.ssr.js", + mode: "server", + options: {}, + }); + moduleObject.addPlugin({ src: path.join(__dirname, "..", "plugins", "composition-api.js"), fileName: "composition-api.js", options: moduleOptions, }); + // fixes problem with multiple composition-api instances moduleObject.extendBuild((config: WebpackConfig) => { config.resolve.alias["@vue/composition-api"] = path.resolve( diff --git a/yarn.lock b/yarn.lock index 2ad585277..6ea5acf85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8896,11 +8896,25 @@ html-minifier@^4.0.0: relateurl "^0.2.7" uglify-js "^3.5.1" +html-parse-stringify2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz#dc5670b7292ca158b7bc916c9a6735ac8872834a" + integrity sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o= + dependencies: + void-elements "^2.0.1" + html-tags@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" integrity sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos= +html-to-vue@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-to-vue/-/html-to-vue-1.2.0.tgz#d123f37f78cc944d67161bc5610bb8563dedbf22" + integrity sha512-x0rcwxsv+Vqz3bUYYw4fwsfzFB25xSH8/LcRQyInVwqXcdmxx3ak3ckX3FW//pDwublPBWVfkjnBWzcfNird4Q== + dependencies: + html-parse-stringify2 "^2.0.1" + html-webpack-plugin@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b" @@ -17119,6 +17133,11 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + vscode-languageserver-types@^3.5.0: version "3.15.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de"