| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- #!/usr/bin/env node
- /**
- * Postinstall fix: patch remove-scoped.js to guard unsafe `.spaces` access.
- *
- * Root cause: remove-scoped.js in @dcloudio/vue-cli-plugin-uni accesses
- * `selector.first.spaces.before` without checking if `selector.first` exists.
- * When postcss-selector-parser produces an empty selector (e.g. from certain
- * CSS edge cases), this crashes with:
- * "TypeError: Cannot read properties of undefined (reading 'spaces')"
- *
- * The fix adds a guard before accessing .spaces on selector.first.
- *
- * Patches BOTH:
- * 1. Project local node_modules (for vue-cli-service / npm build)
- * 2. HBuilderX bundled uniapp-cli (for HBuilderX build)
- */
- const fs = require('fs');
- const path = require('path');
- /**
- * Patch 2: templateLoader.js — remove undefined `recyclableRender` and `components` exports.
- *
- * Root cause: templateLoader.js hardcodes `export { render, staticRenderFns, recyclableRender, components }`
- * but the template compiler (vue-template-compiler / @vue/component-compiler-utils) does NOT define
- * `recyclableRender` or `components` in the output. This causes:
- * "Module parse failed: Export 'recyclableRender' is not defined"
- * "export 'components' was not found in './xxx.vue'"
- *
- * The fix removes the two undefined symbols from the export line.
- */
- function patchTemplateLoader(filePath) {
- let content = fs.readFileSync(filePath, 'utf8');
- // Check if already patched
- if (content.includes("export { render, staticRenderFns }") &&
- !content.includes("recyclableRender")) {
- console.log(` ⏭️ ${filePath} (templateLoader already patched)`);
- return true;
- }
- const oldExport = /export \{ render, staticRenderFns, recyclableRender, components \}/;
- if (oldExport.test(content)) {
- content = content.replace(
- oldExport,
- "export { render, staticRenderFns }"
- );
- fs.writeFileSync(filePath, content, 'utf8');
- console.log(` ✅ ${filePath}`);
- return true;
- }
- console.log(` ❌ ${filePath} (templateLoader unexpected format)`);
- return false;
- }
- /** Find all remove-scoped.js files to patch */
- function findRemoveScopedFiles() {
- const files = [];
- // 1. Project local node_modules
- const localPath = path.join(__dirname, 'node_modules', '@dcloudio',
- 'vue-cli-plugin-uni', 'packages', '@vue', 'component-compiler-utils',
- 'dist', 'stylePlugins', 'remove-scoped.js');
- if (fs.existsSync(localPath)) files.push(localPath);
- // 2. HBuilderX bundled uniapp-cli (common install paths)
- const hbxPaths = [
- 'E:\\develop\\HBuilderX\\plugins\\uniapp-cli',
- 'C:\\Program Files\\HBuilderX\\plugins\\uniapp-cli',
- 'C:\\Program Files (x86)\\HBuilderX\\plugins\\uniapp-cli',
- path.join(process.env.LOCALAPPDATA || '', 'Programs', 'HBuilderX', 'plugins', 'uniapp-cli'),
- ];
- for (const base of hbxPaths) {
- const hbxPath = path.join(base, 'node_modules', '@dcloudio',
- 'vue-cli-plugin-uni', 'packages', '@vue', 'component-compiler-utils',
- 'dist', 'stylePlugins', 'remove-scoped.js');
- if (fs.existsSync(hbxPath)) files.push(hbxPath);
- }
- return files;
- }
- /** Patch a single remove-scoped.js file */
- function patchFile(filePath) {
- let content = fs.readFileSync(filePath, 'utf8');
- // Fix: selector.first.spaces.before = ''; → if (selector.first) { ... }
- if (content.includes("selector.first.spaces.before = '';")) {
- content = content.replace(
- /selector\.first\.spaces\.before = '';/,
- "if (selector.first) { selector.first.spaces.before = ''; }"
- );
- fs.writeFileSync(filePath, content, 'utf8');
- console.log(` ✅ ${filePath}`);
- return true;
- } else if (content.includes("if (selector.first) { selector.first.spaces.before = ''; }")) {
- console.log(` ⏭️ ${filePath} (already patched)`);
- return true;
- } else {
- console.log(` ❌ ${filePath} (unexpected format)`);
- return false;
- }
- }
- // Main
- const files = findRemoveScopedFiles();
- if (files.length === 0) {
- console.log('remove-scoped.js not found in any location, skipping patch');
- } else {
- console.log(`Found ${files.length} remove-scoped.js to patch:`);
- let patched = 0;
- for (const f of files) {
- if (patchFile(f)) patched++;
- }
- console.log(`Done: ${patched}/${files.length} patched`);
- }
- // Patch 2: templateLoader.js
- const templateLoaderPath = path.join(__dirname, 'node_modules', '@dcloudio',
- 'vue-cli-plugin-uni', 'packages', 'vue-loader', 'lib', 'loaders', 'templateLoader.js');
- if (fs.existsSync(templateLoaderPath)) {
- console.log('templateLoader.js found, applying patch:');
- patchTemplateLoader(templateLoaderPath);
- } else {
- console.log('templateLoader.js not found, skipping patch');
- }
|