Build a login form
We are going to build step by step the Felwine generator app. Environment: Mac OS Sonoma
Generate the project​
Install [Node JS]​
We need to install Node JS first: Install Node JS
brew install node
Check if Node has been installed correctly:
node -v
Male sure npm is available as well:
npm -v
Step 2: Create a new Felwine app​
Let's generate a brand new app with the CLI:
npx felwine app new
The felwine will ask a series of questions:
Create the app new
command​
Add the command​
app new
will generate a new Felwine app project.
- Go to
src/commands
- Create the
src/commands/app
- Add a
src/commands/app/main.js
. This file is necessary for every command folder. It does not have to do anything.
export default ({
_ovideType: 'command',
position: 1,
name: 'app',
description: `App management 🤖`,
questions: [
],
example: "$0 app new --appName='MyApp'",
})
Create the project​
- Create a
src/commands/app/new.js
file. This file will hold the actual code for creating new project for the app and the extension.
import ChunkAppContent from '../../lib/chunks/app/content/index.js'
export default ({
_ovideType: 'command',
name: 'new',
description: `Generate a Felwine app 🚀`,
questions: [
],
example: "$0 app new --appID='MyApp'",
handler: async () => {
},
})
As you can see the command is quite empty at this point. Let's start by defining the questions we'll need from the user.
Craft questions​
For this command we'll need:
The id of the cli (the unique command line entry to the cli, the one we will use to call it)
The destination (folder)
The package manager the user wants to use (yarn, npm, pnpm)
Whether the user wants to install dependencies or not
Whether the user wants to initialize a git repository or not
Create a
src/questions/destination.js
file. This file will hold the question for the destination. We are going to use the same question for the extension project, so we are better off extracting it right away.
import path from "path"
export default ({
_ovideType: "question",
type: 'string',
alias: 'd',
message: "Choose a destination",
promptType: "file-tree-selection",
name: "destination",
onlyShowDir: true,
root: "./",
onlyShowValid: false,
enableGoUpperDirectory: true,
hideRoot: false,
hideChildrenOfValid: false,
hideValidationErrorMessage: true,
transformer: (name,) => {
if (!name || !name.length) {
return ''
}
const _name = name.split(path.sep).pop()
return `${_name}`
}
})
In this question we:
- Use the
file-tree-selection
promptType which allows us navigate in the local machine file system and pick a folder. - We only show folders (
onlyShowDir
) - Folders don't need to pass any validation test (
json onlyShowValid: false
)
That's it. The question can be used anywhere in the project by referring to its name (destination
).
Let's create the remaining questions the same way:
src/questions/gitInit.js
export default ({
name: 'gitInit',
type: 'boolean',
defaultValue: true,
message: `Initialize a git repository`,
promptType: 'confirm',
alias: 'g',
})
src/questions/installDependencies.js
export default ({
name: 'installDependencies',
type: 'boolean',
promptType: 'confirm',
alias: 'i',
defaultValue: true,
message: 'Install dependencies'
})
src/questions/packageManager.js
export default ({
name: 'packageManager',
type: 'string',
promptType: 'list',
alias: 'p',
defaultValue: 'npm',
message: "Package manager ('npm', 'yarn' or 'pnpm')",
choices: [{
name: 'npm',
value: 'npm'
}, {
name: 'yarn',
value: 'yarn'
}, {
name: 'pnpm',
value: 'pnpm'
}]
})
src/questions/license.js
export default ({
name: 'license',
type: 'string',
promptType: 'list',
alias: 'l',
defaultValue: 'MIT',
message: 'License',
description: '',
subMessage: '("MIT", ...)',
choices: [
"Apache 2.0",
"MIT",
"Mozilla Public License 2.0",
"BSD 2-Clause (FreeBSD) License",
"BSD 3-Clause (FreeBSD) License",
"Internet Systems Consortium (ISC) License",
"GNU AGPL 3.0",
],
})
Use questions in commands​
Now that we have defined most of the questions, we can use them in the command:
import ChunkAppContent from '../../lib/chunks/app/content/index.js'
export default ({
_ovideType: 'command',
name: 'new',
position: 0,
description: `Generate a Felwine app 🚀`,
questions: [
{
name: 'appID',
type: 'string',
promptType: 'input',
alias: 'n',
defaultValue: 'myappID',
message: 'App ID',
validators: [{
id: 'nonempty',
params: { maxParams: 50 }
}]
},
{
name: 'appDescription',
type: 'string',
promptType: 'input',
defaultValue: 'A Felwine app',
message: 'App description',
validators: [{
id: 'nonempty',
params: { maxParams: 12 }
}]
},
{
name: 'installDependencies',
},
{
name: 'license',
},
{
name: 'packageManager',
},
{
name: 'gitInit',
},
{
name: 'destination',
transformers: [{
modes: ['out'],
template: `<%= destination %>/<%= appID %>`
}]
// validators: [{ id: 'nonempty' }]
},
],
example: "$0 app new --appID='MyApp'",
handler: async () => {
},
})
- Note that we chose to keep the
appId
and ``appDescription inline because we don't have plans to reuse them anywhere else. Thus, their full definition is right here. - The question defined in
src/questions
are referenced by theirname
. Any property we might add here will override its corresponding value defined in thesrc/questions/...
file.
At this point we can see what the command help will look like:
npx felwine app new -h
Generate a Felwine app 🚀
Options:
-n, -n, --appID App ID [string] [default: "myappID"]
--appDescription App description [string] [default: "A Felwine app"]
-i, -i, --installDependencies Install dependencies [boolean] [default: true]
-l, -l, --license License [string] [default: "MIT"]
-p, -p, --packageManager Package manager ('npm', 'yarn' or 'pnpm') [string] [default: "npm"]
-g, -g, --gitInit Initialize a git repository [boolean] [default: true]
-d, -d, --destination Choose a destination [string]
Examples:
felwine app new --appID='MyApp'
Build the handler​
We can now build the command handler. Since we are going to generate a project, we'll need a good generator. We'll use the chunks extension that's made for this task. #TODO
Create the extension new
command​
#TODO
Release​
The project is available at https://github.com/felwine-org/generator