@kenkaiiii/gg-pixel 4.3.69 → 4.3.70
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/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 +1 -1
package/dist/index.js
CHANGED
|
@@ -435,7 +435,7 @@ async function install(opts = {}) {
|
|
|
435
435
|
const pkgPath = join2(nodeRoot, "package.json");
|
|
436
436
|
const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
|
|
437
437
|
const projectName = opts.projectName ?? pkg.name ?? nodeRoot.split("/").pop() ?? "unnamed";
|
|
438
|
-
const
|
|
438
|
+
const kind = detectJsProjectKind(pkg, nodeRoot);
|
|
439
439
|
const projectsJsonPath = join2(home, ".gg", "projects.json");
|
|
440
440
|
const envFilePath = join2(nodeRoot, ".env");
|
|
441
441
|
const existing = findMappingByPath(projectsJsonPath, nodeRoot);
|
|
@@ -450,26 +450,31 @@ async function install(opts = {}) {
|
|
|
450
450
|
}
|
|
451
451
|
const pm = detectPackageManager(nodeRoot);
|
|
452
452
|
const packageInstalled = opts.skipPackageInstall ? false : runInstall(nodeRoot, pm, "@kenkaiiii/gg-pixel");
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
453
|
+
const wired = wireFramework({
|
|
454
|
+
kind,
|
|
455
|
+
projectRoot: nodeRoot,
|
|
456
|
+
pkg,
|
|
457
|
+
projectKey: created.key,
|
|
458
|
+
ingestUrl
|
|
459
|
+
});
|
|
460
|
+
if (kind !== "browser" && kind !== "tauri") {
|
|
457
461
|
writeEnvKey(envFilePath, "GG_PIXEL_KEY", created.key);
|
|
458
462
|
}
|
|
459
463
|
writeProjectsMapping(projectsJsonPath, created.id, projectName, nodeRoot);
|
|
460
|
-
const entryWiring = wireEntryFile(nodeRoot, initFilePath, pkg);
|
|
461
464
|
return {
|
|
462
465
|
projectId: created.id,
|
|
463
466
|
projectKey: created.key,
|
|
464
467
|
projectName,
|
|
465
|
-
projectKind:
|
|
466
|
-
initFilePath,
|
|
468
|
+
projectKind: kind,
|
|
469
|
+
initFilePath: wired.primaryInitPath,
|
|
467
470
|
envFilePath,
|
|
468
471
|
projectsJsonPath,
|
|
469
472
|
packageManager: pm,
|
|
470
473
|
packageInstalled,
|
|
471
|
-
entryWiring,
|
|
472
|
-
reused
|
|
474
|
+
entryWiring: wired.entryWiring,
|
|
475
|
+
reused,
|
|
476
|
+
secondaryInit: wired.secondaryInit,
|
|
477
|
+
warnings: wired.warnings
|
|
473
478
|
};
|
|
474
479
|
}
|
|
475
480
|
function findMappingByPath(projectsJsonPath, projectRoot) {
|
|
@@ -659,6 +664,349 @@ function isCommonJsEntry(entryPath, pkg) {
|
|
|
659
664
|
if (entryPath.endsWith(".ts") || entryPath.endsWith(".tsx")) return false;
|
|
660
665
|
return pkg.type !== "module";
|
|
661
666
|
}
|
|
667
|
+
function detectJsProjectKind(pkg, projectRoot) {
|
|
668
|
+
const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
669
|
+
if ("electron" in all) return "electron";
|
|
670
|
+
if (existsSync2(join2(projectRoot, "src-tauri")) || "@tauri-apps/api" in all) return "tauri";
|
|
671
|
+
if ("react-native" in all) return "react-native";
|
|
672
|
+
if ("next" in all) return "nextjs";
|
|
673
|
+
if ("@sveltejs/kit" in all) return "sveltekit";
|
|
674
|
+
if ("nuxt" in all || "nuxt3" in all) return "nuxt";
|
|
675
|
+
if ("@remix-run/react" in all || "@remix-run/node" in all) return "remix";
|
|
676
|
+
if (isBrowserProject(pkg, projectRoot)) return "browser";
|
|
677
|
+
return "node";
|
|
678
|
+
}
|
|
679
|
+
function wireFramework(w) {
|
|
680
|
+
switch (w.kind) {
|
|
681
|
+
case "node":
|
|
682
|
+
return wireNode(w);
|
|
683
|
+
case "browser":
|
|
684
|
+
return wireBrowser(w);
|
|
685
|
+
case "nextjs":
|
|
686
|
+
return wireNextjs(w);
|
|
687
|
+
case "sveltekit":
|
|
688
|
+
return wireSveltekit(w);
|
|
689
|
+
case "nuxt":
|
|
690
|
+
return wireNuxt(w);
|
|
691
|
+
case "remix":
|
|
692
|
+
return wireRemix(w);
|
|
693
|
+
case "electron":
|
|
694
|
+
return wireElectron(w);
|
|
695
|
+
case "tauri":
|
|
696
|
+
return wireTauri(w);
|
|
697
|
+
case "react-native":
|
|
698
|
+
return wireReactNative(w);
|
|
699
|
+
case "python":
|
|
700
|
+
throw new Error("Internal: python should have been handled earlier");
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
function wireNode({ projectRoot, pkg, ingestUrl }) {
|
|
704
|
+
const initPath = join2(projectRoot, "gg-pixel.init.mjs");
|
|
705
|
+
writeFileSync(initPath, renderInitFile(ingestUrl), "utf8");
|
|
706
|
+
return {
|
|
707
|
+
primaryInitPath: initPath,
|
|
708
|
+
entryWiring: wireEntryFile(projectRoot, initPath, pkg),
|
|
709
|
+
warnings: []
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function wireBrowser({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
713
|
+
const initPath = join2(projectRoot, "gg-pixel.init.mjs");
|
|
714
|
+
writeFileSync(initPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
715
|
+
return {
|
|
716
|
+
primaryInitPath: initPath,
|
|
717
|
+
entryWiring: wireEntryFile(projectRoot, initPath, pkg),
|
|
718
|
+
warnings: []
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function wireNextjs({ projectRoot, projectKey, ingestUrl }) {
|
|
722
|
+
const warnings = [];
|
|
723
|
+
const serverInitPath = pickPath(projectRoot, ["instrumentation.ts", "instrumentation.js"]);
|
|
724
|
+
const finalServerPath = serverInitPath ?? join2(projectRoot, "instrumentation.ts");
|
|
725
|
+
writeNextInstrumentation(finalServerPath, ingestUrl);
|
|
726
|
+
const clientInitPath = join2(projectRoot, "gg-pixel.client.mjs");
|
|
727
|
+
writeFileSync(clientInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
728
|
+
const layoutPath = findNextLayout(projectRoot);
|
|
729
|
+
let entryWiring;
|
|
730
|
+
if (!layoutPath) {
|
|
731
|
+
warnings.push(
|
|
732
|
+
'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.'
|
|
733
|
+
);
|
|
734
|
+
entryWiring = { kind: "no_entry_found" };
|
|
735
|
+
} else {
|
|
736
|
+
entryWiring = injectImport(layoutPath, clientInitPath);
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
primaryInitPath: clientInitPath,
|
|
740
|
+
entryWiring,
|
|
741
|
+
secondaryInit: {
|
|
742
|
+
path: finalServerPath,
|
|
743
|
+
description: "Next.js server instrumentation (auto-loaded by Next runtime)"
|
|
744
|
+
},
|
|
745
|
+
warnings
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function writeNextInstrumentation(path, ingestUrl) {
|
|
749
|
+
const existing = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
750
|
+
if (existing.includes("@kenkaiiii/gg-pixel")) return;
|
|
751
|
+
const newContent = existing ? existing + "\n" + nextInstrumentationAppend(ingestUrl) : nextInstrumentationStandalone(ingestUrl);
|
|
752
|
+
writeFileSync(path, newContent, "utf8");
|
|
753
|
+
}
|
|
754
|
+
function nextInstrumentationStandalone(ingestUrl) {
|
|
755
|
+
return `// Next.js auto-loads this file on server start. Pixel hooks the
|
|
756
|
+
// uncaughtExceptionMonitor + unhandledRejection events for API routes,
|
|
757
|
+
// Server Components, and route handlers.
|
|
758
|
+
export async function register() {
|
|
759
|
+
if (process.env.NEXT_RUNTIME === "nodejs") {
|
|
760
|
+
const { initPixel } = await import("@kenkaiiii/gg-pixel");
|
|
761
|
+
initPixel({
|
|
762
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
763
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
`;
|
|
768
|
+
}
|
|
769
|
+
function nextInstrumentationAppend(ingestUrl) {
|
|
770
|
+
return `// gg-pixel: server-side error tracking
|
|
771
|
+
import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
772
|
+
if (typeof process !== "undefined" && process.env.NEXT_RUNTIME === "nodejs") {
|
|
773
|
+
initPixel({
|
|
774
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
775
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
`;
|
|
779
|
+
}
|
|
780
|
+
function findNextLayout(projectRoot) {
|
|
781
|
+
const candidates = [
|
|
782
|
+
"app/layout.tsx",
|
|
783
|
+
"app/layout.jsx",
|
|
784
|
+
"app/layout.ts",
|
|
785
|
+
"src/app/layout.tsx",
|
|
786
|
+
"src/app/layout.jsx",
|
|
787
|
+
"pages/_app.tsx",
|
|
788
|
+
"pages/_app.jsx",
|
|
789
|
+
"src/pages/_app.tsx",
|
|
790
|
+
"src/pages/_app.jsx"
|
|
791
|
+
];
|
|
792
|
+
for (const c of candidates) {
|
|
793
|
+
const p = join2(projectRoot, c);
|
|
794
|
+
if (existsSync2(p)) return p;
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
function wireSveltekit({ projectRoot, projectKey, ingestUrl }) {
|
|
799
|
+
const serverPath = join2(projectRoot, "src/hooks.server.ts");
|
|
800
|
+
const clientPath = join2(projectRoot, "src/hooks.client.ts");
|
|
801
|
+
if (!existsSync2(dirname2(serverPath))) mkdirSync2(dirname2(serverPath), { recursive: true });
|
|
802
|
+
appendOrCreate(
|
|
803
|
+
serverPath,
|
|
804
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
805
|
+
initPixel({
|
|
806
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
807
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
808
|
+
});
|
|
809
|
+
`,
|
|
810
|
+
"@kenkaiiii/gg-pixel"
|
|
811
|
+
);
|
|
812
|
+
appendOrCreate(
|
|
813
|
+
clientPath,
|
|
814
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel/browser";
|
|
815
|
+
initPixel({
|
|
816
|
+
projectKey: ${JSON.stringify(projectKey)},
|
|
817
|
+
ingestUrl: ${JSON.stringify(ingestUrl)},
|
|
818
|
+
});
|
|
819
|
+
`,
|
|
820
|
+
"@kenkaiiii/gg-pixel/browser"
|
|
821
|
+
);
|
|
822
|
+
return {
|
|
823
|
+
primaryInitPath: clientPath,
|
|
824
|
+
entryWiring: { kind: "injected", entryPath: clientPath },
|
|
825
|
+
secondaryInit: {
|
|
826
|
+
path: serverPath,
|
|
827
|
+
description: "SvelteKit server hooks (auto-loaded)"
|
|
828
|
+
},
|
|
829
|
+
warnings: []
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function wireNuxt({ projectRoot, projectKey, ingestUrl }) {
|
|
833
|
+
const pluginsDir = join2(projectRoot, "plugins");
|
|
834
|
+
mkdirSync2(pluginsDir, { recursive: true });
|
|
835
|
+
const serverPath = join2(pluginsDir, "gg-pixel.server.ts");
|
|
836
|
+
const clientPath = join2(pluginsDir, "gg-pixel.client.ts");
|
|
837
|
+
writeFileSync(
|
|
838
|
+
serverPath,
|
|
839
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel";
|
|
840
|
+
export default defineNuxtPlugin(() => {
|
|
841
|
+
initPixel({
|
|
842
|
+
projectKey: process.env.GG_PIXEL_KEY ?? "",
|
|
843
|
+
sink: { kind: "http", ingestUrl: ${JSON.stringify(`${ingestUrl}/ingest`)} },
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
`,
|
|
847
|
+
"utf8"
|
|
848
|
+
);
|
|
849
|
+
writeFileSync(
|
|
850
|
+
clientPath,
|
|
851
|
+
`import { initPixel } from "@kenkaiiii/gg-pixel/browser";
|
|
852
|
+
export default defineNuxtPlugin(() => {
|
|
853
|
+
initPixel({
|
|
854
|
+
projectKey: ${JSON.stringify(projectKey)},
|
|
855
|
+
ingestUrl: ${JSON.stringify(ingestUrl)},
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
`,
|
|
859
|
+
"utf8"
|
|
860
|
+
);
|
|
861
|
+
return {
|
|
862
|
+
primaryInitPath: clientPath,
|
|
863
|
+
entryWiring: { kind: "injected", entryPath: clientPath },
|
|
864
|
+
secondaryInit: { path: serverPath, description: "Nuxt server plugin (auto-loaded)" },
|
|
865
|
+
warnings: []
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
function wireRemix({ projectRoot, projectKey, ingestUrl }) {
|
|
869
|
+
const serverPath = pickPath(projectRoot, ["app/entry.server.tsx", "app/entry.server.jsx"]);
|
|
870
|
+
const clientPath = pickPath(projectRoot, ["app/entry.client.tsx", "app/entry.client.jsx"]);
|
|
871
|
+
const warnings = [];
|
|
872
|
+
const clientInitPath = join2(projectRoot, "gg-pixel.client.mjs");
|
|
873
|
+
writeFileSync(clientInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
874
|
+
if (clientPath) {
|
|
875
|
+
injectImport(clientPath, clientInitPath);
|
|
876
|
+
} else {
|
|
877
|
+
warnings.push(
|
|
878
|
+
"No app/entry.client.tsx found. Run `npx remix reveal` then re-run pixel install."
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
const serverInitPath = join2(projectRoot, "gg-pixel.server.mjs");
|
|
882
|
+
writeFileSync(serverInitPath, renderInitFile(ingestUrl), "utf8");
|
|
883
|
+
let serverEntry = { kind: "no_entry_found" };
|
|
884
|
+
if (serverPath) {
|
|
885
|
+
serverEntry = injectImport(serverPath, serverInitPath);
|
|
886
|
+
} else {
|
|
887
|
+
warnings.push(
|
|
888
|
+
"No app/entry.server.tsx found. Run `npx remix reveal` then re-run pixel install."
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
void serverEntry;
|
|
892
|
+
return {
|
|
893
|
+
primaryInitPath: clientInitPath,
|
|
894
|
+
entryWiring: clientPath ? { kind: "injected", entryPath: clientPath } : { kind: "no_entry_found" },
|
|
895
|
+
secondaryInit: { path: serverInitPath, description: "Remix server init" },
|
|
896
|
+
warnings
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function wireElectron({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
900
|
+
const mainInitPath = join2(projectRoot, "gg-pixel.main.mjs");
|
|
901
|
+
writeFileSync(mainInitPath, renderInitFile(ingestUrl), "utf8");
|
|
902
|
+
const rendererInitPath = join2(projectRoot, "gg-pixel.renderer.mjs");
|
|
903
|
+
writeFileSync(rendererInitPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
904
|
+
const mainEntry = pkg.main ? join2(projectRoot, pkg.main) : pickPath(projectRoot, [
|
|
905
|
+
"main.js",
|
|
906
|
+
"main.ts",
|
|
907
|
+
"src/main.js",
|
|
908
|
+
"src/main.ts",
|
|
909
|
+
"electron/main.js",
|
|
910
|
+
"electron/main.ts"
|
|
911
|
+
]);
|
|
912
|
+
let mainWiring = { kind: "no_entry_found" };
|
|
913
|
+
if (mainEntry && existsSync2(mainEntry)) {
|
|
914
|
+
mainWiring = injectImport(mainEntry, mainInitPath);
|
|
915
|
+
}
|
|
916
|
+
const rendererEntry = pickPath(projectRoot, [
|
|
917
|
+
"src/renderer/index.ts",
|
|
918
|
+
"src/renderer/index.tsx",
|
|
919
|
+
"src/renderer/main.ts",
|
|
920
|
+
"src/renderer/main.tsx",
|
|
921
|
+
"renderer/index.ts",
|
|
922
|
+
"renderer/index.tsx",
|
|
923
|
+
"renderer.ts",
|
|
924
|
+
"renderer.tsx",
|
|
925
|
+
"src/index.tsx",
|
|
926
|
+
"src/main.tsx"
|
|
927
|
+
]);
|
|
928
|
+
if (rendererEntry) {
|
|
929
|
+
injectImport(rendererEntry, rendererInitPath);
|
|
930
|
+
}
|
|
931
|
+
const warnings = [];
|
|
932
|
+
if (!rendererEntry) {
|
|
933
|
+
warnings.push(
|
|
934
|
+
'Could not auto-detect the Electron renderer entry. Add `import "./gg-pixel.renderer.mjs";` to the top of your renderer entry file.'
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
primaryInitPath: rendererInitPath,
|
|
939
|
+
entryWiring: rendererEntry ? { kind: "injected", entryPath: rendererEntry } : { kind: "no_entry_found" },
|
|
940
|
+
secondaryInit: {
|
|
941
|
+
path: mainInitPath,
|
|
942
|
+
description: "Electron main-process init" + (mainWiring.kind === "injected" ? ` (wired into ${mainEntry})` : "")
|
|
943
|
+
},
|
|
944
|
+
warnings
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function wireTauri({ projectRoot, pkg, projectKey, ingestUrl }) {
|
|
948
|
+
const initPath = join2(projectRoot, "gg-pixel.init.mjs");
|
|
949
|
+
writeFileSync(initPath, renderBrowserInitFile(ingestUrl, projectKey), "utf8");
|
|
950
|
+
const entryWiring = wireEntryFile(projectRoot, initPath, pkg);
|
|
951
|
+
return {
|
|
952
|
+
primaryInitPath: initPath,
|
|
953
|
+
entryWiring,
|
|
954
|
+
warnings: [
|
|
955
|
+
"Tauri Rust backend is not instrumented \u2014 no Rust SDK exists yet. Frontend errors are captured."
|
|
956
|
+
]
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function wireReactNative({ projectRoot }) {
|
|
960
|
+
return {
|
|
961
|
+
primaryInitPath: join2(projectRoot, "(not-installed)"),
|
|
962
|
+
entryWiring: { kind: "skipped", reason: "react-native SDK not built yet" },
|
|
963
|
+
warnings: [
|
|
964
|
+
"React Native is not yet supported \u2014 its JS runtime is neither browser nor Node.",
|
|
965
|
+
"A dedicated React Native SDK will be a future slice."
|
|
966
|
+
]
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function pickPath(root, candidates) {
|
|
970
|
+
for (const c of candidates) {
|
|
971
|
+
const p = join2(root, c);
|
|
972
|
+
if (existsSync2(p)) return p;
|
|
973
|
+
}
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
function appendOrCreate(filePath, snippet, marker) {
|
|
977
|
+
if (existsSync2(filePath)) {
|
|
978
|
+
const existing = readFileSync2(filePath, "utf8");
|
|
979
|
+
if (existing.includes(marker)) return;
|
|
980
|
+
writeFileSync(filePath, existing + "\n" + snippet, "utf8");
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
writeFileSync(filePath, snippet, "utf8");
|
|
984
|
+
}
|
|
985
|
+
function injectImport(entryPath, initFilePath) {
|
|
986
|
+
let content;
|
|
987
|
+
try {
|
|
988
|
+
content = readFileSync2(entryPath, "utf8");
|
|
989
|
+
} catch (err) {
|
|
990
|
+
return { kind: "skipped", reason: `unreadable: ${err.message}` };
|
|
991
|
+
}
|
|
992
|
+
const initBasename = initFilePath.split(sep).pop() ?? "gg-pixel.init.mjs";
|
|
993
|
+
if (content.includes(initBasename) || content.includes("@kenkaiiii/gg-pixel")) {
|
|
994
|
+
return { kind: "already_present", entryPath };
|
|
995
|
+
}
|
|
996
|
+
const fromDir = dirname2(entryPath);
|
|
997
|
+
let spec = relative(fromDir, initFilePath).split(sep).join("/");
|
|
998
|
+
if (!spec.startsWith(".")) spec = "./" + spec;
|
|
999
|
+
const importLine = `import ${JSON.stringify(spec)};`;
|
|
1000
|
+
const lines = content.split("\n");
|
|
1001
|
+
let insertAt = 0;
|
|
1002
|
+
if (lines[0]?.startsWith("#!")) insertAt = 1;
|
|
1003
|
+
while (insertAt < lines.length && /^\s*(?:["']use strict["']|\/\/|\/\*)/.test(lines[insertAt] ?? "")) {
|
|
1004
|
+
insertAt++;
|
|
1005
|
+
}
|
|
1006
|
+
const updated = [...lines.slice(0, insertAt), importLine, ...lines.slice(insertAt)].join("\n");
|
|
1007
|
+
writeFileSync(entryPath, updated, "utf8");
|
|
1008
|
+
return { kind: "injected", entryPath };
|
|
1009
|
+
}
|
|
662
1010
|
function isBrowserProject(pkg, projectRoot) {
|
|
663
1011
|
const all = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
664
1012
|
const browserishDeps = [
|
|
@@ -751,7 +1099,8 @@ async function installPython(ctx) {
|
|
|
751
1099
|
packageManager: pm,
|
|
752
1100
|
packageInstalled,
|
|
753
1101
|
entryWiring,
|
|
754
|
-
reused
|
|
1102
|
+
reused,
|
|
1103
|
+
warnings: []
|
|
755
1104
|
};
|
|
756
1105
|
}
|
|
757
1106
|
function readPyprojectName(projectRoot) {
|