@varlock/nextjs-integration 1.0.0 → 1.0.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/dist/plugin.js CHANGED
@@ -1,15 +1,239 @@
1
1
  'use strict';
2
2
 
3
- var fs = require('fs');
4
- var path = require('path');
3
+ var fs3 = require('fs');
4
+ var path2 = require('path');
5
5
  var env = require('varlock/env');
6
- var patchServerResponse = require('varlock/patch-server-response');
7
6
  var patchConsole = require('varlock/patch-console');
7
+ var patchServerResponse = require('varlock/patch-server-response');
8
+ require('varlock');
8
9
 
9
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
11
 
11
- var fs__default = /*#__PURE__*/_interopDefault(fs);
12
- var path__default = /*#__PURE__*/_interopDefault(path);
12
+ var fs3__default = /*#__PURE__*/_interopDefault(fs3);
13
+ var path2__default = /*#__PURE__*/_interopDefault(path2);
14
+
15
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
16
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
17
+ }) : x)(function(x) {
18
+ if (typeof require !== "undefined") return require.apply(this, arguments);
19
+ throw Error('Dynamic require of "' + x + '" is not supported');
20
+ });
21
+ var WEBPACK_PLUGIN_NAME = "VarlockNextWebpackPlugin";
22
+ var latestLoadedVarlockEnv;
23
+ function createStaticReplacementsProxy(debug3) {
24
+ return new Proxy({}, {
25
+ ownKeys(_target) {
26
+ latestLoadedVarlockEnv = JSON.parse(process.env.__VARLOCK_ENV || "{}");
27
+ const replaceKeys = [];
28
+ for (const itemKey in latestLoadedVarlockEnv.config) {
29
+ const item = latestLoadedVarlockEnv.config[itemKey];
30
+ if (!item.isSensitive) replaceKeys.push(`ENV.${itemKey}`);
31
+ }
32
+ debug3("reloaded static replacements keys", replaceKeys);
33
+ return replaceKeys;
34
+ },
35
+ getOwnPropertyDescriptor(_target, prop) {
36
+ const itemKey = prop.toString().split(".")[1];
37
+ const item = latestLoadedVarlockEnv?.config[itemKey];
38
+ if (!item || item.isSensitive) return;
39
+ return {
40
+ value: "",
41
+ // this value is not used, the get handler will return the value
42
+ writable: false,
43
+ enumerable: true,
44
+ configurable: true
45
+ };
46
+ },
47
+ get(_target, prop) {
48
+ const itemKey = prop.toString().split(".")[1];
49
+ const item = latestLoadedVarlockEnv?.config[itemKey];
50
+ if (item && !item.isSensitive) return JSON.stringify(item.value);
51
+ }
52
+ });
53
+ }
54
+ function createWebpackConfigFn(resolvedNextConfig, patchGlobalFsMethods2, debug3, isBuild) {
55
+ const staticReplacementsProxy = createStaticReplacementsProxy(debug3);
56
+ return function webpackConfigFn(webpackConfig, options) {
57
+ debug3("varlockNextConfigPlugin webpack config patching");
58
+ const { dev } = options;
59
+ if (isBuild) patchGlobalFsMethods2();
60
+ if (env.varlockSettings.preventLeaks) {
61
+ patchServerResponse.patchGlobalServerResponse({
62
+ ignoreUrlPatterns: [
63
+ /^\/__nextjs_source-map\?.*/,
64
+ // sourcemaps
65
+ /[?&]_rsc=/
66
+ // RSC payloads are server-side and expected to contain sensitive data
67
+ ],
68
+ // in dev mode, we redact the secrets rather than throwing, because otherwise the dev server crashes
69
+ redactInsteadOfThrow: dev
70
+ });
71
+ }
72
+ const webpack = options.webpack;
73
+ if (resolvedNextConfig.webpack) {
74
+ webpackConfig = resolvedNextConfig.webpack(webpackConfig, options);
75
+ }
76
+ if (!process.env.__VARLOCK_ENV) throw new Error("VarlockNextWebpackPlugin: __VARLOCK_ENV is not set");
77
+ if (options.isServer && options.nextRuntime !== "edge") {
78
+ webpackConfig.module.rules.push({
79
+ test: /\.(js|jsx|ts|tsx|mjs|mts)$/,
80
+ exclude: /node_modules/,
81
+ use: [
82
+ {
83
+ loader: __require.resolve("./loader"),
84
+ options: { bundler: "webpack" }
85
+ }
86
+ ]
87
+ });
88
+ }
89
+ if (options.isServer && options.nextRuntime === "edge") {
90
+ webpackConfig.module.rules.push({
91
+ test: /\.(js|jsx|ts|tsx|mjs|mts)$/,
92
+ exclude: /node_modules/,
93
+ use: [
94
+ {
95
+ loader: __require.resolve("./loader"),
96
+ options: { bundler: "webpack", isEdge: true }
97
+ }
98
+ ]
99
+ });
100
+ }
101
+ debug3("adding ENV.xxx static replacements proxy object");
102
+ webpackConfig.plugins.push(new webpack.DefinePlugin(staticReplacementsProxy));
103
+ if (env.varlockSettings.preventLeaks) {
104
+ webpackConfig.plugins.push({
105
+ apply(compiler) {
106
+ compiler.hooks.assetEmitted.tap(WEBPACK_PLUGIN_NAME, (_file, assetDetails) => {
107
+ const { content, targetPath } = assetDetails;
108
+ if (targetPath.includes("/.next/static/chunks/") || targetPath.endsWith(".html")) {
109
+ try {
110
+ env.scanForLeaks(content, {
111
+ method: "@varlock/nextjs-integration/plugin - assetEmitted hook",
112
+ file: targetPath
113
+ });
114
+ } catch (err) {
115
+ if (dev) {
116
+ fs3__default.default.writeFileSync(targetPath, env.redactSensitiveConfig(content.toString()));
117
+ } else {
118
+ throw err;
119
+ }
120
+ }
121
+ }
122
+ });
123
+ }
124
+ });
125
+ }
126
+ function injectVarlockInitIntoWebpackRuntime(edgeRuntime = false) {
127
+ return function assetUpdateFn(origSource) {
128
+ const origSourceStr = origSource.source();
129
+ const initBundleName = edgeRuntime ? "init-edge" : "init-server";
130
+ const injectorPath = __require.resolve(`varlock/${initBundleName}`);
131
+ const injectorSrc = fs3__default.default.readFileSync(injectorPath, "utf8");
132
+ const rawEnv = process.env.__VARLOCK_ENV;
133
+ const envInline = rawEnv ? `process.env.__VARLOCK_ENV = process.env.__VARLOCK_ENV || ${JSON.stringify(rawEnv)};` : "";
134
+ const updatedSourceStr = [
135
+ envInline,
136
+ // Wrap in IIFE to avoid symbol collisions when bundlers concatenate files.
137
+ // Provide dummy exports/module since the CJS bundle uses `exports.X = ...`
138
+ `(function(exports,module){${injectorSrc}})({},{exports:{}});`,
139
+ origSourceStr
140
+ ].join("\n");
141
+ return new webpack.sources.RawSource(updatedSourceStr);
142
+ };
143
+ }
144
+ const isEdgeRuntime = options.nextRuntime === "edge";
145
+ webpackConfig.plugins.push({
146
+ apply(compiler) {
147
+ compiler.hooks.thisCompilation.tap(WEBPACK_PLUGIN_NAME, (compilation) => {
148
+ compilation.hooks.processAssets.tap(
149
+ {
150
+ name: WEBPACK_PLUGIN_NAME,
151
+ stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
152
+ },
153
+ () => {
154
+ if (isEdgeRuntime) {
155
+ if (compilation.getAsset("edge-runtime-webpack.js")) {
156
+ compilation.updateAsset("edge-runtime-webpack.js", injectVarlockInitIntoWebpackRuntime(true));
157
+ }
158
+ for (const name of ["webpack-runtime.js", "../webpack-runtime.js"]) {
159
+ if (compilation.getAsset(name)) {
160
+ compilation.updateAsset(name, injectVarlockInitIntoWebpackRuntime(true));
161
+ }
162
+ }
163
+ } else if (options.isServer) {
164
+ for (const name of ["webpack-runtime.js", "../webpack-runtime.js", "webpack-api-runtime.js", "../webpack-api-runtime.js"]) {
165
+ if (compilation.getAsset(name)) {
166
+ compilation.updateAsset(name, injectVarlockInitIntoWebpackRuntime());
167
+ }
168
+ }
169
+ }
170
+ }
171
+ );
172
+ });
173
+ }
174
+ });
175
+ return webpackConfig;
176
+ };
177
+ }
178
+ function debug(...args) {
179
+ if (!process.env.DEBUG_VARLOCK_NEXT_INTEGRATION) return;
180
+ console.log("[varlock]", ...args);
181
+ }
182
+ var injectedTurbopackRuntime = false;
183
+ function injectVarlockInitIntoTurbopackRuntime(nextDirPath) {
184
+ if (injectedTurbopackRuntime) return;
185
+ const rawEnv = process.env.__VARLOCK_ENV;
186
+ if (!rawEnv) {
187
+ return;
188
+ }
189
+ const serverRuntimeFiles = [];
190
+ const edgeWrapperFiles = [];
191
+ const walkDir = (dir) => {
192
+ if (!fs3__default.default.existsSync(dir)) return;
193
+ for (const entry of fs3__default.default.readdirSync(dir, { withFileTypes: true })) {
194
+ if (entry.isDirectory()) {
195
+ walkDir(path2__default.default.join(dir, entry.name));
196
+ } else if (entry.name === "[turbopack]_runtime.js") {
197
+ serverRuntimeFiles.push(path2__default.default.join(dir, entry.name));
198
+ } else if (entry.name.includes("edge-wrapper") && entry.name.endsWith(".js")) {
199
+ edgeWrapperFiles.push(path2__default.default.join(dir, entry.name));
200
+ }
201
+ }
202
+ };
203
+ walkDir(nextDirPath);
204
+ debug(`turbopack runtime injection: found ${serverRuntimeFiles.length} server runtime files, ${edgeWrapperFiles.length} edge wrapper files`);
205
+ if (!serverRuntimeFiles.length) {
206
+ return;
207
+ }
208
+ injectedTurbopackRuntime = true;
209
+ const initServerSrc = fs3__default.default.readFileSync(__require.resolve("varlock/init-server"), "utf8");
210
+ const initEdgeSrc = fs3__default.default.readFileSync(__require.resolve("varlock/init-edge"), "utf8");
211
+ const envInline = `process.env.__VARLOCK_ENV = process.env.__VARLOCK_ENV || ${JSON.stringify(rawEnv)};`;
212
+ const iifeWrap = (src) => `(function(exports,module){${src}})({},{exports:{}});`;
213
+ for (const runtimeFile of serverRuntimeFiles) {
214
+ const origSource = fs3__default.default.readFileSync(runtimeFile, "utf8");
215
+ const updatedSource = [
216
+ envInline,
217
+ iifeWrap(initServerSrc),
218
+ origSource
219
+ ].join("\n");
220
+ fs3__default.default.writeFileSync(runtimeFile, updatedSource);
221
+ debug(`injected init-server into turbopack runtime: ${runtimeFile}`);
222
+ }
223
+ for (const wrapperFile of edgeWrapperFiles) {
224
+ const origSource = fs3__default.default.readFileSync(wrapperFile, "utf8");
225
+ const updatedSource = [
226
+ envInline,
227
+ iifeWrap(initEdgeSrc),
228
+ origSource
229
+ ].join("\n");
230
+ fs3__default.default.writeFileSync(wrapperFile, updatedSource);
231
+ debug(`injected init-edge into edge wrapper: ${wrapperFile}`);
232
+ }
233
+ }
234
+ function isInjectedTurbopackRuntime() {
235
+ return injectedTurbopackRuntime;
236
+ }
13
237
 
14
238
  // src/plugin.ts
15
239
  if (!process.env.__VARLOCK_ENV) {
@@ -23,8 +247,21 @@ if (!process.env.__VARLOCK_ENV) {
23
247
  throw new Error("VarlockNextWebpackPlugin: __VARLOCK_ENV is not set");
24
248
  }
25
249
  patchConsole.patchGlobalConsole();
250
+ var IS_TURBOPACK = !!(process.env.TURBOPACK || process.env.TURBOPACK_DEV || process.env.TURBOPACK_BUILD || process.env.npm_config_turbopack);
251
+ if (IS_TURBOPACK && env.varlockSettings.preventLeaks) {
252
+ patchServerResponse.patchGlobalServerResponse({
253
+ ignoreUrlPatterns: [
254
+ /^\/__nextjs_source-map\?.*/,
255
+ // sourcemaps
256
+ /[?&]_rsc=/
257
+ // RSC payloads are server-side and expected to contain sensitive data
258
+ ],
259
+ // always redact in dev to avoid crashing the dev server; prod builds override via init bundles
260
+ redactInsteadOfThrow: true
261
+ });
262
+ }
26
263
  var IS_WORKER = !!process.env.NEXT_PRIVATE_WORKER;
27
- function debug(...args) {
264
+ function debug2(...args) {
28
265
  if (!process.env.DEBUG_VARLOCK_NEXT_INTEGRATION) return;
29
266
  console.log(
30
267
  "plugin",
@@ -33,60 +270,148 @@ function debug(...args) {
33
270
  ...args
34
271
  );
35
272
  }
36
- debug("\u2728 LOADED @varlock/next-integration/plugin module!");
37
- var WEBPACK_PLUGIN_NAME = "VarlockNextWebpackPlugin";
273
+ debug2("\u2728 LOADED @varlock/next-integration/plugin module!");
38
274
  var scannedStaticFiles = false;
39
275
  async function scanStaticFiles(nextDirPath) {
40
276
  scannedStaticFiles = true;
41
- for await (const file of fs__default.default.promises.glob(`${nextDirPath}/**/*.html`)) {
42
- const fileContents = await fs__default.default.promises.readFile(file, "utf8");
277
+ for await (const file of fs3__default.default.promises.glob(`${nextDirPath}/**/*.html`)) {
278
+ const fileContents = await fs3__default.default.promises.readFile(file, "utf8");
43
279
  env.scanForLeaks(fileContents, { method: "nextjs scan static html files", file });
44
280
  }
45
281
  }
282
+ var scrubbedSourcemaps = false;
283
+ async function scrubSourcemaps(nextDirPath) {
284
+ if (scrubbedSourcemaps) return;
285
+ scrubbedSourcemaps = true;
286
+ const envGraph = JSON.parse(process.env.__VARLOCK_ENV || "{}");
287
+ const sensitiveValues = [];
288
+ for (const itemKey in envGraph.config) {
289
+ const item = envGraph.config[itemKey];
290
+ if (item.isSensitive && item.value && typeof item.value === "string" && item.value.length > 0) {
291
+ sensitiveValues.push({
292
+ value: item.value,
293
+ replacement: "*".repeat(item.value.length)
294
+ });
295
+ }
296
+ }
297
+ if (!sensitiveValues.length) {
298
+ debug2("no sensitive values to scrub from sourcemaps");
299
+ return;
300
+ }
301
+ sensitiveValues.sort((a, b) => b.value.length - a.value.length);
302
+ let scrubCount = 0;
303
+ for await (const mapFile of fs3__default.default.promises.glob(`${nextDirPath}/**/*.map`)) {
304
+ const contents = await fs3__default.default.promises.readFile(mapFile, "utf8");
305
+ let scrubbed = contents;
306
+ for (const { value, replacement } of sensitiveValues) {
307
+ scrubbed = scrubbed.replaceAll(value, replacement);
308
+ }
309
+ if (scrubbed !== contents) {
310
+ await fs3__default.default.promises.writeFile(mapFile, scrubbed);
311
+ scrubCount++;
312
+ }
313
+ }
314
+ debug2(`scrubbed sensitive values from ${scrubCount} sourcemap files`);
315
+ }
316
+ var scannedBuildOutput = false;
317
+ async function scanBuildOutputForLeaks(nextDirPath, opts) {
318
+ if (scannedBuildOutput) return;
319
+ scannedBuildOutput = true;
320
+ let preventLeaks = env.varlockSettings.preventLeaks;
321
+ if (preventLeaks === void 0 && process.env.__VARLOCK_ENV) {
322
+ try {
323
+ const envGraph = JSON.parse(process.env.__VARLOCK_ENV);
324
+ preventLeaks = envGraph.settings?.preventLeaks;
325
+ } catch {
326
+ }
327
+ }
328
+ if (process.env.__VARLOCK_ENV) {
329
+ try {
330
+ env.initVarlockEnv();
331
+ } catch {
332
+ }
333
+ }
334
+ const redactionInfo = env.getRedactionMapInfo();
335
+ debug2(`scanBuildOutputForLeaks: preventLeaks=${preventLeaks}, redactionInfo=${JSON.stringify(redactionInfo)}`);
336
+ if (!preventLeaks) return;
337
+ const leakedFiles = [];
338
+ let scannedCount = 0;
339
+ for await (const file of fs3__default.default.promises.glob(`${nextDirPath}/static/chunks/**/*.js`)) {
340
+ scannedCount++;
341
+ const fileContents = await fs3__default.default.promises.readFile(file, "utf8");
342
+ try {
343
+ env.scanForLeaks(fileContents, {
344
+ method: "nextjs post-build scan (static chunks)",
345
+ file
346
+ });
347
+ } catch (err) {
348
+ leakedFiles.push(file);
349
+ await fs3__default.default.promises.writeFile(file, env.redactSensitiveConfig(fileContents));
350
+ }
351
+ }
352
+ debug2(`scanned ${scannedCount} static chunk files in ${nextDirPath}/static/chunks/`);
353
+ for (const ext of ["html"]) {
354
+ for await (const file of fs3__default.default.promises.glob(`${nextDirPath}/**/*.${ext}`)) {
355
+ scannedCount++;
356
+ const fileContents = await fs3__default.default.promises.readFile(file, "utf8");
357
+ try {
358
+ env.scanForLeaks(fileContents, {
359
+ method: `nextjs post-build scan (.${ext})`,
360
+ file
361
+ });
362
+ } catch (err) {
363
+ leakedFiles.push(file);
364
+ await fs3__default.default.promises.writeFile(file, env.redactSensitiveConfig(fileContents));
365
+ }
366
+ }
367
+ }
368
+ debug2(`scanned ${scannedCount} total build output files`);
369
+ if (leakedFiles.length > 0) {
370
+ const msg = `[varlock] \u26A0\uFE0F found and redacted leaked secrets in ${leakedFiles.length} build output file(s):
371
+ ${leakedFiles.map((f) => ` - ${f}`).join("\n")}`;
372
+ if (opts?.failBuild) {
373
+ throw new Error(msg);
374
+ }
375
+ console.error(msg);
376
+ } else {
377
+ debug2("\u2705 no leaks found in build output");
378
+ }
379
+ }
46
380
  function patchGlobalFsMethods() {
47
- const origWriteFileFn = fs__default.default.promises.writeFile;
48
- fs__default.default.promises.writeFile = async function dmnoPatchedWriteFile(...args) {
381
+ debug2("patching global fs methods");
382
+ const origWriteFileFn = fs3__default.default.promises.writeFile;
383
+ fs3__default.default.promises.writeFile = async function dmnoPatchedWriteFile(...args) {
49
384
  const filePath = args[0].toString();
385
+ debug2("fs.promises.writeFile:", filePath);
386
+ if (!isInjectedTurbopackRuntime() && filePath.endsWith("/.next/export-detail.json")) {
387
+ const nextDirPath = filePath.substring(0, filePath.lastIndexOf("/"));
388
+ injectVarlockInitIntoTurbopackRuntime(nextDirPath);
389
+ }
50
390
  if (filePath.endsWith("/.next/next-server.js.nft.json") && !scannedStaticFiles) {
51
391
  const nextDirPath = filePath.substring(0, filePath.lastIndexOf("/"));
52
392
  await scanStaticFiles(nextDirPath);
53
393
  }
394
+ if (filePath.endsWith("/.next/next-server.js.nft.json") || filePath.endsWith("/.next/prerender-manifest.json")) {
395
+ const nextDirPath = filePath.substring(0, filePath.lastIndexOf("/"));
396
+ if (!scrubbedSourcemaps) await scrubSourcemaps(nextDirPath);
397
+ if (!scannedBuildOutput) await scanBuildOutputForLeaks(nextDirPath, { failBuild: true });
398
+ }
54
399
  return origWriteFileFn.call(this, ...args);
55
400
  };
401
+ const origWriteFileSyncFn = fs3__default.default.writeFileSync;
402
+ fs3__default.default.writeFileSync = function dmnoPatchedWriteFileSync(...args) {
403
+ const filePath = args[0].toString();
404
+ debug2("fs.writeFileSync:", filePath);
405
+ if (!isInjectedTurbopackRuntime() && filePath.endsWith("/.next/diagnostics/build-diagnostics.json")) {
406
+ const nextDirPath = filePath.substring(0, filePath.lastIndexOf("/"));
407
+ injectVarlockInitIntoTurbopackRuntime(nextDirPath);
408
+ }
409
+ return origWriteFileSyncFn.call(this, ...args);
410
+ };
56
411
  }
57
- var latestLoadedVarlockEnv;
58
- var StaticReplacementsProxy = new Proxy({}, {
59
- ownKeys(_target) {
60
- latestLoadedVarlockEnv = JSON.parse(process.env.__VARLOCK_ENV || "{}");
61
- const replaceKeys = [];
62
- for (const itemKey in latestLoadedVarlockEnv.config) {
63
- const item = latestLoadedVarlockEnv.config[itemKey];
64
- if (!item.isSensitive) replaceKeys.push(`ENV.${itemKey}`);
65
- }
66
- debug("reloaded static replacements keys", replaceKeys);
67
- return replaceKeys;
68
- },
69
- getOwnPropertyDescriptor(_target, prop) {
70
- const itemKey = prop.toString().split(".")[1];
71
- const item = latestLoadedVarlockEnv?.config[itemKey];
72
- if (!item || item.isSensitive) return;
73
- return {
74
- value: "",
75
- // this value is not used, the get handler will return the value
76
- writable: false,
77
- enumerable: true,
78
- configurable: true
79
- };
80
- },
81
- get(_target, prop) {
82
- const itemKey = prop.toString().split(".")[1];
83
- const item = latestLoadedVarlockEnv?.config[itemKey];
84
- if (item && !item.isSensitive) return JSON.stringify(item.value);
85
- }
86
- });
87
412
  function varlockNextConfigPlugin(_pluginOptions) {
88
413
  return (nextConfig) => {
89
- debug("varlockNextConfigPlugin init fn");
414
+ debug2("varlockNextConfigPlugin init fn");
90
415
  return async (phase, defaults) => {
91
416
  let resolvedNextConfig;
92
417
  if (typeof nextConfig === "function") {
@@ -95,107 +420,75 @@ function varlockNextConfigPlugin(_pluginOptions) {
95
420
  } else {
96
421
  resolvedNextConfig = nextConfig;
97
422
  }
98
- if (process.env.TURBOPACK || process.env.npm_config_turbopack) {
99
- console.error([
100
- "\u{1F6A8} @varlock/nextjs-integration: Turbopack is not yet supported for varlockNextConfigPlugin \u{1F6A8}",
101
- "",
102
- "You can either stop using the `--turbopack` flag",
103
- "or remove this plugin from your config, and only use the @next/env override.",
104
- "However if you don't use the plugin, you will not get all the benefits of this integration.",
105
- ""
106
- ].join("\n"));
107
- throw new Error("varlockNextConfigPlugin: Turbopack is not yet supported");
108
- }
109
- return {
110
- ...resolvedNextConfig,
111
- webpack(webpackConfig, options) {
112
- debug("varlockNextConfigPlugin webpack config patching");
113
- const { dev } = options;
114
- if (env.varlockSettings.preventLeaks) {
115
- patchGlobalFsMethods();
116
- patchServerResponse.patchGlobalServerResponse({
117
- // ignore sourcemaps - although we may in future want to scrub them?
118
- ignoreUrlPatterns: [/^\/__nextjs_source-map\?.*/],
119
- // in dev mode, we redact the secrets rather than throwing, because otherwise the dev server crashes
120
- redactInsteadOfThrow: dev
121
- });
122
- }
123
- const webpack = options.webpack;
124
- if (resolvedNextConfig.webpack) {
125
- webpackConfig = resolvedNextConfig.webpack(webpackConfig, options);
126
- }
127
- if (!process.env.__VARLOCK_ENV) throw new Error("VarlockNextWebpackPlugin: __VARLOCK_ENV is not set");
128
- debug("adding ENV.xxx static replacements proxy object");
129
- webpackConfig.plugins.push(new webpack.DefinePlugin(StaticReplacementsProxy));
130
- if (env.varlockSettings.preventLeaks) {
131
- webpackConfig.plugins.push({
132
- apply(compiler) {
133
- compiler.hooks.assetEmitted.tap(WEBPACK_PLUGIN_NAME, (file, assetDetails) => {
134
- const { content, targetPath } = assetDetails;
135
- if (targetPath.includes("/.next/static/chunks/") || targetPath.endsWith(".html") || targetPath.endsWith(".body") || targetPath.endsWith(".rsc")) {
136
- try {
137
- env.scanForLeaks(content, {
138
- method: "@varlock/nextjs-integration/plugin - assetEmitted hook",
139
- file: targetPath
140
- });
141
- } catch (err) {
142
- if (dev) {
143
- fs__default.default.writeFileSync(targetPath, env.redactSensitiveConfig(content.toString()));
144
- } else {
145
- throw err;
146
- }
147
- }
148
- }
149
- });
150
- }
423
+ const isTurbopack = !!(process.env.TURBOPACK || process.env.TURBOPACK_DEV || process.env.TURBOPACK_BUILD || process.env.npm_config_turbopack);
424
+ const isBuild = phase === "phase-production-build";
425
+ debug2(`turbopack detection: TURBOPACK=${process.env.TURBOPACK}, TURBOPACK_DEV=${process.env.TURBOPACK_DEV}, TURBOPACK_BUILD=${process.env.TURBOPACK_BUILD}, phase=${phase}, isTurbopack=${isTurbopack}, isBuild=${isBuild}`);
426
+ if (isBuild) {
427
+ process.once("beforeExit", () => {
428
+ const nextDirPath = path2__default.default.resolve(process.cwd(), ".next");
429
+ const scanPromises = [];
430
+ if (!scrubbedSourcemaps) scanPromises.push(scrubSourcemaps(nextDirPath));
431
+ if (!scannedBuildOutput) scanPromises.push(scanBuildOutputForLeaks(nextDirPath, { failBuild: true }));
432
+ if (scanPromises.length > 0) {
433
+ Promise.all(scanPromises).catch((err) => {
434
+ console.error("[varlock] post-build scan failed:", err);
435
+ process.exitCode = 1;
151
436
  });
152
437
  }
153
- function injectVarlockInitIntoWebpackRuntime(_edgeRuntime = false) {
154
- return function assetUpdateFn(origSource) {
155
- const origSourceStr = origSource.source();
156
- const injectorPath = path__default.default.resolve(__dirname, "./patch-next-runtime.js");
157
- const injectorSrc = fs__default.default.readFileSync(injectorPath, "utf8");
158
- const updatedSourceStr = [
159
- // TODO: reimplement dynamic/static functionality, which triggers a call to next/headers (or similar)
160
- // when accessing a dynamic env item. This will force the page into dynamic rendering mode
161
- // see DMNO integration for more details
162
- // inline the dmno injector code
163
- injectorSrc,
164
- origSourceStr
165
- ].join("\n");
166
- return new webpack.sources.RawSource(updatedSourceStr);
167
- };
438
+ });
439
+ }
440
+ if (isTurbopack) {
441
+ debug2("turbopack detected, injecting loader rules");
442
+ if (isBuild) patchGlobalFsMethods();
443
+ let turbopackConfig = resolvedNextConfig.turbopack ?? resolvedNextConfig.experimental?.turbo;
444
+ if (!turbopackConfig) {
445
+ turbopackConfig = {};
446
+ resolvedNextConfig.turbopack = turbopackConfig;
447
+ }
448
+ const loaderRule = {
449
+ loaders: [__require.resolve("./loader")]
450
+ };
451
+ turbopackConfig.rules ||= {};
452
+ turbopackConfig.rules["*.{js,jsx,ts,tsx,mjs,mts}"] = loaderRule;
453
+ const varlockNodeModulesPath = path2__default.default.resolve(process.cwd(), "node_modules/varlock");
454
+ let isSymlinked = false;
455
+ try {
456
+ isSymlinked = fs3__default.default.lstatSync(varlockNodeModulesPath).isSymbolicLink();
457
+ } catch {
458
+ }
459
+ if (isSymlinked) {
460
+ debug2("varlock is symlinked, copying dist files for turbopack");
461
+ const varlockDistDir = path2__default.default.resolve(path2__default.default.dirname(__require.resolve("varlock/env")), "..");
462
+ const varlockRoot = path2__default.default.resolve(varlockDistDir, "..");
463
+ const cacheDir = path2__default.default.resolve(process.cwd(), "node_modules/.varlock");
464
+ const cacheDistDir = path2__default.default.join(cacheDir, "dist");
465
+ try {
466
+ fs3__default.default.mkdirSync(cacheDir, { recursive: true });
467
+ fs3__default.default.cpSync(varlockDistDir, cacheDistDir, { recursive: true });
468
+ fs3__default.default.copyFileSync(path2__default.default.join(varlockRoot, "package.json"), path2__default.default.join(cacheDir, "package.json"));
469
+ debug2("copied varlock package files to", cacheDir);
470
+ } catch (err) {
471
+ console.warn("[varlock] failed to copy varlock package files:", err);
168
472
  }
169
- webpackConfig.plugins.push({
170
- apply(compiler) {
171
- compiler.hooks.thisCompilation.tap(WEBPACK_PLUGIN_NAME, (compilation) => {
172
- compilation.hooks.processAssets.tap(
173
- {
174
- name: WEBPACK_PLUGIN_NAME,
175
- stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
176
- },
177
- () => {
178
- if (compilation.getAsset("webpack-runtime.js")) {
179
- compilation.updateAsset("webpack-runtime.js", injectVarlockInitIntoWebpackRuntime());
180
- }
181
- if (compilation.getAsset("../webpack-runtime.js")) {
182
- compilation.updateAsset("../webpack-runtime.js", injectVarlockInitIntoWebpackRuntime());
183
- }
184
- if (compilation.getAsset("webpack-api-runtime.js")) {
185
- compilation.updateAsset("webpack-api-runtime.js", injectVarlockInitIntoWebpackRuntime());
186
- }
187
- if (compilation.getAsset("../webpack-api-runtime.js")) {
188
- compilation.updateAsset("../webpack-api-runtime.js", injectVarlockInitIntoWebpackRuntime());
189
- }
190
- if (compilation.getAsset("edge-runtime-webpack.js")) {
191
- compilation.updateAsset("edge-runtime-webpack.js", injectVarlockInitIntoWebpackRuntime(true));
192
- }
193
- }
194
- );
195
- });
196
- }
197
- });
198
- return webpackConfig;
473
+ turbopackConfig.resolveAlias ||= {};
474
+ turbopackConfig.resolveAlias["varlock/env"] = "./node_modules/.varlock/dist/runtime/env.js";
475
+ turbopackConfig.resolveAlias["varlock/patch-console"] = "./node_modules/.varlock/dist/runtime/patch-console.js";
476
+ turbopackConfig.resolveAlias["varlock/patch-server-response"] = "./node_modules/.varlock/dist/runtime/patch-server-response.js";
477
+ turbopackConfig.resolveAlias["varlock/patch-response"] = "./node_modules/.varlock/dist/runtime/patch-response.js";
478
+ turbopackConfig.resolveAlias["varlock/init-server"] = "./node_modules/.varlock/dist/runtime/init-server.cjs";
479
+ turbopackConfig.resolveAlias["varlock/init-edge"] = "./node_modules/.varlock/dist/runtime/init-edge.cjs";
480
+ debug2("set resolveAlias for varlock subpaths -> ./node_modules/.varlock/dist/...");
481
+ } else {
482
+ debug2("varlock is not symlinked, turbopack can resolve it natively");
483
+ }
484
+ }
485
+ return {
486
+ ...resolvedNextConfig,
487
+ ...isTurbopack && {
488
+ turbopack: resolvedNextConfig.turbopack
489
+ },
490
+ ...!IS_TURBOPACK && {
491
+ webpack: createWebpackConfigFn(resolvedNextConfig, patchGlobalFsMethods, debug2, isBuild)
199
492
  }
200
493
  };
201
494
  };