ox-dep-lint 0.1.0 → 0.1.1
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/CHANGELOG.md +11 -0
- package/dist/check-GClvJhK3.mjs +168 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.mjs +57 -0
- package/dist/config-CpuQhyZE.d.mts +110 -0
- package/dist/engine-7N-4EtzS.mjs +1039 -0
- package/dist/index.d.mts +170 -0
- package/dist/index.mjs +3 -0
- package/dist/plugin.d.mts +42 -0
- package/dist/plugin.mjs +119 -0
- package/package.json +6 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- 0.1.0 was published without its built `dist` files, so neither the CLI nor the plugin could load. A `prepack` script now builds before every pack and publish, so a package cannot go out without them. Use 0.1.1.
|
|
6
|
+
- The `bin` path is written as npm stores it (`dist/cli.mjs`), so publishing no longer warns that it was corrected.
|
|
7
|
+
- First release published from GitHub Actions through npm trusted publishing, with provenance.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
- First release: the oxlint plugin `dep-lint/imports` and the `ox-dep-lint` CLI, sharing one engine. See the README for the rules, the configuration and what is not covered yet.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { a as findingsForFile, c as withoutBaselined, n as createEngine, r as edgeRefusal, s as reads, t as baselineCovers, u as directoriesMatching } from "./engine-7N-4EtzS.mjs";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import nodePath from "node:path";
|
|
4
|
+
// src/graph.ts
|
|
5
|
+
/**
|
|
6
|
+
* Checks that need the whole workspace rather than one file: edges declared only in `package.json`, cycles between
|
|
7
|
+
* packages, packages no kind holds, and baseline entries no finding needs any more. Only the CLI runs these, because an
|
|
8
|
+
* oxlint plugin sees one file at a time and never sees `package.json`.
|
|
9
|
+
*/
|
|
10
|
+
const runtimeFields = [
|
|
11
|
+
"dependencies",
|
|
12
|
+
"optionalDependencies",
|
|
13
|
+
"peerDependencies"
|
|
14
|
+
];
|
|
15
|
+
const graphFindings = (engine) => {
|
|
16
|
+
const { config, workspace } = engine;
|
|
17
|
+
const findings = [];
|
|
18
|
+
const at = {
|
|
19
|
+
line: 1,
|
|
20
|
+
column: 0
|
|
21
|
+
};
|
|
22
|
+
for (const problem of workspace.problems) findings.push({
|
|
23
|
+
rule: "invalid-package",
|
|
24
|
+
file: `${problem.directory}/package.json`,
|
|
25
|
+
...at,
|
|
26
|
+
message: problem.message,
|
|
27
|
+
target: problem.directory
|
|
28
|
+
});
|
|
29
|
+
const members = /* @__PURE__ */ new Set([...workspace.packages.map((entry) => entry.directory), ...workspace.problems.map((problem) => problem.directory)]);
|
|
30
|
+
for (const kind of Object.values(config.kinds)) for (const pattern of kind.packages) for (const directory of directoriesMatching(workspace.root, pattern)) if (!members.has(directory) && existsSync(nodePath.join(workspace.root, directory, "package.json"))) {
|
|
31
|
+
members.add(directory);
|
|
32
|
+
findings.push({
|
|
33
|
+
rule: "package-outside-workspace",
|
|
34
|
+
file: `${directory}/package.json`,
|
|
35
|
+
...at,
|
|
36
|
+
message: `${directory} is a package a kind names, but the root package.json's workspaces leave it out, so nothing in it is checked`,
|
|
37
|
+
target: directory
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
for (const entry of workspace.packages) {
|
|
41
|
+
const manifest = `${entry.directory}/package.json`;
|
|
42
|
+
if (engine.kindOf(entry) === void 0) {
|
|
43
|
+
findings.push({
|
|
44
|
+
rule: "package-without-kind",
|
|
45
|
+
file: manifest,
|
|
46
|
+
...at,
|
|
47
|
+
message: `${entry.name} matches no kind: add its directory to one`,
|
|
48
|
+
target: entry.name
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const kindRule = config.kinds[engine.kindOf(entry) ?? ""];
|
|
53
|
+
const extra = config.packages[entry.name]?.externals ?? [];
|
|
54
|
+
for (const field of config.imports.shippedFields) for (const name of entry.manifest[field]) {
|
|
55
|
+
const outside = !workspace.byName.has(name) && !config.workspaceScopes.some((scope) => name.startsWith(scope));
|
|
56
|
+
const installed = entry.aliases.get(name) ?? name;
|
|
57
|
+
if (outside && kindRule !== void 0 && kindRule.externals !== "any" && !kindRule.externals.includes(installed) && !extra.includes(installed)) findings.push({
|
|
58
|
+
rule: "forbidden-external",
|
|
59
|
+
file: manifest,
|
|
60
|
+
...at,
|
|
61
|
+
message: `${field} names ${installed === name ? name : `${name} (${installed})`}, which its kind does not allow`,
|
|
62
|
+
target: installed
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
for (const field of config.imports.manifestEdgeFields) for (const name of entry.manifest[field]) {
|
|
66
|
+
const to = workspace.byName.get(name);
|
|
67
|
+
if (to === void 0) {
|
|
68
|
+
if (config.workspaceScopes.some((scope) => name.startsWith(scope))) findings.push({
|
|
69
|
+
rule: "unknown-workspace-package",
|
|
70
|
+
file: manifest,
|
|
71
|
+
...at,
|
|
72
|
+
message: `${field} names ${name}, which is not a workspace package`,
|
|
73
|
+
target: name
|
|
74
|
+
});
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const refusal = edgeRefusal(engine, entry, to, true);
|
|
78
|
+
if (refusal !== void 0 && to.name !== entry.name) findings.push({
|
|
79
|
+
rule: refusal.rule,
|
|
80
|
+
file: manifest,
|
|
81
|
+
...at,
|
|
82
|
+
message: `${field}: ${refusal.why}`,
|
|
83
|
+
target: name
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
findings.push(...packageCycles(engine));
|
|
88
|
+
return findings;
|
|
89
|
+
};
|
|
90
|
+
/** Cycles between workspace packages through their runtime dependency fields, each reported once. */
|
|
91
|
+
const packageCycles = (engine) => {
|
|
92
|
+
const { workspace } = engine;
|
|
93
|
+
const edges = new Map(workspace.packages.map((entry) => [entry.name, [...new Set(runtimeFields.flatMap((field) => entry.manifest[field]))].filter((name) => workspace.byName.has(name) && name !== entry.name)]));
|
|
94
|
+
const state = /* @__PURE__ */ new Map();
|
|
95
|
+
const findings = [];
|
|
96
|
+
const seen = /* @__PURE__ */ new Set();
|
|
97
|
+
const visit = (name, path) => {
|
|
98
|
+
if (state.get(name) === "done") return;
|
|
99
|
+
if (state.get(name) === "visiting") {
|
|
100
|
+
const cycle = [...path.slice(path.indexOf(name)), name];
|
|
101
|
+
const key = cycle.slice(0, -1).toSorted().join(" ");
|
|
102
|
+
if (!seen.has(key)) {
|
|
103
|
+
seen.add(key);
|
|
104
|
+
const first = workspace.byName.get(name);
|
|
105
|
+
findings.push({
|
|
106
|
+
rule: "package-cycle",
|
|
107
|
+
file: `${first?.directory ?? name}/package.json`,
|
|
108
|
+
line: 1,
|
|
109
|
+
column: 0,
|
|
110
|
+
message: `dependency cycle: ${cycle.join(" -> ")}`,
|
|
111
|
+
target: key
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
state.set(name, "visiting");
|
|
117
|
+
for (const next of edges.get(name) ?? []) visit(next, [...path, name]);
|
|
118
|
+
state.set(name, "done");
|
|
119
|
+
};
|
|
120
|
+
for (const name of [...edges.keys()].toSorted()) visit(name, []);
|
|
121
|
+
return findings;
|
|
122
|
+
};
|
|
123
|
+
/** Baseline entries that no finding in `findings` needs; each is a finding of its own, so the baseline only shrinks. */
|
|
124
|
+
const staleBaseline = (engine, findings) => engine.config.baseline.filter((entry) => !findings.some((finding) => baselineCovers(entry, finding))).map((entry) => ({
|
|
125
|
+
rule: "stale-baseline",
|
|
126
|
+
file: entry.file,
|
|
127
|
+
line: 1,
|
|
128
|
+
column: 0,
|
|
129
|
+
message: `the baseline entry for ${entry.rule}${entry.target === void 0 ? "" : ` (${entry.target})`} is no longer needed: remove it`,
|
|
130
|
+
target: entry.rule
|
|
131
|
+
}));
|
|
132
|
+
// src/check.ts
|
|
133
|
+
/** The whole check: every source file of the workspace through the engine, plus the whole-graph checks and the baseline. */
|
|
134
|
+
const isFile = (path) => {
|
|
135
|
+
try {
|
|
136
|
+
return statSync(path).isFile();
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Every source file the engine reads, found by walking each package's directory (skipping `node_modules` and dot
|
|
143
|
+
* directories). A walk rather than `git ls-files`: generated and gitignored sources still ship, and a nested repository
|
|
144
|
+
* or submodule inside a package is still part of it.
|
|
145
|
+
*/
|
|
146
|
+
const listSourceFiles = (engine) => {
|
|
147
|
+
const files = /* @__PURE__ */ new Set();
|
|
148
|
+
const walk = (relative) => {
|
|
149
|
+
for (const entry of readdirSync(nodePath.join(engine.workspace.root, relative), { withFileTypes: true })) {
|
|
150
|
+
const path = `${relative}/${entry.name}`;
|
|
151
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) walk(path);
|
|
152
|
+
else if ((entry.isFile() || entry.isSymbolicLink() && isFile(nodePath.join(engine.workspace.root, path))) && reads(engine, path)) files.add(path);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
for (const entry of engine.workspace.packages) walk(entry.directory);
|
|
156
|
+
return [...files].toSorted();
|
|
157
|
+
};
|
|
158
|
+
const checkWorkspace = (root, config) => {
|
|
159
|
+
const engine = createEngine(root, config);
|
|
160
|
+
const files = listSourceFiles(engine);
|
|
161
|
+
const all = [...files.flatMap((file) => findingsForFile(engine, file, readFileSync(nodePath.join(root, file), "utf8"))).filter((finding) => finding.rule !== "package-without-kind"), ...graphFindings(engine)];
|
|
162
|
+
return {
|
|
163
|
+
files: files.length,
|
|
164
|
+
findings: [...withoutBaselined(engine, all), ...staleBaseline(engine, all)]
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
const formatFinding = (finding) => `${finding.file}:${finding.line}:${finding.column + 1} ${finding.rule}: ${finding.message}`;
|
|
168
|
+
export { staleBaseline as a, graphFindings as i, formatFinding as n, listSourceFiles as r, checkWorkspace as t };
|
package/dist/cli.d.mts
ADDED
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { d as findWorkspaceRoot } from "./engine-7N-4EtzS.mjs";
|
|
3
|
+
import { n as formatFinding, t as checkWorkspace } from "./check-GClvJhK3.mjs";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import nodePath from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
/**
|
|
10
|
+
* `ox-dep-lint [--root <dir>] [--config <file>]`: checks the workspace and exits 1 on any finding. The configuration is
|
|
11
|
+
* `ox-dep-lint.config.{ts,mts,js,mjs,json}` at the workspace root unless `--config` names one.
|
|
12
|
+
*/
|
|
13
|
+
const configNames = [
|
|
14
|
+
"ox-dep-lint.config.ts",
|
|
15
|
+
"ox-dep-lint.config.mts",
|
|
16
|
+
"ox-dep-lint.config.js",
|
|
17
|
+
"ox-dep-lint.config.mjs",
|
|
18
|
+
"ox-dep-lint.config.json"
|
|
19
|
+
];
|
|
20
|
+
const argument = (name) => {
|
|
21
|
+
const index = process.argv.indexOf(name);
|
|
22
|
+
return index === -1 ? void 0 : process.argv[index + 1];
|
|
23
|
+
};
|
|
24
|
+
const loadConfig = async (path) => {
|
|
25
|
+
if (path.endsWith(".json")) return JSON.parse(readFileSync(path, "utf8"));
|
|
26
|
+
const loaded = await import(pathToFileURL(path).href);
|
|
27
|
+
if (typeof loaded !== "object" || loaded === null || !("default" in loaded)) throw new Error(`${path} has no default export`);
|
|
28
|
+
return loaded.default;
|
|
29
|
+
};
|
|
30
|
+
const main = async () => {
|
|
31
|
+
const root = argument("--root") ?? findWorkspaceRoot(process.cwd());
|
|
32
|
+
if (root === void 0) {
|
|
33
|
+
process.stderr.write("ox-dep-lint: no package.json with workspaces at or above this directory; pass --root\n");
|
|
34
|
+
process.exitCode = 2;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const named = argument("--config");
|
|
38
|
+
const configPath = named === void 0 ? configNames.map((name) => nodePath.join(root, name)).find((path) => existsSync(path)) : nodePath.resolve(named);
|
|
39
|
+
if (configPath === void 0) {
|
|
40
|
+
process.stderr.write(`ox-dep-lint: no configuration (${configNames.join(", ")}) in ${root}\n`);
|
|
41
|
+
process.exitCode = 2;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const started = performance.now();
|
|
45
|
+
const result = checkWorkspace(root, await loadConfig(configPath));
|
|
46
|
+
for (const finding of result.findings) process.stdout.write(`${formatFinding(finding)}\n`);
|
|
47
|
+
const seconds = ((performance.now() - started) / 1e3).toFixed(2);
|
|
48
|
+
process.stdout.write(`ox-dep-lint: ${result.files} files, ${result.findings.length} findings (${seconds} s)\n`);
|
|
49
|
+
process.exitCode = result.findings.length === 0 ? 0 : 1;
|
|
50
|
+
};
|
|
51
|
+
try {
|
|
52
|
+
await main();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
process.stderr.write(`ox-dep-lint: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
55
|
+
process.exitCode = 2;
|
|
56
|
+
}
|
|
57
|
+
export { loadConfig };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a file is checked. `shipped`: every rule. `development` (tests, scripts, fixtures): kind edges, relative imports
|
|
3
|
+
* and unresolved imports are still checked, but devDependencies, test-only specifiers, runtime modules, outside
|
|
4
|
+
* packages and computed `import()` are allowed. `unchecked`: not read at all.
|
|
5
|
+
*/
|
|
6
|
+
type FilePolicy = "shipped" | "development" | "unchecked";
|
|
7
|
+
/** One kind of package (a layer of the workspace): which packages it holds and what they may import. */
|
|
8
|
+
interface KindRule {
|
|
9
|
+
/** Globs over package directories, relative to the repository root (`libraries/*`, `packages/core`). */
|
|
10
|
+
readonly packages: readonly string[];
|
|
11
|
+
/** Kinds whose packages this kind may import, its own included only when listed; `"any"` for tools. */
|
|
12
|
+
readonly mayUse: readonly string[] | "any";
|
|
13
|
+
/** Outside packages shipped code may import and `package.json` may name in `dependencies`, or `"any"`. Default `"any"`. */
|
|
14
|
+
readonly externals?: readonly string[] | "any";
|
|
15
|
+
/** Whether shipped code may import runtime modules (`node:fs`, `fs`, `bun`, `cloudflare:workers`). Default true. */
|
|
16
|
+
readonly runtimeModules?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Refinements for one package, keyed by its name. */
|
|
19
|
+
interface PackageRule {
|
|
20
|
+
/** The workspace packages it may import, a subset of what its kind allows. */
|
|
21
|
+
readonly uses?: readonly string[];
|
|
22
|
+
/** Outside packages beyond its kind's. */
|
|
23
|
+
readonly externals?: readonly string[];
|
|
24
|
+
/** Workspace packages it may import only with `import type`, `export type` or a type-level `import()`. */
|
|
25
|
+
readonly typeOnlyUses?: readonly string[];
|
|
26
|
+
/** Globs over specifiers of a `typeOnlyUses` package that may still be imported as values (`@acme/core/steps`). */
|
|
27
|
+
readonly valueSpecifiers?: readonly string[];
|
|
28
|
+
/** Package-relative globs of files checked as development files though they ship (a conformance suite's entry). */
|
|
29
|
+
readonly testSupportFiles?: readonly string[];
|
|
30
|
+
}
|
|
31
|
+
/** Files of one policy, by package-relative globs. The first class that matches a file wins; others are shipped. */
|
|
32
|
+
interface FileClass {
|
|
33
|
+
readonly files: readonly string[];
|
|
34
|
+
readonly policy: FilePolicy;
|
|
35
|
+
}
|
|
36
|
+
declare const ruleNames: readonly ["unknown-workspace-package", "package-without-kind", "package-outside-workspace", "invalid-package", "forbidden-edge", "type-only-edge", "relative-import-leaves-package", "package-path-escape", "shipped-imports-development-file", "self-import-by-name", "forbidden-external", "runtime-module", "test-only-in-shipped", "undeclared-import", "computed-import", "unresolved-import", "package-cycle", "stale-baseline", "parse-error"];
|
|
37
|
+
type RuleName = (typeof ruleNames)[number];
|
|
38
|
+
/** A finding the repository tolerates for now. The check fails when no finding needs it any more, so the list shrinks. */
|
|
39
|
+
interface BaselineEntry {
|
|
40
|
+
/** Repository-relative file (or `package.json` path for a manifest finding). */
|
|
41
|
+
readonly file: string;
|
|
42
|
+
readonly rule: RuleName;
|
|
43
|
+
/** The specifier or package the finding names. */
|
|
44
|
+
readonly target: string;
|
|
45
|
+
readonly reason: string;
|
|
46
|
+
}
|
|
47
|
+
type ManifestField = "dependencies" | "devDependencies" | "optionalDependencies" | "peerDependencies";
|
|
48
|
+
interface ImportPolicy {
|
|
49
|
+
/** `import(expression)`, an escaped literal, `createRequire`: an error in shipped code (default), everywhere, or never. */
|
|
50
|
+
readonly computed?: "error-in-shipped" | "error" | "allow";
|
|
51
|
+
/** A package importing itself by its own name. Default `"allow"`. */
|
|
52
|
+
readonly selfByName?: "error" | "allow";
|
|
53
|
+
/** A relative path or alias that names no file. Default `"error"`. */
|
|
54
|
+
readonly unresolved?: "error" | "ignore";
|
|
55
|
+
/** Which `package.json` fields count as edges between workspace packages. Default all four. */
|
|
56
|
+
readonly manifestEdgeFields?: readonly ManifestField[];
|
|
57
|
+
/**
|
|
58
|
+
* Every package a file imports must be declared in its own `package.json`. Shipped code needs `dependencies`,
|
|
59
|
+
* `peerDependencies` or `optionalDependencies` (`shippedFields`); a type-only import and a development file may also
|
|
60
|
+
* use devDependencies and the root's. Default true. knip's `unlisted` misses files no entry reaches and root
|
|
61
|
+
* devDependencies used by shipped code.
|
|
62
|
+
*/
|
|
63
|
+
readonly requireDeclared?: boolean;
|
|
64
|
+
/** The fields that satisfy a shipped import. Default `dependencies`, `peerDependencies`, `optionalDependencies`. */
|
|
65
|
+
readonly shippedFields?: readonly ManifestField[];
|
|
66
|
+
}
|
|
67
|
+
interface Config {
|
|
68
|
+
/** Scopes whose specifiers must name a workspace package (`@file-transfer/`). */
|
|
69
|
+
readonly workspaceScopes: readonly string[];
|
|
70
|
+
readonly kinds: Readonly<Record<string, KindRule>>;
|
|
71
|
+
readonly packages?: Readonly<Record<string, PackageRule>>;
|
|
72
|
+
/** Default: tests, scripts and fixtures are development files. */
|
|
73
|
+
readonly fileClasses?: readonly FileClass[];
|
|
74
|
+
/** Default: `node:`, `bun:`, `cloudflare:` prefixes and every bare Node built-in plus `bun`. */
|
|
75
|
+
readonly runtimeModules?: {
|
|
76
|
+
readonly prefixes?: readonly string[];
|
|
77
|
+
readonly names?: readonly string[];
|
|
78
|
+
};
|
|
79
|
+
/** Prefixes of a bundler's virtual modules, which have nothing on disk to check. Default `virtual:`, `astro:`. */
|
|
80
|
+
readonly virtualModules?: readonly string[];
|
|
81
|
+
/** Globs over specifiers only development files may import: `fast-check` is that specifier alone, `fast-check/**` its subpaths. */
|
|
82
|
+
readonly testOnlySpecifiers?: readonly string[];
|
|
83
|
+
readonly imports?: ImportPolicy;
|
|
84
|
+
readonly baseline?: readonly BaselineEntry[];
|
|
85
|
+
/** Repository-relative globs of files never read. */
|
|
86
|
+
readonly ignore?: readonly string[];
|
|
87
|
+
/** Package-relative globs of build output, never read. Default `dist/**` and `coverage/**`; `[]` reads everything. */
|
|
88
|
+
readonly output?: readonly string[];
|
|
89
|
+
}
|
|
90
|
+
/** The configuration with every default filled in. */
|
|
91
|
+
interface ResolvedConfig {
|
|
92
|
+
readonly workspaceScopes: readonly string[];
|
|
93
|
+
readonly kinds: Readonly<Record<string, Required<KindRule>>>;
|
|
94
|
+
readonly packages: Readonly<Record<string, PackageRule>>;
|
|
95
|
+
readonly fileClasses: readonly FileClass[];
|
|
96
|
+
readonly runtimePrefixes: readonly string[];
|
|
97
|
+
readonly runtimeNames: ReadonlySet<string>;
|
|
98
|
+
readonly virtualPrefixes: readonly string[];
|
|
99
|
+
readonly testOnlySpecifiers: readonly string[];
|
|
100
|
+
readonly imports: Required<ImportPolicy>;
|
|
101
|
+
readonly baseline: readonly BaselineEntry[];
|
|
102
|
+
readonly ignore: readonly string[];
|
|
103
|
+
readonly output: readonly string[];
|
|
104
|
+
}
|
|
105
|
+
declare const defaultFileClasses: readonly FileClass[];
|
|
106
|
+
/** Identity, for type checking a configuration file. */
|
|
107
|
+
declare const defineConfig: (config: Config) => Config;
|
|
108
|
+
declare const configProblems: (config: Config) => string[];
|
|
109
|
+
declare const resolveConfig: (config: Config) => ResolvedConfig;
|
|
110
|
+
export { ImportPolicy as a, PackageRule as c, configProblems as d, defaultFileClasses as f, FilePolicy as i, ResolvedConfig as l, resolveConfig as m, Config as n, KindRule as o, defineConfig as p, FileClass as r, ManifestField as s, BaselineEntry as t, RuleName as u };
|