A partial re-write

Back

Formidable has been in the works for some time now. Everytime I use it, I always find an easy or better way of implementing some of its features.

In the past week I made some important changes to the framework's core, and in this article I will be discussing these new changes.

New Installer

The Formidable installer is no longer a part of the craftsman cli, instead, its a standalone package which you can install independently of the craftsman cli which makes it much easier to maintain.

The new and improved installer allows you to base off your new application on the dev branch and you can now install a new application in the current directory.

The onboarding process has been improved to include inertia:

formidable-onboarding

The new installer now lets you select your preferred frontend framework during the onboarding process.

React and Vue use Inertia under the hood, while Imba is native to Formidable. When choosing Imba, you also get to choose between creating a Single-page Application and a Blank canvas. The Single-page Application will add a basic scaffolding for SPA.

To install the new Formidable installer, run:

npm i -g @formidablejs/installer

New CLI (craftsman)

The new craftsman CLI has been built into the framework's core, making it easy to create Formidable plugins that register new commands.

This new cli is heavily inspired by Laravel's own artisan cli, borrowing a lot of the Laravel commands such as the down and up commands.

formidable-craftsman

Note, now you need to run the craftsman cli with node. To see the complete commands list, run: node craftsman.

To create a new command, simply run:

node craftsman make:command Hello

This will create a new Hello.imba class under app/Console/Commands directory.

Now, you will need to register your newely created command. To do this, open the app/Console/Kernel.imba class and import your comnmand then register it:

import { ConsoleKernel } from '@formidablejs/framework'
import { Hello } from './Commands/Hello'
export class Kernel < ConsoleKernel
get registered
[
Hello
]

Once done, run node craftsman hello and you should see Hello World.

To edit your new command, open app/Console/Commands/Hello.imba:

import { Command } from '@formidablejs/framework'
import { Prop } from '@formidablejs/framework'
export class Hello < Command
get signature
'hello'
get description
'My command description'
get props
{
}
def handle
self.write "<fg:green>Hello World</fg:green>"

The handle function is what gets executed when the command is ran.

To accept arguments, we can add them in the signature and wrap them around curly braces:

get signature
'hello {name}`

To read our new name argument, we can use the argument function:

def handle
self.write "<fg:green>Hello {argument('name')}</fg:green>"

Then run:

node craftsman hello Donald

We will then see hello Donald.

Now, lets make the name argument optional and give it a description:

import { Command } from '@formidablejs/framework'
import { Prop } from '@formidablejs/framework'
export class Hello < Command
get signature
'hello {?name}'
get description
'My command description'
get props
{
name: Prop.string!.description('Your name')
}
def handle
self.write "<fg:green>Hello {argument('name', 'Stranger')}</fg:green>"

Results:

hello-command

There's a lot you can do with craftman commands, we can require options (e.g. --read. etc) and document our arguments or options.

Interactive Shell

Another cool addition/fix, is the interactive shell. This has always been available but it wasn't working as intended. Now that Formidable's cli "craftsman" works directly with the codebase, it means the interactive shell, can easily read the application's context. e.g. We can pass the User model into the context, and the interactive shell will be able to access it.

To play around witht the shell, just run:

node craftsman shell

If you would like to access any of your code from the shell, just import the code in the context config:

import { User } from '../app/Models/User'
export {
User: User
}

interactive-shell

As you can see, the interactive shell is great for tinkering with your database data without too much hassle.

Testing

This is by far my favorite update/feature. Formidable now lets you write your tests using Imba. Why is this a big deal? Well, previously you would write your tests in JavaScript, this was a bit strange considering the fact that Formidable is a Imba framework. So now that you can write your tests in Imba, it means we no longer have to create a JavaScript build of your application before running tests.

A typical test now looks like:

const { current } = require '../storage/framework/address.json'
const { SuperTest } = require 'supertest'
const request = require 'supertest'
describe 'Application (e2e)', do
# @type {SuperTest}
let app
beforeAll do app = request current
it '/ (GET: Hello World)', do
app.get('/')
.set('Accept-Language', 'en')
.expect(200)
.expect('Hello World')
it '/ (GET: Hola Mundo)', do
app.get('/')
.set('Accept-Language', 'es')
.expect(200)
.expect('Hola Mundo')

And previously looked like:

const { Application, request } = require('../.formidable/server.app');
const { helpers: { config } } = require('@formidablejs/framework');
/**
* Skip if not in testing environment
*/
const maybe = config('app.env') === 'testing'
? describe
: describe.skip
maybe('Application (e2e)', () => {
let app;
beforeAll(async () => {
await Application.then((formidable) => {
(app = formidable.fastify()).ready();
});
});
afterAll(async () => await app.close());
it('/ (GET: Hello World)', () => {
return request(app.server)
.get('/')
.set('Accept-Language', 'en')
.expect(200)
.expect('Hello World');
});
it('/ (GET: Hola Mundo)', () => {
return request(app.server)
.get('/')
.set('Accept-Language', 'es')
.expect(200)
.expect('Hola Mundo');
});
})

Because we no longer build Formidable projects, it means before testing your endpoints, you need to run your server with the --addr flag:

node craftsman serve --addr

This creates a address.json file under the storage/framework directory with the current address.

That's all for now.

© Donald Pakkies.
github