From 4744afa412eba8ca12e1a955c857e11266677383 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 09:19:32 -0600 Subject: [PATCH 1/6] Keep empty files when bundling Quarto in our builds --- build/lib/quarto.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/lib/quarto.ts b/build/lib/quarto.ts index f30b27a48649..5e08459ea6d1 100644 --- a/build/lib/quarto.ts +++ b/build/lib/quarto.ts @@ -36,7 +36,7 @@ function getQuartoWindows(version: string): Stream { verbose: true, timeoutSeconds: 90, }) - .pipe(unzip()); + .pipe(unzip({ keepEmpty: true })); } /** From 922c0c11a9e6fe3bff5c1e5ec2a71108ec09376b Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 10:54:34 -0600 Subject: [PATCH 2/6] Add debug logging for empty files in Windows --- build/lib/quarto.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build/lib/quarto.ts b/build/lib/quarto.ts index 5e08459ea6d1..ed61a39c7f0f 100644 --- a/build/lib/quarto.ts +++ b/build/lib/quarto.ts @@ -36,7 +36,15 @@ function getQuartoWindows(version: string): Stream { verbose: true, timeoutSeconds: 90, }) - .pipe(unzip({ keepEmpty: true })); + .pipe(unzip({ keepEmpty: true })) + // Add a debug step to log all files, including empty ones + .pipe(es.through(function (file) { + const size = file.contents ? file.contents.length : 0; + if (size === 0) { + fancyLog(`Empty file detected: ${file.path}`); + } + this.emit('data', file); + })); } /** From 5336878051f708cc246d372f1029540d6f8f674e Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 13:08:13 -0600 Subject: [PATCH 3/6] Directly check zip file using a different library, then try unzipping --- build/lib/quarto.ts | 111 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 6 deletions(-) diff --git a/build/lib/quarto.ts b/build/lib/quarto.ts index ed61a39c7f0f..99ef738fb48d 100644 --- a/build/lib/quarto.ts +++ b/build/lib/quarto.ts @@ -11,6 +11,8 @@ import gulp = require('gulp'); import util = require('./util'); import rename = require('gulp-rename'); import path = require('path'); +import fs = require('fs'); +import os = require('os'); /** * Get the base URL for the quarto download @@ -28,21 +30,118 @@ function getBaseUrl(version: string): string { * @param version The version of quarto to download * @returns A stream */ +/** + * Check if specific files exist in a ZIP buffer directly without using gulp-unzip + * + * @param zipBuffer The ZIP file as a Buffer + * @param filesToCheck Array of file paths to check for + */ +function checkZipContents(zipBuffer: Buffer, filesToCheck: string[]): void { + // Use the yauzl module for unzipping + const yauzl = require('yauzl'); + + const tempPath = path.join(os.tmpdir(), `quarto-check-${Date.now()}.zip`); + + try { + // Write the buffer to a temp file + fs.writeFileSync(tempPath, zipBuffer); + + // Open the ZIP file + yauzl.open(tempPath, { lazyEntries: true }, (err: Error | null, zipfile: any) => { + if (err) { + fancyLog(`Error checking ZIP contents: ${err}`); + return; + } + + fancyLog(`Checking ZIP file contents directly (bypassing gulp-unzip)`); + + // Track files we find + const foundFiles = new Set(); + + zipfile.on('entry', (entry: any) => { + const fileName = entry.fileName; + const fileSize = entry.uncompressedSize; + + // Check if this is one of our tracked files + for (const fileToCheck of filesToCheck) { + if (fileName.endsWith(fileToCheck)) { + fancyLog(`DIRECT ZIP CHECK - FOUND: ${fileName}, size: ${fileSize} bytes`); + foundFiles.add(fileToCheck); + } + } + + // Continue reading entries + zipfile.readEntry(); + }); + + zipfile.on('end', () => { + // Check for files that weren't found + for (const fileToCheck of filesToCheck) { + if (!foundFiles.has(fileToCheck)) { + fancyLog(`DIRECT ZIP CHECK - NOT FOUND: ${fileToCheck}`); + } + } + + // Clean up + try { + fs.unlinkSync(tempPath); + } catch (e) { + // Ignore errors during cleanup + } + }); + + // Start reading entries + zipfile.readEntry(); + }); + } catch (err) { + fancyLog(`Error in checkZipContents: ${err}`); + } +} + function getQuartoWindows(version: string): Stream { const unzip = require('gulp-unzip'); const basename = `quarto-${version}-win`; - return fetchUrls([`${basename}.zip`], { + + // Files we want to track + const filesToTrack = [ + 'share/formats/pdf/pandoc/after-body.tex', + 'share/formats/pdf/pandoc/before-bib.tex', + 'share/formats/pdf/pandoc/before-title.tex' + ]; + + // Get the fetch stream + const fetchStream = fetchUrls([`${basename}.zip`], { base: getBaseUrl(version), verbose: true, timeoutSeconds: 90, - }) + }); + + // Add logging to check raw ZIP content before unzipping + const preUnzipStream = fetchStream.pipe(es.through(function (file) { + fancyLog(`Received ZIP file: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); + + // Check ZIP contents directly if we have file contents + if (file.contents) { + checkZipContents(file.contents, filesToTrack); + } + + this.emit('data', file); + })); + + // Unzip with keepEmpty option and add detailed logging + return preUnzipStream .pipe(unzip({ keepEmpty: true })) - // Add a debug step to log all files, including empty ones .pipe(es.through(function (file) { - const size = file.contents ? file.contents.length : 0; - if (size === 0) { - fancyLog(`Empty file detected: ${file.path}`); + // Log all files + fancyLog(`Extracted file: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); + + // Check specifically for our tracked files + for (const trackFile of filesToTrack) { + if (file.path.endsWith(trackFile)) { + fancyLog(`FOUND TRACKED FILE: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); + } } + this.emit('data', file); })); } From 709ec920f5f7e7716993b9b0d538e3eeaca77770 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 14:04:51 -0600 Subject: [PATCH 4/6] Revert "Directly check zip file using a different library, then try unzipping" This reverts commit 5336878051f708cc246d372f1029540d6f8f674e. --- build/lib/quarto.ts | 111 +++----------------------------------------- 1 file changed, 6 insertions(+), 105 deletions(-) diff --git a/build/lib/quarto.ts b/build/lib/quarto.ts index 99ef738fb48d..ed61a39c7f0f 100644 --- a/build/lib/quarto.ts +++ b/build/lib/quarto.ts @@ -11,8 +11,6 @@ import gulp = require('gulp'); import util = require('./util'); import rename = require('gulp-rename'); import path = require('path'); -import fs = require('fs'); -import os = require('os'); /** * Get the base URL for the quarto download @@ -30,118 +28,21 @@ function getBaseUrl(version: string): string { * @param version The version of quarto to download * @returns A stream */ -/** - * Check if specific files exist in a ZIP buffer directly without using gulp-unzip - * - * @param zipBuffer The ZIP file as a Buffer - * @param filesToCheck Array of file paths to check for - */ -function checkZipContents(zipBuffer: Buffer, filesToCheck: string[]): void { - // Use the yauzl module for unzipping - const yauzl = require('yauzl'); - - const tempPath = path.join(os.tmpdir(), `quarto-check-${Date.now()}.zip`); - - try { - // Write the buffer to a temp file - fs.writeFileSync(tempPath, zipBuffer); - - // Open the ZIP file - yauzl.open(tempPath, { lazyEntries: true }, (err: Error | null, zipfile: any) => { - if (err) { - fancyLog(`Error checking ZIP contents: ${err}`); - return; - } - - fancyLog(`Checking ZIP file contents directly (bypassing gulp-unzip)`); - - // Track files we find - const foundFiles = new Set(); - - zipfile.on('entry', (entry: any) => { - const fileName = entry.fileName; - const fileSize = entry.uncompressedSize; - - // Check if this is one of our tracked files - for (const fileToCheck of filesToCheck) { - if (fileName.endsWith(fileToCheck)) { - fancyLog(`DIRECT ZIP CHECK - FOUND: ${fileName}, size: ${fileSize} bytes`); - foundFiles.add(fileToCheck); - } - } - - // Continue reading entries - zipfile.readEntry(); - }); - - zipfile.on('end', () => { - // Check for files that weren't found - for (const fileToCheck of filesToCheck) { - if (!foundFiles.has(fileToCheck)) { - fancyLog(`DIRECT ZIP CHECK - NOT FOUND: ${fileToCheck}`); - } - } - - // Clean up - try { - fs.unlinkSync(tempPath); - } catch (e) { - // Ignore errors during cleanup - } - }); - - // Start reading entries - zipfile.readEntry(); - }); - } catch (err) { - fancyLog(`Error in checkZipContents: ${err}`); - } -} - function getQuartoWindows(version: string): Stream { const unzip = require('gulp-unzip'); const basename = `quarto-${version}-win`; - - // Files we want to track - const filesToTrack = [ - 'share/formats/pdf/pandoc/after-body.tex', - 'share/formats/pdf/pandoc/before-bib.tex', - 'share/formats/pdf/pandoc/before-title.tex' - ]; - - // Get the fetch stream - const fetchStream = fetchUrls([`${basename}.zip`], { + return fetchUrls([`${basename}.zip`], { base: getBaseUrl(version), verbose: true, timeoutSeconds: 90, - }); - - // Add logging to check raw ZIP content before unzipping - const preUnzipStream = fetchStream.pipe(es.through(function (file) { - fancyLog(`Received ZIP file: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); - - // Check ZIP contents directly if we have file contents - if (file.contents) { - checkZipContents(file.contents, filesToTrack); - } - - this.emit('data', file); - })); - - // Unzip with keepEmpty option and add detailed logging - return preUnzipStream + }) .pipe(unzip({ keepEmpty: true })) + // Add a debug step to log all files, including empty ones .pipe(es.through(function (file) { - // Log all files - fancyLog(`Extracted file: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); - - // Check specifically for our tracked files - for (const trackFile of filesToTrack) { - if (file.path.endsWith(trackFile)) { - fancyLog(`FOUND TRACKED FILE: ${file.path}, size: ${file.contents ? file.contents.length : 0} bytes`); - } + const size = file.contents ? file.contents.length : 0; + if (size === 0) { + fancyLog(`Empty file detected: ${file.path}`); } - this.emit('data', file); })); } From 01fcf6b3d7c0096f13a45f8b482afb1c045e19d2 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 14:05:00 -0600 Subject: [PATCH 5/6] Revert "Add debug logging for empty files in Windows" This reverts commit 922c0c11a9e6fe3bff5c1e5ec2a71108ec09376b. --- build/lib/quarto.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/build/lib/quarto.ts b/build/lib/quarto.ts index ed61a39c7f0f..5e08459ea6d1 100644 --- a/build/lib/quarto.ts +++ b/build/lib/quarto.ts @@ -36,15 +36,7 @@ function getQuartoWindows(version: string): Stream { verbose: true, timeoutSeconds: 90, }) - .pipe(unzip({ keepEmpty: true })) - // Add a debug step to log all files, including empty ones - .pipe(es.through(function (file) { - const size = file.contents ? file.contents.length : 0; - if (size === 0) { - fancyLog(`Empty file detected: ${file.path}`); - } - this.emit('data', file); - })); + .pipe(unzip({ keepEmpty: true })); } /** From ac0cf8311f50bfad8ffa8667588ef069175a67c7 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Tue, 7 Oct 2025 14:10:39 -0600 Subject: [PATCH 6/6] =?UTF-8?q?OMG=20I=20didn't=20compile=20to=20JS=20?= =?UTF-8?q?=F0=9F=98=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/lib/quarto.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/lib/quarto.js b/build/lib/quarto.js index cc93d5c6eb63..8ffc6a40dfe3 100644 --- a/build/lib/quarto.js +++ b/build/lib/quarto.js @@ -73,7 +73,7 @@ function getQuartoWindows(version) { verbose: true, timeoutSeconds: 90, }) - .pipe(unzip()); + .pipe(unzip({ keepEmpty: true })); } /** * Gets a stream that downloads and unpacks the quarto executable for macOS