@vistta/cli 1.2.1 → 1.2.3

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 CHANGED
@@ -34,12 +34,12 @@ npm install @vistta/cli
34
34
  type checking still covers the whole configured project.
35
35
 
36
36
  - `vistta lint [patterns] [--fix]`
37
- Runs project-local ESLint against the configured files or supplied patterns.
37
+ Runs ESLint against the configured files or supplied patterns.
38
38
  `--fix` applies available fixes. Errors and warnings produce a nonzero exit
39
39
  status.
40
40
 
41
41
  - `vistta fmt [patterns] [--check|--write]`
42
- Checks formatting with project-local Prettier without changing files by
42
+ Checks formatting with Prettier without changing files by
43
43
  default. `--check` makes that mode explicit; `--write` formats files in place.
44
44
  `--fix` is also accepted as a write alias. Check and write modes cannot be
45
45
  combined.
@@ -116,7 +116,9 @@ Comments and trailing commas are supported. If both files exist, `tsconfig.json`
116
116
  takes precedence. The existing `cliOptions` block remains supported; `cli` wins
117
117
  when both specify the same setting.
118
118
 
119
- Install the tools you want to use locally:
119
+ ESLint, Prettier, and TypeScript are included with the CLI; no separate tool
120
+ installation is required. To use project-specific versions, optionally install
121
+ them locally:
120
122
 
121
123
  ```sh
122
124
  npm install --save-dev eslint prettier typescript
@@ -172,8 +174,11 @@ vistta run validate
172
174
  exit code. Lint warnings also fail the check.
173
175
  - Checks are read-only unless `--fix` or `fmt --write` is supplied. Fix mode
174
176
  applies Prettier formatting and ESLint fixes; type checking never emits files.
175
- - Tools are resolved from the current project, not downloaded automatically.
176
- Only enabled tools need to be installed. ESLint 9+ and Prettier 3 are supported.
177
+ - Tools are resolved from the current project first, falling back to the
178
+ CLI-bundled versions when absent. Commands never download tools at runtime.
179
+ Broken project installations report their errors instead of silently falling
180
+ back. ESLint 9+ and Prettier 3 are supported. Plugins and custom parsers must
181
+ still be installed in the project.
177
182
  - `format.include` and `lint.include` override top-level `include`. Explicit
178
183
  command-line patterns override both and are relative to the working directory.
179
184
  The corresponding `exclude` settings, `compilerOptions.outDir`, dependencies,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vistta/cli",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "exports": {
@@ -35,8 +35,11 @@
35
35
  "@vistta/fs": "^1.1.0",
36
36
  "@vistta/utils": "^1.1.0",
37
37
  "esbuild": "^0.28.2",
38
+ "eslint": "^9.39.5",
38
39
  "jsonc-parser": "^3.3.1",
39
- "semver": "^7.8.5"
40
+ "prettier": "^3.9.9",
41
+ "semver": "^7.8.5",
42
+ "typescript": "^6.0.3"
40
43
  },
41
44
  "author": "Tiago Terenas Almeida",
42
45
  "license": "GPL-3.0-only",
@@ -1,11 +1,12 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
4
5
  import { tmpdir } from "node:os";
5
6
  import { dirname, join, resolve } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
8
  import { loadProjectConfig } from "../config.js";
8
- import { planTasks, runChecks, runTasks, toolFiles } from "../tools.js";
9
+ import { planTasks, projectTool, runChecks, runTasks, toolFiles } from "../tools.js";
9
10
 
10
11
  function fixture(files) {
11
12
  const cwd = mkdtempSync(join(tmpdir(), "vistta-config-"));
@@ -129,6 +130,51 @@ suite("CLI configuration", async () => {
129
130
  });
130
131
 
131
132
  suite("CLI tool adapters", async () => {
133
+ await test("uses bundled tools when the project does not install them", async () => {
134
+ const cwd = fixture({
135
+ "package.json": { name: "fixture", type: "module" },
136
+ "tsconfig.json": {
137
+ include: ["src/*.js"],
138
+ compilerOptions: { allowJs: true, checkJs: true, noEmit: true, types: [], target: "ES2022" },
139
+ cli: { lint: { rules: { "no-unused-vars": "error" } } },
140
+ },
141
+ "src/main.js": "export const value = 1;\n",
142
+ });
143
+ try {
144
+ for (const name of ["eslint", "prettier", "typescript"]) {
145
+ const bundled = await import(pathToFileURL(createRequire(import.meta.url).resolve(name)).href);
146
+ assert.equal(await projectTool(name, cwd), bundled.default || bundled);
147
+ }
148
+ assert.equal(await runChecks(["format", "lint", "types"], loadProjectConfig(cwd), { cwd }), 0);
149
+ const check = spawnSync(process.execPath, [fileURLToPath(new URL("../bin.js", import.meta.url)), "check"], {
150
+ cwd,
151
+ encoding: "utf8",
152
+ });
153
+ assert.equal(check.status, 0, check.stdout + check.stderr);
154
+ } finally {
155
+ rmSync(cwd, { recursive: true, force: true });
156
+ }
157
+ });
158
+
159
+ await test("prefers project tools and does not hide broken project installations", async () => {
160
+ const cwd = fixture({
161
+ "node_modules/prettier/package.json": { main: "index.cjs" },
162
+ "node_modules/prettier/index.cjs": 'module.exports = { version: "project-prettier" };',
163
+ "node_modules/eslint/package.json": { main: "index.cjs" },
164
+ "node_modules/eslint/index.cjs": 'throw new Error("Broken project ESLint");',
165
+ "node_modules/typescript/package.json": { exports: { "./internal": "./index.cjs" } },
166
+ "broken/node_modules/prettier/package.json": { main: "missing.cjs" },
167
+ });
168
+ try {
169
+ assert.equal((await projectTool("prettier", cwd)).version, "project-prettier");
170
+ await assert.rejects(projectTool("eslint", cwd), /Broken project ESLint/);
171
+ await assert.rejects(projectTool("typescript", cwd), { code: "ERR_PACKAGE_PATH_NOT_EXPORTED" });
172
+ await assert.rejects(projectTool("prettier", join(cwd, "broken")), { code: "MODULE_NOT_FOUND" });
173
+ } finally {
174
+ rmSync(cwd, { recursive: true, force: true });
175
+ }
176
+ });
177
+
132
178
  await test("formats and lints temporary files with project-local tools and checks types without emitting", async () => {
133
179
  const root = resolve(import.meta.dirname, "../../..");
134
180
  const cwd = fixture({
package/tools.js CHANGED
@@ -9,8 +9,18 @@ export async function projectTool(name, cwd) {
9
9
  let entry;
10
10
  try {
11
11
  entry = require.resolve(name);
12
- } catch {
13
- throw new Error(`Missing project tool "${name}". Install it locally: npm install --save-dev ${name}`);
12
+ } catch (error) {
13
+ if (
14
+ error.code !== "MODULE_NOT_FOUND" ||
15
+ require.resolve.paths(name)?.some((path) => existsSync(resolve(path, name, "package.json")))
16
+ )
17
+ throw error;
18
+ try {
19
+ entry = createRequire(import.meta.url).resolve(name);
20
+ } catch (error) {
21
+ if (error.code !== "MODULE_NOT_FOUND") throw error;
22
+ throw new Error(`Missing tool "${name}". Reinstall @vistta/cli or install it locally: npm install --save-dev ${name}`);
23
+ }
14
24
  }
15
25
  const module = await import(pathToFileURL(entry).href);
16
26
  return module.default || module;