The code generator that depends on specific files is designed to implement a Single Source of Truth (SSOT).
In some case, you may need to maintain a file content that has symbols that depend on specific files. For example, to load all component files, declare their paths in the entry point file:
@forward "components/button";
@forward "components/card";
@forward "components/disclosure";
// ...
However, it is a waste of time to manually change the content each time these files are added or removed.
Instead, you can automate this task by using drygen. First, create the following configuration file named drygen.config.js
:
module.exports = {
rules: [
{
name: "components import",
dependencies: ["components/**/*.scss"],
outputs: [
{
path: "_components.scss",
template: "import.scss.ejs",
},
],
},
],
};
Then create an EJS template file named import.scss.ejs
to render the file content:
<% dependencies.forEach((dep) => { -%>
@forward "<%- join(relative(dep.dir), dep.name.replace(/^_/, "")) %>";
<% }); -%>
The variables are exposed in the templates:
dependencies
: an array ofDependencyFile
. It contains a file path, contents, etcrelative
: returns a relative path from the output filejoin
: a reference to POSIX specificationpath.join()
Finally, run the command:
$ drygen
The following file will be generated:
@forward "components/button";
@forward "components/card";
@forward "components/disclosure";
If you want to write the file every time the dependency files are changed, you can add the --watch
flag:
$ drygen --watch
If you want to see more usage examples, please refer to the examples
directory.
$ npm install --save-dev drygen
$ drygen --help
Options:
--help Show help [boolean]
--version Show version number [boolean]
--root a path to project root [string]
-c, --config a path to configuration file [string]
-w, --watch watch file changes [boolean]
Before running drygen, you need to create a configuration file named drygen.config.js
or drygen.config.cjs
inside project root.
module.exports = {
rules: [
{
name: "components import",
dependencies: ["components/**/*.scss"],
outputs: [
{
path: "_components.scss",
template: "import.scss.ejs",
},
],
},
],
};
Since drygen ships with TypeScript typings, you can leverage your IDE’s IntelliSense with jsdoc type hints:
/**
* @type {import("drygen").UserConfig}
*/
module.exports = {
rules: [
{
name: "components import",
dependencies: ["components/**/*.scss"],
outputs: [
{
path: "_components.scss",
template: "import.scss.ejs",
},
],
},
],
};
Type: object[]
Define an array of the rules for file generation.
Type: string
Will be output as logs in your terminal, and can be referenced from the templates.
Type: string[] | Record<keyof any, string[]>
A list of glob matcher patterns. See supported minimatch
patterns.
If you specify an array, dependencies
variable exposed to the templates will be an array:
{
// ...
dependencies: ["components/**/*.scss"],
}
<% dependencies.forEach((dep) => { -%>
@forward "<%- relative(dep.path) %>";
<% }); -%>
If you specify an object of arrays, dependencies
variable exposed to the templates will be an object:
{
// ...
dependencies: {
objects: ["objects/**/*.scss"],
components: ["components/**/*.scss"],
},
}
<% dependencies.objects.forEach((dep) => { -%>
@use "<%- relative(dep.path); %>";
<% }); -%>
<% dependencies.components.forEach((dep) => { -%>
@use "<%- relative(dep.path); %>";
<% }); -%>
Type: ({ path: string, template: string, context?: {} } | ({ name, dependencies }: { name: string, dependencies: DependencyFile[] | Record<keyof any, DependencyFile[]> }) => Promise<{ path: string, template: string, context?: {} } | { path: string, template: string, context?: {} }[]>)[]
An array of the entries for the output file. In most cases, you would specify it as an object:
{
// ...
outputs: [
{
path: "_component.scss",
template: "import.scss.ejs",
context: { greet: "Hi" }, // optional
},
],
}
path
: a path to the output filetemplate
: a path to the EJS template file, see templating section for detailscontext
: an optional object passed to the templates file, can be referenced bycontext
variable
If the entry needs to be based on dependencies
, you can specify a function instead:
{
// ...
outputs: [
({ name, dependencies }) => {
const json = JSON.parse(dependencies[0].content);
return {
path: "output.scss",
template: "output.scss.ejs",
context: json,
};
},
],
}
name
: a name of the ruledependencies
: a list ofDependencyFile
And it can return an array:
{
// ...
outputs: [
({ name, dependencies }) => {
const json = JSON.parse(dependencies[0].content);
return [
{
path: "shared.scss",
template: "shared.scss.ejs",
context: json,
},
{
path: "shared.js",
template: "shared.js.ejs",
context: json,
},
]
},
],
}
Or it needs to call async function, you can specify a async function instead:
{
// ...
outputs: [
async ({ name, dependencies }) => {
const inputJSON = JSON.parse(dependencies[0].content)
const data = await asyncProcess(inputJSON);
return {
path: "output.scss",
template: "output.scss.ejs",
context: data,
};
},
],
}
drygen uses EJS as a template engine.
The following variables are exposed in the templates.
Type: string
The value passed to rules[].name
.
Type: DependencyFile[] | Record<keyof any, DependencyFile[]>
A list of DependencyFile
. The type will be based on the value passed to rules[].dependencies
.
type DependencyFile = {
path: string;
dir: string;
root: string;
base: string;
name: string;
ext: string;
content: Buffer;
stats: fs.Stats;
};
path
: an absolute path to the filedir
:dir
property of the return value ofpath.parse()
root
:root
property of the return value ofpath.parse()
base
:base
property of the return value ofpath.parse()
name
:name
property of the return value ofpath.parse()
ext
:ext
property of the return value ofpath.parse()
content
: contents of the filestats
:fs.Stats
object provides information about the file
Type: Record<keyof any, any>
The value passed to rules[].outputs[].context
.
Type: (...paths: string[]) => string
A reference to POSIX specification path.join()
.
Type: (to: string) => string
Returns a relative path from the output file.
Type: (path: string, extension: string) => string
Replaces the file extension with another one. The wrapper for replace-ext
module.
The core modules of Change Case and titleCase
are also available.