d34-fix-stray-imports.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #!/usr/bin/env node
  2. /**
  3. * D34: 修复「import 落在 </script> 之后」的历史残留
  4. * C 批脚本把品牌色 import 追加到了 </script> 与 <style> 之间 -> 模块作用域外,运行期标识符未定义。
  5. * 处理:把这段的 import 行搬回 <script> 内;已存在同路径 import 则合并去重。
  6. */
  7. const fs = require('fs');
  8. const path = require('path');
  9. const DRY = process.argv.includes('--dry');
  10. const ROOT = path.resolve(__dirname, '..');
  11. const SKIP = new Set(['components/mp-html/node/node.vue']); // mp-html 自带多 script 结构
  12. function walk(dir, out = []) {
  13. for (const name of fs.readdirSync(dir)) {
  14. if (['node_modules', 'dist', 'unpackage', '.git', 'scripts', '.tmp_equiv'].includes(name)) continue;
  15. const p = path.join(dir, name);
  16. const st = fs.statSync(p);
  17. if (st.isDirectory()) walk(p, out);
  18. else if (name.endsWith('.vue')) out.push(p);
  19. }
  20. return out;
  21. }
  22. function parseNames(imp) {
  23. const m = imp.match(/import\s*\{([^}]*)\}\s*from\s*(['"][^'"]+['"])/);
  24. if (!m) return null;
  25. return {
  26. names: m[1].split(',').map(s => s.trim()).filter(Boolean),
  27. from: m[2],
  28. };
  29. }
  30. let fixedFiles = 0;
  31. let movedImports = 0;
  32. for (const abs of walk(ROOT)) {
  33. const rel = path.relative(ROOT, abs).split(path.sep).join('/');
  34. if (SKIP.has(rel)) continue;
  35. let src = fs.readFileSync(abs, 'utf8');
  36. const open = src.match(/<script[^>]*>/);
  37. const closeIdx = src.indexOf('</script>');
  38. const styleIdx = src.search(/<style/);
  39. if (!open || closeIdx === -1) continue;
  40. const endRegion = styleIdx === -1 ? src.length : styleIdx;
  41. if (endRegion <= closeIdx) continue;
  42. const after = src.slice(closeIdx + 9, endRegion);
  43. const importLines = after.match(/^\s*import\s[^\n]*$/gm);
  44. if (!importLines) continue;
  45. // 从尾部区域删除这些 import 行
  46. let newAfter = after;
  47. for (const line of importLines) newAfter = newAfter.replace(line + '\n', '').replace(line + '\r\n', '').replace(line, '');
  48. const eol = src.includes('\r\n') ? '\r\n' : '\n';
  49. // 逐个搬回 script 内
  50. const openIdx = src.indexOf(open[0]);
  51. let scriptBodyStart = openIdx + open[0].length;
  52. let scriptBody = src.slice(scriptBodyStart, closeIdx);
  53. for (const line of importLines) {
  54. const parsed = parseNames(line);
  55. if (!parsed) continue;
  56. const escFrom = parsed.from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  57. const existingRe = new RegExp("import\\s*\\{([^}]*)\\}\\s*from\\s*" + escFrom + ';?');
  58. const ex = scriptBody.match(existingRe);
  59. if (ex) {
  60. const merged = [...new Set([...ex[1].split(',').map(s => s.trim()).filter(Boolean), ...parsed.names])];
  61. scriptBody = scriptBody.replace(ex[0], 'import { ' + merged.join(', ') + ' } from ' + parsed.from);
  62. } else {
  63. const lead = (scriptBody.match(/^\r?\n/) || [''])[0];
  64. scriptBody = lead + 'import { ' + parsed.names.join(', ') + ' } from ' + parsed.from + eol + scriptBody.slice(lead.length);
  65. }
  66. movedImports++;
  67. }
  68. const out = src.slice(0, scriptBodyStart) + scriptBody + src.slice(closeIdx, closeIdx + 9) + newAfter + src.slice(endRegion);
  69. if (!DRY) fs.writeFileSync(abs, out, 'utf8');
  70. fixedFiles++;
  71. console.log((DRY ? '[DRY] ' : '') + rel + ' imports:' + importLines.length);
  72. }
  73. console.log('文件:', fixedFiles, '搬移 import:', movedImports);