mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
test: simplify grit policy fixtures
This commit is contained in:
parent
8ae6aab1fb
commit
805e7bfdf4
40 changed files with 233 additions and 183 deletions
|
|
@ -195,7 +195,6 @@
|
|||
"scripts/**/*.mjs",
|
||||
"src/**/*.{css,ts,tsx}",
|
||||
"tests/**/*.{mjs,ts,tsx}",
|
||||
"!tests/fixtures/grit-policy",
|
||||
"!src/generated"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ Use focused scripts while iterating, but run `npm run check` before handoff. CI
|
|||
|
||||
Keep rule suppressions local and include the Obsidian-specific reason when a native Obsidian UI pattern intentionally diverges from a generic browser rule.
|
||||
|
||||
Grit policy fixtures protect matcher semantics rather than diagnostic wording, source locations, or the exact shape of Biome configuration. Change the checked-in valid and invalid fixtures only when a policy's accepted or rejected source shape intentionally changes.
|
||||
Grit policy cases protect matcher semantics rather than diagnostic wording, source locations, or the exact shape of Biome configuration. Keep one valid and invalid source case for each policy, and change a case only when that policy's accepted or rejected source shape intentionally changes.
|
||||
|
||||
## Generated and Loaded Files
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"entry": ["src/main.ts", "scripts/**/*.mjs", "*.config.{ts,mjs}", "tests/**/*.test.{ts,tsx}"],
|
||||
"project": ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}", "scripts/**/*.mjs", "*.config.{ts,mjs}"],
|
||||
"ignore": ["src/generated/**", "tests/fixtures/grit-policy/**"],
|
||||
"ignore": ["src/generated/**"],
|
||||
"ignoreBinaries": ["codex", "taskkill"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import type { Client } from "../connection/client";
|
||||
import type { Root } from "../root";
|
||||
|
||||
export type Escape = Client | Root;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import "obsidian";
|
||||
|
||||
export { value } from "./owner";
|
||||
export * from "./barrel";
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { Panel } from "../panel/toolbar-actions";
|
||||
|
||||
export type Escape = Panel;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { Catalog } from "../../../../../../app-server/protocol/catalog";
|
||||
|
||||
export type Escape = Catalog;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import type { Protocol } from "../../../../app-server/protocol/catalog";
|
||||
import type { Host } from "../host/contracts";
|
||||
import type { Workspace } from "../../../../workspace/panel-coordinator";
|
||||
|
||||
export type Escape = Protocol | Host | Workspace;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export const checkedAt = Date.now();
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { View } from "../presentation/thread-stream/view-model";
|
||||
|
||||
export type Escape = View;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { Runtime } from "../host/session-runtime";
|
||||
|
||||
export type Escape = Runtime;
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
import { signal } from "@preact/signals";
|
||||
import type { State } from "../application/state/store";
|
||||
import type { ReadModel } from "../panel/shell-read-model";
|
||||
|
||||
export const escaped = signal(1);
|
||||
export type Escape = State | ReadModel | Signal<string>;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import type { State } from "../application/state/store";
|
||||
import { addDomEventListener } from "../../../../shared/dom/events.dom";
|
||||
import { renderPreactRoot } from "../../../../shared/dom/preact-root.dom";
|
||||
|
||||
export function Escape(props: { state: State }): JSX.Element {
|
||||
document.body.append(document.createElement("div"));
|
||||
void addDomEventListener;
|
||||
void renderPreactRoot;
|
||||
return <input defaultValue={String(props.state)} />;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
import type { Connection } from "../../app-server/connection/client";
|
||||
import type { Workspace } from "../../workspace/panel-coordinator";
|
||||
|
||||
export type Escape = Connection | Workspace;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export const misplaced = 1;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { Client } from "../../../app-server/connection/client";
|
||||
|
||||
export type Escape = Client;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
class Runner {
|
||||
constructor(readonly stop: () => void) {}
|
||||
}
|
||||
|
||||
export const runner = new Runner(() => runner.stop());
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import type { Generated } from "../generated/app-server/types";
|
||||
|
||||
export async function read(client: { request(method: string): Promise<void> }): Promise<void> {
|
||||
await client.request("config/read");
|
||||
}
|
||||
|
||||
export type Escape = Generated;
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import type { Feature } from "../../features/escape";
|
||||
|
||||
export function first<T>(iterator: Iterator<T>): T {
|
||||
return iterator.next().value as T;
|
||||
}
|
||||
|
||||
export type EscapedFeature = Feature;
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#codex-panel-fixture * {
|
||||
color: #fff;
|
||||
padding: 12px;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import type { ChatState } from "../features/chat/application/state/store";
|
||||
|
||||
export type Escape = ChatState;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export interface ProtocolValue {
|
||||
id: string;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function serviceValue(): string {
|
||||
return "safe";
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type DomainValue = string;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function convert(value: string): string {
|
||||
return value;
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export interface ChatStateValue {
|
||||
ready: boolean;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type HostValue = string;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type PanelValue = string;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type PresentationValue = string;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function label(value: string): string {
|
||||
return value.trim();
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function SafeView(): JSX.Element {
|
||||
return <span>Safe</span>;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type RewriteValue = string;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type WorkflowValue = string;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function createRuntime(): object {
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type SettingsValue = string;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export function identity<T>(value: T): T {
|
||||
return value;
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
.codex-panel-fixture {
|
||||
color: var(--text-normal);
|
||||
padding: var(--size-4-2);
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export type WorkspaceValue = string;
|
||||
|
|
@ -1,13 +1,198 @@
|
|||
import { spawnSync } from "node:child_process";
|
||||
import { cp, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const biomeBin = path.join(repoRoot, "node_modules", ".bin", "biome");
|
||||
const fixtureRoot = path.join(repoRoot, "tests", "fixtures", "grit-policy");
|
||||
const workspaces = new Set();
|
||||
let projectPluginsPromise;
|
||||
|
||||
const policyCases = [
|
||||
policyCase("no-handwritten-reexports.grit", "src/example.ts", 'export { value } from "./owner";', "const value = 1;\nexport { value };"),
|
||||
policyCase("no-responsibility-root-module-files.grit", "src/domain/escape.ts", "export const value = 1;", "export const value = 1;", {
|
||||
validPath: "src/domain/example/safe.ts",
|
||||
}),
|
||||
policyCase(
|
||||
"no-misplaced-tsx.grit",
|
||||
"src/features/threads/escape.tsx",
|
||||
"export const value = 1;",
|
||||
"export function Safe(): JSX.Element { return <span />; }",
|
||||
{
|
||||
validPath: "src/features/chat/ui/safe.tsx",
|
||||
},
|
||||
),
|
||||
policyCase(
|
||||
"no-self-referential-initializer-callback.grit",
|
||||
"src/example.ts",
|
||||
"const runner = new Runner(() => runner.stop());",
|
||||
"let runner: Runner;\nrunner = new Runner(() => runner.stop());",
|
||||
),
|
||||
policyCase(
|
||||
"no-unsafe-iterator-value.grit",
|
||||
"src/example.ts",
|
||||
"export const value = iterator.next().value;",
|
||||
"const result = iterator.next();\nexport const value = result.done ? undefined : result.value;",
|
||||
),
|
||||
policyCase(
|
||||
"no-uncontrolled-preact-form-state.grit",
|
||||
"src/features/chat/ui/form.tsx",
|
||||
'export function Form(): JSX.Element { return <input defaultValue="draft" />; }',
|
||||
'export function Form(): JSX.Element { return <input value="draft" />; }',
|
||||
),
|
||||
policyCase(
|
||||
"no-domain-outer-layer-imports.grit",
|
||||
"src/domain/example/value.ts",
|
||||
'import type { App } from "obsidian";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-lower-level-feature-imports.grit",
|
||||
"src/shared/runtime/escape.ts",
|
||||
'import type { Feature } from "../../features/escape";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-app-server-subfolder-root-imports.grit",
|
||||
"src/app-server/protocol/escape.ts",
|
||||
'import type { Root } from "../root";',
|
||||
'import type { Client } from "../connection/client";',
|
||||
),
|
||||
policyCase(
|
||||
"no-app-server-connection-boundary-imports.grit",
|
||||
"src/app-server/protocol/escape.ts",
|
||||
'import type { Client } from "../connection/client";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-app-server-protocol-boundary-imports.grit",
|
||||
"src/settings/escape.ts",
|
||||
'import type { Catalog } from "../app-server/protocol/catalog";',
|
||||
'import type { Catalog } from "../app-server/services/catalog";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-turn-item-non-turn-protocol-imports.grit",
|
||||
"src/features/chat/app-server/mappers/thread-stream/turn-items.ts",
|
||||
'import type { Catalog } from "../../../../../../app-server/protocol/catalog";',
|
||||
'import type { Turn } from "../../../../../../app-server/protocol/turn";',
|
||||
),
|
||||
policyCase(
|
||||
"no-generated-app-server-boundary-imports.grit",
|
||||
"src/settings/escape.ts",
|
||||
'import type { Generated } from "../generated/app-server/types";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-app-server-direct-rpcs.grit",
|
||||
"src/settings/escape.ts",
|
||||
'export async function read(client) { await client.request("config/read"); }',
|
||||
"export async function read(service) { await service.readConfig(); }",
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-workspace-boundary-imports.grit",
|
||||
"src/features/chat/host/escape.ts",
|
||||
'import type { Workspace } from "../../../workspace/panel-coordinator";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-feature-workspace-boundary-imports.grit",
|
||||
"src/features/selection-rewrite/escape.ts",
|
||||
'import type { Workspace } from "../../workspace/panel-coordinator";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-workspace-chat-internal-imports.grit",
|
||||
"src/workspace/escape.ts",
|
||||
'import type { State } from "../features/chat/application/state/store";',
|
||||
'import type { Host } from "../features/chat/host/contracts";',
|
||||
),
|
||||
policyCase(
|
||||
"no-thread-workflow-app-server-imports.grit",
|
||||
"src/features/threads/workflows/escape.ts",
|
||||
'import type { Client } from "../../../app-server/connection/client";',
|
||||
'import type { Transport } from "./ports";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-application-outer-layer-imports.grit",
|
||||
"src/features/chat/application/escape.ts",
|
||||
'import type { Host } from "../host/contracts";',
|
||||
'import type { Item } from "../domain/thread-stream/items";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-app-server-outer-layer-imports.grit",
|
||||
"src/features/chat/app-server/escape.ts",
|
||||
'import type { Panel } from "../panel/toolbar-actions";',
|
||||
'import type { Store } from "../application/state/store";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-host-rendering-layer-imports.grit",
|
||||
"src/features/chat/host/escape.ts",
|
||||
'import type { View } from "../presentation/thread-stream/view-model";',
|
||||
'import type { Store } from "../application/state/store";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-panel-runtime-boundary-imports.grit",
|
||||
"src/features/chat/panel/escape.ts",
|
||||
'import type { Host } from "../host/contracts";',
|
||||
'import type { View } from "../presentation/thread-stream/view-model";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-presentation-outer-layer-imports.grit",
|
||||
"src/features/chat/presentation/escape.ts",
|
||||
'import type { Store } from "../application/state/store";',
|
||||
'import type { Item } from "../domain/thread-stream/items";',
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-ui-outer-layer-imports.grit",
|
||||
"src/features/chat/ui/escape.ts",
|
||||
'import type { Store } from "../application/state/store";',
|
||||
'import type { View } from "../presentation/thread-stream/view-model";',
|
||||
),
|
||||
policyCase(
|
||||
"no-preact-signal-imports.grit",
|
||||
"src/settings/escape.ts",
|
||||
'import { signal } from "@preact/signals";',
|
||||
"export const value = 1;",
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-shell-read-model-imports.grit",
|
||||
"src/features/chat/presentation/escape.ts",
|
||||
'import type { ReadModel } from "../panel/shell-read-model";',
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-chat-signal-type-references.grit",
|
||||
"src/features/chat/presentation/escape.ts",
|
||||
"export type Value = Signal<string>;",
|
||||
"export type Value = string;",
|
||||
),
|
||||
policyCase(
|
||||
"no-state-module-side-effects.grit",
|
||||
"src/features/chat/application/state/escape.ts",
|
||||
"export const now = Date.now();",
|
||||
"export const now = 1;",
|
||||
),
|
||||
policyCase(
|
||||
"no-implicit-dom-bridges.grit",
|
||||
"src/features/chat/ui/escape.ts",
|
||||
'export const element = document.createElement("div");',
|
||||
"export const value = 1;",
|
||||
),
|
||||
policyCase(
|
||||
"no-dom-events-imports.grit",
|
||||
"src/features/chat/ui/escape.ts",
|
||||
'import { addDomEventListener } from "../../../shared/dom/events.dom";',
|
||||
"export const value = 1;",
|
||||
),
|
||||
policyCase(
|
||||
"no-preact-root-imports.grit",
|
||||
"src/features/chat/ui/escape.ts",
|
||||
'import { renderPreactRoot } from "../../../shared/dom/preact-root.dom";',
|
||||
"export const value = 1;",
|
||||
),
|
||||
policyCase("no-restricted-css-policy.grit", "src/styles/escape.css", ".escape { color: #fff; }", ".safe { color: var(--text-normal); }"),
|
||||
];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([...workspaces].map((workspace) => rm(workspace, { recursive: true, force: true })));
|
||||
|
|
@ -15,48 +200,53 @@ afterEach(async () => {
|
|||
});
|
||||
|
||||
describe("Biome Grit policies", () => {
|
||||
it("wires every checked-in policy once with a non-empty scope", async () => {
|
||||
const config = parseJsonc(await readFile(path.join(repoRoot, "biome.jsonc"), "utf8"));
|
||||
const configured = config.plugins.map((plugin) => ({
|
||||
path: typeof plugin === "string" ? plugin : plugin.path,
|
||||
includes: typeof plugin === "string" ? [] : plugin.includes,
|
||||
}));
|
||||
const checkedIn = await gritPolicyPaths(path.join(repoRoot, "scripts", "grit"));
|
||||
it("keeps one semantic case for every configured policy", async () => {
|
||||
const configured = (await projectPlugins()).map((plugin) => path.basename(pluginPath(plugin))).sort();
|
||||
const covered = policyCases.map((testCase) => testCase.plugin).sort();
|
||||
|
||||
expect(new Set(configured.map((plugin) => plugin.path)).size).toBe(configured.length);
|
||||
expect(configured.every((plugin) => plugin.includes.length > 0)).toBe(true);
|
||||
expect(configured.map((plugin) => plugin.path.replace(/^\.\//, "")).sort()).toEqual(checkedIn);
|
||||
expect(new Set(configured).size).toBe(configured.length);
|
||||
expect(new Set(covered).size).toBe(covered.length);
|
||||
expect(covered).toEqual(configured);
|
||||
});
|
||||
|
||||
it("rejects the checked-in invalid fixture for every retained matcher", async () => {
|
||||
const config = parseJsonc(await readFile(path.join(repoRoot, "biome.jsonc"), "utf8"));
|
||||
for (const plugin of config.plugins) {
|
||||
const workspace = await fixtureWorkspace();
|
||||
await writePluginConfig(workspace, plugin);
|
||||
const result = biomeLint(workspace, await fixtureSourcePaths(workspace));
|
||||
expect(result.status, `${pluginPath(plugin)}\n${result.output}`).toBe(1);
|
||||
expect(result.pluginErrors, `${pluginPath(plugin)}\n${result.output}`).toBeGreaterThan(0);
|
||||
}
|
||||
}, 30_000);
|
||||
it.each(policyCases)("$plugin rejects its invalid source", async (testCase) => {
|
||||
const result = await lintPolicyCase(testCase, "invalid");
|
||||
|
||||
it("accepts the checked-in valid fixture for every retained matcher", async () => {
|
||||
const config = parseJsonc(await readFile(path.join(repoRoot, "biome.jsonc"), "utf8"));
|
||||
for (const plugin of config.plugins) {
|
||||
const workspace = await fixtureWorkspace("valid");
|
||||
await writePluginConfig(workspace, plugin);
|
||||
const result = biomeLint(workspace, await fixtureSourcePaths(workspace));
|
||||
expect(result.status, `${pluginPath(plugin)}\n${result.output}`).toBe(0);
|
||||
expect(result.pluginErrors, pluginPath(plugin)).toBe(0);
|
||||
}
|
||||
}, 30_000);
|
||||
expect(result.status, result.output).toBe(1);
|
||||
expect(result.pluginErrors, result.output).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it.each(policyCases)("$plugin accepts its valid source", async (testCase) => {
|
||||
const result = await lintPolicyCase(testCase, "valid");
|
||||
|
||||
expect(result.status, result.output).toBe(0);
|
||||
expect(result.pluginErrors, result.output).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
async function fixtureWorkspace(kind = "invalid") {
|
||||
const parent = await mkdtemp(path.join(tmpdir(), "codex-panel-grit-policy-"));
|
||||
const workspace = path.join(parent, "fixture");
|
||||
workspaces.add(parent);
|
||||
await cp(path.join(fixtureRoot, kind), workspace, { recursive: true });
|
||||
return workspace;
|
||||
function policyCase(plugin, invalidPath, invalidSource, validSource, options = {}) {
|
||||
return {
|
||||
plugin,
|
||||
invalid: { path: invalidPath, source: invalidSource },
|
||||
valid: { path: options.validPath ?? invalidPath, source: validSource },
|
||||
};
|
||||
}
|
||||
|
||||
async function lintPolicyCase(testCase, variant) {
|
||||
const plugin = (await projectPlugins()).find((candidate) => path.basename(pluginPath(candidate)) === testCase.plugin);
|
||||
if (!plugin) throw new Error(`Missing configured Grit policy: ${testCase.plugin}`);
|
||||
const workspace = await mkdtemp(path.join(tmpdir(), "codex-panel-grit-policy-"));
|
||||
workspaces.add(workspace);
|
||||
const fixture = testCase[variant];
|
||||
const fixturePath = path.join(workspace, fixture.path);
|
||||
await mkdir(path.dirname(fixturePath), { recursive: true });
|
||||
await Promise.all([writeFile(fixturePath, fixture.source), writePluginConfig(workspace, plugin)]);
|
||||
return biomeLint(workspace, fixture.path);
|
||||
}
|
||||
|
||||
async function projectPlugins() {
|
||||
projectPluginsPromise ??= readFile(path.join(repoRoot, "biome.jsonc"), "utf8").then((source) => parseJsonc(source).plugins);
|
||||
return projectPluginsPromise;
|
||||
}
|
||||
|
||||
async function writePluginConfig(workspace, plugin) {
|
||||
|
|
@ -68,8 +258,8 @@ async function writePluginConfig(workspace, plugin) {
|
|||
await writeFile(path.join(workspace, "biome.json"), JSON.stringify(config));
|
||||
}
|
||||
|
||||
function biomeLint(workspace, targets) {
|
||||
const result = spawnSync(biomeBin, ["lint", ...targets, "--config-path", workspace, "--reporter=json", "--max-diagnostics=none"], {
|
||||
function biomeLint(workspace, target) {
|
||||
const result = spawnSync(biomeBin, ["lint", target, "--config-path", workspace, "--reporter=json", "--max-diagnostics=none"], {
|
||||
cwd: workspace,
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
|
@ -81,36 +271,10 @@ function biomeLint(workspace, targets) {
|
|||
};
|
||||
}
|
||||
|
||||
async function fixtureSourcePaths(workspace, directory = "src") {
|
||||
const { readdir } = await import("node:fs/promises");
|
||||
const entries = await readdir(path.join(workspace, directory), { withFileTypes: true });
|
||||
const paths = await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const relativePath = path.join(directory, entry.name);
|
||||
return entry.isDirectory() ? fixtureSourcePaths(workspace, relativePath) : [relativePath];
|
||||
}),
|
||||
);
|
||||
return paths.flat();
|
||||
}
|
||||
|
||||
function pluginPath(plugin) {
|
||||
return typeof plugin === "string" ? plugin : plugin.path;
|
||||
}
|
||||
|
||||
async function gritPolicyPaths(directory) {
|
||||
const { readdir } = await import("node:fs/promises");
|
||||
const entries = await readdir(directory, { withFileTypes: true });
|
||||
const paths = await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const entryPath = path.join(directory, entry.name);
|
||||
if (entry.isDirectory()) return gritPolicyPaths(entryPath);
|
||||
if (entry.isFile() && entry.name.endsWith(".grit")) return [path.relative(repoRoot, entryPath).split(path.sep).join("/")];
|
||||
return [];
|
||||
}),
|
||||
);
|
||||
return paths.flat().sort();
|
||||
}
|
||||
|
||||
function parseJsonc(source) {
|
||||
return JSON.parse(source.replace(/^\s*\/\/.*$/gm, ""));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,5 @@
|
|||
"types": ["node", "vitest"],
|
||||
"lib": ["ES2022", "DOM"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx", "vitest.config.ts"],
|
||||
"exclude": ["tests/fixtures/grit-policy/**"]
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx", "vitest.config.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue