@kenkaiiii/gg-pixel 4.3.69 → 4.3.71
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/cli.js +367 -11
- package/dist/cli.js.map +1 -1
- package/dist/deno.d.ts +63 -0
- package/dist/deno.js +328 -0
- package/dist/deno.js.map +1 -0
- package/dist/index.cjs +360 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +360 -11
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ async function install(opts = {}) {
|
|
|
28
28
|
const pkgPath = join(nodeRoot, "package.json");
|
|
29
29
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
30
30
|
const projectName = opts.projectName ?? pkg.name ?? nodeRoot.split("/").pop() ?? "unnamed";
|
|
31
|
-
const
|
|
31
|
+
const kind = detectJsProjectKind(pkg, nodeRoot);
|
|
32
32
|
const projectsJsonPath = join(home, ".gg", "projects.json");
|
|
33
33
|
const envFilePath = join(nodeRoot, ".env");
|
|
34
34
|
const existing = findMappingByPath(projectsJsonPath, nodeRoot);
|
|
@@ -43,26 +43,31 @@ async function install(opts = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
const pm = detectPackageManager(nodeRoot);
|
|
45
45
|
const packageInstalled = opts.skipPackageInstall ? false : runInstall(nodeRoot, pm, "@kenkaiiii/gg-pixel");
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
const wired = wireFramework({
|
|
47
|
+
kind,
|
|
48
|
+
projectRoot: nodeRoot,
|
|
49
|
+
pkg,
|
|
50
|
+
projectKey: created.key,
|
|
51
|
+
ingestUrl
|
|
52
|
+
});
|
|
53
|
+
if (kind !== "browser" && kind !== "tauri") {
|
|
50
54
|
writeEnvKey(envFilePath, "GG_PIXEL_KEY", created.key);
|
|
51
55
|
}
|
|
52
56
|
writeProjectsMapping(projectsJsonPath, created.id, projectName, nodeRoot);
|
|
53
|
-
const entryWiring = wireEntryFile(nodeRoot, initFilePath, pkg);
|
|
54
57
|
return {
|
|
55
58
|
projectId: created.id,
|
|
56
59
|
projectKey: created.key,
|
|
57
60
|
projectName,
|
|
58
|
-
projectKind:
|
|
59
|
-
initFilePath,
|
|
61
|
+
projectKind: kind,
|
|
62
|
+
initFilePath: wired.primaryInitPath,
|
|
60
63
|
envFilePath,
|
|
61
64
|
projectsJsonPath,
|
|
62
65
|
packageManager: pm,
|
|
63
66
|
packageInstalled,
|
|
64
|
-
entryWiring,
|
|
65
|
-
reused
|
|
67
|
+
entryWiring: wired.entryWiring,
|
|
68
|
+
reused,
|
|
69
|
+
secondaryInit: wired.secondaryInit,
|
|
70
|
+
warnings: wired.warnings
|
|
66
71
|
};
|
|
67
72
|
}
|
|
68
73
|
function findMappingByPath(projectsJsonPath, projectRoot) {
|
|
@@ -252,6 +257,349 @@ function isCommonJsEntry(entryPath, pkg) {
|
|
|
252
257
|
if (entryPath.endsWith(".ts") || entryPath.endsWith(".tsx")) return false;
|
|
253
258
|
return pkg.type !== "module";
|
|
254
259
|
}
|
|
260
|
+
function detectJsProjectKind(pkg, projectRoot) {
|
|
261
|
+
const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
262
|
+
if ("electron" in all) return "electron";
|
|
263
|
+
if (existsSync(join(projectRoot, "src-tauri")) || "@tauri-apps/api" in all) return "tauri";
|
|
264
|
+
if ("react-native" in all) return "react-native";
|
|
265
|
+
if ("next" in all) return "nextjs";
|
|
266
|
+
if ("@sveltejs/kit" in all) return "sveltekit";
|
|
267
|
+
if ("nuxt" in all || "nuxt3" in all) return "nuxt";
|
|
268
|
+
if ("@remix-run/react" in all || "@remix-run/node" in all) return "remix";
|
|
269
|
+
if (isBrowserProject(pkg, projectRoot)) return "browser";
|
|
270
|
+
return "node";
|
|
271
|
+
}
|
|
272
|
+
function wireFramework(w) {
|
|
273
|
+
switch (w.kind) {
|
|
274
|
+
case "node":
|
|
275
|
+
return wireNode(w);
|
|
276
|
+
case "browser":
|
|
277
|
+
return wireBrowser(w);
|
|
278
|
+
case "nextjs":
|
|
279
|
+
return wireNextjs(w);
|
|
280
|
+
case "sveltekit":
|
|
281
|
+
return wireSveltekit(w);
|
|
282
|
+
case "nuxt":
|
|
283
|
+
return wireNuxt(w);
|
|
284
|
+
case "remix":
|
|
285
|
+
return wireRemix(w);
|
|
286
|
+
case "electron":
|
|
287
|
+
return wireElectron(w);
|
|
288
|
+
case "tauri":
|
|
289
|
+
return wireTauri(w);
|
|
290
|
+
case "react-native":
|
|
291
|
+
return wireReactNative(w);
|
|
292
|
+
case "python":
|
|
293
|
+
throw new Error("Internal: python should have been handled earlier");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function wireNode({ projectRoot, pkg, ingestUrl }) {
|
|
297
|
+
const initPath = join(projectRoot, "gg-pixel.init.mjs");
|
|
298
|
+
writeFileSync(initPath, renderInitFile(ingestUrl), "utf8");
|
|
299
|
+
return {
|
|
300
|
+
primaryInitPath: initPath,
|
|
301
|
+
entryWiring: wireEntryFile(projectRoot, initPath, pkg),
|
|
302
|
+
warnings: []
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function wireBrowser({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
306
|
+
const initPath = join(projectRoot, "gg-pixel.init.mjs");
|
|
307
|
+
writeFileSync(initPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
308
|
+
return {
|
|
309
|
+
primaryInitPath: initPath,
|
|
310
|
+
entryWiring: wireEntryFile(projectRoot, initPath, pkg),
|
|
311
|
+
warnings: []
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function wireNextjs({ projectRoot, projectKey, ingestUrl }) {
|
|
315
|
+
const warnings = [];
|
|
316
|
+
const serverInitPath = pickPath(projectRoot, ["instrumentation.ts", "instrumentation.js"]);
|
|
317
|
+
const finalServerPath = serverInitPath ?? join(projectRoot, "instrumentation.ts");
|
|
318
|
+
writeNextInstrumentation(finalServerPath, ingestUrl);
|
|
319
|
+
const clientInitPath = join(projectRoot, "gg-pixel.client.mjs");
|
|
320
|
+
writeFileSync(clientInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
321
|
+
const layoutPath = findNextLayout(projectRoot);
|
|
322
|
+
let entryWiring;
|
|
323
|
+
if (!layoutPath) {
|
|
324
|
+
warnings.push(
|
|
325
|
+
'Could not auto-wire the Next.js client init \u2014 no app/layout.{tsx,jsx} or pages/_app.{tsx,jsx} found. Add `import "./gg-pixel.client.mjs";` to your root layout/_app.'
|
|
326
|
+
);
|
|
327
|
+
entryWiring = { kind: "no_entry_found" };
|
|
328
|
+
} else {
|
|
329
|
+
entryWiring = injectImport(layoutPath, clientInitPath);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
primaryInitPath: clientInitPath,
|
|
333
|
+
entryWiring,
|
|
334
|
+
secondaryInit: {
|
|
335
|
+
path: finalServerPath,
|
|
336
|
+
description: "Next.js server instrumentation (auto-loaded by Next runtime)"
|
|
337
|
+
},
|
|
338
|
+
warnings
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function writeNextInstrumentation(path, ingestUrl) {
|
|
342
|
+
const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
343
|
+
if (existing.includes("@kenkaiiii/gg-pixel")) return;
|
|
344
|
+
const newContent = existing ? existing + "\n" + nextInstrumentationAppend(ingestUrl) : nextInstrumentationStandalone(ingestUrl);
|
|
345
|
+
writeFileSync(path, newContent, "utf8");
|
|
346
|
+
}
|
|
347
|
+
function nextInstrumentationStandalone(ingestUrl) {
|
|
348
|
+
return `// Next.js auto-loads this file on server start. Pixel hooks the
|
|
349
|
+
// uncaughtExceptionMonitor + unhandledRejection events for API routes,
|
|
350
|
+
// Server Components, and route handlers.
|
|
351
|
+
export async function register() {
|
|
352
|
+
if (process.env.NEXT_RUNTIME === "nodejs") {
|
|
353
|
+
const { initPixel } = await import("@kenkaiiii/gg-pixel");
|
|
354
|
+
initPixel({
|
|
355
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
356
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
function nextInstrumentationAppend(ingestUrl) {
|
|
363
|
+
return `// gg-pixel: server-side error tracking
|
|
364
|
+
import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
365
|
+
if (typeof process !== "undefined" && process.env.NEXT_RUNTIME === "nodejs") {
|
|
366
|
+
initPixel({
|
|
367
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
368
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
function findNextLayout(projectRoot) {
|
|
374
|
+
const candidates = [
|
|
375
|
+
"app/layout.tsx",
|
|
376
|
+
"app/layout.jsx",
|
|
377
|
+
"app/layout.ts",
|
|
378
|
+
"src/app/layout.tsx",
|
|
379
|
+
"src/app/layout.jsx",
|
|
380
|
+
"pages/_app.tsx",
|
|
381
|
+
"pages/_app.jsx",
|
|
382
|
+
"src/pages/_app.tsx",
|
|
383
|
+
"src/pages/_app.jsx"
|
|
384
|
+
];
|
|
385
|
+
for (const c of candidates) {
|
|
386
|
+
const p = join(projectRoot, c);
|
|
387
|
+
if (existsSync(p)) return p;
|
|
388
|
+
}
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
function wireSveltekit({ projectRoot, projectKey, ingestUrl }) {
|
|
392
|
+
const serverPath = join(projectRoot, "src/hooks.server.ts");
|
|
393
|
+
const clientPath = join(projectRoot, "src/hooks.client.ts");
|
|
394
|
+
if (!existsSync(dirname(serverPath))) mkdirSync(dirname(serverPath), { recursive: true });
|
|
395
|
+
appendOrCreate(
|
|
396
|
+
serverPath,
|
|
397
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
398
|
+
initPixel({
|
|
399
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
400
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
401
|
+
});
|
|
402
|
+
`,
|
|
403
|
+
"@kenkaiiii/gg-pixel"
|
|
404
|
+
);
|
|
405
|
+
appendOrCreate(
|
|
406
|
+
clientPath,
|
|
407
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel/browser";
|
|
408
|
+
initPixel({
|
|
409
|
+
projectKey: ${JSON.stringify(projectKey)},
|
|
410
|
+
ingestUrl: ${JSON.stringify(ingestUrl)},
|
|
411
|
+
});
|
|
412
|
+
`,
|
|
413
|
+
"@kenkaiiii/gg-pixel/browser"
|
|
414
|
+
);
|
|
415
|
+
return {
|
|
416
|
+
primaryInitPath: clientPath,
|
|
417
|
+
entryWiring: { kind: "injected", entryPath: clientPath },
|
|
418
|
+
secondaryInit: {
|
|
419
|
+
path: serverPath,
|
|
420
|
+
description: "SvelteKit server hooks (auto-loaded)"
|
|
421
|
+
},
|
|
422
|
+
warnings: []
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function wireNuxt({ projectRoot, projectKey, ingestUrl }) {
|
|
426
|
+
const pluginsDir = join(projectRoot, "plugins");
|
|
427
|
+
mkdirSync(pluginsDir, { recursive: true });
|
|
428
|
+
const serverPath = join(pluginsDir, "gg-pixel.server.ts");
|
|
429
|
+
const clientPath = join(pluginsDir, "gg-pixel.client.ts");
|
|
430
|
+
writeFileSync(
|
|
431
|
+
serverPath,
|
|
432
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
433
|
+
export default defineNuxtPlugin(() => {
|
|
434
|
+
initPixel({
|
|
435
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
436
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
`,
|
|
440
|
+
"utf8"
|
|
441
|
+
);
|
|
442
|
+
writeFileSync(
|
|
443
|
+
clientPath,
|
|
444
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel/browser";
|
|
445
|
+
export default defineNuxtPlugin(() => {
|
|
446
|
+
initPixel({
|
|
447
|
+
projectKey: ${JSON.stringify(projectKey)},
|
|
448
|
+
ingestUrl: ${JSON.stringify(ingestUrl)},
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
`,
|
|
452
|
+
"utf8"
|
|
453
|
+
);
|
|
454
|
+
return {
|
|
455
|
+
primaryInitPath: clientPath,
|
|
456
|
+
entryWiring: { kind: "injected", entryPath: clientPath },
|
|
457
|
+
secondaryInit: { path: serverPath, description: "Nuxt server plugin (auto-loaded)" },
|
|
458
|
+
warnings: []
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
function wireRemix({ projectRoot, projectKey, ingestUrl }) {
|
|
462
|
+
const serverPath = pickPath(projectRoot, ["app/entry.server.tsx", "app/entry.server.jsx"]);
|
|
463
|
+
const clientPath = pickPath(projectRoot, ["app/entry.client.tsx", "app/entry.client.jsx"]);
|
|
464
|
+
const warnings = [];
|
|
465
|
+
const clientInitPath = join(projectRoot, "gg-pixel.client.mjs");
|
|
466
|
+
writeFileSync(clientInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
467
|
+
if (clientPath) {
|
|
468
|
+
injectImport(clientPath, clientInitPath);
|
|
469
|
+
} else {
|
|
470
|
+
warnings.push(
|
|
471
|
+
"No app/entry.client.tsx found. Run `npx remix reveal` then re-run pixel install."
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
const serverInitPath = join(projectRoot, "gg-pixel.server.mjs");
|
|
475
|
+
writeFileSync(serverInitPath, renderInitFile(ingestUrl), "utf8");
|
|
476
|
+
let serverEntry = { kind: "no_entry_found" };
|
|
477
|
+
if (serverPath) {
|
|
478
|
+
serverEntry = injectImport(serverPath, serverInitPath);
|
|
479
|
+
} else {
|
|
480
|
+
warnings.push(
|
|
481
|
+
"No app/entry.server.tsx found. Run `npx remix reveal` then re-run pixel install."
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
void serverEntry;
|
|
485
|
+
return {
|
|
486
|
+
primaryInitPath: clientInitPath,
|
|
487
|
+
entryWiring: clientPath ? { kind: "injected", entryPath: clientPath } : { kind: "no_entry_found" },
|
|
488
|
+
secondaryInit: { path: serverInitPath, description: "Remix server init" },
|
|
489
|
+
warnings
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function wireElectron({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
493
|
+
const mainInitPath = join(projectRoot, "gg-pixel.main.mjs");
|
|
494
|
+
writeFileSync(mainInitPath, renderInitFile(ingestUrl), "utf8");
|
|
495
|
+
const rendererInitPath = join(projectRoot, "gg-pixel.renderer.mjs");
|
|
496
|
+
writeFileSync(rendererInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
497
|
+
const mainEntry = pkg.main ? join(projectRoot, pkg.main) : pickPath(projectRoot, [
|
|
498
|
+
"main.js",
|
|
499
|
+
"main.ts",
|
|
500
|
+
"src/main.js",
|
|
501
|
+
"src/main.ts",
|
|
502
|
+
"electron/main.js",
|
|
503
|
+
"electron/main.ts"
|
|
504
|
+
]);
|
|
505
|
+
let mainWiring = { kind: "no_entry_found" };
|
|
506
|
+
if (mainEntry && existsSync(mainEntry)) {
|
|
507
|
+
mainWiring = injectImport(mainEntry, mainInitPath);
|
|
508
|
+
}
|
|
509
|
+
const rendererEntry = pickPath(projectRoot, [
|
|
510
|
+
"src/renderer/index.ts",
|
|
511
|
+
"src/renderer/index.tsx",
|
|
512
|
+
"src/renderer/main.ts",
|
|
513
|
+
"src/renderer/main.tsx",
|
|
514
|
+
"renderer/index.ts",
|
|
515
|
+
"renderer/index.tsx",
|
|
516
|
+
"renderer.ts",
|
|
517
|
+
"renderer.tsx",
|
|
518
|
+
"src/index.tsx",
|
|
519
|
+
"src/main.tsx"
|
|
520
|
+
]);
|
|
521
|
+
if (rendererEntry) {
|
|
522
|
+
injectImport(rendererEntry, rendererInitPath);
|
|
523
|
+
}
|
|
524
|
+
const warnings = [];
|
|
525
|
+
if (!rendererEntry) {
|
|
526
|
+
warnings.push(
|
|
527
|
+
'Could not auto-detect the Electron renderer entry. Add `import "./gg-pixel.renderer.mjs";` to the top of your renderer entry file.'
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
primaryInitPath: rendererInitPath,
|
|
532
|
+
entryWiring: rendererEntry ? { kind: "injected", entryPath: rendererEntry } : { kind: "no_entry_found" },
|
|
533
|
+
secondaryInit: {
|
|
534
|
+
path: mainInitPath,
|
|
535
|
+
description: "Electron main-process init" + (mainWiring.kind === "injected" ? ` (wired into ${mainEntry})` : "")
|
|
536
|
+
},
|
|
537
|
+
warnings
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
function wireTauri({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
541
|
+
const initPath = join(projectRoot, "gg-pixel.init.mjs");
|
|
542
|
+
writeFileSync(initPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
543
|
+
const entryWiring = wireEntryFile(projectRoot, initPath, pkg);
|
|
544
|
+
return {
|
|
545
|
+
primaryInitPath: initPath,
|
|
546
|
+
entryWiring,
|
|
547
|
+
warnings: [
|
|
548
|
+
"Tauri Rust backend is not instrumented \u2014 no Rust SDK exists yet. Frontend errors are captured."
|
|
549
|
+
]
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function wireReactNative({ projectRoot }) {
|
|
553
|
+
return {
|
|
554
|
+
primaryInitPath: join(projectRoot, "(not-installed)"),
|
|
555
|
+
entryWiring: { kind: "skipped", reason: "react-native SDK not built yet" },
|
|
556
|
+
warnings: [
|
|
557
|
+
"React Native is not yet supported \u2014 its JS runtime is neither browser nor Node.",
|
|
558
|
+
"A dedicated React Native SDK will be a future slice."
|
|
559
|
+
]
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function pickPath(root, candidates) {
|
|
563
|
+
for (const c of candidates) {
|
|
564
|
+
const p = join(root, c);
|
|
565
|
+
if (existsSync(p)) return p;
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
function appendOrCreate(filePath, snippet, marker) {
|
|
570
|
+
if (existsSync(filePath)) {
|
|
571
|
+
const existing = readFileSync(filePath, "utf8");
|
|
572
|
+
if (existing.includes(marker)) return;
|
|
573
|
+
writeFileSync(filePath, existing + "\n" + snippet, "utf8");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
writeFileSync(filePath, snippet, "utf8");
|
|
577
|
+
}
|
|
578
|
+
function injectImport(entryPath, initFilePath) {
|
|
579
|
+
let content;
|
|
580
|
+
try {
|
|
581
|
+
content = readFileSync(entryPath, "utf8");
|
|
582
|
+
} catch (err) {
|
|
583
|
+
return { kind: "skipped", reason: `unreadable: ${err.message}` };
|
|
584
|
+
}
|
|
585
|
+
const initBasename = initFilePath.split(sep).pop() ?? "gg-pixel.init.mjs";
|
|
586
|
+
if (content.includes(initBasename) || content.includes("@kenkaiiii/gg-pixel")) {
|
|
587
|
+
return { kind: "already_present", entryPath };
|
|
588
|
+
}
|
|
589
|
+
const fromDir = dirname(entryPath);
|
|
590
|
+
let spec = relative(fromDir, initFilePath).split(sep).join("/");
|
|
591
|
+
if (!spec.startsWith(".")) spec = "./" + spec;
|
|
592
|
+
const importLine = `import ${JSON.stringify(spec)};`;
|
|
593
|
+
const lines = content.split("\n");
|
|
594
|
+
let insertAt = 0;
|
|
595
|
+
if (lines[0]?.startsWith("#!")) insertAt = 1;
|
|
596
|
+
while (insertAt < lines.length && /^\s*(?:["']use strict["']|\/\/|\/\*)/.test(lines[insertAt] ?? "")) {
|
|
597
|
+
insertAt++;
|
|
598
|
+
}
|
|
599
|
+
const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join("\n");
|
|
600
|
+
writeFileSync(entryPath, updated, "utf8");
|
|
601
|
+
return { kind: "injected", entryPath };
|
|
602
|
+
}
|
|
255
603
|
function isBrowserProject(pkg, projectRoot) {
|
|
256
604
|
const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
257
605
|
const browserishDeps = [
|
|
@@ -344,7 +692,8 @@ async function installPython(ctx) {
|
|
|
344
692
|
packageManager: pm,
|
|
345
693
|
packageInstalled,
|
|
346
694
|
entryWiring,
|
|
347
|
-
reused
|
|
695
|
+
reused,
|
|
696
|
+
warnings: []
|
|
348
697
|
};
|
|
349
698
|
}
|
|
350
699
|
function readPyprojectName(projectRoot) {
|
|
@@ -533,6 +882,13 @@ async function main(argv) {
|
|
|
533
882
|
if (!result.packageInstalled && !args.skipPackageInstall) {
|
|
534
883
|
console.log(` \u26A0 Package install failed via ${result.packageManager}. Run it manually.`);
|
|
535
884
|
}
|
|
885
|
+
if (result.secondaryInit) {
|
|
886
|
+
console.log(` Also wrote: ${result.secondaryInit.path}`);
|
|
887
|
+
console.log(` ${result.secondaryInit.description}`);
|
|
888
|
+
}
|
|
889
|
+
for (const w of result.warnings) {
|
|
890
|
+
console.log(` \u26A0 ${w}`);
|
|
891
|
+
}
|
|
536
892
|
console.log("");
|
|
537
893
|
}
|
|
538
894
|
main(process.argv.slice(2)).catch((err) => {
|