@prestack/thread 0.0.2 → 0.1.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.
- package/CHANGELOG.md +98 -0
- package/dist/core/{asking.d.ts → answers.d.ts} +6 -14
- package/dist/core/{asking.js → answers.js} +13 -332
- package/dist/core/{asking.ts → answers.ts} +14 -357
- package/dist/core/attach.js +2 -2
- package/dist/core/attach.ts +2 -3
- package/dist/core/chain.js +4 -11
- package/dist/core/chain.ts +4 -3
- package/dist/core/client.d.ts +57 -0
- package/dist/core/client.js +186 -0
- package/dist/core/client.ts +227 -0
- package/dist/core/descend.d.ts +2 -0
- package/dist/core/descend.js +109 -16
- package/dist/core/descend.ts +120 -16
- package/dist/core/files.d.ts +45 -0
- package/dist/core/files.js +95 -0
- package/dist/core/files.ts +137 -0
- package/dist/core/init.d.ts +1 -0
- package/dist/core/init.js +10 -2
- package/dist/core/init.ts +8 -2
- package/dist/core/invoked.js +1 -1
- package/dist/core/invoked.ts +1 -1
- package/dist/core/rows.d.ts +2 -0
- package/dist/core/rows.js +65 -0
- package/dist/core/rows.ts +58 -0
- package/dist/core/tree.js +1 -1
- package/dist/core/tree.ts +1 -1
- package/dist/derivation/attach.js +0 -12
- package/dist/derivation/attach.ts +0 -12
- package/dist/derivation/generators/build.js +8 -7
- package/dist/derivation/generators/build.ts +8 -7
- package/dist/derivation/generators/release.d.ts +1 -1
- package/dist/derivation/generators/release.js +15 -23
- package/dist/derivation/generators/release.ts +15 -15
- package/dist/integrations/attach.js +1 -19
- package/dist/integrations/attach.ts +1 -19
- package/dist/integrations/generators/read.js +2 -2
- package/dist/integrations/generators/read.ts +4 -4
- package/dist/integrations/generators/vault.js +4 -12
- package/dist/integrations/generators/vault.ts +4 -4
- package/dist/integrations/module.js +5 -3
- package/dist/integrations/module.ts +5 -3
- package/dist/integrations/vault.js +5 -5
- package/dist/integrations/vault.ts +5 -5
- package/dist/integrity/attach.js +0 -12
- package/dist/integrity/attach.ts +0 -12
- package/dist/integrity/check-threads.js +1 -1
- package/dist/integrity/check-threads.ts +1 -1
- package/dist/integrity/constraint.d.ts +11 -2
- package/dist/integrity/constraint.js +163 -20
- package/dist/integrity/constraint.ts +174 -22
- package/dist/integrity/generators/compiler.js +1 -1
- package/dist/integrity/generators/compiler.ts +1 -1
- package/dist/integrity/generators/gate.js +8 -16
- package/dist/integrity/generators/gate.ts +8 -7
- package/dist/integrity/generators/publish.js +14 -26
- package/dist/integrity/generators/publish.ts +14 -17
- package/dist/integrity/generators/trial.d.ts +2 -2
- package/dist/integrity/generators/trial.js +108 -50
- package/dist/integrity/generators/trial.ts +107 -48
- package/dist/integrity/lint.js +3 -3
- package/dist/integrity/lint.ts +3 -3
- package/dist/integrity/lsp/diagnostics.js +1 -1
- package/dist/integrity/lsp/diagnostics.ts +1 -1
- package/dist/integrity/lsp/lint.js +1 -1
- package/dist/integrity/lsp/lint.ts +1 -1
- package/dist/integrity/slopnator.d.ts +1 -14
- package/dist/integrity/slopnator.js +8 -40
- package/dist/integrity/slopnator.ts +9 -51
- package/dist/integrity/slopnators/sl_broken_lineage.js +4 -2
- package/dist/integrity/slopnators/sl_broken_lineage.ts +4 -2
- package/dist/integrity/slopnators/sl_declared_elsewhere.js +24 -3
- package/dist/integrity/slopnators/sl_declared_elsewhere.ts +24 -3
- package/dist/integrity/slopnators/sl_derived_drift.js +10 -4
- package/dist/integrity/slopnators/sl_derived_drift.ts +10 -4
- package/dist/integrity/slopnators/sl_inert_island.js +7 -13
- package/dist/integrity/slopnators/sl_inert_island.ts +7 -16
- package/dist/integrity/slopnators/sl_inert_slopnator.js +8 -8
- package/dist/integrity/slopnators/sl_inert_slopnator.ts +8 -8
- package/dist/integrity/slopnators/sl_missing_row.js +14 -9
- package/dist/integrity/slopnators/sl_missing_row.ts +14 -10
- package/dist/integrity/slopnators/sl_no_child_process.js +8 -8
- package/dist/integrity/slopnators/sl_no_child_process.ts +8 -8
- package/dist/integrity/slopnators/sl_no_import_cycle.js +1 -1
- package/dist/integrity/slopnators/sl_no_import_cycle.ts +1 -1
- package/dist/integrity/slopnators/sl_no_such_command.js +20 -5
- package/dist/integrity/slopnators/sl_no_such_command.ts +22 -5
- package/dist/integrity/slopnators/sl_only_takes_a_literal.js +8 -8
- package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +8 -8
- package/dist/integrity/slopnators/sl_stale_path.js +91 -18
- package/dist/integrity/slopnators/sl_stale_path.ts +97 -18
- package/dist/integrity/slopnators/sl_twice_meant.js +25 -2
- package/dist/integrity/slopnators/sl_twice_meant.ts +28 -2
- package/dist/integrity/slopnators/sl_twice_parented.js +26 -5
- package/dist/integrity/slopnators/sl_twice_parented.ts +26 -5
- package/dist/integrity/slopnators/sl_undeclared_node.js +18 -2
- package/dist/integrity/slopnators/sl_undeclared_node.ts +18 -2
- package/dist/integrity/slopnators/sl_undeclared_operation.js +3 -1
- package/dist/integrity/slopnators/sl_undeclared_operation.ts +3 -1
- package/dist/integrity/slopnators/sl_undeclared_variant.js +6 -2
- package/dist/integrity/slopnators/sl_undeclared_variant.ts +6 -2
- package/dist/integrity/slopnators/sl_ungoverned_island.js +3 -4
- package/dist/integrity/slopnators/sl_ungoverned_island.ts +3 -4
- package/dist/integrity/slopnators/sl_unkept_rows.js +1 -1
- package/dist/integrity/slopnators/sl_unkept_rows.ts +1 -1
- package/dist/integrity/slopnators/sl_unpaired_member.js +13 -8
- package/dist/integrity/slopnators/sl_unpaired_member.ts +16 -8
- package/dist/integrity/slopnators/sl_unpaired_slopnator.js +16 -3
- package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +16 -3
- package/dist/integrity/slopnators/sl_unthreaded.d.ts +1 -0
- package/dist/integrity/slopnators/sl_unthreaded.js +18 -10
- package/dist/integrity/slopnators/sl_unthreaded.ts +19 -11
- package/dist/integrity/slopnators/sl_untranslated_key.js +1 -1
- package/dist/integrity/slopnators/sl_untranslated_key.ts +1 -1
- package/dist/integrity/slopnators/sl_unversioned.js +16 -2
- package/dist/integrity/slopnators/sl_unversioned.ts +16 -2
- package/dist/integrity/sweep.js +17 -3
- package/dist/integrity/sweep.ts +20 -3
- package/dist/knowledge/attach.js +0 -6
- package/dist/knowledge/attach.ts +0 -6
- package/dist/knowledge/generators/docsgen.js +12 -3
- package/dist/knowledge/generators/docsgen.ts +12 -2
- package/dist/knowledge/generators/glossary.js +1 -1
- package/dist/knowledge/generators/glossary.ts +1 -1
- package/dist/operations/attach.js +2 -26
- package/dist/operations/attach.ts +2 -27
- package/dist/operations/dispatch.js +2 -3
- package/dist/operations/dispatch.ts +2 -3
- package/dist/operations/generators/capability.js +4 -7
- package/dist/operations/generators/capability.ts +3 -6
- package/dist/operations/generators/compile.js +8 -13
- package/dist/operations/generators/compile.ts +8 -4
- package/dist/operations/generators/permits.js +4 -13
- package/dist/operations/generators/permits.ts +3 -3
- package/dist/operations/generators/seed.js +5 -2
- package/dist/operations/generators/seed.ts +6 -2
- package/dist/operations/generators/trial.js +10 -10
- package/dist/operations/generators/trial.ts +10 -10
- package/dist/operations/http.js +4 -1
- package/dist/operations/http.ts +6 -1
- package/package.json +2 -2
- package/dist/core/cli.d.ts +0 -2
- package/dist/core/cli.js +0 -1761
- package/dist/core/cli.ts +0 -2004
- package/dist/core/generators/meta.d.ts +0 -2
- package/dist/core/generators/meta.js +0 -829
- package/dist/core/generators/meta.ts +0 -1006
- package/dist/derivation/generators/derivation.d.ts +0 -3
- package/dist/derivation/generators/derivation.js +0 -273
- package/dist/derivation/generators/derivation.ts +0 -347
- package/dist/derivation/makers/release.d.ts +0 -1
- package/dist/derivation/makers/release.js +0 -107
- package/dist/derivation/makers/release.ts +0 -135
- package/dist/integrations/generators/secret.d.ts +0 -1
- package/dist/integrations/generators/secret.js +0 -233
- package/dist/integrations/generators/secret.ts +0 -289
- package/dist/integrations/generators/target.d.ts +0 -1
- package/dist/integrations/generators/target.js +0 -775
- package/dist/integrations/generators/target.ts +0 -944
- package/dist/integrity/generators/rule.d.ts +0 -2
- package/dist/integrity/generators/rule.js +0 -213
- package/dist/integrity/generators/rule.ts +0 -271
- package/dist/integrity/generators/seal.d.ts +0 -4
- package/dist/integrity/generators/seal.js +0 -101
- package/dist/integrity/generators/seal.ts +0 -117
- package/dist/integrity/sealed.d.ts +0 -1
- package/dist/integrity/sealed.js +0 -56
- package/dist/integrity/sealed.ts +0 -56
- package/dist/knowledge/generators/skills.d.ts +0 -1
- package/dist/knowledge/generators/skills.js +0 -222
- package/dist/knowledge/generators/skills.ts +0 -263
- package/dist/operations/generators/grant.d.ts +0 -1
- package/dist/operations/generators/grant.js +0 -267
- package/dist/operations/generators/grant.ts +0 -336
- package/dist/operations/generators/operation.d.ts +0 -1
- package/dist/operations/generators/operation.js +0 -811
- package/dist/operations/generators/operation.ts +0 -972
- package/dist/operations/makers/capability.d.ts +0 -1
- package/dist/operations/makers/capability.js +0 -117
- package/dist/operations/makers/capability.ts +0 -166
- package/dist/operations/makers/trial.d.ts +0 -1
- package/dist/operations/makers/trial.js +0 -413
- package/dist/operations/makers/trial.ts +0 -520
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process'
|
|
2
|
-
import { existsSync, readFileSync } from '
|
|
2
|
+
import { existsSync, readFileSync } from '../../core/files.ts'
|
|
3
|
+
import { valuesIn } from '../../core/rows.ts'
|
|
3
4
|
import { join } from 'node:path'
|
|
4
5
|
import { argv, env, execPath, stderr, stdout } from 'node:process'
|
|
5
6
|
import { pathToFileURL } from 'node:url'
|
|
7
|
+
import { TYPED } from '../../core/answers.ts'
|
|
6
8
|
import { CLI, exit } from '../../core/invoked.ts'
|
|
7
9
|
import { named } from '../../core/scope.ts'
|
|
8
10
|
import { findTree, inTree } from '../../core/tree.ts'
|
|
@@ -36,11 +38,6 @@ const CHECKS: readonly Check[] = [
|
|
|
36
38
|
runs: 'pnpm exec eslint .',
|
|
37
39
|
because: 'the rules that live on the lint surface, which is the only one carrying a fixer the sweep can report but not repair',
|
|
38
40
|
},
|
|
39
|
-
{
|
|
40
|
-
id: 'seal',
|
|
41
|
-
runs: 'pnpm seal:check',
|
|
42
|
-
because: 'the package must be the bytes it says it is. CHECKED and never regenerated, because a release that rewrites its own seal can never fail it',
|
|
43
|
-
},
|
|
44
41
|
]
|
|
45
42
|
|
|
46
43
|
const MINE = /^(?:npx |pnpm )?thread /
|
|
@@ -93,7 +90,7 @@ const PRODUCTION = 'production'
|
|
|
93
90
|
const secrets = inTree(TREE, 'secrets.ts')
|
|
94
91
|
|
|
95
92
|
const declared = existsSync(secrets)
|
|
96
|
-
? (((
|
|
93
|
+
? ((valuesIn(readFileSync(secrets)) as { SECRETS?: readonly Secret[] }).SECRETS ?? [])
|
|
97
94
|
: []
|
|
98
95
|
|
|
99
96
|
const vaulted = declared.some((one) => one.id === TOKEN)
|
|
@@ -132,14 +129,14 @@ stdout.write(
|
|
|
132
129
|
|
|
133
130
|
stdout.write('publish: deriving and building what the packaging rows state first, so every check below reads the files this release ships rather than the source they were written from.\n')
|
|
134
131
|
|
|
135
|
-
const made = run(
|
|
132
|
+
const made = run(`${TYPED} packaged --build`)
|
|
136
133
|
stdout.write(`publish: packaged exited ${String(made)}\n`)
|
|
137
134
|
if (made !== 0) {
|
|
138
135
|
refuse(
|
|
139
136
|
'packaged',
|
|
140
137
|
made,
|
|
141
138
|
'a package whose manifest and release note are not what its rows state would go out describing itself as something nobody declared, and one that did not compile would go out promising files it does not have',
|
|
142
|
-
|
|
139
|
+
`${TYPED} packaged --build`,
|
|
143
140
|
)
|
|
144
141
|
}
|
|
145
142
|
|
|
@@ -155,14 +152,14 @@ for (const [index, check] of CHECKS.entries()) {
|
|
|
155
152
|
if (code !== 0) refuse(check.id, code, check.because, check.runs)
|
|
156
153
|
}
|
|
157
154
|
|
|
158
|
-
const numbered = run(
|
|
155
|
+
const numbered = run(`${TYPED} packaged --release`)
|
|
159
156
|
stdout.write(`publish: packaged --release exited ${String(numbered)}\n`)
|
|
160
157
|
if (numbered !== 0) {
|
|
161
158
|
refuse(
|
|
162
159
|
'packaged',
|
|
163
160
|
numbered,
|
|
164
161
|
'the next release number is written onto the manifests only once every check has passed, and writing it refused, so nothing here carries a number to go out under',
|
|
165
|
-
|
|
162
|
+
`${TYPED} packaged --release`,
|
|
166
163
|
)
|
|
167
164
|
}
|
|
168
165
|
|
|
@@ -174,19 +171,19 @@ const sent = run(`pnpm -r publish --registry ${registry} --no-git-checks --acces
|
|
|
174
171
|
|
|
175
172
|
if (sent !== 0) {
|
|
176
173
|
stderr.write(`\npublish: the checks passed and the publish itself exited ${String(sent)}.\n`)
|
|
177
|
-
const back = run(
|
|
174
|
+
const back = run(`${TYPED} packaged`)
|
|
178
175
|
stderr.write(
|
|
179
176
|
back === 0
|
|
180
177
|
? 'publish: the manifests carry the last recorded release again and no note was written, so the next `thread publish` numbers this release once more.\n'
|
|
181
|
-
:
|
|
178
|
+
: `publish: the manifests still carry the number that did not go out. Run \`${TYPED} packaged\` to put the last recorded release back.\n`,
|
|
182
179
|
)
|
|
183
180
|
exit(1)
|
|
184
181
|
}
|
|
185
182
|
|
|
186
|
-
const recorded = run(
|
|
183
|
+
const recorded = run(`${TYPED} packaged --record`)
|
|
187
184
|
stdout.write(`publish: packaged --record exited ${String(recorded)}\n`)
|
|
188
185
|
if (recorded !== 0) {
|
|
189
|
-
stderr.write(
|
|
186
|
+
stderr.write(`\npublish: the packages went out and the note recording them refused to be written, so the next publish would number this release again. Run \`${TYPED} packaged --record\` and read what it says.\n`)
|
|
190
187
|
exit(1)
|
|
191
188
|
}
|
|
192
189
|
|
|
@@ -200,7 +197,7 @@ interface Packaged {
|
|
|
200
197
|
const sheet = inTree(TREE, SHEET)
|
|
201
198
|
|
|
202
199
|
const stated = existsSync(sheet)
|
|
203
|
-
? ((
|
|
200
|
+
? (valuesIn(readFileSync(sheet)) as { PACKAGES?: readonly Packaged[] })
|
|
204
201
|
: {}
|
|
205
202
|
|
|
206
203
|
const shipped = stated.PACKAGES ?? []
|
|
@@ -243,5 +240,5 @@ if (asked(['rev-parse', '--is-inside-work-tree']) !== 'true') {
|
|
|
243
240
|
}
|
|
244
241
|
}
|
|
245
242
|
|
|
246
|
-
stdout.write(`\npublish: done. What went out was derived, then built, then passed the gate
|
|
243
|
+
stdout.write(`\npublish: done. What went out was derived, then built, then passed the gate and the lint, in that order. The compile inside \`thread packaged --build\` is the typecheck.\n`)
|
|
247
244
|
exit(0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
|
-
import type { Constraint } from '../constraint.ts';
|
|
3
|
-
import { type Byproduct, type Slopnator
|
|
2
|
+
import type { Constraint, Sourced } from '../constraint.ts';
|
|
3
|
+
import { type Byproduct, type Slopnator } from '../slopnator.ts';
|
|
4
4
|
export declare const TRIALS_PATH: string;
|
|
5
5
|
export interface Scenario {
|
|
6
6
|
readonly files: readonly Sourced[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as disk from 'node:fs';
|
|
2
|
+
import { answeredBy, existsSync, readFileSync } from "../../core/files.js";
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
4
|
import { exit, invoked } from "../../core/invoked.js";
|
|
4
5
|
import { Linter } from 'eslint';
|
|
5
6
|
const TREE = findTree();
|
|
@@ -16,8 +17,9 @@ const WRAPPINGS = [
|
|
|
16
17
|
(source) => `const _probe = () => { ${source} }`,
|
|
17
18
|
(source) => `const _probe = <Probe>{${source}}</Probe>`,
|
|
18
19
|
];
|
|
20
|
+
const canonical = (at) => at.replace(/^threads\//, `${TREE.name}/`);
|
|
19
21
|
export function probeAt(rule) {
|
|
20
|
-
const path = rule.where.paths[0] ?? 'src/';
|
|
22
|
+
const path = canonical(rule.where.paths[0] ?? 'src/');
|
|
21
23
|
if (path === '.')
|
|
22
24
|
return PROBE;
|
|
23
25
|
return path.endsWith('/') ? `${path}${PROBE}` : `${path}/${PROBE}`;
|
|
@@ -28,8 +30,9 @@ function requiresOf(rule, reports) {
|
|
|
28
30
|
: `${rule.id} stays silent on this source — the fix it recommends is not itself refused`;
|
|
29
31
|
}
|
|
30
32
|
function scenarioOf(rule, proof) {
|
|
31
|
-
if (typeof proof !== 'string')
|
|
32
|
-
return proof;
|
|
33
|
+
if (typeof proof !== 'string') {
|
|
34
|
+
return { at: canonical(proof.at), files: proof.files.map((one) => ({ ...one, at: canonical(one.at) })) };
|
|
35
|
+
}
|
|
33
36
|
const path = probeAt(rule);
|
|
34
37
|
return { files: [{ at: path, source: proof }], at: path };
|
|
35
38
|
}
|
|
@@ -74,10 +77,95 @@ function config(rule, module, parser) {
|
|
|
74
77
|
parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
|
|
75
78
|
},
|
|
76
79
|
plugins: { rule: { rules: { [rule.id]: module } } },
|
|
77
|
-
rules: { [`rule
|
|
80
|
+
rules: { [`rule/${rule.id}`]: 'error' },
|
|
78
81
|
},
|
|
79
82
|
];
|
|
80
83
|
}
|
|
84
|
+
const flat = (at) => (at.length > 1 && at.endsWith('/') ? at.replace(/\/+$/, '') : at);
|
|
85
|
+
const under = (at) => at.startsWith(ROOT) || `${at}/` === ROOT;
|
|
86
|
+
const own = (at) => under(at) && !at.slice(ROOT.length).split('/').includes('node_modules');
|
|
87
|
+
function beside(at) {
|
|
88
|
+
try {
|
|
89
|
+
return disk.readdirSync(at, { withFileTypes: true });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function outside(at) {
|
|
96
|
+
const one = disk.statSync(at, { throwIfNoEntry: false });
|
|
97
|
+
if (one === undefined)
|
|
98
|
+
return 'none';
|
|
99
|
+
return one.isFile() ? 'file' : one.isDirectory() ? 'folder' : 'other';
|
|
100
|
+
}
|
|
101
|
+
const GONE = 'is not in the scenario this trial stands, and while it runs the scenario is the whole tree.';
|
|
102
|
+
function answering(files) {
|
|
103
|
+
const held = new Map(files.map((one) => [resolve(ROOT, one.at), one.source]));
|
|
104
|
+
const folders = new Set();
|
|
105
|
+
const parents = (at) => {
|
|
106
|
+
for (let up = dirname(at); under(`${up}/`); up = dirname(up))
|
|
107
|
+
folders.add(up);
|
|
108
|
+
};
|
|
109
|
+
for (const at of held.keys())
|
|
110
|
+
parents(at);
|
|
111
|
+
const answers = {
|
|
112
|
+
kind: (at) => (held.has(at) ? 'file' : folders.has(at) ? 'folder' : own(at) ? 'none' : outside(at)),
|
|
113
|
+
read: (at) => {
|
|
114
|
+
const said = held.get(at);
|
|
115
|
+
if (said !== undefined)
|
|
116
|
+
return said;
|
|
117
|
+
if (own(at))
|
|
118
|
+
throw new Error(`${at} ${GONE}`);
|
|
119
|
+
return disk.readFileSync(at, 'utf8');
|
|
120
|
+
},
|
|
121
|
+
write: (at, said) => {
|
|
122
|
+
held.set(at, said);
|
|
123
|
+
parents(at);
|
|
124
|
+
},
|
|
125
|
+
list: (at) => {
|
|
126
|
+
const inside = new Map();
|
|
127
|
+
const found = own(at) ? [] : folders.has(at) ? beside(at) : disk.readdirSync(at, { withFileTypes: true });
|
|
128
|
+
for (const one of found)
|
|
129
|
+
inside.set(one.name, { name: one.name, kind: one.isDirectory() ? 'folder' : 'file' });
|
|
130
|
+
for (const path of folders)
|
|
131
|
+
if (dirname(path) === at)
|
|
132
|
+
inside.set(basename(path), { name: basename(path), kind: 'folder' });
|
|
133
|
+
for (const path of held.keys())
|
|
134
|
+
if (dirname(path) === at)
|
|
135
|
+
inside.set(basename(path), { name: basename(path), kind: 'file' });
|
|
136
|
+
return [...inside.values()];
|
|
137
|
+
},
|
|
138
|
+
stat: (at) => {
|
|
139
|
+
const said = held.get(at);
|
|
140
|
+
if (said !== undefined)
|
|
141
|
+
return { size: said.length, modified: 0 };
|
|
142
|
+
if (folders.has(at))
|
|
143
|
+
return { size: 0, modified: 0 };
|
|
144
|
+
if (own(at))
|
|
145
|
+
throw new Error(`${at} ${GONE}`);
|
|
146
|
+
const one = disk.statSync(at);
|
|
147
|
+
return { size: one.size, modified: one.mtimeMs };
|
|
148
|
+
},
|
|
149
|
+
remove: (at) => {
|
|
150
|
+
held.delete(at);
|
|
151
|
+
folders.delete(at);
|
|
152
|
+
},
|
|
153
|
+
real: (at) => (own(at) || held.has(at) || folders.has(at) ? at : disk.realpathSync(at)),
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
kind: (at) => answers.kind(flat(at)),
|
|
157
|
+
read: (at) => answers.read(flat(at)),
|
|
158
|
+
write: (at, said) => {
|
|
159
|
+
answers.write(flat(at), said);
|
|
160
|
+
},
|
|
161
|
+
list: (at) => answers.list(flat(at)),
|
|
162
|
+
stat: (at) => answers.stat(flat(at)),
|
|
163
|
+
remove: (at) => {
|
|
164
|
+
answers.remove(flat(at));
|
|
165
|
+
},
|
|
166
|
+
real: (at) => answers.real(flat(at)),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
81
169
|
export function runTrial(trial, rule, module, parser, slopnator) {
|
|
82
170
|
const linter = new Linter();
|
|
83
171
|
const failures = [];
|
|
@@ -91,45 +179,18 @@ export function runTrial(trial, rule, module, parser, slopnator) {
|
|
|
91
179
|
};
|
|
92
180
|
}
|
|
93
181
|
const target = trial.scenario.files.find((file) => file.at === trial.scenario.at);
|
|
94
|
-
if (target === undefined) {
|
|
95
|
-
return {
|
|
96
|
-
id: trial.id,
|
|
97
|
-
rule: rule.id,
|
|
98
|
-
passed: false,
|
|
99
|
-
parsed: false,
|
|
100
|
-
failures: [`the scenario writes no file at "${trial.scenario.at}", the path it lints, so nothing could have been measured`],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
for (const file of trial.scenario.files) {
|
|
104
|
-
if (!existsSync(join(ROOT, file.at)))
|
|
105
|
-
continue;
|
|
106
|
-
return {
|
|
107
|
-
id: trial.id,
|
|
108
|
-
rule: rule.id,
|
|
109
|
-
passed: false,
|
|
110
|
-
parsed: false,
|
|
111
|
-
failures: [`the scenario would overwrite "${file.at}", which is a real file in this repository — a trial may not stand on a path somebody authored`],
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
const written = [];
|
|
115
|
-
const created = [];
|
|
116
182
|
let parsed = false;
|
|
117
183
|
let satisfied = false;
|
|
184
|
+
answeredBy(answering(trial.scenario.files));
|
|
185
|
+
forget();
|
|
118
186
|
try {
|
|
119
|
-
for (const file of trial.scenario.files) {
|
|
120
|
-
const path = join(ROOT, file.at);
|
|
121
|
-
const made = mkdirSync(dirname(path), { recursive: true });
|
|
122
|
-
if (made !== undefined)
|
|
123
|
-
created.push(made);
|
|
124
|
-
writeFileSync(path, file.source);
|
|
125
|
-
written.push(path);
|
|
126
|
-
}
|
|
127
|
-
forget();
|
|
128
|
-
const path = join(ROOT, trial.scenario.at);
|
|
129
187
|
if (slopnator !== undefined && !reads(slopnator)) {
|
|
130
188
|
parsed = true;
|
|
131
189
|
satisfied = slopnator.checks(ROOT).length > 0 === trial.verdict.reports;
|
|
132
190
|
}
|
|
191
|
+
else if (target === undefined) {
|
|
192
|
+
failures.push(`the scenario writes no file at "${trial.scenario.at}", the path it lints, so nothing could have been measured`);
|
|
193
|
+
}
|
|
133
194
|
else if (slopnator !== undefined && sweeps(slopnator)) {
|
|
134
195
|
const held = slopnator.holds(undefined, {
|
|
135
196
|
filename: trial.scenario.at,
|
|
@@ -143,32 +204,29 @@ export function runTrial(trial, rule, module, parser, slopnator) {
|
|
|
143
204
|
else {
|
|
144
205
|
for (const wrap of WRAPPINGS) {
|
|
145
206
|
const text = wrap(target.source);
|
|
146
|
-
writeFileSync(path, text);
|
|
147
207
|
const messages = linter.verify(text, config(rule, module, parser), trial.scenario.at);
|
|
148
208
|
if (messages.some((message) => message.fatal === true))
|
|
149
209
|
continue;
|
|
150
210
|
parsed = true;
|
|
151
|
-
satisfied = messages.some((message) => message.ruleId === `rule
|
|
211
|
+
satisfied = messages.some((message) => message.ruleId === `rule/${rule.id}`) === trial.verdict.reports;
|
|
152
212
|
break;
|
|
153
213
|
}
|
|
154
214
|
}
|
|
155
215
|
}
|
|
216
|
+
catch (thrown) {
|
|
217
|
+
failures.push(`this trial could not run: ${thrown instanceof Error ? thrown.message : String(thrown)} — its scenario does not describe a world this rule can be measured against`);
|
|
218
|
+
}
|
|
156
219
|
finally {
|
|
157
|
-
|
|
158
|
-
if (existsSync(path))
|
|
159
|
-
rmSync(path);
|
|
160
|
-
for (const path of created)
|
|
161
|
-
if (existsSync(path))
|
|
162
|
-
rmSync(path, { recursive: true });
|
|
220
|
+
answeredBy(undefined);
|
|
163
221
|
forget();
|
|
164
222
|
}
|
|
165
|
-
if (!parsed) {
|
|
223
|
+
if (failures.length === 0 && !parsed) {
|
|
166
224
|
failures.push('no wrapping of this source parsed, so nothing was measured — a trial that cannot run is not a trial that passed');
|
|
167
225
|
}
|
|
168
|
-
else if (!satisfied) {
|
|
226
|
+
else if (failures.length === 0 && !satisfied) {
|
|
169
227
|
failures.push(trial.verdict.reports
|
|
170
|
-
? `expected
|
|
171
|
-
: `expected
|
|
228
|
+
? `expected ${rule.id} to report on this source and it stayed silent — the rule documents a form its own slopnator does not refuse`
|
|
229
|
+
: `expected ${rule.id} to stay silent on this source and it reported — the rule refuses the fix it recommends`);
|
|
172
230
|
}
|
|
173
231
|
return { id: trial.id, rule: rule.id, passed: failures.length === 0, parsed, failures };
|
|
174
232
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as disk from 'node:fs'
|
|
2
|
+
import { answeredBy, existsSync, readFileSync, type Answering, type Kind, type Listed } from '../../core/files.ts'
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
3
4
|
import { pathToFileURL } from 'node:url'
|
|
4
5
|
|
|
5
6
|
import { exit, invoked } from '../../core/invoked.ts'
|
|
@@ -7,11 +8,11 @@ import { exit, invoked } from '../../core/invoked.ts'
|
|
|
7
8
|
import type { Linter as LinterType, Rule } from 'eslint'
|
|
8
9
|
import { Linter } from 'eslint'
|
|
9
10
|
|
|
10
|
-
import type { Constraint } from '../constraint.ts'
|
|
11
|
+
import type { Constraint, Proof, Sourced } from '../constraint.ts'
|
|
11
12
|
|
|
12
13
|
const TREE = findTree()
|
|
13
14
|
import { CONSTRAINTS } from '../constraint.ts'
|
|
14
|
-
import { NOT_WRITTEN, ROOT, governs, load, proofsOf, reads, rules, slopnatorName, sweeps, write, type Byproduct, type
|
|
15
|
+
import { NOT_WRITTEN, ROOT, governs, load, proofsOf, reads, rules, slopnatorName, sweeps, write, type Byproduct, type Slopnator } from '../slopnator.ts'
|
|
15
16
|
import { forget } from '../slopnators/sl_unthreaded.ts'
|
|
16
17
|
import { findTree, inTree } from '../../core/tree.ts'
|
|
17
18
|
|
|
@@ -60,8 +61,10 @@ export interface Run {
|
|
|
60
61
|
readonly measured: number
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
const canonical = (at: string): string => at.replace(/^threads\//, `${TREE.name}/`)
|
|
65
|
+
|
|
63
66
|
export function probeAt(rule: Constraint): string {
|
|
64
|
-
const path = rule.where.paths[0] ?? 'src/'
|
|
67
|
+
const path = canonical(rule.where.paths[0] ?? 'src/')
|
|
65
68
|
if (path === '.') return PROBE
|
|
66
69
|
return path.endsWith('/') ? `${path}${PROBE}` : `${path}/${PROBE}`
|
|
67
70
|
}
|
|
@@ -73,7 +76,9 @@ function requiresOf(rule: Constraint, reports: boolean): string {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
function scenarioOf(rule: Constraint, proof: Proof): Scenario {
|
|
76
|
-
if (typeof proof !== 'string')
|
|
79
|
+
if (typeof proof !== 'string') {
|
|
80
|
+
return { at: canonical(proof.at), files: proof.files.map((one) => ({ ...one, at: canonical(one.at) })) }
|
|
81
|
+
}
|
|
77
82
|
const path = probeAt(rule)
|
|
78
83
|
return { files: [{ at: path, source: proof }], at: path }
|
|
79
84
|
}
|
|
@@ -124,11 +129,91 @@ function config(rule: Constraint, module: Rule.RuleModule, parser: unknown): Par
|
|
|
124
129
|
parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true } },
|
|
125
130
|
},
|
|
126
131
|
plugins: { rule: { rules: { [rule.id]: module } } },
|
|
127
|
-
rules: { [`rule
|
|
132
|
+
rules: { [`rule/${rule.id}`]: 'error' },
|
|
128
133
|
},
|
|
129
134
|
] as unknown as Parameters<LinterType['verify']>[1]
|
|
130
135
|
}
|
|
131
136
|
|
|
137
|
+
const flat = (at: string): string => (at.length > 1 && at.endsWith('/') ? at.replace(/\/+$/, '') : at)
|
|
138
|
+
|
|
139
|
+
const under = (at: string): boolean => at.startsWith(ROOT) || `${at}/` === ROOT
|
|
140
|
+
|
|
141
|
+
const own = (at: string): boolean => under(at) && !at.slice(ROOT.length).split('/').includes('node_modules')
|
|
142
|
+
|
|
143
|
+
function beside(at: string): readonly disk.Dirent[] {
|
|
144
|
+
try {
|
|
145
|
+
return disk.readdirSync(at, { withFileTypes: true })
|
|
146
|
+
} catch {
|
|
147
|
+
return []
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function outside(at: string): Kind {
|
|
152
|
+
const one = disk.statSync(at, { throwIfNoEntry: false })
|
|
153
|
+
if (one === undefined) return 'none'
|
|
154
|
+
return one.isFile() ? 'file' : one.isDirectory() ? 'folder' : 'other'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const GONE = 'is not in the scenario this trial stands, and while it runs the scenario is the whole tree.'
|
|
158
|
+
|
|
159
|
+
function answering(files: readonly Sourced[]): Answering {
|
|
160
|
+
const held = new Map<string, string>(files.map((one) => [resolve(ROOT, one.at), one.source]))
|
|
161
|
+
const folders = new Set<string>()
|
|
162
|
+
const parents = (at: string): void => {
|
|
163
|
+
for (let up = dirname(at); under(`${up}/`); up = dirname(up)) folders.add(up)
|
|
164
|
+
}
|
|
165
|
+
for (const at of held.keys()) parents(at)
|
|
166
|
+
|
|
167
|
+
const answers: Answering = {
|
|
168
|
+
kind: (at) => (held.has(at) ? 'file' : folders.has(at) ? 'folder' : own(at) ? 'none' : outside(at)),
|
|
169
|
+
read: (at) => {
|
|
170
|
+
const said = held.get(at)
|
|
171
|
+
if (said !== undefined) return said
|
|
172
|
+
if (own(at)) throw new Error(`${at} ${GONE}`)
|
|
173
|
+
return disk.readFileSync(at, 'utf8')
|
|
174
|
+
},
|
|
175
|
+
write: (at, said) => {
|
|
176
|
+
held.set(at, said)
|
|
177
|
+
parents(at)
|
|
178
|
+
},
|
|
179
|
+
list: (at) => {
|
|
180
|
+
const inside = new Map<string, Listed>()
|
|
181
|
+
const found = own(at) ? [] : folders.has(at) ? beside(at) : disk.readdirSync(at, { withFileTypes: true })
|
|
182
|
+
for (const one of found) inside.set(one.name, { name: one.name, kind: one.isDirectory() ? 'folder' : 'file' })
|
|
183
|
+
for (const path of folders) if (dirname(path) === at) inside.set(basename(path), { name: basename(path), kind: 'folder' })
|
|
184
|
+
for (const path of held.keys()) if (dirname(path) === at) inside.set(basename(path), { name: basename(path), kind: 'file' })
|
|
185
|
+
return [...inside.values()]
|
|
186
|
+
},
|
|
187
|
+
stat: (at) => {
|
|
188
|
+
const said = held.get(at)
|
|
189
|
+
if (said !== undefined) return { size: said.length, modified: 0 }
|
|
190
|
+
if (folders.has(at)) return { size: 0, modified: 0 }
|
|
191
|
+
if (own(at)) throw new Error(`${at} ${GONE}`)
|
|
192
|
+
const one = disk.statSync(at)
|
|
193
|
+
return { size: one.size, modified: one.mtimeMs }
|
|
194
|
+
},
|
|
195
|
+
remove: (at) => {
|
|
196
|
+
held.delete(at)
|
|
197
|
+
folders.delete(at)
|
|
198
|
+
},
|
|
199
|
+
real: (at) => (own(at) || held.has(at) || folders.has(at) ? at : disk.realpathSync(at)),
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
kind: (at) => answers.kind(flat(at)),
|
|
204
|
+
read: (at) => answers.read(flat(at)),
|
|
205
|
+
write: (at, said) => {
|
|
206
|
+
answers.write(flat(at), said)
|
|
207
|
+
},
|
|
208
|
+
list: (at) => answers.list(flat(at)),
|
|
209
|
+
stat: (at) => answers.stat(flat(at)),
|
|
210
|
+
remove: (at) => {
|
|
211
|
+
answers.remove(flat(at))
|
|
212
|
+
},
|
|
213
|
+
real: (at) => answers.real(flat(at)),
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
132
217
|
export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule, parser: unknown, slopnator?: Slopnator): Outcome {
|
|
133
218
|
const linter = new Linter()
|
|
134
219
|
const failures: string[] = []
|
|
@@ -144,47 +229,19 @@ export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule
|
|
|
144
229
|
}
|
|
145
230
|
|
|
146
231
|
const target = trial.scenario.files.find((file) => file.at === trial.scenario.at)
|
|
147
|
-
if (target === undefined) {
|
|
148
|
-
return {
|
|
149
|
-
id: trial.id,
|
|
150
|
-
rule: rule.id,
|
|
151
|
-
passed: false,
|
|
152
|
-
parsed: false,
|
|
153
|
-
failures: [`the scenario writes no file at "${trial.scenario.at}", the path it lints, so nothing could have been measured`],
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
for (const file of trial.scenario.files) {
|
|
158
|
-
if (!existsSync(join(ROOT, file.at))) continue
|
|
159
|
-
return {
|
|
160
|
-
id: trial.id,
|
|
161
|
-
rule: rule.id,
|
|
162
|
-
passed: false,
|
|
163
|
-
parsed: false,
|
|
164
|
-
failures: [`the scenario would overwrite "${file.at}", which is a real file in this repository — a trial may not stand on a path somebody authored`],
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
232
|
|
|
168
|
-
const written: string[] = []
|
|
169
|
-
const created: string[] = []
|
|
170
233
|
let parsed = false
|
|
171
234
|
let satisfied = false
|
|
172
235
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const path = join(ROOT, file.at)
|
|
176
|
-
const made = mkdirSync(dirname(path), { recursive: true })
|
|
177
|
-
if (made !== undefined) created.push(made)
|
|
178
|
-
writeFileSync(path, file.source)
|
|
179
|
-
written.push(path)
|
|
180
|
-
}
|
|
181
|
-
forget()
|
|
182
|
-
|
|
183
|
-
const path = join(ROOT, trial.scenario.at)
|
|
236
|
+
answeredBy(answering(trial.scenario.files))
|
|
237
|
+
forget()
|
|
184
238
|
|
|
239
|
+
try {
|
|
185
240
|
if (slopnator !== undefined && !reads(slopnator)) {
|
|
186
241
|
parsed = true
|
|
187
242
|
satisfied = slopnator.checks(ROOT).length > 0 === trial.verdict.reports
|
|
243
|
+
} else if (target === undefined) {
|
|
244
|
+
failures.push(`the scenario writes no file at "${trial.scenario.at}", the path it lints, so nothing could have been measured`)
|
|
188
245
|
} else if (slopnator !== undefined && sweeps(slopnator)) {
|
|
189
246
|
const held = slopnator.holds(undefined, {
|
|
190
247
|
filename: trial.scenario.at,
|
|
@@ -197,27 +254,29 @@ export function runTrial(trial: Trial, rule: Constraint, module: Rule.RuleModule
|
|
|
197
254
|
} else {
|
|
198
255
|
for (const wrap of WRAPPINGS) {
|
|
199
256
|
const text = wrap(target.source)
|
|
200
|
-
writeFileSync(path, text)
|
|
201
257
|
const messages = linter.verify(text, config(rule, module, parser), trial.scenario.at)
|
|
202
258
|
if (messages.some((message) => message.fatal === true)) continue
|
|
203
259
|
parsed = true
|
|
204
|
-
satisfied = messages.some((message) => message.ruleId === `rule
|
|
260
|
+
satisfied = messages.some((message) => message.ruleId === `rule/${rule.id}`) === trial.verdict.reports
|
|
205
261
|
break
|
|
206
262
|
}
|
|
207
263
|
}
|
|
264
|
+
} catch (thrown) {
|
|
265
|
+
failures.push(
|
|
266
|
+
`this trial could not run: ${thrown instanceof Error ? thrown.message : String(thrown)} — its scenario does not describe a world this rule can be measured against`,
|
|
267
|
+
)
|
|
208
268
|
} finally {
|
|
209
|
-
|
|
210
|
-
for (const path of created) if (existsSync(path)) rmSync(path, { recursive: true })
|
|
269
|
+
answeredBy(undefined)
|
|
211
270
|
forget()
|
|
212
271
|
}
|
|
213
272
|
|
|
214
|
-
if (!parsed) {
|
|
273
|
+
if (failures.length === 0 && !parsed) {
|
|
215
274
|
failures.push('no wrapping of this source parsed, so nothing was measured — a trial that cannot run is not a trial that passed')
|
|
216
|
-
} else if (!satisfied) {
|
|
275
|
+
} else if (failures.length === 0 && !satisfied) {
|
|
217
276
|
failures.push(
|
|
218
277
|
trial.verdict.reports
|
|
219
|
-
? `expected
|
|
220
|
-
: `expected
|
|
278
|
+
? `expected ${rule.id} to report on this source and it stayed silent — the rule documents a form its own slopnator does not refuse`
|
|
279
|
+
: `expected ${rule.id} to stay silent on this source and it reported — the rule refuses the fix it recommends`,
|
|
221
280
|
)
|
|
222
281
|
}
|
|
223
282
|
|
package/dist/integrity/lint.js
CHANGED
|
@@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import { Linter } from 'eslint';
|
|
3
3
|
import tseslint from 'typescript-eslint';
|
|
4
4
|
import { THING } from "../core/scope.js";
|
|
5
|
-
import { CONSTRAINTS } from "./constraint.js";
|
|
5
|
+
import { CONSTRAINTS, shown } from "./constraint.js";
|
|
6
6
|
import { NOT_WRITTEN, governs, reads, visits, load, ROOT, rules as appRules, slopnatorName, } from "./slopnator.js";
|
|
7
7
|
const PROBE = {
|
|
8
8
|
filename: 'probe.ts',
|
|
@@ -18,7 +18,7 @@ const oneLine = (text) => text.replace(/\s*\n\s*/g, ' ').trim();
|
|
|
18
18
|
export function agentInstructions() {
|
|
19
19
|
return [...CONSTRAINTS]
|
|
20
20
|
.sort((one, two) => (one.id < two.id ? -1 : one.id > two.id ? 1 : 0))
|
|
21
|
-
.map((rule) => `${oneLine(rule.instead)} — never ${oneLine(rule.bans)}. Wrong: \`${oneLine(rule.wrong)}\`. Right: \`${oneLine(rule.right)}\`. [${rule.id}]`);
|
|
21
|
+
.map((rule) => `${oneLine(rule.instead)} — never ${oneLine(rule.bans)}. Wrong: \`${oneLine(shown(rule.wrong))}\`. Right: \`${oneLine(shown(rule.right))}\`. [${rule.id}]`);
|
|
22
22
|
}
|
|
23
23
|
export function defineRule(rule, slopnators) {
|
|
24
24
|
const expected = slopnatorName(rule.id);
|
|
@@ -155,7 +155,7 @@ export function theRules() {
|
|
|
155
155
|
message: message.message,
|
|
156
156
|
...(found === undefined
|
|
157
157
|
? {}
|
|
158
|
-
: { bans: found.bans, instead: found.instead, wrong: found.wrong, right: found.right }),
|
|
158
|
+
: { bans: found.bans, instead: found.instead, wrong: shown(found.wrong), right: shown(found.right) }),
|
|
159
159
|
};
|
|
160
160
|
});
|
|
161
161
|
}
|
package/dist/integrity/lint.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Linter, type Rule } from 'eslint'
|
|
|
5
5
|
import tseslint from 'typescript-eslint'
|
|
6
6
|
|
|
7
7
|
import { THING } from '../core/scope.ts'
|
|
8
|
-
import { CONSTRAINTS, type Constraint } from './constraint.ts'
|
|
8
|
+
import { CONSTRAINTS, shown, type Constraint } from './constraint.ts'
|
|
9
9
|
import {
|
|
10
10
|
NOT_WRITTEN,
|
|
11
11
|
governs,
|
|
@@ -40,7 +40,7 @@ export function agentInstructions(): readonly string[] {
|
|
|
40
40
|
.sort((one, two) => (one.id < two.id ? -1 : one.id > two.id ? 1 : 0))
|
|
41
41
|
.map(
|
|
42
42
|
(rule) =>
|
|
43
|
-
`${oneLine(rule.instead)} — never ${oneLine(rule.bans)}. Wrong: \`${oneLine(rule.wrong)}\`. Right: \`${oneLine(rule.right)}\`. [${rule.id}]`,
|
|
43
|
+
`${oneLine(rule.instead)} — never ${oneLine(rule.bans)}. Wrong: \`${oneLine(shown(rule.wrong))}\`. Right: \`${oneLine(shown(rule.right))}\`. [${rule.id}]`,
|
|
44
44
|
)
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -221,7 +221,7 @@ export function theRules(): RuleRunner {
|
|
|
221
221
|
message: message.message,
|
|
222
222
|
...(found === undefined
|
|
223
223
|
? {}
|
|
224
|
-
: { bans: found.bans, instead: found.instead, wrong: found.wrong, right: found.right }),
|
|
224
|
+
: { bans: found.bans, instead: found.instead, wrong: shown(found.wrong), right: shown(found.right) }),
|
|
225
225
|
}
|
|
226
226
|
})
|
|
227
227
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync } from
|
|
2
|
+
import { existsSync } from "../../core/files.js";
|
|
3
3
|
import { extname, join } from 'node:path';
|
|
4
4
|
import { theRules } from "../lint.js";
|
|
5
5
|
const LINTABLE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.jsx']);
|