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

@servant/servant-generators

v1.0.11

Published

Generator core for servant

Downloads

12

Readme

Servant Servant generators

Quick references: Command line, Node API, servant.json, dev-server

What is it?

Servants generators is a runner tool that is used to run custom or prepared generator code to generate, update or change some components, prepared piece of codes and another files into specified folder. Generator can create files, messages and data by answering questions that are generated or prepared by generator or another generators. These generators are possible to extend and use generator in generator.

Installation

1. Globally installed

You need the latest version of nodejs and then run:

npm install @servant/servant-generators -g

Then you can run in current directory:

sg

Caveats

Installing globally is not recommended.

2. Locally installed

You need the latest version of nodejs and then run:

npm install @servant/servant-generators

Then you can run in current directory:

"node_modules/.bin/sg"

3. Installed and init with npx

You need the latest version of nodejs and then run

npx @servant/servant-generators

This command run generators runtime.

Command line API

Simple generators command line api provide simple way how to run generators. You need to be in folder with generators or specified directories with generator and also output folder for generated content. For examples we will count with globally installed module.

Run without parameters

sg

This simple commands load generators from current folder or node_modules folder and generate outputs into ./.output folder.

Run with generators folders

sg /pth/to/dir1 /pth/to/dir2

sg --dir=/pth/to/dir1 --dir=/pth/to/dir2

sg /pth/to/dir1,/pth/to/dir2

sg --dir=/pth/to/dir1,/pth/to/dir2

This command load generators from current folder, node_modules folder and also /pth/to/dir1 and /pth/to/dir2 folders. Outputs will be generated into ./.output folder.

Run with output folder

sg /pth/to/dir1 /pth/to/dir2 --output=/pth/output

This command load generators from current folder, node_modules folder and also /pth/to/dir1 and /pth/to/dir2 folders. Outputs will be generated into /pth/output folder.

Run with predefined generator

sg /pth/to/dir1 /pth/to/dir2 --generator=generator-name

This command load generators from current folder, node_modules folder and also /pth/to/dir1 and /pth/to/dir2 folders and automatically run generator with name generator-name. If not found, there will be error reported. Outputs will be generated into ./.output folder.

Run with debug flag

sg /pth/to/dir1 /pth/to/dir2 --debug

This flag is used for reporting more info for errors for better debugging.

Node API + Ink React components

Main nodejs api is hidden in api property in module.

.loader method

api.loader(directories: Array<string>): Promise<GeneratorsList>

This method is used to loading generators from defined directories.

Method get list of directories and return GeneratorsList that contains list of all loaded generators and list of errors that occurred during loading. Result of this method is provided to next one, that is runner or multiRunner.

import { api } from "@servant/servant-generators";

api.loader(["path/to/generators1", "path/to/generators2"]).then((list: GeneratorsList) => {
	//some stuff
});

.runner method

api.runner<T, D>(previous: GeneratorResults<D>, input: GeneratorInput, generator: Generator<T, D>, props?: T): Promise<GeneratorRunnerResults<D>>

This method is used to run one generator and get results from it.

Method get previous results of previous generator as parameter previous. If you run first generator in row, it's possible to use define constant GeneratorEmptyResults that can be provided.

Next parameter is input that is basically loaded generator from loaded method, that contains questions, answers, messages and manifest file definition.

Last required parameter is generator that is function of generator to run and collect results.

Optional properties parameter props is used to provide some data into generator and used tma in generator function body.

import { api, GeneratorEmptyResults, Generator } from "@servant/servant-generators";

const generator: Generator = (data, fn, next, props) => {
	next("seccess");
};

api.runner(
	GeneratorEmptyResults,
	{ manifest, answers: [], questions: [], messages: [] },
	generator,
	{}
).then((list: GeneratorRunnerResults<D>) => {
	//some stuff
});

.multiRunner method

api.multiRunner<T, D>(previous: GeneratorResults<D>, loaded: GeneratorLoaded<T, D>, props?: T): Promise<GeneratorRunnerResults<D>>

This method is used to run one generator tha use another generators (by using use property in manifest) and get results from all.

Method get previous results of previous generator as parameter previous. If you run first generator in row, it's possible to use define constant GeneratorEmptyResults that can be provided.

Next parameter is loaded that is loaded generator from loaded method, that contains questions, answers, messages and manifest file definition.

Optional properties parameter props is used to provide some data into generator and used tma in generator function body.

import { api, GeneratorEmptyResults } from "@servant/servant-generators";

api.runner(GeneratorEmptyResults, loaded, {}).then((list: GeneratorRunnerResults<D>) => {
	//some stuff
});

.saver method

api.saver<D>(into: string | undefined, results: GeneratorResults<D>): Promise<GeneratorSaverResults>

This method is used to save all generated files and its content physical on disk by using result from runner and multiRunner

Parameter into used to set directory where generator generate content. This must be existing directory. Its possible to use also results.output property, that can be set by generator code inside.

Parameter results needs to get results that was return from runner or multiRunner functions.

import { api } from "@servant/servant-generators";

api.saver("path/to/output", results).then((list: GeneratorSaverResults) => {
	//some stuff
});

ink view Generator

This is view for rendering generator using ink. This method internally run api.runner method.

import { view, GeneratorEmptyResults } from "@servant/servant-generators";

<view.Generator
	previous={GeneratorEmptyResults} //OPTIONAL: previous generator results
	columns={[10, 20]} //OPTIONAL: sizes of columns in chars
	config={config} //config of generator
	manifest={manifest} //manifest file of generator
	generator={generator} //main function of generator
	use={use} //all another used generators
	props={props} //OPTIONAL: Custom props
	onDone={(results) => {}} //OPTIONAL: on done callbkac with results and helpers
/>;

ink view Generators

This is view for rendering generators list and select using ink. This method internally run api.loader method.

import { view, useGeneratorsList } from "@servant/servant-generators";

const { directories, status, list } = useGeneratorsList(["path/to/gen1", "path/to/gen2"]);

<view.Generators
	list={list} //list of all loaded generators
	status={status} //status of loading
	directories={directories} //all directories used for loading
	debug={false} //OPTIONAL: debug mode
	onSelect={(gen) => {}} // on generator select or "exit" string
	onErrors={(errors) => {}} //OPTIONAL: list of all errors occured during loading
/>;

ink view Generated

This is view for rendering save results using ink. This method internally run api.saver method.

import { view } from "@servant/servant-generators";

<view.Generated
	debug={false} //OPTIONAL: debug mode
	loaded={loadedGenerator} //root loaded generator that creates results
	results={results} //results of root generator
	onDone={(results) => {}} //OPTIONAL:save done and provide save results
/>;

ink view Main

This is view used for composing all previous components. Loads, select, runs and save generators results. All in one view.

import { view } from "@servant/servant-generators";

<view.Main
	dirs={["/path/to/generators"]} //list of directories with generators
	output="/path/to/output" //OPTIONAL: Directory where ouput will be generated, optional because can be set inside generator
	generator="generator-name" //OPTIONAL: Generator name tha will be run instantly if exists, selection will be skiped
	debug={false} //OPTIONAL: debug mode
	props={{}} //OPTIONAL: Custom props
	onDone={(results) => {}} //OPTIONAL: on done callback that provide saved results
	onErrors={(errors) => {}} //OPTIONAL: on errors callback that provide list of errors
	preparedQuestions={[]} //OPTIONAL:
/>;

Structure of generator, manifest and usage

Generator program can be written as typescript or javascript and need to have 2 required files with specified structure and content.

 .
 ├── generator-folder          # Folder with generator
 │   ├── manifest.json         # Manifest file with info about generator and entry data
 │   ├── index.js (index.ts)   # File that is named in manifest.json and is main entry file of generator
 │   └── ...                   # OPTIONAL: Other files that are imported by entry file
 └── ...

So there needs to be at lest 2 file in generator folder. 1) manifest.json that contains generator info and some run data that are used after generator loaded and 2) index.js or index.ts file that is used as entry and named in manifest.json.

Structure of manifest.json

{
	"engine": "servant-generator",
	"name": "my-app",
	"description": "My App",
	"entry": "index.js",
	"use": ["local:my-app-2"],
	"compiled": false
}

"engine": "servant-generator" required

This is a special property that is used to determine running engine. For now only valid value is "servant-generator". Every manifest, that has no this particular engine will not be marked as generator compatible manifest and will be ignored. This is because manifest json are used for more applications.

"name": string required

Name of generator that will be used in list of generators. Must be unique so its recommended to use prefixes and other stuff that can identify your generator if you choose to publish it.

"description": string required

Description of simple usage for generator. It's recommended to summarize base function of generator for user that want to use it.

"entry": string

This is main file of generator that will be loaded by Servant. Generator can be written in javascript or typescript sou you can use js or ts file in entry.

Default: ./index.js

"use": Array<string>

List of generators that are will be used and run before this one. It's good for composing more generators into one bigger. For now oly local generators are supported.

Local generators local: prefix

Local generators mean that's generator needs to be loaded with generator that use this generator. Will be matched by name and throw error if loader can not found this generator in list. Basically if you have dir1/generator1 which use dir2/generator2 by directive "use": ["local:generator2"] you must load generators from directory dir1 and from directory dir2. If not, loaded generates error that is not able to load generator1.

Default: []

Examples:

  • "use": ["local:my-app3", "local:my-app4"]

"compiled": boolean

This flag is optional and mark generator as already build, so Servant skip rebuilding of this generator and run it directly. Its boost load time and recommended to use this flag when publish generator into npm registry or some similar registry for packages.

Default: false

Structure of index.ts entry file

This is an entry file of generator and is used for setup generator by config and also implement core of generator that can create, update or delete files and other things to create what you need.

import { Generator, GeneratorConfig } from "@servant/servant-generators";

type CustomData = {
	//custom data object
};

//generator config object
export const config: GeneratorConfig = {
	//list of all questions that you need to get from user, generator will start asking and after all
	//responses will be answered
	questions: [
		{
			//type of question, can be "string" or "select"
			type: "string",
			//id of question, will be used for creating answers, must be unique
			id: "filename",
			//OPTIONAL: category of question, used for better visual experience, determine category of question
			category: "packagejson",
			//human redable question for user
			question: "File name to save?",
		},
		{
			//type of question, can be "string" or "select"
			type: "string",
			//id of question, will be used for creating answers, must be unique
			id: "version",
			//OPTIONAL: category of question, used for better visual experience, determine category of question
			category: "packagejson",
			//human redable question for user
			question: "What version to use?",
			//OPTIONAL: default preselected value of answer, will be used when user do not enter anything and proceesd to next question
			defaultValue: { label: "1.0.0", value: () => "1.0.0" },
			//OPTIONAL: some hits or examples for question answer
			tip: "Write version in semantic version system.",
			//OPTIONAL: This is unique key that can be used in generator, if ommited, id is used
			key: "version",
			//OPTIONAL: Conditions object that is used to determine when show question to user, will be described later in this file
			condition: {},
		},
		{
			//type of question, can be "string" or "select"
			type: "select",
			//id of question, will be used for creating answers, must be unique
			id: "type",
			//OPTIONAL: category of question, used for better visual experience, determine category of question
			category: "packagejson",
			//human redable question for user
			question: "What type will be used?",
			//OPTIONAL: some hits or examples for question answer
			tip: "Select type of project. This is similar to type package.json property.",
			//OPTIONAL: default preselected value of answer, will be used when user do not enter anything and proceesd to next question
			defaultValue: { label: "module", value: () => "module" },
			//List of all possible values for select
			values: [
				{ label: "module", value: () => "module" },
				{ label: "es5", value: () => "es5" },
				{ label: "es2020", value: () => "es2020" },
			],
			//OPTIONAL: Set limit of values that are displayd on screen, default 10
			limit: 10,
			//OPTIONAL: This is unique key that can be used in generator, if ommited, id is used
			key: "type",
			//OPTIONAL: Conditions object that is used to determine when show question to user, will be described later in this file
			condition: {},
		},
	],
	//list of all pre answer questions, use this to provide already answered questions and
	//user dont need to fill it again
	answers: [
		{
			//id of question to answer
			id: "filename",
			//values of answer
			value: "package.json",
		},
	],
	//list of all messages that will be showed when generator finish work
	messages: [
		{
			//id of message, must be unique
			id: "message1",
			//type of message, can by "info" | "warning" | "error" | "success"
			type: "success",
			//OPTIONAL: Title of message, will be show diffrent way than text
			title: "Created",
			//Text of message to show to user
			text: "File was successfully created.",
			//OPTIONAL: Conditions object that is used to determine when show message to user, will be described later in this file
			condition: {},
		},
	],
	//map of categories and category color
	categories: {
		//[key] is category name used in question.category property
		//[value] is category color compatible with cli colors
		packagejson: "yellow",
	},
};

//generator function
// Inti generator there is provided handlers for answer / question manipulation and also
// handlers for file and directories creating, updating and removing. Its possible to also set
// custom data or add new messages and answers / questions
//Function next => used for call to end generator resultion and move to next one or end of generation.
// This function get status as "success" or "error"
//Props => These are props that are provided into generator runner or into Main component of exterted package
export const generator: Generator<unknown, CustomData> = ({ getAnswer }, fn, next, props) => {
	const filename = getAnswer("filename")?.value;
	const version = getAnswer("version")?.value;
	const type = getAnswer("type")?.value;

	fn.createFile(
		"./" + filename,
		JSON.stringify({
			name: "my-cool-project",
			version,
			type,
		})
	);

	next("success");
};

How to use condition for questions?

Every question can also have defined conditions. This is object, that contains rules, when question will be server when condition is meet. If not, this question was never answer and generator skip it. This is good if more complex generator is created.

Basic setup of AND band OR condition

export const config: GeneratorConfig = {
	questions: [
		{
			//REST PROPS OF QUESTION OBJECT
			//...
			//This condition will be meet when target = "web" and entry = "true"
			condition: {
				and: {
					target: "web",
					entry: "true",
				},
			},
		},
		{
			//REST PROPS OF QUESTION OBJECT
			//...
			//This condition will be meet when target = "web" or entry = "true"
			condition: {
				or: {
					target: "web",
					entry: "true",
				},
			},
		},
		{
			//REST PROPS OF QUESTION OBJECT
			//...
			//This condition will be meet when (target = "web" and entry = "true") AND (force = "true" or skip = "true")
			condition: {
				and: {
					target: "web",
					entry: "true",
				},
				//AND => there is automatically AND operator
				or: {
					force: "true",
					skip: "true",
				},
			},
		},
	],
	//...
};

Complex setup of composed conditions

If key in or or and condition is object, its behave automatically as inner condition. So conditions can be nested.

export const config: GeneratorConfig = {
	questions: [
		{
			//REST PROPS OF QUESTION OBJECT
			//...
			//This condition will be meet when (target = "web" and entry = "true") OR (target = "page" and entry = "true")
			condition: {
				or: {
					//name of condition, its doesnt matter, its only for better understanding of inner condition
					target_is_web: {
						and: {
							target: "web",
							entry: "true",
						},
					},
					//name of condition, its doesnt matter, its only for better understanding of inner condition
					target_is_page: {
						and: {
							target: "page",
							entry: "true",
						},
					},
				},
			},
		},
	],
	//...
};

After setup this two files in directory, you can run sg /path/to/root/dir where directory with generator is and see that servant generators will run and give to a list of available generators.

Donate me

| QR | Paypal | | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | |

License

Licensed under MIT