@keak/sdk 1.0.8 → 2.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keak/sdk",
3
- "version": "1.0.8",
3
+ "version": "2.0.0",
4
4
  "description": "Production-ready A/B testing and experimentation SDK for React applications with visual editing, source mapping, and real-time variant testing",
5
5
  "author": "Keak Team",
6
6
  "homepage": "https://www.keak.com/",
@@ -27,11 +27,6 @@
27
27
  "import": "./dist/toolbar.js",
28
28
  "default": "./dist/toolbar.js"
29
29
  },
30
- "./toolbar/KeakToolbarNew": {
31
- "types": "./dist/toolbar/KeakToolbarNew.d.ts",
32
- "import": "./dist/toolbar/KeakToolbarNew.js",
33
- "default": "./dist/toolbar/KeakToolbarNew.js"
34
- },
35
30
  "./toolbar.css": "./dist/toolbar.css",
36
31
  "./keak-toolbar.css": "./dist/keak-toolbar.css",
37
32
  "./plugins/next": "./src/plugins/next.cjs",
@@ -80,14 +75,9 @@
80
75
  }
81
76
  },
82
77
  "dependencies": {
83
- "@jsx-tool/jsx-tool": "^0.0.21",
84
- "@radix-ui/react-dropdown-menu": "^2.1.16",
85
- "@radix-ui/react-progress": "^1.1.7",
86
- "@radix-ui/react-slot": "^1.2.3",
87
- "@supabase/supabase-js": "^2.86.0",
88
- "class-variance-authority": "^0.7.1",
78
+ "@babel/parser": "^7.26.3",
79
+ "@babel/traverse": "^7.26.5",
89
80
  "clsx": "^2.1.1",
90
- "framer-motion": "^12.23.12",
91
81
  "lucide-react": "^0.542.0",
92
82
  "tailwind-merge": "^3.3.1",
93
83
  "tw-animate-css": "^1.3.8"
@@ -342,7 +342,7 @@ class KeakInstaller {
342
342
  if (bodyMatch) {
343
343
  const bodyContent = bodyMatch[2].trim();
344
344
  const wrapped = `${bodyMatch[1]}
345
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
345
+ <KeakProvider>
346
346
  ${bodyContent}
347
347
  <KeakToolbar />
348
348
  </KeakProvider>
@@ -371,7 +371,7 @@ ${bodyContent}
371
371
  const componentMatch = modified.match(/(return\s*\(?\s*)([\s\S]*?)(\s*\)?;?\s*})/);
372
372
  if (componentMatch) {
373
373
  const wrapped = `${componentMatch[1]}
374
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
374
+ <KeakProvider>
375
375
  ${componentMatch[2]}
376
376
  <KeakToolbar />
377
377
  </KeakProvider>
@@ -408,7 +408,7 @@ ${bodyContent}
408
408
  const renderMatch = modified.match(/(ReactDOM\.render\s*\(\s*)([\s\S]*?)(\s*,\s*document\.getElementById)/);
409
409
  if (renderMatch) {
410
410
  const wrapped = `${renderMatch[1]}
411
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
411
+ <KeakProvider>
412
412
  ${renderMatch[2]}
413
413
  <KeakToolbar />
414
414
  </KeakProvider>
@@ -420,7 +420,7 @@ ${bodyContent}
420
420
  const renderMatch = modified.match(/(root\.render\s*\(\s*)([\s\S]*?)(\s*\))/);
421
421
  if (renderMatch) {
422
422
  const wrapped = `${renderMatch[1]}
423
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
423
+ <KeakProvider>
424
424
  ${renderMatch[2]}
425
425
  <KeakToolbar />
426
426
  </KeakProvider>
@@ -513,7 +513,7 @@ Please add Keak to your app manually:
513
513
  ${colors.green}import { KeakProvider } from '@keak/sdk';${colors.reset}
514
514
 
515
515
  3. Wrap your main component:
516
- ${colors.green}<KeakProvider config={{ apiKey: 'demo', debug: true }}><YourApp /></KeakProvider>${colors.reset}
516
+ ${colors.green}<KeakProvider><YourApp /></KeakProvider>${colors.reset}
517
517
 
518
518
  Example for ${this.framework}:
519
519
  ${this.getFrameworkExample()}
@@ -529,7 +529,7 @@ import { KeakProvider } from '@keak/sdk';
529
529
  export default function RootLayout({ children }) {
530
530
  return (
531
531
  <html><body>
532
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
532
+ <KeakProvider>
533
533
  {children}
534
534
  </KeakProvider>
535
535
  </body></html>);
@@ -539,7 +539,7 @@ export default function RootLayout({ children }) {
539
539
  import { KeakProvider } from '@keak/sdk';
540
540
 
541
541
  root.render(
542
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
542
+ <KeakProvider>
543
543
  <App />
544
544
  </KeakProvider>
545
545
  );`,
@@ -548,7 +548,7 @@ root.render(
548
548
  import { KeakProvider } from '@keak/sdk';
549
549
 
550
550
  ReactDOM.createRoot(document.getElementById('root')).render(
551
- <KeakProvider config={{ apiKey: 'demo', debug: true }}>
551
+ <KeakProvider>
552
552
  <App />
553
553
  </KeakProvider>
554
554
  );`
@@ -1,170 +1,94 @@
1
1
  /**
2
- * Babel plugin to inject source location metadata into JSX elements
3
- * This enables precise DOM element to source code mapping
2
+ * Keak Babel Plugin - Source Location Injector
3
+ * Simplified version inspired by react-source-lens
4
+ * Injects data-keak-file and data-keak-line attributes into JSX elements
4
5
  */
5
6
 
6
- console.log('[Keak Babel Plugin] MODULE LOADED - This means Babel is requiring this file');
7
-
8
7
  const path = require('path');
9
8
 
10
- let processedCount = 0;
11
-
12
9
  function keakSourceInjectorPlugin({ types: t }) {
13
- console.log('[Keak Babel Plugin] Plugin function called - Plugin loaded and initialized');
14
10
  return {
15
11
  name: 'keak-source-injector',
16
12
  visitor: {
17
13
  Program: {
18
- enter(path, state) {
19
- // Check if this file imports from next/font
20
- // If so, skip the entire file to avoid Babel/SWC conflict
21
- state.skipFile = false;
14
+ enter(programPath, state) {
15
+ // Skip in production builds
16
+ if (process.env.NODE_ENV === 'production' && !state.opts.forceProduction) {
17
+ state.skipFile = true;
18
+ return;
19
+ }
22
20
 
23
- path.traverse({
21
+ // Skip next/font files (Babel/SWC conflict)
22
+ state.skipFile = false;
23
+ programPath.traverse({
24
24
  ImportDeclaration(importPath) {
25
25
  const source = importPath.node.source.value;
26
26
  if (source.includes('next/font') || source.includes('@next/font')) {
27
27
  state.skipFile = true;
28
- importPath.stop(); // Stop traversing
28
+ importPath.stop();
29
29
  }
30
30
  }
31
31
  });
32
- },
33
- exit() {
34
- if (processedCount > 0) {
35
- console.log(`[Keak Babel Plugin] Processed ${processedCount} JSX elements`);
36
- processedCount = 0;
37
- }
38
32
  }
39
33
  },
40
- JSXOpeningElement(nodePath, state) {
41
- // Skip entire file if it uses next/font (Babel/SWC conflict)
42
- if (state.skipFile) {
43
- return;
44
- }
45
34
 
46
- // Skip if we're not in development or if disabled
47
- if (process.env.NODE_ENV === 'production' && !state.opts.forceProduction) {
35
+ JSXOpeningElement(nodePath, state) {
36
+ // Skip if file should be skipped or no location info
37
+ if (state.skipFile || !nodePath.node.loc) {
48
38
  return;
49
39
  }
50
40
 
51
- // Skip if element already has keak source attribute
52
- const existingAttr = nodePath.node.attributes.find((attr) =>
41
+ // Skip if already has keak attributes
42
+ const hasKeakAttr = nodePath.node.attributes.some((attr) =>
53
43
  t.isJSXAttribute(attr) &&
54
44
  t.isJSXIdentifier(attr.name) &&
55
- attr.name.name === 'data-keak-src'
45
+ (attr.name.name === 'data-keak-file' || attr.name.name === 'data-keak-src')
56
46
  );
57
-
58
- if (existingAttr) {
59
- return;
60
- }
61
-
62
- // Get source location info
63
- const loc = nodePath.node.loc;
64
- if (!loc) return;
47
+ if (hasKeakAttr) return;
65
48
 
66
49
  const filename = state.file.opts.filename;
67
- // Skip if filename is not available or not a valid string
68
- if (!filename || typeof filename !== 'string') {
69
- return;
70
- }
71
-
72
- // Skip next/font loader files to avoid Babel/SWC conflict
73
- // next/font requires SWC and cannot be processed by Babel
74
- if (filename.includes('next/font') || filename.includes('@next/font')) {
75
- return;
76
- }
77
-
78
- // Create relative path from project root
79
- const relativePath = path.relative(process.cwd(), filename);
80
-
81
- // Create source location string: "file:line:column"
82
- const sourceLocation = `${relativePath}:${loc.start.line}:${loc.start.column}`;
83
-
84
- // Get element name for filtering
85
- const elementName = nodePath.node.name;
86
- let tagName = '';
87
-
88
- if (t.isJSXIdentifier(elementName)) {
89
- tagName = elementName.name;
90
- } else if (t.isJSXMemberExpression(elementName)) {
91
- // Handle cases like <motion.div>
92
- tagName = elementName.property.name;
93
- }
94
-
95
- // Only instrument elements we care about
96
- const targetElements = new Set([
97
- 'div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
98
- 'button', 'a', 'input', 'textarea', 'select', 'form',
99
- 'img', 'svg', 'canvas', 'video', 'audio',
100
- 'section', 'article', 'header', 'footer', 'nav', 'main',
101
- 'ul', 'ol', 'li', 'table', 'tr', 'td', 'th'
102
- ]);
103
-
104
- // Also instrument custom components (capitalized names)
105
- const isCustomComponent = tagName && tagName[0] === tagName[0].toUpperCase();
106
- const isTargetElement = targetElements.has(tagName.toLowerCase());
107
-
108
- if (!isTargetElement && !isCustomComponent) {
109
- return;
110
- }
111
-
112
- // Create the data attribute
113
- const sourceAttr = t.jsxAttribute(
114
- t.jsxIdentifier('data-keak-src'),
115
- t.stringLiteral(sourceLocation)
116
- );
117
-
118
- // Add component context if available
119
- let componentName = '';
120
- let currentPath = nodePath;
121
-
122
- // Walk up to find the containing component
123
- while (currentPath) {
124
- if (currentPath.isFunctionDeclaration() && currentPath.node.id) {
125
- componentName = currentPath.node.id.name;
126
- break;
127
- }
128
-
129
- if (currentPath.isVariableDeclarator() && t.isIdentifier(currentPath.node.id)) {
130
- const init = currentPath.node.init;
131
- if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
132
- componentName = currentPath.node.id.name;
133
- break;
50
+ if (!filename || typeof filename !== 'string') return;
51
+
52
+ // Smart relative path detection (like react-source-lens)
53
+ let sourceFile;
54
+
55
+ // Try src/ directory first
56
+ const srcParts = filename.split('/src/');
57
+ if (srcParts.length > 1) {
58
+ sourceFile = 'src/' + srcParts[srcParts.length - 1];
59
+ } else {
60
+ // Try app/ directory (Next.js App Router)
61
+ const appParts = filename.split('/app/');
62
+ if (appParts.length > 1) {
63
+ sourceFile = 'app/' + appParts[appParts.length - 1];
64
+ } else {
65
+ // Try components/ directory
66
+ const compParts = filename.split('/components/');
67
+ if (compParts.length > 1) {
68
+ sourceFile = 'components/' + compParts[compParts.length - 1];
69
+ } else {
70
+ // Fallback: relative from cwd
71
+ sourceFile = path.relative(process.cwd(), filename);
134
72
  }
135
73
  }
136
-
137
- currentPath = currentPath.parentPath;
138
- }
139
-
140
- // Add component context attribute if found
141
- if (componentName) {
142
- const componentAttr = t.jsxAttribute(
143
- t.jsxIdentifier('data-keak-component'),
144
- t.stringLiteral(componentName)
145
- );
146
- nodePath.node.attributes.push(componentAttr);
147
74
  }
148
75
 
149
- // Add the source location attribute
150
- nodePath.node.attributes.push(sourceAttr);
151
- processedCount++;
152
-
153
- // Optional: Add element index within component for disambiguation
154
- if (state.opts.includeElementIndex) {
155
- const elementIndex = state.elementIndex || 0;
156
- state.elementIndex = elementIndex + 1;
157
-
158
- const indexAttr = t.jsxAttribute(
159
- t.jsxIdentifier('data-keak-idx'),
160
- t.stringLiteral(String(elementIndex))
161
- );
162
- nodePath.node.attributes.push(indexAttr);
163
- }
76
+ const { line } = nodePath.node.loc.start;
77
+
78
+ // Add data-keak-file and data-keak-line attributes
79
+ nodePath.node.attributes.push(
80
+ t.jsxAttribute(
81
+ t.jsxIdentifier('data-keak-file'),
82
+ t.stringLiteral(sourceFile)
83
+ ),
84
+ t.jsxAttribute(
85
+ t.jsxIdentifier('data-keak-line'),
86
+ t.stringLiteral(String(line))
87
+ )
88
+ );
164
89
  }
165
90
  }
166
91
  };
167
92
  }
168
93
 
169
94
  module.exports = keakSourceInjectorPlugin;
170
-
@@ -1,245 +1,73 @@
1
1
  /**
2
2
  * Keak Next.js Plugin
3
- *
4
- * Automatically injects source location attributes into JSX elements
5
- * for use with Keak IDE element selection.
6
- *
3
+ * Simplified version - injects source location attributes in development only
4
+ *
7
5
  * Usage in next.config.js:
8
- *
9
- * const { withKeak } = require('@keak/sdk/plugins/next');
10
- *
11
- * module.exports = withKeak({
12
- * // your existing Next.js config
13
- * });
14
- *
15
- * Or for ESM (next.config.mjs):
16
- *
17
- * import { withKeak } from '@keak/sdk/plugins/next';
18
- * export default withKeak({ ... });
6
+ * const { withKeak } = require('@keak/sdk/next');
7
+ * module.exports = withKeak({ ... });
19
8
  */
20
9
 
21
10
  const path = require('path');
11
+ const fs = require('fs');
22
12
 
23
13
  function withKeak(nextConfig = {}) {
24
- console.log('[Keak] withKeak() called - plugin loaded successfully');
25
14
  return {
26
15
  ...nextConfig,
27
16
  webpack(config, options) {
28
- console.log('[Keak] webpack() called with options:', { dev: options.dev, isServer: options.isServer });
29
- const { dev, isServer } = options;
17
+ const { dev } = options;
30
18
 
31
- // Only inject in development mode (both client and server)
32
- // Server components also need attributes for source mapping
33
- if (dev) {
34
- console.log('[Keak] Configuring Keak webpack loader for source injection...');
35
- console.log('[Keak] DEBUG: dev=', dev, 'isServer=', isServer, 'processing:', isServer ? 'server build' : 'client build');
19
+ // Only inject loader in development mode
20
+ if (!dev) {
21
+ // Call original webpack config if it exists
22
+ if (typeof nextConfig.webpack === 'function') {
23
+ return nextConfig.webpack(config, options);
24
+ }
25
+ return config;
26
+ }
36
27
 
37
- // Helper function to resolve modules from the project's node_modules
38
- const resolveFromProject = (moduleName) => {
39
- try {
40
- // Try to resolve from the project's context (where next.config.js is)
41
- return require.resolve(moduleName, {
42
- paths: [process.cwd()]
43
- });
44
- } catch (e) {
45
- // Fallback to resolving from @keak/sdk's context
46
- console.warn(`[Keak] Could not resolve ${moduleName} from project, falling back to @keak/sdk`);
47
- return require.resolve(moduleName);
48
- }
49
- };
28
+ // Resolve loader path from project .keak directory or fallback
29
+ const projectKeakLoader = path.resolve(process.cwd(), '.keak/webpack-loader-babel/index.js');
30
+ const keakLoaderPath = fs.existsSync(projectKeakLoader)
31
+ ? projectKeakLoader
32
+ : path.resolve(__dirname, 'webpack-loader-babel/index.js');
50
33
 
51
- // Ensure module.rules exists
34
+ // Ensure config.module.rules exists
35
+ if (!config.module || !config.module.rules) {
52
36
  config.module = config.module || {};
53
37
  config.module.rules = config.module.rules || [];
38
+ }
54
39
 
55
- // Add Keak loader to run pre-SWC for client code
56
- // The loader resolves from .keak/webpack-loader-babel which is copied by keak-code during project setup.
57
- const fs = require('fs');
58
- const pathModule = require('path');
59
-
60
- console.log('[Keak] About to resolve loader path...');
61
- console.log('[Keak] Current working directory:', process.cwd());
62
-
63
- // Try to find the loader in the project's .keak directory (copied by keak-code setup)
64
- // Using the Babel-based loader that runs BEFORE SWC (pre-compilation)
65
- const projectKeakLoader = pathModule.resolve(process.cwd(), '.keak/webpack-loader-babel/index.js');
66
-
67
- let keakLoaderPath;
68
- let loaderSource = 'unknown';
69
-
70
- // Check if loader exists in project .keak directory
71
- try {
72
- if (fs.existsSync(projectKeakLoader)) {
73
- console.log('[Keak] Found loader in project .keak directory');
74
- keakLoaderPath = projectKeakLoader;
75
- loaderSource = 'project';
76
- } else {
77
- // Fallback to project loader path even if it doesn't exist yet
78
- console.warn('[Keak] Loader not found, using fallback path:', projectKeakLoader);
79
- keakLoaderPath = projectKeakLoader;
80
- loaderSource = 'fallback';
81
- }
82
- } catch (pathError) {
83
- console.error('[Keak] Error resolving loader path:', pathError.message);
84
- keakLoaderPath = projectKeakLoader;
85
- loaderSource = 'error';
86
- }
87
-
88
- console.log('[Keak] ✅ Loader path resolved:', keakLoaderPath, '(source:', loaderSource, ')');
89
- console.log('[Keak] About to inspect webpack rules...');
90
- console.log('[Keak] DEBUG: config exists?', !!config);
91
- console.log('[Keak] DEBUG: config.module exists?', !!config.module);
92
- console.log('[Keak] DEBUG: config.module.rules exists?', !!(config.module && config.module.rules));
93
- console.log('[Keak] DEBUG: config.module.rules is array?', Array.isArray(config.module && config.module.rules));
94
-
95
- // Wrap everything in try-catch to catch any errors
96
- try {
97
- console.log('[Keak] DEBUG: Entering try block');
98
- console.log('[Keak] DEBUG: config.module exists?', !!config.module);
99
- console.log('[Keak] DEBUG: config.module.rules exists?', !!(config.module && config.module.rules));
100
- console.log('[Keak] Total webpack rules:', config.module && config.module.rules ? config.module.rules.length : 'undefined');
101
-
102
- if (!config.module || !config.module.rules) {
103
- console.error('[Keak] ❌ ERROR: config.module.rules is undefined!');
104
- return config;
105
- }
106
-
107
- // Log first few rules to understand structure
108
- try {
109
- console.log('[Keak] First few rules structure:');
110
- config.module.rules.slice(0, 5).forEach((r, i) => {
111
- console.log(`[Keak] Rule ${i}:`, {
112
- hasOneOf: Array.isArray(r.oneOf),
113
- oneOfLength: Array.isArray(r.oneOf) ? r.oneOf.length : 'N/A',
114
- test: r.test ? r.test.toString().substring(0, 50) : 'no test',
115
- hasUse: !!r.use,
116
- useType: r.use ? (Array.isArray(r.use) ? 'array' : typeof r.use) : 'none'
117
- });
118
- });
119
- } catch (e) {
120
- console.log('[Keak] Error logging rules:', e.message);
121
- }
122
-
123
- // Based on Next.js webpack structure: Find the oneOf array and add our loader
124
- // Next.js uses oneOf arrays to process files - we need to add our loader there
125
- let loaderAdded = false;
126
-
127
- console.log('[Keak] Attempting Strategy 1: Finding oneOf rule...');
128
- // Strategy 1: Find the oneOf rule (recommended approach from web search)
129
- const oneOfRule = config.module.rules.find((rule) => Array.isArray(rule.oneOf));
130
- console.log('[Keak] Strategy 1 result:', oneOfRule ? 'Found oneOf rule' : 'No oneOf rule found');
131
-
132
- if (oneOfRule && oneOfRule.oneOf) {
133
- console.log(`[Keak] ✅ Found oneOf array with ${oneOfRule.oneOf.length} entries`);
134
-
135
- // Add our loader to the BEGINNING of oneOf array so it runs BEFORE SWC
136
- // This transforms raw JSX to inject attributes before compilation
137
- const preRule = {
138
- test: /\.[jt]sx?$/,
139
- exclude: [
140
- /node_modules/,
141
- /\.next\//,
142
- /webpack/,
143
- /next\/dist/
144
- // NOTE: Removed /\.server\.[jt]sx?$/ exclusion to process server components
145
- ],
146
- enforce: 'pre', // Run BEFORE SWC (pre-compilation)
147
- use: [
148
- {
149
- loader: keakLoaderPath,
150
- options: {
151
- projectRoot: process.cwd(),
152
- includeElementIndex: true,
153
- devWarnings: dev,
154
- skipPatterns: [
155
- // NOTE: Removed /(\.server\.[jt]sx?$)/ to process server components
156
- /webpack/,
157
- /\.next\//,
158
- /next\/dist/
159
- ]
160
- }
161
- }
162
- ]
163
- };
164
-
165
- // Add to the BEGINNING of oneOf array (runs before SWC processes files)
166
- oneOfRule.oneOf.unshift(preRule);
167
-
168
- console.log(`[Keak] ✅ Added pre-compilation loader to oneOf array (now ${oneOfRule.oneOf.length} entries, runs before SWC)`);
169
- loaderAdded = true;
170
- } else {
171
- console.log('[Keak] No oneOf array found, trying alternative approaches...');
172
-
173
- // Strategy 2: Look for rules with oneOf property (nested)
174
- for (let i = 0; i < config.module.rules.length; i++) {
175
- const rule = config.module.rules[i];
176
- if (rule.oneOf && Array.isArray(rule.oneOf)) {
177
- console.log(`[Keak] Found oneOf in rule ${i} with ${rule.oneOf.length} entries`);
178
-
179
- // Add pre-compilation loader to the BEGINNING of this oneOf array
180
- rule.oneOf.unshift({
181
- test: /\.[jt]sx?$/,
182
- exclude: [/node_modules/, /\.next\//, /webpack/, /next\/dist/],
183
- enforce: 'pre', // Run BEFORE SWC
184
- use: [
185
- {
186
- loader: keakLoaderPath,
187
- options: {
188
- projectRoot: process.cwd(),
189
- includeElementIndex: true,
190
- devWarnings: dev,
191
- skipPatterns: [] // Process all files including server components
192
- }
193
- }
194
- ]
195
- });
196
- console.log(`[Keak] ✅ Added pre-compilation loader to oneOf array (now ${rule.oneOf.length} entries)`);
197
- loaderAdded = true;
198
- break;
199
- }
40
+ // Create the loader rule
41
+ const keakRule = {
42
+ test: /\.[jt]sx?$/,
43
+ exclude: [
44
+ /node_modules/,
45
+ /\.next\//,
46
+ /webpack/,
47
+ /next\/dist/
48
+ ],
49
+ enforce: 'pre', // Run BEFORE SWC compilation
50
+ use: [
51
+ {
52
+ loader: keakLoaderPath,
53
+ options: {
54
+ projectRoot: process.cwd()
200
55
  }
201
56
  }
202
-
203
- // Strategy 3: Fallback - Add as new rule with enforce: 'pre' at top level
204
- // This is less ideal but should still work
205
- if (!loaderAdded) {
206
- console.log('[Keak] Fallback: Adding pre-compilation loader rule to top-level rules');
207
- config.module.rules.unshift({
208
- test: /\.[jt]sx?$/,
209
- exclude: [/node_modules/, /\.next\//, /webpack/, /next\/dist/],
210
- enforce: 'pre', // Run BEFORE SWC
211
- use: [
212
- {
213
- loader: keakLoaderPath,
214
- options: {
215
- projectRoot: process.cwd(),
216
- includeElementIndex: true,
217
- devWarnings: dev,
218
- skipPatterns: [] // Process all files including server components
219
- }
220
- }
221
- ]
222
- });
223
- console.log('[Keak] ✅ Added pre-compilation loader rule with enforce:pre (fallback)');
224
- loaderAdded = true;
225
- }
226
-
227
- if (!loaderAdded) {
228
- console.error('[Keak] ❌ ERROR: Failed to add loader using any strategy!');
229
- } else {
230
- console.log('[Keak] ✅ Loader successfully added to webpack config');
231
- }
232
- } catch (error) {
233
- console.error('[Keak] ❌ ERROR in loader injection block:', error);
234
- console.error('[Keak] Error message:', error.message);
235
- console.error('[Keak] Error stack:', error.stack);
236
- // Don't throw - let webpack continue even if loader injection fails
237
- }
57
+ ]
58
+ };
59
+
60
+ // Try to inject into oneOf array (Next.js standard structure)
61
+ const oneOfRule = config.module.rules.find((rule) => Array.isArray(rule.oneOf));
62
+ if (oneOfRule && oneOfRule.oneOf) {
63
+ // Add to beginning so it runs before SWC
64
+ oneOfRule.oneOf.unshift(keakRule);
238
65
  } else {
239
- console.log('[Keak] Skipping loader injection - dev:', dev, 'isServer:', isServer);
66
+ // Fallback: Add as top-level rule with enforce: 'pre'
67
+ config.module.rules.unshift(keakRule);
240
68
  }
241
69
 
242
- // Call the original webpack config function if it exists
70
+ // Call original webpack config if it exists
243
71
  if (typeof nextConfig.webpack === 'function') {
244
72
  return nextConfig.webpack(config, options);
245
73
  }
@@ -250,4 +78,3 @@ function withKeak(nextConfig = {}) {
250
78
  }
251
79
 
252
80
  module.exports = { withKeak };
253
-