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
6 changes: 6 additions & 0 deletions .changeset/fix-bundler-worker-dataclone.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@doc-kit/core': patch
'@doc-kit/generator-react': patch
---

Exclude main-thread-only bundler options from worker thread configuration to prevent DataCloneError.
3 changes: 3 additions & 0 deletions packages/core/src/generators/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ declare global {

hasParallelProcessor?: boolean;

/** Configuration keys excluded from worker-thread messages. */
workerConfigOmit?: string[];

/**
* Receive the dependency's output as an async iterable of its chunks, as
* they are produced, instead of one collected array. With one thread, the
Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/threading/__tests__/parallel.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,47 @@ describe('createParallelWorker', () => {
deepStrictEqual(chunks, [[1, 2], [3, 4], [5]]);
});

it('does not clone main-thread-only HTML bundler functions', async () => {
const htmlGenerator = await loadGenerator('html');
const transferred = [];
const pool = {
run(task) {
const copy = structuredClone(task);
transferred.push(copy);

return Promise.resolve(copy.input);
},
};
const bundler = {
buildServer: async () => 'file:///library.mjs',
buildClient: async () => ({}),
compile: async code => code,
};
const configuration = {
threads: 2,
chunkSize: 1,
html: { title: 'Worker regression test', bundler },
};
const worker = createParallelWorker(
'html',
htmlGenerator,
pool,
configuration
);
const results = await collectStream(worker.stream([1, 2, 3], {}));

deepStrictEqual(results, [1, 2, 3]);
strictEqual(transferred.length, 3);

for (const task of transferred) {
deepStrictEqual(task.configuration.html, {
title: 'Worker regression test',
});
}

strictEqual(configuration.html.bundler, bundler);
});

describe('with streamed batches', () => {
it(
'starts a batch before the next one arrives',
Expand Down
21 changes: 18 additions & 3 deletions packages/core/src/threading/parallel.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import logger from '#logger/index.mjs';
import { isAsyncIterable } from '#utils/misc.mjs';
import { isAsyncIterable, omitKeys } from '#utils/misc.mjs';

import { CHUNKS_PER_THREAD } from './constants.mjs';

Expand Down Expand Up @@ -76,7 +76,15 @@ export default function createParallelWorker(
) {
const { threads, chunkSize } = configuration;

const { name } = generator;
const { name, workerConfigOmit } = generator;

const workerConfiguration =
workerConfigOmit?.length && configuration[name]
? {
...configuration,
[name]: omitKeys(configuration[name], workerConfigOmit),
}
: configuration;

return {
/**
Expand Down Expand Up @@ -165,7 +173,14 @@ export default function createParallelWorker(
running++;

const task = pool.run(
createTask(batch, indices, extra, configuration, specifier, name)
createTask(
batch,
indices,
extra,
workerConfiguration,
specifier,
name
)
);

tasks.push(
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/html/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,8 @@ export default {
// page count, so it is farmed out to the worker pool.
hasParallelProcessor: true,

// The bundler runs on the main thread and contains non-cloneable functions.
workerConfigOmit: ['bundler'],

processChunk,
};