@iqual/nuxt-icms 1.6.1 → 1.6.2-rc.2
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 +86 -0
- package/__tests__/anchors.spec.ts +43 -0
- package/__tests__/contract/blokkliComponents.spec.ts +59 -0
- package/__tests__/contract/componentFragments.spec.ts +24 -0
- package/__tests__/contract/languages.spec.ts +63 -0
- package/__tests__/contract/project.ts +144 -0
- package/__tests__/hreflang.spec.ts +39 -0
- package/{tests → __tests__}/pages.spec.ts +14 -5
- package/app.config.ts +15 -0
- package/app.vue +3 -21
- package/assets/css/backend.css +56 -0
- package/assets/css/base/base.css +2 -2
- package/assets/css/base/typography.css +2 -2
- package/assets/css/ckeditor.css +3 -4
- package/assets/css/components/decorations.css +1 -1
- package/assets/css/components/editor.css +1 -1
- package/assets/css/components/grid-container.css +19 -19
- package/assets/css/fe-fixes.css +6 -12
- package/assets/css/theme/colors/color-primary.css +33 -11
- package/assets/css/theme/colors/color-secondary.css +33 -11
- package/assets/css/utilities/utilities.css +1 -1
- package/assets/symbols/anchor.svg +3 -0
- package/blokkli.editAdapter.ts +56 -0
- package/bundles.ts +6 -6
- package/cache/__tests__/pageCache.spec.ts +540 -0
- package/cache/cacheTagRegistry.ts +124 -0
- package/cache/enabledForRequest.ts +21 -0
- package/cache/multiCache.serverOptions.ts +58 -0
- package/cache/pageCache.ts +360 -0
- package/components/Blokkli/Anchor/icon.svg +3 -0
- package/components/Blokkli/Anchor/index.vue +13 -0
- package/components/Blokkli/AnchorNavigation/icon.svg +8 -0
- package/components/Blokkli/AnchorNavigation/index.vue +9 -0
- package/components/Blokkli/Card/Element/index.vue +0 -1
- package/components/Blokkli/Collapsible/Element/index.vue +5 -0
- package/components/Blokkli/FAQ/Element/index.vue +0 -1
- package/components/Blokkli/Fragment/Separator/icon.svg +4 -0
- package/components/Blokkli/Fragment/Separator/index.vue +87 -0
- package/components/Blokkli/Gallery/index.vue +1 -3
- package/components/Blokkli/IconText/Element/index.vue +0 -1
- package/components/Blokkli/Introduction/index.vue +0 -1
- package/components/Blokkli/Listing/icon.svg +9 -0
- package/components/Blokkli/Listing/index.vue +24 -0
- package/components/Blokkli/Logo/Element/index.vue +0 -1
- package/components/Blokkli/Table/index.vue +0 -1
- package/components/Blokkli/Team/Element/index.vue +0 -1
- package/components/Blokkli/Team/Layout/index.vue +0 -1
- package/components/Blokkli/Testimonial/Element/index.vue +0 -1
- package/components/Blokkli/Text/index.vue +0 -1
- package/components/DesignSystem/Atoms/Button.vue +3 -0
- package/components/DesignSystem/Atoms/GridRow.vue +3 -1
- package/components/DesignSystem/Molecules/ButtonGroup.vue +5 -4
- package/components/DesignSystem/Molecules/IconText.vue +6 -6
- package/components/DesignSystem/Molecules/MediaLightbox.vue +8 -8
- package/components/DesignSystem/Molecules/MediaSlider.vue +9 -11
- package/components/DesignSystem/Molecules/Partnership.vue +7 -4
- package/components/DesignSystem/Molecules/Table.vue +35 -19
- package/components/DesignSystem/Molecules/TitleComposition.vue +46 -15
- package/components/DesignSystem/Molecules/iFrame.vue +2 -2
- package/components/DesignSystem/Organisms/CallToAction.vue +8 -7
- package/components/DesignSystem/Organisms/CallToActionFeature.vue +8 -7
- package/components/DesignSystem/Organisms/CallToActionFullwidth.vue +9 -8
- package/components/DesignSystem/Organisms/ContentBlock.vue +26 -16
- package/components/DesignSystem/Organisms/FacetedListing.vue +38 -17
- package/components/DesignSystem/Organisms/FactCard.vue +6 -7
- package/components/DesignSystem/Organisms/FeatureCard.vue +14 -14
- package/components/DesignSystem/Organisms/FlipTeaser.vue +19 -19
- package/components/DesignSystem/Organisms/HeaderHero.vue +11 -11
- package/components/DesignSystem/Organisms/HeaderTextImage.vue +10 -10
- package/components/DesignSystem/Organisms/LinkList.vue +20 -12
- package/components/DesignSystem/Organisms/LogoElement.vue +7 -4
- package/components/DesignSystem/Organisms/Map.vue +1 -1
- package/components/DesignSystem/Organisms/SearchResults.vue +15 -6
- package/components/DesignSystem/Organisms/SpotlightCard.vue +18 -18
- package/components/DesignSystem/Organisms/TeamElement.vue +19 -18
- package/components/DesignSystem/Organisms/TeaserCard.vue +16 -17
- package/components/DesignSystem/Organisms/TeaserSimple.vue +4 -31
- package/components/DesignSystem/Organisms/Testimonial.vue +13 -13
- package/components/DesignSystem/Organisms/TextCard.vue +10 -10
- package/components/DesignSystem/Organisms/TimelineElement.vue +20 -19
- package/components/DesignSystem/Organisms/TwoColumnTextElement.vue +5 -5
- package/components/DesignSystem/README.md +59 -40
- package/components/Drupal/Breadcrumb/index.vue +25 -12
- package/components/Drupal/Messages/index.vue +9 -4
- package/components/Drupal/Webform/index.vue +271 -51
- package/components/Drupal/Webform/mutation.graphql +17 -1
- package/components/Form/HiddenInput.vue +1 -4
- package/components/Form/SelectInput.vue +1 -1
- package/components/Form/SubmitButton.vue +2 -2
- package/components/Form/TextAreaInput.vue +3 -3
- package/components/Form/TextEmailInput.vue +1 -1
- package/components/Form/TextInput.vue +1 -1
- package/components/Form/TextPhoneInput.vue +1 -1
- package/components/Form/WebformMarkup.vue +1 -1
- package/components/Form/composables/useFormDefaultValue.ts +9 -13
- package/components/Form/composables/useFormElement.ts +5 -3
- package/components/Form/utils/types.ts +3 -3
- package/components/Media/Image/fragment.graphql +4 -0
- package/components/Media/Image/index.vue +27 -17
- package/components/Media/RemoteVideo/index.vue +3 -3
- package/components/Media/Video/index.vue +1 -1
- package/components/Node/Icms/Page/SearchResult/fragment.graphql +1 -1
- package/components/Node/Icms/Page/SearchResult/index.vue +2 -2
- package/components/Node/Icms/Page/Teaser/index.vue +1 -1
- package/components/Node/Icms/Page/index.vue +8 -2
- package/components/Paragraph/Anchor/fragment.graphql +4 -0
- package/components/Paragraph/Anchor/index.vue +48 -0
- package/components/Paragraph/AnchorNavigation/fragment.graphql +3 -0
- package/components/Paragraph/AnchorNavigation/index.vue +154 -0
- package/components/Paragraph/BlokkliFragment/fragment.graphql +5 -0
- package/components/Paragraph/Button/Element/fragment.graphql +1 -1
- package/components/Paragraph/Button/Element/index.vue +13 -8
- package/components/Paragraph/Card/Element/index.vue +1 -1
- package/components/Paragraph/Card/Layout/index.vue +9 -9
- package/components/Paragraph/Collapsible/Element/index.vue +2 -2
- package/components/Paragraph/Collapsible/Layout/index.vue +19 -18
- package/components/Paragraph/Contact/index.vue +17 -17
- package/components/Paragraph/Downloads/fragment.graphql +1 -1
- package/components/Paragraph/Downloads/index.vue +13 -13
- package/components/Paragraph/FAQ/Element/index.vue +4 -3
- package/components/Paragraph/FAQ/Layout/index.vue +42 -21
- package/components/Paragraph/FromLibrary/fragment.graphql +17 -0
- package/components/Paragraph/Gallery/index.vue +14 -14
- package/components/Paragraph/HeroImage/index.vue +26 -19
- package/components/Paragraph/IconText/Element/index.vue +4 -4
- package/components/Paragraph/IconText/Layout/index.vue +12 -12
- package/components/Paragraph/Introduction/index.vue +17 -17
- package/components/Paragraph/LinkLists/Element/fragment.graphql +1 -1
- package/components/Paragraph/LinkLists/Element/index.vue +5 -5
- package/components/Paragraph/LinkLists/Layout/index.vue +9 -9
- package/components/Paragraph/Listing/fragment.graphql +14 -0
- package/components/Paragraph/Listing/index.vue +91 -0
- package/components/Paragraph/Logo/Element/fragment.graphql +1 -1
- package/components/Paragraph/Logo/Element/index.vue +7 -4
- package/components/Paragraph/Media/index.vue +23 -21
- package/components/Paragraph/Partnerships/Element/fragment.graphql +1 -1
- package/components/Paragraph/Partnerships/Element/index.vue +10 -7
- package/components/Paragraph/Partnerships/Layout/index.vue +13 -13
- package/components/Paragraph/SingleTestimonial/index.vue +13 -13
- package/components/Paragraph/Table/index.vue +12 -12
- package/components/Paragraph/Team/Element/index.vue +16 -14
- package/components/Paragraph/Team/Layout/index.vue +9 -9
- package/components/Paragraph/Teasers/index.vue +14 -17
- package/components/Paragraph/Testimonial/Element/index.vue +19 -18
- package/components/Paragraph/Testimonial/Layout/index.vue +24 -18
- package/components/Paragraph/Text/fragment.graphql +1 -1
- package/components/Paragraph/Text/index.vue +8 -8
- package/components/Paragraph/TextMedia/index.vue +22 -22
- package/components/Paragraph/Timeline/Element/index.vue +19 -19
- package/components/Paragraph/Timeline/Layout/index.vue +51 -20
- package/components/Paragraph/TwoColumnText/Element/index.vue +13 -13
- package/components/Paragraph/TwoColumnText/Layout/index.vue +1 -1
- package/components/Paragraph/iFrame/index.vue +8 -8
- package/components/Search/Facets/FacetDropdownUI.vue +25 -31
- package/components/Search/Facets/FacetedSearchUI.vue +72 -48
- package/components/Search/Facets/fragment.graphql +20 -15
- package/components/Section/Footer/index.vue +55 -32
- package/components/Section/Header/Buttons.vue +4 -5
- package/components/Section/Header/DesktopMainMenu.vue +5 -4
- package/components/Section/Header/DesktopMainMenuDropdown.vue +5 -5
- package/components/Section/Header/LanguageDropdown.vue +2 -3
- package/components/Section/Header/Logo.vue +3 -3
- package/components/Section/Header/MainButtons.vue +4 -5
- package/components/Section/Header/MetaButtons.vue +5 -6
- package/components/Section/Header/MetaMenu.vue +1 -1
- package/components/Section/Header/MobileMainMenu.vue +1 -1
- package/components/Section/Header/MobileMainMenuItem.vue +2 -2
- package/components/Section/Header/index.vue +6 -13
- package/composables/__tests__/useImageSources.spec.ts +91 -0
- package/composables/breadcrumb.ts +12 -7
- package/composables/useAccordionScroll.ts +12 -4
- package/composables/useAllowedFragments.ts +14 -0
- package/composables/useAnchors.ts +48 -0
- package/composables/useChildTitleWeight.ts +1 -1
- package/composables/useCurrentLangcodeEnum.ts +1 -1
- package/composables/useCurrentNodeId.ts +8 -0
- package/composables/useDrupalUrl.ts +15 -0
- package/composables/useEnrichFacetLabels.ts +8 -6
- package/composables/useEntityRegistry.ts +3 -3
- package/composables/useGlobalConfig.ts +1 -2
- package/composables/useGraphqlCacheability.ts +12 -1
- package/composables/useHreflangLinks.ts +54 -0
- package/composables/useImageSources.ts +70 -28
- package/composables/useInitData.ts +1 -2
- package/composables/useIsHomepage.ts +1 -1
- package/composables/useLinkUrl.ts +30 -0
- package/composables/useMediaLightbox.ts +62 -50
- package/composables/useMenu.ts +1 -3
- package/composables/useMessageBus.ts +14 -7
- package/composables/useMobileMenuOverlayBus.ts +1 -1
- package/composables/useNavigationOffset.ts +49 -6
- package/composables/usePageCache.ts +33 -0
- package/composables/useSiteShell.ts +11 -0
- package/composables/useTheme.ts +17 -13
- package/config/__tests__/stubs/nuxt-multi-cache-config.ts +2 -0
- package/config/blokkli.ts +7 -13
- package/config/easyTexts.ts +1 -1
- package/config/graphqlMiddleware.ts +11 -4
- package/config/index.ts +5 -2
- package/config/languageNegotiation.ts +1 -1
- package/config/multiCache.ts +6 -1
- package/config/postcss-drupal/postcss.config.cjs +2 -2
- package/config/runtimeConfig.ts +7 -2
- package/config/svgIconSprite.ts +1 -2
- package/config/vuepal.ts +16 -18
- package/error.vue +1 -22
- package/layouts/default.vue +2 -6
- package/layouts/error.vue +7 -15
- package/modules/blokkliFragments.ts +76 -0
- package/modules/dynamicEntityComponentResolver.ts +47 -47
- package/modules/dynamicParagraphResolver.ts +43 -8
- package/modules/pageCache.ts +106 -0
- package/nuxt.config.ts +62 -33
- package/package.json +23 -22
- package/pages/[...slug]/index.vue +25 -4
- package/pages/index.vue +3 -3
- package/pages/node/[nid]/index.vue +2 -3
- package/patches/@blokkli+editor+2.0.0-alpha.13.patch +15 -1
- package/plugins/currentNode.ts +17 -0
- package/plugins/graphqlMiddleware.ts +10 -2
- package/plugins/initData.ts +15 -7
- package/plugins/registerNodeIcmsPageComponents.ts +6 -2
- package/plugins/siteShell.client.ts +94 -0
- package/queries/facetTermLabels.graphql +1 -5
- package/queries/fragment.linkUrl.graphql +12 -0
- package/queries/query.contentSearch.graphql +17 -2
- package/queries/query.initData.graphql +1 -1
- package/scripts/create-shade.ts +13 -4
- package/scripts/update-icms.ts +3 -1
- package/server/__tests__/graphqlMiddleware.serverOptions.spec.ts +2 -2
- package/server/__tests__/turnstile.spec.ts +119 -0
- package/server/api/icms/blokkli/last-changed.get.ts +24 -0
- package/server/api/icon/[...params].ts +3 -9
- package/server/graphqlMiddleware.serverOptions.ts +9 -8
- package/server/helpers/index.ts +0 -1
- package/server/middleware/turnstile.ts +51 -0
- package/server/plugins/frameAncestors.ts +20 -0
- package/server/plugins/pageCache.ts +59 -0
- package/server/utils/__tests__/accessDeniedHandover.spec.ts +49 -0
- package/server/utils/__tests__/backendRequestHeaders.spec.ts +115 -0
- package/server/utils/accessDeniedHandover.ts +48 -0
- package/server/utils/backendRequestHeaders.ts +51 -0
- package/server/utils/cacheability.ts +4 -4
- package/shared/__tests__/blokkliState.spec.ts +54 -0
- package/shared/__tests__/csp.spec.ts +53 -0
- package/shared/__tests__/drupalUrl.spec.ts +50 -0
- package/shared/blokkliState.ts +40 -0
- package/shared/csp.ts +47 -0
- package/shared/drupalUrl.ts +30 -0
- package/tailwind.config.ts +0 -5
- package/types/breadcrumb.ts +22 -0
- package/types/global.ts +15 -14
- package/types/langcode.ts +16 -0
- package/types/layerConfig.ts +84 -0
- package/utils/pageCache.ts +95 -0
- package/utils/turnstile.ts +35 -0
- package/composables/useWebformEnrichment.ts +0 -68
- package/server/__tests__/graphqlMiddleware.ts +0 -41
- package/server/multiCache.serverOptions.ts +0 -32
- package/vitest.config.mts +0 -28
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* icms: {
|
|
19
19
|
* components: {
|
|
20
20
|
* fragments: ['paragraphIcmsLayoutText', 'paragraphIcmsLayoutImage'],
|
|
21
|
+
* wrapperFragments: ['paragraphFromLibrary'],
|
|
21
22
|
* teaserFragments: ['paragraphIcmsEventOccurrence'],
|
|
22
23
|
* searchResultFragments: ['paragraphIcmsEventOccurrence']
|
|
23
24
|
* }
|
|
@@ -32,6 +33,9 @@
|
|
|
32
33
|
*
|
|
33
34
|
* Each layer can define:
|
|
34
35
|
* - `fragments`: Full paragraph fragments (e.g., 'paragraphIcmsLayoutText')
|
|
36
|
+
* - `wrapperFragments`: Fragments of paragraphs embedding another paragraph
|
|
37
|
+
* (e.g., 'paragraphFromLibrary'); spread into `paragraph` but not into
|
|
38
|
+
* `paragraphInLibrary`, the fragment the embedded paragraph uses
|
|
35
39
|
* - `teaserFragments`: Teaser variant fragments (e.g., 'paragraphIcmsEventOccurrence')
|
|
36
40
|
* - `searchResultFragments`: Search result variant fragments
|
|
37
41
|
*
|
|
@@ -40,6 +44,7 @@
|
|
|
40
44
|
* the fragments available in GraphQL queries throughout the application.
|
|
41
45
|
*/
|
|
42
46
|
import { defineNuxtModule } from 'nuxt/kit'
|
|
47
|
+
import type { IcmsLayerConfig } from '../types/layerConfig'
|
|
43
48
|
|
|
44
49
|
/**
|
|
45
50
|
* Module definition for the dynamicParagraphResolver
|
|
@@ -51,9 +56,24 @@ export default defineNuxtModule({
|
|
|
51
56
|
name: 'dynamicParagraphResolver',
|
|
52
57
|
},
|
|
53
58
|
setup(_options, nuxt) {
|
|
59
|
+
const layerConfigs: IcmsLayerConfig[] = nuxt.options._layers
|
|
60
|
+
.map((layer) => layer.config?.icms)
|
|
61
|
+
.filter((config): config is IcmsLayerConfig => !!config)
|
|
62
|
+
|
|
54
63
|
// Collect all paragraph fragments from the Nuxt layers.
|
|
55
|
-
const paragraphFragments =
|
|
56
|
-
.map(
|
|
64
|
+
const paragraphFragments = layerConfigs
|
|
65
|
+
.map((config) => config.components?.fragments)
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.flat()
|
|
68
|
+
.map((fragmentName) => {
|
|
69
|
+
return `...${fragmentName}`
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Wrapper paragraphs (from_library) embed another paragraph. They belong
|
|
73
|
+
// in `paragraph` but not in the fragment the embedded paragraph uses:
|
|
74
|
+
// a fragment spreading itself is a GraphQL validation error.
|
|
75
|
+
const wrapperFragments = layerConfigs
|
|
76
|
+
.map((config) => config.components?.wrapperFragments)
|
|
57
77
|
.filter(Boolean)
|
|
58
78
|
.flat()
|
|
59
79
|
.map((fragmentName) => {
|
|
@@ -62,6 +82,14 @@ export default defineNuxtModule({
|
|
|
62
82
|
|
|
63
83
|
const fragment = `
|
|
64
84
|
fragment paragraph on Paragraph {
|
|
85
|
+
id
|
|
86
|
+
parentId
|
|
87
|
+
${[...paragraphFragments, ...wrapperFragments].join('\n ')}
|
|
88
|
+
}
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
const inLibraryFragment = `
|
|
92
|
+
fragment paragraphInLibrary on Paragraph {
|
|
65
93
|
id
|
|
66
94
|
parentId
|
|
67
95
|
${paragraphFragments.join('\n ')}
|
|
@@ -69,8 +97,8 @@ fragment paragraph on Paragraph {
|
|
|
69
97
|
`
|
|
70
98
|
|
|
71
99
|
// Collect teaser fragments from all layers
|
|
72
|
-
const paragraphTeaserFragments =
|
|
73
|
-
.map(
|
|
100
|
+
const paragraphTeaserFragments = layerConfigs
|
|
101
|
+
.map((config) => config.components?.teaserFragments)
|
|
74
102
|
.filter(Boolean)
|
|
75
103
|
.flat()
|
|
76
104
|
.filter((fragmentName) => typeof fragmentName === 'string')
|
|
@@ -83,8 +111,8 @@ fragment paragraphTeaser on Paragraph {
|
|
|
83
111
|
`
|
|
84
112
|
|
|
85
113
|
// Collect search result fragments from all layers
|
|
86
|
-
const paragraphSearchResultFragments =
|
|
87
|
-
.map(
|
|
114
|
+
const paragraphSearchResultFragments = layerConfigs
|
|
115
|
+
.map((config) => config.components?.searchResultFragments)
|
|
88
116
|
.filter(Boolean)
|
|
89
117
|
.flat()
|
|
90
118
|
.filter((fragmentName) => typeof fragmentName === 'string')
|
|
@@ -98,8 +126,15 @@ fragment paragraphSearchResult on Paragraph {
|
|
|
98
126
|
|
|
99
127
|
nuxt.hooks.hookOnce('nuxt-graphql-middleware:init', (context) => {
|
|
100
128
|
context.addDocument('fragment.paragraph.graphql', fragment)
|
|
129
|
+
context.addDocument(
|
|
130
|
+
'fragment.paragraphInLibrary.graphql',
|
|
131
|
+
inLibraryFragment,
|
|
132
|
+
)
|
|
101
133
|
context.addDocument('fragment.paragraphTeaser.graphql', teaserFragment)
|
|
102
|
-
context.addDocument(
|
|
134
|
+
context.addDocument(
|
|
135
|
+
'fragment.paragraphSearchResult.graphql',
|
|
136
|
+
searchResultFragment,
|
|
137
|
+
)
|
|
103
138
|
})
|
|
104
|
-
}
|
|
139
|
+
},
|
|
105
140
|
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ships the ICMS multi-cache server options with the layer (ICMS-268).
|
|
3
|
+
*
|
|
4
|
+
* nuxt-multi-cache only looks for `multiCache.serverOptions.ts` in the app's
|
|
5
|
+
* own `server/` directory, never in a layer. It imports that file from the
|
|
6
|
+
* generated `#nuxt-multi-cache/server-options` template. When the app has no
|
|
7
|
+
* file of its own, this module replaces that template with one importing
|
|
8
|
+
* `cache/multiCache.serverOptions.ts` of this package — Nuxt's addTemplate()
|
|
9
|
+
* replaces a template with the same file name.
|
|
10
|
+
*
|
|
11
|
+
* A project that needs different server options keeps its own
|
|
12
|
+
* `server/multiCache.serverOptions.ts`; it then wins, as before.
|
|
13
|
+
*
|
|
14
|
+
* Like nuxt-multi-cache does for the app's file, the server options are
|
|
15
|
+
* replaced with an empty stub in the client build, which would otherwise
|
|
16
|
+
* bundle server-only code. The Vue app's SSR build gets only the ICMS
|
|
17
|
+
* `enabledForRequest`, the one option nuxt-multi-cache's app-side helpers
|
|
18
|
+
* need, with or without a project file: the full options import
|
|
19
|
+
* nitropack/runtime, which only resolves in Nitro's bundle — the Vite dev
|
|
20
|
+
* server can't load it. Nitro always uses the full options.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync } from 'node:fs'
|
|
23
|
+
import { join } from 'node:path'
|
|
24
|
+
import {
|
|
25
|
+
addTemplate,
|
|
26
|
+
addVitePlugin,
|
|
27
|
+
createResolver,
|
|
28
|
+
defineNuxtModule,
|
|
29
|
+
useLogger,
|
|
30
|
+
} from 'nuxt/kit'
|
|
31
|
+
|
|
32
|
+
const TEMPLATE = 'nuxt-multi-cache/server-options.js'
|
|
33
|
+
const EXTENSIONS = ['.ts', '.mts', '.js', '.mjs']
|
|
34
|
+
|
|
35
|
+
export default defineNuxtModule({
|
|
36
|
+
meta: { name: 'icmsPageCache' },
|
|
37
|
+
setup(_options, nuxt) {
|
|
38
|
+
const logger = useLogger('icms')
|
|
39
|
+
|
|
40
|
+
const resolver = createResolver(import.meta.url)
|
|
41
|
+
addVitePlugin(
|
|
42
|
+
{
|
|
43
|
+
name: 'icms-page-cache:app-ssr',
|
|
44
|
+
enforce: 'pre',
|
|
45
|
+
transform(_code: string, id: string) {
|
|
46
|
+
// Also with a project file: it typically extends the ICMS options.
|
|
47
|
+
// Templates are virtual modules (virtual:nuxt:.nuxt%2F…).
|
|
48
|
+
const path = decodeURIComponent(id.split('?')[0]!)
|
|
49
|
+
if (path.endsWith('/' + TEMPLATE)) {
|
|
50
|
+
return [
|
|
51
|
+
`import { enabledForRequest } from ${JSON.stringify(resolver.resolve('../cache/enabledForRequest'))}`,
|
|
52
|
+
'export const serverOptions = { enabledForRequest }',
|
|
53
|
+
].join('\n')
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{ client: false },
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const appServerOptions = EXTENSIONS.map((extension) =>
|
|
61
|
+
join(nuxt.options.serverDir, 'multiCache.serverOptions' + extension),
|
|
62
|
+
).find((path) => existsSync(path))
|
|
63
|
+
if (appServerOptions) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
addVitePlugin(
|
|
68
|
+
{
|
|
69
|
+
name: 'icms-page-cache:server-only',
|
|
70
|
+
enforce: 'pre',
|
|
71
|
+
transform(_code: string, id: string) {
|
|
72
|
+
if (id.includes('nuxt-icms/cache/multiCache.serverOptions')) {
|
|
73
|
+
return 'export default function () { return {} }'
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{ server: false },
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
nuxt.hook('modules:done', () => {
|
|
81
|
+
const hasTemplate = nuxt.options.build.templates.some(
|
|
82
|
+
(template) => template.filename === TEMPLATE,
|
|
83
|
+
)
|
|
84
|
+
if (!hasTemplate) {
|
|
85
|
+
// Fail-safe: server/plugins/pageCache.ts disables the page cache
|
|
86
|
+
// when the server options have no bounded route storage.
|
|
87
|
+
logger.error(
|
|
88
|
+
`nuxt-multi-cache no longer generates "${TEMPLATE}"; the ICMS ` +
|
|
89
|
+
'page cache stays disabled. See modules/pageCache.ts.',
|
|
90
|
+
)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
addTemplate({
|
|
95
|
+
filename: TEMPLATE,
|
|
96
|
+
write: true,
|
|
97
|
+
getContents: () =>
|
|
98
|
+
[
|
|
99
|
+
"import serverOptionsFactory from '@iqual/nuxt-icms/cache/multiCache.serverOptions'",
|
|
100
|
+
'const serverOptions = serverOptionsFactory()',
|
|
101
|
+
'export { serverOptions }',
|
|
102
|
+
].join('\n'),
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
},
|
|
106
|
+
})
|
package/nuxt.config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
1
|
+
import { resolve, dirname, join } from 'node:path'
|
|
2
2
|
|
|
3
3
|
import easyTexts from './config/easyTexts'
|
|
4
4
|
import blokkli from './config/blokkli'
|
|
@@ -14,17 +14,10 @@ import vuepal from './config/vuepal'
|
|
|
14
14
|
import { breakpoints } from './config/breakpoints'
|
|
15
15
|
|
|
16
16
|
import { fileURLToPath } from 'node:url'
|
|
17
|
-
import { dirname, join } from 'node:path'
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const ONE_YEAR = 31_536_000
|
|
22
|
-
const ONE_MONTH = 2_592_000
|
|
23
18
|
const IS_DEV = process.env.NODE_ENV === 'development'
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
const NUXT_IMAGE_CLOUDFLARE_BASE_URL =
|
|
20
|
+
process.env.NUXT_IMAGE_CLOUDFLARE_BASE_URL ?? 'https://iqual.cloud'
|
|
28
21
|
|
|
29
22
|
const currentDir = dirname(fileURLToPath(import.meta.url))
|
|
30
23
|
|
|
@@ -60,15 +53,6 @@ export default defineNuxtConfig({
|
|
|
60
53
|
htmlAttrs: {
|
|
61
54
|
tagDuplicateStrategy: 'merge',
|
|
62
55
|
},
|
|
63
|
-
link: [
|
|
64
|
-
{ rel: 'shortcut icon', type: 'image/x-icon', href: '/favicon/favicon.ico' },
|
|
65
|
-
{ rel: 'apple-touch-icon', href: '/favicon/favicon/apple-touch-icon.png' },
|
|
66
|
-
{ rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon/favicon-96x96.png' },
|
|
67
|
-
{ rel: 'icon', type: 'image/svg+xml', href: '/favicon/favicon.svg' },
|
|
68
|
-
{ rel: 'manifest', href: '/favicon/site.webmanifest' },
|
|
69
|
-
{ rel: 'icon', type: 'image/png', sizes: '192x192', href: '/favicon/web-app-manifest-192x192.png' },
|
|
70
|
-
{ rel: 'icon', type: 'image/png', sizes: '512x512', href: '/favicon/web-app-manifest-512x512.png' }
|
|
71
|
-
],
|
|
72
56
|
},
|
|
73
57
|
},
|
|
74
58
|
|
|
@@ -76,9 +60,7 @@ export default defineNuxtConfig({
|
|
|
76
60
|
* Site-wide CSS (PostCSS compiled if within ~/assets)
|
|
77
61
|
* @see https://nuxt.com/docs/getting-started/styling
|
|
78
62
|
*/
|
|
79
|
-
css: [
|
|
80
|
-
join(currentDir, './assets/css/fe-fixes.css'),
|
|
81
|
-
],
|
|
63
|
+
css: [join(currentDir, './assets/css/fe-fixes.css')],
|
|
82
64
|
|
|
83
65
|
/**
|
|
84
66
|
* Nuxt.js modules
|
|
@@ -90,9 +72,14 @@ export default defineNuxtConfig({
|
|
|
90
72
|
'nuxt-easy-texts',
|
|
91
73
|
'nuxt-page-dependencies',
|
|
92
74
|
'nuxt-multi-cache',
|
|
75
|
+
// Ships the ICMS multi-cache server options; see modules/pageCache.ts.
|
|
76
|
+
'@iqual/nuxt-icms/modules/pageCache',
|
|
93
77
|
'vuepal',
|
|
94
78
|
'nuxt-language-negotiation',
|
|
95
79
|
'nuxt-svg-icon-sprite',
|
|
80
|
+
// Registers the `blokkli:alter-features` hook, which @blokkli/editor runs
|
|
81
|
+
// during its own setup — so it has to be in place before that module.
|
|
82
|
+
'@iqual/nuxt-icms/modules/blokkliFragments',
|
|
96
83
|
'@blokkli/editor',
|
|
97
84
|
'nuxt-viewport',
|
|
98
85
|
...(IS_DEV ? ['@nuxt/eslint'] : []),
|
|
@@ -100,13 +87,25 @@ export default defineNuxtConfig({
|
|
|
100
87
|
'@iqual/nuxt-icms/modules/dynamicParagraphResolver',
|
|
101
88
|
'@iqual/nuxt-icms/modules/dynamicEntityComponentResolver',
|
|
102
89
|
'nuxt-maplibre',
|
|
90
|
+
'@nuxtjs/turnstile',
|
|
103
91
|
],
|
|
104
92
|
|
|
105
93
|
image: {
|
|
94
|
+
// Pin the @nuxt/image v1 default screens: v2 dropped `xs` and `xxl`, and
|
|
95
|
+
// useImageSources() derives every srcset from these keys.
|
|
96
|
+
screens: {
|
|
97
|
+
xs: 320,
|
|
98
|
+
sm: 640,
|
|
99
|
+
md: 768,
|
|
100
|
+
lg: 1024,
|
|
101
|
+
xl: 1280,
|
|
102
|
+
xxl: 1536,
|
|
103
|
+
'2xl': 1536,
|
|
104
|
+
},
|
|
106
105
|
provider: 'cloudflare',
|
|
107
106
|
cloudflare: {
|
|
108
|
-
baseURL: NUXT_IMAGE_CLOUDFLARE_BASE_URL
|
|
109
|
-
}
|
|
107
|
+
baseURL: NUXT_IMAGE_CLOUDFLARE_BASE_URL,
|
|
108
|
+
},
|
|
110
109
|
},
|
|
111
110
|
|
|
112
111
|
pageDependencies: {
|
|
@@ -114,7 +113,7 @@ export default defineNuxtConfig({
|
|
|
114
113
|
},
|
|
115
114
|
|
|
116
115
|
$meta: {
|
|
117
|
-
name: 'icms'
|
|
116
|
+
name: 'icms',
|
|
118
117
|
},
|
|
119
118
|
|
|
120
119
|
icms: {
|
|
@@ -125,6 +124,7 @@ export default defineNuxtConfig({
|
|
|
125
124
|
'paragraphIcmsLayoutText',
|
|
126
125
|
'paragraphIcmsLayoutTextMedia',
|
|
127
126
|
'paragraphIcmsLayoutTeasers',
|
|
127
|
+
'paragraphIcmsLayoutListing',
|
|
128
128
|
'paragraphIcmsLayoutHeroImage',
|
|
129
129
|
'paragraphIcmsLayoutCallToAction',
|
|
130
130
|
'paragraphIcmsLayoutCards',
|
|
@@ -141,11 +141,12 @@ export default defineNuxtConfig({
|
|
|
141
141
|
'paragraphIcmsLayoutPartnerships',
|
|
142
142
|
'paragraphIcmsLayoutDownloads',
|
|
143
143
|
'paragraphIcmsLayoutMedia',
|
|
144
|
-
'paragraphIcmsLayoutDownloads',
|
|
145
144
|
'paragraphIcmsLayoutIframe',
|
|
146
145
|
'paragraphIcmsLayoutTimeline',
|
|
147
146
|
'paragraphIcmsLayoutLinkLists',
|
|
148
147
|
'paragraphIcmsLayoutCollapsible',
|
|
148
|
+
'paragraphIcmsLayoutAnchor',
|
|
149
|
+
'paragraphIcmsLayoutAnchorNavigation',
|
|
149
150
|
// Element components
|
|
150
151
|
'paragraphIcmsButtonElement',
|
|
151
152
|
'paragraphIcmsCardElement',
|
|
@@ -159,8 +160,15 @@ export default defineNuxtConfig({
|
|
|
159
160
|
'paragraphIcmsTimelineElement',
|
|
160
161
|
'paragraphIcmsLinkListElement',
|
|
161
162
|
'paragraphIcmsCollapsibleElement',
|
|
162
|
-
|
|
163
|
+
// Placeholder for frontend-defined fragments (defineBlokkliFragment)
|
|
164
|
+
'paragraphBlokkliFragment',
|
|
165
|
+
],
|
|
166
|
+
// Reusable blocks from the paragraph library
|
|
167
|
+
wrapperFragments: ['paragraphFromLibrary'],
|
|
163
168
|
},
|
|
169
|
+
// Blökkli fragments (components/Blokkli/Fragment/*) are discovered and
|
|
170
|
+
// offered to editors automatically; a project switches the feature off
|
|
171
|
+
// with `blokkliFragments: false`. See modules/blokkliFragments.ts.
|
|
164
172
|
},
|
|
165
173
|
|
|
166
174
|
sourcemap: IS_DEV,
|
|
@@ -168,7 +176,7 @@ export default defineNuxtConfig({
|
|
|
168
176
|
hooks: {
|
|
169
177
|
'vite:extend'({ config }) {
|
|
170
178
|
if (config.server && config.server.hmr) {
|
|
171
|
-
// @ts-
|
|
179
|
+
// @ts-expect-error `hmr` narrows to `true | HmrOptions`.
|
|
172
180
|
config.server.hmr.protocol = 'wss'
|
|
173
181
|
}
|
|
174
182
|
},
|
|
@@ -184,9 +192,12 @@ export default defineNuxtConfig({
|
|
|
184
192
|
sourcemap: false,
|
|
185
193
|
minify: 'esbuild',
|
|
186
194
|
},
|
|
187
|
-
esbuild:
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
// Never enable esbuild's `keepNames` here: it wraps every function in a
|
|
196
|
+
// `__name()` helper that is defined once per chunk, and MapLibre builds its
|
|
197
|
+
// web worker by stringifying functions into a blob. The serialised source
|
|
198
|
+
// then calls a helper that does not exist in the worker, the worker dies on
|
|
199
|
+
// its first statement, and every map renders as an empty background with no
|
|
200
|
+
// tiles and no error (ICMS-642). It breaks production builds only.
|
|
190
201
|
css: {
|
|
191
202
|
devSourcemap: true,
|
|
192
203
|
},
|
|
@@ -238,8 +249,26 @@ export default defineNuxtConfig({
|
|
|
238
249
|
},
|
|
239
250
|
|
|
240
251
|
maplibre: {
|
|
241
|
-
css: false
|
|
252
|
+
css: false,
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Cloudflare Turnstile — spam protection for decoupled webforms.
|
|
257
|
+
*
|
|
258
|
+
* Both keys default to an empty string, which disables the check end to end:
|
|
259
|
+
* no widget is rendered and the server middleware lets submissions through.
|
|
260
|
+
* A project turns it on by setting NUXT_PUBLIC_TURNSTILE_SITE_KEY and
|
|
261
|
+
* NUXT_TURNSTILE_SECRET_KEY, which also override the values at runtime
|
|
262
|
+
* because the module maps them onto runtimeConfig.
|
|
263
|
+
*
|
|
264
|
+
* The empty strings are deliberate: without them the module falls back to
|
|
265
|
+
* Cloudflare's "always passes" test keys in development, which loads the
|
|
266
|
+
* Turnstile script on every local page that carries a form.
|
|
267
|
+
*/
|
|
268
|
+
turnstile: {
|
|
269
|
+
siteKey: process.env.NUXT_PUBLIC_TURNSTILE_SITE_KEY || '',
|
|
270
|
+
secretKey: process.env.NUXT_TURNSTILE_SECRET_KEY || '',
|
|
242
271
|
},
|
|
243
272
|
|
|
244
273
|
compatibilityDate: '2024-08-14',
|
|
245
|
-
})
|
|
274
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iqual/nuxt-icms",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2-rc.2",
|
|
4
4
|
"description": "ICMS Base",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -13,54 +13,55 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@blokkli/editor": "2.0.0-alpha.13",
|
|
16
|
-
"@elastic/elasticsearch": "~8.19.
|
|
16
|
+
"@elastic/elasticsearch": "~8.19.2",
|
|
17
17
|
"@iframe-resizer/child": "~5.5.9",
|
|
18
18
|
"@iframe-resizer/vue": "~5.5.9",
|
|
19
|
-
"@nuxt/image": "~
|
|
20
|
-
"@nuxt/kit": "~4.
|
|
19
|
+
"@nuxt/image": "~2.2.0",
|
|
20
|
+
"@nuxt/kit": "~4.6.2",
|
|
21
21
|
"@nuxt/scripts": "~0.11.13",
|
|
22
|
-
"@nuxt/schema": "~4.
|
|
23
|
-
"@nuxt/ui": "~4.
|
|
22
|
+
"@nuxt/schema": "~4.6.2",
|
|
23
|
+
"@nuxt/ui": "~4.12.0",
|
|
24
24
|
"@nuxtjs/color-mode": "~3.5.2",
|
|
25
25
|
"@nuxtjs/turnstile": "1.0.0",
|
|
26
26
|
"@vueuse/core": "~12.8.2",
|
|
27
|
-
"caniuse-lite": "~1.0.
|
|
27
|
+
"caniuse-lite": "~1.0.30001817",
|
|
28
28
|
"clsx": "~2.1.1",
|
|
29
29
|
"culori": "~4.0.2",
|
|
30
30
|
"h3": "~1.15.11",
|
|
31
31
|
"lucide-vue-next": "~0.469.0",
|
|
32
|
-
"nitropack": "~2.13.
|
|
33
|
-
"nuxt": "~4.
|
|
34
|
-
"nuxt-easy-texts": "~2.
|
|
32
|
+
"nitropack": "~2.13.5",
|
|
33
|
+
"nuxt": "~4.6.2",
|
|
34
|
+
"nuxt-easy-texts": "~2.2.0",
|
|
35
35
|
"nuxt-graphql-middleware": "5.4.0",
|
|
36
36
|
"nuxt-language-negotiation": "~2.0.2",
|
|
37
37
|
"nuxt-maplibre": "~1.2.2",
|
|
38
38
|
"nuxt-multi-cache": "~4.0.3",
|
|
39
39
|
"nuxt-page-dependencies": "~1.2.0",
|
|
40
40
|
"nuxt-svg-icon-sprite": "~2.0.2",
|
|
41
|
-
"nuxt-viewport": "~2.
|
|
41
|
+
"nuxt-viewport": "~2.6.1",
|
|
42
42
|
"ofetch": "~1.5.1",
|
|
43
43
|
"patch-package": "~8.0.1",
|
|
44
|
-
"svgo": "~3.3.
|
|
44
|
+
"svgo": "~3.3.5",
|
|
45
45
|
"swiper": "~10.3.1",
|
|
46
|
-
"tailwind-merge": "~
|
|
47
|
-
"tailwind-variants": "~
|
|
46
|
+
"tailwind-merge": "~3.7.0",
|
|
47
|
+
"tailwind-variants": "~3.3.1",
|
|
48
48
|
"tailwind-animate": "~0.2.10",
|
|
49
|
+
"typescript": "~5.9.3",
|
|
49
50
|
"ufo": "~1.6.4",
|
|
50
|
-
"unstorage": "~1.17.
|
|
51
|
-
"vite": "~7.3.
|
|
52
|
-
"vue": "~3.5.
|
|
51
|
+
"unstorage": "~1.17.6",
|
|
52
|
+
"vite": "~7.3.7",
|
|
53
|
+
"vue": "~3.5.44",
|
|
53
54
|
"vue3-carousel": "0.3.1",
|
|
54
55
|
"vue3-click-away": "~1.2.4",
|
|
55
|
-
"vuepal": "~3.0.
|
|
56
|
-
"zod": "~4.
|
|
56
|
+
"vuepal": "~3.0.6",
|
|
57
|
+
"zod": "~4.6.7"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
|
-
"@tailwindcss/postcss": "^4.
|
|
60
|
+
"@tailwindcss/postcss": "^4.3.3",
|
|
60
61
|
"@types/iframe-resizer": "^4.0.0",
|
|
61
62
|
"postcss-cli": "^11.0.1",
|
|
62
63
|
"postcss-hexrgba": "github:thormeier/postcss-hexrgba",
|
|
63
|
-
"postcss-nesting": "^14.0.
|
|
64
|
-
"sass": "^1.
|
|
64
|
+
"postcss-nesting": "^14.0.3",
|
|
65
|
+
"sass": "^1.106.0"
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<component :is="entityComponent" v-bind="entity" />
|
|
4
4
|
</div>
|
|
5
|
-
|
|
6
5
|
</template>
|
|
7
6
|
|
|
8
7
|
<script lang="ts" setup>
|
|
9
|
-
|
|
10
8
|
const { get } = useEntityRegistry()
|
|
11
9
|
|
|
12
10
|
const entityComponent = computed(() => get(entity.value?.__typename))
|
|
@@ -20,7 +18,30 @@ definePageMeta({
|
|
|
20
18
|
path: '/:slug(.*)*',
|
|
21
19
|
})
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
usePageCache()
|
|
22
|
+
|
|
23
|
+
const { entity, drupalRoute } = await useDrupalRouteQuery('slug', {
|
|
24
|
+
noError: true,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
if (!entity.value) {
|
|
28
|
+
// A Drupal route without an entity to render (admin, user, forms) is a
|
|
29
|
+
// backend page. Links to such paths are router links, so a click reaches
|
|
30
|
+
// this page instead of the router in front of the frontend: hand over to
|
|
31
|
+
// the backend domain rather than showing a 404.
|
|
32
|
+
if (drupalRoute.value.name) {
|
|
33
|
+
await navigateTo(useDrupalUrl()(useRoute().fullPath), {
|
|
34
|
+
external: true,
|
|
35
|
+
redirectCode: 302,
|
|
36
|
+
})
|
|
37
|
+
} else {
|
|
38
|
+
throw createError({
|
|
39
|
+
statusCode: 404,
|
|
40
|
+
statusMessage: 'Page not found',
|
|
41
|
+
fatal: true,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
24
45
|
|
|
25
46
|
await renderPageDependencies()
|
|
26
|
-
</script>
|
|
47
|
+
</script>
|
package/pages/index.vue
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<component :is="entityComponent" v-bind="entity" />
|
|
4
4
|
</div>
|
|
5
|
-
|
|
6
5
|
</template>
|
|
7
6
|
|
|
8
7
|
<script lang="ts" setup>
|
|
9
|
-
|
|
10
8
|
const { get } = useEntityRegistry()
|
|
11
9
|
|
|
12
10
|
const entityComponent = computed(() => get(entity.value?.__typename))
|
|
@@ -20,7 +18,9 @@ definePageMeta({
|
|
|
20
18
|
hideBreadcrumb: true,
|
|
21
19
|
})
|
|
22
20
|
|
|
21
|
+
usePageCache()
|
|
22
|
+
|
|
23
23
|
const { entity } = await useDrupalRouteQuery('slug')
|
|
24
24
|
|
|
25
25
|
await renderPageDependencies()
|
|
26
|
-
</script>
|
|
26
|
+
</script>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<component :is="entityComponent" v-bind="node"/>
|
|
2
|
+
<component :is="entityComponent" v-bind="node" />
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script lang="ts" setup>
|
|
6
|
-
|
|
7
6
|
defineOptions({
|
|
8
7
|
name: 'PageNodeCanonical',
|
|
9
8
|
})
|
|
@@ -17,4 +16,4 @@ const { entity: node } = await useDrupalRouteQuery('nodeCanonical')
|
|
|
17
16
|
const entityComponent = computed(() => get(node.value?.__typename))
|
|
18
17
|
|
|
19
18
|
await renderPageDependencies()
|
|
20
|
-
</script>
|
|
19
|
+
</script>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/node_modules/@blokkli/editor/dist/module.mjs b/node_modules/@blokkli/editor/dist/module.mjs
|
|
2
|
-
index 0ff0d3b..
|
|
2
|
+
index 0ff0d3b..2dee9c3 100644
|
|
3
3
|
--- a/node_modules/@blokkli/editor/dist/module.mjs
|
|
4
4
|
+++ b/node_modules/@blokkli/editor/dist/module.mjs
|
|
5
5
|
@@ -271,16 +271,31 @@ class CollectedBlockFile extends CollectedFile {
|
|
@@ -162,3 +162,17 @@ index 0ff0d3b..5329e9f 100644
|
|
|
162
162
|
await Promise.all(allFiles.map((filePath) => this.addFile(filePath)));
|
|
163
163
|
}
|
|
164
164
|
runHooks() {
|
|
165
|
+
@@ -7417,6 +7531,13 @@ const runtimeOptions = defineCodeTemplate(
|
|
166
|
+
item.variations.forEach((variation) => {
|
|
167
|
+
if (hasOptions) {
|
|
168
|
+
OPTIONS.set(variation, item.varName);
|
|
169
|
+
+ // Fragments: defineBlokkli() looks up their options as
|
|
170
|
+
+ // `fragment:<name>__default`, while getVariations() only yields
|
|
171
|
+
+ // `fragment:<name>`. Without the suffixed alias the lookup misses
|
|
172
|
+
+ // and a fragment never receives its (global) options.
|
|
173
|
+
+ if (!isBlock(item.definition)) {
|
|
174
|
+
+ OPTIONS.set(variation + "__default", item.varName);
|
|
175
|
+
+ }
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
if (isBlock(item.definition)) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks the node ID of the currently viewed Drupal route so components
|
|
3
|
+
* deep in the tree (e.g. faceted listings) can reference it without each
|
|
4
|
+
* node component having to provide it.
|
|
5
|
+
*/
|
|
6
|
+
export default defineNuxtPlugin({
|
|
7
|
+
name: 'starterkit:current-node',
|
|
8
|
+
setup(app) {
|
|
9
|
+
const currentNodeId = useCurrentNodeId()
|
|
10
|
+
app.hooks.hook('vuepal:drupal-route', (data) => {
|
|
11
|
+
currentNodeId.value =
|
|
12
|
+
data.drupalRoute?.entityType === 'node' && data.drupalRoute.entityId
|
|
13
|
+
? String(data.drupalRoute.entityId)
|
|
14
|
+
: null
|
|
15
|
+
})
|
|
16
|
+
},
|
|
17
|
+
})
|
|
@@ -44,7 +44,15 @@ export default defineNuxtPlugin({
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (import.meta.server && event) {
|
|
47
|
-
|
|
47
|
+
const headers = buildProxyRequestHeaders(event)
|
|
48
|
+
// The internal request to the GraphQL server route has no Host header of
|
|
49
|
+
// its own; pass on the public host, from which the route derives the
|
|
50
|
+
// forwarded host for Drupal (see server/utils/backendRequestHeaders.ts).
|
|
51
|
+
const host = event.node.req.headers.host
|
|
52
|
+
if (host) {
|
|
53
|
+
headers.host = host
|
|
54
|
+
}
|
|
55
|
+
state.fetchOptions.headers = headers
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
state.fetchOptions.onResponse = (result) => {
|
|
@@ -71,4 +79,4 @@ export default defineNuxtPlugin({
|
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
},
|
|
74
|
-
})
|
|
82
|
+
})
|
package/plugins/initData.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import type {
|
|
3
2
|
GlobalConfigFragment,
|
|
4
3
|
InitDataQuery,
|
|
@@ -79,11 +78,20 @@ export default defineNuxtPlugin({
|
|
|
79
78
|
if (!event) {
|
|
80
79
|
throw new Error('Missing request event.')
|
|
81
80
|
}
|
|
82
|
-
const {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const {
|
|
82
|
+
value: cachedResponse,
|
|
83
|
+
addToCache,
|
|
84
|
+
cacheTags,
|
|
85
|
+
} = await useDataCache<InitDataResponse>(
|
|
86
|
+
'initData_' + language.value,
|
|
87
|
+
event,
|
|
88
|
+
)
|
|
89
|
+
// A cached response skips the GraphQL request, so pass its tags on to
|
|
90
|
+
// the page in the route cache (only the tags: bubbling the whole cache
|
|
91
|
+
// item would make the page as permanent as the init data).
|
|
92
|
+
if (cachedResponse) {
|
|
93
|
+
useRouteCache((route) => route.addTags(cacheTags))
|
|
94
|
+
}
|
|
87
95
|
const response = cachedResponse || (await useGraphqlQuery('initData'))
|
|
88
96
|
data.value = mapInitData(response)
|
|
89
97
|
if (!cachedResponse && response.extensions.cacheability?.isCacheable) {
|
|
@@ -105,4 +113,4 @@ export default defineNuxtPlugin({
|
|
|
105
113
|
watch(language, loadInitData)
|
|
106
114
|
}
|
|
107
115
|
},
|
|
108
|
-
})
|
|
116
|
+
})
|