@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.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,23 +17,1138 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/vite.ts
|
|
21
31
|
var vite_exports = {};
|
|
22
32
|
__export(vite_exports, {
|
|
33
|
+
collectReviewFigmaReleaseSnapshot: () => collectReviewFigmaReleaseSnapshot,
|
|
34
|
+
createReviewFigmaImageApiUrl: () => createReviewFigmaImageApiUrl,
|
|
35
|
+
createReviewFigmaImagesSnapshot: () => createReviewFigmaImagesSnapshot,
|
|
36
|
+
createReviewFigmaReleaseSnapshot: () => createReviewFigmaReleaseSnapshot,
|
|
37
|
+
readReviewFigmaServerToken: () => readReviewFigmaServerToken,
|
|
38
|
+
renderReviewFigmaImage: () => renderReviewFigmaImage,
|
|
39
|
+
renderReviewFigmaServerImage: () => renderReviewFigmaServerImage,
|
|
40
|
+
requireReviewFigmaServerToken: () => requireReviewFigmaServerToken,
|
|
23
41
|
reviewDataLocator: () => reviewDataLocator,
|
|
42
|
+
reviewFigmaImageStore: () => reviewFigmaImageStore,
|
|
24
43
|
reviewSourceLocator: () => reviewSourceLocator
|
|
25
44
|
});
|
|
26
45
|
module.exports = __toCommonJS(vite_exports);
|
|
46
|
+
|
|
47
|
+
// src/vite/figma-image-store.ts
|
|
48
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
49
|
+
var import_vite = require("vite");
|
|
50
|
+
|
|
51
|
+
// src/vite/figma-asset.ts
|
|
52
|
+
function parseReviewFigmaImageFormat(value) {
|
|
53
|
+
return value === "webp" || value === "png" || value === "jpg" ? value : void 0;
|
|
54
|
+
}
|
|
55
|
+
function getStoreRenderFormat(renderFormat, imageFormat) {
|
|
56
|
+
if (renderFormat === "jpg" || renderFormat === "png") return renderFormat;
|
|
57
|
+
if (imageFormat === "jpg") return "jpg";
|
|
58
|
+
return "png";
|
|
59
|
+
}
|
|
60
|
+
function getReviewFigmaImageFormatFromMimeType(mimeType) {
|
|
61
|
+
if (mimeType === "image/webp") return "webp";
|
|
62
|
+
if (mimeType === "image/png") return "png";
|
|
63
|
+
if (mimeType === "image/jpeg") return "jpg";
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function normalizeImageMimeType(value) {
|
|
67
|
+
const mimeType = value?.split(";")[0]?.trim().toLowerCase();
|
|
68
|
+
if (mimeType === "image/jpg") return "image/jpeg";
|
|
69
|
+
if (mimeType === "image/jpeg" || mimeType === "image/png" || mimeType === "image/webp") {
|
|
70
|
+
return mimeType;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function createReviewFigmaAssetStorageKey(id, imageFormat) {
|
|
75
|
+
return `${id}.${getReviewFigmaAssetExtension(imageFormat)}`;
|
|
76
|
+
}
|
|
77
|
+
function createReviewFigmaAssetUrl(assetEndpoint, storageKey) {
|
|
78
|
+
return `${assetEndpoint}/${encodeURIComponent(storageKey)}`;
|
|
79
|
+
}
|
|
80
|
+
function getReviewFigmaAssetStorageKeyFromPathname(pathname, assetEndpoint) {
|
|
81
|
+
try {
|
|
82
|
+
const storageKey = decodeURIComponent(
|
|
83
|
+
pathname.slice(assetEndpoint.length + 1)
|
|
84
|
+
);
|
|
85
|
+
return isSafeReviewFigmaAssetStorageKey(storageKey) ? storageKey : null;
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function isSafeReviewFigmaAssetStorageKey(value) {
|
|
91
|
+
return /^figma_[a-z0-9_]+\.(webp|png|jpg)$/.test(value);
|
|
92
|
+
}
|
|
93
|
+
function getReviewFigmaAssetExtension(format) {
|
|
94
|
+
return format === "jpg" ? "jpg" : format;
|
|
95
|
+
}
|
|
96
|
+
function getReviewFigmaAssetMimeType(storageKey) {
|
|
97
|
+
if (storageKey.endsWith(".jpg")) return "image/jpeg";
|
|
98
|
+
if (storageKey.endsWith(".webp")) return "image/webp";
|
|
99
|
+
return "image/png";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/figma/parse.ts
|
|
103
|
+
var FIGMA_NODE_REF_SEPARATOR = "->";
|
|
104
|
+
function parseReviewFigmaNodeRef(value) {
|
|
105
|
+
if (typeof value !== "string") return normalizeReviewFigmaNodeRef(value);
|
|
106
|
+
const input = value.trim();
|
|
107
|
+
if (!input) return null;
|
|
108
|
+
return parseReviewFigmaNodeRefValue(input) ?? parseReviewFigmaUrl(input);
|
|
109
|
+
}
|
|
110
|
+
function requireReviewFigmaNodeRef(value) {
|
|
111
|
+
const ref = parseReviewFigmaNodeRef(value);
|
|
112
|
+
if (!ref) {
|
|
113
|
+
throw new Error("A Figma node link or fileKey->nodeId value is required.");
|
|
114
|
+
}
|
|
115
|
+
return ref;
|
|
116
|
+
}
|
|
117
|
+
function parseReviewFigmaNodeRefValue(value) {
|
|
118
|
+
const [fileKey, nodeId, extra] = value.split(FIGMA_NODE_REF_SEPARATOR).map((part) => part.trim());
|
|
119
|
+
if (!fileKey || !nodeId || extra !== void 0) return null;
|
|
120
|
+
return normalizeReviewFigmaNodeRef({ fileKey, nodeId });
|
|
121
|
+
}
|
|
122
|
+
function parseReviewFigmaUrl(value) {
|
|
123
|
+
let url;
|
|
124
|
+
try {
|
|
125
|
+
url = new URL(value);
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const fileKey = getFigmaFileKey(url);
|
|
130
|
+
const nodeId = normalizeFigmaNodeId(url.searchParams.get("node-id"));
|
|
131
|
+
if (!fileKey || !nodeId) return null;
|
|
132
|
+
return { fileKey, nodeId, sourceUrl: value };
|
|
133
|
+
}
|
|
134
|
+
function getFigmaFileKey(url) {
|
|
135
|
+
if (!/(^|\.)figma\.com$/i.test(url.hostname)) return null;
|
|
136
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
137
|
+
const fileKeyIndex = parts.findIndex(
|
|
138
|
+
(part) => ["design", "file", "proto", "board"].includes(part)
|
|
139
|
+
);
|
|
140
|
+
const fileKey = fileKeyIndex >= 0 ? parts[fileKeyIndex + 1] : null;
|
|
141
|
+
return normalizeFigmaFileKey(fileKey);
|
|
142
|
+
}
|
|
143
|
+
function normalizeReviewFigmaNodeRef(value) {
|
|
144
|
+
const fileKey = normalizeFigmaFileKey(value?.fileKey);
|
|
145
|
+
const nodeId = normalizeFigmaNodeId(value?.nodeId);
|
|
146
|
+
if (!fileKey || !nodeId) return null;
|
|
147
|
+
const sourceUrl = normalizeOptionalString(value?.sourceUrl);
|
|
148
|
+
return sourceUrl ? { fileKey, nodeId, sourceUrl } : { fileKey, nodeId };
|
|
149
|
+
}
|
|
150
|
+
function normalizeFigmaFileKey(value) {
|
|
151
|
+
return normalizeOptionalString(value)?.replace(/[?#].*$/, "") ?? null;
|
|
152
|
+
}
|
|
153
|
+
function normalizeFigmaNodeId(value) {
|
|
154
|
+
const nodeId = normalizeOptionalString(value);
|
|
155
|
+
if (!nodeId) return null;
|
|
156
|
+
if (nodeId.includes(":")) return nodeId;
|
|
157
|
+
return nodeId.replace(/-/g, ":");
|
|
158
|
+
}
|
|
159
|
+
function normalizeOptionalString(value) {
|
|
160
|
+
if (typeof value !== "string") return null;
|
|
161
|
+
return value.trim() || null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/figma/token.ts
|
|
165
|
+
var DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY = "FIGMA_TOKEN";
|
|
166
|
+
var REVIEW_FIGMA_TOKEN_MISSING_CODE = "DFWR_FIGMA_TOKEN_MISSING";
|
|
167
|
+
var ReviewFigmaTokenError = class extends Error {
|
|
168
|
+
constructor(envKey = DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY) {
|
|
169
|
+
super(
|
|
170
|
+
`Figma image rendering requires server env ${envKey}. Set ${envKey} in the dev/server environment; do not expose it as VITE_${envKey}.`
|
|
171
|
+
);
|
|
172
|
+
this.code = REVIEW_FIGMA_TOKEN_MISSING_CODE;
|
|
173
|
+
this.name = "ReviewFigmaTokenError";
|
|
174
|
+
this.envKey = envKey;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
function readReviewFigmaToken(options = {}) {
|
|
178
|
+
if (options.enabled === false) return null;
|
|
179
|
+
const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
180
|
+
return normalizeReviewFigmaToken(options.token ?? options.env?.[envKey]);
|
|
181
|
+
}
|
|
182
|
+
function requireReviewFigmaToken(options = {}) {
|
|
183
|
+
const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
184
|
+
const token = readReviewFigmaToken(options);
|
|
185
|
+
if (!token) throw new ReviewFigmaTokenError(envKey);
|
|
186
|
+
return token;
|
|
187
|
+
}
|
|
188
|
+
function normalizeReviewFigmaToken(value) {
|
|
189
|
+
if (typeof value !== "string") return null;
|
|
190
|
+
return value.trim() || null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/figma/render.ts
|
|
194
|
+
var DEFAULT_FIGMA_API_BASE_URL = "https://api.figma.com";
|
|
195
|
+
async function renderReviewFigmaImage(options) {
|
|
196
|
+
const token = requireReviewFigmaToken({ token: options.token });
|
|
197
|
+
const ref = requireReviewFigmaNodeRef(options.figmaUrl);
|
|
198
|
+
const renderFormat = options.format ?? "png";
|
|
199
|
+
const requestUrl = createReviewFigmaImageApiUrl({
|
|
200
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
201
|
+
fileKey: ref.fileKey,
|
|
202
|
+
nodeId: ref.nodeId,
|
|
203
|
+
format: renderFormat,
|
|
204
|
+
scale: options.scale,
|
|
205
|
+
useAbsoluteBounds: options.useAbsoluteBounds
|
|
206
|
+
});
|
|
207
|
+
const fetchImage = options.fetch ?? globalThis.fetch;
|
|
208
|
+
if (!fetchImage) throw new Error("Figma image rendering requires fetch.");
|
|
209
|
+
const response = await fetchImage(requestUrl, {
|
|
210
|
+
headers: {
|
|
211
|
+
"X-Figma-Token": token
|
|
212
|
+
},
|
|
213
|
+
signal: options.signal
|
|
214
|
+
});
|
|
215
|
+
const body = await response.json().catch(() => null);
|
|
216
|
+
if (!response.ok) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
body?.err || `Figma image render failed with ${response.status}`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const imageUrl = body?.images?.[ref.nodeId];
|
|
222
|
+
if (!imageUrl) {
|
|
223
|
+
throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
fileKey: ref.fileKey,
|
|
227
|
+
nodeId: ref.nodeId,
|
|
228
|
+
figmaUrl: typeof options.figmaUrl === "string" ? options.figmaUrl : options.figmaUrl.sourceUrl,
|
|
229
|
+
imageUrl,
|
|
230
|
+
renderFormat
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function createReviewFigmaImageApiUrl({
|
|
234
|
+
apiBaseUrl = DEFAULT_FIGMA_API_BASE_URL,
|
|
235
|
+
fileKey,
|
|
236
|
+
nodeId,
|
|
237
|
+
format = "png",
|
|
238
|
+
scale,
|
|
239
|
+
useAbsoluteBounds
|
|
240
|
+
}) {
|
|
241
|
+
const url = new URL(`/v1/images/${encodeURIComponent(fileKey)}`, apiBaseUrl);
|
|
242
|
+
url.searchParams.set("ids", nodeId);
|
|
243
|
+
url.searchParams.set("format", format);
|
|
244
|
+
if (typeof scale === "number" && Number.isFinite(scale) && scale > 0) {
|
|
245
|
+
url.searchParams.set("scale", String(scale));
|
|
246
|
+
}
|
|
247
|
+
if (useAbsoluteBounds !== void 0) {
|
|
248
|
+
url.searchParams.set("use_absolute_bounds", String(useAbsoluteBounds));
|
|
249
|
+
}
|
|
250
|
+
return url.toString();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/figma/image.store.ts
|
|
254
|
+
var DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT = "/__dfwr/figma-images";
|
|
255
|
+
function getReviewFigmaImageTargetKey(target) {
|
|
256
|
+
return JSON.stringify(normalizeReviewFigmaImageTarget(target));
|
|
257
|
+
}
|
|
258
|
+
function getReviewFigmaImageMimeType(format) {
|
|
259
|
+
if (format === "jpg") return "image/jpeg";
|
|
260
|
+
if (format === "png") return "image/png";
|
|
261
|
+
return "image/webp";
|
|
262
|
+
}
|
|
263
|
+
function normalizeReviewFigmaImageTarget(target) {
|
|
264
|
+
if (target.type === "figma-node") {
|
|
265
|
+
return {
|
|
266
|
+
type: target.type,
|
|
267
|
+
projectId: target.projectId,
|
|
268
|
+
fileKey: target.fileKey,
|
|
269
|
+
nodeId: target.nodeId
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
type: target.type,
|
|
274
|
+
projectId: target.projectId,
|
|
275
|
+
pageUrl: target.pageUrl,
|
|
276
|
+
slot: target.slot ?? "",
|
|
277
|
+
viewport: target.viewport ? {
|
|
278
|
+
label: target.viewport.label ?? "",
|
|
279
|
+
width: target.viewport.width ?? null,
|
|
280
|
+
height: target.viewport.height ?? null,
|
|
281
|
+
scope: target.viewport.scope ?? ""
|
|
282
|
+
} : null
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/vite/figma-image-store.server.ts
|
|
287
|
+
var import_promises2 = require("fs/promises");
|
|
288
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
289
|
+
|
|
290
|
+
// src/figma/image.snapshot.ts
|
|
291
|
+
function createReviewFigmaImagesSnapshot(images, options = {}) {
|
|
292
|
+
const targetKeys = options.targets?.length ? new Set(options.targets.map(getReviewFigmaImageTargetKey)) : null;
|
|
293
|
+
return images.filter((image) => {
|
|
294
|
+
if (options.projectId && image.projectId !== options.projectId) {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
if (targetKeys && !targetKeys.has(getReviewFigmaImageTargetKey(image.target))) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
}).map(cloneReviewFigmaImage).sort(compareReviewFigmaSnapshotImages);
|
|
302
|
+
}
|
|
303
|
+
function createReviewFigmaReleaseSnapshot({
|
|
304
|
+
images,
|
|
305
|
+
projectId,
|
|
306
|
+
releaseId,
|
|
307
|
+
label,
|
|
308
|
+
createdAt,
|
|
309
|
+
targets
|
|
310
|
+
}) {
|
|
311
|
+
return {
|
|
312
|
+
version: 1,
|
|
313
|
+
projectId,
|
|
314
|
+
...releaseId ? { releaseId } : null,
|
|
315
|
+
...label ? { label } : null,
|
|
316
|
+
createdAt: createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
317
|
+
figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {
|
|
318
|
+
projectId,
|
|
319
|
+
targets
|
|
320
|
+
})
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
async function collectReviewFigmaReleaseSnapshot({
|
|
324
|
+
store,
|
|
325
|
+
targets,
|
|
326
|
+
...snapshotOptions
|
|
327
|
+
}) {
|
|
328
|
+
const imagesByTarget = await Promise.all(
|
|
329
|
+
targets.map((target) => store.listImages(target))
|
|
330
|
+
);
|
|
331
|
+
return createReviewFigmaReleaseSnapshot({
|
|
332
|
+
...snapshotOptions,
|
|
333
|
+
targets,
|
|
334
|
+
images: dedupeReviewFigmaImages(imagesByTarget.flat())
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
function dedupeReviewFigmaImages(images) {
|
|
338
|
+
return Array.from(new Map(images.map((image) => [image.id, image])).values());
|
|
339
|
+
}
|
|
340
|
+
function cloneReviewFigmaImage(image) {
|
|
341
|
+
return {
|
|
342
|
+
...image,
|
|
343
|
+
target: cloneReviewFigmaImageTarget(image.target)
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function cloneReviewFigmaImageTarget(target) {
|
|
347
|
+
if (target.type === "figma-node") {
|
|
348
|
+
return {
|
|
349
|
+
type: target.type,
|
|
350
|
+
projectId: target.projectId,
|
|
351
|
+
fileKey: target.fileKey,
|
|
352
|
+
nodeId: target.nodeId
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
type: target.type,
|
|
357
|
+
projectId: target.projectId,
|
|
358
|
+
pageUrl: target.pageUrl,
|
|
359
|
+
slot: target.slot,
|
|
360
|
+
viewport: target.viewport ? {
|
|
361
|
+
label: target.viewport.label,
|
|
362
|
+
width: target.viewport.width,
|
|
363
|
+
height: target.viewport.height,
|
|
364
|
+
scope: target.viewport.scope
|
|
365
|
+
} : void 0
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
function compareReviewFigmaSnapshotImages(a, b) {
|
|
369
|
+
return a.projectId.localeCompare(b.projectId) || getReviewFigmaImageTargetKey(a.target).localeCompare(
|
|
370
|
+
getReviewFigmaImageTargetKey(b.target)
|
|
371
|
+
) || a.order - b.order || a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/vite/figma-image-store.image.ts
|
|
375
|
+
var import_promises = require("fs/promises");
|
|
376
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
377
|
+
function requireReviewFigmaRequestToken({
|
|
378
|
+
enabled,
|
|
379
|
+
env,
|
|
380
|
+
envKey,
|
|
381
|
+
requestToken,
|
|
382
|
+
token
|
|
383
|
+
}) {
|
|
384
|
+
const tokenEnvKey = envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;
|
|
385
|
+
const figmaToken = readReviewFigmaToken({ token, env, envKey: tokenEnvKey, enabled }) || readReviewFigmaToken({
|
|
386
|
+
token: requestToken,
|
|
387
|
+
env: {},
|
|
388
|
+
envKey: tokenEnvKey,
|
|
389
|
+
enabled
|
|
390
|
+
});
|
|
391
|
+
if (!figmaToken) throw new ReviewFigmaTokenError(tokenEnvKey);
|
|
392
|
+
return figmaToken;
|
|
393
|
+
}
|
|
394
|
+
async function readReviewFigmaNodeName({
|
|
395
|
+
apiBaseUrl,
|
|
396
|
+
enabled,
|
|
397
|
+
env,
|
|
398
|
+
envKey,
|
|
399
|
+
fetchOption,
|
|
400
|
+
fileKey,
|
|
401
|
+
nodeId,
|
|
402
|
+
token,
|
|
403
|
+
requestToken
|
|
404
|
+
}) {
|
|
405
|
+
const figmaToken = requireReviewFigmaRequestToken({
|
|
406
|
+
enabled,
|
|
407
|
+
env,
|
|
408
|
+
envKey,
|
|
409
|
+
requestToken,
|
|
410
|
+
token
|
|
411
|
+
});
|
|
412
|
+
const fetchNode = fetchOption ?? globalThis.fetch;
|
|
413
|
+
if (!fetchNode) throw new Error("Figma node name lookup requires fetch.");
|
|
414
|
+
const response = await fetchNode(
|
|
415
|
+
createReviewFigmaNodeApiUrl({ apiBaseUrl, fileKey, nodeId }),
|
|
416
|
+
{
|
|
417
|
+
headers: {
|
|
418
|
+
"X-Figma-Token": figmaToken
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
const body = await response.json().catch(() => null);
|
|
423
|
+
if (!response.ok) {
|
|
424
|
+
throw new Error(body?.err || `Figma node lookup failed with ${response.status}`);
|
|
425
|
+
}
|
|
426
|
+
const nodes = body?.nodes;
|
|
427
|
+
const node = nodes?.[nodeId] ?? Object.values(nodes ?? {})[0];
|
|
428
|
+
return normalizeOptionalText(node?.document?.name);
|
|
429
|
+
}
|
|
430
|
+
function createReviewFigmaNodeApiUrl({
|
|
431
|
+
apiBaseUrl = "https://api.figma.com",
|
|
432
|
+
fileKey,
|
|
433
|
+
nodeId
|
|
434
|
+
}) {
|
|
435
|
+
const url = new URL(
|
|
436
|
+
`/v1/files/${encodeURIComponent(fileKey)}/nodes`,
|
|
437
|
+
apiBaseUrl
|
|
438
|
+
);
|
|
439
|
+
url.searchParams.set("ids", nodeId);
|
|
440
|
+
return url.toString();
|
|
441
|
+
}
|
|
442
|
+
async function createReviewFigmaImage({
|
|
443
|
+
assetDir,
|
|
444
|
+
assetEndpoint,
|
|
445
|
+
currentImages,
|
|
446
|
+
env,
|
|
447
|
+
input,
|
|
448
|
+
options,
|
|
449
|
+
requestToken
|
|
450
|
+
}) {
|
|
451
|
+
const ref = parseReviewFigmaNodeRef(input.figmaUrl);
|
|
452
|
+
if (!ref) {
|
|
453
|
+
throw new Error("A Figma node copy link or fileKey->nodeId value is required.");
|
|
454
|
+
}
|
|
455
|
+
const id = createReviewFigmaImageId();
|
|
456
|
+
const explicitLabel = normalizeOptionalText(input.label);
|
|
457
|
+
if (input.asset) {
|
|
458
|
+
const cachedAsset2 = await cacheReviewFigmaProvidedImageAsset({
|
|
459
|
+
assetDir,
|
|
460
|
+
assetEndpoint,
|
|
461
|
+
id,
|
|
462
|
+
asset: input.asset,
|
|
463
|
+
options
|
|
464
|
+
});
|
|
465
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
466
|
+
const order2 = typeof input.order === "number" && Number.isFinite(input.order) ? input.order : getNextImageOrder(currentImages, input.target);
|
|
467
|
+
return {
|
|
468
|
+
id,
|
|
469
|
+
projectId: input.target.projectId,
|
|
470
|
+
target: input.target,
|
|
471
|
+
figmaUrl: input.figmaUrl,
|
|
472
|
+
fileKey: ref.fileKey,
|
|
473
|
+
nodeId: ref.nodeId,
|
|
474
|
+
imageUrl: cachedAsset2.imageUrl,
|
|
475
|
+
imageFormat: cachedAsset2.imageFormat,
|
|
476
|
+
mimeType: cachedAsset2.mimeType,
|
|
477
|
+
label: explicitLabel,
|
|
478
|
+
order: order2,
|
|
479
|
+
storageKey: cachedAsset2.storageKey,
|
|
480
|
+
width: input.asset.width,
|
|
481
|
+
height: input.asset.height,
|
|
482
|
+
byteSize: cachedAsset2.byteSize,
|
|
483
|
+
createdAt: now2,
|
|
484
|
+
updatedAt: now2
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const nodeLabelPromise = explicitLabel ? Promise.resolve(void 0) : readReviewFigmaNodeName({
|
|
488
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
489
|
+
enabled: options.enabled,
|
|
490
|
+
env,
|
|
491
|
+
envKey: options.envKey,
|
|
492
|
+
fetchOption: options.fetch,
|
|
493
|
+
fileKey: ref.fileKey,
|
|
494
|
+
nodeId: ref.nodeId,
|
|
495
|
+
token: options.token,
|
|
496
|
+
requestToken
|
|
497
|
+
}).catch(() => void 0);
|
|
498
|
+
const targetImageFormat = input.imageFormat ?? options.imageFormat ?? "webp";
|
|
499
|
+
const renderFormat = getStoreRenderFormat(options.renderFormat, targetImageFormat);
|
|
500
|
+
const rendered = await renderReviewFigmaImage({
|
|
501
|
+
figmaUrl: input.figmaUrl,
|
|
502
|
+
format: renderFormat,
|
|
503
|
+
scale: options.renderScale,
|
|
504
|
+
useAbsoluteBounds: options.useAbsoluteBounds,
|
|
505
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
506
|
+
fetch: options.fetch,
|
|
507
|
+
token: requireReviewFigmaRequestToken({
|
|
508
|
+
enabled: options.enabled,
|
|
509
|
+
env,
|
|
510
|
+
envKey: options.envKey,
|
|
511
|
+
requestToken,
|
|
512
|
+
token: options.token
|
|
513
|
+
})
|
|
514
|
+
});
|
|
515
|
+
const cachedAsset = await cacheReviewFigmaImageAsset({
|
|
516
|
+
assetDir,
|
|
517
|
+
assetEndpoint,
|
|
518
|
+
id,
|
|
519
|
+
imageUrl: rendered.imageUrl,
|
|
520
|
+
options,
|
|
521
|
+
renderFormat,
|
|
522
|
+
targetImageFormat
|
|
523
|
+
});
|
|
524
|
+
const imageFormat = cachedAsset?.imageFormat ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
525
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
526
|
+
const order = typeof input.order === "number" && Number.isFinite(input.order) ? input.order : getNextImageOrder(currentImages, input.target);
|
|
527
|
+
const nodeLabel = await nodeLabelPromise;
|
|
528
|
+
return {
|
|
529
|
+
id,
|
|
530
|
+
projectId: input.target.projectId,
|
|
531
|
+
target: input.target,
|
|
532
|
+
figmaUrl: input.figmaUrl,
|
|
533
|
+
fileKey: rendered.fileKey,
|
|
534
|
+
nodeId: rendered.nodeId,
|
|
535
|
+
imageUrl: cachedAsset?.imageUrl ?? rendered.imageUrl,
|
|
536
|
+
imageFormat,
|
|
537
|
+
mimeType: cachedAsset?.mimeType ?? getReviewFigmaImageMimeType(imageFormat),
|
|
538
|
+
label: explicitLabel ?? nodeLabel,
|
|
539
|
+
order,
|
|
540
|
+
storageKey: cachedAsset?.storageKey,
|
|
541
|
+
byteSize: cachedAsset?.byteSize,
|
|
542
|
+
createdAt: now,
|
|
543
|
+
updatedAt: now
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
async function cacheReviewFigmaProvidedImageAsset({
|
|
547
|
+
assetDir,
|
|
548
|
+
assetEndpoint,
|
|
549
|
+
id,
|
|
550
|
+
asset,
|
|
551
|
+
options
|
|
552
|
+
}) {
|
|
553
|
+
const decodedAsset = decodeReviewFigmaImageAsset(asset);
|
|
554
|
+
const storageKey = createReviewFigmaAssetStorageKey(
|
|
555
|
+
id,
|
|
556
|
+
decodedAsset.imageFormat
|
|
557
|
+
);
|
|
558
|
+
await (0, import_promises.mkdir)(assetDir, { recursive: true });
|
|
559
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(assetDir, storageKey), decodedAsset.data);
|
|
560
|
+
return {
|
|
561
|
+
imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),
|
|
562
|
+
imageFormat: decodedAsset.imageFormat,
|
|
563
|
+
mimeType: decodedAsset.mimeType,
|
|
564
|
+
storageKey,
|
|
565
|
+
byteSize: decodedAsset.data.byteLength
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
function decodeReviewFigmaImageAsset(asset) {
|
|
569
|
+
const mimeType = normalizeImageMimeType(asset.mimeType);
|
|
570
|
+
if (!mimeType) throw new Error("Unsupported Figma image asset MIME type.");
|
|
571
|
+
const imageFormat = getReviewFigmaImageFormatFromMimeType(mimeType) ?? asset.imageFormat;
|
|
572
|
+
const match = /^data:([^;,]+);base64,([a-zA-Z0-9+/=\s]+)$/.exec(
|
|
573
|
+
asset.dataUrl
|
|
574
|
+
);
|
|
575
|
+
if (!match) throw new Error("Valid Figma image asset data URL is required.");
|
|
576
|
+
const dataUrlMimeType = normalizeImageMimeType(match[1]);
|
|
577
|
+
if (dataUrlMimeType && dataUrlMimeType !== mimeType) {
|
|
578
|
+
throw new Error("Figma image asset MIME type mismatch.");
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
data: Buffer.from(match[2].replace(/\s/g, ""), "base64"),
|
|
582
|
+
imageFormat,
|
|
583
|
+
mimeType
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async function cacheReviewFigmaImageAsset({
|
|
587
|
+
assetDir,
|
|
588
|
+
assetEndpoint,
|
|
589
|
+
id,
|
|
590
|
+
imageUrl,
|
|
591
|
+
options,
|
|
592
|
+
renderFormat,
|
|
593
|
+
targetImageFormat
|
|
594
|
+
}) {
|
|
595
|
+
if (options.cacheAssets === false) return null;
|
|
596
|
+
const asset = await downloadReviewFigmaImageAsset({
|
|
597
|
+
fetchOption: options.fetch,
|
|
598
|
+
imageUrl,
|
|
599
|
+
renderFormat,
|
|
600
|
+
targetImageFormat,
|
|
601
|
+
transformAsset: options.transformAsset
|
|
602
|
+
});
|
|
603
|
+
const storageKey = createReviewFigmaAssetStorageKey(id, asset.imageFormat);
|
|
604
|
+
await (0, import_promises.mkdir)(assetDir, { recursive: true });
|
|
605
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(assetDir, storageKey), asset.data);
|
|
606
|
+
return {
|
|
607
|
+
imageUrl: createReviewFigmaAssetUrl(assetEndpoint, storageKey),
|
|
608
|
+
imageFormat: asset.imageFormat,
|
|
609
|
+
mimeType: asset.mimeType,
|
|
610
|
+
storageKey,
|
|
611
|
+
byteSize: asset.data.byteLength
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
async function downloadReviewFigmaImageAsset({
|
|
615
|
+
fetchOption,
|
|
616
|
+
imageUrl,
|
|
617
|
+
renderFormat,
|
|
618
|
+
targetImageFormat,
|
|
619
|
+
transformAsset
|
|
620
|
+
}) {
|
|
621
|
+
const fetchImage = fetchOption ?? globalThis.fetch;
|
|
622
|
+
if (!fetchImage) throw new Error("Figma image caching requires fetch.");
|
|
623
|
+
const response = await fetchImage(imageUrl);
|
|
624
|
+
if (!response.ok) {
|
|
625
|
+
throw new Error(`Figma image download failed with ${response.status}`);
|
|
626
|
+
}
|
|
627
|
+
const sourceMimeType = normalizeImageMimeType(response.headers.get("content-type")) ?? getReviewFigmaImageMimeType(renderFormat === "jpg" ? "jpg" : "png");
|
|
628
|
+
const sourceImageFormat = getReviewFigmaImageFormatFromMimeType(sourceMimeType) ?? (renderFormat === "jpg" ? "jpg" : "png");
|
|
629
|
+
const sourceData = new Uint8Array(await response.arrayBuffer());
|
|
630
|
+
const transformed = transformAsset ? await transformAsset({
|
|
631
|
+
data: sourceData,
|
|
632
|
+
imageFormat: sourceImageFormat,
|
|
633
|
+
mimeType: sourceMimeType,
|
|
634
|
+
targetFormat: targetImageFormat
|
|
635
|
+
}) : null;
|
|
636
|
+
const imageFormat = transformed?.imageFormat ?? sourceImageFormat;
|
|
637
|
+
const mimeType = normalizeImageMimeType(transformed?.mimeType) ?? getReviewFigmaImageMimeType(imageFormat);
|
|
638
|
+
const data = createBufferFromImageData(transformed?.data ?? sourceData);
|
|
639
|
+
return { data, imageFormat, mimeType };
|
|
640
|
+
}
|
|
641
|
+
async function deleteReviewFigmaImageAsset(assetDir, storageKey) {
|
|
642
|
+
if (!storageKey) return;
|
|
643
|
+
if (!isSafeReviewFigmaAssetStorageKey(storageKey)) return;
|
|
644
|
+
await (0, import_promises.rm)(import_node_path.default.join(assetDir, storageKey), { force: true }).catch(() => null);
|
|
645
|
+
}
|
|
646
|
+
function createBufferFromImageData(data) {
|
|
647
|
+
return data instanceof ArrayBuffer ? Buffer.from(new Uint8Array(data)) : Buffer.from(data);
|
|
648
|
+
}
|
|
649
|
+
function listImagesForTarget(images, target) {
|
|
650
|
+
const targetKey = getReviewFigmaImageTargetKey(target);
|
|
651
|
+
return images.filter((image) => getReviewFigmaImageTargetKey(image.target) === targetKey).sort(compareReviewFigmaImages);
|
|
652
|
+
}
|
|
653
|
+
function reorderReviewFigmaImages(images, input) {
|
|
654
|
+
const targetKey = getReviewFigmaImageTargetKey(input.target);
|
|
655
|
+
const orderById = new Map(input.imageIds.map((id, index) => [id, index]));
|
|
656
|
+
const targetImages = listImagesForTarget(images, input.target);
|
|
657
|
+
const nextTargetImages = targetImages.map((image) => ({
|
|
658
|
+
...image,
|
|
659
|
+
order: orderById.get(image.id) ?? input.imageIds.length + image.order,
|
|
660
|
+
updatedAt: orderById.has(image.id) ? (/* @__PURE__ */ new Date()).toISOString() : image.updatedAt
|
|
661
|
+
})).sort(compareReviewFigmaImages);
|
|
662
|
+
const nextTargetImageById = new Map(
|
|
663
|
+
nextTargetImages.map((image, index) => [image.id, { ...image, order: index }])
|
|
664
|
+
);
|
|
665
|
+
const allImages = images.map(
|
|
666
|
+
(image) => getReviewFigmaImageTargetKey(image.target) === targetKey ? nextTargetImageById.get(image.id) ?? image : image
|
|
667
|
+
);
|
|
668
|
+
return {
|
|
669
|
+
allImages,
|
|
670
|
+
targetImages: listImagesForTarget(allImages, input.target)
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function updateReviewFigmaImage(images, id, patch) {
|
|
674
|
+
const index = images.findIndex((image) => image.id === id);
|
|
675
|
+
if (index < 0) return null;
|
|
676
|
+
const nextImage = {
|
|
677
|
+
...images[index],
|
|
678
|
+
label: patch.label === void 0 ? images[index].label : normalizeOptionalText(patch.label),
|
|
679
|
+
order: typeof patch.order === "number" && Number.isFinite(patch.order) ? patch.order : images[index].order,
|
|
680
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
681
|
+
};
|
|
682
|
+
const nextImages = [...images];
|
|
683
|
+
nextImages[index] = nextImage;
|
|
684
|
+
return { image: nextImage, images: nextImages };
|
|
685
|
+
}
|
|
686
|
+
async function readReviewFigmaImageStoreFile(dataFile) {
|
|
687
|
+
try {
|
|
688
|
+
const raw = await (0, import_promises.readFile)(dataFile, "utf8");
|
|
689
|
+
const parsed = JSON.parse(raw);
|
|
690
|
+
return {
|
|
691
|
+
version: 1,
|
|
692
|
+
images: Array.isArray(parsed.images) ? parsed.images.flatMap((image) => isReviewFigmaImage(image) ? [image] : []) : []
|
|
693
|
+
};
|
|
694
|
+
} catch (error) {
|
|
695
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
696
|
+
return { version: 1, images: [] };
|
|
697
|
+
}
|
|
698
|
+
throw error;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
async function writeReviewFigmaImageStoreFile(dataFile, data) {
|
|
702
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(dataFile), { recursive: true });
|
|
703
|
+
await (0, import_promises.writeFile)(
|
|
704
|
+
dataFile,
|
|
705
|
+
`${JSON.stringify({ version: 1, images: data.images }, null, 2)}
|
|
706
|
+
`,
|
|
707
|
+
"utf8"
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
function getNextImageOrder(images, target) {
|
|
711
|
+
const targetImages = listImagesForTarget(images, target);
|
|
712
|
+
return targetImages.length ? Math.max(...targetImages.map((image) => image.order)) + 1 : 0;
|
|
713
|
+
}
|
|
714
|
+
function compareReviewFigmaImages(a, b) {
|
|
715
|
+
return a.order - b.order || a.createdAt.localeCompare(b.createdAt);
|
|
716
|
+
}
|
|
717
|
+
function createReviewFigmaImageId() {
|
|
718
|
+
return `figma_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
719
|
+
}
|
|
720
|
+
function isReviewFigmaImage(value) {
|
|
721
|
+
if (!value || typeof value !== "object") return false;
|
|
722
|
+
const image = value;
|
|
723
|
+
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";
|
|
724
|
+
}
|
|
725
|
+
function isNodeError(error) {
|
|
726
|
+
if (!error || typeof error !== "object") return false;
|
|
727
|
+
return "code" in error;
|
|
728
|
+
}
|
|
729
|
+
function normalizeOptionalText(value) {
|
|
730
|
+
if (typeof value !== "string") return void 0;
|
|
731
|
+
return value.trim() || void 0;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/vite/figma-image-store.server.ts
|
|
735
|
+
async function handleReviewFigmaImageStoreRequest({
|
|
736
|
+
dataFile,
|
|
737
|
+
assetDir,
|
|
738
|
+
assetEndpoint,
|
|
739
|
+
endpoint,
|
|
740
|
+
method,
|
|
741
|
+
options,
|
|
742
|
+
pathname,
|
|
743
|
+
requestUrl,
|
|
744
|
+
body,
|
|
745
|
+
env,
|
|
746
|
+
requestToken
|
|
747
|
+
}) {
|
|
748
|
+
if (method === "OPTIONS") return { status: 204, body: null };
|
|
749
|
+
if ((method === "GET" || method === "POST") && pathname === `${endpoint}/snapshot`) {
|
|
750
|
+
const input = parseReleaseSnapshotInput(body, requestUrl, options.projectId);
|
|
751
|
+
if (!input) return jsonError(400, "valid snapshot input is required.");
|
|
752
|
+
if (options.projectId && input.projectId !== options.projectId) {
|
|
753
|
+
return jsonError(403, "snapshot project is not allowed.");
|
|
754
|
+
}
|
|
755
|
+
if (input.targets.some((target) => !isAllowedProjectTarget(target, options.projectId))) {
|
|
756
|
+
return jsonError(403, "snapshot target project is not allowed.");
|
|
757
|
+
}
|
|
758
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
759
|
+
return {
|
|
760
|
+
status: 200,
|
|
761
|
+
body: createReviewFigmaReleaseSnapshot({
|
|
762
|
+
images: data.images,
|
|
763
|
+
projectId: input.projectId,
|
|
764
|
+
releaseId: input.releaseId,
|
|
765
|
+
label: input.label,
|
|
766
|
+
targets: input.targets.length > 0 ? input.targets : void 0
|
|
767
|
+
})
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (method === "GET" && pathname === endpoint) {
|
|
771
|
+
const target = parseTargetParam(requestUrl.searchParams.get("target"));
|
|
772
|
+
if (!target) return jsonError(400, "target query is required.");
|
|
773
|
+
if (!isAllowedProjectTarget(target, options.projectId)) {
|
|
774
|
+
return { status: 200, body: [] };
|
|
775
|
+
}
|
|
776
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
777
|
+
return { status: 200, body: listImagesForTarget(data.images, target) };
|
|
778
|
+
}
|
|
779
|
+
if (method === "POST" && pathname === endpoint) {
|
|
780
|
+
const input = parseAddImageInput(body);
|
|
781
|
+
if (!input) return jsonError(400, "valid add image input is required.");
|
|
782
|
+
if (!isAllowedProjectTarget(input.target, options.projectId)) {
|
|
783
|
+
return jsonError(403, "target project is not allowed.");
|
|
784
|
+
}
|
|
785
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
786
|
+
const image = await createReviewFigmaImage({
|
|
787
|
+
assetDir,
|
|
788
|
+
assetEndpoint,
|
|
789
|
+
currentImages: data.images,
|
|
790
|
+
env,
|
|
791
|
+
input,
|
|
792
|
+
options,
|
|
793
|
+
requestToken
|
|
794
|
+
});
|
|
795
|
+
data.images = [image, ...data.images];
|
|
796
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
797
|
+
return { status: 201, body: image };
|
|
798
|
+
}
|
|
799
|
+
if (method === "PATCH" && pathname === `${endpoint}/reorder`) {
|
|
800
|
+
const input = parseReorderImagesInput(body);
|
|
801
|
+
if (!input) return jsonError(400, "valid reorder input is required.");
|
|
802
|
+
if (!isAllowedProjectTarget(input.target, options.projectId)) {
|
|
803
|
+
return jsonError(403, "target project is not allowed.");
|
|
804
|
+
}
|
|
805
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
806
|
+
const images = reorderReviewFigmaImages(data.images, input);
|
|
807
|
+
data.images = images.allImages;
|
|
808
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
809
|
+
return { status: 200, body: images.targetImages };
|
|
810
|
+
}
|
|
811
|
+
const id = getEndpointItemId(pathname, endpoint);
|
|
812
|
+
if (id && method === "PATCH") {
|
|
813
|
+
const patch = parseUpdateImageInput(body);
|
|
814
|
+
if (!patch) return jsonError(400, "valid update patch is required.");
|
|
815
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
816
|
+
const result = updateReviewFigmaImage(data.images, id, patch);
|
|
817
|
+
if (!result) return jsonError(404, `Figma image not found: ${id}`);
|
|
818
|
+
if (!isAllowedProjectTarget(result.image.target, options.projectId)) {
|
|
819
|
+
return jsonError(403, "target project is not allowed.");
|
|
820
|
+
}
|
|
821
|
+
data.images = result.images;
|
|
822
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
823
|
+
return { status: 200, body: result.image };
|
|
824
|
+
}
|
|
825
|
+
if (id && method === "DELETE") {
|
|
826
|
+
const data = await readReviewFigmaImageStoreFile(dataFile);
|
|
827
|
+
const image = data.images.find((item) => item.id === id);
|
|
828
|
+
if (!image) return jsonError(404, `Figma image not found: ${id}`);
|
|
829
|
+
if (!isAllowedProjectTarget(image.target, options.projectId)) {
|
|
830
|
+
return jsonError(403, "target project is not allowed.");
|
|
831
|
+
}
|
|
832
|
+
data.images = data.images.filter((item) => item.id !== id);
|
|
833
|
+
await writeReviewFigmaImageStoreFile(dataFile, data);
|
|
834
|
+
await deleteReviewFigmaImageAsset(assetDir, image.storageKey);
|
|
835
|
+
return { status: 200, body: { ok: true } };
|
|
836
|
+
}
|
|
837
|
+
return jsonError(405, "method not allowed.");
|
|
838
|
+
}
|
|
839
|
+
async function readJsonRequestBody(req) {
|
|
840
|
+
if (req.method === "GET" || req.method === "DELETE") return null;
|
|
841
|
+
const chunks = [];
|
|
842
|
+
for await (const chunk of req) {
|
|
843
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
844
|
+
}
|
|
845
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
846
|
+
if (!raw) return null;
|
|
847
|
+
return JSON.parse(raw);
|
|
848
|
+
}
|
|
849
|
+
function sendJson(res, status, body) {
|
|
850
|
+
res.statusCode = status;
|
|
851
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
852
|
+
if (status === 204) {
|
|
853
|
+
res.end();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
res.end(JSON.stringify(body ?? null));
|
|
857
|
+
}
|
|
858
|
+
async function sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname) {
|
|
859
|
+
const storageKey = getReviewFigmaAssetStorageKeyFromPathname(pathname, assetEndpoint);
|
|
860
|
+
if (!storageKey) {
|
|
861
|
+
sendPlainText(res, 400, "Invalid Figma image asset path.");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
try {
|
|
865
|
+
const data = await (0, import_promises2.readFile)(import_node_path2.default.join(assetDir, storageKey));
|
|
866
|
+
res.statusCode = 200;
|
|
867
|
+
res.setHeader("Content-Type", getReviewFigmaAssetMimeType(storageKey));
|
|
868
|
+
res.setHeader("Cache-Control", "private, max-age=31536000, immutable");
|
|
869
|
+
res.end(data);
|
|
870
|
+
} catch (error) {
|
|
871
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
872
|
+
sendPlainText(res, 404, "Figma image asset not found.");
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
sendPlainText(
|
|
876
|
+
res,
|
|
877
|
+
500,
|
|
878
|
+
error instanceof Error ? error.message : "Figma image asset request failed."
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function sendPlainText(res, status, body) {
|
|
883
|
+
res.statusCode = status;
|
|
884
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
885
|
+
res.end(body);
|
|
886
|
+
}
|
|
887
|
+
function parseTargetParam(value) {
|
|
888
|
+
if (!value) return null;
|
|
889
|
+
try {
|
|
890
|
+
return parseReviewFigmaImageTarget(JSON.parse(value));
|
|
891
|
+
} catch {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function parseAddImageInput(value) {
|
|
896
|
+
if (!value || typeof value !== "object") return null;
|
|
897
|
+
const input = value;
|
|
898
|
+
const target = parseReviewFigmaImageTarget(input.target);
|
|
899
|
+
if (!target || typeof input.figmaUrl !== "string") return null;
|
|
900
|
+
return {
|
|
901
|
+
target,
|
|
902
|
+
figmaUrl: input.figmaUrl,
|
|
903
|
+
label: typeof input.label === "string" ? input.label : void 0,
|
|
904
|
+
order: typeof input.order === "number" ? input.order : void 0,
|
|
905
|
+
imageFormat: parseReviewFigmaImageFormat(input.imageFormat),
|
|
906
|
+
asset: parseAddImageAssetInput(input.asset)
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function parseAddImageAssetInput(value) {
|
|
910
|
+
if (!value || typeof value !== "object") return void 0;
|
|
911
|
+
const input = value;
|
|
912
|
+
const imageFormat = parseReviewFigmaImageFormat(input.imageFormat);
|
|
913
|
+
if (!imageFormat || typeof input.dataUrl !== "string" || typeof input.mimeType !== "string") {
|
|
914
|
+
return void 0;
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
dataUrl: input.dataUrl,
|
|
918
|
+
imageFormat,
|
|
919
|
+
mimeType: input.mimeType,
|
|
920
|
+
byteSize: typeof input.byteSize === "number" ? input.byteSize : void 0,
|
|
921
|
+
width: typeof input.width === "number" ? input.width : void 0,
|
|
922
|
+
height: typeof input.height === "number" ? input.height : void 0
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function parseUpdateImageInput(value) {
|
|
926
|
+
if (!value || typeof value !== "object") return null;
|
|
927
|
+
const input = value;
|
|
928
|
+
return {
|
|
929
|
+
label: typeof input.label === "string" ? input.label : void 0,
|
|
930
|
+
order: typeof input.order === "number" ? input.order : void 0
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function parseReorderImagesInput(value) {
|
|
934
|
+
if (!value || typeof value !== "object") return null;
|
|
935
|
+
const input = value;
|
|
936
|
+
const target = parseReviewFigmaImageTarget(input.target);
|
|
937
|
+
if (!target || !Array.isArray(input.imageIds)) return null;
|
|
938
|
+
return {
|
|
939
|
+
target,
|
|
940
|
+
imageIds: input.imageIds.filter((id) => typeof id === "string")
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function parseReleaseSnapshotInput(value, requestUrl, fallbackProjectId) {
|
|
944
|
+
const input = value && typeof value === "object" ? value : null;
|
|
945
|
+
const projectId = normalizeOptionalText(
|
|
946
|
+
typeof input?.projectId === "string" ? input.projectId : requestUrl.searchParams.get("projectId") ?? fallbackProjectId
|
|
947
|
+
);
|
|
948
|
+
if (!projectId) return null;
|
|
949
|
+
return {
|
|
950
|
+
projectId,
|
|
951
|
+
releaseId: normalizeOptionalText(
|
|
952
|
+
typeof input?.releaseId === "string" ? input.releaseId : requestUrl.searchParams.get("releaseId")
|
|
953
|
+
),
|
|
954
|
+
label: normalizeOptionalText(
|
|
955
|
+
typeof input?.label === "string" ? input.label : requestUrl.searchParams.get("label")
|
|
956
|
+
),
|
|
957
|
+
targets: parseReleaseSnapshotTargets(input?.targets, requestUrl)
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
function parseReleaseSnapshotTargets(value, requestUrl) {
|
|
961
|
+
const bodyTargets = Array.isArray(value) ? value.flatMap((target) => {
|
|
962
|
+
const parsed = parseReviewFigmaImageTarget(target);
|
|
963
|
+
return parsed ? [parsed] : [];
|
|
964
|
+
}) : [];
|
|
965
|
+
const queryTargets = requestUrl.searchParams.getAll("target").flatMap((target) => {
|
|
966
|
+
const parsed = parseTargetParam(target);
|
|
967
|
+
return parsed ? [parsed] : [];
|
|
968
|
+
});
|
|
969
|
+
const targetByKey = new Map(
|
|
970
|
+
[...bodyTargets, ...queryTargets].map((target) => [
|
|
971
|
+
getReviewFigmaImageTargetKey(target),
|
|
972
|
+
target
|
|
973
|
+
])
|
|
974
|
+
);
|
|
975
|
+
return Array.from(targetByKey.values());
|
|
976
|
+
}
|
|
977
|
+
function parseReviewFigmaImageTarget(value) {
|
|
978
|
+
if (!value || typeof value !== "object") return null;
|
|
979
|
+
const target = value;
|
|
980
|
+
if (target.type === "route") {
|
|
981
|
+
if (typeof target.projectId !== "string" || typeof target.pageUrl !== "string") {
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
return {
|
|
985
|
+
type: "route",
|
|
986
|
+
projectId: target.projectId,
|
|
987
|
+
pageUrl: target.pageUrl,
|
|
988
|
+
viewport: target.viewport && typeof target.viewport === "object" ? target.viewport : void 0,
|
|
989
|
+
slot: typeof target.slot === "string" ? target.slot : void 0
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
if (target.type === "figma-node") {
|
|
993
|
+
if (typeof target.projectId !== "string" || typeof target.fileKey !== "string" || typeof target.nodeId !== "string") {
|
|
994
|
+
return null;
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
type: "figma-node",
|
|
998
|
+
projectId: target.projectId,
|
|
999
|
+
fileKey: target.fileKey,
|
|
1000
|
+
nodeId: target.nodeId
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
function normalizeEndpoint(endpoint) {
|
|
1006
|
+
const normalized = endpoint.trim().replace(/\/+$/, "");
|
|
1007
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
1008
|
+
}
|
|
1009
|
+
function getEndpointItemId(pathname, endpoint) {
|
|
1010
|
+
if (!pathname.startsWith(`${endpoint}/`)) return null;
|
|
1011
|
+
const value = pathname.slice(endpoint.length + 1);
|
|
1012
|
+
if (!value || value.includes("/")) return null;
|
|
1013
|
+
return decodeURIComponent(value);
|
|
1014
|
+
}
|
|
1015
|
+
function isAllowedProjectTarget(target, projectId) {
|
|
1016
|
+
return !projectId || target.projectId === projectId;
|
|
1017
|
+
}
|
|
1018
|
+
function jsonError(status, error) {
|
|
1019
|
+
return { status, body: { error } };
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/vite/figma-image-store.ts
|
|
1023
|
+
var readReviewFigmaServerToken = (options = {}) => readReviewFigmaToken({
|
|
1024
|
+
token: options.token,
|
|
1025
|
+
env: options.env ?? getServerEnv(),
|
|
1026
|
+
envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
1027
|
+
enabled: options.enabled
|
|
1028
|
+
});
|
|
1029
|
+
var requireReviewFigmaServerToken = (options = {}) => requireReviewFigmaToken({
|
|
1030
|
+
token: options.token,
|
|
1031
|
+
env: options.env ?? getServerEnv(),
|
|
1032
|
+
envKey: options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
1033
|
+
enabled: options.enabled
|
|
1034
|
+
});
|
|
1035
|
+
var renderReviewFigmaServerImage = (options) => {
|
|
1036
|
+
const { token, env, envKey, enabled, ...renderOptions } = options;
|
|
1037
|
+
const explicitToken = typeof token === "string" ? token.trim() : token;
|
|
1038
|
+
return renderReviewFigmaImage({
|
|
1039
|
+
...renderOptions,
|
|
1040
|
+
token: explicitToken || requireReviewFigmaServerToken({ env, envKey, enabled })
|
|
1041
|
+
});
|
|
1042
|
+
};
|
|
1043
|
+
var reviewFigmaImageStore = (options = {}) => {
|
|
1044
|
+
let root = "";
|
|
1045
|
+
let dataFile = "";
|
|
1046
|
+
let assetDir = "";
|
|
1047
|
+
let env = {};
|
|
1048
|
+
const enabled = options.enabled ?? true;
|
|
1049
|
+
const endpoint = normalizeEndpoint(
|
|
1050
|
+
options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT
|
|
1051
|
+
);
|
|
1052
|
+
const assetEndpoint = normalizeEndpoint(
|
|
1053
|
+
options.assetEndpoint ?? `${endpoint}/assets`
|
|
1054
|
+
);
|
|
1055
|
+
return {
|
|
1056
|
+
name: "df-web-review-kit-figma-image-store",
|
|
1057
|
+
apply: "serve",
|
|
1058
|
+
configResolved(config) {
|
|
1059
|
+
root = config.root;
|
|
1060
|
+
dataFile = import_node_path3.default.resolve(
|
|
1061
|
+
root,
|
|
1062
|
+
options.dataFile ?? ".df-review/figma-images.json"
|
|
1063
|
+
);
|
|
1064
|
+
assetDir = options.assetDir ? import_node_path3.default.resolve(root, options.assetDir) : import_node_path3.default.join(import_node_path3.default.dirname(dataFile), "figma-assets");
|
|
1065
|
+
env = {
|
|
1066
|
+
...(0, import_vite.loadEnv)(config.mode, config.envDir, ""),
|
|
1067
|
+
...getServerEnv(),
|
|
1068
|
+
...options.env ?? {}
|
|
1069
|
+
};
|
|
1070
|
+
},
|
|
1071
|
+
configureServer(server) {
|
|
1072
|
+
if (!enabled) return;
|
|
1073
|
+
server.middlewares.use(async (req, res, next) => {
|
|
1074
|
+
const requestUrl = new URL(req.url ?? "/", "http://localhost");
|
|
1075
|
+
const pathname = requestUrl.pathname;
|
|
1076
|
+
if (pathname.startsWith(`${assetEndpoint}/`)) {
|
|
1077
|
+
await sendReviewFigmaAsset(res, assetDir, assetEndpoint, pathname);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (pathname !== endpoint && !pathname.startsWith(`${endpoint}/`)) {
|
|
1081
|
+
next();
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
try {
|
|
1085
|
+
const response = await handleReviewFigmaImageStoreRequest({
|
|
1086
|
+
dataFile,
|
|
1087
|
+
assetDir,
|
|
1088
|
+
assetEndpoint,
|
|
1089
|
+
endpoint,
|
|
1090
|
+
options,
|
|
1091
|
+
env,
|
|
1092
|
+
pathname,
|
|
1093
|
+
requestUrl,
|
|
1094
|
+
method: req.method ?? "GET",
|
|
1095
|
+
body: await readJsonRequestBody(req),
|
|
1096
|
+
requestToken: readRequestFigmaToken(req)
|
|
1097
|
+
});
|
|
1098
|
+
sendJson(res, response.status, response.body);
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
sendJson(res, 500, {
|
|
1101
|
+
error: error instanceof Error ? error.message : "Figma image store request failed."
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
};
|
|
1108
|
+
function readRequestFigmaToken(req) {
|
|
1109
|
+
const value = req.headers?.["x-figma-token"];
|
|
1110
|
+
const token = Array.isArray(value) ? value[0] : value;
|
|
1111
|
+
return typeof token === "string" ? token.trim() || null : null;
|
|
1112
|
+
}
|
|
1113
|
+
function getServerEnv() {
|
|
1114
|
+
const runtime = globalThis;
|
|
1115
|
+
return runtime.process?.env ?? {};
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/vite.ts
|
|
27
1119
|
var VIRTUAL_JSX_DEV_RUNTIME_ID = "\0@designfever/web-review-kit/source-locator/jsx-dev-runtime";
|
|
1120
|
+
var REVIEW_SOURCE_ENV_DEFINE_KEYS = [
|
|
1121
|
+
["__DF_WRK_REVIEW_SOURCE_ROOT__", "VITE_REVIEW_SOURCE_ROOT"],
|
|
1122
|
+
["__DF_WRK_REVIEW_SOURCE_EDITOR__", "VITE_REVIEW_SOURCE_EDITOR"],
|
|
1123
|
+
[
|
|
1124
|
+
"__DF_WRK_REVIEW_SOURCE_URL_TEMPLATE__",
|
|
1125
|
+
"VITE_REVIEW_SOURCE_URL_TEMPLATE"
|
|
1126
|
+
]
|
|
1127
|
+
];
|
|
1128
|
+
var createReviewSourceEnvReplacements = (env = {}) => {
|
|
1129
|
+
return Object.fromEntries(
|
|
1130
|
+
REVIEW_SOURCE_ENV_DEFINE_KEYS.map(([defineKey, envKey]) => [
|
|
1131
|
+
defineKey,
|
|
1132
|
+
JSON.stringify(env[envKey] ?? "")
|
|
1133
|
+
])
|
|
1134
|
+
);
|
|
1135
|
+
};
|
|
1136
|
+
var injectReviewSourceEnv = (code, replacements) => {
|
|
1137
|
+
let nextCode = code;
|
|
1138
|
+
for (const [defineKey, value] of Object.entries(replacements)) {
|
|
1139
|
+
nextCode = nextCode.split(`typeof ${defineKey}`).join(`typeof ${value}`).split(`: ${defineKey}`).join(`: ${value}`);
|
|
1140
|
+
}
|
|
1141
|
+
return nextCode === code ? null : nextCode;
|
|
1142
|
+
};
|
|
28
1143
|
var reviewSourceLocator = (options = {}) => {
|
|
29
1144
|
let runtimeOptions = createRuntimeOptions(options);
|
|
1145
|
+
let sourceEnvReplacements = createReviewSourceEnvReplacements();
|
|
30
1146
|
return {
|
|
31
1147
|
name: "df-web-review-kit-source-locator",
|
|
32
1148
|
enforce: "pre",
|
|
33
1149
|
configResolved(config) {
|
|
34
1150
|
runtimeOptions = createRuntimeOptions(options, config);
|
|
1151
|
+
sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);
|
|
35
1152
|
},
|
|
36
1153
|
resolveId(id, importer) {
|
|
37
1154
|
if (!runtimeOptions.enabled) return null;
|
|
@@ -42,12 +1159,17 @@ var reviewSourceLocator = (options = {}) => {
|
|
|
42
1159
|
load(id) {
|
|
43
1160
|
if (id !== VIRTUAL_JSX_DEV_RUNTIME_ID) return null;
|
|
44
1161
|
return createJsxDevRuntime(runtimeOptions);
|
|
1162
|
+
},
|
|
1163
|
+
transform(code) {
|
|
1164
|
+
const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);
|
|
1165
|
+
return injectedCode ? { code: injectedCode, map: null } : null;
|
|
45
1166
|
}
|
|
46
1167
|
};
|
|
47
1168
|
};
|
|
48
1169
|
var reviewDataLocator = (options = {}) => {
|
|
49
1170
|
let root = normalizePath(options.root ?? "");
|
|
50
1171
|
let enabled = options.enabled ?? false;
|
|
1172
|
+
let sourceEnvReplacements = createReviewSourceEnvReplacements();
|
|
51
1173
|
const include = (options.include ?? []).map(createRuntimeMatcher);
|
|
52
1174
|
const exclude = (options.exclude ?? ["node_modules", "dist"]).map(
|
|
53
1175
|
createRuntimeMatcher
|
|
@@ -62,26 +1184,34 @@ var reviewDataLocator = (options = {}) => {
|
|
|
62
1184
|
configResolved(config) {
|
|
63
1185
|
root = normalizePath(options.root ?? config.root ?? "");
|
|
64
1186
|
enabled = options.enabled ?? config.command === "serve";
|
|
1187
|
+
sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);
|
|
65
1188
|
},
|
|
66
1189
|
transform(code, id) {
|
|
1190
|
+
const envInjectedCode = injectReviewSourceEnv(
|
|
1191
|
+
code,
|
|
1192
|
+
sourceEnvReplacements
|
|
1193
|
+
);
|
|
1194
|
+
const inputCode = envInjectedCode ?? code;
|
|
67
1195
|
if (!enabled) return null;
|
|
68
1196
|
const file = normalizePath(id.split("?")[0]);
|
|
69
1197
|
const relativeFile = root && file.startsWith(root + "/") ? file.slice(root.length + 1) : file;
|
|
70
1198
|
if (include.length > 0 && !include.some((m) => matchesPath(m, file, relativeFile)))
|
|
71
|
-
return null;
|
|
72
|
-
if (exclude.some((m) => matchesPath(m, file, relativeFile)))
|
|
1199
|
+
return envInjectedCode ? { code: envInjectedCode, map: null } : null;
|
|
1200
|
+
if (exclude.some((m) => matchesPath(m, file, relativeFile))) {
|
|
1201
|
+
return envInjectedCode ? { code: envInjectedCode, map: null } : null;
|
|
1202
|
+
}
|
|
73
1203
|
const sourceFile = (options.filePath ?? "relative") === "absolute" ? file : relativeFile;
|
|
74
1204
|
const regex = new RegExp(componentSource, "g");
|
|
75
1205
|
let changed = false;
|
|
76
|
-
const out =
|
|
1206
|
+
const out = inputCode.replace(
|
|
77
1207
|
regex,
|
|
78
1208
|
(_match, pre, comp, quote, name, offset) => {
|
|
79
|
-
const line =
|
|
1209
|
+
const line = inputCode.slice(0, offset + pre.length).split("\n").length;
|
|
80
1210
|
changed = true;
|
|
81
1211
|
return `${pre}${JSON.stringify(fileKey)}: ${JSON.stringify(sourceFile)}, ${JSON.stringify(lineKey)}: ${line}, ${comp}${quote}${name}${quote}`;
|
|
82
1212
|
}
|
|
83
1213
|
);
|
|
84
|
-
return changed ? { code: out, map: null } : null;
|
|
1214
|
+
return changed || envInjectedCode ? { code: out, map: null } : null;
|
|
85
1215
|
}
|
|
86
1216
|
};
|
|
87
1217
|
};
|
|
@@ -230,7 +1360,16 @@ function normalizePath(value) {
|
|
|
230
1360
|
}
|
|
231
1361
|
// Annotate the CommonJS export names for ESM import in node:
|
|
232
1362
|
0 && (module.exports = {
|
|
1363
|
+
collectReviewFigmaReleaseSnapshot,
|
|
1364
|
+
createReviewFigmaImageApiUrl,
|
|
1365
|
+
createReviewFigmaImagesSnapshot,
|
|
1366
|
+
createReviewFigmaReleaseSnapshot,
|
|
1367
|
+
readReviewFigmaServerToken,
|
|
1368
|
+
renderReviewFigmaImage,
|
|
1369
|
+
renderReviewFigmaServerImage,
|
|
1370
|
+
requireReviewFigmaServerToken,
|
|
233
1371
|
reviewDataLocator,
|
|
1372
|
+
reviewFigmaImageStore,
|
|
234
1373
|
reviewSourceLocator
|
|
235
1374
|
});
|
|
236
1375
|
//# sourceMappingURL=vite.cjs.map
|