frontpl 0.5.0 → 0.6.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/README.md +50 -0
- package/dist/cli.mjs +8 -1
- package/dist/index.d.mts +24 -6
- package/dist/index.mjs +2 -2
- package/dist/{oxfmt-DQBJ0JMs.mjs → package-BtUDK9bn.mjs} +378 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,6 +80,52 @@ What it does:
|
|
|
80
80
|
|
|
81
81
|
Use `--yes` (or `-y`) to skip confirmations and use defaults inferred from root scripts.
|
|
82
82
|
|
|
83
|
+
### `frontpl pkg`
|
|
84
|
+
|
|
85
|
+
Normalize current directory `package.json` for npm publishing (requires `github.com` git remote).
|
|
86
|
+
|
|
87
|
+
What it does:
|
|
88
|
+
|
|
89
|
+
- Requires current directory to contain `package.json`
|
|
90
|
+
- Requires git repository with `remote.origin.url` on `github.com`
|
|
91
|
+
- Updates publish metadata:
|
|
92
|
+
- `homepage`
|
|
93
|
+
- `bugs.url`
|
|
94
|
+
- `repository` (`type`, `url`, and `directory` when run in monorepo subfolder)
|
|
95
|
+
- Applies publish defaults when missing:
|
|
96
|
+
- `private: false`, `version: "0.0.0"`
|
|
97
|
+
- `license`:
|
|
98
|
+
- interactive select on each run (`MIT` / `Apache-2.0`)
|
|
99
|
+
- with `--yes`, keeps existing license; defaults to `MIT` only when missing
|
|
100
|
+
- option keys aligned with GitHub Licenses API: `mit` / `apache-2.0`
|
|
101
|
+
- `type: "module"`, `files: ["dist"]`
|
|
102
|
+
- `main`, `types`, and `exports` pointing to `dist/index`
|
|
103
|
+
- `publishConfig.access: "public"`
|
|
104
|
+
- `engines.node: ">=22.0.0"`
|
|
105
|
+
- `scripts.prepublishOnly` from existing `scripts.build`
|
|
106
|
+
|
|
107
|
+
Use `--yes` (or `-y`) to skip confirmation.
|
|
108
|
+
|
|
109
|
+
### `frontpl bump [target]`
|
|
110
|
+
|
|
111
|
+
Update current `package.json#version` without opening an editor.
|
|
112
|
+
|
|
113
|
+
Interactive mode (`frontpl bump`):
|
|
114
|
+
|
|
115
|
+
- Shows concrete candidate versions for `patch` / `minor` / `major`
|
|
116
|
+
- e.g. current `1.2.3` -> `1.2.4` / `1.3.0` / `2.0.0`
|
|
117
|
+
- `custom`: set explicit version text
|
|
118
|
+
|
|
119
|
+
Direct mode (`frontpl bump <target>`):
|
|
120
|
+
|
|
121
|
+
- `frontpl bump patch`
|
|
122
|
+
- `frontpl bump minor`
|
|
123
|
+
- `frontpl bump major`
|
|
124
|
+
- `frontpl bump 1.8.0`
|
|
125
|
+
|
|
126
|
+
When using `minor`/`major`, trailing segments are reset to `0`.
|
|
127
|
+
Use `--dry-run` to preview from/to version without writing `package.json`.
|
|
128
|
+
|
|
83
129
|
### `frontpl ci`
|
|
84
130
|
|
|
85
131
|
Add or update CI/Release workflows for an existing project (run it in your repo root).
|
|
@@ -131,6 +177,10 @@ What it does:
|
|
|
131
177
|
- `format:check`: `oxfmt --check`
|
|
132
178
|
- Ensures `devDependencies.oxfmt` exists (defaults to `latest` when missing)
|
|
133
179
|
- Creates or updates `.oxfmtrc.json`
|
|
180
|
+
- Rebuild mode writes baseline formatter options:
|
|
181
|
+
- `$schema: "./node_modules/oxfmt/configuration_schema.json"`
|
|
182
|
+
- `useTabs: false`, `indentWidth: 2`, `lineWidth: 100`
|
|
183
|
+
- `trailingComma: "all"`, `semi: true`, `singleQuote: false`, `arrowParens: "always"`
|
|
134
184
|
- Optionally removes `prettier` / `prettier-plugin-*` / `@prettier/plugin-*` dependencies, `package.json#prettier`, and Prettier config files (`.prettierrc*`, `prettier.config.*`)
|
|
135
185
|
- Optionally installs dependencies with detected package manager
|
|
136
186
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as runBump, i as runCi, n as runOxfmt, o as runAdd, r as runOxlint, s as runInit, t as runPackage } from "./package-BtUDK9bn.mjs";
|
|
3
3
|
import bin from "tiny-bin";
|
|
4
4
|
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -19,6 +19,13 @@ async function main() {
|
|
|
19
19
|
await runOxlint({ yes: options.yes === true });
|
|
20
20
|
}).command("oxfmt", "Add/migrate formatter to oxfmt in current project").option("--yes, -y", "Skip confirmations and use defaults").action(async (options) => {
|
|
21
21
|
await runOxfmt({ yes: options.yes === true });
|
|
22
|
+
}).command("bump", "Bump package.json version").argument("[target]", "patch | minor | major | <version>").option("--dry-run", "Show the next version without writing package.json").action(async (options, args) => {
|
|
23
|
+
await runBump({
|
|
24
|
+
targetArg: args[0],
|
|
25
|
+
dryRun: options.dryRun === true
|
|
26
|
+
});
|
|
27
|
+
}).command("pkg", "Normalize package.json for npm publishing using GitHub remote").option("--yes, -y", "Skip confirmations and use defaults").action(async (options) => {
|
|
28
|
+
await runPackage({ yes: options.yes === true });
|
|
22
29
|
}).run();
|
|
23
30
|
}
|
|
24
31
|
main();
|
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
//#region src/commands/add.d.ts
|
|
2
|
-
type CommandOptions$
|
|
2
|
+
type CommandOptions$4 = {
|
|
3
3
|
nameArg?: string;
|
|
4
4
|
yes?: boolean;
|
|
5
5
|
};
|
|
6
6
|
declare function runAdd({
|
|
7
7
|
nameArg,
|
|
8
8
|
yes
|
|
9
|
-
}?: CommandOptions$
|
|
9
|
+
}?: CommandOptions$4): Promise<void>;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/commands/bump.d.ts
|
|
12
|
+
type CommandOptions$3 = {
|
|
13
|
+
targetArg?: string;
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
};
|
|
16
|
+
declare function runBump({
|
|
17
|
+
targetArg,
|
|
18
|
+
dryRun
|
|
19
|
+
}?: CommandOptions$3): Promise<void>;
|
|
10
20
|
//#endregion
|
|
11
21
|
//#region src/commands/ci.d.ts
|
|
12
22
|
declare function runCi(): Promise<undefined>;
|
|
@@ -20,19 +30,27 @@ declare function runInit({
|
|
|
20
30
|
declare function validateProjectName(value: string | undefined): "Project name is required" | "Project name is too long" | "Project name cannot start with '.'" | "Project name cannot start with '_'" | "Use letters, numbers, '.', '_' or '-'" | undefined;
|
|
21
31
|
//#endregion
|
|
22
32
|
//#region src/commands/oxlint.d.ts
|
|
23
|
-
type CommandOptions$
|
|
33
|
+
type CommandOptions$2 = {
|
|
24
34
|
yes?: boolean;
|
|
25
35
|
};
|
|
26
36
|
declare function runOxlint({
|
|
27
37
|
yes
|
|
28
|
-
}?: CommandOptions$
|
|
38
|
+
}?: CommandOptions$2): Promise<void>;
|
|
29
39
|
//#endregion
|
|
30
40
|
//#region src/commands/oxfmt.d.ts
|
|
31
|
-
type CommandOptions = {
|
|
41
|
+
type CommandOptions$1 = {
|
|
32
42
|
yes?: boolean;
|
|
33
43
|
};
|
|
34
44
|
declare function runOxfmt({
|
|
35
45
|
yes
|
|
46
|
+
}?: CommandOptions$1): Promise<void>;
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/commands/package.d.ts
|
|
49
|
+
type CommandOptions = {
|
|
50
|
+
yes?: boolean;
|
|
51
|
+
};
|
|
52
|
+
declare function runPackage({
|
|
53
|
+
yes
|
|
36
54
|
}?: CommandOptions): Promise<void>;
|
|
37
55
|
//#endregion
|
|
38
56
|
//#region src/lib/templates.d.ts
|
|
@@ -88,4 +106,4 @@ declare function githubDependabotTemplate(opts: {
|
|
|
88
106
|
workingDirectory: string;
|
|
89
107
|
}): string;
|
|
90
108
|
//#endregion
|
|
91
|
-
export { githubCliCiWorkflowTemplate, githubDependabotTemplate, oxlintConfigTemplate, packageJsonTemplate, runAdd, runCi, runInit, runOxfmt, runOxlint, validateProjectName, workspaceRootPackageJsonTemplate };
|
|
109
|
+
export { githubCliCiWorkflowTemplate, githubDependabotTemplate, oxlintConfigTemplate, packageJsonTemplate, runAdd, runBump, runCi, runInit, runOxfmt, runOxlint, runPackage, validateProjectName, workspaceRootPackageJsonTemplate };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as runBump, c as validateProjectName, d as oxlintConfigTemplate, f as packageJsonTemplate, i as runCi, l as githubCliCiWorkflowTemplate, n as runOxfmt, o as runAdd, p as workspaceRootPackageJsonTemplate, r as runOxlint, s as runInit, t as runPackage, u as githubDependabotTemplate } from "./package-BtUDK9bn.mjs";
|
|
2
2
|
|
|
3
|
-
export { githubCliCiWorkflowTemplate, githubDependabotTemplate, oxlintConfigTemplate, packageJsonTemplate, runAdd, runCi, runInit, runOxfmt, runOxlint, validateProjectName, workspaceRootPackageJsonTemplate };
|
|
3
|
+
export { githubCliCiWorkflowTemplate, githubDependabotTemplate, oxlintConfigTemplate, packageJsonTemplate, runAdd, runBump, runCi, runInit, runOxfmt, runOxlint, runPackage, validateProjectName, workspaceRootPackageJsonTemplate };
|
|
@@ -2,7 +2,7 @@ import { cancel, confirm, intro, isCancel, outro, select, spinner, text } from "
|
|
|
2
2
|
import { access, mkdir, readFile, readdir, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
5
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
|
|
8
8
|
//#region src/lib/versions.ts
|
|
@@ -136,7 +136,16 @@ function oxlintConfigTemplate({ useVitest }) {
|
|
|
136
136
|
].join("\n");
|
|
137
137
|
}
|
|
138
138
|
function oxfmtConfigTemplate() {
|
|
139
|
-
return JSON.stringify({
|
|
139
|
+
return JSON.stringify({
|
|
140
|
+
$schema: "./node_modules/oxfmt/configuration_schema.json",
|
|
141
|
+
useTabs: false,
|
|
142
|
+
indentWidth: 2,
|
|
143
|
+
lineWidth: 100,
|
|
144
|
+
trailingComma: "all",
|
|
145
|
+
semi: true,
|
|
146
|
+
singleQuote: false,
|
|
147
|
+
arrowParens: "always"
|
|
148
|
+
}, null, 2) + "\n";
|
|
140
149
|
}
|
|
141
150
|
function tsdownConfigTemplate() {
|
|
142
151
|
return [
|
|
@@ -427,7 +436,7 @@ async function detectPackageManager(rootDir) {
|
|
|
427
436
|
const pmField = (await readPackageJson(path.join(rootDir, "package.json")))?.packageManager;
|
|
428
437
|
if (pmField) {
|
|
429
438
|
const pm = pmField.split("@")[0] ?? "";
|
|
430
|
-
if (isPackageManager(pm)) return pm;
|
|
439
|
+
if (isPackageManager$1(pm)) return pm;
|
|
431
440
|
}
|
|
432
441
|
const candidates = [];
|
|
433
442
|
if (await pathExists(path.join(rootDir, "pnpm-lock.yaml"))) candidates.push("pnpm");
|
|
@@ -438,7 +447,7 @@ async function detectPackageManager(rootDir) {
|
|
|
438
447
|
if (await pathExists(path.join(rootDir, "deno.json")) || await pathExists(path.join(rootDir, "deno.jsonc"))) candidates.push("deno");
|
|
439
448
|
return candidates.length === 1 ? candidates[0] : void 0;
|
|
440
449
|
}
|
|
441
|
-
function isPackageManager(value) {
|
|
450
|
+
function isPackageManager$1(value) {
|
|
442
451
|
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun" || value === "deno";
|
|
443
452
|
}
|
|
444
453
|
|
|
@@ -467,7 +476,7 @@ function resolveCommand(command) {
|
|
|
467
476
|
|
|
468
477
|
//#endregion
|
|
469
478
|
//#region src/commands/init.ts
|
|
470
|
-
function pmRun$
|
|
479
|
+
function pmRun$2(pm, script) {
|
|
471
480
|
switch (pm) {
|
|
472
481
|
case "npm": return `npm run ${script}`;
|
|
473
482
|
case "pnpm": return `pnpm run ${script}`;
|
|
@@ -483,7 +492,7 @@ async function runInit({ nameArg }) {
|
|
|
483
492
|
initialValue: nameArg ?? "my-frontend",
|
|
484
493
|
validate: validateProjectName
|
|
485
494
|
});
|
|
486
|
-
if (isCancel(projectName)) return onCancel$
|
|
495
|
+
if (isCancel(projectName)) return onCancel$3();
|
|
487
496
|
const packageManager = await select({
|
|
488
497
|
message: "Package manager",
|
|
489
498
|
initialValue: "pnpm",
|
|
@@ -510,37 +519,37 @@ async function runInit({ nameArg }) {
|
|
|
510
519
|
}
|
|
511
520
|
]
|
|
512
521
|
});
|
|
513
|
-
if (isCancel(packageManager)) return onCancel$
|
|
522
|
+
if (isCancel(packageManager)) return onCancel$3();
|
|
514
523
|
const pnpmWorkspace = packageManager === "pnpm" ? await confirm({
|
|
515
524
|
message: "pnpm workspace mode (monorepo skeleton)?",
|
|
516
525
|
initialValue: false
|
|
517
526
|
}) : false;
|
|
518
|
-
if (isCancel(pnpmWorkspace)) return onCancel$
|
|
527
|
+
if (isCancel(pnpmWorkspace)) return onCancel$3();
|
|
519
528
|
const useOxlint = await confirm({
|
|
520
529
|
message: "Enable oxlint (@kingsword/lint-config preset)?",
|
|
521
530
|
initialValue: true
|
|
522
531
|
});
|
|
523
|
-
if (isCancel(useOxlint)) return onCancel$
|
|
532
|
+
if (isCancel(useOxlint)) return onCancel$3();
|
|
524
533
|
const useOxfmt = await confirm({
|
|
525
534
|
message: "Enable oxfmt (code formatting)?",
|
|
526
535
|
initialValue: true
|
|
527
536
|
});
|
|
528
|
-
if (isCancel(useOxfmt)) return onCancel$
|
|
537
|
+
if (isCancel(useOxfmt)) return onCancel$3();
|
|
529
538
|
const useVitest = await confirm({
|
|
530
539
|
message: "Add Vitest?",
|
|
531
540
|
initialValue: false
|
|
532
541
|
});
|
|
533
|
-
if (isCancel(useVitest)) return onCancel$
|
|
542
|
+
if (isCancel(useVitest)) return onCancel$3();
|
|
534
543
|
const useTsdown = await confirm({
|
|
535
544
|
message: "Add tsdown build?",
|
|
536
545
|
initialValue: true
|
|
537
546
|
});
|
|
538
|
-
if (isCancel(useTsdown)) return onCancel$
|
|
547
|
+
if (isCancel(useTsdown)) return onCancel$3();
|
|
539
548
|
const initGit = await confirm({
|
|
540
549
|
message: "Initialize a git repository?",
|
|
541
550
|
initialValue: true
|
|
542
551
|
});
|
|
543
|
-
if (isCancel(initGit)) return onCancel$
|
|
552
|
+
if (isCancel(initGit)) return onCancel$3();
|
|
544
553
|
const githubActions = await select({
|
|
545
554
|
message: "GitHub Actions workflows",
|
|
546
555
|
initialValue: "ci",
|
|
@@ -559,7 +568,7 @@ async function runInit({ nameArg }) {
|
|
|
559
568
|
}
|
|
560
569
|
]
|
|
561
570
|
});
|
|
562
|
-
if (isCancel(githubActions)) return onCancel$
|
|
571
|
+
if (isCancel(githubActions)) return onCancel$3();
|
|
563
572
|
const releaseMode = githubActions === "ci+release" ? await select({
|
|
564
573
|
message: "Release workflows",
|
|
565
574
|
initialValue: "tag",
|
|
@@ -578,17 +587,17 @@ async function runInit({ nameArg }) {
|
|
|
578
587
|
}
|
|
579
588
|
]
|
|
580
589
|
}) : void 0;
|
|
581
|
-
if (isCancel(releaseMode)) return onCancel$
|
|
590
|
+
if (isCancel(releaseMode)) return onCancel$3();
|
|
582
591
|
const addDependabot = initGit && githubActions !== "none" ? await confirm({
|
|
583
592
|
message: "Add Dependabot config (.github/dependabot.yml)?",
|
|
584
593
|
initialValue: true
|
|
585
594
|
}) : false;
|
|
586
|
-
if (isCancel(addDependabot)) return onCancel$
|
|
595
|
+
if (isCancel(addDependabot)) return onCancel$3();
|
|
587
596
|
const trustedPublishing = githubActions === "ci+release" && packageManager !== "deno" ? await confirm({
|
|
588
597
|
message: "Release: npm trusted publishing (OIDC)?",
|
|
589
598
|
initialValue: true
|
|
590
599
|
}) : void 0;
|
|
591
|
-
if (isCancel(trustedPublishing)) return onCancel$
|
|
600
|
+
if (isCancel(trustedPublishing)) return onCancel$3();
|
|
592
601
|
const rootDir = path.resolve(process.cwd(), projectName);
|
|
593
602
|
if (await pathExists(rootDir)) {
|
|
594
603
|
cancel(`Directory already exists: ${rootDir}`);
|
|
@@ -655,9 +664,9 @@ async function runInit({ nameArg }) {
|
|
|
655
664
|
if (packageManager === "deno") await writeText(path.join(rootDir, "deno.json"), JSON.stringify({ nodeModulesDir: "auto" }, null, 2) + "\n");
|
|
656
665
|
if (githubActions !== "none") {
|
|
657
666
|
const workingDirectory = ".";
|
|
658
|
-
const lintCommand = useOxlint && packageManager !== "deno" ? pmRun$
|
|
659
|
-
const formatCheckCommand = useOxfmt && packageManager !== "deno" ? pmRun$
|
|
660
|
-
const testCommand = useVitest && packageManager !== "deno" ? pmRun$
|
|
667
|
+
const lintCommand = useOxlint && packageManager !== "deno" ? pmRun$2(packageManager, "lint") : void 0;
|
|
668
|
+
const formatCheckCommand = useOxfmt && packageManager !== "deno" ? pmRun$2(packageManager, "format:check") : void 0;
|
|
669
|
+
const testCommand = useVitest && packageManager !== "deno" ? pmRun$2(packageManager, "test") : void 0;
|
|
661
670
|
await writeText(path.join(rootDir, ".github/workflows/ci.yml"), githubCliCiWorkflowTemplate({
|
|
662
671
|
packageManager,
|
|
663
672
|
nodeVersion: 22,
|
|
@@ -702,7 +711,7 @@ function validateProjectName(value) {
|
|
|
702
711
|
if (name.startsWith("_")) return "Project name cannot start with '_'";
|
|
703
712
|
if (!/^[A-Za-z0-9._-]+$/.test(name)) return "Use letters, numbers, '.', '_' or '-'";
|
|
704
713
|
}
|
|
705
|
-
function onCancel$
|
|
714
|
+
function onCancel$3() {
|
|
706
715
|
cancel("Cancelled");
|
|
707
716
|
process.exitCode = 0;
|
|
708
717
|
}
|
|
@@ -740,7 +749,7 @@ async function runAdd({ nameArg, yes = false } = {}) {
|
|
|
740
749
|
validate: validateProjectName
|
|
741
750
|
});
|
|
742
751
|
if (!packageName) return;
|
|
743
|
-
if (isCancel(packageName)) return onCancel();
|
|
752
|
+
if (isCancel(packageName)) return onCancel$2();
|
|
744
753
|
const packageDir = path.join(rootDir, "packages", packageName);
|
|
745
754
|
if (await pathExists(packageDir)) {
|
|
746
755
|
cancel(`Package already exists: ${packageDir}`);
|
|
@@ -753,12 +762,12 @@ async function runAdd({ nameArg, yes = false } = {}) {
|
|
|
753
762
|
message: "Add Vitest?",
|
|
754
763
|
initialValue: useVitestDefault
|
|
755
764
|
});
|
|
756
|
-
if (isCancel(useVitest)) return onCancel();
|
|
765
|
+
if (isCancel(useVitest)) return onCancel$2();
|
|
757
766
|
const useTsdown = yes ? useTsdownDefault : await confirm({
|
|
758
767
|
message: "Add tsdown build?",
|
|
759
768
|
initialValue: useTsdownDefault
|
|
760
769
|
});
|
|
761
|
-
if (isCancel(useTsdown)) return onCancel();
|
|
770
|
+
if (isCancel(useTsdown)) return onCancel$2();
|
|
762
771
|
const resolvedPackageManagerField = await resolvePnpmPackageManagerField(packageManagerField);
|
|
763
772
|
const rootHasOxlint = Boolean(rootPkg?.devDependencies?.oxlint) || Boolean(rootPkg?.devDependencies?.["oxlint-tsgolint"]) || await pathExists(path.join(rootDir, "oxlint.config.ts"));
|
|
764
773
|
const typescriptVersion = rootPkg?.devDependencies?.typescript ?? "latest";
|
|
@@ -806,7 +815,157 @@ function resolvePackageNameFromArg(nameArg) {
|
|
|
806
815
|
}
|
|
807
816
|
return value;
|
|
808
817
|
}
|
|
809
|
-
function onCancel() {
|
|
818
|
+
function onCancel$2() {
|
|
819
|
+
cancel("Cancelled");
|
|
820
|
+
process.exitCode = 0;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
//#endregion
|
|
824
|
+
//#region src/commands/bump.ts
|
|
825
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
826
|
+
async function runBump({ targetArg, dryRun = false } = {}) {
|
|
827
|
+
intro("frontpl (bump)");
|
|
828
|
+
const cwd = process.cwd();
|
|
829
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
830
|
+
const pkg = await readPackageJson(packageJsonPath);
|
|
831
|
+
if (!pkg) {
|
|
832
|
+
cancel("Missing package.json. Run this command in a Node package directory.");
|
|
833
|
+
process.exitCode = 1;
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const currentVersion = pkg.version?.trim();
|
|
837
|
+
const resolved = targetArg ? resolveVersionFromTargetArg({
|
|
838
|
+
currentVersion,
|
|
839
|
+
targetArg
|
|
840
|
+
}) : await resolveVersionInteractively({ currentVersion });
|
|
841
|
+
if (isCancel(resolved)) return onCancel$1();
|
|
842
|
+
if (!resolved.ok) {
|
|
843
|
+
cancel(resolved.reason);
|
|
844
|
+
process.exitCode = 1;
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (!dryRun) {
|
|
848
|
+
pkg.version = resolved.nextVersion;
|
|
849
|
+
await writePackageJson(packageJsonPath, pkg);
|
|
850
|
+
}
|
|
851
|
+
outro([
|
|
852
|
+
dryRun ? "Dry run. Calculated package.json version update." : "Done. Updated package.json version.",
|
|
853
|
+
`- from: ${currentVersion ?? "(missing)"}`,
|
|
854
|
+
`- to: ${resolved.nextVersion}`,
|
|
855
|
+
`- mode: ${resolved.mode}`
|
|
856
|
+
].join("\n"));
|
|
857
|
+
}
|
|
858
|
+
function resolveVersionFromTargetArg(opts) {
|
|
859
|
+
const target = opts.targetArg.trim();
|
|
860
|
+
if (!target) return {
|
|
861
|
+
ok: false,
|
|
862
|
+
reason: "Target is required (patch | minor | major | <version>)"
|
|
863
|
+
};
|
|
864
|
+
if (target === "patch" || target === "minor" || target === "major") {
|
|
865
|
+
const nextVersion = bumpSemver(opts.currentVersion, target);
|
|
866
|
+
if (!nextVersion) return {
|
|
867
|
+
ok: false,
|
|
868
|
+
reason: "Current version is missing or invalid for bump mode. Set an explicit version (e.g. `frontpl bump 1.2.3`)."
|
|
869
|
+
};
|
|
870
|
+
return {
|
|
871
|
+
ok: true,
|
|
872
|
+
mode: target,
|
|
873
|
+
nextVersion
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
if (!SEMVER_PATTERN.test(target)) return {
|
|
877
|
+
ok: false,
|
|
878
|
+
reason: "Invalid version. Use semver like 1.2.3 (or prerelease/build variants)."
|
|
879
|
+
};
|
|
880
|
+
return {
|
|
881
|
+
ok: true,
|
|
882
|
+
mode: "set",
|
|
883
|
+
nextVersion: target
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
async function resolveVersionInteractively(opts) {
|
|
887
|
+
const currentVersion = opts.currentVersion;
|
|
888
|
+
const patchVersion = bumpSemver(currentVersion, "patch");
|
|
889
|
+
const minorVersion = bumpSemver(currentVersion, "minor");
|
|
890
|
+
const majorVersion = bumpSemver(currentVersion, "major");
|
|
891
|
+
if (!patchVersion || !minorVersion || !majorVersion) {
|
|
892
|
+
const customVersion = await text({
|
|
893
|
+
message: "Current version is missing/invalid. Enter target version",
|
|
894
|
+
initialValue: "0.1.0",
|
|
895
|
+
validate: (value = "") => SEMVER_PATTERN.test(value.trim()) ? void 0 : "Use semver like 1.2.3 (or prerelease/build variants)"
|
|
896
|
+
});
|
|
897
|
+
if (isCancel(customVersion)) return customVersion;
|
|
898
|
+
return {
|
|
899
|
+
ok: true,
|
|
900
|
+
mode: "set",
|
|
901
|
+
nextVersion: String(customVersion).trim()
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const choice = await select({
|
|
905
|
+
message: `Select next version (current: ${currentVersion})`,
|
|
906
|
+
initialValue: "patch",
|
|
907
|
+
options: [
|
|
908
|
+
{
|
|
909
|
+
value: "patch",
|
|
910
|
+
label: patchVersion,
|
|
911
|
+
hint: "patch"
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
value: "minor",
|
|
915
|
+
label: minorVersion,
|
|
916
|
+
hint: "minor"
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
value: "major",
|
|
920
|
+
label: majorVersion,
|
|
921
|
+
hint: "major"
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
value: "custom",
|
|
925
|
+
label: "custom",
|
|
926
|
+
hint: "set explicit version"
|
|
927
|
+
}
|
|
928
|
+
]
|
|
929
|
+
});
|
|
930
|
+
if (isCancel(choice)) return choice;
|
|
931
|
+
if (choice === "custom") {
|
|
932
|
+
const customVersion = await text({
|
|
933
|
+
message: "Target version",
|
|
934
|
+
initialValue: currentVersion,
|
|
935
|
+
validate: (value = "") => SEMVER_PATTERN.test(value.trim()) ? void 0 : "Use semver like 1.2.3 (or prerelease/build variants)"
|
|
936
|
+
});
|
|
937
|
+
if (isCancel(customVersion)) return customVersion;
|
|
938
|
+
return {
|
|
939
|
+
ok: true,
|
|
940
|
+
mode: "set",
|
|
941
|
+
nextVersion: String(customVersion).trim()
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
ok: true,
|
|
946
|
+
mode: choice,
|
|
947
|
+
nextVersion: choice === "patch" ? patchVersion : choice === "minor" ? minorVersion : majorVersion
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function bumpSemver(version, mode) {
|
|
951
|
+
const parsed = parseSemverCore(version);
|
|
952
|
+
if (!parsed) return;
|
|
953
|
+
const { major, minor, patch } = parsed;
|
|
954
|
+
if (mode === "patch") return `${major}.${minor}.${patch + 1}`;
|
|
955
|
+
if (mode === "minor") return `${major}.${minor + 1}.0`;
|
|
956
|
+
return `${major + 1}.0.0`;
|
|
957
|
+
}
|
|
958
|
+
function parseSemverCore(version) {
|
|
959
|
+
if (!version) return;
|
|
960
|
+
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
961
|
+
if (!match) return;
|
|
962
|
+
return {
|
|
963
|
+
major: Number.parseInt(match[1], 10),
|
|
964
|
+
minor: Number.parseInt(match[2], 10),
|
|
965
|
+
patch: Number.parseInt(match[3], 10)
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
function onCancel$1() {
|
|
810
969
|
cancel("Cancelled");
|
|
811
970
|
process.exitCode = 0;
|
|
812
971
|
}
|
|
@@ -1044,9 +1203,9 @@ async function resolveCiCommands(rootDir, workingDirectory, packageManager) {
|
|
|
1044
1203
|
runLint,
|
|
1045
1204
|
runFormatCheck,
|
|
1046
1205
|
runTests,
|
|
1047
|
-
lintCommand: runLint && hasLint ? pmRun(packageManager, "lint") : runLint ? await promptCommand("Lint command", pmRun(packageManager, "lint")) : void 0,
|
|
1048
|
-
formatCheckCommand: runFormatCheck && hasFormatCheck ? pmRun(packageManager, "format:check") : runFormatCheck ? await promptCommand("Format check command", pmRun(packageManager, "format:check")) : void 0,
|
|
1049
|
-
testCommand: runTests && hasTest ? pmRun(packageManager, "test") : runTests ? await promptCommand("Test command", pmRun(packageManager, "test")) : void 0
|
|
1206
|
+
lintCommand: runLint && hasLint ? pmRun$1(packageManager, "lint") : runLint ? await promptCommand("Lint command", pmRun$1(packageManager, "lint")) : void 0,
|
|
1207
|
+
formatCheckCommand: runFormatCheck && hasFormatCheck ? pmRun$1(packageManager, "format:check") : runFormatCheck ? await promptCommand("Format check command", pmRun$1(packageManager, "format:check")) : void 0,
|
|
1208
|
+
testCommand: runTests && hasTest ? pmRun$1(packageManager, "test") : runTests ? await promptCommand("Test command", pmRun$1(packageManager, "test")) : void 0
|
|
1050
1209
|
};
|
|
1051
1210
|
}
|
|
1052
1211
|
async function promptCommand(message, initialValue) {
|
|
@@ -1058,7 +1217,7 @@ async function promptCommand(message, initialValue) {
|
|
|
1058
1217
|
if (isCancel(value)) return abort$2();
|
|
1059
1218
|
return String(value).trim();
|
|
1060
1219
|
}
|
|
1061
|
-
function pmRun(pm, script) {
|
|
1220
|
+
function pmRun$1(pm, script) {
|
|
1062
1221
|
switch (pm) {
|
|
1063
1222
|
case "npm": return `npm run ${script}`;
|
|
1064
1223
|
case "pnpm": return `pnpm run ${script}`;
|
|
@@ -1592,4 +1751,193 @@ function abort(opts = {}) {
|
|
|
1592
1751
|
}
|
|
1593
1752
|
|
|
1594
1753
|
//#endregion
|
|
1595
|
-
|
|
1754
|
+
//#region src/commands/package.ts
|
|
1755
|
+
async function runPackage({ yes = false } = {}) {
|
|
1756
|
+
intro("frontpl (pkg)");
|
|
1757
|
+
const cwd = process.cwd();
|
|
1758
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
1759
|
+
const pkg = await readPackageJson(packageJsonPath);
|
|
1760
|
+
if (!pkg) {
|
|
1761
|
+
cancel("Missing package.json. Run this command in a Node package directory.");
|
|
1762
|
+
process.exitCode = 1;
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const gitRoot = runGitCapture(cwd, ["rev-parse", "--show-toplevel"]);
|
|
1766
|
+
if (!gitRoot) {
|
|
1767
|
+
cancel("Current directory is not inside a git repository.");
|
|
1768
|
+
process.exitCode = 1;
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
const originUrl = runGitCapture(cwd, [
|
|
1772
|
+
"config",
|
|
1773
|
+
"--get",
|
|
1774
|
+
"remote.origin.url"
|
|
1775
|
+
]);
|
|
1776
|
+
if (!originUrl) {
|
|
1777
|
+
cancel("Missing git remote `origin`. Set a GitHub remote before normalizing package.json.");
|
|
1778
|
+
process.exitCode = 1;
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
const repository = parseGithubRepository(originUrl);
|
|
1782
|
+
if (!repository) {
|
|
1783
|
+
cancel("Only github.com remotes are supported by `frontpl pkg`.");
|
|
1784
|
+
process.exitCode = 1;
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
const shouldApply = yes || await confirm({
|
|
1788
|
+
message: "Normalize package.json for npm publish defaults?",
|
|
1789
|
+
initialValue: true
|
|
1790
|
+
});
|
|
1791
|
+
if (isCancel(shouldApply)) return onCancel();
|
|
1792
|
+
if (!shouldApply) {
|
|
1793
|
+
cancel("Skipped package.json normalization");
|
|
1794
|
+
process.exitCode = 0;
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
const packageManager = await resolvePackageManager(pkg.packageManager, gitRoot, cwd);
|
|
1798
|
+
const selectedLicense = await resolveLicenseSelection(pkg, yes);
|
|
1799
|
+
if (isCancel(selectedLicense)) return onCancel();
|
|
1800
|
+
const repositoryDirectory = resolveRepositoryDirectory(gitRoot, cwd);
|
|
1801
|
+
normalizePackageJson(pkg, {
|
|
1802
|
+
repository,
|
|
1803
|
+
repositoryDirectory,
|
|
1804
|
+
packageManager,
|
|
1805
|
+
selectedLicense: selectedLicense ?? void 0
|
|
1806
|
+
});
|
|
1807
|
+
await writePackageJson(packageJsonPath, pkg);
|
|
1808
|
+
outro([
|
|
1809
|
+
"Done. Normalized package.json for publishing.",
|
|
1810
|
+
`- repository: ${repository.owner}/${repository.repo}`,
|
|
1811
|
+
`- packageManager: ${packageManager}`,
|
|
1812
|
+
`- repositoryDirectory: ${repositoryDirectory ?? "(root)"}`
|
|
1813
|
+
].join("\n"));
|
|
1814
|
+
}
|
|
1815
|
+
function normalizePackageJson(pkg, opts) {
|
|
1816
|
+
const { repository, repositoryDirectory, packageManager, selectedLicense } = opts;
|
|
1817
|
+
pkg.private = false;
|
|
1818
|
+
if (!pkg.version) pkg.version = "0.0.0";
|
|
1819
|
+
pkg.license = selectedLicense ?? pkg.license ?? "MIT";
|
|
1820
|
+
if (!pkg.type) pkg.type = "module";
|
|
1821
|
+
if (!pkg.files || pkg.files.length === 0) pkg.files = ["dist"];
|
|
1822
|
+
if (!pkg.main) pkg.main = "./dist/index.mjs";
|
|
1823
|
+
if (!pkg.types) pkg.types = "./dist/index.d.mts";
|
|
1824
|
+
if (!pkg.exports) pkg.exports = { ".": {
|
|
1825
|
+
types: pkg.types,
|
|
1826
|
+
import: pkg.main,
|
|
1827
|
+
require: pkg.main
|
|
1828
|
+
} };
|
|
1829
|
+
const repoUrl = `git+https://github.com/${repository.owner}/${repository.repo}.git`;
|
|
1830
|
+
pkg.homepage = `https://github.com/${repository.owner}/${repository.repo}#readme`;
|
|
1831
|
+
pkg.bugs = { url: `https://github.com/${repository.owner}/${repository.repo}/issues` };
|
|
1832
|
+
pkg.repository = {
|
|
1833
|
+
type: "git",
|
|
1834
|
+
url: repoUrl,
|
|
1835
|
+
...repositoryDirectory ? { directory: repositoryDirectory } : {}
|
|
1836
|
+
};
|
|
1837
|
+
const publishConfig = pkg.publishConfig && typeof pkg.publishConfig === "object" ? { ...pkg.publishConfig } : {};
|
|
1838
|
+
if (!publishConfig.access) publishConfig.access = "public";
|
|
1839
|
+
pkg.publishConfig = publishConfig;
|
|
1840
|
+
const engines = pkg.engines ? { ...pkg.engines } : {};
|
|
1841
|
+
if (!engines.node) engines.node = ">=22.0.0";
|
|
1842
|
+
pkg.engines = engines;
|
|
1843
|
+
if (pkg.scripts && typeof pkg.scripts.build === "string" && !pkg.scripts.prepublishOnly) pkg.scripts = {
|
|
1844
|
+
...pkg.scripts,
|
|
1845
|
+
prepublishOnly: pmRun(packageManager, "build")
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
async function resolveLicenseSelection(pkg, yes) {
|
|
1849
|
+
if (yes) return pkg.license ? void 0 : "MIT";
|
|
1850
|
+
const currentLicense = pkg.license?.trim();
|
|
1851
|
+
const selectedLicense = await select({
|
|
1852
|
+
message: currentLicense ? `Select license for publish metadata (current: ${currentLicense})` : "package.json missing `license`. Select one",
|
|
1853
|
+
initialValue: currentLicense === "Apache-2.0" ? "Apache-2.0" : "MIT",
|
|
1854
|
+
options: [{
|
|
1855
|
+
value: "MIT",
|
|
1856
|
+
label: "MIT",
|
|
1857
|
+
hint: "GitHub API key: mit"
|
|
1858
|
+
}, {
|
|
1859
|
+
value: "Apache-2.0",
|
|
1860
|
+
label: "Apache-2.0",
|
|
1861
|
+
hint: "GitHub API key: apache-2.0"
|
|
1862
|
+
}]
|
|
1863
|
+
});
|
|
1864
|
+
if (isCancel(selectedLicense)) return selectedLicense;
|
|
1865
|
+
return selectedLicense;
|
|
1866
|
+
}
|
|
1867
|
+
async function resolvePackageManager(packageManagerField, gitRoot, cwd) {
|
|
1868
|
+
const fromField = packageManagerField?.split("@")[0];
|
|
1869
|
+
if (isPackageManager(fromField)) return fromField;
|
|
1870
|
+
const fromGitRoot = await detectPackageManager(gitRoot);
|
|
1871
|
+
if (fromGitRoot) return fromGitRoot;
|
|
1872
|
+
const fromCwd = await detectPackageManager(cwd);
|
|
1873
|
+
if (fromCwd) return fromCwd;
|
|
1874
|
+
return "pnpm";
|
|
1875
|
+
}
|
|
1876
|
+
function isPackageManager(value) {
|
|
1877
|
+
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun" || value === "deno";
|
|
1878
|
+
}
|
|
1879
|
+
function resolveRepositoryDirectory(gitRoot, cwd) {
|
|
1880
|
+
const relativePath = path.relative(gitRoot, cwd);
|
|
1881
|
+
if (!relativePath || relativePath === ".") return;
|
|
1882
|
+
if (relativePath.startsWith("..")) return;
|
|
1883
|
+
return relativePath.split(path.sep).join("/");
|
|
1884
|
+
}
|
|
1885
|
+
function parseGithubRepository(remoteUrl) {
|
|
1886
|
+
const trimmed = remoteUrl.trim();
|
|
1887
|
+
const sshMatch = trimmed.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1888
|
+
if (sshMatch) return {
|
|
1889
|
+
owner: sshMatch[1],
|
|
1890
|
+
repo: sshMatch[2]
|
|
1891
|
+
};
|
|
1892
|
+
const sshProtocolMatch = trimmed.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1893
|
+
if (sshProtocolMatch) return {
|
|
1894
|
+
owner: sshProtocolMatch[1],
|
|
1895
|
+
repo: sshProtocolMatch[2]
|
|
1896
|
+
};
|
|
1897
|
+
const gitProtocolMatch = trimmed.match(/^git:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1898
|
+
if (gitProtocolMatch) return {
|
|
1899
|
+
owner: gitProtocolMatch[1],
|
|
1900
|
+
repo: gitProtocolMatch[2]
|
|
1901
|
+
};
|
|
1902
|
+
try {
|
|
1903
|
+
const url = new URL(trimmed);
|
|
1904
|
+
if (url.hostname !== "github.com") return;
|
|
1905
|
+
const [owner, repo] = url.pathname.replace(/^\/|\/$/g, "").split("/");
|
|
1906
|
+
if (!owner || !repo) return;
|
|
1907
|
+
return {
|
|
1908
|
+
owner,
|
|
1909
|
+
repo: repo.replace(/\.git$/, "")
|
|
1910
|
+
};
|
|
1911
|
+
} catch {
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
function runGitCapture(cwd, args) {
|
|
1916
|
+
const result = spawnSync("git", args, {
|
|
1917
|
+
cwd,
|
|
1918
|
+
encoding: "utf8",
|
|
1919
|
+
stdio: [
|
|
1920
|
+
"ignore",
|
|
1921
|
+
"pipe",
|
|
1922
|
+
"ignore"
|
|
1923
|
+
]
|
|
1924
|
+
});
|
|
1925
|
+
if (result.status !== 0) return;
|
|
1926
|
+
return result.stdout.trim() || void 0;
|
|
1927
|
+
}
|
|
1928
|
+
function pmRun(pm, script) {
|
|
1929
|
+
switch (pm) {
|
|
1930
|
+
case "npm": return `npm run ${script}`;
|
|
1931
|
+
case "pnpm": return `pnpm run ${script}`;
|
|
1932
|
+
case "yarn": return `yarn ${script}`;
|
|
1933
|
+
case "bun": return `bun run ${script}`;
|
|
1934
|
+
case "deno": return script;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
function onCancel() {
|
|
1938
|
+
cancel("Cancelled");
|
|
1939
|
+
process.exitCode = 0;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
//#endregion
|
|
1943
|
+
export { runBump as a, validateProjectName as c, oxlintConfigTemplate as d, packageJsonTemplate as f, runCi as i, githubCliCiWorkflowTemplate as l, runOxfmt as n, runAdd as o, workspaceRootPackageJsonTemplate as p, runOxlint as r, runInit as s, runPackage as t, githubDependabotTemplate as u };
|