@nocobase/cli 2.1.11-test.1 → 2.1.11-test.10

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.
@@ -77,7 +77,7 @@ export default class SelfUpdate extends Command {
77
77
  message: flags.skills
78
78
  ? `Update ${status.packageName} from ${status.currentVersion} to ${status.latestVersion} and refresh the globally installed NocoBase AI coding skills?`
79
79
  : `Update ${status.packageName} from ${status.currentVersion} to ${status.latestVersion}?`,
80
- default: false,
80
+ default: true,
81
81
  });
82
82
  }
83
83
  catch {
@@ -7,14 +7,12 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import fs from 'node:fs';
10
- import fsp from 'node:fs/promises';
10
+ import os from 'node:os';
11
11
  import path from 'node:path';
12
12
  import { fileURLToPath } from 'node:url';
13
- import { resolveCliHomeDir } from './cli-home.js';
14
13
  import { commandOutput, run } from './run-npm.js';
15
14
  const DEFAULT_PACKAGE_NAME = '@nocobase/cli';
16
15
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
17
- const INSTALL_METHOD_CACHE_FILE = 'self-install-methods.json';
18
16
  function normalizePath(value) {
19
17
  return path.resolve(value);
20
18
  }
@@ -119,6 +117,14 @@ function detectInstallMethod(packageRoot, options = {}) {
119
117
  if (options.yarnGlobalDir && isSubPath(options.yarnGlobalDir, packageRoot)) {
120
118
  return 'yarn-global';
121
119
  }
120
+ if (isPnpmGlobalInstallPath({
121
+ packageName: options.packageName ?? DEFAULT_PACKAGE_NAME,
122
+ packageRoot,
123
+ pnpmGlobalBin: options.pnpmGlobalBin,
124
+ pnpmGlobalRoot: options.pnpmGlobalRoot,
125
+ })) {
126
+ return 'pnpm-global';
127
+ }
122
128
  if (isPnpmGlobalPath(packageRoot)) {
123
129
  return 'pnpm-global';
124
130
  }
@@ -136,43 +142,130 @@ function isPnpmGlobalPath(packageRoot) {
136
142
  }
137
143
  return segments.slice(globalIndex + 2).includes('node_modules');
138
144
  }
139
- function getInstallMethodCacheFile() {
140
- return path.join(resolveCliHomeDir('global'), INSTALL_METHOD_CACHE_FILE);
145
+ function normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot) {
146
+ const normalizedGlobalRoot = normalizePath(pnpmGlobalRoot);
147
+ return path.basename(normalizedGlobalRoot) === 'node_modules'
148
+ ? normalizedGlobalRoot
149
+ : path.join(normalizedGlobalRoot, 'node_modules');
141
150
  }
142
- function getInstallMethodCacheKey(packageRoot) {
143
- return normalizePath(path.join(packageRoot, 'bin', 'run.js'));
151
+ function packageNameToPath(packageName) {
152
+ return packageName.split('/').filter(Boolean);
144
153
  }
145
- async function readInstallMethodCache() {
154
+ function isSameRealPath(left, right) {
146
155
  try {
147
- const raw = await fsp.readFile(getInstallMethodCacheFile(), 'utf8');
148
- return JSON.parse(raw);
156
+ return normalizePath(fs.realpathSync(left)) === normalizePath(fs.realpathSync(right));
149
157
  }
150
158
  catch {
151
- return {};
159
+ return false;
152
160
  }
153
161
  }
154
- async function writeInstallMethodCache(state) {
155
- const filePath = getInstallMethodCacheFile();
156
- await fsp.mkdir(path.dirname(filePath), { recursive: true });
157
- await fsp.writeFile(filePath, JSON.stringify(state, null, 2));
162
+ function isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot) {
163
+ if (isSubPath(pnpmGlobalRoot, packageRoot)) {
164
+ return true;
165
+ }
166
+ const globalNodeModulesDir = normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot);
167
+ const globalProjectDir = path.dirname(globalNodeModulesDir);
168
+ if (!isSubPath(globalProjectDir, packageRoot)) {
169
+ return false;
170
+ }
171
+ const segments = normalizePath(packageRoot).split(path.sep).filter(Boolean);
172
+ const pnpmStoreIndex = segments.indexOf('.pnpm');
173
+ if (pnpmStoreIndex === -1) {
174
+ return false;
175
+ }
176
+ return segments.slice(pnpmStoreIndex + 1).includes('node_modules');
158
177
  }
159
- async function readCachedInstallMethod(packageRoot) {
160
- const state = await readInstallMethodCache();
161
- return state.entries?.[getInstallMethodCacheKey(packageRoot)];
178
+ function isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName) {
179
+ return isSameRealPath(path.join(normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot), ...packageNameToPath(packageName)), packageRoot);
162
180
  }
163
- async function writeCachedInstallMethod(packageRoot, entry) {
164
- const state = await readInstallMethodCache();
165
- const entries = {
166
- ...(state.entries ?? {}),
167
- [getInstallMethodCacheKey(packageRoot)]: entry,
168
- };
169
- await writeInstallMethodCache({ entries });
181
+ function isPnpmGlobalBinProjectPath(pnpmGlobalBin, packageRoot, packageName) {
182
+ const pnpmHome = path.dirname(normalizePath(pnpmGlobalBin));
183
+ const globalDir = path.join(pnpmHome, 'global');
184
+ if (isSubPath(globalDir, packageRoot)) {
185
+ return true;
186
+ }
187
+ let entries;
188
+ try {
189
+ entries = fs.readdirSync(globalDir);
190
+ }
191
+ catch {
192
+ return false;
193
+ }
194
+ return entries.some((entry) => {
195
+ const pnpmGlobalRoot = path.join(globalDir, entry, 'node_modules');
196
+ return (isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot)
197
+ || isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName));
198
+ });
199
+ }
200
+ function isPnpmGlobalInstallPath(options) {
201
+ if (options.pnpmGlobalRoot) {
202
+ const matchedRoot = isPnpmGlobalRootPath(options.pnpmGlobalRoot, options.packageRoot)
203
+ || isPnpmGlobalPackagePath(options.pnpmGlobalRoot, options.packageRoot, options.packageName);
204
+ if (matchedRoot) {
205
+ return true;
206
+ }
207
+ }
208
+ if (options.pnpmGlobalBin) {
209
+ return isPnpmGlobalBinProjectPath(options.pnpmGlobalBin, options.packageRoot, options.packageName);
210
+ }
211
+ return false;
212
+ }
213
+ function readCurrentBinPath(currentBinPath) {
214
+ const candidate = String(currentBinPath ?? process.argv[1] ?? '').trim();
215
+ if (!candidate) {
216
+ return undefined;
217
+ }
218
+ return normalizePath(candidate);
219
+ }
220
+ function cleanCommandPath(value) {
221
+ const trimmed = value.trim();
222
+ return trimmed ? trimmed : undefined;
223
+ }
224
+ function resolvePackageManagerProbeCwd() {
225
+ const candidates = [os.homedir(), os.tmpdir(), path.parse(process.cwd()).root];
226
+ for (const candidate of candidates) {
227
+ if (!candidate) {
228
+ continue;
229
+ }
230
+ try {
231
+ if (fs.statSync(candidate).isDirectory()) {
232
+ return candidate;
233
+ }
234
+ }
235
+ catch {
236
+ // Ignore invalid probe cwd candidates and continue to the next one.
237
+ }
238
+ }
239
+ return undefined;
170
240
  }
171
241
  async function readGlobalPrefix(commandOutputFn) {
172
242
  try {
173
- return (await commandOutputFn('npm', ['prefix', '-g'], {
243
+ return cleanCommandPath(await commandOutputFn('npm', ['prefix', '-g'], {
244
+ cwd: resolvePackageManagerProbeCwd(),
174
245
  errorName: 'npm prefix',
175
- })).trim();
246
+ }));
247
+ }
248
+ catch {
249
+ return undefined;
250
+ }
251
+ }
252
+ async function readPnpmGlobalBin(commandOutputFn) {
253
+ try {
254
+ return cleanCommandPath(await commandOutputFn('pnpm', ['bin', '-g'], {
255
+ cwd: resolvePackageManagerProbeCwd(),
256
+ errorName: 'pnpm bin',
257
+ }));
258
+ }
259
+ catch {
260
+ return undefined;
261
+ }
262
+ }
263
+ async function readPnpmGlobalRoot(commandOutputFn) {
264
+ try {
265
+ return cleanCommandPath(await commandOutputFn('pnpm', ['root', '-g'], {
266
+ cwd: resolvePackageManagerProbeCwd(),
267
+ errorName: 'pnpm root',
268
+ }));
176
269
  }
177
270
  catch {
178
271
  return undefined;
@@ -180,9 +273,21 @@ async function readGlobalPrefix(commandOutputFn) {
180
273
  }
181
274
  async function readYarnGlobalDir(commandOutputFn) {
182
275
  try {
183
- return (await commandOutputFn('yarn', ['global', 'dir'], {
276
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'dir'], {
277
+ cwd: resolvePackageManagerProbeCwd(),
184
278
  errorName: 'yarn global dir',
185
- })).trim();
279
+ }));
280
+ }
281
+ catch {
282
+ return undefined;
283
+ }
284
+ }
285
+ async function readYarnGlobalBin(commandOutputFn) {
286
+ try {
287
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'bin'], {
288
+ cwd: resolvePackageManagerProbeCwd(),
289
+ errorName: 'yarn global bin',
290
+ }));
186
291
  }
187
292
  catch {
188
293
  return undefined;
@@ -242,28 +347,45 @@ export function getSelfUpdatePackageSpec(status) {
242
347
  }
243
348
  export async function inspectSelfInstall(options = {}) {
244
349
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
350
+ const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
245
351
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
246
- const cachedInstallMethod = await readCachedInstallMethod(packageRoot);
247
- const globalPrefix = cachedInstallMethod?.globalPrefix ?? await readGlobalPrefix(commandOutputFn);
248
- const yarnGlobalDir = cachedInstallMethod?.yarnGlobalDir ?? await readYarnGlobalDir(commandOutputFn);
249
- const installMethod = cachedInstallMethod?.installMethod
250
- ?? detectInstallMethod(packageRoot, {
251
- globalPrefix,
252
- yarnGlobalDir,
253
- });
254
- if (!cachedInstallMethod) {
255
- await writeCachedInstallMethod(packageRoot, {
256
- installMethod,
257
- globalPrefix,
258
- yarnGlobalDir,
259
- });
260
- }
352
+ const currentBinPath = readCurrentBinPath(options.currentBinPath);
353
+ const globalPrefix = await readGlobalPrefix(commandOutputFn);
354
+ const pnpmGlobalBin = await readPnpmGlobalBin(commandOutputFn);
355
+ const pnpmGlobalRoot = await readPnpmGlobalRoot(commandOutputFn);
356
+ const yarnGlobalDir = await readYarnGlobalDir(commandOutputFn);
357
+ const yarnGlobalBin = await readYarnGlobalBin(commandOutputFn);
358
+ const installMethod = detectInstallMethodFromSignals({
359
+ packageRoot,
360
+ currentBinPath,
361
+ globalPrefix,
362
+ packageName,
363
+ pnpmGlobalBin,
364
+ pnpmGlobalRoot,
365
+ yarnGlobalBin,
366
+ yarnGlobalDir,
367
+ });
261
368
  return {
262
369
  packageRoot,
263
370
  installMethod,
264
371
  globalPrefix,
265
372
  };
266
373
  }
374
+ function detectInstallMethodFromSignals(options) {
375
+ if (options.currentBinPath && options.yarnGlobalBin && isSubPath(options.yarnGlobalBin, options.currentBinPath)) {
376
+ return 'yarn-global';
377
+ }
378
+ if (options.currentBinPath && options.pnpmGlobalBin && isSubPath(options.pnpmGlobalBin, options.currentBinPath)) {
379
+ return 'pnpm-global';
380
+ }
381
+ return detectInstallMethod(options.packageRoot, {
382
+ globalPrefix: options.globalPrefix,
383
+ packageName: options.packageName,
384
+ pnpmGlobalBin: options.pnpmGlobalBin,
385
+ pnpmGlobalRoot: options.pnpmGlobalRoot,
386
+ yarnGlobalDir: options.yarnGlobalDir,
387
+ });
388
+ }
267
389
  export async function inspectSelfStatus(options = {}) {
268
390
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
269
391
  const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
@@ -271,6 +393,8 @@ export async function inspectSelfStatus(options = {}) {
271
393
  const channel = options.channel && options.channel !== 'auto' ? options.channel : detectChannel(currentVersion);
272
394
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
273
395
  const { installMethod, globalPrefix } = await inspectSelfInstall({
396
+ currentBinPath: options.currentBinPath,
397
+ packageName,
274
398
  packageRoot,
275
399
  commandOutputFn,
276
400
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/cli",
3
- "version": "2.1.11-test.1",
3
+ "version": "2.1.11-test.10",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",