@@ -5,8 +5,6 @@ import { existsSync, readdirSync } from 'node:fs'
55import { mkdir , readFile , rm , writeFile } from 'node:fs/promises'
66import { dirname , join , resolve } from 'node:path'
77import { gunzipSync } from 'node:zlib'
8- import defu from 'defu'
9- import { parseTar } from 'nanotar'
108import { providers } from './providers'
119import { registryProvider } from './registry'
1210import { cacheDirectory , debug , download , normalizeHeaders } from './utils'
@@ -58,8 +56,81 @@ async function installDependencies(options: InstallOptions): Promise < void> {
5856 } )
5957}
6058
59+ export interface TarEntry {
60+ name : string
61+ type : 'file' | 'directory'
62+ size : number
63+ data ?: Uint8Array
64+ }
65+
66+ /**
67+ * Parse a tar archive buffer into entries (POSIX ustar + GNU long name support)
68+ */
69+ export function parseTar ( data : Uint8Array ) : TarEntry [ ] {
70+ const entries : TarEntry [ ] = [ ]
71+ let offset = 0
72+ const decoder = new TextDecoder ( )
73+ let longName : string | null = null
74+
75+ while ( offset + 512 <= data . length ) {
76+ const header = data . subarray ( offset , offset + 512 )
77+
78+ // End of archive: zero block
79+ let allZero = true
80+ for ( let i = 0 ; i < 512 ; i ++ ) {
81+ if ( header [ i ] !== 0 ) {
82+ allZero = false
83+ break
84+ }
85+ }
86+ if ( allZero )
87+ break
88+
89+ const nameRaw = decoder . decode ( header . subarray ( 0 , 100 ) ) . replace ( / \0 .* $ / , '' )
90+ const prefix = decoder . decode ( header . subarray ( 345 , 500 ) ) . replace ( / \0 .* $ / , '' )
91+ const sizeStr = decoder . decode ( header . subarray ( 124 , 136 ) ) . replace ( / \0 .* $ / , '' ) . trim ( )
92+ const size = sizeStr ? Number . parseInt ( sizeStr , 8 ) : 0
93+ const typeflag = String . fromCharCode ( header [ 156 ] ! )
94+
95+ offset += 512
96+ const dataBlocks = Math . ceil ( size / 512 ) * 512
97+
98+ // GNU long name extension
99+ if ( typeflag === 'L' ) {
100+ longName = decoder . decode ( data . subarray ( offset , offset + size ) ) . replace ( / \0 .* $ / , '' )
101+ offset += dataBlocks
102+ continue
103+ }
104+
105+ // Pax extended header — extract path if present
106+ if ( typeflag === 'x' || typeflag === 'g' ) {
107+ const paxData = decoder . decode ( data . subarray ( offset , offset + size ) )
108+ const pathMatch = paxData . match ( / \d + p a t h = ( .+ ) \n / )
109+ if ( pathMatch )
110+ longName = pathMatch [ 1 ] !
111+ offset += dataBlocks
112+ continue
113+ }
114+
115+ const name = longName ?? ( prefix ? `${ prefix } /${ nameRaw } ` : nameRaw )
116+ longName = null
117+
118+ if ( typeflag === '5' ) {
119+ entries . push ( { name, type : 'directory' , size } )
120+ }
121+ else if ( typeflag === '0' || typeflag === '\0' || typeflag === '' ) {
122+ const fileData = size > 0 ? data . slice ( offset , offset + size ) : undefined
123+ entries . push ( { name, type : 'file' , size, data : fileData } )
124+ }
125+
126+ offset += dataBlocks
127+ }
128+
129+ return entries
130+ }
131+
61132/**
62- * Extract a tarball using nanotar (cross - platform)
133+ * Extract a tarball (cross- platform)
63134*/
64135async function extractTar ( options : GitItExtractOptions ) : Promise < void > {
65136 const { file, cwd, onentry } = options
@@ -214,13 +285,11 @@ export async function downloadTemplate(
214285input : string ,
215286options : DownloadTemplateOptions = { } ,
216287) : Promise < DownloadTemplateResult > {
217- options = defu (
218- {
219- registry : process . env . GITIT_REGISTRY ,
220- auth : process . env . GITIT_AUTH ,
221- } ,
222- options ,
223- ) as DownloadTemplateOptions
288+ options = {
289+ ...options ,
290+ registry : process . env . GITIT_REGISTRY ?? options . registry ,
291+ auth : process . env . GITIT_AUTH ?? options . auth ,
292+ } as DownloadTemplateOptions
224293
225294 // Load hooks
226295 const hooks = loadHooks ( options )
0 commit comments