usetraceforge-cli 0.1.4 → 0.1.5
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/installers/nextjs.js +68 -66
- package/package.json +5 -4
|
@@ -2,9 +2,10 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { spinner } from "@clack/prompts";
|
|
4
4
|
import chalk from "chalk";
|
|
5
|
+
import { Project, SyntaxKind } from "ts-morph";
|
|
5
6
|
export async function installNextJs(apiKey, endpoint) {
|
|
6
7
|
const s = spinner();
|
|
7
|
-
s.start("
|
|
8
|
+
s.start("Agent: Scanning and configuring Next.js...");
|
|
8
9
|
try {
|
|
9
10
|
// 1. Add API key and endpoint to .env.local
|
|
10
11
|
const envPath = path.resolve(process.cwd(), ".env.local");
|
|
@@ -18,62 +19,10 @@ export async function installNextJs(apiKey, endpoint) {
|
|
|
18
19
|
else {
|
|
19
20
|
fs.writeFileSync(envPath, envVar);
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!fs.existsSync(componentsDir)) {
|
|
24
|
-
fs.mkdirSync(componentsDir);
|
|
25
|
-
}
|
|
26
|
-
const providerCode = `"use client";
|
|
27
|
-
import { useEffect } from "react";
|
|
28
|
-
import TraceForge from "usetraceforge";
|
|
29
|
-
import { TraceForgeErrorBoundary } from "usetraceforge/react";
|
|
30
|
-
|
|
31
|
-
let initialized = false;
|
|
32
|
-
|
|
33
|
-
export function TraceForgeProvider({ children }: { children: React.ReactNode }) {
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (initialized) return;
|
|
36
|
-
TraceForge.init({
|
|
37
|
-
apiKey: process.env.NEXT_PUBLIC_TRACEFORGE_API_KEY!,
|
|
38
|
-
endpoint: process.env.NEXT_PUBLIC_TRACEFORGE_INGEST_URL,
|
|
39
|
-
autoCapture: true,
|
|
40
|
-
});
|
|
41
|
-
initialized = true;
|
|
42
|
-
}, []);
|
|
43
|
-
|
|
44
|
-
return <TraceForgeErrorBoundary>{children}</TraceForgeErrorBoundary>;
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
fs.writeFileSync(path.join(componentsDir, "TraceForgeProvider.tsx"), providerCode);
|
|
48
|
-
// 3. Find layout.tsx
|
|
49
|
-
const layoutPaths = [
|
|
50
|
-
path.resolve(process.cwd(), "app/layout.tsx"),
|
|
51
|
-
path.resolve(process.cwd(), "src/app/layout.tsx"),
|
|
52
|
-
];
|
|
53
|
-
let targetLayout = null;
|
|
54
|
-
for (const p of layoutPaths) {
|
|
55
|
-
if (fs.existsSync(p)) {
|
|
56
|
-
targetLayout = p;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (!targetLayout) {
|
|
61
|
-
s.stop(chalk.yellow("Could not find app/layout.tsx. Please configure TraceForge manually."));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
// 4. Inject code into layout.tsx
|
|
65
|
-
let layoutCode = fs.readFileSync(targetLayout, "utf-8");
|
|
66
|
-
if (layoutCode.includes("TraceForgeProvider")) {
|
|
67
|
-
s.stop(chalk.green("TraceForge is already configured in layout.tsx!"));
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const importStatement = `import { TraceForgeProvider } from "@/components/TraceForgeProvider";\n`;
|
|
71
|
-
layoutCode = importStatement + layoutCode;
|
|
72
|
-
layoutCode = layoutCode.replace(/(<body[^>]*>)/g, `$1\n <TraceForgeProvider>`);
|
|
73
|
-
layoutCode = layoutCode.replace(/<\/body>/g, ` </TraceForgeProvider>\n </body>`);
|
|
74
|
-
fs.writeFileSync(targetLayout, layoutCode);
|
|
75
|
-
// 5. Update next.config.js / next.config.mjs
|
|
22
|
+
const project = new Project();
|
|
23
|
+
// 2. Next.js Config AST Injection
|
|
76
24
|
const configPaths = [
|
|
25
|
+
path.resolve(process.cwd(), "next.config.ts"),
|
|
77
26
|
path.resolve(process.cwd(), "next.config.mjs"),
|
|
78
27
|
path.resolve(process.cwd(), "next.config.js"),
|
|
79
28
|
];
|
|
@@ -85,25 +34,78 @@ export function TraceForgeProvider({ children }: { children: React.ReactNode })
|
|
|
85
34
|
}
|
|
86
35
|
}
|
|
87
36
|
if (targetConfig) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
37
|
+
const sourceFile = project.addSourceFileAtPath(targetConfig);
|
|
38
|
+
const text = sourceFile.getFullText();
|
|
39
|
+
if (!text.includes("withTraceForgeConfig")) {
|
|
40
|
+
const isEsm = targetConfig.endsWith(".ts") || targetConfig.endsWith(".mjs") || text.includes("export default");
|
|
41
|
+
if (isEsm) {
|
|
42
|
+
sourceFile.addImportDeclaration({
|
|
43
|
+
namedImports: ["withTraceForgeConfig"],
|
|
44
|
+
moduleSpecifier: "usetraceforge/next-plugin"
|
|
45
|
+
});
|
|
46
|
+
const defaultExport = sourceFile.getExportAssignment(d => !d.isExportEquals());
|
|
47
|
+
if (defaultExport) {
|
|
48
|
+
const expression = defaultExport.getExpression();
|
|
49
|
+
defaultExport.setExpression(`withTraceForgeConfig(${expression.getText()})`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log(chalk.yellow("\nAgent: Could not parse export default in next.config. Please wrap your config manually."));
|
|
53
|
+
}
|
|
95
54
|
}
|
|
96
55
|
else {
|
|
56
|
+
// Fallback to simpler regex for CJS files if AST binary expression is too complex
|
|
57
|
+
let configCode = fs.readFileSync(targetConfig, "utf-8");
|
|
97
58
|
configCode = `const { withTraceForgeConfig } = require("usetraceforge/next-plugin");\n` + configCode;
|
|
98
59
|
configCode = configCode.replace(/module\.exports = (.+);/g, `module.exports = withTraceForgeConfig($1);`);
|
|
60
|
+
fs.writeFileSync(targetConfig, configCode);
|
|
99
61
|
}
|
|
100
|
-
|
|
62
|
+
sourceFile.saveSync();
|
|
101
63
|
}
|
|
102
64
|
}
|
|
103
|
-
|
|
65
|
+
else {
|
|
66
|
+
console.log(chalk.yellow("\nAgent: Could not locate next.config.ts/mjs. Please configure Webpack loader manually."));
|
|
67
|
+
}
|
|
68
|
+
// 3. Layout AST Injection
|
|
69
|
+
const layoutPaths = [
|
|
70
|
+
path.resolve(process.cwd(), "app/layout.tsx"),
|
|
71
|
+
path.resolve(process.cwd(), "src/app/layout.tsx"),
|
|
72
|
+
];
|
|
73
|
+
let targetLayout = null;
|
|
74
|
+
for (const p of layoutPaths) {
|
|
75
|
+
if (fs.existsSync(p)) {
|
|
76
|
+
targetLayout = p;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (targetLayout) {
|
|
81
|
+
const sourceFile = project.addSourceFileAtPath(targetLayout);
|
|
82
|
+
if (!sourceFile.getFullText().includes("TraceForgeProvider")) {
|
|
83
|
+
sourceFile.addImportDeclaration({
|
|
84
|
+
namedImports: ["TraceForgeProvider"],
|
|
85
|
+
moduleSpecifier: "usetraceforge/react"
|
|
86
|
+
});
|
|
87
|
+
// Find body tag
|
|
88
|
+
const jsxElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement);
|
|
89
|
+
let bodyTag = jsxElements.find(el => el.getOpeningElement().getTagNameNode().getText() === "body");
|
|
90
|
+
if (bodyTag) {
|
|
91
|
+
const childrenText = bodyTag.getJsxChildren().map(c => c.getText()).join("");
|
|
92
|
+
const opening = bodyTag.getOpeningElement().getText();
|
|
93
|
+
const closing = bodyTag.getClosingElement().getText();
|
|
94
|
+
bodyTag.replaceWithText(`${opening}\n <TraceForgeProvider>\n ${childrenText}\n </TraceForgeProvider>\n ${closing}`);
|
|
95
|
+
sourceFile.saveSync();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(chalk.yellow("\nAgent: Could not find <body> tag in layout.tsx. Please add <TraceForgeProvider> manually."));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(chalk.yellow("\nAgent: Could not find layout.tsx. Please add <TraceForgeProvider> manually."));
|
|
104
|
+
}
|
|
105
|
+
s.stop(chalk.green("Agent: Next.js configuration complete!"));
|
|
104
106
|
}
|
|
105
107
|
catch (error) {
|
|
106
|
-
s.stop(chalk.red("
|
|
108
|
+
s.stop(chalk.red("Agent: An error occurred while parsing files. Please configure manually."));
|
|
107
109
|
console.error(error);
|
|
108
110
|
}
|
|
109
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "usetraceforge-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "TraceForge CLI Wizard for 2-click installations",
|
|
5
5
|
"bin": {
|
|
6
6
|
"traceforge": "./dist/index.js"
|
|
@@ -18,11 +18,12 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@clack/prompts": "^0.7.0",
|
|
20
20
|
"chalk": "^5.3.0",
|
|
21
|
-
"execa": "^9.3.0"
|
|
21
|
+
"execa": "^9.3.0",
|
|
22
|
+
"ts-morph": "^28.0.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^20.0.0",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
26
|
+
"tsx": "^4.16.2",
|
|
27
|
+
"typescript": "^5.5.4"
|
|
27
28
|
}
|
|
28
29
|
}
|