@theholocron/astromech 5.0.0-alpha.5 → 5.0.0-alpha.7

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
@@ -546,6 +546,46 @@ declare const CI_ORDER: string[];
546
546
  /** Ordered (task contexts in {@link CI_ORDER}, then extras), de-duplicated. */
547
547
  declare function requiredChecks(config: TasksConfig): string[];
548
548
  //#endregion
549
+ //#region src/resolver.d.ts
550
+ /**
551
+ * Resolves the absolute path to a Bucket A tool's shared `@theholocron/*-config`
552
+ * entry point, installed in the consuming repo's own `node_modules` — and the
553
+ * CLI flag(s) to hand it via (`--config`, `--extends`, …). `run.ts` splices the
554
+ * result into the command it builds for a tool/detect runner or a linter-group
555
+ * entry, right after the tool's own args.
556
+ *
557
+ * Every mapped package's resolved entry point already carries a ready-to-use
558
+ * default export (a plain re-export for `prettier`/`commitlint`; an invoked,
559
+ * zero-arg preset for `eslint`/`vitest`/`tsdown` — verified end-to-end against
560
+ * each tool's real `--config`/`--extends` loader, not just the file's shape).
561
+ * A repo doesn't need this package installed at all — every lookup degrades to
562
+ * `[]` (no flag added, the tool falls back to its own auto-discovery of a
563
+ * local file, unchanged from today) rather than throwing, so this is additive
564
+ * and safe to run unconditionally.
565
+ *
566
+ * `semantic-release`, `devmoji`, `editorconfig-checker`, and `knip` are
567
+ * deliberately absent — `semantic-release-config`'s `defineConfig()` needs
568
+ * real per-repo data (branches, npm options) a static `--extends <path>` can't
569
+ * carry; `devmoji` runs through a git hook template, not `holocron run
570
+ * <task>`; `editorconfig-checker` has no shared-config package yet; `knip` is
571
+ * repo-specific by nature, never a shared-config candidate. See
572
+ * `.notes/tech-config-resolution.spec.md` (#676).
573
+ */
574
+ interface ResolverDeps {
575
+ readFile: (path: string) => string;
576
+ fileExists: (path: string) => boolean;
577
+ }
578
+ /** Every tool name the resolver knows how to point at a shared config. */
579
+ declare const RESOLVABLE_TOOLS: ReadonlySet<string>;
580
+ /**
581
+ * `<flag> <absolute path>` for `tool`, resolved against `cwd`'s
582
+ * `node_modules` — or `[]` when the tool isn't mapped, the package isn't
583
+ * installed, its `package.json` doesn't declare the export subpath this
584
+ * needs, or the resolved file doesn't actually exist on disk (a stale
585
+ * install, or a package version that predates the export existing).
586
+ */
587
+ declare function resolveToolConfig(tool: string, cwd: string, deps: ResolverDeps): string[];
588
+ //#endregion
549
589
  //#region src/reusable.d.ts
550
590
  /**
551
591
  * The **reusable** GitHub Actions surface pushed to `theholocron/.github` by
@@ -573,4 +613,4 @@ declare const WORKFLOW_TEMPLATE_PROPERTIES: Record<string, string>;
573
613
  */
574
614
  declare function reusableTemplates(): Map<string, string>;
575
615
  //#endregion
576
- export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask };
616
+ export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, RESOLVABLE_TOOLS, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type ResolverDeps, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, resolveToolConfig, reusableTemplates, runCi, runTask };
package/dist/index.mjs CHANGED
@@ -327,6 +327,98 @@ function resolveLinters(opts) {
327
327
  }));
328
328
  }
329
329
  //#endregion
330
+ //#region src/resolver.ts
331
+ /**
332
+ * `library` is every mapped tool's org-wide default variant today — every
333
+ * currently-migrated repo (`holocron`, `clients`, `utils`, `configs`,
334
+ * `themes`, `observability`, `skills`) is a publishable library package, and
335
+ * `eslint-config`/`vitest-config`/`tsdown-config` all name their
336
+ * general-purpose preset exactly that. Picking a different variant per repo
337
+ * (an app-type template, say) needs new `holocron.config.ts` surface this
338
+ * doesn't add yet — deliberately out of scope until a real consumer needs it.
339
+ */
340
+ const TOOL_CONFIGS = {
341
+ eslint: {
342
+ package: "@theholocron/eslint-config",
343
+ exportPath: "./bundles/library",
344
+ flag: ["--config"]
345
+ },
346
+ prettier: {
347
+ package: "@theholocron/prettier-config",
348
+ exportPath: ".",
349
+ flag: ["--config"]
350
+ },
351
+ vitest: {
352
+ package: "@theholocron/vitest-config",
353
+ exportPath: "./bundles/library",
354
+ flag: ["--config"]
355
+ },
356
+ tsdown: {
357
+ package: "@theholocron/tsdown-config",
358
+ exportPath: "./presets/library",
359
+ flag: ["--config"]
360
+ },
361
+ commitlint: {
362
+ package: "@theholocron/commitlint-config",
363
+ exportPath: ".",
364
+ flag: ["--config"]
365
+ }
366
+ };
367
+ /** Every tool name the resolver knows how to point at a shared config. */
368
+ const RESOLVABLE_TOOLS = new Set(Object.keys(TOOL_CONFIGS));
369
+ /**
370
+ * `<flag> <absolute path>` for `tool`, resolved against `cwd`'s
371
+ * `node_modules` — or `[]` when the tool isn't mapped, the package isn't
372
+ * installed, its `package.json` doesn't declare the export subpath this
373
+ * needs, or the resolved file doesn't actually exist on disk (a stale
374
+ * install, or a package version that predates the export existing).
375
+ */
376
+ function resolveToolConfig(tool, cwd, deps) {
377
+ const spec = TOOL_CONFIGS[tool];
378
+ if (!spec) return [];
379
+ const pkgDir = joinPath(cwd, "node_modules", ...spec.package.split("/"));
380
+ const pkgJsonPath = joinPath(pkgDir, "package.json");
381
+ if (!deps.fileExists(pkgJsonPath)) return [];
382
+ let pkg;
383
+ try {
384
+ pkg = JSON.parse(deps.readFile(pkgJsonPath));
385
+ } catch {
386
+ return [];
387
+ }
388
+ const relative = resolveExportEntry(pkg.exports?.[spec.exportPath]);
389
+ if (!relative) return [];
390
+ const absolute = joinPath(pkgDir, relative.replace(/^\.\//, ""));
391
+ if (!deps.fileExists(absolute)) return [];
392
+ return [...spec.flag, absolute];
393
+ }
394
+ /**
395
+ * Unwrap a `package.json#exports` subpath entry down to its file path —
396
+ * either a bare string, or an object with condition keys (`import`,
397
+ * `default`, `require`, …) that may themselves nest one level (`{ import: {
398
+ * types, default } }`, the shape every `@theholocron/*-config` package
399
+ * actually uses). Prefers `import` (this org ships ESM), then `default`,
400
+ * then `require` as a last resort.
401
+ */
402
+ function resolveExportEntry(entry) {
403
+ if (typeof entry === "string") return entry;
404
+ if (entry === null || typeof entry !== "object") return void 0;
405
+ const conditions = entry;
406
+ const value = conditions.import ?? conditions.default ?? conditions.require;
407
+ if (typeof value === "string") return value;
408
+ if (typeof value === "object" && value !== null) return resolveExportEntry(value);
409
+ }
410
+ /**
411
+ * A minimal, POSIX-safe `path.join` — avoids importing `node:path` just for
412
+ * this. Every path in play here is either already POSIX (a `package.json`
413
+ * `exports` value, always forward-slashed regardless of platform) or built
414
+ * from platform-neutral segments (`cwd`, `"node_modules"`, a scoped package
415
+ * name split on `/`), so a manual join is safe and keeps this module
416
+ * dependency-free like the rest of `run.ts`'s helpers.
417
+ */
418
+ function joinPath(...segments) {
419
+ return segments.map((s, i) => i === 0 ? s.replace(/\/+$/, "") : s.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
420
+ }
421
+ //#endregion
330
422
  //#region src/run.ts
331
423
  /**
332
424
  * `holocron run <task> [job] [-- <passthrough>]` — run a registry task (or one
@@ -448,8 +540,13 @@ function runTask(input) {
448
540
  const runner = resolveRunner(def.local, cwd, listDir);
449
541
  if (runner) {
450
542
  const flags = (def.flags?.[runner.tool] ?? []).filter((f) => !passthrough.includes(f));
543
+ const configFlags = resolveToolConfig(runner.tool, cwd, {
544
+ readFile,
545
+ fileExists
546
+ });
451
547
  return run(resolveBin(cwd, runner.tool, fileExists), [
452
548
  ...runner.args,
549
+ ...configFlags,
453
550
  ...flags,
454
551
  ...passthrough
455
552
  ]);
@@ -582,7 +679,7 @@ function runAllJobs(input, jobs, passthrough) {
582
679
  * turbo/a package script would have already caught in steps 1–2.
583
680
  */
584
681
  function runLinterGroup(input, names, passthrough) {
585
- const { print, logger, listDir, lookPath, cwd, task } = input;
682
+ const { print, logger, listDir, lookPath, cwd, task, readFile, fileExists } = input;
586
683
  const dryRun = input.dryRun ?? false;
587
684
  const runOne = makeRunOne(input);
588
685
  let rootFiles;
@@ -609,7 +706,15 @@ function runLinterGroup(input, names, passthrough) {
609
706
  print(`! ${name} — ${bin} not on PATH${def.installHint ? `. ${def.installHint}` : ""} (enforced in CI)`);
610
707
  continue;
611
708
  }
612
- reports.push(runOne(found, [...def.localArgs ?? [], ...passthrough]));
709
+ const configFlags = resolveToolConfig(name, cwd, {
710
+ readFile,
711
+ fileExists
712
+ });
713
+ reports.push(runOne(found, [
714
+ ...configFlags,
715
+ ...def.localArgs ?? [],
716
+ ...passthrough
717
+ ]));
613
718
  }
614
719
  if (reports.length === 0) {
615
720
  const required = input.required && anyHasLocalBin;
@@ -1445,4 +1550,4 @@ function createAstromech(options) {
1445
1550
  };
1446
1551
  }
1447
1552
  //#endregion
1448
- export { CI_ORDER, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, TASKS, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask };
1553
+ export { CI_ORDER, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, RESOLVABLE_TOOLS, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, TASKS, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, resolveToolConfig, reusableTemplates, runCi, runTask };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/astromech",
3
- "version": "5.0.0-alpha.5",
3
+ "version": "5.0.0-alpha.7",
4
4
  "description": "The Holocron task runner — one task manifest drives `holocron run`, `holocron ci`, the CI workflows, package.json scripts, linters, and required checks.",
5
5
  "keywords": [
6
6
  "ci",
@@ -37,7 +37,7 @@
37
37
  "dist"
38
38
  ],
39
39
  "dependencies": {
40
- "@theholocron/datapad": "5.0.0-alpha.5"
40
+ "@theholocron/datapad": "5.0.0-alpha.7"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@theholocron/eslint-config": "^8.0.0",
@@ -53,7 +53,7 @@
53
53
  "tsdown": "^0.22.14",
54
54
  "typescript": "^5.9.3",
55
55
  "vitest": "^4.1.11",
56
- "@theholocron/rollup-plugin-transform-template": "5.0.0-alpha.5"
56
+ "@theholocron/rollup-plugin-transform-template": "5.0.0-alpha.7"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=22"