@shopware/cms-base-layer 0.0.0-canary-20260119085417 → 0.0.0-canary-20260119102238
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -349,7 +349,7 @@ No additional packages needed to be installed.
|
|
|
349
349
|
|
|
350
350
|
Full changelog for stable version is available [here](https://github.com/shopware/frontends/blob/main/packages/cms-base-layer/CHANGELOG.md)
|
|
351
351
|
|
|
352
|
-
### Latest changes: 0.0.0-canary-
|
|
352
|
+
### Latest changes: 0.0.0-canary-20260119102238
|
|
353
353
|
|
|
354
354
|
### Minor Changes
|
|
355
355
|
|
|
@@ -357,6 +357,8 @@ Full changelog for stable version is available [here](https://github.com/shopwar
|
|
|
357
357
|
|
|
358
358
|
Includes new `SwFilterDropdown` and `SwProductListingFiltersHorizontal` components, with a `displayMode` prop added to all filter components to support both layouts.
|
|
359
359
|
|
|
360
|
+
- [#2214](https://github.com/shopware/frontends/pull/2214) [`ccb9384`](https://github.com/shopware/frontends/commit/ccb93849be07f1b6a4e192de02579a528b5b6ac4) Thanks [@mdanilowicz](https://github.com/mdanilowicz)! - Add full TypeScript typing to html-to-vue helper functions
|
|
361
|
+
|
|
360
362
|
### Patch Changes
|
|
361
363
|
|
|
362
364
|
- [#2210](https://github.com/shopware/frontends/pull/2210) [`c6b88b7`](https://github.com/shopware/frontends/commit/c6b88b7d2c50054188356aeb0f83053554d442f5) Thanks [@mkucmus](https://github.com/mkucmus)! - Anchor tags with "btn btn-primary" classes from the API were not being
|
|
@@ -1,51 +1,80 @@
|
|
|
1
|
-
//@ts-nocheck
|
|
2
1
|
/**
|
|
3
2
|
* Based on the https://github.com/HCESrl/html-to-vue
|
|
4
3
|
*/
|
|
4
|
+
// @ts-expect-error - html-to-ast has type definition issues with package.json exports
|
|
5
5
|
import { parse } from "html-to-ast";
|
|
6
6
|
import type { NodeObject } from "./getOptionsFromNode";
|
|
7
7
|
|
|
8
|
+
type ASTNode = NodeObject | NodeObject[];
|
|
9
|
+
|
|
10
|
+
type VisitCallback = (
|
|
11
|
+
node: unknown,
|
|
12
|
+
parent: NodeObject | null,
|
|
13
|
+
key: string | null,
|
|
14
|
+
index: number | undefined,
|
|
15
|
+
) => void;
|
|
16
|
+
|
|
17
|
+
type ExtraComponentConfig = {
|
|
18
|
+
conditions: (node: NodeObject) => boolean;
|
|
19
|
+
renderer?: unknown;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RectifyConfig = {
|
|
23
|
+
extraComponentsMap?: Record<string, ExtraComponentConfig>;
|
|
24
|
+
};
|
|
25
|
+
|
|
8
26
|
/**
|
|
9
27
|
* Visit each node in the AST - with callback (adapted from https://lihautan.com/manipulating-ast-with-javascript/)
|
|
10
|
-
* @param
|
|
11
|
-
* @param
|
|
28
|
+
* @param ast html-parse-stringify AST
|
|
29
|
+
* @param callback
|
|
12
30
|
*/
|
|
13
|
-
function _visitAST(ast, callback) {
|
|
14
|
-
function _visit(
|
|
31
|
+
function _visitAST(ast: ASTNode, callback: VisitCallback): void {
|
|
32
|
+
function _visit(
|
|
33
|
+
node: unknown,
|
|
34
|
+
parent: NodeObject | null,
|
|
35
|
+
key: string | null,
|
|
36
|
+
index: number | undefined,
|
|
37
|
+
): void {
|
|
15
38
|
callback(node, parent, key, index);
|
|
16
39
|
if (Array.isArray(node)) {
|
|
17
40
|
// node is an array
|
|
18
|
-
node.forEach((value,
|
|
19
|
-
_visit
|
|
41
|
+
node.forEach((value, idx) => {
|
|
42
|
+
_visit(value, node as unknown as NodeObject, null, idx);
|
|
20
43
|
});
|
|
21
44
|
} else if (isNode(node)) {
|
|
22
45
|
const keys = Object.keys(node);
|
|
23
46
|
for (let i = 0; i < keys.length; i++) {
|
|
24
|
-
const
|
|
47
|
+
const childKey = keys[i];
|
|
48
|
+
if (childKey === undefined) continue;
|
|
49
|
+
const child = (node as Record<string, unknown>)[childKey];
|
|
25
50
|
if (Array.isArray(child)) {
|
|
26
51
|
for (let j = 0; j < child.length; j++) {
|
|
27
|
-
_visit
|
|
52
|
+
_visit(child[j], node, key, j);
|
|
28
53
|
}
|
|
29
54
|
} else if (isNode(child)) {
|
|
30
|
-
_visit
|
|
55
|
+
_visit(child, node, key, undefined);
|
|
31
56
|
}
|
|
32
57
|
}
|
|
33
58
|
}
|
|
34
59
|
}
|
|
35
|
-
_visit
|
|
60
|
+
_visit(ast, null, null, undefined);
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
/**
|
|
39
64
|
*
|
|
40
65
|
* @param node html-parse-stringify AST node
|
|
41
|
-
* @returns {boolean
|
|
66
|
+
* @returns {boolean}
|
|
42
67
|
*/
|
|
43
|
-
export function isNode(node:
|
|
44
|
-
return
|
|
68
|
+
export function isNode(node: unknown): node is NodeObject {
|
|
69
|
+
return (
|
|
70
|
+
typeof node === "object" &&
|
|
71
|
+
node !== null &&
|
|
72
|
+
typeof (node as NodeObject).type !== "undefined"
|
|
73
|
+
);
|
|
45
74
|
}
|
|
46
75
|
|
|
47
|
-
export function generateAST(html) {
|
|
48
|
-
return parse(html);
|
|
76
|
+
export function generateAST(html: string): ASTNode {
|
|
77
|
+
return parse(html) as ASTNode;
|
|
49
78
|
}
|
|
50
79
|
|
|
51
80
|
/**
|
|
@@ -54,16 +83,21 @@ export function generateAST(html) {
|
|
|
54
83
|
* @param config
|
|
55
84
|
* @returns {*}
|
|
56
85
|
*/
|
|
57
|
-
export function rectifyAST(ast, config) {
|
|
58
|
-
const _ast = JSON.parse(JSON.stringify(ast));
|
|
86
|
+
export function rectifyAST(ast: ASTNode, config: RectifyConfig): ASTNode {
|
|
87
|
+
const _ast = JSON.parse(JSON.stringify(ast)) as ASTNode;
|
|
59
88
|
const keys = config.extraComponentsMap
|
|
60
89
|
? Object.keys(config.extraComponentsMap)
|
|
61
90
|
: [];
|
|
62
91
|
_visitAST(_ast, (node) => {
|
|
92
|
+
if (!isNode(node)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
63
95
|
// checking whether the AST has some components that has to become Vue Components
|
|
64
96
|
for (let i = 0; i < keys.length; i++) {
|
|
65
97
|
const currentKey = keys[i];
|
|
66
|
-
if (
|
|
98
|
+
if (currentKey === undefined) continue;
|
|
99
|
+
const componentConfig = config.extraComponentsMap?.[currentKey];
|
|
100
|
+
if (componentConfig?.conditions(node)) {
|
|
67
101
|
node.name = currentKey;
|
|
68
102
|
}
|
|
69
103
|
}
|
|
@@ -3,17 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { ComponentInternalInstance, h } from "vue";
|
|
6
|
-
import { generateAST, rectifyAST } from "./ast";
|
|
7
|
-
import { renderer } from "./renderer";
|
|
6
|
+
import { type RectifyConfig, generateAST, rectifyAST } from "./ast";
|
|
7
|
+
import { type RendererConfig, renderer } from "./renderer";
|
|
8
8
|
|
|
9
|
-
type DefaultConfig =
|
|
10
|
-
container: {
|
|
11
|
-
type: string;
|
|
12
|
-
};
|
|
13
|
-
extraComponentsMap: Record<string, unknown>;
|
|
14
|
-
renderAnyway: boolean;
|
|
15
|
-
textTransformer: (text: string) => string;
|
|
16
|
-
};
|
|
9
|
+
type DefaultConfig = RendererConfig;
|
|
17
10
|
|
|
18
11
|
const defaultConfig: DefaultConfig = {
|
|
19
12
|
container: {
|
|
@@ -33,7 +26,10 @@ export function renderHtml(
|
|
|
33
26
|
) {
|
|
34
27
|
const mergedConfig = Object.assign(defaultConfig, config);
|
|
35
28
|
const _ast = generateAST(html);
|
|
36
|
-
const
|
|
29
|
+
const rectifyConfig: RectifyConfig = {
|
|
30
|
+
extraComponentsMap: config.extraComponentsMap,
|
|
31
|
+
};
|
|
32
|
+
const _rectifiedAst = rectifyAST(_ast, rectifyConfig);
|
|
37
33
|
|
|
38
34
|
return renderer(
|
|
39
35
|
_rectifiedAst,
|
|
@@ -1,26 +1,86 @@
|
|
|
1
|
-
//@ts-nocheck
|
|
2
1
|
/**
|
|
3
2
|
* Based on the https://github.com/HCESrl/html-to-vue
|
|
4
3
|
*/
|
|
5
4
|
|
|
5
|
+
import type { ComponentInternalInstance, VNode } from "vue";
|
|
6
6
|
import { isNode } from "./ast";
|
|
7
|
+
import type { NodeObject } from "./getOptionsFromNode";
|
|
7
8
|
import { getOptionsFromNode } from "./getOptionsFromNode";
|
|
8
9
|
|
|
10
|
+
type ASTNode = NodeObject | NodeObject[];
|
|
11
|
+
|
|
12
|
+
type RawChildren = string | number | boolean | VNode;
|
|
13
|
+
|
|
14
|
+
type ExtraComponentRenderer = (
|
|
15
|
+
node: NodeObject,
|
|
16
|
+
children: RawChildren[],
|
|
17
|
+
createElement: typeof import("vue").h,
|
|
18
|
+
context: ComponentInternalInstance | null,
|
|
19
|
+
) => VNode;
|
|
20
|
+
|
|
21
|
+
export type ExtraComponentConfig = {
|
|
22
|
+
conditions: (node: NodeObject) => boolean;
|
|
23
|
+
renderer: ExtraComponentRenderer;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type RendererConfig = {
|
|
27
|
+
container: {
|
|
28
|
+
type: string;
|
|
29
|
+
};
|
|
30
|
+
extraComponentsMap: Record<string, ExtraComponentConfig>;
|
|
31
|
+
renderAnyway: boolean;
|
|
32
|
+
textTransformer: (text: string) => string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function flattenChildren(
|
|
36
|
+
children: RawChildren | RawChildren[] | undefined,
|
|
37
|
+
): RawChildren[] {
|
|
38
|
+
if (children === undefined) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(children)) {
|
|
42
|
+
const result: RawChildren[] = [];
|
|
43
|
+
for (const child of children) {
|
|
44
|
+
if (child !== null && child !== undefined) {
|
|
45
|
+
if (Array.isArray(child)) {
|
|
46
|
+
result.push(...flattenChildren(child));
|
|
47
|
+
} else {
|
|
48
|
+
result.push(child);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
return [children];
|
|
55
|
+
}
|
|
56
|
+
|
|
9
57
|
/**
|
|
10
58
|
* rendering the ast into vue render functions
|
|
11
|
-
* @param
|
|
12
|
-
* @param
|
|
13
|
-
* @param
|
|
14
|
-
* @param
|
|
59
|
+
* @param ast AST generated by html-parse-stringify
|
|
60
|
+
* @param config our configuration
|
|
61
|
+
* @param createElement vue's createElement
|
|
62
|
+
* @param context vue functional component context
|
|
63
|
+
* @param resolveUrl function to resolve URLs
|
|
15
64
|
*/
|
|
16
|
-
export function renderer(
|
|
17
|
-
|
|
65
|
+
export function renderer(
|
|
66
|
+
ast: ASTNode,
|
|
67
|
+
config: RendererConfig,
|
|
68
|
+
createElement: typeof import("vue").h,
|
|
69
|
+
context: ComponentInternalInstance | null,
|
|
70
|
+
resolveUrl: (url: string) => string,
|
|
71
|
+
): VNode {
|
|
72
|
+
function _render(
|
|
73
|
+
h: typeof createElement,
|
|
74
|
+
node: unknown,
|
|
75
|
+
): RawChildren | RawChildren[] | undefined {
|
|
18
76
|
if (Array.isArray(node)) {
|
|
19
|
-
const nodes = [];
|
|
77
|
+
const nodes: RawChildren[] = [];
|
|
20
78
|
// node is an array
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
79
|
+
for (const subnode of node) {
|
|
80
|
+
const rendered = _render(h, subnode);
|
|
81
|
+
const flattened = flattenChildren(rendered);
|
|
82
|
+
nodes.push(...flattened);
|
|
83
|
+
}
|
|
24
84
|
return nodes;
|
|
25
85
|
}
|
|
26
86
|
|
|
@@ -31,26 +91,26 @@ export function renderer(ast, config, createElement, context, resolveUrl) {
|
|
|
31
91
|
}
|
|
32
92
|
if (node.type === "tag") {
|
|
33
93
|
const transformedNode = getOptionsFromNode(node, resolveUrl);
|
|
34
|
-
const children = [];
|
|
35
|
-
node.children
|
|
36
|
-
|
|
37
|
-
|
|
94
|
+
const children: RawChildren[] = [];
|
|
95
|
+
for (const child of node.children) {
|
|
96
|
+
const rendered = _render(h, child);
|
|
97
|
+
const flattened = flattenChildren(rendered);
|
|
98
|
+
children.push(...flattened);
|
|
99
|
+
}
|
|
38
100
|
// if it's an extra component use custom renderer
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
node,
|
|
43
|
-
children,
|
|
44
|
-
h,
|
|
45
|
-
context,
|
|
46
|
-
);
|
|
101
|
+
const componentConfig = config.extraComponentsMap[node.name];
|
|
102
|
+
if (componentConfig !== undefined) {
|
|
103
|
+
return componentConfig.renderer(node, children, h, context);
|
|
47
104
|
}
|
|
48
105
|
// else, create normal html element
|
|
49
106
|
return h(node.name, transformedNode, [...children]);
|
|
50
107
|
}
|
|
51
108
|
}
|
|
109
|
+
return undefined;
|
|
52
110
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
111
|
+
|
|
112
|
+
const rendered = _render(createElement, ast);
|
|
113
|
+
const children = flattenChildren(rendered);
|
|
114
|
+
|
|
115
|
+
return createElement(config.container.type, context?.data || {}, children);
|
|
56
116
|
}
|