From 4f0784172f65e9636564e73281616bc0f835d754 Mon Sep 17 00:00:00 2001 From: sunnylqm Date: Sat, 11 Jan 2025 20:20:16 +0800 Subject: [PATCH] fix expo cli --- package.json | 2 +- src/{app.js => app.ts} | 11 ++-- src/bundle.js | 135 ++++++++++++++++++++++------------------- src/types.ts | 2 + src/utils/index.js | 10 +-- 5 files changed, 87 insertions(+), 73 deletions(-) rename src/{app.js => app.ts} (91%) diff --git a/package.json b/package.json index 37a9adb..d639f98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-update-cli", - "version": "1.38.1", + "version": "1.38.2", "description": "Command tools for javaScript updater with `pushy` service for react native apps.", "main": "index.js", "bin": { diff --git a/src/app.js b/src/app.ts similarity index 91% rename from src/app.js rename to src/app.ts index df8521e..34648bd 100644 --- a/src/app.js +++ b/src/app.ts @@ -3,6 +3,7 @@ import fs from 'node:fs'; import Table from 'tty-table'; import { post, get, doDelete } from './api'; +import type { Platform } from './types'; const validPlatforms = { ios: 1, @@ -10,14 +11,14 @@ const validPlatforms = { harmony: 1, }; -export function checkPlatform(platform) { +export function checkPlatform(platform: Platform) { if (!validPlatforms[platform]) { throw new Error(`无法识别的平台 '${platform}'`); } return platform; } -export function getSelectedApp(platform) { +export function getSelectedApp(platform: Platform) { checkPlatform(platform); if (!fs.existsSync('update.json')) { @@ -34,7 +35,7 @@ export function getSelectedApp(platform) { return updateInfo[platform]; } -export async function listApp(platform) { +export async function listApp(platform: Platform) { const { data } = await get('/app/list'); const list = platform ? data.filter((v) => v.platform === platform) : data; @@ -58,12 +59,12 @@ export async function listApp(platform) { return list; } -export async function chooseApp(platform) { +export async function chooseApp(platform: Platform) { const list = await listApp(platform); while (true) { const id = await question('输入应用 id:'); - const app = list.find((v) => v.id === (id | 0)); + const app = list.find((v) => v.id === Number(id)); if (app) { return app; } diff --git a/src/bundle.js b/src/bundle.js index f486a9a..429f982 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -6,12 +6,14 @@ import { open as openZipFile } from 'yauzl'; import { question, printVersionCommand } from './utils'; import { checkPlatform } from './app'; import { spawn, spawnSync } from 'node:child_process'; +import semverSatisfies from 'semver/functions/satisfies'; const g2js = require('gradle-to-js/lib/parser'); -import os from 'os'; +import os from 'node:os'; const properties = require('properties'); -const path = require('path'); -let bsdiff, hdiff, diff; +let bsdiff; +let hdiff; +let diff; try { bsdiff = require('node-bsdiff').diff; } catch (e) {} @@ -39,9 +41,9 @@ async function runReactNativeBundleCommand( } } - let reactNativeBundleArgs = []; + const reactNativeBundleArgs = []; - let envArgs = process.env.PUSHY_ENV_ARGS; + const envArgs = process.env.PUSHY_ENV_ARGS; if (envArgs) { Array.prototype.push.apply( @@ -59,8 +61,19 @@ async function runReactNativeBundleCommand( cliPath = require.resolve('@expo/cli', { paths: [process.cwd()], }); - usingExpo = true; - } catch (e) { + const expoCliVersion = JSON.parse( + fs.readFileSync( + require.resolve('@expo/cli/package.json', { + paths: [process.cwd()], + }), + ), + ).version; + // expo cli 0.10.17 (expo 49) 开始支持 bundle:embed + if (semverSatisfies(expoCliVersion, '>= 0.10.17')) { + usingExpo = true; + } + } catch (e) {} + if (!usingExpo) { try { // rn >= 0.75 cliPath = require.resolve('@react-native-community/cli/build/bin.js', { @@ -73,8 +86,12 @@ async function runReactNativeBundleCommand( }); } } - const bundleCommand = usingExpo ? 'export:embed' : platform === 'harmony' ? 'bundle-harmony' : 'bundle'; - if (platform == 'harmony') { + const bundleCommand = usingExpo + ? 'export:embed' + : platform === 'harmony' + ? 'bundle-harmony' + : 'bundle'; + if (platform === 'harmony') { Array.prototype.push.apply(reactNativeBundleArgs, [ cliPath, bundleCommand, @@ -91,8 +108,7 @@ async function runReactNativeBundleCommand( if (config) { reactNativeBundleArgs.push('--config', config); } - } - else{ + } else { Array.prototype.push.apply(reactNativeBundleArgs, [ cliPath, bundleCommand, @@ -108,16 +124,16 @@ async function runReactNativeBundleCommand( platform, '--reset-cache', ]); - + if (sourcemapOutput) { reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput); } - + if (config) { reactNativeBundleArgs.push('--config', config); } } - + const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs); console.log( `Running bundle command: node ${reactNativeBundleArgs.join(' ')}`, @@ -147,7 +163,7 @@ async function runReactNativeBundleCommand( properties.parse( './android/gradle.properties', { path: true }, - function (error, props) { + (error, props) => { if (error) { console.error(error); resolve(null); @@ -166,7 +182,7 @@ async function runReactNativeBundleCommand( fs.existsSync('ios/Pods/hermes-engine') ) { hermesEnabled = true; - }else if (platform === 'harmony') { + } else if (platform === 'harmony') { await copyHarmonyBundle(outputFolder); } if (hermesEnabled) { @@ -193,7 +209,7 @@ async function copyHarmonyBundle(outputFolder) { } await fs.remove(path.join(harmonyRawPath, 'update.json')); await fs.copy('update.json', path.join(harmonyRawPath, 'update.json')); - + await fs.ensureDir(outputFolder); await fs.copy(harmonyRawPath, outputFolder); } catch (error) { @@ -238,7 +254,7 @@ async function compileHermesByteCode( outputFolder, sourcemapOutput, ) { - console.log(`Hermes enabled, now compiling to hermes bytecode:\n`); + console.log('Hermes enabled, now compiling to hermes bytecode:\n'); // >= rn 0.69 const rnDir = path.dirname( require.resolve('react-native', { @@ -264,13 +280,11 @@ async function compileHermesByteCode( if (sourcemapOutput) { fs.copyFileSync( sourcemapOutput, - path.join(outputFolder, bundleName + '.txt.map'), + path.join(outputFolder, `${bundleName}.txt.map`), ); args.push('-output-source-map'); } - console.log( - 'Running hermesc: ' + hermesCommand + ' ' + args.join(' ') + '\n', - ); + console.log(`Running hermesc: ${hermesCommand} ${args.join(' ')}`); spawnSync(hermesCommand, args, { stdio: 'ignore', }); @@ -280,13 +294,13 @@ async function compileHermesByteCode( if (!fs.existsSync(composerPath)) { return; } - console.log(`Composing source map`); + console.log('Composing source map'); spawnSync( 'node', [ composerPath, - path.join(outputFolder, bundleName + '.txt.map'), - path.join(outputFolder, bundleName + '.map'), + path.join(outputFolder, `${bundleName}.txt.map`), + path.join(outputFolder, `${bundleName}.map`), '-o', sourcemapOutput, ], @@ -295,7 +309,7 @@ async function compileHermesByteCode( }, ); } - fs.removeSync(path.join(outputFolder, bundleName + '.txt.map')); + fs.removeSync(path.join(outputFolder, `${bundleName}.txt.map`)); } async function pack(dir, output) { @@ -320,7 +334,7 @@ async function pack(dir, output) { zipfile.addFile(fullPath, rel + name); } else if (stat.isDirectory()) { //console.log('adding: ' + rel+name+'/'); - addDirectory(fullPath, rel + name + '/'); + addDirectory(fullPath, `${rel}${name}/`); } } } @@ -328,14 +342,12 @@ async function pack(dir, output) { addDirectory(dir, ''); zipfile.outputStream.on('error', (err) => reject(err)); - zipfile.outputStream - .pipe(fs.createWriteStream(output)) - .on('close', function () { - resolve(); - }); + zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', () => { + resolve(); + }); zipfile.end(); }); - console.log('ppk热更包已生成并保存到: ' + output); + console.log(`ppk热更包已生成并保存到: ${output}`); } export function readEntire(entry, zipFile) { @@ -360,7 +372,7 @@ export function readEntire(entry, zipFile) { function basename(fn) { const m = /^(.+\/)[^\/]+\/?$/.exec(fn); - return m && m[1]; + return m?.[1]; } async function diffFromPPK(origin, next, output) { @@ -377,7 +389,10 @@ async function diffFromPPK(origin, next, output) { // isFile originMap[entry.crc32] = entry.fileName; - if (entry.fileName === 'index.bundlejs' || entry.fileName === 'bundle.harmony.js') { + if ( + entry.fileName === 'index.bundlejs' || + entry.fileName === 'bundle.harmony.js' + ) { // This is source. return readEntire(entry, zipFile).then((v) => (originSource = v)); } @@ -386,7 +401,7 @@ async function diffFromPPK(origin, next, output) { if (!originSource) { throw new Error( - `Bundle file not found! Please use default bundle file name and path.`, + 'Bundle file not found! Please use default bundle file name and path.', ); } @@ -398,11 +413,9 @@ async function diffFromPPK(origin, next, output) { zipfile.outputStream.on('error', (err) => { throw err; }); - zipfile.outputStream - .pipe(fs.createWriteStream(output)) - .on('close', function () { - resolve(); - }); + zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', () => { + resolve(); + }); }); const addedEntry = {}; @@ -439,7 +452,7 @@ async function diffFromPPK(origin, next, output) { ); //console.log('End diff'); }); - }else if (entry.fileName === 'bundle.harmony.js') { + } else if (entry.fileName === 'bundle.harmony.js') { //console.log('Found bundle'); return readEntire(entry, nextZipfile).then((newSource) => { //console.log('Begin diff'); @@ -471,7 +484,7 @@ async function diffFromPPK(origin, next, output) { addEntry(basename(entry.fileName)); return new Promise((resolve, reject) => { - nextZipfile.openReadStream(entry, function (err, readStream) { + nextZipfile.openReadStream(entry, (err, readStream) => { if (err) { return reject(err); } @@ -487,9 +500,9 @@ async function diffFromPPK(origin, next, output) { const deletes = {}; - for (let k in originEntries) { + for (const k in originEntries) { if (!newEntries[k]) { - console.log('Delete ' + k); + console.log(`Delete ${k}`); deletes[k] = 1; } } @@ -538,7 +551,7 @@ async function diffFromPackage( if (!originSource) { throw new Error( - `Bundle file not found! Please use default bundle file name and path.`, + 'Bundle file not found! Please use default bundle file name and path.', ); } @@ -550,11 +563,9 @@ async function diffFromPackage( zipfile.outputStream.on('error', (err) => { throw err; }); - zipfile.outputStream - .pipe(fs.createWriteStream(output)) - .on('close', function () { - resolve(); - }); + zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', () => { + resolve(); + }); }); await enumZipEntries(next, (entry, nextZipfile) => { @@ -581,7 +592,7 @@ async function diffFromPackage( ); //console.log('End diff'); }); - }else { + } else { // If same file. if (originEntries[entry.fileName] === entry.crc32) { copies[entry.fileName] = ''; @@ -594,7 +605,7 @@ async function diffFromPackage( } return new Promise((resolve, reject) => { - nextZipfile.openReadStream(entry, function (err, readStream) { + nextZipfile.openReadStream(entry, (err, readStream) => { if (err) { return reject(err); } @@ -630,7 +641,7 @@ export async function enumZipEntries(zipFn, callback, nestedPath = '') { !entry.fileName.endsWith('/') && entry.fileName.toLowerCase().endsWith('.hap') ) { - const tempDir = path.join(os.tmpdir(), 'nested_zip_' + Date.now()); + const tempDir = path.join(os.tmpdir(), `nested_zip_${Date.now()}`); await fs.ensureDir(tempDir); const tempZipPath = path.join(tempDir, 'temp.zip'); @@ -644,7 +655,7 @@ export async function enumZipEntries(zipFn, callback, nestedPath = '') { }); }); - await enumZipEntries(tempZipPath, callback, fullPath + '/'); + await enumZipEntries(tempZipPath, callback, `${fullPath}/`); await fs.remove(tempDir); } @@ -697,7 +708,7 @@ function diffArgsCheck(args, options, diffFn) { return { origin, next, - realOutput: output.replace(/\$\{time\}/g, '' + Date.now()), + realOutput: output.replace(/\$\{time\}/g, `${Date.now()}`), }; } @@ -707,15 +718,15 @@ export const commands = { options.platform || (await question('平台(ios/android/harmony):')), ); - let { bundleName, entryFile, intermediaDir, output, dev, sourcemap } = + const { bundleName, entryFile, intermediaDir, output, dev, sourcemap } = translateOptions({ ...options, platform, }); - const sourcemapOutput = path.join(intermediaDir, bundleName + '.map'); + const sourcemapOutput = path.join(intermediaDir, `${bundleName}.map`); - const realOutput = output.replace(/\$\{time\}/g, '' + Date.now()); + const realOutput = output.replace(/\$\{time\}/g, `${Date.now()}`); if (!platform) { throw new Error('Platform must be specified.'); @@ -723,7 +734,7 @@ export const commands = { const { version, major, minor } = getRNVersion(); - console.log('Bundling with react-native: ', version); + console.log(`Bundling with react-native: ${version}`); await runReactNativeBundleCommand( bundleName, @@ -817,7 +828,7 @@ export const commands = { await diffFromPackage(origin, next, realOutput, 'main.jsbundle', (v) => { const m = /^Payload\/[^/]+\/(.+)$/.exec(v); - return m && m[1]; + return m?.[1]; }); console.log(`${realOutput} generated.`); @@ -832,7 +843,7 @@ export const commands = { await diffFromPackage(origin, next, realOutput, 'main.jsbundle', (v) => { const m = /^Payload\/[^/]+\/(.+)$/.exec(v); - return m && m[1]; + return m?.[1]; }); console.log(`${realOutput} generated.`); diff --git a/src/types.ts b/src/types.ts index 0aeab4a..457f659 100644 --- a/src/types.ts +++ b/src/types.ts @@ -6,3 +6,5 @@ declare global { export interface Session { token: string; } + +export type Platform = 'ios' | 'android' | 'harmony'; diff --git a/src/utils/index.js b/src/utils/index.js index 25c55b9..a27294e 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -22,12 +22,12 @@ export async function question(query, password) { export function translateOptions(options) { const ret = {}; - for (let key in options) { + for (const key in options) { const v = options[key]; if (typeof v === 'string') { - ret[key] = v.replace(/\$\{(\w+)\}/g, function (v, n) { - return options[n] || process.env[n] || v; - }); + ret[key] = v.replace(/\$\{(\w+)\}/g, (v, n) => + options[n] || process.env[n] || v, + ); } else { ret[key] = v; } @@ -217,7 +217,7 @@ export async function printVersionCommand() { ); } else if (semverSatisfies(pushyVersion, '10.0.0 - 10.17.0')) { console.warn( - `当前版本已不再支持,请升级到 v10 的最新小版本(代码无需改动,可直接热更): npm i react-native-update@10`, + '当前版本已不再支持,请升级到 v10 的最新小版本(代码无需改动,可直接热更): npm i react-native-update@10', ); } }