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

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,157 @@ 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');
150
+ }
151
+ function readSiblingPnpmGlobalNodeModulesRoots(pnpmGlobalRoot) {
152
+ const globalNodeModulesDir = normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot);
153
+ const globalProjectDir = path.dirname(globalNodeModulesDir);
154
+ let entries;
155
+ try {
156
+ entries = fs.readdirSync(globalProjectDir);
157
+ }
158
+ catch {
159
+ return [];
160
+ }
161
+ return entries
162
+ .filter((entry) => {
163
+ if (entry === path.basename(globalNodeModulesDir)) {
164
+ return false;
165
+ }
166
+ try {
167
+ return fs.statSync(path.join(globalProjectDir, entry)).isDirectory();
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ })
173
+ .map((entry) => path.join(globalProjectDir, entry, 'node_modules'));
174
+ }
175
+ function readPnpmGlobalNodeModulesRoots(pnpmGlobalRoot) {
176
+ return [normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot), ...readSiblingPnpmGlobalNodeModulesRoots(pnpmGlobalRoot)];
177
+ }
178
+ function packageNameToPath(packageName) {
179
+ return packageName.split('/').filter(Boolean);
180
+ }
181
+ function isSameRealPath(left, right) {
182
+ try {
183
+ return normalizePath(fs.realpathSync(left)) === normalizePath(fs.realpathSync(right));
184
+ }
185
+ catch {
186
+ return false;
187
+ }
188
+ }
189
+ function isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot) {
190
+ if (isSubPath(pnpmGlobalRoot, packageRoot)) {
191
+ return true;
192
+ }
193
+ const globalNodeModulesDir = normalizePnpmGlobalNodeModulesRoot(pnpmGlobalRoot);
194
+ const globalProjectDir = path.dirname(globalNodeModulesDir);
195
+ if (!isSubPath(globalProjectDir, packageRoot)) {
196
+ return false;
197
+ }
198
+ const segments = normalizePath(packageRoot).split(path.sep).filter(Boolean);
199
+ const pnpmStoreIndex = segments.indexOf('.pnpm');
200
+ if (pnpmStoreIndex === -1) {
201
+ return false;
202
+ }
203
+ return segments.slice(pnpmStoreIndex + 1).includes('node_modules');
141
204
  }
142
- function getInstallMethodCacheKey(packageRoot) {
143
- return normalizePath(path.join(packageRoot, 'bin', 'run.js'));
205
+ function isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName) {
206
+ return readPnpmGlobalNodeModulesRoots(pnpmGlobalRoot).some((pnpmGlobalNodeModulesRoot) => isSameRealPath(path.join(pnpmGlobalNodeModulesRoot, ...packageNameToPath(packageName)), packageRoot));
144
207
  }
145
- async function readInstallMethodCache() {
208
+ function isPnpmGlobalBinProjectPath(pnpmGlobalBin, packageRoot, packageName) {
209
+ const pnpmHome = path.dirname(normalizePath(pnpmGlobalBin));
210
+ const globalDir = path.join(pnpmHome, 'global');
211
+ if (isSubPath(globalDir, packageRoot)) {
212
+ return true;
213
+ }
214
+ let entries;
146
215
  try {
147
- const raw = await fsp.readFile(getInstallMethodCacheFile(), 'utf8');
148
- return JSON.parse(raw);
216
+ entries = fs.readdirSync(globalDir);
149
217
  }
150
218
  catch {
151
- return {};
219
+ return false;
152
220
  }
221
+ return entries.some((entry) => {
222
+ const pnpmGlobalRoot = path.join(globalDir, entry);
223
+ return (isPnpmGlobalRootPath(pnpmGlobalRoot, packageRoot)
224
+ || isPnpmGlobalPackagePath(pnpmGlobalRoot, packageRoot, packageName));
225
+ });
153
226
  }
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));
227
+ function isPnpmGlobalInstallPath(options) {
228
+ if (options.pnpmGlobalRoot) {
229
+ const matchedRoot = isPnpmGlobalRootPath(options.pnpmGlobalRoot, options.packageRoot)
230
+ || isPnpmGlobalPackagePath(options.pnpmGlobalRoot, options.packageRoot, options.packageName);
231
+ if (matchedRoot) {
232
+ return true;
233
+ }
234
+ }
235
+ if (options.pnpmGlobalBin) {
236
+ return isPnpmGlobalBinProjectPath(options.pnpmGlobalBin, options.packageRoot, options.packageName);
237
+ }
238
+ return false;
158
239
  }
159
- async function readCachedInstallMethod(packageRoot) {
160
- const state = await readInstallMethodCache();
161
- return state.entries?.[getInstallMethodCacheKey(packageRoot)];
240
+ function readCurrentBinPath(currentBinPath) {
241
+ const candidate = String(currentBinPath ?? process.argv[1] ?? '').trim();
242
+ if (!candidate) {
243
+ return undefined;
244
+ }
245
+ return normalizePath(candidate);
162
246
  }
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 });
247
+ function cleanCommandPath(value) {
248
+ const trimmed = value.trim();
249
+ return trimmed ? trimmed : undefined;
250
+ }
251
+ function resolvePackageManagerProbeCwd() {
252
+ const candidates = [os.homedir(), os.tmpdir(), path.parse(process.cwd()).root];
253
+ for (const candidate of candidates) {
254
+ if (!candidate) {
255
+ continue;
256
+ }
257
+ try {
258
+ if (fs.statSync(candidate).isDirectory()) {
259
+ return candidate;
260
+ }
261
+ }
262
+ catch {
263
+ // Ignore invalid probe cwd candidates and continue to the next one.
264
+ }
265
+ }
266
+ return undefined;
170
267
  }
171
268
  async function readGlobalPrefix(commandOutputFn) {
172
269
  try {
173
- return (await commandOutputFn('npm', ['prefix', '-g'], {
270
+ return cleanCommandPath(await commandOutputFn('npm', ['prefix', '-g'], {
271
+ cwd: resolvePackageManagerProbeCwd(),
174
272
  errorName: 'npm prefix',
175
- })).trim();
273
+ }));
274
+ }
275
+ catch {
276
+ return undefined;
277
+ }
278
+ }
279
+ async function readPnpmGlobalBin(commandOutputFn) {
280
+ try {
281
+ return cleanCommandPath(await commandOutputFn('pnpm', ['bin', '-g'], {
282
+ cwd: resolvePackageManagerProbeCwd(),
283
+ errorName: 'pnpm bin',
284
+ }));
285
+ }
286
+ catch {
287
+ return undefined;
288
+ }
289
+ }
290
+ async function readPnpmGlobalRoot(commandOutputFn) {
291
+ try {
292
+ return cleanCommandPath(await commandOutputFn('pnpm', ['root', '-g'], {
293
+ cwd: resolvePackageManagerProbeCwd(),
294
+ errorName: 'pnpm root',
295
+ }));
176
296
  }
177
297
  catch {
178
298
  return undefined;
@@ -180,9 +300,21 @@ async function readGlobalPrefix(commandOutputFn) {
180
300
  }
181
301
  async function readYarnGlobalDir(commandOutputFn) {
182
302
  try {
183
- return (await commandOutputFn('yarn', ['global', 'dir'], {
303
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'dir'], {
304
+ cwd: resolvePackageManagerProbeCwd(),
184
305
  errorName: 'yarn global dir',
185
- })).trim();
306
+ }));
307
+ }
308
+ catch {
309
+ return undefined;
310
+ }
311
+ }
312
+ async function readYarnGlobalBin(commandOutputFn) {
313
+ try {
314
+ return cleanCommandPath(await commandOutputFn('yarn', ['global', 'bin'], {
315
+ cwd: resolvePackageManagerProbeCwd(),
316
+ errorName: 'yarn global bin',
317
+ }));
186
318
  }
187
319
  catch {
188
320
  return undefined;
@@ -242,28 +374,45 @@ export function getSelfUpdatePackageSpec(status) {
242
374
  }
243
375
  export async function inspectSelfInstall(options = {}) {
244
376
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
377
+ const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
245
378
  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
- }
379
+ const currentBinPath = readCurrentBinPath(options.currentBinPath);
380
+ const globalPrefix = await readGlobalPrefix(commandOutputFn);
381
+ const pnpmGlobalBin = await readPnpmGlobalBin(commandOutputFn);
382
+ const pnpmGlobalRoot = await readPnpmGlobalRoot(commandOutputFn);
383
+ const yarnGlobalDir = await readYarnGlobalDir(commandOutputFn);
384
+ const yarnGlobalBin = await readYarnGlobalBin(commandOutputFn);
385
+ const installMethod = detectInstallMethodFromSignals({
386
+ packageRoot,
387
+ currentBinPath,
388
+ globalPrefix,
389
+ packageName,
390
+ pnpmGlobalBin,
391
+ pnpmGlobalRoot,
392
+ yarnGlobalBin,
393
+ yarnGlobalDir,
394
+ });
261
395
  return {
262
396
  packageRoot,
263
397
  installMethod,
264
398
  globalPrefix,
265
399
  };
266
400
  }
401
+ function detectInstallMethodFromSignals(options) {
402
+ if (options.currentBinPath && options.yarnGlobalBin && isSubPath(options.yarnGlobalBin, options.currentBinPath)) {
403
+ return 'yarn-global';
404
+ }
405
+ if (options.currentBinPath && options.pnpmGlobalBin && isSubPath(options.pnpmGlobalBin, options.currentBinPath)) {
406
+ return 'pnpm-global';
407
+ }
408
+ return detectInstallMethod(options.packageRoot, {
409
+ globalPrefix: options.globalPrefix,
410
+ packageName: options.packageName,
411
+ pnpmGlobalBin: options.pnpmGlobalBin,
412
+ pnpmGlobalRoot: options.pnpmGlobalRoot,
413
+ yarnGlobalDir: options.yarnGlobalDir,
414
+ });
415
+ }
267
416
  export async function inspectSelfStatus(options = {}) {
268
417
  const packageRoot = options.packageRoot ? normalizePath(options.packageRoot) : PACKAGE_ROOT;
269
418
  const packageName = options.packageName ?? DEFAULT_PACKAGE_NAME;
@@ -271,6 +420,8 @@ export async function inspectSelfStatus(options = {}) {
271
420
  const channel = options.channel && options.channel !== 'auto' ? options.channel : detectChannel(currentVersion);
272
421
  const commandOutputFn = options.commandOutputFn ?? commandOutput;
273
422
  const { installMethod, globalPrefix } = await inspectSelfInstall({
423
+ currentBinPath: options.currentBinPath,
424
+ packageName,
274
425
  packageRoot,
275
426
  commandOutputFn,
276
427
  });
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.11",
4
4
  "description": "NocoBase Command Line Tool",
5
5
  "type": "module",
6
6
  "main": "dist/generated/command-registry.js",