diff --git a/tools/build/vite/index.js b/tools/build/vite/index.js index 8758e6b5..50ec3bec 100644 --- a/tools/build/vite/index.js +++ b/tools/build/vite/index.js @@ -1,9 +1,17 @@ +/* eslint-env node */ import { fileURLToPath } from 'node:url'; import { readdirSync } from 'node:fs'; import { resolve } from 'node:path'; import flattenHtmlPagesDirectoryPlugin from './flatten-html-pages-directory'; +const checkIfIsProduction = (mode) => { + if (mode) return mode === 'production'; + if (process.env.NODE_ENV) return process.env.NODE_ENV === 'production'; + + return false; +}; + /** * Creates a valid vite config set up for a Connect extension that uses Vite + Vue * @@ -14,63 +22,72 @@ import flattenHtmlPagesDirectoryPlugin from './flatten-html-pages-directory'; * @param {object} config.vuePlugin - '@vitejs/vue' plugin instance * @param {object} viteOptions - your custom vite config options * - * @returns {object} - Valid vite config set up for a connect extension + * @returns {function({mode: ('development'|'production')}): {resolve: *&{alias: *&{"~": string}}, build: *&{minify: string|boolean, emptyOutDir: boolean, sourcemap, rollupOptions: *&{output: *&{manualChunks(*): (string|undefined), format: string, dir: *}, input: {}}, outDir: *}, plugins, root: *, base: string}} - Valid vite config set up for a connect extension */ -export const defineExtensionConfig = (config, viteOptions = {}) => { - const { srcDir, srcUrl, outputDir, vuePlugin } = config; +export const defineExtensionConfig = + (config, viteOptions = {}) => + ({ mode }) => { + const { srcDir, srcUrl, outputDir, vuePlugin } = config; + + if (!srcDir) throw new Error('"srcDir" is required'); + if (!outputDir) throw new Error('"outputDir" is required'); + if (!vuePlugin) throw new Error('"vuePlugin" is required'); + if (!srcUrl) throw new Error('"srcUrl" is required'); - if (!srcDir) throw new Error('"srcDir" is required'); - if (!outputDir) throw new Error('"outputDir" is required'); - if (!vuePlugin) throw new Error('"vuePlugin" is required'); - if (!srcUrl) throw new Error('"srcUrl" is required'); + const isProduction = checkIfIsProduction(mode); - return { - ...viteOptions, + return { + ...viteOptions, - resolve: { - ...viteOptions.resolve, + resolve: { + ...viteOptions.resolve, - alias: { - ...viteOptions.resolve?.alias, + alias: { + ...viteOptions.resolve?.alias, - '~': fileURLToPath(srcUrl), + '~': fileURLToPath(srcUrl), + }, }, - }, - plugins: [vuePlugin, flattenHtmlPagesDirectoryPlugin, ...(viteOptions.plugins || [])], + plugins: [vuePlugin, flattenHtmlPagesDirectoryPlugin, ...(viteOptions.plugins || [])], + + root: srcDir, + base: '/static', - root: srcDir, - base: '/static', + build: { + // Enable minification on production builds + minify: isProduction ? 'esbuild' : false, + // Enable sourcemaps on non-production builds + sourcemap: !isProduction, - build: { - ...viteOptions.build, + ...viteOptions.build, - outDir: outputDir, - emptyOutDir: true, + outDir: outputDir, + emptyOutDir: true, - rollupOptions: { - ...viteOptions.build?.rollupOptions, + rollupOptions: { + ...viteOptions.build?.rollupOptions, - // Load all pages in {{srcDir}}/pages/{{pageName}}/index.html as entrypoints - input: readdirSync(resolve(srcDir, 'pages')).reduce((entryPoints, pageName) => { - entryPoints[pageName] = resolve(srcDir, 'pages/', pageName, 'index.html'); + // Load all pages in {{srcDir}}/pages/{{pageName}}/index.html as entrypoints + input: readdirSync(resolve(srcDir, 'pages')).reduce((entryPoints, pageName) => { + entryPoints[pageName] = resolve(srcDir, 'pages/', pageName, 'index.html'); - return entryPoints; - }, {}), + return entryPoints; + }, {}), - output: { - ...viteOptions.build?.rollupOptions?.output, + output: { + ...viteOptions.build?.rollupOptions?.output, - format: 'es', - dir: outputDir, + format: 'es', + dir: outputDir, - // Split node_modules into a "vendor" chunk, and @cloudblueconnect modules into a "connect" chunk - manualChunks(id) { - if (id.includes('@cloudblueconnect')) return 'connect'; - if (id.includes('node_modules')) return 'vendor'; + // Split node_modules into a "vendor" chunk, and @cloudblueconnect modules into a "connect" chunk + manualChunks(id) { + if (id.includes('@cloudblueconnect')) return 'connect'; + if (id.includes('node_modules')) return 'vendor'; + }, }, }, }, - }, + }; }; -}; diff --git a/tools/build/vite/index.spec.js b/tools/build/vite/index.spec.js index 5090f025..c84bc872 100644 --- a/tools/build/vite/index.spec.js +++ b/tools/build/vite/index.spec.js @@ -63,7 +63,7 @@ describe('#defineExtensionConfig function', () => { let error; try { - defineExtensionConfig(config); + defineExtensionConfig(config)({ mode: 'production' }); } catch (e) { error = e; } @@ -72,7 +72,7 @@ describe('#defineExtensionConfig function', () => { }); }); - it('returns the base config', () => { + it('returns the base config for production mode', () => { const config = { srcDir: '/my/source/dir', srcUrl: 'file://my/source/dir', @@ -80,7 +80,7 @@ describe('#defineExtensionConfig function', () => { vuePlugin: { name: 'vuepluginstub' }, }; - result = defineExtensionConfig(config); + result = defineExtensionConfig(config)({ mode: 'production' }); expect(result).toEqual({ resolve: { @@ -92,6 +92,46 @@ describe('#defineExtensionConfig function', () => { root: '/my/source/dir', base: '/static', build: { + minify: 'esbuild', + sourcemap: false, + outDir: '/my/output/dir', + emptyOutDir: true, + rollupOptions: { + input: { + fsReaddirSyncStub: 'pathResolveStub', + }, + output: { + format: 'es', + dir: '/my/output/dir', + manualChunks: expect.any(Function), + }, + }, + }, + }); + }); + + it('returns the base config for development mode', () => { + const config = { + srcDir: '/my/source/dir', + srcUrl: 'file://my/source/dir', + outputDir: '/my/output/dir', + vuePlugin: { name: 'vuepluginstub' }, + }; + + result = defineExtensionConfig(config)({ mode: 'development' }); + + expect(result).toEqual({ + resolve: { + alias: { + '~': 'urlFileUrlToPathStub', + }, + }, + plugins: [{ name: 'vuepluginstub' }, 'flattenHtmlPagesDirectoryPluginStub'], + root: '/my/source/dir', + base: '/static', + build: { + minify: false, + sourcemap: true, outDir: '/my/output/dir', emptyOutDir: true, rollupOptions: { @@ -126,6 +166,7 @@ describe('#defineExtensionConfig function', () => { }, plugins: ['other-vite-plugin'], build: { + minify: 'custom-value-that-ignores-mode', someProperty: 'someValue', rollupOptions: { bar: 'baz', @@ -136,7 +177,7 @@ describe('#defineExtensionConfig function', () => { }, }; - result = defineExtensionConfig(config, customViteConfig); + result = defineExtensionConfig(config, customViteConfig)({ mode: 'production' }); expect(result).toEqual({ foo: 'bar', @@ -155,6 +196,8 @@ describe('#defineExtensionConfig function', () => { root: '/my/source/dir', base: '/static', build: { + minify: 'custom-value-that-ignores-mode', + sourcemap: false, someProperty: 'someValue', outDir: '/my/output/dir', emptyOutDir: true, @@ -182,7 +225,7 @@ describe('#defineExtensionConfig function', () => { vuePlugin: { name: 'vuepluginstub' }, }; - result = defineExtensionConfig(config); + result = defineExtensionConfig(config)({ mode: 'production' }); expect(resolve).toHaveBeenCalledWith('/my/source/dir', 'pages'); expect(readdirSync).toHaveBeenCalledWith('pathResolveStub'); @@ -215,7 +258,8 @@ describe('#defineExtensionConfig function', () => { outputDir: '/my/output/dir', vuePlugin: { name: 'vuepluginstub' }, }; - const manualChunksFn = defineExtensionConfig(config).build.rollupOptions.output.manualChunks; + const manualChunksFn = defineExtensionConfig(config)({ mode: 'production' }).build + .rollupOptions.output.manualChunks; result = manualChunksFn(moduleId);