@monosmith/plugin-sdk 0.3.0 → 0.4.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.
|
@@ -7,6 +7,6 @@ export declare const CONFORMANCE_STACK = "conformance";
|
|
|
7
7
|
* exist, and, for a language plugin, that its own `discover` returns what it
|
|
8
8
|
* made, with the kind and the stack it was asked for, tasks with commands, and
|
|
9
9
|
* that the dry runs of `version/write`, `remove`, `publish` and `published`
|
|
10
|
-
* answer.
|
|
10
|
+
* answer, and, for a plugin that changes kinds, what `project/kind` edits.
|
|
11
11
|
*/
|
|
12
12
|
export declare function checkGeneration(client: PluginClient, workspace: string, report: ConformanceReport): Promise<void>;
|
|
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { effectiveRoles } from '../plugin-roles/index.js';
|
|
4
4
|
import { projectKindDirectory } from '../projects/index.js';
|
|
5
|
+
import { checkKindChange } from './check-kind-change.use-case.js';
|
|
5
6
|
import { findManifests } from './find-manifests.algorithm.js';
|
|
6
7
|
import { goList, goQuote } from './go-list.mapper.js';
|
|
7
8
|
/** The stack name generated projects are asked to record. */
|
|
@@ -15,7 +16,7 @@ function article(kind) {
|
|
|
15
16
|
* exist, and, for a language plugin, that its own `discover` returns what it
|
|
16
17
|
* made, with the kind and the stack it was asked for, tasks with commands, and
|
|
17
18
|
* that the dry runs of `version/write`, `remove`, `publish` and `published`
|
|
18
|
-
* answer.
|
|
19
|
+
* answer, and, for a plugin that changes kinds, what `project/kind` edits.
|
|
19
20
|
*/
|
|
20
21
|
export async function checkGeneration(client, workspace, report) {
|
|
21
22
|
const language = effectiveRoles(client.info).includes('language');
|
|
@@ -69,6 +70,9 @@ export async function checkGeneration(client, workspace, report) {
|
|
|
69
70
|
if (client.supports('remove')) {
|
|
70
71
|
await attempt(report, `would remove the ${params.kind} (dry run)`, () => client.call('remove', { project, dryRun: true }, { timeoutMs: 60_000 }));
|
|
71
72
|
}
|
|
73
|
+
if (client.supports('project/kind')) {
|
|
74
|
+
await checkKindChange(client, workspace, project, projects, report);
|
|
75
|
+
}
|
|
72
76
|
if (params.kind === 'package' && client.supports('publish')) {
|
|
73
77
|
await attempt(report, 'publishes the package (dry run)', () => client.call('publish', { project, version: '1.2.3', dryRun: true }, { timeoutMs: 5 * 60_000 }));
|
|
74
78
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginClient } from '../host/index.ts';
|
|
2
|
+
import type { Project } from '../protocol/index.ts';
|
|
3
|
+
import type { ConformanceReport } from './conformance-report.model.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Asks a plugin that declares `changeKind` what making a generated project
|
|
6
|
+
* another kind edits, and checks it answers with a refusal or with edits to
|
|
7
|
+
* files that exist, and writes nothing itself: the host applies the edits.
|
|
8
|
+
*/
|
|
9
|
+
export declare function checkKindChange(client: PluginClient, workspace: string, project: Project, all: readonly Project[], report: ConformanceReport): Promise<void>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join, posix } from 'node:path';
|
|
3
|
+
import { projectKindDirectory } from '../projects/index.js';
|
|
4
|
+
import { findManifests } from './find-manifests.algorithm.js';
|
|
5
|
+
import { goList } from './go-list.mapper.js';
|
|
6
|
+
/** The kind a generated project is asked to become: a lib or an app is published as a package, a package goes back to a lib. */
|
|
7
|
+
function kindChangeTarget(kind) {
|
|
8
|
+
if (kind === 'app')
|
|
9
|
+
return 'lib';
|
|
10
|
+
return kind === 'lib' ? 'package' : 'lib';
|
|
11
|
+
}
|
|
12
|
+
function article(kind) {
|
|
13
|
+
return kind === 'app' ? 'an app' : `a ${kind}`;
|
|
14
|
+
}
|
|
15
|
+
/** The content of every manifest of the workspace, by path. */
|
|
16
|
+
function snapshot(workspace, names) {
|
|
17
|
+
const content = new Map();
|
|
18
|
+
for (const manifest of findManifests(workspace, names)) {
|
|
19
|
+
const text = readFileSync(join(workspace, ...manifest.split('/')), 'utf8');
|
|
20
|
+
content.set(manifest, text);
|
|
21
|
+
}
|
|
22
|
+
return content;
|
|
23
|
+
}
|
|
24
|
+
function same(a, b) {
|
|
25
|
+
return a.size === b.size && [...a].every(([path, text]) => b.get(path) === text);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Asks a plugin that declares `changeKind` what making a generated project
|
|
29
|
+
* another kind edits, and checks it answers with a refusal or with edits to
|
|
30
|
+
* files that exist, and writes nothing itself: the host applies the edits.
|
|
31
|
+
*/
|
|
32
|
+
export async function checkKindChange(client, workspace, project, all, report) {
|
|
33
|
+
const to = kindChangeTarget(project.kind);
|
|
34
|
+
const newRoot = `${projectKindDirectory(to)}/${posix.basename(project.root)}`;
|
|
35
|
+
const check = `answers what making the ${project.kind} ${article(to)} edits (dry run)`;
|
|
36
|
+
const names = client.info.manifests ?? [];
|
|
37
|
+
const before = snapshot(workspace, names);
|
|
38
|
+
let answer;
|
|
39
|
+
try {
|
|
40
|
+
answer = await client.call('project/kind', { project, to, newRoot, projects: [...all] }, { timeoutMs: 5 * 60_000 });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
report.add(check, false, error instanceof Error ? error.message : String(error));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const files = answer.files ?? [];
|
|
47
|
+
const missing = files.map(file => file.path).filter(path => !existsSync(join(workspace, ...path.split('/'))));
|
|
48
|
+
if ((answer.refused ?? '') === '' && files.length === 0) {
|
|
49
|
+
report.add(check, false, 'it answered with neither a refusal nor an edit: a changed kind is recorded in a file');
|
|
50
|
+
}
|
|
51
|
+
else if (missing.length > 0) {
|
|
52
|
+
report.add(check, false, `edits files that do not exist (paths are those before the move): ${goList(missing)}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const untouched = same(before, snapshot(workspace, names));
|
|
56
|
+
report.add(check, untouched, 'it wrote to the workspace: it must only answer with the new content, and let the host write it');
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monosmith/plugin-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The TypeScript SDK for writing smith plugins: the protocol's types, the serve loop, a test host, the conformance checks, templating and key-order-keeping JSON editing. No runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|