@shopware/cms-base-layer 0.0.0-canary-20250116171244

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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/components/SwCategoryNavigation.vue +44 -0
  4. package/components/SwCategoryNavigationLink.vue +57 -0
  5. package/components/SwContactForm.vue +392 -0
  6. package/components/SwListingProductPrice.vue +88 -0
  7. package/components/SwMedia3D.vue +34 -0
  8. package/components/SwNewsletterForm.vue +347 -0
  9. package/components/SwPagination.vue +106 -0
  10. package/components/SwProductAddToCart.vue +93 -0
  11. package/components/SwProductCard.vue +285 -0
  12. package/components/SwProductGallery.vue +39 -0
  13. package/components/SwProductListingFilter.vue +42 -0
  14. package/components/SwProductListingFilters.vue +292 -0
  15. package/components/SwProductPrice.vue +99 -0
  16. package/components/SwProductReviews.vue +99 -0
  17. package/components/SwProductUnits.vue +54 -0
  18. package/components/SwSharedPrice.vue +19 -0
  19. package/components/SwSlider.vue +328 -0
  20. package/components/SwVariantConfigurator.vue +116 -0
  21. package/components/listing-filters/SwFilterPrice.vue +160 -0
  22. package/components/listing-filters/SwFilterProperties.vue +123 -0
  23. package/components/listing-filters/SwFilterRating.vue +101 -0
  24. package/components/listing-filters/SwFilterShippingFree.vue +104 -0
  25. package/components/public/cms/CmsGenericBlock.md +27 -0
  26. package/components/public/cms/CmsGenericBlock.vue +63 -0
  27. package/components/public/cms/CmsGenericElement.md +31 -0
  28. package/components/public/cms/CmsGenericElement.vue +38 -0
  29. package/components/public/cms/CmsNoComponent.vue +27 -0
  30. package/components/public/cms/CmsPage.md +36 -0
  31. package/components/public/cms/CmsPage.vue +65 -0
  32. package/components/public/cms/block/CmsBlockCategoryNavigation.vue +16 -0
  33. package/components/public/cms/block/CmsBlockCenterText.vue +26 -0
  34. package/components/public/cms/block/CmsBlockCrossSelling.vue +15 -0
  35. package/components/public/cms/block/CmsBlockCustomForm.vue +17 -0
  36. package/components/public/cms/block/CmsBlockDefault.vue +14 -0
  37. package/components/public/cms/block/CmsBlockForm.vue +17 -0
  38. package/components/public/cms/block/CmsBlockGalleryBuybox.vue +25 -0
  39. package/components/public/cms/block/CmsBlockImage.vue +16 -0
  40. package/components/public/cms/block/CmsBlockImageBubbleRow.vue +32 -0
  41. package/components/public/cms/block/CmsBlockImageCover.vue +17 -0
  42. package/components/public/cms/block/CmsBlockImageFourColumn.vue +29 -0
  43. package/components/public/cms/block/CmsBlockImageGallery.vue +18 -0
  44. package/components/public/cms/block/CmsBlockImageHighlightRow.vue +27 -0
  45. package/components/public/cms/block/CmsBlockImageSimpleGrid.vue +24 -0
  46. package/components/public/cms/block/CmsBlockImageSlider.vue +17 -0
  47. package/components/public/cms/block/CmsBlockImageText.vue +19 -0
  48. package/components/public/cms/block/CmsBlockImageTextBubble.vue +51 -0
  49. package/components/public/cms/block/CmsBlockImageTextCover.vue +25 -0
  50. package/components/public/cms/block/CmsBlockImageTextGallery.vue +85 -0
  51. package/components/public/cms/block/CmsBlockImageTextRow.vue +43 -0
  52. package/components/public/cms/block/CmsBlockImageThreeColumn.vue +21 -0
  53. package/components/public/cms/block/CmsBlockImageThreeCover.vue +27 -0
  54. package/components/public/cms/block/CmsBlockImageTwoColumn.vue +25 -0
  55. package/components/public/cms/block/CmsBlockProductDescriptionReviews.vue +15 -0
  56. package/components/public/cms/block/CmsBlockProductHeading.vue +26 -0
  57. package/components/public/cms/block/CmsBlockProductListing.vue +17 -0
  58. package/components/public/cms/block/CmsBlockProductSlider.vue +16 -0
  59. package/components/public/cms/block/CmsBlockProductThreeColumn.vue +22 -0
  60. package/components/public/cms/block/CmsBlockSidebarFilter.vue +17 -0
  61. package/components/public/cms/block/CmsBlockText.vue +15 -0
  62. package/components/public/cms/block/CmsBlockTextHero.vue +15 -0
  63. package/components/public/cms/block/CmsBlockTextOnImage.vue +20 -0
  64. package/components/public/cms/block/CmsBlockTextTeaser.vue +16 -0
  65. package/components/public/cms/block/CmsBlockTextTeaserSection.vue +21 -0
  66. package/components/public/cms/block/CmsBlockTextThreeColumn.vue +22 -0
  67. package/components/public/cms/block/CmsBlockTextTwoColumn.vue +28 -0
  68. package/components/public/cms/block/CmsBlockVimeoVideo.vue +17 -0
  69. package/components/public/cms/block/CmsBlockYoutubeVideo.vue +17 -0
  70. package/components/public/cms/element/CmsElementBuyBox.md +1 -0
  71. package/components/public/cms/element/CmsElementBuyBox.vue +190 -0
  72. package/components/public/cms/element/CmsElementCategoryNavigation.md +1 -0
  73. package/components/public/cms/element/CmsElementCategoryNavigation.vue +167 -0
  74. package/components/public/cms/element/CmsElementCrossSelling.md +1 -0
  75. package/components/public/cms/element/CmsElementCrossSelling.vue +106 -0
  76. package/components/public/cms/element/CmsElementCustomForm.md +1 -0
  77. package/components/public/cms/element/CmsElementCustomForm.vue +27 -0
  78. package/components/public/cms/element/CmsElementForm.md +1 -0
  79. package/components/public/cms/element/CmsElementForm.vue +27 -0
  80. package/components/public/cms/element/CmsElementImage.md +1 -0
  81. package/components/public/cms/element/CmsElementImage.vue +105 -0
  82. package/components/public/cms/element/CmsElementImageGallery.md +1 -0
  83. package/components/public/cms/element/CmsElementImageGallery.vue +249 -0
  84. package/components/public/cms/element/CmsElementImageGallery3dPlaceholder.vue +53 -0
  85. package/components/public/cms/element/CmsElementImageSlider.md +1 -0
  86. package/components/public/cms/element/CmsElementImageSlider.vue +29 -0
  87. package/components/public/cms/element/CmsElementManufacturerLogo.md +1 -0
  88. package/components/public/cms/element/CmsElementManufacturerLogo.vue +11 -0
  89. package/components/public/cms/element/CmsElementProductBox.md +1 -0
  90. package/components/public/cms/element/CmsElementProductBox.vue +14 -0
  91. package/components/public/cms/element/CmsElementProductDescriptionReviews.md +1 -0
  92. package/components/public/cms/element/CmsElementProductDescriptionReviews.vue +109 -0
  93. package/components/public/cms/element/CmsElementProductListing.md +1 -0
  94. package/components/public/cms/element/CmsElementProductListing.vue +245 -0
  95. package/components/public/cms/element/CmsElementProductName.md +1 -0
  96. package/components/public/cms/element/CmsElementProductName.vue +10 -0
  97. package/components/public/cms/element/CmsElementProductSlider.md +1 -0
  98. package/components/public/cms/element/CmsElementProductSlider.vue +80 -0
  99. package/components/public/cms/element/CmsElementSidebarFilter.md +1 -0
  100. package/components/public/cms/element/CmsElementSidebarFilter.vue +12 -0
  101. package/components/public/cms/element/CmsElementText.md +1 -0
  102. package/components/public/cms/element/CmsElementText.vue +186 -0
  103. package/components/public/cms/element/CmsElementVimeoVideo.md +1 -0
  104. package/components/public/cms/element/CmsElementVimeoVideo.vue +63 -0
  105. package/components/public/cms/element/CmsElementYoutubeVideo.md +1 -0
  106. package/components/public/cms/element/CmsElementYoutubeVideo.vue +43 -0
  107. package/components/public/cms/section/CmsSectionDefault.md +3 -0
  108. package/components/public/cms/section/CmsSectionDefault.vue +21 -0
  109. package/components/public/cms/section/CmsSectionSidebar.md +3 -0
  110. package/components/public/cms/section/CmsSectionSidebar.vue +49 -0
  111. package/components/public/cms/skeleton/ProductCardSkeleton.vue +44 -0
  112. package/dist/index.d.mts +5 -0
  113. package/dist/index.d.ts +5 -0
  114. package/dist/index.mjs +31 -0
  115. package/helpers/clientOnly.ts +11 -0
  116. package/helpers/html-to-vue/ast.ts +72 -0
  117. package/helpers/html-to-vue/getOptionsFromNode.test.ts +129 -0
  118. package/helpers/html-to-vue/getOptionsFromNode.ts +52 -0
  119. package/helpers/html-to-vue/renderToHtml.ts +45 -0
  120. package/helpers/html-to-vue/renderer.ts +56 -0
  121. package/helpers/media/isSpatial.ts +8 -0
  122. package/index.cjs +7 -0
  123. package/nuxt.config.ts +21 -0
  124. package/package.json +69 -0
@@ -0,0 +1,49 @@
1
+ <script setup lang="ts">
2
+ import { useCmsSection } from "@shopware/composables";
3
+ import type { CmsSectionSidebar } from "@shopware/composables";
4
+ import { getTranslatedProperty } from "@shopware/helpers";
5
+ import { computed } from "vue";
6
+ import { useCategory } from "#imports";
7
+
8
+ const props = defineProps<{
9
+ content: CmsSectionSidebar;
10
+ }>();
11
+ const { getPositionContent } = useCmsSection(props.content);
12
+
13
+ const sidebarBlocks = getPositionContent("sidebar");
14
+ const mainBlocks = getPositionContent("main");
15
+ const mobileBehavior = computed(() => props.content.mobileBehavior);
16
+ const { category } = useCategory();
17
+ </script>
18
+
19
+ <template>
20
+ <div class="cms-section-sidebar grid grid-cols-12 md:grid">
21
+ <div class="col-span-12 mx-8 md:mx-5 mt-8">
22
+ <h1 class="text-4xl font-extrabold tracking-tight text-gray-900">
23
+ {{ getTranslatedProperty(category, "name") }}
24
+ </h1>
25
+ </div>
26
+ <div class="col-span-12 md:col-span-7 lg:col-span-9 order-1 md:order-2">
27
+ <CmsGenericBlock
28
+ v-for="cmsBlock in mainBlocks"
29
+ :key="cmsBlock.id"
30
+ class="overflow-auto"
31
+ :content="cmsBlock"
32
+ />
33
+ </div>
34
+ <div
35
+ :class="{
36
+ 'align-top col-span-12 md:col-span-5 lg:col-span-3 order-2 md:order-1':
37
+ mobileBehavior !== 'hidden',
38
+ 'hidden md:block': mobileBehavior === 'hidden',
39
+ }"
40
+ >
41
+ <CmsGenericBlock
42
+ v-for="cmsBlock in sidebarBlocks"
43
+ :key="cmsBlock.id"
44
+ class="overflow-auto"
45
+ :content="cmsBlock"
46
+ />
47
+ </div>
48
+ </div>
49
+ </template>
@@ -0,0 +1,44 @@
1
+ <template>
2
+ <div
3
+ role="status"
4
+ class="max-w-sm p-4 border border-gray-200 rounded shadow animate-pulse md:p-6 dark:border-gray-700"
5
+ >
6
+ <div
7
+ class="flex items-center justify-center h-48 mb-4 bg-gray-300 rounded dark:bg-gray-700"
8
+ >
9
+ <svg
10
+ class="w-10 h-10 text-gray-200 dark:text-gray-600"
11
+ aria-hidden="true"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ fill="currentColor"
14
+ viewBox="0 0 16 20"
15
+ >
16
+ <path
17
+ d="M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2ZM10.5 6a1.5 1.5 0 1 1 0 2.999A1.5 1.5 0 0 1 10.5 6Zm2.221 10.515a1 1 0 0 1-.858.485h-8a1 1 0 0 1-.9-1.43L5.6 10.039a.978.978 0 0 1 .936-.57 1 1 0 0 1 .9.632l1.181 2.981.541-1a.945.945 0 0 1 .883-.522 1 1 0 0 1 .879.529l1.832 3.438a1 1 0 0 1-.031.988Z"
18
+ />
19
+ <path
20
+ d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z"
21
+ />
22
+ </svg>
23
+ </div>
24
+ <div
25
+ class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4"
26
+ ></div>
27
+ <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
28
+ <div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
29
+ <div class="mt-4 text-right">
30
+ <div
31
+ class="mt-8 h-2 bg-gray-200 rounded-full dark:bg-gray-700 w-24"
32
+ ></div>
33
+ <button
34
+ disabled
35
+ tabindex="-1"
36
+ class="select-none font-sans font-bold text-center uppercase transition-all disabled:opacity-50 disabled:shadow-none disabled:pointer-events-none text-xs py-3 px-6 rounded-lg text-white shadow-gray-900/10 hover:shadow-gray-900/20 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none h-8 w-20 bg-gray-300 shadow-none hover:shadow-none"
37
+ type="button"
38
+ >
39
+ &nbsp;
40
+ </button>
41
+ </div>
42
+ <span class="sr-only">Loading...</span>
43
+ </div>
44
+ </template>
@@ -0,0 +1,5 @@
1
+ import { NuxtModule } from '@nuxt/schema';
2
+
3
+ declare const nuxtModule: NuxtModule;
4
+
5
+ export { nuxtModule as default };
@@ -0,0 +1,5 @@
1
+ import { NuxtModule } from '@nuxt/schema';
2
+
3
+ declare const nuxtModule: NuxtModule;
4
+
5
+ export { nuxtModule as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,31 @@
1
+ import { resolve } from 'node:path';
2
+ import { defineNuxtModule } from '@nuxt/kit';
3
+
4
+
5
+
6
+ // -- Unbuild CommonJS Shims --
7
+ import __cjs_url__ from 'url';
8
+ import __cjs_path__ from 'path';
9
+ import __cjs_mod__ from 'module';
10
+ const __filename = __cjs_url__.fileURLToPath(import.meta.url);
11
+ const __dirname = __cjs_path__.dirname(__filename);
12
+ const require = __cjs_mod__.createRequire(import.meta.url);
13
+ const nuxtModule = defineNuxtModule({
14
+ meta: {
15
+ name: "@shopware/cms-base",
16
+ configKey: "shopware-cms"
17
+ },
18
+ setup(_, nuxt) {
19
+ nuxt.hook("components:dirs", (c) => {
20
+ c.push({
21
+ path: resolve(__dirname, "../components/public"),
22
+ global: true
23
+ });
24
+ });
25
+ nuxt.options.imports.transform = nuxt.options.imports.transform || {};
26
+ nuxt.options.imports.transform.include = nuxt.options.imports.transform?.include || [];
27
+ nuxt.options.imports.transform?.include.push(/.+cms-base.+/);
28
+ }
29
+ });
30
+
31
+ export { nuxtModule as default };
@@ -0,0 +1,11 @@
1
+ import { defineComponent, onMounted, ref } from "vue";
2
+
3
+ export const ClientOnly = defineComponent({
4
+ setup(_, { slots }) {
5
+ const init = ref(false);
6
+ onMounted(() => {
7
+ init.value = true;
8
+ });
9
+ return () => (init.value && slots.default ? slots.default() : null);
10
+ },
11
+ });
@@ -0,0 +1,72 @@
1
+ //@ts-nocheck
2
+ /**
3
+ * Based on the https://github.com/HCESrl/html-to-vue
4
+ */
5
+ import { parse } from "html-to-ast";
6
+ import type { NodeObject } from "./getOptionsFromNode";
7
+
8
+ /**
9
+ * Visit each node in the AST - with callback (adapted from https://lihautan.com/manipulating-ast-with-javascript/)
10
+ * @param {*} ast html-parse-stringify AST
11
+ * @param {*} callback
12
+ */
13
+ function _visitAST(ast, callback) {
14
+ function _visit(node, parent, key, index) {
15
+ callback(node, parent, key, index);
16
+ if (Array.isArray(node)) {
17
+ // node is an array
18
+ node.forEach((value, index) => {
19
+ _visit.call(this, value, node, null, index);
20
+ });
21
+ } else if (isNode(node)) {
22
+ const keys = Object.keys(node);
23
+ for (let i = 0; i < keys.length; i++) {
24
+ const child = node[keys[i]];
25
+ if (Array.isArray(child)) {
26
+ for (let j = 0; j < child.length; j++) {
27
+ _visit.call(this, child[j], node, key, j);
28
+ }
29
+ } else if (isNode(child)) {
30
+ _visit.call(this, child, node, key, undefined);
31
+ }
32
+ }
33
+ }
34
+ }
35
+ _visit.call(this, ast, null, undefined, undefined);
36
+ }
37
+
38
+ /**
39
+ *
40
+ * @param node html-parse-stringify AST node
41
+ * @returns {boolean|boolean}
42
+ */
43
+ export function isNode(node: NodeObject) {
44
+ return typeof node === "object" && typeof node.type !== "undefined";
45
+ }
46
+
47
+ export function generateAST(html) {
48
+ return parse(html);
49
+ }
50
+
51
+ /**
52
+ * Converts ast html nodes in vue components
53
+ * @param ast
54
+ * @param config
55
+ * @returns {*}
56
+ */
57
+ export function rectifyAST(ast, config) {
58
+ const _ast = JSON.parse(JSON.stringify(ast));
59
+ const keys = config.extraComponentsMap
60
+ ? Object.keys(config.extraComponentsMap)
61
+ : [];
62
+ _visitAST(_ast, (node) => {
63
+ // checking whether the AST has some components that has to become Vue Components
64
+ for (let i = 0; i < keys.length; i++) {
65
+ const currentKey = keys[i];
66
+ if (config.extraComponentsMap[currentKey].conditions(node)) {
67
+ node.name = currentKey;
68
+ }
69
+ }
70
+ });
71
+ return _ast;
72
+ }
@@ -0,0 +1,129 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { getOptionsFromNode } from "./getOptionsFromNode";
3
+ import type { NodeObject } from "./getOptionsFromNode";
4
+
5
+ describe("getOptionsFromNode", () => {
6
+ const consoleErrorSpy = vi.spyOn(console, "error");
7
+ const urlResolverMock = vi.fn();
8
+
9
+ beforeEach(() => {
10
+ vi.resetAllMocks();
11
+ consoleErrorSpy.mockImplementation(() => {});
12
+ urlResolverMock.mockImplementation((url) => `resolved-url${url}`);
13
+ });
14
+
15
+ it("should return empty object if node is undefined", () => {
16
+ const options = getOptionsFromNode(
17
+ undefined as unknown as NodeObject,
18
+ urlResolverMock,
19
+ );
20
+
21
+ expect(options).toEqual({
22
+ attrs: {},
23
+ });
24
+ });
25
+
26
+ it("should return options object with style, classNames, and align", () => {
27
+ const node = {
28
+ attrs: {
29
+ style: "color: red",
30
+ class: "my-class",
31
+ align: "center",
32
+ },
33
+ };
34
+
35
+ const options = getOptionsFromNode(
36
+ node as unknown as NodeObject,
37
+ urlResolverMock,
38
+ );
39
+
40
+ expect(options).toEqual({
41
+ style: "color: red",
42
+ class: "my-class",
43
+ align: "center",
44
+ attrs: {},
45
+ });
46
+ });
47
+
48
+ it("should return options object without style, classNames, and align if they are not present in node.attrs", () => {
49
+ const node = {
50
+ attrs: {},
51
+ };
52
+
53
+ const options = getOptionsFromNode(
54
+ node as unknown as NodeObject,
55
+ urlResolverMock,
56
+ );
57
+
58
+ expect(options).toEqual({
59
+ attrs: {},
60
+ });
61
+ });
62
+
63
+ it("should return empty object when no attrs in node", () => {
64
+ const node = {
65
+ attrs: undefined,
66
+ };
67
+
68
+ const options = getOptionsFromNode(
69
+ node as unknown as NodeObject,
70
+ urlResolverMock,
71
+ );
72
+
73
+ expect(options).toEqual({
74
+ attrs: {},
75
+ });
76
+ });
77
+
78
+ it("should resolve URL if attrs.href exists", () => {
79
+ const node = {
80
+ attrs: {
81
+ href: "/path/to/page",
82
+ },
83
+ };
84
+
85
+ const options = getOptionsFromNode(
86
+ node as unknown as NodeObject,
87
+ urlResolverMock,
88
+ );
89
+
90
+ expect(options.attrs.href).toEqual("resolved-url/path/to/page");
91
+ });
92
+
93
+ it('should add additional attrs to "attrs" object', () => {
94
+ const node = {
95
+ attrs: {
96
+ href: "/path/to/page",
97
+ "data-test": "test",
98
+ },
99
+ };
100
+
101
+ const options = getOptionsFromNode(
102
+ node as unknown as NodeObject,
103
+ urlResolverMock,
104
+ );
105
+
106
+ expect(options.attrs.href).toEqual("resolved-url/path/to/page");
107
+ expect(options.attrs["data-test"]).toEqual("test");
108
+ });
109
+
110
+ it("should show console error if something went wrong", () => {
111
+ const node = {
112
+ attrs: {
113
+ href: "/path/to/page",
114
+ },
115
+ };
116
+
117
+ const consoleErrorSpy = vi.spyOn(console, "error");
118
+
119
+ const options = getOptionsFromNode(
120
+ node as unknown as NodeObject,
121
+ undefined as unknown as (url: string) => string,
122
+ );
123
+
124
+ expect(options).toEqual({
125
+ attrs: {},
126
+ });
127
+ expect(consoleErrorSpy).toBeCalled();
128
+ });
129
+ });
@@ -0,0 +1,52 @@
1
+ export type NodeObject = {
2
+ type: string;
3
+ name: string;
4
+ attrs?: Options;
5
+ children: NodeObject[];
6
+ voidElement: boolean;
7
+ content: string;
8
+ };
9
+
10
+ type Options = {
11
+ align?: string;
12
+ attrs: Record<string, string>;
13
+ class?: string;
14
+ color?: string;
15
+ style?: string;
16
+ href?: string;
17
+ };
18
+
19
+ export function getOptionsFromNode(
20
+ node: NodeObject,
21
+ resolveUrl: (url: string) => string,
22
+ ): Options {
23
+ const response: Options = {
24
+ attrs: {},
25
+ };
26
+ try {
27
+ if (!node?.attrs) {
28
+ return response;
29
+ }
30
+
31
+ const { align, style, class: classNames, href, ...attrs } = node.attrs;
32
+
33
+ if (align) {
34
+ response.align = align;
35
+ }
36
+ if (style) {
37
+ response.style = style;
38
+ }
39
+ if (classNames) {
40
+ response.class = classNames;
41
+ }
42
+ if (attrs && Object.keys(attrs).length > 0) {
43
+ response.attrs = attrs as unknown as Record<string, string>;
44
+ }
45
+ if (href) {
46
+ response.attrs.href = resolveUrl(href);
47
+ }
48
+ } catch (e) {
49
+ console.error("[Shopware][cms][getOptionsFromNode] error", e);
50
+ }
51
+ return response;
52
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Based on the https://github.com/HCESrl/html-to-vue
3
+ */
4
+
5
+ import type { ComponentInternalInstance, h } from "vue";
6
+ import { generateAST, rectifyAST } from "./ast";
7
+ import { renderer } from "./renderer";
8
+
9
+ type DefaultConfig = {
10
+ container: {
11
+ type: string;
12
+ };
13
+ extraComponentsMap: Record<string, unknown>;
14
+ renderAnyway: boolean;
15
+ textTransformer: (text: string) => string;
16
+ };
17
+
18
+ const defaultConfig: DefaultConfig = {
19
+ container: {
20
+ type: "div",
21
+ },
22
+ extraComponentsMap: {},
23
+ renderAnyway: false,
24
+ textTransformer: (text: string) => text,
25
+ };
26
+
27
+ export function renderHtml(
28
+ html: string,
29
+ config: Partial<DefaultConfig>,
30
+ createElement: typeof h,
31
+ context: ComponentInternalInstance | null,
32
+ resolveUrl: (url: string) => string,
33
+ ) {
34
+ const mergedConfig = Object.assign(defaultConfig, config);
35
+ const _ast = generateAST(html);
36
+ const _rectifiedAst = rectifyAST(_ast, config);
37
+
38
+ return renderer(
39
+ _rectifiedAst,
40
+ mergedConfig,
41
+ createElement,
42
+ context,
43
+ resolveUrl,
44
+ );
45
+ }
@@ -0,0 +1,56 @@
1
+ //@ts-nocheck
2
+ /**
3
+ * Based on the https://github.com/HCESrl/html-to-vue
4
+ */
5
+
6
+ import { isNode } from "./ast";
7
+ import { getOptionsFromNode } from "./getOptionsFromNode";
8
+
9
+ /**
10
+ * rendering the ast into vue render functions
11
+ * @param {*} ast AST generated by html-parse-stringify
12
+ * @param {*} config our configuration
13
+ * @param {*} createElement vue's createElement
14
+ * @param {*} context vue functional component context
15
+ */
16
+ export function renderer(ast, config, createElement, context, resolveUrl) {
17
+ function _render(h, node) {
18
+ if (Array.isArray(node)) {
19
+ const nodes = [];
20
+ // node is an array
21
+ node.forEach((subnode, index) => {
22
+ nodes.push(_render.call(this, h, subnode, node, null, index, h));
23
+ });
24
+ return nodes;
25
+ }
26
+
27
+ if (isNode(node)) {
28
+ // node is either a node with children or a node or a text node
29
+ if (node.type === "text") {
30
+ return config.textTransformer(node.content); // return text
31
+ }
32
+ if (node.type === "tag") {
33
+ const transformedNode = getOptionsFromNode(node, resolveUrl);
34
+ const children = [];
35
+ node.children.forEach((child, index) => {
36
+ children.push(_render.call(this, h, child, transformedNode, index));
37
+ });
38
+ // if it's an extra component use custom renderer
39
+ if (typeof config.extraComponentsMap[node.name] !== "undefined") {
40
+ return config.extraComponentsMap[node.name].renderer.call(
41
+ this,
42
+ transformedNode,
43
+ children,
44
+ h,
45
+ context,
46
+ );
47
+ }
48
+ // else, create normal html element
49
+ return h(node.name, transformedNode, [...children]);
50
+ }
51
+ }
52
+ }
53
+ return createElement(config.container.type, context.data, [
54
+ ..._render.call(this, createElement, ast),
55
+ ]);
56
+ }
@@ -0,0 +1,8 @@
1
+ export function isSpatial<
2
+ T extends {
3
+ fileExtension: string;
4
+ url?: string;
5
+ },
6
+ >(media: T) {
7
+ return media.fileExtension === "glb" || !!media.url?.endsWith(".glb");
8
+ }
package/index.cjs ADDED
@@ -0,0 +1,7 @@
1
+ // CommonJS proxy to bypass jiti transforms from nuxt 2 and using native ESM
2
+ module.exports = function (...args) {
3
+ return import("./dist/index.mjs").then((m) => m.default.call(this, ...args));
4
+ };
5
+
6
+ // eslint-disable-next-line
7
+ module.exports.meta = require("./package.json");
package/nuxt.config.ts ADDED
@@ -0,0 +1,21 @@
1
+ import type { NuxtConfig } from "@nuxt/schema";
2
+ import { defineNuxtConfig } from "nuxt/config";
3
+ export default defineNuxtConfig({
4
+ components: [
5
+ {
6
+ path: "./components/public",
7
+ pathPrefix: false,
8
+ // global: true,
9
+ },
10
+ {
11
+ path: "./components/",
12
+ pattern: "Sw*",
13
+ extensions: [".vue"],
14
+ global: false,
15
+ },
16
+ ],
17
+ build: {
18
+ transpile: ["@shopware/cms-base-layer"],
19
+ },
20
+ telemetry: false,
21
+ }) as NuxtConfig;
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@shopware/cms-base-layer",
3
+ "version": "0.0.0-canary-20250116171244",
4
+ "description": "Vue CMS Nuxt Layer for Shopware",
5
+ "author": "Shopware",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/shopware/frontends.git"
9
+ },
10
+ "homepage": "https://frontends.shopware.com/framework/shopping-experiences.html",
11
+ "keywords": [
12
+ "Shopware",
13
+ "Vue",
14
+ "CMS",
15
+ "Nuxt",
16
+ "Layer"
17
+ ],
18
+ "bugs": {
19
+ "url": "https://github.com/shopware/frontends/issues"
20
+ },
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "main": "./nuxt.config.ts",
24
+ "files": [
25
+ "dist",
26
+ "components",
27
+ "helpers",
28
+ "index.cjs",
29
+ "nuxt.config.ts"
30
+ ],
31
+ "dependencies": {
32
+ "@nuxt/kit": "3.14.1592",
33
+ "@tresjs/cientos": "4.0.2",
34
+ "@tresjs/core": "4.3.1",
35
+ "@vuelidate/core": "2.0.3",
36
+ "@vuelidate/validators": "2.0.4",
37
+ "@vueuse/core": "11.2.0",
38
+ "entities": "5.0.0",
39
+ "html-to-ast": "0.0.6",
40
+ "three": "0.166.1",
41
+ "vue": "3.5.13",
42
+ "xss": "1.0.15",
43
+ "@shopware/composables": "0.0.0-canary-20250116171244",
44
+ "@shopware/helpers": "0.0.0-canary-20250116171244",
45
+ "@shopware/api-client": "1.2.0"
46
+ },
47
+ "devDependencies": {
48
+ "@biomejs/biome": "1.8.3",
49
+ "@nuxt/schema": "3.14.1592",
50
+ "@types/three": "0.166.0",
51
+ "@vitest/coverage-v8": "2.1.8",
52
+ "nuxt": "3.14.1592",
53
+ "typescript": "5.6.3",
54
+ "unbuild": "2.0.0",
55
+ "vitest": "2.1.8",
56
+ "vue-router": "4.5.0",
57
+ "vue-tsc": "2.1.10",
58
+ "tsconfig": "0.0.0"
59
+ },
60
+ "scripts": {
61
+ "build": "unbuild",
62
+ "dev": "unbuild --stub",
63
+ "lint": "biome check .",
64
+ "lint:fix": "biome check . --write && pnpm run typecheck",
65
+ "typecheck": "tsc --noEmit",
66
+ "test": "vitest run",
67
+ "test:watch": "vitest"
68
+ }
69
+ }