@hawk.so/nextjs 1.0.0 → 1.1.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 +161 -115
- package/dist/client.js +163 -0
- package/dist/{index.cjs → config.cjs} +82 -45
- package/dist/{index.d.cts → config.d.cts} +1 -1
- package/dist/config.d.ts +75 -0
- package/dist/{index.js → config.js} +72 -35
- package/dist/edge.js +435 -0
- package/dist/index.d.ts +280 -40
- package/dist/react.cjs +199 -0
- package/dist/react.js +179 -0
- package/dist/server.cjs +500 -0
- package/dist/server.js +452 -0
- package/package.json +38 -13
|
@@ -27,19 +27,44 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
30
|
+
// src/config/index.ts
|
|
31
|
+
var config_exports = {};
|
|
32
|
+
__export(config_exports, {
|
|
33
33
|
withHawkConfig: () => withHawkConfig
|
|
34
34
|
});
|
|
35
|
-
module.exports = __toCommonJS(
|
|
35
|
+
module.exports = __toCommonJS(config_exports);
|
|
36
36
|
|
|
37
|
-
// src/with-hawk-config.ts
|
|
37
|
+
// src/config/with-hawk-config.ts
|
|
38
38
|
var import_node_module = require("module");
|
|
39
|
-
var
|
|
39
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
40
40
|
var import_node_process2 = __toESM(require("process"), 1);
|
|
41
41
|
|
|
42
|
-
// src/
|
|
42
|
+
// src/config/instrumentation.ts
|
|
43
|
+
var import_promises = require("fs/promises");
|
|
44
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
45
|
+
var DEFAULT_PAGE_EXTENSIONS = ["tsx", "ts", "jsx", "js"];
|
|
46
|
+
var INSTRUMENTATION_FILES = ["instrumentation", "instrumentation-client"];
|
|
47
|
+
async function exists(filePath) {
|
|
48
|
+
try {
|
|
49
|
+
await (0, import_promises.access)(filePath);
|
|
50
|
+
return true;
|
|
51
|
+
} catch (_error) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function findMissingInstrumentationFiles(projectDir, pageExtensions = DEFAULT_PAGE_EXTENSIONS) {
|
|
56
|
+
const missing = [];
|
|
57
|
+
for (const name of INSTRUMENTATION_FILES) {
|
|
58
|
+
const candidates = ["", "src"].flatMap((dir) => pageExtensions.map((extension) => import_node_path.default.join(projectDir, dir, `${name}.${extension}`)));
|
|
59
|
+
const found = await Promise.all(candidates.map(exists));
|
|
60
|
+
if (!found.includes(true)) {
|
|
61
|
+
missing.push(name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return missing;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/config/release.ts
|
|
43
68
|
var import_node_child_process = require("child_process");
|
|
44
69
|
var import_node_process = __toESM(require("process"), 1);
|
|
45
70
|
var RELEASE_ENV_VARIABLE = "HAWK_RELEASE";
|
|
@@ -85,7 +110,7 @@ function resolveRelease(release, cwd) {
|
|
|
85
110
|
return getGitRevision(cwd) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
// src/token.ts
|
|
113
|
+
// src/config/token.ts
|
|
89
114
|
var import_node_buffer = require("buffer");
|
|
90
115
|
function getIntegrationId(token) {
|
|
91
116
|
try {
|
|
@@ -101,29 +126,29 @@ function getDefaultCollectorEndpoint(integrationId) {
|
|
|
101
126
|
return `https://${integrationId}.k1.hawk.so/release`;
|
|
102
127
|
}
|
|
103
128
|
|
|
104
|
-
// src/upload-release.ts
|
|
129
|
+
// src/config/upload-release.ts
|
|
130
|
+
var import_promises5 = require("fs/promises");
|
|
131
|
+
|
|
132
|
+
// src/config/cleanup.ts
|
|
105
133
|
var import_promises4 = require("fs/promises");
|
|
134
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
106
135
|
|
|
107
|
-
// src/
|
|
136
|
+
// src/config/sourcemaps.ts
|
|
137
|
+
var import_node_crypto = require("crypto");
|
|
108
138
|
var import_promises3 = require("fs/promises");
|
|
109
139
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
110
140
|
|
|
111
|
-
// src/
|
|
112
|
-
var
|
|
141
|
+
// src/config/utils/fs.ts
|
|
142
|
+
var import_node_buffer2 = require("buffer");
|
|
113
143
|
var import_promises2 = require("fs/promises");
|
|
114
144
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
115
|
-
|
|
116
|
-
// src/utils/fs.ts
|
|
117
|
-
var import_node_buffer2 = require("buffer");
|
|
118
|
-
var import_promises = require("fs/promises");
|
|
119
|
-
var import_node_path = __toESM(require("path"), 1);
|
|
120
145
|
var MAX_PARALLEL_FILE_OPERATIONS = 16;
|
|
121
146
|
async function listFiles(dir) {
|
|
122
147
|
try {
|
|
123
|
-
const entries = await (0,
|
|
148
|
+
const entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
|
|
124
149
|
const files = await Promise.all(entries.map(async (entry) => {
|
|
125
150
|
if (entry.isDirectory()) {
|
|
126
|
-
return (await listFiles(
|
|
151
|
+
return (await listFiles(import_node_path2.default.join(dir, entry.name))).map((file) => `${entry.name}/${file}`);
|
|
127
152
|
}
|
|
128
153
|
return entry.isFile() ? [entry.name] : [];
|
|
129
154
|
}));
|
|
@@ -136,7 +161,7 @@ async function listFiles(dir) {
|
|
|
136
161
|
}
|
|
137
162
|
}
|
|
138
163
|
async function readFileTail(filePath, maxBytes) {
|
|
139
|
-
const file = await (0,
|
|
164
|
+
const file = await (0, import_promises2.open)(filePath, "r");
|
|
140
165
|
try {
|
|
141
166
|
const { size } = await file.stat();
|
|
142
167
|
const length = Math.min(maxBytes, size);
|
|
@@ -152,7 +177,7 @@ async function readFileTail(filePath, maxBytes) {
|
|
|
152
177
|
}
|
|
153
178
|
}
|
|
154
179
|
|
|
155
|
-
// src/utils/pool.ts
|
|
180
|
+
// src/config/utils/pool.ts
|
|
156
181
|
async function mapConcurrently(items, limit, mapper) {
|
|
157
182
|
const results = [];
|
|
158
183
|
let nextIndex = 0;
|
|
@@ -168,7 +193,7 @@ async function mapConcurrently(items, limit, mapper) {
|
|
|
168
193
|
return results;
|
|
169
194
|
}
|
|
170
195
|
|
|
171
|
-
// src/sourcemaps.ts
|
|
196
|
+
// src/config/sourcemaps.ts
|
|
172
197
|
var STATIC_DIR = "static";
|
|
173
198
|
var PUBLIC_PATH_PREFIX = "/_next/";
|
|
174
199
|
var PATH_HASH_LENGTH = 8;
|
|
@@ -196,7 +221,7 @@ function getSourceMapPath(chunk, content) {
|
|
|
196
221
|
return void 0;
|
|
197
222
|
}
|
|
198
223
|
try {
|
|
199
|
-
const mapPath =
|
|
224
|
+
const mapPath = import_node_path3.default.posix.join(import_node_path3.default.posix.dirname(chunk), decodeURIComponent(url));
|
|
200
225
|
return mapPath.startsWith("../") ? void 0 : mapPath;
|
|
201
226
|
} catch (_error) {
|
|
202
227
|
return void 0;
|
|
@@ -206,28 +231,28 @@ function encodeUrlPath(filePath) {
|
|
|
206
231
|
return filePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
207
232
|
}
|
|
208
233
|
function getSourceMapName(chunk) {
|
|
209
|
-
const extension =
|
|
234
|
+
const extension = import_node_path3.default.posix.extname(chunk);
|
|
210
235
|
const hash = (0, import_node_crypto.createHash)("sha256").update(chunk).digest("hex").slice(0, PATH_HASH_LENGTH);
|
|
211
|
-
return `${
|
|
236
|
+
return `${import_node_path3.default.posix.basename(chunk, extension)}.${hash}${extension}.map`;
|
|
212
237
|
}
|
|
213
238
|
async function fileExists(filePath) {
|
|
214
239
|
try {
|
|
215
|
-
await (0,
|
|
240
|
+
await (0, import_promises3.access)(filePath);
|
|
216
241
|
return true;
|
|
217
242
|
} catch (_error) {
|
|
218
243
|
return false;
|
|
219
244
|
}
|
|
220
245
|
}
|
|
221
246
|
async function findClientSourceMaps(distDir) {
|
|
222
|
-
const staticDir =
|
|
247
|
+
const staticDir = import_node_path3.default.join(distDir, STATIC_DIR);
|
|
223
248
|
const chunks = (await listFiles(staticDir)).filter((entry) => /\.[cm]?js$/.test(entry)).sort();
|
|
224
249
|
const maps = await mapConcurrently(chunks, MAX_PARALLEL_FILE_OPERATIONS, async (chunk) => {
|
|
225
|
-
const mapPath = getSourceMapPath(chunk, await readFileTail(
|
|
226
|
-
if (mapPath === void 0 || !await fileExists(
|
|
250
|
+
const mapPath = getSourceMapPath(chunk, await readFileTail(import_node_path3.default.join(staticDir, chunk), CHUNK_TAIL_SIZE));
|
|
251
|
+
if (mapPath === void 0 || !await fileExists(import_node_path3.default.join(staticDir, mapPath))) {
|
|
227
252
|
return void 0;
|
|
228
253
|
}
|
|
229
254
|
return {
|
|
230
|
-
path:
|
|
255
|
+
path: import_node_path3.default.join(staticDir, mapPath),
|
|
231
256
|
chunkUrl: `${PUBLIC_PATH_PREFIX}${STATIC_DIR}/${encodeUrlPath(chunk)}`,
|
|
232
257
|
name: getSourceMapName(chunk)
|
|
233
258
|
};
|
|
@@ -252,32 +277,32 @@ function prepareSourceMap(content, chunkUrl) {
|
|
|
252
277
|
return JSON.stringify(map);
|
|
253
278
|
}
|
|
254
279
|
|
|
255
|
-
// src/cleanup.ts
|
|
280
|
+
// src/config/cleanup.ts
|
|
256
281
|
var CSS_SOURCE_MAPPING_URL = /(?:^|\n)\/\*[#@] sourceMappingURL=[^\s*]+\s*\*\/\s*$/;
|
|
257
282
|
async function deleteClientSourceMaps(distDir) {
|
|
258
|
-
const staticDir =
|
|
283
|
+
const staticDir = import_node_path4.default.join(distDir, STATIC_DIR);
|
|
259
284
|
const maps = (await listFiles(staticDir)).filter((entry) => entry.endsWith(".map"));
|
|
260
|
-
await mapConcurrently(maps, MAX_PARALLEL_FILE_OPERATIONS, (entry) => (0,
|
|
285
|
+
await mapConcurrently(maps, MAX_PARALLEL_FILE_OPERATIONS, (entry) => (0, import_promises4.unlink)(import_node_path4.default.join(staticDir, entry)));
|
|
261
286
|
return maps.length;
|
|
262
287
|
}
|
|
263
288
|
async function stripSourceMappingUrlComments(distDir) {
|
|
264
|
-
const staticDir =
|
|
289
|
+
const staticDir = import_node_path4.default.join(distDir, STATIC_DIR);
|
|
265
290
|
const assets = (await listFiles(staticDir)).filter((entry) => /\.([cm]?js|css)$/.test(entry));
|
|
266
291
|
const updated = await mapConcurrently(assets, MAX_PARALLEL_FILE_OPERATIONS, async (entry) => {
|
|
267
|
-
const filePath =
|
|
292
|
+
const filePath = import_node_path4.default.join(staticDir, entry);
|
|
268
293
|
const pattern = entry.endsWith(".css") ? CSS_SOURCE_MAPPING_URL : JS_SOURCE_MAPPING_URL;
|
|
269
|
-
const content = await (0,
|
|
294
|
+
const content = await (0, import_promises4.readFile)(filePath, "utf8");
|
|
270
295
|
const stripped = content.replace(pattern, "");
|
|
271
296
|
if (stripped === content) {
|
|
272
297
|
return false;
|
|
273
298
|
}
|
|
274
|
-
await (0,
|
|
299
|
+
await (0, import_promises4.writeFile)(filePath, stripped);
|
|
275
300
|
return true;
|
|
276
301
|
});
|
|
277
302
|
return updated.filter(Boolean).length;
|
|
278
303
|
}
|
|
279
304
|
|
|
280
|
-
// src/commits.ts
|
|
305
|
+
// src/config/commits.ts
|
|
281
306
|
var import_node_child_process2 = require("child_process");
|
|
282
307
|
var import_node_util = require("util");
|
|
283
308
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process2.execFile);
|
|
@@ -303,7 +328,7 @@ async function getCommits(repo, count) {
|
|
|
303
328
|
});
|
|
304
329
|
}
|
|
305
330
|
|
|
306
|
-
// src/uploader.ts
|
|
331
|
+
// src/config/uploader.ts
|
|
307
332
|
var MAX_RESPONSE_PREVIEW_LENGTH = 200;
|
|
308
333
|
function sanitizeCollectorMessage(message) {
|
|
309
334
|
return message.startsWith("Integration token invalid") ? "Integration token invalid" : message;
|
|
@@ -339,7 +364,7 @@ async function sendCommits(collector, commits) {
|
|
|
339
364
|
await send(collector, body);
|
|
340
365
|
}
|
|
341
366
|
|
|
342
|
-
// src/utils/log.ts
|
|
367
|
+
// src/config/utils/log.ts
|
|
343
368
|
var consoleColors = {
|
|
344
369
|
fgRed: 31,
|
|
345
370
|
fgGreen: 32,
|
|
@@ -356,7 +381,7 @@ function getErrorMessage(error) {
|
|
|
356
381
|
return error instanceof Error ? error.message : String(error);
|
|
357
382
|
}
|
|
358
383
|
|
|
359
|
-
// src/upload-release.ts
|
|
384
|
+
// src/config/upload-release.ts
|
|
360
385
|
var MAX_PARALLEL_UPLOADS = 5;
|
|
361
386
|
var REQUEST_TIMEOUT = 3e4;
|
|
362
387
|
function getCollector(options) {
|
|
@@ -404,7 +429,7 @@ async function uploadSourceMaps(collector, distDir) {
|
|
|
404
429
|
let sentCount = 0;
|
|
405
430
|
await mapConcurrently(maps, MAX_PARALLEL_UPLOADS, async (map) => {
|
|
406
431
|
try {
|
|
407
|
-
const content = prepareSourceMap(await (0,
|
|
432
|
+
const content = prepareSourceMap(await (0, import_promises5.readFile)(map.path, "utf8"), map.chunkUrl);
|
|
408
433
|
await sendSourceMap(collector, map.name, content);
|
|
409
434
|
sentCount += 1;
|
|
410
435
|
} catch (error) {
|
|
@@ -446,9 +471,10 @@ async function uploadRelease(metadata, options) {
|
|
|
446
471
|
}
|
|
447
472
|
}
|
|
448
473
|
|
|
449
|
-
// src/with-hawk-config.ts
|
|
474
|
+
// src/config/with-hawk-config.ts
|
|
450
475
|
var MIN_NEXT_VERSION = "15.4.1";
|
|
451
476
|
var DEFAULT_COMMITS_NUMBER = 5;
|
|
477
|
+
var TOKEN_ENV_VARIABLES = ["HAWK_TOKEN", "NEXT_PUBLIC_HAWK_TOKEN"];
|
|
452
478
|
function isVersionAtLeast(version, minVersion) {
|
|
453
479
|
const toNumbers = (value) => value.split("-")[0].split(".").map(Number);
|
|
454
480
|
const actual = toNumbers(version);
|
|
@@ -463,12 +489,22 @@ function isVersionAtLeast(version, minVersion) {
|
|
|
463
489
|
}
|
|
464
490
|
function getNextVersion(projectDir) {
|
|
465
491
|
try {
|
|
466
|
-
const requireFromProject = (0, import_node_module.createRequire)(
|
|
492
|
+
const requireFromProject = (0, import_node_module.createRequire)(import_node_path5.default.join(projectDir, "package.json"));
|
|
467
493
|
return requireFromProject("next/package.json").version;
|
|
468
494
|
} catch (_error) {
|
|
469
495
|
return void 0;
|
|
470
496
|
}
|
|
471
497
|
}
|
|
498
|
+
function getEnvToken() {
|
|
499
|
+
return TOKEN_ENV_VARIABLES.map((name) => import_node_process2.default.env[name]).find((value) => value !== void 0 && value !== "");
|
|
500
|
+
}
|
|
501
|
+
async function checkInstrumentationFiles(projectDir, pageExtensions) {
|
|
502
|
+
const missing = await findMissingInstrumentationFiles(projectDir, pageExtensions);
|
|
503
|
+
if (missing.length > 0) {
|
|
504
|
+
const files = missing.map((name) => `${name}.ts`).join(" and ");
|
|
505
|
+
log(`${files} not found, so errors are not caught. See https://github.com/codex-team/hawk.nextjs#error-tracking`, consoleColors.fgYellow);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
472
508
|
function getCommitsOptions(commits = true) {
|
|
473
509
|
if (commits === false) {
|
|
474
510
|
return false;
|
|
@@ -495,7 +531,7 @@ function applyHawkConfig(config, options) {
|
|
|
495
531
|
log(`Next.js ${nextVersion} is not supported, update it to ${MIN_NEXT_VERSION} or newer to send releases`, consoleColors.fgYellow);
|
|
496
532
|
return result;
|
|
497
533
|
}
|
|
498
|
-
const token = options.token ??
|
|
534
|
+
const token = options.token ?? getEnvToken();
|
|
499
535
|
const hasToken = token !== void 0 && token !== "";
|
|
500
536
|
const canUpload = hasToken && (options.collectorEndpoint !== void 0 || getIntegrationId(token) !== void 0);
|
|
501
537
|
if (canUpload && config.productionBrowserSourceMaps === void 0) {
|
|
@@ -516,6 +552,7 @@ function applyHawkConfig(config, options) {
|
|
|
516
552
|
if (userHook !== void 0) {
|
|
517
553
|
await userHook(metadata);
|
|
518
554
|
}
|
|
555
|
+
await checkInstrumentationFiles(metadata.projectDir, config.pageExtensions);
|
|
519
556
|
await uploadRelease(metadata, uploadOptions);
|
|
520
557
|
}
|
|
521
558
|
};
|
|
@@ -20,7 +20,7 @@ interface CommitsOptions {
|
|
|
20
20
|
*/
|
|
21
21
|
interface HawkNextOptions {
|
|
22
22
|
/**
|
|
23
|
-
* Hawk Integration Token. Default is the HAWK_TOKEN environment variable
|
|
23
|
+
* Hawk Integration Token. Default is the HAWK_TOKEN or NEXT_PUBLIC_HAWK_TOKEN environment variable
|
|
24
24
|
*/
|
|
25
25
|
token?: string;
|
|
26
26
|
/**
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/// <reference path="../global.d.ts" />
|
|
2
|
+
import { NextConfig } from 'next';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options of the commits collecting
|
|
6
|
+
*/
|
|
7
|
+
interface CommitsOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Path to the repository with `.git` directory. Default is the Next.js project directory
|
|
10
|
+
*/
|
|
11
|
+
repo?: string;
|
|
12
|
+
/**
|
|
13
|
+
* How many recent commits to send. Default is 5
|
|
14
|
+
*/
|
|
15
|
+
number?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options of the Hawk Next.js integration
|
|
20
|
+
*/
|
|
21
|
+
interface HawkNextOptions {
|
|
22
|
+
/**
|
|
23
|
+
* Hawk Integration Token. Default is the HAWK_TOKEN or NEXT_PUBLIC_HAWK_TOKEN environment variable
|
|
24
|
+
*/
|
|
25
|
+
token?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Unique identifier of the release.
|
|
28
|
+
* Default is HAWK_RELEASE environment variable, commit SHA from CI or git, or the build time
|
|
29
|
+
*/
|
|
30
|
+
release?: string | number;
|
|
31
|
+
/**
|
|
32
|
+
* Whether to remove client source maps after sending. Default is true
|
|
33
|
+
*/
|
|
34
|
+
removeSourceMaps?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Options for sending recent commits with the release, or false to skip. Enabled by default
|
|
37
|
+
*/
|
|
38
|
+
commits?: boolean | CommitsOptions;
|
|
39
|
+
/**
|
|
40
|
+
* Collector endpoint overwrite
|
|
41
|
+
*/
|
|
42
|
+
collectorEndpoint?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Context passed by Next.js to the config function
|
|
46
|
+
*/
|
|
47
|
+
interface NextConfigContext {
|
|
48
|
+
/**
|
|
49
|
+
* Default Next.js config
|
|
50
|
+
*/
|
|
51
|
+
defaultConfig: NextConfig;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Next.js config exported as a function
|
|
55
|
+
*/
|
|
56
|
+
type NextConfigFunction = (phase: string, context: NextConfigContext) => NextConfig | Promise<NextConfig>;
|
|
57
|
+
/**
|
|
58
|
+
* Wraps Next.js config to send releases with source maps to Hawk.
|
|
59
|
+
*
|
|
60
|
+
* - exposes the release id to the app code as `process.env.HAWK_RELEASE`
|
|
61
|
+
* - enables client source maps for production builds
|
|
62
|
+
* - after `next build` sends client source maps and recent commits to Hawk and removes the maps
|
|
63
|
+
* @param nextConfig - Next.js config object
|
|
64
|
+
* @param options - Hawk plugin options
|
|
65
|
+
*/
|
|
66
|
+
declare function withHawkConfig(nextConfig: NextConfig, options?: HawkNextOptions): NextConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Wraps Next.js config function to send releases with source maps to Hawk
|
|
69
|
+
* @param nextConfig - function returning Next.js config
|
|
70
|
+
* @param options - Hawk plugin options
|
|
71
|
+
*/
|
|
72
|
+
declare function withHawkConfig(nextConfig: NextConfigFunction, options?: HawkNextOptions): NextConfigFunction;
|
|
73
|
+
|
|
74
|
+
export { withHawkConfig };
|
|
75
|
+
export type { CommitsOptions, HawkNextOptions, NextConfigFunction };
|
|
@@ -1,9 +1,34 @@
|
|
|
1
|
-
// src/with-hawk-config.ts
|
|
1
|
+
// src/config/with-hawk-config.ts
|
|
2
2
|
import { createRequire } from "module";
|
|
3
|
-
import
|
|
3
|
+
import path5 from "path";
|
|
4
4
|
import process2 from "process";
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/config/instrumentation.ts
|
|
7
|
+
import { access } from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
9
|
+
var DEFAULT_PAGE_EXTENSIONS = ["tsx", "ts", "jsx", "js"];
|
|
10
|
+
var INSTRUMENTATION_FILES = ["instrumentation", "instrumentation-client"];
|
|
11
|
+
async function exists(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
await access(filePath);
|
|
14
|
+
return true;
|
|
15
|
+
} catch (_error) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function findMissingInstrumentationFiles(projectDir, pageExtensions = DEFAULT_PAGE_EXTENSIONS) {
|
|
20
|
+
const missing = [];
|
|
21
|
+
for (const name of INSTRUMENTATION_FILES) {
|
|
22
|
+
const candidates = ["", "src"].flatMap((dir) => pageExtensions.map((extension) => path.join(projectDir, dir, `${name}.${extension}`)));
|
|
23
|
+
const found = await Promise.all(candidates.map(exists));
|
|
24
|
+
if (!found.includes(true)) {
|
|
25
|
+
missing.push(name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return missing;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/config/release.ts
|
|
7
32
|
import { execFileSync } from "child_process";
|
|
8
33
|
import process from "process";
|
|
9
34
|
var RELEASE_ENV_VARIABLE = "HAWK_RELEASE";
|
|
@@ -49,7 +74,7 @@ function resolveRelease(release, cwd) {
|
|
|
49
74
|
return getGitRevision(cwd) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
// src/token.ts
|
|
77
|
+
// src/config/token.ts
|
|
53
78
|
import { Buffer } from "buffer";
|
|
54
79
|
function getIntegrationId(token) {
|
|
55
80
|
try {
|
|
@@ -65,29 +90,29 @@ function getDefaultCollectorEndpoint(integrationId) {
|
|
|
65
90
|
return `https://${integrationId}.k1.hawk.so/release`;
|
|
66
91
|
}
|
|
67
92
|
|
|
68
|
-
// src/upload-release.ts
|
|
93
|
+
// src/config/upload-release.ts
|
|
69
94
|
import { readFile as readFile2 } from "fs/promises";
|
|
70
95
|
|
|
71
|
-
// src/cleanup.ts
|
|
96
|
+
// src/config/cleanup.ts
|
|
72
97
|
import { readFile, unlink, writeFile } from "fs/promises";
|
|
73
|
-
import
|
|
98
|
+
import path4 from "path";
|
|
74
99
|
|
|
75
|
-
// src/sourcemaps.ts
|
|
100
|
+
// src/config/sourcemaps.ts
|
|
76
101
|
import { createHash } from "crypto";
|
|
77
|
-
import { access } from "fs/promises";
|
|
78
|
-
import
|
|
102
|
+
import { access as access2 } from "fs/promises";
|
|
103
|
+
import path3 from "path";
|
|
79
104
|
|
|
80
|
-
// src/utils/fs.ts
|
|
105
|
+
// src/config/utils/fs.ts
|
|
81
106
|
import { Buffer as Buffer2 } from "buffer";
|
|
82
107
|
import { open, readdir } from "fs/promises";
|
|
83
|
-
import
|
|
108
|
+
import path2 from "path";
|
|
84
109
|
var MAX_PARALLEL_FILE_OPERATIONS = 16;
|
|
85
110
|
async function listFiles(dir) {
|
|
86
111
|
try {
|
|
87
112
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
88
113
|
const files = await Promise.all(entries.map(async (entry) => {
|
|
89
114
|
if (entry.isDirectory()) {
|
|
90
|
-
return (await listFiles(
|
|
115
|
+
return (await listFiles(path2.join(dir, entry.name))).map((file) => `${entry.name}/${file}`);
|
|
91
116
|
}
|
|
92
117
|
return entry.isFile() ? [entry.name] : [];
|
|
93
118
|
}));
|
|
@@ -116,7 +141,7 @@ async function readFileTail(filePath, maxBytes) {
|
|
|
116
141
|
}
|
|
117
142
|
}
|
|
118
143
|
|
|
119
|
-
// src/utils/pool.ts
|
|
144
|
+
// src/config/utils/pool.ts
|
|
120
145
|
async function mapConcurrently(items, limit, mapper) {
|
|
121
146
|
const results = [];
|
|
122
147
|
let nextIndex = 0;
|
|
@@ -132,7 +157,7 @@ async function mapConcurrently(items, limit, mapper) {
|
|
|
132
157
|
return results;
|
|
133
158
|
}
|
|
134
159
|
|
|
135
|
-
// src/sourcemaps.ts
|
|
160
|
+
// src/config/sourcemaps.ts
|
|
136
161
|
var STATIC_DIR = "static";
|
|
137
162
|
var PUBLIC_PATH_PREFIX = "/_next/";
|
|
138
163
|
var PATH_HASH_LENGTH = 8;
|
|
@@ -160,7 +185,7 @@ function getSourceMapPath(chunk, content) {
|
|
|
160
185
|
return void 0;
|
|
161
186
|
}
|
|
162
187
|
try {
|
|
163
|
-
const mapPath =
|
|
188
|
+
const mapPath = path3.posix.join(path3.posix.dirname(chunk), decodeURIComponent(url));
|
|
164
189
|
return mapPath.startsWith("../") ? void 0 : mapPath;
|
|
165
190
|
} catch (_error) {
|
|
166
191
|
return void 0;
|
|
@@ -170,28 +195,28 @@ function encodeUrlPath(filePath) {
|
|
|
170
195
|
return filePath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
171
196
|
}
|
|
172
197
|
function getSourceMapName(chunk) {
|
|
173
|
-
const extension =
|
|
198
|
+
const extension = path3.posix.extname(chunk);
|
|
174
199
|
const hash = createHash("sha256").update(chunk).digest("hex").slice(0, PATH_HASH_LENGTH);
|
|
175
|
-
return `${
|
|
200
|
+
return `${path3.posix.basename(chunk, extension)}.${hash}${extension}.map`;
|
|
176
201
|
}
|
|
177
202
|
async function fileExists(filePath) {
|
|
178
203
|
try {
|
|
179
|
-
await
|
|
204
|
+
await access2(filePath);
|
|
180
205
|
return true;
|
|
181
206
|
} catch (_error) {
|
|
182
207
|
return false;
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
async function findClientSourceMaps(distDir) {
|
|
186
|
-
const staticDir =
|
|
211
|
+
const staticDir = path3.join(distDir, STATIC_DIR);
|
|
187
212
|
const chunks = (await listFiles(staticDir)).filter((entry) => /\.[cm]?js$/.test(entry)).sort();
|
|
188
213
|
const maps = await mapConcurrently(chunks, MAX_PARALLEL_FILE_OPERATIONS, async (chunk) => {
|
|
189
|
-
const mapPath = getSourceMapPath(chunk, await readFileTail(
|
|
190
|
-
if (mapPath === void 0 || !await fileExists(
|
|
214
|
+
const mapPath = getSourceMapPath(chunk, await readFileTail(path3.join(staticDir, chunk), CHUNK_TAIL_SIZE));
|
|
215
|
+
if (mapPath === void 0 || !await fileExists(path3.join(staticDir, mapPath))) {
|
|
191
216
|
return void 0;
|
|
192
217
|
}
|
|
193
218
|
return {
|
|
194
|
-
path:
|
|
219
|
+
path: path3.join(staticDir, mapPath),
|
|
195
220
|
chunkUrl: `${PUBLIC_PATH_PREFIX}${STATIC_DIR}/${encodeUrlPath(chunk)}`,
|
|
196
221
|
name: getSourceMapName(chunk)
|
|
197
222
|
};
|
|
@@ -216,19 +241,19 @@ function prepareSourceMap(content, chunkUrl) {
|
|
|
216
241
|
return JSON.stringify(map);
|
|
217
242
|
}
|
|
218
243
|
|
|
219
|
-
// src/cleanup.ts
|
|
244
|
+
// src/config/cleanup.ts
|
|
220
245
|
var CSS_SOURCE_MAPPING_URL = /(?:^|\n)\/\*[#@] sourceMappingURL=[^\s*]+\s*\*\/\s*$/;
|
|
221
246
|
async function deleteClientSourceMaps(distDir) {
|
|
222
|
-
const staticDir =
|
|
247
|
+
const staticDir = path4.join(distDir, STATIC_DIR);
|
|
223
248
|
const maps = (await listFiles(staticDir)).filter((entry) => entry.endsWith(".map"));
|
|
224
|
-
await mapConcurrently(maps, MAX_PARALLEL_FILE_OPERATIONS, (entry) => unlink(
|
|
249
|
+
await mapConcurrently(maps, MAX_PARALLEL_FILE_OPERATIONS, (entry) => unlink(path4.join(staticDir, entry)));
|
|
225
250
|
return maps.length;
|
|
226
251
|
}
|
|
227
252
|
async function stripSourceMappingUrlComments(distDir) {
|
|
228
|
-
const staticDir =
|
|
253
|
+
const staticDir = path4.join(distDir, STATIC_DIR);
|
|
229
254
|
const assets = (await listFiles(staticDir)).filter((entry) => /\.([cm]?js|css)$/.test(entry));
|
|
230
255
|
const updated = await mapConcurrently(assets, MAX_PARALLEL_FILE_OPERATIONS, async (entry) => {
|
|
231
|
-
const filePath =
|
|
256
|
+
const filePath = path4.join(staticDir, entry);
|
|
232
257
|
const pattern = entry.endsWith(".css") ? CSS_SOURCE_MAPPING_URL : JS_SOURCE_MAPPING_URL;
|
|
233
258
|
const content = await readFile(filePath, "utf8");
|
|
234
259
|
const stripped = content.replace(pattern, "");
|
|
@@ -241,7 +266,7 @@ async function stripSourceMappingUrlComments(distDir) {
|
|
|
241
266
|
return updated.filter(Boolean).length;
|
|
242
267
|
}
|
|
243
268
|
|
|
244
|
-
// src/commits.ts
|
|
269
|
+
// src/config/commits.ts
|
|
245
270
|
import { execFile } from "child_process";
|
|
246
271
|
import { promisify } from "util";
|
|
247
272
|
var execFileAsync = promisify(execFile);
|
|
@@ -267,7 +292,7 @@ async function getCommits(repo, count) {
|
|
|
267
292
|
});
|
|
268
293
|
}
|
|
269
294
|
|
|
270
|
-
// src/uploader.ts
|
|
295
|
+
// src/config/uploader.ts
|
|
271
296
|
var MAX_RESPONSE_PREVIEW_LENGTH = 200;
|
|
272
297
|
function sanitizeCollectorMessage(message) {
|
|
273
298
|
return message.startsWith("Integration token invalid") ? "Integration token invalid" : message;
|
|
@@ -303,7 +328,7 @@ async function sendCommits(collector, commits) {
|
|
|
303
328
|
await send(collector, body);
|
|
304
329
|
}
|
|
305
330
|
|
|
306
|
-
// src/utils/log.ts
|
|
331
|
+
// src/config/utils/log.ts
|
|
307
332
|
var consoleColors = {
|
|
308
333
|
fgRed: 31,
|
|
309
334
|
fgGreen: 32,
|
|
@@ -320,7 +345,7 @@ function getErrorMessage(error) {
|
|
|
320
345
|
return error instanceof Error ? error.message : String(error);
|
|
321
346
|
}
|
|
322
347
|
|
|
323
|
-
// src/upload-release.ts
|
|
348
|
+
// src/config/upload-release.ts
|
|
324
349
|
var MAX_PARALLEL_UPLOADS = 5;
|
|
325
350
|
var REQUEST_TIMEOUT = 3e4;
|
|
326
351
|
function getCollector(options) {
|
|
@@ -410,9 +435,10 @@ async function uploadRelease(metadata, options) {
|
|
|
410
435
|
}
|
|
411
436
|
}
|
|
412
437
|
|
|
413
|
-
// src/with-hawk-config.ts
|
|
438
|
+
// src/config/with-hawk-config.ts
|
|
414
439
|
var MIN_NEXT_VERSION = "15.4.1";
|
|
415
440
|
var DEFAULT_COMMITS_NUMBER = 5;
|
|
441
|
+
var TOKEN_ENV_VARIABLES = ["HAWK_TOKEN", "NEXT_PUBLIC_HAWK_TOKEN"];
|
|
416
442
|
function isVersionAtLeast(version, minVersion) {
|
|
417
443
|
const toNumbers = (value) => value.split("-")[0].split(".").map(Number);
|
|
418
444
|
const actual = toNumbers(version);
|
|
@@ -427,12 +453,22 @@ function isVersionAtLeast(version, minVersion) {
|
|
|
427
453
|
}
|
|
428
454
|
function getNextVersion(projectDir) {
|
|
429
455
|
try {
|
|
430
|
-
const requireFromProject = createRequire(
|
|
456
|
+
const requireFromProject = createRequire(path5.join(projectDir, "package.json"));
|
|
431
457
|
return requireFromProject("next/package.json").version;
|
|
432
458
|
} catch (_error) {
|
|
433
459
|
return void 0;
|
|
434
460
|
}
|
|
435
461
|
}
|
|
462
|
+
function getEnvToken() {
|
|
463
|
+
return TOKEN_ENV_VARIABLES.map((name) => process2.env[name]).find((value) => value !== void 0 && value !== "");
|
|
464
|
+
}
|
|
465
|
+
async function checkInstrumentationFiles(projectDir, pageExtensions) {
|
|
466
|
+
const missing = await findMissingInstrumentationFiles(projectDir, pageExtensions);
|
|
467
|
+
if (missing.length > 0) {
|
|
468
|
+
const files = missing.map((name) => `${name}.ts`).join(" and ");
|
|
469
|
+
log(`${files} not found, so errors are not caught. See https://github.com/codex-team/hawk.nextjs#error-tracking`, consoleColors.fgYellow);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
436
472
|
function getCommitsOptions(commits = true) {
|
|
437
473
|
if (commits === false) {
|
|
438
474
|
return false;
|
|
@@ -459,7 +495,7 @@ function applyHawkConfig(config, options) {
|
|
|
459
495
|
log(`Next.js ${nextVersion} is not supported, update it to ${MIN_NEXT_VERSION} or newer to send releases`, consoleColors.fgYellow);
|
|
460
496
|
return result;
|
|
461
497
|
}
|
|
462
|
-
const token = options.token ??
|
|
498
|
+
const token = options.token ?? getEnvToken();
|
|
463
499
|
const hasToken = token !== void 0 && token !== "";
|
|
464
500
|
const canUpload = hasToken && (options.collectorEndpoint !== void 0 || getIntegrationId(token) !== void 0);
|
|
465
501
|
if (canUpload && config.productionBrowserSourceMaps === void 0) {
|
|
@@ -480,6 +516,7 @@ function applyHawkConfig(config, options) {
|
|
|
480
516
|
if (userHook !== void 0) {
|
|
481
517
|
await userHook(metadata);
|
|
482
518
|
}
|
|
519
|
+
await checkInstrumentationFiles(metadata.projectDir, config.pageExtensions);
|
|
483
520
|
await uploadRelease(metadata, uploadOptions);
|
|
484
521
|
}
|
|
485
522
|
};
|