arashi 1.24.0 → 1.24.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.
- package/bin/prepare-spawn-command.js +44 -0
- package/bin/update.js +118 -17
- package/package.json +2 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const WINDOWS_BATCH_FILE = /\.(?:cmd|bat)$/i;
|
|
2
|
+
const CMD_ARGUMENT_VARIABLE_PREFIX = "ARASHI_CMD_ARGUMENT_";
|
|
3
|
+
|
|
4
|
+
// Quote according to CommandLineToArgvW's rules. The result is stored in an
|
|
5
|
+
// environment variable and introduced with ordinary expansion. Cmd expands each
|
|
6
|
+
// fixed %VARIABLE% token once, but does not rescan user-controlled contents as
|
|
7
|
+
// command syntax. Delayed expansion stays disabled so literal ! characters survive.
|
|
8
|
+
const quoteWindowsArgument = (argument) =>
|
|
9
|
+
`"${argument.replaceAll(/(\\*)"/g, String.raw`$1$1\"`).replace(/(\\*)$/, "$1$1")}"`;
|
|
10
|
+
|
|
11
|
+
export function prepareSpawnCommand(
|
|
12
|
+
command,
|
|
13
|
+
platform = process.platform,
|
|
14
|
+
env = process.env,
|
|
15
|
+
forceWindowsShell = false,
|
|
16
|
+
) {
|
|
17
|
+
const executable = command[0];
|
|
18
|
+
if (
|
|
19
|
+
platform !== "win32" ||
|
|
20
|
+
(!forceWindowsShell && !WINDOWS_BATCH_FILE.test(executable))
|
|
21
|
+
) {
|
|
22
|
+
return { args: command.slice(1), command: executable, windowsVerbatimArguments: false };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const values = command.map((argument) => quoteWindowsArgument(argument));
|
|
26
|
+
const variableNames = values.map((_value, index) => `${CMD_ARGUMENT_VARIABLE_PREFIX}${index}`);
|
|
27
|
+
|
|
28
|
+
const commandInterpreter =
|
|
29
|
+
Object.entries(env).find(([key]) => key.toLowerCase() === "comspec")?.[1] ?? "cmd.exe";
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
args: ["/d", "/v:off", "/s", "/c", `"${variableNames.map((name) => `%${name}%`).join(" ")}"`],
|
|
33
|
+
command: commandInterpreter,
|
|
34
|
+
env: {
|
|
35
|
+
...Object.fromEntries(
|
|
36
|
+
Object.entries(env).filter(
|
|
37
|
+
([name]) => !name.toUpperCase().startsWith(CMD_ARGUMENT_VARIABLE_PREFIX),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
...Object.fromEntries(variableNames.map((name, index) => [name, values[index]])),
|
|
41
|
+
},
|
|
42
|
+
windowsVerbatimArguments: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
package/bin/update.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { getPlatformInfo, installBinary, MANUAL_INSTALL_URL, PACKAGE_NAME } from "./install-binary.js";
|
|
7
|
+
import { prepareSpawnCommand } from "./prepare-spawn-command.js";
|
|
7
8
|
|
|
8
9
|
export const UPDATE_COMMAND_DESCRIPTION = "Check for and apply Arashi updates";
|
|
9
10
|
|
|
@@ -97,15 +98,84 @@ export async function fetchLatestGitHubRelease({ fetchImpl = fetch, repo = "corw
|
|
|
97
98
|
};
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
function normalizeInstallPath(value) {
|
|
102
|
+
return String(value ?? "")
|
|
103
|
+
.trim()
|
|
104
|
+
.replaceAll("\\", "/")
|
|
105
|
+
.replace(/\/+$/, "")
|
|
106
|
+
.toLowerCase();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function readEnvironmentValue(env, name) {
|
|
110
|
+
return Object.entries(env).find(([key]) => key.toLowerCase() === name.toLowerCase())?.[1] ?? "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readEnvironmentPath(env, name) {
|
|
114
|
+
return normalizeInstallPath(readEnvironmentValue(env, name));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function detectPackageManagerFromInstallRoot(rootDir, env) {
|
|
118
|
+
const root = normalizeInstallPath(rootDir);
|
|
119
|
+
if (!root) return null;
|
|
120
|
+
|
|
121
|
+
const userProfile = readEnvironmentPath(env, "USERPROFILE");
|
|
122
|
+
const home = userProfile || readEnvironmentPath(env, "HOME");
|
|
123
|
+
const appData =
|
|
124
|
+
readEnvironmentPath(env, "APPDATA") ||
|
|
125
|
+
(userProfile ? `${userProfile}/appdata/roaming` : "");
|
|
126
|
+
const localAppData =
|
|
127
|
+
readEnvironmentPath(env, "LOCALAPPDATA") ||
|
|
128
|
+
(userProfile ? `${userProfile}/appdata/local` : "");
|
|
129
|
+
|
|
130
|
+
if (home && root === `${home}/.vite-plus/packages/${PACKAGE_NAME}/current/package`) {
|
|
131
|
+
return "vite-plus";
|
|
132
|
+
}
|
|
133
|
+
if (appData && root === `${appData}/npm/node_modules/${PACKAGE_NAME}`) return "npm";
|
|
134
|
+
if (localAppData && root === `${localAppData}/yarn/data/global/node_modules/${PACKAGE_NAME}`) {
|
|
135
|
+
return "yarn";
|
|
136
|
+
}
|
|
137
|
+
if (userProfile && root === `${userProfile}/.bun/install/global/node_modules/${PACKAGE_NAME}`) {
|
|
138
|
+
return "bun";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const pnpmHomes = new Set(
|
|
142
|
+
[localAppData ? `${localAppData}/pnpm` : "", readEnvironmentPath(env, "PNPM_HOME")].filter(Boolean),
|
|
143
|
+
);
|
|
144
|
+
for (const pnpmHome of pnpmHomes) {
|
|
145
|
+
const relativeRoot = root.startsWith(`${pnpmHome}/`) ? root.slice(pnpmHome.length + 1) : "";
|
|
146
|
+
if (
|
|
147
|
+
/^global\/[^/]+\/(?:\.pnpm\/[^/]+\/node_modules|node_modules)\/arashi$/.test(relativeRoot)
|
|
148
|
+
) {
|
|
149
|
+
return "pnpm";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
100
156
|
export function selectPackageManagerCommand({ env = process.env, rootDir } = {}) {
|
|
101
|
-
const
|
|
102
|
-
|
|
157
|
+
const installRootManager = detectPackageManagerFromInstallRoot(rootDir, env);
|
|
158
|
+
if (installRootManager === "vite-plus") {
|
|
159
|
+
return { args: ["update", "-g", PACKAGE_NAME], command: "vp", label: "Vite+" };
|
|
160
|
+
}
|
|
161
|
+
if (installRootManager === "pnpm") {
|
|
162
|
+
return { args: ["add", "-g", `${PACKAGE_NAME}@latest`], command: "pnpm", label: "pnpm" };
|
|
163
|
+
}
|
|
164
|
+
if (installRootManager === "yarn") {
|
|
165
|
+
return { args: ["global", "add", `${PACKAGE_NAME}@latest`], command: "yarn", label: "yarn" };
|
|
166
|
+
}
|
|
167
|
+
if (installRootManager === "bun") {
|
|
168
|
+
return { args: ["add", "-g", `${PACKAGE_NAME}@latest`], command: "bun", label: "bun" };
|
|
169
|
+
}
|
|
170
|
+
if (installRootManager === "npm") {
|
|
171
|
+
return { args: ["install", "-g", `${PACKAGE_NAME}@latest`], command: "npm", label: "npm" };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const userAgent = readEnvironmentValue(env, "npm_config_user_agent");
|
|
175
|
+
const execPath = readEnvironmentValue(env, "npm_execpath");
|
|
103
176
|
const combined = `${userAgent} ${execPath}`.toLowerCase();
|
|
104
|
-
const normalizedRootDir = String(rootDir ?? "").toLowerCase();
|
|
105
177
|
const looksLikeVitePlus =
|
|
106
|
-
combined.includes("vite-plus") ||
|
|
107
|
-
/(^|[\\/\s])vp(?:\.exe)?($|[\\/\s])/.test(combined) ||
|
|
108
|
-
/(^|[\\/])\.vite-plus([\\/]|$)/.test(normalizedRootDir);
|
|
178
|
+
combined.includes("vite-plus") || /(^|[\\/\s])vp(?:\.exe)?($|[\\/\s])/.test(combined);
|
|
109
179
|
|
|
110
180
|
if (looksLikeVitePlus) {
|
|
111
181
|
return { args: ["update", "-g", PACKAGE_NAME], command: "vp", label: "Vite+" };
|
|
@@ -212,7 +282,8 @@ export async function runNpmManagedUpdate(argv = [], options = {}) {
|
|
|
212
282
|
return 0;
|
|
213
283
|
}
|
|
214
284
|
|
|
215
|
-
const packageManager =
|
|
285
|
+
const packageManager =
|
|
286
|
+
options.packageManager ?? selectPackageManagerCommand({ ...options, rootDir });
|
|
216
287
|
log(`Update available: ${PACKAGE_NAME} v${metadata.version} -> v${latestVersion}`);
|
|
217
288
|
|
|
218
289
|
if (flags.check) {
|
|
@@ -240,10 +311,21 @@ export async function runNpmManagedUpdate(argv = [], options = {}) {
|
|
|
240
311
|
}
|
|
241
312
|
|
|
242
313
|
const spawnSyncImpl = options.spawnSyncImpl ?? spawnSync;
|
|
243
|
-
const
|
|
244
|
-
|
|
314
|
+
const platform = options.platform ?? process.platform;
|
|
315
|
+
const env = options.env ?? process.env;
|
|
316
|
+
const updateCwd = options.updateCwd ?? dirname(process.execPath);
|
|
317
|
+
const invocation = prepareSpawnCommand(
|
|
318
|
+
[packageManager.command, ...packageManager.args],
|
|
319
|
+
platform,
|
|
320
|
+
env,
|
|
321
|
+
true,
|
|
322
|
+
);
|
|
323
|
+
const result = spawnSyncImpl(invocation.command, invocation.args, {
|
|
324
|
+
cwd: updateCwd,
|
|
245
325
|
encoding: "utf8",
|
|
326
|
+
env: invocation.env ?? env,
|
|
246
327
|
stdio: "inherit",
|
|
328
|
+
windowsVerbatimArguments: invocation.windowsVerbatimArguments,
|
|
247
329
|
});
|
|
248
330
|
|
|
249
331
|
if (result.error) {
|
|
@@ -256,20 +338,39 @@ export async function runNpmManagedUpdate(argv = [], options = {}) {
|
|
|
256
338
|
return 1;
|
|
257
339
|
}
|
|
258
340
|
|
|
259
|
-
let updatedMetadata = metadata;
|
|
260
341
|
try {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
342
|
+
let activeRootDir = rootDir;
|
|
343
|
+
if (packageManager.command === "pnpm") {
|
|
344
|
+
const rootInvocation = prepareSpawnCommand([packageManager.command, "root", "-g"], platform, env, true);
|
|
345
|
+
const rootResult = spawnSyncImpl(rootInvocation.command, rootInvocation.args, {
|
|
346
|
+
cwd: updateCwd,
|
|
347
|
+
encoding: "utf8",
|
|
348
|
+
env: rootInvocation.env ?? env,
|
|
349
|
+
windowsVerbatimArguments: rootInvocation.windowsVerbatimArguments,
|
|
350
|
+
});
|
|
351
|
+
if (rootResult.error) throw rootResult.error;
|
|
352
|
+
if (rootResult.status !== 0) {
|
|
353
|
+
throw new Error(`pnpm root -g failed with exit code ${rootResult.status ?? "unknown"}`);
|
|
354
|
+
}
|
|
355
|
+
const globalRoot = String(rootResult.stdout ?? "").trim();
|
|
356
|
+
if (!globalRoot) throw new Error("pnpm root -g returned an empty path");
|
|
357
|
+
const separator = platform === "win32" ? "\\" : "/";
|
|
358
|
+
activeRootDir = `${globalRoot.replace(/[\\/]+$/, "")}${separator}${PACKAGE_NAME}`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let updatedMetadata = metadata;
|
|
362
|
+
try {
|
|
363
|
+
updatedMetadata = await readPackageMetadata(activeRootDir, options);
|
|
364
|
+
} catch {
|
|
365
|
+
updatedMetadata = { ...metadata, version: latestVersion };
|
|
366
|
+
}
|
|
265
367
|
|
|
266
|
-
try {
|
|
267
368
|
const installBinaryImpl = options.installBinaryImpl ?? installBinary;
|
|
268
369
|
const installResult = await installBinaryImpl({
|
|
269
370
|
...options,
|
|
270
371
|
binDir: options.binDir,
|
|
271
372
|
force: true,
|
|
272
|
-
rootDir,
|
|
373
|
+
rootDir: activeRootDir,
|
|
273
374
|
version: updatedMetadata.version,
|
|
274
375
|
});
|
|
275
376
|
log(`✓ Updated ${PACKAGE_NAME} from v${metadata.version} to v${updatedMetadata.version}.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arashi",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.1",
|
|
4
4
|
"description": "Git worktree manager for meta-repositories - The eye of the storm for your development workflow",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"bin/arashi",
|
|
31
31
|
"bin/arashi.js",
|
|
32
32
|
"bin/install-binary.js",
|
|
33
|
+
"bin/prepare-spawn-command.js",
|
|
33
34
|
"bin/update.js",
|
|
34
35
|
"bin/arashi.bat",
|
|
35
36
|
"bin/arashi.ps1",
|