paperlint 3.2.0 → 4.0.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/CLAUDE.md +2 -2
- package/dist/adapters/banal/failure.d.ts +1 -7
- package/dist/adapters/banal/failure.d.ts.map +1 -1
- package/dist/adapters/banal/failure.js +5 -7
- package/dist/adapters/banal/failure.js.map +1 -1
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +8 -11
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/install.d.ts.map +1 -1
- package/dist/adapters/banal/install.js +4 -3
- package/dist/adapters/banal/install.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +9 -4
- package/dist/adapters/banal/invocation.d.ts.map +1 -1
- package/dist/adapters/banal/invocation.js +13 -1
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/locate.d.ts +12 -9
- package/dist/adapters/banal/locate.d.ts.map +1 -1
- package/dist/adapters/banal/locate.js +19 -12
- package/dist/adapters/banal/locate.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +10 -1
- package/dist/adapters/banal/run.d.ts.map +1 -1
- package/dist/adapters/banal/run.js +8 -4
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +1 -1
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +2 -1
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -2
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +6 -3
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/node/workspace.io.d.ts.map +1 -1
- package/dist/adapters/node/workspace.io.js +4 -2
- package/dist/adapters/node/workspace.io.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +4 -2
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/build-engine.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +8 -1
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +2 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +5 -6
- package/dist/build.js.map +1 -1
- package/dist/caller-path.d.ts +3 -0
- package/dist/caller-path.d.ts.map +1 -0
- package/dist/caller-path.js +13 -0
- package/dist/caller-path.js.map +1 -0
- package/dist/cli.d.ts +10 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +62 -12
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +12 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +2 -1
- package/dist/doctor.js.map +1 -1
- package/dist/domain/paths.d.ts +8 -2
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +12 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/ready.d.ts +24 -0
- package/dist/domain/ready.d.ts.map +1 -0
- package/dist/domain/ready.js +11 -0
- package/dist/domain/ready.js.map +1 -0
- package/dist/domain/record.d.ts +8 -0
- package/dist/domain/record.d.ts.map +1 -0
- package/dist/domain/record.js +8 -0
- package/dist/domain/record.js.map +1 -0
- package/dist/domain/sha256.d.ts.map +1 -1
- package/dist/domain/sha256.js +4 -2
- package/dist/domain/sha256.js.map +1 -1
- package/dist/engine.d.ts +23 -22
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +1 -1
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -1
- package/dist/eslint-rules/install-path-literals.d.ts +2 -2
- package/dist/eslint-rules/install-path-literals.js.map +1 -1
- package/dist/eslint-rules/paper-research-question.d.ts +1 -1
- package/dist/eslint-rules/paper-research-question.js.map +1 -1
- package/dist/eslint-rules/paper-stages.d.ts +2 -2
- package/dist/eslint-rules/paper-stages.js.map +1 -1
- package/dist/eslint-rules/paper-typography.d.ts +6 -6
- package/dist/eslint-rules/paper-typography.js.map +1 -1
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +1 -1
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -1
- package/dist/eslint-rules/review-frontmatter.d.ts +2 -2
- package/dist/eslint-rules/review-frontmatter.js.map +1 -1
- package/dist/eslint-rules/sibling-frontmatter.d.ts +1 -1
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -1
- package/dist/eslint-rules/temp-root-realpath.d.ts +1 -1
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -1
- package/dist/eslint-rules/tex-build.d.ts +2 -2
- package/dist/eslint-rules/tex-build.js.map +1 -1
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +29 -26
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +17 -2
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +3 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +18 -8
- package/dist/init.js.map +1 -1
- package/dist/lib/paper-config.d.ts.map +1 -1
- package/dist/lib/paper-config.js +1 -5
- package/dist/lib/paper-config.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +5 -1
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -6
- package/dist/paper-files.js.map +1 -1
- package/dist/paper-settings.js +3 -3
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts +51 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +23 -15
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +2 -3
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/tool-installer.d.ts +2 -14
- package/dist/ports/tool-installer.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +16 -4
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +4 -4
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts +26 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +9 -9
- package/dist/rules-config.js.map +1 -1
- package/dist/tex-requirements.d.ts +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +14 -2
- package/dist/tex-requirements.js.map +1 -1
- package/dist/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +20 -7
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +2 -2
- package/eslint-rules/bib-reachable-entry.ts +1 -1
- package/eslint-rules/install-path-literals.ts +2 -2
- package/eslint-rules/paper-research-question.ts +1 -1
- package/eslint-rules/paper-stages.ts +2 -2
- package/eslint-rules/paper-typography.ts +2 -2
- package/eslint-rules/pdf-last-page-balance.ts +1 -1
- package/eslint-rules/review-frontmatter.ts +1 -1
- package/eslint-rules/temp-root-realpath.ts +1 -1
- package/eslint-rules/tex-build.ts +2 -2
- package/eslint.config.mjs +7 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
- package/fixtures/install-path-literals/clean.md +2 -2
- package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
- package/fixtures/install-path-literals/defect.md +2 -2
- package/hooks/paper-status-gates.sh +9 -19
- package/lib/paper-config.harness.mjs +1 -1
- package/lib/paper-config.ts +1 -5
- package/lib/skill-checks.mjs +24 -381
- package/lib/skill-checks.test.mjs +63 -115
- package/lib/skill-corpus.mjs +10 -251
- package/lib/skill-corpus.test.mjs +2 -13
- package/lib/skill-trigger-cases.mjs +1 -1
- package/package.json +1 -1
- package/scripts/coverage-config.test.ts +0 -1
- package/scripts/harness-api.frozen.json +0 -1
- package/skills/analyze-sibling-paper/SKILL.md +1 -39
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
- package/skills/argument-arc/SKILL.md +2 -36
- package/skills/argument-arc/SKILL.md.spec.ts +1 -43
- package/skills/build-benchmark/SKILL.md +1 -41
- package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
- package/skills/camera-ready/SKILL.md +1 -34
- package/skills/camera-ready/SKILL.md.spec.ts +0 -33
- package/skills/cold-read-diff/SKILL.md +1 -35
- package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
- package/skills/draft-paper/SKILL.md +2 -41
- package/skills/draft-paper/SKILL.md.spec.ts +1 -50
- package/skills/extend-paper/SKILL.md +2 -33
- package/skills/extend-paper/SKILL.md.spec.ts +1 -42
- package/skills/find-venue/SKILL.md +2 -38
- package/skills/find-venue/SKILL.md.spec.ts +1 -47
- package/skills/grade-paper-writing/SKILL.md +3 -38
- package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
- package/skills/grade-paper-writing/prose-lint.mjs +2 -3
- package/skills/grade-paper-writing/prose-lint.test.ts +1 -1
- package/skills/harden-paper/SKILL.md +1 -39
- package/skills/harden-paper/SKILL.md.spec.ts +0 -38
- package/skills/map-prior-work/SKILL.md +1 -35
- package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.ts +3 -4
- package/skills/paper-adversarial-review/SKILL.md +2 -35
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
- package/skills/paper-pipeline/SKILL.md +2 -28
- package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
- package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
- package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
- package/skills/paper-pipeline/scripts/README.md +30 -225
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
- package/skills/paper-pipeline/scripts/consumer.mjs +23 -104
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
- package/skills/paper-status/SKILL.md +2 -42
- package/skills/paper-status/SKILL.md.spec.ts +1 -41
- package/skills/pc-panel-review/SKILL.md +2 -46
- package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
- package/skills/plan-paper-timeline/SKILL.md +2 -36
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
- package/skills/render-paper/SKILL.md +1 -37
- package/skills/render-paper/SKILL.md.spec.ts +0 -36
- package/skills/research-ideate/SKILL.md +2 -39
- package/skills/research-ideate/SKILL.md.spec.ts +1 -47
- package/skills/study-accepted-papers/SKILL.md +1 -34
- package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
- package/skills/submit-paper/SKILL.md +1 -33
- package/skills/submit-paper/SKILL.md.spec.ts +0 -32
- package/skills/sweep-design-space/SKILL.md +1 -38
- package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
- package/skills/tighten-paper/SKILL.md +2 -39
- package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
- package/skills/tighten-paper/structure.mjs +4 -11
- package/skills/verify-citations/SKILL.md +2 -38
- package/skills/verify-citations/SKILL.md.spec.ts +1 -37
- package/src/adapters/banal/failure.ts +9 -10
- package/src/adapters/banal/index.test.ts +2 -2
- package/src/adapters/banal/index.ts +8 -10
- package/src/adapters/banal/install.ts +9 -4
- package/src/adapters/banal/invocation.test.ts +20 -8
- package/src/adapters/banal/invocation.ts +23 -8
- package/src/adapters/banal/locate.test.ts +2 -2
- package/src/adapters/banal/locate.ts +27 -21
- package/src/adapters/banal/run.test.ts +2 -2
- package/src/adapters/banal/run.ts +16 -10
- package/src/adapters/banal/settings.ts +2 -2
- package/src/adapters/curl/download.test.ts +4 -4
- package/src/adapters/memory/index.ts +4 -3
- package/src/adapters/node/files.test.ts +5 -3
- package/src/adapters/node/process.io.ts +9 -2
- package/src/adapters/node/process.test.ts +4 -4
- package/src/adapters/node/workspace.io.ts +9 -4
- package/src/adapters/references/index.ts +7 -2
- package/src/build-engine.test.ts +11 -6
- package/src/build-engine.ts +10 -5
- package/src/build.test.ts +10 -11
- package/src/build.ts +8 -9
- package/src/caller-path.ts +13 -0
- package/src/cli.harness.mjs +0 -2
- package/src/cli.test.ts +19 -8
- package/src/cli.ts +82 -25
- package/src/doctor.test.ts +1 -1
- package/src/doctor.ts +14 -5
- package/src/domain/paths.test.ts +15 -0
- package/src/domain/paths.ts +20 -1
- package/src/domain/ready.ts +31 -0
- package/src/domain/record.test.ts +20 -0
- package/src/domain/record.ts +12 -0
- package/src/domain/sha256.ts +5 -3
- package/src/engine.ts +30 -2
- package/src/facts-file.ts +43 -28
- package/src/hooks-settings.harness.ts +17 -0
- package/src/hooks-settings.ts +28 -4
- package/src/init.test.ts +25 -5
- package/src/init.ts +26 -12
- package/src/new-paper.harness.mjs +7 -23
- package/src/new-paper.ts +7 -3
- package/src/paper-files.test.ts +22 -19
- package/src/paper-files.ts +16 -27
- package/src/paper-settings.test.ts +2 -2
- package/src/paper-settings.ts +5 -5
- package/src/pdf-facts.test.ts +16 -5
- package/src/pdf-facts.ts +79 -22
- package/src/pdf-geometry.ts +5 -3
- package/src/ports/tool-installer.ts +2 -17
- package/src/presets.ts +28 -14
- package/src/references.test.ts +25 -19
- package/src/references.ts +4 -5
- package/src/rules-config.ts +40 -11
- package/src/tex-requirements.test.ts +21 -0
- package/src/tex-requirements.ts +29 -6
- package/src/toolchain.test.ts +9 -5
- package/src/toolchain.ts +2 -2
- package/src/types.ts +5 -3
- package/src/venue-rules.test.ts +8 -4
- package/src/venue-rules.ts +21 -11
- package/skills/paper-pipeline/scripts/announce.mjs +0 -67
- package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
- package/skills/paper-pipeline/scripts/fixtures/scorecard.ts +0 -100
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
- package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
- package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -750
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
- package/skills/paper-pipeline/scripts/status.mjs +0 -295
- package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type Environment,
|
|
10
10
|
type HostDirs,
|
|
11
11
|
} from "../../domain/host.ts";
|
|
12
|
-
import type
|
|
12
|
+
import { absolutePath, type AbsolutePath } from "../../domain/paths.ts";
|
|
13
13
|
|
|
14
14
|
/** An explicit banal to use instead of paperlint's (a path to the script). */
|
|
15
15
|
export const BANAL_ENV = "BANAL";
|
|
@@ -29,7 +29,7 @@ export interface BanalSettings {
|
|
|
29
29
|
|
|
30
30
|
/** `resolve` with an absolute base never consults the process's cwd. */
|
|
31
31
|
const absolute = (cwd: string, p: string): AbsolutePath =>
|
|
32
|
-
resolve(cwd, p)
|
|
32
|
+
absolutePath(resolve(cwd, p));
|
|
33
33
|
|
|
34
34
|
/** An unset variable and an empty one mean the same: not set. */
|
|
35
35
|
const set = (v: string | undefined): string | null => (v ? v : null);
|
|
@@ -15,18 +15,18 @@ import { tmpdir } from "node:os";
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
17
|
import { afterAll as after, test } from "vitest";
|
|
18
|
-
import
|
|
18
|
+
import { absolutePath, joinPath } from "../../domain/paths.ts";
|
|
19
19
|
import { curlDownload, curlFailure } from "./download.io.ts";
|
|
20
20
|
import type { ProcessExit } from "../../ports/process.ts";
|
|
21
21
|
import { spawnProcess } from "../node/index.ts";
|
|
22
22
|
|
|
23
|
-
const root =
|
|
24
|
-
mkdtempSync(join(tmpdir(), "paperlint-download-test-")),
|
|
23
|
+
const root = absolutePath(
|
|
24
|
+
realpathSync(mkdtempSync(join(tmpdir(), "paperlint-download-test-"))),
|
|
25
25
|
);
|
|
26
26
|
after(() => {
|
|
27
27
|
rmSync(root, { recursive: true, force: true });
|
|
28
28
|
});
|
|
29
|
-
const at = (...p: string[]) =>
|
|
29
|
+
const at = (...p: string[]) => joinPath(root, ...p);
|
|
30
30
|
|
|
31
31
|
const dl = (env: Record<string, string>) => {
|
|
32
32
|
const tmp = at("dl-tmp");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* In-memory adapters for the app layer's tests: every port, no disk, no processes, no network.
|
|
3
3
|
* Each one RECORDS what it was asked, so a test asserts on the calls instead of on side effects.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
6
6
|
import type { Download } from "../../ports/download.ts";
|
|
7
7
|
import type { Files } from "../../ports/files.ts";
|
|
8
8
|
import type { Command, ProcessExit, RunProcess } from "../../ports/process.ts";
|
|
@@ -69,11 +69,12 @@ export interface MemoryWorkspace extends Workspace {
|
|
|
69
69
|
export function memoryWorkspace(dir = "/scratch"): MemoryWorkspace {
|
|
70
70
|
const written: MemoryWorkspace["written"] = [];
|
|
71
71
|
const ended: ("returned" | "threw")[] = [];
|
|
72
|
+
const at = absolutePath(dir);
|
|
72
73
|
const scratch: Scratch = {
|
|
73
|
-
dir:
|
|
74
|
+
dir: at,
|
|
74
75
|
write(name, content, mode) {
|
|
75
76
|
written.push({ name, content, mode });
|
|
76
|
-
return `${dir}/${name}`
|
|
77
|
+
return absolutePath(`${dir}/${name}`);
|
|
77
78
|
},
|
|
78
79
|
};
|
|
79
80
|
return {
|
|
@@ -4,14 +4,16 @@ import { existsSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { afterAll as after, test } from "vitest";
|
|
7
|
-
import
|
|
7
|
+
import { absolutePath, joinPath } from "../../domain/paths.ts";
|
|
8
8
|
import { errorCode, nodeFiles } from "./files.io.ts";
|
|
9
9
|
|
|
10
|
-
const root =
|
|
10
|
+
const root = absolutePath(
|
|
11
|
+
realpathSync(mkdtempSync(join(tmpdir(), "paperlint-files-test-"))),
|
|
12
|
+
);
|
|
11
13
|
after(() => {
|
|
12
14
|
rmSync(root, { recursive: true, force: true });
|
|
13
15
|
});
|
|
14
|
-
const at = (...p: string[]) =>
|
|
16
|
+
const at = (...p: string[]) => joinPath(root, ...p);
|
|
15
17
|
|
|
16
18
|
test("Files: a missing file reads as null and is not a file", () => {
|
|
17
19
|
assert.equal(nodeFiles.readBytes(at("nope")), null);
|
|
@@ -11,8 +11,15 @@ import {
|
|
|
11
11
|
} from "node:child_process";
|
|
12
12
|
import type { Command, ProcessExit, RunProcess } from "../../ports/process.ts";
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Node's `spawnSync` — the one overload this file calls, text in and text out — or a stand-in with its
|
|
16
|
+
* shape (a harness records the calls through one).
|
|
17
|
+
*/
|
|
18
|
+
export type SpawnSync = (
|
|
19
|
+
file: string,
|
|
20
|
+
args: readonly string[],
|
|
21
|
+
options: SpawnSyncOptionsWithStringEncoding,
|
|
22
|
+
) => SpawnSyncReturns<string>;
|
|
16
23
|
|
|
17
24
|
const errno = (r: SpawnSyncReturns<string>): string | undefined =>
|
|
18
25
|
codeOf(r.error);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import assert from "node:assert/strict";
|
|
7
7
|
import { test } from "vitest";
|
|
8
|
-
import
|
|
8
|
+
import { absolutePath } from "../../domain/paths.ts";
|
|
9
9
|
import type { Command } from "../../ports/process.ts";
|
|
10
10
|
import { spawnProcess } from "./process.io.ts";
|
|
11
11
|
|
|
@@ -62,19 +62,19 @@ test("output past maxOutputBytes is a spawn failure, named by Node's own message
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
test("cwd is the Command's when it names one", () => {
|
|
65
|
-
const r = run.run({ ...sh("pwd"), cwd: "/"
|
|
65
|
+
const r = run.run({ ...sh("pwd"), cwd: absolutePath("/") });
|
|
66
66
|
assert.deepEqual(r.kind === "exited" && r.stdout, "/\n");
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test("a spawn that reports no status, no signal and no error reads as exit 1, not success", () => {
|
|
70
|
-
const silent = spawnProcess((
|
|
70
|
+
const silent = spawnProcess(() => ({
|
|
71
71
|
pid: 0,
|
|
72
72
|
output: [],
|
|
73
73
|
stdout: "",
|
|
74
74
|
stderr: "",
|
|
75
75
|
status: null,
|
|
76
76
|
signal: null,
|
|
77
|
-
}))
|
|
77
|
+
}));
|
|
78
78
|
assert.deepEqual(silent.run(sh("true")), {
|
|
79
79
|
kind: "exited",
|
|
80
80
|
status: 1,
|
|
@@ -10,14 +10,18 @@ import {
|
|
|
10
10
|
writeFileSync,
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
absolutePath,
|
|
15
|
+
joinPath,
|
|
16
|
+
type AbsolutePath,
|
|
17
|
+
} from "../../domain/paths.ts";
|
|
14
18
|
import type { NotAPromise, Scratch, Workspace } from "../../ports/workspace.ts";
|
|
15
19
|
|
|
16
20
|
function scratchIn(dir: AbsolutePath): Scratch {
|
|
17
21
|
return {
|
|
18
22
|
dir,
|
|
19
23
|
write(name, content, mode): AbsolutePath {
|
|
20
|
-
const at =
|
|
24
|
+
const at = joinPath(dir, name);
|
|
21
25
|
writeFileSync(at, content);
|
|
22
26
|
if (mode === "exec") chmodSync(at, 0o755);
|
|
23
27
|
return at;
|
|
@@ -29,9 +33,10 @@ function scratchIn(dir: AbsolutePath): Scratch {
|
|
|
29
33
|
export function tmpWorkspace(tmpDir: string): Workspace {
|
|
30
34
|
return {
|
|
31
35
|
within<T>(prefix: string, use: (s: Scratch) => NotAPromise<T>): T {
|
|
32
|
-
|
|
36
|
+
// `realpath` answers with an absolute path, whatever `tmpDir` was.
|
|
37
|
+
const dir = absolutePath(realpathSync(mkdtempSync(join(tmpDir, prefix))));
|
|
33
38
|
try {
|
|
34
|
-
return use(scratchIn(dir
|
|
39
|
+
return use(scratchIn(dir));
|
|
35
40
|
} finally {
|
|
36
41
|
rmSync(dir, { recursive: true, force: true });
|
|
37
42
|
}
|
|
@@ -44,6 +44,7 @@ import type {
|
|
|
44
44
|
BibEntry,
|
|
45
45
|
} from "../../../skills/verify-citations/scripts/bib-authors.mjs";
|
|
46
46
|
import { messageOf } from "../../domain/text.ts";
|
|
47
|
+
import { isRecord } from "../../domain/record.ts";
|
|
47
48
|
|
|
48
49
|
const describeAuthors = (f: AuthorFinding): string =>
|
|
49
50
|
[
|
|
@@ -188,12 +189,16 @@ function grown(
|
|
|
188
189
|
): LookupCache {
|
|
189
190
|
const { cache, usable } = loaded;
|
|
190
191
|
const { store, dblp } = run;
|
|
191
|
-
|
|
192
|
+
// verify-cites stores each answer as an object; only such a value is a response the cache can hold.
|
|
193
|
+
const fresh = Object.entries(store).filter(
|
|
194
|
+
(e): e is [string, Readonly<Record<string, unknown>>] =>
|
|
195
|
+
!usable.citations.has(e[0]) && isRecord(e[1]),
|
|
196
|
+
);
|
|
192
197
|
const freshDblp = [...dblp].filter(([k]) => !usable.dblp.has(k));
|
|
193
198
|
if (fresh.length === 0 && freshDblp.length === 0) return cache;
|
|
194
199
|
const dated = fresh.map(([k, response]): [string, CachedResponse] => [
|
|
195
200
|
k,
|
|
196
|
-
{ fetched: today(), response
|
|
201
|
+
{ fetched: today(), response },
|
|
197
202
|
]);
|
|
198
203
|
return {
|
|
199
204
|
citations: new Map([...cache.citations, ...dated]),
|
package/src/build-engine.test.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { dirname, join } from "node:path";
|
|
|
9
9
|
import { test } from "vitest";
|
|
10
10
|
import { useTempDir } from "../test/support.ts";
|
|
11
11
|
import { prepareEngine, refusal } from "./build-engine.ts";
|
|
12
|
+
import type { Runner } from "./engine.ts";
|
|
12
13
|
|
|
13
14
|
const work = useTempDir("paperlint-build-engine-test-");
|
|
14
15
|
const TEX = { packages: { acmart: ["acmart.cls"] }, tools: {} };
|
|
@@ -20,11 +21,12 @@ const texAt = (dir: string): string => {
|
|
|
20
21
|
return dir;
|
|
21
22
|
};
|
|
22
23
|
/** kpsewhich answers per bin directory. */
|
|
23
|
-
const fakeRun =
|
|
24
|
-
(
|
|
24
|
+
const fakeRun =
|
|
25
|
+
(found: Record<string, string>): Runner =>
|
|
26
|
+
(cmd) => ({
|
|
25
27
|
stdout: found[dirname(cmd)] ?? "",
|
|
26
28
|
status: 0,
|
|
27
|
-
})
|
|
29
|
+
});
|
|
28
30
|
|
|
29
31
|
async function go(o: Parameters<typeof prepareEngine>[0]) {
|
|
30
32
|
const out: string[] = [];
|
|
@@ -101,7 +103,10 @@ test("a complete cache is used from an environment with no PATH at all", async (
|
|
|
101
103
|
|
|
102
104
|
test("the refusal line for a decision to use a tree names nothing missing", () => {
|
|
103
105
|
assert.equal(
|
|
104
|
-
refusal(
|
|
106
|
+
refusal(
|
|
107
|
+
{ kind: "use-cache", tree: { label: "cache", bin: "/b", missing: [] } },
|
|
108
|
+
{ system: null },
|
|
109
|
+
),
|
|
105
110
|
"✗ paperlint build: no TeX Live with every package these papers need — run `npx paperlint toolchain` (missing: 0 package(s): )",
|
|
106
111
|
);
|
|
107
112
|
});
|
|
@@ -122,14 +127,14 @@ test("without an ask or an install given: Enter is yes, and the default install
|
|
|
122
127
|
PAPERLINT_CTAN_MIRROR: "https://mirror.invalid/tlnet",
|
|
123
128
|
PATH: join(work, "nothing"),
|
|
124
129
|
},
|
|
125
|
-
run: (
|
|
130
|
+
run: (cmd) => {
|
|
126
131
|
calls.push(cmd.split("/").pop() ?? cmd);
|
|
127
132
|
return {
|
|
128
133
|
status: 22,
|
|
129
134
|
stdout: "",
|
|
130
135
|
stderr: "curl: (6) could not resolve\n",
|
|
131
136
|
};
|
|
132
|
-
}
|
|
137
|
+
},
|
|
133
138
|
});
|
|
134
139
|
assert.deepEqual(r, { ok: false, code: 1 });
|
|
135
140
|
assert.ok(calls.includes("curl"), calls.join(","));
|
package/src/build-engine.ts
CHANGED
|
@@ -62,10 +62,12 @@ type Resolved = Required<Omit<EngineOptions, "install">> & {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
/** The options a caller actually set — `undefined` must still get its default, so no plain spread. */
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
65
|
+
function defined<T extends object>(o: T): Partial<T> {
|
|
66
|
+
const out: Partial<T> = {};
|
|
67
|
+
// `for…in` hands out `o`'s keys typed as its keys; an options literal has no inherited ones.
|
|
68
|
+
for (const k in o) if (o[k] !== undefined) out[k] = o[k];
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
69
71
|
|
|
70
72
|
function withDefaults(o: EngineOptions): Resolved {
|
|
71
73
|
const r = {
|
|
@@ -133,7 +135,10 @@ const names = (missing: readonly string[]): string =>
|
|
|
133
135
|
`${String(missing.length)} package(s): ${missing.join(", ")}`;
|
|
134
136
|
|
|
135
137
|
/** The one line a run without a human gets. It names the command and what is missing. */
|
|
136
|
-
export function refusal(
|
|
138
|
+
export function refusal(
|
|
139
|
+
d: EngineDecision,
|
|
140
|
+
facts: Pick<EngineFacts, "system">,
|
|
141
|
+
): string {
|
|
137
142
|
const missing = d.kind === "refuse" || d.kind === "ask" ? d.missing : [];
|
|
138
143
|
const sys =
|
|
139
144
|
facts.system && facts.system.missing.length
|
package/src/build.test.ts
CHANGED
|
@@ -8,7 +8,7 @@ import assert from "node:assert/strict";
|
|
|
8
8
|
import { existsSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { test } from "vitest";
|
|
11
|
-
import { useTempDir, writeTree } from "../test/support.ts";
|
|
11
|
+
import { present, useTempDir, writeTree } from "../test/support.ts";
|
|
12
12
|
import {
|
|
13
13
|
bibInput,
|
|
14
14
|
buildPaper,
|
|
@@ -64,16 +64,16 @@ function bibtexRun(f: Fake, cwd: string) {
|
|
|
64
64
|
}
|
|
65
65
|
function fakeRun(f: Fake): NonNullable<BuildOptions["run"]> {
|
|
66
66
|
let pass = 0;
|
|
67
|
-
return (
|
|
67
|
+
return (bin, _args, opts) => {
|
|
68
68
|
if (bin === f.missing)
|
|
69
69
|
return {
|
|
70
70
|
error: Object.assign(new Error("spawn ENOENT"), { code: "ENOENT" }),
|
|
71
71
|
status: null,
|
|
72
72
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
73
|
+
// The build always runs a pass in the paper's directory.
|
|
74
|
+
const cwd = present(opts.cwd, "the pass's cwd");
|
|
75
|
+
return bin === "pdflatex" ? latexPass(f, cwd, ++pass) : bibtexRun(f, cwd);
|
|
76
|
+
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const NO_GEOMETRY: Geometry = {
|
|
@@ -146,11 +146,10 @@ test("documentclass: an empty name is no class; a macro inside the name contribu
|
|
|
146
146
|
// `\documentclass` came back bare has no name to read, and is no class.
|
|
147
147
|
assert.equal(
|
|
148
148
|
parseDocumentclass("\\documentclass", {
|
|
149
|
-
parse: () =>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}) as never,
|
|
149
|
+
parse: () => ({
|
|
150
|
+
type: "root",
|
|
151
|
+
content: [{ type: "macro", content: "documentclass" }],
|
|
152
|
+
}),
|
|
154
153
|
}),
|
|
155
154
|
null,
|
|
156
155
|
);
|
package/src/build.ts
CHANGED
|
@@ -61,7 +61,8 @@ import { banalMeasurer, parseBanalSettings } from "./adapters/banal/index.ts";
|
|
|
61
61
|
// eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
|
|
62
62
|
import { hostDirs, nodeAdapters, nodeFiles } from "./adapters/node/index.ts";
|
|
63
63
|
import { whyNoGeometry } from "./domain/geometry.ts";
|
|
64
|
-
import
|
|
64
|
+
import { callerPath } from "./caller-path.ts";
|
|
65
|
+
import type { Runner } from "./engine.ts";
|
|
65
66
|
import type { Files } from "./ports/files.ts";
|
|
66
67
|
import type { MeasureGeometry } from "./ports/measure-geometry.ts";
|
|
67
68
|
import type { CheckReferences } from "./ports/check-references.ts";
|
|
@@ -76,7 +77,6 @@ import {
|
|
|
76
77
|
import {
|
|
77
78
|
nextStep,
|
|
78
79
|
summarize,
|
|
79
|
-
TRACKED,
|
|
80
80
|
type BibInput,
|
|
81
81
|
type Hashes,
|
|
82
82
|
type Observation,
|
|
@@ -128,7 +128,7 @@ export interface PaperFacts {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/** A process runner with `spawnSync`'s shape — the port the harness replaces. */
|
|
131
|
-
export type Runner
|
|
131
|
+
export type { Runner };
|
|
132
132
|
|
|
133
133
|
/** What a step's `run` receives. `env` is the environment accumulated by the steps before it. */
|
|
134
134
|
export interface BuildContext {
|
|
@@ -286,9 +286,8 @@ function hashes(paperDir: string): Hashes {
|
|
|
286
286
|
const p = join(paperDir, `${JOB}.${ext}`);
|
|
287
287
|
return existsSync(p) ? sha(readFileSync(p)) : null;
|
|
288
288
|
};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return out as Hashes;
|
|
289
|
+
// A literal of the mapped type: a new tracked extension without a hash here is a compile error.
|
|
290
|
+
return { aux: h("aux"), toc: h("toc"), out: h("out"), bbl: h("bbl") };
|
|
292
291
|
}
|
|
293
292
|
|
|
294
293
|
const readOr = (path: string, enc: BufferEncoding): string | null =>
|
|
@@ -387,8 +386,8 @@ function latexPass(
|
|
|
387
386
|
|
|
388
387
|
/** The two streams of a finished run, as they really arrive (see `printed`). */
|
|
389
388
|
interface Streams {
|
|
390
|
-
readonly stdout
|
|
391
|
-
readonly stderr
|
|
389
|
+
readonly stdout?: string | undefined;
|
|
390
|
+
readonly stderr?: string | undefined;
|
|
392
391
|
}
|
|
393
392
|
|
|
394
393
|
/**
|
|
@@ -694,7 +693,7 @@ function defaultMeasurer(
|
|
|
694
693
|
return banalMeasurer(
|
|
695
694
|
nodeAdapters({ tmpDir: dirs.tmp }),
|
|
696
695
|
parseBanalSettings(b.env, dirs),
|
|
697
|
-
b.projectRoot
|
|
696
|
+
callerPath(b.projectRoot),
|
|
698
697
|
);
|
|
699
698
|
}
|
|
700
699
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path as a caller gave it, made absolute where it enters a `Files` call.
|
|
3
|
+
*
|
|
4
|
+
* The app's use cases take paper directories and files as strings, and some callers pass them as
|
|
5
|
+
* typed on a command line (`skills/render-paper/extract-pdf-facts.mjs <paper.pdf>`). A relative one
|
|
6
|
+
* has always meant the process's cwd — `node:fs` resolved it there. Resolving it HERE, the same way,
|
|
7
|
+
* makes the `AbsolutePath` brand a checked fact rather than an assertion: the value handed to the port
|
|
8
|
+
* is absolute, and nothing downstream has to wonder.
|
|
9
|
+
*/
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { absolutePath, type AbsolutePath } from "./domain/paths.ts";
|
|
12
|
+
|
|
13
|
+
export const callerPath = (p: string): AbsolutePath => absolutePath(resolve(p));
|
package/src/cli.harness.mjs
CHANGED
package/src/cli.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { existsSync } from "node:fs";
|
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
11
11
|
import { test } from "vitest";
|
|
12
12
|
import { runNode, useTempDir, writeTree } from "../test/support.ts";
|
|
13
|
+
import type { runToolchain } from "./toolchain.ts";
|
|
13
14
|
import {
|
|
14
15
|
chooseVenue,
|
|
15
16
|
initTexLive,
|
|
@@ -57,6 +58,18 @@ test("parseArgs: the boolean flags of build and toolchain, and a value flag with
|
|
|
57
58
|
);
|
|
58
59
|
});
|
|
59
60
|
|
|
61
|
+
test("🔴 parseSettings refuses a mistyped `structure` instead of letting `lint` crash on it", () => {
|
|
62
|
+
// Guards: `markers: 5` used to pass the boundary, and `checkStructure` then threw
|
|
63
|
+
// "rules.markers.some is not a function" from inside `paperlint lint`.
|
|
64
|
+
for (const structure of [{ markers: 5 }, "yes", { requireOneOf: ["a"] }]) {
|
|
65
|
+
const r = parseSettings({ structure }, "paperlint.json", "/r");
|
|
66
|
+
assert.equal(r.ok, false, JSON.stringify(structure));
|
|
67
|
+
assert.match(r.error, /^paperlint\.json: "structure" must be false/);
|
|
68
|
+
}
|
|
69
|
+
const off = parseSettings({ structure: false }, "paperlint.json", "/r");
|
|
70
|
+
assert.equal(off.ok && off.value.structure, false);
|
|
71
|
+
});
|
|
72
|
+
|
|
60
73
|
test("parseSettings refuses a non-object, names one unknown key or several, and a mistyped default", () => {
|
|
61
74
|
const error = (json: unknown) => {
|
|
62
75
|
const r = parseSettings(json, "paperlint.json", "/r");
|
|
@@ -146,7 +159,7 @@ test("runHook: a runtime with no cli.js beside it, and a hook killed by a signal
|
|
|
146
159
|
});
|
|
147
160
|
const killed = runHook("paper-edit-guard", {
|
|
148
161
|
err: (m: string) => messages.push(m),
|
|
149
|
-
run: (
|
|
162
|
+
run: () => ({ status: null, signal: "SIGKILL" }),
|
|
150
163
|
});
|
|
151
164
|
assert.deepEqual(
|
|
152
165
|
{ noCli, killed, messages: messages.map((m) => m.split(" from ")[0]) },
|
|
@@ -363,7 +376,8 @@ test("a paper's paperlint.json that does not parse stops the lint with its path"
|
|
|
363
376
|
});
|
|
364
377
|
|
|
365
378
|
test("silentOptionalRules: a config with no rules turns nothing on", async () => {
|
|
366
|
-
|
|
379
|
+
const eslint = { calculateConfigForFile: () => Promise.resolve(undefined) };
|
|
380
|
+
assert.deepEqual(await silentOptionalRules(eslint, [], {}), []);
|
|
367
381
|
});
|
|
368
382
|
|
|
369
383
|
test("init with no path and --format md sets up the current directory", async () => {
|
|
@@ -538,8 +552,8 @@ test("ownVersion: the manifest's version; an unreadable manifest or a non-string
|
|
|
538
552
|
|
|
539
553
|
test("init's TeX Live install runs the toolchain over the project's papers, never a check", () => {
|
|
540
554
|
const dir = writeTree(join(root, "init-tex"), { "package.json": "{}" });
|
|
541
|
-
const calls:
|
|
542
|
-
const fake = (
|
|
555
|
+
const calls: Parameters<typeof runToolchain>[0][] = [];
|
|
556
|
+
const fake: typeof runToolchain = (o) => (calls.push(o), 0);
|
|
543
557
|
const log = () => {};
|
|
544
558
|
const here = initTexLive(
|
|
545
559
|
parseArgs(["init"]),
|
|
@@ -553,10 +567,7 @@ test("init's TeX Live install runs the toolchain over the project's papers, neve
|
|
|
553
567
|
);
|
|
554
568
|
assert.deepEqual([here.install(), there.install()], [0, 0]);
|
|
555
569
|
assert.deepEqual(
|
|
556
|
-
calls.map((c) =>
|
|
557
|
-
const o = c as { check: boolean; tex: unknown; banal: unknown };
|
|
558
|
-
return [o.check, o.tex, typeof o.banal];
|
|
559
|
-
}),
|
|
570
|
+
calls.map((c) => [c.check, c.tex, typeof c.banal]),
|
|
560
571
|
[
|
|
561
572
|
[false, toolchainTex(dir), "object"],
|
|
562
573
|
[false, toolchainTex(join(dir, "sub")), "object"],
|