usetraceforge-cli 0.1.3 → 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.
@@ -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("Configuring Next.js...");
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,34 +19,53 @@ export async function installNextJs(apiKey, endpoint) {
18
19
  else {
19
20
  fs.writeFileSync(envPath, envVar);
20
21
  }
21
- // 2. Generate TraceForgeProvider.tsx
22
- const componentsDir = path.resolve(process.cwd(), "components");
23
- if (!fs.existsSync(componentsDir)) {
24
- fs.mkdirSync(componentsDir);
22
+ const project = new Project();
23
+ // 2. Next.js Config AST Injection
24
+ const configPaths = [
25
+ path.resolve(process.cwd(), "next.config.ts"),
26
+ path.resolve(process.cwd(), "next.config.mjs"),
27
+ path.resolve(process.cwd(), "next.config.js"),
28
+ ];
29
+ let targetConfig = null;
30
+ for (const p of configPaths) {
31
+ if (fs.existsSync(p)) {
32
+ targetConfig = p;
33
+ break;
34
+ }
25
35
  }
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
36
+ if (targetConfig) {
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
+ }
54
+ }
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");
58
+ configCode = `const { withTraceForgeConfig } = require("usetraceforge/next-plugin");\n` + configCode;
59
+ configCode = configCode.replace(/module\.exports = (.+);/g, `module.exports = withTraceForgeConfig($1);`);
60
+ fs.writeFileSync(targetConfig, configCode);
61
+ }
62
+ sourceFile.saveSync();
63
+ }
64
+ }
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
49
69
  const layoutPaths = [
50
70
  path.resolve(process.cwd(), "app/layout.tsx"),
51
71
  path.resolve(process.cwd(), "src/app/layout.tsx"),
@@ -57,25 +77,35 @@ export function TraceForgeProvider({ children }: { children: React.ReactNode })
57
77
  break;
58
78
  }
59
79
  }
60
- if (!targetLayout) {
61
- s.stop(chalk.yellow("Could not find app/layout.tsx. Please configure TraceForge manually."));
62
- return;
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
+ }
63
101
  }
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;
102
+ else {
103
+ console.log(chalk.yellow("\nAgent: Could not find layout.tsx. Please add <TraceForgeProvider> manually."));
69
104
  }
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
- s.stop(chalk.green("Next.js configuration complete!"));
105
+ s.stop(chalk.green("Agent: Next.js configuration complete!"));
76
106
  }
77
107
  catch (error) {
78
- s.stop(chalk.red("Failed to configure Next.js automatically."));
108
+ s.stop(chalk.red("Agent: An error occurred while parsing files. Please configure manually."));
79
109
  console.error(error);
80
110
  }
81
111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usetraceforge-cli",
3
- "version": "0.1.3",
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
- "typescript": "^5.5.4",
26
- "tsx": "^4.16.2"
26
+ "tsx": "^4.16.2",
27
+ "typescript": "^5.5.4"
27
28
  }
28
29
  }