Giter Club home page Giter Club logo

promzard's Introduction

promzard

A prompting wizard for building files from specialized PromZard modules. Used by npm init.

A reimplementation of @SubStack's prompter, which does not use AST traversal.

From another point of view, it's a reimplementation of @Marak's wizard which doesn't use schemas.

The goal is a nice drop-in enhancement for npm init.

Usage

const promzard = require('promzard')
const data = await promzard(inputFile, optionalContextAdditions, options)

In the inputFile you can have something like this:

const fs = require('fs/promises')
module.exports = {
  "greeting": prompt("Who shall you greet?", "world", (who) => `Hello, ${who}`),
  "filename": __filename,
  "directory": async () => {
    const entries = await fs.readdir(__dirname)
    return entries.map(e => `entry: ${e}`)
  }
}

When run, promzard will display the prompts and resolve the async functions in order, and then either give you an error, or the resolved data, ready to be dropped into a JSON file or some other place.

promzard(inputFile, ctx, options)

The inputFile is just a node module. You can require() things, set module.exports, etc. Whatever that module exports is the result, and it is walked over to call any functions as described below.

The only caveat is that you must give PromZard the full absolute path to the module (you can get this via Node's require.resolve.) Also, the prompt function is injected into the context object, so watch out.

Whatever you put in that ctx will of course also be available in the module. You can get quite fancy with this, passing in existing configs and so on.

options.backupFile

Use the backupFile option as a fallback when inputFile fails to be read.

Class: promzard.PromZard(file, ctx, options).load()

Just like the promzard function, but the class that makes it all happen. The load method returns a promise which will resolve to the resolved data or throw with an error.

prompt(...)

In the promzard input module, you can call the prompt function. This prompts the user to input some data. The arguments are interpreted based on type:

  1. string The first string encountered is the prompt. The second is the default value.
  2. function A transformer function which receives the data and returns something else. More than meets the eye.
  3. object The prompt member is the prompt, the default member is the default value, and the transform is the transformer.

Whatever the final value is, that's what will be put on the resulting object.

Functions

If there are any functions on the promzard input module's exports, then promzard will await each of them. This way, your module can do asynchronous actions if necessary to validate or ascertain whatever needs verification.

The functions are called in the context of the ctx object.

In the async function, you can also call prompt() and return the result of the prompt.

For example, this works fine in a promzard module:

exports.asyncPrompt = async function () {
  const st = await fs.stat(someFile)
  // if there's an error, no prompt, just error
  // otherwise prompt and use the actual file size as the default
  return prompt('file size', st.size)
}

You can also return other async functions in the async function callback. Though that's a bit silly, it could be a handy way to reuse functionality in some cases.

Sync vs Async

The prompt() function is not synchronous, though it appears that way. It just returns a token that is swapped out when the data object is walked over asynchronously later, and returns a token.

For that reason, prompt() calls whose results don't end up on the data object are never shown to the user. For example, this will only prompt once:

exports.promptThreeTimes = prompt('prompt me once', 'shame on you')
exports.promptThreeTimes = prompt('prompt me twice', 'um....')
exports.promptThreeTimes = prompt('you cant prompt me again')

Isn't this exactly the sort of 'looks sync' that you said was bad about other libraries?

Yeah, sorta. I wouldn't use promzard for anything more complicated than a wizard that spits out prompts to set up a config file or something. Maybe there are other use cases I haven't considered.

promzard's People

Contributors

bmeck avatar danmactough avatar dependabot[bot] avatar eldinoyev avatar github-actions[bot] avatar isaacs avatar lukekarrys avatar michaelnisi avatar wraithgar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

promzard's Issues

Mistype in promzard.js

It seems that here is a mistype:

    d = a.default || d
    t = a.tranform || t
  }

tranform -> transform

promzard.PromZard fires data even when error occurred

Regarding the class promzard.PromZard the docs say:

Emits either a data event with the data, or a error event if it blows up. If error is emitted, then data never will be.

The following code throws an error in the input file. The result is that both error and data events are emitted.

const fs = require('fs')
const path = require('path')
const { inspect } = require('util')
const { PromZard } = require('promzard')

const inputFile = path.resolve(__dirname, 'input.js')
fs.writeFileSync(inputFile, `throw new Error('Break stuff')`)

const pz = new PromZard(inputFile, {})
pz.on('error', err => console.log('error: ' + err))
pz.on('data', data => console.log('data: ' + inspect(data)))

Output:

error: Error: Break stuff
data: {}

An example for effect in the wild: This bug causes init-package-json to continue wanting to write a file even after an error occurred during processing of the input file. Sample output using the input file from above with init-package-json:

Break stuff
About to write to /tmp/tmp.1uWPpEnFlC/package.json:

{
  "name": "",
  "version": ""
}


Is this OK? (yes) n
Aborted.

Missing license

Please add a license file to clarify what license this is distributed under.

I'd like to use node/npm for a project at work, but npm depends on this package and I'm not allowed to use software that doesn't have a clear license.

Thanks!

is there a way to validate and reprompt on bad input?

neat module @isaacs I'm wondering if there's a way to reprompt when a field fails validation? For instance example/npm-init/init-input.js reads version, but there's nothing preventing me from typing 'heh' at the prompt and having that accepted. I'd want to enforce semver for this field, and ditto for other field types. I looked at isaacs/npm/blob/master/lib/adduser.js to see how this was achieved, and notice it doesnt rely on promzard (uses your read package at a lower level.) Is this an intentional design decision, or what?

Conditional prompts and conditional object creation

I'm not seeing a way to prompt conditionally or conditionally create an object. As a concrete example of both, consider a fancier way of prompting for the repository information from npm init. First, we prompt for the repo type:

repository type:

Now, if the user enters a value, we would prompt for the repository itself:

git repository:
-or-
svn repository:

(Yes, I know having different prompts in this case is silly; it's just a convenient example.)

If the user entered no value for repository type, though, we would not prompt for the repository URL at all.

If the user then enters a URL, the result would be:

{ type: "git", url: "my git url" }

but if the user did not enter a URL (or did not enter a type), we would leave the result undefined (i.e. no object at all), so that none of the repository structure would show up in package.json.

Is there a way to do this?

[FEATURE] <title>

What / Why

n/a

When

  • n/a

Where

  • n/a

How

Current Behavior

  • n/a

Expected Behavior

  • n/a

Who

  • n/a

References

  • n/a

[BUG] <title>

What / Why

n/a

When

  • n/a

Where

  • n/a

How

Current Behavior

  • n/a

Steps to Reproduce

  • n/a

Expected Behavior

  • n/a

Who

  • n/a

References

  • n/a

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.