Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define the assets directory for a build using SDK_VERSION #796

Merged
merged 1 commit into from
Feb 20, 2025
Merged
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
360 changes: 187 additions & 173 deletions vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {sentryVitePlugin} from '@sentry/vite-plugin';
import react from '@vitejs/plugin-react';
import path from 'path';
import type {OutputOptions} from 'rollup';
import {defineConfig} from 'vite';
import {defineConfig, loadEnv} from 'vite';
import jsconfigPaths from 'vite-jsconfig-paths';
import eslint from 'vite-plugin-eslint2';
import {coverageConfigDefaults} from 'vitest/config';
Expand Down Expand Up @@ -35,204 +35,218 @@ const buildTargetDefined = process.env.BUILD_TARGET !== undefined;
*
* - the react-intl translations are not distributed yet (also broken in CRA/babel build!)
*/
const esmOutput = {
dir: 'dist/esm',
format: 'esm',
preserveModules: true,
preserveModulesRoot: 'src',
entryFileNames: '[name].js',
assetFileNames: ({name}) => {
if (name?.endsWith('.css')) {
return '[name].[ext]';
}
return 'static/media/[name].[hash:8].[ext]';
},
} satisfies OutputOptions;
const esmOutput = (buildDist: string) =>
({
dir: `${buildDist}/esm`,
format: 'esm',
preserveModules: true,
preserveModulesRoot: 'src',
entryFileNames: '[name].js',
assetFileNames: ({name}) => {
if (name?.endsWith('.css')) {
return '[name].[ext]';
}
return 'static/media/[name].[hash:8].[ext]';
},
}) satisfies OutputOptions;

const esmBundleOutput = {
dir: 'dist',
format: 'esm',
preserveModules: false,
entryFileNames: 'open-forms-sdk.mjs',
assetFileNames: ({name}) => {
if (name === 'style.css') {
return 'open-forms-sdk.css';
}
return 'static/media/[name].[hash:8].[ext]';
},
inlineDynamicImports: false,
} satisfies OutputOptions;
const esmBundleOutput = (buildDist: string) =>
({
dir: buildDist,
format: 'esm',
preserveModules: false,
entryFileNames: 'open-forms-sdk.mjs',
assetFileNames: ({name}) => {
if (name === 'style.css') {
return 'open-forms-sdk.css';
}
return 'static/media/[name].[hash:8].[ext]';
},
inlineDynamicImports: false,
}) satisfies OutputOptions;

/**
* Rollup output options for UMD bundle, included in the NPM package but
* the primary distribution mechanism is in a Docker image.
*
* @deprecated - it's better to use the ESM bundle which has separate chunks.
*/
const umdOutput = {
dir: 'dist',
format: 'umd',
exports: 'named',
name: 'OpenForms',
generatedCode: 'es2015',
entryFileNames: 'open-forms-sdk.js',
assetFileNames: ({name}) => {
if (name === 'style.css') {
return 'open-forms-sdk.css';
}
return 'static/media/[name].[hash:8].[ext]';
},
inlineDynamicImports: true,
} satisfies OutputOptions;
const umdOutput = (buildDist: string) =>
({
dir: buildDist,
format: 'umd',
exports: 'named',
name: 'OpenForms',
generatedCode: 'es2015',
entryFileNames: 'open-forms-sdk.js',
assetFileNames: ({name}) => {
if (name === 'style.css') {
return 'open-forms-sdk.css';
}
return 'static/media/[name].[hash:8].[ext]';
},
inlineDynamicImports: true,
}) satisfies OutputOptions;

const getOutput = (buildTarget: typeof process.env.BUILD_TARGET): OutputOptions => {
const getOutput = (
buildTarget: typeof process.env.BUILD_TARGET,
buildDist: string
): OutputOptions => {
switch (buildTarget) {
case 'esm-bundle': {
return esmBundleOutput;
return esmBundleOutput(buildDist);
}
case 'esm': {
return esmOutput;
return esmOutput(buildDist);
}
case 'umd':
default: {
return umdOutput;
return umdOutput(buildDist);
}
}
};

export default defineConfig(({mode}) => ({
base: './',
publicDir: false,
server: {
port: 3000,
},
plugins: [
// BIG DISCLAIMER - Vite only processes files with the .jsx or .tsx extension with
// babel, and changing this configuration is... cumbersome and comes with a performance
// penalty. This manifests if you're using react-intl in .js/.mjs/.ts files etc., as
// they don't get transformed to inject the message ID. The solution is to rename the
// file extension to .jsx/.tsx
react({babel: {babelrc: true}}),
jsconfigPaths(),
eslint({
build: true,
emitErrorAsWarning: mode === 'development',
}),
cjsTokens(),
ejsPlugin(),
// @formio/protected-eval requires js-interpeter (a forked version), which includes
// this['Interpreter'] = Interpreter. When this is bundled, it becomes a strict module
// and 'this' doesn't point to the window object, but is undefined, and causes the SDK
// to crash.
replace({
preventAssignment: false,
include: ['**/node_modules/js-interpreter/interpreter.js'],
delimiters: ['', ''],
values: {
"this\['Interpreter'\]": "window['Interpreter']",
},
}),
/**
* Plugin to ignore (S)CSS when bundling to UMD bundle target, since we use the ESM
* bundle to generate these.
*
* @todo Remove this when we drop the UMD bundle entirely (in 4.0?)
*/
{
name: 'ignore-styles-esm-bundle',
transform(code, id) {
if (!buildTargetDefined) return;
if (buildTarget === 'umd' && (id.endsWith('.css') || id.endsWith('scss'))) {
// skip processing
return {code: '', map: null};
}
},
export default defineConfig(({mode}) => {
const env = loadEnv(mode, process.cwd(), '');
let buildDist = 'dist';
if (env.SDK_VERSION && env.SDK_VERSION !== 'latest') {
buildDist = `${buildDist}/${env.SDK_VERSION}`;
}

return {
base: './',
publicDir: false,
server: {
port: 3000,
},
sentryVitePlugin({
silent: mode === 'development',
release: {
create: false,
inject: false,
plugins: [
// BIG DISCLAIMER - Vite only processes files with the .jsx or .tsx extension with
// babel, and changing this configuration is... cumbersome and comes with a performance
// penalty. This manifests if you're using react-intl in .js/.mjs/.ts files etc., as
// they don't get transformed to inject the message ID. The solution is to rename the
// file extension to .jsx/.tsx
react({babel: {babelrc: true}}),
jsconfigPaths(),
eslint({
build: true,
emitErrorAsWarning: mode === 'development',
}),
cjsTokens(),
ejsPlugin(),
// @formio/protected-eval requires js-interpeter (a forked version), which includes
// this['Interpreter'] = Interpreter. When this is bundled, it becomes a strict module
// and 'this' doesn't point to the window object, but is undefined, and causes the SDK
// to crash.
replace({
preventAssignment: false,
include: ['**/node_modules/js-interpreter/interpreter.js'],
delimiters: ['', ''],
values: {
"this\['Interpreter'\]": "window['Interpreter']",
},
}),
/**
* Plugin to ignore (S)CSS when bundling to UMD bundle target, since we use the ESM
* bundle to generate these.
*
* @todo Remove this when we drop the UMD bundle entirely (in 4.0?)
*/
{
name: 'ignore-styles-esm-bundle',
transform(code, id) {
if (!buildTargetDefined) return;
if (buildTarget === 'umd' && (id.endsWith('.css') || id.endsWith('scss'))) {
// skip processing
return {code: '', map: null};
}
},
},
sourcemaps: {
disable: true,
sentryVitePlugin({
silent: mode === 'development',
release: {
create: false,
inject: false,
},
sourcemaps: {
disable: true,
},
bundleSizeOptimizations: {
excludeDebugStatements: true,
excludeTracing: true,
excludeReplayCanvas: true,
excludeReplayShadowDom: true,
excludeReplayIframe: true,
excludeReplayWorker: true,
},
telemetry: false,
}),
// must be last!
codecovVitePlugin({
enableBundleAnalysis: buildTarget !== 'esm' && process.env.CODECOV_TOKEN !== undefined,
bundleName: '@open-formulieren/sdk',
uploadToken: process.env.CODECOV_TOKEN,
}),
],
resolve: {
alias: {
// ensure react-router imports don't end up with multiple copies/installations. See
// https://github.com/remix-run/react-router/issues/12785 for more context.
'react-router/dom': path.resolve(
'./node_modules/react-router/dist/development/dom-export.mjs'
),
'react-router': path.resolve('./node_modules/react-router/dist/development/index.mjs'),
},
bundleSizeOptimizations: {
excludeDebugStatements: true,
excludeTracing: true,
excludeReplayCanvas: true,
excludeReplayShadowDom: true,
excludeReplayIframe: true,
excludeReplayWorker: true,
},
telemetry: false,
}),
// must be last!
codecovVitePlugin({
enableBundleAnalysis: buildTarget !== 'esm' && process.env.CODECOV_TOKEN !== undefined,
bundleName: '@open-formulieren/sdk',
uploadToken: process.env.CODECOV_TOKEN,
}),
],
resolve: {
alias: {
// ensure react-router imports don't end up with multiple copies/installations. See
// https://github.com/remix-run/react-router/issues/12785 for more context.
'react-router/dom': path.resolve(
'./node_modules/react-router/dist/development/dom-export.mjs'
),
'react-router': path.resolve('./node_modules/react-router/dist/development/index.mjs'),
},
},
build: {
target: 'modules', // the default
assetsInlineLimit: 8 * 1024, // 8 KiB
cssCodeSplit: false,
sourcemap: buildTarget !== 'esm',
outDir: 'dist',
// we write the .mjs file to the same directory
emptyOutDir: buildTarget !== 'esm-bundle',
rollupOptions: {
input: 'src/sdk.jsx',
// do not externalize anything in UMD build - bundle everything
external: buildTarget === 'esm' ? packageRegexes : undefined,
output: getOutput(buildTarget),
preserveEntrySignatures: 'strict',
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `$fa-font-path: '@fortawesome/fontawesome-free/webfonts/';`,
charset: false,
build: {
target: 'modules', // the default
assetsInlineLimit: 8 * 1024, // 8 KiB
cssCodeSplit: false,
sourcemap: buildTarget !== 'esm',
outDir: buildDist,
// we write the .mjs file to the same directory
emptyOutDir: buildTarget !== 'esm-bundle',
rollupOptions: {
input: 'src/sdk.jsx',
// do not externalize anything in UMD build - bundle everything
external: buildTarget === 'esm' ? packageRegexes : undefined,
output: getOutput(buildTarget, buildDist),
preserveEntrySignatures: 'strict',
},
},
},
test: {
environment: 'jsdom',
environmentOptions: {
jsdom: {
url: 'http://localhost',
css: {
preprocessorOptions: {
scss: {
additionalData: `$fa-font-path: '@fortawesome/fontawesome-free/webfonts/';`,
charset: false,
},
},
},
globals: true, // for compatibility with jest
// See https://vitest.dev/guide/migration.html#fake-timers-defaults
fakeTimers: {
toFake: ['setTimeout', 'clearTimeout', 'Date'],
},
setupFiles: ['./src/vitest.setup.mjs'],
coverage: {
provider: 'istanbul',
include: ['src/**/*.{js,jsx,ts,tsx}'],
exclude: [
'src/**/*.d.ts',
'src/**/*.stories.{js,jsx,ts,tsx}',
'src/api-mocks/*',
'src/**/mocks.{js,jsx}',
'src/story-utils/*',
...coverageConfigDefaults.exclude,
],
reporter: ['text', 'cobertura', 'html'],
test: {
environment: 'jsdom',
environmentOptions: {
jsdom: {
url: 'http://localhost',
},
},
globals: true, // for compatibility with jest
// See https://vitest.dev/guide/migration.html#fake-timers-defaults
fakeTimers: {
toFake: ['setTimeout', 'clearTimeout', 'Date'],
},
setupFiles: ['./src/vitest.setup.mjs'],
coverage: {
provider: 'istanbul',
include: ['src/**/*.{js,jsx,ts,tsx}'],
exclude: [
'src/**/*.d.ts',
'src/**/*.stories.{js,jsx,ts,tsx}',
'src/api-mocks/*',
'src/**/mocks.{js,jsx}',
'src/story-utils/*',
...coverageConfigDefaults.exclude,
],
reporter: ['text', 'cobertura', 'html'],
},
},
},
}));
};
});