npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

drygen

v0.0.5

Published

The code generator that depends on specific files is designed to implement a Single Source of Truth (SSOT).

Downloads

87

Readme

drygen

The code generator that depends on specific files is designed to implement a Single Source of Truth (SSOT).

Overview

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 of DependencyFile. It contains a file path, contents, etc
  • relative: returns a relative path from the output file
  • join: a reference to POSIX specification path.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.

Installation

$ npm install --save-dev drygen

Command Line Interface

$ 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]

Configuration

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",
				},
			],
		},
	],
};

Options

rules

Type: object[]

Define an array of the rules for file generation.

rules[].name

Type: string

Will be output as logs in your terminal, and can be referenced from the templates.

rules[].dependencies

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); %>";
<% }); -%>

rules[].outputs

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 file
  • template: a path to the EJS template file, see templating section for details
  • context: an optional object passed to the templates file, can be referenced by context 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,
			};
		},
	],
}

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,
			};
		},
	],
}

Templating

drygen uses EJS as a template engine.

Variables

The following variables are exposed in the templates.

name

Type: string

The value passed to rules[].name.

dependencies

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 file
  • dir: dir property of the return value of path.parse()
  • root: root property of the return value of path.parse()
  • base: base property of the return value of path.parse()
  • name: name property of the return value of path.parse()
  • ext: ext property of the return value of path.parse()
  • content: contents of the file
  • stats: fs.Stats object provides information about the file

context

Type: Record<keyof any, any>

The value passed to rules[].outputs[].context.

join(...paths)

Type: (...paths: string[]) => string

A reference to POSIX specification path.join().

relative(to)

Type: (to: string) => string

Returns a relative path from the output file.

replaceExt(path, extension)

Type: (path: string, extension: string) => string

Replaces the file extension with another one. The wrapper for replace-ext module.

Change Case modules

The core modules of Change Case and titleCase are also available.