@wwkit/shared 1.0.12 → 1.0.14
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.
- package/package.json +1 -1
- package/src/shell.js +115 -0
package/package.json
CHANGED
package/src/shell.js
CHANGED
|
@@ -110,6 +110,45 @@ export class Shell {
|
|
|
110
110
|
return path.join(os.homedir(), '.profile')
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* 检测当前用户所有需要写入的 shell profile 文件
|
|
115
|
+
*
|
|
116
|
+
* 返回数组以覆盖多种场景(login / non-login / 不同 shell):
|
|
117
|
+
* - zsh: ~/.zshrc(交互式 zsh 总是读取)
|
|
118
|
+
* - bash: ~/.bashrc(非登录交互式)+ ~/.bash_profile(登录式,仅当已存在)
|
|
119
|
+
* - fish: ~/.config/fish/config.fish(fish 唯一配置文件)
|
|
120
|
+
* - 未知: ~/.profile(POSIX 通用回退)
|
|
121
|
+
*
|
|
122
|
+
* 每项含 `path`(绝对路径)和 `syntax`('export' | 'fish'),
|
|
123
|
+
* 调用方据此生成正确语法的变量设置行。
|
|
124
|
+
*
|
|
125
|
+
* @returns {{ path: string, syntax: 'export' | 'fish' }[]}
|
|
126
|
+
*/
|
|
127
|
+
getProfiles() {
|
|
128
|
+
const home = os.homedir()
|
|
129
|
+
const shell = this.env.SHELL || ''
|
|
130
|
+
const profiles = []
|
|
131
|
+
|
|
132
|
+
if (shell.includes('fish')) {
|
|
133
|
+
profiles.push({
|
|
134
|
+
path: path.join(home, '.config', 'fish', 'config.fish'),
|
|
135
|
+
syntax: 'fish',
|
|
136
|
+
})
|
|
137
|
+
} else if (shell.includes('zsh')) {
|
|
138
|
+
profiles.push({ path: path.join(home, '.zshrc'), syntax: 'export' })
|
|
139
|
+
} else if (shell.includes('bash')) {
|
|
140
|
+
profiles.push({ path: path.join(home, '.bashrc'), syntax: 'export' })
|
|
141
|
+
const bp = path.join(home, '.bash_profile')
|
|
142
|
+
if (fs.existsSync(bp)) {
|
|
143
|
+
profiles.push({ path: bp, syntax: 'export' })
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
profiles.push({ path: path.join(home, '.profile'), syntax: 'export' })
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return profiles
|
|
150
|
+
}
|
|
151
|
+
|
|
113
152
|
/**
|
|
114
153
|
* 将 shell 初始化行写入 profile(按 marker 去重)
|
|
115
154
|
*
|
|
@@ -132,6 +171,82 @@ export class Shell {
|
|
|
132
171
|
return true
|
|
133
172
|
}
|
|
134
173
|
|
|
174
|
+
/**
|
|
175
|
+
* 持久化设置系统环境变量(跨平台)
|
|
176
|
+
*
|
|
177
|
+
* Unix: 将变量设置行写入所有相关 shell profile(getProfiles()),使用
|
|
178
|
+
* begin/end 标记块管理区块,支持幂等(值相同则跳过)和值更新
|
|
179
|
+
* (路径变化时自动替换)。覆盖 bash/zsh/fish 及 login/non-login 场景。
|
|
180
|
+
* Windows: 使用 `setx` 设置用户级环境变量(写入注册表,始终覆盖)。
|
|
181
|
+
*
|
|
182
|
+
* @param {string} name - 环境变量名
|
|
183
|
+
* @param {string} value - 环境变量值
|
|
184
|
+
* @returns {boolean} 是否写入/更新了至少一个目标(全部已存在且值相同则 false)
|
|
185
|
+
*/
|
|
186
|
+
setEnvVar(name, value) {
|
|
187
|
+
if (this.isWindows) {
|
|
188
|
+
try {
|
|
189
|
+
const result = spawnSync('setx', [name, String(value)], {
|
|
190
|
+
encoding: 'utf8',
|
|
191
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
192
|
+
})
|
|
193
|
+
return result.status === 0
|
|
194
|
+
} catch {
|
|
195
|
+
return false
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const escaped = String(value).replace(/[$`\\"]/g, '\\$&')
|
|
200
|
+
let wrote = false
|
|
201
|
+
|
|
202
|
+
for (const { path: profilePath, syntax } of this.getProfiles()) {
|
|
203
|
+
const line = syntax === 'fish'
|
|
204
|
+
? `set -gx ${name} "${escaped}"`
|
|
205
|
+
: `export ${name}="${escaped}"`
|
|
206
|
+
if (this._writeEnvBlock(profilePath, name, line)) {
|
|
207
|
+
wrote = true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return wrote
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* 向指定 profile 文件写入/更新一个环境变量标记块
|
|
216
|
+
*
|
|
217
|
+
* @param {string} profilePath - profile 文件绝对路径
|
|
218
|
+
* @param {string} name - 环境变量名(用于生成标记)
|
|
219
|
+
* @param {string} line - 变量设置行(含正确语法)
|
|
220
|
+
* @returns {boolean} 是否写入/更新了该文件
|
|
221
|
+
*/
|
|
222
|
+
_writeEnvBlock(profilePath, name, line) {
|
|
223
|
+
let existing = ''
|
|
224
|
+
try {
|
|
225
|
+
existing = fs.readFileSync(profilePath, 'utf8')
|
|
226
|
+
} catch {}
|
|
227
|
+
|
|
228
|
+
const beginMarker = `# >>> wwkit ${name} >>>`
|
|
229
|
+
const endMarker = `# <<< wwkit ${name} <<<`
|
|
230
|
+
const block = `${beginMarker}\n${line}\n${endMarker}`
|
|
231
|
+
|
|
232
|
+
const beginIdx = existing.indexOf(beginMarker)
|
|
233
|
+
if (beginIdx !== -1) {
|
|
234
|
+
const endIdx = existing.indexOf(endMarker)
|
|
235
|
+
if (endIdx !== -1 && existing.includes(line)) {
|
|
236
|
+
return false
|
|
237
|
+
}
|
|
238
|
+
const afterEnd = endIdx !== -1 ? endIdx + endMarker.length : beginIdx + beginMarker.length
|
|
239
|
+
existing = existing.slice(0, beginIdx) + block + existing.slice(afterEnd)
|
|
240
|
+
fs.writeFileSync(profilePath, existing, 'utf8')
|
|
241
|
+
return true
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const dir = path.dirname(profilePath)
|
|
245
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
246
|
+
fs.appendFileSync(profilePath, `\n${block}\n`, 'utf8')
|
|
247
|
+
return true
|
|
248
|
+
}
|
|
249
|
+
|
|
135
250
|
/**
|
|
136
251
|
* 检测系统命令是否可用
|
|
137
252
|
* Windows: where <cmd>;Unix: which <cmd>
|