diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 049e5bf2..10d07e2c 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -8,7 +8,6 @@ on: jobs: format-check: - name: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b190e46..4fef60d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,6 @@ on: jobs: release: - name: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index d3bd001e..e2f56d0f 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,17 @@ single-purpose repos, monorepos, monorepo workspaces and common tooling:** ```bash # initialize a monorepo -yarn dlx moker create --monorepo my-repo -cd my-repo +yarn dlx moker create --monorepo my-monorepo +cd my-monorepo # install common tools -yarn moker use prettier husky lint-staged doctoc semantic-release +yarn moker use prettier doctoc semantic-release # create workspaces yarn moker add --template express server yarn moker add --template cra client +yarn moker add --template lib shared +yarn moker add --template bandersnatch cli ``` ## Features @@ -36,13 +38,18 @@ yarn moker add --template cra client - [Getting started](#getting-started) - [Prerequisites](#prerequisites) - - [Create monorepo](#create-monorepo) - - [Use plugins](#use-plugins) - - [Add workspace](#add-workspace) + - [Usage](#usage) + - [Creating a single-purpose repo](#creating-a-single-purpose-repo) + - [Creating a monorepo](#creating-a-monorepo) + - [Creating a monorepo workspace](#creating-a-monorepo-workspace) + - [Using plugins](#using-plugins) + - [Using templates](#using-templates) + - [Using plugins and templates together](#using-plugins-and-templates-together) - [Available plugins](#available-plugins) - [`dependabot` _repo_](#dependabot-_repo_) - [`devcontainer` _repo_](#devcontainer-_repo_) - [`doctoc` _repo_](#doctoc-_repo_) + - [`esbuild` _repo or workspace_](#esbuild-_repo-or-workspace_) - [`github-actions` _repo_](#github-actions-_repo_) - [`husky` _repo_](#husky-_repo_) - [`jest` _repo or workspace_](#jest-_repo-or-workspace_) @@ -56,6 +63,7 @@ yarn moker add --template cra client - [`common` _repo_](#common-_repo_) - [`cra` _repo or workspace_](#cra-_repo-or-workspace_) - [`express` _repo or workspace_](#express-_repo-or-workspace_) + - [`github-actions` _repo_](#github-actions-_repo_-1) - [`lib` _repo or workspace_](#lib-_repo-or-workspace_) - [Commands](#commands) - [Contributing](#contributing) @@ -80,69 +88,108 @@ You will need Node v14+ and Yarn v2+ in order to use `moker`. corepack prepare yarn@stable --activate ``` -## Create monorepo +## Usage -Create a new monorepo: +`moker` is distributed as a NPM package, and can be run with `yarn dlx`: + +```bash +yarn dlx moker +``` + +> **Note**: Note that when we use `yarn dlx moker` to create a new repo, `moker` +> is added as a dependency to your new repo, so we can simply use `yarn moker` +> to execute commands from within the repo directory. + +## Creating a single-purpose repo + +Create a new repo: ```bash -yarn dlx moker create --monorepo my-repo +yarn dlx moker create my-repo ``` -This will initialize a new monorepo in the `my-repo` directory. +This will initialize a new repo in the `my-repo` directory. -> **Note**: Note that we use `yarn dlx moker` to create a new monorepo. Once we -> are inside our monorepo, we can simply use `yarn moker` to execute commands. +## Creating a monorepo + +Create a new monorepo: + +```bash +yarn dlx moker create --monorepo my-monorepo +``` + +This will initialize a new monorepo in the `my-monorepo` directory. > 🤓 _Default_: The monorepo is initiated with Yarn without Zero-Installs and in > legacy `nodeLinker: node-modules` mode because a lot of packages are not yet > compatible with PnP or require a workaround. -## Use plugins +## Creating a monorepo workspace + +To add a new workspace (a.k.a. monorepo package) to your monorepo, run this from +within the monorepo directory: + +```bash +yarn moker add my-workspace +``` + +Workspaces are added in a customizable subdirectory of the monorepo (the default +is `packages`). + +## Using plugins + +Of course you want additional tools installed. You can add plugins with: + +```bash +yarn moker use prettier +``` -Of course you want additional tools installed at the monorepo level, add them -with: +You can install multiple plugins at once: ```bash -cd my-repo -yarn moker use prettier husky lint-staged +yarn moker use prettier lint-staged husky ``` Plugins may work together. For example, `lint-staged` will install a pre-commit hook which formats code if `prettier` and `husky` are installed. The order in which plugins are added does not matter. -See the section [available plugins](#available-plugins) for a list of options. +> **Note**: Some plugins only work at the repo or workspace level, `moker` will +> warn you if you try to add a plugin at the wrong level. -> **Note**: To quickly get started with the most common plugins, use a monorepo -> template like so: -> -> ```bash -> yarn dlx moker create --template common my-repo -> ``` +See the section [available plugins](#available-plugins) for a list of options. -## Add workspace +## Using templates -To add a new workspace (a.k.a. monorepo package) to your monorepo, use: +You can templates when creating repos: ```bash -yarn moker add my-workspace +yarn dlx moker create --template common my-repo ``` -Workspaces are added in a customizable subdirectory of the monorepo (the default -is `packages`). - -You can also use a workspace template, e.g.: +You can also use templates when creating workspaces: ```bash yarn moker add --template lib shared -yarn moker add --template express server -yarn moker add --template cra client -yarn moker add --template bandersnatch cli +``` + +You can install multiple templates at once: + +```bash +yarn dlx moker create --template common github-action my-action ``` See the section [available templates](#available-templates) for a list of options. +## Using plugins and templates together + +Plugins and templates can be used together, for example: + +```bash +yarn dlx moker create --template express --use prettier my-repo +``` + # Available plugins ## `dependabot` _repo_ @@ -173,6 +220,15 @@ This plugin adds a script to generate a table of contents for the README using If you have the `husky` plugin installed, it will also add a pre-commit hook. +## `esbuild` _repo or workspace_ + +This plugin sets up [esbuild](https://esbuild.github.io) and adds a `build` and +`build:watch` script to the repo or both the workspace and the monorepo. + +> 🤓 _Default_: If you have the `typescript` plugin installed as well, we'll +> assume that you want to build to a bundle instead of transpiled TypeScript. We +> will still use `tsc` for type checking. + ## `github-actions` _repo_ This plugin creates a simple `ci.yml` @@ -283,6 +339,12 @@ app (web client). Scaffolds a simple [express](https://expressjs.com) HTTP app with the [typescript](#typescript-workspace) and [jest](#jest-workspace) plugins. +## `github-actions` _repo_ + +Scaffolds a +[custom GitHub Action](https://docs.github.com/en/actions/creating-actions/about-custom-actions) +template. + ## `lib` _repo or workspace_ A plain shared library template with the [typescript](#typescript-workspace) and @@ -298,11 +360,11 @@ Contributions are very welcome! ## Roadmap -- [ ] Add LICENSE file to repo - [ ] Support for `swc`/`esbuild` - [ ] A compat lib (which builds cjs and mjs targets) - [ ] Blog post / tutorial - [ ] Docs for writing custom plugins / templates +- [x] Add LICENSE file to repo - [x] Adapt for non-monorepo use-cases - [x] github-actions plugin - [x] devcontainer plugin diff --git a/TODO.md b/TODO.md index e505dad8..74299cd0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,7 @@ ### TODOs -| Filename | line # | TODO | -| :------------------------------------------------------------------------- | :----: | :--------------------------- | -| [packages/core/src/yarnrc.ts](packages/core/src/yarnrc.ts#L23) | 23 | etc, fix later | -| [packages/plugins/src/jest/jest.ts](packages/plugins/src/jest/jest.ts#L31) | 31 | install jest without ts-jest | +| Filename | line # | TODO | +| :------------------------------------------------------------------------- | :----: | :--------------------------------- | +| [packages/core/src/license.ts](packages/core/src/license.ts#L43) | 43 | Contents is not perfect yet, e.g.: | +| [packages/core/src/yarnrc.ts](packages/core/src/yarnrc.ts#L23) | 23 | etc, fix later | +| [packages/plugins/src/jest/jest.ts](packages/plugins/src/jest/jest.ts#L31) | 31 | install jest without ts-jest | diff --git a/packages/cli/moker.js b/packages/cli/moker.js index 196b3b23..7761db32 100755 --- a/packages/cli/moker.js +++ b/packages/cli/moker.js @@ -3,7 +3,7 @@ import { writeError } from "@mokr/core"; import cli from "./dist/cli.js"; -cli.runOrRepl().catch((err) => { +function errorHandler(err) { if (process.env["DEBUG"]) { console.debug(err); } else { @@ -11,4 +11,9 @@ cli.runOrRepl().catch((err) => { } process.exit(1); -}); +} + +cli.runOrRepl().catch(errorHandler); + +// Compat layer for Node < 16 +process.on("unhandledRejection", errorHandler); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index d61eb742..df15bb8e 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -9,6 +9,6 @@ Object.values(commands).forEach((command) => cli.add(command)); // Some assertions we always need assertYarnVersion(3); -assertNodeVersion(16); +assertNodeVersion(14); export default cli; diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts index b5468aae..65bf2eb5 100644 --- a/packages/cli/src/commands/add.ts +++ b/packages/cli/src/commands/add.ts @@ -17,7 +17,8 @@ export const add = command("add") }) .option("template", { description: "Use workspace template", - type: "string", + type: "array", + default: [] as string[], }) .option("plugin", { description: "Kick-start with this plugin", @@ -41,9 +42,9 @@ export const add = command("add") () => addWorkspace({ directory, name: workspaceName }) ); - if (template) { - await task(`Apply template ${template}`, () => - applyTemplate({ directory: workspaceDirectory, name: template }) + for (const name of template) { + await task(`Apply template ${name}`, () => + applyTemplate({ directory: workspaceDirectory, name }) ); } diff --git a/packages/cli/src/commands/create.ts b/packages/cli/src/commands/create.ts index 3a9b079c..3f35d25c 100644 --- a/packages/cli/src/commands/create.ts +++ b/packages/cli/src/commands/create.ts @@ -1,10 +1,12 @@ import { applyTemplate, + AVAILABLE_LICENSES, createMonorepo, createRepo, DEFAULT_LICENSE, DEFAULT_SCOPED, DEFAULT_WORKSPACES_DIRECTORY, + getUsername, isReadableAndWritableDirectory, task, } from "@mokr/core"; @@ -29,7 +31,8 @@ export const create = command("create") }) .option("template", { description: "Use repo template", - type: "string", + type: "array", + default: [] as string[], }) .option("plugin", { description: "Kick-start with this plugin", @@ -38,10 +41,15 @@ export const create = command("create") }) .option("license", { description: "License", - choices: ["MIT", "GPLv3"], + choices: AVAILABLE_LICENSES, prompt: "What license do you want to publish your packages with?", default: DEFAULT_LICENSE, }) + .option("upstream", { + description: "Upstream repository provider and user", + prompt: "Optional upstream repository provider and user (e.g. github:user)", + default: `github:${getUsername()}`, + }) .option("workspacesDirectory", { description: "Workspaces directory (only used with --monorepo)", default: DEFAULT_WORKSPACES_DIRECTORY, @@ -56,32 +64,42 @@ export const create = command("create") "Initialize repo even if path already exists. WARNING: some files may be overwritten!", type: "boolean", }) - .action(async ({ path, monorepo, template, plugin, force, ...options }) => { - const directory = resolve(path); - const type = monorepo ? "monorepo" : "repo"; - const initializer = monorepo ? createMonorepo : createRepo; - - if ((await isReadableAndWritableDirectory({ directory })) && !force) { - throw new Error(`${directory} already exists`); - } + .action( + async ({ + path, + upstream, + monorepo, + template, + plugin, + force, + ...options + }) => { + const directory = resolve(path); + const type = monorepo ? "monorepo" : "repo"; + const initializer = monorepo ? createMonorepo : createRepo; - await task(`Create new ${type} in ${directory}`, () => - initializer({ directory, ...options }) - ); + if ((await isReadableAndWritableDirectory({ directory })) && !force) { + throw new Error(`${directory} already exists`); + } - if (template) { - await task(`Apply template ${template}`, () => - applyTemplate({ directory, name: template }) + await task(`Create new ${type} in ${directory}`, () => + initializer({ directory, upstream, ...options }) ); - } - for (const name of plugin) { - await addPlugin({ directory, name }); - } + for (const name of template) { + await task(`Apply template ${name}`, () => + applyTemplate({ directory, name }) + ); + } - await loadPlugins({ directory }); + for (const name of plugin) { + await addPlugin({ directory, name }); + } - await updateDependencies({ directory }); + await loadPlugins({ directory }); - await format({ directory }); - }); + await updateDependencies({ directory }); + + await format({ directory }); + } + ); diff --git a/packages/cli/src/commands/reinstall.ts b/packages/cli/src/commands/reinstall.ts index 8979a794..0726a916 100644 --- a/packages/cli/src/commands/reinstall.ts +++ b/packages/cli/src/commands/reinstall.ts @@ -4,8 +4,8 @@ import { installPlugin, isMonorepo, loadAllPlugins, - logWarning, task, + warning, } from "@mokr/core"; import { command } from "bandersnatch"; import { resolve } from "node:path"; @@ -66,5 +66,5 @@ export const reinstall = command("reinstall") await format({ directory }); - logWarning(REINSTALL_WARNING); + warning(REINSTALL_WARNING); }); diff --git a/packages/cli/src/commands/use.ts b/packages/cli/src/commands/use.ts index 3d053c73..1305897b 100644 --- a/packages/cli/src/commands/use.ts +++ b/packages/cli/src/commands/use.ts @@ -1,4 +1,4 @@ -import { hasPlugin, installPlugin, logWarning, task } from "@mokr/core"; +import { hasPlugin, installPlugin, task, warning } from "@mokr/core"; import { command } from "bandersnatch"; import { resolve } from "node:path"; import { REINSTALL_WARNING } from "../constants.js"; @@ -38,6 +38,6 @@ export const use = command("use") await format({ directory }); if (reinstall) { - logWarning(REINSTALL_WARNING); + warning(REINSTALL_WARNING); } }); diff --git a/packages/core/package.json b/packages/core/package.json index ecd12c66..05c76421 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,14 +26,18 @@ "dependencies": { "chalk": "5.2.0", "deepmerge": "4.2.2", + "hosted-git-info": "6.1.1", + "license": "1.0.3", "ora": "6.1.2", "pkg-up": "^4.0.0", "yaml": "2.1.3" }, "devDependencies": { + "@types/hosted-git-info": "3.0.2", "@types/jest": "29.2.4", "@types/node": "18.11.13", "jest": "29.3.1", + "sort-package-json": "2.1.0", "ts-jest": "29.0.3", "typescript": "4.9.4" }, diff --git a/packages/core/src/gitignore.ts b/packages/core/src/gitignore.ts index 57768cb8..f14eff0b 100644 --- a/packages/core/src/gitignore.ts +++ b/packages/core/src/gitignore.ts @@ -1,11 +1,11 @@ import os from "node:os"; -import { join } from "node:path"; +import { resolve } from "node:path"; import { isReadableAndWritableFile, readFile, writeFile } from "./file.js"; const FILENAME = ".gitignore"; export async function readGitignore({ directory }: { directory: string }) { - return (await readFile({ path: join(directory, FILENAME) })) + return (await readFile({ path: resolve(directory, FILENAME) })) .trim() .split(os.EOL); } @@ -19,7 +19,7 @@ export async function writeGitignore({ lines: string[]; append?: boolean; }) { - const path = join(directory, FILENAME); + const path = resolve(directory, FILENAME); if (append && (await isReadableAndWritableFile({ path }))) { let existingLines = await readGitignore({ directory }); diff --git a/packages/core/src/io.ts b/packages/core/src/io.ts index e11a0400..0db75f11 100644 --- a/packages/core/src/io.ts +++ b/packages/core/src/io.ts @@ -3,7 +3,7 @@ import ora from "ora"; type Log = { message: string; - type: "info" | "warning"; + type: "debug" | "info" | "warning"; }; let messages: Log[]; @@ -14,15 +14,21 @@ function resetLog() { resetLog(); -export function log(message: string, type: "info" | "warning" = "info") { +export function log(message: string, type: Log["type"] = "info") { messages.push({ message, type }); } -export function logInfo(message: string) { +export function debug(message: string) { + if (process.env["DEBUG"]) { + log(message, "debug"); + } +} + +export function info(message: string) { log(message, "info"); } -export function logWarning(message: string) { +export function warning(message: string) { log(message, "warning"); } @@ -30,6 +36,10 @@ export function writeInfo(message: string) { return console.log(chalk.blue(message)); } +export function writeDebug(message: string) { + return console.debug(chalk.gray(message)); +} + export function writeWarning(message: string) { return console.warn(chalk.bgYellow.black(message)); } @@ -38,6 +48,17 @@ export function writeError(message: string) { return console.error(chalk.bgRed.white(message)); } +export async function flushLogs() { + for (const { message, type } of messages) { + type === "warning" + ? writeWarning(message) + : type === "debug" + ? writeDebug(message) + : writeInfo(message); + } + resetLog(); +} + export async function task(title: string, callback: () => Promise) { const spinner = ora(title).start(); let result: T; @@ -46,7 +67,7 @@ export async function task(title: string, callback: () => Promise) { result = await callback(); } catch (error) { spinner.fail(); - resetLog(); + flushLogs(); throw error; } @@ -56,10 +77,7 @@ export async function task(title: string, callback: () => Promise) { spinner.succeed(title); } - for (const { message, type } of messages) { - type === "warning" ? writeWarning(message) : writeInfo(message); - } - resetLog(); + flushLogs(); return result; } diff --git a/packages/core/src/license.ts b/packages/core/src/license.ts new file mode 100644 index 00000000..cadd7385 --- /dev/null +++ b/packages/core/src/license.ts @@ -0,0 +1,52 @@ +import { getLicense } from "license"; +import { resolve } from "node:path"; +import { readFile, writeFile } from "./file.js"; +import type { AVAILABLE_LICENSES } from "./repo.js"; + +const FILENAME = "LICENSE"; + +export async function readLicense({ directory }: { directory: string }) { + return readFile({ path: resolve(directory, FILENAME) }); +} + +export async function writeLicense({ + directory, + contents, +}: { + directory: string; + contents: string; +}) { + const path = resolve(directory, FILENAME); + + return writeFile({ + path, + contents, + }); +} + +export async function generateLicense({ + directory, + license, + author, +}: { + directory: string; + license: typeof AVAILABLE_LICENSES[number]; + author: string; +}) { + const path = resolve(directory, FILENAME); + const contents = getLicense(license, { + author, + year: String(new Date().getFullYear()), + }); + + /** + * @todo Contents is not perfect yet, e.g.: + * - On one line: "MIT License Copyright [...]" + * - Lines should be wrapped at 80 characters + */ + + return writeFile({ + path, + contents, + }); +} diff --git a/packages/core/src/monorepo.ts b/packages/core/src/monorepo.ts index 5abaf949..7efafe1d 100644 --- a/packages/core/src/monorepo.ts +++ b/packages/core/src/monorepo.ts @@ -33,11 +33,13 @@ export async function createMonorepo({ directory, scoped = DEFAULT_SCOPED, license = DEFAULT_LICENSE, + upstream, workspacesDirectory = DEFAULT_WORKSPACES_DIRECTORY, }: CreateMonorepoOptions) { await createRepo({ directory, license, + upstream, additionalPackageOptions: { private: true, workspaces: [`${workspacesDirectory}/*`], diff --git a/packages/core/src/package.ts b/packages/core/src/package.ts index 044699c2..ffb66592 100644 --- a/packages/core/src/package.ts +++ b/packages/core/src/package.ts @@ -1,4 +1,5 @@ -import { join } from "node:path"; +import { resolve } from "node:path"; +import sortPackageJson from "sort-package-json"; import { isReadableAndWritableFile, removeFile } from "./file.js"; import { readJson, updateJson, writeJson } from "./json.js"; import type { Undefinable } from "./utils/types.js"; @@ -95,11 +96,6 @@ export type Package = { [k: string]: any; }; -// fhewo jfjewio fjiewo jfiewo fjiewo fjjiowe jfiowe jfiowe jfi owejfio wejifo jweifo jiweo - -/** - * fjiewof jfiewo jfioew jfiowe jfiowejfi owejifo wjeiof jewiof jiwoef jiwoe fjiowe jfiowe jfio - */ type Person = | { [k: string]: any; @@ -122,15 +118,15 @@ type LintStagedOptions = { const FILENAME = "package.json"; export async function hasPackage({ directory }: { directory: string }) { - return isReadableAndWritableFile({ path: join(directory, FILENAME) }); + return isReadableAndWritableFile({ path: resolve(directory, FILENAME) }); } export async function readPackage({ directory }: { directory: string }) { - return readJson({ path: join(directory, FILENAME) }); + return readJson({ path: resolve(directory, FILENAME) }); } export async function removePackage({ directory }: { directory: string }) { - return removeFile({ path: join(directory, FILENAME) }); + return removeFile({ path: resolve(directory, FILENAME) }); } export async function writePackage({ @@ -142,7 +138,8 @@ export async function writePackage({ data: Undefinable; append?: boolean; }) { - await writeJson({ path: join(directory, FILENAME), data, append }); + await writeJson({ path: resolve(directory, FILENAME), data, append }); + await sortPackage({ directory }); } export async function updatePackage({ @@ -152,5 +149,13 @@ export async function updatePackage({ directory: string; merge: (existingData: Package) => Package; }) { - await updateJson({ path: join(directory, FILENAME), merge }); + await updateJson({ path: resolve(directory, FILENAME), merge }); + await sortPackage({ directory }); +} + +export async function sortPackage({ directory }: { directory: string }) { + await updateJson({ + path: resolve(directory, FILENAME), + merge: sortPackageJson, + }); } diff --git a/packages/core/src/plugin.ts b/packages/core/src/plugin.ts index c09b3b67..20851705 100644 --- a/packages/core/src/plugin.ts +++ b/packages/core/src/plugin.ts @@ -40,6 +40,7 @@ const CORE_PLUGINS = [ "dependabot", "todos", "doctoc", + "esbuild", ]; export function isPlugin(plugin: unknown): plugin is Plugin { @@ -72,15 +73,16 @@ export async function importPlugin({ directory, name }: PluginOptions) { throw new Error(`Plugin ${name} does not exist or is not valid`); } - await validateType({ directory, type: plugin.type }); + await validateType({ directory, name, type: plugin.type }); return plugin; } export async function validateType({ directory, + name, type, -}: DirOption & { type: PluginType }) { +}: DirOption & { name: string; type: PluginType }) { const repo = await isRepo({ directory }); const monorepo = await isMonorepo({ directory }); const workspace = await isWorkspace({ directory }); @@ -88,25 +90,33 @@ export async function validateType({ switch (type) { case PluginType.Repo: if (!repo) { - throw new Error(`Plugin can only be used at repo level`); + throw new Error( + `Plugin or template ${name} can only be used at repo level` + ); } break; case PluginType.RepoOrWorkspace: if (!repo && !workspace) { - throw new Error(`Plugin can only be used at repo or workspace level`); + throw new Error( + `Plugin or template ${name} can only be used at repo or workspace level` + ); } break; case PluginType.Monorepo: if (!monorepo) { - throw new Error(`Plugin can only be used at monorepo level`); + throw new Error( + `Plugin or template ${name} can only be used at monorepo level` + ); } break; case PluginType.Workspace: if (!workspace) { - throw new Error(`Plugin can only be used at workspace level`); + throw new Error( + `Plugin or template ${name} can only be used at workspace level` + ); } break; } diff --git a/packages/core/src/repo.ts b/packages/core/src/repo.ts index 4f2f05dc..87d49d46 100644 --- a/packages/core/src/repo.ts +++ b/packages/core/src/repo.ts @@ -1,6 +1,9 @@ +import hostedGitInfo from "hosted-git-info"; import { basename, join } from "node:path"; import { isDirectory, isReadableAndWritableDirectory } from "./directory.js"; +import { generateLicense } from "./license.js"; import { hasPackage, Package, writePackage } from "./package.js"; +import { exec, getAuthor } from "./utils/index.js"; import { writeReadme } from "./workspace.js"; import { enqueueInstallDependency, @@ -8,7 +11,8 @@ import { initYarnNewRepo, } from "./yarn.js"; -export const DEFAULT_LICENSE = "MIT"; +export const AVAILABLE_LICENSES = ["MIT", "GPL-3.0"] as const; +export const DEFAULT_LICENSE: typeof AVAILABLE_LICENSES[number] = "MIT"; export type RepoPackage = Package; @@ -17,15 +21,22 @@ type DirOption = { }; export type CreateRepoOptions = DirOption & { - license?: string; + license?: typeof AVAILABLE_LICENSES[number]; + upstream: string | undefined; additionalPackageOptions?: T; }; export async function createRepo({ directory, license = DEFAULT_LICENSE, + upstream, additionalPackageOptions, }: CreateRepoOptions) { + const name = basename(directory); + const authorObject = await getAuthor(); + const author = `${authorObject.name} <${authorObject.email}>`; + const repository = upstream ? `${upstream}/${name}` : undefined; + if (await isReadableAndWritableDirectory({ directory })) { await initYarnExistingRepo({ directory }); } else { @@ -35,9 +46,11 @@ export async function createRepo({ await writePackage({ directory, data: { - name: basename(directory), + name, version: "0.0.0", license, + author, + repository, moker: { plugins: [], }, @@ -48,6 +61,16 @@ export async function createRepo({ }, }); + if (repository) { + const gitRemoteUrl = hostedGitInfo.fromUrl(repository)?.ssh(); + + if (gitRemoteUrl) { + await exec("git", ["remote", "add", "origin", gitRemoteUrl], { + cwd: directory, + }); + } + } + if (additionalPackageOptions) { await writePackage({ directory, data: additionalPackageOptions }); } @@ -59,6 +82,8 @@ export async function createRepo({ }); await writeReadme({ directory }); + + await generateLicense({ directory, license, author }); } export async function isRepo({ directory }: DirOption) { diff --git a/packages/core/src/template.ts b/packages/core/src/template.ts index c09aaa4d..f7403185 100644 --- a/packages/core/src/template.ts +++ b/packages/core/src/template.ts @@ -13,7 +13,14 @@ type TemplateOptions = { name: string; }; -const CORE_TEMPLATES = ["common", "lib", "cra", "bandersnatch", "express"]; +const CORE_TEMPLATES = [ + "common", + "lib", + "cra", + "bandersnatch", + "express", + "github-action", +]; export function isTemplate(template: unknown): template is Template { return !!( @@ -41,7 +48,7 @@ export async function importTemplate({ directory, name }: TemplateOptions) { throw new Error(`Template ${name} does not exist or is not valid`); } - await validateType({ directory, type: template.type }); + await validateType({ directory, name, type: template.type }); return template; } diff --git a/packages/core/src/utils/author.ts b/packages/core/src/utils/author.ts new file mode 100644 index 00000000..f0ab58dd --- /dev/null +++ b/packages/core/src/utils/author.ts @@ -0,0 +1,20 @@ +import { exec } from "./exec.js"; + +export async function getAuthor() { + const name = (await exec("git", ["config", "user.name"])).stdout.trim(); + const email = (await exec("git", ["config", "user.email"])).stdout.trim(); + + return { name, email }; +} + +export function getUsername() { + const { env } = process; + + return ( + env["SUDO_USER"] || + env["LOGNAME"] || + env["USER"] || + env["LNAME"] || + env["USERNAME"] + ); +} diff --git a/packages/core/src/utils/exec.ts b/packages/core/src/utils/exec.ts index d42cddc0..1cfa6e8d 100644 --- a/packages/core/src/utils/exec.ts +++ b/packages/core/src/utils/exec.ts @@ -1,4 +1,5 @@ import childProcess, { ChildProcess } from "node:child_process"; +import { debug } from "../io.js"; function childAwaiter(child: ChildProcess): Promise { return new Promise(function (resolve, reject) { @@ -16,6 +17,8 @@ export async function exec( io = "return" as "return" | "passthrough", } = {} ) { + debug(`spawning "${cmd} ${args.join(" ")}" in "${cwd}"`); + const child = childProcess.spawn(cmd, args, { shell: true, stdio: io === "passthrough" ? "inherit" : "pipe", diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 2604c053..a59089fe 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,4 +1,5 @@ export * from "./assertions.js"; +export * from "./author.js"; export * from "./exec.js"; export * from "./merge.js"; export * from "./string.js"; diff --git a/packages/core/src/yarn.ts b/packages/core/src/yarn.ts index 876f129b..226e130e 100644 --- a/packages/core/src/yarn.ts +++ b/packages/core/src/yarn.ts @@ -1,5 +1,6 @@ import { createDirectory } from "./directory.js"; import { writeGitignore } from "./gitignore.js"; +import { sortPackage } from "./package.js"; import { exec } from "./utils/exec.js"; import { writeYarnrc } from "./yarnrc.js"; @@ -153,17 +154,24 @@ export async function runDependencyQueues({ directory }: DirOption) { return; } - if (dependencies.size || devDependencies.size) { + if (dependencies.size) { + const installArgs = ["add", "--exact", ...Array.from(dependencies)]; + + await exec("yarn", installArgs, { cwd: directory }); + + queues.install.set(directory, new Set()); + } + + if (devDependencies.size) { const installArgs = [ "add", "--exact", - ...Array.from(dependencies), - ...Array.from(devDependencies).map((dependency) => `--dev ${dependency}`), + "--dev", + ...Array.from(devDependencies), ]; await exec("yarn", installArgs, { cwd: directory }); - queues.install.set(directory, new Set()); queues.installDev.set(directory, new Set()); } @@ -174,6 +182,8 @@ export async function runDependencyQueues({ directory }: DirOption) { queues.remove.set(directory, new Set()); } + + await sortPackage({ directory }); } export async function getWorkspaces({ directory }: DirOption) { diff --git a/packages/plugins/src/esbuild/esbuild.ts b/packages/plugins/src/esbuild/esbuild.ts new file mode 100644 index 00000000..eb22215e --- /dev/null +++ b/packages/plugins/src/esbuild/esbuild.ts @@ -0,0 +1,96 @@ +import { + enqueueInstallDependency, + enqueueRemoveDependency, + getMonorepoDirectory, + hasPlugin, + PluginArgs, + PluginType, + updatePackage, + warning, + writeGitignore, + writePackage, +} from "@mokr/core"; + +async function install({ directory }: PluginArgs) { + enqueueInstallDependency({ + directory, + identifier: "esbuild", + dev: true, + }); + + await writeGitignore({ + directory, + lines: ["", "# artifacts", "/dist"], + }); +} + +async function remove({ directory }: PluginArgs) { + enqueueRemoveDependency({ + directory, + identifier: "esbuild", + }); + + await updatePackage({ + directory, + merge: (existingData) => { + delete existingData?.["scripts"]?.["build:typecheck"]; + delete existingData?.["scripts"]?.["build:bundle"]; + return existingData; + }, + }); + + warning("Please review your package.json manually"); +} + +async function load({ directory }: PluginArgs) { + const monorepoDirectory = await getMonorepoDirectory({ directory }); + + if (await hasPlugin({ directory, name: "typescript" })) { + await writePackage({ + directory, + data: { + scripts: { + clean: "rm -rf dist", + build: "yarn clean && yarn build:typecheck && yarn build:bundle", + "build:typecheck": "tsc --noEmit", + "build:bundle": + "esbuild --bundle --platform=node --target=es2022 --outdir=dist src/index.ts", + }, + }, + }); + } else { + await writePackage({ + directory, + data: { + type: "module", + main: "dist/index.js", + files: ["dist"], + scripts: { + clean: "rm -rf dist", + build: + "yarn clean && esbuild --bundle --platform=node --target=es2022 --outdir=dist src/index.ts", + prepublish: "yarn build", + }, + }, + }); + } + + if (monorepoDirectory) { + // Monorepo scripts + await writePackage({ + directory: monorepoDirectory, + data: { + scripts: { + build: "yarn workspaces foreach --topological --verbose run build", + }, + }, + }); + } +} + +export const esbuild = { + type: PluginType.RepoOrWorkspace, + install, + remove, + load, +}; diff --git a/packages/plugins/src/husky/husky.ts b/packages/plugins/src/husky/husky.ts index 915720ea..d0b37886 100644 --- a/packages/plugins/src/husky/husky.ts +++ b/packages/plugins/src/husky/husky.ts @@ -4,13 +4,13 @@ import { installDependency, isMonorepo, isReadableAndWritableFile, - logWarning, PluginArgs, PluginType, readFile, removeDirectory, removeFile, updatePackage, + warning, writeFile, writePackage, } from "@mokr/core"; @@ -44,7 +44,7 @@ async function remove({ directory }: PluginArgs) { directory, merge: (existingData) => { if (existingData.scripts?.["postinstall"] !== "husky install") { - logWarning( + warning( "Can't automatically remove 'husky install' from package.json, please manually review the 'postinstall' script." ); return existingData; diff --git a/packages/plugins/src/index.ts b/packages/plugins/src/index.ts index b1c3d9a3..a63685f4 100644 --- a/packages/plugins/src/index.ts +++ b/packages/plugins/src/index.ts @@ -1,6 +1,7 @@ export * from "./dependabot/dependabot.js"; export * from "./devcontainer/devcontainer.js"; export * from "./doctoc/doctoc.js"; +export * from "./esbuild/esbuild.js"; export * from "./githubActions/githubActions.js"; export * from "./husky/husky.js"; export * from "./jest/jest.js"; diff --git a/packages/plugins/src/jest/jest.ts b/packages/plugins/src/jest/jest.ts index b02b2349..91780bec 100644 --- a/packages/plugins/src/jest/jest.ts +++ b/packages/plugins/src/jest/jest.ts @@ -3,10 +3,10 @@ import { enqueueRemoveDependency, getMonorepoDirectory, hasPlugin, - logWarning, PluginArgs, PluginType, removeFile, + warning, writeFile, writePackage, } from "@mokr/core"; @@ -75,7 +75,7 @@ async function remove({ directory }: PluginArgs) { await removeFile({ path: join(directory, JEST_CONFIG_FILENAME) }); } catch {} - logWarning("Please review package.json manually"); + warning("Please review package.json manually"); } async function load() {} diff --git a/packages/plugins/src/typescript/tsconfig.ts b/packages/plugins/src/typescript/tsconfig.ts index 42aaf222..c46e9877 100644 --- a/packages/plugins/src/typescript/tsconfig.ts +++ b/packages/plugins/src/typescript/tsconfig.ts @@ -2,6 +2,7 @@ import { readJson, removeFile, writeJson } from "@mokr/core"; import path from "node:path"; // https://github.com/ffflorian/schemastore-updater/blob/main/schemas/tsconfig/index.d.ts +// https://github.com/microsoft/TypeScript/tree/main/lib export type Tsconfig = CompilerOptionsDefinition & CompileOnSaveDefinition & TypeAcquisitionDefinition & @@ -152,6 +153,14 @@ type CompilerOptionsDefinition = { | "es2021.promise" | "es2021.string" | "es2021.weakref" + | "es2022" + | "es2022.array" + | "es2022.error" + | "es2022.full" + | "es2022.intl" + | "es2022.object" + | "es2022.sharedmemory" + | "es2022.string" | "esnext" | "esnext.array" | "esnext.asynciterable" diff --git a/packages/plugins/src/typescript/typescript.ts b/packages/plugins/src/typescript/typescript.ts index 77837f00..9a6e6862 100644 --- a/packages/plugins/src/typescript/typescript.ts +++ b/packages/plugins/src/typescript/typescript.ts @@ -3,10 +3,10 @@ import { enqueueInstallDependency, enqueueRemoveDependency, getMonorepoDirectory, - logWarning, PluginArgs, PluginType, removeYarnPlugin, + warning, writeGitignore, writePackage, } from "@mokr/core"; @@ -21,18 +21,20 @@ const TSCONFIG_WORKSPACE: Tsconfig = { }, include: ["src/**/*"], }; + +// https://github.com/tsconfig/bases/blob/main/bases/node18-strictest-esm.combined.json const TSCONFIG_BASE: Tsconfig = { $schema: "https://json.schemastore.org/tsconfig", - display: "Node 16 + ESM + Strictest", + display: "Node 18 + ESM + Strictest", compilerOptions: { - lib: ["es2021"], + lib: ["es2022"], module: "es2022", - target: "es2021", - moduleResolution: "node", + target: "es2022", strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, + moduleResolution: "node", allowUnusedLabels: false, allowUnreachableCode: false, exactOptionalPropertyTypes: true, @@ -44,6 +46,8 @@ const TSCONFIG_BASE: Tsconfig = { noUnusedLocals: true, noUnusedParameters: true, importsNotUsedAsValues: "error", + + // Custom declaration: true, declarationMap: true, }, @@ -62,7 +66,7 @@ async function install({ directory }: PluginArgs) { await writeGitignore({ directory, - lines: ["# artifacts", "/dist", "/types"], + lines: ["", "# artifacts", "/dist", "/types"], }); await writePackage({ @@ -133,7 +137,7 @@ async function remove({ directory }: PluginArgs) { await removeTsconfig({ directory: monorepoDirectory }); } - logWarning("Please review your package.json manually"); + warning("Please review your package.json manually"); } async function load() {} diff --git a/packages/plugins/static/ci.yml b/packages/plugins/static/ci.yml index 6c33f218..a741aa82 100644 --- a/packages/plugins/static/ci.yml +++ b/packages/plugins/static/ci.yml @@ -11,16 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - include: - - node-version: 14 - node-lts: true - - node-version: 16 - node-lts: true - - node-version: 18 - node-lts: true - - node-version: 19 - node-lts: false - continue-on-error: ${{ ! matrix.node-lts }} + node-version: [14, 16, 18] steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 diff --git a/packages/plugins/static/format-check.yml b/packages/plugins/static/format-check.yml index 049e5bf2..10d07e2c 100644 --- a/packages/plugins/static/format-check.yml +++ b/packages/plugins/static/format-check.yml @@ -8,7 +8,6 @@ on: jobs: format-check: - name: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/packages/plugins/static/release.yml b/packages/plugins/static/release.yml index 7b190e46..4fef60d0 100644 --- a/packages/plugins/static/release.yml +++ b/packages/plugins/static/release.yml @@ -7,7 +7,6 @@ on: jobs: release: - name: release runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/packages/templates/package.json b/packages/templates/package.json index 1f8145e7..d408d6d0 100644 --- a/packages/templates/package.json +++ b/packages/templates/package.json @@ -22,7 +22,8 @@ "build:watch": "tsc --watch" }, "dependencies": { - "@mokr/core": "workspace:*" + "@mokr/core": "workspace:*", + "@mokr/plugins": "workspace:*" }, "devDependencies": { "@types/node": "18.11.12", diff --git a/packages/templates/src/bandersnatch.ts b/packages/templates/src/bandersnatch.ts index f243fdd6..0b6744d1 100644 --- a/packages/templates/src/bandersnatch.ts +++ b/packages/templates/src/bandersnatch.ts @@ -53,6 +53,6 @@ cli.run().catch(console.error); } export const bandersnatch = { - type: PluginType.Workspace, + type: PluginType.RepoOrWorkspace, apply, }; diff --git a/packages/templates/src/common.ts b/packages/templates/src/common.ts index 0077d6fa..594694e8 100644 --- a/packages/templates/src/common.ts +++ b/packages/templates/src/common.ts @@ -2,16 +2,23 @@ import { installPlugin, PluginType, TemplateArgs } from "@mokr/core"; async function apply({ directory }: TemplateArgs) { await installPlugin({ directory, name: "prettier" }); + await installPlugin({ directory, name: "husky" }); + await installPlugin({ directory, name: "lint-staged" }); + await installPlugin({ directory, name: "github-actions" }); + await installPlugin({ directory, name: "semantic-release" }); + await installPlugin({ directory, name: "devcontainer" }); + await installPlugin({ directory, name: "doctoc" }); + await installPlugin({ directory, name: "todos" }); } export const common = { - type: PluginType.Monorepo, + type: PluginType.Repo, apply, }; diff --git a/packages/templates/src/cra.ts b/packages/templates/src/cra.ts index b05b5961..30059585 100644 --- a/packages/templates/src/cra.ts +++ b/packages/templates/src/cra.ts @@ -3,8 +3,8 @@ import { getMonorepoDirectory, PluginType, readPackage, + removeDirectory, removeFile, - removePackage, TemplateArgs, writePackage, } from "@mokr/core"; @@ -14,9 +14,7 @@ async function apply({ directory }: TemplateArgs) { const monorepoDirectory = await getMonorepoDirectory({ directory }); const oldPackage = await readPackage({ directory }); - await removePackage({ directory }); - - await removeFile({ path: join(directory, "README.md") }); + await removeDirectory({ directory }); await exec( "yarn", @@ -33,14 +31,16 @@ async function apply({ directory }: TemplateArgs) { ); // Weird problem where we are left with a package-lock.json file after installation - await removeFile({ - path: join(monorepoDirectory ?? directory, "package-lock.json"), - }); + if (monorepoDirectory) { + await removeFile({ + path: join(monorepoDirectory ?? directory, "package-lock.json"), + }); + } await writePackage({ directory, data: oldPackage }); } export const cra = { - type: PluginType.Workspace, + type: PluginType.RepoOrWorkspace, apply, }; diff --git a/packages/templates/src/express.ts b/packages/templates/src/express.ts index 19881008..2ab31bce 100644 --- a/packages/templates/src/express.ts +++ b/packages/templates/src/express.ts @@ -10,7 +10,6 @@ import { basename, join } from "node:path"; async function apply({ directory }: TemplateArgs) { await installPlugin({ directory, name: "typescript" }); - await installPlugin({ directory, name: "jest" }); enqueueInstallDependency({ directory, identifier: "express" }); @@ -54,6 +53,6 @@ app.listen(process.env.PORT || 3000); } export const express = { - type: PluginType.Workspace, + type: PluginType.RepoOrWorkspace, apply, }; diff --git a/packages/templates/src/githubAction.ts b/packages/templates/src/githubAction.ts new file mode 100644 index 00000000..13a4fcba --- /dev/null +++ b/packages/templates/src/githubAction.ts @@ -0,0 +1,156 @@ +import { + enqueueInstallDependency, + hasPlugin, + installPlugin, + PluginType, + readGitignore, + TemplateArgs, + warning, + writeFile, + writeGitignore, + writeYaml, +} from "@mokr/core"; +import { addPreCommitHookCommand } from "@mokr/plugins"; +import { basename, join, resolve } from "node:path"; + +async function apply({ directory }: TemplateArgs) { + const name = basename(directory); + const description = `Description of ${name} action`; + + await installPlugin({ directory, name: "typescript" }); + + await installPlugin({ directory, name: "esbuild" }); + + // Dist directory with generated bundle should be checked in + await writeGitignore({ + directory, + lines: ( + await readGitignore({ directory }) + ).filter((line) => line !== "/dist"), + append: false, + }); + + // But Git should ignore generated files + await writeFile({ + path: resolve(directory, ".gitattributes"), + contents: `*.js linguist-generated=true`, + }); + + // Use Husky to build before commit + await installPlugin({ directory, name: "husky" }); + + await addPreCommitHookCommand({ + directory, + command: "yarn build && git add dist", + }); + + // Required action.yml + await writeYaml({ + path: resolve(directory, "action.yml"), + data: { + name, + description, + inputs: { + "gh-token": { + description: "GitHub token with read access to the repository", + required: false, + default: "${{ github.token }}", + }, + }, + outputs: { + result: { + description: "Some result from this action", + }, + }, + runs: { + using: "node16", + main: "dist/index.js", + }, + }, + }); + + // Example code + enqueueInstallDependency({ + directory, + identifier: ["@actions/core", "@actions/github"], + }); + + await writeFile({ + path: join(directory, "src/index.ts"), + contents: ` +import { getInput, info, setFailed, setOutput } from "@actions/core"; +import { context, getOctokit } from "@actions/github"; + +async function run() { + const ghToken = getInput("gh-token"); + + if (!ghToken) { + throw new Error("The GitHub token is missing"); + } + + const octokit = getOctokit(ghToken); + const { owner, repo } = context.repo; + + const response = await octokit.rest.repos.getReadme({ + owner, + repo, + }); + + info("Action complete"); + + return { + result: response.data.content, + }; +} + +run() + .then((outputs) => + Object.entries(outputs).forEach(([key, value]) => { + setOutput(key, value); + }) + ) + .catch(setFailed); +`, + }); + + await writeFile({ + path: join(directory, "README.md"), + contents: ` +# ${name} action + +${description} + +## Usage + +\`\`\`yaml +- uses: ${name}@v1 + with: + gh-token: \${{ secrets.GH_PAT }} +\`\`\` + +## Inputs + +| name | required | default | description | +| ---------- | -------- | -------------- | ----------------------------------------------- | +| \`gh-token\` | | \`github.token\` | GitHub token with read access to the repository | + + +## Outputs + +| name | description | +| -------- | ---------------------------- | +| \`result\` | Some result from this action | +`, + }); + + if (await hasPlugin({ directory, name: "semantic-release" })) { + warning( + 'Please modify your ".releaserc.json" file to set "semantic-release-yarn.npmPublish" to "false" to prevent uploading this package to NPM. See [semantic-release-yarn plugin options](https://github.com/hongaar/semantic-release-yarn#plugin-options) for more information.' + ); + } +} + +export const githubAction = { + type: PluginType.Repo, + apply, +}; diff --git a/packages/templates/src/index.ts b/packages/templates/src/index.ts index 8cb66164..30ecb8ff 100644 --- a/packages/templates/src/index.ts +++ b/packages/templates/src/index.ts @@ -2,4 +2,5 @@ export * from "./bandersnatch.js"; export * from "./common.js"; export * from "./cra.js"; export * from "./express.js"; +export * from "./githubAction.js"; export * from "./lib.js"; diff --git a/packages/templates/src/lib.ts b/packages/templates/src/lib.ts index 5330b9e0..f09459d2 100644 --- a/packages/templates/src/lib.ts +++ b/packages/templates/src/lib.ts @@ -32,6 +32,6 @@ test("adds 1 + 2 to equal 3", () => { } export const lib = { - type: PluginType.Workspace, + type: PluginType.RepoOrWorkspace, apply, }; diff --git a/yarn.lock b/yarn.lock index 0e182b61..bc471a00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -735,13 +735,17 @@ __metadata: version: 0.0.0-use.local resolution: "@mokr/core@workspace:packages/core" dependencies: + "@types/hosted-git-info": 3.0.2 "@types/jest": 29.2.4 "@types/node": 18.11.13 chalk: 5.2.0 deepmerge: 4.2.2 + hosted-git-info: 6.1.1 jest: 29.3.1 + license: 1.0.3 ora: 6.1.2 pkg-up: ^4.0.0 + sort-package-json: 2.1.0 ts-jest: 29.0.3 typescript: 4.9.4 yaml: 2.1.3 @@ -781,6 +785,7 @@ __metadata: resolution: "@mokr/templates@workspace:packages/templates" dependencies: "@mokr/core": "workspace:*" + "@mokr/plugins": "workspace:*" "@types/node": 18.11.12 typescript: 4.9.4 languageName: unknown @@ -1328,6 +1333,13 @@ __metadata: languageName: node linkType: hard +"@ovyerus/licenses@npm:^6.4.4": + version: 6.4.4 + resolution: "@ovyerus/licenses@npm:6.4.4" + checksum: 52d5e607655056b262940943a18a021c91afac8eabc7323b64dda42db6781e3c76f9949aef3f2b8271d5bf82fc6b14bab2279a643765f0abc08f7e572dcc625e + languageName: node + linkType: hard + "@pnpm/network.ca-file@npm:^1.0.1": version: 1.0.2 resolution: "@pnpm/network.ca-file@npm:1.0.2" @@ -1594,6 +1606,13 @@ __metadata: languageName: node linkType: hard +"@types/hosted-git-info@npm:3.0.2": + version: 3.0.2 + resolution: "@types/hosted-git-info@npm:3.0.2" + checksum: b922a6f13c79d0388762a92e943d098cbc950be20754508de4946d3529ecab730e3be01ebdbe0a235b85ddcc3f7251d8df3073d98978d43018a76200b47e548d + languageName: node + linkType: hard + "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.4 resolution: "@types/istanbul-lib-coverage@npm:2.0.4" @@ -2323,7 +2342,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.3.1": +"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b @@ -2540,6 +2559,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -2698,6 +2728,20 @@ __metadata: languageName: node linkType: hard +"configstore@npm:^5.0.1": + version: 5.0.1 + resolution: "configstore@npm:5.0.1" + dependencies: + dot-prop: ^5.2.0 + graceful-fs: ^4.1.2 + make-dir: ^3.0.0 + unique-string: ^2.0.0 + write-file-atomic: ^3.0.0 + xdg-basedir: ^4.0.0 + checksum: 60ef65d493b63f96e14b11ba7ec072fdbf3d40110a94fb7199d1c287761bdea5c5244e76b2596325f30c1b652213aa75de96ea20afd4a5f82065e61ea090988e + languageName: node + linkType: hard + "console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -2869,7 +2913,7 @@ __metadata: languageName: node linkType: hard -"decamelize@npm:^1.1.0": +"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa @@ -2943,6 +2987,20 @@ __metadata: languageName: node linkType: hard +"detect-indent@npm:^6.0.0": + version: 6.1.0 + resolution: "detect-indent@npm:6.1.0" + checksum: ab953a73c72dbd4e8fc68e4ed4bfd92c97eb6c43734af3900add963fd3a9316f3bc0578b018b24198d4c31a358571eff5f0656e81a1f3b9ad5c547d58b2d093d + languageName: node + linkType: hard + +"detect-indent@npm:^7.0.1": + version: 7.0.1 + resolution: "detect-indent@npm:7.0.1" + checksum: cbf3f0b1c3c881934ca94428e1179b26ab2a587e0d719031d37a67fb506d49d067de54ff057cb1e772e75975fed5155c01cd4518306fee60988b1486e3fc7768 + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -2950,6 +3008,13 @@ __metadata: languageName: node linkType: hard +"detect-newline@npm:^4.0.0": + version: 4.0.0 + resolution: "detect-newline@npm:4.0.0" + checksum: 52767347c70f485b2d1db6493dde57b8c3c1f249e24bad7eb7424cc1129200aa7e671902ede18bc94a8b69e10dec91456aab4c7e2478559d9eedb31ef3847f36 + languageName: node + linkType: hard + "dezalgo@npm:^1.0.0": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -3037,7 +3102,7 @@ __metadata: languageName: node linkType: hard -"dot-prop@npm:^5.1.0": +"dot-prop@npm:^5.1.0, dot-prop@npm:^5.2.0": version: 5.3.0 resolution: "dot-prop@npm:5.3.0" dependencies: @@ -3497,6 +3562,13 @@ __metadata: languageName: node linkType: hard +"fuzzy-search@npm:^3.2.1": + version: 3.2.1 + resolution: "fuzzy-search@npm:3.2.1" + checksum: 65d50909e69216f830a6e88eade08df00662e2c081edb02063404cc922cc7319ef8958eff781aa4e3e64543a394a912cd8c80abb4c6b158c119e811364e7cfb9 + languageName: node + linkType: hard + "gauge@npm:^4.0.3": version: 4.0.4 resolution: "gauge@npm:4.0.4" @@ -3536,7 +3608,7 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": +"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 @@ -3564,6 +3636,20 @@ __metadata: languageName: node linkType: hard +"git-config-path@npm:^2.0.0": + version: 2.0.0 + resolution: "git-config-path@npm:2.0.0" + checksum: f67bee619b76a339d39dee6094c4db914512e3ca7e5ec0a05421b81a6ad0221d6fccfcc0b1c5127cf5af4ed3a49184279bde91b61c003f1cff58ac61e7139cfc + languageName: node + linkType: hard + +"git-hooks-list@npm:^3.0.0": + version: 3.0.0 + resolution: "git-hooks-list@npm:3.0.0" + checksum: 32b3b8fed611b81e5ed069279608a6fcb6122901dcc56cb53f666977d006c6e7ab82febfd8c38ce56e6d47d17d5d822847bf857f3bd33980b8a036e55efcad7f + languageName: node + linkType: hard + "git-log-parser@npm:^1.2.0": version: 1.2.0 resolution: "git-log-parser@npm:1.2.0" @@ -3635,7 +3721,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^13.1.1": +"globby@npm:^13.1.1, globby@npm:^13.1.2": version: 13.1.2 resolution: "globby@npm:13.1.2" dependencies: @@ -3717,6 +3803,15 @@ __metadata: languageName: node linkType: hard +"hosted-git-info@npm:6.1.1, hosted-git-info@npm:^6.0.0, hosted-git-info@npm:^6.1.1": + version: 6.1.1 + resolution: "hosted-git-info@npm:6.1.1" + dependencies: + lru-cache: ^7.5.1 + checksum: fcd3ca2eaa05f3201425ccbb8aa47f88cdda4a3a6d79453f8e269f7171356278bd1db08f059d8439eb5eaa91c6a8a20800fc49cca6e9e4e899b202a332d5ba6b + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -3742,15 +3837,6 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^6.0.0, hosted-git-info@npm:^6.1.1": - version: 6.1.1 - resolution: "hosted-git-info@npm:6.1.1" - dependencies: - lru-cache: ^7.5.1 - checksum: fcd3ca2eaa05f3201425ccbb8aa47f88cdda4a3a6d79453f8e269f7171356278bd1db08f059d8439eb5eaa91c6a8a20800fc49cca6e9e4e899b202a332d5ba6b - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -3945,7 +4031,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:^1.3.4, ini@npm:~1.3.0": +"ini@npm:^1.3.4, ini@npm:^1.3.5, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3 @@ -4169,6 +4255,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.1.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce + languageName: node + linkType: hard + "is-plain-object@npm:^5.0.0": version: 5.0.0 resolution: "is-plain-object@npm:5.0.0" @@ -4199,6 +4292,13 @@ __metadata: languageName: node linkType: hard +"is-typedarray@npm:^1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 + languageName: node + linkType: hard + "is-unicode-supported@npm:^1.1.0": version: 1.3.0 resolution: "is-unicode-supported@npm:1.3.0" @@ -5169,6 +5269,25 @@ __metadata: languageName: node linkType: hard +"license@npm:1.0.3": + version: 1.0.3 + resolution: "license@npm:1.0.3" + dependencies: + "@ovyerus/licenses": ^6.4.4 + configstore: ^5.0.1 + detect-indent: ^6.0.0 + fuzzy-search: ^3.2.1 + git-config-path: ^2.0.0 + parse-git-config: ^3.0.0 + prompts: ^2.3.2 + wrap-text: ^1.0.8 + yargs: ^15.3.1 + bin: + license: dist/cli/index.js + checksum: 674a791565313598312ab0370a201873a5c76f5d0834da525dab04e5d0f9f706cc29456d30034cab030ace6cfb279b3df0dc05db5152baa59b4021a4161cfd55 + languageName: node + linkType: hard + "lilconfig@npm:2.0.6": version: 2.0.6 resolution: "lilconfig@npm:2.0.6" @@ -6839,6 +6958,16 @@ __metadata: languageName: node linkType: hard +"parse-git-config@npm:^3.0.0": + version: 3.0.0 + resolution: "parse-git-config@npm:3.0.0" + dependencies: + git-config-path: ^2.0.0 + ini: ^1.3.5 + checksum: 243aa781d08f3208e94708a59e3410af90a7e831054c08775e7aeccaa23fc03240092ffd829412ee3dbe57f33ae876883b7e63171499041d4522e92d7d624c13 + languageName: node + linkType: hard + "parse-json@npm:^4.0.0": version: 4.0.0 resolution: "parse-json@npm:4.0.0" @@ -7071,7 +7200,7 @@ __metadata: languageName: node linkType: hard -"prompts@npm:^2.0.1": +"prompts@npm:^2.0.1, prompts@npm:^2.3.2": version: 2.4.2 resolution: "prompts@npm:2.4.2" dependencies: @@ -7390,6 +7519,13 @@ __metadata: languageName: node linkType: hard +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: e9e294695fea08b076457e9ddff854e81bffbe248ed34c1eec348b7abbd22a0d02e8d75506559e2265e96978f3c4720bd77a6dad84755de8162b357eb6c778c7 + languageName: node + linkType: hard + "resolve-cwd@npm:^3.0.0": version: 3.0.0 resolution: "resolve-cwd@npm:3.0.0" @@ -7771,6 +7907,29 @@ __metadata: languageName: node linkType: hard +"sort-object-keys@npm:^1.1.3": + version: 1.1.3 + resolution: "sort-object-keys@npm:1.1.3" + checksum: abea944d6722a1710a1aa6e4f9509da085d93d5fc0db23947cb411eedc7731f80022ce8fa68ed83a53dd2ac7441fcf72a3f38c09b3d9bbc4ff80546aa2e151ad + languageName: node + linkType: hard + +"sort-package-json@npm:2.1.0": + version: 2.1.0 + resolution: "sort-package-json@npm:2.1.0" + dependencies: + detect-indent: ^7.0.1 + detect-newline: ^4.0.0 + git-hooks-list: ^3.0.0 + globby: ^13.1.2 + is-plain-obj: ^4.1.0 + sort-object-keys: ^1.1.3 + bin: + sort-package-json: cli.js + checksum: 72b0055676f607e0f2385a102baad05fc9a4cd62b2e4b6ab1dc13e083c36adc83130c4a6c8fb53857f8a816c0b17901a1479fef21719b1f31df09a7b9287f27a + languageName: node + linkType: hard + "source-map-support@npm:0.5.13": version: 0.5.13 resolution: "source-map-support@npm:0.5.13" @@ -8335,6 +8494,15 @@ __metadata: languageName: node linkType: hard +"typedarray-to-buffer@npm:^3.1.5": + version: 3.1.5 + resolution: "typedarray-to-buffer@npm:3.1.5" + dependencies: + is-typedarray: ^1.0.0 + checksum: 99c11aaa8f45189fcfba6b8a4825fd684a321caa9bd7a76a27cf0c7732c174d198b99f449c52c3818107430b5f41c0ccbbfb75cb2ee3ca4a9451710986d61a60 + languageName: node + linkType: hard + "typescript@npm:4.9.4": version: 4.9.4 resolution: "typescript@npm:4.9.4" @@ -8608,6 +8776,13 @@ __metadata: languageName: node linkType: hard +"which-module@npm:^2.0.0": + version: 2.0.0 + resolution: "which-module@npm:2.0.0" + checksum: 809f7fd3dfcb2cdbe0180b60d68100c88785084f8f9492b0998c051d7a8efe56784492609d3f09ac161635b78ea29219eb1418a98c15ce87d085bce905705c9c + languageName: node + linkType: hard + "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -8668,6 +8843,13 @@ __metadata: languageName: node linkType: hard +"wrap-text@npm:^1.0.8": + version: 1.0.9 + resolution: "wrap-text@npm:1.0.9" + checksum: 390f5e2bf2de4c993a33ebca2735e8c2fa82cdee2f2c6daa973362188a0d43be2559571239fbd3df469da86bc7219ef3287883fd9865595ffc84887df39c347f + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -8675,6 +8857,18 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^3.0.0": + version: 3.0.3 + resolution: "write-file-atomic@npm:3.0.3" + dependencies: + imurmurhash: ^0.1.4 + is-typedarray: ^1.0.0 + signal-exit: ^3.0.2 + typedarray-to-buffer: ^3.1.5 + checksum: c55b24617cc61c3a4379f425fc62a386cc51916a9b9d993f39734d005a09d5a4bb748bc251f1304e7abd71d0a26d339996c275955f527a131b1dcded67878280 + languageName: node + linkType: hard + "write-file-atomic@npm:^4.0.0, write-file-atomic@npm:^4.0.1": version: 4.0.2 resolution: "write-file-atomic@npm:4.0.2" @@ -8695,6 +8889,13 @@ __metadata: languageName: node linkType: hard +"xdg-basedir@npm:^4.0.0": + version: 4.0.0 + resolution: "xdg-basedir@npm:4.0.0" + checksum: 0073d5b59a37224ed3a5ac0dd2ec1d36f09c49f0afd769008a6e9cd3cd666bd6317bd1c7ce2eab47e1de285a286bad11a9b038196413cd753b79770361855f3c + languageName: node + linkType: hard + "xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" @@ -8702,6 +8903,13 @@ __metadata: languageName: node linkType: hard +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 014dfcd9b5f4105c3bb397c1c8c6429a9df004aa560964fb36732bfb999bfe83d45ae40aeda5b55d21b1ee53d8291580a32a756a443e064317953f08025b1aa4 + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8" @@ -8730,6 +8938,16 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 + languageName: node + linkType: hard + "yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.3": version: 20.2.9 resolution: "yargs-parser@npm:20.2.9" @@ -8744,6 +8962,25 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^15.3.1": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: ^6.0.0 + decamelize: ^1.2.0 + find-up: ^4.1.0 + get-caller-file: ^2.0.1 + require-directory: ^2.1.1 + require-main-filename: ^2.0.0 + set-blocking: ^2.0.0 + string-width: ^4.2.0 + which-module: ^2.0.0 + y18n: ^4.0.0 + yargs-parser: ^18.1.2 + checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373 + languageName: node + linkType: hard + "yargs@npm:^16.2.0": version: 16.2.0 resolution: "yargs@npm:16.2.0"