Route tests through a #testing alias, ban ../ imports, and document the alias ownership rule
Check / check (pull_request) Successful in 46s
Check / check (pull_request) Successful in 46s
This commit is contained in:
@@ -17,6 +17,9 @@ as skipped instead of silently passing.
|
|||||||
`codex`/`claude`/`npx` binaries and a fake Gitea through
|
`codex`/`claude`/`npx` binaries and a fake Gitea through
|
||||||
`protect-source.sh` -> `prepare.ts` -> `run.ts` -> `post.ts`; repeat that
|
`protect-source.sh` -> `prepare.ts` -> `run.ts` -> `post.ts`; repeat that
|
||||||
for changes to the phase flow ([[bot-agents-runtime-no-build]]).
|
for changes to the phase flow ([[bot-agents-runtime-no-build]]).
|
||||||
- `src/testing.ts` holds the shared test helpers; tests follow
|
- `src/testing.ts` holds the shared test helpers and is the `#testing` file
|
||||||
|
alias. Every test imports it as `#testing`, never `./testing.ts`, and it
|
||||||
|
re-exports the source helpers `tests/` needs, since `tests/` may not use
|
||||||
|
`../`. Tests follow
|
||||||
[[tests-layout-vitest]] (unit tests in `src/*.test.ts`, repo-level ones in
|
[[tests-layout-vitest]] (unit tests in `src/*.test.ts`, repo-level ones in
|
||||||
`tests/`).
|
`tests/`).
|
||||||
|
|||||||
@@ -1,75 +1,85 @@
|
|||||||
---
|
---
|
||||||
description: Shared code comes through # aliases in package.json imports, each src/ subfolder's entry is index.ts, barrels keep imports few; never ../ out of a directory
|
description: An alias owns its target (folder index.ts = folder and below, any other file = that file); no overlap; outside an area only the exact alias reaches it; ../ is banned in every file; no folder names in the rule
|
||||||
---
|
---
|
||||||
|
|
||||||
## `#` aliases
|
## `#` aliases own their targets
|
||||||
|
|
||||||
Shared code is imported through Node subpath imports declared in
|
Shared code is imported through Node subpath imports declared in
|
||||||
`package.json` `imports`, never by a relative path that climbs out of a
|
`package.json` `imports`. Node, TypeScript, Vite, Vite+'s config bundler, and
|
||||||
directory.
|
Oxlint's plugin loader all resolve them natively, so `.ts` source runs
|
||||||
|
unbuilt. `vp pack` inlines them, so they never reach package consumers.
|
||||||
|
|
||||||
**Targets (user rule, 2026-10-16):** an alias may point ONLY at a direct
|
One rule for every file, never tied to folder or file names (user rule
|
||||||
subfolder of `src/`: its `index.ts` (`"#helpers": "./src/helpers/index.ts"`)
|
2026-10-17; it replaces the earlier "aliases only point at direct `src/`
|
||||||
or a `#x/*` pattern inside that one folder. Never alias anything outside
|
subfolders" rule and the "import-check checks only aliased folders" scope):
|
||||||
`src/` (`scripts/`, root files), `src/` itself (no `#plugin` for
|
|
||||||
`./src/index.ts`), or a deeper folder (`./src/a/b/`). Code that would need
|
- **An alias may point anywhere** and owns its target. An alias to a
|
||||||
such an alias uses a relative import instead; `scripts/` and
|
folder's `index.ts` owns that folder and everything below it; an alias to
|
||||||
`vite.config.ts` import each other relatively. Layer rules
|
any other file owns only that file.
|
||||||
(`enforce-layer-order`) must still pass, so reorder entries if needed.
|
- **No overlap.** No file may be owned by 2 aliases: no alias inside another
|
||||||
|
alias's area, and no 2 aliases with the same target. import-check's
|
||||||
|
`enforce-layer-order` reports it once at the top of every file 2 aliases
|
||||||
|
claim. Fix by merging the aliases or moving files apart.
|
||||||
|
- **Outside an area, only the exact alias reaches it.** A deeper alias
|
||||||
|
specifier (`#a/one.ts`, `no-barrel-bypass`) and a relative path into the
|
||||||
|
area (`./a/one.ts`, `no-relative-bypass`; `./b/f.ts` to a file alias,
|
||||||
|
`prefer-alias-import`, fixed to the alias) are bypasses.
|
||||||
|
- **Inside an area, relative imports of siblings and subfolders are free.**
|
||||||
|
- **`../` is banned in every file**, owned or not (`no-parent-import`).
|
||||||
|
Anything above a file needs an alias.
|
||||||
|
- **Files no alias owns** need no `index.ts` and no alias, and import `./`
|
||||||
|
siblings and subfolders freely.
|
||||||
|
- Unchanged: no `/*` subpath patterns, the key order of `imports` is the
|
||||||
|
layer order `enforce-layer-order` checks (add a new alias at the right
|
||||||
|
layer), and entry files are `index.ts` only.
|
||||||
|
|
||||||
```jsonc
|
```jsonc
|
||||||
"imports": {
|
"imports": {
|
||||||
"#helpers": "./src/helpers/index.ts",
|
"#a": "./a/index.ts", // owns a/**
|
||||||
"#components/*": "./src/components/*"
|
"#f": "./b/f.ts", // owns only b/f.ts
|
||||||
|
"#a-sub": "./a/sub/index.ts", // no: inside #a
|
||||||
|
"#a2": "./a/index.ts" // no: same target as #a
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { typeNameTail } from "#helpers"; // yes
|
// in main.ts, which no alias owns
|
||||||
import { typeNameTail } from "../helpers/ast.ts"; // no: import/no-relative-parent-imports
|
import { a } from "#a"; // yes
|
||||||
|
import { one } from "#a/one.ts"; // no: deeper alias specifier
|
||||||
|
import { one } from "./a/one.ts"; // no: relative path into #a
|
||||||
|
import { f } from "#f"; // yes
|
||||||
|
import { f } from "./b/f.ts"; // no: relative path to a file alias
|
||||||
|
import { g } from "./b/g.ts"; // yes: unowned
|
||||||
|
// in a/one.ts, owned by #a
|
||||||
|
import { two } from "./two.ts"; // yes: sibling
|
||||||
|
import { deep } from "./sub/deep.ts"; // yes: subfolder of the same area
|
||||||
|
import { x } from "../x.ts"; // no: no ../ anywhere
|
||||||
```
|
```
|
||||||
|
|
||||||
Node, TypeScript, Vite, Vite+'s config bundler, and Oxlint's plugin loader
|
|
||||||
all resolve them natively, so `.ts` source runs unbuilt. `vp pack` inlines them,
|
|
||||||
so they never reach package consumers.
|
|
||||||
|
|
||||||
- Deno's trailing-slash mappings (`"#components/"`) don't exist in Node; they
|
|
||||||
become patterns, `"#components/*": "./src/components/*"`.
|
|
||||||
- Aliases point at `.ts` files only. There is no `#package` alias
|
- Aliases point at `.ts` files only. There is no `#package` alias
|
||||||
([[ts-only-files-and-imports]]).
|
([[ts-only-files-and-imports]]).
|
||||||
- In repos linted by import-check, the key order of `imports` is the layer
|
- Lint `jsPlugins` entries in `vite.config.ts` stay path strings, even when
|
||||||
order `enforce-layer-order` checks; add a new alias at the right layer.
|
the file has an alias.
|
||||||
- Keep test-only helpers in their own alias (e.g. `#testing`) so they never
|
- Keep test-only helpers in their own alias (e.g. `#testing`) so they never
|
||||||
land in a bundle.
|
land in a bundle.
|
||||||
|
|
||||||
## `index.ts` entries and barrels
|
## Barrels
|
||||||
|
|
||||||
- Every `src/` subfolder's entry file is `index.ts`, never `mod.ts` (decided
|
- An area used from outside exposes its API through its alias target, and
|
||||||
2026-10-12; import-check's `enforce-mod-file` requires it only in folders a
|
callers import the alias (`#ui`), not files inside it.
|
||||||
`#` alias owns). Folders outside `src/`, such as `scripts/`, have none.
|
|
||||||
- A directory used from outside exposes its API through its `index.ts`
|
|
||||||
barrel, and callers import the barrel (`#ui`), not files inside it
|
|
||||||
(`no-barrel-bypass`).
|
|
||||||
- Barrels are also the standard fix for `import/max-dependencies`: one barrel
|
- Barrels are also the standard fix for `import/max-dependencies`: one barrel
|
||||||
import replaces many direct ones ([[lint-overrides-policy]]).
|
import replaces many direct ones ([[lint-overrides-policy]]).
|
||||||
- `import/no-cycle` is on; a barrel must not create a cycle.
|
- `import/no-cycle` is on; a barrel must not create a cycle.
|
||||||
|
|
||||||
Relative imports inside one directory (`./sibling.ts`) are fine, with the
|
Relative imports are written with the `.ts` extension.
|
||||||
`.ts` extension written out.
|
|
||||||
|
|
||||||
## `scripts/` is flat (user rule, 2026-10-16)
|
## `scripts/` is flat (user rule, 2026-10-16)
|
||||||
|
|
||||||
- `scripts/` has no subfolders and no `index.ts`. Barrels and `index.ts`
|
- `scripts/` has no subfolders and no `index.ts`.
|
||||||
entries exist only inside `src/`; delete any `index.ts` outside it.
|
|
||||||
- Each tool is one file, `scripts/<name>.ts`, imported directly by file:
|
- Each tool is one file, `scripts/<name>.ts`, imported directly by file:
|
||||||
`import { decorators } from "./scripts/decorators.ts";` in
|
`import { decorators } from "./scripts/decorators.ts";` in
|
||||||
`vite.config.ts`.
|
`vite.config.ts`. No alias owns `scripts/`, so these `./` imports are free.
|
||||||
- Code in a former `scripts/<tool>/` folder is merged or renamed into flat
|
- Code in a former `scripts/<tool>/` folder is merged or renamed into flat
|
||||||
`scripts/<name>.ts` files. Non-TS assets (HTML, images, fixtures) move to
|
`scripts/<name>.ts` files. Non-TS assets (HTML, images, fixtures) move to
|
||||||
where they belong (next to the tool's output, `.agents/`, or a gitignored
|
where they belong (next to the tool's output, `.agents/`, or a gitignored
|
||||||
scratch dir). A subfolder that is really an app moves under `src/` as an
|
scratch dir). A subfolder that is really an app moves under an alias.
|
||||||
aliased folder.
|
|
||||||
- import-check 0.10.0+ checks folder layout only where the importer or the
|
|
||||||
target sits in a `#`-aliased folder, so `vite.config.ts` →
|
|
||||||
`./scripts/x.ts` passes without any override. Remove overrides that only
|
|
||||||
dodged import-check.
|
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ practice.
|
|||||||
off for `src/rules/index.ts`, the rule registry, which must import every
|
off for `src/rules/index.ts`, the rule registry, which must import every
|
||||||
rule. It is not a precedent for apps.
|
rule. It is not a precedent for apps.
|
||||||
- **`scripts/**`:** a narrow override is allowed when a tooling script truly
|
- **`scripts/**`:** a narrow override is allowed when a tooling script truly
|
||||||
needs it, e.g. `no-console` in a CLI script. Never one that only dodges
|
needs it, e.g. `no-console` in a CLI script.
|
||||||
import-check (e.g. `no-relative-bypass` for `vite.config.ts`); import-check
|
|
||||||
ignores folders no `#` alias owns.
|
|
||||||
- **Generated files:** list them in `ignorePatterns`, never `overrides`.
|
- **Generated files:** list them in `ignorePatterns`, never `overrides`.
|
||||||
- **Anything else needs the user's approval first.** When the user is away,
|
- **Anything else needs the user's approval first.** When the user is away,
|
||||||
don't add it: refactor, or leave the violation failing and log the question.
|
don't add it: refactor, or leave the violation failing and log the question.
|
||||||
@@ -45,3 +43,8 @@ pedantic hits in 274 files, of which `max-lines-per-function` 206,
|
|||||||
`max-lines` 145, `max-classes-per-file` 59, `max-dependencies` 45 (median 14
|
`max-lines` 145, `max-classes-per-file` 59, `max-dependencies` 45 (median 14
|
||||||
imports, worst 29). The user chose refactoring over raising limits or
|
imports, worst 29). The user chose refactoring over raising limits or
|
||||||
matching memona's old hugoalh limits (2048 lines per file, no function limit).
|
matching memona's old hugoalh limits (2048 lines per file, no function limit).
|
||||||
|
|
||||||
|
Alias rule: an alias may point anywhere and owns its target (a folder's
|
||||||
|
`index.ts` owns the folder and below, any other file owns only itself); no
|
||||||
|
two aliases may own the same file, and `../` is banned everywhere
|
||||||
|
([[imports-aliases-barrels]]).
|
||||||
|
|||||||
@@ -30,3 +30,9 @@ The `imports` key order can carry meaning: import-check's
|
|||||||
|
|
||||||
Refresh the lockfile with `npm install` (or `npm install
|
Refresh the lockfile with `npm install` (or `npm install
|
||||||
--package-lock-only` for a version bump) and commit it with the change.
|
--package-lock-only` for a version bump) and commit it with the change.
|
||||||
|
|
||||||
|
Alias rule: an alias may point anywhere and owns its target (a folder's
|
||||||
|
`index.ts` owns the folder and everything below it; any other file owns only
|
||||||
|
itself). No two aliases may own the same file, outside an area only the exact
|
||||||
|
alias reaches it, and `../` is banned in every file
|
||||||
|
([[imports-aliases-barrels]]).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
description: Vitest via vp test; unit tests sit next to code as *.test.ts, regression/integration tests in tests/ via the public entry; scripts/ has no tests
|
description: Vitest via vp test; unit tests sit next to code as *.test.ts, regression/integration tests in tests/ via the public entry (through its # alias, never ../); scripts/ has no tests
|
||||||
---
|
---
|
||||||
|
|
||||||
Decided 2026-10-14 for all repos.
|
Decided 2026-10-14 for all repos.
|
||||||
@@ -11,9 +11,12 @@ Decided 2026-10-14 for all repos.
|
|||||||
config needed. Packages publish only `dist/`, so colocated tests cost nothing
|
config needed. Packages publish only `dist/`, so colocated tests cost nothing
|
||||||
in the tarball.
|
in the tarball.
|
||||||
- **Regression and integration tests** live in the top-level `tests/` folder
|
- **Regression and integration tests** live in the top-level `tests/` folder
|
||||||
and use only the public entry point through a relative import
|
and use only the public entry point, imported through its `#` alias (e.g.
|
||||||
(`../src/index.ts`; no alias may target `src/` itself, see
|
`#plugin` for `./src/plugin/index.ts`), never internals and never `../`,
|
||||||
[[imports-aliases-barrels]]), never internals. `vp test tests` runs just these.
|
which is banned in every file. `vp test tests` runs just these
|
||||||
|
([[imports-aliases-barrels]]). In bot-agents, `tests/` imports only
|
||||||
|
`#testing` (`./src/testing.ts`), which re-exports the few source helpers
|
||||||
|
those tests need.
|
||||||
- **No in-source tests** (`import.meta.vitest`).
|
- **No in-source tests** (`import.meta.vitest`).
|
||||||
- **`scripts/` gets no tests** (2026-10-15): it is repo tooling, not
|
- **`scripts/` gets no tests** (2026-10-15): it is repo tooling, not
|
||||||
production code (`project-lint.ts`, `decorators.ts`, `lit-check.ts`,
|
production code (`project-lint.ts`, `decorators.ts`, `lit-check.ts`,
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ dependency. Keep the sources to erasable syntax and `.ts` imports, which
|
|||||||
type-checks, and `vp test` runs Vitest. Unit tests sit next to the code as
|
type-checks, and `vp test` runs Vitest. Unit tests sit next to the code as
|
||||||
`src/*.test.ts`; repository-level tests live in `tests/`.
|
`src/*.test.ts`; repository-level tests live in `tests/`.
|
||||||
|
|
||||||
`src/` is flat, so `package.json` has no `imports` aliases and modules, tests,
|
An alias in `package.json` `imports` owns its target: a folder's `index.ts`
|
||||||
and `scripts/` import each other relatively. If one is ever added, it may point
|
owns that folder and everything below it, any other file owns only that file.
|
||||||
only at a direct subfolder of `src/` (its `index.ts`, or a `#x/*` pattern
|
No file may be owned by 2 aliases. Outside an area, only the exact alias
|
||||||
inside it); never at `src/` itself, a deeper folder, or anything outside `src/`.
|
reaches it (no deeper `#` specifier, no relative path); inside it, relative
|
||||||
|
imports of siblings and subfolders are free. `../` is banned in every file
|
||||||
|
(`import/no-relative-parent-imports`). The one alias is `#testing`
|
||||||
|
(`./src/testing.ts`): every test imports it by alias, and `tests/` reaches
|
||||||
|
`src/` only through it. Other `src/` files import `./` siblings freely.
|
||||||
`scripts/` is flat: one file per tool, no subfolders and no `index.ts`
|
`scripts/` is flat: one file per tool, no subfolders and no `index.ts`
|
||||||
(barrels live only in `src/`), imported directly by file path.
|
(barrels live only in `src/`), imported directly by file path.
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "A Gitea composite action that runs a coding agent for approved issue and pull request automation.",
|
"description": "A Gitea composite action that runs a coding agent for approved issue and pull request automation.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"imports": {
|
||||||
|
"#testing": "./src/testing.ts"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/core": "^0.17.0",
|
"@eslint/core": "^0.17.0",
|
||||||
"@types/node": "^24.19.2",
|
"@types/node": "^24.19.2",
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ const rules: DummyRuleMap = {
|
|||||||
],
|
],
|
||||||
// The explicit-types template's extras.
|
// The explicit-types template's extras.
|
||||||
"import/no-cycle": "error",
|
"import/no-cycle": "error",
|
||||||
|
// `../` is banned in every file; anything above a file needs a `#` alias.
|
||||||
|
"import/no-relative-parent-imports": "error",
|
||||||
"typescript/consistent-type-imports": "error",
|
"typescript/consistent-type-imports": "error",
|
||||||
"typescript/no-import-type-side-effects": "error",
|
"typescript/no-import-type-side-effects": "error",
|
||||||
"typescript/no-non-null-assertion": "error",
|
"typescript/no-non-null-assertion": "error",
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ import {
|
|||||||
retryInvalidToken,
|
retryInvalidToken,
|
||||||
stageCodexAuth,
|
stageCodexAuth,
|
||||||
} from "./auth.ts";
|
} from "./auth.ts";
|
||||||
import { currentGid, currentUid, IS_WINDOWS, makeTempDir } from "./testing.ts";
|
import { currentGid, currentUid, IS_WINDOWS, makeTempDir } from "#testing";
|
||||||
|
|
||||||
test("retries once after an invalid refresh token", async () => {
|
test("retries once after an invalid refresh token", async () => {
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
readProfileFile,
|
readProfileFile,
|
||||||
} from "./isolation.ts";
|
} from "./isolation.ts";
|
||||||
import type { RuntimeProfile } from "./runtime.ts";
|
import type { RuntimeProfile } from "./runtime.ts";
|
||||||
import { currentUid, IS_ROOT, IS_WINDOWS, makeTempDir } from "./testing.ts";
|
import { currentUid, IS_ROOT, IS_WINDOWS, makeTempDir } from "#testing";
|
||||||
|
|
||||||
const profile = (uid: number, home: string): RuntimeProfile => ({
|
const profile = (uid: number, home: string): RuntimeProfile => ({
|
||||||
index: uid - 20_000,
|
index: uid - 20_000,
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import { isNotFound } from "./errors.ts";
|
|||||||
import { postRuntime } from "./post.ts";
|
import { postRuntime } from "./post.ts";
|
||||||
import type { PostContext } from "./post.ts";
|
import type { PostContext } from "./post.ts";
|
||||||
import type { RuntimeManifest, RuntimeOutcome } from "./runtime.ts";
|
import type { RuntimeManifest, RuntimeOutcome } from "./runtime.ts";
|
||||||
import { currentGid, currentUid, IS_WINDOWS, makeTempDir } from "./testing.ts";
|
import { currentGid, currentUid, IS_WINDOWS, makeTempDir } from "#testing";
|
||||||
|
|
||||||
const context: PostContext = {
|
const context: PostContext = {
|
||||||
apiUrl: "https://gitea.example/api/v1",
|
apiUrl: "https://gitea.example/api/v1",
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import { assert, expect, test } from "vite-plus/test";
|
|||||||
import { output } from "./command.ts";
|
import { output } from "./command.ts";
|
||||||
import { prepareRuntime } from "./runtime.ts";
|
import { prepareRuntime } from "./runtime.ts";
|
||||||
import type { RuntimeProfile } from "./runtime.ts";
|
import type { RuntimeProfile } from "./runtime.ts";
|
||||||
import { IS_ROOT, makeTempDir } from "./testing.ts";
|
import { IS_ROOT, makeTempDir } from "#testing";
|
||||||
|
|
||||||
const MASTER_KEY = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8";
|
const MASTER_KEY = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8";
|
||||||
const ENCRYPTED = "v1:ICEiIyQlJicoKSor:oV_FAgnsN3p1FyegJxwGVdyjfyPio6qDeSUwbA";
|
const ENCRYPTED = "v1:ICEiIyQlJicoKSor:oV_FAgnsN3p1FyegJxwGVdyjfyPio6qDeSUwbA";
|
||||||
|
|||||||
+8
-1
@@ -1,4 +1,6 @@
|
|||||||
// Test support: the current user's IDs and a fresh temporary directory.
|
// Test support: the current user's IDs and a fresh temporary directory, plus
|
||||||
|
// the source helpers that the repository-level tests in tests/ need. Those
|
||||||
|
// tests sit outside src/ and reach it only through the `#testing` alias.
|
||||||
import { mkdtemp } from "node:fs/promises";
|
import { mkdtemp } from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
@@ -21,3 +23,8 @@ export const IS_WINDOWS: boolean = process.platform === "win32";
|
|||||||
export function makeTempDir(): Promise<string> {
|
export function makeTempDir(): Promise<string> {
|
||||||
return mkdtemp(join(tmpdir(), "bot-agents-test-"));
|
return mkdtemp(join(tmpdir(), "bot-agents-test-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { output } from "./command.ts";
|
||||||
|
export { isNotFound } from "./errors.ts";
|
||||||
|
export { isMapping } from "./json.ts";
|
||||||
|
export type { JsonObject } from "./json.ts";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
|
|||||||
|
|
||||||
import { expect, test } from "vite-plus/test";
|
import { expect, test } from "vite-plus/test";
|
||||||
|
|
||||||
import { output } from "../src/command.ts";
|
import { output } from "#testing";
|
||||||
|
|
||||||
// Each phase runs as `node src/<phase>.ts` with no build step. Node loads the
|
// Each phase runs as `node src/<phase>.ts` with no build step. Node loads the
|
||||||
// whole import graph, stripping types, before the phase reads its first
|
// whole import graph, stripping types, before the phase reads its first
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import process from "node:process";
|
|||||||
import { assert, expect, test } from "vite-plus/test";
|
import { assert, expect, test } from "vite-plus/test";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
|
|
||||||
import { output } from "../src/command.ts";
|
import { isMapping, isNotFound, makeTempDir, output } from "#testing";
|
||||||
import { isNotFound } from "../src/errors.ts";
|
import type { JsonObject } from "#testing";
|
||||||
import { isMapping } from "../src/json.ts";
|
|
||||||
import type { JsonObject } from "../src/json.ts";
|
|
||||||
import { makeTempDir } from "../src/testing.ts";
|
|
||||||
|
|
||||||
const mapping = (value: unknown, at: string): JsonObject => {
|
const mapping = (value: unknown, at: string): JsonObject => {
|
||||||
assert(isMapping(value), at);
|
assert(isMapping(value), at);
|
||||||
|
|||||||
Reference in New Issue
Block a user