Building an App builder tool
Creating the same type of Vite-based applications is tedious and repetitive, even when it's the same Vite command. This post will cover one way to create a builder tool for Vite-based applications. It will also cover how to add utilities like Prettier, ESLint, and Playwright to the project. ## How The NPM [init](https://docs.npmjs.com/cli/v9/commands/npm-init) command allows you initialize remote packages, like a Vite project, either from an NPM package or a GitHub repository. For eaxmple, the following command will create a new Vite project using the React template: ```bash npm create vite@latest my-vite-app -- --template react ``` This command will create a new directory called `my-vite-app` and initialize it with the React template from Vite. This process has replaced the older `create-react-app` command. The process to create this builder package is to create it just like any other Node project. Because I'm using Typescript I will use `tsx` to run the code and `tsup` to transpile the code to Javascript that will be execute when we run the code through `npm init`. ### The script The script will ask the user a series of questions and then create a Vite project based on the answers. We will not include the templates for each project, but they will be included in the companion repository. In the first part of the script, we import all required modules and write functions to emulate CommonJS's `__filename` and `__dirname` variables that are not available in ES modules. ```typescript import { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync } from 'node:fs'; import { dirname, resolve, join } from 'node:path'; import { fileURLToPath } from 'url' import { Package, DependencyCollection } from "@manuth/package-json-editor"; import prompts from 'prompts'; // emulate CommonJS __filename & __dirname const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) ``` In the main function we create the prompts and add packages based on the results. We first select the framework: Vue, React, vanilla Typescript and vanilla Javascript. Vanilla Typescript and Javascript are not frameworks but are treated like one for this script. We also check if we want to use a route with Vue and React. We then ask the user if they want to install supporting libraries: Playwright for testing, Prettier for code formatting, and ESLint for code linting and fixing errors. The default is not to install any of them unless the user specifies otherwise. ```typescript async function main() { const cwd = process.cwd(); // 1. Select framework const { framework } = await prompts({ type: 'select', name: 'framework', message: 'Select a framework:', choices: [ { title: 'Vue', value: 'vue' }, { title: 'React', value: 'react' }, { title: 'Vanilla TS', value: 'ts' }, { title: 'Vanilla JS', value: 'js' } ], initial: 0 }); let useRouter = false; if (framework === 'vue' || framework === 'react') { const res = await prompts({ type: 'confirm', name: 'useRouter', message: `Install ${framework === 'vue' ? 'Vue Router' : 'React Router'}?`, initial: true }); useRouter = res.useRouter; } // 2. Tooling const { playwright, prettier, eslint } = await prompts([ { type: 'confirm', name: 'playwright', message: 'Install Playwright test?', initial: false }, { type: 'confirm', name: 'prettier', message: 'Install Prettier?', initial: false }, { type: 'confirm', name: 'eslint', message: 'Install ESLint?', initial: false } ]); ``` We create the `package.json` file using the `@manuth/package-json-editor` package. This package allows us to create a package.json file and add dependencies to it. We set up the initial set of packages that will be installed by default: Vite (the build system), Typescript (for all projects), and @types/node (to provide Typescript types for Node.js). We install the current version of the packages using the `>=` operator to install the latest version of each package to ensure that the project is always up to date, rather than pin to a specific version. We also configure the scripts for the package. These will be run with `npm run