@designfever/web-review-kit 0.6.0 → 0.7.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/.env.sample +32 -0
- package/README.md +35 -10
- package/dist/chunk-2ZLU5FTD.js +602 -0
- package/dist/chunk-2ZLU5FTD.js.map +1 -0
- package/dist/{chunk-IN36JHEU.js → chunk-RPVLRULC.js} +504 -143
- package/dist/chunk-RPVLRULC.js.map +1 -0
- package/dist/image.types-BmzkFSPX.d.cts +71 -0
- package/dist/image.types-BmzkFSPX.d.ts +71 -0
- package/dist/index.cjs +1037 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -3
- package/dist/index.d.ts +29 -3
- package/dist/index.js +50 -3
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +10339 -5168
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +39 -4
- package/dist/react-shell.d.ts +39 -4
- package/dist/react-shell.js +9687 -4856
- package/dist/react-shell.js.map +1 -1
- package/dist/token-Dt-ZH-YO.d.cts +88 -0
- package/dist/token-nJXPPdYX.d.ts +88 -0
- package/dist/{types-DFHHVRBc.d.cts → types-DT9Z66mV.d.cts} +13 -1
- package/dist/{types-DFHHVRBc.d.ts → types-DT9Z66mV.d.ts} +13 -1
- package/dist/vite.cjs +1144 -5
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +45 -1
- package/dist/vite.d.ts +45 -1
- package/dist/vite.js +829 -5
- package/dist/vite.js.map +1 -1
- package/docs/README.md +13 -7
- package/docs/adapters.md +128 -0
- package/docs/adaptor.sample.ts +13 -1
- package/docs/architecture.md +2 -1
- package/docs/code-review-0.6.0.md +232 -0
- package/docs/db-setup.md +4 -2
- package/docs/figma-image-mvp-0.7.0.md +330 -0
- package/docs/figma-overlay.md +11 -4
- package/docs/installation.md +42 -7
- package/docs/release-notes-0.7.0.md +132 -0
- package/docs/release-notes-0.7.1.md +34 -0
- package/package.json +6 -2
- package/dist/chunk-IN36JHEU.js.map +0 -1
package/dist/vite.js
CHANGED
|
@@ -1,12 +1,814 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
3
|
+
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
4
|
+
ReviewFigmaTokenError,
|
|
5
|
+
collectReviewFigmaReleaseSnapshot,
|
|
6
|
+
createReviewFigmaAssetStorageKey,
|
|
7
|
+
createReviewFigmaAssetUrl,
|
|
8
|
+
createReviewFigmaImageApiUrl,
|
|
9
|
+
createReviewFigmaImagesSnapshot,
|
|
10
|
+
createReviewFigmaReleaseSnapshot,
|
|
11
|
+
getReviewFigmaAssetMimeType,
|
|
12
|
+
getReviewFigmaAssetStorageKeyFromPathname,
|
|
13
|
+
getReviewFigmaImageFormatFromMimeType,
|
|
14
|
+
getReviewFigmaImageMimeType,
|
|
15
|
+
getReviewFigmaImageTargetKey,
|
|
16
|
+
getStoreRenderFormat,
|
|
17
|
+
isSafeReviewFigmaAssetStorageKey,
|
|
18
|
+
normalizeImageMimeType,
|
|
19
|
+
parseReviewFigmaImageFormat,
|
|
20
|
+
parseReviewFigmaNodeRef,
|
|
21
|
+
readReviewFigmaToken,
|
|
22
|
+
renderReviewFigmaImage,
|
|
23
|
+
requireReviewFigmaToken
|
|
24
|
+
} from "./chunk-2ZLU5FTD.js";
|
|
25
|
+
|
|
26
|
+
// src/vite/figma-image-store.ts
|
|
27
|
+
import path3 from "path";
|
|
28
|
+
import { loadEnv } from "vite";
|
|
29
|
+
|
|
30
|
+
// src/vite/figma-image-store.server.ts
|
|
31
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
32
|
+
import path2 from "path";
|
|
33
|
+
|
|
34
|
+
// src/vite/figma-image-store.image.ts
|
|
35
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
36
|
+
import path from "path";
|
|
37
|
+
function requireReviewFigmaRequestToken({
|
|
38
|
+
enabled,
|
|
39
|
+
env,
|
|
40
|
+
envKey,
|
|
41
|
+
requestToken,
|
|
42
|
+
token
|
|
43
|
+
}) {
|
|
44
|
+
const tokenEnvKey = envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
45
|
+
const figmaToken = readReviewFigmaToken({ token, env, envKey: tokenEnvKey, enabled }) || readReviewFigmaToken({
|
|
46
|
+
token: requestToken,
|
|
47
|
+
env: {},
|
|
48
|
+
envKey: tokenEnvKey,
|
|
49
|
+
enabled
|
|
50
|
+
});
|
|
51
|
+
if (!figmaToken) throw new ReviewFigmaTokenError(tokenEnvKey);
|
|
52
|
+
return figmaToken;
|
|
53
|
+
}
|
|
54
|
+
async function readReviewFigmaNodeName({
|
|
55
|
+
apiBaseUrl,
|
|
56
|
+
enabled,
|
|
57
|
+
env,
|
|
58
|
+
envKey,
|
|
59
|
+
fetchOption,
|
|
60
|
+
fileKey,
|
|
61
|
+
nodeId,
|
|
62
|
+
token,
|
|
63
|
+
requestToken
|
|
64
|
+
}) {
|
|
65
|
+
const figmaToken = requireReviewFigmaRequestToken({
|
|
66
|
+
enabled,
|
|
67
|
+
env,
|
|
68
|
+
envKey,
|
|
69
|
+
requestToken,
|
|
70
|
+
token
|
|
71
|
+
});
|
|
72
|
+
const fetchNode = fetchOption ?? globalThis.fetch;
|
|
73
|
+
if (!fetchNode) throw new Error("Figma node name lookup requires fetch.");
|
|
74
|
+
const response = await fetchNode(
|
|
75
|
+
createReviewFigmaNodeApiUrl({ apiBaseUrl, fileKey, nodeId }),
|
|
76
|
+
{
|
|
77
|
+
headers: {
|
|
78
|
+
"X-Figma-Token": figmaToken
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
const body = await response.json().catch(() => null);
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
throw new Error(body?.err || `Figma node lookup failed with ${response.status}`);
|
|
85
|
+
}
|
|
86
|
+
const nodes = body?.nodes;
|
|
87
|
+
const node = nodes?.[nodeId] ?? Object.values(nodes ?? {})[0];
|
|
88
|
+
return normalizeOptionalText(node?.document?.name);
|
|
89
|
+
}
|
|
90
|
+
function createReviewFigmaNodeApiUrl({
|
|
91
|
+
apiBaseUrl = "https://api.figma.com",
|
|
92
|
+
fileKey,
|
|
93
|
+
nodeId
|
|
94
|
+
}) {
|
|
95
|
+
const url = new URL(
|
|
96
|
+
`/v1/files/${encodeURIComponent(fileKey)}/nodes`,
|
|
97
|
+
apiBaseUrl
|
|
98
|
+
);
|
|
99
|
+
url.searchParams.set("ids", nodeId);
|
|
100
|
+
return url.toString();
|
|
101
|
+
}
|
|
102
|
+
async function createReviewFigmaImage({
|
|
103
|
+
assetDir,
|
|
104
|
+
assetEndpoint,
|
|
105
|
+
currentImages,
|
|
106
|
+
env,
|
|
107
|
+
input,
|
|
108
|
+
options,
|
|
109
|
+
requestToken
|
|
110
|
+
}) {
|
|
111
|
+
const ref = parseReviewFigmaNodeRef(input.figmaUrl);
|
|
112
|
+
if (!ref) {
|
|
113
|
+
throw new Error("A Figma node copy link or fileKey->nodeId value is required.");
|
|
114
|
+
}
|
|
115
|
+
const id = createReviewFigmaImageId();
|
|
116
|
+
const explicitLabel = normalizeOptionalText(input.label);
|
|
117
|
+
if (input.asset) {
|
|
118
|
+
const cachedAsset2 = await cacheReviewFigmaProvidedImageAsset({
|
|
119
|
+
assetDir,
|
|
120
|
+
assetEndpoint,
|
|
121
|
+
id,
|
|
122
|
+
asset: input.asset,
|
|
123
|
+
options
|
|
124
|
+
});
|
|
125
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
126
|
+
const order2 = typeof input.order === "number" && Number.isFinite(input.order) ? input.order : getNextImageOrder(currentImages, input.target);
|
|
127
|
+
return {
|
|
128
|
+
id,
|
|
129
|
+
projectId: input.target.projectId,
|
|
130
|
+
target: input.target,
|
|
131
|
+
figmaUrl: input.figmaUrl,
|
|
132
|
+
fileKey: ref.fileKey,
|
|
133
|
+
nodeId: ref.nodeId,
|
|
134
|
+
imageUrl: cachedAsset2.imageUrl,
|
|
135
|
+
imageFormat: cachedAsset2.imageFormat,
|
|
136
|
+
mimeType: cachedAsset2.mimeType,
|
|
137
|
+
label: explicitLabel,
|
|
138
|
+
order: order2,
|
|
139
|
+
storageKey: cachedAsset2.storageKey,
|
|
140
|
+
width: input.asset.width,
|
|
141
|
+
height: input.asset.height,
|
|
142
|
+
byteSize: cachedAsset2.byteSize,
|
|
143
|
+
createdAt: now2,
|
|
144
|
+
updatedAt: now2
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const nodeLabelPromise = explicitLabel ? Promise.resolve(void 0) : readReviewFigmaNodeName({
|
|
148
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
149
|
+
enabled: options.enabled,
|
|
150
|
+
env,
|
|
151
|
+
envKey: options.envKey,
|
|
152
|
+
fetchOption: options.fetch,
|
|
153
|
+
fileKey: ref.fileKey,
|
|
154
|
+
nodeId: ref.nodeId,
|
|
155
|
+
token: options.token,
|
|
156
|
+
requestToken
|
|
157
|
+
}).catch(() => void 0);
|
|
158
|
+
const targetImageFormat = input.imageFormat ?? options.imageFormat ?? "webp";
|
|
159
|
+
const renderFormat = getStoreRenderFormat(options.renderFormat, targetImageFormat);
|
|
160
|
+
const rendered = await renderReviewFigmaImage({
|
|
161
|
+
figmaUrl: input.figmaUrl,
|
|
162
|
+
format: renderFormat,
|
|
163
|
+
scale: options.renderScale,
|
|
164
|
+
useAbsoluteBounds: options.useAbsoluteBounds,
|
|
165
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
166
|
+
fetch: options.fetch,
|
|
167
|
+
token: requireReviewFigmaRequestToken({
|
|
168
|
+
enabled: options.enabled,
|
|
169
|
+
env,
|
|
170
|
+
envKey: options.envKey,
|
|
171
|
+
requestToken,
|
|
172
|
+
token: options.token
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
const cachedAsset = await cacheReviewFigmaImageAsset({
|
|
176
|
+
assetDir,
|
|
177
|
+
assetEndpoint,
|
|
178
|
+
id,
|
|
179
|
+
imageUrl: rendered.imageUrl,
|
|
180
|
+
options,
|
|
181
|
+
renderFormat,
|
|
182
|
+
targetImageFormat
|
|
183
|
+
});
|
|
184
|
+
const imageFormat = cachedAsset?.imageFormat ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
185
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
186
|
+
const order = typeof input.order === "number" && Number.isFinite(input.order) ? input.order : getNextImageOrder(currentImages, input.target);
|
|
187
|
+
const nodeLabel = await nodeLabelPromise;
|
|
188
|
+
return {
|
|
189
|
+
id,
|
|
190
|
+
projectId: input.target.projectId,
|
|
191
|
+
target: input.target,
|
|
192
|
+
figmaUrl: input.figmaUrl,
|
|
193
|
+
fileKey: rendered.fileKey,
|
|
194
|
+
nodeId: rendered.nodeId,
|
|
195
|
+
imageUrl: cachedAsset?.imageUrl ?? rendered.imageUrl,
|
|
196
|
+
imageFormat,
|
|
197
|
+
mimeType: cachedAsset?.mimeType ?? getReviewFigmaImageMimeType(imageFormat),
|
|
198
|
+
label: explicitLabel ?? nodeLabel,
|
|
199
|
+
order,
|
|
200
|
+
storageKey: cachedAsset?.storageKey,
|
|
201
|
+
byteSize: cachedAsset?.byteSize,
|
|
202
|
+
createdAt: now,
|
|
203
|
+
updatedAt: now
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function cacheReviewFigmaProvidedImageAsset({
|
|
207
|
+
assetDir,
|
|
208
|
+
assetEndpoint,
|
|
209
|
+
id,
|
|
210
|
+
asset,
|
|
211
|
+
options
|
|
212
|
+
}) {
|
|
213
|
+
const decodedAsset = decodeReviewFigmaImageAsset(asset);
|
|
214
|
+
const storageKey = createReviewFigmaAssetStorageKey(
|
|
215
|
+
id,
|
|
216
|
+
decodedAsset.imageFormat
|
|
217
|
+
);
|
|
218
|
+
await mkdir(assetDir, { recursive: true });
|
|
219
|
+
await writeFile(path.join(assetDir, storageKey), decodedAsset.data);
|
|
220
|
+
return {
|
|
221
|
+
imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),
|
|
222
|
+
imageFormat: decodedAsset.imageFormat,
|
|
223
|
+
mimeType: decodedAsset.mimeType,
|
|
224
|
+
storageKey,
|
|
225
|
+
byteSize: decodedAsset.data.byteLength
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function decodeReviewFigmaImageAsset(asset) {
|
|
229
|
+
const mimeType = normalizeImageMimeType(asset.mimeType);
|
|
230
|
+
if (!mimeType) throw new Error("Unsupported Figma image asset MIME type.");
|
|
231
|
+
const imageFormat = getReviewFigmaImageFormatFromMimeType(mimeType) ?? asset.imageFormat;
|
|
232
|
+
const match = /^data:([^;,]+);base64,([a-zA-Z0-9+/=\s]+)$/.exec(
|
|
233
|
+
asset.dataUrl
|
|
234
|
+
);
|
|
235
|
+
if (!match) throw new Error("Valid Figma image asset data URL is required.");
|
|
236
|
+
const dataUrlMimeType = normalizeImageMimeType(match[1]);
|
|
237
|
+
if (dataUrlMimeType && dataUrlMimeType !== mimeType) {
|
|
238
|
+
throw new Error("Figma image asset MIME type mismatch.");
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
data: Buffer.from(match[2].replace(/\s/g, ""), "base64"),
|
|
242
|
+
imageFormat,
|
|
243
|
+
mimeType
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async function cacheReviewFigmaImageAsset({
|
|
247
|
+
assetDir,
|
|
248
|
+
assetEndpoint,
|
|
249
|
+
id,
|
|
250
|
+
imageUrl,
|
|
251
|
+
options,
|
|
252
|
+
renderFormat,
|
|
253
|
+
targetImageFormat
|
|
254
|
+
}) {
|
|
255
|
+
if (options.cacheAssets === false) return null;
|
|
256
|
+
const asset = await downloadReviewFigmaImageAsset({
|
|
257
|
+
fetchOption: options.fetch,
|
|
258
|
+
imageUrl,
|
|
259
|
+
renderFormat,
|
|
260
|
+
targetImageFormat,
|
|
261
|
+
transformAsset: options.transformAsset
|
|
262
|
+
});
|
|
263
|
+
const storageKey = createReviewFigmaAssetStorageKey(id, asset.imageFormat);
|
|
264
|
+
await mkdir(assetDir, { recursive: true });
|
|
265
|
+
await writeFile(path.join(assetDir, storageKey), asset.data);
|
|
266
|
+
return {
|
|
267
|
+
imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),
|
|
268
|
+
imageFormat: asset.imageFormat,
|
|
269
|
+
mimeType: asset.mimeType,
|
|
270
|
+
storageKey,
|
|
271
|
+
byteSize: asset.data.byteLength
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
async function downloadReviewFigmaImageAsset({
|
|
275
|
+
fetchOption,
|
|
276
|
+
imageUrl,
|
|
277
|
+
renderFormat,
|
|
278
|
+
targetImageFormat,
|
|
279
|
+
transformAsset
|
|
280
|
+
}) {
|
|
281
|
+
const fetchImage = fetchOption ?? globalThis.fetch;
|
|
282
|
+
if (!fetchImage) throw new Error("Figma image caching requires fetch.");
|
|
283
|
+
const response = await fetchImage(imageUrl);
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
throw new Error(`Figma image download failed with ${response.status}`);
|
|
286
|
+
}
|
|
287
|
+
const sourceMimeType = normalizeImageMimeType(response.headers.get("content-type")) ?? getReviewFigmaImageMimeType(renderFormat === "jpg" ? "jpg" : "png");
|
|
288
|
+
const sourceImageFormat = getReviewFigmaImageFormatFromMimeType(sourceMimeType) ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
289
|
+
const sourceData = new Uint8Array(await response.arrayBuffer());
|
|
290
|
+
const transformed = transformAsset ? await transformAsset({
|
|
291
|
+
data: sourceData,
|
|
292
|
+
imageFormat: sourceImageFormat,
|
|
293
|
+
mimeType: sourceMimeType,
|
|
294
|
+
targetFormat: targetImageFormat
|
|
295
|
+
}) : null;
|
|
296
|
+
const imageFormat = transformed?.imageFormat ?? sourceImageFormat;
|
|
297
|
+
const mimeType = normalizeImageMimeType(transformed?.mimeType) ?? getReviewFigmaImageMimeType(imageFormat);
|
|
298
|
+
const data = createBufferFromImageData(transformed?.data ?? sourceData);
|
|
299
|
+
return { data, imageFormat, mimeType };
|
|
300
|
+
}
|
|
301
|
+
async function deleteReviewFigmaImageAsset(assetDir, storageKey) {
|
|
302
|
+
if (!storageKey) return;
|
|
303
|
+
if (!isSafeReviewFigmaAssetStorageKey(storageKey)) return;
|
|
304
|
+
await rm(path.join(assetDir, storageKey), { force: true }).catch(() => null);
|
|
305
|
+
}
|
|
306
|
+
function createBufferFromImageData(data) {
|
|
307
|
+
return data instanceof ArrayBuffer ? Buffer.from(new Uint8Array(data)) : Buffer.from(data);
|
|
308
|
+
}
|
|
309
|
+
function listImagesForTarget(images, target) {
|
|
310
|
+
const targetKey = getReviewFigmaImageTargetKey(target);
|
|
311
|
+
return images.filter((image) => getReviewFigmaImageTargetKey(image.target) === targetKey).sort(compareReviewFigmaImages);
|
|
312
|
+
}
|
|
313
|
+
function reorderReviewFigmaImages(images, input) {
|
|
314
|
+
const targetKey = getReviewFigmaImageTargetKey(input.target);
|
|
315
|
+
const orderById = new Map(input.imageIds.map((id, index) => [id, index]));
|
|
316
|
+
const targetImages = listImagesForTarget(images, input.target);
|
|
317
|
+
const nextTargetImages = targetImages.map((image) => ({
|
|
318
|
+
...image,
|
|
319
|
+
order: orderById.get(image.id) ?? input.imageIds.length + image.order,
|
|
320
|
+
updatedAt: orderById.has(image.id) ? (/* @__PURE__ */ new Date()).toISOString() : image.updatedAt
|
|
321
|
+
})).sort(compareReviewFigmaImages);
|
|
322
|
+
const nextTargetImageById = new Map(
|
|
323
|
+
nextTargetImages.map((image, index) => [image.id, { ...image, order: index }])
|
|
324
|
+
);
|
|
325
|
+
const allImages = images.map(
|
|
326
|
+
(image) => getReviewFigmaImageTargetKey(image.target) === targetKey ? nextTargetImageById.get(image.id) ?? image : image
|
|
327
|
+
);
|
|
328
|
+
return {
|
|
329
|
+
allImages,
|
|
330
|
+
targetImages: listImagesForTarget(allImages, input.target)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function updateReviewFigmaImage(images, id, patch) {
|
|
334
|
+
const index = images.findIndex((image) => image.id === id);
|
|
335
|
+
if (index < 0) return null;
|
|
336
|
+
const nextImage = {
|
|
337
|
+
...images[index],
|
|
338
|
+
label: patch.label === void 0 ? images[index].label : normalizeOptionalText(patch.label),
|
|
339
|
+
order: typeof patch.order === "number" && Number.isFinite(patch.order) ? patch.order : images[index].order,
|
|
340
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
341
|
+
};
|
|
342
|
+
const nextImages = [...images];
|
|
343
|
+
nextImages[index] = nextImage;
|
|
344
|
+
return { image: nextImage, images: nextImages };
|
|
345
|
+
}
|
|
346
|
+
async function readReviewFigmaImageStoreFile(dataFile) {
|
|
347
|
+
try {
|
|
348
|
+
const raw = await readFile(dataFile, "utf8");
|
|
349
|
+
const parsed = JSON.parse(raw);
|
|
350
|
+
return {
|
|
351
|
+
version: 1,
|
|
352
|
+
images: Array.isArray(parsed.images) ? parsed.images.flatMap((image) => isReviewFigmaImage(image) ? [image] : []) : []
|
|
353
|
+
};
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
356
|
+
return { version: 1, images: [] };
|
|
357
|
+
}
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async function writeReviewFigmaImageStoreFile(dataFile, data) {
|
|
362
|
+
await mkdir(path.dirname(dataFile), { recursive: true });
|
|
363
|
+
await writeFile(
|
|
364
|
+
dataFile,
|
|
365
|
+
`${JSON.stringify({ version: 1, images: data.images }, null, 2)}
|
|
366
|
+
`,
|
|
367
|
+
"utf8"
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
function getNextImageOrder(images, target) {
|
|
371
|
+
const targetImages = listImagesForTarget(images, target);
|
|
372
|
+
return targetImages.length ? Math.max(...targetImages.map((image) => image.order)) + 1 : 0;
|
|
373
|
+
}
|
|
374
|
+
function compareReviewFigmaImages(a, b) {
|
|
375
|
+
return a.order - b.order || a.createdAt.localeCompare(b.createdAt);
|
|
376
|
+
}
|
|
377
|
+
function createReviewFigmaImageId() {
|
|
378
|
+
return `figma_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
379
|
+
}
|
|
380
|
+
function isReviewFigmaImage(value) {
|
|
381
|
+
if (!value || typeof value !== "object") return false;
|
|
382
|
+
const image = value;
|
|
383
|
+
return typeof image.id === "string" && typeof image.projectId === "string" && typeof image.figmaUrl === "string" && typeof image.fileKey === "string" && typeof image.nodeId === "string" && typeof image.imageUrl === "string" && typeof image.order === "number" && typeof image.createdAt === "string" && typeof image.updatedAt === "string";
|
|
384
|
+
}
|
|
385
|
+
function isNodeError(error) {
|
|
386
|
+
if (!error || typeof error !== "object") return false;
|
|
387
|
+
return "code" in error;
|
|
388
|
+
}
|
|
389
|
+
function normalizeOptionalText(value) {
|
|
390
|
+
if (typeof value !== "string") return void 0;
|
|
391
|
+
return value.trim() || void 0;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/vite/figma-image-store.server.ts
|
|
395
|
+
async function handleReviewFigmaImageStoreRequest({
|
|
396
|
+
dataFile,
|
|
397
|
+
assetDir,
|
|
398
|
+
assetEndpoint,
|
|
399
|
+
endpoint,
|
|
400
|
+
method,
|
|
401
|
+
options,
|
|
402
|
+
pathname,
|
|
403
|
+
requestUrl,
|
|
404
|
+
body,
|
|
405
|
+
env,
|
|
406
|
+
requestToken
|
|
407
|
+
}) {
|
|
408
|
+
if (method === "OPTIONS") return { status: 204, body: null };
|
|
409
|
+
if ((method === "GET" || method === "POST") && pathname === `${endpoint}/snapshot`) {
|
|
410
|
+
const input = parseReleaseSnapshotInput(body, requestUrl, options.projectId);
|
|
411
|
+
if (!input) return jsonError(400, "valid snapshot input is required.");
|
|
412
|
+
if (options.projectId && input.projectId !== options.projectId) {
|
|
413
|
+
return jsonError(403, "snapshot project is not allowed.");
|
|
414
|
+
}
|
|
415
|
+
if (input.targets.some((target) => !isAllowedProjectTarget(target, options.projectId))) {
|
|
416
|
+
return jsonError(403, "snapshot target project is not allowed.");
|
|
417
|
+
}
|
|
418
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
419
|
+
return {
|
|
420
|
+
status: 200,
|
|
421
|
+
body: createReviewFigmaReleaseSnapshot({
|
|
422
|
+
images: data.images,
|
|
423
|
+
projectId: input.projectId,
|
|
424
|
+
releaseId: input.releaseId,
|
|
425
|
+
label: input.label,
|
|
426
|
+
targets: input.targets.length > 0 ? input.targets : void 0
|
|
427
|
+
})
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (method === "GET" && pathname === endpoint) {
|
|
431
|
+
const target = parseTargetParam(requestUrl.searchParams.get("target"));
|
|
432
|
+
if (!target) return jsonError(400, "target query is required.");
|
|
433
|
+
if (!isAllowedProjectTarget(target, options.projectId)) {
|
|
434
|
+
return { status: 200, body: [] };
|
|
435
|
+
}
|
|
436
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
437
|
+
return { status: 200, body: listImagesForTarget(data.images, target) };
|
|
438
|
+
}
|
|
439
|
+
if (method === "POST" && pathname === endpoint) {
|
|
440
|
+
const input = parseAddImageInput(body);
|
|
441
|
+
if (!input) return jsonError(400, "valid add image input is required.");
|
|
442
|
+
if (!isAllowedProjectTarget(input.target, options.projectId)) {
|
|
443
|
+
return jsonError(403, "target project is not allowed.");
|
|
444
|
+
}
|
|
445
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
446
|
+
const image = await createReviewFigmaImage({
|
|
447
|
+
assetDir,
|
|
448
|
+
assetEndpoint,
|
|
449
|
+
currentImages: data.images,
|
|
450
|
+
env,
|
|
451
|
+
input,
|
|
452
|
+
options,
|
|
453
|
+
requestToken
|
|
454
|
+
});
|
|
455
|
+
data.images = [image, ...data.images];
|
|
456
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
457
|
+
return { status: 201, body: image };
|
|
458
|
+
}
|
|
459
|
+
if (method === "PATCH" && pathname === `${endpoint}/reorder`) {
|
|
460
|
+
const input = parseReorderImagesInput(body);
|
|
461
|
+
if (!input) return jsonError(400, "valid reorder input is required.");
|
|
462
|
+
if (!isAllowedProjectTarget(input.target, options.projectId)) {
|
|
463
|
+
return jsonError(403, "target project is not allowed.");
|
|
464
|
+
}
|
|
465
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
466
|
+
const images = reorderReviewFigmaImages(data.images, input);
|
|
467
|
+
data.images = images.allImages;
|
|
468
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
469
|
+
return { status: 200, body: images.targetImages };
|
|
470
|
+
}
|
|
471
|
+
const id = getEndpointItemId(pathname, endpoint);
|
|
472
|
+
if (id && method === "PATCH") {
|
|
473
|
+
const patch = parseUpdateImageInput(body);
|
|
474
|
+
if (!patch) return jsonError(400, "valid update patch is required.");
|
|
475
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
476
|
+
const result = updateReviewFigmaImage(data.images, id, patch);
|
|
477
|
+
if (!result) return jsonError(404, `Figma image not found: ${id}`);
|
|
478
|
+
if (!isAllowedProjectTarget(result.image.target, options.projectId)) {
|
|
479
|
+
return jsonError(403, "target project is not allowed.");
|
|
480
|
+
}
|
|
481
|
+
data.images = result.images;
|
|
482
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
483
|
+
return { status: 200, body: result.image };
|
|
484
|
+
}
|
|
485
|
+
if (id && method === "DELETE") {
|
|
486
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
487
|
+
const image = data.images.find((item) => item.id === id);
|
|
488
|
+
if (!image) return jsonError(404, `Figma image not found: ${id}`);
|
|
489
|
+
if (!isAllowedProjectTarget(image.target, options.projectId)) {
|
|
490
|
+
return jsonError(403, "target project is not allowed.");
|
|
491
|
+
}
|
|
492
|
+
data.images = data.images.filter((item) => item.id !== id);
|
|
493
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
494
|
+
await deleteReviewFigmaImageAsset(assetDir, image.storageKey);
|
|
495
|
+
return { status: 200, body: { ok: true } };
|
|
496
|
+
}
|
|
497
|
+
return jsonError(405, "method not allowed.");
|
|
498
|
+
}
|
|
499
|
+
async function readJsonRequestBody(req) {
|
|
500
|
+
if (req.method === "GET" || req.method === "DELETE") return null;
|
|
501
|
+
const chunks = [];
|
|
502
|
+
for await (const chunk of req) {
|
|
503
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
504
|
+
}
|
|
505
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
506
|
+
if (!raw) return null;
|
|
507
|
+
return JSON.parse(raw);
|
|
508
|
+
}
|
|
509
|
+
function sendJson(res, status, body) {
|
|
510
|
+
res.statusCode = status;
|
|
511
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
512
|
+
if (status === 204) {
|
|
513
|
+
res.end();
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
res.end(JSON.stringify(body ?? null));
|
|
517
|
+
}
|
|
518
|
+
async function sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname) {
|
|
519
|
+
const storageKey = getReviewFigmaAssetStorageKeyFromPathname(pathname, assetEndpoint);
|
|
520
|
+
if (!storageKey) {
|
|
521
|
+
sendPlainText(res, 400, "Invalid Figma image asset path.");
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
const data = await readFile2(path2.join(assetDir, storageKey));
|
|
526
|
+
res.statusCode = 200;
|
|
527
|
+
res.setHeader("Content-Type", getReviewFigmaAssetMimeType(storageKey));
|
|
528
|
+
res.setHeader("Cache-Control", "private, max-age=31536000, immutable");
|
|
529
|
+
res.end(data);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
532
|
+
sendPlainText(res, 404, "Figma image asset not found.");
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
sendPlainText(
|
|
536
|
+
res,
|
|
537
|
+
500,
|
|
538
|
+
error instanceof Error ? error.message : "Figma image asset request failed."
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function sendPlainText(res, status, body) {
|
|
543
|
+
res.statusCode = status;
|
|
544
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
545
|
+
res.end(body);
|
|
546
|
+
}
|
|
547
|
+
function parseTargetParam(value) {
|
|
548
|
+
if (!value) return null;
|
|
549
|
+
try {
|
|
550
|
+
return parseReviewFigmaImageTarget(JSON.parse(value));
|
|
551
|
+
} catch {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
function parseAddImageInput(value) {
|
|
556
|
+
if (!value || typeof value !== "object") return null;
|
|
557
|
+
const input = value;
|
|
558
|
+
const target = parseReviewFigmaImageTarget(input.target);
|
|
559
|
+
if (!target || typeof input.figmaUrl !== "string") return null;
|
|
560
|
+
return {
|
|
561
|
+
target,
|
|
562
|
+
figmaUrl: input.figmaUrl,
|
|
563
|
+
label: typeof input.label === "string" ? input.label : void 0,
|
|
564
|
+
order: typeof input.order === "number" ? input.order : void 0,
|
|
565
|
+
imageFormat: parseReviewFigmaImageFormat(input.imageFormat),
|
|
566
|
+
asset: parseAddImageAssetInput(input.asset)
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function parseAddImageAssetInput(value) {
|
|
570
|
+
if (!value || typeof value !== "object") return void 0;
|
|
571
|
+
const input = value;
|
|
572
|
+
const imageFormat = parseReviewFigmaImageFormat(input.imageFormat);
|
|
573
|
+
if (!imageFormat || typeof input.dataUrl !== "string" || typeof input.mimeType !== "string") {
|
|
574
|
+
return void 0;
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
dataUrl: input.dataUrl,
|
|
578
|
+
imageFormat,
|
|
579
|
+
mimeType: input.mimeType,
|
|
580
|
+
byteSize: typeof input.byteSize === "number" ? input.byteSize : void 0,
|
|
581
|
+
width: typeof input.width === "number" ? input.width : void 0,
|
|
582
|
+
height: typeof input.height === "number" ? input.height : void 0
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function parseUpdateImageInput(value) {
|
|
586
|
+
if (!value || typeof value !== "object") return null;
|
|
587
|
+
const input = value;
|
|
588
|
+
return {
|
|
589
|
+
label: typeof input.label === "string" ? input.label : void 0,
|
|
590
|
+
order: typeof input.order === "number" ? input.order : void 0
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
function parseReorderImagesInput(value) {
|
|
594
|
+
if (!value || typeof value !== "object") return null;
|
|
595
|
+
const input = value;
|
|
596
|
+
const target = parseReviewFigmaImageTarget(input.target);
|
|
597
|
+
if (!target || !Array.isArray(input.imageIds)) return null;
|
|
598
|
+
return {
|
|
599
|
+
target,
|
|
600
|
+
imageIds: input.imageIds.filter((id) => typeof id === "string")
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function parseReleaseSnapshotInput(value, requestUrl, fallbackProjectId) {
|
|
604
|
+
const input = value && typeof value === "object" ? value : null;
|
|
605
|
+
const projectId = normalizeOptionalText(
|
|
606
|
+
typeof input?.projectId === "string" ? input.projectId : requestUrl.searchParams.get("projectId") ?? fallbackProjectId
|
|
607
|
+
);
|
|
608
|
+
if (!projectId) return null;
|
|
609
|
+
return {
|
|
610
|
+
projectId,
|
|
611
|
+
releaseId: normalizeOptionalText(
|
|
612
|
+
typeof input?.releaseId === "string" ? input.releaseId : requestUrl.searchParams.get("releaseId")
|
|
613
|
+
),
|
|
614
|
+
label: normalizeOptionalText(
|
|
615
|
+
typeof input?.label === "string" ? input.label : requestUrl.searchParams.get("label")
|
|
616
|
+
),
|
|
617
|
+
targets: parseReleaseSnapshotTargets(input?.targets, requestUrl)
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function parseReleaseSnapshotTargets(value, requestUrl) {
|
|
621
|
+
const bodyTargets = Array.isArray(value) ? value.flatMap((target) => {
|
|
622
|
+
const parsed = parseReviewFigmaImageTarget(target);
|
|
623
|
+
return parsed ? [parsed] : [];
|
|
624
|
+
}) : [];
|
|
625
|
+
const queryTargets = requestUrl.searchParams.getAll("target").flatMap((target) => {
|
|
626
|
+
const parsed = parseTargetParam(target);
|
|
627
|
+
return parsed ? [parsed] : [];
|
|
628
|
+
});
|
|
629
|
+
const targetByKey = new Map(
|
|
630
|
+
[...bodyTargets, ...queryTargets].map((target) => [
|
|
631
|
+
getReviewFigmaImageTargetKey(target),
|
|
632
|
+
target
|
|
633
|
+
])
|
|
634
|
+
);
|
|
635
|
+
return Array.from(targetByKey.values());
|
|
636
|
+
}
|
|
637
|
+
function parseReviewFigmaImageTarget(value) {
|
|
638
|
+
if (!value || typeof value !== "object") return null;
|
|
639
|
+
const target = value;
|
|
640
|
+
if (target.type === "route") {
|
|
641
|
+
if (typeof target.projectId !== "string" || typeof target.pageUrl !== "string") {
|
|
642
|
+
return null;
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
type: "route",
|
|
646
|
+
projectId: target.projectId,
|
|
647
|
+
pageUrl: target.pageUrl,
|
|
648
|
+
viewport: target.viewport && typeof target.viewport === "object" ? target.viewport : void 0,
|
|
649
|
+
slot: typeof target.slot === "string" ? target.slot : void 0
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
if (target.type === "figma-node") {
|
|
653
|
+
if (typeof target.projectId !== "string" || typeof target.fileKey !== "string" || typeof target.nodeId !== "string") {
|
|
654
|
+
return null;
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
type: "figma-node",
|
|
658
|
+
projectId: target.projectId,
|
|
659
|
+
fileKey: target.fileKey,
|
|
660
|
+
nodeId: target.nodeId
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
function normalizeEndpoint(endpoint) {
|
|
666
|
+
const normalized = endpoint.trim().replace(/\/+$/, "");
|
|
667
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
668
|
+
}
|
|
669
|
+
function getEndpointItemId(pathname, endpoint) {
|
|
670
|
+
if (!pathname.startsWith(`${endpoint}/`)) return null;
|
|
671
|
+
const value = pathname.slice(endpoint.length + 1);
|
|
672
|
+
if (!value || value.includes("/")) return null;
|
|
673
|
+
return decodeURIComponent(value);
|
|
674
|
+
}
|
|
675
|
+
function isAllowedProjectTarget(target, projectId) {
|
|
676
|
+
return !projectId || target.projectId === projectId;
|
|
677
|
+
}
|
|
678
|
+
function jsonError(status, error) {
|
|
679
|
+
return { status, body: { error } };
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/vite/figma-image-store.ts
|
|
683
|
+
var readReviewFigmaServerToken = (options = {}) => readReviewFigmaToken({
|
|
684
|
+
token: options.token,
|
|
685
|
+
env: options.env ?? getServerEnv(),
|
|
686
|
+
envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
687
|
+
enabled: options.enabled
|
|
688
|
+
});
|
|
689
|
+
var requireReviewFigmaServerToken = (options = {}) => requireReviewFigmaToken({
|
|
690
|
+
token: options.token,
|
|
691
|
+
env: options.env ?? getServerEnv(),
|
|
692
|
+
envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
693
|
+
enabled: options.enabled
|
|
694
|
+
});
|
|
695
|
+
var renderReviewFigmaServerImage = (options) => {
|
|
696
|
+
const { token, env, envKey, enabled, ...renderOptions } = options;
|
|
697
|
+
const explicitToken = typeof token === "string" ? token.trim() : token;
|
|
698
|
+
return renderReviewFigmaImage({
|
|
699
|
+
...renderOptions,
|
|
700
|
+
token: explicitToken || requireReviewFigmaServerToken({ env, envKey, enabled })
|
|
701
|
+
});
|
|
702
|
+
};
|
|
703
|
+
var reviewFigmaImageStore = (options = {}) => {
|
|
704
|
+
let root = "";
|
|
705
|
+
let dataFile = "";
|
|
706
|
+
let assetDir = "";
|
|
707
|
+
let env = {};
|
|
708
|
+
const enabled = options.enabled ?? true;
|
|
709
|
+
const endpoint = normalizeEndpoint(
|
|
710
|
+
options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT
|
|
711
|
+
);
|
|
712
|
+
const assetEndpoint = normalizeEndpoint(
|
|
713
|
+
options.assetEndpoint ?? `${endpoint}/assets`
|
|
714
|
+
);
|
|
715
|
+
return {
|
|
716
|
+
name: "df-web-review-kit-figma-image-store",
|
|
717
|
+
apply: "serve",
|
|
718
|
+
configResolved(config) {
|
|
719
|
+
root = config.root;
|
|
720
|
+
dataFile = path3.resolve(
|
|
721
|
+
root,
|
|
722
|
+
options.dataFile ?? ".df-review/figma-images.json"
|
|
723
|
+
);
|
|
724
|
+
assetDir = options.assetDir ? path3.resolve(root, options.assetDir) : path3.join(path3.dirname(dataFile), "figma-assets");
|
|
725
|
+
env = {
|
|
726
|
+
...loadEnv(config.mode, config.envDir, ""),
|
|
727
|
+
...getServerEnv(),
|
|
728
|
+
...options.env ?? {}
|
|
729
|
+
};
|
|
730
|
+
},
|
|
731
|
+
configureServer(server) {
|
|
732
|
+
if (!enabled) return;
|
|
733
|
+
server.middlewares.use(async (req, res, next) => {
|
|
734
|
+
const requestUrl = new URL(req.url ?? "/", "http://localhost");
|
|
735
|
+
const pathname = requestUrl.pathname;
|
|
736
|
+
if (pathname.startsWith(`${assetEndpoint}/`)) {
|
|
737
|
+
await sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (pathname !== endpoint && !pathname.startsWith(`${endpoint}/`)) {
|
|
741
|
+
next();
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
try {
|
|
745
|
+
const response = await handleReviewFigmaImageStoreRequest({
|
|
746
|
+
dataFile,
|
|
747
|
+
assetDir,
|
|
748
|
+
assetEndpoint,
|
|
749
|
+
endpoint,
|
|
750
|
+
options,
|
|
751
|
+
env,
|
|
752
|
+
pathname,
|
|
753
|
+
requestUrl,
|
|
754
|
+
method: req.method ?? "GET",
|
|
755
|
+
body: await readJsonRequestBody(req),
|
|
756
|
+
requestToken: readRequestFigmaToken(req)
|
|
757
|
+
});
|
|
758
|
+
sendJson(res, response.status, response.body);
|
|
759
|
+
} catch (error) {
|
|
760
|
+
sendJson(res, 500, {
|
|
761
|
+
error: error instanceof Error ? error.message : "Figma image store request failed."
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
};
|
|
768
|
+
function readRequestFigmaToken(req) {
|
|
769
|
+
const value = req.headers?.["x-figma-token"];
|
|
770
|
+
const token = Array.isArray(value) ? value[0] : value;
|
|
771
|
+
return typeof token === "string" ? token.trim() || null : null;
|
|
772
|
+
}
|
|
773
|
+
function getServerEnv() {
|
|
774
|
+
const runtime = globalThis;
|
|
775
|
+
return runtime.process?.env ?? {};
|
|
776
|
+
}
|
|
777
|
+
|
|
1
778
|
// src/vite.ts
|
|
2
779
|
var VIRTUAL_JSX_DEV_RUNTIME_ID = "\0@designfever/web-review-kit/source-locator/jsx-dev-runtime";
|
|
780
|
+
var REVIEW_SOURCE_ENV_DEFINE_KEYS = [
|
|
781
|
+
["__DF_WRK_REVIEW_SOURCE_ROOT__", "VITE_REVIEW_SOURCE_ROOT"],
|
|
782
|
+
["__DF_WRK_REVIEW_SOURCE_EDITOR__", "VITE_REVIEW_SOURCE_EDITOR"],
|
|
783
|
+
[
|
|
784
|
+
"__DF_WRK_REVIEW_SOURCE_URL_TEMPLATE__",
|
|
785
|
+
"VITE_REVIEW_SOURCE_URL_TEMPLATE"
|
|
786
|
+
]
|
|
787
|
+
];
|
|
788
|
+
var createReviewSourceEnvReplacements = (env = {}) => {
|
|
789
|
+
return Object.fromEntries(
|
|
790
|
+
REVIEW_SOURCE_ENV_DEFINE_KEYS.map(([defineKey, envKey]) => [
|
|
791
|
+
defineKey,
|
|
792
|
+
JSON.stringify(env[envKey] ?? "")
|
|
793
|
+
])
|
|
794
|
+
);
|
|
795
|
+
};
|
|
796
|
+
var injectReviewSourceEnv = (code, replacements) => {
|
|
797
|
+
let nextCode = code;
|
|
798
|
+
for (const [defineKey, value] of Object.entries(replacements)) {
|
|
799
|
+
nextCode = nextCode.split(`typeof ${defineKey}`).join(`typeof ${value}`).split(`: ${defineKey}`).join(`: ${value}`);
|
|
800
|
+
}
|
|
801
|
+
return nextCode === code ? null : nextCode;
|
|
802
|
+
};
|
|
3
803
|
var reviewSourceLocator = (options = {}) => {
|
|
4
804
|
let runtimeOptions = createRuntimeOptions(options);
|
|
805
|
+
let sourceEnvReplacements = createReviewSourceEnvReplacements();
|
|
5
806
|
return {
|
|
6
807
|
name: "df-web-review-kit-source-locator",
|
|
7
808
|
enforce: "pre",
|
|
8
809
|
configResolved(config) {
|
|
9
810
|
runtimeOptions = createRuntimeOptions(options, config);
|
|
811
|
+
sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);
|
|
10
812
|
},
|
|
11
813
|
resolveId(id, importer) {
|
|
12
814
|
if (!runtimeOptions.enabled) return null;
|
|
@@ -17,12 +819,17 @@ var reviewSourceLocator = (options = {}) => {
|
|
|
17
819
|
load(id) {
|
|
18
820
|
if (id !== VIRTUAL_JSX_DEV_RUNTIME_ID) return null;
|
|
19
821
|
return createJsxDevRuntime(runtimeOptions);
|
|
822
|
+
},
|
|
823
|
+
transform(code) {
|
|
824
|
+
const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);
|
|
825
|
+
return injectedCode ? { code: injectedCode, map: null } : null;
|
|
20
826
|
}
|
|
21
827
|
};
|
|
22
828
|
};
|
|
23
829
|
var reviewDataLocator = (options = {}) => {
|
|
24
830
|
let root = normalizePath(options.root ?? "");
|
|
25
831
|
let enabled = options.enabled ?? false;
|
|
832
|
+
let sourceEnvReplacements = createReviewSourceEnvReplacements();
|
|
26
833
|
const include = (options.include ?? []).map(createRuntimeMatcher);
|
|
27
834
|
const exclude = (options.exclude ?? ["node_modules", "dist"]).map(
|
|
28
835
|
createRuntimeMatcher
|
|
@@ -37,26 +844,34 @@ var reviewDataLocator = (options = {}) => {
|
|
|
37
844
|
configResolved(config) {
|
|
38
845
|
root = normalizePath(options.root ?? config.root ?? "");
|
|
39
846
|
enabled = options.enabled ?? config.command === "serve";
|
|
847
|
+
sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);
|
|
40
848
|
},
|
|
41
849
|
transform(code, id) {
|
|
850
|
+
const envInjectedCode = injectReviewSourceEnv(
|
|
851
|
+
code,
|
|
852
|
+
sourceEnvReplacements
|
|
853
|
+
);
|
|
854
|
+
const inputCode = envInjectedCode ?? code;
|
|
42
855
|
if (!enabled) return null;
|
|
43
856
|
const file = normalizePath(id.split("?")[0]);
|
|
44
857
|
const relativeFile = root && file.startsWith(root + "/") ? file.slice(root.length + 1) : file;
|
|
45
858
|
if (include.length > 0 && !include.some((m) => matchesPath(m, file, relativeFile)))
|
|
46
|
-
return null;
|
|
47
|
-
if (exclude.some((m) => matchesPath(m, file, relativeFile)))
|
|
859
|
+
return envInjectedCode ? { code: envInjectedCode, map: null } : null;
|
|
860
|
+
if (exclude.some((m) => matchesPath(m, file, relativeFile))) {
|
|
861
|
+
return envInjectedCode ? { code: envInjectedCode, map: null } : null;
|
|
862
|
+
}
|
|
48
863
|
const sourceFile = (options.filePath ?? "relative") === "absolute" ? file : relativeFile;
|
|
49
864
|
const regex = new RegExp(componentSource, "g");
|
|
50
865
|
let changed = false;
|
|
51
|
-
const out =
|
|
866
|
+
const out = inputCode.replace(
|
|
52
867
|
regex,
|
|
53
868
|
(_match, pre, comp, quote, name, offset) => {
|
|
54
|
-
const line =
|
|
869
|
+
const line = inputCode.slice(0, offset + pre.length).split("\n").length;
|
|
55
870
|
changed = true;
|
|
56
871
|
return `${pre}${JSON.stringify(fileKey)}: ${JSON.stringify(sourceFile)}, ${JSON.stringify(lineKey)}: ${line}, ${comp}${quote}${name}${quote}`;
|
|
57
872
|
}
|
|
58
873
|
);
|
|
59
|
-
return changed ? { code: out, map: null } : null;
|
|
874
|
+
return changed || envInjectedCode ? { code: out, map: null } : null;
|
|
60
875
|
}
|
|
61
876
|
};
|
|
62
877
|
};
|
|
@@ -204,7 +1019,16 @@ function normalizePath(value) {
|
|
|
204
1019
|
`;
|
|
205
1020
|
}
|
|
206
1021
|
export {
|
|
1022
|
+
collectReviewFigmaReleaseSnapshot,
|
|
1023
|
+
createReviewFigmaImageApiUrl,
|
|
1024
|
+
createReviewFigmaImagesSnapshot,
|
|
1025
|
+
createReviewFigmaReleaseSnapshot,
|
|
1026
|
+
readReviewFigmaServerToken,
|
|
1027
|
+
renderReviewFigmaImage,
|
|
1028
|
+
renderReviewFigmaServerImage,
|
|
1029
|
+
requireReviewFigmaServerToken,
|
|
207
1030
|
reviewDataLocator,
|
|
1031
|
+
reviewFigmaImageStore,
|
|
208
1032
|
reviewSourceLocator
|
|
209
1033
|
};
|
|
210
1034
|
//# sourceMappingURL=vite.js.map
|