中文

Add func to an Existing Project

Add an isolated func CLI entry to an existing TypeScript project without replacing its current runtime or build flow.

Updated 2 weeks ago

We recommend creating a new directory with its own package.json. The CLI can import public application modules or shared packages, while its runtime dependencies, tests, and generated bundle remain outside the host application’s package and build pipeline.

Repository structure
.
|-- src                     existing application
|-- package.json            existing application package
+-- tools
    +-- admin-cli           independent func package
        |-- src
        |-- tests
        |-- package.json
        +-- dist            independent bundle output

This boundary prevents command files and CLI-only dependencies from being discovered by an application bundler that scans the host package. It also lets you publish, version, test, or remove the CLI independently. In a monorepo, add the new directory to the existing workspace configuration instead of installing it into the application package.

Pass the directory name to the creator from its parent directory. The following command creates the CLI package as tools/admin-cli:

Terminal
cd tools
npm init func@latest admin-cli

cd admin-cli
npm install
npm run dev -- --help

Share business code through an existing workspace package or a stable public module. Avoid importing the application bootstrap merely to reach a service; bootstraps often start servers, connect to databases, or register process handlers as an import side effect.

Alternative: isolate func inside the existing package

A shared package is reasonable when the repository cannot add another workspace or the CLI is a private interface to the same application code. Install the runtime and development tool without replacing the application’s existing scripts:

Terminal
npm install func
npm install --save-dev funcgo

Keep every CLI-owned file under a dedicated directory. A small func application normally consists of one executable entry, one root @FuncModule, command classes, and optional service classes:

Isolated CLI source
src/cli
|-- commands
|   +-- hello.command.ts
|-- cli.module.ts
+-- index.ts
src/cli/index.ts
import { run } from 'func'
import { CliModule } from './cli.module'

run(CliModule)
src/cli/cli.module.ts
import { FuncModule } from 'func'
import { HelloCommand } from './commands/hello.command'

@FuncModule({
  commands: [HelloCommand],
})
export class CliModule {}
src/cli/commands/hello.command.ts
import { Command, Handler } from 'func'

@Command({
  name: 'hello',
  description: 'Print a greeting',
})
export class HelloCommand {
  @Handler()
  run() {
    console.log('Hello from func')
  }
}

Why this does not start the host application

func decorators attach metadata to the classes that declare them; they do not parse arguments or execute handlers on import. The explicit run(CliModule) call starts dispatch. As long as only src/cli/index.ts calls run and the host application does not import that entry, func does not replace the application runtime or bootstrap.

Commands may safely import shared, side-effect-free business modules. If shared logic needs state or collaborators, wrap it in a @Service and register that service in the CLI module instead of initializing global state in a command file.

Run with explicit paths first

Run funcgo setup without --fix to inspect its suggestions. In an existing package, automatic setup may propose replacing generic dev and build scripts, so keep the first integration explicit:

Terminal
npx funcgo setup
npx funcgo dev -f src/cli/index.ts -- --help
npx funcgo dev -f src/cli/index.ts -- hello

Once the boundary is proven, add dedicated scripts such as cli:dev and cli:build; do not take over names already owned by the application.

Build to an isolated output

Send the CLI bundle to its own output directory so the host build never packages it accidentally. Add generated output to .gitignore when it should not be committed.

Terminal
npx funcgo build -f src/cli/index.ts -o dist/cli

Add permanent func configuration and a bin mapping only when the CLI should become a public package interface:

package.json
{
  "func": {
    "entry": "src/cli/index.ts",
    "outDir": "dist/cli"
  },
  "bin": {
    "admin-cli": "./dist/cli/bin.js"
  }
}