You are reading Edgio Applications v4 docs. Check out our latest docs for Edgio Applications v7.
Edgio
Edgio

Vue Storefront 1

Example Site

1. Install Vue Storefront

Install the Vue Storefront application using this guide: https://docs.vuestorefront.io/guide/installation/linux-mac.html
Note: Edgio requires Node version >= 14, so before the installation it’s recommended to run:
Bash
1nvm use 14

2. Prepare VSF files for Edgio

  • In the new VSF project, go to src/themes/default (or any theme you’re using) and remove .git folder from it to save that in Git VCS.
  • Go to .gitignore file and remove config/local.json line to keep it tracked.

3. Install Edgio

  • Install Edgio packages: yarn add -D -W @layer0/cli && yarn add -W @layer0/core @layer0/prefetch @layer0/devtools
  • Create a file called layer0.config.js in the root directory of your project and configure your origin and images hosts as backends. For example:
JavaScript
1module.exports = {
2 routes: './layer0/routes.js',
3 backends: {
4 origin: {
5 domainOrIp: 'demo.vuestorefront.io',
6 hostHeader: 'demo.vuestorefront.io',
7 },
8 },
9 connector: './layer0',
10 includeNodeModules: true,
11 includeFiles: {
12 './dist-layer0-server/server.js': true,
13 './dist': true,
14 './config/default.json': 'config/production.json',
15 },
16}
  • Create layer0/service-worker.js, which will contain the source code for your service worker:
JavaScript
1import { Prefetcher } from '@layer0/prefetch/sw'
2import { clientsClaim, skipWaiting } from 'workbox-core'
3import { precacheAndRoute } from 'workbox-precaching'
4
5skipWaiting()
6clientsClaim()
7precacheAndRoute(self.__WB_MANIFEST || [])
8
9new Prefetcher().route()
  • Create layer0/browser.js with the following content:
JavaScript
1import install from '@layer0/prefetch/window/install'
2import installDevtools from '@layer0/devtools/install'
3
4document.addEventListener('DOMContentLoaded', () => {
5 install()
6 installDevtools()
7})
  • Create layer0/routes.js. Here you will configure caching for each route in your application. Here is an example:
JavaScript
1import { CACHE_ASSETS, CACHE_PAGES } from './cache'
2import { Router } from '@layer0/core/router'
3import { BACKENDS } from '@layer0/core/constants'
4
5const DIST_APP = 'dist'
6const DIST_LAYER0_CLIENT = 'dist-layer0-client'
7const DIST_LAYER0_ASSETS = 'dist-layer0-assets'
8
9const SPLAT = ':path*'
10const SUFFIX_SPLAT = `:suffix/${SPLAT}`
11
12const router = new Router()
13
14const pages = [
15 // home
16 `/`,
17 // plp
18 `/men.html`,
19 `/men${SUFFIX_SPLAT}`,
20 `/women.html`,
21 `/women${SUFFIX_SPLAT}`,
22 `/gear.html`,
23 `/gear${SUFFIX_SPLAT}`,
24 `/training.html`,
25 `/training${SUFFIX_SPLAT}`,
26 // other
27 `/sale`,
28 `/sale${SUFFIX_SPLAT}`,
29 `/about-us`,
30 `/about-us${SUFFIX_SPLAT}`,
31 `/i/about-us`,
32 `/i/about-us${SUFFIX_SPLAT}`,
33 `/i/customer-service`,
34 `/i/customer-service${SUFFIX_SPLAT}`,
35 `/store-locator`,
36 `/store-locator${SUFFIX_SPLAT}`,
37 `/delivery`,
38 `/delivery${SUFFIX_SPLAT}`,
39 `/returns`,
40 `/returns${SUFFIX_SPLAT}`,
41 `/privacy`,
42 `/privacy${SUFFIX_SPLAT}`,
43 `/size-guide`,
44 `/size-guide${SUFFIX_SPLAT}`,
45 `/contact`,
46 `/contact${SUFFIX_SPLAT}`,
47 // pdp
48 `/abominable-${SUFFIX_SPLAT}`,
49 `/adrienne-${SUFFIX_SPLAT}`,
50 `/advanced-${SUFFIX_SPLAT}`,
51 `/aeon-${SUFFIX_SPLAT}`,
52 `/aero-${SUFFIX_SPLAT}`,
53 `/aether-${SUFFIX_SPLAT}`,
54 `/affirm-${SUFFIX_SPLAT}`,
55 `/aim-${SUFFIX_SPLAT}`,
56 `/ajax-${SUFFIX_SPLAT}`,
57 `/ana-${SUFFIX_SPLAT}`,
58 `/angel-${SUFFIX_SPLAT}`,
59 `/antonia-${SUFFIX_SPLAT}`,
60 `/apollo-${SUFFIX_SPLAT}`,
61 `/arcadio-${SUFFIX_SPLAT}`,
62 `/argus-${SUFFIX_SPLAT}`,
63 `/ariel-${SUFFIX_SPLAT}`,
64 `/artemis-${SUFFIX_SPLAT}`,
65 `/atlas-${SUFFIX_SPLAT}`,
66 `/atomic-${SUFFIX_SPLAT}`,
67 `/augusta-${SUFFIX_SPLAT}`,
68 `/autumn-${SUFFIX_SPLAT}`,
69 `/bag/endeavor-${SUFFIX_SPLAT}`,
70 `/balboa-${SUFFIX_SPLAT}`,
71 `/bardot-${SUFFIX_SPLAT}`,
72 `/beaumont-${SUFFIX_SPLAT}`,
73 `/beginner-${SUFFIX_SPLAT}`,
74 `/bella-${SUFFIX_SPLAT}`,
75 `/bess-${SUFFIX_SPLAT}`,
76 `/bolo-${SUFFIX_SPLAT}`,
77 `/breathe-${SUFFIX_SPLAT}`,
78 `/bruno-${SUFFIX_SPLAT}`,
79 `/caesar-${SUFFIX_SPLAT}`,
80 `/carina-${SUFFIX_SPLAT}`,
81 `/cassia-${SUFFIX_SPLAT}`,
82 `/cassius-${SUFFIX_SPLAT}`,
83 `/celeste-${SUFFIX_SPLAT}`,
84 `/chaz-${SUFFIX_SPLAT}`,
85 `/chloe-${SUFFIX_SPLAT}`,
86 `/circe-${SUFFIX_SPLAT}`,
87 `/clamber-${SUFFIX_SPLAT}`,
88 `/cobalt-${SUFFIX_SPLAT}`,
89 `/compete-${SUFFIX_SPLAT}`,
90 `/cora-${SUFFIX_SPLAT}`,
91 `/cronus-${SUFFIX_SPLAT}`,
92 `/crown-${SUFFIX_SPLAT}`,
93 `/cruise-${SUFFIX_SPLAT}`,
94 `/daphne-${SUFFIX_SPLAT}`,
95 `/daria-${SUFFIX_SPLAT}`,
96 `/dash-${SUFFIX_SPLAT}`,
97 `/deion-${SUFFIX_SPLAT}`,
98 `/deirdre-${SUFFIX_SPLAT}`,
99 `/desiree-${SUFFIX_SPLAT}`,
100 `/diana-${SUFFIX_SPLAT}`,
101 `/didi-${SUFFIX_SPLAT}`,
102 `/diva-${SUFFIX_SPLAT}`,
103 `/driven-${SUFFIX_SPLAT}`,
104 `/dual-${SUFFIX_SPLAT}`,
105 `/echo-${SUFFIX_SPLAT}`,
106 `/electra-${SUFFIX_SPLAT}`,
107 `/elisa-${SUFFIX_SPLAT}`,
108 `/emma-${SUFFIX_SPLAT}`,
109 `/endurance-${SUFFIX_SPLAT}`,
110 `/eos-${SUFFIX_SPLAT}`,
111 `/erica-${SUFFIX_SPLAT}`,
112 `/erikssen-${SUFFIX_SPLAT}`,
113 `/fiona-${SUFFIX_SPLAT}`,
114 `/frankie-${SUFFIX_SPLAT}`,
115 `/fusion-${SUFFIX_SPLAT}`,
116 `/gabrielle-${SUFFIX_SPLAT}`,
117 `/geo-${SUFFIX_SPLAT}`,
118 `/go-${SUFFIX_SPLAT}`,
119 `/gobi-${SUFFIX_SPLAT}`,
120 `/grayson-${SUFFIX_SPLAT}`,
121 `/gwen-${SUFFIX_SPLAT}`,
122 `/gwyn-${SUFFIX_SPLAT}`,
123 `/harmony-${SUFFIX_SPLAT}`,
124 `/hawkeye-${SUFFIX_SPLAT}`,
125 `/helena-${SUFFIX_SPLAT}`,
126 `/helios-${SUFFIX_SPLAT}`,
127 `/hera-${SUFFIX_SPLAT}`,
128 `/hollister-${SUFFIX_SPLAT}`,
129 `/hyperion-${SUFFIX_SPLAT}`,
130 `/ida-${SUFFIX_SPLAT}`,
131 `/impulse-${SUFFIX_SPLAT}`,
132 `/ina-${SUFFIX_SPLAT}`,
133 `/inez-${SUFFIX_SPLAT}`,
134 `/ingrid-${SUFFIX_SPLAT}`,
135 `/iris-${SUFFIX_SPLAT}`,
136 `/jade-${SUFFIX_SPLAT}`,
137 `/josie-${SUFFIX_SPLAT}`,
138 `/joust-${SUFFIX_SPLAT}`,
139 `/juliana-${SUFFIX_SPLAT}`,
140 `/juno-${SUFFIX_SPLAT}`,
141 `/jupiter-${SUFFIX_SPLAT}`,
142 `/karissa-${SUFFIX_SPLAT}`,
143 `/karmen-${SUFFIX_SPLAT}`,
144 `/kenobi-${SUFFIX_SPLAT}`,
145 `/kratos-${SUFFIX_SPLAT}`,
146 `/lando-${SUFFIX_SPLAT}`,
147 `/layla-${SUFFIX_SPLAT}`,
148 `/leah-${SUFFIX_SPLAT}`,
149 `/lifelong-${SUFFIX_SPLAT}`,
150 `/livingston-${SUFFIX_SPLAT}`,
151 `/logan-${SUFFIX_SPLAT}`,
152 `/lono-${SUFFIX_SPLAT}`,
153 `/lucia-${SUFFIX_SPLAT}`,
154 `/luma-${SUFFIX_SPLAT}`,
155 `/mach-${SUFFIX_SPLAT}`,
156 `/marco-${SUFFIX_SPLAT}`,
157 `/mars-${SUFFIX_SPLAT}`,
158 `/maxima-${SUFFIX_SPLAT}`,
159 `/maya-${SUFFIX_SPLAT}`,
160 `/meteor-${SUFFIX_SPLAT}`,
161 `/miko-${SUFFIX_SPLAT}`,
162 `/mimi-${SUFFIX_SPLAT}`,
163 `/minerva-${SUFFIX_SPLAT}`,
164 `/mona-${SUFFIX_SPLAT}`,
165 `/montana-${SUFFIX_SPLAT}`,
166 `/nadia-${SUFFIX_SPLAT}`,
167 `/neve-${SUFFIX_SPLAT}`,
168 `/nona-${SUFFIX_SPLAT}`,
169 `/nora-${SUFFIX_SPLAT}`,
170 `/olivia-${SUFFIX_SPLAT}`,
171 `/orestes-${SUFFIX_SPLAT}`,
172 `/orion-${SUFFIX_SPLAT}`,
173 `/oslo-${SUFFIX_SPLAT}`,
174 `/overnight-${SUFFIX_SPLAT}`,
175 `/phoebe-${SUFFIX_SPLAT}`,
176 `/pierce-${SUFFIX_SPLAT}`,
177 `/portia-${SUFFIX_SPLAT}`,
178 `/prima-${SUFFIX_SPLAT}`,
179 `/primo-${SUFFIX_SPLAT}`,
180 `/proteus-${SUFFIX_SPLAT}`,
181 `/pursuit-${SUFFIX_SPLAT}`,
182 `/push-${SUFFIX_SPLAT}`,
183 `/quest-${SUFFIX_SPLAT}`,
184 `/radiant-${SUFFIX_SPLAT}`,
185 `/rapha-${SUFFIX_SPLAT}`,
186 `/riona-${SUFFIX_SPLAT}`,
187 `/rival-${SUFFIX_SPLAT}`,
188 `/rocco-${SUFFIX_SPLAT}`,
189 `/ryker-${SUFFIX_SPLAT}`,
190 `/sahara-${SUFFIX_SPLAT}`,
191 `/savvy-${SUFFIX_SPLAT}`,
192 `/selene-${SUFFIX_SPLAT}`,
193 `/set-${SUFFIX_SPLAT}`,
194 `/sinbad-${SUFFIX_SPLAT}`,
195 `/sol-${SUFFIX_SPLAT}`,
196 `/solo-${SUFFIX_SPLAT}`,
197 `/sparta-${SUFFIX_SPLAT}`,
198 `/sprite-${SUFFIX_SPLAT}`,
199 `/stark-${SUFFIX_SPLAT}`,
200 `/stellar-${SUFFIX_SPLAT}`,
201 `/strike-${SUFFIX_SPLAT}`,
202 `/strive-${SUFFIX_SPLAT}`,
203 `/summit-${SUFFIX_SPLAT}`,
204 `/supernova-${SUFFIX_SPLAT}`,
205 `/sybil-${SUFFIX_SPLAT}`,
206 `/sylvia-${SUFFIX_SPLAT}`,
207 `/taurus-${SUFFIX_SPLAT}`,
208 `/teton-${SUFFIX_SPLAT}`,
209 `/thorpe-${SUFFIX_SPLAT}`,
210 `/tiberius-${SUFFIX_SPLAT}`,
211 `/tiffany-${SUFFIX_SPLAT}`,
212 `/torque-${SUFFIX_SPLAT}`,
213 `/tristan-${SUFFIX_SPLAT}`,
214 `/troy-${SUFFIX_SPLAT}`,
215 `/typhon-${SUFFIX_SPLAT}`,
216 `/viktor-${SUFFIX_SPLAT}`,
217 `/voyage-${SUFFIX_SPLAT}`,
218 `/vulcan-${SUFFIX_SPLAT}`,
219 `/wayfarer-${SUFFIX_SPLAT}`,
220 `/yoga-${SUFFIX_SPLAT}`,
221 `/zeppelin-${SUFFIX_SPLAT}`,
222 `/zing-${SUFFIX_SPLAT}`,
223 `/zoe-${SUFFIX_SPLAT}`,
224 `/zoltan-${SUFFIX_SPLAT}`,
225]
226
227// Prevent search engine bot(s) from indexing
228// Read more on: https://docs.layer0.co/guides/cookbook#blocking-search-engine-crawlers
229router.noIndexPermalink()
230
231// static prerendering
232router.prerender(pages.filter(page => !page.includes(SPLAT)).map((i) => ({ path: i })))
233
234// layer0 static files
235router.get('/service-worker.js', ({ serviceWorker, cache }) => {
236 cache(CACHE_ASSETS)
237 serviceWorker(`${DIST_LAYER0_CLIENT}/service-worker.js`)
238})
239router.get('/main.js', ({ serveStatic, cache }) => {
240 cache(CACHE_ASSETS)
241 serveStatic(`${DIST_LAYER0_CLIENT}/browser.js`)
242})
243
244// assets
245router.get(`/dist/${SPLAT}`, ({ serveStatic, cache }) => {
246 cache(CACHE_ASSETS)
247 serveStatic(`${DIST_APP}/${SPLAT}`)
248})
249router.get(`/assets/${SPLAT}`, ({ serveStatic, cache }) => {
250 cache(CACHE_ASSETS)
251 serveStatic(`${DIST_LAYER0_ASSETS}/${SPLAT}`)
252})
253router.get(`/img/${SPLAT}`, ({ proxy, cache }) => {
254 cache(CACHE_ASSETS)
255 proxy('origin')
256})
257
258// api
259router.get(`/api/catalog/${SPLAT}`, ({ proxy, cache }) => {
260 cache(CACHE_PAGES)
261 proxy('origin')
262})
263router.get(`/api/stock/${SPLAT}`, ({ proxy, cache }) => {
264 cache(CACHE_PAGES)
265 proxy('origin')
266})
267
268// pages
269pages.forEach(page => {
270 router.get(page, ({ cache, renderWithApp }) => {
271 cache(CACHE_PAGES)
272 renderWithApp()
273 })
274})
275
276// fallback
277router.fallback(({ renderWithApp }) => {
278 renderWithApp()
279})
280
281export default router
  • Add layer0/cache.js file. For example:
JavaScript
1const TIME_1H = 60 * 60
2const TIME_4H = TIME_1H * 4
3const TIME_1D = TIME_1H * 24
4/**
5 * The default cache setting for pages in the shopping flow
6 */
7export const CACHE_PAGES = {
8 edge: {
9 maxAgeSeconds: TIME_4H,
10 forcePrivateCaching: true,
11 staleWhileRevalidateSeconds: TIME_1H, // this way stale items can still be prefetched
12 },
13 browser: {
14 maxAgeSeconds: TIME_4H,
15 serviceWorkerSeconds: TIME_4H,
16 },
17}
18/**
19 * The default cache setting for static assets like JS, CSS, and images.
20 */
21export const CACHE_ASSETS = {
22 edge: {
23 maxAgeSeconds: TIME_1D,
24 forcePrivateCaching: true,
25 staleWhileRevalidateSeconds: TIME_1H, // this way stale items can still be prefetched
26 },
27 browser: {
28 maxAgeSeconds: TIME_1D,
29 serviceWorkerSeconds: TIME_1D,
30 },
31}
  • Create layer0/webpack.layer0.client.config.js. This webpack configuration will bundle the service worker and the code to install it when the app loads in the browser.
JavaScript
1const path = require('path')
2const webpack = require('webpack')
3module.exports = {
4 entry: {
5 browser: './layer0/browser.js',
6 'service-worker': './layer0/service-worker.js',
7 },
8 mode: 'production',
9 resolve: {
10 extensions: ['.js'],
11 },
12 output: {
13 filename: '[name].js',
14 path: path.resolve(__dirname, '../dist-layer0'),
15 },
16 plugins: [
17 new webpack.DefinePlugin({
18 'process.env.DEBUG_SW': JSON.stringify(process.env.DEBUG_SW || false),
19 }),
20 ],
21}
  • Add layer0/prod.js:
JavaScript
1const { createServer } = require('http')
2const { join } = require('path')
3
4module.exports = function prod(port) {
5 const server = require(join(process.cwd(), 'dist-layer0-server', 'server.js'))
6 .default
7 return new Promise(resolve => createServer(server).listen(port, resolve))
8}
  • Add core/build/webpack.layer0.config.ts file:
TypeScript
1import { buildLocaleIgnorePattern } from '@vue-storefront/i18n/helpers'
2import path from 'path'
3import fs from 'fs'
4import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'
5import VueLoaderPlugin from 'vue-loader/lib/plugin'
6import autoprefixer from 'autoprefixer'
7import webpack from 'webpack'
8import dayjs from 'dayjs'
9import config from 'config'
10// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
11// eslint-disable-next-line import/first
12import themeRoot from './theme-path'
13
14const themesRoot = '../../src/themes'
15const themeResources = themeRoot + '/resource'
16const themeCSS = themeRoot + '/css'
17const themeApp = themeRoot + '/App.vue'
18const themedIndex = path.join(themeRoot, '/templates/index.template.html')
19const themedIndexMinimal = path.join(themeRoot, '/templates/index.minimal.template.html')
20const themedIndexBasic = path.join(themeRoot, '/templates/index.basic.template.html')
21const themedIndexAmp = path.join(themeRoot, '/templates/index.amp.template.html')
22const postcssConfig = {
23 loader: 'postcss-loader',
24 options: {
25 ident: 'postcss',
26 plugins: loader => [
27 require('postcss-flexbugs-fixes'),
28 require('autoprefixer')({
29 flexbox: 'no-2009',
30 }),
31 ],
32 },
33}
34var nodeModules = {}
35fs.readdirSync(path.resolve(__dirname, '../../node_modules'))
36 .filter(function(x) {
37 return ['.bin'].indexOf(x) === -1
38 })
39 .forEach(function(mod) {
40 nodeModules[mod] = 'commonjs ' + mod
41 })
42const isProd = process.env.NODE_ENV === 'production'
43// todo: usemultipage-webpack-plugin for multistore
44export default {
45 plugins: [
46 new webpack.ContextReplacementPlugin(/dayjs[/\\]locale$/, buildLocaleIgnorePattern()),
47 new webpack.ProgressPlugin(),
48 /* new BundleAnalyzerPlugin({
49 generateStatsFile: true
50 }), */
51 new CaseSensitivePathsPlugin(),
52 new VueLoaderPlugin(),
53 new webpack.DefinePlugin({
54 'process.env.__APPVERSION__': JSON.stringify(require('../../package.json').version),
55 'process.env.__BUILDTIME__': JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss')),
56 }),
57 // Server
58 new webpack.DefinePlugin({
59 'process.env.VUE_ENV': '"server"',
60 }),
61 // define `config` package
62 new webpack.DefinePlugin({ CONFIG: JSON.stringify(require('config')) }),
63 ],
64 // devtool: 'source-map',
65 output: {
66 path: path.resolve(__dirname, '../../dist-layer0-server'),
67 publicPath: '/dist-layer0-server/',
68 filename: 'server.js',
69 libraryTarget: 'commonjs',
70 },
71 resolveLoader: {
72 modules: ['node_modules', path.resolve(__dirname, themesRoot)],
73 },
74 resolve: {
75 modules: ['node_modules', path.resolve(__dirname, themesRoot)],
76 extensions: ['.js', '.vue', '.gql', '.graphqls', '.ts'],
77 alias: {
78 // Main aliases
79 // 'config': path.resolve(__dirname, './config.json'),
80 src: path.resolve(__dirname, '../../src'),
81 // Theme aliases
82 theme: themeRoot,
83 'theme/app': themeApp,
84 'theme/css': themeCSS,
85 'theme/resource': themeResources,
86 // Backward compatible
87 '@vue-storefront/core/lib/store/multistore': path.resolve(__dirname, '../lib/multistore.ts'),
88 'src/modules/order-history/components/UserOrders': path.resolve(
89 __dirname,
90 '../../core/modules/order/components/UserOrdersHistory',
91 ),
92 '@vue-storefront/core/modules/social-share/components/WebShare': path.resolve(
93 __dirname,
94 '../../src/themes/default/components/theme/WebShare.vue',
95 ),
96 '@vue-storefront/core/helpers/initCacheStorage': path.resolve(
97 __dirname,
98 '../lib/storage-manager.ts',
99 ),
100
101 // Server aliases
102 'create-api': './create-api-server.js',
103 },
104 },
105 module: {
106 rules: [
107 {
108 enforce: 'pre',
109 test: /\.(js|vue,ts)$/,
110 loader: 'eslint-loader',
111 exclude: [/node_modules/, /test/],
112 },
113 {
114 test: /\.vue$/,
115 loader: 'vue-loader',
116 options: {
117 preserveWhitespace: false,
118 postcss: [autoprefixer()],
119 },
120 },
121 {
122 test: /\.ts$/,
123 loader: 'ts-loader',
124 options: {
125 appendTsSuffixTo: [/\.vue$/],
126 },
127 exclude: /node_modules/,
128 },
129 {
130 test: /\.js$/,
131 loader: 'babel-loader',
132 include: [
133 path.resolve(__dirname, '../../node_modules/@vue-storefront'),
134 path.resolve(__dirname, '../../src'),
135 path.resolve(__dirname, '../../core'),
136 ],
137 },
138 {
139 test: /\.(png|jpg|gif|svg)$/,
140 loader: 'file-loader',
141 options: {
142 name: '[name].[ext]?[hash]',
143 },
144 },
145 {
146 test: /\.css$/,
147 use: ['vue-style-loader', 'css-loader', postcssConfig],
148 },
149 {
150 test: /\.scss$/,
151 use: ['vue-style-loader', 'css-loader', postcssConfig, 'sass-loader'],
152 },
153 {
154 test: /\.sass$/,
155 use: [
156 'vue-style-loader',
157 'css-loader',
158 postcssConfig,
159 {
160 loader: 'sass-loader',
161 options: {
162 indentedSyntax: true,
163 },
164 },
165 ],
166 },
167 {
168 test: /\.(woff|woff2|eot|ttf)(\?.*$|$)/,
169 loader: 'url-loader?importLoaders=1&limit=10000',
170 },
171 {
172 test: /\.(graphqls|gql)$/,
173 exclude: /node_modules/,
174 loader: ['graphql-tag/loader'],
175 },
176 // {
177 // test: /core\/build\/config\.json$/,
178 // loader: path.resolve('core/build/purge-config.js')
179 // }
180 ],
181 },
182 // Server
183 mode: 'production',
184 devtool: 'sourcemap',
185 optimization: {
186 minimize: false,
187 },
188 // isClient: false,
189 // isDev: false,
190 target: 'node',
191 entry: ['@babel/polyfill', './core/scripts/server.ts'],
192 externals: nodeModules,
193}
  • Add layer0:* scripts into package.json file:
JSON
1...
2"layer0:build:server": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-build.json\" webpack --config ./core/build/webpack.layer0.config.ts --mode production --hide-modules",
3"layer0:build:assets": "ncp ./src/themes/default/assets ./dist-layer0-assets",
4"layer0:build:client": "cross-env NODE_ENV=production webpack --progress --config layer0/webpack.layer0.client.config.js && 0 build",
5"layer0:build": "yarn layer0:build:server && yarn layer0:build:assets && yarn layer0:build:client",
6"layer0:clean": "rimraf dist-layer0-assets && rimraf dist-layer0-client && rimraf dist-layer0-server && rimraf .layer0",
7"layer0:start:prod": "0 run --production",
8"layer0:deploy": "0 deploy --team=my-team --skip-build",
9...
  • Ignore Edgio build in .gitignore:
Bash
1# Edgio
2/dist-layer0-assets
3/dist-layer0-client
4/dist-layer0-server
5/.layer0
  • Find all *.template.html files in VSF app and add Edgio scripts into <head> there:
HTML
1<script src="/service-worker.js" defer></script>
2<script src="/__layer0__/cache-manifest.js" defer></script>
3<script src="/main.js" defer></script>
4<script src="/__layer0__/devtools/install.js" defer></script>
  • Finally, you are ready to build and deploy the app:
Bash
1yarn build
2yarn layer0:build
3yarn layer0:deploy