1
0
mirror of https://gitcode.com/github-mirrors/react-native-update-cli.git synced 2025-11-23 00:36:11 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

...

10 Commits

Author SHA1 Message Date
sunnylqm
78159f362b Update version to 2.2.2 in package.json and refactor argument handling in runReactNativeBundleCommand for better clarity and efficiency. 2025-10-24 22:04:24 +08:00
sunnylqm
b76440d018 Update version to 2.2.1 in package.json and refactor argument handling in runReactNativeBundleCommand for improved clarity and efficiency. 2025-10-24 20:41:46 +08:00
Sunny Luo
e3c951bc1b Update package.json 2025-09-23 15:37:35 +08:00
波仔糕
f6ed8872bd add logic to support android aab package hot update (#17)
* add logic to support android aab package hot update

* update

* udpate
2025-09-23 15:22:34 +08:00
sunnylqm
fd46bafb98 Update version to 2.1.3 in package.json; set baseUrl in .swcrc; refactor API query logic to use dynamic base URL and update defaultEndpoints in constants.ts 2025-09-14 17:25:35 +08:00
sunny.ll
917f99f2ab Fix package selection logic to match by package name instead of version; improve error handling for package retrieval. 2025-09-10 17:11:12 +08:00
sunny.ll
ac13464d4d Enhance package command options to include packageId and packageVersion; improve package selection logic with error handling for missing packages. 2025-09-10 16:18:34 +08:00
sunny.ll
f82bab887a Remove console log for module registration in module-manager.ts 2025-09-10 15:38:56 +08:00
sunny.ll
07ff19fbe5 Update version to 2.1.1 in package.json and improve error handling for package loading in dep-versions.ts 2025-09-10 11:05:24 +08:00
sunny.ll
a4467717bc Add deletePackage command with confirmation and error handling; update version to 2.1.0 and enhance localization for delete operations 2025-09-09 12:34:06 +08:00
13 changed files with 213 additions and 65 deletions

1
.swcrc
View File

@@ -1,5 +1,6 @@
{
"jsc": {
"baseUrl": "./src",
"loose": true,
"target": "es2018",
"parser": {

View File

@@ -62,6 +62,19 @@
}
}
},
"deletePackage": {
"options": {
"appId": {
"hasValue": true
},
"packageVersion": {
"hasValue": true
},
"packageId": {
"hasValue": true
}
}
},
"publish": {
"options": {
"platform": {

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-update-cli",
"version": "2.0.1",
"version": "2.2.2",
"description": "command line tool for react-native-update (remote updates for react native)",
"main": "index.js",
"bin": {

View File

@@ -10,18 +10,16 @@ import packageJson from '../package.json';
import type { Package, Session } from './types';
import {
credentialFile,
defaultEndpoint,
pricingPageUrl,
} from './utils/constants';
import { t } from './utils/i18n';
import { getBaseUrl } from 'utils/http-helper';
const tcpPing = util.promisify(tcpp.ping);
let session: Session | undefined;
let savedSession: Session | undefined;
const host =
process.env.PUSHY_REGISTRY || process.env.RNU_API || defaultEndpoint;
const userAgent = `react-native-update-cli/${packageJson.version}`;
@@ -64,7 +62,9 @@ export const closeSession = () => {
};
async function query(url: string, options: fetch.RequestInit) {
const resp = await fetch(url, options);
const baseUrl = await getBaseUrl;
const fullUrl = `${baseUrl}${url}`;
const resp = await fetch(fullUrl, options);
const text = await resp.text();
let json: any;
try {
@@ -83,7 +83,7 @@ async function query(url: string, options: fetch.RequestInit) {
function queryWithoutBody(method: string) {
return (api: string) =>
query(host + api, {
query(api, {
method,
headers: {
'User-Agent': userAgent,
@@ -94,7 +94,7 @@ function queryWithoutBody(method: string) {
function queryWithBody(method: string) {
return (api: string, body?: Record<string, any>) =>
query(host + api, {
query(api, {
method,
headers: {
'User-Agent': userAgent,
@@ -116,6 +116,7 @@ export async function uploadFile(fn: string, key?: string) {
});
let realUrl = url;
if (backupUrl) {
// @ts-ignore
if (global.USE_ACC_OSS) {
realUrl = backupUrl;
} else {

View File

@@ -75,15 +75,12 @@ async function runReactNativeBundleCommand({
const envArgs = process.env.PUSHY_ENV_ARGS;
if (envArgs) {
Array.prototype.push.apply(
reactNativeBundleArgs,
envArgs.trim().split(/\s+/),
);
reactNativeBundleArgs.push(...envArgs.trim().split(/\s+/));
}
fs.emptyDirSync(outputFolder);
let cliPath: string | undefined;
let cliPath = '';
let usingExpo = false;
const getExpoCli = () => {
@@ -104,7 +101,7 @@ async function runReactNativeBundleCommand({
if (satisfies(expoCliVersion, '>= 0.10.17')) {
usingExpo = true;
} else {
cliPath = undefined;
cliPath = '';
}
} catch (e) {}
};
@@ -166,49 +163,32 @@ async function runReactNativeBundleCommand({
bundleCommand = 'build';
}
if (platform === 'harmony') {
Array.prototype.push.apply(reactNativeBundleArgs, [
cliPath,
bundleCommand,
'--dev',
dev,
'--entry-file',
entryFile,
]);
reactNativeBundleArgs.push(cliPath, bundleCommand);
if (sourcemapOutput) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
}
if (config) {
reactNativeBundleArgs.push('--config', config);
}
} else {
Array.prototype.push.apply(reactNativeBundleArgs, [
cliPath,
bundleCommand,
if (platform !== 'harmony') {
reactNativeBundleArgs.push(
'--platform',
platform,
'--assets-dest',
outputFolder,
'--bundle-output',
path.join(outputFolder, bundleName),
'--platform',
platform,
'--reset-cache',
]);
);
}
if (cli.taro) {
reactNativeBundleArgs.push(...['--type', 'rn']);
} else {
reactNativeBundleArgs.push(...['--dev', dev, '--entry-file', entryFile]);
}
if (cli.taro) {
reactNativeBundleArgs.push('--type', 'rn');
} else {
reactNativeBundleArgs.push('--dev', dev, '--entry-file', entryFile);
}
if (sourcemapOutput) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
}
if (sourcemapOutput) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
}
if (config) {
reactNativeBundleArgs.push('--config', config);
}
if (config) {
reactNativeBundleArgs.push('--config', config);
}
const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs);
@@ -597,6 +577,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
}
const copies = {};
const copiesv2 = {};
const zipfile = new YazlZipFile();
@@ -668,6 +649,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
addEntry(base);
}
copies[entry.fileName] = originMap[entry.crc32];
copiesv2[entry.crc32] = entry.fileName;
return;
}
@@ -700,7 +682,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
//console.log({copies, deletes});
zipfile.addBuffer(
Buffer.from(JSON.stringify({ copies, deletes })),
Buffer.from(JSON.stringify({ copies, copiesv2, deletes })),
'__diff.json',
);
zipfile.end();
@@ -747,6 +729,7 @@ async function diffFromPackage(
}
const copies = {};
const copiesv2 = {};
const zipfile = new YazlZipFile();
@@ -792,6 +775,7 @@ async function diffFromPackage(
// If moved from other place
if (originMap[entry.crc32]) {
copies[entry.fileName] = originMap[entry.crc32];
copiesv2[entry.crc32] = entry.fileName;
return;
}
@@ -810,7 +794,10 @@ async function diffFromPackage(
}
});
zipfile.addBuffer(Buffer.from(JSON.stringify({ copies })), '__diff.json');
zipfile.addBuffer(
Buffer.from(JSON.stringify({ copies, copiesv2 })),
'__diff.json',
);
zipfile.end();
await writePromise;
}

View File

@@ -131,4 +131,10 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
unnamed: '(Unnamed)',
dryRun: 'Below is the dry-run result, no actual operation will be performed:',
usingCustomVersion: 'Using custom version: {{version}}',
confirmDeletePackage:
'Confirm delete native package {{packageId}}? This operation cannot be undone (Y/N):',
deletePackageSuccess: 'Native package {{packageId}} deleted successfully',
deletePackageError:
'Failed to delete native package {{packageId}}: {{error}}',
usageDeletePackage: 'Usage: cresc deletePackage [packageId] --appId [appId]',
};

View File

@@ -124,4 +124,9 @@ export default {
unnamed: '(未命名)',
dryRun: '以下是 dry-run 模拟运行结果,不会实际执行任何操作:',
usingCustomVersion: '使用自定义版本:{{version}}',
confirmDeletePackage: '确认删除原生包 {{packageId}}? 此操作不可撤销 (Y/N):',
deletePackageSuccess: '原生包 {{packageId}} 删除成功',
deletePackageError: '删除原生包 {{packageId}} 失败: {{error}}',
usageDeletePackage:
'使用方法: pushy deletePackage [packageId] --appId [appId]',
};

View File

@@ -39,9 +39,9 @@ export class ModuleManager {
module.init(this.provider);
}
console.log(
`Module '${module.name}' (v${module.version}) registered successfully`,
);
// console.log(
// `Module '${module.name}' (v${module.version}) registered successfully`,
// );
}
unregisterModule(moduleName: string): void {

View File

@@ -1,4 +1,4 @@
import { get, getAllPackages, post, uploadFile } from './api';
import { get, getAllPackages, post, uploadFile, doDelete } from './api';
import { question, saveToLocal } from './utils';
import { t } from './utils/i18n';
@@ -212,4 +212,53 @@ export const packageCommands = {
const { appId } = await getSelectedApp(platform);
await listPackage(appId);
},
deletePackage: async ({
args,
options,
}: {
args: string[];
options: { appId?: string, packageId?: string, packageVersion?: string };
}) => {
let { appId, packageId, packageVersion } = options;
if (!appId) {
const platform = await getPlatform();
appId = (await getSelectedApp(platform)).appId as string;
}
// If no packageId provided as argument, let user choose from list
if (!packageId) {
const allPkgs = await getAllPackages(appId);
if (!allPkgs) {
throw new Error(t('noPackagesFound', { appId }));
}
const selectedPackage = allPkgs.find((pkg) => pkg.name === packageVersion);
if (!selectedPackage) {
throw new Error(t('packageNotFound', { packageVersion }));
}
packageId = selectedPackage.id;
}
// Confirm deletion
// const confirmDelete = await question(
// t('confirmDeletePackage', { packageId }),
// );
// if (
// confirmDelete.toLowerCase() !== 'y' &&
// confirmDelete.toLowerCase() !== 'yes'
// ) {
// console.log(t('cancelled'));
// return;
// }
try {
await doDelete(`/app/${appId}/package/${packageId}`);
console.log(t('deletePackageSuccess', { packageId }));
} catch (error: any) {
throw new Error(
t('deletePackageError', { packageId, error: error.message }),
);
}
},
};

View File

@@ -10,6 +10,6 @@ export const pricingPageUrl = IS_CRESC
? 'https://cresc.dev/pricing'
: 'https://pushy.reactnative.cn/pricing.html';
export const defaultEndpoint = IS_CRESC
? 'https://api.cresc.dev'
: 'https://update.reactnative.cn/api';
export const defaultEndpoints = IS_CRESC
? ['https://api.cresc.dev', 'https://api.cresc.app']
: ['https://update.reactnative.cn/api', 'https://update.react-native.cn/api'];

View File

@@ -1,4 +1,9 @@
const currentPackage = require(`${process.cwd()}/package.json`);
let currentPackage = null;
try {
currentPackage = require(`${process.cwd()}/package.json`);
} catch (e) {
// console.warn('No package.json file were found');
}
const _depVersions: Record<string, string> = {};
@@ -24,12 +29,9 @@ if (currentPackage) {
export const depVersions = Object.keys(_depVersions)
.sort() // Sort the keys alphabetically
.reduce(
(obj, key) => {
obj[key] = _depVersions[key]; // Rebuild the object with sorted keys
return obj;
},
{} as Record<string, string>,
);
.reduce((obj, key) => {
obj[key] = _depVersions[key]; // Rebuild the object with sorted keys
return obj;
}, {} as Record<string, string>);
// console.log({ depVersions });

83
src/utils/http-helper.ts Normal file
View File

@@ -0,0 +1,83 @@
import { defaultEndpoints } from './constants';
// const baseUrl = `http://localhost:9000`;
// let baseUrl = SERVER.main[0];
// const baseUrl = `https://p.reactnative.cn/api`;
export function promiseAny<T>(promises: Promise<T>[]) {
return new Promise<T>((resolve, reject) => {
let count = 0;
for (const promise of promises) {
Promise.resolve(promise)
.then(resolve)
.catch(() => {
count++;
if (count === promises.length) {
reject(new Error('All promises were rejected'));
}
});
}
});
}
export const ping = async (url: string) => {
let pingFinished = false;
return Promise.race([
fetch(url, {
method: 'HEAD',
})
.then(({ status, statusText }) => {
pingFinished = true;
if (status === 200) {
// console.log('ping success', url);
return url;
}
// console.log('ping failed', url, status, statusText);
throw new Error('ping failed');
})
.catch((e) => {
pingFinished = true;
// console.log('ping error', url, e);
throw new Error('ping error');
}),
new Promise((_, reject) =>
setTimeout(() => {
reject(new Error('ping timeout'));
if (!pingFinished) {
// console.log('ping timeout', url);
}
}, 2000),
),
]) as Promise<string | null>;
};
export const testUrls = async (urls?: string[]) => {
if (!urls?.length) {
return null;
}
const ret = await promiseAny(urls.map(ping));
if (ret) {
return ret;
}
// console.log('all ping failed, use first url:', urls[0]);
return urls[0];
};
export const getBaseUrl = (async () => {
const testEndpoint = process.env.PUSHY_REGISTRY || process.env.RNU_API;
if (testEndpoint) {
return testEndpoint;
}
return testUrls(defaultEndpoints.map((url) => `${url}/status`)).then(
(ret) => {
let baseUrl = defaultEndpoints[0];
if (ret) {
// remove /status
baseUrl = ret.replace('/status', '');
}
// console.log('baseUrl', baseUrl);
return baseUrl;
},
);
})();

View File

@@ -4,7 +4,8 @@
"target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"lib": [
"ESNext"
"ESNext",
"DOM"
] /* Specify library files to be included in the compilation. */,
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true /* Report errors in .js files. */,