Merkur config
The merkur.config.mjs
file is main configuration file for Merkur CLI and your project.
The full merkur config can be look like:
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig, emitter, }) {
return {
extends: [ '@merkur/preact/cli' ], // Merkur predefined extender
task: { // defined tasks to building your widget, default are node, es13 and es9
node: {
name: 'node',
build: ESBuildConfiguration, // https://esbuild.github.io/api/#build
}
es13: {
name: 'es13',
folder: 'es13',
build: ESBuildConfiguration, // https://esbuild.github.io/api/#build
}
es9: {
name: 'es9',
folder: 'es9',
build: ESBuildConfiguration, // https://esbuild.github.io/api/#build
}
},
devServer: { // configuration for Merkur dev server
protocol: 'http:',
host: 'localhost:4445',
port: 4445,
staticPath: '/static',
staticFolder: '{project_folder}/build/static',
origin: 'http://localhost:4445'
},
defaultEntries: {
// entries for your project, you can override it with creating /src/entries/{client|server.js} file
client: [
'{project_folder}/node_modules/@merkur/preact/entries/client.js'
],
server: [
'{project_folder}/node_modules/@merkur/preact/entries/server.js'
]
},
playground: {
template: '{project_folder}/node_modules/@merkur/cli/src/templates/playground.ejs',
templateFolder: '{project_folder}/node_modules/@merkur/cli/src/templates',
serverTemplateFolder: '{project_folder}/server/playground/templates',
path: '/',
widgetHandler: AsyncFunction,
widgetParams: Function,
},
socketServer: {
protocol: 'ws:',
host: 'localhost:4321',
port: 4321
},
widgetServer: { // configuration for Merkur widget production server
protocol: 'http:',
host: 'localhost:4444',
port: 4444,
staticPath: '/static',
staticFolder: '{project_folder}/build/static',
buildFolder: '{project_folder}/build',
clusters: 3,
origin: 'http://localhost:4444'
cors: {
options: {
origin: [
new RegExp('^https?://localhost(:[0-9]+)?$'),
new RegExp('^https?://127\\.0\\.0\\.1(:[0-9]+)?$'),
],
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'],
optionsSuccessStatus: 200,
}
}
},
HMR: true,
constant: {
HOST: 'localhost',
}
onCliConfig, Function, //extending hook for cli config,
onMerkurConfig: Function, // extending hook for merkur config,
onTaskConfig: Function, // extending hook for task config,
onTaskBuild, Function, // extending hook esbuild config
};
}
How to define custom task
For example we create custom task for bundling ES11 version of Merkur widget very simple.
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig }) {
return {
task: {
es11: {
name: 'es11',
folder: 'es11',
build: { // ESBuildConfiguration option
platform: 'browser',
target: 'es2020',
outdir: `${cliConfig.staticFolder}/es11`
}
}
},
};
}
Or If you want to bundle Merkur widget and some JS asset for your Merkur widget with own entry point. You can use Merkur CLI to define custom tasks for that asset. For example:
/**
* @type import('@merkur/cli').defineConfig
*/
export default function () {
return {
onMerkurConfig({ cliConfig, merkurConfig }) {
const { projectFolder, isProduction, staticFolder } = cliConfig;
merkurConfig.task.es13Asset = {
name: 'es13Asset',
folder: 'es13',
build: {
entryPoints: [`${projectFolder}/src/customAsset.js`],
entryNames: !isProduction ? 'customAsset' : 'customAsset.[hash]',
platform: 'browser',
outdir: `${staticFolder}/es13`,
plugins: merkurConfig.task['es13'].build.plugins,
},
};
merkurConfig.task.es9Asset = {
name: 'es9Asset',
folder: 'es9',
build: {
entryPoints: [`${projectFolder}/src/customAsset.js`],
entryNames: !isProduction ? 'customAsset' : 'customAsset.[hash]',
platform: 'browser',
target: 'es2018',
outdir: `${staticFolder}/es9`,
plugins: merkurConfig.task['es9'].build.plugins,
},
};
return merkurConfig;
},
onCliConfig({ cliConfig }) {
// add es13Asset task to be run for `merkur dev`
if (cliConfig.command === 'dev') {
cliConfig.runTasks.push('es13Asset');
}
},
};
}
How to add esbuild plugin
By default Merkur CLI as esbuild load only css files. If you want to use some css preprocessors like less
or others. You must install esbuild plugin for that with npm install esbuild-plugin-less --save dev
command. Then add new installed package in merkur.config.js
to esbuild configuration.
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig, emitter, }) {
const loaders = [];
try {
const { lessLoader } = await import('esbuild-plugin-less');
loaders.push(
lessLoader({})
);
} catch {
// Fail silently
}
return {
onTaskBuild({ build }) {
build.plugins.push(...loaders);
return build;
},
};
}
The esbuild-plugin-*
must be dynamic imported with try/catch
block because merkur.config.js
is used for all Merkur CLI commands and of course for merkur start
where dev dependencies can be missed. It depends on your CI/CD workflow. But we predict that you run merkur start
command only with production dependencies where dev dependencies miss.
Or you want to use Tailwind CSS framework. You must install esbuild plugin with npm install esbuild-plugin-tailwindcss --save-dev
command. Then add new installed package in merkur.config.js
to esbuild configuration.
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig, emitter, }) {
const loaders = [];
try {
const { tailwindPlugin } = await import('esbuild-plugin-tailwindcss');
loaders.push(
tailwindPlugin({})
);
} catch {
// Fail silently
}
return {
onTaskBuild({ build }) {
build.plugins.push(...loaders);
return build;
},
};
}
You must create file tailwind.config.js
at the root of the project.
// ./tailwind.config.js
export default {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
// For more, see: https://tailwindcss.com/docs/configuration
};
You must create file index.css and import index.css file to ./src/widget.js
.
/* ./src/style.css */
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
How to override playground widgetParams to widget API
By default playground page pass all route query params to widgetParams. If you want to modify that logic which can be helpful for example with @merkur/plugin-router
which route defined routes by pathname
then is useful set widgetParams pathname
to req.path
. You can also reconfigure regular path
for playground page for which playground page works.
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig, emitter, }) {
return {
playground: {
widgetParams: req => {
return new URLSearchParams({ pathname: req.path });
},
path: /(\/$|\/some-folder\/.*)/g
}
}
}
Custom function in playground templates
In some use case you need use custom helper function in playground templates. For that use case you can use playground
property in merkur.config.mjs
file to define your custom function as show example below.
function helperFunction() {
return 'Hello world';
}
/**
* @type import('@merkur/cli').defineConfig
*/
export default function ({ cliConfig, emitter, }) {
return {
playground: {
utils: {
helperFunction
}
}
}
}
<div><%- merkurConfig.playground.utils.helperFunction() %></div> // result <div>Hello world</div>