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 removeconfig/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'45skipWaiting()6clientsClaim()7precacheAndRoute(self.__WB_MANIFEST || [])89new Prefetcher().route()
- Create
layer0/browser.js
with the following content:
JavaScript
1import install from '@layer0/prefetch/window/install'2import installDevtools from '@layer0/devtools/install'34document.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'45const DIST_APP = 'dist'6const DIST_LAYER0_CLIENT = 'dist-layer0-client'7const DIST_LAYER0_ASSETS = 'dist-layer0-assets'89const SPLAT = ':path*'10const SUFFIX_SPLAT = `:suffix/${SPLAT}`1112const router = new Router()1314const pages = [15 // home16 `/`,17 // plp18 `/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 // other27 `/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 // pdp48 `/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]226227// Prevent search engine bot(s) from indexing228// Read more on: https://docs.layer0.co/guides/cookbook#blocking-search-engine-crawlers229router.noIndexPermalink()230231// static prerendering232router.prerender(pages.filter(page => !page.includes(SPLAT)).map((i) => ({ path: i })))233234// layer0 static files235router.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})243244// assets245router.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})257258// api259router.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})267268// pages269pages.forEach(page => {270 router.get(page, ({ cache, renderWithApp }) => {271 cache(CACHE_PAGES)272 renderWithApp()273 })274})275276// fallback277router.fallback(({ renderWithApp }) => {278 renderWithApp()279})280281export default router
- Add
layer0/cache.js
file. For example:
JavaScript
1const TIME_1H = 60 * 602const TIME_4H = TIME_1H * 43const TIME_1D = TIME_1H * 244/**5 * The default cache setting for pages in the shopping flow6 */7export const CACHE_PAGES = {8 edge: {9 maxAgeSeconds: TIME_4H,10 forcePrivateCaching: true,11 staleWhileRevalidateSeconds: TIME_1H, // this way stale items can still be prefetched12 },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 prefetched26 },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')34module.exports = function prod(port) {5 const server = require(join(process.cwd(), 'dist-layer0-server', 'server.js'))6 .default7 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/first12import themeRoot from './theme-path'1314const 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) === -138 })39 .forEach(function(mod) {40 nodeModules[mod] = 'commonjs ' + mod41 })42const isProd = process.env.NODE_ENV === 'production'43// todo: usemultipage-webpack-plugin for multistore44export default {45 plugins: [46 new webpack.ContextReplacementPlugin(/dayjs[/\\]locale$/, buildLocaleIgnorePattern()),47 new webpack.ProgressPlugin(),48 /* new BundleAnalyzerPlugin({49 generateStatsFile: true50 }), */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 // Server58 new webpack.DefinePlugin({59 'process.env.VUE_ENV': '"server"',60 }),61 // define `config` package62 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 aliases79 // 'config': path.resolve(__dirname, './config.json'),80 src: path.resolve(__dirname, '../../src'),81 // Theme aliases82 theme: themeRoot,83 'theme/app': themeApp,84 'theme/css': themeCSS,85 'theme/resource': themeResources,86 // Backward compatible87 '@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 ),100101 // Server aliases102 '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 // Server183 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 intopackage.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# Edgio2/dist-layer0-assets3/dist-layer0-client4/dist-layer0-server5/.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 build2yarn layer0:build3yarn layer0:deploy