frontpl 0.5.0 → 0.6.0
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 +46 -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-D5wcPeH8.mjs} +368 -29
- 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).
|
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-D5wcPeH8.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-D5wcPeH8.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
|
|
@@ -427,7 +427,7 @@ async function detectPackageManager(rootDir) {
|
|
|
427
427
|
const pmField = (await readPackageJson(path.join(rootDir, "package.json")))?.packageManager;
|
|
428
428
|
if (pmField) {
|
|
429
429
|
const pm = pmField.split("@")[0] ?? "";
|
|
430
|
-
if (isPackageManager(pm)) return pm;
|
|
430
|
+
if (isPackageManager$1(pm)) return pm;
|
|
431
431
|
}
|
|
432
432
|
const candidates = [];
|
|
433
433
|
if (await pathExists(path.join(rootDir, "pnpm-lock.yaml"))) candidates.push("pnpm");
|
|
@@ -438,7 +438,7 @@ async function detectPackageManager(rootDir) {
|
|
|
438
438
|
if (await pathExists(path.join(rootDir, "deno.json")) || await pathExists(path.join(rootDir, "deno.jsonc"))) candidates.push("deno");
|
|
439
439
|
return candidates.length === 1 ? candidates[0] : void 0;
|
|
440
440
|
}
|
|
441
|
-
function isPackageManager(value) {
|
|
441
|
+
function isPackageManager$1(value) {
|
|
442
442
|
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun" || value === "deno";
|
|
443
443
|
}
|
|
444
444
|
|
|
@@ -467,7 +467,7 @@ function resolveCommand(command) {
|
|
|
467
467
|
|
|
468
468
|
//#endregion
|
|
469
469
|
//#region src/commands/init.ts
|
|
470
|
-
function pmRun$
|
|
470
|
+
function pmRun$2(pm, script) {
|
|
471
471
|
switch (pm) {
|
|
472
472
|
case "npm": return `npm run ${script}`;
|
|
473
473
|
case "pnpm": return `pnpm run ${script}`;
|
|
@@ -483,7 +483,7 @@ async function runInit({ nameArg }) {
|
|
|
483
483
|
initialValue: nameArg ?? "my-frontend",
|
|
484
484
|
validate: validateProjectName
|
|
485
485
|
});
|
|
486
|
-
if (isCancel(projectName)) return onCancel$
|
|
486
|
+
if (isCancel(projectName)) return onCancel$3();
|
|
487
487
|
const packageManager = await select({
|
|
488
488
|
message: "Package manager",
|
|
489
489
|
initialValue: "pnpm",
|
|
@@ -510,37 +510,37 @@ async function runInit({ nameArg }) {
|
|
|
510
510
|
}
|
|
511
511
|
]
|
|
512
512
|
});
|
|
513
|
-
if (isCancel(packageManager)) return onCancel$
|
|
513
|
+
if (isCancel(packageManager)) return onCancel$3();
|
|
514
514
|
const pnpmWorkspace = packageManager === "pnpm" ? await confirm({
|
|
515
515
|
message: "pnpm workspace mode (monorepo skeleton)?",
|
|
516
516
|
initialValue: false
|
|
517
517
|
}) : false;
|
|
518
|
-
if (isCancel(pnpmWorkspace)) return onCancel$
|
|
518
|
+
if (isCancel(pnpmWorkspace)) return onCancel$3();
|
|
519
519
|
const useOxlint = await confirm({
|
|
520
520
|
message: "Enable oxlint (@kingsword/lint-config preset)?",
|
|
521
521
|
initialValue: true
|
|
522
522
|
});
|
|
523
|
-
if (isCancel(useOxlint)) return onCancel$
|
|
523
|
+
if (isCancel(useOxlint)) return onCancel$3();
|
|
524
524
|
const useOxfmt = await confirm({
|
|
525
525
|
message: "Enable oxfmt (code formatting)?",
|
|
526
526
|
initialValue: true
|
|
527
527
|
});
|
|
528
|
-
if (isCancel(useOxfmt)) return onCancel$
|
|
528
|
+
if (isCancel(useOxfmt)) return onCancel$3();
|
|
529
529
|
const useVitest = await confirm({
|
|
530
530
|
message: "Add Vitest?",
|
|
531
531
|
initialValue: false
|
|
532
532
|
});
|
|
533
|
-
if (isCancel(useVitest)) return onCancel$
|
|
533
|
+
if (isCancel(useVitest)) return onCancel$3();
|
|
534
534
|
const useTsdown = await confirm({
|
|
535
535
|
message: "Add tsdown build?",
|
|
536
536
|
initialValue: true
|
|
537
537
|
});
|
|
538
|
-
if (isCancel(useTsdown)) return onCancel$
|
|
538
|
+
if (isCancel(useTsdown)) return onCancel$3();
|
|
539
539
|
const initGit = await confirm({
|
|
540
540
|
message: "Initialize a git repository?",
|
|
541
541
|
initialValue: true
|
|
542
542
|
});
|
|
543
|
-
if (isCancel(initGit)) return onCancel$
|
|
543
|
+
if (isCancel(initGit)) return onCancel$3();
|
|
544
544
|
const githubActions = await select({
|
|
545
545
|
message: "GitHub Actions workflows",
|
|
546
546
|
initialValue: "ci",
|
|
@@ -559,7 +559,7 @@ async function runInit({ nameArg }) {
|
|
|
559
559
|
}
|
|
560
560
|
]
|
|
561
561
|
});
|
|
562
|
-
if (isCancel(githubActions)) return onCancel$
|
|
562
|
+
if (isCancel(githubActions)) return onCancel$3();
|
|
563
563
|
const releaseMode = githubActions === "ci+release" ? await select({
|
|
564
564
|
message: "Release workflows",
|
|
565
565
|
initialValue: "tag",
|
|
@@ -578,17 +578,17 @@ async function runInit({ nameArg }) {
|
|
|
578
578
|
}
|
|
579
579
|
]
|
|
580
580
|
}) : void 0;
|
|
581
|
-
if (isCancel(releaseMode)) return onCancel$
|
|
581
|
+
if (isCancel(releaseMode)) return onCancel$3();
|
|
582
582
|
const addDependabot = initGit && githubActions !== "none" ? await confirm({
|
|
583
583
|
message: "Add Dependabot config (.github/dependabot.yml)?",
|
|
584
584
|
initialValue: true
|
|
585
585
|
}) : false;
|
|
586
|
-
if (isCancel(addDependabot)) return onCancel$
|
|
586
|
+
if (isCancel(addDependabot)) return onCancel$3();
|
|
587
587
|
const trustedPublishing = githubActions === "ci+release" && packageManager !== "deno" ? await confirm({
|
|
588
588
|
message: "Release: npm trusted publishing (OIDC)?",
|
|
589
589
|
initialValue: true
|
|
590
590
|
}) : void 0;
|
|
591
|
-
if (isCancel(trustedPublishing)) return onCancel$
|
|
591
|
+
if (isCancel(trustedPublishing)) return onCancel$3();
|
|
592
592
|
const rootDir = path.resolve(process.cwd(), projectName);
|
|
593
593
|
if (await pathExists(rootDir)) {
|
|
594
594
|
cancel(`Directory already exists: ${rootDir}`);
|
|
@@ -655,9 +655,9 @@ async function runInit({ nameArg }) {
|
|
|
655
655
|
if (packageManager === "deno") await writeText(path.join(rootDir, "deno.json"), JSON.stringify({ nodeModulesDir: "auto" }, null, 2) + "\n");
|
|
656
656
|
if (githubActions !== "none") {
|
|
657
657
|
const workingDirectory = ".";
|
|
658
|
-
const lintCommand = useOxlint && packageManager !== "deno" ? pmRun$
|
|
659
|
-
const formatCheckCommand = useOxfmt && packageManager !== "deno" ? pmRun$
|
|
660
|
-
const testCommand = useVitest && packageManager !== "deno" ? pmRun$
|
|
658
|
+
const lintCommand = useOxlint && packageManager !== "deno" ? pmRun$2(packageManager, "lint") : void 0;
|
|
659
|
+
const formatCheckCommand = useOxfmt && packageManager !== "deno" ? pmRun$2(packageManager, "format:check") : void 0;
|
|
660
|
+
const testCommand = useVitest && packageManager !== "deno" ? pmRun$2(packageManager, "test") : void 0;
|
|
661
661
|
await writeText(path.join(rootDir, ".github/workflows/ci.yml"), githubCliCiWorkflowTemplate({
|
|
662
662
|
packageManager,
|
|
663
663
|
nodeVersion: 22,
|
|
@@ -702,7 +702,7 @@ function validateProjectName(value) {
|
|
|
702
702
|
if (name.startsWith("_")) return "Project name cannot start with '_'";
|
|
703
703
|
if (!/^[A-Za-z0-9._-]+$/.test(name)) return "Use letters, numbers, '.', '_' or '-'";
|
|
704
704
|
}
|
|
705
|
-
function onCancel$
|
|
705
|
+
function onCancel$3() {
|
|
706
706
|
cancel("Cancelled");
|
|
707
707
|
process.exitCode = 0;
|
|
708
708
|
}
|
|
@@ -740,7 +740,7 @@ async function runAdd({ nameArg, yes = false } = {}) {
|
|
|
740
740
|
validate: validateProjectName
|
|
741
741
|
});
|
|
742
742
|
if (!packageName) return;
|
|
743
|
-
if (isCancel(packageName)) return onCancel();
|
|
743
|
+
if (isCancel(packageName)) return onCancel$2();
|
|
744
744
|
const packageDir = path.join(rootDir, "packages", packageName);
|
|
745
745
|
if (await pathExists(packageDir)) {
|
|
746
746
|
cancel(`Package already exists: ${packageDir}`);
|
|
@@ -753,12 +753,12 @@ async function runAdd({ nameArg, yes = false } = {}) {
|
|
|
753
753
|
message: "Add Vitest?",
|
|
754
754
|
initialValue: useVitestDefault
|
|
755
755
|
});
|
|
756
|
-
if (isCancel(useVitest)) return onCancel();
|
|
756
|
+
if (isCancel(useVitest)) return onCancel$2();
|
|
757
757
|
const useTsdown = yes ? useTsdownDefault : await confirm({
|
|
758
758
|
message: "Add tsdown build?",
|
|
759
759
|
initialValue: useTsdownDefault
|
|
760
760
|
});
|
|
761
|
-
if (isCancel(useTsdown)) return onCancel();
|
|
761
|
+
if (isCancel(useTsdown)) return onCancel$2();
|
|
762
762
|
const resolvedPackageManagerField = await resolvePnpmPackageManagerField(packageManagerField);
|
|
763
763
|
const rootHasOxlint = Boolean(rootPkg?.devDependencies?.oxlint) || Boolean(rootPkg?.devDependencies?.["oxlint-tsgolint"]) || await pathExists(path.join(rootDir, "oxlint.config.ts"));
|
|
764
764
|
const typescriptVersion = rootPkg?.devDependencies?.typescript ?? "latest";
|
|
@@ -806,7 +806,157 @@ function resolvePackageNameFromArg(nameArg) {
|
|
|
806
806
|
}
|
|
807
807
|
return value;
|
|
808
808
|
}
|
|
809
|
-
function onCancel() {
|
|
809
|
+
function onCancel$2() {
|
|
810
|
+
cancel("Cancelled");
|
|
811
|
+
process.exitCode = 0;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
//#endregion
|
|
815
|
+
//#region src/commands/bump.ts
|
|
816
|
+
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
817
|
+
async function runBump({ targetArg, dryRun = false } = {}) {
|
|
818
|
+
intro("frontpl (bump)");
|
|
819
|
+
const cwd = process.cwd();
|
|
820
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
821
|
+
const pkg = await readPackageJson(packageJsonPath);
|
|
822
|
+
if (!pkg) {
|
|
823
|
+
cancel("Missing package.json. Run this command in a Node package directory.");
|
|
824
|
+
process.exitCode = 1;
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
const currentVersion = pkg.version?.trim();
|
|
828
|
+
const resolved = targetArg ? resolveVersionFromTargetArg({
|
|
829
|
+
currentVersion,
|
|
830
|
+
targetArg
|
|
831
|
+
}) : await resolveVersionInteractively({ currentVersion });
|
|
832
|
+
if (isCancel(resolved)) return onCancel$1();
|
|
833
|
+
if (!resolved.ok) {
|
|
834
|
+
cancel(resolved.reason);
|
|
835
|
+
process.exitCode = 1;
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
if (!dryRun) {
|
|
839
|
+
pkg.version = resolved.nextVersion;
|
|
840
|
+
await writePackageJson(packageJsonPath, pkg);
|
|
841
|
+
}
|
|
842
|
+
outro([
|
|
843
|
+
dryRun ? "Dry run. Calculated package.json version update." : "Done. Updated package.json version.",
|
|
844
|
+
`- from: ${currentVersion ?? "(missing)"}`,
|
|
845
|
+
`- to: ${resolved.nextVersion}`,
|
|
846
|
+
`- mode: ${resolved.mode}`
|
|
847
|
+
].join("\n"));
|
|
848
|
+
}
|
|
849
|
+
function resolveVersionFromTargetArg(opts) {
|
|
850
|
+
const target = opts.targetArg.trim();
|
|
851
|
+
if (!target) return {
|
|
852
|
+
ok: false,
|
|
853
|
+
reason: "Target is required (patch | minor | major | <version>)"
|
|
854
|
+
};
|
|
855
|
+
if (target === "patch" || target === "minor" || target === "major") {
|
|
856
|
+
const nextVersion = bumpSemver(opts.currentVersion, target);
|
|
857
|
+
if (!nextVersion) return {
|
|
858
|
+
ok: false,
|
|
859
|
+
reason: "Current version is missing or invalid for bump mode. Set an explicit version (e.g. `frontpl bump 1.2.3`)."
|
|
860
|
+
};
|
|
861
|
+
return {
|
|
862
|
+
ok: true,
|
|
863
|
+
mode: target,
|
|
864
|
+
nextVersion
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
if (!SEMVER_PATTERN.test(target)) return {
|
|
868
|
+
ok: false,
|
|
869
|
+
reason: "Invalid version. Use semver like 1.2.3 (or prerelease/build variants)."
|
|
870
|
+
};
|
|
871
|
+
return {
|
|
872
|
+
ok: true,
|
|
873
|
+
mode: "set",
|
|
874
|
+
nextVersion: target
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
async function resolveVersionInteractively(opts) {
|
|
878
|
+
const currentVersion = opts.currentVersion;
|
|
879
|
+
const patchVersion = bumpSemver(currentVersion, "patch");
|
|
880
|
+
const minorVersion = bumpSemver(currentVersion, "minor");
|
|
881
|
+
const majorVersion = bumpSemver(currentVersion, "major");
|
|
882
|
+
if (!patchVersion || !minorVersion || !majorVersion) {
|
|
883
|
+
const customVersion = await text({
|
|
884
|
+
message: "Current version is missing/invalid. Enter target version",
|
|
885
|
+
initialValue: "0.1.0",
|
|
886
|
+
validate: (value = "") => SEMVER_PATTERN.test(value.trim()) ? void 0 : "Use semver like 1.2.3 (or prerelease/build variants)"
|
|
887
|
+
});
|
|
888
|
+
if (isCancel(customVersion)) return customVersion;
|
|
889
|
+
return {
|
|
890
|
+
ok: true,
|
|
891
|
+
mode: "set",
|
|
892
|
+
nextVersion: String(customVersion).trim()
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
const choice = await select({
|
|
896
|
+
message: `Select next version (current: ${currentVersion})`,
|
|
897
|
+
initialValue: "patch",
|
|
898
|
+
options: [
|
|
899
|
+
{
|
|
900
|
+
value: "patch",
|
|
901
|
+
label: patchVersion,
|
|
902
|
+
hint: "patch"
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
value: "minor",
|
|
906
|
+
label: minorVersion,
|
|
907
|
+
hint: "minor"
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
value: "major",
|
|
911
|
+
label: majorVersion,
|
|
912
|
+
hint: "major"
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
value: "custom",
|
|
916
|
+
label: "custom",
|
|
917
|
+
hint: "set explicit version"
|
|
918
|
+
}
|
|
919
|
+
]
|
|
920
|
+
});
|
|
921
|
+
if (isCancel(choice)) return choice;
|
|
922
|
+
if (choice === "custom") {
|
|
923
|
+
const customVersion = await text({
|
|
924
|
+
message: "Target version",
|
|
925
|
+
initialValue: currentVersion,
|
|
926
|
+
validate: (value = "") => SEMVER_PATTERN.test(value.trim()) ? void 0 : "Use semver like 1.2.3 (or prerelease/build variants)"
|
|
927
|
+
});
|
|
928
|
+
if (isCancel(customVersion)) return customVersion;
|
|
929
|
+
return {
|
|
930
|
+
ok: true,
|
|
931
|
+
mode: "set",
|
|
932
|
+
nextVersion: String(customVersion).trim()
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
return {
|
|
936
|
+
ok: true,
|
|
937
|
+
mode: choice,
|
|
938
|
+
nextVersion: choice === "patch" ? patchVersion : choice === "minor" ? minorVersion : majorVersion
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function bumpSemver(version, mode) {
|
|
942
|
+
const parsed = parseSemverCore(version);
|
|
943
|
+
if (!parsed) return;
|
|
944
|
+
const { major, minor, patch } = parsed;
|
|
945
|
+
if (mode === "patch") return `${major}.${minor}.${patch + 1}`;
|
|
946
|
+
if (mode === "minor") return `${major}.${minor + 1}.0`;
|
|
947
|
+
return `${major + 1}.0.0`;
|
|
948
|
+
}
|
|
949
|
+
function parseSemverCore(version) {
|
|
950
|
+
if (!version) return;
|
|
951
|
+
const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
952
|
+
if (!match) return;
|
|
953
|
+
return {
|
|
954
|
+
major: Number.parseInt(match[1], 10),
|
|
955
|
+
minor: Number.parseInt(match[2], 10),
|
|
956
|
+
patch: Number.parseInt(match[3], 10)
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function onCancel$1() {
|
|
810
960
|
cancel("Cancelled");
|
|
811
961
|
process.exitCode = 0;
|
|
812
962
|
}
|
|
@@ -1044,9 +1194,9 @@ async function resolveCiCommands(rootDir, workingDirectory, packageManager) {
|
|
|
1044
1194
|
runLint,
|
|
1045
1195
|
runFormatCheck,
|
|
1046
1196
|
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
|
|
1197
|
+
lintCommand: runLint && hasLint ? pmRun$1(packageManager, "lint") : runLint ? await promptCommand("Lint command", pmRun$1(packageManager, "lint")) : void 0,
|
|
1198
|
+
formatCheckCommand: runFormatCheck && hasFormatCheck ? pmRun$1(packageManager, "format:check") : runFormatCheck ? await promptCommand("Format check command", pmRun$1(packageManager, "format:check")) : void 0,
|
|
1199
|
+
testCommand: runTests && hasTest ? pmRun$1(packageManager, "test") : runTests ? await promptCommand("Test command", pmRun$1(packageManager, "test")) : void 0
|
|
1050
1200
|
};
|
|
1051
1201
|
}
|
|
1052
1202
|
async function promptCommand(message, initialValue) {
|
|
@@ -1058,7 +1208,7 @@ async function promptCommand(message, initialValue) {
|
|
|
1058
1208
|
if (isCancel(value)) return abort$2();
|
|
1059
1209
|
return String(value).trim();
|
|
1060
1210
|
}
|
|
1061
|
-
function pmRun(pm, script) {
|
|
1211
|
+
function pmRun$1(pm, script) {
|
|
1062
1212
|
switch (pm) {
|
|
1063
1213
|
case "npm": return `npm run ${script}`;
|
|
1064
1214
|
case "pnpm": return `pnpm run ${script}`;
|
|
@@ -1592,4 +1742,193 @@ function abort(opts = {}) {
|
|
|
1592
1742
|
}
|
|
1593
1743
|
|
|
1594
1744
|
//#endregion
|
|
1595
|
-
|
|
1745
|
+
//#region src/commands/package.ts
|
|
1746
|
+
async function runPackage({ yes = false } = {}) {
|
|
1747
|
+
intro("frontpl (pkg)");
|
|
1748
|
+
const cwd = process.cwd();
|
|
1749
|
+
const packageJsonPath = path.join(cwd, "package.json");
|
|
1750
|
+
const pkg = await readPackageJson(packageJsonPath);
|
|
1751
|
+
if (!pkg) {
|
|
1752
|
+
cancel("Missing package.json. Run this command in a Node package directory.");
|
|
1753
|
+
process.exitCode = 1;
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
const gitRoot = runGitCapture(cwd, ["rev-parse", "--show-toplevel"]);
|
|
1757
|
+
if (!gitRoot) {
|
|
1758
|
+
cancel("Current directory is not inside a git repository.");
|
|
1759
|
+
process.exitCode = 1;
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
const originUrl = runGitCapture(cwd, [
|
|
1763
|
+
"config",
|
|
1764
|
+
"--get",
|
|
1765
|
+
"remote.origin.url"
|
|
1766
|
+
]);
|
|
1767
|
+
if (!originUrl) {
|
|
1768
|
+
cancel("Missing git remote `origin`. Set a GitHub remote before normalizing package.json.");
|
|
1769
|
+
process.exitCode = 1;
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
const repository = parseGithubRepository(originUrl);
|
|
1773
|
+
if (!repository) {
|
|
1774
|
+
cancel("Only github.com remotes are supported by `frontpl pkg`.");
|
|
1775
|
+
process.exitCode = 1;
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
const shouldApply = yes || await confirm({
|
|
1779
|
+
message: "Normalize package.json for npm publish defaults?",
|
|
1780
|
+
initialValue: true
|
|
1781
|
+
});
|
|
1782
|
+
if (isCancel(shouldApply)) return onCancel();
|
|
1783
|
+
if (!shouldApply) {
|
|
1784
|
+
cancel("Skipped package.json normalization");
|
|
1785
|
+
process.exitCode = 0;
|
|
1786
|
+
return;
|
|
1787
|
+
}
|
|
1788
|
+
const packageManager = await resolvePackageManager(pkg.packageManager, gitRoot, cwd);
|
|
1789
|
+
const selectedLicense = await resolveLicenseSelection(pkg, yes);
|
|
1790
|
+
if (isCancel(selectedLicense)) return onCancel();
|
|
1791
|
+
const repositoryDirectory = resolveRepositoryDirectory(gitRoot, cwd);
|
|
1792
|
+
normalizePackageJson(pkg, {
|
|
1793
|
+
repository,
|
|
1794
|
+
repositoryDirectory,
|
|
1795
|
+
packageManager,
|
|
1796
|
+
selectedLicense: selectedLicense ?? void 0
|
|
1797
|
+
});
|
|
1798
|
+
await writePackageJson(packageJsonPath, pkg);
|
|
1799
|
+
outro([
|
|
1800
|
+
"Done. Normalized package.json for publishing.",
|
|
1801
|
+
`- repository: ${repository.owner}/${repository.repo}`,
|
|
1802
|
+
`- packageManager: ${packageManager}`,
|
|
1803
|
+
`- repositoryDirectory: ${repositoryDirectory ?? "(root)"}`
|
|
1804
|
+
].join("\n"));
|
|
1805
|
+
}
|
|
1806
|
+
function normalizePackageJson(pkg, opts) {
|
|
1807
|
+
const { repository, repositoryDirectory, packageManager, selectedLicense } = opts;
|
|
1808
|
+
pkg.private = false;
|
|
1809
|
+
if (!pkg.version) pkg.version = "0.0.0";
|
|
1810
|
+
pkg.license = selectedLicense ?? pkg.license ?? "MIT";
|
|
1811
|
+
if (!pkg.type) pkg.type = "module";
|
|
1812
|
+
if (!pkg.files || pkg.files.length === 0) pkg.files = ["dist"];
|
|
1813
|
+
if (!pkg.main) pkg.main = "./dist/index.mjs";
|
|
1814
|
+
if (!pkg.types) pkg.types = "./dist/index.d.mts";
|
|
1815
|
+
if (!pkg.exports) pkg.exports = { ".": {
|
|
1816
|
+
types: pkg.types,
|
|
1817
|
+
import: pkg.main,
|
|
1818
|
+
require: pkg.main
|
|
1819
|
+
} };
|
|
1820
|
+
const repoUrl = `git+https://github.com/${repository.owner}/${repository.repo}.git`;
|
|
1821
|
+
pkg.homepage = `https://github.com/${repository.owner}/${repository.repo}#readme`;
|
|
1822
|
+
pkg.bugs = { url: `https://github.com/${repository.owner}/${repository.repo}/issues` };
|
|
1823
|
+
pkg.repository = {
|
|
1824
|
+
type: "git",
|
|
1825
|
+
url: repoUrl,
|
|
1826
|
+
...repositoryDirectory ? { directory: repositoryDirectory } : {}
|
|
1827
|
+
};
|
|
1828
|
+
const publishConfig = pkg.publishConfig && typeof pkg.publishConfig === "object" ? { ...pkg.publishConfig } : {};
|
|
1829
|
+
if (!publishConfig.access) publishConfig.access = "public";
|
|
1830
|
+
pkg.publishConfig = publishConfig;
|
|
1831
|
+
const engines = pkg.engines ? { ...pkg.engines } : {};
|
|
1832
|
+
if (!engines.node) engines.node = ">=22.0.0";
|
|
1833
|
+
pkg.engines = engines;
|
|
1834
|
+
if (pkg.scripts && typeof pkg.scripts.build === "string" && !pkg.scripts.prepublishOnly) pkg.scripts = {
|
|
1835
|
+
...pkg.scripts,
|
|
1836
|
+
prepublishOnly: pmRun(packageManager, "build")
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
async function resolveLicenseSelection(pkg, yes) {
|
|
1840
|
+
if (yes) return pkg.license ? void 0 : "MIT";
|
|
1841
|
+
const currentLicense = pkg.license?.trim();
|
|
1842
|
+
const selectedLicense = await select({
|
|
1843
|
+
message: currentLicense ? `Select license for publish metadata (current: ${currentLicense})` : "package.json missing `license`. Select one",
|
|
1844
|
+
initialValue: currentLicense === "Apache-2.0" ? "Apache-2.0" : "MIT",
|
|
1845
|
+
options: [{
|
|
1846
|
+
value: "MIT",
|
|
1847
|
+
label: "MIT",
|
|
1848
|
+
hint: "GitHub API key: mit"
|
|
1849
|
+
}, {
|
|
1850
|
+
value: "Apache-2.0",
|
|
1851
|
+
label: "Apache-2.0",
|
|
1852
|
+
hint: "GitHub API key: apache-2.0"
|
|
1853
|
+
}]
|
|
1854
|
+
});
|
|
1855
|
+
if (isCancel(selectedLicense)) return selectedLicense;
|
|
1856
|
+
return selectedLicense;
|
|
1857
|
+
}
|
|
1858
|
+
async function resolvePackageManager(packageManagerField, gitRoot, cwd) {
|
|
1859
|
+
const fromField = packageManagerField?.split("@")[0];
|
|
1860
|
+
if (isPackageManager(fromField)) return fromField;
|
|
1861
|
+
const fromGitRoot = await detectPackageManager(gitRoot);
|
|
1862
|
+
if (fromGitRoot) return fromGitRoot;
|
|
1863
|
+
const fromCwd = await detectPackageManager(cwd);
|
|
1864
|
+
if (fromCwd) return fromCwd;
|
|
1865
|
+
return "pnpm";
|
|
1866
|
+
}
|
|
1867
|
+
function isPackageManager(value) {
|
|
1868
|
+
return value === "npm" || value === "pnpm" || value === "yarn" || value === "bun" || value === "deno";
|
|
1869
|
+
}
|
|
1870
|
+
function resolveRepositoryDirectory(gitRoot, cwd) {
|
|
1871
|
+
const relativePath = path.relative(gitRoot, cwd);
|
|
1872
|
+
if (!relativePath || relativePath === ".") return;
|
|
1873
|
+
if (relativePath.startsWith("..")) return;
|
|
1874
|
+
return relativePath.split(path.sep).join("/");
|
|
1875
|
+
}
|
|
1876
|
+
function parseGithubRepository(remoteUrl) {
|
|
1877
|
+
const trimmed = remoteUrl.trim();
|
|
1878
|
+
const sshMatch = trimmed.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1879
|
+
if (sshMatch) return {
|
|
1880
|
+
owner: sshMatch[1],
|
|
1881
|
+
repo: sshMatch[2]
|
|
1882
|
+
};
|
|
1883
|
+
const sshProtocolMatch = trimmed.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1884
|
+
if (sshProtocolMatch) return {
|
|
1885
|
+
owner: sshProtocolMatch[1],
|
|
1886
|
+
repo: sshProtocolMatch[2]
|
|
1887
|
+
};
|
|
1888
|
+
const gitProtocolMatch = trimmed.match(/^git:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
1889
|
+
if (gitProtocolMatch) return {
|
|
1890
|
+
owner: gitProtocolMatch[1],
|
|
1891
|
+
repo: gitProtocolMatch[2]
|
|
1892
|
+
};
|
|
1893
|
+
try {
|
|
1894
|
+
const url = new URL(trimmed);
|
|
1895
|
+
if (url.hostname !== "github.com") return;
|
|
1896
|
+
const [owner, repo] = url.pathname.replace(/^\/|\/$/g, "").split("/");
|
|
1897
|
+
if (!owner || !repo) return;
|
|
1898
|
+
return {
|
|
1899
|
+
owner,
|
|
1900
|
+
repo: repo.replace(/\.git$/, "")
|
|
1901
|
+
};
|
|
1902
|
+
} catch {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
function runGitCapture(cwd, args) {
|
|
1907
|
+
const result = spawnSync("git", args, {
|
|
1908
|
+
cwd,
|
|
1909
|
+
encoding: "utf8",
|
|
1910
|
+
stdio: [
|
|
1911
|
+
"ignore",
|
|
1912
|
+
"pipe",
|
|
1913
|
+
"ignore"
|
|
1914
|
+
]
|
|
1915
|
+
});
|
|
1916
|
+
if (result.status !== 0) return;
|
|
1917
|
+
return result.stdout.trim() || void 0;
|
|
1918
|
+
}
|
|
1919
|
+
function pmRun(pm, script) {
|
|
1920
|
+
switch (pm) {
|
|
1921
|
+
case "npm": return `npm run ${script}`;
|
|
1922
|
+
case "pnpm": return `pnpm run ${script}`;
|
|
1923
|
+
case "yarn": return `yarn ${script}`;
|
|
1924
|
+
case "bun": return `bun run ${script}`;
|
|
1925
|
+
case "deno": return script;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
function onCancel() {
|
|
1929
|
+
cancel("Cancelled");
|
|
1930
|
+
process.exitCode = 0;
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
//#endregion
|
|
1934
|
+
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 };
|