rn-update-cli 1.0.0
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/LICENSE +21 -0
- package/README.md +167 -0
- package/README.zh-CN.md +167 -0
- package/cli.json +525 -0
- package/lib/api.d.ts +56 -0
- package/lib/api.js +671 -0
- package/lib/app.d.ts +107 -0
- package/lib/app.js +329 -0
- package/lib/auto-update.d.ts +35 -0
- package/lib/auto-update.js +507 -0
- package/lib/bin.d.ts +2 -0
- package/lib/bin.js +133 -0
- package/lib/bundle-pack.d.ts +6 -0
- package/lib/bundle-pack.js +91 -0
- package/lib/bundle-runner.d.ts +168 -0
- package/lib/bundle-runner.js +1305 -0
- package/lib/bundle.d.ts +45 -0
- package/lib/bundle.js +226 -0
- package/lib/cache.d.ts +10 -0
- package/lib/cache.js +25 -0
- package/lib/commands.d.ts +11 -0
- package/lib/commands.js +72 -0
- package/lib/diff.d.ts +18 -0
- package/lib/diff.js +690 -0
- package/lib/exports.d.ts +11 -0
- package/lib/exports.js +20 -0
- package/lib/install.d.ts +9 -0
- package/lib/install.js +51 -0
- package/lib/locales/en.d.ts +187 -0
- package/lib/locales/en.js +194 -0
- package/lib/locales/zh.d.ts +187 -0
- package/lib/locales/zh.js +194 -0
- package/lib/native-package.d.ts +21 -0
- package/lib/native-package.js +298 -0
- package/lib/package.d.ts +68 -0
- package/lib/package.js +487 -0
- package/lib/provider.d.ts +24 -0
- package/lib/provider.js +244 -0
- package/lib/standalone-publish.d.ts +17 -0
- package/lib/standalone-publish.js +433 -0
- package/lib/symbolicate.d.ts +35 -0
- package/lib/symbolicate.js +130 -0
- package/lib/types.d.ts +152 -0
- package/lib/types.js +2 -0
- package/lib/user.d.ts +8 -0
- package/lib/user.js +50 -0
- package/lib/utils/add-gitignore.d.ts +6 -0
- package/lib/utils/add-gitignore.js +44 -0
- package/lib/utils/app-info-parser/aab.d.ts +27 -0
- package/lib/utils/app-info-parser/aab.js +256 -0
- package/lib/utils/app-info-parser/apk.d.ts +19 -0
- package/lib/utils/app-info-parser/apk.js +95 -0
- package/lib/utils/app-info-parser/app.d.ts +9 -0
- package/lib/utils/app-info-parser/app.js +15 -0
- package/lib/utils/app-info-parser/index.d.ts +21 -0
- package/lib/utils/app-info-parser/index.js +51 -0
- package/lib/utils/app-info-parser/ipa.d.ts +19 -0
- package/lib/utils/app-info-parser/ipa.js +101 -0
- package/lib/utils/app-info-parser/resource-finder.d.ts +49 -0
- package/lib/utils/app-info-parser/resource-finder.js +429 -0
- package/lib/utils/app-info-parser/utils.d.ts +36 -0
- package/lib/utils/app-info-parser/utils.js +140 -0
- package/lib/utils/app-info-parser/xml-parser/binary.d.ts +61 -0
- package/lib/utils/app-info-parser/xml-parser/binary.js +577 -0
- package/lib/utils/app-info-parser/xml-parser/manifest.d.ts +15 -0
- package/lib/utils/app-info-parser/xml-parser/manifest.js +207 -0
- package/lib/utils/app-info-parser/zip.d.ts +29 -0
- package/lib/utils/app-info-parser/zip.js +97 -0
- package/lib/utils/check-lockfile.d.ts +6 -0
- package/lib/utils/check-lockfile.js +93 -0
- package/lib/utils/check-plugin.d.ts +12 -0
- package/lib/utils/check-plugin.js +36 -0
- package/lib/utils/command-result.d.ts +8 -0
- package/lib/utils/command-result.js +27 -0
- package/lib/utils/constants.d.ts +10 -0
- package/lib/utils/constants.js +39 -0
- package/lib/utils/dep-versions.d.ts +20 -0
- package/lib/utils/dep-versions.js +129 -0
- package/lib/utils/git.d.ts +14 -0
- package/lib/utils/git.js +64 -0
- package/lib/utils/hbcTransform.d.ts +111 -0
- package/lib/utils/hbcTransform.js +561 -0
- package/lib/utils/hermes-base.d.ts +227 -0
- package/lib/utils/hermes-base.js +1287 -0
- package/lib/utils/hermes-literals.d.ts +64 -0
- package/lib/utils/hermes-literals.js +260 -0
- package/lib/utils/http-helper.d.ts +10 -0
- package/lib/utils/http-helper.js +150 -0
- package/lib/utils/i18n.d.ts +22 -0
- package/lib/utils/i18n.js +46 -0
- package/lib/utils/index.d.ts +67 -0
- package/lib/utils/index.js +545 -0
- package/lib/utils/latest-version/index.d.ts +154 -0
- package/lib/utils/latest-version/index.js +331 -0
- package/lib/utils/options.d.ts +10 -0
- package/lib/utils/options.js +68 -0
- package/lib/utils/plugin-config.d.ts +14 -0
- package/lib/utils/plugin-config.js +66 -0
- package/lib/utils/runtime.d.ts +54 -0
- package/lib/utils/runtime.js +263 -0
- package/lib/utils/slim-sourcemap.d.ts +28 -0
- package/lib/utils/slim-sourcemap.js +106 -0
- package/lib/utils/zip-entries.d.ts +10 -0
- package/lib/utils/zip-entries.js +211 -0
- package/lib/utils/zip-options.d.ts +17 -0
- package/lib/utils/zip-options.js +190 -0
- package/lib/utils/zip-range.d.ts +148 -0
- package/lib/utils/zip-range.js +581 -0
- package/lib/utils/zip-raw-writer.d.ts +52 -0
- package/lib/utils/zip-raw-writer.js +209 -0
- package/lib/version-operations.d.ts +21 -0
- package/lib/version-operations.js +320 -0
- package/lib/versions.d.ts +66 -0
- package/lib/versions.js +402 -0
- package/package.json +113 -0
- package/proto/Configuration.proto +183 -0
- package/proto/Resources.proto +569 -0
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* [INPUT]: 依赖 Node 子进程、文件系统、环境变量、全局包管理器布局与本地化文案
|
|
4
|
+
* [OUTPUT]: 对外提供 PackageManager、AutoUpdateNotice、全局包管理器探测及后台更新编排
|
|
5
|
+
* [POS]: CLI 后台自更新边界,负责包管理器归属、锁与失败重试,不参与前台命令分发
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.detectGlobalPackageManager = detectGlobalPackageManager;
|
|
12
|
+
exports.buildUpdateCommand = buildUpdateCommand;
|
|
13
|
+
exports.launchAutoUpdate = launchAutoUpdate;
|
|
14
|
+
exports.consumeAutoUpdateNotice = consumeAutoUpdateNotice;
|
|
15
|
+
const child_process_1 = require("child_process");
|
|
16
|
+
const fs_1 = require("fs");
|
|
17
|
+
const os_1 = require("os");
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const PACKAGE_NAME = 'rn-update-cli';
|
|
20
|
+
const WORKER_ENV = 'RNU_AUTO_UPDATE_WORKER';
|
|
21
|
+
const CANDIDATE_ENV = 'RNU_AUTO_UPDATE_CANDIDATE';
|
|
22
|
+
const LOCK_STALE_MS = 10 * 60 * 1000;
|
|
23
|
+
const UPDATE_TIMEOUT_MS = 2 * 60 * 1000;
|
|
24
|
+
const NETWORK_RETRY_MS = 6 * 60 * 60 * 1000;
|
|
25
|
+
const FAILURE_RETRY_MS = 24 * 60 * 60 * 1000;
|
|
26
|
+
const PERMISSION_RETRY_MS = 7 * 24 * 60 * 60 * 1000;
|
|
27
|
+
const OUTPUT_LIMIT = 32 * 1024;
|
|
28
|
+
function cacheDir() {
|
|
29
|
+
if (process.env.XDG_CACHE_HOME) {
|
|
30
|
+
return path_1.default.join(process.env.XDG_CACHE_HOME, PACKAGE_NAME);
|
|
31
|
+
}
|
|
32
|
+
switch (process.platform) {
|
|
33
|
+
case 'darwin':
|
|
34
|
+
return path_1.default.join((0, os_1.homedir)(), 'Library', 'Caches', PACKAGE_NAME);
|
|
35
|
+
case 'win32':
|
|
36
|
+
return path_1.default.join(process.env.LOCALAPPDATA ?? path_1.default.join((0, os_1.homedir)(), 'AppData', 'Local'), PACKAGE_NAME, 'Cache');
|
|
37
|
+
default:
|
|
38
|
+
return path_1.default.join((0, os_1.homedir)(), '.cache', PACKAGE_NAME);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function stateFile() {
|
|
42
|
+
return path_1.default.join(cacheDir(), 'auto-update.json');
|
|
43
|
+
}
|
|
44
|
+
function lockDir() {
|
|
45
|
+
return path_1.default.join(cacheDir(), 'auto-update.lock');
|
|
46
|
+
}
|
|
47
|
+
function readState() {
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse((0, fs_1.readFileSync)(stateFile(), 'utf8'));
|
|
50
|
+
if (!parsed ||
|
|
51
|
+
!['running', 'updated', 'failed'].includes(parsed.status) ||
|
|
52
|
+
typeof parsed.targetVersion !== 'string' ||
|
|
53
|
+
typeof parsed.attemptedAt !== 'number') {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function writeState(state) {
|
|
63
|
+
try {
|
|
64
|
+
(0, fs_1.mkdirSync)(cacheDir(), { recursive: true });
|
|
65
|
+
const temporary = `${stateFile()}.${process.pid}.tmp`;
|
|
66
|
+
(0, fs_1.writeFileSync)(temporary, JSON.stringify(state));
|
|
67
|
+
(0, fs_1.renameSync)(temporary, stateFile());
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// A read-only cache must never interfere with the CLI or the update.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function normalizePath(value) {
|
|
74
|
+
let resolved = path_1.default.resolve(value);
|
|
75
|
+
try {
|
|
76
|
+
resolved = fs_1.realpathSync.native(resolved);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// The caller may supply a path that was replaced during an update.
|
|
80
|
+
}
|
|
81
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
82
|
+
}
|
|
83
|
+
function isWithin(child, parent) {
|
|
84
|
+
const relative = path_1.default.relative(normalizePath(parent), normalizePath(child));
|
|
85
|
+
return (relative === '' ||
|
|
86
|
+
(!relative.startsWith('..') && !path_1.default.isAbsolute(relative)));
|
|
87
|
+
}
|
|
88
|
+
function managerFromUserAgent(env) {
|
|
89
|
+
const value = `${env.npm_config_user_agent ?? ''} ${env.npm_execpath ?? ''}`.toLowerCase();
|
|
90
|
+
if (/\bbun(?:\/|\s|$)/.test(value))
|
|
91
|
+
return 'bun';
|
|
92
|
+
if (/\bpnpm(?:\/|\s|$)/.test(value))
|
|
93
|
+
return 'pnpm';
|
|
94
|
+
if (/\byarn(?:\/|\s|$)/.test(value))
|
|
95
|
+
return 'yarn';
|
|
96
|
+
if (/\bnpm(?:\/|\s|$)/.test(value))
|
|
97
|
+
return 'npm';
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
function parseManager(value) {
|
|
101
|
+
const manager = value?.trim().toLowerCase();
|
|
102
|
+
return manager === 'npm' ||
|
|
103
|
+
manager === 'pnpm' ||
|
|
104
|
+
manager === 'yarn' ||
|
|
105
|
+
manager === 'bun'
|
|
106
|
+
? manager
|
|
107
|
+
: undefined;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Identify the package manager that owns this global installation. Project
|
|
111
|
+
* lockfiles are deliberately ignored: they say how the app is installed, not
|
|
112
|
+
* how this CLI was installed.
|
|
113
|
+
*/
|
|
114
|
+
function detectGlobalPackageManager({ packageRoot, env = process.env, npmGlobalDir, yarnGlobalDir, }) {
|
|
115
|
+
const root = normalizePath(packageRoot);
|
|
116
|
+
const normalized = root.replaceAll('\\', '/').toLowerCase();
|
|
117
|
+
if (yarnGlobalDir && isWithin(root, yarnGlobalDir))
|
|
118
|
+
return 'yarn';
|
|
119
|
+
if (npmGlobalDir && isWithin(root, npmGlobalDir))
|
|
120
|
+
return 'npm';
|
|
121
|
+
if (/\/(?:\.bun|bun)\/install\/global\/node_modules\//.test(normalized)) {
|
|
122
|
+
return 'bun';
|
|
123
|
+
}
|
|
124
|
+
if (/\/pnpm\/global\/[^/]+\//.test(normalized))
|
|
125
|
+
return 'pnpm';
|
|
126
|
+
if (/\/(?:\.config\/yarn\/global|yarn\/data\/global)\/node_modules\//.test(normalized)) {
|
|
127
|
+
return 'yarn';
|
|
128
|
+
}
|
|
129
|
+
// Custom npm prefixes conventionally use <prefix>/lib/node_modules on
|
|
130
|
+
// Unix and <prefix>/node_modules on Windows. Only accept the generic form
|
|
131
|
+
// when the package is outside the current project, so npx and local installs
|
|
132
|
+
// are never self-modified.
|
|
133
|
+
const cwdNodeModules = path_1.default.join(normalizePath(process.cwd()), 'node_modules');
|
|
134
|
+
const configuredPrefixModules = env.npm_config_prefix
|
|
135
|
+
? path_1.default.join(normalizePath(env.npm_config_prefix), 'node_modules')
|
|
136
|
+
: undefined;
|
|
137
|
+
const looksGlobal = normalized.includes('/lib/node_modules/') ||
|
|
138
|
+
(process.platform === 'win32' &&
|
|
139
|
+
(normalized.includes('/npm/node_modules/') ||
|
|
140
|
+
(configuredPrefixModules && isWithin(root, configuredPrefixModules))));
|
|
141
|
+
if (looksGlobal && !isWithin(root, cwdNodeModules)) {
|
|
142
|
+
return (parseManager(env.RNU_AUTO_UPDATE_PACKAGE_MANAGER) ??
|
|
143
|
+
managerFromUserAgent(env) ??
|
|
144
|
+
'npm');
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
function globalDirectories() {
|
|
149
|
+
try {
|
|
150
|
+
const dirs = require('global-dirs');
|
|
151
|
+
return {
|
|
152
|
+
npmGlobalDir: dirs.npm?.packages,
|
|
153
|
+
npmPrefix: dirs.npm?.prefix,
|
|
154
|
+
yarnGlobalDir: dirs.yarn?.packages,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return {};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function sanitizeRegistryUrl(value) {
|
|
162
|
+
try {
|
|
163
|
+
const registry = new URL(value);
|
|
164
|
+
if (registry.protocol !== 'http:' && registry.protocol !== 'https:') {
|
|
165
|
+
return 'https://registry.npmjs.org/';
|
|
166
|
+
}
|
|
167
|
+
// Credentials belong in the user's package-manager configuration, not in
|
|
168
|
+
// a process argument or our state file.
|
|
169
|
+
registry.username = '';
|
|
170
|
+
registry.password = '';
|
|
171
|
+
return registry.toString();
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return 'https://registry.npmjs.org/';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function buildUpdateCommand(manager, targetVersion, registryUrl, installPrefix) {
|
|
178
|
+
const target = `${PACKAGE_NAME}@${targetVersion}`;
|
|
179
|
+
const registry = sanitizeRegistryUrl(registryUrl);
|
|
180
|
+
// Windows package managers are .cmd shims and therefore run through
|
|
181
|
+
// cmd.exe. Keep the URL out of that command line (it may contain shell
|
|
182
|
+
// metacharacters); runPackageManager supplies the same value through the
|
|
183
|
+
// environment instead.
|
|
184
|
+
const registryArgs = process.platform === 'win32' ? [] : [`--registry=${registry}`];
|
|
185
|
+
switch (manager) {
|
|
186
|
+
case 'pnpm':
|
|
187
|
+
return {
|
|
188
|
+
command: process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm',
|
|
189
|
+
args: ['add', '--global', target, '--reporter=silent', ...registryArgs],
|
|
190
|
+
};
|
|
191
|
+
case 'yarn':
|
|
192
|
+
return {
|
|
193
|
+
command: process.platform === 'win32' ? 'yarn.cmd' : 'yarn',
|
|
194
|
+
args: [
|
|
195
|
+
'global',
|
|
196
|
+
'add',
|
|
197
|
+
target,
|
|
198
|
+
'--non-interactive',
|
|
199
|
+
'--silent',
|
|
200
|
+
...registryArgs,
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
case 'bun':
|
|
204
|
+
return {
|
|
205
|
+
command: process.platform === 'win32' ? 'bun.exe' : 'bun',
|
|
206
|
+
args: ['add', '--global', target, '--no-progress', ...registryArgs],
|
|
207
|
+
};
|
|
208
|
+
default:
|
|
209
|
+
return {
|
|
210
|
+
command: process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
|
211
|
+
args: [
|
|
212
|
+
'install',
|
|
213
|
+
'--global',
|
|
214
|
+
target,
|
|
215
|
+
...(installPrefix && process.platform !== 'win32'
|
|
216
|
+
? [`--prefix=${installPrefix}`]
|
|
217
|
+
: []),
|
|
218
|
+
'--no-audit',
|
|
219
|
+
'--no-fund',
|
|
220
|
+
'--loglevel=error',
|
|
221
|
+
...registryArgs,
|
|
222
|
+
],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function inferNpmPrefix(packageRoot, npmGlobalDir, npmPrefix) {
|
|
227
|
+
if (npmGlobalDir && npmPrefix && isWithin(packageRoot, npmGlobalDir)) {
|
|
228
|
+
return normalizePath(npmPrefix);
|
|
229
|
+
}
|
|
230
|
+
const modules = path_1.default.dirname(normalizePath(packageRoot));
|
|
231
|
+
if (path_1.default.basename(modules).toLowerCase() !== 'node_modules') {
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
const modulesParent = path_1.default.dirname(modules);
|
|
235
|
+
return path_1.default.basename(modulesParent).toLowerCase() === 'lib'
|
|
236
|
+
? path_1.default.dirname(modulesParent)
|
|
237
|
+
: modulesParent;
|
|
238
|
+
}
|
|
239
|
+
function isDisabled(env) {
|
|
240
|
+
const disabled = (value) => value === '0' || value?.toLowerCase() === 'false';
|
|
241
|
+
const enabled = (value) => value === '1' || value?.toLowerCase() === 'true';
|
|
242
|
+
return (disabled(env.RNU_AUTO_UPDATE) ||
|
|
243
|
+
enabled(env.RNU_DISABLE_AUTO_UPDATE) ||
|
|
244
|
+
enabled(env.NO_UPDATE_NOTIFIER) ||
|
|
245
|
+
enabled(env.CI));
|
|
246
|
+
}
|
|
247
|
+
function shouldRetry(state, target) {
|
|
248
|
+
if (!state || state.targetVersion !== target)
|
|
249
|
+
return true;
|
|
250
|
+
if (state.status === 'updated')
|
|
251
|
+
return false;
|
|
252
|
+
if (state.status === 'running') {
|
|
253
|
+
return Date.now() - state.attemptedAt >= LOCK_STALE_MS;
|
|
254
|
+
}
|
|
255
|
+
return !state.retryAt || state.retryAt <= Date.now();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Start a detached updater after the foreground command has completed. This
|
|
259
|
+
* function is synchronous on purpose: it is safe to call from process.exit.
|
|
260
|
+
*/
|
|
261
|
+
function launchAutoUpdate(currentVersion, targetVersion, env = process.env) {
|
|
262
|
+
if (!targetVersion ||
|
|
263
|
+
targetVersion === currentVersion ||
|
|
264
|
+
isDisabled(env) ||
|
|
265
|
+
!shouldRetry(readState(), targetVersion)) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
const packageRoot = path_1.default.resolve(__dirname, '..');
|
|
269
|
+
const directories = globalDirectories();
|
|
270
|
+
const detected = detectGlobalPackageManager({
|
|
271
|
+
packageRoot,
|
|
272
|
+
env,
|
|
273
|
+
npmGlobalDir: directories.npmGlobalDir,
|
|
274
|
+
yarnGlobalDir: directories.yarnGlobalDir,
|
|
275
|
+
});
|
|
276
|
+
if (!detected)
|
|
277
|
+
return false;
|
|
278
|
+
const packageManager = parseManager(env.RNU_AUTO_UPDATE_PACKAGE_MANAGER) ?? detected;
|
|
279
|
+
let registryUrl = 'https://registry.npmjs.org/';
|
|
280
|
+
try {
|
|
281
|
+
const registryModule = require('registry-auth-token/registry-url');
|
|
282
|
+
const getRegistryUrl = (registryModule.default ?? registryModule);
|
|
283
|
+
registryUrl = getRegistryUrl('');
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// Keep the public npm registry fallback when config parsing fails.
|
|
287
|
+
}
|
|
288
|
+
const candidate = {
|
|
289
|
+
currentVersion,
|
|
290
|
+
targetVersion,
|
|
291
|
+
packageRoot: normalizePath(packageRoot),
|
|
292
|
+
packageManager,
|
|
293
|
+
registryUrl: sanitizeRegistryUrl(registryUrl),
|
|
294
|
+
installPrefix: packageManager === 'npm' && detected === 'npm'
|
|
295
|
+
? inferNpmPrefix(packageRoot, directories.npmGlobalDir, directories.npmPrefix)
|
|
296
|
+
: undefined,
|
|
297
|
+
};
|
|
298
|
+
try {
|
|
299
|
+
const worker = (0, child_process_1.spawn)(process.execPath, [__filename], {
|
|
300
|
+
detached: true,
|
|
301
|
+
env: {
|
|
302
|
+
...env,
|
|
303
|
+
[WORKER_ENV]: '1',
|
|
304
|
+
[CANDIDATE_ENV]: JSON.stringify(candidate),
|
|
305
|
+
},
|
|
306
|
+
stdio: 'ignore',
|
|
307
|
+
windowsHide: true,
|
|
308
|
+
});
|
|
309
|
+
worker.once('error', () => { });
|
|
310
|
+
worker.unref();
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function acquireLock() {
|
|
318
|
+
try {
|
|
319
|
+
(0, fs_1.mkdirSync)(cacheDir(), { recursive: true });
|
|
320
|
+
try {
|
|
321
|
+
(0, fs_1.mkdirSync)(lockDir());
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
if ((0, fs_1.existsSync)(lockDir()) &&
|
|
326
|
+
Date.now() - (0, fs_1.statSync)(lockDir()).mtimeMs >= LOCK_STALE_MS) {
|
|
327
|
+
(0, fs_1.rmSync)(lockDir(), { force: true, recursive: true });
|
|
328
|
+
(0, fs_1.mkdirSync)(lockDir());
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function appendBounded(current, chunk) {
|
|
339
|
+
const combined = `${current}${chunk.toString()}`;
|
|
340
|
+
return combined.length > OUTPUT_LIMIT
|
|
341
|
+
? combined.slice(combined.length - OUTPUT_LIMIT)
|
|
342
|
+
: combined;
|
|
343
|
+
}
|
|
344
|
+
function classifyFailure(error) {
|
|
345
|
+
if (/eacces|eperm|permission denied|access is denied/i.test(error)) {
|
|
346
|
+
return 'permission';
|
|
347
|
+
}
|
|
348
|
+
if (/econn|enet|eai_again|enotfound|timed?\s*out|timeout|certificate|socket|network|fetch/i.test(error)) {
|
|
349
|
+
return 'network';
|
|
350
|
+
}
|
|
351
|
+
if (/enoent|not recognized|command not found/i.test(error)) {
|
|
352
|
+
return 'package-manager';
|
|
353
|
+
}
|
|
354
|
+
return 'unknown';
|
|
355
|
+
}
|
|
356
|
+
function retryDelay(kind) {
|
|
357
|
+
if (kind === 'permission')
|
|
358
|
+
return PERMISSION_RETRY_MS;
|
|
359
|
+
if (kind === 'network')
|
|
360
|
+
return NETWORK_RETRY_MS;
|
|
361
|
+
return FAILURE_RETRY_MS;
|
|
362
|
+
}
|
|
363
|
+
function compactMessage(value) {
|
|
364
|
+
return value
|
|
365
|
+
.split(String.fromCharCode(27))
|
|
366
|
+
.map((part, index) => index === 0 ? part : part.replace(/^\[[0-9;]*m/, ''))
|
|
367
|
+
.join('')
|
|
368
|
+
.trim()
|
|
369
|
+
.slice(-2000);
|
|
370
|
+
}
|
|
371
|
+
async function runPackageManager(command, args, registryUrl, installPrefix) {
|
|
372
|
+
return new Promise((resolve) => {
|
|
373
|
+
let output = '';
|
|
374
|
+
let settled = false;
|
|
375
|
+
const child = (0, child_process_1.spawn)(command, args, {
|
|
376
|
+
env: {
|
|
377
|
+
...process.env,
|
|
378
|
+
npm_config_audit: 'false',
|
|
379
|
+
npm_config_fund: 'false',
|
|
380
|
+
...(installPrefix
|
|
381
|
+
? {
|
|
382
|
+
NPM_CONFIG_PREFIX: installPrefix,
|
|
383
|
+
npm_config_prefix: installPrefix,
|
|
384
|
+
}
|
|
385
|
+
: {}),
|
|
386
|
+
npm_config_registry: sanitizeRegistryUrl(registryUrl),
|
|
387
|
+
npm_config_update_notifier: 'false',
|
|
388
|
+
},
|
|
389
|
+
shell: process.platform === 'win32' && command.endsWith('.cmd'),
|
|
390
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
391
|
+
windowsHide: true,
|
|
392
|
+
});
|
|
393
|
+
const finish = (code, extra = '') => {
|
|
394
|
+
if (settled)
|
|
395
|
+
return;
|
|
396
|
+
settled = true;
|
|
397
|
+
clearTimeout(timer);
|
|
398
|
+
resolve({ code, output: appendBounded(output, extra) });
|
|
399
|
+
};
|
|
400
|
+
child.stdout?.on('data', (chunk) => {
|
|
401
|
+
output = appendBounded(output, chunk);
|
|
402
|
+
});
|
|
403
|
+
child.stderr?.on('data', (chunk) => {
|
|
404
|
+
output = appendBounded(output, chunk);
|
|
405
|
+
});
|
|
406
|
+
child.once('error', (error) => finish(127, error.message));
|
|
407
|
+
child.once('close', (code, signal) => finish(code ?? 1, signal ? `\nTerminated by ${signal}` : ''));
|
|
408
|
+
const timer = setTimeout(() => {
|
|
409
|
+
child.kill('SIGTERM');
|
|
410
|
+
finish(124, '\nUpdate timed out');
|
|
411
|
+
}, UPDATE_TIMEOUT_MS);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async function runWorker(candidate) {
|
|
415
|
+
if (!acquireLock())
|
|
416
|
+
return;
|
|
417
|
+
const previous = readState();
|
|
418
|
+
const running = {
|
|
419
|
+
...candidate,
|
|
420
|
+
status: 'running',
|
|
421
|
+
attemptedAt: Date.now(),
|
|
422
|
+
failureCount: previous?.targetVersion === candidate.targetVersion
|
|
423
|
+
? (previous.failureCount ?? 0) + 1
|
|
424
|
+
: 1,
|
|
425
|
+
};
|
|
426
|
+
writeState(running);
|
|
427
|
+
try {
|
|
428
|
+
if (process.platform !== 'win32') {
|
|
429
|
+
(0, fs_1.accessSync)(path_1.default.dirname(candidate.packageRoot), fs_1.constants.W_OK);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
const failureKind = 'permission';
|
|
434
|
+
writeState({
|
|
435
|
+
...running,
|
|
436
|
+
status: 'failed',
|
|
437
|
+
finishedAt: Date.now(),
|
|
438
|
+
retryAt: Date.now() + retryDelay(failureKind),
|
|
439
|
+
failureKind,
|
|
440
|
+
message: compactMessage(String(error)),
|
|
441
|
+
});
|
|
442
|
+
(0, fs_1.rmSync)(lockDir(), { force: true, recursive: true });
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const update = buildUpdateCommand(candidate.packageManager, candidate.targetVersion, candidate.registryUrl, candidate.installPrefix);
|
|
446
|
+
const result = await runPackageManager(update.command, update.args, candidate.registryUrl, candidate.installPrefix);
|
|
447
|
+
if (result.code === 0) {
|
|
448
|
+
writeState({
|
|
449
|
+
...running,
|
|
450
|
+
status: 'updated',
|
|
451
|
+
finishedAt: Date.now(),
|
|
452
|
+
message: undefined,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
const detail = compactMessage(result.output);
|
|
457
|
+
const failureKind = classifyFailure(detail);
|
|
458
|
+
writeState({
|
|
459
|
+
...running,
|
|
460
|
+
status: 'failed',
|
|
461
|
+
finishedAt: Date.now(),
|
|
462
|
+
retryAt: Date.now() + retryDelay(failureKind),
|
|
463
|
+
failureKind,
|
|
464
|
+
message: detail,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
(0, fs_1.rmSync)(lockDir(), { force: true, recursive: true });
|
|
468
|
+
}
|
|
469
|
+
function consumeAutoUpdateNotice(currentVersion) {
|
|
470
|
+
const state = readState();
|
|
471
|
+
if (!state || state.reportedAt)
|
|
472
|
+
return undefined;
|
|
473
|
+
if (state.status === 'updated' && currentVersion === state.targetVersion) {
|
|
474
|
+
writeState({ ...state, reportedAt: Date.now() });
|
|
475
|
+
return {
|
|
476
|
+
kind: 'updated',
|
|
477
|
+
currentVersion: state.currentVersion,
|
|
478
|
+
targetVersion: state.targetVersion,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
if (state.status === 'failed' && state.failureKind === 'permission') {
|
|
482
|
+
writeState({ ...state, reportedAt: Date.now() });
|
|
483
|
+
const update = buildUpdateCommand(state.packageManager, state.targetVersion, state.registryUrl, state.installPrefix);
|
|
484
|
+
return {
|
|
485
|
+
kind: 'permission',
|
|
486
|
+
currentVersion,
|
|
487
|
+
targetVersion: state.targetVersion,
|
|
488
|
+
command: [update.command, ...update.args].join(' '),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
if (process.env[WORKER_ENV] === '1') {
|
|
494
|
+
try {
|
|
495
|
+
const candidate = JSON.parse(process.env[CANDIDATE_ENV] ?? '');
|
|
496
|
+
if (candidate?.packageRoot &&
|
|
497
|
+
candidate.targetVersion &&
|
|
498
|
+
parseManager(candidate.packageManager)) {
|
|
499
|
+
void runWorker(candidate).catch(() => {
|
|
500
|
+
(0, fs_1.rmSync)(lockDir(), { force: true, recursive: true });
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
// Malformed worker input is ignored; foreground CLI operation is over.
|
|
506
|
+
}
|
|
507
|
+
}
|
package/lib/bin.d.ts
ADDED
package/lib/bin.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
/**
|
|
8
|
+
* [INPUT]: 依赖 cli.json 命令声明、动态命令加载器、会话存储、版本提示与本地源码/编译产物时间戳
|
|
9
|
+
* [OUTPUT]: 对外提供 Pakta CLI 进程入口;统一解析参数、恢复会话、调度命令和输出错误,并阻止源码仓库运行过期 lib
|
|
10
|
+
* [POS]: CLI 最外层适配器,只负责进程生命周期与命令分发;业务发布、上传和网络协议下沉到 commands 与领域模块
|
|
11
|
+
*/
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const api_1 = require("./api");
|
|
15
|
+
const commands_1 = require("./commands");
|
|
16
|
+
const utils_1 = require("./utils");
|
|
17
|
+
const i18n_1 = require("./utils/i18n");
|
|
18
|
+
function isTruthyEnv(value) {
|
|
19
|
+
return value === '1' || value?.toLowerCase() === 'true';
|
|
20
|
+
}
|
|
21
|
+
function newestSourceMtime(directory) {
|
|
22
|
+
let newest = 0;
|
|
23
|
+
for (const entry of fs_1.default.readdirSync(directory, { withFileTypes: true })) {
|
|
24
|
+
const entryPath = path_1.default.join(directory, entry.name);
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
newest = Math.max(newest, newestSourceMtime(entryPath));
|
|
27
|
+
}
|
|
28
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
29
|
+
newest = Math.max(newest, fs_1.default.statSync(entryPath).mtimeMs);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return newest;
|
|
33
|
+
}
|
|
34
|
+
function assertFreshLocalBuild() {
|
|
35
|
+
const projectRoot = path_1.default.resolve(__dirname, '..');
|
|
36
|
+
const sourceRoot = path_1.default.join(projectRoot, 'src');
|
|
37
|
+
if (path_1.default.basename(__dirname) !== 'lib' || !fs_1.default.existsSync(sourceRoot)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (newestSourceMtime(sourceRoot) > fs_1.default.statSync(__filename).mtimeMs) {
|
|
41
|
+
throw new Error('CLI 编译产物已过期。请在 rn-update-cli 目录执行 npm run build 后重试。');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function printUsage(exitCode = 1) {
|
|
45
|
+
console.log('React Native Update CLI');
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('Commands:');
|
|
48
|
+
for (const name of commands_1.commandNames) {
|
|
49
|
+
console.log(` ${name}`);
|
|
50
|
+
}
|
|
51
|
+
console.log('');
|
|
52
|
+
console.log('Special commands:');
|
|
53
|
+
console.log(' list: List all available commands');
|
|
54
|
+
console.log(' help: Show this help message');
|
|
55
|
+
console.log('');
|
|
56
|
+
console.log('Visit `https://github.com/reactnativecn/react-native-update` for document.');
|
|
57
|
+
process.exit(exitCode);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Errors reaching the top level are almost always about the user's input,
|
|
61
|
+
* project or network: print their message. The stack trace, which only helps
|
|
62
|
+
* when the CLI itself is at fault, is printed on request (RNU_DEBUG=1).
|
|
63
|
+
*/
|
|
64
|
+
function reportError(err) {
|
|
65
|
+
if (isTruthyEnv(process.env.RNU_DEBUG)) {
|
|
66
|
+
console.error(err instanceof Error ? err.stack : err);
|
|
67
|
+
// fetch failures wrap the real reason (ECONNREFUSED, TLS, ...) as `cause`
|
|
68
|
+
for (let cause = err?.cause; cause; cause = cause?.cause) {
|
|
69
|
+
console.error('Caused by:', cause);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
74
|
+
console.error((0, i18n_1.t)('errorStackHint'));
|
|
75
|
+
}
|
|
76
|
+
async function run() {
|
|
77
|
+
try {
|
|
78
|
+
assertFreshLocalBuild();
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
reportError(err);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const versionOnly = ['-v', '--version', 'version'].includes(process.argv[2] ?? '');
|
|
85
|
+
// The registry check for newer versions runs alongside the command (from a
|
|
86
|
+
// 1-day cache when possible) and only ever delays `-v`/`version` itself; its
|
|
87
|
+
// hint is printed once the command is done, or at exit for commands that
|
|
88
|
+
// exit on their own.
|
|
89
|
+
const versionCheck = await (0, utils_1.printVersionCommand)({ wait: versionOnly });
|
|
90
|
+
if (versionOnly) {
|
|
91
|
+
versionCheck.startAutoUpdate();
|
|
92
|
+
process.exit();
|
|
93
|
+
}
|
|
94
|
+
process.on('exit', (code) => {
|
|
95
|
+
if (code === 0) {
|
|
96
|
+
versionCheck.printHints();
|
|
97
|
+
versionCheck.startAutoUpdate();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
// inside the try: an unknown command or option is reported like any other
|
|
102
|
+
// error instead of crashing with an unhandled rejection
|
|
103
|
+
const argv = require('cli-arguments').parse(require('../cli.json'));
|
|
104
|
+
global.NO_INTERACTIVE =
|
|
105
|
+
Boolean(argv.options['no-interactive']) ||
|
|
106
|
+
isTruthyEnv(process.env.NO_INTERACTIVE);
|
|
107
|
+
global.USE_ACC_OSS =
|
|
108
|
+
Boolean(argv.options.acc) || isTruthyEnv(process.env.USE_ACC_OSS);
|
|
109
|
+
await (0, api_1.loadSession)();
|
|
110
|
+
if (argv.command === 'help' || argv.command === 'list') {
|
|
111
|
+
printUsage(0);
|
|
112
|
+
}
|
|
113
|
+
const handler = (0, commands_1.loadCommandHandler)(argv.command);
|
|
114
|
+
if (!handler) {
|
|
115
|
+
throw new Error((0, i18n_1.t)('unknownCommand', { command: argv.command }));
|
|
116
|
+
}
|
|
117
|
+
await handler(argv);
|
|
118
|
+
// a check still in flight (cold cache) gets a short grace period; the
|
|
119
|
+
// registry request itself is unref'd, so exiting never waits on it
|
|
120
|
+
await versionCheck.settle(500);
|
|
121
|
+
versionCheck.printHints();
|
|
122
|
+
versionCheck.startAutoUpdate();
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (err?.status === 401) {
|
|
126
|
+
console.log((0, i18n_1.t)('loginFirst'));
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
reportError(err);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
run();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖文件系统、路径、yazl ZIP 写入器、本地化文案与 ZIP 条目压缩策略
|
|
3
|
+
* [OUTPUT]: 对外提供 packBundle,将 bundle 目录打包为 CLI 发布制品
|
|
4
|
+
* [POS]: CLI PPK 制品打包器,只决定条目边界和压缩方式,不负责 bundle 生成与上传
|
|
5
|
+
*/
|
|
6
|
+
export declare function packBundle(dir: string, output: string, bundleName?: string): Promise<void>;
|