├── docs
└── images
│ ├── azure-static-endpoint.png
│ ├── azure-static-website.png
│ ├── netlify-build-settings.png
│ └── netlify-env-vars.png
├── netlify.toml
├── readme.md
├── sitecore
├── Uniform SitecoreJSS Demo-items.0.0.1.zip
├── config
│ ├── uniform-jss.config
│ └── uniform-jss.unicorn.config
└── serialization
│ ├── content
│ ├── uniform-jss.yml
│ └── uniform-jss
│ │ ├── Home.yml
│ │ ├── Home
│ │ ├── About.yml
│ │ └── About
│ │ │ ├── local.yml
│ │ │ └── local
│ │ │ ├── heros.yml
│ │ │ └── heros
│ │ │ └── About Hero.yml
│ │ ├── global.yml
│ │ └── global
│ │ ├── Heros.yml
│ │ └── Heros
│ │ └── Default Hero.yml
│ ├── layouts
│ ├── uniform-jss.yml
│ └── uniform-jss
│ │ └── Uniform JSS.yml
│ ├── placeholders
│ ├── uniform-jss.yml
│ └── uniform-jss
│ │ └── uniform-jss-content.yml
│ ├── renderings
│ ├── uniform-jss.yml
│ └── uniform-jss
│ │ ├── Hero.yml
│ │ └── HeroImage.yml
│ └── templates
│ ├── uniform-jss.yml
│ └── uniform-jss
│ ├── Components.yml
│ ├── Components
│ ├── Hero.yml
│ └── Hero
│ │ ├── Call to action.yml
│ │ ├── Call to action
│ │ ├── primaryCTALink.yml
│ │ ├── primaryCTATitle.yml
│ │ ├── secondaryCTALink.yml
│ │ └── secondaryCTATitle.yml
│ │ ├── Content.yml
│ │ ├── Content
│ │ ├── image.yml
│ │ ├── subtitle.yml
│ │ ├── text.yml
│ │ └── title.yml
│ │ └── __Standard Values.yml
│ ├── Pages.yml
│ └── Pages
│ ├── CommonPage.yml
│ └── CommonPage
│ └── __Standard Values.yml
└── src
├── .babelrc
├── .env.sample
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── assets
└── css
│ └── tailwind.css
├── components
├── Footer
│ └── Footer.vue
├── Hero
│ ├── Hero.vue
│ ├── MenuDesktop.vue
│ └── MenuMobile.vue
├── HeroImage
│ ├── HeroImage.vue
│ ├── MenuDesktop.vue
│ └── MenuMobile.vue
├── LanguageSwitcherSample.vue
├── LinkList
│ └── LinkList.vue
├── Logo
│ └── Logo.vue
├── NavLinks
│ └── NavLinks.vue
├── NuxtLinkWrapper
│ └── NuxtLinkWrapper.vue
├── RoutableSitecoreLink
│ └── RoutableSitecoreLink.vue
└── RouterNavigationSample.vue
├── data
├── content
│ ├── .gitignore
│ └── Global Heros
│ │ └── Default Hero
│ │ └── en.yml
├── context
│ ├── about
│ │ └── en.json
│ └── en.json
└── routes
│ ├── about
│ └── en.yml
│ └── en.yml
├── deploy.js
├── jest.config.js
├── layouts
├── README.md
├── default.vue
└── error.vue
├── lib
└── layoutServiceUtils.js
├── modules
├── express
│ └── initialize.js
├── jss
│ ├── data-fetcher
│ │ ├── axios-data-fetcher-plugin.js
│ │ ├── initialize.js
│ │ └── install-data-fetcher-plugin.js
│ ├── disconnected-mode
│ │ ├── getDisconnectedModeMiddlewares.js
│ │ └── initialize.js
│ ├── i18n
│ │ ├── module.js
│ │ └── sitecore-jss-i18n-plugin.js
│ ├── rendering-host
│ │ ├── initialize.js
│ │ └── nuxt-jss-rendering-host-middleware.js
│ ├── sitecore-proxy
│ │ ├── create-sitecore-proxy-middleware.js
│ │ ├── default-proxy-configuration.js
│ │ ├── handle-proxy-response.js
│ │ ├── initialize.js
│ │ └── nuxt-jss-proxy-middleware.js
│ ├── standard
│ │ ├── configure-router.js
│ │ ├── initialize.js
│ │ ├── sitecore-jss-config-runtime-plugin.js
│ │ └── sitecore-jss-placeholder-plugin.js
│ └── tracking-api
│ │ ├── initialize.js
│ │ └── sitecore-jss-tracking-api-plugin.js
└── uniform
│ ├── disconnected-export
│ └── initialize.js
│ └── services
│ ├── initialize.js
│ └── logging
│ └── consoleLogger.js
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
├── README.md
└── _.vue
├── plugins
└── export-route-data-context-plugin.js
├── scripts
├── bootstrap.js
├── generate-component-factory.js
└── generate-config.js
├── server
├── server.config.js
├── server.js
└── tunnel.js
├── sitecore
├── .gitignore
├── config
│ └── uniform-jss.config
├── definitions
│ ├── components
│ │ └── Hero.sitecore.js
│ ├── config.js
│ ├── content.sitecore.js
│ ├── dictionary.sitecore.js
│ ├── placeholders.sitecore.js
│ └── routes.sitecore.js
└── pipelines
│ └── generateMedia.patch.js
├── static
├── README.md
└── favicon.ico
├── store
├── app
│ └── index.js
├── index.js
└── ui
│ └── index.js
├── tailwind.config.js
├── temp
└── .gitignore
└── uniform.config.js
/docs/images/azure-static-endpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/docs/images/azure-static-endpoint.png
--------------------------------------------------------------------------------
/docs/images/azure-static-website.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/docs/images/azure-static-website.png
--------------------------------------------------------------------------------
/docs/images/netlify-build-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/docs/images/netlify-build-settings.png
--------------------------------------------------------------------------------
/docs/images/netlify-env-vars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/docs/images/netlify-env-vars.png
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | base = "src/"
3 | publish = "out/"
4 | command = "npm run export"
--------------------------------------------------------------------------------
/sitecore/Uniform SitecoreJSS Demo-items.0.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/sitecore/Uniform SitecoreJSS Demo-items.0.0.1.zip
--------------------------------------------------------------------------------
/sitecore/config/uniform-jss.config:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
36 |
44 |
45 |
46 |
47 |
56 |
64 |
65 |
77 |
78 |
79 |
80 | mw=100,mh=50
81 |
82 |
83 | mw=300
84 | mw=100
85 |
86 |
87 |
88 |
89 |
94 |
95 |
96 |
97 | $(url)
98 |
99 | true
100 | true
101 |
102 |
103 | false
104 | false
105 | false
106 | false
107 | true
108 |
109 |
110 |
111 |
112 |
113 | context
114 |
115 | /sitecore/templates/Project/uniform-jss
116 |
117 |
118 |
119 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/sitecore/config/uniform-jss.unicorn.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | /unicorn.aspx
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "b506a73e-c9bb-4c13-8f2c-836b1e9f59dc"
3 | Parent: "0de95ae4-41ab-4d01-9eb0-67441b7c2450"
4 | Template: "061cba15-5474-4b91-8a06-17903b102b82"
5 | Path: "/sitecore/content/uniform-jss"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value:
11 | Languages:
12 | - Language: en
13 | Fields:
14 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
15 | Hint: __Display name
16 | Value: Uniform (JSS)
17 | Versions:
18 | - Version: 1
19 | Fields:
20 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
21 | Hint: __Created
22 | Value: 20200214T190709Z
23 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/Home.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "5505b7b1-b454-459e-9d36-06a4d89755ad"
3 | Parent: "b506a73e-c9bb-4c13-8f2c-836b1e9f59dc"
4 | Template: "bc93942a-b0e5-4bd3-a872-a9b8b5c7d4f9"
5 | Path: "/sitecore/content/uniform-jss/Home"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: Network/16x16/home.png
11 | - ID: "1172f251-dad4-4efb-a329-0c63500e4f1e"
12 | Hint: __Masters
13 | Type: TreelistEx
14 | Value: "{A65EAFF8-35C8-4929-A4A3-D2543B2867EA}"
15 | - ID: "a4f985d9-98b3-4b52-aaaf-4344f6e747c6"
16 | Hint: __Workflow
17 | Value: "{A5BC37E7-ED96-4C1E-8590-A26E64DB55EA}"
18 | - ID: "f1a1fe9e-a60c-4ddb-a3a0-bb5b29fe732e"
19 | Hint: __Renderings
20 | Type: layout
21 | Value: |
22 |
24 |
26 |
32 |
33 |
34 | Languages:
35 | - Language: en
36 | Versions:
37 | - Version: 1
38 | Fields:
39 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
40 | Hint: __Created
41 | Value: 20200214T190718Z
42 | - ID: "3e431de1-525e-47a3-b6b0-1ccbec3a8c98"
43 | Hint: __Workflow state
44 | Value: "{190B1C84-F1BE-47ED-AA41-F42193D9C8FC}"
45 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/Home/About.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "0faa6199-bb54-455b-bacc-3b018c3fac1e"
3 | Parent: "5505b7b1-b454-459e-9d36-06a4d89755ad"
4 | Template: "bc93942a-b0e5-4bd3-a872-a9b8b5c7d4f9"
5 | Path: "/sitecore/content/uniform-jss/Home/About"
6 | DB: master
7 | SharedFields:
8 | - ID: "f1a1fe9e-a60c-4ddb-a3a0-bb5b29fe732e"
9 | Hint: __Renderings
10 | Type: layout
11 | Value: |
12 |
14 |
16 |
22 |
23 |
24 | Languages:
25 | - Language: en
26 | Versions:
27 | - Version: 1
28 | Fields:
29 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
30 | Hint: __Created
31 | Value: 20200228T213909Z
32 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/Home/About/local.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "143c02e9-41e5-4803-a702-42d3bf01c141"
3 | Parent: "0faa6199-bb54-455b-bacc-3b018c3fac1e"
4 | Template: "a87a00b1-e6db-45ab-8b54-636fec3b5523"
5 | Path: "/sitecore/content/uniform-jss/Home/About/local"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: business/16x16/index.png
11 | Languages:
12 | - Language: en
13 | Fields:
14 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
15 | Hint: __Display name
16 | Value: Local Page Content
17 | Versions:
18 | - Version: 1
19 | Fields:
20 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
21 | Hint: __Created
22 | Value: 20200304T235806Z
23 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/Home/About/local/heros.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "f5461277-f3b0-44c3-bbb0-1b38ccb6709a"
3 | Parent: "143c02e9-41e5-4803-a702-42d3bf01c141"
4 | Template: "a87a00b1-e6db-45ab-8b54-636fec3b5523"
5 | Path: "/sitecore/content/uniform-jss/Home/About/local/heros"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: business/16x16/index.png
11 | - ID: "1172f251-dad4-4efb-a329-0c63500e4f1e"
12 | Hint: __Masters
13 | Type: TreelistEx
14 | Value: "{DD6D4B50-2296-4146-9F48-7C61FF989E07}"
15 | Languages:
16 | - Language: en
17 | Fields:
18 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
19 | Hint: __Display name
20 | Value: Local Page Heros
21 | Versions:
22 | - Version: 1
23 | Fields:
24 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
25 | Hint: __Created
26 | Value: 20200304T235839Z
27 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/Home/About/local/heros/About Hero.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "da5c6f8a-5cbd-4bb3-b7dd-ce8e866ab407"
3 | Parent: "f5461277-f3b0-44c3-bbb0-1b38ccb6709a"
4 | Template: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
5 | Path: "/sitecore/content/uniform-jss/Home/About/local/heros/About Hero"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "0e14385e-493c-478e-8c3c-beb0839b5df3"
13 | Hint: text
14 | Value: Connecting the two worlds of Enterprise DXP and JAMstack
15 | - ID: "20753c20-34e3-4a03-8f34-1232250ae849"
16 | Hint: subtitle
17 | Value: Uniform
18 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
19 | Hint: __Created
20 | Value: 20200304T235910Z
21 | - ID: "a4e63708-f7f5-4c53-9348-31e8b9a1b1e8"
22 | Hint: title
23 | Value: About
24 | - ID: "e928db78-cc24-4e20-beaa-e8e6956283f1"
25 | Hint: image
26 | Value: "https://images.unsplash.com/photo-1472148083604-64f1084980b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80"
27 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/global.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "46a00631-b1dc-4545-935e-ff0e3225ba7c"
3 | Parent: "b506a73e-c9bb-4c13-8f2c-836b1e9f59dc"
4 | Template: "a87a00b1-e6db-45ab-8b54-636fec3b5523"
5 | Path: "/sitecore/content/uniform-jss/global"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: Network/16x16/environment.png
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 200
14 | Languages:
15 | - Language: en
16 | Fields:
17 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
18 | Hint: __Display name
19 | Value: Global Content
20 | Versions:
21 | - Version: 1
22 | Fields:
23 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
24 | Hint: __Created
25 | Value: 20200228T212621Z
26 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/global/Heros.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "085c4ffa-12ec-41d4-b95b-a78f8492434b"
3 | Parent: "46a00631-b1dc-4545-935e-ff0e3225ba7c"
4 | Template: "a87a00b1-e6db-45ab-8b54-636fec3b5523"
5 | Path: "/sitecore/content/uniform-jss/global/Heros"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: network/16x16/environment.png
11 | - ID: "1172f251-dad4-4efb-a329-0c63500e4f1e"
12 | Hint: __Masters
13 | Type: TreelistEx
14 | Value: "{CE957D4A-5770-492C-A61A-695293C3F941}"
15 | Languages:
16 | - Language: en
17 | Fields:
18 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
19 | Hint: __Display name
20 | Value: Global Heros
21 | Versions:
22 | - Version: 1
23 | Fields:
24 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
25 | Hint: __Created
26 | Value: 20200228T212630Z
27 |
--------------------------------------------------------------------------------
/sitecore/serialization/content/uniform-jss/global/Heros/Default Hero.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "bb13d540-3676-4d38-b6cf-7724a17a2f46"
3 | Parent: "085c4ffa-12ec-41d4-b95b-a78f8492434b"
4 | Template: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
5 | Path: "/sitecore/content/uniform-jss/global/Heros/Default Hero"
6 | DB: master
7 | SharedFields:
8 | - ID: "a4f985d9-98b3-4b52-aaaf-4344f6e747c6"
9 | Hint: __Workflow
10 | Value: "{A5BC37E7-ED96-4C1E-8590-A26E64DB55EA}"
11 | Languages:
12 | - Language: en
13 | Versions:
14 | - Version: 1
15 | Fields:
16 | - ID: "0e14385e-493c-478e-8c3c-beb0839b5df3"
17 | Hint: text
18 | Value: Uniform helps connect your existing digital marketing stack and JAMstackify it.
19 | - ID: "14f5cbb0-bafd-4414-a740-8e85d1fc6a66"
20 | Hint: secondaryCTALink
21 | Value: |
22 |
23 | - ID: "15bc044d-e93e-4139-ad94-9f4d141fcaaa"
24 | Hint: secondaryCTATitle
25 | Value: Schedule live demo
26 | - ID: "20218d3b-770b-4c46-90a8-7acba5136204"
27 | Hint: primaryCTALink
28 | Value: |
29 |
30 | - ID: "20753c20-34e3-4a03-8f34-1232250ae849"
31 | Hint: subtitle
32 | Value: to Uniform
33 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
34 | Hint: __Created
35 | Value: 20200214T191703Z
36 | - ID: "3e431de1-525e-47a3-b6b0-1ccbec3a8c98"
37 | Hint: __Workflow state
38 | Value: "{190B1C84-F1BE-47ED-AA41-F42193D9C8FC}"
39 | - ID: "a4e63708-f7f5-4c53-9348-31e8b9a1b1e8"
40 | Hint: title
41 | Value: Welcome
42 | - ID: "e928db78-cc24-4e20-beaa-e8e6956283f1"
43 | Hint: image
44 | Value:
45 | - ID: "f64d1b7f-b721-43d3-b8b9-37270f765c0c"
46 | Hint: primaryCTATitle
47 | Value: Learn more
48 |
--------------------------------------------------------------------------------
/sitecore/serialization/layouts/uniform-jss.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "e9330290-a76c-4c69-bbfc-2245bab3f1f4"
3 | Parent: "da04b275-8838-4a3a-afee-817cf1fdd2eb"
4 | Template: "93227c5d-4fef-474d-94c0-f252ec8e8219"
5 | Path: "/sitecore/layout/Layouts/Project/uniform-jss"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200302T030423Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/layouts/uniform-jss/Uniform JSS.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "bdaa4402-015a-4f42-bf43-d5e083a02a20"
3 | Parent: "e9330290-a76c-4c69-bbfc-2245bab3f1f4"
4 | Template: "35c61e90-47dd-43dd-83a8-d1c4d5119720"
5 | Path: "/sitecore/layout/Layouts/Project/uniform-jss/Uniform JSS"
6 | DB: master
7 | SharedFields:
8 | - ID: "80334869-86dc-4472-aa89-44cf1b2f6c9b"
9 | Hint: Placeholders
10 | Type: Treelist
11 | Value: "{DEE69BFC-9B92-473B-802A-16DAFF595011}"
12 | Languages:
13 | - Language: en
14 | Versions:
15 | - Version: 1
16 | Fields:
17 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
18 | Hint: __Created
19 | Value: 20200302T030433Z
20 |
--------------------------------------------------------------------------------
/sitecore/serialization/placeholders/uniform-jss.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "d098f13c-4629-48bf-8e85-e5812649face"
3 | Parent: "f5f0fbe3-61ad-4967-a5d8-8d760331d6a1"
4 | Template: "a87a00b1-e6db-45ab-8b54-636fec3b5523"
5 | Path: "/sitecore/layout/Placeholder Settings/Project/uniform-jss"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200228T212816Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/placeholders/uniform-jss/uniform-jss-content.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "dee69bfc-9b92-473b-802a-16daff595011"
3 | Parent: "d098f13c-4629-48bf-8e85-e5812649face"
4 | Template: "5c547d4e-7111-4995-95b0-6b561751bf2e"
5 | Path: "/sitecore/layout/Placeholder Settings/Project/uniform-jss/uniform-jss-content"
6 | DB: master
7 | SharedFields:
8 | - ID: "7256bdab-1fd2-49dd-b205-cb4873d2917c"
9 | Hint: Placeholder Key
10 | Value: "uniform-jss-content"
11 | - ID: "e391b526-d0c5-439d-803e-17512eae6222"
12 | Hint: Allowed Controls
13 | Type: TreelistEx
14 | Value: |
15 | {FB53DB9E-75B0-4DBD-A14B-3BD053ECEF59}
16 | {BDAFEC7C-B6C0-4E7F-B5FC-FC18D4F841F7}
17 | Languages:
18 | - Language: da
19 | Fields:
20 | - ID: "87871ff5-1965-46d6-884f-01d6a0b9c4c1"
21 | Hint: Description
22 | Value: |
23 |
24 |
Indholdsplaceholder
25 |
26 |
27 |
28 |

29 |
Indholdsplaceholderen er den primære placeholder.
30 |
31 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
32 | Hint: __Display name
33 | Value: indhold
34 | Versions:
35 | - Version: 1
36 | Fields:
37 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
38 | Hint: __Created
39 | Value: 20190328T131427Z
40 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
41 | Hint: __Created by
42 | Value: |
43 | sitecore\Admin
44 | - Language: "de-DE"
45 | Fields:
46 | - ID: "87871ff5-1965-46d6-884f-01d6a0b9c4c1"
47 | Hint: Description
48 | Value: |
49 |
50 |
Inhaltsplatzhalter
51 |
52 |
53 |
54 |

55 |
Der Inhaltsplatzhalter ist der Hauptplatzhalter.
56 |
57 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
58 | Hint: __Display name
59 | Value: Inhalt
60 | Versions:
61 | - Version: 1
62 | Fields:
63 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
64 | Hint: __Created
65 | Value: 20190328T132519Z
66 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
67 | Hint: __Created by
68 | Value: |
69 | sitecore\Admin
70 | - Language: en
71 | Fields:
72 | - ID: "87871ff5-1965-46d6-884f-01d6a0b9c4c1"
73 | Hint: Description
74 | Value: |
75 |
76 |
Uniform JSS demo Placeholder
77 |
78 |
79 |

80 |
The content placeholder is the main placeholder.
81 |
82 |
83 |
84 |
85 |
86 |
87 | Versions:
88 | - Version: 1
89 | Fields:
90 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
91 | Hint: __Created
92 | Value: 20080110T175900Z
93 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
94 | Hint: __Created by
95 | Value: |
96 | sitecore\Admin
97 | - Language: "ja-JP"
98 | Fields:
99 | - ID: "87871ff5-1965-46d6-884f-01d6a0b9c4c1"
100 | Hint: Description
101 | Value: |
102 |
103 |
コンテンツ プレースホルダー
104 |
105 |
106 |
107 |

108 |
コンテンツ プレースホルダーはメインのプレースホルダーです。
109 |
110 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
111 | Hint: __Display name
112 | Value: コンテンツ (content)
113 | Versions:
114 | - Version: 1
115 | Fields:
116 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
117 | Hint: __Created
118 | Value: 20190328T133902Z
119 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
120 | Hint: __Created by
121 | Value: |
122 | sitecore\Admin
123 | - Language: "zh-CN"
124 | Fields:
125 | - ID: "87871ff5-1965-46d6-884f-01d6a0b9c4c1"
126 | Hint: Description
127 | Value: |
128 |
129 |
内容占位符
130 |
131 |
132 |
133 |

134 |
135 |
136 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
137 | Hint: __Display name
138 | Value: 内容
139 | Versions:
140 | - Version: 1
141 | Fields:
142 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
143 | Hint: __Created
144 | Value: 20190328T134752Z
145 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
146 | Hint: __Created by
147 | Value: |
148 | sitecore\Admin
149 |
--------------------------------------------------------------------------------
/sitecore/serialization/renderings/uniform-jss.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "408a7178-2c9a-4d99-8853-2c0a170b616c"
3 | Parent: "1995806f-0a84-42b5-93b0-88f0e2ff872c"
4 | Template: "7ee0975b-0698-493e-b3a2-0b2ef33d0522"
5 | Path: "/sitecore/layout/Renderings/Project/uniform-jss"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200214T191139Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/renderings/uniform-jss/Hero.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
3 | Parent: "408a7178-2c9a-4d99-8853-2c0a170b616c"
4 | Template: "04646a89-996f-4ee7-878a-ffdbf1f0ef0d"
5 | Path: "/sitecore/layout/Renderings/Project/uniform-jss/Hero"
6 | DB: master
7 | SharedFields:
8 | - ID: "003a72cd-4cd6-4392-9862-41d4159929cd"
9 | Hint: Data source
10 | Value:
11 | - ID: "037fe404-dd19-4bf7-8e30-4dadf68b27b0"
12 | Hint: componentName
13 | Value: Hero
14 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
15 | Hint: __Icon
16 | Value: software/16x16/component_yellow.png
17 | - ID: "1a7c85e5-dc0b-490d-9187-bb1dbcb4c72f"
18 | Hint: Datasource Template
19 | Value: "/sitecore/templates/Project/uniform-jss/Components/Hero"
20 | - ID: "7d8ae35f-9ed1-43b5-96a2-0a5f040d4e4e"
21 | Hint: Open Properties after Add
22 | Value: 1
23 | - ID: "b5b27af1-25ef-405c-87ce-369b3a004016"
24 | Hint: Datasource Location
25 | Value: "/sitecore/content/uniform-jss/global/heros|./local/heros"
26 | - ID: "e441abe7-2ca3-4640-ae26-3789967925d7"
27 | Hint: Compatible Renderings
28 | Type: Treelist
29 | Value:
30 | Languages:
31 | - Language: en
32 | Versions:
33 | - Version: 1
34 | Fields:
35 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
36 | Hint: __Created
37 | Value: 20200214T190824Z
38 |
--------------------------------------------------------------------------------
/sitecore/serialization/renderings/uniform-jss/HeroImage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "bdafec7c-b6c0-4e7f-b5fc-fc18d4f841f7"
3 | Parent: "408a7178-2c9a-4d99-8853-2c0a170b616c"
4 | Template: "04646a89-996f-4ee7-878a-ffdbf1f0ef0d"
5 | Path: "/sitecore/layout/Renderings/Project/uniform-jss/HeroImage"
6 | DB: master
7 | SharedFields:
8 | - ID: "003a72cd-4cd6-4392-9862-41d4159929cd"
9 | Hint: Data source
10 | Value:
11 | - ID: "037fe404-dd19-4bf7-8e30-4dadf68b27b0"
12 | Hint: componentName
13 | Value: HeroImage
14 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
15 | Hint: __Icon
16 | Value: Software/16x16/component_yellow.png
17 | - ID: "1a7c85e5-dc0b-490d-9187-bb1dbcb4c72f"
18 | Hint: Datasource Template
19 | Value: "/sitecore/templates/Project/uniform-jss/Components/Hero"
20 | - ID: "7d8ae35f-9ed1-43b5-96a2-0a5f040d4e4e"
21 | Hint: Open Properties after Add
22 | Value: 1
23 | - ID: "b5b27af1-25ef-405c-87ce-369b3a004016"
24 | Hint: Datasource Location
25 | Value: "/sitecore/content/uniform-jss/global/heros|./local/heros"
26 | - ID: "e441abe7-2ca3-4640-ae26-3789967925d7"
27 | Hint: Compatible Renderings
28 | Type: Treelist
29 | Value:
30 | Languages:
31 | - Language: en
32 | Fields:
33 | - ID: "b5e02ad9-d56f-4c41-a065-a133db87bdeb"
34 | Hint: __Display name
35 | Value: Hero with Image
36 | Versions:
37 | - Version: 1
38 | Fields:
39 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
40 | Hint: __Created
41 | Value: 20200214T190824Z
42 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "dd99a4cb-f8cb-4764-a1f9-274c950173fc"
3 | Parent: "825b30b4-b40b-422e-9920-23a1b6bda89c"
4 | Template: "0437fee2-44c9-46a6-abe9-28858d9fee8c"
5 | Path: "/sitecore/templates/Project/uniform-jss"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200219T020039Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "2150aa09-85f3-47a4-826b-f03b468d0fd6"
3 | Parent: "dd99a4cb-f8cb-4764-a1f9-274c950173fc"
4 | Template: "0437fee2-44c9-46a6-abe9-28858d9fee8c"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200219T020056Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
3 | Parent: "2150aa09-85f3-47a4-826b-f03b468d0fd6"
4 | Template: "ab86861a-6030-46c5-b394-e8f99e8b87db"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: Network/16x16/id_card.png
11 | - ID: "12c33f3f-86c5-43a5-aeb4-5598cec45116"
12 | Hint: __Base template
13 | Type: tree list
14 | Value: "{1930BBEB-7805-471A-A3BE-4858AC7CF696}"
15 | - ID: "f7d48a55-2158-4f02-9356-756654404f73"
16 | Hint: __Standard values
17 | Value: "{908A3C35-455F-4EAC-9C93-B87C7BBD6311}"
18 | Languages:
19 | - Language: en
20 | Versions:
21 | - Version: 1
22 | Fields:
23 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
24 | Hint: __Created
25 | Value: 20200219T020101Z
26 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Call to action.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "47bac466-3abf-487e-b014-5e19ea237ff3"
3 | Parent: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
4 | Template: "e269fbb5-3750-427a-9149-7aa950b49301"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Call to action"
6 | DB: master
7 | SharedFields:
8 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
9 | Hint: __Sortorder
10 | Value: 200
11 | Languages:
12 | - Language: en
13 | Versions:
14 | - Version: 1
15 | Fields:
16 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
17 | Hint: __Created
18 | Value: 20200229T003600Z
19 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Call to action/primaryCTALink.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "20218d3b-770b-4c46-90a8-7acba5136204"
3 | Parent: "47bac466-3abf-487e-b014-5e19ea237ff3"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Call to action/primaryCTALink"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: General Link
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 100
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T003601Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Call to action/primaryCTATitle.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "f64d1b7f-b721-43d3-b8b9-37270f765c0c"
3 | Parent: "47bac466-3abf-487e-b014-5e19ea237ff3"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Call to action/primaryCTATitle"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: "Single-Line Text"
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 200
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T003601Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Call to action/secondaryCTALink.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "14f5cbb0-bafd-4414-a740-8e85d1fc6a66"
3 | Parent: "47bac466-3abf-487e-b014-5e19ea237ff3"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Call to action/secondaryCTALink"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: General Link
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 300
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T003601Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Call to action/secondaryCTATitle.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "15bc044d-e93e-4139-ad94-9f4d141fcaaa"
3 | Parent: "47bac466-3abf-487e-b014-5e19ea237ff3"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Call to action/secondaryCTATitle"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: "Single-Line Text"
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 400
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T003601Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Content.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "4fc5ab9d-579c-4564-b1d5-4d115703ca56"
3 | Parent: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
4 | Template: "e269fbb5-3750-427a-9149-7aa950b49301"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Content"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200228T212354Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Content/image.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "e928db78-cc24-4e20-beaa-e8e6956283f1"
3 | Parent: "4fc5ab9d-579c-4564-b1d5-4d115703ca56"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Content/image"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: "Single-Line Text"
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 300
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T011120Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Content/subtitle.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "20753c20-34e3-4a03-8f34-1232250ae849"
3 | Parent: "4fc5ab9d-579c-4564-b1d5-4d115703ca56"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Content/subtitle"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: "Single-Line Text"
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 150
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200229T000037Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Content/text.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "0e14385e-493c-478e-8c3c-beb0839b5df3"
3 | Parent: "4fc5ab9d-579c-4564-b1d5-4d115703ca56"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Content/text"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: Rich Text
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 200
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200228T212354Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/Content/title.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "a4e63708-f7f5-4c53-9348-31e8b9a1b1e8"
3 | Parent: "4fc5ab9d-579c-4564-b1d5-4d115703ca56"
4 | Template: "455a3e98-a627-4b40-8035-e683a0331ac7"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/Content/title"
6 | DB: master
7 | SharedFields:
8 | - ID: "ab162cc0-dc80-4abf-8871-998ee5d7ba32"
9 | Hint: Type
10 | Value: "Single-Line Text"
11 | - ID: "ba3f86a2-4a1c-4d78-b63d-91c2779c1b5e"
12 | Hint: __Sortorder
13 | Value: 100
14 | Languages:
15 | - Language: en
16 | Versions:
17 | - Version: 1
18 | Fields:
19 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
20 | Hint: __Created
21 | Value: 20200228T212354Z
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Components/Hero/__Standard Values.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "908a3c35-455f-4eac-9c93-b87c7bbd6311"
3 | Parent: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
4 | Template: "dd6d4b50-2296-4146-9f48-7c61ff989e07"
5 | Path: "/sitecore/templates/Project/uniform-jss/Components/Hero/__Standard Values"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200228T212356Z
15 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
16 | Hint: __Created by
17 | Value: |
18 | sitecore\Admin
19 | - ID: "a4e63708-f7f5-4c53-9348-31e8b9a1b1e8"
20 | Hint: title
21 | Value: $name
22 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Pages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "97bcb300-2ce0-4040-98e4-9f55acd310c1"
3 | Parent: "dd99a4cb-f8cb-4764-a1f9-274c950173fc"
4 | Template: "0437fee2-44c9-46a6-abe9-28858d9fee8c"
5 | Path: "/sitecore/templates/Project/uniform-jss/Pages"
6 | DB: master
7 | Languages:
8 | - Language: en
9 | Versions:
10 | - Version: 1
11 | Fields:
12 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
13 | Hint: __Created
14 | Value: 20200219T020106Z
15 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Pages/CommonPage.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "bc93942a-b0e5-4bd3-a872-a9b8b5c7d4f9"
3 | Parent: "97bcb300-2ce0-4040-98e4-9f55acd310c1"
4 | Template: "ab86861a-6030-46c5-b394-e8f99e8b87db"
5 | Path: "/sitecore/templates/Project/uniform-jss/Pages/CommonPage"
6 | DB: master
7 | SharedFields:
8 | - ID: "06d5295c-ed2f-4a54-9bf2-26228d113318"
9 | Hint: __Icon
10 | Value: Applications/16x16/document_plain.png
11 | - ID: "12c33f3f-86c5-43a5-aeb4-5598cec45116"
12 | Hint: __Base template
13 | Type: tree list
14 | Value: |
15 | {1930BBEB-7805-471A-A3BE-4858AC7CF696}
16 | {B36BA9FD-0DC0-49C8-BEA2-E55D70E6AF28}
17 | - ID: "f7d48a55-2158-4f02-9356-756654404f73"
18 | Hint: __Standard values
19 | Value: "{E87F5226-5089-4D69-8B16-ABE69B134125}"
20 | Languages:
21 | - Language: en
22 | Versions:
23 | - Version: 1
24 | Fields:
25 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
26 | Hint: __Created
27 | Value: 20200219T020047Z
28 |
--------------------------------------------------------------------------------
/sitecore/serialization/templates/uniform-jss/Pages/CommonPage/__Standard Values.yml:
--------------------------------------------------------------------------------
1 | ---
2 | ID: "e87f5226-5089-4d69-8b16-abe69b134125"
3 | Parent: "bc93942a-b0e5-4bd3-a872-a9b8b5c7d4f9"
4 | Template: "bc93942a-b0e5-4bd3-a872-a9b8b5c7d4f9"
5 | Path: "/sitecore/templates/Project/uniform-jss/Pages/CommonPage/__Standard Values"
6 | DB: master
7 | SharedFields:
8 | - ID: "f1a1fe9e-a60c-4ddb-a3a0-bb5b29fe732e"
9 | Hint: __Renderings
10 | Type: layout
11 | Value: |
12 |
13 |
16 |
17 | Languages:
18 | - Language: en
19 | Versions:
20 | - Version: 1
21 | Fields:
22 | - ID: "25bed78c-4957-4165-998a-ca1b52f67497"
23 | Hint: __Created
24 | Value: 20200228T213729Z
25 | - ID: "5dd74568-4d4b-44c1-b513-0af5f4cda34f"
26 | Hint: __Created by
27 | Value: |
28 | sitecore\Admin
29 |
--------------------------------------------------------------------------------
/src/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "test": {
4 | "presets": [
5 | [
6 | "@babel/preset-env",
7 | {
8 | "targets": {
9 | "node": "current"
10 | }
11 | }
12 | ]
13 | ]
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/.env.sample:
--------------------------------------------------------------------------------
1 | UNIFORM_API_KEY={layout-service-api-key-here}
2 | UNIFORM_API_URL=https://uniform-jss-nuxt.dev.local
3 |
4 | # important: make sure these values are set as provided below
5 | UNIFORM_API_SITENAME='uniform-jss-kit'
6 | UNIFORM_API_MAPSERVICE='/uniform/api/content/${UNIFORM_API_SITENAME}/map'
7 |
8 | # Helps resolve connection issue enable if connecting to Sitecore over HTTPS with dev certs to avoid Node.js connection issues
9 | NODE_TLS_REJECT_UNAUTHORIZED=0
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 |
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 |
41 | # Temporarily local private deps
42 | !data/node_modules/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # Optional npm cache directory
48 | .npm
49 |
50 | # Optional eslint cache
51 | .eslintcache
52 |
53 | # Optional REPL history
54 | .node_repl_history
55 |
56 | # Output of 'npm pack'
57 | *.tgz
58 |
59 | # Yarn Integrity file
60 | .yarn-integrity
61 |
62 | # dotenv environment variables file
63 | .env
64 |
65 | # parcel-bundler cache (https://parceljs.org/)
66 | .cache
67 |
68 | # next.js build output
69 | .next
70 |
71 | # nuxt.js build output
72 | .nuxt
73 |
74 | # Nuxt generate
75 | ./dist
76 | /out
77 |
78 | # vuepress build output
79 | .vuepress/dist
80 |
81 | # Serverless directories
82 | .serverless
83 |
84 | # IDE / Editor
85 | .idea
86 | .editorconfig
87 |
88 | # Service worker
89 | sw.*
90 |
91 | # Mac OSX
92 | .DS_Store
93 |
94 | # Vim swap files
95 | *.swp
96 | /scjssconfig.json
97 |
98 | netlify.config.json
99 |
100 | # Uniform temp folder
101 | .temp
--------------------------------------------------------------------------------
/src/.npmrc:
--------------------------------------------------------------------------------
1 | //registry.npmjs.org/:_authToken=${NPM_TOKEN}
--------------------------------------------------------------------------------
/src/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 |
--------------------------------------------------------------------------------
/src/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "es5",
4 | "tabWidth": 2,
5 | "arrowParens": "always",
6 | "printWidth": 100,
7 | "endOfLine": "auto"
8 | }
9 |
--------------------------------------------------------------------------------
/src/assets/css/tailwind.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss/base';
2 | @import 'tailwindcss/components';
3 | @import 'tailwindcss/utilities';
4 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
25 |
26 | © {{ new Date().getFullYear() }} Uniform Systems, Inc. All rights reserved.
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/components/Hero/Hero.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
26 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
76 |
86 |
87 |
88 |
89 |
90 |
114 |
115 |
116 |
117 |
121 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
206 |
--------------------------------------------------------------------------------
/src/components/Hero/MenuDesktop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
44 |
45 |
46 |
67 |
--------------------------------------------------------------------------------
/src/components/Hero/MenuMobile.vue:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
44 |
65 |
--------------------------------------------------------------------------------
/src/components/HeroImage/HeroImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
12 |
13 |
20 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
34 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
56 |
57 |
58 |
59 |
60 |
![]()
65 |
66 |
67 |
68 |
69 |
118 |
--------------------------------------------------------------------------------
/src/components/HeroImage/MenuDesktop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
31 |
32 |
33 |
34 |
55 |
--------------------------------------------------------------------------------
/src/components/HeroImage/MenuMobile.vue:
--------------------------------------------------------------------------------
1 |
2 |
42 |
43 |
44 |
65 |
--------------------------------------------------------------------------------
/src/components/LanguageSwitcherSample.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Current language: {{ $jss.getCurrentLanguage() }}
4 | change language:
5 |
8 |
9 |
10 |
23 |
--------------------------------------------------------------------------------
/src/components/LinkList/LinkList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
--------------------------------------------------------------------------------
/src/components/Logo/Logo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
33 |
34 |
35 |
36 |
44 |
--------------------------------------------------------------------------------
/src/components/NavLinks/NavLinks.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | About
7 |
8 |
9 |
10 |
18 |
--------------------------------------------------------------------------------
/src/components/NuxtLinkWrapper/NuxtLinkWrapper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
39 |
--------------------------------------------------------------------------------
/src/components/RoutableSitecoreLink/RoutableSitecoreLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | {{ field.value.text || field.value.href }}
11 |
12 |
13 | {{ field.value.text || field.value.href }}
14 |
15 |
16 |
17 |
48 |
--------------------------------------------------------------------------------
/src/components/RouterNavigationSample.vue:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
--------------------------------------------------------------------------------
/src/data/content/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/src/data/content/.gitignore
--------------------------------------------------------------------------------
/src/data/content/Global Heros/Default Hero/en.yml:
--------------------------------------------------------------------------------
1 | id: global-default-hero
2 | name: Default Hero
3 | template: Hero
4 | fields:
5 | title: Welcome
6 | subtitle: to Uniform
7 | text: Uniform helps connect your existing digital marketing stack and JAMstackify it.
8 | image:
9 | primaryCTALink:
10 | href: http://uniform.dev
11 | primaryCTATitle: Learn more
12 | secondaryCTALink:
13 | href: https://calendly.com/uniformdemo/intro
14 | secondaryCTATitle: Schedule live demo
15 |
--------------------------------------------------------------------------------
/src/data/context/about/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "pageEditing": false,
3 | "site": { "name": "website" },
4 | "pageState": "normal",
5 | "language": "en",
6 | "personalization": {
7 | "rules": {
8 | "70886a78-86e8-48b8-b023-46d0816c8400": [
9 | {
10 | "id": "8563e333-ef77-46e4-b267-a1f0fa067413",
11 | "name": "Bluekai rule",
12 | "condition": "return (function(){var c=(a, b)=>(a && b) ? a.toLowerCase().indexOf(b.toLowerCase()) != -1 : false; var m = document.cookie.match(new RegExp('(^| )uniform.bluekai=([^;]+)')); if (m) { return c(m[2], 'Sitecore+-+Business+Traveler+Audience')}return false;})();",
13 | "data": "320c2c78-d198-42d7-b69e-39f00134bca5",
14 | "component": "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
15 | },
16 | {
17 | "id": null,
18 | "name": "Default",
19 | "condition": "return true;",
20 | "data": "global-default-hero",
21 | "component": "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
22 | }
23 | ]
24 | },
25 | "data": {
26 | "320c2c78-d198-42d7-b69e-39f00134bca5": {
27 | "subtitle": { "value": "BlueKai cookie rule data source" },
28 | "title": { "value": "(About page) Hero for BlueKai cookie" },
29 | "text": {
30 | "value": "This is the data source set on the rule that depends on a BlueKai cookie being set."
31 | }
32 | },
33 | "global-default-hero": {
34 | "subtitle": { "value": "Uniform" },
35 | "title": { "value": "About" },
36 | "text": { "value": "Connecting the two worlds of Enterprise DXP and JAMstack" },
37 | "image": {
38 | "value": "https://images.unsplash.com/photo-1472148083604-64f1084980b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80"
39 | }
40 | }
41 | }
42 | },
43 | "tracking": {
44 | "campaigns": {
45 | "f5e4b73b-9843-44db-9ce7-bc372440d74b": { "name": "Test Campaign" }
46 | },
47 | "events": {
48 | "35f570b4-5ea6-4313-9653-3787966ea377": {
49 | "name": "Business Travel Page Viewed",
50 | "points": 0.0
51 | }
52 | },
53 | "profiles": {
54 | "24dff2cf-b30a-4b75-8967-2fe3ded82271": {
55 | "name": "Focus",
56 | "keys": {
57 | "03379af5-f1ae-4610-b15b-4c7f1032b464": {
58 | "key": "Background",
59 | "value": 1.0
60 | },
61 | "5fdd9829-e689-454d-9abc-8f95ae68744c": {
62 | "key": "Practical",
63 | "value": 2.0
64 | },
65 | "f5652c06-676b-4e12-a9d0-06d000e5f1c8": {
66 | "key": "Process",
67 | "value": 3.0
68 | },
69 | "b32bfacc-3494-4127-b050-cf50078e2b4c": {
70 | "key": "Scope",
71 | "value": 4.0
72 | }
73 | },
74 | "total": 10.0
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/data/context/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "pageEditing": false,
3 | "site": { "name": "website" },
4 | "pageState": "normal",
5 | "language": "en",
6 | "personalization": {
7 | "rules": {
8 | "70886a78-86e8-48b8-b023-46d0816c8400": [
9 | {
10 | "id": "8563e333-ef77-46e4-b267-a1f0fa067413",
11 | "name": "Bluekai rule",
12 | "condition": "return (function(){var c=(a, b)=>(a && b) ? a.toLowerCase().indexOf(b.toLowerCase()) != -1 : false; var m = document.cookie.match(new RegExp('(^| )uniform.bluekai=([^;]+)')); if (m) { return c(m[2], 'Sitecore+-+Business+Traveler+Audience')}return false;})();",
13 | "data": "320c2c78-d198-42d7-b69e-39f00134bca5",
14 | "component": "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
15 | },
16 | {
17 | "id": "54c3e333-ef77-46e4-b267-a1f0fa067413",
18 | "name": "Query string rule",
19 | "condition": "return new URLSearchParams(window.location.search).has(\"uniform-parameter\");",
20 | "data": "d50afb41-e95d-40d2-b0a8-b5d318d00dd2",
21 | "component": "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
22 | },
23 | {
24 | "id": null,
25 | "name": "Default",
26 | "condition": "return true;",
27 | "data": "global-default-hero",
28 | "component": "fb53db9e-75b0-4dbd-a14b-3bd053ecef59"
29 | }
30 | ]
31 | },
32 | "data": {
33 | "320c2c78-d198-42d7-b69e-39f00134bca5": {
34 | "subtitle": { "value": "BlueKai cookie rule data source" },
35 | "title": { "value": "Hero for BlueKai cookie" },
36 | "text": {
37 | "value": "This is the data source set on the rule that depends on a BlueKai cookie being set."
38 | }
39 | },
40 | "d50afb41-e95d-40d2-b0a8-b5d318d00dd2": {
41 | "subtitle": { "value": "Query string parameter rule data source" },
42 | "title": { "value": "Hero for query string" },
43 | "text": {
44 | "value": "This is the data source set on the rule that depends on the query string parameter uniform-parameter being set."
45 | }
46 | },
47 | "global-default-hero": {
48 | "subtitle": { "value": "Rendering data source" },
49 | "title": { "value": "Hero set on rendering" },
50 | "text": { "value": "This is the data source set on the rendering." }
51 | }
52 | }
53 | },
54 | "tracking": {
55 | "campaigns": {
56 | "f5e4b73b-9843-44db-9ce7-bc372440d74b": { "name": "Test Campaign" }
57 | },
58 | "events": {
59 | "35f570b4-5ea6-4313-9653-3787966ea377": {
60 | "name": "Business Travel Page Viewed",
61 | "points": 0.0
62 | }
63 | },
64 | "profiles": {
65 | "24dff2cf-b30a-4b75-8967-2fe3ded82271": {
66 | "name": "Focus",
67 | "keys": {
68 | "03379af5-f1ae-4610-b15b-4c7f1032b464": {
69 | "key": "Background",
70 | "value": 1.0
71 | },
72 | "5fdd9829-e689-454d-9abc-8f95ae68744c": {
73 | "key": "Practical",
74 | "value": 2.0
75 | },
76 | "f5652c06-676b-4e12-a9d0-06d000e5f1c8": {
77 | "key": "Process",
78 | "value": 3.0
79 | },
80 | "b32bfacc-3494-4127-b050-cf50078e2b4c": {
81 | "key": "Scope",
82 | "value": 4.0
83 | }
84 | },
85 | "total": 10.0
86 | }
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/data/routes/about/en.yml:
--------------------------------------------------------------------------------
1 | template: CommonPage
2 | name: About
3 | placeholders:
4 | uniform-jss-kit-content:
5 | - componentName: Hero
6 | id: global-default-hero-2
7 | uid: 70886a78-86e8-48b8-b023-46d0816c8400
8 | fields:
9 | title: About
10 | subtitle: Uniform
11 | text: Connecting the two worlds of Enterprise DXP and JAMstack
12 | image: https://images.unsplash.com/photo-1472148083604-64f1084980b9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1200&q=80
13 |
--------------------------------------------------------------------------------
/src/data/routes/en.yml:
--------------------------------------------------------------------------------
1 | # This is a route layout definition.
2 | # The route definition defines which Sitecore components are present on a route,
3 | # what their content data is, and which _placeholder_ they are placed in.
4 |
5 | # This particular route definition is for the home route - '/', so it defines the
6 | # components shown on the initial page of the app.
7 |
8 | # You may use equivalent JSON files instead of YAML if you prefer;
9 | # however YAML is simpler to read and allows comments like this one :)
10 |
11 | # Setting an ID is optional, but it will allow referring to this item in internal links
12 | # the ID can be a app-wide-unique string, or a GUID value.
13 | id: home-page
14 | name: Home
15 | template: CommonPage
16 | icon: Network/16x16/home.png
17 |
18 | # Define the page layout starting at the root placeholder - in this case, 'uniform-jss-kit-content'
19 | # root placeholder names are defined in the package.json config section (required for Sitecore deployment)
20 | placeholders:
21 | uniform-jss-kit-content:
22 | - id: global-default-hero
23 | uid: 70886a78-86e8-48b8-b023-46d0816c8400
24 |
--------------------------------------------------------------------------------
/src/deploy.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | dotenv.config();
3 | const { createPublishProvider } = require('@uniformdev/publishing-all');
4 | createPublishProvider().deploy('out');
5 |
--------------------------------------------------------------------------------
/src/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleNameMapper: {
3 | '^@/(.*)$': '/$1',
4 | '^~/(.*)$': '/$1',
5 | '^vue$': 'vue/dist/vue.common.js',
6 | },
7 | moduleFileExtensions: ['js', 'vue', 'json'],
8 | transform: {
9 | '^.+\\.js$': 'babel-jest',
10 | '.*\\.(vue)$': 'vue-jest',
11 | },
12 | collectCoverage: true,
13 | collectCoverageFrom: ['/components/**/*.vue', '/pages/**/*.vue'],
14 | };
15 |
--------------------------------------------------------------------------------
/src/layouts/README.md:
--------------------------------------------------------------------------------
1 | # LAYOUTS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your Application Layouts.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
8 |
--------------------------------------------------------------------------------
/src/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
--------------------------------------------------------------------------------
/src/layouts/error.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Page not found
4 |
5 | An error occurred: status {{ error.statusCode }}. Check the server console / logs.
6 |
7 |
8 | Site: {{ siteName }}
9 |
10 | Language: {{ language }}
11 |
12 |
Go to Home page
13 |
14 |
15 |
16 |
37 |
--------------------------------------------------------------------------------
/src/lib/layoutServiceUtils.js:
--------------------------------------------------------------------------------
1 | import { dataApi } from '@sitecore-jss/sitecore-jss-vue';
2 |
3 | export function createLayoutServiceClient(config, { nuxtContext } = {}) {
4 | return {
5 | getRouteData: (route, language) => getRouteData(route, language, config, nuxtContext),
6 | };
7 | }
8 |
9 | function getRouteData(route, language, config, nuxtContext) {
10 | const fetchOptions = {
11 | // NOTE: we want to proxy client-side layout service requests through the Nuxt server so
12 | // that cookies are properly maintained. Therefore, we set the Layout Service
13 | // config `host` value to empty when in a client context.
14 | // The Nuxt server or hosting platform (for static sites) will then handle requests for `/sitecore/api/layout/...`
15 | layoutServiceConfig: { host: process.client ? '' : config.sitecoreApiHost },
16 | querystringParams: {
17 | sc_lang: language,
18 | sc_apikey: config.sitecoreApiKey,
19 | sc_site: config.sitecoreSiteName,
20 | },
21 | fetcher: nuxtContext.$jss.dataFetcher,
22 | };
23 |
24 | // NOTE: using `!process.client` will help ensure dead code elimination during minification for the client bundle.
25 | if (
26 | !process.client &&
27 | nuxtContext?.app?.context?.isStatic &&
28 | nuxtContext?.app?.getExportRouteDataContext
29 | ) {
30 | // export mode
31 | // Fetch layout data from Layout Service, then write the data to disk.
32 | const { exportRouteDataWriter } = nuxtContext.app.getExportRouteDataContext();
33 | let apiData;
34 | return fetchFromApi(route, fetchOptions)
35 | .then((data) => {
36 | apiData = data;
37 | return exportRouteDataWriter(route, language, data);
38 | })
39 | .then(() => apiData);
40 | } else if (process.env.NODE_ENV === 'production') {
41 | // If we're in production mode and not running a static site, then fetch directly from API.
42 | if (!process.static) {
43 | return fetchFromApi(route, fetchOptions);
44 | }
45 | // production mode (i.e. the app is "running" somewhere)
46 | // Attempt to fetch layout data from disk, and fall back to Layout Service if disk fetch returns 404.
47 | return fetchFromDisk(route, language, fetchOptions.fetcher).catch((err) => {
48 | if (err.response && err.response.status === 404) {
49 | return fetchFromApi(route, fetchOptions);
50 | }
51 | console.error(err);
52 | });
53 | } else {
54 | // development mode
55 | // Fetch layout data from Layout Service
56 | return fetchFromApi(route, fetchOptions);
57 | }
58 | }
59 |
60 | // note: if `str` is undefined, no leading slash will be applied/returned.
61 | function ensureLeadingSlash(str) {
62 | if (str && !str.startsWith('/')) {
63 | return `/${str}`;
64 | }
65 | return str;
66 | }
67 |
68 | function fetchFromDisk(route, language, dataFetcher) {
69 | let formattedRoute = ensureLeadingSlash(route);
70 | if (formattedRoute === '/') {
71 | formattedRoute = '/home';
72 | }
73 |
74 | const filePath = `/data${formattedRoute}/${language}.json`;
75 | return dataFetcher(filePath).then((response) => {
76 | // note: `dataFetcher` returns the parsed response, but we're only interested in
77 | // the `data` property, which is what is returned by the `dataApi.fetchRouteData` function.
78 | return response.data;
79 | });
80 | }
81 |
82 | function fetchFromApi(route, fetchOptions) {
83 | // Layout Service may resolve items (routes) incorrectly without a leading slash. This is
84 | // particularly true for nested routes, e.g. home/sub1/sub2/sub3. Therefore, we ensure
85 | // that the item/route being requested has a leading slash.
86 | const formattedRoute = ensureLeadingSlash(route);
87 |
88 | return dataApi.fetchRouteData(formattedRoute, fetchOptions);
89 | }
90 |
--------------------------------------------------------------------------------
/src/modules/express/initialize.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const compression = require('compression');
3 |
4 | module.exports = initialize;
5 |
6 | // Nuxt uses `connect` as a server when starting a Nuxt app.
7 | // This module "hacks" around the use of connect by injecting an `express` server
8 | // instance into the Nuxt server instance just prior to server startup, i.e.
9 | // in the `render:before` hook.
10 | function initialize(moduleOptions) {
11 | // Nuxt config is available via `this.options`
12 | const nuxtConfig = this.options;
13 |
14 | this.nuxt.hook('render:before', async (nuxtServer) => {
15 | // `connect` has a `stack` property that Nuxt uses for removing/replacing middleware.
16 | // `express` has a similar property but it is on the Express `router` object that is attached
17 | // to an express instance.
18 | // For compatibility, we use the Express `lazyrouter` method to ensure the `server._router`
19 | // property is initialized and defined. Then we add a `stack` property to the Express server
20 | // object and assign the Express router.stack property.
21 | // This seems to be sufficient to satisfy the Nuxt usage of connect.
22 | const expressApp = express();
23 | expressApp.lazyrouter();
24 | expressApp.stack = expressApp._router.stack;
25 |
26 | // Add compression middleware.
27 | expressApp.use(compression());
28 |
29 | // Disable built-in Nuxt/connect compressor. Nuxt only activates compression for production mode.
30 | // We want to enable compression regardless of mode for consistency. In particular, there may
31 | // be other middleware that modify the response headers and content length.
32 | nuxtConfig.render = {
33 | ...nuxtConfig.render,
34 | compressor: null,
35 | };
36 |
37 | // Assign the Express server/app to the `app` property on the Nuxt server instance. This will
38 | // replace the default `connect` app created by Nuxt.
39 | nuxtServer.app = expressApp;
40 | });
41 | }
42 |
--------------------------------------------------------------------------------
/src/modules/jss/data-fetcher/axios-data-fetcher-plugin.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { installDataFetcherPlugin } from '~/modules/jss/data-fetcher/install-data-fetcher-plugin';
3 |
4 | export default function initialize(context) {
5 | // Create and install the plugin.
6 | installDataFetcherPlugin(dataFetcher, context);
7 | }
8 |
9 | /**
10 | * Implements a data fetcher using Axios - replace with your favorite
11 | * SSR-capable HTTP or fetch library if you like. See HttpJsonFetcher type
12 | * in sitecore-jss library for implementation details/notes.
13 | * @param {string} url The URL to request; may include query string
14 | * @param {any} data Optional data to POST with the request.
15 | */
16 | function dataFetcher(url, data, options) {
17 | return axios({
18 | url,
19 | method: data ? 'POST' : 'GET',
20 | data,
21 | // note: axios needs to use `withCredentials: true` in order for Sitecore cookies to be included in CORS requests
22 | // which is necessary for analytics and such
23 | withCredentials: true,
24 | ...options,
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/src/modules/jss/data-fetcher/initialize.js:
--------------------------------------------------------------------------------
1 | const nodePath = require('path');
2 |
3 | module.exports = initialize;
4 |
5 | function initialize(moduleOptions) {
6 | switch (moduleOptions.dataFetcherType) {
7 | case 'axios':
8 | default: {
9 | this.addPlugin(nodePath.resolve(__dirname, 'axios-data-fetcher-plugin.js'));
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/modules/jss/data-fetcher/install-data-fetcher-plugin.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export function installDataFetcherPlugin(dataFetcher, nuxtContext) {
4 | // Define the plugin.
5 | const plugin = {
6 | install: (VueInstance) =>
7 | install({
8 | dataFetcher,
9 | VueInstance,
10 | nuxtContext,
11 | }),
12 | key: 'SitecoreJssDataFetcherPlugin',
13 | };
14 |
15 | // Note: we don't use the built-in Nuxt plugin "inject" functionality because it
16 | // will assign the plugin to the Vue instance using the `key` property. However, we
17 | // want all JSS-related plugins to be accessible via the `$jss` property, so we
18 | // need to use the Vue-provided `Vue.use()` syntax to install JSS plugins.
19 | Vue.use(plugin);
20 | }
21 |
22 | function install({ dataFetcher, VueInstance, nuxtContext }) {
23 | if (nuxtContext.$jss && nuxtContext.$jss.dataFetcher) {
24 | console.log('JSS Data Fetcher plugin already installed.', nuxtContext.$jss.dataFetcher);
25 | return;
26 | }
27 |
28 | // Define properties that will be available in the `$jss` object.
29 | // Note: these properties are _not_ intended to be reactive, which is why they are
30 | // all functions - to help make it clear to devs that the properties are not reactive.
31 | const pluginProps = {
32 | dataFetcher,
33 | };
34 |
35 | // Who knows what other JSS plugins are doing? So be kind and merge our plugin props
36 | // with any existing `$jss` object properties.
37 | nuxtContext.$jss = Object.assign(nuxtContext.$jss || {}, pluginProps);
38 | nuxtContext.app.$jss = Object.assign(nuxtContext.app.$jss || {}, pluginProps);
39 | VueInstance.prototype.$jss = Object.assign(VueInstance.prototype.$jss || {}, pluginProps);
40 | }
41 |
--------------------------------------------------------------------------------
/src/modules/jss/disconnected-mode/getDisconnectedModeMiddlewares.js:
--------------------------------------------------------------------------------
1 | const serveStatic = require('serve-static');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const {
5 | createDisconnectedDictionaryService,
6 | ManifestManager,
7 | createDisconnectedLayoutService,
8 | } = require('@sitecore-jss/sitecore-jss-dev-tools');
9 |
10 | module.exports = {
11 | getDisconnectedModeMiddlewares,
12 | };
13 |
14 | async function getDisconnectedModeMiddlewares({ jssConfig }) {
15 | const touchToReloadFilePath = path.resolve(path.join(__dirname, '../../../temp/config.js'));
16 |
17 | const options = {
18 | appRoot: path.join(__dirname, '../../../'),
19 | appName: jssConfig.jssAppName,
20 | watchPaths: [path.join(__dirname, '../../../data')],
21 | language: jssConfig.defaultLanguage,
22 | onManifestUpdated: (manifest) => {
23 | // if we can resolve the config file, we can alter it to force reloading the app automatically
24 | // instead of waiting for a manual reload. We must materially alter the _contents_ of the file to trigger
25 | // an actual reload, so we append "// reloadnow" to the file each time. This will not cause a problem,
26 | // since every build regenerates the config file from scratch and it's ignored from source control.
27 | if (fs.existsSync(touchToReloadFilePath)) {
28 | const currentFileContents = fs.readFileSync(touchToReloadFilePath, 'utf8');
29 | const newFileContents = `${currentFileContents}\n// reloadnow`;
30 | fs.writeFileSync(touchToReloadFilePath, newFileContents, 'utf8');
31 |
32 | console.log('Manifest data updated. Reloading the browser.');
33 | } else {
34 | console.log('Manifest data updated. Refresh the browser to see latest content!');
35 | }
36 | },
37 | };
38 |
39 | // the manifest manager maintains the state of the disconnected manifest data during the course of the dev run
40 | // it provides file watching services, and language switching capabilities
41 | const manifestManager = new ManifestManager({
42 | appName: options.appName,
43 | rootPath: options.appRoot,
44 | watchOnlySourceFiles: options.watchPaths,
45 | requireArg: options.requireArg,
46 | sourceFiles: options.sourceFiles,
47 | });
48 |
49 | return manifestManager.getManifest(options.language).then((manifest) => {
50 | // creates a fake version of the Sitecore Layout Service that is powered by your disconnected manifest file
51 | const layoutService = createDisconnectedLayoutService({
52 | manifest,
53 | manifestLanguageChangeCallback: manifestManager.getManifest,
54 | customizeContext: options.customizeContext,
55 | customizeRoute: options.customizeRoute,
56 | customizeRendering: options.customizeRendering,
57 | });
58 |
59 | // creates a fake version of the Sitecore Dictionary Service that is powered by your disconnected manifest file
60 | const dictionaryService = createDisconnectedDictionaryService({
61 | manifest,
62 | manifestLanguageChangeCallback: manifestManager.getManifest,
63 | });
64 |
65 | // set up live reloading of the manifest when any manifest source file is changed
66 | manifestManager.setManifestUpdatedCallback((newManifest) => {
67 | layoutService.updateManifest(newManifest);
68 | dictionaryService.updateManifest(newManifest);
69 | if (options.onManifestUpdated) {
70 | options.onManifestUpdated(newManifest);
71 | }
72 | });
73 |
74 | // define our disconnected service middlewares
75 | return [
76 | { path: '/assets', handler: serveStatic(path.join(options.appRoot, 'assets')) },
77 | { path: '/data/media', handler: serveStatic(path.join(options.appRoot, 'data/media')) },
78 | { path: '/sitecore/api/layout/render', handler: layoutService.middleware },
79 | {
80 | path: '/sitecore/api/jss/dictionary/:appName/:language',
81 | handler: dictionaryService.middleware,
82 | },
83 | ];
84 | });
85 | }
86 |
--------------------------------------------------------------------------------
/src/modules/jss/disconnected-mode/initialize.js:
--------------------------------------------------------------------------------
1 | // https://nuxtjs.org/guide/modules#write-a-basic-module
2 | const { getDisconnectedModeMiddlewares } = require('./getDisconnectedModeMiddlewares');
3 | const { getConfig } = require('../../../temp/config');
4 |
5 | module.exports = initialize;
6 |
7 | async function initialize(moduleOptions) {
8 | if (!moduleOptions.enabled) {
9 | return;
10 | }
11 |
12 | const middlewares = await getDisconnectedModeMiddlewares({
13 | jssConfig: getConfig(),
14 | });
15 |
16 | middlewares.forEach((middleware) => {
17 | this.addServerMiddleware(middleware);
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/src/modules/jss/i18n/module.js:
--------------------------------------------------------------------------------
1 | const nodePath = require('path');
2 |
3 | module.exports = initialize;
4 |
5 | function initialize(moduleOptions) {
6 | if (!moduleOptions.enabled) {
7 | return;
8 | }
9 |
10 | this.addPlugin(nodePath.resolve(__dirname, 'sitecore-jss-i18n-plugin.js'));
11 | }
12 |
--------------------------------------------------------------------------------
/src/modules/jss/i18n/sitecore-jss-i18n-plugin.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | export default function initialize(nuxtContext) {
4 | if (!nuxtContext.$jss || !nuxtContext.$jss.dataFetcher) {
5 | throw new Error(
6 | 'Unable to initialize the JSS i18n plugin. No `dataFetcher` instance was available. Ensure that a JSS `dataFetcher` module or plugin has been installed and initialized.'
7 | );
8 | }
9 | if (!nuxtContext.$jss || !nuxtContext.$jss.getRuntimeConfig) {
10 | throw new Error(
11 | 'Unable to initialize the JSS Tracking API plugin. No `getRuntimeConfig` method was available. Ensure that a JSS `getRuntimeConfig` plugin has been installed and initialized.'
12 | );
13 | }
14 |
15 | // Initialize i18n store first (so we can access the store during the init process).
16 | // Then resolve the state value (which may require an async fetch to retrieve dictionary data).
17 | // Then set the store value.
18 | // And finally, install the plugin. We install the plugin last because of the possible
19 | // async fetch to retrieve dictionary data.
20 | initializeI18nStore(nuxtContext.store);
21 |
22 | return resolveStateValue(nuxtContext)
23 | .then((state) => {
24 | // After successful i18n init, track the i18n values in state
25 | nuxtContext.store.commit('i18n/seti18n', state);
26 | })
27 | .then(() => {
28 | // After successful i18n and store init, install the i18n plugin.
29 | const plugin = {
30 | install: (VueInstance) => install(VueInstance, nuxtContext),
31 | key: 'SitecoreJssi18nextPlugin',
32 | };
33 |
34 | Vue.use(plugin);
35 | });
36 | }
37 |
38 | function initializeI18nStore(store) {
39 | // Create and register a Vuex store for i18n state.
40 | const namespace = 'i18n';
41 |
42 | const storeModule = {
43 | namespaced: true,
44 | state: () => ({ language: '', dictionary: null }),
45 | mutations: {
46 | seti18n(state, { language, dictionary }) {
47 | state.language = language;
48 | state.dictionary = dictionary;
49 | },
50 | },
51 | };
52 |
53 | store.registerModule(namespace, storeModule, {
54 | preserveState: Boolean(store.state[namespace]),
55 | });
56 | }
57 |
58 | async function resolveStateValue(nuxtContext) {
59 | const { req, params, store, $jss } = nuxtContext;
60 |
61 | const jssConfig = $jss.getRuntimeConfig();
62 |
63 | // `req` is only defined for SSR.
64 | // If in SSR or static export, we need to initialize i18n using the route language parameter,
65 | // using the default language as fallback.
66 | if (req || nuxtContext?.app?.context?.isStatic) {
67 | // If the request has `jssData.viewBag` properties for language and dictionary, then
68 | // use the values to populate the state. This is primarily for JSS Rendering Host support.
69 | // NOTE: `req` is undefined during export/generate, so be sure to null-check it.
70 | if (req?.jssData?.viewBag?.language && req?.jssData?.viewBag?.dictionary) {
71 | return Promise.resolve({
72 | language: req.jssData.viewBag.language,
73 | dictionary: req.jssData.viewBag.dictionary,
74 | });
75 | }
76 |
77 | // Else assume we need to resolve the language and fetch dictionary data.
78 | const resolvedLanguage = params.lang || jssConfig.defaultLanguage;
79 | return fetchDictionaryData(resolvedLanguage, nuxtContext).then((dictionary) => {
80 | return {
81 | language: resolvedLanguage,
82 | dictionary,
83 | };
84 | });
85 | } else {
86 | // If not SSR, then assume we're hydrating from SSR state and use the language and dictionary
87 | // provided via SSR state.
88 | return Promise.resolve({
89 | language: store.state.i18n.language,
90 | dictionary: store.state.i18n.dictionary,
91 | });
92 | }
93 | }
94 |
95 | function install(VueInstance, nuxtContext) {
96 | if (nuxtContext.$jss?.getCurrentLanguage) {
97 | console.log('JSS i18n plugin already installed.');
98 | return;
99 | }
100 |
101 | // Define properties that will be available in the `$jss` object.
102 | // Note: these properties are _not_ intended to be reactive, which is why they are
103 | // all functions - to help make it clear to devs that the properties are not reactive.
104 | const props = {
105 | getCurrentLanguage: () => nuxtContext.store.state.i18n.language,
106 | getCurrentDictionary: () => {
107 | return nuxtContext.store.state.i18n.dictionary;
108 | },
109 | translate: (phrase) => nuxtContext.store.state.i18n.dictionary[phrase],
110 | changeLanguage: (newLanguage) => changeLanguage(newLanguage, nuxtContext),
111 | };
112 |
113 | // Who knows what other JSS plugins are doing? So be kind and merge our plugin props
114 | // with any existing `$jss` object properties.
115 | nuxtContext.$jss = Object.assign(nuxtContext.$jss || {}, props);
116 | nuxtContext.app.$jss = Object.assign(nuxtContext.app.$jss || {}, props);
117 | VueInstance.prototype.$jss = Object.assign(VueInstance.prototype.$jss || {}, props);
118 | }
119 |
120 | function changeLanguage(newLanguage, nuxtContext) {
121 | // If the language hasn't changed, don't do anything.
122 | if (newLanguage === nuxtContext.store.state.i18n.language) {
123 | return Promise.resolve(false);
124 | }
125 |
126 | // Ftch new dictionary data and change the i18n state data.
127 | // Then change the app route so that the app can fetch Layout Service data for the new
128 | // language and then re-render.
129 | return fetchDictionaryData(newLanguage, nuxtContext).then((dictionary) => {
130 | nuxtContext.store.commit('i18n/seti18n', {
131 | language: newLanguage,
132 | dictionary,
133 | });
134 |
135 | const newRoute = `/${newLanguage}${
136 | nuxtContext.app.router.currentRoute.params.sitecoreRoute || ''
137 | }`;
138 |
139 | nuxtContext.app.router.push(newRoute);
140 | return true;
141 | });
142 | }
143 |
144 | function fetchDictionaryData(language, nuxtContext) {
145 | if (
146 | nuxtContext.store.state.i18n?.dictionary &&
147 | language === nuxtContext.store.state.i18n.language
148 | ) {
149 | return Promise.resolve(nuxtContext.store.state.i18n.dictionary);
150 | }
151 |
152 | const jssConfig = nuxtContext.$jss.getRuntimeConfig();
153 | const dictionaryServiceUrl = `${jssConfig.sitecoreApiHost}/sitecore/api/jss/dictionary/${jssConfig.jssAppName}/${language}?sc_apikey=${jssConfig.sitecoreApiKey}&sc_site=${jssConfig.sitecoreSiteName}`;
154 |
155 | return nuxtContext.$jss.dataFetcher(dictionaryServiceUrl).then((response) => {
156 | if (response.data && response.data.phrases) {
157 | return response.data.phrases;
158 | }
159 | return null;
160 | });
161 | }
162 |
--------------------------------------------------------------------------------
/src/modules/jss/rendering-host/initialize.js:
--------------------------------------------------------------------------------
1 | const bodyParser = require('body-parser');
2 | const cors = require('cors');
3 | const WebpackRequireFromPlugin = require('webpack-require-from');
4 | const { getJssRenderingHostMiddleware } = require('./nuxt-jss-rendering-host-middleware');
5 | // TODO: attach jss config to Nuxt config or module options
6 | const { getConfig: getJssConfigRenderingHost } = require('../../../temp/jss-config-rendering-host');
7 |
8 | module.exports = initialize;
9 |
10 | function initialize(moduleOptions) {
11 | if (!moduleOptions.enabled) {
12 | return;
13 | }
14 |
15 | // In Nuxt modules, the current Nuxt instance is available via `this.nuxt`.
16 | const nuxtApp = this.nuxt;
17 |
18 | // In Nuxt modules, Nuxt config (nuxt.config.js) is available via `this.options`.
19 | const nuxtConfig = this.options;
20 |
21 | setupMiddleware(nuxtApp, moduleOptions);
22 | configureWebpack(nuxtConfig);
23 | configureHooks(nuxtApp, nuxtConfig, moduleOptions.resolveRenderingHostPublicUrl);
24 | }
25 |
26 | function setupMiddleware(nuxtApp, { renderingHostListenRoute }) {
27 | const jssConfig = getJssConfigRenderingHost();
28 | if (!jssConfig) {
29 | console.warn(
30 | 'JSS config was not provided to JSS rendering host, therefore JSS rendering host middleware is disabled.'
31 | );
32 | return;
33 | }
34 |
35 | const renderingHostConfig = {
36 | app: nuxtApp,
37 | jssConfig,
38 | };
39 |
40 | // We use the `render:setupMiddleware` Nuxt hook instead of `this.addMiddleware` because using
41 | // `this.addMiddleware` results in a `server.use(middleware)` call. However, for rendering host
42 | // we specifically only want to add the middleware for POST requests to `renderingHostListenRoute`.
43 | // NOTE: the `render:setupMiddleware` hook is fired before any other middleware are registered.
44 | nuxtApp.hook('render:setupMiddleware', async (server) => {
45 | server.use(cors());
46 |
47 | // Setup body parsing middleware for incoming POST requests.
48 | const jsonBodyParser = bodyParser.json({
49 | limit: '2mb',
50 | });
51 |
52 | // Setup the JSS rendering host route.
53 | // By default, Sitecore will use the `/jss-render` route, but this can be configured via
54 | // JSS app config, e.g. ``.
55 | // So we allow `renderingHostRoute` to be configured via `moduleOptions` so that devs
56 | // can configure the middleware to match the path configured in Sitecore.
57 | const resolvedRenderingHostListenRoute = renderingHostListenRoute || '/jss-render';
58 | server.post(
59 | resolvedRenderingHostListenRoute,
60 | jsonBodyParser,
61 | getJssRenderingHostMiddleware(renderingHostConfig)
62 | );
63 | });
64 | }
65 |
66 | function configureWebpack(nuxtConfig) {
67 | // webpack-require-from plugin allows us to specify where the client-side webpack `publicPath`
68 | // property value should be read from. For JSS rendering host requests, the actual `publicPath`
69 | // property we need isn't known until runtime, so we inject the `__nuxt_public_path__` property
70 | // into the outgoing HTML for the rendering host request (see the `configureHooks` method below).
71 | // Then we use the webpack-require-from plugin to instruct Webpack to read from the
72 | // `window.__nuxt_public_path__` variable.
73 | // This allows requests for async webpack chunks (and anything else dependent on webpack publicPath)
74 | // to succeed because they'll use the absolute URL to our Nuxt server instead of a relative path
75 | // that will resolve to the Sitecore server.
76 | // NOTE: we use the `suppressErrors` option so that if `__nuxt_public_path__` is undefined on
77 | // the client, no console errors will be shown. This will happen for requests that are not JSS
78 | // rendering host requests.
79 | const webpackRequireFrom = new WebpackRequireFromPlugin({
80 | variableName: '__nuxt_public_path__',
81 | suppressErrors: true,
82 | });
83 | nuxtConfig.build.plugins = [...nuxtConfig.build.plugins, webpackRequireFrom];
84 |
85 | // // Customize the `hotMiddleware` configuration so that requests to the HMR endpoint
86 | // // can succeed in both local development and in JSS rendering host.
87 | // // TODO: check https://github.com/nuxt/nuxt.js/pull/7318 to see if any changes need to be made
88 | // // to the below config.
89 | nuxtConfig.build.hotMiddleware = {
90 | ...nuxtConfig.build.hotMiddleware,
91 | // The `path` value below needs to match what Nuxt/webpack will emit as the URL for the webpack HMR script.
92 | // The `//` is intentional due to how Nuxt invokes the hotmiddleware. By default, Nuxt will
93 | // prepend the client path with `router.base`, which has a default value of `/`. Then the
94 | // `publicPath` gets prepended to the path, resulting in `{host}/_nuxt//__webpack_hmr/client`.
95 | path: '/_nuxt//__webpack_hmr/client',
96 | client: {
97 | ...nuxtConfig.build.hotMiddleware.client,
98 | // Set to true to use webpack publicPath as prefix of path.
99 | dynamicPublicPath: true,
100 | },
101 | };
102 | }
103 |
104 | function configureHooks(nuxtApp, nuxtConfig, resolveRenderingHostPublicUrl) {
105 | // When the rendering host middleware executes, it renders the Nuxt app and sends the generated
106 | // HTML back to Sitecore for serving. By default, all Nuxt/Webpack-generated assets are referenced
107 | // via relative URLs, e.g. `/_nuxt/runtime.js`. However, because the HTML will be served by Sitecore,
108 | // the HTML _must_ use absolute URLs to reference Nuxt/Webpack-generated assets,
109 | // e.g. `http://localhost:3000/_nuxt/runtime.js`, otherwise the browser won't be able to resolve the URLs.
110 | //
111 | // Nuxt/Webpack provide a setting named `publicPath` that normally solves this problem. If you provide
112 | // a value for `publicPath` it will be prepended to any URLs for Nuxt/Webpack-generated assets.
113 | // However, you can only set `publicPath` at build time or app start time. This is a problem for
114 | // JSS Rendering Host because the URL for the rendering host may not be known at build/start time
115 | // and the URL may be different from the URL that the Nuxt web server is running on.
116 | //
117 | // Therefore, we want to be able to both declare a `publicPath` and prepend asset URLs on a per-request basis.
118 | // To do this, we use a combination of webpack config modification (in the configureWebpack method above) as
119 | // well as intercepting and tag rendering.
120 |
121 | // The issue stems from this function in the `vue-ssr-renderer` package:
122 | // https://github.com/vuejs/vue/blob/e8ca21ff1d7af92649902952c97418ad3ffd014f/src/server/template-renderer/index.js#L86
123 | // That function binds the `renderStyles` and `renderScripts` methods of the Vue SSR `renderContext`
124 | // object to the `TemplateRenderer` instance, and we can't get access to the `TemplateRenderer` instance
125 | // at runtime, and therefore can't change the `TemplateRenderer.publicPath` property dynamically.
126 |
127 | // The `vue-renderer:ssr:context` hook fires just after the Nuxt app is rendered.
128 | // At this point, the html of the app _body_ has been rendered to string.
129 | // However, , and preload/prefetch elements have not been
130 | // rendered to the element yet. So, we can intercept the calls that render
131 | // the and tags and use regex replacement to prepend asset URLs with
132 | // the rendering host public URL.
133 | nuxtApp.hook('vue-renderer:ssr:context', async (renderContext) => {
134 | if (!renderContext.isJssRenderingHostRequest) {
135 | return;
136 | }
137 |
138 | // If there is a `jssConfig` attached to the `req` object, assign it to the `renderContext.nuxt` object.
139 | // This makes the `jssConfig` available as a property on the SSR state object that is serialized to
140 | // the HTML document via `__NUXT__`.
141 | // This behavior useful for ensuring the rendering host JSS config is available to client code and we can
142 | // use the rendering host JSS config to override the runtime config values when necessary.
143 | if (renderContext.req.jssConfig) {
144 | renderContext.nuxt.jssConfig = renderContext.req.jssConfig;
145 | }
146 |
147 | const publicPath = resolveRenderingHostPublicUrl();
148 |
149 | // Prepend rendering host public path to `src` values.
150 | const originalRenderScripts = renderContext.renderScripts;
151 | renderContext.renderScripts = () => {
152 | const scripts = originalRenderScripts();
153 |
154 | const newScripts = scripts.replace(
155 | /(`;
194 |
195 | templateParams.HEAD += publicPathScript;
196 | });
197 | }
198 |
--------------------------------------------------------------------------------
/src/modules/jss/rendering-host/nuxt-jss-rendering-host-middleware.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getJssRenderingHostMiddleware,
3 | };
4 |
5 | function getJssRenderingHostMiddleware({ app, jssConfig } = {}) {
6 | return async function middleware(req, res) {
7 | req.setTimeout(36000, () => {
8 | console.error('request timed out');
9 | });
10 |
11 | try {
12 | const jssData = resolveJssData(req);
13 |
14 | // Server rendering functions expect `GET` requests, but we're handling a `POST` request.
15 | // so change the incoming request method.
16 | req.method = 'GET';
17 | // nuxt.js reads from the req.url property, so set it accordingly
18 | req.url = jssData.renderPath;
19 |
20 | console.log('rendering host handling request', req.url);
21 |
22 | // Allows the app to easily determine whether or not it is being rendered via JSS rendering host.
23 | req.isJssRenderingHostRequest = true;
24 | // Attach the parsed JSS data as an arbitrary property on the `req` object
25 | req.jssData = jssData;
26 | // Attach the JSS config values. These should be the _remote_ `sitecoreApiHost`, `sitecoreApiKey`,
27 | // and `sitecoreSiteName` values, not necessarily the same as the generated values from `temp/config.js`.
28 | // This allows us to override the generated config values at runtime. Otherwise, when the app is
29 | // served by the Sitecore server, the JSS app may be making layout service/dictionary service requests
30 | // to the `sitecoreApiHost` value specified in `temp/config.js`. And that value could be,
31 | // for example, `localhost:3000` if running the app in disconnected mode. That could result in
32 | // CORS errors and unexpected data. So, we provide the remote/"connected" mode config values so the app can
33 | // use those if/when necessary.
34 | if (jssConfig) {
35 | req.jssConfig = jssConfig;
36 | }
37 |
38 | const routeInfo = {
39 | pathname: jssData.renderPath,
40 | };
41 |
42 | // render app and return
43 | // renderResult is an object: { html, error, redirected }
44 | // The object passed as the second argument to `renderRoute` will be added to the
45 | // Nuxt SSR `renderContext` object. The `req` and `res` properties are required, but
46 | // you can also add custom properties, e.g. `isJssRenderingHostRequest`, that will
47 | // be available anywhere you have access to the SSR `renderContext` object.
48 | const renderResult = await app.renderRoute(routeInfo.pathname, {
49 | req,
50 | res,
51 | isJssRenderingHostRequest: true,
52 | });
53 |
54 | // TODO: need to handle 404 and/or redirect
55 | if (renderResult.error) {
56 | console.error(renderResult.error);
57 | }
58 | // TODO: need to handle 404 and/or redirect
59 | if (renderResult.redirected) {
60 | console.log('redirected', renderResult.redirected);
61 | }
62 |
63 | res.send({
64 | html: renderResult.html,
65 | status: 200,
66 | redirect: '',
67 | });
68 | } catch (err) {
69 | console.error(err);
70 | res.send({
71 | html: `JSS app rendering error: ${err}`,
72 | status: 500,
73 | redirect: '',
74 | });
75 | }
76 | };
77 | }
78 |
79 | function resolveJssData(req) {
80 | // We assume req.body has already been parsed as JSON via something like `body-parser` middleware.
81 | const invocationInfo = req.body;
82 |
83 | // By default, the JSS server invokes this route with the following body data structure:
84 | // {
85 | // id: 'JSS app name',
86 | // args: ['route path', 'serialized layout data object', 'serialized viewbag object'],
87 | // functionName: 'renderView',
88 | // moduleName: 'server.bundle'
89 | // }
90 |
91 | const result = {
92 | route: null,
93 | viewBag: null,
94 | renderPath: '',
95 | };
96 |
97 | if (!invocationInfo || !invocationInfo.args || !Array.isArray(invocationInfo.args)) {
98 | return result;
99 | }
100 |
101 | result.renderPath = invocationInfo.args[0];
102 |
103 | if (invocationInfo.args.length > 0 && invocationInfo.args[1]) {
104 | result.route = tryParseJson(invocationInfo.args[1]);
105 | }
106 |
107 | if (invocationInfo.args.length > 1 && invocationInfo.args[2]) {
108 | result.viewBag = tryParseJson(invocationInfo.args[2]);
109 | }
110 |
111 | return result;
112 | }
113 |
114 | function tryParseJson(jsonString) {
115 | try {
116 | const json = JSON.parse(jsonString);
117 | // handle non-exception-throwing cases
118 | if (json && typeof json === 'object' && json !== null) {
119 | return json;
120 | }
121 | } catch (e) {
122 | console.error(`error parsing json string '${jsonString}'`, e);
123 | }
124 |
125 | return null;
126 | }
127 |
--------------------------------------------------------------------------------
/src/modules/jss/sitecore-proxy/default-proxy-configuration.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | getProxyConfiguration,
3 | };
4 |
5 | function getProxyConfiguration({
6 | jssConfig,
7 | isDevEnv,
8 | layoutServiceRouteResolver,
9 | doNotProxyPrefixesList = [],
10 | shouldDirectProxyPrefixList = [],
11 | }) {
12 | // These are _prefixes_ for routes that should not be proxied at all.
13 | const doNotProxyList = ['/static', '/fonts'].concat(doNotProxyPrefixesList);
14 |
15 | // These are _prefixes_ for routes that should be directly proxied to Sitecore.
16 | const shouldDirectProxyList = [
17 | '/sitecore/api',
18 | '/api',
19 | '/-/jssmedia',
20 | '/-/media',
21 | '/_/media',
22 | '/_/jssmedia',
23 | '/layouts/system',
24 | ].concat(shouldDirectProxyPrefixList);
25 |
26 | const proxyConfiguration = {
27 | // JSS configuration
28 | jssConfig,
29 | doNotProxyResolver: getDoNotProxyResolver(doNotProxyList),
30 | shouldDirectProxyResolver: getShouldProxyResolver(shouldDirectProxyList),
31 |
32 | // The `layoutServiceRouteResolver` function attempts to resolve a matching app route for the current request.
33 | // The match should return an object in the format: { params: { sitecoreRoute, language } }
34 | // Where `sitecoreRoute` is the value that will be used for the `item` parameter in a Layout Service request,
35 | // and `language` is the value that will be used for the `sc_lang` parameter in a Layout Service request.
36 | layoutServiceRouteResolver,
37 |
38 | options: {
39 | modifyCookies: getModifyCookies(isDevEnv),
40 | modifyLayoutServiceData: getModifyLayoutServiceData(),
41 | handleProxyRedirect: getHandleProxyRedirect(jssConfig),
42 | },
43 | };
44 |
45 | return proxyConfiguration;
46 | }
47 |
48 | function getDoNotProxyResolver(doNotProxyList) {
49 | // This function determines whether the proxy middleware should handle the request or not.
50 | // This function is executed before any other actions are performed so the request can
51 | // fall through to the next middleware as soon as possible.
52 | return (req) => {
53 | if (req.method !== 'GET' && req.method !== 'POST') {
54 | return true;
55 | }
56 | const compareUrl = req.url.toLowerCase();
57 | const doNotProxy = doNotProxyList.some((prefix) => compareUrl.startsWith(prefix));
58 | return doNotProxy;
59 | };
60 | }
61 |
62 | function getShouldProxyResolver(shouldDirectProxyList) {
63 | // This function determines whether the proxy middleware should directly proxy the request
64 | // to the Sitecore host. Otherwise, it is assumed that the request should be transformed/rewritten
65 | // into a Layout Service request. This allows the proxy middleware to also proxy non-LayoutService
66 | // requests for GraphQL, Sitecore media library, and other Sitecore APIs.
67 | // This function is executed after `doNotProxyResolver` and before `layoutServiceRouteResolver`.
68 | return (req) => {
69 | const compareUrl = req.url.toLowerCase();
70 | return shouldDirectProxyList.some((prefix) => compareUrl.startsWith(prefix));
71 | };
72 | }
73 |
74 | function getModifyCookies(isDevEnv) {
75 | // This function allows you to modify the cookies that will be sent in the outgoing response.
76 | // `cookies` is an array of cookie objects, e.g. { name: '', value: '', domain: '', httpOnly: '', ...otherCookieProperties }
77 | // The `cookies` array and elements should be modified in place, no return value is
78 | // expected from this method.
79 | return (cookies, outgoingServerResponse, proxyResponse, proxyOptions) => {
80 | cookies.forEach((cookie) => {
81 | if (cookie.name === '.AspNet.Cookies') {
82 | if (isDevEnv) {
83 | // In development, we are making requests from an insecure origin, e.g. http://jss.local.dev
84 | // The `.AspNet.Cookies` cookie, however, is flagged as `Secure`, so it won't be set
85 | // for an insecure origin.
86 | // Therefore, when in development mode, we remove the `Secure` property from the cookie.
87 | cookie.secure = false;
88 | }
89 | // We may want to share the .AspNet.Cookies cookie across domains, so we
90 | // set the `domain` value.
91 | // NOTE: this should probably be set on the Sitecore server intead.
92 | // cookie.domain = '.mydomain.com';
93 | }
94 | });
95 | };
96 | }
97 |
98 | function getModifyLayoutServiceData() {
99 | // This function allows you to modify the Layout Service data received from a proxied layout
100 | // service request.
101 | // The `layoutServiceData` argument should be modified in place, no return value is expected
102 | // from this method.
103 | // IMPORTANT: Be mindful of what you do in this method. Modifying a large JSON object could
104 | // be expensive in terms of performance.
105 | // return (
106 | // layoutServiceData,
107 | // incomingRequest,
108 | // outgoingServerResponse,
109 | // proxyResponse,
110 | // proxyOptions
111 | // ) => {};
112 | return undefined;
113 | }
114 |
115 | function getHandleProxyRedirect(jssConfig) {
116 | // This function allows you to handle redirects received from a proxy response.
117 | // return (incomingRequest, outgoingServerResponse, proxyResponse, proxyOptions) => {
118 | // // IMPORTANT: you must pipe or end the response in this method.
119 | // outgoingServerResponse.end();
120 | // };
121 | return undefined;
122 | }
123 |
--------------------------------------------------------------------------------
/src/modules/jss/sitecore-proxy/initialize.js:
--------------------------------------------------------------------------------
1 | // NOTE: for some reason, using ES module syntax (import/export) didn't work for
2 | // this file and subsequent imported files. Had to use CommonJS module syntax
3 | // to prevent "invalid syntax" errors on Nuxt startup. Oddly, all other modules
4 | // seem to work fine with ESM syntax. It wasn't worth investigating further.
5 |
6 | const { getProxyMiddleware } = require('./nuxt-jss-proxy-middleware');
7 | const { getConfig: getJssConfigSitecoreProxy } = require('../../../temp/jss-config-sitecore-proxy');
8 |
9 | module.exports = initialize;
10 |
11 | function initialize(moduleOptions) {
12 | if (!moduleOptions.enabled) {
13 | return;
14 | }
15 |
16 | // In Nuxt modules, `moduleOptions` is the object passed to the module via
17 | // the module declaration in `nuxt.config.js` file.
18 | const isDevEnv = moduleOptions.isDevEnv;
19 |
20 | // In Nuxt modules, Nuxt config (nuxt.config.js) is available via `this.options`.
21 | const nuxtConfig = this.options;
22 |
23 | const middleware = getProxyMiddleware({
24 | jssConfig: getJssConfigSitecoreProxy(),
25 | isDevEnv,
26 | nuxtConfig,
27 | });
28 |
29 | this.addServerMiddleware(middleware);
30 | }
31 |
--------------------------------------------------------------------------------
/src/modules/jss/sitecore-proxy/nuxt-jss-proxy-middleware.js:
--------------------------------------------------------------------------------
1 | const Router = require('vue-router');
2 | const nodePath = require('path');
3 |
4 | const { createSitecoreProxyMiddleware } = require('./create-sitecore-proxy-middleware');
5 | const { getProxyConfiguration } = require('./default-proxy-configuration');
6 |
7 | module.exports = {
8 | getProxyMiddleware,
9 | };
10 |
11 | function getProxyMiddleware({ jssConfig, isDevEnv, nuxtConfig }) {
12 | // Obtain a list of the configured Sitecore routes for the app.
13 | // These route patterns are currently defined in nuxt.config.js via the `router.extendRoutes` property.
14 | const routes = [];
15 | nuxtConfig.router.extendRoutes(routes, nodePath.resolve);
16 | const router = new Router({
17 | mode: 'abstract',
18 | routes,
19 | });
20 |
21 | const layoutServiceRouteResolver = (req) => {
22 | const match = router.match(req.url);
23 | const defaultRoute = {
24 | params: {
25 | sitecoreRoute: '/',
26 | },
27 | };
28 |
29 | if (!match || !match.params) {
30 | return defaultRoute;
31 | }
32 |
33 | if (!match.params.sitecoreRoute) {
34 | match.params.sitecoreRoute = defaultRoute.params.sitecoreRoute;
35 | }
36 |
37 | return match;
38 | };
39 |
40 | const proxyConfig = getProxyConfiguration({
41 | jssConfig,
42 | isDevEnv,
43 | layoutServiceRouteResolver,
44 | doNotProxyPrefixesList: ['/_nuxt', '/__webpack_hmr', '/_loading/sse'],
45 | });
46 |
47 | const proxyMiddleware = createSitecoreProxyMiddleware(proxyConfig);
48 |
49 | return proxyMiddleware;
50 | }
51 |
--------------------------------------------------------------------------------
/src/modules/jss/standard/configure-router.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | configureRouter,
3 | };
4 |
5 | function configureRouter(nuxtConfig) {
6 | const originalExtendRoutes = nuxtConfig.router.extendRoutes;
7 | nuxtConfig.router.extendRoutes = (routes, resolve) => {
8 | // If there is an existing `extendRoutes` config customization, allow it to run.
9 | if (typeof originalExtendRoutes === 'function') {
10 | originalExtendRoutes(routes, resolve);
11 | }
12 |
13 | // We want JSS route patterns to be first in the `routes` array, so we use `unshift` to
14 | // insert them at the beginning of the array.
15 | // We also want the JSS route patterns inserted by most "complex" pattern to least "complex",
16 | // so we order them in reverse in the `routePatterns` array, so that most complex pattern
17 | // will end up being first in the routes array.
18 | const routePatterns = [
19 | '/:sitecoreRoute*',
20 | '/:lang([a-z]{2})/:sitecoreRoute*',
21 | '/:lang([a-z]{2}-[A-Z]{2})/:sitecoreRoute*',
22 | ];
23 | routePatterns.forEach((routePattern) => {
24 | routes.unshift({
25 | path: routePattern,
26 | components: {
27 | default: resolve(__dirname, '../../../pages/_.vue'),
28 | },
29 | });
30 | });
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/modules/jss/standard/initialize.js:
--------------------------------------------------------------------------------
1 | const nodePath = require('path');
2 | const { configureRouter } = require('./configure-router');
3 |
4 | module.exports = initialize;
5 |
6 | function initialize({ dataFetcherType } = {}) {
7 | if (!dataFetcherType) {
8 | throw new Error('JSS Standard module must be initialized with a `dataFetcherType` specified.');
9 | }
10 | // Add JSS data-fetcher module
11 | this.addModule(['~/modules/jss/data-fetcher/initialize', { dataFetcherType }]);
12 |
13 | // Add JSS Placeholder plugin
14 | this.addPlugin(nodePath.resolve(__dirname, 'sitecore-jss-placeholder-plugin.js'));
15 |
16 | // Add JSS Config plugin
17 | this.addPlugin(nodePath.resolve(__dirname, 'sitecore-jss-config-runtime-plugin.js'));
18 |
19 | const nuxtConfig = this.options;
20 |
21 | // Configure JSS router customizations.
22 | configureRouter(nuxtConfig);
23 |
24 | const nuxtApp = this.nuxt;
25 |
26 | // IMPORTANT: Other JSS plugins may be dependent on the data fetcher and/or config plugins,
27 | // so we need to ensure that those plugins are installed before any other
28 | // JSS plugins. Unfortunately, Nuxt modules do not "push" plugins into the plugins
29 | // array. Instead, when calling `this.addPlugin` from a module, a plugin is added
30 | // to the "front" of the array, i.e. `unshift`.
31 | // The current Nuxt-recommended approach for guaranteeing plugin install order is
32 | // to use the `extendPlugins` hook or `extendPlugins` config option:
33 | // https://nuxtjs.org/api/configuration-extend-plugins
34 | nuxtApp.hook('builder:extendPlugins', (plugins) => {
35 | const dataFetcherPluginIndex = plugins.findIndex(
36 | (plugin) => plugin.src.indexOf('data-fetcher-plugin') !== -1
37 | );
38 |
39 | if (dataFetcherPluginIndex !== -1) {
40 | const dataFetcherPlugin = plugins[dataFetcherPluginIndex];
41 | plugins.splice(dataFetcherPluginIndex, 1);
42 | plugins.unshift(dataFetcherPlugin);
43 | }
44 |
45 | const configPluginIndex = plugins.findIndex(
46 | (plugin) => plugin.src.indexOf('jss-config-runtime-plugin') !== -1
47 | );
48 |
49 | if (configPluginIndex !== -1) {
50 | const configPlugin = plugins[configPluginIndex];
51 | plugins.splice(configPluginIndex, 1);
52 | plugins.unshift(configPlugin);
53 | }
54 | });
55 | }
56 |
--------------------------------------------------------------------------------
/src/modules/jss/standard/sitecore-jss-config-runtime-plugin.js:
--------------------------------------------------------------------------------
1 | import { getConfig } from '~/temp/config';
2 |
3 | import Vue from 'vue';
4 |
5 | export default function initialize(nuxtContext) {
6 | // Define the plugin.
7 | const plugin = {
8 | install: (VueInstance) =>
9 | install({
10 | VueInstance,
11 | nuxtContext,
12 | }),
13 | key: 'SitecoreJssConfigRuntimePlugin',
14 | };
15 |
16 | // Note: we don't use the built-in Nuxt plugin "inject" functionality because it
17 | // will assign the plugin to the Vue instance using the `key` property. However, we
18 | // want all JSS-related plugins to be accessible via the `$jss` property, so we
19 | // need to use the Vue-provided `Vue.use()` syntax to install JSS plugins.
20 | Vue.use(plugin);
21 | }
22 |
23 | function install({ VueInstance, nuxtContext }) {
24 | if (nuxtContext.$jss && nuxtContext.$jss.getRuntimeConfig) {
25 | console.log('JSS Runtime Config plugin already installed.', nuxtContext.$jss.getRuntimeConfig);
26 | return;
27 | }
28 |
29 | // Define properties that will be available in the `$jss` object.
30 | // Note: these properties are _not_ intended to be reactive, which is why they are
31 | // all functions - to help make it clear to devs that the properties are not reactive.
32 | const pluginProps = {
33 | getRuntimeConfig: getConfig,
34 | };
35 |
36 | // Who knows what other JSS plugins are doing? So be kind and merge our plugin props
37 | // with any existing `$jss` object properties.
38 | nuxtContext.$jss = Object.assign(nuxtContext.$jss || {}, pluginProps);
39 | nuxtContext.app.$jss = Object.assign(nuxtContext.app.$jss || {}, pluginProps);
40 | VueInstance.prototype.$jss = Object.assign(VueInstance.prototype.$jss || {}, pluginProps);
41 | }
42 |
--------------------------------------------------------------------------------
/src/modules/jss/standard/sitecore-jss-placeholder-plugin.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { SitecoreJssPlaceholderPlugin } from '@sitecore-jss/sitecore-jss-vue';
3 | import componentFactory from '~/temp/componentFactory';
4 |
5 | export default function initialize(context) {
6 | Vue.use(SitecoreJssPlaceholderPlugin, { componentFactory });
7 | }
8 |
--------------------------------------------------------------------------------
/src/modules/jss/tracking-api/initialize.js:
--------------------------------------------------------------------------------
1 | const nodePath = require('path');
2 |
3 | module.exports = initialize;
4 |
5 | function initialize(moduleOptions) {
6 | if (!moduleOptions.enabled) {
7 | return;
8 | }
9 |
10 | this.addPlugin(nodePath.resolve(__dirname, 'sitecore-jss-tracking-api-plugin.js'));
11 | }
12 |
--------------------------------------------------------------------------------
/src/modules/jss/tracking-api/sitecore-jss-tracking-api-plugin.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import { trackingApi } from '@sitecore-jss/sitecore-jss-tracking';
3 |
4 | export default function initialize(nuxtContext) {
5 | if (!nuxtContext.$jss || !nuxtContext.$jss.dataFetcher) {
6 | throw new Error(
7 | 'Unable to initialize the JSS Tracking API plugin. No `dataFetcher` instance was available. Ensure that a JSS `dataFetcher` module or plugin has been installed and initialized.'
8 | );
9 | }
10 | if (!nuxtContext.$jss || !nuxtContext.$jss.getRuntimeConfig) {
11 | throw new Error(
12 | 'Unable to initialize the JSS Tracking API plugin. No `getRuntimeConfig` method was available. Ensure that a JSS `getRuntimeConfig` plugin has been installed and initialized.'
13 | );
14 | }
15 |
16 | const jssConfig = nuxtContext.$jss.getRuntimeConfig();
17 |
18 | // Vue plugins must export a function named 'install'
19 | function install(VueInstance) {
20 | if (nuxtContext.$jss && nuxtContext.$jss.trackingApi) {
21 | console.log('JSS Tracking API plugin already installed.', nuxtContext.$jss.trackingApi);
22 | return;
23 | }
24 |
25 | VueInstance.prototype.$jss = {
26 | // there may be other JSS plugins installed, merge existing properties
27 | ...VueInstance.prototype.$jss,
28 | trackingApi: createTrackingApiClient(jssConfig, nuxtContext.$jss.dataFetcher),
29 | };
30 | }
31 |
32 | const plugin = { install, key: 'SitecoreJssTrackingApiPlugin' };
33 | // Note: we don't use the built-in Nuxt plugin "inject" functionality because it
34 | // will assign the plugin to the Vue instance using the `key` property. However, we
35 | // want all JSS-related plugins to be accessible via the `$jss` property, so we
36 | // need to use the Vue-provided `Vue.use()` syntax to install JSS plugins.
37 | Vue.use(plugin);
38 | }
39 |
40 | function createTrackingApiClient(jssConfig, dataFetcher) {
41 | const trackingApiOptions = {
42 | host: jssConfig.sitecoreApiHost,
43 | querystringParams: {
44 | sc_apikey: jssConfig.sitecoreApiKey,
45 | sc_site: jssConfig.sitecoreSiteName,
46 | },
47 | fetcher: dataFetcher,
48 | };
49 |
50 | const abandonOptions = {
51 | action: 'flush',
52 | ...trackingApiOptions,
53 | };
54 |
55 | return {
56 | trackEvent: (...args) => trackingApi.trackEvent(...args, trackingApiOptions),
57 | abandon: () => trackingApi.trackEvent([], abandonOptions),
58 | };
59 | }
60 |
--------------------------------------------------------------------------------
/src/modules/uniform/disconnected-export/initialize.js:
--------------------------------------------------------------------------------
1 | const nodePath = require('path');
2 | const { ManifestManager } = require('@sitecore-jss/sitecore-jss-dev-tools');
3 | const { getConfig } = require('../../../temp/config');
4 |
5 | module.exports = initialize;
6 |
7 | function initialize(moduleOptions) {
8 | if (!moduleOptions.enabled) {
9 | return;
10 | }
11 |
12 | const jssConfig = getConfig();
13 |
14 | this.options.generate.routes = async () => {
15 | try {
16 | const manifestManager = new ManifestManager({
17 | appName: jssConfig.jssAppName,
18 | rootPath: nodePath.join(__dirname, '..'),
19 | });
20 |
21 | const manifestPathMap = [];
22 | // eslint-disable-next-line no-inner-declarations
23 | function generateManifestPathMap(route, parentPath, params, depth = 0) {
24 | // first/initial route should resolve to `/` instead of a named route.
25 | // i.e. we don't want `/home`, we just want `/` for the first/initial route.
26 | const routeName = depth === 0 ? '' : route.name;
27 | const routePath = `${urlJoin(parentPath, routeName).toLowerCase()}`;
28 |
29 | manifestPathMap.push(routePath);
30 |
31 | // traverse the route tree
32 | if (route.children) {
33 | route.children.forEach((child) => {
34 | generateManifestPathMap(child, routePath, params, (depth += 1));
35 | });
36 | }
37 | }
38 |
39 | const languages = jssConfig.appLanguages;
40 | // If the app has more than one language, we need to generate a path map for each language so
41 | // we can prefix routes with a language parameter. We also need to modify the nonManifestPathMap
42 | // routes to add language parameter.
43 | if (Array.isArray(languages) && languages.length > 1) {
44 | for (const language of languages) {
45 | const manifest = await manifestManager.getManifest(language);
46 | // Prefix manifest routes with language name
47 | generateManifestPathMap(manifest.items.routes[0], `/${language}`);
48 | }
49 | } else {
50 | const language = jssConfig.defaultLanguage;
51 | const manifest = await manifestManager.getManifest(language);
52 | generateManifestPathMap(manifest.items.routes[0], `/`);
53 | }
54 |
55 | return manifestPathMap;
56 | } catch (error) {
57 | console.error(error);
58 | return [];
59 | }
60 | };
61 | }
62 |
63 | function urlJoin(...parts) {
64 | // Trim each part to remove slashes (leading or trailing), then join the parts using a slash.
65 | const joinedParts = parts.map((part) => trim(part, '/')).join('/');
66 | // Trim any extraneous slashes from the joined parts, then prefix the result with a slash
67 | // to ensure a leading slash.
68 | const url = `/${trim(joinedParts, '/')}`;
69 | return url;
70 | }
71 |
72 | function trim(str, char) {
73 | function getSliceStartIndex(str1) {
74 | let startCharIndex = -1;
75 | while (str1.charAt(++startCharIndex) === char);
76 | return startCharIndex;
77 | }
78 |
79 | function getSliceEndIndex(str1) {
80 | let endCharIndex = str1.length;
81 | while (str1.charAt(--endCharIndex) === char);
82 | return endCharIndex + 1;
83 | }
84 | return str.slice(getSliceStartIndex(str), getSliceEndIndex(str));
85 | }
86 |
--------------------------------------------------------------------------------
/src/modules/uniform/services/initialize.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const {
3 | attachUniformServicesToServer,
4 | parseUniformServerConfig,
5 | } = require('@uniformdev/common-server');
6 | const {
7 | NuxtBuildAndExportEngine,
8 | config: getUniformNuxtConfig,
9 | } = require('@uniformdev/nuxt-server');
10 | const { createPublishProvider } = require('@uniformdev/publishing-all');
11 |
12 | // Import a logger
13 | const { consoleLogger } = require('./logging/consoleLogger');
14 |
15 | module.exports = initialize;
16 |
17 | function initialize(moduleOptions) {
18 | if (!moduleOptions.enabled) {
19 | return;
20 | }
21 |
22 | // In Nuxt modules, the current Nuxt instance is available via `this.nuxt`.
23 | const nuxtApp = this.nuxt;
24 |
25 | extendNuxtConfig(this.options);
26 | setupMiddleware(nuxtApp, moduleOptions);
27 | }
28 |
29 | function extendNuxtConfig(nuxtConfig) {
30 | const uniformNuxtConfig = getUniformNuxtConfig(consoleLogger);
31 |
32 | nuxtConfig.env = {
33 | ...nuxtConfig.env,
34 | ...uniformNuxtConfig.env,
35 | };
36 |
37 | nuxtConfig.generate = {
38 | ...nuxtConfig.generate,
39 | routes: uniformNuxtConfig.generate.routes,
40 | };
41 | }
42 |
43 | function setupMiddleware(nuxtApp, moduleOptions) {
44 | // We use the `render:setupMiddleware` Nuxt hook instead of `this.addMiddleware` because using
45 | // `this.addMiddleware` results in a `server.use(middleware)` call. However, for Uniform services,
46 | // we only want to register middleware for specific request methods and paths.
47 | // NOTE: the `render:setupMiddleware` hook is fired before any other middleware are registered.
48 | nuxtApp.hook('render:setupMiddleware', async (server) => {
49 | // Setup Uniform config and attach Uniform-specific middleware to the existing server.
50 | const uniformServerConfig = resolveUniformServerConfig(nuxtApp);
51 |
52 | const buildAndExportEngine = new NuxtBuildAndExportEngine(uniformServerConfig);
53 |
54 | const publishProviderOptions = {
55 | config: uniformServerConfig,
56 | logger: consoleLogger,
57 | };
58 |
59 | const options = {
60 | uniformServerConfig,
61 | publishProvider: createPublishProvider(publishProviderOptions),
62 | createPublishProvider: createPublishProvider
63 | };
64 |
65 | attachUniformServicesToServer(
66 | server,
67 | buildAndExportEngine,
68 | moduleOptions.logger || consoleLogger,
69 | options
70 | );
71 | });
72 | }
73 |
74 | function resolveUniformServerConfig(nuxtApp) {
75 | let uniformServerConfig;
76 |
77 | // Attempt to resolve a path to `src/uniform.config`
78 | const uniformConfigPath = nuxtApp.resolver.resolveAlias('~/uniform.config.js');
79 |
80 | // If `uniform.config.js` file exists, get config from there.
81 | if (fs.existsSync(uniformConfigPath)) {
82 | const uniformConfig = require(uniformConfigPath);
83 | // If the config module exports a `getUniformServerConfig` function, call it.
84 | if (typeof uniformConfig.getUniformServerConfig === 'function') {
85 | uniformServerConfig = uniformConfig.getUniformServerConfig();
86 | } else {
87 | // Otherwise, assume the config module exports a config object.
88 | uniformServerConfig = uniformConfig;
89 | }
90 | } else {
91 | // Otherwise, use `parseUniformServerConfig` method from Uniform library to
92 | // resolve config from environment variables.
93 | uniformServerConfig = parseUniformServerConfig(process.env, consoleLogger, false);
94 | }
95 |
96 | return uniformServerConfig;
97 | }
98 |
--------------------------------------------------------------------------------
/src/modules/uniform/services/logging/consoleLogger.js:
--------------------------------------------------------------------------------
1 | const log = require('loglevel');
2 |
3 | // Be sure to set a level in order for loglevel to bind to the console properly.
4 | // Otherwise, the exported logger instance will have `noop` functions for all
5 | // console methods.
6 | // Likely an issue due to how loglevel exports itself.
7 |
8 | // NOTE: UNIFORM_OPTIONS_DEBUG is exposed via WebpackDefinePlugin.
9 | const level = process.env.UNIFORM_OPTIONS_DEBUG === '1' ? 'debug' : 'info';
10 | log.setLevel(level);
11 |
12 | exports.consoleLogger = log;
13 |
--------------------------------------------------------------------------------
/src/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const serverConfig = require('./server/server.config');
2 |
3 | const nuxtConfig = {
4 | mode: 'universal',
5 | /*
6 | ** Headers of the page
7 | */
8 | head: {
9 | meta: [
10 | { charset: 'utf-8' },
11 | { name: 'viewport', content: 'width=device-width, initial-scale=1' },
12 | ],
13 | link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
14 | },
15 | /*
16 | ** Customize the progress-bar color
17 | */
18 | loading: false,
19 | /*
20 | ** Global CSS
21 | */
22 | css: [],
23 | /*
24 | ** Plugins to load before mounting the App
25 | */
26 | plugins: [{ src: '~/plugins/export-route-data-context-plugin', mode: 'server' }],
27 | /*
28 | ** Nuxt.js dev-modules
29 | */
30 | buildModules: [
31 | // Doc: https://github.com/nuxt-community/nuxt-tailwindcss
32 | '@nuxtjs/tailwindcss',
33 | ],
34 | /*
35 | ** Nuxt.js modules
36 | */
37 | modules: [
38 | // IMPORTANT: the Express module should be first so that other modules can
39 | // attach Express middleware (instead of Connect middleware).
40 | '~/modules/express/initialize',
41 | // IMPORTANT: the JSS Standard module should be loaded before other JSS modules.
42 | // Some (not all) JSS modules may depend on the Standard modules or plugins being
43 | // installed / initialized. e.g. config, dataFetcher
44 | ['~/modules/jss/standard/initialize', { dataFetcherType: 'axios' }],
45 | // JSS i18n module
46 | ['~/modules/jss/i18n/module', { enabled: true }],
47 | // JSS Disconnected Mode module
48 | [
49 | '~/modules/jss/disconnected-mode/initialize',
50 | { enabled: process.env.JSS_MODE === 'disconnected' },
51 | ],
52 | // JSS Rendering Host module
53 | [
54 | '~/modules/jss/rendering-host/initialize',
55 | {
56 | enabled: true,
57 | resolveRenderingHostPublicUrl: (nuxtApp, nuxtCfg) => {
58 | const serverUrl = serverConfig.resolveServerUrl();
59 | const publicServerUrl = serverConfig.resolvePublicServerUrl();
60 | return publicServerUrl.url || serverUrl.url;
61 | },
62 | },
63 | ],
64 | // JSS Tracking API module
65 | ['~/modules/jss/tracking-api/initialize', { enabled: true }],
66 | // Uniform Disconnected Mode Export module
67 | [
68 | '~/modules/uniform/disconnected-export/initialize',
69 | { enabled: process.env.JSS_MODE === 'disconnected' },
70 | ],
71 | // Uniform Standard Services module
72 | ['~/modules/uniform/services/initialize', { enabled: true }],
73 | // Nuxt PWA module
74 | '@nuxtjs/pwa',
75 | // JSS Sitecore Proxy module
76 | // NOTE: the proxy module should likely be installed last (after all other modules)
77 | // because it adds server middleware that may respond to requests that are intended
78 | // to be handled by other middleware / route definitions. In other words, if the proxy
79 | // middleware handles a request, it will likely forward/proxy that request to Sitecore.
80 | [
81 | '~/modules/jss/sitecore-proxy/initialize',
82 | {
83 | enabled: process.env.JSS_MODE !== 'disconnected',
84 | isDevEnv: process.env.NODE_ENV !== 'production',
85 | },
86 | ],
87 | ],
88 | /*
89 | ** Build configuration
90 | */
91 | build: {
92 | /*
93 | ** You can extend webpack config here
94 | */
95 | extend(config, ctx) {
96 | if (ctx.isDev) {
97 | config.devtool = ctx.isClient ? 'source-map' : 'inline-source-map';
98 | }
99 | },
100 | },
101 | generate: {
102 | dir: 'out',
103 | crawler: false,
104 | },
105 | server: {
106 | port: serverConfig.resolveListeningPort(),
107 | },
108 | };
109 |
110 | module.exports = nuxtConfig;
111 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uniformdev/uniform-sitecore-jss-nuxtjs-starterkit",
3 | "version": "1.0.0",
4 | "private": true,
5 | "config": {
6 | "appName": "uniform-jss-kit",
7 | "rootPlaceholders": [
8 | "uniform-jss-kit-content"
9 | ],
10 | "sitecoreConfigPath": "/App_Config/Include/zzz",
11 | "graphQLEndpointPath": "/api/uniform-jss-kit",
12 | "language": "en",
13 | "appLanguages": [
14 | "en"
15 | ]
16 | },
17 | "scripts": {
18 | "bootstrap": "node scripts/bootstrap.js",
19 | "deploy": "npm run export && node deploy.js",
20 | "export": "cross-env-shell JSS_MODE=connected NUXT_EXPORT=true \"npm-run-all --serial bootstrap nuxt:generate\"",
21 | "generate-manifest": "jss manifest -c -d",
22 | "start": "cross-env-shell JSS_MODE=connected \"npm-run-all --serial bootstrap --parallel nuxt:dev watch-components\"",
23 | "start:disconnected": "cross-env-shell JSS_MODE=disconnected \"npm-run-all --serial bootstrap --parallel nuxt:dev watch-components\"",
24 | "start:disconnected:withtunnel": "cross-env-shell JSS_MODE=disconnected \"npm-run-all --serial bootstrap --parallel nuxt:start:withtunnel watch-components\"",
25 | "start:production": "cross-env-shell JSS_MODE=connected NODE_ENV=production \"npm-run-all --serial bootstrap nuxt:build nuxt:start",
26 | "start:withtunnel": "cross-env-shell JSS_MODE=connected \"npm-run-all --serial bootstrap --parallel nuxt:start:withtunnel watch-components\"",
27 | "nuxt:dev": "nuxt dev",
28 | "nuxt:build": "nuxt build",
29 | "nuxt:generate": "nuxt generate",
30 | "nuxt:start": "node server/server.js --start",
31 | "nuxt:start:withtunnel": "node server/tunnel.js",
32 | "watch-components": "node scripts/generate-component-factory.js --watch",
33 | "test": "jest"
34 | },
35 | "dependencies": {
36 | "@nuxtjs/pwa": "^3.3.5",
37 | "@sitecore-jss/sitecore-jss-tracking": "~13.0.0",
38 | "@sitecore-jss/sitecore-jss-vue": "~13.0.0",
39 | "@uniformdev/nuxt-server": "4.0.210223-3",
40 | "@uniformdev/publishing-all": "4.0.210223-3",
41 | "axios": "^0.19.2",
42 | "body-parser": "^1.19.0",
43 | "compression": "^1.7.4",
44 | "cors": "^2.8.5",
45 | "express": "^4.16.4",
46 | "follow-redirects": "^1.10.0",
47 | "loglevel": "^1.6.7",
48 | "nuxt": "^2.15.8",
49 | "set-cookie-parser": "^2.4.3",
50 | "vue-loading-overlay": "^3.3.2"
51 | },
52 | "devDependencies": {
53 | "@babel/register": "^7.14.5",
54 | "@nuxtjs/tailwindcss": "^4.1.3",
55 | "@sitecore-jss/sitecore-jss-cli": "^13.0.0",
56 | "@sitecore-jss/sitecore-jss-dev-tools": "^13.0.0",
57 | "@sitecore-jss/sitecore-jss-manifest": "^13.0.0",
58 | "@vue/test-utils": "^1.0.3",
59 | "babel-eslint": "^10.1.0",
60 | "babel-jest": "^26.1.0",
61 | "chokidar": "^3.2.0",
62 | "cross-env": "^7.0.2",
63 | "dotenv": "^8.2.0",
64 | "jest": "^26.1.0",
65 | "ngrok": "^4.0.1",
66 | "nodemon": "^2.0.2",
67 | "npm-run-all": "^4.1.5",
68 | "postcss": "^8.3.3",
69 | "serve-static": "^1.14.1",
70 | "tailwindcss": "^2.1.4",
71 | "typescript": "^4.3.2",
72 | "vue-jest": "^4.0.0-beta.3",
73 | "webpack-require-from": "^1.8.1"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/pages/README.md:
--------------------------------------------------------------------------------
1 | # PAGES
2 |
3 | This directory contains your Application Views and Routes.
4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application.
5 |
6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
7 |
--------------------------------------------------------------------------------
/src/pages/_.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
112 |
113 |
118 |
156 |
--------------------------------------------------------------------------------
/src/plugins/export-route-data-context-plugin.js:
--------------------------------------------------------------------------------
1 | /*
2 | During Next export, we need to export Layout Service data to static JSON files that are emitted to the
3 | same output directory as the static site export artifacts.
4 |
5 | The easiest place to do this is within the `getRouteData` function used in the
6 | `src/lib/layoutServiceUtils.js` file. However, we don't want to pollute that file with a bunch of
7 | export-specific code, so use this module to provide any export-specific functionality.
8 | */
9 |
10 | import fs from 'fs';
11 | import nodePath from 'path';
12 |
13 | export default (pluginContext) => {
14 | const { app } = pluginContext;
15 | app.getExportRouteDataContext = () => {
16 | const exportOutDir = resolveExportOutDir();
17 |
18 | function exportRouteDataWriter(routePath, language, data) {
19 | let routeName = routePath === '/' ? 'home' : routePath;
20 | if (routeName.startsWith('/')) {
21 | routeName = routeName.substring(1);
22 | }
23 |
24 | // NOTE: routeName may contain nested paths, e.g. /news-events/press-releases/release-01
25 | // Need to ensure that we create the folder structure to match.
26 | const dataFolder = `${exportOutDir}/data/${routeName}`;
27 | const filePath = `${dataFolder}/${language}.json`;
28 |
29 | return ensureDirectoryExists(dataFolder).then(() => {
30 | return writeFile(filePath, data);
31 | });
32 | }
33 |
34 | return {
35 | exportRouteDataWriter,
36 | exportOutDir,
37 | };
38 | };
39 | };
40 |
41 | function resolveExportOutDir() {
42 | // Unfortunately there isn't a great way to obtain the resolved `nuxtConfig.generate.out` value that
43 | // Nuxt uses for exporting at runtime.
44 | // So, we ask that if devs use a custom `nuxt.config.generate.out` property for `nuxt generate` that they
45 | // also set an environment variable that we can use when writing export data.
46 | // Our default value for `nuxt.config.generate.out` is set to the `/out` folder in the src root,
47 | // so we use that as our default here as well.
48 | const exportOutDir = process.env.EXPORT_OUT_DIR || nodePath.resolve(process.cwd(), 'out');
49 | return exportOutDir;
50 | }
51 |
52 | function writeFile(filePath, data) {
53 | return new Promise((resolve, reject) => {
54 | fs.writeFile(filePath, JSON.stringify(data, null, 2), { encoding: 'utf-8' }, (err) => {
55 | if (err) {
56 | reject(err);
57 | } else {
58 | resolve();
59 | }
60 | });
61 | });
62 | }
63 |
64 | function ensureDirectoryExists(dirPath) {
65 | return new Promise((resolve, reject) => {
66 | if (fs.existsSync(dirPath)) {
67 | resolve();
68 | return;
69 | }
70 |
71 | // The `recursive` option ensures that nested paths are fully created.
72 | // For example, `/out/data/boutiques-restaurants/agatha` would create
73 | // the following folder structure:
74 | // out
75 | // data
76 | // boutiques-restaurants
77 | // agatha
78 | fs.mkdir(dirPath, { recursive: true }, (err) => {
79 | if (err) {
80 | reject(err);
81 | } else {
82 | resolve();
83 | }
84 | });
85 | });
86 | }
87 |
--------------------------------------------------------------------------------
/src/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const {
2 | generateRuntimeConfig,
3 | generateRenderingHostConfig,
4 | generateSitecoreProxyConfig,
5 | } = require('./generate-config');
6 | const { getUniformConfig } = require('../uniform.config');
7 | /*
8 | BOOTSTRAPPING
9 | The bootstrap process runs before build, and generates JS that needs to be
10 | included into the build - specifically, the component name to component mapping,
11 | and the global config module(s).
12 | */
13 |
14 | // Resolve execution modes
15 | const disconnected = process.env.JSS_MODE === 'disconnected';
16 | const isExport = process.env.NUXT_EXPORT === 'true';
17 |
18 | /*
19 | CONFIG GENERATION
20 | Generates the /src/temp/config.js file which contains runtime configuration
21 | that the app can import and use.
22 | */
23 | const runtimeConfigOverrides = getRuntimeConfigOverrides();
24 | generateRuntimeConfig(runtimeConfigOverrides);
25 |
26 | // Rendering Host config generation can be removed / disabled if rendering host is not being used.
27 | generateRenderingHostConfig();
28 |
29 | // Sitecore Proxy config generation can be removed / disabled if Sitecore proxy is not being used.
30 | generateSitecoreProxyConfig();
31 |
32 | /*
33 | COMPONENT FACTORY GENERATION
34 | */
35 | require('./generate-component-factory');
36 |
37 | function getRuntimeConfigOverrides() {
38 | const configOverride = {};
39 |
40 | const uniformConfig = getUniformConfig();
41 |
42 | if (disconnected) {
43 | const port = process.env.PORT || 3000;
44 | configOverride.sitecoreApiHost = `http://localhost:${port}`;
45 | }
46 |
47 | if (isExport) {
48 | const siteName = uniformConfig.UNIFORM_API_SITENAME;
49 | if (siteName) {
50 | configOverride.sitecoreSiteName = siteName;
51 | }
52 |
53 | const apiKey = process.env.UNIFORM_API_KEY;
54 | if (apiKey) {
55 | configOverride.apiKey = apiKey;
56 | }
57 |
58 | const layoutServiceHost = uniformConfig.UNIFORM_API_URL;
59 | if (layoutServiceHost) {
60 | configOverride.sitecoreApiHost = layoutServiceHost;
61 | }
62 | }
63 |
64 | return configOverride;
65 | }
66 |
--------------------------------------------------------------------------------
/src/scripts/generate-component-factory.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const chokidar = require('chokidar');
4 |
5 | /*
6 | COMPONENT FACTORY GENERATION
7 | Generates the /src/temp/componentFactory.js file which maps React components
8 | to JSS components.
9 |
10 | The component factory is a mapping between a string name and a React component instance.
11 | When the Sitecore Layout service returns a layout definition, it returns named components.
12 | This mapping is used to construct the component hierarchy for the layout.
13 |
14 | The default convention uses the parent folder name as the component name,
15 | but it is customizable in generateComponentFactory().
16 |
17 | NOTE: this script can run in two modes. The default mode, the component factory file is written once.
18 | But if `--watch` is a process argument, the component factory source folder will be watched,
19 | and the componentFactory.js rewritten on added or deleted files.
20 | This is used during `jss start` to pick up new or removed components at runtime.
21 | */
22 |
23 | /* eslint-disable no-console */
24 |
25 | const componentFactoryPath = path.resolve('temp/componentFactory.js');
26 | const componentRootPath = path.resolve(path.join(__dirname, '../components'));
27 |
28 | const isWatch = process.argv.some((arg) => arg === '--watch');
29 |
30 | if (isWatch) {
31 | watchComponentFactory();
32 | } else {
33 | writeComponentFactory();
34 | }
35 |
36 | function watchComponentFactory() {
37 | console.log(`Watching for changes to component factory sources in ${componentRootPath}...`);
38 |
39 | chokidar
40 | .watch(componentRootPath, { ignoreInitial: true, awaitWriteFinish: true })
41 | .on('add', writeComponentFactory)
42 | .on('unlink', writeComponentFactory);
43 | }
44 |
45 | function writeComponentFactory() {
46 | const componentFactory = generateComponentFactory();
47 |
48 | console.log(`Writing component factory to ${componentFactoryPath}`);
49 |
50 | fs.writeFileSync(componentFactoryPath, componentFactory, { encoding: 'utf8' });
51 | }
52 |
53 | function generateComponentFactory() {
54 | // by convention, we expect to find Vue components
55 | // * under /src/components
56 | // * with a .vue extension to define a component file
57 | // If you'd like to use your own convention, encode it below.
58 | // NOTE: generating the component factory is also totally optional,
59 | // and it can be maintained manually if preferred.
60 |
61 | const imports = [];
62 | const registrations = [];
63 | const ignoreComponent = (componentFile) => {
64 | if (componentFile.indexOf('MenuMobile') !== -1 || componentFile.indexOf('MenuDesktop') !== -1) {
65 | return true;
66 | }
67 | return false;
68 | };
69 |
70 | const componentFiles = extractVueFiles(componentRootPath);
71 | componentFiles.forEach((componentFile) => {
72 | if (!fs.existsSync(componentFile)) {
73 | return;
74 | }
75 | if (ignoreComponent(componentFile)) {
76 | return;
77 | }
78 |
79 | console.debug(`Registering JSS component ${componentFile}`);
80 | const componentName = path.basename(componentFile, '.vue');
81 | const importVarName = componentName.replace(/[^\w]+/g, '');
82 |
83 | imports.push(
84 | `import ${importVarName} from '../components/${componentFile
85 | .replace(path.join(componentRootPath, '/'), '')
86 | .replace(/\\/g, '/')
87 | .replace('.vue', '')}';`
88 | );
89 |
90 | // Create component factory map entries for Sitecore-named renderings/components.
91 | // You can also use this opportunity to provide aliases for component names.
92 | // For instance, if the component name is MyComponent, then register both
93 | // `MyComponent` and `My Component` as map references to the MyComponent component.
94 | // This can be useful when migrated existing components/renderings that may not
95 | // have been create specifically for JSS.
96 | const componentNameAndAliases = [
97 | componentName,
98 | // componentName.match(/([A-Z][a-z]+)/g).join(' '),
99 | ];
100 | componentNameAndAliases.forEach((componentName) => {
101 | registrations.push(`components.set('${componentName}', ${importVarName});`);
102 | });
103 | });
104 |
105 | return `/* eslint-disable */
106 | // Do not edit this file, it is auto-generated at build time!
107 | // See scripts/generate-component-factory.js to modify the generation of this file.
108 | ${imports.join('\n')}
109 |
110 | const components = new Map();
111 | ${registrations.join('\n')}
112 |
113 | export default function componentFactory(componentName) {
114 | return components.get(componentName);
115 | };
116 | `;
117 | }
118 |
119 | /**
120 | * Recursively iterates the given folderPath, creating a flat array of found .vue file paths.
121 | * For example, given the following folder structure and using `/components` as the root folderPath:
122 | * /components/component0.vue
123 | * /components/subfolder/component1.vue
124 | *
125 | * The output would be:
126 | * ['component0.vue', 'subfolder/component1.vue']
127 | */
128 | function extractVueFiles(folderPath) {
129 | let results = [];
130 | fs.readdirSync(folderPath).forEach((pathName) => {
131 | const computedPath = path.join(folderPath, pathName);
132 | const stat = fs.statSync(computedPath);
133 | if (stat && stat.isDirectory()) {
134 | results = results.concat(extractVueFiles(computedPath));
135 | } else if (path.extname(computedPath).toLowerCase() === '.vue') {
136 | results.push(computedPath);
137 | }
138 | });
139 | return results;
140 | }
141 |
--------------------------------------------------------------------------------
/src/scripts/generate-config.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const packageConfig = require('../package.json');
4 |
5 | /* eslint-disable no-console */
6 |
7 | /**
8 | * Generate config
9 | * The object returned from this function will be made available by importing src/temp/config.js.
10 | * This is executed prior to the build running, so it's a way to inject environment or build config-specific
11 | * settings as variables into the JSS app.
12 | * NOTE! Any configs returned here will be written into the client-side JS bundle. DO NOT PUT SECRETS HERE.
13 | * @param {object} configOverrides Keys in this object will override any equivalent global config keys.
14 | */
15 | module.exports = {
16 | generateRuntimeConfig,
17 | generateRenderingHostConfig,
18 | generateSitecoreProxyConfig,
19 | };
20 |
21 | function generateRuntimeConfig(configOverrides) {
22 | const standardConfig = resolveStandardConfig();
23 |
24 | const envConfigKeyMap = {
25 | SITECORE_API_HOST: 'sitecoreApiHost',
26 | SITECORE_API_KEY: 'sitecoreApiKey',
27 | SITECORE_GRAPHQL_ENDPOINT: 'graphQLEndpoint',
28 | SITECORE_GRAPHQL_ENDPOINT_PATH: 'graphQLEndpointPath',
29 | SITECORE_JSS_APP_NAME: 'jssAppName',
30 | SITECORE_JSS_DEFAULT_LANGUAGE: 'defaultLanguage',
31 | SITECORE_SITE_NAME: 'sitecoreSiteName',
32 | };
33 |
34 | const envConfig = resolveEnvConfig(envConfigKeyMap);
35 |
36 | const config = Object.assign(standardConfig, envConfig, configOverrides);
37 |
38 | const moduleFormats = [
39 | { exportStatement: 'export ', filename: 'config.js' },
40 | { exportStatement: 'module.exports = { getConfig };\n', filename: 'config.cjs.js' },
41 | ];
42 | moduleFormats.forEach((moduleFormat) => {
43 | const configText = `/* eslint-disable */
44 | // Do not edit this file, it is auto-generated at build time!
45 | // See scripts/generate-config.js to modify the generation of this file.
46 | ${moduleFormat.exportStatement} function getConfig() {
47 | const config = ${JSON.stringify(config, null, 2)};
48 |
49 | if (typeof window !== 'undefined' && typeof window.__NUXT__ !== 'undefined' && typeof window.__NUXT__.jssConfig !== 'undefined') {
50 | Object.assign(config, window.__NUXT__.jssConfig);
51 | }
52 |
53 | return config;
54 | }`;
55 |
56 | const configPath = writeConfigFile(configText, moduleFormat.filename);
57 | console.log(`Runtime config written to ${configPath}`);
58 | });
59 | }
60 |
61 | function generateRenderingHostConfig(configOverrides) {
62 | const standardConfig = resolveStandardConfig();
63 |
64 | const envConfigKeyMap = {
65 | RENDERING_HOST_SITECORE_API_HOST: 'sitecoreApiHost',
66 | RENDERING_HOST_SITECORE_API_KEY: 'sitecoreApiKey',
67 | RENDERING_HOST_SITECORE_SITE_NAME: 'sitecoreSiteName',
68 | };
69 |
70 | const envConfig = resolveEnvConfig(envConfigKeyMap);
71 |
72 | const config = Object.assign(standardConfig, envConfig, configOverrides);
73 |
74 | const configText = `/* eslint-disable */
75 | // Do not edit this file, it is auto-generated at build time!
76 | // See scripts/generate-config.js to modify the generation of this file.
77 | module.exports = { getConfig };
78 | function getConfig() {
79 | const config = ${JSON.stringify(config, null, 2)};
80 | return config;
81 | }`;
82 |
83 | const configPath = writeConfigFile(configText, 'jss-config-rendering-host.js');
84 |
85 | console.log(`Rendering Host config written to ${configPath}`);
86 | }
87 |
88 | function generateSitecoreProxyConfig(configOverrides) {
89 | const standardConfig = resolveStandardConfig();
90 |
91 | const envConfigKeyMap = {
92 | SITECORE_PROXY_SITECORE_API_HOST: 'sitecoreApiHost',
93 | SITECORE_PROXY_SITECORE_API_KEY: 'sitecoreApiKey',
94 | SITECORE_PROXY_SITECORE_SITE_NAME: 'sitecoreSiteName',
95 | };
96 |
97 | const envConfig = resolveEnvConfig(envConfigKeyMap);
98 |
99 | const config = Object.assign(standardConfig, envConfig, configOverrides);
100 |
101 | const configText = `/* eslint-disable */
102 | // Do not edit this file, it is auto-generated at build time!
103 | // See scripts/generate-config.js to modify the generation of this file.
104 | module.exports = { getConfig };
105 | function getConfig() {
106 | const config = ${JSON.stringify(config, null, 2)};
107 | return config;
108 | }`;
109 |
110 | const configPath = writeConfigFile(configText, 'jss-config-sitecore-proxy.js');
111 |
112 | console.log(`Sitecore Proxy config written to ${configPath}`);
113 | }
114 |
115 | function resolveEnvConfig(envConfigKeyMap) {
116 | const envConfig = {};
117 | Object.keys(envConfigKeyMap).forEach((envConfigKey) => {
118 | if (process.env[envConfigKey]) {
119 | envConfig[envConfigKeyMap[envConfigKey]] = process.env[envConfigKey];
120 | }
121 | });
122 |
123 | return envConfig;
124 | }
125 |
126 | function resolveStandardConfig() {
127 | const defaultConfig = {
128 | sitecoreApiKey: 'no-api-key-set',
129 | sitecoreApiHost: '',
130 | jssAppName: 'Unknown',
131 | };
132 |
133 | // require + combine config sources
134 | const scjssConfig = transformScJssConfig();
135 | const packageJson = transformPackageConfig();
136 |
137 | // optional:
138 | // do any other dynamic config source (e.g. environment-specific config files)
139 | // Object.assign merges the objects in order, so the
140 | // package.json config can override the calculated config,
141 | // scjssconfig.json overrides it,
142 | // and finally config passed in the configOverrides param wins.
143 | const config = Object.assign(defaultConfig, packageJson, scjssConfig);
144 |
145 | // The GraphQL endpoint is an example of making a _computed_ config setting
146 | // based on other config settings.
147 | addGraphQLConfig(config);
148 |
149 | return config;
150 | }
151 |
152 | function transformScJssConfig() {
153 | // scjssconfig.json may not exist if you've never run setup
154 | // so if it doesn't we substitute a fake object
155 | let config;
156 | try {
157 | // eslint-disable-next-line global-require
158 | config = require('../scjssconfig.json');
159 | } catch (e) {
160 | return {};
161 | }
162 |
163 | if (!config) return {};
164 |
165 | return {
166 | sitecoreApiKey: config.sitecore.apiKey,
167 | sitecoreApiHost: config.sitecore.layoutServiceHost,
168 | };
169 | }
170 |
171 | function transformPackageConfig() {
172 | if (!packageConfig.config) {
173 | return {};
174 | }
175 |
176 | return {
177 | jssAppName: packageConfig.config.appName,
178 | // Typically, app name and site name will be the same, but sometimes they're not.
179 | // And that causes all sorts of unpleasantness. So we allow for separate config values.
180 | sitecoreSiteName: packageConfig.config.sitecoreSiteName || packageConfig.config.appName,
181 | defaultLanguage: packageConfig.config.language || 'en',
182 | graphQLEndpointPath: packageConfig.config.graphQLEndpointPath || null,
183 | appLanguages: packageConfig.config.appLanguages || [packageConfig.config.language || 'en'],
184 | };
185 | }
186 |
187 | /**
188 | * Adds the GraphQL endpoint URL to the config object, and ensures that components needed to calculate it are valid
189 | */
190 | function addGraphQLConfig(baseConfig) {
191 | if (!baseConfig.graphQLEndpointPath || typeof baseConfig.sitecoreApiHost === 'undefined') {
192 | console.error(
193 | 'The `graphQLEndpointPath` and/or `layoutServiceHost` configurations were not defined. You may need to run `jss setup`.'
194 | );
195 | process.exit(1);
196 | }
197 |
198 | // eslint-disable-next-line no-param-reassign
199 | baseConfig.graphQLEndpoint = `${baseConfig.sitecoreApiHost}${baseConfig.graphQLEndpointPath}?sc_apikey=${baseConfig.sitecoreApiKey}`;
200 | }
201 |
202 | function writeConfigFile(configData, filename) {
203 | const configPath = path.resolve(`temp/${filename}`);
204 |
205 | fs.writeFileSync(configPath, configData, { encoding: 'utf8' });
206 |
207 | return configPath;
208 | }
209 |
--------------------------------------------------------------------------------
/src/server/server.config.js:
--------------------------------------------------------------------------------
1 | // Process values provided in `.env` file(s)
2 | const { config: dotenvConfig } = require('dotenv');
3 | dotenvConfig();
4 |
5 | module.exports = {
6 | resolveServerUrl,
7 | resolvePublicServerUrl,
8 | resolveListeningPort,
9 | };
10 |
11 | function resolveListeningPort() {
12 | return process.env.PORT || 3000;
13 | }
14 |
15 | function resolveServerUrl() {
16 | const serverPort = process.env.SERVER_PORT || process.env.PORT || 3000;
17 | const serverHostname = process.env.SERVER_HOST_NAME || 'localhost';
18 | const serverProtocol = process.env.SERVER_PROTOCOL || 'http';
19 |
20 | return {
21 | parts: {
22 | port: serverPort,
23 | hostname: serverHostname,
24 | protocol: serverProtocol,
25 | },
26 | url: `${serverProtocol}://${serverHostname}:${serverPort}`,
27 | };
28 | }
29 |
30 | function resolvePublicServerUrl() {
31 | const publicPort = process.env.SERVER_PUBLIC_PORT;
32 | const publicHostname = process.env.SERVER_PUBLIC_HOST_NAME;
33 | const publicProtocol = process.env.SERVER_PUBLIC_PROTOCOL;
34 |
35 | return {
36 | parts: {
37 | port: publicPort,
38 | hostname: publicHostname,
39 | protocol: publicProtocol,
40 | },
41 | // If no publicHostName has been resolved, return undefined for the `url` value
42 | url: publicHostname
43 | ? `${publicProtocol}://${publicHostname}${publicPort ? ':' + publicPort : ''}`
44 | : undefined,
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/src/server/server.js:
--------------------------------------------------------------------------------
1 | const nuxt = require('nuxt');
2 | const { showBanner } = require('@nuxt/cli/dist/cli-banner');
3 |
4 | // Allow starting from the command line or starting from exported `start` method.
5 | if (process.argv.some((arg) => arg === '--start')) {
6 | start();
7 | }
8 |
9 | module.exports = {
10 | start,
11 | };
12 |
13 | async function start({ tunnelUrl } = {}) {
14 | const isDev = process.env.NODE_ENV !== 'production';
15 |
16 | // Init Nuxt.js
17 | const app = await nuxt.loadNuxt(isDev ? 'dev' : 'start');
18 |
19 | // Build only in dev mode.
20 | // In production mode, the app should be built before starting the server.
21 | if (isDev) {
22 | await nuxt.build(app);
23 | }
24 |
25 | await app.listen(app.options.server.port);
26 |
27 | // If the server was started with a tunnel (e.g. ngrok) pointing to it, display
28 | // the tunnel url in the banner for convenience.
29 | if (tunnelUrl) {
30 | app.options.cli.badgeMessages = [...app.options.cli.badgeMessages, `Tunnel URL: ${tunnelUrl}`];
31 | }
32 |
33 | // Show the server banner. Bit of a hack to use `showBanner` from the Nuxt CLI package,
34 | // but not really another way to do it via Nuxt programmatically.
35 | showBanner(app);
36 | }
37 |
--------------------------------------------------------------------------------
/src/server/tunnel.js:
--------------------------------------------------------------------------------
1 | const ngrok = require('ngrok');
2 | const { URL } = require('url');
3 | const serverConfig = require('./server.config');
4 |
5 | const serverUrl = serverConfig.resolveServerUrl();
6 |
7 | startTunnel(serverUrl.parts.hostname, {
8 | proto: serverUrl.parts.protocol,
9 | port: serverUrl.parts.port,
10 | })
11 | .then((tunnelUrl) => {
12 | const parsedTunnelUrl = new URL(tunnelUrl);
13 | process.env.SERVER_PUBLIC_HOST_NAME = parsedTunnelUrl.hostname;
14 | process.env.SERVER_PUBLIC_PORT = parsedTunnelUrl.port;
15 | // node URL.protocol returns the protocol name along with a trailing `:`, we don't need that.
16 | process.env.SERVER_PUBLIC_PROTOCOL = parsedTunnelUrl.protocol.replace(':', '');
17 |
18 | // start the Express server
19 | require('./server').start({ tunnelUrl });
20 | })
21 | .catch((err) => {
22 | console.error(err);
23 | });
24 |
25 | // This function starts an ngrok tunnel that will expose the express server
26 | // via a public URL, e.g. https://13453.ngrok.io
27 | async function startTunnel(serverHostname, options = { port: 80, proto: 'http', quiet: false }) {
28 | if (!serverHostname) {
29 | throw new Error(
30 | 'Unable to start tunnel as no hostname for the underlying server was specified.'
31 | );
32 | }
33 |
34 | const rewriteHost = `${serverHostname}:${options.port}`;
35 | const finalOptions = {
36 | ...options,
37 | host_header: 'rewrite',
38 | addr: rewriteHost,
39 | };
40 |
41 | return ngrok.connect(finalOptions).then((url) => {
42 | if (!options.quiet) {
43 | console.log(`Tunnel started, forwarding '${url}' to '${rewriteHost}'`);
44 | }
45 | return url;
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/src/sitecore/.gitignore:
--------------------------------------------------------------------------------
1 | manifest
2 | package
3 | update-package
4 | *.deploysecret.config
--------------------------------------------------------------------------------
/src/sitecore/config/uniform-jss.config:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
24 |
25 |
26 |
27 |
36 |
65 |
66 |
67 |
68 |
77 |
85 |
86 |
98 |
99 |
100 |
101 | mw=100,mh=50
102 |
103 |
104 | mw=300
105 | mw=100
106 |
107 |
108 |
109 |
110 |
115 |
116 |
117 |
118 | $(url)
119 |
120 | true
121 | true
122 |
123 |
124 | false
125 | false
126 | false
127 | false
128 | true
129 |
130 |
131 |
132 |
133 |
134 | context
135 |
136 | /sitecore/templates/Project/uniform-jss-kit
137 |
138 |
139 |
140 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/components/Hero.sitecore.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | import { CommonFieldTypes, SitecoreIcon, Manifest } from '@sitecore-jss/sitecore-jss-manifest';
3 |
4 | /**
5 | * Adds the Hero component to the disconnected manifest.
6 | * This function is invoked by convention (*.sitecore.js) when `jss manifest` is run.
7 | * @param {Manifest} manifest Manifest instance to add components to
8 | */
9 | export default function(manifest) {
10 | manifest.addComponent({
11 | name: 'Hero',
12 | displayName: 'Hero',
13 | // totally optional, but fun
14 | icon: 'software/16x16/component_yellow.png',
15 | fields: [
16 | { name: 'title', type: CommonFieldTypes.SingleLineText, section: 'Content' },
17 | { name: 'subtitle', type: CommonFieldTypes.SingleLineText, section: 'Content' },
18 | { name: 'text', type: CommonFieldTypes.RichText, section: 'Content' },
19 | { name: 'image', type: CommonFieldTypes.SingleLineText, section: 'Content' },
20 | { name: 'primaryCTALink', type: CommonFieldTypes.GeneralLink, section: 'Call to action' },
21 | { name: 'primaryCTATitle', type: CommonFieldTypes.SingleLineText, section: 'Call to action' },
22 | { name: 'secondaryCTALink', type: CommonFieldTypes.GeneralLink, section: 'Call to action' },
23 | {
24 | name: 'secondaryCTATitle',
25 | type: CommonFieldTypes.SingleLineText,
26 | section: 'Call to action',
27 | },
28 | ],
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/config.js:
--------------------------------------------------------------------------------
1 | // this file is imported by default prior to executing the jss manifest command
2 | // use this to enable transpilation or any other pre-manifest configurations that are needed.
3 |
4 | console.log('Enabling Babel 7 transpilation for the manifest...');
5 |
6 | // register Babel compiler
7 | require('@babel/register')({
8 | presets: [
9 | [
10 | '@babel/preset-env',
11 | {
12 | targets: {
13 | node: 'current',
14 | },
15 | },
16 | ],
17 | ],
18 | // override React default Babel config
19 | babelrc: false,
20 | });
21 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/content.sitecore.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import { Manifest, ItemDefinition } from '@sitecore-jss/sitecore-jss-manifest';
3 | import { mergeFs, MergeFsResult } from '@sitecore-jss/sitecore-jss-dev-tools';
4 | import path from 'path';
5 | import fs from 'fs';
6 | /* eslint-enable no-unused-vars */
7 |
8 | /**
9 | * Adds non-route content items to the disconnected manifest.
10 | * Content items are conventionally defined in /data/content, similar to route items.
11 | * This function is invoked by convention (*.sitecore.js) when `jss manifest` is run.
12 | * @param {Manifest} manifest
13 | * @returns {Promise}
14 | */
15 | export default function addContentToManifest(manifest) {
16 | const rootItemName = 'global';
17 | const rootItemDisplayName = 'Global Content';
18 | const startPath = './data/content'; // relative to process invocation (i.e. where package.json lives)
19 |
20 | if (!fs.existsSync(startPath)) return Promise.resolve();
21 |
22 | return mergeFs(startPath)
23 | .then((result) => {
24 | const items = convertToItems(
25 | result,
26 | path.resolve(startPath),
27 | rootItemName,
28 | rootItemDisplayName,
29 | manifest.language
30 | );
31 | return items;
32 | })
33 | .then((contentData) => {
34 | if (contentData) {
35 | manifest.addContent(contentData);
36 | }
37 | });
38 | }
39 |
40 | /**
41 | * Maps filesystem content data into manifest content item data.
42 | * @param {MergeFsResult} data Filesystem data (files and folders under current path)
43 | * @param {string} basePath The base physical path to calculate relative item paths from
44 | * @param {string} rootItemName Name of the root item to place non-content items under in Sitecore. Normally $siteRoot/Content.
45 | * @param {string} language Language the manifest is being created in. Conventionally affects the expected filename.
46 | * @returns {ItemDefinition}
47 | */
48 | function convertToItems(data, basePath, rootItemName, rootItemDisplayName, language) {
49 | const itemPath = convertPhsyicalPathToItemRelativePath(data.path, basePath);
50 | const name = itemPath.substr(itemPath.lastIndexOf('/') + 1);
51 |
52 | let result;
53 |
54 | const contentItemPattern = new RegExp(`^${language}\\.(yaml|yml|json)$`, 'i');
55 |
56 | const contentFileData = data.files.find((f) => contentItemPattern.test(f.filename));
57 |
58 | if (contentFileData && contentFileData.contents) {
59 | // the path has a valid content item definition
60 | result = contentFileData.contents;
61 |
62 | // Set the path to the item when imported in Sitecore.
63 | // NOTE: Importing to any Sitecore path the import user has rights to is allowed; '$site/Content' is a convention only.
64 | result.path = itemPath;
65 |
66 | // content item name defaults to parent folder name if not explicit
67 | if (!result.name) {
68 | result.name = name;
69 | }
70 | } else if (data.folders.length > 0) {
71 | // The path does not have a content item definition (i.e. en.yml),
72 | // but it does have child folders (which may contain valid content items)
73 | // it will be defined as a Folder item in Sitecore.
74 | result = {
75 | path: itemPath,
76 | name: name || rootItemName,
77 | displayName: name || rootItemDisplayName || rootItemName,
78 | template: 'Folder',
79 | children: [],
80 | };
81 | }
82 |
83 | // recursively process child paths
84 | if (data.folders.length > 0) {
85 | result.children = data.folders
86 | .map((folder) =>
87 | convertToItems(folder, basePath, rootItemName, rootItemDisplayName, language)
88 | )
89 | .filter((item) => item); // remove null results
90 | }
91 |
92 | return result;
93 | }
94 |
95 | /**
96 | * Converts a physical filesystem path into a relative Sitecore item path.
97 | * i.e. if physicalPath = /var/log and basePath = /var, this returns /log.
98 | * @param {string} physicalPath
99 | * @param {string} basePath
100 | */
101 | function convertPhsyicalPathToItemRelativePath(physicalPath, basePath) {
102 | const targetPathSeparator = '/';
103 |
104 | // normalize path separators to /
105 | const normalizedPath = physicalPath.replace(basePath, '').replace(/\\/g, targetPathSeparator);
106 |
107 | if (!normalizedPath) {
108 | return targetPathSeparator;
109 | }
110 |
111 | return normalizedPath.indexOf(targetPathSeparator) > 0
112 | ? `${targetPathSeparator}${normalizedPath}`
113 | : normalizedPath;
114 | }
115 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/dictionary.sitecore.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | import { Manifest } from '@sitecore-jss/sitecore-jss-manifest';
3 | import { mergeFs } from '@sitecore-jss/sitecore-jss-dev-tools';
4 | import fs from 'fs';
5 |
6 | /**
7 | * Reads dictionary definition file in /data/dictionary,
8 | * then emits the dictionary into the disconnected manifest.
9 | * Invoked by convention (*.sitecore.js) when `jss manifest` is run.
10 | * @param {Manifest} manifest
11 | * @returns {Promise}
12 | */
13 | export default function addDictionaryToManifest(manifest) {
14 | const startPath = './data/dictionary'; // relative to process invocation (i.e. where package.json lives)
15 |
16 | if (!fs.existsSync(startPath)) return;
17 |
18 | // eslint-disable-next-line consistent-return
19 | return mergeFs(startPath)
20 | .then((result) => mergeDictionaryFiles(result, manifest.language))
21 | .then((mergedDictionary) => convertToManifestDictionary(mergedDictionary))
22 | .then((dictionary) => manifest.addDictionary(dictionary));
23 | }
24 |
25 | function convertToManifestDictionary(mergedDictionary) {
26 | return Object.keys(mergedDictionary).map((key) => ({
27 | key,
28 | value: mergedDictionary[key],
29 | // optional: if you wished to specify the exact ID of a dictionary item when imported,
30 | // you could pass an 'id' property here that was a GUID or unique (app-wide) string
31 | }));
32 | }
33 |
34 | /**
35 | * Maps a filesystem dictionary file into an object that represents the dictionary.
36 | * @param {MergeFsResult} data Filesystem data (files and folders under current path)
37 | * @param {string} language Language the manifest is being created in. Conventionally affects the expected filename.
38 | * @returns {object} Key-value mappings for the dictionary
39 | */
40 | function mergeDictionaryFiles(data, language) {
41 | let dictionaryResult = {};
42 |
43 | // regex that matches the expected dictionary file name
44 | const dictionaryFilePattern = new RegExp(`^${language}\\.(yaml|yml|json)$`, 'i');
45 | const dictionaryFileData = data.files.find((f) => dictionaryFilePattern.test(f.filename));
46 |
47 | if (dictionaryFileData && dictionaryFileData.contents) {
48 | // customize here to modify the dictionary or apply conventions
49 | dictionaryResult = dictionaryFileData.contents;
50 | }
51 |
52 | return dictionaryResult;
53 | }
54 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/placeholders.sitecore.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-unused-vars
2 | import { Manifest } from '@sitecore-jss/sitecore-jss-manifest';
3 |
4 | /**
5 | * Adding placeholders is optional but allows setting a user-friendly display name. Placeholder Settings
6 | * items will be created for any placeholders explicitly added, or discovered in your routes and component definitions.
7 | * Invoked by convention (*.sitecore.js) when `jss manifest` is run.
8 | * @param {Manifest} manifest
9 | */
10 | export default function addPlaceholdersToManifest(manifest) {
11 | manifest.addPlaceholder({ name: 'uniform-jss-content', displayName: 'uniform-jss-content' });
12 | }
13 |
--------------------------------------------------------------------------------
/src/sitecore/definitions/routes.sitecore.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import path from 'path';
3 | import { Manifest, RouteDefinition, CommonFieldTypes } from '@sitecore-jss/sitecore-jss-manifest';
4 | import { mergeFs, MergeFsResult } from '@sitecore-jss/sitecore-jss-dev-tools';
5 |
6 | /* eslint-enable no-unused-vars */
7 |
8 | /**
9 | * Collects the disconnected routes defined in data/routes into the manifest.
10 | * Invoked by convention (*.sitecore.js) when `jss manifest` is run.
11 | * Alter this method if you wish to store disconnected route data in some way other than the default,
12 | * or to preprocess the route data before it is sent to Sitecore to be ingested - for example to add fields to the route type.
13 | * @param {Manifest} manifest The manifest instance to add routes to
14 | * @returns {Promise}
15 | */
16 | export default function addRoutesToManifest(manifest) {
17 | // Configure the default route type for the app
18 | // this lets us enable route-level data fields,
19 | // which most apps will want for metadata like page titles, SEO metas, or OpenGraph.
20 | // You can add additional non-default route types using `manifest.addRouteType()`,
21 | // which routes can use by setting `template: YourCustomRouteTypeName` in their definition.
22 | const appTemplateSection = 'Page Metadata';
23 |
24 | manifest.setDefaultRouteType({
25 | name: 'CommonPage',
26 | fields: [
27 | {
28 | name: 'pageTitle',
29 | displayName: 'Page Title',
30 | section: appTemplateSection,
31 | type: CommonFieldTypes.SingleLineText,
32 | },
33 | ],
34 | icon: 'Applications/16x16/document_plain.png',
35 | insertOptions: ['CommonPage'],
36 | });
37 |
38 | return mergeFs('./data/routes') // relative to process invocation (i.e. your package.json)
39 | .then((result) => convertToRoutes(result, manifest.language))
40 | .then((routeData) => {
41 | manifest.addRoute(routeData);
42 | });
43 | }
44 |
45 | /**
46 | * Maps filesystem data into manifest route data.
47 | * This is where custom conventions regarding route data would go.
48 | * @param {MergeFsResult} data Filesystem data (files and folders under current path)
49 | * @param {string} language Language the manifest is being created in. Conventionally affects the expected filename.
50 | * @returns {RouteDefinition}
51 | */
52 | function convertToRoutes(data, language) {
53 | let routeData;
54 |
55 | // regex that matches the expected route file name
56 | const routeFilePattern = new RegExp(`^${language}\\.(yaml|yml|json)$`, 'i');
57 |
58 | // find the expected file in the list of files in the current folder
59 | const routeFileData = data.files.find((f) => routeFilePattern.test(f.filename));
60 |
61 | // parse the route data file contents
62 | if (routeFileData && routeFileData.contents) {
63 | routeData = routeFileData.contents;
64 |
65 | if (!routeData.name) {
66 | // no name = imply one from parent folder name
67 | routeData.name = path.basename(path.dirname(routeFileData.path));
68 | // special case for the home route item as its parent folder is 'routes'
69 | if (routeData.name === 'routes') routeData.name = 'home';
70 | }
71 | } else {
72 | console.warn(
73 | `Route data file not found: ${data.path}\\${language}.(yaml|yml|json).
74 | The route will not be added to the manifest. Empty folders can cause this warning.`
75 | );
76 | }
77 |
78 | // recursively crawl child routes (folders)
79 | if (routeData && data.folders.length > 0) {
80 | routeData.children = data.folders
81 | .map((folder) => convertToRoutes(folder, language))
82 | .filter((route) => route); // remove null results
83 | }
84 |
85 | return routeData;
86 | }
87 |
--------------------------------------------------------------------------------
/src/sitecore/pipelines/generateMedia.patch.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra';
2 | import nodePath from 'path';
3 |
4 | // This pipeline patch allows us to hook into the JSS manifest generation process.
5 | // Specifically, we add a pipeline processor to the `generateMedia` pipeline.
6 | // The processor is intended to run after all other `generateMedia` processors and will
7 | // copy the media items identified in the manifest to the `/public` folder for static export.
8 |
9 | // This usage of `resolve` assumes that the manifest generation process is invoked from
10 | // the project root folder and the `./public` folder is relative to the project root folder.
11 | const staticExportAssetFolderPath = nodePath.resolve('./public');
12 |
13 | export const config = (pipelines) => {
14 | // We only want to execute our custom pipeline processor when the app is being statically exported.
15 | if (process.env.SITE_RUNTIME_ENV !== 'static') {
16 | return;
17 | }
18 |
19 | const pipeline = pipelines.getPipeline('generateMedia');
20 |
21 | pipeline.addProcessor({
22 | name: 'copyMediaToPublic',
23 | process: (processorArgs) => {
24 | if (!processorArgs.media || !Array.isArray(processorArgs.media)) {
25 | return processorArgs;
26 | }
27 |
28 | processorArgs.media.forEach((media) => {
29 | if (!media.src) {
30 | console.warn(
31 | `Media object ${JSON.stringify(
32 | media
33 | )} did not have an expected 'src' property. Its media item will not be deployed.`
34 | );
35 | return;
36 | }
37 |
38 | const mediaSourcePath = nodePath.isAbsolute(media.src) ? `.${media.src}` : media.src;
39 |
40 | if (fs.existsSync(mediaSourcePath)) {
41 | if (!fs.statSync(mediaSourcePath).isFile()) {
42 | console.warn(
43 | `Source media path referred to in manifest data is not a file: ${mediaSourcePath}`
44 | );
45 | return;
46 | }
47 | const mediaDestinationPath = nodePath.join(staticExportAssetFolderPath, media.src);
48 | const mediaDestinationFolder = nodePath.dirname(mediaDestinationPath);
49 | fs.ensureDirSync(mediaDestinationFolder);
50 | fs.copySync(mediaSourcePath, mediaDestinationPath);
51 | console.log(
52 | `Copied media for static export from: ${mediaSourcePath} to: ${mediaDestinationPath}`
53 | );
54 | return { source: mediaSourcePath, destination: mediaDestinationPath, success: true };
55 | }
56 | });
57 |
58 | return processorArgs;
59 | },
60 | });
61 | };
62 |
--------------------------------------------------------------------------------
/src/static/README.md:
--------------------------------------------------------------------------------
1 | # STATIC
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your static files.
6 | Each file inside this directory is mapped to `/`.
7 | Thus you'd want to delete this README.md before deploying to production.
8 |
9 | Example: `/static/robots.txt` is mapped as `/robots.txt`.
10 |
11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
12 |
--------------------------------------------------------------------------------
/src/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uniformdev/sitecore-jss-nuxtjs-starterkit/f1e2ea584da52e02fb48af6297811da5abb88bf3/src/static/favicon.ico
--------------------------------------------------------------------------------
/src/store/app/index.js:
--------------------------------------------------------------------------------
1 | import { createLayoutServiceClient } from '../../lib/layoutServiceUtils';
2 | import { getConfig } from '../../temp/config';
3 |
4 | export const state = () => ({
5 | sitecoreContext: {},
6 | routeData: null,
7 | currentRoute: '',
8 | routeDataFetchStatus: '',
9 | routeDataFetchError: null,
10 | });
11 |
12 | export const mutations = {
13 | setLayoutData(state, { layoutData }) {
14 | if (!layoutData) {
15 | return;
16 | }
17 |
18 | const routeData = layoutData.sitecore && layoutData.sitecore.route;
19 | state.routeData = routeData;
20 |
21 | const context = (layoutData.sitecore && layoutData.sitecore.context) || {};
22 | state.sitecoreContext = {
23 | ...context,
24 | routeName: routeData && routeData.name,
25 | itemId: routeData && routeData.itemId,
26 | };
27 | },
28 | setCurrentRoute(state, { route }) {
29 | state.currentRoute = route;
30 | },
31 | setRouteDataFetchStatus(state, { status, error }) {
32 | state.routeDataFetchStatus = status;
33 | state.routeDataFetchError = error;
34 | },
35 | };
36 |
37 | export const actions = {
38 | getLayoutData(context, { route, language, nuxtContext }) {
39 | const { req } = nuxtContext;
40 |
41 | // If the incoming request exists it means we're in SSR.
42 | // If the incoming request has a `jssData` property, it means the app
43 | // is responding to either a proxy request or a JSS rendering host request.
44 | // In either case, we set layout data from the `jssData` property instead
45 | // of trying to fetch data from Layout Service.
46 | if (req && req.jssData) {
47 | context.commit('setLayoutData', { layoutData: req.jssData.route });
48 | context.commit('setCurrentRoute', { route });
49 | return Promise.resolve();
50 | } else {
51 | // This is a client-side request for layout data, e.g. route change.
52 | const config = getConfig();
53 | const layoutServiceClient = createLayoutServiceClient(config, { nuxtContext });
54 |
55 | context.commit('setRouteDataFetchStatus', { status: 'loading', error: null });
56 | return layoutServiceClient
57 | .getRouteData(route, language)
58 | .then((layoutData) => {
59 | context.commit('setLayoutData', { layoutData });
60 | context.commit('setCurrentRoute', { route });
61 | context.commit('setRouteDataFetchStatus', { status: '', error: null });
62 | })
63 | .catch((error) => {
64 | if (error.response && error.response.data && error.response.data.sitecore) {
65 | context.commit('setLayoutData', { layoutData: error.response.data });
66 | }
67 | context.commit('setCurrentRoute', { route });
68 | context.commit('setRouteDataFetchStatus', { status: 'error', error });
69 | });
70 | }
71 | },
72 | nuxtServerInit(nuxtContext) {
73 | // implement any server-specific actions here
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | export const actions = {
2 | // `nuxtServerInit` is only invoked if it is defined in the `store/index.js` file. It will not be automatically
3 | // invoked if it is defined in a store module, e.g. `store/app/index.js`
4 | // That said, we can still define a `nuxtServerInit` within a store module and simply call it from the "root"
5 | // `nuxtServerInit` function, like below.
6 | // https://nuxtjs.org/guide/vuex-store#the-nuxtserverinit-action
7 | nuxtServerInit(context, nuxtContext) {
8 | context.dispatch('app/nuxtServerInit', nuxtContext);
9 | },
10 | };
11 |
--------------------------------------------------------------------------------
/src/store/ui/index.js:
--------------------------------------------------------------------------------
1 | export const state = () => ({});
2 |
3 | export const mutations = {};
4 |
5 | export const actions = {};
6 |
--------------------------------------------------------------------------------
/src/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | ** TailwindCSS Configuration File
3 | **
4 | ** Docs: https://tailwindcss.com/docs/configuration
5 | ** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
6 | */
7 | module.exports = {
8 | theme: {},
9 | variants: {},
10 | plugins: [],
11 | purge: {
12 | // Learn more on https://tailwindcss.com/docs/controlling-file-size/#removing-unused-css
13 | // IMPORTANT: the following settings are _necessary_ in order for Nuxt to not bundle
14 | // all CSS.
15 | enabled: process.env.NODE_ENV === 'production',
16 | content: [
17 | 'components/**/*.vue',
18 | 'layouts/**/*.vue',
19 | 'pages/**/*.vue',
20 | 'plugins/**/*.js',
21 | 'nuxt.config.js',
22 | ],
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/src/temp/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 | !GraphQLFragmentTypes.json
4 |
--------------------------------------------------------------------------------
/src/uniform.config.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | const { parseUniformConfig } = require('@uniformdev/common');
3 | const { parseUniformServerConfig } = require('@uniformdev/common-server');
4 |
5 | // This file is not required, but it provides default standard values for the starter kit.
6 | // You can override the values via a `.env` file if necessary or set the environment variables
7 | // prior to app start.
8 | const defaultConfig = {
9 | PORT: 3000,
10 | UNIFORM_API_DEFAULT_LANGUAGE: 'en',
11 | UNIFORM_API_SITENAME: 'uniform-jss-kit',
12 | UNIFORM_API_URL: 'http://localhost:3000',
13 | UNIFORM_DATA_URL: 'http://localhost:3000',
14 | UNIFORM_OPTIONS_DEBUG: false,
15 | UNIFORM_OPTIONS_PREFETCH_LINKS: false,
16 | UNIFORM_OPTIONS_MVC_SPA_ENABLED: false,
17 | UNIFORM_OPTIONS_MVC_SUPPORT: false,
18 | };
19 |
20 | const defaultServerConfig = {
21 | ...defaultConfig,
22 | UNIFORM_API_TOKEN: '12345',
23 | UNIFORM_MODE: 'mixed',
24 | UNIFORM_PUBLISH_FAKE_PUBLIC_URL: 'http://localhost:3000',
25 | UNIFORM_PUBLISH_PREFETCH_ENABLED: false,
26 | UNIFORM_PUBLISH_TARGET: 'none',
27 | };
28 |
29 | module.exports = {
30 | getUniformConfig() {
31 | setEnvVars(defaultConfig);
32 | const uniformConfig = parseUniformConfig(process.env);
33 | return uniformConfig;
34 | },
35 | getUniformServerConfig() {
36 | setEnvVars(defaultServerConfig);
37 | const uniformServerConfig = parseUniformServerConfig(process.env);
38 | return uniformServerConfig;
39 | },
40 | };
41 |
42 | function setEnvVars(config) {
43 | dotenv.config();
44 |
45 | Object.keys(config).forEach((configKey) => {
46 | process.env[configKey] = process.env[configKey] || config[configKey];
47 | });
48 | }
49 |
--------------------------------------------------------------------------------