{"id":15011733,"url":"https://github.com/preactjs/preact-cli-experiment","last_synced_at":"2025-10-19T13:31:53.092Z","repository":{"id":36238059,"uuid":"209160156","full_name":"preactjs/preact-cli-experiment","owner":"preactjs","description":null,"archived":false,"fork":false,"pushed_at":"2022-12-11T07:31:57.000Z","size":8231,"stargazers_count":4,"open_issues_count":97,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-29T15:32:39.732Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/preactjs.png","metadata":{"funding":{"github":["preactjs"],"open_collective":"preact"},"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-17T21:30:41.000Z","updated_at":"2021-12-07T17:56:18.000Z","dependencies_parsed_at":"2023-01-17T00:01:03.107Z","dependency_job_id":null,"html_url":"https://github.com/preactjs/preact-cli-experiment","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preactjs%2Fpreact-cli-experiment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preactjs%2Fpreact-cli-experiment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preactjs%2Fpreact-cli-experiment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/preactjs%2Fpreact-cli-experiment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/preactjs","download_url":"https://codeload.github.com/preactjs/preact-cli-experiment/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237143133,"owners_count":19262137,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:41:34.726Z","updated_at":"2025-10-19T13:31:52.604Z","avatar_url":"https://github.com/preactjs.png","language":"TypeScript","funding_links":["https://github.com/sponsors/preactjs","https://opencollective.com/preact"],"categories":[],"sub_categories":[],"readme":"# ⚛️ Preact CLI (Experiment)\n\n[![Build Status](https://travis-ci.org/preactjs/preact-cli-experiment.svg?branch=master)](https://travis-ci.org/preactjs/preact-cli-experiment)\n\nNew CLI for Preact featuring a plugin-based system.\n\n\u003c!-- prettier-ignore-start --\u003e\n\n- [⚛️ Preact CLI (Experiment)](#%e2%9a%9b%ef%b8%8f-preact-cli-experiment)\n\t- [Install for development](#install-for-development)\n- [Run](#run)\n\t- [Run for development](#run-for-development)\n- [Reference guide](#reference-guide)\n\t- [The Plugin API](#the-plugin-api)\n\t\t- [List of available hooks](#list-of-available-hooks)\n\t\t\t- [`install`](#install)\n\t\t\t- [`cli`](#cli)\n\t\t\t- [`build` / `watch`](#build--watch)\n\t\t- [`PluginAPI` instance](#pluginapi-instance)\n\t\t\t- [`setStatus(text?: string, type?: \"info\" | \"error\" | \"fatal\" | \"success\")`](#setstatustext-string-type-%22info%22--%22error%22--%22fatal%22--%22success%22)\n\t\t\t- [`async getRegistry(): Promise\u003cPluginRegistry\u003e`](#async-getregistry-promisepluginregistry)\n\t\t\t- [`registerCommand(name: string, options?: CommandOptions): Command`](#registercommandname-string-options-commandoptions-command)\n\t\t\t- [`chainWebpack(chainer: WebpackChainer)`](#chainwebpackchainer-webpackchainer)\n\t\t\t- [`async applyTemplate(fileOrFolder: string, context: Record\u003cstring, string\u003e, base?: string): Promise\u003cRecord\u003cstring, string\u003e\u003e`](#async-applytemplatefileorfolder-string-context-recordstring-string-base-string-promiserecordstring-string)\n\t\t\t- [`async writeFileTree(files: Record\u003cstring, string\u003e, base?: string)`](#async-writefiletreefiles-recordstring-string-base-string)\n\t\t\t- [`getChains(): WebpackChainer[]`](#getchains-webpackchainer)\n\t\t- [The Plugin Registry](#the-plugin-registry)\n\t\t\t- [`plugin(name:string|PluginAPI): RegistryPluginActionWrapper`](#pluginnamestringpluginapi-registrypluginactionwrapper)\n\t\t\t- [`hookWebpackChain(config: import(\"webpack-chain\")): void`](#hookwebpackchainconfig-import%22webpack-chain%22-void)\n\t\t\t- [`async invoke\u003cT\u003e(funcName: string, options?:any): Promise\u003c(T|undefined)[]\u003e`](#async-invoketfuncname-string-optionsany-promisetundefined)\n\n\u003c!-- prettier-ignore-end --\u003e\n\n # Install\n\n```bash\nnpm install -g @preact/cli\n```\n\n**WARNING**: This will shadow the existing `preact-cli` endpoint. Uninstall this package to get the stable version back.\n\n## Install for development\n\nThis project is a monorepo and uses `yarn` and `lerna` to manage internal and external dependencies.\n\n# Run\n\n```bash\nUsage: preact [options] [command]\n\nOptions:\n  -V, --version                              output the version number\n  --cwd \u003ccwd\u003e                                Sets working directory (default is current)\n  --pm \u003cnpm|yarn\u003e                            Sets package manager (default NPM)\n  -d, --debug                                Activate debug options\n  -h, --help                                 output usage information\n\nCommands:\n  add \u003cplugin\u003e                               Add a Preact CLI plugin to the project\n  build [options] [src]                      Build the current project into static files\n  watch [options] [src]                      Launch a dev server with hot-reload\n  create [options] [template] [name] [dest]  Legacy command to create a project from either the official template,\n                                             or a user-defined one\n  info                                       Outputs information about your system. Used to troubleshoot issues.\n  invoke [options] [plugin]                  Invokes plugin(s) to finish installation\n  new [options] [name] [dir]                 Creates a new Preact projec\n```\n\n## Run for development\n\nYou can run a development version (without transpiling) of the CLI by going to `packages/cli` and running `yarn dev` .\n\n**Note**: As it is in its early stages, running the CLI this way only works in Unix-style environments. Windows users, use MSYS or WSL.\n\n# Reference guide\n\nThe CLI resolves any installed plugins at startup, and hooks into exported functions for them to tap into the CLI. With plugins, you can **create new commands**, **mutate the webpack configuration** (uses `webpack-chain` ), and even create new functionality by invoking other plugins' exported functions.\n\nYou can take a look at how the [`new`](packages/cli/src/plugins/new.ts) command is built as an internal plugin, and how the [`build`](packages/cli/plugins/build.ts) command hooks into other plugins to mutate the webpack configuration.\n\n## The Plugin API\n\nEach exported function from a plugin, when called, is passed a `PluginAPI` instance it can use to interact with the CLI. Each exported function is used with the following signature:\n\n```typescript\n/**\n * Hooks into the Preact CLI.\n * @param api API instance for the plugin\n * @param opts Options for the hook, contains the global CLI options, can contain more depending on the hook\n * @return Value defined and used depending on the hook.\n */\nfunction hook(api: PluginAPI, opts: CLIArguments /* Can also include more properties depending on the hook */): any {}\n```\n\n### List of available hooks\n\n#### `install`\n\nGets called when the plugin is installed via `preact add \u003cplugin\u003e`, or invoked manually with `preact invoke \u003cplugin\u003e`.\n\n#### `cli`\n\nGets called on CLI startup.\n\n```typescript\nfunction cli(api: PluginAPI, opts: CLIArguments): void {}\n```\n\n#### `build` / `watch`\n\nGets called at the start of the build step.s\n\n```typescript\ninterface BuildArgv {\n    analyze: boolean;\n    brotli: boolean;\n    clean: boolean;\n    dest: string;\n    esm: boolean;\n    inlineCss: boolean;\n    prerender: boolean;\n    preload: boolean;\n    production: boolean;\n    sw: boolean;\n    template?: string;\n}\n\nfunction build(api: PluginAPI, opts: CLIArguments \u0026 BuildArgv): void;\n```\n\n### `PluginAPI` instance\n\nThe Plugin API object is a class instance that's shared between hooks, though a shared state cannot be saved there.\n\n#### `setStatus(text?: string, type?: \"info\" | \"error\" | \"fatal\" | \"success\")`\n\nPrints to terminal the current status of the process. This is used for end-user logging. Proper feedback is important, as otherwise the user might think the CLI is stuck, and _will_ abort it early. Internally, Preact CLI uses `ora` to show an animated spinner.\n\nWhen called without arguments, this stops and removes the spinner, persisting the last message into stderr. When called with one argument, the new status text, the spinner updates its status text. When called with two arguments, a logging text and a type identifier, the spinner persists the input text prefixed with an icon corresponding to the type.\n\n**Examples**:\n\n```typescript\napi.setStatus(\"Status\"); // @preact/cli:plugin:foo [/] Status\napi.setStatus(); // @preact/cli:plugin:foo  Status\napi.setStatus(\"Working...\"); // @preact/cli:plugin:foo [/] Working...\napi.setStatus(\"Creation complete\", \"success\"); // @preact/cli:plugin:foo ✔️ Creaction complete \\n @preact/cli:plugin:foo [/] Working...\napi.setStatus(\"FATAL ERROR\", \"fatal\"); // @preact/cli:plugin:foo ❌ FATAL ERROR [program exists with code 1]\n```\n\n#### `async getRegistry(): Promise\u003cPluginRegistry\u003e`\n\nReturns a promise to the current PluginRegistry instance. See the [PluginRegistry](#the-plugin-registry) reference entry for more info.\n\n#### `registerCommand(name: string, options?: CommandOptions): Command`\n\nRegisters a new command to add to the CLI. This returns a `Command` object from te `commander` package allowing you to personalize the command, add options, and set an action callback.\n\n**Example**: [The `new` command](packages/cli/src/plugins/new.ts)\n\n#### `chainWebpack(chainer: WebpackChainer)`\n\n```typescript\ntype WebpackChainer = (config: webpackChain.Config) =\u003e void;\n```\n\nAdds a transformer function for the webpack configuration.\n\nThe function doesn't get called immediately; rather all transformer functions are collected and applied during the build step.\n\n**Example**:\n\n```typescript\n// Add a minifier plugin\napi.chainWebpack(config =\u003e config.plugin(\"terser\").use(TerserPlugin, [terserOptions]));\n```\n\n#### `async applyTemplate(fileOrFolder: string, context: Record\u003cstring, string\u003e, base?: string): Promise\u003cRecord\u003cstring, string\u003e\u003e`\n\nRenders a file or folder template into the project. Returns an object with relative filenames as keys, and file contents as values, to be passed to `writeFileTree` to write to disk.\n\n- `fileOrFolder` is the file or folder to render; is relative to `base` or absolute.\n- `context` is an object containing template variables as keys and their content as values. This context gets merged with a default context which contains the environment variables as `env` and the current working directory as `cwd` .\n- `base` is the base folder - it maps to the project folder. All output files are applied form this base folder into the project root. When unspecified, it is the project directory.\n\n**Example**:\n\nSee [The `build` command](packages/cli/src/plugins/build.ts) for an example of plugin using `applyTemplate` .\n\n#### `async writeFileTree(files: Record\u003cstring, string\u003e, base?: string)`\n\nWrites the given object to disk, relative to `base` .\n\n- `files` is a dictionary of relative path keys and content values (like the object returned by `applyTemplate` ) to write to disk.\n- `base` is the base directory from which all relatives files are mapped to. If unspecified, it is the project directory. If relative, it is relative to the project directory.\n\n**Example**\n\n```typescript\nconst project = {\n    \"package.json\" : JSON.stringify(pkg, null, 2),\n    \"README.md\": generateReadme();\n    \"src/index.js\": createEntrypoint({name: \"foobar\"})\n};\napi.writeFileTree(project);\n\nconst sources = {\n    \"routes/index.jsx\": createRoute(\"index\"),\n    \"routes/profile/index.jsx\": createRoute(\"profile\", \"developit\"),\n    \"routes/prodile/solarliner.jsx\": createRoute(\"profile\", \"solarliner\")\n}\napi.writeFileTree(files, \"src\");\n```\n\n#### `getChains(): WebpackChainer[]`\n\n**WARNING**: Internal function.\n\nReturns an array of all defined `webpack-chain` transform functions by the plugin.\n\n### The Plugin Registry\n\nThe plugin registry holds instances of plugins for all installed Preact CLI plugins. This allows you to call other plugins' exposed functions to tap into the CLI's pluggable features to extends its functionalities.\n\n#### `plugin(name:string|PluginAPI): RegistryPluginActionWrapper`\n\n```typescript\ninterface RegistryPluginActionWrapper {\n    /** Invoke the plugin's hook named `funcName`. */\n    async invoke\u003cT\u003e(funcName:string, options?:any): T;\n    /** Return the PluginAPI instance wrapping the plugin. */\n    instance(): PluginAPI;\n}\n```\n\nReturn a wrapper object on registry action, but for one plugin. `name` can either be a string object or an existing `PluginAPI` object. In the former case, the plugin will be resolved and loaded. In the later case, the instance is directly used.\n\n#### `hookWebpackChain(config: import(\"webpack-chain\")): void`\n\nTransforms the `config` object using all transform functions defined by plugins in the registry. Note that these functions get registered when running hooks, so if you're writing a new command, you _need_ to take care of calling the appropriate hook.\n\n#### `async invoke\u003cT\u003e(funcName: string, options?:any): Promise\u003c(T|undefined)[]\u003e`\n\nInvokes the hook `funcName` on every plugin that implements it. The return value is the return value of the hooks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreactjs%2Fpreact-cli-experiment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpreactjs%2Fpreact-cli-experiment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpreactjs%2Fpreact-cli-experiment/lists"}