From e6b8371479c24b72873a5ee683b1067802dad6df Mon Sep 17 00:00:00 2001 From: Joram van den Boezem Date: Thu, 24 Nov 2022 21:33:10 +0000 Subject: [PATCH] feat: doctoc and todo plugins --- .husky/pre-commit | 7 +- README.md | 47 ++- TODO.md | 13 +- package.json | 10 +- packages/core/src/plugin.ts | 2 + packages/plugins/src/dependabot/dependabot.ts | 90 +---- .../src/devcontainer/devcontainerJson.ts | 334 +----------------- packages/plugins/src/doctoc/doctoc.ts | 71 ++++ packages/plugins/src/husky/husky.ts | 92 +++++ packages/plugins/src/index.ts | 2 + packages/plugins/src/lintStaged/lintStaged.ts | 93 +---- packages/plugins/src/todos/todos.ts | 72 ++++ packages/templates/src/common.ts | 2 + yarn.lock | 8 +- 14 files changed, 305 insertions(+), 538 deletions(-) create mode 100644 packages/plugins/src/doctoc/doctoc.ts create mode 100644 packages/plugins/src/todos/todos.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index 055e2e9d..2b3a5f48 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,6 +2,7 @@ . "$(dirname -- "$0")/_/husky.sh" yarn doctoc -yarn leasot -git add . -yarn lint-staged \ No newline at end of file +yarn prettier --write README.md +yarn todos +yarn prettier --write TODO.md +yarn lint-staged diff --git a/README.md b/README.md index 24759972..856a2a97 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,14 @@ yarn moker add --template cra client - [Available plugins](#available-plugins) - [`dependabot` _monorepo_](#dependabot-_monorepo_) - [`devcontainer` _monorepo_](#devcontainer-_monorepo_) + - [`doctoc` _monorepo_](#doctoc-_monorepo_) - [`github-actions` _monorepo_](#github-actions-_monorepo_) - [`husky` _monorepo_](#husky-_monorepo_) + - [`jest` _workspace_](#jest-_workspace_) - [`lint-staged` _monorepo_](#lint-staged-_monorepo_) - [`prettier` _monorepo_](#prettier-_monorepo_) - - [`jest` _workspace_](#jest-_workspace_) - [`semantic-release` _monorepo_](#semantic-release-_monorepo_) + - [`todos` _workspace_](#todos-_workspace_) - [`typescript` _workspace_](#typescript-_workspace_) - [Available templates](#available-templates) - [`common` _monorepo_](#common-_monorepo_) @@ -163,6 +165,13 @@ image. If you have the `prettier` plugin installed, it will add the [Prettier VS Code extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode). +## `doctoc` _monorepo_ + +This plugin adds a script to generate a table of contents for the README using +[doctoc](https://github.com/thlorenz/doctoc). + +If you have the `husky` plugin installed, it will also add a pre-commit hook. + ## `github-actions` _monorepo_ This plugin creates a simple `ci.yml` @@ -186,6 +195,11 @@ repository: This plugin sets up [Husky](https://typicode.github.io/husky/#/) at the monorepo level. +## `jest` _workspace_ + +This plugin sets up [Jest](https://jestjs.io) and adds a `test` and `watch:test` +script to both the workspace and the monorepo. + ## `lint-staged` _monorepo_ This plugin sets up [lint-staged](https://github.com/okonet/lint-staged) at the @@ -211,11 +225,6 @@ This plugin sets up [Prettier](https://prettier.io). > always be truncated to match whatever the `printWidth` setting is. This makes > it so much easier to read and write markdown files! -## `jest` _workspace_ - -This plugin sets up [Jest](https://jestjs.io) and adds a `test` and `watch:test` -script to both the workspace and the monorepo. - ## `semantic-release` _monorepo_ This plugin sets up @@ -252,6 +261,13 @@ Otherwise, the `semantic-release` process will skip the `publish` step. [patch-semantic-commit.js]: https://github.com/hongaar/moker/blob/main/scripts/patch-semantic-commit.js +## `todos` _workspace_ + +This plugin adds a script to generate a TODO markdown file from all code +annotations using [leasot](https://github.com/pgilad/leasot). + +If you have the `husky` plugin installed, it will also add a pre-commit hook. + ## `typescript` _workspace_ This plugin sets up [TypeScript](https://www.typescriptlang.org) and adds a @@ -261,15 +277,8 @@ This plugin sets up [TypeScript](https://www.typescriptlang.org) and adds a ## `common` _monorepo_ -This is the only monorepo template at this point. It simply installs these -plugins in the monorepo: - -- `prettier` -- `husky` -- `lint-staged` -- `semantic-release` -- `github-actions` -- `devcontainer` +This is the only monorepo template at this point. It simply installs all +available monorepo plugins. ## `bandersnatch` _workspace_ @@ -298,16 +307,16 @@ Contributions are very welcome! ## Roadmap -- [x] github-actions plugin -- [x] devcontainer plugin -- [ ] leasot (todos) plugin -- [ ] doctoc plugin - [ ] Add LICENSE file to monorepo - [ ] Support for `swc`/`esbuild` - [ ] A compat lib (which builds cjs and mjs targets) - [ ] Adapt for non-monorepo use-cases (?) - [ ] Blog post / tutorial - [ ] Docs for writing custom plugins / templates +- [x] github-actions plugin +- [x] devcontainer plugin +- [x] leasot (todos) plugin +- [x] doctoc plugin - [x] semantic-release plugin - [x] Port templates - [x] Support for BYO plugins/templates diff --git a/TODO.md b/TODO.md index 36711363..c7446d96 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,8 @@ ### TODOs -| Filename | line # | TODO | -|:------|:------:|:------| -| [packages/core/src/yarnrc.ts](packages/core/src/yarnrc.ts#L23) | 23 | etc, fix later | -| [packages/cli/src/commands/create.ts](packages/cli/src/commands/create.ts#L16) | 16 | re-enable prompts | -| [packages/cli/src/commands/list.ts](packages/cli/src/commands/list.ts#L15) | 15 | list workspaces | -| [packages/plugins/src/jest/jest.ts](packages/plugins/src/jest/jest.ts#L35) | 35 | install jest without ts-jest | + +| Filename | line # | TODO | +| :----------------------------------------------------------------------------- | :----: | :--------------------------- | +| [packages/core/src/yarnrc.ts](packages/core/src/yarnrc.ts#L23) | 23 | etc, fix later | +| [packages/cli/src/commands/create.ts](packages/cli/src/commands/create.ts#L16) | 16 | re-enable prompts | +| [packages/cli/src/commands/list.ts](packages/cli/src/commands/list.ts#L15) | 15 | list workspaces | +| [packages/plugins/src/jest/jest.ts](packages/plugins/src/jest/jest.ts#L35) | 35 | install jest without ts-jest | diff --git a/package.json b/package.json index befab792..34e06262 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "semantic-release", "github-actions", "devcontainer", - "dependabot" + "dependabot", + "todos", + "doctoc" ] }, "packageManager": "yarn@3.2.4", @@ -31,16 +33,16 @@ "format": "prettier --write --ignore-unknown .", "format-check": "prettier --check --ignore-unknown .", "doctoc": "doctoc README.md", - "leasot": "leasot --exit-nicely --reporter markdown --ignore \"**/node_modules\" \"**/*.ts\" > TODO.md", + "todos": "leasot --exit-nicely --reporter markdown --ignore \"**/node_modules\" \"**/*.ts\" > TODO.md", "release": "semantic-release" }, "devDependencies": { "@semantic-release/changelog": "6.0.1", "@semantic-release/git": "10.0.1", "@types/prettier": "^2", - "doctoc": "^2.2.1", + "doctoc": "2.2.1", "husky": "8.0.2", - "leasot": "^13.2.0", + "leasot": "13.2.0", "lint-staged": "13.0.3", "prettier": "2.7.1", "semantic-release": "19.0.5" diff --git a/packages/core/src/plugin.ts b/packages/core/src/plugin.ts index d52d67b0..ea8f6e6a 100644 --- a/packages/core/src/plugin.ts +++ b/packages/core/src/plugin.ts @@ -32,6 +32,8 @@ const CORE_PLUGINS = [ "jest", "semantic-release", "dependabot", + "todos", + "doctoc", ]; export function isPlugin(plugin: unknown): plugin is Plugin { diff --git a/packages/plugins/src/dependabot/dependabot.ts b/packages/plugins/src/dependabot/dependabot.ts index e00f1191..b9e8a3e2 100644 --- a/packages/plugins/src/dependabot/dependabot.ts +++ b/packages/plugins/src/dependabot/dependabot.ts @@ -22,59 +22,30 @@ type GitHubDependabotV2Config = { updates: PackageEcosystem[]; registries?: Registries; }; -/** - * Element for each one package manager that you want GitHub Dependabot to monitor for new versions - */ + interface PackageEcosystem { - /** - * Customize which updates are allowed - */ allow?: { "dependency-name"?: string; "dependency-type"?: DependencyType; [k: string]: any; }[]; - /** - * Assignees to set on pull requests - */ assignees?: string[]; - /** - * Commit message preferences - */ "commit-message"?: { prefix?: string; "prefix-development"?: string; include?: "scope"; [k: string]: any; }; - /** - * Location of package manifests - */ directory: string; - /** - * Ignore certain dependencies or versions - */ ignore?: { "dependency-name"?: string; "dependency-type"?: DependencyType; versions?: string[]; [k: string]: any; }[]; - /** - * Labels to set on pull requests - */ labels?: string[]; - /** - * Milestone to set on pull requests - */ milestone?: string | number; - /** - * Limit number of open pull requests for version updates - */ "open-pull-requests-limit"?: number; - /** - * Package manager to use - */ "package-ecosystem": | "bundler" | "cargo" @@ -92,32 +63,14 @@ interface PackageEcosystem { | "pip" | "pub" | "terraform"; - /** - * Pull request branch name preferences - */ "pull-request-branch-name"?: { - /** - * Change separator for PR branch name - */ separator: string; [k: string]: any; }; - /** - * Disable automatic rebasing - */ "rebase-strategy"?: "auto" | "disabled"; - /** - * Reviewers to set on pull requests - */ reviewers?: string[]; - /** - * Schedule preferences - */ schedule: { interval?: ScheduleInterval; - /** - * Specify an alternative day to check for updates - */ day?: | "monday" | "tuesday" @@ -126,23 +79,11 @@ interface PackageEcosystem { | "friday" | "saturday" | "sunday"; - /** - * Specify an alternative time of day to check for updates (format: hh:mm) - */ time?: string; - /** - * The time zone identifier must be from the Time Zone database maintained by IANA - */ timezone?: string; [k: string]: any; }; - /** - * Branch to create pull requests against - */ "target-branch"?: string; - /** - * How to update manifest version requirements - */ "versioning-strategy"?: | "lockfile-only" | "auto" @@ -151,18 +92,9 @@ interface PackageEcosystem { | "increase-if-necessary"; [k: string]: any; } -/** - * The top-level registries key is optional. It allows you to specify authentication details that Dependabot can use to access private package registries. - */ + interface Registries { - /** - * This interface was referenced by `Registries`'s JSON-Schema definition - * via the `patternProperty` ".*". - */ [k: string]: { - /** - * Identifies the type of registry. - */ type: | "composer-repository" | "docker-registry" @@ -174,29 +106,11 @@ interface Registries { | "python-index" | "rubygems-server" | "terraform-registry"; - /** - * The URL to use to access the dependencies in this registry. The protocol is optional. If not specified, https:// is assumed. Dependabot adds or ignores trailing slashes as required. - */ url?: string; - /** - * The username that Dependabot uses to access the registry. - */ username?: string; - /** - * A reference to a Dependabot secret containing the password for the specified user. - */ password?: string; - /** - * A reference to a Dependabot secret containing an access key for this registry. - */ key?: string; - /** - * A reference to a Dependabot secret containing an access token for this registry. - */ token?: string; - /** - * For registries with type: python-index, if the boolean value is true, pip resolves dependencies by using the specified URL rather than the base URL of the Python Package Index (by default https://pypi.org/simple). - */ "replaces-base"?: boolean; organization?: string; }; diff --git a/packages/plugins/src/devcontainer/devcontainerJson.ts b/packages/plugins/src/devcontainer/devcontainerJson.ts index f6141ee7..07b9c8a6 100644 --- a/packages/plugins/src/devcontainer/devcontainerJson.ts +++ b/packages/plugins/src/devcontainer/devcontainerJson.ts @@ -9,31 +9,13 @@ type DevcontainerJsonBase = ) & DevContainerCommon) | { - /** - * A name for the dev container which can be displayed to the user. - */ name?: string; - /** - * Features to add to the dev container. - */ features?: { [k: string]: any; }; - /** - * Ports that are forwarded from the container to the local machine. Can be an integer port number, or a string of the format "host:port_number". - */ forwardPorts?: (number | string)[]; portsAttributes?: { - /** - * A port, range of ports (ex. "40000-55000"), or regular expression (ex. ".+\\/server.js"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression. - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "(^\d+(-\d+)?$)|(.+)". - */ [k: string]: { - /** - * Defines the action that occurs when the port is discovered for automatic forwarding - */ onAutoForward?: | "notify" | "openBrowser" @@ -41,177 +23,87 @@ type DevcontainerJsonBase = | "openPreview" | "silent" | "ignore"; - /** - * Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port. - */ elevateIfNeeded?: boolean; - /** - * Label that will be shown in the UI for this port. - */ label?: string; requireLocalPort?: boolean; - /** - * The protocol to use when forwarding this port. - */ protocol?: "http" | "https"; [k: string]: any; }; }; otherPortsAttributes?: { - /** - * Defines the action that occurs when the port is discovered for automatic forwarding - */ onAutoForward?: | "notify" | "openBrowser" | "openPreview" | "silent" | "ignore"; - /** - * Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port. - */ elevateIfNeeded?: boolean; - /** - * Label that will be shown in the UI for this port. - */ label?: string; requireLocalPort?: boolean; - /** - * The protocol to use when forwarding this port. - */ protocol?: "http" | "https"; }; - /** - * Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder. - */ updateRemoteUserUID?: boolean; - /** - * Container environment variables. - */ containerEnv?: { [k: string]: string; }; - /** - * The user the container will be started with. The default is the user on the Docker image. - */ containerUser?: string; - /** - * Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax. - */ mounts?: (Mount | string)[]; - /** - * Passes the --init flag when creating the dev container. - */ init?: boolean; - /** - * Passes the --privileged flag when creating the dev container. - */ privileged?: boolean; - /** - * Passes docker capabilities to include when creating the dev container. - */ capAdd?: string[]; - /** - * Passes docker security options to include when creating the dev container. - */ securityOpt?: string[]; - /** - * Remote environment variables to set for processes spawned in the container including lifecycle scripts and any remote editor/IDE server process. - */ remoteEnv?: { [k: string]: string | null; }; - /** - * The username to use for spawning processes in the container including lifecycle scripts and any remote editor/IDE server process. The default is the same user as the container. - */ remoteUser?: string; - /** - * A command to run locally before anything else. This command is run before "onCreateCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ initializeCommand?: string | string[]; - /** - * A command to run when creating the container. This command is run after "initializeCommand" and before "updateContentCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ onCreateCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run when creating the container and rerun when the workspace content was updated while creating the container. This command is run after "onCreateCommand" and before "postCreateCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ updateContentCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run after creating the container. This command is run after "updateContentCommand" and before "postStartCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postCreateCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run after starting the container. This command is run after "postCreateCommand" and before "postAttachCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postStartCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run when attaching to the container. This command is run after "postStartCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postAttachCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * The user command to wait for before continuing execution in the background while the UI is starting up. The default is "updateContentCommand". - */ waitFor?: | "initializeCommand" | "onCreateCommand" | "updateContentCommand" | "postCreateCommand" | "postStartCommand"; - /** - * User environment probe to run. The default is "loginInteractiveShell". - */ userEnvProbe?: | "none" | "loginShell" | "loginInteractiveShell" | "interactiveShell"; - /** - * Host hardware requirements. - */ hostRequirements?: { - /** - * Number of required CPUs. - */ cpus?: number; - /** - * Amount of required RAM in bytes. Supports units tb, gb, mb and kb. - */ memory?: string; - /** - * Amount of required disk space in bytes. Supports units tb, gb, mb and kb. - */ storage?: string; [k: string]: any; }; - /** - * Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations. - */ customizations?: { [k: string]: any; }; @@ -219,52 +111,26 @@ type DevcontainerJsonBase = [k: string]: any; }; }; + type DockerfileContainer = | { - /** - * Docker build-related options. - */ build: { - /** - * The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file. - */ dockerfile: string; - /** - * The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file. - */ context?: string; [k: string]: any; } & BuildOptions; [k: string]: any; } | ({ - /** - * The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file. - */ dockerFile: string; - /** - * The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file. - */ context?: string; [k: string]: any; } & { - /** - * Docker build-related options. - */ build?: { - /** - * Target stage in a multi-stage build. - */ target?: string; - /** - * Build arguments. - */ args?: { [k: string]: string; }; - /** - * The image to consider as a cache. Use an array to specify multiple images. - */ cacheFrom?: string | string[]; [k: string]: any; }; @@ -272,111 +138,47 @@ type DockerfileContainer = }); interface BuildOptions { - /** - * Target stage in a multi-stage build. - */ target?: string; - /** - * Build arguments. - */ args?: { [k: string]: string; }; - /** - * The image to consider as a cache. Use an array to specify multiple images. - */ cacheFrom?: string | string[]; [k: string]: any; } + interface ImageContainer { - /** - * The docker image that will be used to create the container. - */ image: string; [k: string]: any; } + interface NonComposeBase { - /** - * Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. "8000:8010". - */ appPort?: number | string | (number | string)[]; - /** - * The arguments required when starting in the container. - */ runArgs?: string[]; - /** - * Action to take when the user disconnects from the container in their editor. The default is to stop the container. - */ shutdownAction?: "none" | "stopContainer"; - /** - * Whether to overwrite the command specified in the image. The default is true. - */ overrideCommand?: boolean; - /** - * The path of the workspace folder inside the container. - */ workspaceFolder?: string; - /** - * The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project. - */ workspaceMount?: string; [k: string]: any; } interface ComposeContainer { - /** - * The name of the docker-compose file(s) used to start the services. - */ dockerComposeFile: string | string[]; - /** - * The service you want to work on. This is considered the primary container for your dev environment which your editor will connect to. - */ service: string; - /** - * An array of services that should be started and stopped. - */ runServices?: string[]; - /** - * The path of the workspace folder inside the container. This is typically the target path of a volume mount in the docker-compose.yml. - */ workspaceFolder: string; - /** - * Action to take when the user disconnects from the primary container in their editor. The default is to stop all of the compose containers. - */ shutdownAction?: "none" | "stopCompose"; - /** - * Whether to overwrite the command specified in the image. The default is false. - */ overrideCommand?: boolean; [k: string]: any; } interface DevContainerCommon { - /** - * A name for the dev container which can be displayed to the user. - */ name?: string; - /** - * Features to add to the dev container. - */ features?: { [k: string]: any; }; - /** - * Ports that are forwarded from the container to the local machine. Can be an integer port number, or a string of the format "host:port_number". - */ forwardPorts?: (number | string)[]; portsAttributes?: { - /** - * A port, range of ports (ex. "40000-55000"), or regular expression (ex. ".+\\/server.js"). For a port number or range, the attributes will apply to that port number or range of port numbers. Attributes which use a regular expression will apply to ports whose associated process command line matches the expression. - * - * This interface was referenced by `undefined`'s JSON-Schema definition - * via the `patternProperty` "(^\d+(-\d+)?$)|(.+)". - */ [k: string]: { - /** - * Defines the action that occurs when the port is discovered for automatic forwarding - */ onAutoForward?: | "notify" | "openBrowser" @@ -384,177 +186,87 @@ interface DevContainerCommon { | "openPreview" | "silent" | "ignore"; - /** - * Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port. - */ elevateIfNeeded?: boolean; - /** - * Label that will be shown in the UI for this port. - */ label?: string; requireLocalPort?: boolean; - /** - * The protocol to use when forwarding this port. - */ protocol?: "http" | "https"; [k: string]: any; }; }; otherPortsAttributes?: { - /** - * Defines the action that occurs when the port is discovered for automatic forwarding - */ onAutoForward?: | "notify" | "openBrowser" | "openPreview" | "silent" | "ignore"; - /** - * Automatically prompt for elevation (if needed) when this port is forwarded. Elevate is required if the local port is a privileged port. - */ elevateIfNeeded?: boolean; - /** - * Label that will be shown in the UI for this port. - */ label?: string; requireLocalPort?: boolean; - /** - * The protocol to use when forwarding this port. - */ protocol?: "http" | "https"; }; - /** - * Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default when opening from a local folder. - */ updateRemoteUserUID?: boolean; - /** - * Container environment variables. - */ containerEnv?: { [k: string]: string; }; - /** - * The user the container will be started with. The default is the user on the Docker image. - */ containerUser?: string; - /** - * Mount points to set up when creating the container. See Docker's documentation for the --mount option for the supported syntax. - */ mounts?: (Mount | string)[]; - /** - * Passes the --init flag when creating the dev container. - */ init?: boolean; - /** - * Passes the --privileged flag when creating the dev container. - */ privileged?: boolean; - /** - * Passes docker capabilities to include when creating the dev container. - */ capAdd?: string[]; - /** - * Passes docker security options to include when creating the dev container. - */ securityOpt?: string[]; - /** - * Remote environment variables to set for processes spawned in the container including lifecycle scripts and any remote editor/IDE server process. - */ remoteEnv?: { [k: string]: string | null; }; - /** - * The username to use for spawning processes in the container including lifecycle scripts and any remote editor/IDE server process. The default is the same user as the container. - */ remoteUser?: string; - /** - * A command to run locally before anything else. This command is run before "onCreateCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ initializeCommand?: string | string[]; - /** - * A command to run when creating the container. This command is run after "initializeCommand" and before "updateContentCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ onCreateCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run when creating the container and rerun when the workspace content was updated while creating the container. This command is run after "onCreateCommand" and before "postCreateCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ updateContentCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run after creating the container. This command is run after "updateContentCommand" and before "postStartCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postCreateCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run after starting the container. This command is run after "postCreateCommand" and before "postAttachCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postStartCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * A command to run when attaching to the container. This command is run after "postStartCommand". If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell. - */ postAttachCommand?: | string | string[] | { [k: string]: string | string[]; }; - /** - * The user command to wait for before continuing execution in the background while the UI is starting up. The default is "updateContentCommand". - */ waitFor?: | "initializeCommand" | "onCreateCommand" | "updateContentCommand" | "postCreateCommand" | "postStartCommand"; - /** - * User environment probe to run. The default is "loginInteractiveShell". - */ userEnvProbe?: | "none" | "loginShell" | "loginInteractiveShell" | "interactiveShell"; - /** - * Host hardware requirements. - */ hostRequirements?: { - /** - * Number of required CPUs. - */ cpus?: number; - /** - * Amount of required RAM in bytes. Supports units tb, gb, mb and kb. - */ memory?: string; - /** - * Amount of required disk space in bytes. Supports units tb, gb, mb and kb. - */ storage?: string; [k: string]: any; }; - /** - * Tool-specific configuration. Each tool should use a JSON object subproperty with a unique name to group its customizations. - */ customizations?: { [k: string]: any; }; @@ -565,17 +277,8 @@ interface DevContainerCommon { } interface Mount { - /** - * Mount type. - */ type: "bind" | "volume"; - /** - * Mount source. - */ source: string; - /** - * Mount target. - */ target: string; } @@ -583,37 +286,19 @@ interface Mount { interface DevcontainerJsonVSCode { customizations?: { vscode?: { - /** - * An array of extensions that should be installed into the container. - */ extensions?: string[]; - /** - * Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again. - */ settings?: { [k: string]: any; }; - /** - * The port VS Code can use to connect to its backend. - */ devPort?: number; [k: string]: any; }; [k: string]: any; }; - /** - * An array of extensions that should be installed into the container. - */ extensions?: string[]; - /** - * Machine specific settings that should be copied into the container. These are only copied when connecting to the container for the first time, rebuilding the container then triggers it again. - */ settings?: { [k: string]: any; }; - /** - * The port VS Code can use to connect to its backend. - */ devPort?: number; [k: string]: any; } @@ -621,28 +306,15 @@ interface DevcontainerJsonVSCode { // https://raw.githubusercontent.com/microsoft/vscode/main/extensions/configuration-editing/schemas/devContainer.codespaces.schema.json interface DevcontainerJsonCodespaces { customizations?: { - /** - * Customizations specific to GitHub Codespaces - */ codespaces?: { - /** - * Configuration relative to the given repositories, following the format 'owner/repo'. - * A wildcard (*) is permitted for the repo name (eg: 'microsoft/*') - */ repositories?: { [k: string]: any; }; - /** - * The paths to the files to open when the codespace is created. Paths are relative to the workspace. - */ openFiles?: string[]; [k: string]: any; }; [k: string]: any; }; - /** - * Codespaces-specific configuration. - */ codespaces?: { [k: string]: any; }; diff --git a/packages/plugins/src/doctoc/doctoc.ts b/packages/plugins/src/doctoc/doctoc.ts new file mode 100644 index 00000000..fef2a33d --- /dev/null +++ b/packages/plugins/src/doctoc/doctoc.ts @@ -0,0 +1,71 @@ +import { + enqueueInstallDependency, + enqueueRemoveDependency, + hasPlugin, + PluginArgs, + PluginType, + updatePackage, + writePackage, +} from "@mokr/core"; +import { + addPreCommitHookCommand, + removePreCommitHookCommand, +} from "../husky/husky.js"; + +const PRE_COMMIT_HOOK_COMMAND = "yarn doctoc"; +const PRE_COMMIT_HOOK_COMMAND_FORMAT = "yarn prettier --write README.md"; + +async function install({ directory }: PluginArgs) { + enqueueInstallDependency({ directory, identifier: "doctoc", dev: true }); + + await writePackage({ + directory, + data: { + scripts: { + doctoc: "doctoc README.md", + }, + }, + }); +} + +async function remove({ directory }: PluginArgs) { + enqueueRemoveDependency({ directory, identifier: "doctoc" }); + + await updatePackage({ + directory, + merge: (existingData) => { + delete existingData?.["scripts"]?.["doctoc"]; + return existingData; + }, + }); + + // Remove husky integration + removePreCommitHookCommand({ directory, command: PRE_COMMIT_HOOK_COMMAND }); + removePreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND_FORMAT, + }); +} + +async function load({ directory }: PluginArgs) { + if (await hasPlugin({ directory, name: "husky" })) { + await addPreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND, + }); + + if (await hasPlugin({ directory, name: "prettier" })) { + await addPreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND_FORMAT, + }); + } + } +} + +export const doctoc = { + type: PluginType.Monorepo, + install, + remove, + load, +}; diff --git a/packages/plugins/src/husky/husky.ts b/packages/plugins/src/husky/husky.ts index ee852f60..8452e082 100644 --- a/packages/plugins/src/husky/husky.ts +++ b/packages/plugins/src/husky/husky.ts @@ -2,15 +2,22 @@ import { enqueueRemoveDependency, exec, installDependency, + isReadableAndWritableFile, logWarning, PluginArgs, PluginType, + readFile, removeDirectory, + removeFile, updatePackage, + writeFile, writePackage, } from "@mokr/core"; +import os from "node:os"; import { join } from "node:path"; +const PRE_COMMIT_HOOK_FILE = ".husky/pre-commit"; + async function install({ directory }: PluginArgs) { // Don't enqueue because we need it immediately await installDependency({ directory, identifier: "husky", dev: true }); @@ -59,3 +66,88 @@ export const husky = { remove, load, }; + +function getPreCommitHookPath({ directory }: PluginArgs) { + return join(directory, PRE_COMMIT_HOOK_FILE); +} + +async function getPreCommitHookCommands({ directory }: PluginArgs) { + if ( + !(await isReadableAndWritableFile({ + path: getPreCommitHookPath({ directory }), + })) + ) { + return []; + } + + return ( + await readFile({ + path: getPreCommitHookPath({ directory }), + }) + ) + .trim() + .split(os.EOL) + .filter((line) => line !== ""); +} + +async function setPreCommitHookCommands({ + directory, + commands, +}: { + directory: string; + commands: string[]; +}) { + return writeFile({ + path: getPreCommitHookPath({ directory }), + contents: commands.join(os.EOL), + }); +} + +async function hasPreCommitHookCommand({ + directory, + command, +}: PluginArgs & { command: string }) { + return (await getPreCommitHookCommands({ directory })).includes(command); +} + +export async function addPreCommitHookCommand({ + directory, + command, +}: PluginArgs & { command: string }) { + if (await hasPreCommitHookCommand({ directory, command })) { + return; + } + + await exec("yarn", ["husky", "add", PRE_COMMIT_HOOK_FILE, `"${command}"`], { + cwd: directory, + }); +} + +export async function removePreCommitHookCommand({ + directory, + command, +}: PluginArgs & { command: string }) { + if (!(await hasPreCommitHookCommand({ directory, command }))) { + return; + } + + // Remove the command from the pre-commit + const newCommands = (await getPreCommitHookCommands({ directory })).filter( + (line) => line !== command + ); + + if (newCommands.length === 2) { + /** + * We're left with only these lines: + * + * ``` + * #!/usr/bin/env sh + * . "$(dirname -- "$0")/_/husky.sh" + * ``` + */ + await removeFile({ path: getPreCommitHookPath({ directory }) }); + return; + } + + await setPreCommitHookCommands({ directory, commands: newCommands }); +} diff --git a/packages/plugins/src/index.ts b/packages/plugins/src/index.ts index 59121611..b1c3d9a3 100644 --- a/packages/plugins/src/index.ts +++ b/packages/plugins/src/index.ts @@ -1,9 +1,11 @@ export * from "./dependabot/dependabot.js"; export * from "./devcontainer/devcontainer.js"; +export * from "./doctoc/doctoc.js"; export * from "./githubActions/githubActions.js"; export * from "./husky/husky.js"; export * from "./jest/jest.js"; export * from "./lintStaged/lintStaged.js"; export * from "./prettier/prettier.js"; export * from "./semanticRelease/semanticRelease.js"; +export * from "./todos/todos.js"; export * from "./typescript/typescript.js"; diff --git a/packages/plugins/src/lintStaged/lintStaged.ts b/packages/plugins/src/lintStaged/lintStaged.ts index 06b1f6e2..3020699c 100644 --- a/packages/plugins/src/lintStaged/lintStaged.ts +++ b/packages/plugins/src/lintStaged/lintStaged.ts @@ -1,22 +1,18 @@ import { enqueueInstallDependency, enqueueRemoveDependency, - exec, hasPlugin, - isReadableAndWritableFile, PluginArgs, PluginType, - readFile, - removeFile, updatePackage, - writeFile, writePackage, } from "@mokr/core"; -import os from "node:os"; -import { join } from "node:path"; +import { + addPreCommitHookCommand, + removePreCommitHookCommand, +} from "../husky/husky.js"; const PRE_COMMIT_HOOK_COMMAND = "yarn lint-staged"; -const PRE_COMMIT_HOOK_FILE = ".husky/pre-commit"; async function install({ directory }: PluginArgs) { enqueueInstallDependency({ @@ -39,28 +35,7 @@ async function remove({ directory }: PluginArgs) { }); // Remove husky integration - if (await hasPreCommitHookCommand({ directory })) { - // There doesn't seem to be a way to remove a hook command with husky - - // Remove the command from the pre-commit - const newCommands = (await getPreCommitHookCommands({ directory })).filter( - (command) => command !== PRE_COMMIT_HOOK_COMMAND - ); - - if (newCommands.length === 2) { - /** - * We're left with only these lines: - * - * ``` - * #!/usr/bin/env sh - * . "$(dirname -- "$0")/_/husky.sh" - * ``` - */ - return removeFile({ path: getPreCommitHookPath({ directory }) }); - } - - await setPreCommitHookCommands({ directory, commands: newCommands }); - } + removePreCommitHookCommand({ directory, command: PRE_COMMIT_HOOK_COMMAND }); } async function load({ directory }: PluginArgs) { @@ -75,17 +50,11 @@ async function load({ directory }: PluginArgs) { }); } - if ( - (await hasPlugin({ directory, name: "husky" })) && - !(await hasPreCommitHookCommand({ directory })) - ) { - await exec( - "yarn", - ["husky", "add", PRE_COMMIT_HOOK_FILE, `"${PRE_COMMIT_HOOK_COMMAND}"`], - { - cwd: directory, - } - ); + if (await hasPlugin({ directory, name: "husky" })) { + await addPreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND, + }); } } @@ -95,45 +64,3 @@ export const lintStaged = { remove, load, }; - -function getPreCommitHookPath({ directory }: PluginArgs) { - return join(directory, PRE_COMMIT_HOOK_FILE); -} - -async function getPreCommitHookCommands({ directory }: PluginArgs) { - if ( - !(await isReadableAndWritableFile({ - path: getPreCommitHookPath({ directory }), - })) - ) { - return []; - } - - return ( - await readFile({ - path: getPreCommitHookPath({ directory }), - }) - ) - .trim() - .split(os.EOL) - .filter((line) => line !== ""); -} - -async function setPreCommitHookCommands({ - directory, - commands, -}: { - directory: string; - commands: string[]; -}) { - return writeFile({ - path: getPreCommitHookPath({ directory }), - contents: commands.join(os.EOL), - }); -} - -async function hasPreCommitHookCommand({ directory }: PluginArgs) { - return (await getPreCommitHookCommands({ directory })).includes( - PRE_COMMIT_HOOK_COMMAND - ); -} diff --git a/packages/plugins/src/todos/todos.ts b/packages/plugins/src/todos/todos.ts new file mode 100644 index 00000000..69075647 --- /dev/null +++ b/packages/plugins/src/todos/todos.ts @@ -0,0 +1,72 @@ +import { + enqueueInstallDependency, + enqueueRemoveDependency, + hasPlugin, + PluginArgs, + PluginType, + updatePackage, + writePackage, +} from "@mokr/core"; +import { + addPreCommitHookCommand, + removePreCommitHookCommand, +} from "../husky/husky.js"; + +const PRE_COMMIT_HOOK_COMMAND = "yarn todos"; +const PRE_COMMIT_HOOK_COMMAND_FORMAT = "yarn prettier --write TODO.md"; + +async function install({ directory }: PluginArgs) { + enqueueInstallDependency({ directory, identifier: "leasot", dev: true }); + + await writePackage({ + directory, + data: { + scripts: { + todos: + 'leasot --exit-nicely --reporter markdown --ignore "**/node_modules" "**/*.ts" > TODO.md', + }, + }, + }); +} + +async function remove({ directory }: PluginArgs) { + enqueueRemoveDependency({ directory, identifier: "leasot" }); + + await updatePackage({ + directory, + merge: (existingData) => { + delete existingData?.["scripts"]?.["todos"]; + return existingData; + }, + }); + + // Remove husky integration + removePreCommitHookCommand({ directory, command: PRE_COMMIT_HOOK_COMMAND }); + removePreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND_FORMAT, + }); +} + +async function load({ directory }: PluginArgs) { + if (await hasPlugin({ directory, name: "husky" })) { + await addPreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND, + }); + + if (await hasPlugin({ directory, name: "prettier" })) { + await addPreCommitHookCommand({ + directory, + command: PRE_COMMIT_HOOK_COMMAND_FORMAT, + }); + } + } +} + +export const todos = { + type: PluginType.Monorepo, + install, + remove, + load, +}; diff --git a/packages/templates/src/common.ts b/packages/templates/src/common.ts index e201616c..104c0752 100644 --- a/packages/templates/src/common.ts +++ b/packages/templates/src/common.ts @@ -7,6 +7,8 @@ async function apply({ directory }: TemplateArgs) { 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 = { diff --git a/yarn.lock b/yarn.lock index b1f7fed4..e46b0210 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,9 +84,9 @@ __metadata: "@semantic-release/changelog": 6.0.1 "@semantic-release/git": 10.0.1 "@types/prettier": ^2 - doctoc: ^2.2.1 + doctoc: 2.2.1 husky: 8.0.2 - leasot: ^13.2.0 + leasot: 13.2.0 lint-staged: 13.0.3 prettier: 2.7.1 semantic-release: 19.0.5 @@ -1610,7 +1610,7 @@ __metadata: languageName: node linkType: hard -"doctoc@npm:^2.2.1": +"doctoc@npm:2.2.1": version: 2.2.1 resolution: "doctoc@npm:2.2.1" dependencies: @@ -2751,7 +2751,7 @@ __metadata: languageName: node linkType: hard -"leasot@npm:^13.2.0": +"leasot@npm:13.2.0": version: 13.2.0 resolution: "leasot@npm:13.2.0" dependencies: