diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..65d46c9 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,23 @@ +{ + "recommendations": [ + "aaron-bond.better-comments", + "astro-build.houston", + "biomejs.biome", + "bradlc.vscode-tailwindcss", + "charliermarsh.ruff", + "chunsen.bracket-select", + "davidanson.vscode-markdownlint", + "dbaeumer.vscode-eslint", + "fabiospampinato.vscode-open-multiple-files", + "github.github-vscode-theme", + "lokalise.i18n-ally", + "mikekscholz.pop-icon-theme", + "ms-python.python", + "neptunedesign.vs-sequential-number", + "streetsidesoftware.code-spell-checker", + "unifiedjs.vscode-mdx", + "usernamehw.errorlens", + "usernamehw.remove-empty-lines", + "yzhang.markdown-all-in-one" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6acd833 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,74 @@ +{ + "compounds": [ + { + "configurations": ["Launch Next.js"], + "name": "Launch Next.js" + }, + { + "configurations": ["Launch Next.js", "Launch Next.js in Chrome"], + "name": "Launch Next.js and Chrome" + } + ], + "configurations": [ + { + "name": "Attach by Process ID", + "processId": "${command:PickProcess}", + "request": "attach", + "restart": true, + "type": "node" + }, + { + "console": "internalConsole", + "env": { + "NODE_OPTIONS": "--inspect" + }, + "name": "Launch Next.js", + "program": "${workspaceFolder}/node_modules/next/dist/bin/next", + "request": "launch", + "sourceMaps": true, + "trace": true, + "type": "node" + }, + { + "name": "Launch Next.js in Chrome", + "request": "launch", + "type": "chrome", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + }, + { + "args": ["--runInBand"], + "envFile": "${workspaceFolder}/.env", + "name": "Launch All Jest Tests", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "request": "launch", + "type": "node" + }, + { + "args": ["${relativeFile}"], + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "name": "Launch Current Jest Test", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "request": "launch", + "type": "node" + }, + { + "console": "integratedTerminal", + "env": { + "NODE_OPTIONS": "--loader=tsx" + }, + "name": "Launch Ava Test (experimental)", + "outputCapture": "std", + "program": "${workspaceFolder}/node_modules/ava/entrypoints/cli.js", + "request": "launch", + "runtimeArgs": ["${file}", "--break", "debug"], + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ava", + "runtimeVersion": "20.10.0", + "skipFiles": ["/**/*.js"], + "type": "node" + } + ], + "type": "commonjs", + "version": "0.2.0" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..41eeb3c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,454 @@ +{ + "[appts.reliverse]": { + "vscodePreset": "default" + }, + "[css]": { + "editor.defaultFormatter": "vscode.css-language-features" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json5]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[markdown]": { + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + }, + "[mdx]": { + "editor.wordWrap": "on" + }, + "[python]": { + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[yaml]": { + "editor.defaultFormatter": "redhat.vscode-yaml" + }, + "biome.enabled": true, + "breadcrumbs.enabled": true, + "cSpell.enabled": true, + "cSpell.userWords": [ + "Menlo", + "Monaspace", + "astro", + "biomejs", + "dotjs", + "mjml", + "pwsh", + "quickfix", + "rgba", + "tson" + ], + "cSpell.words": ["callout", "combobox", "reliverse", "utapi"], + "css.lint.important": "ignore", + "css.lint.unknownAtRules": "ignore", + "debug.toolBarLocation": "docked", + "diffEditor.experimental.showMoves": true, + "diffEditor.hideUnchangedRegions.enabled": true, + "editor.acceptSuggestionOnCommitCharacter": true, + "editor.acceptSuggestionOnEnter": "on", + "editor.bracketPairColorization.enabled": true, + "editor.bracketPairColorization.independentColorPoolPerBracketType": true, + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.addMissingImports": "never", + "source.fixAll": "never", + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never", + "source.organizeImports.biome": "never", + "source.removeUnused": "never" + }, + "editor.colorDecorators": true, + "editor.cursorBlinking": "phase", + "editor.cursorSmoothCaretAnimation": "on", + "editor.cursorStyle": "line", + "editor.defaultFormatter": "biomejs.biome", + "editor.detectIndentation": false, + "editor.formatOnPaste": false, + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.formatOnType": false, + "editor.guides.bracketPairs": true, + "editor.guides.indentation": false, + "editor.hideCursorInOverviewRuler": false, + "editor.inlayHints.enabled": "offUnlessPressed", + "editor.inlineSuggest.enabled": true, + "editor.inlineSuggest.showToolbar": "always", + "editor.insertSpaces": true, + "editor.linkedEditing": true, + "editor.minimap.autohide": false, + "editor.minimap.enabled": true, + "editor.minimap.renderCharacters": false, + "editor.multiCursorModifier": "alt", + "editor.parameterHints.enabled": true, + "editor.quickSuggestions": { + "comments": true, + "other": true, + "strings": true + }, + "editor.quickSuggestionsDelay": 10, + "editor.rulers": [54, 74, 119], + "editor.smoothScrolling": true, + "editor.snippets.codeActions.enabled": true, + "editor.stickyScroll.enabled": true, + "editor.suggest.insertMode": "insert", + "editor.suggest.localityBonus": true, + "editor.suggestOnTriggerCharacters": true, + "editor.suggestSelection": "first", + "editor.tabCompletion": "off", + "editor.tabSize": 2, + "editor.tokenColorCustomizations": { + "comments": "#746f68" + }, + "editor.unicodeHighlight.allowedCharacters": { + "a": true, + "І": true, + "А": true, + "В": true, + "Е": true, + "З": true, + "Н": true, + "О": true, + "Р": true, + "С": true, + "Т": true, + "У": true, + "а": true, + "б": true, + "г": true, + "е": true, + "з": true, + "н": true, + "о": true, + "р": true, + "с": true, + "у": true, + "і": true, + "ا": true, + "ه": true, + "–": true + }, + "editor.unicodeHighlight.allowedLocales": { + "tr": true + }, + "editor.wordBasedSuggestions": "currentDocument", + "eslint.codeActionsOnSave.mode": "problems", + "eslint.enable": true, + "eslint.format.enable": true, + "eslint.ignoreUntitled": true, + "eslint.lintTask.enable": true, + "eslint.lintTask.options": ".", + "eslint.probe": [ + "MDX", + "astro", + "github-actions-workflow", + "html", + "javascript", + "javascriptreact", + "json", + "json5", + "jsonc", + "markdown", + "toml", + "typescript", + "typescriptreact", + "yaml" + ], + "eslint.rules.customizations": [ + { + "rule": "@stylistic/*", + "severity": "off" + }, + { + "rule": "perfectionist/*", + "severity": "off" + }, + { + "rule": "@typescript-eslint/consistent-type-imports", + "severity": "info" + }, + { + "rule": "import-x/newline-after-import", + "severity": "info" + }, + { + "rule": "readable-tailwind/*", + "severity": "info" + }, + { + "rule": "curly", + "severity": "info" + }, + { + "rule": "no-lonely-if", + "severity": "info" + }, + { + "rule": "@stylistic/linebreak-style", + "severity": "info" + }, + { + "rule": "jsonc/indent", + "severity": "info" + }, + { + "rule": "RULES-BELOW-ARE-NOT-AUTOFIXABLE", + "severity": "warn" + }, + { + "rule": "@typescript-eslint/no-unused-vars", + "severity": "warn" + }, + { + "rule": "@stylistic/no-tabs", + "severity": "error" + }, + { + "rule": "@stylistic/no-mixed-spaces-and-tabs", + "severity": "error" + }, + { + "rule": "@stylistic/no-mixed-operators", + "severity": "error" + }, + { + "rule": "@stylistic/max-statements-per-line", + "severity": "error" + }, + { + "rule": "@stylistic/max-len", + "severity": "error" + }, + { + "rule": "@stylistic/line-comment-position", + "severity": "error" + }, + { + "rule": "@stylistic/jsx-pascal-case", + "severity": "error" + }, + { + "rule": "@stylistic/jsx-child-element-spacing", + "severity": "error" + } + ], + "eslint.timeBudget.onFixes": { + "error": 25000, + "warn": 25000 + }, + "eslint.timeBudget.onValidation": { + "error": 25000, + "warn": 25000 + }, + "eslint.validate": [ + "MDX", + "astro", + "github-actions-workflow", + "html", + "javascript", + "javascriptreact", + "json", + "json5", + "jsonc", + "markdown", + "toml", + "typescript", + "typescriptreact", + "yaml" + ], + "extensions.ignoreRecommendations": false, + "files.associations": { + "*.json": "json", + "*.mdx": "mdx", + "*.toml": "properties", + "*.txt": "plaintext", + ".env.example": "properties" + }, + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "git.autofetch": true, + "git.confirmSync": false, + "git.enableSmartCommit": true, + "git.openRepositoryInParentFolders": "never", + "html.format.indentInnerHtml": true, + "i18n-ally.displayLanguage": "en-US", + "i18n-ally.enabledFrameworks": ["general", "next-intl", "react"], + "i18n-ally.keysInUse": [ + "Manifest.name", + "faq.1.details", + "faq.1.summary", + "faq.2.details", + "faq.2.summary", + "faq.3.details", + "faq.3.summary", + "name" + ], + "i18n-ally.keystyle": "nested", + "i18n-ally.localesPaths": ["messages/default"], + "i18n-ally.sourceLanguage": "en-US", + "javascript.format.semicolons": "insert", + "javascript.inlayHints.enumMemberValues.enabled": true, + "javascript.inlayHints.functionLikeReturnTypes.enabled": true, + "javascript.inlayHints.parameterNames.enabled": "all", + "javascript.inlayHints.parameterTypes.enabled": true, + "javascript.preferences.importModuleSpecifier": "non-relative", + "javascript.updateImportsOnFileMove.enabled": "always", + "json.validate.enable": false, + "markdown.extension.preview.autoShowPreviewToSide": false, + "markdown.preview.scrollEditorWithPreview": true, + "markdown.preview.scrollPreviewWithEditor": true, + "markdownlint.config": { + "MD033": false, + "MD041": false + }, + "mdx.server.enable": true, + "openMultipleFiles.exclude": [ + "**/*.gif", + "**/*.jpeg", + "**/*.png", + "**/*.svg", + "**/*.txt", + "**/.git", + "**/.idea", + "**/.million", + "**/.next", + "**/.nyc_output", + "**/.pnp.*", + "**/.turbo", + "**/.venv", + "**/.yarn", + "**/build", + "**/cluster/deprecated", + "**/coverage", + "**/dist", + "**/dist-dev", + "**/fixture", + "**/node_modules", + "**/package-lock.json", + "**/public", + "**/target", + "**/yarn.lock", + "**/yarn-error.log", + "addons/.output" + ], + "openMultipleFiles.ignore": [".gitignore"], + "openMultipleFiles.limit": 1000, + "outline.problems.badges": false, + "prettify-ts.viewNestedTypes": true, + "problems.defaultViewMode": "table", + "problems.showCurrentInStatus": true, + "problems.sortOrder": "position", + "ruff.lineLength": 88, + "ruff.nativeServer": true, + "search.exclude": { + "**/*.lock": true, + "**/.eslintcache": true, + "**/.idea": true, + "**/.next": true, + "**/.pnp.*": true, + "**/.venv": true, + "**/.yarn": true, + "**/build": true, + "**/dist": true, + "**/next-env.d.ts": true, + "**/package-lock.json": true, + "**/pnpm-lock.yaml": true, + "**/reset.d.ts": true, + "**/tsconfig.tsbuildinfo": true, + "**/yarn-error.log": true + }, + "search.useIgnoreFiles": false, + "tailwindCSS.classAttributes": ["class", "className", "classNames"], + "tailwindCSS.experimental.classRegex": [ + ["(?:'|\"|`)([^']*)(?:'|\"|`)", "cx\\(([^)]*)\\)"], + ["[\"'`]([^\"'`]*).*?[\"'`]", "cva\\(([^)]*)\\)"] + ], + "terminal.integrated.allowedLinkSchemes": [ + "C", + "file", + "http", + "https", + "mailto", + "vscode", + "vscode-insiders" + ], + "terminal.integrated.smoothScrolling": true, + "totalTypeScript.hideAllTips": true, + "totalTypeScript.hideBasicTips": true, + "tsEssentialPlugins.arrayMethodsSnippets.enable": true, + "tsEssentialPlugins.fixSuggestionsSorting": true, + "tsEssentialPlugins.globalLibCompletions.action": "mark", + "tsEssentialPlugins.jsxEmmet.modernize": true, + "tsEssentialPlugins.outline.arraysTuplesNumberedItems": true, + "tsEssentialPlugins.patchOutline": true, + "tsEssentialPlugins.renameImportNameOfFileRename": true, + "tsEssentialPlugins.signatureHelp.excludeBlockScope": true, + "tsEssentialPlugins.skipNodeModulesReferences": true, + "tsEssentialPlugins.suggestions.localityBonus": true, + "tsEssentialPlugins.tupleHelpSignature": true, + "typescript.enablePromptUseWorkspaceTsdk": true, + "typescript.format.semicolons": "insert", + "typescript.inlayHints.enumMemberValues.enabled": true, + "typescript.inlayHints.parameterNames.enabled": "all", + "typescript.preferences.autoImportFileExcludePatterns": [ + "next/dist/client/router.d.ts", + "next/router.d.ts" + ], + "typescript.preferences.importModuleSpecifier": "non-relative", + "typescript.preferences.includePackageJsonAutoImports": "on", + "typescript.referencesCodeLens.enabled": true, + "typescript.reportStyleChecksAsWarnings": true, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.updateImportsOnFileMove.enabled": "always", + "typescript.validate.enable": true, + "window.autoDetectColorScheme": true, + "window.commandCenter": true, + "window.title": "${dirty}${folderName}${separator}${activeEditorMedium}", + "workbench.colorCustomizations": { + "terminal.ansiBlack": "#21222c", + "terminal.ansiBlue": "#bd93f9", + "terminal.ansiBrightBlack": "#8d92ff", + "terminal.ansiBrightBlue": "#bd93f9", + "terminal.ansiBrightCyan": "#2cccff", + "terminal.ansiBrightGreen": "#20E3B2", + "terminal.ansiBrightMagenta": "#ff6bcb", + "terminal.ansiBrightRed": "#ff7979", + "terminal.ansiBrightWhite": "#ffffff", + "terminal.ansiBrightYellow": "#EAC394", + "terminal.ansiCyan": "#8be9fd", + "terminal.ansiGreen": "#20e3b2", + "terminal.ansiMagenta": "#ff6bcb", + "terminal.ansiRed": "#ff5555", + "terminal.ansiWhite": "#f8f8f2", + "terminal.ansiYellow": "#fde181", + "terminalCursor.background": "#868690", + "terminalCursor.foreground": "#43444D" + }, + "workbench.editor.enablePreview": false, + "workbench.layoutControl.enabled": true, + "workbench.panel.defaultLocation": "bottom", + "workbench.statusBar.visible": true +} diff --git a/README.md b/README.md index 6969ec5..2e85682 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,178 @@ *Build the Sites. Build the Apps. Build the Games. Build Anything.* +**Reliverse CLI**, or simply **Reliverse**, is a command-line tool designed to streamline the setup and configuration of JavaScript/TypeScript projects (for now), with a focus on React and Next.js templates. It allows you to effortlessly bootstrap projects, including the [Relivator Next.js template](https://github.com/blefnk/relivator-nextjs-template), or any other template from GitHub. Reliverse also assists in managing configuration files and resolving potential conflicts between different tools like ESLint, Prettier, and Biome. + ## TL;DR -At its current stage, Reliverse CLI is a powerful website builder right in your terminal. You can start from scratch or with a template, setting everything up automatically or customizing it to your exact preferences. With all the tools pre-configured and ready to go, you can build exactly what you envision. +**It's a single tool for everything.** At its current stage, Reliverse CLI is a powerful CLI website builder, means project bootstrapper, right in your terminal. But it's not going to be just a website builder in the future. And even already you can start from scratch or with a template, setting everything up automatically or customizing it to your exact preferences. With all the tools pre-configured and ready to go, you can build exactly what you envision. + +Remember the feeling of empowerment when you first used a website builder like WordPress? It gave you the freedom to create. But eventually, you hit limits—PageSpeed Insights flagged issues, the performance lagged, and the bloated size of your site became hard to ignore. + +*That’s where Reliverse comes in.* Reliverse is designed to fix the problems of traditional website builders, offering a universal toolset that lets you create anything you can imagine—efficiently and with ease. ## Get Started -**Prerequisites**: +Reliverse is still in its early stages, but it already allows you to bootstrap websites quickly. Soon, advanced customization options will be available, along with game-building tools and other exciting features. You're going to love what's coming. + +By the way, you might imagine that a CLI doing so many things would become bloated, like an elephant in the room, but don’t worry—it’s going to be lean. This is the dream of a creator, a dream that must become reality. Everything has to be perfect. + +*Psst... We’re already working on a frontend version of the builder too!* 😉 + +## Installation + +You should install [Git](https://git-scm.com), [VSCode](https://code.visualstudio.com), and [Node.js LTS](https://nodejs.org/en/download/package-manager) first, then use: -- [Node.js](https://nodejs.org/en/download/package-manager) -- [bun](https://bun.sh) or [pnpm](https://pnpm.io/installation#using-corepack) +- [bun](https://bun.sh) i -g reliverse +- or: [pnpm](https://pnpm.io/installation#using-corepack) add -g reliverse +- or: [yarn](https://yarnpkg.com) global add reliverse +- or: [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) i -g reliverse -**Installation**: +## Usage + +Once installed, you can use **Reliverse CLI** to create new projects or manage existing ones. Navigate to the root of your desired directory and run: ```bash -bun i -g reliverse -# or -pnpm i -g reliverse +reliverse ``` -**Usage**: +## Features + +- **Create Projects**: Build new projects from scratch, including your own version of **Relivator Next.js template**, or install any other templates from GitHub. +- **Support for JavaScript/TypeScript Projects**: Primarily designed for React and Next.js projects, but works with other JavaScript/TypeScript libraries as well. +- **Automatic Configuration Management**: Handles project configuration, including ESLint, Biome, Putout, GitHub, and IDE settings. +- **Conflict Resolution**: Detects existing configurations and helps resolve file conflicts, giving you control over which files to keep or replace. +- **Interactive Prompts**: Customize your setup through interactive prompts that allow you to select which file categories to include. +- **Template-Driven**: Automatically clones and installs templates from GitHub to kickstart your development. +- **Unlimited possibilities**: It can work not only with templates! Are you going to clone a JS library? Feel free to use Reliverse! +- **Anything else?!**: Currently, the CLI is optimized for JS/TS projects (like React, Astro, Vue, Svelte, etc.). But in the future, it will be able to work even with native video game applications! This is the dream of Reliverse's founder–a single tool designed for everything–for anything! + +### Commands + +The Reliverse CLI provides a series of interactive prompts to guide you through the project setup: + +1. **Create a New Project**: Build from scratch or use predefined templates. +2. **Install GitHub Templates**: Install any JavaScript/TypeScript project by providing a GitHub repository URL. +3. **Manage Configurations**: The CLI checks for existing configuration files and assists you in handling any conflicts. +4. **File Categories**: You can select from a variety of configuration categories for your project setup, such as ESLint, Biome, Putout, GitHub settings, and IDE preferences. +5. **Post-editing**: Reliverse never leaves you, if you wish it to. It holds your hand throughout your entire development journey. That's why you can run `reliverse` even in an already installed project. If your project's `package.json` contains an `appts` script, that's also a local piece of `reliverse` within your project. + +### Example Workflow + +Here’s an example session of using **Reliverse CLI**: ```bash -reliverse +$ reliverse + +? How do you want to proceed? + 1. I want to build my own Relivator from scratch + 2. I just want to install a template from GitHub + +? Select the file categories you want to download: + ◉ eslint, biome, putout + ◉ GitHub + ◉ IDE + ◉ Reliverse configs + +? Do you want to replace all existing files? (N opens Conflict Management menu) (y/N) ``` -## A Website Builder for the Modern Age +## Configuration Categories -Remember the feeling of empowerment when you first used a website builder like WordPress? It gave you the freedom to create. But eventually, you hit limits—PageSpeed Insights flagged issues, the performance lagged, and the bloated size of your site became hard to ignore. +When setting up a project, you can choose from the following file categories: -*That’s where Reliverse comes in.* Reliverse is designed to fix the problems of traditional website builders, offering a universal toolset that lets you create anything you can imagine—efficiently and with ease. +1. **eslint, biome, putout** + - `.eslintrc.js`, `biome.json`, `.putout.json` +2. **GitHub** + - `.github`, `README.md` +3. **IDE** + - `.vscode` +4. **Reliverse configs** + - `reliverse.config.ts`, `reliverse.info.ts` -Reliverse is still in its early stages, but it already allows you to bootstrap websites quickly. Soon, advanced customization options will be available, along with game-building tools and other exciting features. You're going to love what's coming. +## Conflict Management -By the way, you might imagine that a CLI doing so many things would become bloated, like an elephant in the room, but don’t worry—it’s going to be lean. This is the dream of a creator, a dream that must become reality. Everything has to be perfect. +**Reliverse CLI** helps you handle configuration conflicts for existing files such as `.eslintrc.cjs` or `prettier.config.js`. It prompts you with options to: -*Psst... We’re already working on a frontend version of the builder too!* 😉 +- **Remove**: Delete the existing file. +- **Rename**: Rename the file (e.g., add `.txt` to disable it). +- **Do nothing**: Keep the existing file. + +### Conflict Example + +```bash +? .eslintrc.cjs file exists. Do you want to remove or rename it? + 1. Remove + 2. Rename to .eslintrc.cjs.txt + 3. Do nothing +``` + +### Prettier Conflict Example + +```bash +? prettier.config.js found. Biome will be installed, so Prettier is not necessary. + 1. Remove + 2. Rename to prettier.config.js.txt + 3. Do nothing +``` + +## Installing Other Templates + +You can install any JavaScript/TypeScript project (it is not limited to e.g. Next.js templates, it can be anything, even js libs) from a GitHub repository by providing the repository URL during the interactive setup: + +```bash +$ reliverse + +? How do you want to proceed? + 1. I want to build my own Relivator from scratch + 2. I just want to install a template from GitHub + 3. I want to clone a library/tool from the GitHub + +? Enter the GitHub repository link: (e.g., `https://github.com/user/repo`) +``` + +Reliverse will then clone the repository and set up the project. + +## Development + +### Clone the Repository + +#### Reliverse's Methond + +Just use Reliverse itself to install Reliverse locally 😄 + +Visit [Installation](#installation) section and choose `Tools Installation` when select `Clone Reliverse Repository for Local Development`. + +#### Classical Method + +To contribute to **Reliverse CLI**, you can clone the repository and install the dependencies: + +```bash +git clone https://github.com/reliverse/cli.git +cd reliverse +bun i # OR pnpm i OR yarn install OR npm i +``` + +### Running Locally + +To run the CLI locally for development purposes, use: + +```bash +bun dev +# or +pnpm dev +# or +yarn dev +# or +npm dev +``` + +## Contributing + +We welcome contributions! Feel free to open issues or submit pull requests. Please make sure your code passes all tests and follows our linting guidelines, by just running `bun appts`, before submitting. + +Reliverse uses a different, non-standard approach compared to other bootstrappers. The author of this project has seen many CLIs that handle project bootstrapping. Many of them are truly cool. But their repositories contain a ton files that are later bundled into a single index.js, which is like the installer wizard. As a result, their repositories are often cluttered with a large number of files. Most of their repositories are so-called monorepos, which also adds to the complexity. In contrast, the reliverse/cli repository downloads specific files from already existing repositories and only copies from its own or generates files when it is really necessary. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for more details. diff --git a/biome.json b/biome.json index 832b1c9..b7d4a9a 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.2/schema.json", "css": { "formatter": { "enabled": false, @@ -10,7 +10,7 @@ "quoteStyle": "double" }, "linter": { - "enabled": true + "enabled": false }, "parser": { "allowWrongLineComments": false, @@ -21,7 +21,21 @@ "attributePosition": "auto", "enabled": true, "formatWithErrors": false, - "ignore": [".astro", "dist", ".venv", "addons/.output", "node_modules"], + "ignore": [ + ".git/", + ".idea/", + ".million/", + ".next/", + ".turbo/", + ".vercel/", + "addons/.output/", + "build/", + "cluster/", + "dist/", + "drizzle/", + "node_modules/", + "public/" + ], "indentStyle": "space", "indentWidth": 2, "lineEnding": "lf", @@ -64,11 +78,118 @@ }, "linter": { "enabled": true, - "ignore": [".astro", "dist", ".venv", "addons/.output", "node_modules"], + "ignore": [ + ".git/", + ".idea/", + ".million/", + ".next/", + ".turbo/", + ".vercel/", + "addons/.output/", + "build/", + "cluster/", + "dist/", + "drizzle/", + "node_modules/", + "public/" + ], "rules": { + "a11y": { + "noAriaHiddenOnFocusable": "off", + "noBlankTarget": "off", + "noNoninteractiveElementToInteractiveRole": "off", + "noRedundantRoles": "off", + "useFocusableInteractive": "off", + "useKeyWithClickEvents": "off", + "useSemanticElements": "off" + }, + "complexity": { + "noBannedTypes": "off", + "noForEach": "off", + "noUselessFragments": "off", + "noUselessLoneBlockStatements": "off", + "useArrowFunction": "off", + "useFlatMap": "off", + "useLiteralKeys": "off", + "useOptionalChain": "off", + "useRegexLiterals": "off", + "useSimpleNumberKeys": "off" + }, + "correctness": { + "noChildrenProp": "off", + "noConstantCondition": "off", + "noEmptyPattern": "off", + "noInvalidConstructorSuper": "off", + "noInvalidNewBuiltin": "off", + "noInvalidUseBeforeDeclaration": "off", + "noPrecisionLoss": "off", + "noRenderReturnValue": "off", + "noStringCaseMismatch": "off", + "noSwitchDeclarations": "off", + "noUndeclaredVariables": "off", + "noUnnecessaryContinue": "off", + "noUnreachable": "off", + "noUnreachableSuper": "off", + "noUnsafeFinally": "off", + "noUnsafeOptionalChaining": "off", + "noUnusedLabels": "off", + "noUnusedPrivateClassMembers": "off", + "noUnusedVariables": "off", + "noVoidElementsWithChildren": "off", + "noVoidTypeReturn": "off", + "useExhaustiveDependencies": "off", + "useHookAtTopLevel": "off", + "useIsNan": "off", + "useValidForDirection": "off", + "useYield": "off" + }, + "performance": { + "noAccumulatingSpread": "off", + "noDelete": "off" + }, "recommended": true, + "security": { + "noDangerouslySetInnerHtml": "off" + }, "style": { - "noInferrableTypes": "off" + "noInferrableTypes": "off", + "noNonNullAssertion": "off", + "noParameterAssign": "off", + "noUselessElse": "off", + "useEnumInitializers": "off", + "useExportType": "off", + "useImportType": "off", + "useNamingConvention": "off", + "useNodejsImportProtocol": "off", + "useNumberNamespace": "off", + "useShorthandFunctionType": "off", + "useWhile": "off" + }, + "suspicious": { + "noApproximativeNumericConstant": "off", + "noArrayIndexKey": "off", + "noAssignInExpressions": "off", + "noCommentText": "off", + "noCompareNegZero": "off", + "noConfusingLabels": "off", + "noConstEnum": "off", + "noDoubleEquals": "off", + "noDuplicateObjectKeys": "off", + "noEmptyInterface": "off", + "noExplicitAny": "off", + "noExtraNonNullAssertion": "off", + "noGlobalIsFinite": "off", + "noGlobalIsNan": "off", + "noImplicitAnyLet": "off", + "noMisleadingCharacterClass": "off", + "noMisrefactoredShorthandAssign": "off", + "noSelfCompare": "off", + "noShadowRestrictedNames": "off", + "noSparseArray": "off", + "noUnsafeNegation": "off", + "useIsArray": "off", + "useNamespaceKeyword": "off", + "useValidTypeof": "off" } } }, diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000..8783b4c Binary files /dev/null and b/bun.lockb differ diff --git a/eslint.config.js b/eslint.config.js index dd4f05d..943ceaf 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,26 +1,2499 @@ // @ts-check -import eslint from "@eslint/js"; +import js from "@eslint/js"; +import reactCommunity from "@eslint-react/eslint-plugin"; +// @ts-expect-error missing types +import nextPlugin from "@next/eslint-plugin-next"; +import stylistic from "@stylistic/eslint-plugin"; +import tanstack from "@tanstack/eslint-plugin-query"; +// @ts-expect-error missing types +import barrel from "eslint-plugin-barrel-files"; +// @ts-expect-error missing types +import drizzle from "eslint-plugin-drizzle"; +// @ts-expect-error missing types +import eslintComments from "eslint-plugin-eslint-comments"; +import importX from "eslint-plugin-import-x"; +import jsonc from "eslint-plugin-jsonc"; +// @ts-expect-error missing types +import jsxA11yPlugin from "eslint-plugin-jsx-a11y"; +// @ts-expect-error missing types +import markdown from "eslint-plugin-markdown"; +import nodePlugin from "eslint-plugin-n"; +// @ts-expect-error missing types +import noComments from "eslint-plugin-no-comments"; +// @ts-expect-error missing types +import noRelative from "eslint-plugin-no-relative-import-paths"; +import perfectionist from "eslint-plugin-perfectionist"; +// @ts-expect-error missing types +import promisePlugin from "eslint-plugin-promise"; +// @ts-expect-error missing types +import reactJsxRuntime from "eslint-plugin-react/configs/jsx-runtime.js"; +// @ts-expect-error missing types +import reactRecommended from "eslint-plugin-react/configs/recommended.js"; +// @ts-expect-error missing types +import reactHooks from "eslint-plugin-react-hooks"; +// @ts-expect-error missing types +import reactRefresh from "eslint-plugin-react-refresh"; +import tailwindReadable from "eslint-plugin-readable-tailwind"; +import * as regexp from "eslint-plugin-regexp"; +import sonarjs from "eslint-plugin-sonarjs"; +// @ts-expect-error missing types +import eslintPluginSort from "eslint-plugin-sort"; +// @ts-expect-error missing types +import sortExports from "eslint-plugin-sort-exports"; +// @ts-expect-error missing types +import tailwindcss from "eslint-plugin-tailwindcss"; +import unicorn from "eslint-plugin-unicorn"; +import yaml from "eslint-plugin-yml"; +import globals from "globals"; import tseslint from "typescript-eslint"; +// The current Relivator 1.2.6 version comes with many predefined ESLint configs. +// Run the `bun reli:setup` to easily switch between them and set up other tooling. +// Current: addons\scripts\reliverse\relicon\setup\configs\eslint.config.ultimate.ts +// +const stylisticConfig = stylistic.configs.customize({ + indent: 2, + jsx: true, + quoteProps: "as-needed", + quotes: "double", + semi: true, +}); + +// =================================================================== +// Steps if you want to use the new React Compiler: +// - npx nypm add -D babel-plugin-react-compiler +// - npx nypm add -D eslint-plugin-react-compiler +// - import compiler from "eslint-plugin-react-compiler"; +// - Add the compiler plugin to the plugins object (["react-compiler": compiler,]) +// - Add the compiler rules to the rules object +// - Set experimental.reactCompiler to true in next.config.js +// @see https://react.dev/learn/react-compiler +// =================================================================== +// +// @see https://eslint.org +// @see https://typescript-eslint.io +// @see https://github.com/blefnk/relivator +// export default tseslint.config( + js.configs.recommended, { - ignores: ["**/dist/"], + ignores: [ + "**/.{git,next,astro,turbo,million,output}/", + "**/{node_modules,build,dist,drizzle}/", + "**/{cluster,public}/", + "pnpm-lock.yaml", + ], }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - ...tseslint.configs.stylisticTypeChecked, { + name: "@reliverse/eslint-config/core", + extends: [ + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + sonarjs.configs.recommended, + nodePlugin.configs["flat/recommended-module"], + reactCommunity.configs["recommended-type-checked"], + eslintPluginSort.configs["flat/recommended"], + unicorn.configs["flat/recommended"], + regexp.configs["flat/recommended"], + ], + files: ["**/*.{js,ts,tsx}"], languageOptions: { + globals: { + ...globals.builtin, + ...globals.browser, + ...globals.es2024, + ...globals.node, + }, + parser: tseslint.parser, parserOptions: { + // TODO: `projectService` throws an error when creating new files not implicitly + // TODO: included in tsconfig.json (thats we are using `project: true` for now) + ecmaFeatures: { + impliedStrict: true, + jsx: true, + }, project: true, tsconfigRootDir: import.meta.dirname, warnOnUnsupportedTypeScriptVersion: false, }, }, + linterOptions: { + reportUnusedDisableDirectives: "warn", + }, + plugins: { + ...reactRecommended.plugins, + "@next/next": nextPlugin, + "@stylistic": stylistic, + "@tanstack/query": tanstack, + "@typescript-eslint": tseslint.plugin, + "barrel-files": { rules: barrel.rules }, + drizzle: drizzle, + "eslint-comments": eslintComments, + "import-x": importX, + "jsx-a11y": { + rules: jsxA11yPlugin.rules, + }, + "no-relative-import-paths": noRelative, + perfectionist: perfectionist, + promise: promisePlugin, + "react/jsx-runtime": reactJsxRuntime, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + "readable-tailwind": tailwindReadable, + sort: eslintPluginSort, + "sort-exports": sortExports, + tailwindcss: tailwindcss, + }, + rules: { + // @see https://eslint.org/docs/latest/use/configure + ...perfectionist.configs["recommended-natural"].rules, + ...stylisticConfig.rules, + ...importX.configs.recommended.rules, + ...jsxA11yPlugin.configs.recommended.rules, + ...nextPlugin.configs.recommended.rules, + ...promisePlugin.configs.recommended.rules, + ...reactHooks.configs.recommended.rules, + ...reactJsxRuntime.rules, + ...reactRecommended.rules, + ...tailwindReadable.configs.error.rules, + ...tailwindReadable.configs.warning.rules, + ...tailwindcss.configs.recommended.rules, + + // @see https://eslint-react.xyz/rules/overview + "@eslint-react/dom/no-dangerously-set-innerhtml": "off", + "@eslint-react/hooks-extra/ensure-custom-hooks-using-other-hooks": "off", + "@eslint-react/hooks-extra/no-direct-set-state-in-use-effect": "off", + "@eslint-react/hooks-extra/no-redundant-custom-hook": "off", + "@eslint-react/no-array-index-key": "off", + "@eslint-react/no-duplicate-key": "off", + "@eslint-react/no-leaked-conditional-rendering": "off", + "@eslint-react/no-unstable-context-value": "off", + "@eslint-react/no-unstable-default-props": "off", + "@eslint-react/prefer-destructuring-assignment": "off", + "@eslint-react/prefer-read-only-props": "off", + + // @see https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-plugin + "@next/next/no-duplicate-head": "off", + "@next/next/no-html-link-for-pages": "off", + + // @see https://eslint.style/rules + "@stylistic/array-bracket-newline": [ + "off", + { + minItems: 10, + multiline: true, + }, + ], + "@stylistic/array-bracket-spacing": [ + "warn", + "never", + { + arraysInArrays: false, + objectsInArrays: false, + singleValue: false, + }, + ], + "@stylistic/arrow-parens": ["warn", "always"], + + // { requireForBlockBody: true }, + "@stylistic/arrow-spacing": [ + "warn", + { + after: true, + before: true, + }, + ], + "@stylistic/block-spacing": ["warn", "always"], + "@stylistic/brace-style": [ + "off", + "1tbs", + { + allowSingleLine: true, + }, + ], + "@stylistic/comma-dangle": [ + "warn", + { + // TODO: fix `Unexpected trailing comma.` when `type<>` + arrays: "always-multiline", + enums: "always-multiline", + exports: "always-multiline", + functions: "always-multiline", + generics: "always-multiline", + imports: "always-multiline", + objects: "always-multiline", + tuples: "always-multiline", + }, + ], + "@stylistic/comma-spacing": [ + "warn", + { + after: true, + before: false, + }, + ], + "@stylistic/comma-style": ["warn", "last"], + "@stylistic/computed-property-spacing": ["off", "always"], + "@stylistic/dot-location": ["warn", "property"], + "@stylistic/eol-last": "warn", + "@stylistic/function-call-argument-newline": ["warn", "consistent"], + "@stylistic/function-call-spacing": ["warn", "never"], + "@stylistic/function-paren-newline": [ + // TODO: fix incompatibility with biome (or with something else) + "off", + "multiline-arguments", + ], + "@stylistic/generator-star-spacing": [ + "warn", + { + after: true, + before: false, + method: { + after: false, + before: true, + }, + }, + ], + "@stylistic/implicit-arrow-linebreak": ["off", "beside"], + "@stylistic/indent": [ + // TODO: fix incompatibility with biome (or with something else) + "off", + 2, + { + ignoredNodes: [ + "TSTypeLiteral > TSPropertySignature", + "TSUnionType > TSTypeLiteral", + ], + SwitchCase: 1, + }, + ], + "@stylistic/indent-binary-ops": ["off", 2], + + // @see https://eslint.style/rules + "@stylistic/jsx-child-element-spacing": "off", + "@stylistic/jsx-closing-bracket-location": "off", + "@stylistic/jsx-closing-tag-location": "off", + "@stylistic/jsx-curly-newline": "off", + "@stylistic/jsx-indent": [ + "off", + 2, + { + checkAttributes: true, + indentLogicalExpressions: true, + }, + ], + "@stylistic/jsx-indent-props": ["warn", 2], + "@stylistic/jsx-one-expression-per-line": "off", + "@stylistic/jsx-pascal-case": "off", + "@stylistic/jsx-self-closing-comp": "off", + "@stylistic/jsx-wrap-multilines": [ + "warn", + { + arrow: "parens", + assignment: "parens", + condition: "ignore", + declaration: "parens", + logical: "ignore", + prop: "ignore", + return: "parens", + }, + ], + "@stylistic/key-spacing": [ + "warn", + { + afterColon: true, + beforeColon: false, + }, + ], + "@stylistic/keyword-spacing": [ + "warn", + { + after: true, + before: true, + }, + ], + "@stylistic/linebreak-style": ["warn", "unix"], + "@stylistic/lines-around-comment": [ + "warn", + { + afterBlockComment: false, + afterHashbangComment: true, + afterLineComment: false, + allowArrayEnd: true, + allowArrayStart: true, + allowBlockEnd: true, + allowBlockStart: true, + allowClassEnd: true, + allowClassStart: true, + allowEnumEnd: true, + allowEnumStart: true, + allowInterfaceEnd: true, + allowInterfaceStart: true, + allowModuleEnd: true, + allowModuleStart: true, + allowObjectEnd: true, + allowObjectStart: true, + allowTypeEnd: true, + allowTypeStart: true, + applyDefaultIgnorePatterns: true, + beforeBlockComment: true, + beforeLineComment: true, + ignorePattern: + "@type\\s.+|@ts-expect-error|biome-ignore|TODO:|import", + }, + ], + + // @see https://eslint.style/rules/default/max-len + "@stylistic/max-len": [ + // @see https://github.com/eslint/eslint/issues/11325 + "warn", + { + // TODO: change to 80 in 1.3.0 GA release (@see https://github.com/prettier/prettier/issues) + code: 120, + ignoreComments: false, + ignorePattern: "^[\\s]*(//|