# @plumeria/init

Source: https://plumeria.dev/docs/api-reference/init





[`@plumeria/init`](https://www.npmjs.com/package/@plumeria/init) sets Plumeria up in a project that already exists. There are eight packages and a plugin per bundler; the choice between them is not a judgement, it is a detection, so the command makes it.

```sh title="Terminal"
npx @plumeria/init
```

It reads the project, asks what it cannot read, shows the plan, and only then writes. Nothing is installed or patched before you answer.

```sh title="Terminal"
✔ detected  next  pnpm  typescript

  ➡︎ install  @plumeria/core @plumeria/next-plugin @plumeria/eslint-plugin oxlint
  + write    plumeria.d.ts     classStyle
  ~ patch    next.config.ts    withPlumeria(...)
  ~ patch    eslint.config.ts  plumeria.configs.recommended
  ~ patch    package.json      build: plumerialint -- next build

Apply? (Y/n)
```

Commit before running, so the writes can be reverted with `git checkout`.

## What it detects [#what-it-detects]

The package manager comes from `packageManager`, or from the lockfile when that field is absent. Both are looked for upwards from the directory init runs in, so a package inside a workspace is installed with the manager the workspace root uses rather than with `npm`.

The bundler comes from the dependencies, and from the config files when nothing is installed yet — `next`, `vite`, `astro`, `webpack`, `rspack`, `rollup`, `rolldown`, `esbuild`, `farm` and `bun`. Next.js is read before the bundler Next.js carries, so a Next project gets [`@plumeria/next-plugin`](/docs/api-reference/plugins/next-plugin); everything else gets [`@plumeria/unplugin`](/docs/api-reference/plugins/unplugin) with the factory that matches. Pass `--bundler <name>` to decide it yourself.

## What it asks [#what-it-asks]

### Which spelling of a two-named property does this project write? [#which-spelling-of-a-two-named-property-does-this-project-write]

```sh title="Terminal"
  1: logical   marginBlockStart, insetInlineStart
  2: physical  marginTop, left
  3: both      no policy
```

One answer reaches both levels. The bundler plugin receives `withoutPhysicalProperties` or `withoutLogicalProperties`, so the counterpart cannot compile, and ESLint receives `@plumeria/no-physical-properties` or `@plumeria/no-logical-properties`, so it is reported while you type. They are the same choice made twice, and setting them from one answer is what keeps their directions from disagreeing.

`--sizes` extends both to the size axis, where `width` and `inlineSize` are the pair.

On a codebase that already has styles, answer `3` and follow [Optional rules](/docs/api-reference/plugins/eslint-plugin/optional-rules) instead: the rule offers a rename on every occurrence, which is what a migration needs, and the build option belongs afterwards.

### Expand a border shorthand into the three declarations it sets? [#expand-a-border-shorthand-into-the-three-declarations-it-sets]

Turns on `@plumeria/expand-border-shorthands`. A `border` shorthand crosses the axis shorthands without either containing the other, so specificity cannot rank the pair and the outcome depends on the order it was written in. Expanding removes the shape. On by default.

### Which JSX prop carries styles? [#which-jsx-prop-carries-styles]

`classStyle` unless you name another. A renamed prop is written into the plugin as `styleProp` and declared in `plumeria.d.ts` at the same time, so the two cannot disagree — a mismatch type-checks but is never compiled away.

### Set up `@plumeria/eslint-plugin` and the `plumerialint` build guard? [#set-up-plumeriaeslint-plugin-and-the-plumerialint-build-guard]

Adds the plugin and `oxlint`, writes or extends the flat config, and puts `plumerialint --` in front of the `build` script, so a style error stops the build before the bundler runs.

A renamed prop is rejected unless it is an identifier TypeScript can declare, and unless React leaves the name free — `className`, `style`, `key`, `ref` and `children` are refused, from the prompt and from `--style-prop` alike.

## What it writes [#what-it-writes]

| File               | What lands in it                                                                                         |
| ------------------ | -------------------------------------------------------------------------------------------------------- |
| `plumeria.d.ts`    | the styling prop TypeScript reads                                                                        |
| the bundler config | the plugin, and the options the answers chose                                                            |
| `eslint.config.ts` | `plumeria.configs.recommended` and the rules that were asked for                                         |
| `package.json`     | `plumerialint --` in front of the `build` script, and `rimraf .next` before `dev` and `build` on Next.js |

An existing file is patched, not replaced. The import lands after the last import, the plugin joins the `plugins` array, and a Next config has its default export wrapped:

```ts title="next.config.ts"
import type { NextConfig } from 'next';
import { withPlumeria } from '@plumeria/next-plugin'; // [!code ++]

const nextConfig: NextConfig = {
  reactStrictMode: true,
};

export default nextConfig; // [!code --]
export default withPlumeria(nextConfig, { withoutLogicalProperties: true }); // [!code ++]
```

```ts title="vite.config.ts"
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import plumeria from '@plumeria/unplugin'; // [!code ++]

export default defineConfig({
  plugins: [react(), plumeria.vite({ withoutLogicalProperties: true })], // [!code ++]
});
```

On Next.js a `pre` script clears the build cache, which is what keeps a version change from being read as a compile error. One is added for each of `dev` and `build` that the project actually has, so a project with no scripts gets none — and `rimraf` is only installed when one is added:

```json title="package.json"
  "scripts": {
    "predev": "rimraf .next",
    "prebuild": "rimraf .next",
    "dev": "next dev",
    "build": "plumerialint -- next build"
  }
```

A `predev` or `prebuild` the project already wrote is left as it is. Clearing the cache is a Next.js concern rather than a lint one, so `--no-eslint` leaves it in place and drops only `plumerialint`.

What counts as already set up is the plugin being *called*, not merely imported. A bare `plumeria.version`, and a call inside a comment, a string or a regex literal, all read as not set up, because none of them registers anything; an import nothing calls is completed rather than skipped, and the binding the file already named is the one that gets called. An import spanning several lines is read as one statement, so it is never duplicated. A second run writes nothing, and a half-finished one is finished.

The `plugins` array it extends is the one the config object owns. A `plugins` belonging to a nested key — `test` for Vitest, say — is never written into. Where the plugin cannot be placed safely — a config whose own object has no `plugins` array, a build script rather than a config, a legacy `.eslintrc` — nothing is touched, the snippet is printed, and the command exits with `1`.

## Options [#options]

```sh title="Terminal"
npx @plumeria/init --dry-run            # show the plan, write nothing
npx @plumeria/init --yes                # take every default, ask nothing
npx @plumeria/init --physical --sizes   # answer the spelling question up front
npx @plumeria/init --style-prop sx
npx @plumeria/init --bundler rollup
npx @plumeria/init --no-eslint          # leave ESLint and the plumerialint guard out
npx @plumeria/init --no-install         # write the configs, print the command
npx @plumeria/init --cwd packages/app
```

Without a TTY it never asks — every unanswered question takes its default, which is what makes it usable in CI. Colour comes from `node:util`'s `styleText`, which leaves the text alone when the output is not a terminal, so a piped or logged run carries no escape codes.

## Installation [#installation]

None. Run it with `npx` and it is gone again afterwards. Install it only if you want it pinned in the repo, where the command is `plumeria-init`:

<Tabs items="['npm',  'yarn', 'pnpm']">
  <Tab>
    ```sh title="Terminal"
    npm i --save-dev @plumeria/init
    ```
  </Tab>

  <Tab>
    ```sh title="Terminal"
    yarn add -D @plumeria/init
    ```
  </Tab>

  <Tab>
    ```sh title="Terminal"
    pnpm i --save-dev @plumeria/init
    ```
  </Tab>
</Tabs>

<Cards>
  <Card title="Set it up by hand instead" href="/docs/getting-started/installation" />

  <Card title="Move an existing codebase onto Plumeria" href="/docs/api-reference/codemod" />
</Cards>
