@prestack/thread 0.0.3 → 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 +310 -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 +243 -0
- package/dist/core/client.ts +279 -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 +145 -0
- package/dist/core/files.ts +193 -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 +7 -1
- package/dist/integrity/generators/compiler.ts +10 -1
- package/dist/integrity/generators/gate.js +23 -18
- package/dist/integrity/generators/gate.ts +29 -9
- 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,16 +1,9 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
1
|
import { spawnSync } from 'node:child_process';
|
|
10
|
-
import { existsSync, readFileSync } from
|
|
2
|
+
import { existsSync, readFileSync } from "../../core/files.js";
|
|
3
|
+
import { valuesIn } from "../../core/rows.js";
|
|
11
4
|
import { join } from 'node:path';
|
|
12
5
|
import { argv, env, execPath, stderr, stdout } from 'node:process';
|
|
13
|
-
import {
|
|
6
|
+
import { TYPED } from "../../core/answers.js";
|
|
14
7
|
import { CLI, exit } from "../../core/invoked.js";
|
|
15
8
|
import { named } from "../../core/scope.js";
|
|
16
9
|
import { findTree, inTree } from "../../core/tree.js";
|
|
@@ -31,11 +24,6 @@ const CHECKS = [
|
|
|
31
24
|
runs: 'pnpm exec eslint .',
|
|
32
25
|
because: 'the rules that live on the lint surface, which is the only one carrying a fixer the sweep can report but not repair',
|
|
33
26
|
},
|
|
34
|
-
{
|
|
35
|
-
id: 'seal',
|
|
36
|
-
runs: 'pnpm seal:check',
|
|
37
|
-
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',
|
|
38
|
-
},
|
|
39
27
|
];
|
|
40
28
|
const MINE = /^(?:npx |pnpm )?thread /;
|
|
41
29
|
function run(what, extra) {
|
|
@@ -79,7 +67,7 @@ const TOKEN = 'sec_registry_token';
|
|
|
79
67
|
const PRODUCTION = 'production';
|
|
80
68
|
const secrets = inTree(TREE, 'secrets.ts');
|
|
81
69
|
const declared = existsSync(secrets)
|
|
82
|
-
? ((
|
|
70
|
+
? (valuesIn(readFileSync(secrets)).SECRETS ?? [])
|
|
83
71
|
: [];
|
|
84
72
|
const vaulted = declared.some((one) => one.id === TOKEN);
|
|
85
73
|
if (vaulted && !sealedIn(ROOT, PRODUCTION).includes(TOKEN)) {
|
|
@@ -108,10 +96,10 @@ stdout.write(vaulted
|
|
|
108
96
|
? `publish: ${TOKEN} is declared and sealed for ${PRODUCTION}. It is opened only after every check passes, and handed to pnpm as its auth setting for ${registry}.\n`
|
|
109
97
|
: `publish: no row declares ${TOKEN}, so this publish uses ${signedAs}. Declare it with \`thread create secret\` to publish with a token sealed in this repository instead.\n`);
|
|
110
98
|
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');
|
|
111
|
-
const made = run(
|
|
99
|
+
const made = run(`${TYPED} packaged --build`);
|
|
112
100
|
stdout.write(`publish: packaged exited ${String(made)}\n`);
|
|
113
101
|
if (made !== 0) {
|
|
114
|
-
refuse('packaged', made, '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',
|
|
102
|
+
refuse('packaged', made, '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', `${TYPED} packaged --build`);
|
|
115
103
|
}
|
|
116
104
|
stdout.write(`\npublish: ${String(CHECKS.length)} check(s) stand between this tree and ${registry}. Any one of them stops it.\n`);
|
|
117
105
|
for (const check of CHECKS)
|
|
@@ -124,32 +112,32 @@ for (const [index, check] of CHECKS.entries()) {
|
|
|
124
112
|
if (code !== 0)
|
|
125
113
|
refuse(check.id, code, check.because, check.runs);
|
|
126
114
|
}
|
|
127
|
-
const numbered = run(
|
|
115
|
+
const numbered = run(`${TYPED} packaged --release`);
|
|
128
116
|
stdout.write(`publish: packaged --release exited ${String(numbered)}\n`);
|
|
129
117
|
if (numbered !== 0) {
|
|
130
|
-
refuse('packaged', numbered, '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',
|
|
118
|
+
refuse('packaged', numbered, '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', `${TYPED} packaged --release`);
|
|
131
119
|
}
|
|
132
120
|
const handed = vaulted ? { [setting()]: await tokened() } : undefined;
|
|
133
121
|
stdout.write(`\npublish: every check passed. Publishing to ${registry} with ${vaulted ? `${TOKEN} from this repository's vault` : signedAs}.\n`);
|
|
134
122
|
const sent = run(`pnpm -r publish --registry ${registry} --no-git-checks --access public`, handed);
|
|
135
123
|
if (sent !== 0) {
|
|
136
124
|
stderr.write(`\npublish: the checks passed and the publish itself exited ${String(sent)}.\n`);
|
|
137
|
-
const back = run(
|
|
125
|
+
const back = run(`${TYPED} packaged`);
|
|
138
126
|
stderr.write(back === 0
|
|
139
127
|
? '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'
|
|
140
|
-
:
|
|
128
|
+
: `publish: the manifests still carry the number that did not go out. Run \`${TYPED} packaged\` to put the last recorded release back.\n`);
|
|
141
129
|
exit(1);
|
|
142
130
|
}
|
|
143
|
-
const recorded = run(
|
|
131
|
+
const recorded = run(`${TYPED} packaged --record`);
|
|
144
132
|
stdout.write(`publish: packaged --record exited ${String(recorded)}\n`);
|
|
145
133
|
if (recorded !== 0) {
|
|
146
|
-
stderr.write(
|
|
134
|
+
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`);
|
|
147
135
|
exit(1);
|
|
148
136
|
}
|
|
149
137
|
const SHEET = 'packaging.ts';
|
|
150
138
|
const sheet = inTree(TREE, SHEET);
|
|
151
139
|
const stated = existsSync(sheet)
|
|
152
|
-
? (
|
|
140
|
+
? valuesIn(readFileSync(sheet))
|
|
153
141
|
: {};
|
|
154
142
|
const shipped = stated.PACKAGES ?? [];
|
|
155
143
|
function versionAt(folder) {
|
|
@@ -191,5 +179,5 @@ else {
|
|
|
191
179
|
stdout.write(`publish: committed the derived artifacts to ${branch} — ${going}.\n`);
|
|
192
180
|
}
|
|
193
181
|
}
|
|
194
|
-
stdout.write(`\npublish: done. What went out was derived, then built, then passed the gate
|
|
182
|
+
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`);
|
|
195
183
|
exit(0);
|
|
@@ -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
|
|