@toghen/figma 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +18 -2
- package/cli/bin.mjs +19 -0
- package/cli/bridge.ts +243 -0
- package/cli/check.ts +62 -0
- package/cli/figcraft.ts +764 -0
- package/cli/figcraftApply.ts +1067 -0
- package/cli/figcraftFile.ts +476 -0
- package/cli/figcraftLibrary.ts +147 -0
- package/cli/figcraftRecord.ts +121 -0
- package/cli/figcraftScene.ts +661 -0
- package/cli/main.ts +218 -0
- package/cli/members.ts +65 -0
- package/cli/run.ts +756 -0
- package/component-files.md +665 -0
- package/dist/code.js +24161 -0
- package/dist/manifest.json +23 -0
- package/dist/ui.html +6918 -0
- package/guide.md +985 -0
- package/main/code.ts +50 -0
- package/main/components/apply.ts +704 -0
- package/main/components/endOfRun.ts +168 -0
- package/main/components/errors.ts +17 -0
- package/main/components/fieldWriter.ts +497 -0
- package/main/components/file.ts +263 -0
- package/main/components/receipt.ts +228 -0
- package/main/components/resolve.ts +1193 -0
- package/main/components/run.ts +273 -0
- package/main/components/snapshot.ts +341 -0
- package/main/components/stamp.ts +312 -0
- package/main/components/update.ts +465 -0
- package/main/dispatcher.ts +33 -0
- package/main/engines/batchRunner.ts +200 -0
- package/main/engines/ioAdapter.ts +132 -0
- package/main/engines/operationExecutor.ts +512 -0
- package/main/engines/propertyMapper.ts +221 -0
- package/main/engines/queryEngine.ts +45 -0
- package/main/engines/styleSyncEngine.ts +444 -0
- package/main/engines/variableSyncEngine.ts +561 -0
- package/main/handlers/auditSelection.ts +60 -0
- package/main/handlers/boundLayers.ts +51 -0
- package/main/handlers/cancelOperation.ts +18 -0
- package/main/handlers/executeApplyBindings.ts +69 -0
- package/main/handlers/executeScaffold.ts +59 -0
- package/main/handlers/executeStyleSync.ts +121 -0
- package/main/handlers/executeVariableSync.ts +82 -0
- package/main/handlers/exportDocument.ts +114 -0
- package/main/handlers/getSelection.ts +43 -0
- package/main/handlers/index.ts +30 -0
- package/main/handlers/inspectCorrespondence.ts +172 -0
- package/main/handlers/loadConventions.ts +86 -0
- package/main/handlers/loadDocument.ts +253 -0
- package/main/handlers/lock.ts +270 -0
- package/main/handlers/planApply.ts +157 -0
- package/main/handlers/planApplyBindings.ts +79 -0
- package/main/handlers/planScaffold.ts +84 -0
- package/main/handlers/planStyleSync.ts +49 -0
- package/main/handlers/planVariableSync.ts +49 -0
- package/main/handlers/readSelectionBindings.ts +160 -0
- package/main/handlers/resizeUi.ts +105 -0
- package/main/handlers/runComponent.ts +65 -0
- package/main/handlers/templates.ts +462 -0
- package/main/handlers/tokenGraph.ts +122 -0
- package/main/runtimeState.ts +66 -0
- package/main/utils/abortController.ts +11 -0
- package/main/utils/boundVariables.ts +84 -0
- package/main/utils/buildPayloads.ts +85 -0
- package/main/utils/correspondence.ts +658 -0
- package/main/utils/designSource.ts +122 -0
- package/main/utils/figmaCollectionIndex.ts +120 -0
- package/main/utils/figmaStyleAdapter.ts +268 -0
- package/main/utils/figmaTokenAdapter.ts +255 -0
- package/main/utils/flattenDesignSystem.ts +38 -0
- package/main/utils/folds.ts +203 -0
- package/main/utils/fontStack.ts +57 -0
- package/main/utils/lists.ts +4 -0
- package/main/utils/lockStore.ts +72 -0
- package/main/utils/moves.ts +67 -0
- package/main/utils/pageRoots.ts +58 -0
- package/main/utils/planSymbols.ts +96 -0
- package/main/utils/purpose.ts +351 -0
- package/main/utils/recentStore.ts +166 -0
- package/main/utils/resolverModes.ts +271 -0
- package/main/utils/rootRecord.ts +137 -0
- package/main/utils/shardedData.ts +142 -0
- package/main/utils/standing.ts +106 -0
- package/main/utils/styleTarget.ts +51 -0
- package/main/utils/sum.ts +612 -0
- package/main/utils/symbolicIdRegistry.ts +26 -0
- package/main/utils/syncState.ts +133 -0
- package/main/utils/templateStore.ts +73 -0
- package/main/utils/tokenPath.ts +28 -0
- package/main/utils/tokenStamps.ts +91 -0
- package/manifest.json +15 -0
- package/package.json +64 -4
- package/reference.md +1609 -0
- package/shared/components/artboard.ts +126 -0
- package/shared/components/axes.ts +420 -0
- package/shared/components/bindings.ts +64 -0
- package/shared/components/blueprint.ts +248 -0
- package/shared/components/check.ts +705 -0
- package/shared/components/diff.ts +668 -0
- package/shared/components/fields.ts +404 -0
- package/shared/components/groups.ts +306 -0
- package/shared/components/index.ts +140 -0
- package/shared/components/members.ts +280 -0
- package/shared/components/model.ts +273 -0
- package/shared/components/order.ts +53 -0
- package/shared/components/read.ts +480 -0
- package/shared/components/stack.ts +124 -0
- package/shared/components/steps.ts +186 -0
- package/shared/components/validate.ts +565 -0
- package/shared/conventions.ts +28 -0
- package/shared/inputs.ts +40 -0
- package/shared/operations.ts +233 -0
- package/shared/pendingNames.ts +39 -0
- package/shared/protocol.ts +588 -0
- package/shared/tokenTypeMap.ts +21 -0
package/cli/main.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `toghen-figma`: the one command.
|
|
3
|
+
*
|
|
4
|
+
* toghen-figma variables <entry.json>… [--name <name>] [--overrides <map.json>] [--collection <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>]
|
|
5
|
+
* toghen-figma components <file>… [--resolver <entry.json>] [--name <name>] [--overrides <map.json>] [--input <document>] [--page <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>]
|
|
6
|
+
* toghen-figma sync <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--collection <name>] [--input <document>] [--page <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>]
|
|
7
|
+
* toghen-figma check <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--input <document>]
|
|
8
|
+
* toghen-figma members <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--input <document>]
|
|
9
|
+
* toghen-figma lock <library name>
|
|
10
|
+
* toghen-figma lock <library.figcraft> (--figcraft <port> | --figcraft-file <path.figcraft>) [--dry-run]
|
|
11
|
+
*
|
|
12
|
+
* `variables`, `components` and `sync` take `--library <name>`: two plugins pair, the run builds in the library file,
|
|
13
|
+
* and the library's keys are written into the target file's lock (`run.ts`, `copyKeys`). `lock` only copies the keys.
|
|
14
|
+
* With FigCraft, `--library` names the library's `.figcraft` file: the run builds there, publishes and saves it, and
|
|
15
|
+
* enables it in the target, accepts its updates and writes the lock (`run.ts`, `finishFigcraftLibrary`).
|
|
16
|
+
* Every `.json` argument is the entry the core's `load` takes, in order: one resolver document, one token file, or
|
|
17
|
+
* several token files (the last wins). A file is a `<name>.component.kdl` (a component set) or a `<name>.artboard.kdl`
|
|
18
|
+
* (a frame of instances, built from `--input`).
|
|
19
|
+
* Every command runs against a Figma file through the plugin (`run.ts`), which does all the reading of a file:
|
|
20
|
+
* the command line sends text. `--figcraft <port>` runs `variables`, `components` and `sync` against the file FigCraft shows instead (`figcraft.ts`),
|
|
21
|
+
* and `--figcraft-file <path.figcraft>` against a file with no app, through `figcraft-cli mcp`, saving it at the end.
|
|
22
|
+
* `bin.mjs` runs it; this module exports `main`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { check } from './check.js';
|
|
26
|
+
import { members } from './members.js';
|
|
27
|
+
import { isShareLink } from './figcraftLibrary.js';
|
|
28
|
+
import { run, type RunIo } from './run.js';
|
|
29
|
+
|
|
30
|
+
export type { RunIo as Io } from './run.js';
|
|
31
|
+
|
|
32
|
+
const USAGE = `usage: toghen-figma variables <entry.json>… [--name <name>] [--overrides <map.json>] [--collection <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>] [--library <library>]
|
|
33
|
+
toghen-figma components <file>… [--resolver <entry.json>] [--name <name>] [--overrides <map.json>] [--input <document>] [--page <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>] [--library <library> | --use <library>…]
|
|
34
|
+
toghen-figma sync <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--collection <name>] [--input <document>] [--page <name>] [--dry-run] [--allow-breaking] [--overwrite-drift] [--verbose] [--figcraft <port> | --figcraft-file <path.figcraft>] [--library <library> | --use <library>…]
|
|
35
|
+
toghen-figma check <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--input <document>]
|
|
36
|
+
toghen-figma members <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--input <document>]
|
|
37
|
+
toghen-figma lock <library name>
|
|
38
|
+
toghen-figma lock <library.figcraft> (--figcraft <port> | --figcraft-file <path.figcraft>) [--dry-run]
|
|
39
|
+
|
|
40
|
+
every .json argument is the entry, in order: one resolver document, one token file, or several token files merged in order, the last winning.
|
|
41
|
+
--name names the collection of token files; it defaults to the name of the folder that holds them. --overrides is a JSON object that maps
|
|
42
|
+
a $ref as written to the location to read instead, resolved against the document that holds the $ref.
|
|
43
|
+
a file is a <name>.component.kdl (a component set) or a <name>.artboard.kdl (a frame of instances with their properties and slots set).
|
|
44
|
+
files run in dependency order, each component file after every file that defines a component it names, artboards last; files that name each other in a cycle stop the run. --input is a resolver document whose tokens, resolved at their default contexts and nested by path,
|
|
45
|
+
are the data an artboard file reads with (input); it is read only when an artboard file is among the files.
|
|
46
|
+
check reads the files against the design system as the plugin's check does, with no plugin and no Figma file, prints each problem as <file>:<line>:<column> <message>, and exits 1 when a file has an error.
|
|
47
|
+
members reads the files the same way and prints each one's component, members and problems as JSON, the members a build would make.
|
|
48
|
+
--figcraft runs into the file FigCraft shows: its variables, its styles and the component and artboard files, through the control port it was started with (figcraft --control <port>), with no plugin.
|
|
49
|
+
--figcraft-file runs the same into a .figcraft file with no app, through figcraft-cli mcp (0.8.0 or later; FIGCRAFT_CLI names it, else the one on the PATH),
|
|
50
|
+
and saves the file once the run has completed. A file that does not exist yet is made. A dry run, or a run that stops partway, saves nothing.
|
|
51
|
+
--library pairs two plugins, one in the library file (named as Figma names the library) and one in the target file: the run builds in the library file,
|
|
52
|
+
then writes the library's keys into the target file's lock. lock does only the second half. Publishing the library and accepting its update are a person's steps.
|
|
53
|
+
with --figcraft or --figcraft-file, --library names the library's .figcraft file: the run builds there, publishes it and saves it, then enables it in
|
|
54
|
+
the target (the file FigCraft shows, or the --figcraft-file), accepts that library's updates and writes the target's lock. lock does the target's half.
|
|
55
|
+
--use declares a library the component and artboard files may name components, styles and tokens from, through the target's lock; it may be given
|
|
56
|
+
more than once. In Figma it is the library's name, as the lock holds it; in FigCraft its .figcraft file, which the run enables and whose lock entry it
|
|
57
|
+
refreshes first (a dry run neither). Without --use, every name is found in the file, as before.
|
|
58
|
+
a plan prints its skips counted by cause, and lists the rows of every cause but unchanged, ten per cause and then how many more.
|
|
59
|
+
--verbose lists every skip, the unchanged ones too, and every patch of a component set with the fields it writes.
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
/** An artboard file by its name; any other file is a component file. */
|
|
63
|
+
const isArtboard = (file: string): boolean => /\.artboard\.kdl$/.test(file);
|
|
64
|
+
|
|
65
|
+
/** A part of the entry: a resolver document or a token file. */
|
|
66
|
+
const isEntry = (file: string): boolean => /\.json$/i.test(file);
|
|
67
|
+
|
|
68
|
+
/** The flags of the load, which every command takes: they shape the entry, or `--resolver`'s. */
|
|
69
|
+
const LOAD = ['--name', '--overrides'];
|
|
70
|
+
|
|
71
|
+
/** Which flags each command takes. */
|
|
72
|
+
const FLAGS: Record<string, { value: string[]; switches: string[] }> = {
|
|
73
|
+
variables: {
|
|
74
|
+
value: ['--collection', '--figcraft', '--figcraft-file', '--library', ...LOAD],
|
|
75
|
+
switches: ['--dry-run', '--allow-breaking', '--overwrite-drift', '--verbose'],
|
|
76
|
+
},
|
|
77
|
+
components: {
|
|
78
|
+
value: ['--page', '--resolver', '--input', '--figcraft', '--figcraft-file', '--library', '--use', ...LOAD],
|
|
79
|
+
switches: ['--dry-run', '--allow-breaking', '--overwrite-drift', '--verbose'],
|
|
80
|
+
},
|
|
81
|
+
sync: {
|
|
82
|
+
value: ['--collection', '--page', '--input', '--figcraft', '--figcraft-file', '--library', '--use', ...LOAD],
|
|
83
|
+
switches: ['--dry-run', '--allow-breaking', '--overwrite-drift', '--verbose'],
|
|
84
|
+
},
|
|
85
|
+
check: { value: ['--input', ...LOAD], switches: [] },
|
|
86
|
+
members: { value: ['--input', ...LOAD], switches: [] },
|
|
87
|
+
lock: { value: ['--figcraft', '--figcraft-file'], switches: ['--dry-run'] },
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/** Runs the command. Returns the exit code: 0, or 1 when anything failed. */
|
|
91
|
+
export async function main(argv: string[], io: RunIo): Promise<number> {
|
|
92
|
+
const [command, ...rest] = argv;
|
|
93
|
+
if (command === undefined || !(command in FLAGS)) {
|
|
94
|
+
io.stderr(`${command === undefined ? 'no command given' : `unknown command ${command}`}\n${USAGE}`);
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const allowed = FLAGS[command];
|
|
99
|
+
const positionals: string[] = [];
|
|
100
|
+
const options: Record<string, string | true> = {};
|
|
101
|
+
/** `--use`, which may be given more than once: a library each. */
|
|
102
|
+
const uses: string[] = [];
|
|
103
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
104
|
+
const arg = rest[i];
|
|
105
|
+
if (allowed.switches.includes(arg)) options[arg] = true;
|
|
106
|
+
else if (allowed.value.includes(arg)) {
|
|
107
|
+
const value = rest[i + 1];
|
|
108
|
+
if (value === undefined || value.startsWith('--')) return fail(io, `${arg} needs a value`);
|
|
109
|
+
if (arg === '--use') uses.push(value);
|
|
110
|
+
else options[arg] = value;
|
|
111
|
+
i += 1;
|
|
112
|
+
} else if (arg.startsWith('--')) return fail(io, `unknown option ${arg} for ${command}`);
|
|
113
|
+
else positionals.push(arg);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const figcraftFile = typeof options['--figcraft-file'] === 'string' ? options['--figcraft-file'] : undefined;
|
|
117
|
+
const inFigcraft = options['--figcraft'] !== undefined || figcraftFile !== undefined;
|
|
118
|
+
if (figcraftFile !== undefined && options['--figcraft'] !== undefined)
|
|
119
|
+
return fail(io, '--figcraft and --figcraft-file do not go together: one runs into the app, the other into a file with no app');
|
|
120
|
+
if (figcraftFile !== undefined && !/\.figcraft$/i.test(figcraftFile))
|
|
121
|
+
return fail(io, `--figcraft-file takes a .figcraft file, which the run saves; got ${figcraftFile}`);
|
|
122
|
+
let figcraft: number | undefined;
|
|
123
|
+
if (typeof options['--figcraft'] === 'string') {
|
|
124
|
+
figcraft = Number(options['--figcraft']);
|
|
125
|
+
if (!Number.isInteger(figcraft) || figcraft < 1 || figcraft > 65535)
|
|
126
|
+
return fail(io, `--figcraft takes the port FigCraft's control socket listens on, a number; got ${options['--figcraft']}`);
|
|
127
|
+
}
|
|
128
|
+
const into = { ...(figcraft !== undefined ? { figcraft } : {}), ...(figcraftFile !== undefined ? { figcraftFile } : {}) };
|
|
129
|
+
|
|
130
|
+
/** In FigCraft a library is its `.figcraft` file, on this machine: a team library's share link reaches a server. */
|
|
131
|
+
const figcraftLibrary = (path: string, flag: string): string | undefined => {
|
|
132
|
+
if (isShareLink(path))
|
|
133
|
+
return `${flag} ${path} names a team library on a figcraft-server, which reaches outside this machine and is not built: name a .figcraft file`;
|
|
134
|
+
if (!/\.figcraft$/i.test(path)) return `${flag} takes the library's .figcraft file with --figcraft or --figcraft-file; got ${path}`;
|
|
135
|
+
return undefined;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
if (command === 'lock') {
|
|
139
|
+
if (positionals.length !== 1)
|
|
140
|
+
return fail(
|
|
141
|
+
io,
|
|
142
|
+
inFigcraft
|
|
143
|
+
? "lock takes the library's .figcraft file, and nothing else"
|
|
144
|
+
: "lock takes the library's name, as Figma names it, and nothing else",
|
|
145
|
+
);
|
|
146
|
+
const wrong = inFigcraft ? figcraftLibrary(positionals[0], 'lock') : undefined;
|
|
147
|
+
if (wrong) return fail(io, wrong);
|
|
148
|
+
if (!inFigcraft && options['--dry-run'] !== undefined)
|
|
149
|
+
return fail(io, "lock --dry-run is FigCraft's: in Figma, lock writes nothing a dry run could hold back");
|
|
150
|
+
return run({ files: [], dryRun: options['--dry-run'] === true, allowBreaking: false, library: positionals[0], ...into }, io);
|
|
151
|
+
}
|
|
152
|
+
const library = typeof options['--library'] === 'string' ? options['--library'] : undefined;
|
|
153
|
+
if (library !== undefined && inFigcraft) {
|
|
154
|
+
const wrong = figcraftLibrary(library, '--library');
|
|
155
|
+
if (wrong) return fail(io, wrong);
|
|
156
|
+
}
|
|
157
|
+
if (uses.length > 0 && library !== undefined)
|
|
158
|
+
return fail(
|
|
159
|
+
io,
|
|
160
|
+
'--use and --library do not go together: --library runs the files into the library, --use runs them into a file that uses libraries',
|
|
161
|
+
);
|
|
162
|
+
for (const use of inFigcraft ? uses : []) {
|
|
163
|
+
const wrong = figcraftLibrary(use, '--use');
|
|
164
|
+
if (wrong) return fail(io, wrong);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const entries = positionals.filter(isEntry);
|
|
168
|
+
const files = positionals.filter((file) => !isEntry(file));
|
|
169
|
+
if (command === 'components' && entries.length > 0)
|
|
170
|
+
return fail(io, `components takes the design system with --resolver; ${entries.join(', ')} is not a component file`);
|
|
171
|
+
if (command !== 'components' && entries.length === 0) return fail(io, 'no entry given: a resolver document or token files (.json)');
|
|
172
|
+
if ((command === 'check' || command === 'members') && files.length === 0) return fail(io, 'no component file given');
|
|
173
|
+
if (command === 'variables' && files.length > 0)
|
|
174
|
+
return fail(io, 'variables takes the entry and nothing else; sync runs component files too');
|
|
175
|
+
if (command !== 'variables' && files.length === 0)
|
|
176
|
+
return fail(io, command === 'sync' ? 'no component file given; variables syncs the tokens alone' : 'no component file given');
|
|
177
|
+
const entry = entries.length === 1 ? entries[0] : entries.length > 1 ? entries : undefined;
|
|
178
|
+
|
|
179
|
+
const input = typeof options['--input'] === 'string' ? options['--input'] : undefined;
|
|
180
|
+
if (input !== undefined && !files.some(isArtboard))
|
|
181
|
+
return fail(io, '--input is read only when an artboard file (<name>.artboard.kdl) is among the files; none is');
|
|
182
|
+
|
|
183
|
+
const loading = {
|
|
184
|
+
...(typeof options['--name'] === 'string' ? { name: options['--name'] } : {}),
|
|
185
|
+
...(typeof options['--overrides'] === 'string' ? { overrides: options['--overrides'] } : {}),
|
|
186
|
+
};
|
|
187
|
+
// A component file is run before an artboard file: the artboard makes instances of what the components made.
|
|
188
|
+
const ordered = [...files.filter((file) => !isArtboard(file)), ...files.filter(isArtboard)];
|
|
189
|
+
|
|
190
|
+
if (command === 'members') return members({ entry: entry!, ...loading, files: ordered, ...(input !== undefined ? { input } : {}) }, io);
|
|
191
|
+
if (command === 'check') return check({ entry: entry!, ...loading, files: ordered, ...(input !== undefined ? { input } : {}) }, io);
|
|
192
|
+
|
|
193
|
+
return run(
|
|
194
|
+
{
|
|
195
|
+
...(entry !== undefined ? { entry } : {}),
|
|
196
|
+
...loading,
|
|
197
|
+
files: ordered,
|
|
198
|
+
...(input !== undefined ? { input } : {}),
|
|
199
|
+
...(typeof options['--collection'] === 'string' ? { collection: options['--collection'] } : {}),
|
|
200
|
+
...(typeof options['--page'] === 'string' ? { page: options['--page'] } : {}),
|
|
201
|
+
...(typeof options['--resolver'] === 'string' ? { designResolver: options['--resolver'] } : {}),
|
|
202
|
+
dryRun: options['--dry-run'] === true,
|
|
203
|
+
allowBreaking: options['--allow-breaking'] === true,
|
|
204
|
+
...(options['--overwrite-drift'] === true ? { overwriteDrift: true } : {}),
|
|
205
|
+
...(options['--verbose'] === true ? { verbose: true } : {}),
|
|
206
|
+
...(figcraft !== undefined ? { figcraft } : {}),
|
|
207
|
+
...(figcraftFile !== undefined ? { figcraftFile } : {}),
|
|
208
|
+
...(library !== undefined ? { library } : {}),
|
|
209
|
+
...(uses.length > 0 ? { uses } : {}),
|
|
210
|
+
},
|
|
211
|
+
io,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function fail(io: RunIo, message: string): number {
|
|
216
|
+
io.stderr(`${message}\n${USAGE}`);
|
|
217
|
+
return 1;
|
|
218
|
+
}
|
package/cli/members.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `toghen-figma members <entry.json>… <file>… [--name <name>] [--overrides <map.json>] [--input <document>]`: each file
|
|
3
|
+
* read and expanded against the design system, printed as JSON, with no plugin and no Figma file.
|
|
4
|
+
*
|
|
5
|
+
* The reader is the one every run uses (`readComponentText`, `main/components/file.ts`): the members are what a build
|
|
6
|
+
* would make, in the plain model `reference.md` owns (*Component files*). It is for a tool that draws a file's members
|
|
7
|
+
* somewhere Toghen does not, which then reads them here rather than through Toghen's own modules.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Resolver } from '@toghen/lib';
|
|
11
|
+
import { resolve } from 'node:path';
|
|
12
|
+
import { readComponentText } from '../main/components/file.js';
|
|
13
|
+
import { resolveSumModes } from '../main/utils/resolverModes.js';
|
|
14
|
+
import { entryLabel, readInputs, readSource, type Io, type LoadArguments } from './run.js';
|
|
15
|
+
|
|
16
|
+
export interface MembersArguments extends LoadArguments {
|
|
17
|
+
/** A resolver document, one token file, or several in order. */
|
|
18
|
+
entry: string | string[];
|
|
19
|
+
files: string[];
|
|
20
|
+
input?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Prints `[{file, component, artboard?, members, problems}]` on the standard output. 1 when a file has an error, or cannot be read. */
|
|
24
|
+
export async function members(args: MembersArguments, io: Io): Promise<number> {
|
|
25
|
+
let design: Resolver;
|
|
26
|
+
try {
|
|
27
|
+
design = new Resolver((await readSource(args.entry, io, args)).document);
|
|
28
|
+
resolveSumModes(design);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
io.stderr(`${entryLabel(args.entry)}: ${(error as Error).message}\n`);
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
let inputs: Record<string, unknown> | undefined;
|
|
34
|
+
if (args.input !== undefined) {
|
|
35
|
+
try {
|
|
36
|
+
inputs = await readInputs(args.input, io);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
io.stderr(`${args.input}: ${(error as Error).message}\n`);
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const out: unknown[] = [];
|
|
43
|
+
let failed = false;
|
|
44
|
+
for (const file of args.files) {
|
|
45
|
+
let text: string;
|
|
46
|
+
try {
|
|
47
|
+
text = io.readFile(resolve(io.cwd, file));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
io.stderr(`${file} cannot be read: ${(error as Error).message}\n`);
|
|
50
|
+
failed = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const read = readComponentText(text, inputs, design);
|
|
54
|
+
if (read.problems.some((problem) => problem.severity === 'error')) failed = true;
|
|
55
|
+
out.push({
|
|
56
|
+
file,
|
|
57
|
+
component: read.component,
|
|
58
|
+
...(read.artboard ? { artboard: true } : {}),
|
|
59
|
+
members: read.members,
|
|
60
|
+
problems: read.problems,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
io.stdout(`${JSON.stringify(out, null, 1)}\n`);
|
|
64
|
+
return failed ? 1 : 0;
|
|
65
|
+
}
|