Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 56 additions & 39 deletions tools/build/vite/index.js
Original file line number Diff line number Diff line change
@@ -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
*
Expand All @@ -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';
},
},
},
},
},
};
};
};
56 changes: 50 additions & 6 deletions tools/build/vite/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('#defineExtensionConfig function', () => {
let error;

try {
defineExtensionConfig(config);
defineExtensionConfig(config)({ mode: 'production' });
} catch (e) {
error = e;
}
Expand All @@ -72,15 +72,15 @@ 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',
outputDir: '/my/output/dir',
vuePlugin: { name: 'vuepluginstub' },
};

result = defineExtensionConfig(config);
result = defineExtensionConfig(config)({ mode: 'production' });

expect(result).toEqual({
resolve: {
Expand All @@ -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: {
Expand Down Expand Up @@ -126,6 +166,7 @@ describe('#defineExtensionConfig function', () => {
},
plugins: ['other-vite-plugin'],
build: {
minify: 'custom-value-that-ignores-mode',
someProperty: 'someValue',
rollupOptions: {
bar: 'baz',
Expand All @@ -136,7 +177,7 @@ describe('#defineExtensionConfig function', () => {
},
};

result = defineExtensionConfig(config, customViteConfig);
result = defineExtensionConfig(config, customViteConfig)({ mode: 'production' });

expect(result).toEqual({
foo: 'bar',
Expand All @@ -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,
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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);

Expand Down