@rrlab/ts-plugin 0.1.1 → 0.1.2-git-2238423.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/dist/index.d.mts CHANGED
@@ -5,7 +5,6 @@ import { ShellService } from "@vlandoss/clibuddy";
5
5
  declare const TOOL_VERSIONS: {
6
6
  readonly typescript: {
7
7
  readonly install: "^6.0.0";
8
- readonly peer: ">=5.0.0";
9
8
  };
10
9
  readonly "@types/node": {
11
10
  readonly install: ">=20";
@@ -19,6 +18,8 @@ declare class TscService extends ToolService implements TypeChecker {
19
18
  }
20
19
  declare function install(ctx: InstallContext): Promise<InstallResult>;
21
20
  declare function uninstall(ctx: UninstallContext): Promise<UninstallResult>;
22
- declare const ts: (options: void) => import("@rrlab/cli/plugin").Plugin;
21
+ declare const ts: (options?: {
22
+ only?: readonly "tsc"[] | undefined;
23
+ } | undefined) => import("@rrlab/cli/plugin").Plugin;
23
24
  //#endregion
24
25
  export { TOOL_VERSIONS, TscService, ts as default, install, uninstall };
package/dist/index.mjs CHANGED
@@ -1,14 +1,11 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { ToolService, definePlugin } from "@rrlab/cli/plugin";
3
+ import { ToolService, decideScaffold, definePlugin, pickPreset } from "@rrlab/cli/plugin";
4
4
  import { colorize } from "@vlandoss/clibuddy";
5
5
  import { parse } from "comment-json";
6
6
  //#region src/tool-versions.ts
7
7
  const TOOL_VERSIONS = {
8
- typescript: {
9
- install: "^6.0.0",
10
- peer: ">=5.0.0"
11
- },
8
+ typescript: { install: "^6.0.0" },
12
9
  "@types/node": { install: ">=20" }
13
10
  };
14
11
  //#endregion
@@ -62,9 +59,17 @@ const PRESETS = {
62
59
  };
63
60
  const DEFAULT_PRESET = "no-dom-app";
64
61
  async function install(ctx) {
65
- const scaffoldDecision = await decideScaffoldAction(ctx, await pathExists(path.join(ctx.appPkg.dirPath, TSCONFIG)));
62
+ const scaffoldDecision = await decideScaffold(ctx, {
63
+ label: TSCONFIG,
64
+ fileExists: await pathExists(path.join(ctx.appPkg.dirPath, TSCONFIG)),
65
+ patchHint: "update extends, keep my other settings"
66
+ });
66
67
  if (scaffoldDecision === "skip") return { devDependencies: { typescript: TOOL_VERSIONS.typescript.install } };
67
- const presetInfo = PRESETS[await pickPreset(ctx)];
68
+ const presetInfo = PRESETS[await pickPreset(ctx, {
69
+ message: "Which kind of TS project do you need?",
70
+ presets: PRESETS,
71
+ defaultPreset: DEFAULT_PRESET
72
+ })];
68
73
  const devDependencies = {
69
74
  typescript: TOOL_VERSIONS.typescript.install,
70
75
  "@rrlab/ts-config": await ctx.release.resolve("@rrlab/ts-config")
@@ -123,51 +128,6 @@ async function uninstall(ctx) {
123
128
  files
124
129
  };
125
130
  }
126
- async function decideScaffoldAction(ctx, fileExists) {
127
- if (!fileExists) {
128
- if (ctx.flags.yes || ctx.flags.nonInteractive) return "create";
129
- const choice = await ctx.prompts.confirm({
130
- message: `Scaffold ${TSCONFIG} with an @rrlab/ts-config preset?`,
131
- initialValue: true
132
- });
133
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
134
- return choice ? "create" : "skip";
135
- }
136
- if (ctx.flags.yes || ctx.flags.nonInteractive) return "patch";
137
- const choice = await ctx.prompts.select({
138
- message: `${TSCONFIG} already exists. What do you want to do?`,
139
- options: [
140
- {
141
- value: "patch",
142
- label: "Patch — update extends, keep my other settings"
143
- },
144
- {
145
- value: "skip",
146
- label: "Skip — leave it alone"
147
- },
148
- {
149
- value: "overwrite",
150
- label: "Overwrite — replace with a fresh scaffold"
151
- }
152
- ],
153
- initialValue: "patch"
154
- });
155
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
156
- return choice;
157
- }
158
- async function pickPreset(ctx) {
159
- if (ctx.flags.yes || ctx.flags.nonInteractive) return DEFAULT_PRESET;
160
- const choice = await ctx.prompts.select({
161
- message: "Which kind of TS project do you need?",
162
- options: Object.entries(PRESETS).map(([value, meta]) => ({
163
- value,
164
- label: meta.label
165
- })),
166
- initialValue: DEFAULT_PRESET
167
- });
168
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
169
- return choice;
170
- }
171
131
  async function pathExists(p) {
172
132
  try {
173
133
  await fs.access(p);
@@ -181,15 +141,7 @@ const ts = definePlugin(() => ({
181
141
  apiVersion: 1,
182
142
  install,
183
143
  uninstall,
184
- async setup({ shell }) {
185
- const svc = new TscService(shell);
186
- try {
187
- await svc.getBinDir();
188
- } catch (_err) {
189
- throw new Error("@rrlab/ts-plugin requires typescript to be installed in the host project. Run: rr plugins add ts (or: pnpm add -D typescript)");
190
- }
191
- return { tsc: svc };
192
- }
144
+ capabilities: ({ shell }) => ({ tsc: new TscService(shell) })
193
145
  }));
194
146
  //#endregion
195
147
  export { TOOL_VERSIONS, TscService, ts as default, install, uninstall };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rrlab/ts-plugin",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-git-2238423.0",
4
4
  "description": "TypeScript plugin for @rrlab/cli — provides the tsc capability.",
5
5
  "homepage": "https://github.com/variableland/dx/tree/main/run-run/ts-plugin#readme",
6
6
  "bugs": {
@@ -38,12 +38,14 @@
38
38
  },
39
39
  "peerDependencies": {
40
40
  "typescript": ">=5.0.0",
41
- "@rrlab/cli": "0.0.3"
41
+ "@rrlab/cli": "0.0.4-git-2238423.0"
42
42
  },
43
43
  "devDependencies": {
44
+ "@types/semver": "^7.7.1",
45
+ "semver": "^7.8.1",
44
46
  "typescript": "6.0.3",
45
- "@rrlab/cli": "0.0.3",
46
- "@rrlab/tsdown-config": "^0.1.0"
47
+ "@rrlab/tsdown-config": "^0.1.0",
48
+ "@rrlab/cli": "0.0.4-git-2238423.0"
47
49
  },
48
50
  "scripts": {
49
51
  "build": "tsdown",
package/src/index.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import {
4
+ decideScaffold,
4
5
  definePlugin,
5
6
  type FileOp,
6
7
  type InstallContext,
7
8
  type InstallResult,
9
+ pickPreset,
8
10
  ToolService,
9
11
  type TypeChecker,
10
12
  type TypeCheckOptions,
@@ -50,18 +52,24 @@ const PRESETS: Record<Preset, PresetInfo> = {
50
52
 
51
53
  const DEFAULT_PRESET: Preset = "no-dom-app";
52
54
 
53
- type ExistingFileAction = "skip" | "patch" | "overwrite";
54
-
55
55
  export async function install(ctx: InstallContext): Promise<InstallResult> {
56
56
  const tsconfigPath = path.join(ctx.appPkg.dirPath, TSCONFIG);
57
57
  const fileExists = await pathExists(tsconfigPath);
58
+ const scaffoldDecision = await decideScaffold(ctx, {
59
+ label: TSCONFIG,
60
+ fileExists,
61
+ patchHint: "update extends, keep my other settings",
62
+ });
58
63
 
59
- const scaffoldDecision = await decideScaffoldAction(ctx, fileExists);
60
64
  if (scaffoldDecision === "skip") {
61
65
  return { devDependencies: { typescript: TOOL_VERSIONS.typescript.install } };
62
66
  }
63
67
 
64
- const preset = await pickPreset(ctx);
68
+ const preset = await pickPreset(ctx, {
69
+ message: "Which kind of TS project do you need?",
70
+ presets: PRESETS,
71
+ defaultPreset: DEFAULT_PRESET,
72
+ });
65
73
  const presetInfo = PRESETS[preset];
66
74
 
67
75
  const devDependencies: Record<string, string> = {
@@ -96,7 +104,6 @@ export async function uninstall(ctx: UninstallContext): Promise<UninstallResult>
96
104
  return { removeDependencies };
97
105
  }
98
106
 
99
- // Read the current tsconfig to decide between full delete and surgical unset.
100
107
  let existing: Record<string, unknown> | undefined;
101
108
  try {
102
109
  const text = await fs.readFile(tsconfigPath, "utf8");
@@ -119,51 +126,6 @@ export async function uninstall(ctx: UninstallContext): Promise<UninstallResult>
119
126
  return { removeDependencies, files };
120
127
  }
121
128
 
122
- async function decideScaffoldAction(
123
- ctx: InstallContext,
124
- fileExists: boolean,
125
- ): Promise<"create" | "patch" | "overwrite" | "skip"> {
126
- if (!fileExists) {
127
- if (ctx.flags.yes || ctx.flags.nonInteractive) return "create";
128
- const choice = await ctx.prompts.confirm({
129
- message: `Scaffold ${TSCONFIG} with an @rrlab/ts-config preset?`,
130
- initialValue: true,
131
- });
132
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
133
- return choice ? "create" : "skip";
134
- }
135
-
136
- // Existing file. Default to patch for migration safety.
137
- if (ctx.flags.yes || ctx.flags.nonInteractive) return "patch";
138
-
139
- const choice = await ctx.prompts.select<ExistingFileAction>({
140
- message: `${TSCONFIG} already exists. What do you want to do?`,
141
- options: [
142
- { value: "patch", label: "Patch — update extends, keep my other settings" },
143
- { value: "skip", label: "Skip — leave it alone" },
144
- { value: "overwrite", label: "Overwrite — replace with a fresh scaffold" },
145
- ],
146
- initialValue: "patch",
147
- });
148
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
149
- return choice;
150
- }
151
-
152
- async function pickPreset(ctx: InstallContext): Promise<Preset> {
153
- if (ctx.flags.yes || ctx.flags.nonInteractive) return DEFAULT_PRESET;
154
-
155
- const choice = await ctx.prompts.select<Preset>({
156
- message: "Which kind of TS project do you need?",
157
- options: (Object.entries(PRESETS) as Array<[Preset, PresetInfo]>).map(([value, meta]) => ({
158
- value,
159
- label: meta.label,
160
- })),
161
- initialValue: DEFAULT_PRESET,
162
- });
163
- if (ctx.prompts.isCancel(choice)) throw new Error("Cancelled by user.");
164
- return choice;
165
- }
166
-
167
129
  async function pathExists(p: string): Promise<boolean> {
168
130
  try {
169
131
  await fs.access(p);
@@ -173,23 +135,12 @@ async function pathExists(p: string): Promise<boolean> {
173
135
  }
174
136
  }
175
137
 
176
- const ts = definePlugin<void>(() => ({
138
+ const ts = definePlugin(() => ({
177
139
  name: "ts",
178
140
  apiVersion: 1,
179
141
  install,
180
142
  uninstall,
181
- async setup({ shell }) {
182
- const svc = new TscService(shell);
183
- try {
184
- await svc.getBinDir();
185
- } catch (_err) {
186
- throw new Error(
187
- "@rrlab/ts-plugin requires typescript to be installed in the host project. " +
188
- "Run: rr plugins add ts (or: pnpm add -D typescript)",
189
- );
190
- }
191
- return { tsc: svc };
192
- },
143
+ capabilities: ({ shell }) => ({ tsc: new TscService(shell) }),
193
144
  }));
194
145
 
195
146
  export default ts;
@@ -1,6 +1,7 @@
1
1
  export const TOOL_VERSIONS = {
2
- // install range > peer range on purpose: pin latest stable for fresh installs,
3
- // accept TS 5+ if the host already has it.
4
- typescript: { install: "^6.0.0", peer: ">=5.0.0" },
2
+ // `install` is the prescriptive pin used by `rr plugins add`'s nypm call.
3
+ // For typescript we want fresh installs on the latest stable; the looser
4
+ // `>=5.0.0` contract lives in package.json#peerDependencies.
5
+ typescript: { install: "^6.0.0" },
5
6
  "@types/node": { install: ">=20" },
6
7
  } as const;