@01.works/visual-review 0.3.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 +48 -48
- package/dist/hosted-runtime-DHzdpdSj.js +13 -0
- package/dist/index.js +1 -1
- package/dist/{install-C-4G_vDp.js → install-x8QQBGO-.js} +1 -1
- package/dist/next-config.d.ts +4 -14
- package/dist/next-config.js +25 -211
- package/dist/react.js +1 -1
- package/dist/{source-instrumentation-transform-Cx8SIIWh.js → source-instrumentation-transform-qmiVeKpI.js} +3 -15
- package/dist/source-loader.js +2 -33
- package/dist/vite.d.ts +5 -13
- package/dist/vite.js +7 -89
- package/package.json +1 -1
- package/dist/hosted-runtime-1mdUKkOV.js +0 -13
- package/dist/source-index-o5fwJzns.js +0 -1400
- 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 nextSourceIndexUrl, d as cleanupNextSourceMapOutput, f as uploadNextSourceMaps, l as writeNextVisualReviewSourceIndex, o as visualReviewSourceIndexFilename } from "./source-index-o5fwJzns.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,28 +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 sourceMetadataOptions = {
|
|
96
|
-
enabled: sourceMetadataEnabled,
|
|
97
|
-
embedSourcePath: options.embedSourcePath ?? true,
|
|
98
|
-
markerSalt: sourceMetadataEnabled ? resolveBuildLocalMarkerSalt(process.env) : void 0
|
|
99
|
-
};
|
|
100
88
|
const injected = {
|
|
101
89
|
[BUILD_ID_LITERAL]: identity.buildId,
|
|
102
90
|
[GIT_COMMIT_LITERAL]: identity.gitCommit ?? "",
|
|
103
|
-
[BUILD_MODE_LITERAL]:
|
|
91
|
+
[BUILD_MODE_LITERAL]: inferReleaseMode(process.env)
|
|
104
92
|
};
|
|
105
93
|
if (typeof nextConfig === "function") return function visualReviewNextConfig(...arguments_) {
|
|
106
94
|
const resolved = nextConfig.apply(this, arguments_);
|
|
107
|
-
return isPromiseLike(resolved) ? resolved.then((config) => configureNext(config, injected,
|
|
95
|
+
return isPromiseLike(resolved) ? resolved.then((config) => configureNext(config, injected, sourceMetadataEnabled)) : configureNext(resolved, injected, sourceMetadataEnabled);
|
|
108
96
|
};
|
|
109
|
-
return configureNext(nextConfig, injected,
|
|
97
|
+
return configureNext(nextConfig, injected, sourceMetadataEnabled);
|
|
110
98
|
}
|
|
111
|
-
function configureNext(nextConfig, injected,
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
assertReservedLiterals(nextConfig.env, injected, previouslyWrapped);
|
|
99
|
+
function configureNext(nextConfig, injected, sourceMetadataEnabled) {
|
|
100
|
+
const previousBundlerState = getVisualReviewBundlerState(nextConfig);
|
|
101
|
+
assertReservedLiterals(nextConfig.env, injected, previousBundlerState !== void 0);
|
|
115
102
|
let configured = {
|
|
116
103
|
...nextConfig,
|
|
117
104
|
env: {
|
|
@@ -119,84 +106,19 @@ function configureNext(nextConfig, injected, identity, sourceMapUpload, sourceMe
|
|
|
119
106
|
...injected
|
|
120
107
|
}
|
|
121
108
|
};
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const compiler = sourceConfigured.compiler ?? {};
|
|
130
|
-
return markVisualReviewConfig({
|
|
131
|
-
...sourceConfigured,
|
|
132
|
-
...sourceMapUpload ? { productionBrowserSourceMaps: true } : {},
|
|
133
|
-
...sourceMapUpload ? { experimental: {
|
|
134
|
-
...sourceConfigured.experimental,
|
|
135
|
-
serverSourceMaps: true
|
|
136
|
-
} } : {},
|
|
137
|
-
compiler: {
|
|
138
|
-
...compiler,
|
|
139
|
-
runAfterProductionCompile: composeProductionCompileHook(hookMetadata.originalHook, identity, sourceMapUpload, sourceMetadata, nextConfig.basePath, nextConfig.assetPrefix, hookMetadata)
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
function composeProductionCompileHook(existingHook, identity, sourceMapUpload, sourceMetadata, basePath, assetPrefix, metadata) {
|
|
144
|
-
const hook = async function visualReviewAfterProductionCompile(metadata) {
|
|
145
|
-
if (sourceMetadata) removeBuildLocalMarkerSalt(sourceMetadata.saltPath);
|
|
146
|
-
if (existingHook) try {
|
|
147
|
-
await existingHook.call(this, metadata);
|
|
148
|
-
} catch (existingError) {
|
|
149
|
-
if (!sourceMapUpload) throw existingError;
|
|
150
|
-
try {
|
|
151
|
-
await cleanupNextSourceMapOutput(metadata.projectDir, metadata.distDir);
|
|
152
|
-
} catch (cleanupError) {
|
|
153
|
-
throw new AggregateError([existingError, cleanupError], "Next.js production compile hook failed and Visual Review could not clean source maps");
|
|
154
|
-
}
|
|
155
|
-
throw existingError;
|
|
156
|
-
}
|
|
157
|
-
if (sourceMetadata && !sourceMetadata.embedSourcePath) try {
|
|
158
|
-
await writeNextVisualReviewSourceIndex({
|
|
159
|
-
projectDir: metadata.projectDir,
|
|
160
|
-
distDir: metadata.distDir,
|
|
161
|
-
filename: sourceMetadata.filename,
|
|
162
|
-
...basePath ? { basePath } : {},
|
|
163
|
-
...assetPrefix ? { assetPrefix } : {}
|
|
164
|
-
});
|
|
165
|
-
} catch (sourceIndexError) {
|
|
166
|
-
if (!sourceMapUpload) throw sourceIndexError;
|
|
167
|
-
try {
|
|
168
|
-
await cleanupNextSourceMapOutput(metadata.projectDir, metadata.distDir);
|
|
169
|
-
} catch (cleanupError) {
|
|
170
|
-
throw new AggregateError([sourceIndexError, cleanupError], "Visual Review source index failed and Next.js source maps could not be cleaned");
|
|
171
|
-
}
|
|
172
|
-
throw sourceIndexError;
|
|
173
|
-
}
|
|
174
|
-
if (!sourceMapUpload) return;
|
|
175
|
-
await uploadNextSourceMaps({
|
|
176
|
-
projectRoot: metadata.projectDir,
|
|
177
|
-
distDir: metadata.distDir,
|
|
178
|
-
token: sourceMapUpload.token,
|
|
179
|
-
...sourceMapUpload.serviceUrl ? { serviceUrl: sourceMapUpload.serviceUrl } : {},
|
|
180
|
-
buildId: identity.buildId,
|
|
181
|
-
gitCommit: identity.gitCommit,
|
|
182
|
-
mode: sourceMapUpload.mode,
|
|
183
|
-
...basePath ? { basePath } : {},
|
|
184
|
-
...assetPrefix ? { assetPrefix } : {}
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
Object.defineProperty(hook, VISUAL_REVIEW_HOOK, { value: metadata });
|
|
188
|
-
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);
|
|
189
116
|
}
|
|
190
|
-
function
|
|
191
|
-
return
|
|
117
|
+
function getVisualReviewBundlerState(config) {
|
|
118
|
+
return config[VISUAL_REVIEW_CONFIG];
|
|
192
119
|
}
|
|
193
|
-
function
|
|
120
|
+
function captureBundlerState(nextConfig) {
|
|
194
121
|
return {
|
|
195
|
-
originalHook: nextConfig.compiler?.runAfterProductionCompile,
|
|
196
|
-
compilerPresent: Object.prototype.hasOwnProperty.call(nextConfig, "compiler"),
|
|
197
|
-
experimentalPresent: Object.prototype.hasOwnProperty.call(nextConfig, "experimental"),
|
|
198
|
-
productionBrowserSourceMaps: propertyState(nextConfig, "productionBrowserSourceMaps"),
|
|
199
|
-
serverSourceMaps: propertyState(nextConfig.experimental, "serverSourceMaps"),
|
|
200
122
|
turbopack: propertyState(nextConfig, "turbopack"),
|
|
201
123
|
webpack: propertyState(nextConfig, "webpack")
|
|
202
124
|
};
|
|
@@ -207,98 +129,17 @@ function propertyState(value, key) {
|
|
|
207
129
|
value: value?.[key]
|
|
208
130
|
};
|
|
209
131
|
}
|
|
210
|
-
function restoreSourceMapConfiguration(configured, metadata) {
|
|
211
|
-
if (!metadata) return configured;
|
|
212
|
-
const restored = { ...configured };
|
|
213
|
-
if (metadata.productionBrowserSourceMaps.present) restored.productionBrowserSourceMaps = metadata.productionBrowserSourceMaps.value;
|
|
214
|
-
else delete restored.productionBrowserSourceMaps;
|
|
215
|
-
const experimental = { ...restored.experimental ?? {} };
|
|
216
|
-
if (metadata.serverSourceMaps.present) experimental.serverSourceMaps = metadata.serverSourceMaps.value;
|
|
217
|
-
else delete experimental.serverSourceMaps;
|
|
218
|
-
if (!metadata.experimentalPresent && Object.keys(experimental).length === 0) delete restored.experimental;
|
|
219
|
-
else restored.experimental = experimental;
|
|
220
|
-
const compiler = { ...restored.compiler ?? {} };
|
|
221
|
-
if (metadata.originalHook) compiler.runAfterProductionCompile = metadata.originalHook;
|
|
222
|
-
else delete compiler.runAfterProductionCompile;
|
|
223
|
-
if (!metadata.compilerPresent && Object.keys(compiler).length === 0) delete restored.compiler;
|
|
224
|
-
else restored.compiler = compiler;
|
|
225
|
-
restoreProperty(restored, "turbopack", metadata.turbopack);
|
|
226
|
-
restoreProperty(restored, "webpack", metadata.webpack);
|
|
227
|
-
return restored;
|
|
228
|
-
}
|
|
229
132
|
function restoreProperty(target, key, state) {
|
|
230
133
|
if (state.present) target[key] = state.value;
|
|
231
134
|
else delete target[key];
|
|
232
135
|
}
|
|
233
|
-
function resolveNextSourceMetadata(
|
|
234
|
-
if (!markerSalt) throw new Error("Visual Review source marker salt is unavailable");
|
|
235
|
-
const filename = visualReviewSourceIndexFilename(identity.buildId, markerSalt.salt);
|
|
136
|
+
function resolveNextSourceMetadata() {
|
|
236
137
|
const loaderUrl = new URL("./source-loader.js", import.meta.url);
|
|
237
|
-
return {
|
|
238
|
-
embedSourcePath,
|
|
239
|
-
filename,
|
|
240
|
-
indexUrl: nextSourceIndexUrl(basePath, filename),
|
|
241
|
-
loader: loaderUrl.protocol === "file:" ? fileURLToPath(loaderUrl) : resolve(process.cwd(), "packages/review-client/dist/source-loader.js"),
|
|
242
|
-
saltPath: markerSalt.path
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Generates the marker salt and persists it outside the project so compiler
|
|
247
|
-
* workers can read it without the secret entering any build output. The file is
|
|
248
|
-
* owner-only and is removed once the production compile hook has run.
|
|
249
|
-
*/
|
|
250
|
-
function resolveBuildLocalMarkerSalt(env) {
|
|
251
|
-
const inherited = env[NEXT_MARKER_SALT_PATH];
|
|
252
|
-
if (env.NEXT_PRIVATE_BUILD_WORKER === "1" && env.IS_NEXT_WORKER === "true" && typeof inherited === "string" && inherited.length > 0) return {
|
|
253
|
-
salt: readMarkerSaltFile(inherited),
|
|
254
|
-
path: inherited
|
|
255
|
-
};
|
|
256
|
-
const salt = randomBytes(32).toString("hex");
|
|
257
|
-
const path = join(mkdtempSync(join(tmpdir(), "visual-review-marker-salt-")), "salt");
|
|
258
|
-
writeFileSync(path, salt, {
|
|
259
|
-
encoding: "utf8",
|
|
260
|
-
mode: 384
|
|
261
|
-
});
|
|
262
|
-
env[NEXT_MARKER_SALT_PATH] = path;
|
|
263
|
-
trackBuildLocalMarkerSalt(path);
|
|
264
|
-
return {
|
|
265
|
-
salt,
|
|
266
|
-
path
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
function readMarkerSaltFile(saltPath) {
|
|
270
|
-
const salt = readFileSync(saltPath, "utf8").trim();
|
|
271
|
-
if (!/^[0-9a-f]{64}$/u.test(salt)) throw new Error("Visual Review inherited marker salt is invalid");
|
|
272
|
-
return salt;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* `next dev` never reaches the production compile hook, and every compiler
|
|
276
|
-
* worker evaluates the config in its own process, so exit is the only point
|
|
277
|
-
* that reliably removes each salt file it created.
|
|
278
|
-
*/
|
|
279
|
-
function trackBuildLocalMarkerSalt(saltPath) {
|
|
280
|
-
if (trackedMarkerSaltPaths.size === 0) process.once("exit", () => {
|
|
281
|
-
for (const tracked of trackedMarkerSaltPaths) removeMarkerSaltDirectory(tracked);
|
|
282
|
-
});
|
|
283
|
-
trackedMarkerSaltPaths.add(saltPath);
|
|
138
|
+
return { loader: loaderUrl.protocol === "file:" ? fileURLToPath(loaderUrl) : resolve(process.cwd(), "packages/review-client/dist/source-loader.js") };
|
|
284
139
|
}
|
|
285
|
-
function
|
|
286
|
-
trackedMarkerSaltPaths.delete(saltPath);
|
|
287
|
-
removeMarkerSaltDirectory(saltPath);
|
|
288
|
-
}
|
|
289
|
-
function removeMarkerSaltDirectory(saltPath) {
|
|
290
|
-
try {
|
|
291
|
-
rmSync(dirname(saltPath), {
|
|
292
|
-
recursive: true,
|
|
293
|
-
force: true
|
|
294
|
-
});
|
|
295
|
-
} catch {}
|
|
296
|
-
}
|
|
297
|
-
function configureNextSourceMetadata(configured, metadata, sourceMetadata) {
|
|
140
|
+
function configureNextSourceMetadata(configured, sourceMetadata) {
|
|
298
141
|
const bundler = resolveNextBundler();
|
|
299
142
|
const next = { ...configured };
|
|
300
|
-
restoreProperty(next, "turbopack", metadata.turbopack);
|
|
301
|
-
restoreProperty(next, "webpack", metadata.webpack);
|
|
302
143
|
if (bundler === "turbopack") next.turbopack = appendTurbopackRules(next.turbopack, sourceMetadata);
|
|
303
144
|
else next.webpack = appendWebpackRule(next.webpack, sourceMetadata);
|
|
304
145
|
return next;
|
|
@@ -323,11 +164,7 @@ function appendWebpackRule(existing, sourceMetadata) {
|
|
|
323
164
|
test: /\.[cm]?[jt]sx?$/u,
|
|
324
165
|
use: [{
|
|
325
166
|
loader: sourceMetadata.loader,
|
|
326
|
-
options: {
|
|
327
|
-
embedSourcePath: sourceMetadata.embedSourcePath,
|
|
328
|
-
indexUrl: sourceMetadata.indexUrl,
|
|
329
|
-
saltPath: sourceMetadata.saltPath
|
|
330
|
-
}
|
|
167
|
+
options: {}
|
|
331
168
|
}]
|
|
332
169
|
}]
|
|
333
170
|
}
|
|
@@ -347,12 +184,7 @@ function appendTurbopackRules(existing, sourceMetadata) {
|
|
|
347
184
|
condition: { all: [{ not: "foreign" }, "production"] },
|
|
348
185
|
loaders: [{
|
|
349
186
|
loader: sourceMetadata.loader,
|
|
350
|
-
options: {
|
|
351
|
-
compile: true,
|
|
352
|
-
embedSourcePath: sourceMetadata.embedSourcePath,
|
|
353
|
-
indexUrl: sourceMetadata.indexUrl,
|
|
354
|
-
saltPath: sourceMetadata.saltPath
|
|
355
|
-
}
|
|
187
|
+
options: { compile: true }
|
|
356
188
|
}]
|
|
357
189
|
};
|
|
358
190
|
const current = rules[pattern];
|
|
@@ -369,28 +201,10 @@ function isPromiseLikeUnknown(value) {
|
|
|
369
201
|
function isRecord(value) {
|
|
370
202
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
371
203
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
function markVisualReviewConfig(config) {
|
|
376
|
-
Object.defineProperty(config, VISUAL_REVIEW_CONFIG, { value: true });
|
|
204
|
+
function markVisualReviewConfig(config, bundlerState) {
|
|
205
|
+
Object.defineProperty(config, VISUAL_REVIEW_CONFIG, { value: bundlerState });
|
|
377
206
|
return config;
|
|
378
207
|
}
|
|
379
|
-
function resolveSourceMapUpload(option, env) {
|
|
380
|
-
if (option === false || typeof option === "object" && option.enabled === false) return;
|
|
381
|
-
const configured = typeof option === "object" ? option : void 0;
|
|
382
|
-
const token = configured?.token ?? env.VISUAL_REVIEW_SOURCE_MAP_TOKEN;
|
|
383
|
-
if (!(option === true || configured !== void 0 || token !== void 0)) return void 0;
|
|
384
|
-
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");
|
|
385
|
-
return {
|
|
386
|
-
token,
|
|
387
|
-
mode: configured?.mode ?? inferReleaseMode(env),
|
|
388
|
-
...configured?.serviceUrl ? { serviceUrl: configured.serviceUrl } : {}
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
function configuredReleaseMode(option) {
|
|
392
|
-
return typeof option === "object" ? option.mode : void 0;
|
|
393
|
-
}
|
|
394
208
|
function inferReleaseMode(env) {
|
|
395
209
|
if (env.VERCEL_ENV === "preview") return "preview";
|
|
396
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 { i as VISUAL_REVIEW_SOURCE_MARKER_ATTRIBUTE, n as VISUAL_REVIEW_SOURCE_COORDINATE_ATTRIBUTE, r as VISUAL_REVIEW_SOURCE_INDEX_ATTRIBUTE, s as visualReviewSourceMarker, t as VISUAL_REVIEW_EMBEDDED_SOURCE_ATTRIBUTE } from "./source-index-o5fwJzns.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 = options.embedSourcePath ? ` ${VISUAL_REVIEW_EMBEDDED_SOURCE_ATTRIBUTE}="${escapeAttribute(`${sourcePath}:${line1}:${column1}`)}"` : ` ${VISUAL_REVIEW_SOURCE_MARKER_ATTRIBUTE}="${marker}" ${VISUAL_REVIEW_SOURCE_INDEX_ATTRIBUTE}="${options.indexUrl}"`;
|
|
51
|
-
if (!options.embedSourcePath) 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;
|
|
@@ -70,9 +61,6 @@ function instrumentVisualReviewSource(source, options) {
|
|
|
70
61
|
function escapeAttribute(value) {
|
|
71
62
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
72
63
|
}
|
|
73
|
-
function assertMarkerSalt(value) {
|
|
74
|
-
if (!MARKER_SALT.test(value)) throw new Error("Visual Review source marker salt must be a lowercase 32-byte hex secret");
|
|
75
|
-
}
|
|
76
64
|
/**
|
|
77
65
|
* Reports whether a module is first-party project source. Bundler rules already
|
|
78
66
|
* try to exclude dependencies, but Next 15's Turbopack does not honour the
|
|
@@ -108,7 +96,7 @@ function intrinsicJsxNameEnd(name) {
|
|
|
108
96
|
return typeof candidate.end === "number" ? candidate.end : null;
|
|
109
97
|
}
|
|
110
98
|
function hasVisualReviewMarker(attributes) {
|
|
111
|
-
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;
|
|
112
100
|
}
|
|
113
101
|
function visitAst(node, visitor) {
|
|
114
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,10 +14,7 @@ function visualReviewSourceLoader(source, inputMap) {
|
|
|
35
14
|
}
|
|
36
15
|
const transformed = instrumentVisualReviewSource(source, {
|
|
37
16
|
id: this.resourcePath,
|
|
38
|
-
|
|
39
|
-
markerSalt: options.markerSalt,
|
|
40
|
-
projectRoot,
|
|
41
|
-
embedSourcePath: options.embedSourcePath
|
|
17
|
+
projectRoot
|
|
42
18
|
});
|
|
43
19
|
const code = transformed?.code ?? source;
|
|
44
20
|
const map = transformed?.map ?? inputMap;
|
|
@@ -113,16 +89,9 @@ function isRecord(value) {
|
|
|
113
89
|
function parseOptions(value) {
|
|
114
90
|
if (!value || typeof value !== "object") throw new Error("Visual Review source loader options are missing");
|
|
115
91
|
const options = value;
|
|
116
|
-
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");
|
|
117
92
|
if (options.projectRoot !== void 0 && typeof options.projectRoot !== "string") throw new Error("Visual Review source loader project root is invalid");
|
|
118
|
-
if (options.embedSourcePath !== void 0 && typeof options.embedSourcePath !== "boolean") throw new Error("Visual Review source loader embedSourcePath is invalid");
|
|
119
|
-
const markerSalt = typeof options.saltPath === "string" ? readMarkerSaltFile(options.saltPath) : options.markerSalt;
|
|
120
|
-
if (typeof markerSalt !== "string" || !MARKER_SALT.test(markerSalt)) throw new Error("Visual Review source loader marker salt is invalid");
|
|
121
93
|
return {
|
|
122
94
|
...options.compile === true ? { compile: true } : {},
|
|
123
|
-
embedSourcePath: options.embedSourcePath ?? true,
|
|
124
|
-
indexUrl: options.indexUrl,
|
|
125
|
-
markerSalt,
|
|
126
95
|
...typeof options.projectRoot === "string" ? { projectRoot: options.projectRoot } : {}
|
|
127
96
|
};
|
|
128
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,129 +1,47 @@
|
|
|
1
|
-
import { t as resolveVisualReviewBuildIdentity } from "./build-identity-
|
|
2
|
-
import {
|
|
3
|
-
import { t as instrumentVisualReviewSource } from "./source-instrumentation-transform-Cx8SIIWh.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 embedSourcePath = true;
|
|
17
|
-
let sourceIndexFilename;
|
|
18
|
-
let sourceIndexUrl;
|
|
19
|
-
let sourceMarkerSalt;
|
|
20
12
|
let releaseMode;
|
|
21
|
-
let serving = false;
|
|
22
13
|
return {
|
|
23
14
|
name: "01works:visual-review-build-identity",
|
|
24
15
|
enforce: "pre",
|
|
25
16
|
config(config, environment) {
|
|
26
17
|
identity = resolveVisualReviewBuildIdentity(options, {
|
|
27
|
-
cwd:
|
|
18
|
+
cwd: config.root ?? ".",
|
|
28
19
|
env: process.env
|
|
29
20
|
});
|
|
30
21
|
releaseMode = typeof environment?.mode === "string" && environment.mode.length > 0 ? normalizeReleaseMode(environment.mode) : void 0;
|
|
31
22
|
const define = publicLiteralDefinitions(identity, releaseMode);
|
|
32
23
|
assertReservedLiterals(config.define, define, "Vite define");
|
|
33
|
-
if (environment?.command === "serve") {
|
|
34
|
-
sourceMapUpload = void 0;
|
|
35
|
-
serving = true;
|
|
36
|
-
embedSourcePath = options.embedSourcePath ?? true;
|
|
37
|
-
sourceMetadataEnabled = (options.sourceMetadata ?? true) && embedSourcePath;
|
|
38
|
-
sourceMarkerSalt = sourceMetadataEnabled ? randomBytes(32).toString("hex") : void 0;
|
|
39
|
-
return { define };
|
|
40
|
-
}
|
|
41
|
-
sourceMapUpload = resolveSourceMapUpload(options.sourceMaps, process.env);
|
|
42
24
|
sourceMetadataEnabled = options.sourceMetadata ?? true;
|
|
43
|
-
|
|
44
|
-
sourceMarkerSalt = sourceMetadataEnabled ? randomBytes(32).toString("hex") : void 0;
|
|
45
|
-
if (!sourceMapUpload) return { define };
|
|
46
|
-
if (config.build?.sourcemap === "inline") throw new Error("Visual Review source-map upload refuses inline Vite source maps");
|
|
47
|
-
return {
|
|
48
|
-
define,
|
|
49
|
-
build: { sourcemap: "hidden" }
|
|
50
|
-
};
|
|
25
|
+
return { define };
|
|
51
26
|
},
|
|
52
27
|
configResolved(config) {
|
|
53
28
|
resolvedConfig = config;
|
|
54
29
|
if (releaseMode !== void 0 && normalizeReleaseMode(config.mode) !== releaseMode) throw new Error("Visual Review build mode changed after the public literals were injected");
|
|
55
|
-
if (sourceMetadataEnabled) {
|
|
56
|
-
if (!identity || !sourceMarkerSalt) throw new Error("Visual Review source metadata identity is unavailable");
|
|
57
|
-
sourceIndexFilename = visualReviewSourceIndexFilename(identity.buildId, sourceMarkerSalt);
|
|
58
|
-
sourceIndexUrl = viteSourceIndexUrl(config.base, sourceIndexFilename);
|
|
59
|
-
}
|
|
60
|
-
if (!sourceMapUpload) return;
|
|
61
|
-
if (config.build.sourcemap !== "hidden") throw new Error("Visual Review source-map upload requires build.sourcemap=\"hidden\"");
|
|
62
|
-
if (config.build.ssr) throw new Error("Visual Review Vite source-map upload currently supports browser builds only");
|
|
63
30
|
},
|
|
64
31
|
transform(source, id) {
|
|
65
32
|
if (!sourceMetadataEnabled) return null;
|
|
66
|
-
if (!resolvedConfig
|
|
33
|
+
if (!resolvedConfig) throw new Error("Visual Review source metadata was not initialized by Vite");
|
|
67
34
|
const transformed = instrumentVisualReviewSource(source, {
|
|
68
35
|
id,
|
|
69
|
-
|
|
70
|
-
markerSalt: sourceMarkerSalt,
|
|
71
|
-
projectRoot: resolvedConfig.root,
|
|
72
|
-
embedSourcePath
|
|
36
|
+
projectRoot: resolvedConfig.root
|
|
73
37
|
});
|
|
74
38
|
return transformed ? {
|
|
75
39
|
code: transformed.code,
|
|
76
40
|
map: transformed.map
|
|
77
41
|
} : null;
|
|
78
|
-
},
|
|
79
|
-
async closeBundle() {
|
|
80
|
-
if (serving) return;
|
|
81
|
-
if (!identity || !resolvedConfig) throw new Error("Visual Review build adapter was not initialized by Vite");
|
|
82
|
-
const outDir = resolve(resolvedConfig.root, resolvedConfig.build.outDir);
|
|
83
|
-
if (sourceMetadataEnabled && !embedSourcePath) {
|
|
84
|
-
if (!sourceIndexFilename) throw new Error("Visual Review source metadata was not initialized by Vite");
|
|
85
|
-
try {
|
|
86
|
-
await writeViteVisualReviewSourceIndex({
|
|
87
|
-
projectRoot: resolvedConfig.root,
|
|
88
|
-
outDir,
|
|
89
|
-
base: resolvedConfig.base,
|
|
90
|
-
filename: sourceIndexFilename
|
|
91
|
-
});
|
|
92
|
-
} catch (sourceIndexError) {
|
|
93
|
-
if (!sourceMapUpload) throw sourceIndexError;
|
|
94
|
-
try {
|
|
95
|
-
await cleanupViteSourceMapOutput(resolvedConfig.root, outDir);
|
|
96
|
-
} catch (cleanupError) {
|
|
97
|
-
throw new AggregateError([sourceIndexError, cleanupError], "Visual Review source index failed and Vite source maps could not be cleaned");
|
|
98
|
-
}
|
|
99
|
-
throw sourceIndexError;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (!sourceMapUpload) return;
|
|
103
|
-
await uploadViteSourceMaps({
|
|
104
|
-
projectRoot: resolvedConfig.root,
|
|
105
|
-
outDir,
|
|
106
|
-
base: resolvedConfig.base,
|
|
107
|
-
token: sourceMapUpload.token,
|
|
108
|
-
...sourceMapUpload.serviceUrl ? { serviceUrl: sourceMapUpload.serviceUrl } : {},
|
|
109
|
-
buildId: identity.buildId,
|
|
110
|
-
gitCommit: identity.gitCommit,
|
|
111
|
-
mode: normalizeReleaseMode(resolvedConfig.mode)
|
|
112
|
-
});
|
|
113
42
|
}
|
|
114
43
|
};
|
|
115
44
|
}
|
|
116
|
-
function resolveSourceMapUpload(option, env) {
|
|
117
|
-
if (option === false || typeof option === "object" && option.enabled === false) return;
|
|
118
|
-
const configured = typeof option === "object" ? option : void 0;
|
|
119
|
-
const token = configured?.token ?? env.VISUAL_REVIEW_SOURCE_MAP_TOKEN;
|
|
120
|
-
if (!(option === true || configured !== void 0 || token !== void 0)) return void 0;
|
|
121
|
-
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");
|
|
122
|
-
return {
|
|
123
|
-
token,
|
|
124
|
-
...configured?.serviceUrl ? { serviceUrl: configured.serviceUrl } : {}
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
45
|
function normalizeReleaseMode(mode) {
|
|
128
46
|
if (mode === "production") return "production";
|
|
129
47
|
if (mode === "development") return "development";
|