Enhanced version of EJS cli. A command-line tool based on the ejs wrapper, but with many useful features added.
- Support config files to configure ejs options.
- Multiple .ejs files matching to generate html.
- Multiple ways of data injection into templates.
- Global data injection template
- Monitoring .ejs to output .html files in real time.
- Support automatic copying of static resources
$ npm install @wcj/ejs-cli
# Or
$ npm install --global @wcj/ejs-cli$ ejsc "template/**/*"
$ ejsc "template/*.ejs" "template/about/*.ejs" --watchBelow is a help of commands you might find useful. You can use the ejsc and ejs-cli commands:
Usage: ejs-cli <source...> [options]
Options:
-v, --version Show version number
-h, --help Show help
-w, --watch Listen to ejs changes and output HTML (default: "false")
-o, --out Specify the output directory (default: "dist")
-m, --delimiter Use CHARACTER with angle brackets for open/close (defaults to %)
-p, --open-delimiter Use CHARACTER instead of left angle bracket to open.
-c, --close-delimiter Use CHARACTER instead of right angle bracket to close.
-f, --data-file FILE Must be JSON-formatted. Use parsed input from FILE as data for rendering
--global-data Must use JSON format to pass and update data in "globalData".
--rm-whitespace Remove all safe-to-remove whitespace, including leading and trailing
--copy-pattern Use shell patterns to match the files that need to be copied.
--skip-disk-write Skip disk write (default: "false")
--sitemap Enable sitemap generation (default: "false")
--sitemap-prefix The prefix to use for the sitemap URLs, E.q: 'https://***.com/doc/' (default: "")
Examples:
$ ejsc "template/*.ejs" "template/about/*.ejs"
$ ejsc "template/*.ejs" "template/about/*.ejs" --watch
# The above command: matches all `.ejs` files in the template folder
$ ejsc "template/**/*" --watch
$ ejsc "template/**/*" --watch --sitemap --sitemap-prefix 'https://***.com/doc/'
$ ejsc "template/**/*" --data-file "./data.json"
$ ejsc "template/**/*" --global-data "{\"name\": \"ejs-cli\"}"
$ ejs-cli "template/*.ejs" --watch --out build
Copyright 2025Folders and .ejs files starting with an underscore (_) will be ignored.
$ ejs-cli "template/**/*"
$ ejsc "template/**/*"
$ ejsc "template/*.ejs" "template/about/*.ejs"
$ ejsc "template/home.ejs" "template/about.ejs"The above command: matches all .ejs files in the template folder, excluding files starting with _ and .ejs files in folders starting with _.
Inject data by default
PUBLIC_PATH Relative path string concatenation. E.g: ../, ../../.
<link rel="stylesheet" href="<%= PUBLIC_PATH %>static/css/main.css">
<img src="<%= PUBLIC_PATH %>static/img/logo.png" />
<a href="<%= PUBLIC_PATH %>about/index.html"><a>You need to specify the data file --data-file ./data.json on the command line, or configure the globalData field in the configuration
<h2><%= GLOBAL.helloworld %></h2>Use the --global-data parameter to pass JSON-formatted data and update the globalData configuration.
$ ejsc "template/**/*" --global-data "{\"helloworld\": \"ejs-cli\"}"Or specify a JSON file to update the globalData configuration.
$ ejsc "template/**/*" --data-file "./data.json"If the specified ./temp.json injection data content is an array, the value will be assigned to the template variable of TEMP. The variable naming rule is uppercase for file names:
./a/data-name.json=>DATA_NAME./temp/data.json=>DATA./temp/temp.json=>TEMP
//=> ./a/data-name.json
[
{ name: "ejs", version: "v1.2" },
{ name: "auto-config-loader", version: "^1.7.4" },
];The value will be assigned to the template variable of DATA_NAME
<% DATA_NAME.forEach((item) => { %>
<div><%= item.name %>@<%= item.version %></div>
<% }); %>The configuration rules differ slightly: when a data file is specified, the template global variable is the data file name in uppercase; when a data object is specified, the variable is the template name in uppercase.
/**
* @type {import('@wcj/ejs-cli').Options}
*/
export default {
data: {
"template/_list.ejs": "./data/list.json", // => LIST
"template/about/_details.ejs": [
// => DETAILS
{ name: "vidwall", href: "https://github.com/jaywcjlove" },
{ name: "mousio-hint", href: "https://x.com/jaywcjlove" },
],
},
};Read the project's package.json file and inject its data into the template engine so that it can be accessed via GLOBAL.PACKAGE. An example is shown below:
<footer>
<p>© 2017 Company, Inc.</p>
v<%=GLOBAL.PACKAGE.version%>
</footer>Current page compilation time
<div><%=NOW_DATE%></div>Inject data into a specific template, which needs to be configured in .ejscrc.mjs:
/**
* @type {import('@wcj/ejs-cli').Options}
*/
export default {
globalData: {
helloworld: "Hello Wrold!",
},
data: {
"template/about/index.ejs": "./data.json",
"template/about/_details.ejs": "./details.json",
"template/about/_list.ejs": [
{ name: "vidwall", href: "https://github.com/jaywcjlove" },
{ name: "mousio-hint", href: "https://x.com/jaywcjlove" },
],
"template/home.ejs": {
name: "Hello World",
age: 36,
},
},
};Used in template/home.ejs template
<h2><%= name %></h2>
<h3><%= GLOBAL.helloworld %></h3>
<p><%= name %></p>
<p><%= age %></p>By configuring a generic template and its corresponding data file (e.g., template/about/_details.ejs with details.json), multiple pages can be generated in batch. Templates starting with _ are normally ignored as generic modules, but when specified in the configuration, they are rendered in a loop based on the array returned by details.json to generate multiple pages.
// -> details.json
[
{ name: "vidwall", href: "https://...", title: "Vidwall for macOS" },
{ name: "mousio-hint", href: "https://...", title: "Mousio Hint for macOS" },
];Based on the configured data above, the _details.ejs template will generate 2 static pages. The page names are taken from the name field in the data, so this field is required.
Example of generated pages:
about/details/mousio-hint.html
about/details/vidwall.html
The template can use the data from the details.json array, for example:
<p>There are a total of <%= DETAILS.length %> items</p>
<a href="<%= href %>" target="_blank"> View details of <%= name %> </a>In the .ejscrc.mjs configuration, add the beforeSaveHTML method to process and compress HTML using html-minifier.
import { minify } from "html-minifier";
const options = {
includeAutoGeneratedTags: true,
removeAttributeQuotes: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
sortClassName: true,
useShortDoctype: true,
collapseWhitespace: true,
};
/**
* @type {import('@wcj/ejs-cli').Options}
*/
export default {
watchOption: {},
globalData: {},
data: {},
beforeSaveHTML: (html, output, filename) => {
const minHTML = minify(html, options);
return minHTML + "<!-- Hello -->";
},
};In the .ejscrc.mjs configuration, add the afterCopyFile method to process and compress HTML using UglifyJS.
import UglifyJS from "uglify-js-export";
import fs from "fs-extra";
export default {
watchOption: {},
globalData: {},
data: {},
afterCopyFile: (filePath, outputPath) => {
if (filePath.endsWith(".js")) {
const result = UglifyJS.minify(fs.readFileSync(outputPath, "utf-8"));
fs.writeFileSync(outputPath, result.code);
console.log(`π Compress js file success! ${outputPath}`);
}
},
};In the .ejscrc.mjs configuration, add the afterCopyFile method to process and compress HTML using clean-css.
import CleanCSS from "clean-css";
import fs from "fs-extra";
export default {
watchOption: {},
globalData: {},
data: {},
afterCopyFile: (filePath, outputPath) => {
if (filePath.endsWith(".css")) {
const result = new CleanCSS().minify(
fs.readFileSync(outputPath, "utf-8"),
);
fs.writeFileSync(outputPath, result.styles);
console.log(`π Compress css file success! ${outputPath}`);
}
},
};The default configuration is the parameter of EJS, you can add data to inject data into the EJS template, and add watchOption parameter to configure Chokidar settings.
Store .ejscrc.json in the root directory of the project:
{
"watchOption": {},
"data": {
"template/home.ejs": {
"name": "Hello World",
"age": 36
}
}
}Support JSON, JSONC, JSON5, YAML, TOML, INI, CJS, Typescript, and ESM config load.
.ejscrc.mjs config example:
import { minify } from "html-minifier";
import UglifyJS from "uglify-js-export";
import fs from "fs-extra";
/**
* @type {import('@wcj/ejs-cli').Options}
*/
export default {
/** Chokidar's watch parameter settings */
watchOption: {},
sitemap: true,
sitemapPrefix: "https://wangchujiang.com/idoc/",
/** Injecting data into EJS templates */
data: {
"template/about/_details.ejs": "./details.json",
"template/about/_details2.ejs": [
{ name: "vidwall" },
{ name: "mousio-hint" },
],
"template/home.ejs": {
name: "Hello World",
age: 36,
},
},
/**
* Use shell patterns to match the files that need to be copied.
* @default "/**\/*.{css,js,png,jpg,gif,svg,webp,eot,ttf,woff,woff2}"
*/
copyPattern: "",
/**
* Pre-Save HTML Callback Method
* @param html
* @param output
* @param filename
* @returns
*/
beforeSaveHTML: (html, output, filename) => {
return minify(html, options);
},
/**
* Callback method after copying files.
* @param filepath
* @param output
* @returns
*/
afterCopyFile: (filePath, outputPath) => {
if (filePath.endsWith(".js")) {
const result = UglifyJS.minify(fs.readFileSync(outputPath, "utf-8"));
fs.writeFileSync(outputPath, result.code);
console.log(`π Compress js file success! ${outputPath}`);
}
},
};You can configure in package.json:
{
"name": "@wcj/examples",
"version": "0.0.1",
"ejsc": {
"data": {
"template/home.ejs": {
"name": "Hello World",
"age": 36
}
}
}
}For more configuration methods, please see default searchPlaces.
$ npm i
$ npm run buildAs always, thanks to our amazing contributors!
Made with contributors.
MIT Β© Kenny Wong


























