@cosmicdrift/kumiko-guards 0.330.2 → 0.332.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.
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import * as path from "node:path";
20
- import { Project, type SourceFile, SyntaxKind } from "ts-morph";
20
+ import { type Identifier, Node, Project, type SourceFile, SyntaxKind } from "ts-morph";
21
21
  import {
22
22
  type GuardViolation,
23
23
  type RepoCheck,
@@ -70,10 +70,23 @@ export interface Violation {
70
70
  forbiddenCalls: Array<{ name: string; line: number }>;
71
71
  }
72
72
 
73
+ // `import { setupTestStack as stack }` + `stack()` must resolve to the imported
74
+ // name, otherwise an alias hides both entrypoints and forbidden factories.
75
+ function importedNameOf(identifier: Identifier): string {
76
+ const localName = identifier.getText();
77
+ for (const imp of identifier.getSourceFile().getImportDeclarations()) {
78
+ for (const spec of imp.getNamedImports()) {
79
+ if ((spec.getAliasNode()?.getText() ?? spec.getName()) === localName) return spec.getName();
80
+ }
81
+ }
82
+ return localName;
83
+ }
84
+
73
85
  function collectCallNames(sf: SourceFile): Map<string, number[]> {
74
86
  const calls = new Map<string, number[]>();
75
87
  for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
76
- const name = call.getExpression().getText();
88
+ const callee = call.getExpression();
89
+ const name = Node.isIdentifier(callee) ? importedNameOf(callee) : callee.getText();
77
90
  const arr = calls.get(name) ?? [];
78
91
  arr.push(call.getStartLineNumber());
79
92
  calls.set(name, arr);
@@ -144,9 +144,6 @@ function scanFieldFactories(sf: SourceFile, roots: readonly RepoRoot[]): Finding
144
144
  const place = enclosingFieldName(call) ?? `${callee}(...)`;
145
145
  const line = call.getStartLineNumber();
146
146
  findings.push({ file, line, place, callee });
147
- console.warn(
148
- ` [text-field-stance WARN] ${file}:${line} ${callee}(...) at "${place}" has no personal stance — mark ${VALID_PERSONAL_HINT}`,
149
- );
150
147
  }
151
148
  return findings;
152
149
  }
@@ -212,6 +209,10 @@ function analyse(
212
209
  ): GuardOutcome {
213
210
  const { findings } = scan(files, roots);
214
211
  if (!compareBaseline) {
212
+ for (const f of findings)
213
+ console.warn(
214
+ ` [text-field-stance WARN] ${f.file}:${f.line} ${f.callee}(...) at "${f.place}" has no personal stance — mark ${VALID_PERSONAL_HINT}`,
215
+ );
215
216
  console.log(" Baseline comparison skipped (--no-baseline).");
216
217
  return { violations: [] };
217
218
  }
@@ -67,6 +67,8 @@ function isKnownReason(r: string): r is WrapperReason {
67
67
  // ── Built-in filter ─────────────────────────────────────────────────────
68
68
 
69
69
  const BUILTIN_NAMES = new Set([
70
+ "test",
71
+ "exec",
70
72
  "push",
71
73
  "pop",
72
74
  "shift",
@@ -300,9 +302,6 @@ function extractCalleeName(callNode: ReturnType<FnNode["getBody"]> | undefined):
300
302
  if (!expr) return null;
301
303
  if (expr.isKind(SyntaxKind.Identifier)) return expr.getText();
302
304
  if (expr.isKind(SyntaxKind.PropertyAccessExpression)) {
303
- // `/re/.test(x)` is a RegExp method call, not a call to a function named
304
- // "test" — a regex-literal receiver is never a wrapper callee.
305
- if (expr.getExpression().isKind(SyntaxKind.RegularExpressionLiteral)) return null;
306
305
  return expr.getName();
307
306
  }
308
307
  return null;
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { gitEnv } from "./_lib/git-env";
1
2
  export {
2
3
  type AstGuard,
3
4
  buildGuardKitInventory,
package/src/pre-push.sh CHANGED
@@ -6,9 +6,10 @@
6
6
  # scripts/check-wt.sh, delegate to it.
7
7
  # 2. Else if running inside a worktree under the parent workspace (no
8
8
  # check-wt.sh), run this worktree's own package.json scripts
9
- # (typecheck, lint, test, test:dom) directly — the parent's
10
- # `bun check` would otherwise check the main checkout instead of the
11
- # worktree.
9
+ # (typecheck, lint, test, test:dom) plus the consumer-CI guards
10
+ # (kumiko-guards guards/checks, kumiko-guard-comment-lang) directly —
11
+ # the parent's `bun check` would otherwise check the main checkout
12
+ # instead of the worktree.
12
13
  # 3. Else if running inside the cosmicdriftgamestudio Parent-Workspace,
13
14
  # delegate to its `bun check` with KUMIKO_CLI_SCOPE set to THIS
14
15
  # sub-repo only — so a push from one sub-repo doesn't run
@@ -129,6 +130,24 @@ if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
129
130
  cd "$REPO_ROOT" && exec "$REPO_ROOT/scripts/check-wt.sh"
130
131
  fi
131
132
 
133
+ # Runs a guard bin from the worktree's or the parent's node_modules/.bin
134
+ # (same guards consumer CI runs); an unresolvable bin is skipped, not fatal.
135
+ run_guard_bin() {
136
+ GUARD_LABEL="$1"
137
+ GUARD_BIN="$2"
138
+ shift 2
139
+ for GUARD_CANDIDATE in "$REPO_ROOT/node_modules/.bin/$GUARD_BIN" "$PARENT_DIR/node_modules/.bin/$GUARD_BIN"; do
140
+ if [ -x "$GUARD_CANDIDATE" ]; then
141
+ echo "[pre-push] $GUARD_BIN $*"
142
+ if ! "$GUARD_CANDIDATE" "$@"; then
143
+ FAILED="$FAILED $GUARD_LABEL"
144
+ fi
145
+ return 0
146
+ fi
147
+ done
148
+ echo "[pre-push] $GUARD_BIN: not resolvable in worktree/parent, skipped"
149
+ }
150
+
132
151
  # infra#899: no tracked scripts/check-wt.sh — the parent-scoped `bun check`
133
152
  # branch below would check the main checkout, not this worktree. Run the
134
153
  # worktree's own package.json scripts directly instead (only when nested
@@ -137,7 +156,7 @@ fi
137
156
  if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
138
157
  && [ -n "$PARENT_DIR" ]; then
139
158
  cd "$REPO_ROOT"
140
- echo "[pre-push] worktree without scripts/check-wt.sh — running this worktree's package.json scripts (typecheck, lint, test, test:dom)…"
159
+ echo "[pre-push] worktree without scripts/check-wt.sh — running this worktree's package.json scripts (typecheck, lint, test, test:dom) and guards (guards, checks, comment-lang)…"
141
160
  if ! has_package_script test; then
142
161
  echo "[pre-push] FATAL: worktree without a package.json \"test\" script — nothing to run, refusing push." >&2
143
162
  echo "Add a \"test\" script to package.json, or set PRE_PUSH_SKIP=1 to bypass (use sparingly)." >&2
@@ -154,6 +173,9 @@ if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] \
154
173
  echo "[pre-push] $SCRIPT_NAME: no script, skipped"
155
174
  fi
156
175
  done
176
+ run_guard_bin guards kumiko-guards guards
177
+ run_guard_bin checks kumiko-guards checks
178
+ run_guard_bin comment-lang kumiko-guard-comment-lang --touched --base=origin/main
157
179
  if [ -n "$FAILED" ]; then
158
180
  echo "[pre-push] worktree check failed:$FAILED" >&2
159
181
  exit 1