dsh-console-utf8 0.1.2 → 0.1.3
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/CHANGELOG.md +5 -0
- package/dsh-plugin.json +1 -1
- package/lib/plugin.js +41 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## 0.1.3 (2026-09-16)
|
|
8
|
+
|
|
9
|
+
- **A hook file this plugin does not own is never overwritten.** `BASH_ENV` ownership is now decided *before* anything is written — it used to stand down only after `writeShimIfChanged` had already run — and `writeShimIfChanged` refuses to replace a file that exists but does not carry this plugin's marker, raising `ForeignShimError` instead. Following the old warning's advice ("set this plugin's `shimPath` to adopt it") replaced that user's script with no backup. The warning no longer suggests it, and a foreign hook path stands the plugin down with reason `hook path holds a foreign file`.
|
|
10
|
+
- `SHIM_MARKER` and `ForeignShimError` are exported, and five unit tests pin the behaviour: a free path is written, this plugin's own hook follows a code-page change, a foreign file is refused and left byte-identical, ownership is decided before any write, and the marker is exactly what `shimScript()` emits.
|
|
11
|
+
|
|
7
12
|
## 0.1.2 (2026-09-13)
|
|
8
13
|
|
|
9
14
|
- Documentation only, no code change. The READMEs gain a CI badge, the release section records that publishing goes through npm trusted publishing (OIDC) with no stored token, and the duplicated publishing heading is merged into one section.
|
package/dsh-plugin.json
CHANGED
package/lib/plugin.js
CHANGED
|
@@ -120,6 +120,24 @@ export function restoreBashEnv(env, owned) {
|
|
|
120
120
|
return 'restored'
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Substring that identifies a hook file as THIS plugin's own. Any file at the
|
|
125
|
+
* configured `shimPath` that does not carry it belongs to somebody else and is
|
|
126
|
+
* never overwritten.
|
|
127
|
+
*/
|
|
128
|
+
export const SHIM_MARKER = 'dsh-console-utf8'
|
|
129
|
+
|
|
130
|
+
/** Raised instead of replacing a hook file this plugin does not own. */
|
|
131
|
+
export class ForeignShimError extends Error {
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} path - The offending hook path.
|
|
134
|
+
*/
|
|
135
|
+
constructor(path) {
|
|
136
|
+
super(`refusing to overwrite ${path}: the file exists and is not this plugin's hook (move it aside, or point shimPath at a free path)`)
|
|
137
|
+
this.name = 'ForeignShimError'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
123
141
|
/**
|
|
124
142
|
* Write the hook when its content differs, creating the directory if needed.
|
|
125
143
|
*
|
|
@@ -127,9 +145,16 @@ export function restoreBashEnv(env, owned) {
|
|
|
127
145
|
* the fix silently do the wrong thing, so the file is compared rather than
|
|
128
146
|
* assumed.
|
|
129
147
|
*
|
|
148
|
+
* The file is only ever replaced when it is missing, already identical, or
|
|
149
|
+
* recognisably this plugin's own (it carries {@link SHIM_MARKER}). Following
|
|
150
|
+
* the old warning's advice — "set shimPath to adopt an existing BASH_ENV" —
|
|
151
|
+
* used to overwrite that user's script with no backup; a foreign file now
|
|
152
|
+
* raises {@link ForeignShimError} instead, and the plugin stands down.
|
|
153
|
+
*
|
|
130
154
|
* @param {string} path - Hook path.
|
|
131
155
|
* @param {string} content - Desired content.
|
|
132
156
|
* @returns {boolean} True when the file was (re)written.
|
|
157
|
+
* @throws {ForeignShimError} When `path` holds a file this plugin does not own.
|
|
133
158
|
*/
|
|
134
159
|
export function writeShimIfChanged(path, content) {
|
|
135
160
|
let current
|
|
@@ -139,6 +164,7 @@ export function writeShimIfChanged(path, content) {
|
|
|
139
164
|
current = undefined
|
|
140
165
|
}
|
|
141
166
|
if (current === content) return false
|
|
167
|
+
if (current !== undefined && !current.includes(SHIM_MARKER)) throw new ForeignShimError(path)
|
|
142
168
|
mkdirSync(dirname(path), { recursive: true })
|
|
143
169
|
// No BOM, LF endings: the file is sourced by `sh`, never parsed as JSON.
|
|
144
170
|
writeFileSync(path, content, 'utf8')
|
|
@@ -190,24 +216,29 @@ export function setup(deps, config) {
|
|
|
190
216
|
if (config.shellHook) {
|
|
191
217
|
const shimPath = config.shimPath === '' ? defaultShimPath(deps.home) : config.shimPath
|
|
192
218
|
const value = toPosixPath(shimPath)
|
|
193
|
-
let written = false
|
|
194
|
-
try {
|
|
195
|
-
written = deps.ensureShim(shimPath, shimScript(config.codePage))
|
|
196
|
-
} catch (error) {
|
|
197
|
-
deps.log.warn(`could not write the shell hook: ${error instanceof Error ? error.message : String(error)}`)
|
|
198
|
-
result.shim = { path: shimPath, applied: false, reason: 'hook not writable' }
|
|
199
|
-
return result
|
|
200
|
-
}
|
|
201
219
|
|
|
220
|
+
// Ownership is decided BEFORE anything is written. Standing down after a
|
|
221
|
+
// write would already have replaced a file this plugin does not own, so
|
|
222
|
+
// pointing `shimPath` at somebody else's hook must never destroy it.
|
|
202
223
|
const existing = typeof deps.env.BASH_ENV === 'string' && deps.env.BASH_ENV !== '' ? deps.env.BASH_ENV : undefined
|
|
203
224
|
if (existing !== undefined && existing !== value) {
|
|
204
225
|
// Someone else owns BASH_ENV. Clobbering it would break their hook, so
|
|
205
|
-
// this plugin stands down and says why
|
|
206
|
-
deps.log.warn(`BASH_ENV is already ${existing}; left untouched (
|
|
226
|
+
// this plugin stands down and says why.
|
|
227
|
+
deps.log.warn(`BASH_ENV is already ${existing}; left untouched. This plugin only maintains its own hook file (shimPath), so that hook wins — remove it first if this plugin should own BASH_ENV`)
|
|
207
228
|
result.shim = { path: shimPath, applied: false, reason: 'BASH_ENV owned by someone else' }
|
|
208
229
|
return result
|
|
209
230
|
}
|
|
210
231
|
|
|
232
|
+
let written = false
|
|
233
|
+
try {
|
|
234
|
+
written = deps.ensureShim(shimPath, shimScript(config.codePage))
|
|
235
|
+
} catch (error) {
|
|
236
|
+
const reason = error instanceof ForeignShimError ? 'hook path holds a foreign file' : 'hook not writable'
|
|
237
|
+
deps.log.warn(`could not write the shell hook: ${error instanceof Error ? error.message : String(error)}`)
|
|
238
|
+
result.shim = { path: shimPath, applied: false, reason }
|
|
239
|
+
return result
|
|
240
|
+
}
|
|
241
|
+
|
|
211
242
|
deps.env.BASH_ENV = value
|
|
212
243
|
result.shim = {
|
|
213
244
|
path: shimPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-console-utf8",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Keep the Windows console on code page 65001 (UTF-8) for the dsh host and its bash tool commands, so output from Windows-native child processes stops being decoded as mojibake",
|
|
5
5
|
"packageManager": "pnpm@11.22.0",
|
|
6
6
|
"license": "MIT",
|