@stll/oxlint-config 3.1.0 → 3.2.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.
@@ -13,6 +13,8 @@ import { execFileSync, spawnSync } from "node:child_process";
13
13
  import { gte, minVersion, satisfies, valid, validRange } from "semver";
14
14
  import { LineCounter, isAlias, isMap, isNode, isScalar, isSeq, parseAllDocuments, parseDocument } from "yaml";
15
15
  import { pathToFileURL } from "node:url";
16
+ import { parse as parse$3 } from "acorn";
17
+ import { transformSync } from "esbuild";
16
18
  //#region src/toolchain-bun.ts
17
19
  const STABLE_RELEASE = /^\d+\.\d+\.\d+$/u;
18
20
  /**
@@ -31,7 +33,7 @@ const repositoryBun = (floor, manifest) => {
31
33
  };
32
34
  //#endregion
33
35
  //#region src/toolchain-consumer-checks.ts
34
- const consumerRunnerVersion = "3.1.0";
36
+ const consumerRunnerVersion = "3.2.0";
35
37
  const consumerCheckWorkflow = "stella/.github/.github/workflows/package-consumer-compat.yml";
36
38
  const record$11 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
37
39
  const repositoryDirectory = (value) => typeof value === "string" && value !== "" && !value.includes("\\") && !value.includes("${{") && !value.split("").some((character) => {
@@ -359,6 +361,38 @@ const engineFloorSelectorMatches = (value, floor) => {
359
361
  return Number(value.split(".").at(0)) === floor.major && satisfies(value, floor.range);
360
362
  };
361
363
  //#endregion
364
+ //#region src/toolchain-typescript-source-api.ts
365
+ const readSourceApiInput = ({ root, source, tracked }) => {
366
+ if (!tracked.has(source)) return void 0;
367
+ const resolvedRoot = realpathSync(root);
368
+ const input = path.resolve(resolvedRoot, source);
369
+ try {
370
+ if (realpathSync(input) !== input || !lstatSync(input).isFile()) return void 0;
371
+ return readFileSync(input, "utf8");
372
+ } catch {
373
+ return;
374
+ }
375
+ };
376
+ const sourceApiImportProblem = ({ source, content, name }) => {
377
+ if (content === void 0) return "must be a tracked, readable regular source file without symlinks";
378
+ try {
379
+ const { code } = transformSync(content, {
380
+ sourcefile: source,
381
+ loader: source.endsWith("x") ? "tsx" : "ts",
382
+ format: "esm",
383
+ tsconfigRaw: { compilerOptions: { verbatimModuleSyntax: true } },
384
+ logLevel: "silent"
385
+ });
386
+ if (parse$3(code, {
387
+ ecmaVersion: "latest",
388
+ sourceType: "module"
389
+ }).body.some((node) => node.type === "ImportDeclaration" && node.source.value === name && node.specifiers.length > 0)) return void 0;
390
+ return `no longer has a static value import from ${name}; remove the declaration or name its current importer`;
391
+ } catch {
392
+ return "cannot parse the declared compiler API importer";
393
+ }
394
+ };
395
+ //#endregion
362
396
  //#region src/toolchain-packages.ts
363
397
  const packageRules = [
364
398
  "bun-pins",
@@ -413,15 +447,37 @@ const parseTypescriptAllowlist = (input) => {
413
447
  const name = text("name");
414
448
  const spec = text("spec");
415
449
  const dependency = text("dependency");
450
+ const sourceApi = record$8(entry) && entry["kind"] === "source-api";
451
+ const keys = sourceApi ? [
452
+ "workspace",
453
+ "name",
454
+ "spec",
455
+ "reason",
456
+ "kind",
457
+ "sources"
458
+ ] : [...allowlistKeys, ...record$8(entry) && entry["kind"] === "dependency" ? ["kind"] : []];
416
459
  const reason = text("reason");
417
- if (!record$8(entry) || Object.keys(entry).length !== allowlistKeys.length || workspace === void 0 || name === void 0 || spec === void 0 || dependency === void 0 || reason === void 0) throw new Error(`typescriptAllowlist entry ${shown} must have exactly workspace, name, spec, dependency and reason as nonempty strings: ${allowlistFormat}`);
460
+ if (!record$8(entry) || Object.keys(entry).length !== keys.length || Object.keys(entry).some((key) => !keys.includes(key)) || workspace === void 0 || name === void 0 || spec === void 0 || !sourceApi && dependency === void 0 || reason === void 0) throw new Error(`typescriptAllowlist entry ${shown} must have exactly workspace, name, spec, dependency and reason as nonempty strings: ${allowlistFormat}`);
418
461
  if (workspace !== "." && (path.posix.normalize(workspace) !== workspace || workspace.startsWith("/") || workspace.endsWith("/") || workspace === ".." || workspace.startsWith("../"))) throw new Error(`typescriptAllowlist entry ${shown}: workspace must be "." for the root or a normalized relative package directory such as "apps/web"`);
419
462
  if (!exactTypescriptSpec(spec)) throw new Error(`typescriptAllowlist entry ${shown}: spec must be the exact spec the manifest resolves to, such as "6.0.3" or "npm:typescript@6.0.3", not a range or catalog reference`);
420
463
  if (!isTypescriptInstall(name, spec)) throw new Error(`typescriptAllowlist entry ${shown}: name must be typescript or @typescript/native, or an alias whose spec is npm:typescript@<version> or npm:@typescript/native@<version>`);
421
- if (typescriptPackages.has(dependency) || dependency === name) throw new Error(`typescriptAllowlist entry ${shown}: dependency must name the package in that workspace that needs ${name}, not a TypeScript package`);
464
+ if (dependency !== void 0 && (typescriptPackages.has(dependency) || dependency === name)) throw new Error(`typescriptAllowlist entry ${shown}: dependency must name the package in that workspace that needs ${name}, not a TypeScript package`);
422
465
  const key = `${workspace}\0${name}`;
423
466
  if (seen.has(key)) throw new Error(`typescriptAllowlist declares ${name} for ${workspace} more than once; keep one entry`);
424
467
  seen.add(key);
468
+ if (sourceApi) {
469
+ const sources = entry["sources"];
470
+ if (!Array.isArray(sources) || sources.length === 0 || !sources.every((source) => typeof source === "string" && path.posix.normalize(source) === source && !source.startsWith("/") && source !== ".." && !source.startsWith("../") && !source.includes("\\") && !/[?*{}[\]]/.test(source) && /\.[cm]?[jt]sx?$/.test(source) && !/\.d\.[cm]?ts$/.test(source) && !source.split("/").some((part) => ["node_modules", "vendor"].includes(part))) || new Set(sources).size !== sources.length) throw new Error(`typescriptAllowlist source-api entry ${shown} requires unique normalized source file paths`);
471
+ return {
472
+ kind: "source-api",
473
+ workspace,
474
+ name,
475
+ spec,
476
+ reason,
477
+ sources
478
+ };
479
+ }
480
+ if (dependency === void 0) throw new Error("Missing TypeScript dependency owner");
425
481
  return {
426
482
  workspace,
427
483
  name,
@@ -1173,7 +1229,7 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1173
1229
  });
1174
1230
  return `${name} ${shown} in ${file} is not allowed: ${typescriptGuidance}; if a dependency of this workspace needs it, add ${suggestion} to typescriptAllowlist in ${allowlistFile}`;
1175
1231
  }
1176
- return value === entry.spec ? void 0 : `${name} in ${file} resolves to ${shown}, but typescriptAllowlist in ${allowlistFile} allows ${entry.spec} for ${entry.dependency}; declare ${entry.spec} or update the entry's spec`;
1232
+ return value === entry.spec ? void 0 : `${name} in ${file} resolves to ${shown}, but typescriptAllowlist in ${allowlistFile} allows ${entry.spec} for ${entry.kind === "source-api" ? "source API imports" : entry.dependency}; declare ${entry.spec} or update the entry's spec`;
1177
1233
  };
1178
1234
  const peerRange = (value) => typeof value === "string" && validRange(value) !== null;
1179
1235
  for (const [file, json] of manifests) {
@@ -1343,6 +1399,20 @@ const checkPackageFiles = ({ files, policy, typescriptAllowlist }) => {
1343
1399
  return record$8(entries) && Object.hasOwn(entries, name);
1344
1400
  });
1345
1401
  if (!declares(entry.name)) return `${file} no longer declares ${entry.name} in dependencies, devDependencies or optionalDependencies; remove the entry`;
1402
+ if (entry.kind === "source-api") {
1403
+ for (const source of entry.sources) {
1404
+ let owner = path.posix.dirname(source);
1405
+ while (owner !== "." && !manifests.has(`${owner}/package.json`)) owner = path.posix.dirname(owner);
1406
+ if (owner !== entry.workspace) return `${source} belongs to workspace ${owner}, not ${entry.workspace}`;
1407
+ const problem = sourceApiImportProblem({
1408
+ source,
1409
+ content: files[source],
1410
+ name: entry.name
1411
+ });
1412
+ if (problem !== void 0) return `${source}: ${problem}`;
1413
+ }
1414
+ return;
1415
+ }
1346
1416
  if (!declares(entry.dependency)) return `${file} no longer depends on ${entry.dependency}; remove the entry and the ${entry.name} declaration, or name the dependency that still needs it`;
1347
1417
  };
1348
1418
  for (const entry of typescriptAllowlist) {
@@ -1415,11 +1485,15 @@ const yamlSettings = (file, manager) => {
1415
1485
  if (!record$7(settings)) throw new Error(`${manager} lifecycle configuration must be a mapping`);
1416
1486
  return settings;
1417
1487
  };
1488
+ const pnpmLifecycleVersions = ["12.9.1", "12.15.0"];
1418
1489
  const pnpmHooksEnabled = (manifest, context) => {
1419
1490
  const declared = declaredManager(context.packageManager ?? manifest["packageManager"]);
1420
1491
  const dependency = [manifest["dependencies"], manifest["devDependencies"]].flatMap((field) => record$7(field) ? [field["pnpm"]] : []).find((value) => typeof value === "string");
1421
- if ((declared?.at(1) === "pnpm" ? declared.at(2) : dependency) !== "12.9.1") throw new Error("pnpm lifecycle discovery requires declared pnpm@12.9.1");
1422
- const enabled = yamlSettings(context.pnpmWorkspace, "pnpm")["enablePrePostScripts"];
1492
+ const version = declared?.at(1) === "pnpm" ? declared.at(2) : dependency;
1493
+ if (!pnpmLifecycleVersions.some((supported) => version === supported)) throw new Error(`pnpm lifecycle discovery requires declared pnpm@${pnpmLifecycleVersions.join(" or pnpm@")}`);
1494
+ const settings = yamlSettings(context.pnpmWorkspace, "pnpm");
1495
+ if ("include" in settings) throw new Error("Delegated pnpm settings require explicit lifecycle discovery support");
1496
+ const enabled = settings["enablePrePostScripts"];
1423
1497
  if (enabled === void 0) return true;
1424
1498
  if (typeof enabled !== "boolean") throw new Error("pnpm enablePrePostScripts must be a literal boolean");
1425
1499
  return enabled;
@@ -4184,6 +4258,23 @@ const checkToolchain = ({ root, policy: sharedPolicy, onDelegated, onWarning, no
4184
4258
  message: error instanceof Error ? error.message : String(error)
4185
4259
  });
4186
4260
  }
4261
+ for (const entry of configuration.typescriptAllowlist) {
4262
+ if (entry.kind !== "source-api") continue;
4263
+ for (const source of entry.sources) {
4264
+ const content = readSourceApiInput({
4265
+ root,
4266
+ source,
4267
+ tracked: trackedFiles
4268
+ });
4269
+ if (content !== void 0) files[source] = content;
4270
+ else diagnostics.push({
4271
+ rule: "configuration",
4272
+ path: source,
4273
+ line: 1,
4274
+ message: "TypeScript source API evidence must be tracked, readable, regular and free of symlinks"
4275
+ });
4276
+ }
4277
+ }
4187
4278
  const cloud = configuration.cloud;
4188
4279
  const script = files[cloudSetupPath];
4189
4280
  if (cloud === void 0 && trackedFiles.has(".agents/cloud-setup.sh")) diagnostics.push({