Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"pack": "zx script/pack.mjs",
"start": "electron ./dist/main/index.js --inspect=9229",
"lint": "eslint \"src/**/*.{ts,tsx}\"",
"test": "node --experimental-strip-types --disable-warning=ExperimentalWarning --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test src/main/lib/hdc/bundleInfos.test.ts",
"init": "npm run resources",
"resources": "zx script/resources.mjs",
"format": "lsla prettier \"src/**/*.{ts,tsx,scss,css,json}\" \"*.{js,ts,json}\" \"script/*.mjs\" --write",
Expand Down
44 changes: 5 additions & 39 deletions src/main/lib/hdc/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
IBundleInfo,
IpcCleanBundleCache,
IpcCleanBundleData,
IpcGetBundleInfos,
Expand All @@ -21,6 +20,7 @@ import map from 'licia/map'
import startWith from 'licia/startWith'
import filter from 'licia/filter'
import contain from 'licia/contain'
import { getBundleInfosFromDumps } from './bundleInfos'

const logger = log('hdcBundle')

Expand Down Expand Up @@ -65,48 +65,15 @@ function isSystemBundle(bundle: string) {
}

const getBundleInfos: IpcGetBundleInfos = async (connectKey, bundleNames) => {
const result: IBundleInfo[] = []

const dumpInfos = await shell(
connectKey,
map(bundleNames, (name) => `bm dump -n ${name}`)
)
const infos = map(dumpInfos, (dump) => {
const lines = dump.split('\n')
return JSON.parse(lines.slice(1).join('\n'))
})

for (let i = 0, len = bundleNames.length; i < len; i++) {
const bundleName = bundleNames[i]
const bundleInfo: IBundleInfo = {
bundleName,
label: bundleName,
icon: '',
system: false,
versionName: '',
apiTargetVersion: 0,
vendor: '',
installTime: 0,
releaseType: '',
}

const info = infos[i]
const applicationInfo = info.applicationInfo
bundleInfo.system = applicationInfo.isSystemApp
bundleInfo.versionName = applicationInfo.versionName
bundleInfo.apiTargetVersion = applicationInfo.apiTargetVersion
bundleInfo.vendor = applicationInfo.vendor
bundleInfo.installTime = info.installTime
bundleInfo.releaseType = info.releaseType

const mainEntry = info.mainEntry
if (mainEntry) {
const mainModuleInfo =
info.hapModuleInfos[info.hapModuleNames.indexOf(mainEntry)]
bundleInfo.mainAbility =
mainModuleInfo.mainAbility || mainModuleInfo.abilityInfos[0].name
}
const result = getBundleInfosFromDumps(bundleNames, dumpInfos)

for (let i = 0, len = result.length; i < len; i++) {
const bundleInfo = result[i]
const bundleName = bundleInfo.bundleName
if (!bundleInfo.system && !startWith(bundleName, 'com.huawei')) {
try {
const onlineInfo = await getOnlineBundleInfo(bundleName)
Expand All @@ -120,7 +87,6 @@ const getBundleInfos: IpcGetBundleInfos = async (connectKey, bundleNames) => {
logger.error(e)
}
}
result.push(bundleInfo)
}

return result
Expand Down
121 changes: 121 additions & 0 deletions src/main/lib/hdc/bundleInfos.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { getBundleInfosFromDumps } from './bundleInfos.ts'

function dumpText(bundleName: string, info: unknown) {
return `${bundleName}:\n${JSON.stringify(info, null, 4)}`
}

function happyDump(bundleName: string) {
return dumpText(bundleName, {
installTime: 1710000000000,
releaseType: 'Release',
mainEntry: 'entry',
hapModuleNames: ['entry'],
hapModuleInfos: [
{
name: 'entry',
mainAbility: 'EntryAbility',
abilityInfos: [{ name: 'EntryAbility' }],
},
],
applicationInfo: {
isSystemApp: false,
versionName: '1.2.3',
apiTargetVersion: 12,
vendor: 'Example',
},
})
}

function missingMainEntryDump(bundleName: string) {
return dumpText(bundleName, {
installTime: 1710000001000,
releaseType: 'Release',
mainEntry: 'entry',
hapModuleNames: ['feature'],
hapModuleInfos: [
{
name: 'feature',
mainAbility: 'FeatureAbility',
abilityInfos: [{ name: 'FeatureAbility' }],
},
],
applicationInfo: {
isSystemApp: false,
versionName: '2.0.0',
apiTargetVersion: 11,
vendor: 'Other',
},
})
}

describe('getBundleInfosFromDumps', () => {
it('returns bundle info on the happy path', () => {
const bundleName = 'com.example.good'
assert.deepEqual(
getBundleInfosFromDumps([bundleName], [happyDump(bundleName)]),
[
{
bundleName,
label: bundleName,
icon: '',
system: false,
versionName: '1.2.3',
apiTargetVersion: 12,
vendor: 'Example',
installTime: 1710000000000,
releaseType: 'Release',
mainAbility: 'EntryAbility',
},
],
)
})

it('keeps other bundles when one dump is not JSON', () => {
const good = 'com.example.good'
const bad = 'com.example.broken'
const infos = getBundleInfosFromDumps(
[good, bad],
[happyDump(good), `${bad}:\nnot-json {{{`],
)

assert.equal(infos.length, 1)
assert.equal(infos[0].bundleName, good)
assert.equal(infos[0].mainAbility, 'EntryAbility')
})

it('lists a bundle when mainEntry indexOf is -1', () => {
const orphan = 'com.example.orphan'
const infos = getBundleInfosFromDumps(
[orphan],
[missingMainEntryDump(orphan)],
)

assert.equal(infos.length, 1)
assert.equal(infos[0].bundleName, orphan)
assert.equal(infos[0].versionName, '2.0.0')
assert.equal(infos[0].mainAbility, undefined)
})

it('does not empty the list when a bad JSON dump is mixed with indexOf -1', () => {
const good = 'com.example.good'
const bad = 'com.example.broken'
const orphan = 'com.example.orphan'
const infos = getBundleInfosFromDumps(
[good, bad, orphan],
[
happyDump(good),
`${bad}:\n[truncated dump`,
missingMainEntryDump(orphan),
],
)

assert.deepEqual(
infos.map((info) => info.bundleName),
[good, orphan],
)
assert.equal(infos[0].mainAbility, 'EntryAbility')
assert.equal(infos[1].mainAbility, undefined)
})
})
85 changes: 85 additions & 0 deletions src/main/lib/hdc/bundleInfos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
export interface BundleInfo {
bundleName: string
versionName: string
icon: string
label: string
system: boolean
apiTargetVersion: number
vendor: string
installTime: number
releaseType: string
mainAbility?: string
}

export function parseBmDumpJson(dump: string): any {
const lines = dump.split('\n')
return JSON.parse(lines.slice(1).join('\n'))
}

export function createBundleInfo(bundleName: string, info: any): BundleInfo {
const bundleInfo: BundleInfo = {
bundleName,
label: bundleName,
icon: '',
system: false,
versionName: '',
apiTargetVersion: 0,
vendor: '',
installTime: 0,
releaseType: '',
}

const applicationInfo = info.applicationInfo
bundleInfo.system = applicationInfo.isSystemApp
bundleInfo.versionName = applicationInfo.versionName
bundleInfo.apiTargetVersion = applicationInfo.apiTargetVersion
bundleInfo.vendor = applicationInfo.vendor
bundleInfo.installTime = info.installTime
bundleInfo.releaseType = info.releaseType

const mainEntry = info.mainEntry
if (mainEntry) {
const hapModuleNames = info.hapModuleNames
const hapModuleInfos = info.hapModuleInfos
const index = Array.isArray(hapModuleNames)
? hapModuleNames.indexOf(mainEntry)
: -1
const mainModuleInfo =
index === -1 || !Array.isArray(hapModuleInfos)
? undefined
: hapModuleInfos[index]
if (mainModuleInfo) {
const fallbackAbility = Array.isArray(mainModuleInfo.abilityInfos)
? mainModuleInfo.abilityInfos[0]?.name
: undefined
if (mainModuleInfo.mainAbility || fallbackAbility) {
bundleInfo.mainAbility = mainModuleInfo.mainAbility || fallbackAbility
}
}
}

return bundleInfo
}

export function getBundleInfosFromDumps(
bundleNames: string[],
dumpInfos: string[],
): BundleInfo[] {
const result: BundleInfo[] = []

for (let i = 0, len = bundleNames.length; i < len; i++) {
const bundleName = bundleNames[i]
try {
const dump = dumpInfos[i]
if (typeof dump !== 'string') {
continue
}
const info = parseBmDumpJson(dump)
result.push(createBundleInfo(bundleName, info))
} catch {
continue
}
}

return result
}