@plumeria/init 19.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) zss-in-js contributer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @plumeria/init
2
+
3
+ Sets Plumeria up in a project that already exists. No install needed — run it
4
+ with `npx`.
5
+
6
+ ```sh
7
+ npx @plumeria/init
8
+ ```
9
+
10
+ It reads the project, asks what it cannot read, shows the plan, and only then
11
+ writes. Nothing is installed or patched before you say so.
12
+
13
+ ```
14
+ ✔ detected next pnpm typescript
15
+
16
+ ➡︎ install @plumeria/core @plumeria/next-plugin @plumeria/eslint-plugin oxlint
17
+ + write plumeria.d.ts classStyle
18
+ ~ patch next.config.ts withPlumeria(...)
19
+ ~ patch eslint.config.ts plumeria.configs.recommended
20
+ ~ patch package.json build: plumerialint -- next build
21
+ ```
22
+
23
+ ## What it detects
24
+
25
+ The package manager comes from `packageManager` or the lockfile, both looked for
26
+ upwards from the directory init runs in, so a package inside a workspace is
27
+ installed with the manager the workspace root uses. The bundler comes from the
28
+ dependencies, and from the config files when nothing is installed yet: `next`, `vite`, `astro`, `webpack`, `rspack`, `rollup`,
29
+ `rolldown`, `esbuild`, `farm` and `bun`. Pass `--bundler <name>` to decide it
30
+ yourself.
31
+
32
+ Next.js gets `@plumeria/next-plugin`; every other bundler gets
33
+ `@plumeria/unplugin` with the factory that matches it.
34
+
35
+ ## What it asks
36
+
37
+ **Which spelling of a two-named property does this project write?** —
38
+ `marginBlockStart` or `marginTop`. The answer reaches both sides at once: the
39
+ bundler plugin gets `withoutLogicalProperties` or `withoutPhysicalProperties`,
40
+ so the counterpart cannot compile, and ESLint gets
41
+ `@plumeria/no-logical-properties` or `@plumeria/no-physical-properties`, so it
42
+ is reported while you type. Answering "both" sets neither. `--sizes` extends
43
+ the answer to the size axis, where `width` and `inlineSize` are the pair.
44
+
45
+ **Expand a border shorthand into the three declarations it sets?** — turns on
46
+ `@plumeria/expand-border-shorthands`. A `border` shorthand crosses the axis
47
+ shorthands without either containing the other, so leaving it whole makes the
48
+ outcome depend on order.
49
+
50
+ **Which JSX prop carries styles?** — `classStyle` by default. A renamed prop is
51
+ written into the plugin as `styleProp` and declared in `plumeria.d.ts`, so the
52
+ two cannot disagree. It has to be an identifier TypeScript can declare, and a
53
+ name React leaves free; `className`, `style`, `key`, `ref` and `children` are
54
+ refused, from the prompt and from `--style-prop` alike.
55
+
56
+ **Set up `@plumeria/eslint-plugin` and the `plumerialint` build guard?** — adds
57
+ the plugin and `oxlint`, writes or extends the flat config, and prefixes the
58
+ `build` script with `plumerialint --`, so a style error stops the build.
59
+
60
+ ## What it writes
61
+
62
+ - `plumeria.d.ts` — the styling prop TypeScript reads.
63
+ - the bundler config — the plugin, and the options the answers chose. An
64
+ existing config is patched: the import lands after the last import, and the
65
+ plugin joins the `plugins` array. A Next config has its default export
66
+ wrapped in `withPlumeria`.
67
+ - `eslint.config.ts` — `plumeria.configs.recommended` plus the rules that were
68
+ asked for. An existing flat config is extended rather than replaced.
69
+ - `package.json` — `plumerialint --` in front of the `build` script, and on
70
+ Next.js `rimraf .next` before `dev` and before `build`, so a version change is
71
+ not read as a compile error. A `pre` script is added only for a script the
72
+ project actually has, and one it already wrote is left alone. Clearing the
73
+ cache is a Next.js concern rather than a lint one, so `--no-eslint` keeps it.
74
+
75
+ What counts as already set up is the plugin being *called*, not merely imported.
76
+ A bare `plumeria.version`, and a call inside a comment, a string or a regex
77
+ literal, all read as not set up, because none of them registers anything; an import nothing
78
+ calls is completed rather than skipped, and the binding the file already named is
79
+ the one that gets called. An import spanning several lines is read as one
80
+ statement, so it is never duplicated. A second run writes nothing; a
81
+ half-finished one is finished.
82
+
83
+ The `plugins` array it extends is the one the config object owns — a `plugins`
84
+ belonging to a nested key, such as `test` for Vitest, is never written into.
85
+ Where the plugin cannot be placed safely — a config whose own object has no
86
+ `plugins` array, a build script rather than a config, a legacy `.eslintrc` —
87
+ nothing is touched and the snippet is printed instead.
88
+
89
+ ## Options
90
+
91
+ ```sh
92
+ npx @plumeria/init --dry-run # show the plan, write nothing
93
+ npx @plumeria/init --yes # take every default, ask nothing
94
+ npx @plumeria/init --physical --sizes # answer the spelling question up front
95
+ npx @plumeria/init --style-prop sx
96
+ npx @plumeria/init --bundler rollup
97
+ npx @plumeria/init --no-eslint # leave ESLint and the plumerialint guard out
98
+ npx @plumeria/init --no-install # write the configs, print the command
99
+ npx @plumeria/init --cwd packages/app
100
+ ```
101
+
102
+ Without a TTY it never asks — every unanswered question takes its default,
103
+ which is what makes it usable in CI. Colour comes from `node:util`'s
104
+ `styleText`, which leaves the text alone when the output is not a terminal, so a
105
+ piped or logged run carries no escape codes.
106
+
107
+ Installed rather than fetched, the command is `plumeria-init`.
108
+
109
+ Commit before running, so the writes can be reverted with `git checkout`.
package/bin/init.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { main } = require('../dist/cli.js');
4
+
5
+ main(process.argv.slice(2)).then(
6
+ (code) => {
7
+ process.exitCode = code;
8
+ },
9
+ (error) => {
10
+ console.error(`✖ ${(error && error.message) || error}`);
11
+ process.exitCode = 1;
12
+ },
13
+ );
package/dist/cli.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { Bundler } from './detect';
2
+ import type { Answers } from './setup';
3
+ interface Options {
4
+ cwd: string;
5
+ bundler: Bundler | undefined;
6
+ preset: Partial<Answers>;
7
+ yes: boolean;
8
+ dryRun: boolean;
9
+ }
10
+ export declare function parseArgs(argv: string[]): Options | null;
11
+ export declare function main(argv: string[]): Promise<number>;
12
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseArgs = parseArgs;
7
+ exports.main = main;
8
+ const node_child_process_1 = require("node:child_process");
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const detect_1 = require("./detect");
12
+ const prompt_1 = require("./prompt");
13
+ const setup_1 = require("./setup");
14
+ const style_1 = require("./style");
15
+ const { version } = require('../package.json');
16
+ const USAGE = `@plumeria/init — set up Plumeria in this project
17
+
18
+ Usage
19
+ npx @plumeria/init
20
+
21
+ It reads the project, asks what it cannot detect, then installs the packages
22
+ and writes the configs. Nothing is written before the plan is shown.
23
+
24
+ Options
25
+ -y, --yes accept every default, ask nothing
26
+ -d, --dry-run show the plan without writing
27
+ --bundler <name> ${detect_1.BUNDLERS.join(', ')}
28
+ --style-prop <p> the JSX prop that carries styles (default classStyle)
29
+ --logical this project writes logical properties
30
+ --physical this project writes physical properties
31
+ --sizes extend the spelling policy to the size axis
32
+ --no-eslint leave ESLint and the plumerialint guard out
33
+ --no-install write the configs, print the install command
34
+ --cwd <dir> run against another directory
35
+ -h, --help show this message
36
+ -v, --version show the version
37
+
38
+ The spelling answer reaches both sides: the bundler plugin rejects the
39
+ counterpart at compile time, and ESLint reports it while you type.`;
40
+ class UsageError extends Error {
41
+ }
42
+ function parseArgs(argv) {
43
+ if (argv.includes('-h') || argv.includes('--help')) {
44
+ console.log(USAGE);
45
+ return null;
46
+ }
47
+ if (argv.includes('-v') || argv.includes('--version')) {
48
+ console.log(version);
49
+ return null;
50
+ }
51
+ const options = {
52
+ cwd: process.cwd(),
53
+ bundler: undefined,
54
+ preset: {},
55
+ yes: false,
56
+ dryRun: false,
57
+ };
58
+ for (let i = 0; i < argv.length; i++) {
59
+ const arg = argv[i];
60
+ switch (arg) {
61
+ case '-y':
62
+ case '--yes':
63
+ options.yes = true;
64
+ break;
65
+ case '-d':
66
+ case '--dry-run':
67
+ options.dryRun = true;
68
+ break;
69
+ case '--logical':
70
+ options.preset.spelling = 'logical';
71
+ break;
72
+ case '--physical':
73
+ options.preset.spelling = 'physical';
74
+ break;
75
+ case '--both':
76
+ options.preset.spelling = 'both';
77
+ break;
78
+ case '--sizes':
79
+ options.preset.sizes = true;
80
+ break;
81
+ case '--no-eslint':
82
+ options.preset.eslint = false;
83
+ break;
84
+ case '--no-install':
85
+ options.preset.install = false;
86
+ break;
87
+ case '--bundler':
88
+ options.bundler = read(argv, ++i, '--bundler');
89
+ break;
90
+ case '--style-prop':
91
+ options.preset.styleProp = styleProp(read(argv, ++i, '--style-prop'));
92
+ break;
93
+ case '--cwd':
94
+ options.cwd = node_path_1.default.resolve(read(argv, ++i, '--cwd'));
95
+ break;
96
+ default:
97
+ throw new UsageError(`unknown option "${arg}"`);
98
+ }
99
+ }
100
+ if (options.bundler && !detect_1.BUNDLERS.includes(options.bundler)) {
101
+ throw new UsageError(`unknown bundler "${options.bundler}". Available: ${detect_1.BUNDLERS.join(', ')}`);
102
+ }
103
+ if (options.preset.sizes && options.preset.spelling === undefined) {
104
+ throw new UsageError('--sizes needs --logical or --physical');
105
+ }
106
+ return options;
107
+ }
108
+ function styleProp(value) {
109
+ try {
110
+ return (0, setup_1.assertStyleProp)(value);
111
+ }
112
+ catch (error) {
113
+ throw new UsageError(error.message);
114
+ }
115
+ }
116
+ function read(argv, index, flag) {
117
+ const value = argv[index];
118
+ if (!value || value.startsWith('-'))
119
+ throw new UsageError(`${flag} needs a value`);
120
+ return value;
121
+ }
122
+ const MARKS = {
123
+ install: '➡︎',
124
+ write: '+',
125
+ patch: '~',
126
+ manual: '!',
127
+ skip: '·',
128
+ };
129
+ const PAINT = {
130
+ install: style_1.style.install,
131
+ write: style_1.style.write,
132
+ patch: style_1.style.patch,
133
+ manual: style_1.style.manual,
134
+ skip: style_1.style.skip,
135
+ };
136
+ function show(actions) {
137
+ const label = Math.max(...actions.map((action) => action.label.length));
138
+ const named = actions.filter((action) => action.kind !== 'install');
139
+ const file = Math.max(0, ...named.map((action) => action.file.length));
140
+ const pad = (text, width) => ' '.repeat(Math.max(0, width - text.length));
141
+ for (const action of actions) {
142
+ const head = PAINT[action.kind](`${MARKS[action.kind]} ${action.label}`) +
143
+ pad(action.label, label);
144
+ const target = action.kind === 'install'
145
+ ? ''
146
+ : ` ${style_1.style.strong(action.file)}${pad(action.file, file)}`;
147
+ console.log(` ${head}${target} ${action.note}`);
148
+ }
149
+ }
150
+ function apply(root, action) {
151
+ if (action.kind !== 'write' && action.kind !== 'patch')
152
+ return;
153
+ const file = node_path_1.default.join(root, action.file);
154
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
155
+ node_fs_1.default.writeFileSync(file, action.contents);
156
+ }
157
+ function install(root, action) {
158
+ if (action.kind !== 'install')
159
+ return 0;
160
+ const [command, ...args] = action.command.split(' ');
161
+ const result = (0, node_child_process_1.spawnSync)(command, args, {
162
+ cwd: root,
163
+ stdio: 'inherit',
164
+ shell: process.platform === 'win32',
165
+ });
166
+ return result.status ?? 1;
167
+ }
168
+ async function main(argv) {
169
+ let options;
170
+ try {
171
+ options = parseArgs(argv);
172
+ }
173
+ catch (error) {
174
+ if (!(error instanceof UsageError))
175
+ throw error;
176
+ console.error(`${style_1.style.failure('✖')} ${error.message}\n`);
177
+ console.error(USAGE);
178
+ return 1;
179
+ }
180
+ if (!options)
181
+ return 0;
182
+ const detected = (0, detect_1.detect)(options.cwd, options.bundler);
183
+ const found = [
184
+ detected.bundler,
185
+ detected.packageManager,
186
+ detected.typescript ? 'typescript' : 'javascript',
187
+ ]
188
+ .map((name) => style_1.style.strong(name))
189
+ .join(' ');
190
+ console.log(`\n${style_1.style.ok('✔')} detected ${found}`);
191
+ const interactive = !options.yes && process.stdin.isTTY === true;
192
+ let asker;
193
+ let answers;
194
+ try {
195
+ if (interactive) {
196
+ asker = (0, prompt_1.terminal)();
197
+ answers = await (0, prompt_1.ask)(asker, options.preset);
198
+ }
199
+ else {
200
+ answers = { ...setup_1.DEFAULT_ANSWERS, ...options.preset };
201
+ }
202
+ const actions = (0, setup_1.plan)(detected, answers);
203
+ console.log('');
204
+ show(actions);
205
+ if (options.dryRun) {
206
+ console.log(`\n${style_1.style.faint('Run without --dry-run to apply.')}`);
207
+ return 0;
208
+ }
209
+ if (interactive && asker) {
210
+ const go = (await asker.question('\nApply? (Y/n) ')).trim().toLowerCase();
211
+ if (go !== '' && go !== 'y' && go !== 'yes') {
212
+ console.log(style_1.style.faint('Nothing was written.'));
213
+ return 0;
214
+ }
215
+ }
216
+ for (const action of actions)
217
+ apply(detected.root, action);
218
+ const wanted = actions.find((action) => action.kind === 'install');
219
+ if (wanted && answers.install) {
220
+ console.log('');
221
+ const status = install(detected.root, wanted);
222
+ if (status !== 0) {
223
+ console.error(`\n${style_1.style.failure('✖')} ${wanted.command} failed. Run it yourself and try again.`);
224
+ return status;
225
+ }
226
+ }
227
+ else if (wanted) {
228
+ console.log(`\n${style_1.style.install('➡︎')} run: ${wanted.command}`);
229
+ }
230
+ console.log(`\n${style_1.style.ok('✔')} Plumeria is set up.`);
231
+ const manual = actions.filter((action) => action.kind === 'manual');
232
+ if (manual.length === 0)
233
+ return 0;
234
+ console.log(`\n${style_1.style.manual('Left to you — these could not be written safely:')}\n`);
235
+ for (const action of manual) {
236
+ if (action.kind !== 'manual')
237
+ continue;
238
+ console.log(` ${style_1.style.strong(action.file)} ${action.note}`);
239
+ for (const line of action.snippet.split('\n'))
240
+ console.log(style_1.style.faint(` ${line}`));
241
+ console.log('');
242
+ }
243
+ return 1;
244
+ }
245
+ finally {
246
+ asker?.close();
247
+ }
248
+ }
@@ -0,0 +1,28 @@
1
+ export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
2
+ export type Bundler = 'next' | 'vite' | 'astro' | 'webpack' | 'rspack' | 'rollup' | 'rolldown' | 'esbuild' | 'farm' | 'bun';
3
+ export interface Manifest {
4
+ type?: string;
5
+ packageManager?: string;
6
+ scripts?: Record<string, string>;
7
+ dependencies?: Record<string, string>;
8
+ devDependencies?: Record<string, string>;
9
+ }
10
+ export interface Detected {
11
+ root: string;
12
+ manifest: Manifest;
13
+ packageManager: PackageManager;
14
+ bundler: Bundler;
15
+ bundlerConfig: string | undefined;
16
+ eslintConfig: string | undefined;
17
+ declaration: string | undefined;
18
+ typescript: boolean;
19
+ esm: boolean;
20
+ }
21
+ export declare const BUNDLERS: readonly Bundler[];
22
+ export declare const readManifest: (root: string) => Manifest;
23
+ export declare const installed: (manifest: Manifest) => Record<string, string>;
24
+ export declare const detectPackageManager: (root: string, manifest: Manifest) => PackageManager;
25
+ export declare const detectBundler: (root: string, manifest: Manifest) => Bundler | undefined;
26
+ export declare const detect: (root: string, override?: Bundler) => Detected;
27
+ export declare const defaultConfigName: (bundler: Bundler, typescript: boolean) => string;
28
+ export declare const configNames: (bundler: Bundler) => readonly string[];
package/dist/detect.js ADDED
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configNames = exports.defaultConfigName = exports.detect = exports.detectBundler = exports.detectPackageManager = exports.installed = exports.readManifest = exports.BUNDLERS = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ exports.BUNDLERS = [
10
+ 'next',
11
+ 'vite',
12
+ 'astro',
13
+ 'webpack',
14
+ 'rspack',
15
+ 'rollup',
16
+ 'rolldown',
17
+ 'esbuild',
18
+ 'farm',
19
+ 'bun',
20
+ ];
21
+ const LOCKFILES = [
22
+ ['pnpm-lock.yaml', 'pnpm'],
23
+ ['bun.lock', 'bun'],
24
+ ['bun.lockb', 'bun'],
25
+ ['yarn.lock', 'yarn'],
26
+ ['package-lock.json', 'npm'],
27
+ ];
28
+ const CONFIG_NAMES = {
29
+ next: [
30
+ 'next.config.ts',
31
+ 'next.config.mts',
32
+ 'next.config.mjs',
33
+ 'next.config.js',
34
+ 'next.config.cjs',
35
+ ],
36
+ vite: [
37
+ 'vite.config.ts',
38
+ 'vite.config.mts',
39
+ 'vite.config.mjs',
40
+ 'vite.config.js',
41
+ ],
42
+ astro: [
43
+ 'astro.config.ts',
44
+ 'astro.config.mts',
45
+ 'astro.config.mjs',
46
+ 'astro.config.js',
47
+ ],
48
+ webpack: [
49
+ 'webpack.config.ts',
50
+ 'webpack.config.mjs',
51
+ 'webpack.config.js',
52
+ 'webpack.config.cjs',
53
+ ],
54
+ rspack: [
55
+ 'rspack.config.ts',
56
+ 'rspack.config.mjs',
57
+ 'rspack.config.js',
58
+ 'rspack.config.cjs',
59
+ ],
60
+ rollup: ['rollup.config.ts', 'rollup.config.mjs', 'rollup.config.js'],
61
+ rolldown: ['rolldown.config.ts', 'rolldown.config.mjs', 'rolldown.config.js'],
62
+ esbuild: ['build.ts', 'build.mjs', 'build.js', 'esbuild.config.js'],
63
+ farm: ['farm.config.ts', 'farm.config.mjs', 'farm.config.js'],
64
+ bun: ['build.ts', 'build.mjs', 'build.js'],
65
+ };
66
+ const ESLINT_NAMES = [
67
+ 'eslint.config.ts',
68
+ 'eslint.config.mts',
69
+ 'eslint.config.mjs',
70
+ 'eslint.config.js',
71
+ 'eslint.config.cjs',
72
+ '.eslintrc.json',
73
+ '.eslintrc.js',
74
+ '.eslintrc.cjs',
75
+ '.eslintrc',
76
+ ];
77
+ const DECLARATION_NAMES = [
78
+ 'plumeria.d.ts',
79
+ 'src/plumeria.d.ts',
80
+ 'app/plumeria.d.ts',
81
+ 'types/plumeria.d.ts',
82
+ ];
83
+ const readManifest = (root) => {
84
+ const file = node_path_1.default.join(root, 'package.json');
85
+ if (!node_fs_1.default.existsSync(file)) {
86
+ throw new Error(`no package.json in ${root}. Run this from the project root.`);
87
+ }
88
+ return JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
89
+ };
90
+ exports.readManifest = readManifest;
91
+ const installed = (manifest) => ({
92
+ ...manifest.dependencies,
93
+ ...manifest.devDependencies,
94
+ });
95
+ exports.installed = installed;
96
+ const named = (value) => value === 'pnpm' || value === 'yarn' || value === 'bun' || value === 'npm'
97
+ ? value
98
+ : undefined;
99
+ const declaredIn = (dir) => {
100
+ const file = node_path_1.default.join(dir, 'package.json');
101
+ if (!node_fs_1.default.existsSync(file))
102
+ return undefined;
103
+ try {
104
+ const parsed = JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
105
+ return named(parsed.packageManager?.split('@')[0]);
106
+ }
107
+ catch {
108
+ return undefined;
109
+ }
110
+ };
111
+ const detectPackageManager = (root, manifest) => {
112
+ const own = named(manifest.packageManager?.split('@')[0]);
113
+ if (own)
114
+ return own;
115
+ for (let dir = root;;) {
116
+ for (const [lockfile, manager] of LOCKFILES) {
117
+ if (node_fs_1.default.existsSync(node_path_1.default.join(dir, lockfile)))
118
+ return manager;
119
+ }
120
+ if (dir !== root) {
121
+ const declared = declaredIn(dir);
122
+ if (declared)
123
+ return declared;
124
+ }
125
+ const parent = node_path_1.default.dirname(dir);
126
+ if (parent === dir)
127
+ break;
128
+ dir = parent;
129
+ }
130
+ return 'npm';
131
+ };
132
+ exports.detectPackageManager = detectPackageManager;
133
+ const firstExisting = (root, names) => names.find((name) => node_fs_1.default.existsSync(node_path_1.default.join(root, name)));
134
+ const detectBundler = (root, manifest) => {
135
+ const deps = (0, exports.installed)(manifest);
136
+ const has = (name) => name in deps;
137
+ if (has('next'))
138
+ return 'next';
139
+ if (has('astro'))
140
+ return 'astro';
141
+ if (has('@rspack/core') || has('@rspack/cli'))
142
+ return 'rspack';
143
+ if (has('@farmfe/core'))
144
+ return 'farm';
145
+ if (has('vite') || has('@react-router/dev'))
146
+ return 'vite';
147
+ if (has('rolldown'))
148
+ return 'rolldown';
149
+ if (has('rollup'))
150
+ return 'rollup';
151
+ if (has('webpack'))
152
+ return 'webpack';
153
+ if (has('esbuild'))
154
+ return 'esbuild';
155
+ for (const bundler of exports.BUNDLERS) {
156
+ if (bundler === 'bun')
157
+ continue;
158
+ if (firstExisting(root, CONFIG_NAMES[bundler]))
159
+ return bundler;
160
+ }
161
+ if (node_fs_1.default.existsSync(node_path_1.default.join(root, 'bun.lock')) ||
162
+ node_fs_1.default.existsSync(node_path_1.default.join(root, 'bun.lockb'))) {
163
+ return 'bun';
164
+ }
165
+ return undefined;
166
+ };
167
+ exports.detectBundler = detectBundler;
168
+ const detect = (root, override) => {
169
+ const manifest = (0, exports.readManifest)(root);
170
+ const bundler = override ?? (0, exports.detectBundler)(root, manifest);
171
+ if (!bundler) {
172
+ throw new Error(`no bundler found. Pass --bundler <${exports.BUNDLERS.join('|')}>.`);
173
+ }
174
+ const declaration = firstExisting(root, DECLARATION_NAMES);
175
+ const typescript = node_fs_1.default.existsSync(node_path_1.default.join(root, 'tsconfig.json')) ||
176
+ 'typescript' in (0, exports.installed)(manifest);
177
+ return {
178
+ root,
179
+ manifest,
180
+ packageManager: (0, exports.detectPackageManager)(root, manifest),
181
+ bundler,
182
+ bundlerConfig: firstExisting(root, CONFIG_NAMES[bundler]),
183
+ eslintConfig: firstExisting(root, ESLINT_NAMES),
184
+ declaration,
185
+ typescript,
186
+ esm: manifest.type === 'module',
187
+ };
188
+ };
189
+ exports.detect = detect;
190
+ const defaultConfigName = (bundler, typescript) => {
191
+ const names = CONFIG_NAMES[bundler];
192
+ const ending = (suffix) => names.find((name) => name.endsWith(suffix));
193
+ if (typescript)
194
+ return ending('.ts') ?? names[0];
195
+ return ending('.mjs') ?? ending('.js') ?? names[0];
196
+ };
197
+ exports.defaultConfigName = defaultConfigName;
198
+ const configNames = (bundler) => CONFIG_NAMES[bundler];
199
+ exports.configNames = configNames;
@@ -0,0 +1,5 @@
1
+ export { detect, detectBundler, detectPackageManager } from './detect';
2
+ export type { Bundler, Detected, PackageManager } from './detect';
3
+ export { plan } from './setup';
4
+ export type { Action, Answers, Spelling } from './setup';
5
+ export { main } from './cli';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = exports.plan = exports.detectPackageManager = exports.detectBundler = exports.detect = void 0;
4
+ var detect_1 = require("./detect");
5
+ Object.defineProperty(exports, "detect", { enumerable: true, get: function () { return detect_1.detect; } });
6
+ Object.defineProperty(exports, "detectBundler", { enumerable: true, get: function () { return detect_1.detectBundler; } });
7
+ Object.defineProperty(exports, "detectPackageManager", { enumerable: true, get: function () { return detect_1.detectPackageManager; } });
8
+ var setup_1 = require("./setup");
9
+ Object.defineProperty(exports, "plan", { enumerable: true, get: function () { return setup_1.plan; } });
10
+ var cli_1 = require("./cli");
11
+ Object.defineProperty(exports, "main", { enumerable: true, get: function () { return cli_1.main; } });
@@ -0,0 +1,7 @@
1
+ import type { Answers } from './setup';
2
+ export interface Asker {
3
+ question: (prompt: string) => Promise<string>;
4
+ close: () => void;
5
+ }
6
+ export declare const terminal: () => Asker;
7
+ export declare const ask: (asker: Asker, preset: Partial<Answers>) => Promise<Answers>;