@fabernovel/genesis

1.0.0 • Public • Published

Genesis

Quickly boostrap your next project with Fabernovel's opiniated template.

Getting started

To create a project named my-app, run the following command:

npx @fabernovel/genesis my-app

Answer the prompts and you will have a bootstrapped project with the selected technologies.

Contributing

Running the project locally

  1. Clone this repository
  2. Install dependencies with yarn install
  3. Run in seperate terminals both the cli and the generator file with the following commands: i. yarn build:cli ii. yarn build:generator
  4. To test the cli, run ./lib/index.js my-app from the root of this repository

Templater organisation

This templater is build on sao which is a scaffolding tool.

You can find the v2 documentation here.

There are 3 main components to this templater that builds around sao's api:

  1. cli
  2. template
  3. plugins

cli

The cli folder holds all files necessary for the execution of sao.

There are two main files, cli.ts which is the cli wrapper and saofile.ts which is the generator file, the file which describes all operations that need to be done in order to piece together the template and plugins.

Both files are compiled.

template

The template folder holds all mandatory files for the bootstrapped project.

For example, you will find nextjs, storybook, react-i18n, etc.

plugins

The plugins folder holds all optional files for the bootstrapped project.

They will be copied over based on the user's answers during the prompt phase.

However, some special files are not copied over (see Merging files).

They are handle manually. Refer to the filters defined in the add action for the selected plugins.

Creating a plugin

Here are the recommended steps if you want to create a plugin:

  1. Bootstrap your project with the templater
  2. Add your feature to your project
  3. List the files, dependencies that are affected by the addition of the feature

What we call a plugin is a directory containing all the files needed to setup a feature. All our plugins are located in the plugins directory at the root of the templater.

Let's create a plugin that adds KY and SWR to our project. We'll name it ky-swr.

Creating a Plugin Directory

First, let’s create a directory for our plugin inside our source's plugins directory.

mkdir plugins/ky-swr

Adding Plugin to the Prompts

In order for a user to be able to choose our plugin, we need to add the option in the prompts under the cli/prompts.ts file. In our case, the option goes under the fetching strategy question.

module.exports = {
    prompts: [
        {
            name: "fetching",
            message: "Select a fetching strategy",
            type: "select",
            choices: [
                { name: "ky-swr", message: "HTTP (Ky with SWR)" },
            ],
            default: "none",
        },
    ]
}

Make sure the name or value property for the choice is the same with the directory name you've created.

Extending json files (like package.json)

There are 3 json files that will be merged automatically if available in the plugin's folder:

  • package.json
  • babel.config.json
  • .config/eslint-extend-rules.json

For the example, let's create a package.json inside the plugin directory and add ky and swr packages. It will be merged with the template's package.json file.

{
  "dependencies": {
    "ky": "0.28.5",
    "ky-universal": "0.9.1",
    "swr": "0.5.6"
  }
}

Adding new files to template

Now, let's add all files necessary for this feature; they will be copied over to the template's folder structure as is. In our case, we need one file (useHttp.tsx) and we will place it under plugins/ky-swr/src/lib/http-client/useHttp.tsx.

Notice how useHttp.tsx is placed under src/lib/http-client. It will copied over to that location when bootstrapping.

import useSWR, { SWRConfiguration, SWRResponse } from 'swr'

export function useHttp<K extends readonly unknown[], D>(
  key: [...K] | (() => [...K] | null) | null,
  fn: (...args: [...K]) => D | Promise<D>,
  config?: SWRConfiguration<D, Error>,
): SWRResponse<D, Error>

Extending existing template files (with EJS)

The templater considers all files as EJS templates. It is useful if you need to modify/extend file conditionally between answers and other data.

Conditionally add code to template files

You can add content to a file from the template if a plugin is chosen by the user. See an example in src/domains/home/HomeScreen.tsx

        <% if (data_validation == "superstruct") { %>
        <a href="/form-example" className={styles.card}>
          <h2>Form example</h2>
          <p>How to use forms</p>
        </a>
        <% } %>

All named keys inside prompts.ts objects will be available in the context of ejs. For example:

  1. for a simple answer (select, boolean), you will have the value under the form "<question name>: "<selected choice name>"
  2. for a multi select, you will have the value under the form "<question name>": ["choice name 1", "choice name 2", ...]
Merge code in js/ts/tsx files

In the case where adding templating logic is not possible because multiple plugins affect the same area of code, we must use a different strategy. In cli/extend.ts, you will find an object where every plugin can specify code that will be injected in a predefined zone of selected file.

If you need to add a new file to the extend.ts file, ask yourself the use case cannot be solved with simple templating (if) or if multiple plugins will affect the same line of code.

Example with src/pages/_app.tsx :

import { appWithTranslation } from "@core/i18n/appWithTranslation";
import type { AppProps } from "next/app";
import "../styles/globals.css";
<%- _app.import.join("\n") _%>

function MyApp({ Component, pageProps, <%- _app.appProp.join(", ") _%>}: AppProps) {

<%- _app.inner.join("\n") %>
  return <Component {...pageProps} />;
}

export default appWithTranslation(MyApp);

You can see above that 3 zones can be extended by plugins: import, appProp and inner.

You can find an example in plugins/dayjs/extend.js to see how you can add code inside those zones.

SAO's behavior

Merging files

By default, SAO does not have a merge action. It can only do basic CRUD operations.

As such, we mush handle the merge of files ourselves.

For merging code files (eg. .ts & .tsx), see Templating of copied files.

For merging simple data structures like JSON, one must specifically handle that file (see how package.json is handled).

Templating of copied files

In the saofile.ts, you will find numerous actions. All files going through SAO are considered as EJS templates.

In order to merge code from the template and plugins folder for .ts and .tsx files, it is recommended to use an ejs tag.

Adding new features

Default/Mandatory feature

If you want to add a feature that is mandatory, you must add it to the template folder.

Please make sure that you do not break compability with plugins.

Optional feature

Here are the steps to add an optional feature.

  1. Create a folder under plugins. Eg. plugins/tailwindcss
  2. Setup all mandatory files that will be copied over with the template folder structure
  3. Add an answer to the corresponding section or add a new prompt to prompts.ts. The name must be the same as the folder name (eg. tailwindcss)
  4. (optional) You may have to handle specific merging capabilities depending on the feature

Package Sidebar

Install

npm i @fabernovel/genesis

Weekly Downloads

0

Version

1.0.0

License

MIT

Unpacked Size

4.21 MB

Total Files

226

Last publish

Collaborators

  • thomasgiscos
  • gautierb
  • williamgoulois
  • cezarion
  • bastien.gatellier.fabernovel
  • arnaud.mombrial.npmjs
  • victorchabbert