From 181d0068b7ec8fcd76de94f78cec9526b0415ce2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Apr 2026 14:04:26 +0000 Subject: [PATCH 1/2] fix(config): drain dist-archive substreams before zipping The release `jspsych.zip` archive has been silently dropping the alphabetical tail of `dist/*.js` (e.g. `plugin-survey-multi-*`, `plugin-video-*`, `plugin-virtual-chinrest`, `plugin-visual-search-circle`, the `plugin-webgazer-*` plugins) and some `examples/*.html` files since the gulp examples-source split for the binary-encoding fix. Under gulp 5 (streamx-based Vinyl), `merge-stream` races with `gulp-zip`: the zip sink finalizes before slow upstreams flush, notably the 3 MB `plugin-survey/dist/index.browser.js`. The cutoff drifts run-to-run. Drain each substream into a vinyl array first, then feed `gulp-zip` from a single `Readable.from(...)` so every file is in hand before the archive starts. --- .changeset/dist-archive-no-merge-stream.md | 5 ++ packages/config/gulp.js | 93 +++++++++++++--------- packages/config/package.json | 1 - 3 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 .changeset/dist-archive-no-merge-stream.md diff --git a/.changeset/dist-archive-no-merge-stream.md b/.changeset/dist-archive-no-merge-stream.md new file mode 100644 index 0000000000..ea18231141 --- /dev/null +++ b/.changeset/dist-archive-no-merge-stream.md @@ -0,0 +1,5 @@ +--- +"@jspsych/config": patch +--- + +Fix `createCoreDistArchive` dropping plugins from `dist.zip`. Recent release archives were missing the alphabetical tail of `dist/*.js` (e.g. `plugin-survey-multi-*`, `plugin-video-*`, `plugin-virtual-chinrest`, `plugin-visual-search-circle`, the `plugin-webgazer-*` plugins) along with some `examples/*.html` files. Under gulp 5's streamx-based Vinyl, `merge-stream` would race with `gulp-zip` and finalize the archive before slow upstreams (notably the 3 MB `plugin-survey/dist/index.browser.js`) had finished flushing. The task now drains every substream first and feeds the collected files into `gulp-zip` from a single ordered stream. diff --git a/packages/config/gulp.js b/packages/config/gulp.js index a1fe73ba73..082726588f 100644 --- a/packages/config/gulp.js +++ b/packages/config/gulp.js @@ -1,5 +1,6 @@ import { readFileSync } from "fs"; import { sep as pathSeparator } from "path"; +import { Readable } from "stream"; import glob from "glob"; import gulp from "gulp"; @@ -7,10 +8,21 @@ import file from "gulp-file"; import rename from "gulp-rename"; import replace from "gulp-replace"; import zip from "gulp-zip"; -import merge from "merge-stream"; const { dest, src } = gulp; +// Drains a Vinyl object stream and resolves with the emitted files. We collect +// every substream up front instead of feeding `gulp-zip` through `merge-stream` +// because under gulp 5 (streamx-based Vinyl) the zip sink can finalize before +// slow upstreams flush, silently dropping files from `dist.zip`. +const collectFiles = (stream) => + new Promise((resolve, reject) => { + const files = []; + stream.on("data", (vinylFile) => files.push(vinylFile)); + stream.on("end", () => resolve(files)); + stream.on("error", reject); + }); + const readJsonFile = (filename) => JSON.parse(readFileSync(filename, "utf8")); const getAllPackages = () => @@ -63,60 +75,67 @@ const getVersionFileContents = () => "", ].join("\n"); -export const createCoreDistArchive = () => - merge( +export const createCoreDistArchive = async () => { + const fileGroups = await Promise.all([ // index.browser.js files - src("packages/*/dist/index.browser.js", { root: "packages/" }) - // Rename dist files - .pipe( - rename((path) => { - const packageName = path.dirname.split(pathSeparator)[0]; - - path.dirname = "/dist"; - path.basename = packageName; - }) - ) - // Remove sourceMappingURL comments - .pipe(replace(/\/\/# sourceMappingURL=.*\n/g, "")), + collectFiles( + src("packages/*/dist/index.browser.js", { root: "packages/" }) + // Rename dist files + .pipe( + rename((path) => { + const packageName = path.dirname.split(pathSeparator)[0]; + + path.dirname = "/dist"; + path.basename = packageName; + }) + ) + // Remove sourceMappingURL comments + .pipe(replace(/\/\/# sourceMappingURL=.*\n/g, "")) + ), // jspsych.css - src("packages/jspsych/css/jspsych.css").pipe(rename("/dist/jspsych.css")), + collectFiles(src("packages/jspsych/css/jspsych.css").pipe(rename("/dist/jspsych.css"))), // survey.css - src("packages/plugin-survey/css/survey.css").pipe(rename("/dist/survey.css")), - src("packages/plugin-survey/css/survey.min.css").pipe(rename("/dist/survey.min.css")), + collectFiles(src("packages/plugin-survey/css/survey.css").pipe(rename("/dist/survey.css"))), + collectFiles( + src("packages/plugin-survey/css/survey.min.css").pipe(rename("/dist/survey.min.css")) + ), // Examples HTML files - src(["examples/**/*.html"], { base: "." }) - // Rewrite script source paths - .pipe( - replace( - /