dsh-ssh-tunnel 1.0.1 → 1.0.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.
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /*
4
+ * cordis.patch.yml 旧手动挂载块清理(install.sh / install.ps1 共用)。
5
+ *
6
+ * 用法:node strip-mount.cjs <cordis.patch.yml> <pluginId>
7
+ *
8
+ * 删除形如 `- insert:` 且块内含 `id: <pluginId>` 的整块(含缩进更深的子行,
9
+ * 直至出现同缩进或更浅缩进的行),并连同块上方紧邻的注释行一起移除。
10
+ * 幂等:无匹配块时不写文件。写后立即回读断言,失败回滚原内容并以非零码退出。
11
+ *
12
+ * 匹配形态(与 dsh-ssh-tunnel 仓库根 cordis.patch.yml 同构):
13
+ * - insert:
14
+ * - id: ssh-tunnel
15
+ * name: dsh-ssh-tunnel
16
+ */
17
+ const fs = require('fs');
18
+
19
+ function escapeRe(s) {
20
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21
+ }
22
+
23
+ function fail(msg) {
24
+ process.stderr.write('[strip-mount] ' + msg + '\n');
25
+ process.exit(1);
26
+ }
27
+
28
+ function indentOf(line) {
29
+ const m = line.match(/^([ \t]*)/);
30
+ return m ? m[1].length : 0;
31
+ }
32
+
33
+ // 返回所有「含 id 的 insert 块」的 [start, end) 行区间
34
+ function findBlocks(lines, id) {
35
+ const idRe = new RegExp('id:[ \\t]*' + escapeRe(id) + '\\b');
36
+ const blocks = [];
37
+ for (let i = 0; i < lines.length; i++) {
38
+ if (!/^[ \t]*- insert:[ \t]*$/.test(lines[i])) continue;
39
+ const startIndent = indentOf(lines[i]);
40
+ let j = i + 1;
41
+ while (
42
+ j < lines.length &&
43
+ lines[j].trim() !== '' &&
44
+ indentOf(lines[j]) > startIndent
45
+ ) {
46
+ j++;
47
+ }
48
+ if (idRe.test(lines.slice(i, j).join('\n'))) blocks.push([i, j]);
49
+ i = j - 1;
50
+ }
51
+ return blocks;
52
+ }
53
+
54
+ function removeBlocks(lines, blocks) {
55
+ const remove = new Set();
56
+ for (const range of blocks) {
57
+ for (let k = range[0]; k < range[1]; k++) remove.add(k);
58
+ }
59
+ const out = [];
60
+ for (let i = 0; i < lines.length; i++) {
61
+ if (remove.has(i)) {
62
+ // 移除块上方紧邻的注释行
63
+ while (out.length && /^[ \t]*#/.test(out[out.length - 1])) out.pop();
64
+ continue;
65
+ }
66
+ out.push(lines[i]);
67
+ }
68
+ return out;
69
+ }
70
+
71
+ const file = process.argv[2];
72
+ const id = process.argv[3];
73
+ if (!file || !id || process.argv.length !== 4) {
74
+ process.stderr.write('用法: node strip-mount.cjs <cordis.patch.yml> <pluginId>\n');
75
+ process.exit(2);
76
+ }
77
+
78
+ let original;
79
+ try {
80
+ original = fs.readFileSync(file, 'utf8');
81
+ } catch (e) {
82
+ fail('读取失败: ' + e.message);
83
+ }
84
+
85
+ const lines = original.split('\n');
86
+ const blocks = findBlocks(lines, id);
87
+ if (blocks.length === 0) {
88
+ console.log('none');
89
+ process.exit(0);
90
+ }
91
+
92
+ const updated = removeBlocks(lines, blocks).join('\n');
93
+ try {
94
+ fs.writeFileSync(file, updated, 'utf8');
95
+ } catch (e) {
96
+ fail('写入失败: ' + e.message + '(文件保持原样,未做任何修改)');
97
+ }
98
+
99
+ // 写后立即回读断言;失败则回滚原内容并退出非零
100
+ let readback = '';
101
+ try {
102
+ readback = fs.readFileSync(file, 'utf8');
103
+ } catch (e) {
104
+ fail('回读失败: ' + e.message);
105
+ }
106
+ if (findBlocks(readback.split('\n'), id).length !== 0) {
107
+ try {
108
+ fs.writeFileSync(file, original, 'utf8');
109
+ } catch (e2) {
110
+ fail('回读断言失败且回滚也失败,请手动检查该文件: ' + e2.message);
111
+ }
112
+ fail('回读断言失败(id: ' + id + ' 的挂载块仍在),已回滚为原内容。');
113
+ }
114
+ console.log('removed');
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /*
4
+ * pnpm-workspace.yaml 的 minimumReleaseAgeExclude 维护(install.sh / install.ps1 共用)。
5
+ *
6
+ * 用法:
7
+ * node ws-exclude.cjs <pnpm-workspace.yaml> <pkg>
8
+ * 写模式:列表缺失 pkg 时追加(幂等);写后立即回读断言,
9
+ * 断言失败回滚原内容并以非零码退出。
10
+ * node ws-exclude.cjs --check <pnpm-workspace.yaml> <pkg>
11
+ * 检查模式:已包含退出 0(打印 present),否则退出 1(打印 absent)。
12
+ *
13
+ * 「已存在」判定:任意列表项形如 <pkg> 或 <pkg>@<版本/区间>(pkg + '@' 前缀匹配),
14
+ * 例如 pkg=dsh-ssh-tunnel 时,`- dsh-ssh-tunnel` 与 `- dsh-ssh-tunnel@1.0.0` 都算已存在。
15
+ */
16
+ const fs = require('fs');
17
+
18
+ function escapeRe(s) {
19
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
+ }
21
+
22
+ function fail(msg) {
23
+ process.stderr.write('[ws-exclude] ' + msg + '\n');
24
+ process.exit(1);
25
+ }
26
+
27
+ function hasEntry(text, pkg) {
28
+ const re = new RegExp(
29
+ '^[ \\t]*-[ \\t]+' + escapeRe(pkg) + '(?:@[^\\n]*)?[ \\t]*(?:#.*)?$',
30
+ 'm'
31
+ );
32
+ return re.test(text);
33
+ }
34
+
35
+ function buildUpdated(text, pkg) {
36
+ const lines = text.split('\n');
37
+ let headerIdx = -1;
38
+ let baseIndent = '';
39
+ let inlineRest = null;
40
+ for (let i = 0; i < lines.length; i++) {
41
+ const m = lines[i].match(/^([ \t]*)minimumReleaseAgeExclude:(.*)$/);
42
+ if (m) {
43
+ headerIdx = i;
44
+ baseIndent = m[1];
45
+ inlineRest = m[2].trim();
46
+ break;
47
+ }
48
+ }
49
+
50
+ if (headerIdx === -1) {
51
+ // 没有 exclude 段:在文件末尾追加新段
52
+ while (lines.length && lines[lines.length - 1].trim() === '') lines.pop();
53
+ lines.push('minimumReleaseAgeExclude:', ' - ' + pkg, '');
54
+ return lines.join('\n');
55
+ }
56
+
57
+ if (inlineRest && inlineRest !== '[]') {
58
+ fail('minimumReleaseAgeExclude 是内联形式(' + inlineRest + '),不支持自动改写,请手动编辑。');
59
+ }
60
+ if (inlineRest === '[]') {
61
+ // 空列表内联形式展开为块列表
62
+ lines[headerIdx] = baseIndent + 'minimumReleaseAgeExclude:';
63
+ }
64
+
65
+ // 子项缩进取段内首个列表项的缩进,缺省在段键缩进基础上加两格
66
+ let childIndent = baseIndent + ' ';
67
+ for (let j = headerIdx + 1; j < lines.length; j++) {
68
+ if (lines[j].trim() === '') continue;
69
+ const im = lines[j].match(/^([ \t]*)-[ \t]/);
70
+ if (im) childIndent = im[1];
71
+ break;
72
+ }
73
+ lines.splice(headerIdx + 1, 0, childIndent + '- ' + pkg);
74
+ return lines.join('\n');
75
+ }
76
+
77
+ const argv = process.argv.slice(2);
78
+ let mode = 'write';
79
+ if (argv[0] === '--check') {
80
+ mode = 'check';
81
+ argv.shift();
82
+ }
83
+ const file = argv[0];
84
+ const pkg = argv[1];
85
+ if (!file || !pkg || argv.length !== 2) {
86
+ process.stderr.write('用法: node ws-exclude.cjs [--check] <pnpm-workspace.yaml> <pkg>\n');
87
+ process.exit(2);
88
+ }
89
+
90
+ let original;
91
+ try {
92
+ original = fs.readFileSync(file, 'utf8');
93
+ } catch (e) {
94
+ if (mode === 'check') {
95
+ process.stderr.write('[ws-exclude] 文件不存在或不可读: ' + file + '\n');
96
+ process.exit(2);
97
+ }
98
+ fail('读取失败: ' + e.message);
99
+ }
100
+
101
+ if (mode === 'check') {
102
+ if (hasEntry(original, pkg)) {
103
+ console.log('present');
104
+ process.exit(0);
105
+ }
106
+ console.log('absent');
107
+ process.exit(1);
108
+ }
109
+
110
+ if (hasEntry(original, pkg)) {
111
+ console.log('unchanged');
112
+ process.exit(0);
113
+ }
114
+
115
+ let updated;
116
+ try {
117
+ updated = buildUpdated(original, pkg);
118
+ } catch (e) {
119
+ fail('构造更新内容失败: ' + e.message + '(文件未做任何修改)');
120
+ }
121
+
122
+ try {
123
+ fs.writeFileSync(file, updated, 'utf8');
124
+ } catch (e) {
125
+ fail('写入失败: ' + e.message + '(文件保持原样,未做任何修改)');
126
+ }
127
+
128
+ // 写后立即回读断言;失败则回滚原内容并退出非零
129
+ let readback = '';
130
+ try {
131
+ readback = fs.readFileSync(file, 'utf8');
132
+ } catch (e) {
133
+ fail('回读失败: ' + e.message);
134
+ }
135
+ if (!hasEntry(readback, pkg)) {
136
+ try {
137
+ fs.writeFileSync(file, original, 'utf8');
138
+ } catch (e2) {
139
+ fail('回读断言失败且回滚也失败,请手动检查该文件: ' + e2.message);
140
+ }
141
+ fail('回读断言失败(写后内容缺少期望条目 ' + pkg + '),已回滚为原内容。');
142
+ }
143
+ console.log('updated');