@nodii/dev 0.6.8 → 0.6.9
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 +41 -2
- package/dist/cli.js +13 -3
- package/dist/core.js +1 -1
- package/dist/devops-openapi.json +1 -1
- package/dist/figma.js +92 -1
- package/dist/hooks.js +3 -1
- package/package.json +1 -1
package/dist/figma.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { CliError, devRequest, encode } from "./core.js";
|
|
2
3
|
|
|
3
4
|
/** A Figma file key, or the key inside a figma.com file link (a branch link's branch key). */
|
|
4
5
|
export function figmaFileKey(value) {
|
|
@@ -13,3 +14,93 @@ export function figmaFileKey(value) {
|
|
|
13
14
|
/** What to run when Dev has no row for a Figma file (DESIGN_FILE_NOT_FOUND). */
|
|
14
15
|
export const notRegisteredHint = (key) =>
|
|
15
16
|
`${key} is NOT registered in Dev: run nodii-dev figma file add ${key} --name NAME --kind design-system|surfaces|prototype [--module M]`;
|
|
17
|
+
|
|
18
|
+
// Dev's refusals of resubmitDesignChangeSet, said in terms of what to do next.
|
|
19
|
+
const RESUBMIT_REFUSALS = {
|
|
20
|
+
CHANGE_SET_NOT_FOUND: (id) => `No change set ${id} in Dev.`,
|
|
21
|
+
CHANGE_SET_HOLDER_REQUIRED: (id) => `You don't hold ${id}; only its holder re-submits it.`,
|
|
22
|
+
INVALID_CHANGE_SET_STATE: (id) =>
|
|
23
|
+
`${id} is not applying; re-submit once every item is decided and some were rejected (see nodii-dev figma changeset show ${id}).`,
|
|
24
|
+
CHANGE_SET_HAS_NO_REJECTIONS: (id) => `Nothing in ${id} is rejected; run nodii-dev figma apply ${id} --figma-version VERSION instead.`,
|
|
25
|
+
REJECTED_ITEMS_MISSING: (id, details, message) =>
|
|
26
|
+
Array.isArray(details?.missing)
|
|
27
|
+
? [`Re-submit every rejected item in ${id}; missing:`, ...details.missing.map((i) => ` ${i.figmaNodeId} (${i.id}): ${i.comment ?? "no comment"}`)].join("\n")
|
|
28
|
+
: message,
|
|
29
|
+
CHANGE_ITEM_NOT_REJECTED: (id, details, message) =>
|
|
30
|
+
Array.isArray(details?.rejected)
|
|
31
|
+
? [`${message}`, `Rejected items in ${id}:`, ...details.rejected.map((i) => ` ${i.figmaNodeId} (${i.id})`)].join("\n")
|
|
32
|
+
: message,
|
|
33
|
+
CHANGE_SET_CHANGED: (id) => `${id} changed while re-submitting; read it with nodii-dev figma changeset show ${id} and try again.`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** A round-2 payload: { figmaVersion, items: [{ itemId | figmaNodeId, variants: [{ nodeId, beforePngBase64?, afterPngBase64? }] }] }. */
|
|
37
|
+
export function resubmitPayload(text, file) {
|
|
38
|
+
let body;
|
|
39
|
+
try {
|
|
40
|
+
body = JSON.parse(text);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new CliError("USAGE", `${file} is not JSON: ${error.message}`, 2);
|
|
43
|
+
}
|
|
44
|
+
const bad = (why) => new CliError("USAGE", `${file}: ${why}`, 2);
|
|
45
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) throw bad("expected a JSON object");
|
|
46
|
+
if (typeof body.figmaVersion !== "string" || !body.figmaVersion) throw bad("figmaVersion is required");
|
|
47
|
+
if (!Array.isArray(body.items) || body.items.length === 0) throw bad("items must list the rejected items");
|
|
48
|
+
body.items.forEach((item, n) => {
|
|
49
|
+
if (!item?.itemId && !item?.figmaNodeId) throw bad(`items[${n}] needs itemId or figmaNodeId`);
|
|
50
|
+
if (!Array.isArray(item.variants) || item.variants.length === 0) throw bad(`items[${n}] needs at least one variant`);
|
|
51
|
+
item.variants.forEach((v, m) => {
|
|
52
|
+
if (!v?.nodeId) throw bad(`items[${n}].variants[${m}] needs nodeId`);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
return body;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function figmaResubmit(id, file, { request = devRequest, read = (f) => readFile(f, "utf8") } = {}) {
|
|
59
|
+
if (!id || !file) throw new CliError("USAGE", "figma resubmit CHANGE_SET PAYLOAD.json", 2);
|
|
60
|
+
let text;
|
|
61
|
+
try {
|
|
62
|
+
text = await read(file);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new CliError("USAGE", `cannot read ${file}: ${error.message}`, 2);
|
|
65
|
+
}
|
|
66
|
+
const body = resubmitPayload(text, file);
|
|
67
|
+
try {
|
|
68
|
+
return await request("POST", `/api/v1/design/change-sets/${encode(id)}/resubmit`, body);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const say = RESUBMIT_REFUSALS[error?.code];
|
|
71
|
+
if (!say) throw error;
|
|
72
|
+
throw new CliError(error.code, say(id, error.details, error.message), error.exitCode ?? 1, error.details);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function figmaChangeSetShow(id, { request = devRequest } = {}) {
|
|
77
|
+
if (!id) throw new CliError("USAGE", "figma changeset show CHANGE_SET", 2);
|
|
78
|
+
try {
|
|
79
|
+
return await request("GET", `/api/v1/design/change-sets/${encode(id)}`);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error?.code !== "CHANGE_SET_NOT_FOUND") throw error;
|
|
82
|
+
throw new CliError(error.code, `No Figma change set ${id} in Dev (guideline change sets: nodii-dev design changeset show).`, error.exitCode ?? 1, error.details);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const variantLine = (v) =>
|
|
87
|
+
`${v.figmaNodeId} (${[v.beforeImageId && "before", v.afterImageId && "after"].filter(Boolean).join(", ") || "no images"})`;
|
|
88
|
+
|
|
89
|
+
/** One change set as lines: its items' round, decision, comment, the previous round's decision and comment, and variants. */
|
|
90
|
+
export function figmaChangeSetText(cs) {
|
|
91
|
+
const c = cs.itemCounts ?? {};
|
|
92
|
+
const lines = [
|
|
93
|
+
`${cs.id} ${cs.state} ${cs.title}`,
|
|
94
|
+
`file ${cs.file?.name ?? cs.fileKey} (${cs.fileKey}); holder ${cs.holder?.agent?.displayName ?? cs.holder?.agent?.id ?? "none"}`,
|
|
95
|
+
`${c.total ?? cs.items.length} items: ${c.approved ?? 0} approved, ${c.rejected ?? 0} rejected, ${c.pending ?? 0} pending`,
|
|
96
|
+
];
|
|
97
|
+
for (const item of cs.items ?? []) {
|
|
98
|
+
lines.push(`- ${item.decision} round ${item.round} ${item.figmaNodeId} ${item.entryKey ?? "(new)"} ${item.change} item ${item.id}`);
|
|
99
|
+
if (item.comment) lines.push(` comment: ${item.comment}`);
|
|
100
|
+
if (item.previousItemId)
|
|
101
|
+
lines.push(` round ${item.round - 1}: ${item.previousDecision ?? "?"}${item.previousComment ? `: ${item.previousComment}` : ""}`);
|
|
102
|
+
if (item.variants?.length) lines.push(` variants: ${item.variants.map(variantLine).join("; ")}`);
|
|
103
|
+
}
|
|
104
|
+
if (cs.previousRounds?.length) lines.push(`${cs.previousRounds.length} earlier-round item(s) replaced by a re-submit; --json lists them.`);
|
|
105
|
+
return lines.join("\n");
|
|
106
|
+
}
|
package/dist/hooks.js
CHANGED
|
@@ -102,7 +102,9 @@ export async function preHook(payload, request = devRequest) {
|
|
|
102
102
|
if (!response.data?.allowed)
|
|
103
103
|
throw new CliError(
|
|
104
104
|
"FIGMA_LOCK_DENIED",
|
|
105
|
-
response.data?.reason
|
|
105
|
+
response.data?.reason === "change-set-not-editable"
|
|
106
|
+
? `${response.data.changeSetId} is in review; writes wait for the reviewer's decisions (or nodii-dev figma withdraw ${response.data.changeSetId}).`
|
|
107
|
+
: (response.data?.reason ?? "Blocked Figma write: lock not held."),
|
|
106
108
|
2,
|
|
107
109
|
response.data,
|
|
108
110
|
);
|