@01.works/visual-review 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -41
- package/dist/generated-source-context-C6kocvPg.js +1 -0
- package/dist/hosted-runtime-DHzdpdSj.js +13 -0
- package/dist/index.js +1 -1
- package/dist/{install-CyJ52cJi.js → install-x8QQBGO-.js} +1 -1
- package/dist/next-config.d.ts +4 -14
- package/dist/next-config.js +25 -204
- package/dist/react.js +1 -1
- package/dist/{source-instrumentation-transform-BwKYKlp0.js → source-instrumentation-transform-qmiVeKpI.js} +9 -14
- package/dist/source-loader.js +1 -29
- package/dist/vite.d.ts +5 -13
- package/dist/vite.js +6 -81
- package/package.json +1 -1
- package/dist/generated-source-context-9dbnOHdW.js +0 -1
- package/dist/hosted-runtime-CIvL-1-W.js +0 -13
- package/dist/source-index-BdWeiNHZ.js +0 -1398
- package/dist/{build-identity-CgoY3-jy.js → build-identity-DebYe8Qe.js} +1 -1
package/dist/next-config.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import { t as resolveVisualReviewBuildIdentity } from "./build-identity-
|
|
2
|
-
import { a as visualReviewSourceIndexFilename, c as writeNextVisualReviewSourceIndex, d as uploadNextSourceMaps, i as nextSourceIndexUrl, u as cleanupNextSourceMapOutput } from "./source-index-BdWeiNHZ.js";
|
|
1
|
+
import { t as resolveVisualReviewBuildIdentity } from "./build-identity-DebYe8Qe.js";
|
|
3
2
|
import { randomBytes } from "node:crypto";
|
|
4
|
-
import {
|
|
5
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
7
4
|
import { fileURLToPath } from "node:url";
|
|
8
5
|
//#region src/next-config.ts
|
|
9
6
|
const BUILD_ID_LITERAL = "VISUAL_REVIEW_PUBLIC_BUILD_ID";
|
|
10
7
|
const GIT_COMMIT_LITERAL = "VISUAL_REVIEW_PUBLIC_GIT_COMMIT";
|
|
11
8
|
const BUILD_MODE_LITERAL = "VISUAL_REVIEW_PUBLIC_BUILD_MODE";
|
|
12
9
|
const NEXT_BUILD_IDENTITY_RECORD = "VISUAL_REVIEW_INTERNAL_NEXT_BUILD_IDENTITY";
|
|
13
|
-
const NEXT_MARKER_SALT_PATH = "VISUAL_REVIEW_INTERNAL_NEXT_MARKER_SALT_PATH";
|
|
14
10
|
const FULL_GIT_COMMIT = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u;
|
|
15
11
|
const PUBLIC_BUILD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u;
|
|
16
12
|
const DEPLOYMENT_VARIANT = /^[0-9a-f]{32}$/u;
|
|
17
13
|
const GIT_FALLBACK_BUILD_ID = /^(git:([0-9a-f]{40}|[0-9a-f]{64}):deploy:([0-9a-f]{32}))$/u;
|
|
18
14
|
const MAX_NEXT_BUILD_IDENTITY_RECORD_BYTES = 512;
|
|
19
|
-
const trackedMarkerSaltPaths = /* @__PURE__ */ new Set();
|
|
20
|
-
const VISUAL_REVIEW_HOOK = Symbol.for("01works.visual-review.next-source-map-hook");
|
|
21
15
|
const VISUAL_REVIEW_CONFIG = Symbol.for("01works.visual-review.next-config");
|
|
22
16
|
/**
|
|
23
17
|
* Next evaluates next.config again in compiler workers. A Git fallback contains
|
|
@@ -90,24 +84,21 @@ function parseNextBuildIdentityRecord(value) {
|
|
|
90
84
|
}
|
|
91
85
|
function withVisualReview(nextConfig = {}, options = {}) {
|
|
92
86
|
const identity = resolveNextVisualReviewBuildIdentity(options, process.env);
|
|
93
|
-
const sourceMapUpload = resolveSourceMapUpload(options.sourceMaps, process.env);
|
|
94
87
|
const sourceMetadataEnabled = options.sourceMetadata ?? true;
|
|
95
|
-
const sourceMarkerSalt = sourceMetadataEnabled ? resolveBuildLocalMarkerSalt(process.env) : void 0;
|
|
96
88
|
const injected = {
|
|
97
89
|
[BUILD_ID_LITERAL]: identity.buildId,
|
|
98
90
|
[GIT_COMMIT_LITERAL]: identity.gitCommit ?? "",
|
|
99
|
-
[BUILD_MODE_LITERAL]:
|
|
91
|
+
[BUILD_MODE_LITERAL]: inferReleaseMode(process.env)
|
|
100
92
|
};
|
|
101
93
|
if (typeof nextConfig === "function") return function visualReviewNextConfig(...arguments_) {
|
|
102
94
|
const resolved = nextConfig.apply(this, arguments_);
|
|
103
|
-
return isPromiseLike(resolved) ? resolved.then((config) => configureNext(config, injected,
|
|
95
|
+
return isPromiseLike(resolved) ? resolved.then((config) => configureNext(config, injected, sourceMetadataEnabled)) : configureNext(resolved, injected, sourceMetadataEnabled);
|
|
104
96
|
};
|
|
105
|
-
return configureNext(nextConfig, injected,
|
|
97
|
+
return configureNext(nextConfig, injected, sourceMetadataEnabled);
|
|
106
98
|
}
|
|
107
|
-
function configureNext(nextConfig, injected,
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
assertReservedLiterals(nextConfig.env, injected, previouslyWrapped);
|
|
99
|
+
function configureNext(nextConfig, injected, sourceMetadataEnabled) {
|
|
100
|
+
const previousBundlerState = getVisualReviewBundlerState(nextConfig);
|
|
101
|
+
assertReservedLiterals(nextConfig.env, injected, previousBundlerState !== void 0);
|
|
111
102
|
let configured = {
|
|
112
103
|
...nextConfig,
|
|
113
104
|
env: {
|
|
@@ -115,84 +106,19 @@ function configureNext(nextConfig, injected, identity, sourceMapUpload, sourceMe
|
|
|
115
106
|
...injected
|
|
116
107
|
}
|
|
117
108
|
};
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const compiler = sourceConfigured.compiler ?? {};
|
|
126
|
-
return markVisualReviewConfig({
|
|
127
|
-
...sourceConfigured,
|
|
128
|
-
...sourceMapUpload ? { productionBrowserSourceMaps: true } : {},
|
|
129
|
-
...sourceMapUpload ? { experimental: {
|
|
130
|
-
...sourceConfigured.experimental,
|
|
131
|
-
serverSourceMaps: true
|
|
132
|
-
} } : {},
|
|
133
|
-
compiler: {
|
|
134
|
-
...compiler,
|
|
135
|
-
runAfterProductionCompile: composeProductionCompileHook(hookMetadata.originalHook, identity, sourceMapUpload, sourceMetadata, nextConfig.basePath, nextConfig.assetPrefix, hookMetadata)
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
function composeProductionCompileHook(existingHook, identity, sourceMapUpload, sourceMetadata, basePath, assetPrefix, metadata) {
|
|
140
|
-
const hook = async function visualReviewAfterProductionCompile(metadata) {
|
|
141
|
-
if (sourceMetadata) removeBuildLocalMarkerSalt(sourceMetadata.saltPath);
|
|
142
|
-
if (existingHook) try {
|
|
143
|
-
await existingHook.call(this, metadata);
|
|
144
|
-
} catch (existingError) {
|
|
145
|
-
if (!sourceMapUpload) throw existingError;
|
|
146
|
-
try {
|
|
147
|
-
await cleanupNextSourceMapOutput(metadata.projectDir, metadata.distDir);
|
|
148
|
-
} catch (cleanupError) {
|
|
149
|
-
throw new AggregateError([existingError, cleanupError], "Next.js production compile hook failed and Visual Review could not clean source maps");
|
|
150
|
-
}
|
|
151
|
-
throw existingError;
|
|
152
|
-
}
|
|
153
|
-
if (sourceMetadata) try {
|
|
154
|
-
await writeNextVisualReviewSourceIndex({
|
|
155
|
-
projectDir: metadata.projectDir,
|
|
156
|
-
distDir: metadata.distDir,
|
|
157
|
-
filename: sourceMetadata.filename,
|
|
158
|
-
...basePath ? { basePath } : {},
|
|
159
|
-
...assetPrefix ? { assetPrefix } : {}
|
|
160
|
-
});
|
|
161
|
-
} catch (sourceIndexError) {
|
|
162
|
-
if (!sourceMapUpload) throw sourceIndexError;
|
|
163
|
-
try {
|
|
164
|
-
await cleanupNextSourceMapOutput(metadata.projectDir, metadata.distDir);
|
|
165
|
-
} catch (cleanupError) {
|
|
166
|
-
throw new AggregateError([sourceIndexError, cleanupError], "Visual Review source index failed and Next.js source maps could not be cleaned");
|
|
167
|
-
}
|
|
168
|
-
throw sourceIndexError;
|
|
169
|
-
}
|
|
170
|
-
if (!sourceMapUpload) return;
|
|
171
|
-
await uploadNextSourceMaps({
|
|
172
|
-
projectRoot: metadata.projectDir,
|
|
173
|
-
distDir: metadata.distDir,
|
|
174
|
-
token: sourceMapUpload.token,
|
|
175
|
-
...sourceMapUpload.serviceUrl ? { serviceUrl: sourceMapUpload.serviceUrl } : {},
|
|
176
|
-
buildId: identity.buildId,
|
|
177
|
-
gitCommit: identity.gitCommit,
|
|
178
|
-
mode: sourceMapUpload.mode,
|
|
179
|
-
...basePath ? { basePath } : {},
|
|
180
|
-
...assetPrefix ? { assetPrefix } : {}
|
|
181
|
-
});
|
|
182
|
-
};
|
|
183
|
-
Object.defineProperty(hook, VISUAL_REVIEW_HOOK, { value: metadata });
|
|
184
|
-
return hook;
|
|
109
|
+
if (previousBundlerState) {
|
|
110
|
+
restoreProperty(configured, "turbopack", previousBundlerState.turbopack);
|
|
111
|
+
restoreProperty(configured, "webpack", previousBundlerState.webpack);
|
|
112
|
+
}
|
|
113
|
+
const bundlerState = previousBundlerState ?? captureBundlerState(nextConfig);
|
|
114
|
+
if (!sourceMetadataEnabled) return markVisualReviewConfig(configured, bundlerState);
|
|
115
|
+
return markVisualReviewConfig(configureNextSourceMetadata(configured, resolveNextSourceMetadata()), bundlerState);
|
|
185
116
|
}
|
|
186
|
-
function
|
|
187
|
-
return
|
|
117
|
+
function getVisualReviewBundlerState(config) {
|
|
118
|
+
return config[VISUAL_REVIEW_CONFIG];
|
|
188
119
|
}
|
|
189
|
-
function
|
|
120
|
+
function captureBundlerState(nextConfig) {
|
|
190
121
|
return {
|
|
191
|
-
originalHook: nextConfig.compiler?.runAfterProductionCompile,
|
|
192
|
-
compilerPresent: Object.prototype.hasOwnProperty.call(nextConfig, "compiler"),
|
|
193
|
-
experimentalPresent: Object.prototype.hasOwnProperty.call(nextConfig, "experimental"),
|
|
194
|
-
productionBrowserSourceMaps: propertyState(nextConfig, "productionBrowserSourceMaps"),
|
|
195
|
-
serverSourceMaps: propertyState(nextConfig.experimental, "serverSourceMaps"),
|
|
196
122
|
turbopack: propertyState(nextConfig, "turbopack"),
|
|
197
123
|
webpack: propertyState(nextConfig, "webpack")
|
|
198
124
|
};
|
|
@@ -203,97 +129,17 @@ function propertyState(value, key) {
|
|
|
203
129
|
value: value?.[key]
|
|
204
130
|
};
|
|
205
131
|
}
|
|
206
|
-
function restoreSourceMapConfiguration(configured, metadata) {
|
|
207
|
-
if (!metadata) return configured;
|
|
208
|
-
const restored = { ...configured };
|
|
209
|
-
if (metadata.productionBrowserSourceMaps.present) restored.productionBrowserSourceMaps = metadata.productionBrowserSourceMaps.value;
|
|
210
|
-
else delete restored.productionBrowserSourceMaps;
|
|
211
|
-
const experimental = { ...restored.experimental ?? {} };
|
|
212
|
-
if (metadata.serverSourceMaps.present) experimental.serverSourceMaps = metadata.serverSourceMaps.value;
|
|
213
|
-
else delete experimental.serverSourceMaps;
|
|
214
|
-
if (!metadata.experimentalPresent && Object.keys(experimental).length === 0) delete restored.experimental;
|
|
215
|
-
else restored.experimental = experimental;
|
|
216
|
-
const compiler = { ...restored.compiler ?? {} };
|
|
217
|
-
if (metadata.originalHook) compiler.runAfterProductionCompile = metadata.originalHook;
|
|
218
|
-
else delete compiler.runAfterProductionCompile;
|
|
219
|
-
if (!metadata.compilerPresent && Object.keys(compiler).length === 0) delete restored.compiler;
|
|
220
|
-
else restored.compiler = compiler;
|
|
221
|
-
restoreProperty(restored, "turbopack", metadata.turbopack);
|
|
222
|
-
restoreProperty(restored, "webpack", metadata.webpack);
|
|
223
|
-
return restored;
|
|
224
|
-
}
|
|
225
132
|
function restoreProperty(target, key, state) {
|
|
226
133
|
if (state.present) target[key] = state.value;
|
|
227
134
|
else delete target[key];
|
|
228
135
|
}
|
|
229
|
-
function resolveNextSourceMetadata(
|
|
230
|
-
if (!markerSalt) throw new Error("Visual Review source marker salt is unavailable");
|
|
231
|
-
const filename = visualReviewSourceIndexFilename(identity.buildId, markerSalt.salt);
|
|
136
|
+
function resolveNextSourceMetadata() {
|
|
232
137
|
const loaderUrl = new URL("./source-loader.js", import.meta.url);
|
|
233
|
-
return {
|
|
234
|
-
filename,
|
|
235
|
-
indexUrl: nextSourceIndexUrl(basePath, filename),
|
|
236
|
-
loader: loaderUrl.protocol === "file:" ? fileURLToPath(loaderUrl) : resolve(process.cwd(), "packages/review-client/dist/source-loader.js"),
|
|
237
|
-
saltPath: markerSalt.path
|
|
238
|
-
};
|
|
138
|
+
return { loader: loaderUrl.protocol === "file:" ? fileURLToPath(loaderUrl) : resolve(process.cwd(), "packages/review-client/dist/source-loader.js") };
|
|
239
139
|
}
|
|
240
|
-
|
|
241
|
-
* Generates the marker salt and persists it outside the project so compiler
|
|
242
|
-
* workers can read it without the secret entering any build output. The file is
|
|
243
|
-
* owner-only and is removed once the production compile hook has run.
|
|
244
|
-
*/
|
|
245
|
-
function resolveBuildLocalMarkerSalt(env) {
|
|
246
|
-
const inherited = env[NEXT_MARKER_SALT_PATH];
|
|
247
|
-
if (env.NEXT_PRIVATE_BUILD_WORKER === "1" && env.IS_NEXT_WORKER === "true" && typeof inherited === "string" && inherited.length > 0) return {
|
|
248
|
-
salt: readMarkerSaltFile(inherited),
|
|
249
|
-
path: inherited
|
|
250
|
-
};
|
|
251
|
-
const salt = randomBytes(32).toString("hex");
|
|
252
|
-
const path = join(mkdtempSync(join(tmpdir(), "visual-review-marker-salt-")), "salt");
|
|
253
|
-
writeFileSync(path, salt, {
|
|
254
|
-
encoding: "utf8",
|
|
255
|
-
mode: 384
|
|
256
|
-
});
|
|
257
|
-
env[NEXT_MARKER_SALT_PATH] = path;
|
|
258
|
-
trackBuildLocalMarkerSalt(path);
|
|
259
|
-
return {
|
|
260
|
-
salt,
|
|
261
|
-
path
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
function readMarkerSaltFile(saltPath) {
|
|
265
|
-
const salt = readFileSync(saltPath, "utf8").trim();
|
|
266
|
-
if (!/^[0-9a-f]{64}$/u.test(salt)) throw new Error("Visual Review inherited marker salt is invalid");
|
|
267
|
-
return salt;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* `next dev` never reaches the production compile hook, and every compiler
|
|
271
|
-
* worker evaluates the config in its own process, so exit is the only point
|
|
272
|
-
* that reliably removes each salt file it created.
|
|
273
|
-
*/
|
|
274
|
-
function trackBuildLocalMarkerSalt(saltPath) {
|
|
275
|
-
if (trackedMarkerSaltPaths.size === 0) process.once("exit", () => {
|
|
276
|
-
for (const tracked of trackedMarkerSaltPaths) removeMarkerSaltDirectory(tracked);
|
|
277
|
-
});
|
|
278
|
-
trackedMarkerSaltPaths.add(saltPath);
|
|
279
|
-
}
|
|
280
|
-
function removeBuildLocalMarkerSalt(saltPath) {
|
|
281
|
-
trackedMarkerSaltPaths.delete(saltPath);
|
|
282
|
-
removeMarkerSaltDirectory(saltPath);
|
|
283
|
-
}
|
|
284
|
-
function removeMarkerSaltDirectory(saltPath) {
|
|
285
|
-
try {
|
|
286
|
-
rmSync(dirname(saltPath), {
|
|
287
|
-
recursive: true,
|
|
288
|
-
force: true
|
|
289
|
-
});
|
|
290
|
-
} catch {}
|
|
291
|
-
}
|
|
292
|
-
function configureNextSourceMetadata(configured, metadata, sourceMetadata) {
|
|
140
|
+
function configureNextSourceMetadata(configured, sourceMetadata) {
|
|
293
141
|
const bundler = resolveNextBundler();
|
|
294
142
|
const next = { ...configured };
|
|
295
|
-
restoreProperty(next, "turbopack", metadata.turbopack);
|
|
296
|
-
restoreProperty(next, "webpack", metadata.webpack);
|
|
297
143
|
if (bundler === "turbopack") next.turbopack = appendTurbopackRules(next.turbopack, sourceMetadata);
|
|
298
144
|
else next.webpack = appendWebpackRule(next.webpack, sourceMetadata);
|
|
299
145
|
return next;
|
|
@@ -318,10 +164,7 @@ function appendWebpackRule(existing, sourceMetadata) {
|
|
|
318
164
|
test: /\.[cm]?[jt]sx?$/u,
|
|
319
165
|
use: [{
|
|
320
166
|
loader: sourceMetadata.loader,
|
|
321
|
-
options: {
|
|
322
|
-
indexUrl: sourceMetadata.indexUrl,
|
|
323
|
-
saltPath: sourceMetadata.saltPath
|
|
324
|
-
}
|
|
167
|
+
options: {}
|
|
325
168
|
}]
|
|
326
169
|
}]
|
|
327
170
|
}
|
|
@@ -341,11 +184,7 @@ function appendTurbopackRules(existing, sourceMetadata) {
|
|
|
341
184
|
condition: { all: [{ not: "foreign" }, "production"] },
|
|
342
185
|
loaders: [{
|
|
343
186
|
loader: sourceMetadata.loader,
|
|
344
|
-
options: {
|
|
345
|
-
compile: true,
|
|
346
|
-
indexUrl: sourceMetadata.indexUrl,
|
|
347
|
-
saltPath: sourceMetadata.saltPath
|
|
348
|
-
}
|
|
187
|
+
options: { compile: true }
|
|
349
188
|
}]
|
|
350
189
|
};
|
|
351
190
|
const current = rules[pattern];
|
|
@@ -362,28 +201,10 @@ function isPromiseLikeUnknown(value) {
|
|
|
362
201
|
function isRecord(value) {
|
|
363
202
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
364
203
|
}
|
|
365
|
-
function
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
function markVisualReviewConfig(config) {
|
|
369
|
-
Object.defineProperty(config, VISUAL_REVIEW_CONFIG, { value: true });
|
|
204
|
+
function markVisualReviewConfig(config, bundlerState) {
|
|
205
|
+
Object.defineProperty(config, VISUAL_REVIEW_CONFIG, { value: bundlerState });
|
|
370
206
|
return config;
|
|
371
207
|
}
|
|
372
|
-
function resolveSourceMapUpload(option, env) {
|
|
373
|
-
if (option === false || typeof option === "object" && option.enabled === false) return;
|
|
374
|
-
const configured = typeof option === "object" ? option : void 0;
|
|
375
|
-
const token = configured?.token ?? env.VISUAL_REVIEW_SOURCE_MAP_TOKEN;
|
|
376
|
-
if (!(option === true || configured !== void 0 || token !== void 0)) return void 0;
|
|
377
|
-
if (typeof token !== "string" || token.length === 0 || token.length > 4096 || /\s/u.test(token)) throw new Error("Visual Review source-map upload requires VISUAL_REVIEW_SOURCE_MAP_TOKEN in the Node build environment");
|
|
378
|
-
return {
|
|
379
|
-
token,
|
|
380
|
-
mode: configured?.mode ?? inferReleaseMode(env),
|
|
381
|
-
...configured?.serviceUrl ? { serviceUrl: configured.serviceUrl } : {}
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
function configuredReleaseMode(option) {
|
|
385
|
-
return typeof option === "object" ? option.mode : void 0;
|
|
386
|
-
}
|
|
387
208
|
function inferReleaseMode(env) {
|
|
388
209
|
if (env.VERCEL_ENV === "preview") return "preview";
|
|
389
210
|
if (env.VERCEL_ENV === "development") return "development";
|
package/dist/react.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as e}from"./install-
|
|
1
|
+
import{t as e}from"./install-x8QQBGO-.js";import{useEffect as t,useRef as n}from"react";function r({activationParam:r,build:i,collaboration:a,developerTools:o,enabled:s,onError:c,onReady:l,runtime:u,serviceUrl:d,sourceContextResolver:f,styleNonce:p}){let m=n(c),h=n(l),g=i?.buildId,_=i?.gitCommit,v=i?.generatedAt,y=i?.mode,b=o?.reactGrab===!0,x=a?.cursors===!0,S=a?.chat===!0;return m.current=c,h.current=l,t(()=>{if(s!==!0)return;let t=e({activationParam:r,build:g===void 0&&_===void 0&&v===void 0&&y===void 0?void 0:{buildId:g,gitCommit:_,generatedAt:v,mode:y},collaboration:x||S?{cursors:x,chat:S}:void 0,developerTools:b?{reactGrab:!0}:void 0,enabled:s,onError:e=>m.current?.(e),onReady:e=>h.current?.(e),runtime:u,serviceUrl:d,sourceContextResolver:f,styleNonce:p});return()=>t.dispose()},[r,g,y,S,x,b,s,v,_,u,d,f,p]),null}r.displayName=`VisualReview`;export{r as VisualReview};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { n as VISUAL_REVIEW_SOURCE_INDEX_ATTRIBUTE, o as visualReviewSourceMarker, r as VISUAL_REVIEW_SOURCE_MARKER_ATTRIBUTE, t as VISUAL_REVIEW_SOURCE_COORDINATE_ATTRIBUTE } from "./source-index-BdWeiNHZ.js";
|
|
2
|
-
import { createHmac } from "node:crypto";
|
|
3
1
|
import path, { win32 } from "node:path";
|
|
4
2
|
import { parse } from "@babel/parser";
|
|
5
3
|
import MagicString from "magic-string";
|
|
6
4
|
//#region src/source-instrumentation-transform.ts
|
|
5
|
+
const VISUAL_REVIEW_EMBEDDED_SOURCE_ATTRIBUTE = "data-review-source";
|
|
7
6
|
const SOURCE_MODULE = /\.(?:[cm]?[jt]sx?)$/iu;
|
|
8
7
|
const SKIPPED_PATH_SEGMENT = /(?:^|\/)(?:node_modules|\.next|dist|build)(?:\/|$)/u;
|
|
9
|
-
const MARKER_SALT = /^[0-9a-f]{64}$/u;
|
|
10
8
|
/**
|
|
11
9
|
* Adds opaque, build-local markers to intrinsic JSX nodes. Repository paths
|
|
12
10
|
* are used only to derive a digest and never enter the transformed browser
|
|
@@ -14,7 +12,6 @@ const MARKER_SALT = /^[0-9a-f]{64}$/u;
|
|
|
14
12
|
* framework adapter after bundling.
|
|
15
13
|
*/
|
|
16
14
|
function instrumentVisualReviewSource(source, options) {
|
|
17
|
-
assertMarkerSalt(options.markerSalt);
|
|
18
15
|
const sourcePath = normalizedProjectSourcePath(options.projectRoot, options.id);
|
|
19
16
|
if (!sourcePath) return null;
|
|
20
17
|
const ast = parse(source, {
|
|
@@ -42,14 +39,8 @@ function instrumentVisualReviewSource(source, options) {
|
|
|
42
39
|
for (const candidate of candidates) {
|
|
43
40
|
const line1 = candidate.line1;
|
|
44
41
|
const column1 = candidate.column0 + 1;
|
|
45
|
-
const digest = createHmac("sha256", Buffer.from(options.markerSalt, "hex")).update(`${sourcePath}\0${line1}\0${column1}`).digest("hex").slice(0, 32);
|
|
46
|
-
const marker = visualReviewSourceMarker(digest);
|
|
47
|
-
const coordinate = `vrc1_${digest}`;
|
|
48
42
|
const closingWidth = candidate.selfClosing ? 2 : 1;
|
|
49
|
-
|
|
50
|
-
const metadata = ` ${VISUAL_REVIEW_SOURCE_MARKER_ATTRIBUTE}="${marker}" ${VISUAL_REVIEW_SOURCE_INDEX_ATTRIBUTE}="${options.indexUrl}"`;
|
|
51
|
-
edits.overwrite(candidate.start, candidate.nameEnd, `${source.slice(candidate.start, candidate.nameEnd)} ${VISUAL_REVIEW_SOURCE_COORDINATE_ATTRIBUTE}="${coordinate}"`);
|
|
52
|
-
edits.appendLeft(insertionPoint, metadata);
|
|
43
|
+
edits.appendLeft(candidate.end - closingWidth, ` ${VISUAL_REVIEW_EMBEDDED_SOURCE_ATTRIBUTE}="${escapeAttribute(`${sourcePath}:${line1}:${column1}`)}"`);
|
|
53
44
|
markerCount += 1;
|
|
54
45
|
}
|
|
55
46
|
if (markerCount === 0) return null;
|
|
@@ -63,8 +54,12 @@ function instrumentVisualReviewSource(source, options) {
|
|
|
63
54
|
markerCount
|
|
64
55
|
};
|
|
65
56
|
}
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Source paths are project-relative and already validated, but an attribute
|
|
59
|
+
* value still has to survive being written into HTML.
|
|
60
|
+
*/
|
|
61
|
+
function escapeAttribute(value) {
|
|
62
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
68
63
|
}
|
|
69
64
|
/**
|
|
70
65
|
* Reports whether a module is first-party project source. Bundler rules already
|
|
@@ -101,7 +96,7 @@ function intrinsicJsxNameEnd(name) {
|
|
|
101
96
|
return typeof candidate.end === "number" ? candidate.end : null;
|
|
102
97
|
}
|
|
103
98
|
function hasVisualReviewMarker(attributes) {
|
|
104
|
-
return attributes?.some((attribute) => attribute.type === "JSXAttribute" && typeof attribute.name !== "string" && attribute.name?.type === "JSXIdentifier" &&
|
|
99
|
+
return attributes?.some((attribute) => attribute.type === "JSXAttribute" && typeof attribute.name !== "string" && attribute.name?.type === "JSXIdentifier" && attribute.name.name === "data-review-source") ?? false;
|
|
105
100
|
}
|
|
106
101
|
function visitAst(node, visitor) {
|
|
107
102
|
visitor(node);
|
package/dist/source-loader.js
CHANGED
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import { n as isVisualReviewProjectSource, t as instrumentVisualReviewSource } from "./source-instrumentation-transform-
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
1
|
+
import { n as isVisualReviewProjectSource, t as instrumentVisualReviewSource } from "./source-instrumentation-transform-qmiVeKpI.js";
|
|
3
2
|
import { transformSync } from "@babel/core";
|
|
4
3
|
import transformReactJsx from "@babel/plugin-transform-react-jsx";
|
|
5
4
|
import transformTypeScript from "@babel/plugin-transform-typescript";
|
|
6
5
|
//#region src/source-loader.ts
|
|
7
|
-
const MARKER_SALT = /^[0-9a-f]{64}$/u;
|
|
8
|
-
const saltCache = /* @__PURE__ */ new Map();
|
|
9
|
-
/**
|
|
10
|
-
* Reads a build-local marker salt. Next serializes loader options into
|
|
11
|
-
* `required-server-files.json` and into Turbopack edge chunks, so the adapter
|
|
12
|
-
* publishes only this path and keeps the secret in an owner-only file.
|
|
13
|
-
*/
|
|
14
|
-
function readMarkerSaltFile(saltPath) {
|
|
15
|
-
const cached = saltCache.get(saltPath);
|
|
16
|
-
if (cached !== void 0) return cached;
|
|
17
|
-
let contents;
|
|
18
|
-
try {
|
|
19
|
-
contents = readFileSync(saltPath, "utf8").trim();
|
|
20
|
-
} catch {
|
|
21
|
-
throw new Error("Visual Review source loader could not read the marker salt");
|
|
22
|
-
}
|
|
23
|
-
if (!MARKER_SALT.test(contents)) throw new Error("Visual Review source loader marker salt is invalid");
|
|
24
|
-
saltCache.set(saltPath, contents);
|
|
25
|
-
return contents;
|
|
26
|
-
}
|
|
27
6
|
function visualReviewSourceLoader(source, inputMap) {
|
|
28
7
|
const done = this.async();
|
|
29
8
|
try {
|
|
@@ -35,8 +14,6 @@ function visualReviewSourceLoader(source, inputMap) {
|
|
|
35
14
|
}
|
|
36
15
|
const transformed = instrumentVisualReviewSource(source, {
|
|
37
16
|
id: this.resourcePath,
|
|
38
|
-
indexUrl: options.indexUrl,
|
|
39
|
-
markerSalt: options.markerSalt,
|
|
40
17
|
projectRoot
|
|
41
18
|
});
|
|
42
19
|
const code = transformed?.code ?? source;
|
|
@@ -112,14 +89,9 @@ function isRecord(value) {
|
|
|
112
89
|
function parseOptions(value) {
|
|
113
90
|
if (!value || typeof value !== "object") throw new Error("Visual Review source loader options are missing");
|
|
114
91
|
const options = value;
|
|
115
|
-
if (typeof options.indexUrl !== "string" || !options.indexUrl.startsWith("/") || options.indexUrl.startsWith("//") || options.indexUrl.includes("\\")) throw new Error("Visual Review source loader index URL is invalid");
|
|
116
92
|
if (options.projectRoot !== void 0 && typeof options.projectRoot !== "string") throw new Error("Visual Review source loader project root is invalid");
|
|
117
|
-
const markerSalt = typeof options.saltPath === "string" ? readMarkerSaltFile(options.saltPath) : options.markerSalt;
|
|
118
|
-
if (typeof markerSalt !== "string" || !MARKER_SALT.test(markerSalt)) throw new Error("Visual Review source loader marker salt is invalid");
|
|
119
93
|
return {
|
|
120
94
|
...options.compile === true ? { compile: true } : {},
|
|
121
|
-
indexUrl: options.indexUrl,
|
|
122
|
-
markerSalt,
|
|
123
95
|
...typeof options.projectRoot === "string" ? { projectRoot: options.projectRoot } : {}
|
|
124
96
|
};
|
|
125
97
|
}
|
package/dist/vite.d.ts
CHANGED
|
@@ -7,23 +7,15 @@ export interface VisualReviewBuildIdentityOptions {
|
|
|
7
7
|
gitCommit?: string | null;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface VisualReviewViteSourceMapOptions {
|
|
11
|
-
/** Enables upload explicitly. A source-map upload token is still required. */
|
|
12
|
-
enabled?: boolean;
|
|
13
|
-
/** Node-only build secret. Prefer VISUAL_REVIEW_SOURCE_MAP_TOKEN in CI. */
|
|
14
|
-
token?: string;
|
|
15
|
-
/** Hosted service origin. Primarily useful for local/self-hosted build services. */
|
|
16
|
-
serviceUrl?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
10
|
export interface VisualReviewViteOptions extends VisualReviewBuildIdentityOptions {
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Writes `data-review-source="src/App.tsx:42:7"` onto JSX elements by
|
|
13
|
+
* default. Set `false` for identity-only output.
|
|
14
|
+
*/
|
|
23
15
|
sourceMetadata?: boolean;
|
|
24
16
|
}
|
|
25
17
|
|
|
26
|
-
/** Injects public build provenance and
|
|
18
|
+
/** Injects public build provenance and the source path of every JSX element. */
|
|
27
19
|
export declare function visualReview(
|
|
28
20
|
options?: VisualReviewViteOptions,
|
|
29
21
|
): Plugin;
|
package/dist/vite.js
CHANGED
|
@@ -1,122 +1,47 @@
|
|
|
1
|
-
import { t as resolveVisualReviewBuildIdentity } from "./build-identity-
|
|
2
|
-
import {
|
|
3
|
-
import { t as instrumentVisualReviewSource } from "./source-instrumentation-transform-BwKYKlp0.js";
|
|
4
|
-
import { randomBytes } from "node:crypto";
|
|
5
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { t as resolveVisualReviewBuildIdentity } from "./build-identity-DebYe8Qe.js";
|
|
2
|
+
import { t as instrumentVisualReviewSource } from "./source-instrumentation-transform-qmiVeKpI.js";
|
|
6
3
|
//#region src/vite.ts
|
|
7
4
|
const BUILD_ID_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_BUILD_ID";
|
|
8
5
|
const GIT_COMMIT_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_GIT_COMMIT";
|
|
9
6
|
const BUILD_MODE_LITERAL = "process.env.VISUAL_REVIEW_PUBLIC_BUILD_MODE";
|
|
10
|
-
/** Injects public build provenance and
|
|
7
|
+
/** Injects public build provenance and the source path of every JSX element. */
|
|
11
8
|
function visualReview(options = {}) {
|
|
12
9
|
let identity;
|
|
13
10
|
let resolvedConfig;
|
|
14
|
-
let sourceMapUpload;
|
|
15
11
|
let sourceMetadataEnabled = false;
|
|
16
|
-
let sourceIndexFilename;
|
|
17
|
-
let sourceIndexUrl;
|
|
18
|
-
let sourceMarkerSalt;
|
|
19
12
|
let releaseMode;
|
|
20
13
|
return {
|
|
21
14
|
name: "01works:visual-review-build-identity",
|
|
22
15
|
enforce: "pre",
|
|
23
16
|
config(config, environment) {
|
|
24
17
|
identity = resolveVisualReviewBuildIdentity(options, {
|
|
25
|
-
cwd:
|
|
18
|
+
cwd: config.root ?? ".",
|
|
26
19
|
env: process.env
|
|
27
20
|
});
|
|
28
21
|
releaseMode = typeof environment?.mode === "string" && environment.mode.length > 0 ? normalizeReleaseMode(environment.mode) : void 0;
|
|
29
22
|
const define = publicLiteralDefinitions(identity, releaseMode);
|
|
30
23
|
assertReservedLiterals(config.define, define, "Vite define");
|
|
31
|
-
if (environment?.command === "serve") {
|
|
32
|
-
sourceMapUpload = void 0;
|
|
33
|
-
sourceMetadataEnabled = false;
|
|
34
|
-
sourceMarkerSalt = void 0;
|
|
35
|
-
return { define };
|
|
36
|
-
}
|
|
37
|
-
sourceMapUpload = resolveSourceMapUpload(options.sourceMaps, process.env);
|
|
38
24
|
sourceMetadataEnabled = options.sourceMetadata ?? true;
|
|
39
|
-
|
|
40
|
-
if (!sourceMapUpload) return { define };
|
|
41
|
-
if (config.build?.sourcemap === "inline") throw new Error("Visual Review source-map upload refuses inline Vite source maps");
|
|
42
|
-
return {
|
|
43
|
-
define,
|
|
44
|
-
build: { sourcemap: "hidden" }
|
|
45
|
-
};
|
|
25
|
+
return { define };
|
|
46
26
|
},
|
|
47
27
|
configResolved(config) {
|
|
48
28
|
resolvedConfig = config;
|
|
49
29
|
if (releaseMode !== void 0 && normalizeReleaseMode(config.mode) !== releaseMode) throw new Error("Visual Review build mode changed after the public literals were injected");
|
|
50
|
-
if (sourceMetadataEnabled) {
|
|
51
|
-
if (!identity || !sourceMarkerSalt) throw new Error("Visual Review source metadata identity is unavailable");
|
|
52
|
-
sourceIndexFilename = visualReviewSourceIndexFilename(identity.buildId, sourceMarkerSalt);
|
|
53
|
-
sourceIndexUrl = viteSourceIndexUrl(config.base, sourceIndexFilename);
|
|
54
|
-
}
|
|
55
|
-
if (!sourceMapUpload) return;
|
|
56
|
-
if (config.build.sourcemap !== "hidden") throw new Error("Visual Review source-map upload requires build.sourcemap=\"hidden\"");
|
|
57
|
-
if (config.build.ssr) throw new Error("Visual Review Vite source-map upload currently supports browser builds only");
|
|
58
30
|
},
|
|
59
31
|
transform(source, id) {
|
|
60
32
|
if (!sourceMetadataEnabled) return null;
|
|
61
|
-
if (!resolvedConfig
|
|
33
|
+
if (!resolvedConfig) throw new Error("Visual Review source metadata was not initialized by Vite");
|
|
62
34
|
const transformed = instrumentVisualReviewSource(source, {
|
|
63
35
|
id,
|
|
64
|
-
indexUrl: sourceIndexUrl,
|
|
65
|
-
markerSalt: sourceMarkerSalt,
|
|
66
36
|
projectRoot: resolvedConfig.root
|
|
67
37
|
});
|
|
68
38
|
return transformed ? {
|
|
69
39
|
code: transformed.code,
|
|
70
40
|
map: transformed.map
|
|
71
41
|
} : null;
|
|
72
|
-
},
|
|
73
|
-
async closeBundle() {
|
|
74
|
-
if (!identity || !resolvedConfig) throw new Error("Visual Review build adapter was not initialized by Vite");
|
|
75
|
-
const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
76
|
-
if (sourceMetadataEnabled) {
|
|
77
|
-
if (!sourceIndexFilename) throw new Error("Visual Review source metadata was not initialized by Vite");
|
|
78
|
-
try {
|
|
79
|
-
await writeViteVisualReviewSourceIndex({
|
|
80
|
-
projectRoot: resolvedConfig.root,
|
|
81
|
-
outDir,
|
|
82
|
-
base: resolvedConfig.base,
|
|
83
|
-
filename: sourceIndexFilename
|
|
84
|
-
});
|
|
85
|
-
} catch (sourceIndexError) {
|
|
86
|
-
if (!sourceMapUpload) throw sourceIndexError;
|
|
87
|
-
try {
|
|
88
|
-
await cleanupViteSourceMapOutput(resolvedConfig.root, outDir);
|
|
89
|
-
} catch (cleanupError) {
|
|
90
|
-
throw new AggregateError([sourceIndexError, cleanupError], "Visual Review source index failed and Vite source maps could not be cleaned");
|
|
91
|
-
}
|
|
92
|
-
throw sourceIndexError;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (!sourceMapUpload) return;
|
|
96
|
-
await uploadViteSourceMaps({
|
|
97
|
-
projectRoot: resolvedConfig.root,
|
|
98
|
-
outDir,
|
|
99
|
-
base: resolvedConfig.base,
|
|
100
|
-
token: sourceMapUpload.token,
|
|
101
|
-
...sourceMapUpload.serviceUrl ? { serviceUrl: sourceMapUpload.serviceUrl } : {},
|
|
102
|
-
buildId: identity.buildId,
|
|
103
|
-
gitCommit: identity.gitCommit,
|
|
104
|
-
mode: normalizeReleaseMode(resolvedConfig.mode)
|
|
105
|
-
});
|
|
106
42
|
}
|
|
107
43
|
};
|
|
108
44
|
}
|
|
109
|
-
function resolveSourceMapUpload(option, env) {
|
|
110
|
-
if (option === false || typeof option === "object" && option.enabled === false) return;
|
|
111
|
-
const configured = typeof option === "object" ? option : void 0;
|
|
112
|
-
const token = configured?.token ?? env.VISUAL_REVIEW_SOURCE_MAP_TOKEN;
|
|
113
|
-
if (!(option === true || configured !== void 0 || token !== void 0)) return void 0;
|
|
114
|
-
if (typeof token !== "string" || token.length === 0 || /\s/.test(token)) throw new Error("Visual Review source-map upload requires VISUAL_REVIEW_SOURCE_MAP_TOKEN in the Node build environment");
|
|
115
|
-
return {
|
|
116
|
-
token,
|
|
117
|
-
...configured?.serviceUrl ? { serviceUrl: configured.serviceUrl } : {}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
45
|
function normalizeReleaseMode(mode) {
|
|
121
46
|
if (mode === "production") return "production";
|
|
122
47
|
if (mode === "development") return "development";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./generated-frame-CkUKpj5U.js";function t(e){let t=new Map,r=e.sourceIndexBaseUrl;return{unavailableProvenance:()=>f(e,`unavailable`),async resolve(i,a){let o=a?.signal;p(o);let s=i.closest(`[data-visual-review-source]`),c=s?await n(s,t,o,r):null;return p(o),{source:c,componentStack:c?[c]:[],provenance:f(e,c?`fallback`:`unavailable`)}}}}async function n(e,t,n,i){let o=e.getAttribute(`data-visual-review-source`),c=e.getAttribute(`data-visual-review-source-index`);if(!o||!/^vr1_[0-9a-f]{32}$/u.test(o)||!c)return null;let l=r(c,e.ownerDocument,i);if(!l||typeof globalThis.fetch!=`function`)return null;let d=t.get(l);if(!d){if(t.size>=v)return null;d=a(l),t.set(l,d)}let f;try{f=await m(d,n)}catch{return null}let p=s(f,o);return p?u(p):null}function r(e,t,n){if(e.length>2048||e.trim()!==e||e.includes(`\\`)||/[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069]/u.test(e))return null;try{let r=new URL(e,n??t.baseURI);return r.origin!==t.location.origin||r.username||r.password||r.search||r.hash||!i(r.pathname)?null:r.href}catch{return null}}function i(e){return x.test(e)||S.test(e)}async function a(e){let t=await globalThis.fetch(e,{cache:`force-cache`,credentials:`same-origin`});if(!t.ok)throw Error(`Visual Review source index returned HTTP ${t.status}`);let n=t.headers.get(`content-length`),r=n===null?null:Number(n);if(r!==null&&(!Number.isSafeInteger(r)||r<0||r>_))throw Error(`Visual Review source index exceeds 8 MiB`);let i=await o(t);return JSON.parse(i)}async function o(e){let t=e.body?.getReader();if(!t)return``;let n=new TextDecoder(`utf-8`,{fatal:!0}),r=[],i=0;try{for(;;){let{done:e,value:a}=await t.read();if(e)break;if(i+=a.byteLength,i>_)throw await t.cancel(`Visual Review source index exceeds 8 MiB`),Error(`Visual Review source index exceeds 8 MiB`);r.push(n.decode(a,{stream:!0}))}return r.push(n.decode()),r.join(``)}finally{t.releaseLock()}}function s(t,n){if(!l(t)||t.schemaVersion!==1||!l(t.entries))return;let r=t.entries[n];if(!(!Array.isArray(r)||r.length===0||r.length>y))for(let t of r){if(!l(t)||!c(t.runtime))continue;let n=e(t.assetPath),r=d(t.line1,1,h),i=d(t.column0,0,g);if(!(!n||r===null||i===null))return{assetPath:n,line1:r,column0:i,runtime:t.runtime}}}function c(e){return e===`browser`||e===`server-node`||e===`server-edge`}function l(e){return!!e&&typeof e==`object`&&!Array.isArray(e)}function u(e){return{filePath:e.assetPath,lineNumber:e.line1,columnNumber:e.column0+1,componentName:null,origin:`generated`,generated:e}}function d(e,t,n){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=t&&e<=n?e:null}function f(e,t){return{provider:`embedded`,build:e.build,resolvedAt:e.now?.()??Date.now(),sourceMapStatus:t}}function p(e){if(e?.aborted)throw e.reason??Error(`Source context resolution aborted`)}function m(e,t){return t?t.aborted?Promise.reject(t.reason??Error(`Source context resolution aborted`)):new Promise((n,r)=>{let i=()=>r(t.reason??Error(`Source context resolution aborted`));t.addEventListener(`abort`,i,{once:!0}),e.then(e=>{t.removeEventListener(`abort`,i),n(e)},e=>{t.removeEventListener(`abort`,i),r(e)})}):e}const h=1e7,g=1e7,_=8*1024*1024,v=16,y=64,b=`visual-review-sources-[0-9a-f]{16}\\.json`,x=RegExp(`^/(?:[^/]+/)*\\.visual-review/${b}$`,`u`),S=RegExp(`^/(?:[^/]+/)*_next/static/${b}$`,`u`);export{t as createGeneratedSourceContextResolver};
|