2022-03-28 14:30:39 +00:00
|
|
|
/* eslint-disable no-console */
|
2023-06-15 13:57:48 +00:00
|
|
|
import fs from 'fs/promises'
|
|
|
|
import path from 'path'
|
|
|
|
|
2022-04-13 16:42:50 +00:00
|
|
|
import replace from '@rollup/plugin-replace'
|
2023-02-08 09:09:19 +00:00
|
|
|
import yaml, { ValidYamlType } from '@rollup/plugin-yaml'
|
2021-12-28 16:38:39 +00:00
|
|
|
import legacy from '@vitejs/plugin-legacy'
|
2023-03-01 11:48:46 +00:00
|
|
|
import react from '@vitejs/plugin-react-swc'
|
2022-02-08 15:37:43 +00:00
|
|
|
import serveStatic from 'serve-static'
|
2023-06-15 13:57:48 +00:00
|
|
|
import { defineConfig, loadEnv, Plugin } from 'vite'
|
2022-06-30 17:32:57 +00:00
|
|
|
import { VitePWA } from 'vite-plugin-pwa'
|
2022-11-03 16:32:04 +00:00
|
|
|
|
2022-07-25 14:41:27 +00:00
|
|
|
import { runScriptOnFileChange } from './scripts/runScriptOnFileChange'
|
2022-08-03 15:00:50 +00:00
|
|
|
import { pwaOptions } from './vite-pwa-options'
|
2022-01-26 08:53:15 +00:00
|
|
|
|
2022-06-29 20:35:03 +00:00
|
|
|
const env = (mode: string) => loadEnv(mode, process.cwd(), '')
|
|
|
|
|
|
|
|
export default defineConfig(({ command, mode }) => ({
|
2021-12-28 10:56:23 +00:00
|
|
|
resolve: {
|
2023-02-01 00:28:35 +00:00
|
|
|
alias: [{ find: '@', replacement: path.resolve('./source') }],
|
2021-12-28 10:56:23 +00:00
|
|
|
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json'],
|
|
|
|
},
|
2021-12-29 17:21:02 +00:00
|
|
|
publicDir: 'source/public',
|
2022-04-13 16:42:50 +00:00
|
|
|
build: {
|
2022-08-03 16:33:43 +00:00
|
|
|
sourcemap: true,
|
2022-08-03 15:00:50 +00:00
|
|
|
rollupOptions: {
|
|
|
|
output: {
|
|
|
|
chunkFileNames: (chunkInfo) => {
|
|
|
|
if (chunkInfo.isDynamicEntry) {
|
|
|
|
return 'assets/lazy_[name].[hash].js'
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'assets/[name].[hash].js'
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2022-04-13 16:42:50 +00:00
|
|
|
},
|
2022-11-22 15:11:08 +00:00
|
|
|
define: {
|
|
|
|
BRANCH_NAME: JSON.stringify(getBranch(mode)),
|
|
|
|
IS_DEVELOPMENT: mode === 'development',
|
|
|
|
IS_STAGING: mode === 'production' && !isProductionBranch(mode),
|
|
|
|
IS_PRODUCTION: mode === 'production' && isProductionBranch(mode),
|
|
|
|
},
|
2021-12-28 10:56:23 +00:00
|
|
|
plugins: [
|
2022-07-25 14:41:27 +00:00
|
|
|
{
|
|
|
|
name: 'run-script-on-file-change',
|
|
|
|
apply: 'serve',
|
|
|
|
buildStart() {
|
|
|
|
if (mode === 'development') {
|
|
|
|
void runScriptOnFileChange()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2022-04-13 16:42:50 +00:00
|
|
|
command === 'build' &&
|
|
|
|
replace({
|
|
|
|
__SENTRY_DEBUG__: false,
|
|
|
|
preventAssignment: false,
|
|
|
|
}),
|
2021-12-28 10:56:23 +00:00
|
|
|
react({
|
2023-03-01 11:48:46 +00:00
|
|
|
plugins: [['@swc/plugin-styled-components', { pure: true }]],
|
2022-03-03 13:37:53 +00:00
|
|
|
}),
|
2023-02-08 09:09:19 +00:00
|
|
|
yaml({
|
|
|
|
transform(data, filePath) {
|
|
|
|
return filePath.endsWith('/rules-en.yaml')
|
|
|
|
? cleanAutomaticTag(data)
|
|
|
|
: data
|
|
|
|
},
|
|
|
|
}),
|
2021-12-28 10:56:23 +00:00
|
|
|
multipleSPA({
|
|
|
|
defaultSite: 'mon-entreprise',
|
2023-02-23 16:15:57 +00:00
|
|
|
templatePath: './source/entries/template.html',
|
2021-12-28 10:56:23 +00:00
|
|
|
sites: {
|
|
|
|
'mon-entreprise': {
|
2021-12-29 17:21:02 +00:00
|
|
|
lang: 'fr',
|
2023-02-23 16:15:57 +00:00
|
|
|
entry: '/source/entries/entry-fr.tsx',
|
2023-05-30 12:31:01 +00:00
|
|
|
logo: '/source/assets/images/logo-monentreprise.svg',
|
|
|
|
logoAlt: 'Logo mon-entreprise, site Urssaf',
|
2021-12-28 10:56:23 +00:00
|
|
|
},
|
|
|
|
infrance: {
|
2021-12-29 17:21:02 +00:00
|
|
|
lang: 'en',
|
2023-02-23 16:15:57 +00:00
|
|
|
entry: '/source/entries/entry-en.tsx',
|
2023-05-30 12:31:01 +00:00
|
|
|
logo: '/logo-mycompany-share.png',
|
|
|
|
logoAlt: 'Logo My company in France by Urssaf',
|
2021-12-28 10:56:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}),
|
2022-08-03 15:00:50 +00:00
|
|
|
VitePWA(pwaOptions),
|
2021-12-28 16:38:39 +00:00
|
|
|
legacy({
|
|
|
|
targets: ['defaults', 'not IE 11'],
|
|
|
|
}),
|
2021-12-28 10:56:23 +00:00
|
|
|
],
|
2022-02-10 11:07:19 +00:00
|
|
|
server: {
|
2022-11-15 14:26:06 +00:00
|
|
|
port: 3000,
|
2022-06-29 20:35:03 +00:00
|
|
|
hmr: {
|
|
|
|
clientPort:
|
|
|
|
typeof env(mode).HMR_CLIENT_PORT !== 'undefined'
|
|
|
|
? parseInt(env(mode).HMR_CLIENT_PORT)
|
|
|
|
: undefined,
|
|
|
|
},
|
2022-02-10 11:07:19 +00:00
|
|
|
// Keep watching changes in the publicodes package to support live reload
|
|
|
|
// when we iterate on publicodes logic.
|
|
|
|
// https://vitejs.dev/config/#server-watch
|
|
|
|
watch: {
|
|
|
|
ignored: [
|
|
|
|
'!**/node_modules/publicodes/**',
|
|
|
|
'!**/node_modules/publicodes-react/**',
|
|
|
|
],
|
|
|
|
},
|
2022-06-02 11:30:54 +00:00
|
|
|
proxy: {
|
|
|
|
'/api': 'http://localhost:3004',
|
2022-06-29 20:35:03 +00:00
|
|
|
'/twemoji': {
|
2023-01-16 12:24:23 +00:00
|
|
|
target: 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/',
|
2022-06-29 20:35:03 +00:00
|
|
|
changeOrigin: true,
|
|
|
|
rewrite: (path) => path.replace(/^\/twemoji/, ''),
|
2022-09-13 17:32:16 +00:00
|
|
|
timeout: 3 * 1000,
|
2022-06-29 20:35:03 +00:00
|
|
|
},
|
2022-06-02 11:30:54 +00:00
|
|
|
},
|
2022-02-10 11:07:19 +00:00
|
|
|
},
|
|
|
|
optimizeDeps: {
|
2023-02-23 16:15:57 +00:00
|
|
|
entries: ['./source/entries/entry-fr.tsx', './source/entries/entry-en.tsx'],
|
2023-03-01 11:48:46 +00:00
|
|
|
include: ['publicodes-react > react/jsx-runtime'],
|
2022-02-10 11:07:19 +00:00
|
|
|
exclude: ['publicodes-react', 'publicodes'],
|
|
|
|
},
|
2022-11-22 11:12:17 +00:00
|
|
|
ssr: {
|
|
|
|
/**
|
|
|
|
* Prevent listed dependencies from being externalized for SSR build cause some
|
|
|
|
* packages are not esm ready or package.json setup seems wrong, wait this pr to be merge:
|
|
|
|
* markdown-to-jsx: https://github.com/probablyup/markdown-to-jsx/pull/414
|
2023-03-01 13:34:40 +00:00
|
|
|
* styled-components: https://github.com/styled-components/styled-components/issues/3601 (wait v6 release)
|
2022-11-22 11:12:17 +00:00
|
|
|
*/
|
2023-09-07 12:48:22 +00:00
|
|
|
noExternal: [/styled-components|emotion/, /tslib/],
|
2022-11-22 11:12:17 +00:00
|
|
|
},
|
2022-03-03 13:37:53 +00:00
|
|
|
}))
|
2021-12-28 10:56:23 +00:00
|
|
|
|
|
|
|
type MultipleSPAOptions = {
|
|
|
|
defaultSite: string
|
|
|
|
templatePath: string
|
|
|
|
sites: Record<string, Record<string, string>>
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A custom plugin to create multiple virtual html files from a template. Will
|
|
|
|
* generate distinct entry points and single-page application outputs.
|
|
|
|
*/
|
|
|
|
function multipleSPA(options: MultipleSPAOptions): Plugin {
|
|
|
|
const fillTemplate = async (siteName: string) => {
|
|
|
|
const siteData = options.sites[siteName]
|
|
|
|
const template = await fs.readFile(options.templatePath, 'utf-8')
|
|
|
|
const filledTemplate = template
|
|
|
|
.toString()
|
2022-02-09 09:26:56 +00:00
|
|
|
.replace(/\{\{(.+)\}\}/g, (_match, p1) => siteData[(p1 as string).trim()])
|
2022-03-28 14:30:39 +00:00
|
|
|
|
2021-12-28 10:56:23 +00:00
|
|
|
return filledTemplate
|
|
|
|
}
|
2022-03-28 14:30:39 +00:00
|
|
|
|
2021-12-28 10:56:23 +00:00
|
|
|
return {
|
|
|
|
name: 'multiple-spa',
|
|
|
|
enforce: 'pre',
|
|
|
|
|
|
|
|
configureServer(vite) {
|
2022-02-10 11:07:19 +00:00
|
|
|
// TODO: this middleware is specific to the "mon-entreprise" app and
|
|
|
|
// shouldn't be in the "multipleSPA" plugin
|
2022-02-08 15:37:43 +00:00
|
|
|
vite.middlewares.use(
|
|
|
|
'/simulateur-iframe-integration.js',
|
|
|
|
serveStatic(new URL('./dist', import.meta.url).pathname, {
|
|
|
|
index: 'simulateur-iframe-integration.js',
|
|
|
|
})
|
|
|
|
)
|
2021-12-28 10:56:23 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
|
|
vite.middlewares.use(async (req, res, next) => {
|
2022-06-29 20:35:03 +00:00
|
|
|
const url = req.originalUrl?.replace(/^\/%2F/, '/')
|
|
|
|
|
2022-04-24 17:28:32 +00:00
|
|
|
const firstLevelDir = url?.slice(1).split('/')[0]
|
|
|
|
|
2022-04-26 13:16:02 +00:00
|
|
|
if (url && /\?.*html-proxy/.test(url)) {
|
|
|
|
return next()
|
|
|
|
}
|
|
|
|
|
2022-06-30 17:32:57 +00:00
|
|
|
if (url && ['/', '/index.html'].includes(url)) {
|
2022-06-29 20:35:03 +00:00
|
|
|
res.writeHead(302, { Location: '/' + options.defaultSite }).end()
|
2022-07-07 10:22:29 +00:00
|
|
|
}
|
|
|
|
// this condition is for the start:netlify script to match /mon-entreprise or /infrance
|
|
|
|
else if (
|
2022-06-29 20:35:03 +00:00
|
|
|
firstLevelDir &&
|
|
|
|
url &&
|
|
|
|
Object.keys(options.sites)
|
|
|
|
.map((site) => `/${site}.html`)
|
|
|
|
.includes(url)
|
|
|
|
) {
|
|
|
|
const siteName = firstLevelDir.replace('.html', '')
|
|
|
|
const content = await vite.transformIndexHtml(
|
|
|
|
'/' + siteName,
|
|
|
|
await fillTemplate(siteName)
|
|
|
|
)
|
|
|
|
res.end(content)
|
2021-12-28 10:56:23 +00:00
|
|
|
} else if (
|
2022-04-24 17:28:32 +00:00
|
|
|
firstLevelDir &&
|
|
|
|
Object.keys(options.sites).some((name) => firstLevelDir === name)
|
2021-12-28 10:56:23 +00:00
|
|
|
) {
|
2022-04-24 17:28:32 +00:00
|
|
|
const siteName = firstLevelDir
|
2021-12-28 10:56:23 +00:00
|
|
|
const content = await vite.transformIndexHtml(
|
2022-04-24 17:28:32 +00:00
|
|
|
url,
|
|
|
|
await fillTemplate(siteName)
|
2021-12-28 10:56:23 +00:00
|
|
|
)
|
|
|
|
res.end(content)
|
|
|
|
} else {
|
|
|
|
next()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
config(config, { command }) {
|
|
|
|
if (command === 'build' && !config.build?.ssr) {
|
|
|
|
config.build = {
|
|
|
|
...config.build,
|
|
|
|
rollupOptions: {
|
2022-08-03 15:00:50 +00:00
|
|
|
...config.build?.rollupOptions,
|
2021-12-28 10:56:23 +00:00
|
|
|
input: Object.fromEntries(
|
|
|
|
Object.keys(options.sites).map((name) => [
|
|
|
|
name,
|
|
|
|
`virtual:${name}.html`,
|
|
|
|
])
|
|
|
|
),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
resolveId(id) {
|
2022-01-12 13:18:41 +00:00
|
|
|
const pathname = id.split('/').slice(-1)[0]
|
2021-12-28 10:56:23 +00:00
|
|
|
if (pathname?.startsWith('virtual:')) {
|
|
|
|
return pathname.replace('virtual:', '')
|
|
|
|
}
|
2022-03-28 14:30:39 +00:00
|
|
|
|
2021-12-28 10:56:23 +00:00
|
|
|
return null
|
|
|
|
},
|
|
|
|
|
|
|
|
async load(id) {
|
|
|
|
if (
|
|
|
|
Object.keys(options.sites).some((name) => id.endsWith(name + '.html'))
|
|
|
|
) {
|
2021-12-29 17:21:02 +00:00
|
|
|
return await fillTemplate(id.replace(/\.html$/, ''))
|
2021-12-28 10:56:23 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2022-11-22 15:11:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Git branch name
|
|
|
|
*/
|
|
|
|
export const getBranch = (mode: string) => {
|
|
|
|
let branch: string | undefined = env(mode)
|
|
|
|
.VITE_GITHUB_REF?.split('/')
|
|
|
|
?.slice(-1)?.[0]
|
|
|
|
|
|
|
|
if (branch === 'merge') {
|
|
|
|
branch = env(mode).VITE_GITHUB_HEAD_REF
|
|
|
|
}
|
|
|
|
|
|
|
|
return branch ?? ''
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We use this function to hide some features in production while keeping them
|
|
|
|
* in feature-branches. In case we do A/B testing with several branches served
|
|
|
|
* in production, we should add the public faced branch names in the test below.
|
|
|
|
* This is different from the import.meta.env.MODE in that a feature branch may
|
|
|
|
* be build in production mode (with the NODE_ENV) but we may still want to show
|
|
|
|
* or hide some features.
|
|
|
|
*/
|
|
|
|
export const isProductionBranch = (mode: string) => {
|
|
|
|
return ['master', 'next'].includes(getBranch(mode))
|
|
|
|
}
|
2023-02-08 09:09:19 +00:00
|
|
|
|
|
|
|
const cleanAutomaticTag = (data: ValidYamlType): ValidYamlType => {
|
|
|
|
if (typeof data === 'string' && data.startsWith('[automatic] ')) {
|
|
|
|
return data.replace('[automatic] ', '')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
return data.map((val) => cleanAutomaticTag(val))
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data && typeof data === 'object') {
|
|
|
|
return Object.fromEntries<ValidYamlType>(
|
|
|
|
Object.entries(data).map(([key, val]) => [key, cleanAutomaticTag(val)])
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return data
|
|
|
|
}
|