@stag-build/phonebook 0.1.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/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +33 -0
- package/dist/commands/doctor.js +690 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +27 -0
- package/dist/commands/init.js +415 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/engines/android.d.ts +70 -0
- package/dist/engines/android.js +265 -0
- package/dist/engines/android.js.map +1 -0
- package/dist/engines/git.d.ts +4 -0
- package/dist/engines/git.js +13 -0
- package/dist/engines/git.js.map +1 -0
- package/dist/engines/ios.d.ts +55 -0
- package/dist/engines/ios.js +195 -0
- package/dist/engines/ios.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/gradle/catalog.d.ts +110 -0
- package/dist/gradle/catalog.js +413 -0
- package/dist/gradle/catalog.js.map +1 -0
- package/dist/ios/snapshotTestClass.d.ts +59 -0
- package/dist/ios/snapshotTestClass.js +195 -0
- package/dist/ios/snapshotTestClass.js.map +1 -0
- package/dist/manifest.d.ts +32 -0
- package/dist/manifest.js +23 -0
- package/dist/manifest.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +226 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/naming.d.ts +18 -0
- package/dist/naming.js +34 -0
- package/dist/naming.js.map +1 -0
- package/dist/scan/android.d.ts +7 -0
- package/dist/scan/android.js +222 -0
- package/dist/scan/android.js.map +1 -0
- package/dist/scan/hints.d.ts +25 -0
- package/dist/scan/hints.js +338 -0
- package/dist/scan/hints.js.map +1 -0
- package/dist/scan/ios.d.ts +7 -0
- package/dist/scan/ios.js +201 -0
- package/dist/scan/ios.js.map +1 -0
- package/dist/scan/types.d.ts +52 -0
- package/dist/scan/types.js +7 -0
- package/dist/scan/types.js.map +1 -0
- package/dist/site/build.d.ts +17 -0
- package/dist/site/build.js +0 -0
- package/dist/site/build.js.map +1 -0
- package/dist/versions.d.ts +71 -0
- package/dist/versions.js +334 -0
- package/dist/versions.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { copyFile, mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { diagnoseGradleFailure } from '../errors.js';
|
|
6
|
+
import { SCHEMA_VERSION } from '../manifest.js';
|
|
7
|
+
import { parsePreviewName } from '../naming.js';
|
|
8
|
+
import { gitInfo } from './git.js';
|
|
9
|
+
export const EMPTY_PREVIEWS_MESSAGE = 'No previews were recorded. Common causes: (1) packages = listOf(...) in generateComposePreviewRobolectricTests ' +
|
|
10
|
+
'does not match your app package; (2) includePrivatePreviews = true is missing and your @Preview functions are ' +
|
|
11
|
+
'private; (3) the module has no @Preview functions. Run `phonebook doctor` to check 1 and 2.';
|
|
12
|
+
/**
|
|
13
|
+
* Guards against `generate` silently succeeding with zero recorded previews.
|
|
14
|
+
* Throws unless `allowEmpty` is set (the `--allow-empty` CLI flag), in which
|
|
15
|
+
* case the caller downgrades this to a warning and still writes the manifest.
|
|
16
|
+
* Pure/exported so it can be unit-tested without spawning Gradle.
|
|
17
|
+
*/
|
|
18
|
+
export function checkEmptyEntries(entryCount, allowEmpty) {
|
|
19
|
+
if (entryCount === 0 && !allowEmpty) {
|
|
20
|
+
throw new Error(EMPTY_PREVIEWS_MESSAGE);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Runs Roborazzi (with ComposablePreviewScanner-generated tests) via Gradle and
|
|
25
|
+
* harvests the recorded PNGs into a Phonebook bundle.
|
|
26
|
+
*/
|
|
27
|
+
export async function generateAndroid(config, projectDir, outputDir, options = {}) {
|
|
28
|
+
const modules = config.android?.modules ?? [':app'];
|
|
29
|
+
const variant = config.android?.variant ?? 'debug';
|
|
30
|
+
const variantCap = variant[0].toUpperCase() + variant.slice(1);
|
|
31
|
+
const tasks = modules.map((m) => `${m}:recordRoborazzi${variantCap}`);
|
|
32
|
+
await runGradle(projectDir, tasks, options.quiet ?? false);
|
|
33
|
+
const imagesDir = join(outputDir, 'images');
|
|
34
|
+
await mkdir(imagesDir, { recursive: true });
|
|
35
|
+
const entries = [];
|
|
36
|
+
for (const module of modules) {
|
|
37
|
+
const moduleDir = join(projectDir, ...module.split(':').filter(Boolean));
|
|
38
|
+
const roborazziDir = join(moduleDir, 'build', 'outputs', 'roborazzi');
|
|
39
|
+
let files = [];
|
|
40
|
+
try {
|
|
41
|
+
// Recursive: previews named "Component/State" are written into subdirectories.
|
|
42
|
+
files = (await readdir(roborazziDir, { recursive: true }))
|
|
43
|
+
.map(String)
|
|
44
|
+
.filter((f) => f.endsWith('.png'));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new Error(`No Roborazzi output at ${roborazziDir}. Is the Roborazzi plugin with ` +
|
|
48
|
+
`generateComposePreviewRobolectricTests enabled in ${module}?`);
|
|
49
|
+
}
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
const meta = parseRoborazziFileName(file);
|
|
52
|
+
const hash = createHash('sha256');
|
|
53
|
+
hash.update(module + file);
|
|
54
|
+
const imageName = `${hash.digest('hex').slice(0, 16)}.png`;
|
|
55
|
+
await copyFile(join(roborazziDir, file), join(imagesDir, imageName));
|
|
56
|
+
// A dark-uiMode preview named e.g. UserCardDarkPreview is the Dark state
|
|
57
|
+
// of UserCard, not a separate component.
|
|
58
|
+
let functionName = meta.functionName;
|
|
59
|
+
let displayName = meta.displayName;
|
|
60
|
+
if (meta.theme === 'dark' && !displayName) {
|
|
61
|
+
const stripped = functionName.replace(/(?:Dark|Night)(Preview)?$/, '$1');
|
|
62
|
+
if (stripped !== functionName)
|
|
63
|
+
functionName = stripped;
|
|
64
|
+
displayName = 'Dark';
|
|
65
|
+
}
|
|
66
|
+
const { component, state } = parsePreviewName(functionName, displayName);
|
|
67
|
+
entries.push({
|
|
68
|
+
component,
|
|
69
|
+
state,
|
|
70
|
+
module,
|
|
71
|
+
...(meta.sourceFile ? { sourceFile: meta.sourceFile } : {}),
|
|
72
|
+
previewName: meta.fqn,
|
|
73
|
+
image: `images/${imageName}`,
|
|
74
|
+
...(meta.theme ? { theme: meta.theme } : {}),
|
|
75
|
+
...(meta.tags && meta.tags.length > 0 ? { tags: meta.tags } : {}),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
entries.sort((a, b) => a.component.localeCompare(b.component) || a.state.localeCompare(b.state));
|
|
80
|
+
const allowEmpty = options.allowEmpty ?? false;
|
|
81
|
+
checkEmptyEntries(entries.length, allowEmpty);
|
|
82
|
+
if (entries.length === 0 && allowEmpty) {
|
|
83
|
+
console.warn(`warning: ${EMPTY_PREVIEWS_MESSAGE}`);
|
|
84
|
+
}
|
|
85
|
+
const manifest = {
|
|
86
|
+
schemaVersion: SCHEMA_VERSION,
|
|
87
|
+
platform: 'android',
|
|
88
|
+
app: {
|
|
89
|
+
name: config.appName,
|
|
90
|
+
...(await gitInfo(projectDir)),
|
|
91
|
+
generatedAt: new Date().toISOString(),
|
|
92
|
+
},
|
|
93
|
+
entries,
|
|
94
|
+
};
|
|
95
|
+
await writeFile(join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
96
|
+
return manifest;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Roborazzi's machine-generated marker chunks are ALL-CAPS alnum, except that
|
|
100
|
+
* size markers carry a lowercase "dp" unit: WITH_BACKGROUND, UI_MODE_NIGHT_YES,
|
|
101
|
+
* PIXEL_4_XL, but also W360dp / H96dp (from widthDp/heightDp attributes).
|
|
102
|
+
* A user's name word like "Green" (initial cap + lowercase) never matches.
|
|
103
|
+
*/
|
|
104
|
+
const MARKER_CHUNK = '[A-Z0-9]+(?:dp)?';
|
|
105
|
+
const MACHINE_MARKER = new RegExp(`^${MARKER_CHUNK}(?:_${MARKER_CHUNK})+$`);
|
|
106
|
+
/**
|
|
107
|
+
* Roborazzi sometimes glues a machine-generated marker run directly onto a
|
|
108
|
+
* user-chosen display-name token within the same dot-segment, e.g.
|
|
109
|
+
* "Landscape_WIDTH_891DP_HEIGHT_411DP_ORIENTATION_LANDSCAPE" (device-spec
|
|
110
|
+
* annotation glued to the "Landscape" state). Unlike MACHINE_MARKER, this
|
|
111
|
+
* only matches a *trailing* run, leaving a leading name intact.
|
|
112
|
+
*/
|
|
113
|
+
const GLUED_MARKER_SUFFIX = new RegExp(`(?:_${MARKER_CHUNK})+$`);
|
|
114
|
+
/**
|
|
115
|
+
* Roborazzi writes the space in @Preview(name = "On Green") as an underscore.
|
|
116
|
+
* Restore spaces in mixed-case name tokens; a deliberate all-caps state like
|
|
117
|
+
* NIGHT_MODE has no lowercase and is left untouched.
|
|
118
|
+
*/
|
|
119
|
+
function restoreNameSpaces(token) {
|
|
120
|
+
return /[a-z]/.test(token) ? token.replace(/_/g, ' ') : token;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Strips a trailing glued marker run (see GLUED_MARKER_SUFFIX) off `token`,
|
|
124
|
+
* returning the remaining name plus the stripped run as a single tag. Returns
|
|
125
|
+
* undefined when there is no glued suffix, or when stripping it would leave
|
|
126
|
+
* an empty or all-caps-only remainder (a deliberate state like "NIGHT_MODE"
|
|
127
|
+
* has no lowercase to strip down to, so it is left intact).
|
|
128
|
+
*/
|
|
129
|
+
function stripGluedMarker(token) {
|
|
130
|
+
const match = token.match(GLUED_MARKER_SUFFIX);
|
|
131
|
+
if (!match || match.index === undefined)
|
|
132
|
+
return undefined;
|
|
133
|
+
const remainder = token.slice(0, match.index);
|
|
134
|
+
if (remainder.length === 0 || !/[a-z]/.test(remainder))
|
|
135
|
+
return undefined;
|
|
136
|
+
return { name: restoreNameSpaces(remainder), tag: match[0].slice(1) };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Roborazzi + ComposablePreviewScanner names recorded images
|
|
140
|
+
* `<package>.<FileKt>.<PreviewFunction>[.<preview display name>].png`, where a
|
|
141
|
+
* display name containing "/" becomes real subdirectories on disk (verified
|
|
142
|
+
* against Roborazzi 1.72.0):
|
|
143
|
+
* dev.stag.sample.PrimaryButtonKt.PrimaryButtonEnabledPreview.Button/Enabled.png
|
|
144
|
+
* dev.stag.sample.UserCardKt.UserCardPreview.png
|
|
145
|
+
* dev.stag.sample.UserCardKt.UserCardDarkPreview.NIGHT.png (uiMode night)
|
|
146
|
+
*
|
|
147
|
+
* Roborazzi also appends machine-generated ALL-CAPS markers derived from
|
|
148
|
+
* `@Preview` attributes (verified against a real app whose previews are all
|
|
149
|
+
* `@Preview(showBackground = true)`, with no explicit name):
|
|
150
|
+
* com.om.spotifyuiapp...HomeContentKt.HomeContent.WITH_BACKGROUND.png
|
|
151
|
+
* These are not a user-chosen display name and must not become the state;
|
|
152
|
+
* they are collected into `tags` instead. NIGHT/NOTNIGHT (uiMode) become
|
|
153
|
+
* `theme` rather than a tag, as before.
|
|
154
|
+
*
|
|
155
|
+
* `file` is the png path relative to the roborazzi output dir ("/"-separated).
|
|
156
|
+
*/
|
|
157
|
+
export function parseRoborazziFileName(file) {
|
|
158
|
+
const base = file.replace(/\.png$/, '').replace(/\\/g, '/');
|
|
159
|
+
const fqn = base;
|
|
160
|
+
// Path segments beyond the first come from a "/" in the preview display name.
|
|
161
|
+
const [head, ...restPath] = base.split('/');
|
|
162
|
+
const dotParts = head.split('.');
|
|
163
|
+
// The segment ending in "Kt" is the file class; the next one is the function.
|
|
164
|
+
let fnIndex = dotParts.findIndex((p) => p.endsWith('Kt')) + 1;
|
|
165
|
+
if (fnIndex <= 0 || fnIndex >= dotParts.length)
|
|
166
|
+
fnIndex = dotParts.length - 1;
|
|
167
|
+
const functionName = dotParts[fnIndex];
|
|
168
|
+
const fileClass = dotParts[fnIndex - 1];
|
|
169
|
+
const sourceFile = fileClass?.endsWith('Kt') && fnIndex >= 1
|
|
170
|
+
? [...dotParts.slice(0, fnIndex - 1), `${fileClass.slice(0, -2)}.kt`].join('/')
|
|
171
|
+
: undefined;
|
|
172
|
+
// Each remaining "level" (the head's trailing dot segments, then one level
|
|
173
|
+
// per "/" path segment) may itself carry multiple dot-separated tokens: a
|
|
174
|
+
// real display-name token plus Roborazzi's machine-generated markers.
|
|
175
|
+
const levels = [dotParts.slice(fnIndex + 1), ...restPath.map((p) => p.split('.'))];
|
|
176
|
+
let theme;
|
|
177
|
+
const tags = [];
|
|
178
|
+
const nameLevels = [];
|
|
179
|
+
for (const level of levels) {
|
|
180
|
+
const nameTokens = [];
|
|
181
|
+
for (const token of level) {
|
|
182
|
+
if (token === 'NIGHT')
|
|
183
|
+
theme = 'dark';
|
|
184
|
+
else if (token === 'NOTNIGHT')
|
|
185
|
+
theme = 'light';
|
|
186
|
+
else if (MACHINE_MARKER.test(token))
|
|
187
|
+
tags.push(token);
|
|
188
|
+
else {
|
|
189
|
+
const glued = stripGluedMarker(token);
|
|
190
|
+
if (glued) {
|
|
191
|
+
nameTokens.push(glued.name);
|
|
192
|
+
tags.push(glued.tag);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
nameTokens.push(restoreNameSpaces(token));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (nameTokens.length > 0)
|
|
200
|
+
nameLevels.push(nameTokens.join('.'));
|
|
201
|
+
}
|
|
202
|
+
const displayName = nameLevels.length > 0 ? nameLevels.join('/') : undefined;
|
|
203
|
+
return {
|
|
204
|
+
fqn,
|
|
205
|
+
functionName,
|
|
206
|
+
displayName,
|
|
207
|
+
theme,
|
|
208
|
+
sourceFile,
|
|
209
|
+
...(tags.length > 0 ? { tags } : {}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Runs Gradle. When `quiet` is false (the CLI default), output streams
|
|
214
|
+
* straight through to this process's stdout/stderr as it arrives (so a human
|
|
215
|
+
* can watch the build) while a rolling tail is kept alongside it for error
|
|
216
|
+
* translation. When `quiet` is true (used by the MCP server, whose stdout is
|
|
217
|
+
* the JSON-RPC channel and must never carry build output), only the tail is
|
|
218
|
+
* kept and nothing is echoed live; the tail is written to stderr once if the
|
|
219
|
+
* build fails, unless `dumpTailOnFailure` is set to false.
|
|
220
|
+
*
|
|
221
|
+
* `onOutput`, when provided, is called once on exit (success or failure) with
|
|
222
|
+
* the full, untruncated captured output — useful for callers that want to
|
|
223
|
+
* write a complete log file rather than relying on the 200-line tail.
|
|
224
|
+
*/
|
|
225
|
+
export function runGradle(projectDir, tasks, quiet, options = {}) {
|
|
226
|
+
return new Promise((res, rej) => {
|
|
227
|
+
const gradlew = resolve(projectDir, process.platform === 'win32' ? 'gradlew.bat' : 'gradlew');
|
|
228
|
+
const child = spawn(gradlew, [...tasks, '--stacktrace'], {
|
|
229
|
+
cwd: projectDir,
|
|
230
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
231
|
+
});
|
|
232
|
+
let tail = [];
|
|
233
|
+
const full = [];
|
|
234
|
+
const onData = (target) => (data) => {
|
|
235
|
+
if (!quiet)
|
|
236
|
+
target.write(data);
|
|
237
|
+
const chunkLines = data.toString('utf8').split('\n');
|
|
238
|
+
tail.push(...chunkLines);
|
|
239
|
+
if (tail.length > 200)
|
|
240
|
+
tail = tail.slice(-200);
|
|
241
|
+
full.push(...chunkLines);
|
|
242
|
+
};
|
|
243
|
+
child.stdout?.on('data', onData(process.stdout));
|
|
244
|
+
child.stderr?.on('data', onData(process.stderr));
|
|
245
|
+
child.on('error', (err) => rej(new Error(`Failed to run ${gradlew}: ${err.message}`)));
|
|
246
|
+
child.on('exit', (code) => {
|
|
247
|
+
options.onOutput?.(full.join('\n'));
|
|
248
|
+
if (code === 0) {
|
|
249
|
+
res();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const tailText = tail.join('\n');
|
|
253
|
+
const diagnosis = diagnoseGradleFailure(tailText);
|
|
254
|
+
if (diagnosis.length > 0) {
|
|
255
|
+
process.stderr.write(diagnosis.map((line) => `phonebook: ${line}`).join('\n') + '\n');
|
|
256
|
+
}
|
|
257
|
+
const dumpTailOnFailure = options.dumpTailOnFailure ?? true;
|
|
258
|
+
if (quiet && dumpTailOnFailure && tail.length > 0)
|
|
259
|
+
process.stderr.write(tailText + '\n');
|
|
260
|
+
const message = [`Gradle failed (exit ${code}) running: ${tasks.join(' ')}`, ...diagnosis].join('\n');
|
|
261
|
+
rej(new Error(message));
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=android.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"android.js","sourceRoot":"","sources":["../../src/engines/android.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/** Best-effort git metadata for the manifest; absent outside a git repo. */
|
|
3
|
+
export async function gitInfo(projectDir) {
|
|
4
|
+
const commit = await new Promise((res) => {
|
|
5
|
+
const child = spawn('git', ['rev-parse', 'HEAD'], { cwd: projectDir });
|
|
6
|
+
let out = '';
|
|
7
|
+
child.stdout.on('data', (d) => (out += d));
|
|
8
|
+
child.on('error', () => res(undefined));
|
|
9
|
+
child.on('exit', (code) => res(code === 0 ? out.trim() : undefined));
|
|
10
|
+
});
|
|
11
|
+
return commit ? { commit } : {};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/engines/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAkB;IAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAqB,CAAC,GAAG,EAAE,EAAE;QAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC;QACvE,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACxC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PhonebookConfig } from '../config.js';
|
|
2
|
+
import { type Manifest, type ManifestEntry } from '../manifest.js';
|
|
3
|
+
/** Builds the error/warning text for a `generate` run that exported zero snapshots. Exported for tests. */
|
|
4
|
+
export declare function buildEmptySnapshotsMessage(exportDir: string, scheme: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the -only-testing:Target/Class argument so `generate` runs just the
|
|
7
|
+
* snapshot test class instead of the app's whole test suite — an unrelated
|
|
8
|
+
* failing unit test must not kill screenshot generation (seen in a real repo
|
|
9
|
+
* whose own tests were flaky). Explicit config wins; "" disables the filter.
|
|
10
|
+
* Exported for tests.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveOnlyTesting(config: PhonebookConfig, projectDir: string): Promise<string | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* Runs the SnapshotPreviews-backed XCTest target via xcodebuild on a simulator
|
|
15
|
+
* and harvests the exported PNG + JSON sidecar pairs into a Phonebook bundle.
|
|
16
|
+
*
|
|
17
|
+
* SnapshotPreviews exports to the directory given by the SNAPSHOTS_EXPORT_DIR
|
|
18
|
+
* env var in the test-runner process; xcodebuild forwards any TEST_RUNNER_-
|
|
19
|
+
* prefixed variable (prefix stripped) into that process.
|
|
20
|
+
*/
|
|
21
|
+
export declare function generateIos(config: PhonebookConfig, projectDir: string, outputDir: string, options?: {
|
|
22
|
+
quiet?: boolean;
|
|
23
|
+
allowEmpty?: boolean;
|
|
24
|
+
}): Promise<Manifest>;
|
|
25
|
+
/** Shape of the JSON sidecar SnapshotPreviews writes next to each PNG. */
|
|
26
|
+
interface SnapshotSidecar {
|
|
27
|
+
display_name?: string;
|
|
28
|
+
group?: string;
|
|
29
|
+
context?: {
|
|
30
|
+
preview?: {
|
|
31
|
+
container_display_name?: string;
|
|
32
|
+
preferred_color_scheme?: string;
|
|
33
|
+
};
|
|
34
|
+
simulator?: {
|
|
35
|
+
device_name?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Exported for tests. Maps one PNG + sidecar to a manifest entry (minus image path). */
|
|
40
|
+
export declare function mapSidecar(png: string, sidecar: SnapshotSidecar): Omit<ManifestEntry, 'image'>;
|
|
41
|
+
/**
|
|
42
|
+
* Runs xcodebuild. When `quiet` is false (the CLI default), output streams
|
|
43
|
+
* straight to this process's stdout/stderr as it arrives (so a human can
|
|
44
|
+
* watch the build) while a rolling ~200-line tail is kept alongside it for
|
|
45
|
+
* error translation. When `quiet` is true (used by the MCP server, whose
|
|
46
|
+
* stdout is the JSON-RPC channel and must never carry build output), only the
|
|
47
|
+
* tail is kept and nothing is echoed live; the tail is written to stderr once
|
|
48
|
+
* if the build fails.
|
|
49
|
+
*
|
|
50
|
+
* On a non-zero exit, the tail is run through `diagnoseXcodebuildFailure` —
|
|
51
|
+
* any matched diagnosis lines are printed to stderr (`phonebook: `-prefixed)
|
|
52
|
+
* and folded into the rejected Error's message, mirroring `runGradle`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function runXcodebuild(cwd: string, args: string[], env: Record<string, string>, quiet: boolean): Promise<void>;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { copyFile, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { diagnoseXcodebuildFailure } from '../errors.js';
|
|
7
|
+
import { SCHEMA_VERSION } from '../manifest.js';
|
|
8
|
+
import { parsePreviewName, spaceCamelCase } from '../naming.js';
|
|
9
|
+
import { gitInfo } from './git.js';
|
|
10
|
+
import { findSnapshotTestSubclass, findSnapshottingTestsTargets, readPbxprojText } from '../ios/snapshotTestClass.js';
|
|
11
|
+
/** Builds the error/warning text for a `generate` run that exported zero snapshots. Exported for tests. */
|
|
12
|
+
export function buildEmptySnapshotsMessage(exportDir, scheme) {
|
|
13
|
+
return (`xcodebuild succeeded but no snapshots were exported to ${exportDir}. ` +
|
|
14
|
+
`Does the "${scheme}" scheme include a SnapshotPreviews test target, and are your #Preview macros not all ` +
|
|
15
|
+
'filtered out (e.g. by a snapshotPreviews() override that excludes them)?');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the -only-testing:Target/Class argument so `generate` runs just the
|
|
19
|
+
* snapshot test class instead of the app's whole test suite — an unrelated
|
|
20
|
+
* failing unit test must not kill screenshot generation (seen in a real repo
|
|
21
|
+
* whose own tests were flaky). Explicit config wins; "" disables the filter.
|
|
22
|
+
* Exported for tests.
|
|
23
|
+
*/
|
|
24
|
+
export async function resolveOnlyTesting(config, projectDir) {
|
|
25
|
+
const configured = config.ios?.onlyTesting;
|
|
26
|
+
if (configured !== undefined)
|
|
27
|
+
return configured === '' ? undefined : configured;
|
|
28
|
+
try {
|
|
29
|
+
const [subclass, pbxproj] = await Promise.all([
|
|
30
|
+
findSnapshotTestSubclass(projectDir),
|
|
31
|
+
readPbxprojText(projectDir, config.ios?.project ? join(projectDir, config.ios.project) : undefined),
|
|
32
|
+
]);
|
|
33
|
+
if (!subclass || !pbxproj)
|
|
34
|
+
return undefined;
|
|
35
|
+
const targets = findSnapshottingTestsTargets(pbxproj);
|
|
36
|
+
if (targets.length !== 1)
|
|
37
|
+
return undefined;
|
|
38
|
+
return `${targets[0].name}/${subclass.className}`;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Runs the SnapshotPreviews-backed XCTest target via xcodebuild on a simulator
|
|
46
|
+
* and harvests the exported PNG + JSON sidecar pairs into a Phonebook bundle.
|
|
47
|
+
*
|
|
48
|
+
* SnapshotPreviews exports to the directory given by the SNAPSHOTS_EXPORT_DIR
|
|
49
|
+
* env var in the test-runner process; xcodebuild forwards any TEST_RUNNER_-
|
|
50
|
+
* prefixed variable (prefix stripped) into that process.
|
|
51
|
+
*/
|
|
52
|
+
export async function generateIos(config, projectDir, outputDir, options = {}) {
|
|
53
|
+
const ios = config.ios;
|
|
54
|
+
if (!ios?.scheme)
|
|
55
|
+
throw new Error('phonebook.config.json: "ios.scheme" is required');
|
|
56
|
+
if (!ios.project && !ios.workspace) {
|
|
57
|
+
throw new Error('phonebook.config.json: one of "ios.project" or "ios.workspace" is required');
|
|
58
|
+
}
|
|
59
|
+
const simulator = ios.simulator ?? 'iPhone 17 Pro';
|
|
60
|
+
const exportDir = await mkdtemp(join(tmpdir(), 'phonebook-snapshots-'));
|
|
61
|
+
try {
|
|
62
|
+
const args = [
|
|
63
|
+
'test',
|
|
64
|
+
...(ios.workspace ? ['-workspace', ios.workspace] : ['-project', ios.project]),
|
|
65
|
+
'-scheme',
|
|
66
|
+
ios.scheme,
|
|
67
|
+
'-destination',
|
|
68
|
+
`platform=iOS Simulator,name=${simulator}`,
|
|
69
|
+
];
|
|
70
|
+
const onlyTesting = await resolveOnlyTesting(config, projectDir);
|
|
71
|
+
if (onlyTesting)
|
|
72
|
+
args.push(`-only-testing:${onlyTesting}`);
|
|
73
|
+
await runXcodebuild(projectDir, args, {
|
|
74
|
+
TEST_RUNNER_SNAPSHOTS_EXPORT_DIR: exportDir,
|
|
75
|
+
TEST_RUNNER_SNAPSHOTS_RUNNING_FOR_PREVIEWS: '1',
|
|
76
|
+
}, options.quiet ?? false);
|
|
77
|
+
const imagesDir = join(outputDir, 'images');
|
|
78
|
+
await mkdir(imagesDir, { recursive: true });
|
|
79
|
+
const pngs = (await readdir(exportDir)).filter((f) => f.endsWith('.png')).sort();
|
|
80
|
+
if (pngs.length === 0) {
|
|
81
|
+
const message = buildEmptySnapshotsMessage(exportDir, ios.scheme);
|
|
82
|
+
if (!(options.allowEmpty ?? false)) {
|
|
83
|
+
throw new Error(message);
|
|
84
|
+
}
|
|
85
|
+
console.warn(`warning: ${message}`);
|
|
86
|
+
}
|
|
87
|
+
const entries = [];
|
|
88
|
+
for (const png of pngs) {
|
|
89
|
+
const sidecar = await readSidecar(join(exportDir, png.replace(/\.png$/, '.json')));
|
|
90
|
+
const meta = mapSidecar(png, sidecar);
|
|
91
|
+
const hash = createHash('sha256');
|
|
92
|
+
hash.update(png);
|
|
93
|
+
const imageName = `${hash.digest('hex').slice(0, 16)}.png`;
|
|
94
|
+
await copyFile(join(exportDir, png), join(imagesDir, imageName));
|
|
95
|
+
entries.push({ ...meta, image: `images/${imageName}` });
|
|
96
|
+
}
|
|
97
|
+
entries.sort((a, b) => a.component.localeCompare(b.component) || a.state.localeCompare(b.state));
|
|
98
|
+
const manifest = {
|
|
99
|
+
schemaVersion: SCHEMA_VERSION,
|
|
100
|
+
platform: 'ios',
|
|
101
|
+
app: {
|
|
102
|
+
name: config.appName,
|
|
103
|
+
generatedAt: new Date().toISOString(),
|
|
104
|
+
...(await gitInfo(projectDir)),
|
|
105
|
+
},
|
|
106
|
+
entries,
|
|
107
|
+
};
|
|
108
|
+
await writeFile(join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
109
|
+
return manifest;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
await rm(exportDir, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function readSidecar(path) {
|
|
116
|
+
try {
|
|
117
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Exported for tests. Maps one PNG + sidecar to a manifest entry (minus image path). */
|
|
124
|
+
export function mapSidecar(png, sidecar) {
|
|
125
|
+
const container = sidecar.context?.preview?.container_display_name;
|
|
126
|
+
// Unnamed previews get an auto display name like "At line #14" — not a state.
|
|
127
|
+
const rawName = sidecar.display_name?.trim();
|
|
128
|
+
const displayName = rawName && !/^At line #\d+$/.test(rawName) ? rawName : undefined;
|
|
129
|
+
const fallback = container ? container.replace(/\s+/g, '') : png.replace(/\.png$/, '');
|
|
130
|
+
const { component, state } = parsePreviewName(fallback, displayName);
|
|
131
|
+
const scheme = sidecar.context?.preview?.preferred_color_scheme;
|
|
132
|
+
const group = sidecar.group; // e.g. "PhonebookSample/UserCard.swift"
|
|
133
|
+
const module = group?.includes('/') ? group.slice(0, group.indexOf('/')) : undefined;
|
|
134
|
+
return {
|
|
135
|
+
component: component || spaceCamelCase(fallback),
|
|
136
|
+
state,
|
|
137
|
+
module: module ?? 'app',
|
|
138
|
+
sourceFile: group,
|
|
139
|
+
previewName: rawName ?? png,
|
|
140
|
+
theme: scheme === 'dark' ? 'dark' : scheme === 'light' ? 'light' : undefined,
|
|
141
|
+
device: sidecar.context?.simulator?.device_name,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Runs xcodebuild. When `quiet` is false (the CLI default), output streams
|
|
146
|
+
* straight to this process's stdout/stderr as it arrives (so a human can
|
|
147
|
+
* watch the build) while a rolling ~200-line tail is kept alongside it for
|
|
148
|
+
* error translation. When `quiet` is true (used by the MCP server, whose
|
|
149
|
+
* stdout is the JSON-RPC channel and must never carry build output), only the
|
|
150
|
+
* tail is kept and nothing is echoed live; the tail is written to stderr once
|
|
151
|
+
* if the build fails.
|
|
152
|
+
*
|
|
153
|
+
* On a non-zero exit, the tail is run through `diagnoseXcodebuildFailure` —
|
|
154
|
+
* any matched diagnosis lines are printed to stderr (`phonebook: `-prefixed)
|
|
155
|
+
* and folded into the rejected Error's message, mirroring `runGradle`.
|
|
156
|
+
*/
|
|
157
|
+
export function runXcodebuild(cwd, args, env, quiet) {
|
|
158
|
+
return new Promise((resolvePromise, reject) => {
|
|
159
|
+
const child = spawn('xcodebuild', args, {
|
|
160
|
+
cwd: resolve(cwd),
|
|
161
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
162
|
+
env: { ...process.env, ...env },
|
|
163
|
+
});
|
|
164
|
+
let tail = [];
|
|
165
|
+
const onData = (target) => (data) => {
|
|
166
|
+
if (!quiet)
|
|
167
|
+
target.write(data);
|
|
168
|
+
tail.push(...data.toString('utf8').split('\n'));
|
|
169
|
+
if (tail.length > 200)
|
|
170
|
+
tail = tail.slice(-200);
|
|
171
|
+
};
|
|
172
|
+
child.stdout?.on('data', onData(process.stdout));
|
|
173
|
+
child.stderr?.on('data', onData(process.stderr));
|
|
174
|
+
child.on('error', reject);
|
|
175
|
+
child.on('close', (code) => {
|
|
176
|
+
if (code === 0) {
|
|
177
|
+
resolvePromise();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const tailText = tail.join('\n');
|
|
181
|
+
const diagnosis = diagnoseXcodebuildFailure(tailText);
|
|
182
|
+
if (diagnosis.length > 0) {
|
|
183
|
+
process.stderr.write(diagnosis.map((line) => `phonebook: ${line}`).join('\n') + '\n');
|
|
184
|
+
}
|
|
185
|
+
if (quiet && tail.length > 0)
|
|
186
|
+
process.stderr.write(tailText + '\n');
|
|
187
|
+
const message = [
|
|
188
|
+
`xcodebuild failed (exit ${code}) running: xcodebuild ${args.join(' ')}`,
|
|
189
|
+
...diagnosis,
|
|
190
|
+
].join('\n');
|
|
191
|
+
reject(new Error(message));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=ios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/engines/ios.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signature matching over raw Gradle (or xcodebuild) output, turning known
|
|
3
|
+
* failure patterns into human diagnosis lines. Zero lines means "no known
|
|
4
|
+
* signature matched" — the raw output should still be shown to the user.
|
|
5
|
+
*/
|
|
6
|
+
export declare function diagnoseGradleFailure(output: string): string[];
|
|
7
|
+
/**
|
|
8
|
+
* Signature matching over raw xcodebuild output, turning known failure
|
|
9
|
+
* patterns into human diagnosis lines. Zero lines means "no known signature
|
|
10
|
+
* matched" — the raw output should still be shown to the user.
|
|
11
|
+
*/
|
|
12
|
+
export declare function diagnoseXcodebuildFailure(output: string): string[];
|