speccle 0.16.0 → 0.17.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/README.md +7 -0
- package/dist/dialects.js +2 -0
- package/dist/doctor.js +20 -6
- package/dist/render.js +11 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -347,6 +347,13 @@ Joins three inputs into one number per **acceptance criterion**: the `SPEC.md` f
|
|
|
347
347
|
criterion by carrying its `[KEY-n]` token anywhere in its full concatenated name, describe
|
|
348
348
|
titles included.
|
|
349
349
|
|
|
350
|
+
This is the one command bound to a single **test dialect**. The join has to know which
|
|
351
|
+
tests covered each mutant — nothing else can credit a kill to a criterion — and only
|
|
352
|
+
StrykerJS produces that, so `strength` scores a `ts-vitest` repo and no other. On a
|
|
353
|
+
dialect it cannot score, `doctor` reports the stack as `not applicable` rather than
|
|
354
|
+
nagging toward `strength init`. Every other command — `lint`, `claims`, `verify`,
|
|
355
|
+
`risk` — is multi-language regardless.
|
|
356
|
+
|
|
350
357
|
```
|
|
351
358
|
features/checkout/SPEC.md
|
|
352
359
|
CHECKOUT-1 ████████████████████ 100.0% 14/14 Tax rounds half-up to 2dp per line item
|
package/dist/dialects.js
CHANGED
|
@@ -2,6 +2,7 @@ const TS_TEST_FILE = /\.(test|spec)\.[cm]?[jt]sx?$/;
|
|
|
2
2
|
const TS_TEST_TITLE = /\b(?:describe|it|test)(?:\.[\w.]+)*\s*\(\s*(["'`])((?:\\.|(?!\1).)*)\1/g;
|
|
3
3
|
const TS_VITEST = {
|
|
4
4
|
name: "ts-vitest",
|
|
5
|
+
supportsStrength: true,
|
|
5
6
|
isTestFile: (file) => TS_TEST_FILE.test(file),
|
|
6
7
|
readTestNames: (source) => [...source.matchAll(TS_TEST_TITLE)].map((m) => ({ name: m[2], spelling: "bracketed" })),
|
|
7
8
|
};
|
|
@@ -15,6 +16,7 @@ const XCTEST_METHOD = /\bfunc\s+(test[A-Za-z0-9_]*)\s*\(/g;
|
|
|
15
16
|
const SWIFT_ANNOTATED_DECL = /@(?:Test|Suite)\b(?!\s*\(\s*")(?:\s*\([^()]*\))?(?:\s+(?:@\w+|open|public|internal|fileprivate|private|final|static))*\s+(?:actor|class|enum|func|struct)\s+([A-Za-z_]\w*)/g;
|
|
16
17
|
const SWIFT = {
|
|
17
18
|
name: "swift",
|
|
19
|
+
supportsStrength: false,
|
|
18
20
|
isTestFile: (file) => SWIFT_TEST_FILE.test(file) || (file.endsWith(".swift") && file.split("/").includes("Tests")),
|
|
19
21
|
readTestNames(source) {
|
|
20
22
|
const found = [...source.matchAll(SWIFT_DISPLAY_NAME)].map((m) => ({ at: m.index, name: m[1], spelling: "bracketed" }));
|
package/dist/doctor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { access, readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { readConfig } from "./config.js";
|
|
4
|
+
import { DEFAULT_DIALECT, resolveDialect } from "./dialects.js";
|
|
4
5
|
import { ownVersion, STRENGTH_DEPS, STRYKER_CONFIG_NAMES } from "./init.js";
|
|
5
6
|
import { LENSES_DIR } from "./lenses.js";
|
|
6
7
|
import { pinnedVersion, WORKFLOW_FILE } from "./reviewinit.js";
|
|
@@ -25,14 +26,17 @@ export async function doctor(target) {
|
|
|
25
26
|
const workflow = await readMaybe(join(root, WORKFLOW_FILE));
|
|
26
27
|
const driverPin = workflow === undefined ? null : (pinnedVersion(workflow) ?? null);
|
|
27
28
|
const driverStatus = derivePayloadStatus(workflow !== undefined, driverPin, cli);
|
|
29
|
+
const dialect = config?.dialect ?? DEFAULT_DIALECT;
|
|
28
30
|
const provisioned = await anyPresent(root, STRYKER_CONFIG_NAMES);
|
|
29
31
|
const declared = await declaredDeps(root);
|
|
30
32
|
const deps = STRENGTH_DEPS.map((spec) => checkDep(spec, declared));
|
|
31
|
-
const stackStatus = !
|
|
32
|
-
? "
|
|
33
|
-
:
|
|
34
|
-
? "
|
|
35
|
-
: "
|
|
33
|
+
const stackStatus = !scorable(config, dialect)
|
|
34
|
+
? "unsupported"
|
|
35
|
+
: !provisioned
|
|
36
|
+
? "absent"
|
|
37
|
+
: deps.some((dep) => dep.status !== "ok")
|
|
38
|
+
? "drift"
|
|
39
|
+
: "current";
|
|
36
40
|
const current = (status) => status === "current" || status === "absent";
|
|
37
41
|
const ok = current(skillsStatus) &&
|
|
38
42
|
current(lensesStatus) &&
|
|
@@ -44,10 +48,20 @@ export async function doctor(target) {
|
|
|
44
48
|
skills: { recorded: skillsRecorded, bundled: cli, status: skillsStatus },
|
|
45
49
|
lenses: { recorded: lensesRecorded, bundled: cli, status: lensesStatus },
|
|
46
50
|
driver: { recorded: driverPin, bundled: cli, status: driverStatus },
|
|
47
|
-
stack: { provisioned, deps, status: stackStatus },
|
|
51
|
+
stack: { dialect, provisioned, deps, status: stackStatus },
|
|
48
52
|
ok,
|
|
49
53
|
};
|
|
50
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Whether any dialect in force in this repo can be scored. A mixed tree keeps its stack: an
|
|
57
|
+
* override may put one subtree on ts-vitest under a swift default (ADR-0040), and that
|
|
58
|
+
* subtree's heatmap is real. Only a repo with no scorable dialect anywhere has nothing to
|
|
59
|
+
* provision — reporting that as `absent` would nag it toward a stack it can never run.
|
|
60
|
+
*/
|
|
61
|
+
function scorable(config, dialect) {
|
|
62
|
+
const inForce = [dialect, ...(config?.overrides ?? []).map((o) => o.dialect ?? dialect)];
|
|
63
|
+
return inForce.some((name) => resolveDialect(name).supportsStrength);
|
|
64
|
+
}
|
|
51
65
|
function derivePayloadStatus(present, recorded, bundled) {
|
|
52
66
|
if (!present)
|
|
53
67
|
return "absent";
|
package/dist/render.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_DIALECT } from "./dialects.js";
|
|
1
2
|
export function renderConfigInit(report) {
|
|
2
3
|
const lines = [
|
|
3
4
|
report.action === "written"
|
|
@@ -99,10 +100,12 @@ export function renderDoctor(report) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
lines.push("");
|
|
103
|
+
// A repo whose dialect can never carry a stack still counts as bare when nothing else
|
|
104
|
+
// is installed — `init` is the right nudge, and it is not a stack it is missing.
|
|
102
105
|
const allAbsent = report.skills.status === "absent" &&
|
|
103
106
|
report.lenses.status === "absent" &&
|
|
104
107
|
report.driver.status === "absent" &&
|
|
105
|
-
report.stack.status === "absent";
|
|
108
|
+
(report.stack.status === "absent" || report.stack.status === "unsupported");
|
|
106
109
|
if (allAbsent) {
|
|
107
110
|
lines.push("Speccle is not set up here — run `speccle init`");
|
|
108
111
|
}
|
|
@@ -148,6 +151,9 @@ function describeDriver(driver) {
|
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
function describeStack(stack) {
|
|
154
|
+
if (stack.status === "unsupported") {
|
|
155
|
+
return `not applicable — oracle strength needs the ${DEFAULT_DIALECT} dialect, this repo is ${stack.dialect}`;
|
|
156
|
+
}
|
|
151
157
|
if (stack.status === "absent")
|
|
152
158
|
return "not provisioned — run `speccle strength init`";
|
|
153
159
|
if (stack.status === "current")
|
|
@@ -185,7 +191,10 @@ export function renderUpdate(report) {
|
|
|
185
191
|
else {
|
|
186
192
|
lines.push(`driver ${report.driver.from ?? "unpinned"} → ${report.driver.to} — review & commit the diff`);
|
|
187
193
|
}
|
|
188
|
-
if (report.stack.status === "
|
|
194
|
+
if (report.stack.status === "unsupported") {
|
|
195
|
+
lines.push(`stack not applicable — oracle strength needs the ${DEFAULT_DIALECT} dialect`);
|
|
196
|
+
}
|
|
197
|
+
else if (report.stack.status === "absent") {
|
|
189
198
|
lines.push("stack not provisioned — run `speccle strength init`");
|
|
190
199
|
}
|
|
191
200
|
else if (report.stack.status === "current") {
|
package/package.json
CHANGED