hale-commenting-system 3.8.0 → 3.8.2

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.
Files changed (2) hide show
  1. package/package.json +5 -1
  2. package/scripts/integrate.js +208 -81
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "hale-commenting-system",
3
- "version": "3.8.0",
3
+ "version": "3.8.2",
4
4
  "description": "A commenting system for PatternFly React applications that allows designers and developers to add comments directly on design pages, sync with GitHub Issues, and link Jira tickets.",
5
5
  "homepage": "https://www.npmjs.com/package/hale-commenting-system",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://gitlab.cee.redhat.com/juhale/hale-commenting-system.git"
9
+ },
6
10
  "license": "MIT",
7
11
  "main": "src/app/commenting-system/index.ts",
8
12
  "types": "src/app/commenting-system/index.ts",
@@ -170,6 +170,36 @@ function findFile(filename, startDir = process.cwd()) {
170
170
  // Detection Functions
171
171
  // ============================================================================
172
172
 
173
+ function getDevServerPort() {
174
+ // Check environment variable first
175
+ if (process.env.PORT) {
176
+ return process.env.PORT;
177
+ }
178
+
179
+ // Try to read from webpack.dev.js
180
+ try {
181
+ const webpackDevPath = path.join(process.cwd(), 'webpack.dev.js');
182
+ if (fs.existsSync(webpackDevPath)) {
183
+ const content = fs.readFileSync(webpackDevPath, 'utf8');
184
+ // Check for PORT env var pattern: process.env.PORT || '9000'
185
+ const envPortMatch = content.match(/process\.env\.PORT\s*\|\|\s*['"]?(\d+)['"]?/);
186
+ if (envPortMatch) {
187
+ return envPortMatch[1];
188
+ }
189
+ // Check for direct port assignment: port: PORT or port: '9000'
190
+ const portMatch = content.match(/port:\s*(?:PORT|['"]?(\d+)['"]?)/);
191
+ if (portMatch && portMatch[1]) {
192
+ return portMatch[1];
193
+ }
194
+ }
195
+ } catch {
196
+ // Ignore errors
197
+ }
198
+
199
+ // Default fallback
200
+ return '9000';
201
+ }
202
+
173
203
  function detectPatternFlySeed() {
174
204
  const cwd = process.cwd();
175
205
 
@@ -188,7 +218,11 @@ function detectPatternFlySeed() {
188
218
  const packageJsonPath = path.join(cwd, 'package.json');
189
219
  if (fs.existsSync(packageJsonPath)) {
190
220
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
191
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
221
+ const deps = {
222
+ ...packageJson.dependencies,
223
+ ...packageJson.devDependencies,
224
+ ...packageJson.peerDependencies
225
+ };
192
226
  hasPatternFly = !!(
193
227
  deps['@patternfly/react-core'] ||
194
228
  deps['@patternfly/react-icons']
@@ -927,6 +961,82 @@ devServer.app.use(express.json());
927
961
  }
928
962
  }
929
963
 
964
+ function fixWebpackCssLoader() {
965
+ const cwd = process.cwd();
966
+ const webpackDevPath = path.join(cwd, 'webpack.dev.js');
967
+ const webpackCommonPath = path.join(cwd, 'webpack.common.js');
968
+
969
+ // Try to fix CSS loader in webpack.dev.js or webpack.common.js
970
+ const filesToCheck = [webpackDevPath, webpackCommonPath].filter(f => fs.existsSync(f));
971
+
972
+ for (const filePath of filesToCheck) {
973
+ let content = fs.readFileSync(filePath, 'utf8');
974
+
975
+ // Check if CSS loader already has exclude for nested node_modules
976
+ if (content.includes('exclude') && content.includes('node_modules') && content.includes('node_modules')) {
977
+ // Already has exclude pattern, skip
978
+ continue;
979
+ }
980
+
981
+ // Look for CSS loader rule patterns
982
+ const cssLoaderPatterns = [
983
+ // Pattern 1: test: /\.css$/
984
+ /(test:\s*\/\\\.css\\\$\/[,\s]*)/,
985
+ // Pattern 2: test: /\.css$/i
986
+ /(test:\s*\/\\\.css\\\$\/i[,\s]*)/,
987
+ // Pattern 3: test: /\.css$/, with include
988
+ /(test:\s*\/\\\.css\\\$\/[,\s]*\n\s*include:)/,
989
+ ];
990
+
991
+ let modified = false;
992
+ for (const pattern of cssLoaderPatterns) {
993
+ const match = content.match(pattern);
994
+ if (match) {
995
+ // Add exclude after the test pattern
996
+ const excludePattern = match[1] + '\n exclude: /node_modules\\/.*\\/node_modules\\//,';
997
+ content = content.replace(pattern, excludePattern);
998
+ modified = true;
999
+ break;
1000
+ }
1001
+ }
1002
+
1003
+ // Also try to find CSS rules in module.rules array
1004
+ if (!modified) {
1005
+ // Look for CSS rules that use stylePaths or include patterns
1006
+ // Pattern: test: /\.css$/, followed by include: [...stylePaths]
1007
+ // Match: test: /\.css$/, (with optional comma and whitespace)
1008
+ // newline and indentation
1009
+ // include: [...stylePaths]
1010
+ const cssRuleWithIncludePattern = /(test:\s*\/\\?\.css\$\/,?\s*\n\s*)(include:\s*\[.*?stylePaths)/s;
1011
+ const match = content.match(cssRuleWithIncludePattern);
1012
+ if (match) {
1013
+ // Add exclude between test and include
1014
+ const excludeLine = ' exclude: /node_modules\\/.*\\/node_modules\\//,\n';
1015
+ content = content.replace(cssRuleWithIncludePattern, match[1] + excludeLine + match[2]);
1016
+ modified = true;
1017
+ } else {
1018
+ // Try a more flexible pattern - look for any CSS rule with include
1019
+ // Match test: /\.css$/ (with or without backslash escape) followed by include:
1020
+ const flexiblePattern = /(test:\s*\/\\?\.css\$\/,?\s*\n\s*)(include:)/;
1021
+ const flexibleMatch = content.match(flexiblePattern);
1022
+ if (flexibleMatch) {
1023
+ const excludeLine = ' exclude: /node_modules\\/.*\\/node_modules\\//,\n';
1024
+ content = content.replace(flexiblePattern, flexibleMatch[1] + excludeLine + flexibleMatch[2]);
1025
+ modified = true;
1026
+ }
1027
+ }
1028
+ }
1029
+
1030
+ if (modified) {
1031
+ fs.writeFileSync(filePath, content, 'utf8');
1032
+ console.log(` ✅ Updated CSS loader in ${path.basename(filePath)} to exclude nested node_modules`);
1033
+ return true;
1034
+ }
1035
+ }
1036
+
1037
+ return false;
1038
+ }
1039
+
930
1040
  function getPackageVersion() {
931
1041
  try {
932
1042
  // Get the script's directory and find package.json relative to it
@@ -954,8 +1064,8 @@ function getPackageVersion() {
954
1064
  function modifyIndexTsx(filePath) {
955
1065
  const content = fs.readFileSync(filePath, 'utf8');
956
1066
 
957
- // Check if already integrated
958
- if (content.includes('CommentProvider') && content.includes('GitHubAuthProvider')) {
1067
+ // Check if already integrated (look for ProviderAuthProvider or GitHubAuthProvider for backward compat)
1068
+ if (content.includes('CommentProvider') && (content.includes('ProviderAuthProvider') || content.includes('GitHubAuthProvider'))) {
959
1069
  console.log(' ⚠️ Already integrated (providers found)');
960
1070
  return false;
961
1071
  }
@@ -967,8 +1077,9 @@ function modifyIndexTsx(filePath) {
967
1077
  });
968
1078
 
969
1079
  let hasCommentProvider = false;
970
- let hasGitHubAuthProvider = false;
1080
+ let hasProviderAuthProvider = false;
971
1081
  let hasCommentImport = false;
1082
+ let importSource = 'hale-commenting-system'; // Default import path
972
1083
  let routerElement = null;
973
1084
 
974
1085
  // Check existing imports and find Router element
@@ -977,13 +1088,21 @@ function modifyIndexTsx(filePath) {
977
1088
  const source = path.node.source.value;
978
1089
  if (source.includes('commenting-system') || source.includes('@app/commenting-system') || source.includes('hale-commenting-system')) {
979
1090
  hasCommentImport = true;
1091
+ // Detect the import path being used
1092
+ // Always prefer 'hale-commenting-system' over '@app/commenting-system'
1093
+ if (source.includes('hale-commenting-system')) {
1094
+ importSource = 'hale-commenting-system';
1095
+ } else if (source.includes('@app/commenting-system')) {
1096
+ // If old @app/ path is found, we'll update it to hale-commenting-system
1097
+ importSource = 'hale-commenting-system';
1098
+ }
980
1099
  // Check if providers are imported
981
1100
  path.node.specifiers.forEach(spec => {
982
1101
  if (spec.imported && spec.imported.name === 'CommentProvider') {
983
1102
  hasCommentProvider = true;
984
1103
  }
985
- if (spec.imported && spec.imported.name === 'GitHubAuthProvider') {
986
- hasGitHubAuthProvider = true;
1104
+ if (spec.imported && (spec.imported.name === 'ProviderAuthProvider' || spec.imported.name === 'GitHubAuthProvider')) {
1105
+ hasProviderAuthProvider = true;
987
1106
  }
988
1107
  });
989
1108
  }
@@ -995,6 +1114,11 @@ function modifyIndexTsx(filePath) {
995
1114
  }
996
1115
  });
997
1116
 
1117
+ // Always use 'hale-commenting-system' as the package name
1118
+ // Don't use @app/ alias even if it exists in the file - the package
1119
+ // should be imported by its npm package name, not a path alias
1120
+ importSource = 'hale-commenting-system';
1121
+
998
1122
  // Add imports if missing
999
1123
  if (!hasCommentImport) {
1000
1124
  // Find last import declaration
@@ -1010,24 +1134,28 @@ function modifyIndexTsx(filePath) {
1010
1134
  const providerImports = types.importDeclaration(
1011
1135
  [
1012
1136
  types.importSpecifier(types.identifier('CommentProvider'), types.identifier('CommentProvider')),
1013
- types.importSpecifier(types.identifier('GitHubAuthProvider'), types.identifier('GitHubAuthProvider'))
1137
+ types.importSpecifier(types.identifier('ProviderAuthProvider'), types.identifier('ProviderAuthProvider'))
1014
1138
  ],
1015
- types.stringLiteral('hale-commenting-system')
1139
+ types.stringLiteral(importSource)
1016
1140
  );
1017
1141
 
1018
1142
  ast.program.body.splice(importIndex, 0, providerImports);
1019
- } else if (!hasCommentProvider || !hasGitHubAuthProvider) {
1020
- // Update existing import
1143
+ } else if (!hasCommentProvider || !hasProviderAuthProvider) {
1144
+ // Update existing import - also fix @app/commenting-system to hale-commenting-system
1021
1145
  traverse(ast, {
1022
1146
  ImportDeclaration(path) {
1023
1147
  const source = path.node.source.value;
1024
1148
  if (source.includes('commenting-system') || source.includes('@app/commenting-system') || source.includes('hale-commenting-system')) {
1149
+ // Update import source to use hale-commenting-system if it's using @app/ path
1150
+ if (source.includes('@app/commenting-system')) {
1151
+ path.node.source.value = 'hale-commenting-system';
1152
+ }
1025
1153
  const specifiers = path.node.specifiers || [];
1026
1154
  if (!hasCommentProvider) {
1027
1155
  specifiers.push(types.importSpecifier(types.identifier('CommentProvider'), types.identifier('CommentProvider')));
1028
1156
  }
1029
- if (!hasGitHubAuthProvider) {
1030
- specifiers.push(types.importSpecifier(types.identifier('GitHubAuthProvider'), types.identifier('GitHubAuthProvider')));
1157
+ if (!hasProviderAuthProvider) {
1158
+ specifiers.push(types.importSpecifier(types.identifier('ProviderAuthProvider'), types.identifier('ProviderAuthProvider')));
1031
1159
  }
1032
1160
  path.node.specifiers = specifiers;
1033
1161
  }
@@ -1039,28 +1167,30 @@ function modifyIndexTsx(filePath) {
1039
1167
  if (routerElement) {
1040
1168
  const routerChildren = routerElement.node.children;
1041
1169
 
1042
- // Check if already wrapped
1170
+ // Check if already wrapped (check for ProviderAuthProvider or GitHubAuthProvider)
1043
1171
  if (routerChildren.length > 0 &&
1044
- routerChildren[0].type === 'JSXElement' &&
1045
- routerChildren[0].openingElement.name.name === 'GitHubAuthProvider') {
1172
+ routerChildren[0].type === 'JSXElement') {
1173
+ const firstChildName = routerChildren[0].openingElement.name.name;
1174
+ if (firstChildName === 'ProviderAuthProvider' || firstChildName === 'GitHubAuthProvider') {
1046
1175
  console.log(' ⚠️ Already integrated (providers found in JSX)');
1047
1176
  return false;
1177
+ }
1048
1178
  }
1049
1179
 
1050
- // Create provider wrappers
1180
+ // Create provider wrappers in correct order: ProviderAuthProvider -> CommentProvider -> content
1051
1181
  const commentProvider = types.jsxElement(
1052
1182
  types.jsxOpeningElement(types.jsxIdentifier('CommentProvider'), []),
1053
1183
  types.jsxClosingElement(types.jsxIdentifier('CommentProvider')),
1054
1184
  routerChildren
1055
1185
  );
1056
1186
 
1057
- const gitHubAuthProvider = types.jsxElement(
1058
- types.jsxOpeningElement(types.jsxIdentifier('GitHubAuthProvider'), []),
1059
- types.jsxClosingElement(types.jsxIdentifier('GitHubAuthProvider')),
1187
+ const providerAuthProvider = types.jsxElement(
1188
+ types.jsxOpeningElement(types.jsxIdentifier('ProviderAuthProvider'), []),
1189
+ types.jsxClosingElement(types.jsxIdentifier('ProviderAuthProvider')),
1060
1190
  [commentProvider]
1061
1191
  );
1062
1192
 
1063
- routerElement.node.children = [gitHubAuthProvider];
1193
+ routerElement.node.children = [providerAuthProvider];
1064
1194
  }
1065
1195
 
1066
1196
  const output = generate(ast, {
@@ -1195,14 +1325,14 @@ async function main() {
1195
1325
  console.log('🚀 Welcome to Hale Commenting System!\n');
1196
1326
  console.log('This commenting system allows you to:');
1197
1327
  console.log(' • Add comments directly on your design pages');
1198
- console.log(' • Sync comments with GitHub Issues');
1328
+ console.log(' • Sync comments with GitLab Issues (or GitHub)');
1199
1329
  console.log(' • Link Jira tickets to pages');
1200
1330
  console.log(' • Store design goals and context\n');
1201
1331
 
1202
- console.log('Why GitHub?');
1203
- console.log(' We use GitHub Issues to store and sync all comments. When you add a comment');
1204
- console.log(' on a page, it creates a GitHub Issue. This allows comments to persist, sync');
1205
- console.log(' across devices, and be managed like any other GitHub Issue.\n');
1332
+ console.log('Why GitLab?');
1333
+ console.log(' We use GitLab Issues to store and sync all comments. When you add a comment');
1334
+ console.log(' on a page, it creates a GitLab Issue. This allows comments to persist, sync');
1335
+ console.log(' across devices, and be managed like any other GitLab Issue.\n');
1206
1336
 
1207
1337
  console.log('Why Jira?');
1208
1338
  console.log(' You can link Jira tickets to specific pages or sections. This helps connect');
@@ -1263,7 +1393,7 @@ async function main() {
1263
1393
  name: 'setupType',
1264
1394
  message: 'How did you set up your PatternFly Seed project?',
1265
1395
  choices: [
1266
- { name: 'I forked the PatternFly Seed repo on GitHub', value: 'forked' },
1396
+ { name: 'I forked the PatternFly Seed repo', value: 'forked' },
1267
1397
  { name: 'I cloned the PatternFly Seed repo locally', value: 'cloned' },
1268
1398
  { name: 'I\'m not sure', value: 'unknown' }
1269
1399
  ]
@@ -1306,53 +1436,12 @@ async function main() {
1306
1436
  console.log(`\n✅ Detected repository: ${owner}/${repo}\n`);
1307
1437
  }
1308
1438
  } else if (projectSetup === 'cloned') {
1309
- console.log('\n📝 Since you cloned the repo, you can create your own GitHub repository to store comments.\n');
1310
- console.log('Note: This is optional! You can test the system locally first and add GitHub integration later.\n');
1311
- console.log('Steps to create a GitHub repository (optional):');
1312
- console.log('1. Create a new repository on GitHub');
1313
- console.log('2. Add it as a remote: git remote add origin <your-repo-url>');
1314
- console.log('3. Push your code: git push -u origin main\n');
1315
-
1316
- const hasCreated = await prompt([
1317
- {
1318
- type: 'confirm',
1319
- name: 'created',
1320
- message: 'Have you created and pushed to your GitHub repository?',
1321
- default: false
1322
- }
1323
- ]);
1324
-
1325
- if (!hasCreated.created) {
1326
- console.log('\n⏭️ No problem! You can set up the GitHub repository later.');
1327
- console.log(' The system will still work locally for testing.\n');
1439
+ console.log('\n📝 Since you cloned the repo, you can create your own repository to store comments.\n');
1440
+ console.log('Note: This is optional! You can test the system locally first and add integration later.\n');
1441
+ // Don't ask about repository here - wait until issue tracking selection
1328
1442
  // Set placeholder values that can be updated later
1329
- owner = 'YOUR_GITHUB_USERNAME';
1443
+ owner = 'YOUR_USERNAME';
1330
1444
  repo = 'YOUR_REPO_NAME';
1331
- } else {
1332
- // Ask for owner/repo
1333
- const repoAnswers = await prompt([
1334
- {
1335
- type: 'input',
1336
- name: 'owner',
1337
- message: 'What is your GitHub username or organization name?',
1338
- validate: (input) => {
1339
- if (!input.trim()) return 'Owner is required';
1340
- return true;
1341
- }
1342
- },
1343
- {
1344
- type: 'input',
1345
- name: 'repo',
1346
- message: 'What is the name of your GitHub repository?',
1347
- validate: (input) => {
1348
- if (!input.trim()) return 'Repository name is required';
1349
- return true;
1350
- }
1351
- }
1352
- ]);
1353
- owner = repoAnswers.owner;
1354
- repo = repoAnswers.repo;
1355
- }
1356
1445
  } else if (projectSetup === 'unknown') {
1357
1446
  // Try to detect from git
1358
1447
  if (gitInfo && gitInfo.owner && gitInfo.repo) {
@@ -1394,7 +1483,7 @@ async function main() {
1394
1483
  console.log(' • GitHub - Sync with GitHub Issues');
1395
1484
  console.log(' • GitLab - Sync with GitLab Issues (supports self-hosted)');
1396
1485
  console.log(' • Skip - Set up later (you can still use local comments)\n');
1397
-
1486
+
1398
1487
  const platformChoice = await prompt([
1399
1488
  {
1400
1489
  type: 'list',
@@ -1422,8 +1511,9 @@ async function main() {
1422
1511
  console.log('2. Click "New OAuth App"');
1423
1512
  console.log('3. Fill in the form:');
1424
1513
  console.log(' - Application name: Your app name (e.g., "My Design Comments")');
1425
- console.log(' - Homepage URL: http://localhost:9000 (or your dev server URL)');
1426
- console.log(' - Authorization callback URL: http://localhost:9000/api/github-oauth-callback');
1514
+ const devPort = getDevServerPort();
1515
+ console.log(` - Homepage URL: http://localhost:${devPort} (or your dev server URL)`);
1516
+ console.log(` - Authorization callback URL: http://localhost:${devPort}/api/github-oauth-callback`);
1427
1517
  console.log('4. Click "Register application"');
1428
1518
  console.log('5. Copy the Client ID and generate a Client Secret\n');
1429
1519
 
@@ -1668,15 +1758,15 @@ async function main() {
1668
1758
  // Prompt for GitLab instance URL
1669
1759
  console.log('💡 This supports both gitlab.com and self-hosted GitLab instances.');
1670
1760
  console.log(' Examples:');
1671
- console.log(' • https://gitlab.com (public GitLab)');
1672
- console.log(' • https://gitlab.cee.redhat.com (Red Hat internal)\n');
1761
+ console.log(' • https://gitlab.cee.redhat.com (Red Hat internal)');
1762
+ console.log(' • https://gitlab.com (public GitLab)\n');
1673
1763
 
1674
1764
  const gitlabInstanceAnswer = await prompt([
1675
1765
  {
1676
1766
  type: 'input',
1677
1767
  name: 'baseUrl',
1678
1768
  message: 'GitLab instance URL:',
1679
- default: 'https://gitlab.com',
1769
+ default: 'https://gitlab.cee.redhat.com',
1680
1770
  validate: (input) => {
1681
1771
  if (!input.trim()) return 'Base URL is required';
1682
1772
  try {
@@ -1697,7 +1787,8 @@ async function main() {
1697
1787
  console.log('2. Click "Add new application"');
1698
1788
  console.log('3. Fill in the form:');
1699
1789
  console.log(' - Name: Your app name (e.g., "My Design Comments")');
1700
- console.log(' - Redirect URI: http://localhost:9000/api/gitlab-oauth-callback');
1790
+ const devPort = getDevServerPort();
1791
+ console.log(` - Redirect URI: http://localhost:${devPort}/api/gitlab-oauth-callback`);
1701
1792
  console.log(' - Confidential: ✓ (checked)');
1702
1793
  console.log(' - Scopes: ✓ api (full API access)');
1703
1794
  console.log('4. Click "Save application"');
@@ -1728,17 +1819,46 @@ async function main() {
1728
1819
  // Prompt for project path
1729
1820
  console.log('\nWhere do you want to store comments as GitLab Issues?');
1730
1821
  console.log('This should be a project you have maintainer/owner access to.');
1731
- console.log('Format: group/project or namespace/group/project\n');
1822
+ console.log('');
1823
+ console.log('You can paste the full URL or just the path:');
1824
+ console.log(` • Full URL: ${baseUrl}/uxd/prototypes/rhoai`);
1825
+ console.log(' • Path only: uxd/prototypes/rhoai\n');
1732
1826
 
1733
1827
  const projectPathAnswer = await prompt([
1734
1828
  {
1735
1829
  type: 'input',
1736
1830
  name: 'projectPath',
1737
- message: 'GitLab project path (e.g., mygroup/myproject):',
1831
+ message: 'GitLab project path or full URL:',
1738
1832
  validate: (input) => {
1739
1833
  if (!input.trim()) return 'Project path is required';
1740
- if (!input.includes('/')) return 'Project path must include at least one slash (e.g., group/project)';
1741
1834
  return true;
1835
+ },
1836
+ filter: (input) => {
1837
+ // If user pasted full URL, extract the path
1838
+ const trimmed = input.trim();
1839
+
1840
+ // Check if it's a full URL
1841
+ if (trimmed.includes('http://') || trimmed.includes('https://')) {
1842
+ try {
1843
+ const url = new URL(trimmed);
1844
+ // Extract path after domain, remove leading/trailing slashes
1845
+ let path = url.pathname.replace(/^\/+|\/+$/g, '');
1846
+ // Remove common GitLab UI paths like /-/tree/main, /-/settings, etc.
1847
+ path = path.replace(/\/-\/.*$/, '');
1848
+ // Remove .git suffix if present
1849
+ path = path.replace(/\.git$/, '');
1850
+ return path;
1851
+ } catch {
1852
+ // If URL parsing fails, try manual extraction
1853
+ const match = trimmed.match(/gitlab[^/]+\/(.+?)(?:\.git|\/-\/|$)/);
1854
+ if (match) {
1855
+ return match[1].replace(/\/-\/.*$/, '').replace(/\.git$/, '');
1856
+ }
1857
+ }
1858
+ }
1859
+
1860
+ // If it's already a path, clean it up
1861
+ return trimmed.replace(/^\/+|\/+$/, '').replace(/\/-\/.*$/, '').replace(/\.git$/, '');
1742
1862
  }
1743
1863
  }
1744
1864
  ]);
@@ -1897,6 +2017,13 @@ async function main() {
1897
2017
  // Integrate webpack middleware
1898
2018
  console.log('\n📝 webpack.dev.js');
1899
2019
  integrateWebpackMiddleware();
2020
+
2021
+ // Fix CSS loader to exclude nested node_modules
2022
+ console.log('\n📝 Fixing CSS loader configuration...');
2023
+ if (!fixWebpackCssLoader()) {
2024
+ console.log(' ⚠️ Could not auto-fix CSS loader. You may need to manually exclude nested node_modules.');
2025
+ console.log(' Add this to your CSS loader rule: exclude: /node_modules\\/.*\\/node_modules\\//');
2026
+ }
1900
2027
 
1901
2028
  console.log('\n╔══════════════════════════════════════════════════════════╗');
1902
2029
  console.log('║ ✅ Integration Complete! ║');