postinstall-fix.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env node
  2. /**
  3. * Postinstall fix: patch remove-scoped.js to guard unsafe `.spaces` access.
  4. *
  5. * Root cause: remove-scoped.js in @dcloudio/vue-cli-plugin-uni accesses
  6. * `selector.first.spaces.before` without checking if `selector.first` exists.
  7. * When postcss-selector-parser produces an empty selector (e.g. from certain
  8. * CSS edge cases), this crashes with:
  9. * "TypeError: Cannot read properties of undefined (reading 'spaces')"
  10. *
  11. * The fix adds a guard before accessing .spaces on selector.first.
  12. *
  13. * Patches BOTH:
  14. * 1. Project local node_modules (for vue-cli-service / npm build)
  15. * 2. HBuilderX bundled uniapp-cli (for HBuilderX build)
  16. */
  17. const fs = require('fs');
  18. const path = require('path');
  19. /** Find all remove-scoped.js files to patch */
  20. function findRemoveScopedFiles() {
  21. const files = [];
  22. // 1. Project local node_modules
  23. const localPath = path.join(__dirname, 'node_modules', '@dcloudio',
  24. 'vue-cli-plugin-uni', 'packages', '@vue', 'component-compiler-utils',
  25. 'dist', 'stylePlugins', 'remove-scoped.js');
  26. if (fs.existsSync(localPath)) files.push(localPath);
  27. // 2. HBuilderX bundled uniapp-cli (common install paths)
  28. const hbxPaths = [
  29. 'E:\\develop\\HBuilderX\\plugins\\uniapp-cli',
  30. 'C:\\Program Files\\HBuilderX\\plugins\\uniapp-cli',
  31. 'C:\\Program Files (x86)\\HBuilderX\\plugins\\uniapp-cli',
  32. path.join(process.env.LOCALAPPDATA || '', 'Programs', 'HBuilderX', 'plugins', 'uniapp-cli'),
  33. ];
  34. for (const base of hbxPaths) {
  35. const hbxPath = path.join(base, 'node_modules', '@dcloudio',
  36. 'vue-cli-plugin-uni', 'packages', '@vue', 'component-compiler-utils',
  37. 'dist', 'stylePlugins', 'remove-scoped.js');
  38. if (fs.existsSync(hbxPath)) files.push(hbxPath);
  39. }
  40. return files;
  41. }
  42. /** Patch a single remove-scoped.js file */
  43. function patchFile(filePath) {
  44. let content = fs.readFileSync(filePath, 'utf8');
  45. // Fix: selector.first.spaces.before = ''; → if (selector.first) { ... }
  46. if (content.includes("selector.first.spaces.before = '';")) {
  47. content = content.replace(
  48. /selector\.first\.spaces\.before = '';/,
  49. "if (selector.first) { selector.first.spaces.before = ''; }"
  50. );
  51. fs.writeFileSync(filePath, content, 'utf8');
  52. console.log(` ✅ ${filePath}`);
  53. return true;
  54. } else if (content.includes("if (selector.first) { selector.first.spaces.before = ''; }")) {
  55. console.log(` ⏭️ ${filePath} (already patched)`);
  56. return true;
  57. } else {
  58. console.log(` ❌ ${filePath} (unexpected format)`);
  59. return false;
  60. }
  61. }
  62. // Main
  63. const files = findRemoveScopedFiles();
  64. if (files.length === 0) {
  65. console.log('remove-scoped.js not found in any location, skipping patch');
  66. process.exit(0);
  67. }
  68. console.log(`Found ${files.length} remove-scoped.js to patch:`);
  69. let patched = 0;
  70. for (const f of files) {
  71. if (patchFile(f)) patched++;
  72. }
  73. console.log(`Done: ${patched}/${files.length} patched`);