xhs-minitool-creator 1.2.0 → 1.2.1

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 +1 -1
  2. package/src/pack.mjs +50 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xhs-minitool-creator",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "小红书小工具脚手架:创建项目 / Vite 构建预设 / 打包 / 静态校验,内置官方打包规范 Skill",
5
5
  "type": "module",
6
6
  "bin": {
package/src/pack.mjs CHANGED
@@ -46,6 +46,47 @@ function pruneEmptyDirs(dir) {
46
46
  }
47
47
  }
48
48
 
49
+ const COMMENT_RE = /<!--[\s\S]*?-->/g;
50
+ // 哨兵用 NUL 包裹:不会出现在 UTF-8 的 index.html 正文里,且不含引号/斜杠,
51
+ // 不会干扰下面的 href|src、crossorigin、manifest、script 四条正则。
52
+ const NUL = String.fromCharCode(0);
53
+ const COMMENT_SENTINEL = (i) => `${NUL}XMC_COMMENT_${i}${NUL}`;
54
+ const COMMENT_SENTINEL_RE = new RegExp(`${NUL}XMC_COMMENT_(\\d+)${NUL}`, 'g');
55
+
56
+ /** 把 HTML 注释摘成占位符,避免后续正则命中的是注释里的字面量 */
57
+ function maskComments(html) {
58
+ const store = [];
59
+ const masked = html.replace(COMMENT_RE, (m) => {
60
+ const i = store.push(m) - 1;
61
+ return COMMENT_SENTINEL(i);
62
+ });
63
+ return { masked, store };
64
+ }
65
+
66
+ function unmaskComments(html, store) {
67
+ return html.replace(COMMENT_SENTINEL_RE, (_m, i) => store[Number(i)]);
68
+ }
69
+
70
+ /**
71
+ * 注入后自检:确认刚写入的脚本没有被 HTML 注释包裹。
72
+ * 只检查本次注入的标签,因此不会把开发者主动注释掉的旧脚本误判为问题。
73
+ */
74
+ function auditInjectedScripts(html, scripts) {
75
+ for (const s of scripts) {
76
+ const idx = html.indexOf(s);
77
+ if (idx === -1) {
78
+ throw new Error(`index.html 结构异常:注入的脚本未出现在文件中:${s}`);
79
+ }
80
+ const before = html.slice(0, idx);
81
+ if (before.lastIndexOf('<!--') > before.lastIndexOf('-->')) {
82
+ throw new Error(
83
+ `index.html 结构异常:脚本 ${s} 被写进了 HTML 注释内部,真机上不会执行(白屏)。` +
84
+ '常见原因是注释里含有 body 结束标签字面量,导致注入锚点命中了注释。'
85
+ );
86
+ }
87
+ }
88
+ }
89
+
49
90
  /**
50
91
  * 修正 index.html 使其符合容器要求。
51
92
  * 这是整个流程里最容易出错也最有价值的一步。
@@ -58,6 +99,12 @@ export function patchIndexHtml(outDir) {
58
99
 
59
100
  let html = readFileSync(htmlPath, 'utf8');
60
101
 
102
+ // 先把 HTML 注释摘成占位符。否则下面几条正则会命中注释里的
103
+ // 结束标签 / <script src="..."> 字面量:前者会把脚本注入到注释内部(真机白屏),
104
+ // 后者会把开发者注释掉的旧脚本"复活"到文档末尾。手术后原样还原。
105
+ const { masked, store: comments } = maskComments(html);
106
+ html = masked;
107
+
61
108
  // 绝对路径资源 -> 相对路径(离线 zip 根为 /)
62
109
  html = html.replace(/(href|src)="\/([^"]+)"/g, '$1="./$2"');
63
110
  // 去掉 crossorigin(离线同源无需,且部分内核处理异常)
@@ -85,6 +132,9 @@ export function patchIndexHtml(outDir) {
85
132
  }
86
133
  }
87
134
 
135
+ html = unmaskComments(html, comments);
136
+ auditInjectedScripts(html, scripts);
137
+
88
138
  writeFileSync(htmlPath, html);
89
139
  return scripts.length;
90
140
  }