Skip to content

Commit

Permalink
fix(cms-base): import changes (GH-31)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdanilowicz authored and patzick committed Jan 10, 2023
1 parent bf55498 commit 21acce6
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/brave-seahorses-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/cms-base": minor
---

Move html-to-vue into the project
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { useCmsElementConfig } from "@shopware-pwa/composables-next";
import { h } from "vue";
import { CSSProperties } from "vue";
import { decodeHTML } from "entities";
// @ts-ignore
import htmlToVue from "html-to-vue";
// @ts-ignore
const { renderHtml, getOptionsFromNode } = htmlToVue;
import { getOptionsFromNode } from "../../../../helpers/html-to-vue/getOptionsFromNode";
import { renderHtml } from "../../../../helpers/html-to-vue/renderToHtml";
const props = defineProps<{
content: CmsElementText;
Expand Down
71 changes: 71 additions & 0 deletions packages/cms-base/helpers/html-to-vue/ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//@ts-nocheck
/**
* Based on the https://github.com/HCESrl/html-to-vue
*/
import parser from "html-parse-stringify";

/**
* Visit each node in the AST - with callback (adapted from https://lihautan.com/manipulating-ast-with-javascript/)
* @param {*} ast html-parse-stringify AST
* @param {*} callback
*/
function _visitAST(ast, callback) {
function _visit(node, parent, key, index) {
callback(node, parent, key, index);
if (Array.isArray(node)) {
// node is an array
node.forEach((value, index) => {
_visit.call(this, value, node, null, index);
});
} else if (isNode(node)) {
const keys = Object.keys(node);
for (let i = 0; i < keys.length; i++) {
const child = node[keys[i]];
if (Array.isArray(child)) {
for (let j = 0; j < child.length; j++) {
_visit.call(this, child[j], node, key, j);
}
} else if (isNode(child)) {
_visit.call(this, child, node, key, undefined);
}
}
}
}
_visit.call(this, ast, null, undefined, undefined);
}

/**
*
* @param node html-parse-stringify AST node
* @returns {boolean|boolean}
*/
export function isNode(node) {
return typeof node === "object" && typeof node.type !== "undefined";
}

export function generateAST(html) {
return parser.parse(html);
}

/**
* Converts ast html nodes in vue components
* @param ast
* @param config
* @returns {*}
*/
export function rectifyAST(ast, config) {
const _ast = JSON.parse(JSON.stringify(ast));
const keys = config.extraComponentsMap
? Object.keys(config.extraComponentsMap)
: [];
_visitAST(_ast, (node, parent, key, index) => {
// checking whether the AST has some components that has to become Vue Components
for (let i = 0; i < keys.length; i++) {
const currentKey = keys[i];
if (config.extraComponentsMap[currentKey].conditions(node)) {
node.name = currentKey;
}
}
});
return _ast;
}
15 changes: 15 additions & 0 deletions packages/cms-base/helpers/html-to-vue/getOptionsFromNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
type Options = {
style: String;
class: String;
attrs: Object;
};

export function getOptionsFromNode(node: any): Options {
const { style, class: classArs, ...rest } = node.attrs;

return {
style,
attrs: rest,
class: classArs,
};
}
37 changes: 37 additions & 0 deletions packages/cms-base/helpers/html-to-vue/renderToHtml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Based on the https://github.com/HCESrl/html-to-vue
*/

import { generateAST, rectifyAST } from "./ast";
import { renderer } from "./renderer";

type DefaultConfig = {
container: {
type: string;
};
extraComponentsMap: any;
renderAnyway: boolean;
textTransformer: (text: string) => string;
};

const defaultConfig: DefaultConfig = {
container: {
type: "div",
},
extraComponentsMap: {},
renderAnyway: false,
textTransformer: (text: string) => text,
};

export function renderHtml(
html: string,
config: Partial<DefaultConfig>,
createElement: any,
context: any
) {
const mergedConfig = Object.assign(defaultConfig, config);
const _ast = generateAST(html);
const _rectifiedAst = rectifyAST(_ast, config);

return renderer(_rectifiedAst, mergedConfig, createElement, context);
}
53 changes: 53 additions & 0 deletions packages/cms-base/helpers/html-to-vue/renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//@ts-nocheck
/**
* Based on the https://github.com/HCESrl/html-to-vue
*/

import { isNode } from "./ast";
import { getOptionsFromNode } from "./getOptionsFromNode";

/**
* rendering the ast into vue render functions
* @param {*} ast AST generated by html-parse-stringify
* @param {*} config our configuration
* @param {*} createElement vue's createElement
* @param {*} context vue functional component context
*/
export function renderer(ast, config, createElement, context) {
function _render(h, node, parent, key, index) {
if (Array.isArray(node)) {
const nodes = [];
// node is an array
node.forEach((subnode, index) => {
nodes.push(_render.call(this, h, subnode, node, null, index, h));
});
return nodes;
} else if (isNode(node)) {
// node is either a node with children or a node or a text node
if (node.type === "text") {
return config.textTransformer(node.content); // return text
}
if (node.type === "tag") {
const children = [];
node.children.forEach((child, index) => {
children.push(_render.call(this, h, child, node, index));
});
// if it's an extra component use custom renderer
if (typeof config.extraComponentsMap[node.name] !== "undefined") {
return config.extraComponentsMap[node.name].renderer.call(
this,
node,
children,
h,
context
);
}
// else, create normal html element
return h(node.name, getOptionsFromNode(node), [...children]);
}
}
}
return createElement(config.container.type, context.data, [
..._render.call(this, createElement, ast),
]);
}
2 changes: 1 addition & 1 deletion packages/cms-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"entities": "^4.4.0",
"html-to-vue": "^1.4.0"
"html-parse-stringify": "^3.0.1"
},
"devDependencies": {
"@nuxt/schema": "^3.0.0",
Expand Down
20 changes: 7 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 21acce6

Please sign in to comment.