11import fs from 'fs' ;
22import os from 'os' ;
33import path from 'path' ;
4+ import { Readable } from 'stream' ;
5+ import { promisify } from 'util' ;
46import {
57 type Entry ,
68 open as openZipFile ,
79 type ZipFile as YauzlZipFile ,
810} from 'yauzl' ;
11+ import zlib from 'zlib' ;
912import type { Platform } from './types' ;
1013import { ensureDir , remove } from './utils/fs-helpers' ;
1114import {
1215 createOutputZip ,
1316 openEntryStream ,
17+ readEntry ,
1418 writeEntry ,
1519} from './utils/zip-entries' ;
1620import { fitsRawZip , RawZipWriter } from './utils/zip-raw-writer' ;
@@ -72,9 +76,22 @@ export function resolveNativePackageEntry(
7276 return nativePackageRules [ platform ] . resolveEntry ( entryName ) ;
7377}
7478
79+ /**
80+ * Entries kept for the server only: it maps resource names to their (shortened)
81+ * paths through resources.arsc when building a package diff. They are never a
82+ * diff origin, so they stay out of resolveNativePackageEntry.
83+ */
84+ const serverOnlyEntries : Partial < Record < Platform , string > > = {
85+ android : 'resources.arsc' ,
86+ } ;
87+
88+ const deflateRaw = promisify ( zlib . deflateRaw ) ;
89+
7590/** Where the kept entries go: a raw copy (default) or a yazl repack. */
7691interface SlimSink {
7792 copyEntry ( sourceZip : YauzlZipFile , entry : Entry ) : Promise < void > ;
93+ /** like copyEntry, but an entry the source stores uncompressed is deflated */
94+ copyEntryDeflated ( sourceZip : YauzlZipFile , entry : Entry ) : Promise < void > ;
7895 /** add a local file under `fileName`, dated like `like` */
7996 addFile ( filePath : string , fileName : string , like : Entry ) : Promise < void > ;
8097 end ( ) : Promise < void > ;
@@ -97,6 +114,27 @@ class RawCopySink implements SlimSink {
97114 } ) ;
98115 await this . writer . addRawEntry ( entry , readStream ) ;
99116 }
117+ async copyEntryDeflated (
118+ sourceZip : YauzlZipFile ,
119+ entry : Entry ,
120+ ) : Promise < void > {
121+ if ( entry . compressionMethod !== 0 ) {
122+ return this . copyEntry ( sourceZip , entry ) ;
123+ }
124+ const deflated = await deflateRaw ( await readEntry ( entry , sourceZip ) , {
125+ level : zlib . constants . Z_BEST_COMPRESSION ,
126+ } ) ;
127+ await this . writer . addRawEntry (
128+ {
129+ ...entry ,
130+ compressionMethod : 8 ,
131+ compressedSize : deflated . length ,
132+ // deflate strength bits of a stored entry mean nothing
133+ generalPurposeBitFlag : 0 ,
134+ } ,
135+ Readable . from ( [ deflated ] ) ,
136+ ) ;
137+ }
100138 addFile ( filePath : string , fileName : string , like : Entry ) : Promise < void > {
101139 // a nested slim archive is a zip of deflated entries: storing it as-is
102140 // loses nothing and skips another pass over its bytes
@@ -116,13 +154,23 @@ class RepackSink implements SlimSink {
116154 constructor ( output : string ) {
117155 this . output = createOutputZip ( output ) ;
118156 }
119- async copyEntry ( sourceZip : YauzlZipFile , entry : Entry ) : Promise < void > {
157+ copyEntry ( sourceZip : YauzlZipFile , entry : Entry ) : Promise < void > {
158+ return this . add ( sourceZip , entry , entry . compressionMethod !== 0 ) ;
159+ }
160+ copyEntryDeflated ( sourceZip : YauzlZipFile , entry : Entry ) : Promise < void > {
161+ return this . add ( sourceZip , entry , true ) ;
162+ }
163+ private async add (
164+ sourceZip : YauzlZipFile ,
165+ entry : Entry ,
166+ compress : boolean ,
167+ ) : Promise < void > {
120168 const readStream = await openEntryStream ( entry , sourceZip ) ;
121169 await new Promise < void > ( ( resolve , reject ) => {
122170 readStream . once ( 'error' , reject ) ;
123171 readStream . once ( 'end' , ( ) => resolve ( ) ) ;
124172 this . output . zipfile . addReadStream ( readStream , entry . fileName , {
125- compress : entry . compressionMethod !== 0 ,
173+ compress,
126174 mtime : entry . getLastModDate ( ) ,
127175 } ) ;
128176 } ) ;
@@ -248,6 +296,9 @@ async function filterNativePackageArchive(
248296 if ( resolvedEntry . kind === 'bundle' ) {
249297 includedBundles += 1 ;
250298 }
299+ } else if ( entry . fileName === serverOnlyEntries [ platform ] ) {
300+ await sink . copyEntryDeflated ( sourceZip , entry ) ;
301+ includedEntries += 1 ;
251302 }
252303 sourceZip . readEntry ( ) ;
253304 } catch ( error ) {
@@ -270,7 +321,7 @@ async function filterNativePackageArchive(
270321
271322/**
272323 * Repack a native package with only the bundle and resources required by
273- * package-origin diffs. Entry names and nested Harmony HAP structure are kept.
324+ * package-origin diffs, plus the server-only entries . Entry names and nested Harmony HAP structure are kept.
274325 */
275326export async function createSlimNativePackage (
276327 source : string ,
0 commit comments