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

Configure Vite to build the SDK #760

Merged
merged 3 commits into from
Dec 30, 2024
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

# production
/storybook-static
/build
/dist
/dist-vite
/lib
/esm
*.tgz
Expand Down
35 changes: 35 additions & 0 deletions build/plugins.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import lodashTemplate from 'lodash/template';

// inspired on https://dev.to/koistya/using-ejs-with-vite-48id and
// https://github.com/difelice/ejs-loader/blob/master/index.js
export const ejsPlugin = () => ({
name: 'compile-ejs',
async transform(src: string, id: string) {
const options = {
variable: 'ctx',
evaluate: /\{%([\s\S]+?)%\}/g,
interpolate: /\{\{([\s\S]+?)\}\}/g,
escape: /\{\{\{([\s\S]+?)\}\}\}/g,
};
if (id.endsWith('.ejs')) {
// @ts-ignore
const code = lodashTemplate(src, options);
return {code: `export default ${code}`, map: null};
}
},
});

export const cjsTokens = () => ({
name: 'process-cjs-tokens',
async transform(src, id) {
if (
id.endsWith('/design-tokens/dist/tokens.js') ||
id.endsWith('node_modules/@utrecht/design-tokens/dist/tokens.cjs')
) {
return {
code: src.replace('module.exports = ', 'export default '),
map: null,
};
}
},
});
15 changes: 15 additions & 0 deletions build/utils.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {dependencies, peerDependencies} from '../package.json';

const externalPackages = [
...Object.keys(dependencies || {}),
...Object.keys(peerDependencies || {}),
'formiojs',
'lodash',
'@formio/vanilla-text-mask',
'@babel/runtime',
'@utrecht/component-library-react',
];

// Creating regexes of the packages to make sure subpaths of the
// packages are also treated as external
export const packageRegexes = externalPackages.map(packageName => new RegExp(`^${packageName}(/.*)?`));
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"start": "npm run build:design-tokens && node scripts/start.js",
"build": "node scripts/build.js && npm run build:esm",
"build:esm": "rimraf dist/esm && NODE_ENV=production babel --no-babelrc --config-file ./.babelrc.esm-build --ignore 'src/**/*.spec.js','src/**/fixtures/**','src/setupTests.js','src/reportWebVitals.js' src --out-dir dist/esm",
"build:vite": "BUILD_TARGET=umd vite build && BUILD_TARGET=esm vite build",
"test": "TZ=Europe/Amsterdam vitest",
"test:storybook": "test-storybook --coverage",
"clean": "rimraf dist/*",
Expand Down
2 changes: 1 addition & 1 deletion src/formio/components/Number.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {maskInput} from '@formio/vanilla-text-mask';
import {set} from 'lodash';
import set from 'lodash/set';
import {Formio} from 'react-formio';

import {setErrorAttributes} from '../utils';
Expand Down
2 changes: 1 addition & 1 deletion src/formio/validators/plugins.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isEmpty} from 'lodash';
import isEmpty from 'lodash/isEmpty';

import {post} from '../../api';

Expand Down
6 changes: 6 additions & 0 deletions src/sdk.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ProtectedEval from '@formio/protected-eval';
import 'flatpickr';
import lodash from 'lodash';
import {fixIconUrls as fixLeafletIconUrls} from 'map';
import React from 'react';
import {createRoot} from 'react-dom/client';
Expand All @@ -23,6 +24,11 @@ import OpenFormsModule from './formio/module';
import OFLibrary from './formio/templates';
import './styles.scss';

// lodash must be bundled for Formio templates to work properly...
if (typeof window !== 'undefined') {
window._ = lodash;
}

// use protected eval to not rely on unsafe-eval (CSP)
Formio.use(ProtectedEval);

Expand Down
97 changes: 68 additions & 29 deletions vite.config.mts
Original file line number Diff line number Diff line change
@@ -1,44 +1,69 @@
/// <reference types="vitest/config" />
// https://vitejs.dev/config/
import react from '@vitejs/plugin-react';
import lodashTemplate from 'lodash/template';
import type {OutputOptions} from 'rollup';
import {defineConfig} from 'vite';
import jsconfigPaths from 'vite-jsconfig-paths';
import {coverageConfigDefaults} from 'vitest/config';

// inspired on https://dev.to/koistya/using-ejs-with-vite-48id and
// https://github.com/difelice/ejs-loader/blob/master/index.js
const ejsPlugin = () => ({
name: 'compile-ejs',
async transform(src: string, id: string) {
const options = {
variable: 'ctx',
evaluate: /\{%([\s\S]+?)%\}/g,
interpolate: /\{\{([\s\S]+?)\}\}/g,
escape: /\{\{\{([\s\S]+?)\}\}\}/g,
};
if (id.endsWith('.ejs')) {
// @ts-ignore
const code = lodashTemplate(src, options);
return {code: `export default ${code}`, map: null};
import {cjsTokens, ejsPlugin} from './build/plugins.mjs';
import {packageRegexes} from './build/utils.mjs';

// type definition for our custom envvars
declare global {
namespace NodeJS {
interface ProcessEnv {
BUILD_TARGET: 'umd' | 'esm';
}
}
}

const buildTarget = process.env.BUILD_TARGET || 'umd';

/**
* Rollup output options for ESM build, which is what we package in the NPM package
* under the 'esm' subdirectory.
*
* The ESM package is experimental. Known issues:
* @fixme
*
* - the react-intl translations are not distributed yet (also broken in CRA/babel build!)
*/
const esmOutput = {
dir: 'dist-vite/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 cjsTokens = () => ({
name: 'process-cjs-tokens',
async transform(src, id) {
if (
id.endsWith('/design-tokens/dist/tokens.js') ||
id.endsWith('node_modules/@utrecht/design-tokens/dist/tokens.cjs')
) {
return {
code: src.replace('module.exports = ', 'export default '),
map: null,
};
/**
* Rollup output options for UMD bundle, included in the NPM package but
* the primary distribution mechanism is in a Docker image.
*
* @todo - optimize with bundle splitting/chunk management.
*/
const umdOutput = {
dir: 'dist-vite',
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;

export default defineConfig({
base: './',
Expand All @@ -54,6 +79,20 @@ export default defineConfig({
cjsTokens(),
ejsPlugin(),
],
build: {
target: 'modules', // the default
assetsInlineLimit: 8 * 1024, // 8 KiB
cssCodeSplit: false,
sourcemap: buildTarget !== 'esm',
outDir: 'dist-vite/umd',
rollupOptions: {
input: 'src/sdk.jsx',
// do not externalize anything in UMD build - bundle everything
external: buildTarget === 'esm' ? packageRegexes : undefined,
output: buildTarget === 'esm' ? esmOutput : umdOutput,
preserveEntrySignatures: 'strict',
},
},
css: {
preprocessorOptions: {
scss: {
Expand Down
Loading