@theholocron/astromech 5.0.0-alpha.20 → 5.0.0-alpha.200

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.
@@ -0,0 +1,99 @@
1
+ import { QualifiedConfig } from "@commitlint/types";
2
+ //#region src/lint/alex-config/alex-config.d.ts
3
+ /**
4
+ * The org's canonical `alex` (inclusive-language) config, as raw parsed
5
+ * data rather than the pre-rendered file strings `createRcConfig()`/
6
+ * `createIgnoreConfig()` produce for `holocron repo setup` to write into a
7
+ * repo's own `.alexrc.json`/`.alexignore`.
8
+ *
9
+ * `.alexrc.json` is explicitly "generated by `holocron repo setup` — do not
10
+ * edit manually" (this repo's own `AGENTS.md`) — the real source of truth
11
+ * is this JSON file, one canonical copy, not each repo's synced one. A
12
+ * central Sentinel check (holocron#769/#793) reads it directly rather
13
+ * than fetching each repo's own copy via the Contents API — the same "one
14
+ * config, not N copies" reasoning `@theholocron/commitlint-config` already
15
+ * established for commit-message linting.
16
+ */
17
+ declare const ALEX_CONFIG: {
18
+ allow: string[];
19
+ };
20
+ //#endregion
21
+ //#region src/lint/alex-ignore-patterns/alex-ignore-patterns.d.ts
22
+ /**
23
+ * Glob-ish path patterns `alex` skips. Kept as a literal here rather than
24
+ * importing `../templates/configs/alexjs/alexignore` directly — that file
25
+ * (like `commit-msg`/`pre-push`) is deliberately extensionless, matching
26
+ * the real filename `holocron repo setup` writes, which ECMAScript's own
27
+ * module resolution (`moduleResolution: "nodenext"`) can't import across
28
+ * a package boundary without an extension. Keep in sync with that file by
29
+ * hand; it changes rarely.
30
+ */
31
+ declare const ALEX_IGNORE_PATTERNS: string[];
32
+ //#endregion
33
+ //#region src/lint/editorconfig-checker-exclude/editorconfig-checker-exclude.d.ts
34
+ /**
35
+ * The paths editorconfig-checker skips, read from the same
36
+ * `editorconfig-checker.json` template `holocron repo setup` writes as a repo's
37
+ * `.editorconfig-checker.json` — so Sentinel's central editorconfig check
38
+ * (holocron#927) excludes exactly what CI's editorconfig-checker does, from
39
+ * one source.
40
+ *
41
+ * The exclusion that matters most is `\.md$` / `\.mdx$`: the generated
42
+ * `.editorconfig` gives every file tab indentation, but
43
+ * `@theholocron/prettier-config` formats markdown with spaces, so a README's
44
+ * indented code example would fail one tool or the other. Markdown is left
45
+ * to prettier and markdownlint.
46
+ *
47
+ * Each entry is a regular-expression source, matched against a
48
+ * repo-relative path, as editorconfig-checker itself treats them.
49
+ */
50
+ declare const EDITORCONFIG_CHECKER_EXCLUDE: readonly string[];
51
+ //#endregion
52
+ //#region src/lint/is-editorconfig-excluded/is-editorconfig-excluded.d.ts
53
+ /** `true` when `path` matches any {@link EDITORCONFIG_CHECKER_EXCLUDE} pattern. */
54
+ declare function isEditorConfigExcluded(path: string): boolean;
55
+ //#endregion
56
+ //#region src/lint/lint-message/lint-message.type.d.ts
57
+ interface CommitMessageViolation {
58
+ /** Rule name, e.g. `"subject-empty"`. */
59
+ rule: string;
60
+ /** commitlint's own message for the failure, e.g. `"subject may not be empty"`. */
61
+ message: string;
62
+ }
63
+ //#endregion
64
+ //#region src/lint/lint-message/lint-message.d.ts
65
+ declare function lintCommitMessage(message: string, loaded: QualifiedConfig): Promise<CommitMessageViolation[]>;
66
+ //#endregion
67
+ //#region src/lint/lint-commit-msg-file/lint-commit-msg-file.type.d.ts
68
+ interface LintCommitMsgFileResult {
69
+ valid: boolean;
70
+ violations: CommitMessageViolation[];
71
+ }
72
+ //#endregion
73
+ //#region src/lint/lint-commit-msg-file/lint-commit-msg-file.d.ts
74
+ declare function lintCommitMsgFile(filePath: string, cwd?: string): Promise<LintCommitMsgFileResult>;
75
+ //#endregion
76
+ //#region src/lint/prettier-ignore-patterns/prettier-ignore-patterns.d.ts
77
+ /**
78
+ * The org's canonical prettier ignore-pattern list for a central Sentinel
79
+ * check (holocron#769/#819) — the `PRETTIER_CONFIG` object itself doesn't
80
+ * need a home here, since `@theholocron/prettier-config`'s default export
81
+ * already *is* one canonical, importable config (the same "one config,
82
+ * not N copies" reasoning `@theholocron/commitlint-config` established for
83
+ * commit-message linting, and `ALEX_CONFIG` established for inclusive
84
+ * language) — only the ignore list is a Sentinel-specific decision that
85
+ * belongs alongside `ALEX_IGNORE_PATTERNS`, not inside the config package.
86
+ *
87
+ * Reuses `ALEX_IGNORE_PATTERNS`' own entries (`.github/*`, `CHANGELOG.md`,
88
+ * `LICENSE` all independently confirmed to fail a real `prettier.check()`
89
+ * today — machine-generated or unparseable content, same reasoning that
90
+ * excluded them from alex), plus `pnpm-lock.yaml` — prettier-specific
91
+ * (alex never reads a lockfile, but prettier would try to reformat it):
92
+ * machine-written by pnpm's own writer, and not gitignored (lockfiles are
93
+ * tracked), so prettier picks it up by default. Reformatting it
94
+ * fights every subsequent `pnpm install` (this repo's own `.prettierignore`
95
+ * already excludes it for that exact reason).
96
+ */
97
+ declare const PRETTIER_IGNORE_PATTERNS: string[];
98
+ //#endregion
99
+ export { ALEX_CONFIG, ALEX_IGNORE_PATTERNS, type CommitMessageViolation, EDITORCONFIG_CHECKER_EXCLUDE, type LintCommitMsgFileResult, PRETTIER_IGNORE_PATTERNS, isEditorConfigExcluded, lintCommitMessage, lintCommitMsgFile };
@@ -0,0 +1,143 @@
1
+ import { n as alexrc_default, t as editorconfig_checker_default } from "../editorconfig-checker-DPejtR5z.mjs";
2
+ import { readFile } from "node:fs/promises";
3
+ import load from "@commitlint/load";
4
+ import lint from "@commitlint/lint";
5
+ //#region src/lint/alex-config/alex-config.ts
6
+ /**
7
+ * The org's canonical `alex` (inclusive-language) config, as raw parsed
8
+ * data rather than the pre-rendered file strings `createRcConfig()`/
9
+ * `createIgnoreConfig()` produce for `holocron repo setup` to write into a
10
+ * repo's own `.alexrc.json`/`.alexignore`.
11
+ *
12
+ * `.alexrc.json` is explicitly "generated by `holocron repo setup` — do not
13
+ * edit manually" (this repo's own `AGENTS.md`) — the real source of truth
14
+ * is this JSON file, one canonical copy, not each repo's synced one. A
15
+ * central Sentinel check (holocron#769/#793) reads it directly rather
16
+ * than fetching each repo's own copy via the Contents API — the same "one
17
+ * config, not N copies" reasoning `@theholocron/commitlint-config` already
18
+ * established for commit-message linting.
19
+ */
20
+ const ALEX_CONFIG = alexrc_default;
21
+ //#endregion
22
+ //#region src/lint/alex-ignore-patterns/alex-ignore-patterns.ts
23
+ /**
24
+ * Glob-ish path patterns `alex` skips. Kept as a literal here rather than
25
+ * importing `../templates/configs/alexjs/alexignore` directly — that file
26
+ * (like `commit-msg`/`pre-push`) is deliberately extensionless, matching
27
+ * the real filename `holocron repo setup` writes, which ECMAScript's own
28
+ * module resolution (`moduleResolution: "nodenext"`) can't import across
29
+ * a package boundary without an extension. Keep in sync with that file by
30
+ * hand; it changes rarely.
31
+ */
32
+ const ALEX_IGNORE_PATTERNS = [
33
+ ".github/*",
34
+ "CHANGELOG.md",
35
+ "LICENSE"
36
+ ];
37
+ //#endregion
38
+ //#region src/lint/editorconfig-checker-exclude/editorconfig-checker-exclude.ts
39
+ /**
40
+ * The paths editorconfig-checker skips, read from the same
41
+ * `editorconfig-checker.json` template `holocron repo setup` writes as a repo's
42
+ * `.editorconfig-checker.json` — so Sentinel's central editorconfig check
43
+ * (holocron#927) excludes exactly what CI's editorconfig-checker does, from
44
+ * one source.
45
+ *
46
+ * The exclusion that matters most is `\.md$` / `\.mdx$`: the generated
47
+ * `.editorconfig` gives every file tab indentation, but
48
+ * `@theholocron/prettier-config` formats markdown with spaces, so a README's
49
+ * indented code example would fail one tool or the other. Markdown is left
50
+ * to prettier and markdownlint.
51
+ *
52
+ * Each entry is a regular-expression source, matched against a
53
+ * repo-relative path, as editorconfig-checker itself treats them.
54
+ */
55
+ const EDITORCONFIG_CHECKER_EXCLUDE = editorconfig_checker_default.Exclude;
56
+ //#endregion
57
+ //#region src/lint/is-editorconfig-excluded/is-editorconfig-excluded.ts
58
+ /** `true` when `path` matches any {@link EDITORCONFIG_CHECKER_EXCLUDE} pattern. */
59
+ function isEditorConfigExcluded(path) {
60
+ return EDITORCONFIG_CHECKER_EXCLUDE.some((pattern) => new RegExp(pattern).test(path));
61
+ }
62
+ //#endregion
63
+ //#region src/lint/lint-message/lint-message.ts
64
+ /**
65
+ * Lints one commit message string against an already-loaded commitlint
66
+ * config. The genuinely environment-independent half of commit-message
67
+ * linting (holocron#789) — *obtaining* a `QualifiedConfig` differs by
68
+ * caller (Sentinel isolates itself from any ambient config since it runs
69
+ * standalone, away from a real repo checkout; `lintCommitMsgFile` below
70
+ * lets cosmiconfig discover the invoking repo's own
71
+ * `commitlint.config.*`, matching `commitlint --edit`'s own contract) —
72
+ * but linting one message against a loaded config is identical either way.
73
+ *
74
+ * Real `@commitlint/lint` — the same programmatic API `@commitlint/cli`
75
+ * itself uses internally — never a reimplementation of commitlint's rules.
76
+ */
77
+ /** `parserPreset.parserOpts`, if the loaded config sets one — same lookup `@commitlint/cli`'s own `selectParserOpts()` does. */
78
+ function selectParserOpts(parserPreset) {
79
+ return parserPreset?.parserOpts;
80
+ }
81
+ async function lintCommitMessage(message, loaded) {
82
+ const opts = {
83
+ /* istanbul ignore next -- see comment above */
84
+ parserOpts: selectParserOpts(loaded.parserPreset) ?? {},
85
+ plugins: loaded.plugins,
86
+ /* istanbul ignore next -- see comment above */
87
+ ignores: loaded.ignores ?? [],
88
+ defaultIgnores: loaded.defaultIgnores !== false
89
+ };
90
+ const outcome = await lint(message, loaded.rules, opts);
91
+ if (outcome.valid) return [];
92
+ return outcome.errors.map((error) => ({
93
+ rule: error.name,
94
+ message: error.message
95
+ }));
96
+ }
97
+ //#endregion
98
+ //#region src/lint/lint-commit-msg-file/lint-commit-msg-file.ts
99
+ /**
100
+ * `holocron run lint commit-msg <file>` — a real programmatic replacement for
101
+ * shelling out to `commitlint --edit "$1"` from `.husky/commit-msg`
102
+ * (holocron#789). Matches that CLI's own contract exactly: `file` is a
103
+ * path to the commit message being written (the arg git's `commit-msg`
104
+ * hook passes as `$1`), no `--config` override needed — `@commitlint/load`
105
+ * runs from `cwd` with no seed, so cosmiconfig's own normal upward search
106
+ * discovers the invoking repo's real `commitlint.config.*` file, exactly
107
+ * as `commitlint --edit` (no `--config` flag) already does today. Unlike
108
+ * Sentinel's `loadIsolatedConfig()`, no isolation is needed here — this
109
+ * runs inside a real repo checkout with a real `node_modules`, not
110
+ * standalone on a server.
111
+ */
112
+ async function lintCommitMsgFile(filePath, cwd = process.cwd()) {
113
+ const violations = await lintCommitMessage(await readFile(filePath, "utf8"), await load({}, { cwd }));
114
+ return {
115
+ valid: violations.length === 0,
116
+ violations
117
+ };
118
+ }
119
+ //#endregion
120
+ //#region src/lint/prettier-ignore-patterns/prettier-ignore-patterns.ts
121
+ /**
122
+ * The org's canonical prettier ignore-pattern list for a central Sentinel
123
+ * check (holocron#769/#819) — the `PRETTIER_CONFIG` object itself doesn't
124
+ * need a home here, since `@theholocron/prettier-config`'s default export
125
+ * already *is* one canonical, importable config (the same "one config,
126
+ * not N copies" reasoning `@theholocron/commitlint-config` established for
127
+ * commit-message linting, and `ALEX_CONFIG` established for inclusive
128
+ * language) — only the ignore list is a Sentinel-specific decision that
129
+ * belongs alongside `ALEX_IGNORE_PATTERNS`, not inside the config package.
130
+ *
131
+ * Reuses `ALEX_IGNORE_PATTERNS`' own entries (`.github/*`, `CHANGELOG.md`,
132
+ * `LICENSE` all independently confirmed to fail a real `prettier.check()`
133
+ * today — machine-generated or unparseable content, same reasoning that
134
+ * excluded them from alex), plus `pnpm-lock.yaml` — prettier-specific
135
+ * (alex never reads a lockfile, but prettier would try to reformat it):
136
+ * machine-written by pnpm's own writer, and not gitignored (lockfiles are
137
+ * tracked), so prettier picks it up by default. Reformatting it
138
+ * fights every subsequent `pnpm install` (this repo's own `.prettierignore`
139
+ * already excludes it for that exact reason).
140
+ */
141
+ const PRETTIER_IGNORE_PATTERNS = [...ALEX_IGNORE_PATTERNS, "pnpm-lock.yaml"];
142
+ //#endregion
143
+ export { ALEX_CONFIG, ALEX_IGNORE_PATTERNS, EDITORCONFIG_CHECKER_EXCLUDE, PRETTIER_IGNORE_PATTERNS, isEditorConfigExcluded, lintCommitMessage, lintCommitMsgFile };
@@ -0,0 +1,55 @@
1
+ import { loadLayered } from "@theholocron/datapad";
2
+ //#region src/config/schema/schema.ts
3
+ /**
4
+ * The `tasks` config shape — the single manifest of what a repo runs.
5
+ * Read from a dedicated `astromech.config.*` file or the `tasks` key of
6
+ * `holocron.config.*` (see {@link loadTasksConfig}).
7
+ *
8
+ * Runtime validation is the loader's job, not this type's — the type only
9
+ * describes what an author writes.
10
+ */
11
+ /** Normalise a `TaskConfigItem` to a full {@link TaskEntry} with defaults applied. */
12
+ function normalizeTaskEntry(item) {
13
+ return {
14
+ ci: true,
15
+ local: true,
16
+ ...typeof item === "string" ? { name: item } : item
17
+ };
18
+ }
19
+ //#endregion
20
+ //#region src/config/coerce-tasks/coerce-tasks.ts
21
+ /** A bare item array is shorthand for `{ tasks: [...] }`. */
22
+ function coerceTasks(value) {
23
+ if (value == null) return void 0;
24
+ return Array.isArray(value) ? { tasks: value } : value;
25
+ }
26
+ //#endregion
27
+ //#region src/config/load/load.ts
28
+ /**
29
+ * Resolve the task manifest for a repo: the `tasks` key of
30
+ * `holocron.config.*`, then a dedicated `astromech.config.*` merged on
31
+ * top (dedicated wins; item arrays concatenate). Returns `{}` when
32
+ * neither source is present.
33
+ *
34
+ * `walkUp: true` — `holocron run`/`ci` are usable from any subdirectory of
35
+ * a repo, not only its exact root (datapad walks up to the nearest `.git`
36
+ * boundary; see `@theholocron/datapad`'s own doc comment).
37
+ *
38
+ * A caller that has already loaded `holocron.config.*` passes it as
39
+ * `options.holocron` so it is not found and loaded again.
40
+ */
41
+ async function loadTasksConfig(cwd, options = {}) {
42
+ return (await loadLayered({
43
+ cwd,
44
+ name: "astromech",
45
+ walkUp: true,
46
+ fallback: {
47
+ file: "holocron",
48
+ key: "tasks",
49
+ preloaded: options.holocron
50
+ },
51
+ coerce: (raw) => coerceTasks(raw)
52
+ }))?.config ?? {};
53
+ }
54
+ //#endregion
55
+ export { coerceTasks as n, normalizeTaskEntry as r, loadTasksConfig as t };
@@ -1,18 +1,10 @@
1
- //#region src/config/schema.d.ts
2
- /**
3
- * The `tasks` config shape — the single manifest of what a repo runs.
4
- * Read from a dedicated `astromech.config.*` file or the `tasks` key of
5
- * `holocron.config.*` (see {@link loadTasksConfig}).
6
- *
7
- * Runtime validation is the loader's job, not this type's — the type only
8
- * describes what an author writes.
9
- */
1
+ //#region src/config/schema/schema.type.d.ts
10
2
  interface TaskEntry {
11
3
  /** Registry task name — `verification.unitTests`, `sourceQuality.staticAnalysis`, `delivery.build`, … */
12
4
  name: string;
13
5
  /**
14
6
  * Emit a `.github/workflows/<name>.yml` thin caller and include the task
15
- * in `holocron ci`. Default `true`.
7
+ * in `holocron run ci`. Default `true`.
16
8
  */
17
9
  ci?: boolean;
18
10
  /**
@@ -24,7 +16,7 @@ interface TaskEntry {
24
16
  /**
25
17
  * The task's CI check context (from `WORKFLOW_CHECK_CONTEXTS`) is a
26
18
  * required status check in branch protection (`astromech.requiredChecks()`)
27
- * and part of `holocron ci`'s default run.
19
+ * and part of `holocron run ci`'s default run.
28
20
  */
29
21
  required?: boolean;
30
22
  /** Per-repo overrides on the same channel the reusable workflow reads. */
@@ -40,6 +32,15 @@ interface TaskEntry {
40
32
  turbo?: {
41
33
  /** Extra env vars turbo hashes into this task's cache key, appended to the registry default (empty when the task has none). */
42
34
  passThroughEnv?: string[];
35
+ /**
36
+ * Extra input globs appended to the registry default's `inputs`
37
+ * (holocron#845) — for a package whose layout the registry's
38
+ * `"src/**"` default doesn't cover (e.g. a flat-layout package with
39
+ * no `src/` directory at all). Appended, not replaced: the
40
+ * registry's other inputs (`package.json`, `tsdown.config.ts`, …)
41
+ * stay tracked without needing to be repeated here.
42
+ */
43
+ inputs?: string[];
43
44
  };
44
45
  }
45
46
  /** A task is either its bare name (all defaults) or an entry object. */
@@ -58,12 +59,12 @@ interface TasksConfig {
58
59
  /**
59
60
  * Required status-check contexts not backed by a task — codecov gates,
60
61
  * a bundle-build check, … Appended to `astromech.requiredChecks()` after the
61
- * `required`-task contexts. (`DCO` is prepended by `holocron setup` itself.)
62
+ * `required`-task contexts. (`DCO` is prepended by `holocron repo setup` itself.)
62
63
  */
63
64
  extraRequiredChecks?: string[];
64
65
  /**
65
- * Git hooks `holocron setup` installs. `true` / `{ prePush: true }` writes
66
- * `.husky/pre-push` (runs `holocron ci`); `false` / `{ prePush: false }`
66
+ * Git hooks `holocron repo setup` installs. `true` / `{ prePush: true }` writes
67
+ * `.husky/pre-push` (runs `holocron run ci`); `false` / `{ prePush: false }`
67
68
  * opts out. Omitted → on for `protection: "strict"` repos, off otherwise.
68
69
  * When enabled, `packageScripts()` also emits `prepare: "husky"`.
69
70
  */
@@ -71,7 +72,5 @@ interface TasksConfig {
71
72
  prePush?: boolean;
72
73
  };
73
74
  }
74
- /** Normalise a `TaskConfigItem` to a full {@link TaskEntry} with defaults applied. */
75
- declare function normalizeTaskEntry(item: TaskConfigItem): Required<Pick<TaskEntry, "name" | "ci" | "local">> & TaskEntry;
76
75
  //#endregion
77
- export { normalizeTaskEntry as i, TaskEntry as n, TasksConfig as r, TaskConfigItem as t };
76
+ export { TaskEntry as n, TasksConfig as r, TaskConfigItem as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@theholocron/astromech",
3
- "version": "5.0.0-alpha.20",
4
- "description": "The Holocron task runner — one task manifest drives `holocron run`, `holocron ci`, the CI workflows, package.json scripts, linters, and required checks.",
3
+ "version": "5.0.0-alpha.200",
4
+ "description": "The Holocron task runner — one task manifest drives `holocron run`, `holocron run ci`, the CI workflows, package.json scripts, linters, and required checks.",
5
5
  "keywords": [
6
6
  "ci",
7
7
  "holocron",
@@ -31,19 +31,33 @@
31
31
  "types": "./dist/config/index.d.mts",
32
32
  "import": "./dist/config/index.mjs",
33
33
  "default": "./dist/config/index.mjs"
34
+ },
35
+ "./lint": {
36
+ "types": "./dist/lint/index.d.mts",
37
+ "import": "./dist/lint/index.mjs",
38
+ "default": "./dist/lint/index.mjs"
34
39
  }
35
40
  },
36
41
  "files": [
37
42
  "dist"
38
43
  ],
39
44
  "dependencies": {
40
- "@theholocron/datapad": "5.0.0-alpha.20"
45
+ "@commitlint/lint": "^21.2.3",
46
+ "@commitlint/load": "^21.2.3",
47
+ "@commitlint/types": "^21.2.3",
48
+ "@theholocron/fs-utils": "^1.16.0",
49
+ "@theholocron/json-utils": "^1.16.0",
50
+ "@theholocron/pkg-utils": "^1.16.0",
51
+ "@theholocron/datapad": "5.0.0-alpha.200"
41
52
  },
42
53
  "devDependencies": {
43
- "@theholocron/eslint-config": "^8.0.0",
44
- "@theholocron/tsconfig": "^8.0.0",
45
- "@theholocron/tsdown-config": "^8.0.0",
46
- "@theholocron/vitest-config": "^8.0.0",
54
+ "@commitlint/config-conventional": "^21.2.3",
55
+ "@theholocron/commitlint-config": "^8.8.0",
56
+ "@theholocron/eslint-config": "^8.8.2",
57
+ "@theholocron/rollup-plugin-transform-template": "^8.8.2",
58
+ "@theholocron/tsconfig": "^8.8.2",
59
+ "@theholocron/tsdown-config": "^8.8.2",
60
+ "@theholocron/vitest-config": "^8.8.2",
47
61
  "@types/node": "^26",
48
62
  "@vitest/coverage-v8": "^4.1.11",
49
63
  "@vitest/eslint-plugin": "^1.6.27",
@@ -52,8 +66,7 @@
52
66
  "globals": "^17.11.0",
53
67
  "tsdown": "^0.22.14",
54
68
  "typescript": "^5.9.3",
55
- "vitest": "^4.1.11",
56
- "@theholocron/rollup-plugin-transform-template": "5.0.0-alpha.20"
69
+ "vitest": "^4.1.11"
57
70
  },
58
71
  "engines": {
59
72
  "node": ">=22"
@@ -1,11 +0,0 @@
1
- //#region src/config/schema.ts
2
- /** Normalise a `TaskConfigItem` to a full {@link TaskEntry} with defaults applied. */
3
- function normalizeTaskEntry(item) {
4
- return {
5
- ci: true,
6
- local: true,
7
- ...typeof item === "string" ? { name: item } : item
8
- };
9
- }
10
- //#endregion
11
- export { normalizeTaskEntry as t };