Skip to content

Commit cf894dc

Browse files
authored
Merge pull request #8088 from QwikDev/revert-8072-fix-ssg-windows
Revert "fix: ssg on windows"
2 parents 6e866a0 + 5ac92f4 commit cf894dc

File tree

10 files changed

+93
-123
lines changed

10 files changed

+93
-123
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -682,10 +682,10 @@ jobs:
682682
# too slow and flaky. Perhaps better in v2?
683683
# - host: ubuntu-latest
684684
# browser: firefox
685-
- host: windows-latest
686-
browser: chromium
687685
- host: macos-latest
688686
browser: webkit
687+
# - host: windows-latest
688+
# browser: chromium
689689

690690
runs-on: ${{ matrix.settings.host }}
691691

e2e/adapters-e2e/playwright.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,5 @@ export default defineConfig({
4444
port: 3000,
4545
stdout: 'pipe',
4646
reuseExistingServer: !process.env.CI,
47-
timeout: 120000,
4847
},
4948
});

packages/qwik-city/src/adapters/shared/vite/index.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,34 @@ export function viteAdapter(opts: ViteAdapterPluginOptions) {
202202
`\n==============================================`
203203
);
204204
}
205+
if (opts.ssg !== null) {
206+
/**
207+
* HACK: for some reason the build hangs after SSG. `why-is-node-running` shows 4
208+
* culprits:
209+
*
210+
* ```
211+
* There are 4 handle(s) keeping the process running.
212+
*
213+
* # CustomGC
214+
* ./node_modules/.pnpm/[email protected]/node_modules/lightningcss/node/index.js:20 - module.exports = require(`lightningcss-${parts.join('-')}`);
215+
*
216+
* # CustomGC
217+
* ./node_modules/.pnpm/@[email protected]/node_modules/@tailwindcss/oxide/index.js:229 - return require('@tailwindcss/oxide-linux-x64-gnu')
218+
*
219+
* # Timeout
220+
* node_modules/.vite-temp/vite.config.timestamp-1755270314169-a2a97ad5233f9.mjs:357
221+
* ./node_modules/.pnpm/[email protected]_@[email protected][email protected][email protected][email protected][email protected][email protected]/node_modules/vite/dist/node/chunks/dep-CMEinpL-.js:36657 - return (await import(pathToFileURL(tempFileName).href)).default;
222+
*
223+
* # CustomGC
224+
* ./packages/qwik/dist/optimizer.mjs:1328 - const mod2 = module.default.createRequire(import.meta.url)(`../bindings/${triple.platformArchABI}`);
225+
* ```
226+
*
227+
* For now, we'll force exit the process after SSG with some delay.
228+
*/
229+
setTimeout(() => {
230+
process.exit(0);
231+
}, 5000).unref();
232+
}
205233
}
206234
},
207235
},

packages/qwik-city/src/static/main-thread.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,13 @@ export async function mainThread(sys: System) {
8080
while (!isCompleted && main.hasAvailableWorker() && queue.length > 0) {
8181
const staticRoute = queue.shift();
8282
if (staticRoute) {
83-
render(staticRoute).catch((e) => {
84-
console.error(`render failed for ${staticRoute.pathname}`, e);
85-
});
83+
render(staticRoute);
8684
}
8785
}
8886

8987
if (!isCompleted && isRoutesLoaded && queue.length === 0 && active.size === 0) {
9088
isCompleted = true;
91-
completed().catch((e) => {
92-
console.error('SSG completion failed', e);
93-
});
89+
completed();
9490
}
9591
};
9692

@@ -138,7 +134,6 @@ export async function mainThread(sys: System) {
138134

139135
flushQueue();
140136
} catch (e) {
141-
console.error(`render failed for ${staticRoute.pathname}`, e);
142137
isCompleted = true;
143138
reject(e);
144139
}
@@ -221,12 +216,8 @@ export async function mainThread(sys: System) {
221216
flushQueue();
222217
};
223218

224-
loadStaticRoutes().catch((e) => {
225-
console.error('SSG route loading failed', e);
226-
reject(e);
227-
});
219+
loadStaticRoutes();
228220
} catch (e) {
229-
console.error('SSG main thread failed', e);
230221
reject(e);
231222
}
232223
});
@@ -253,6 +244,6 @@ function validateOptions(opts: StaticGenerateOptions) {
253244
try {
254245
new URL(siteOrigin);
255246
} catch (e) {
256-
throw new Error(`Invalid "origin"`, { cause: e as Error });
247+
throw new Error(`Invalid "origin": ${e}`);
257248
}
258249
}
Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { StaticGenerateOptions } from '../types';
22
import { createSystem } from './node-system';
3-
import { isMainThread, workerData, threadId } from 'node:worker_threads';
3+
import { isMainThread, workerData } from 'node:worker_threads';
44
import { mainThread } from '../main-thread';
55
import { workerThread } from '../worker-thread';
66

@@ -15,20 +15,9 @@ export async function generate(opts: StaticGenerateOptions) {
1515
}
1616

1717
if (!isMainThread && workerData) {
18-
const opts = workerData as StaticGenerateOptions;
1918
(async () => {
20-
try {
21-
if (opts.log === 'debug') {
22-
// eslint-disable-next-line no-console
23-
console.debug(`Worker thread starting (ID: ${threadId})`);
24-
}
25-
// self initializing worker thread with workerData
26-
const sys = await createSystem(opts, threadId);
27-
await workerThread(sys);
28-
} catch (error) {
29-
console.error(`Error occurred in worker thread (ID: ${threadId}): ${error}`);
30-
}
31-
})().catch((e) => {
32-
console.error(e);
33-
});
19+
// self initializing worker thread with workerData
20+
const sys = await createSystem(workerData);
21+
await workerThread(sys);
22+
})();
3423
}

packages/qwik-city/src/static/node/node-main.ts

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import type {
1010
import fs from 'node:fs';
1111
import { cpus as nodeCpus } from 'node:os';
1212
import { Worker } from 'node:worker_threads';
13-
import { dirname, extname, isAbsolute, join, resolve } from 'node:path';
13+
import { isAbsolute, resolve } from 'node:path';
1414
import { ensureDir } from './node-system';
1515
import { normalizePath } from '../../utils/fs';
16+
import { createSingleThreadWorker } from '../worker-thread';
1617

1718
export async function createNodeMainProcess(sys: System, opts: StaticGenerateOptions) {
1819
const ssgWorkers: StaticGeneratorWorker[] = [];
@@ -50,34 +51,45 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
5051
}
5152
}
5253

53-
const createWorker = () => {
54+
const singleThreadWorker = await createSingleThreadWorker(sys);
55+
56+
const createWorker = (workerIndex: number) => {
57+
if (workerIndex === 0) {
58+
// same thread worker, don't start a new process
59+
const ssgSameThreadWorker: StaticGeneratorWorker = {
60+
activeTasks: 0,
61+
totalTasks: 0,
62+
63+
render: async (staticRoute) => {
64+
ssgSameThreadWorker.activeTasks++;
65+
ssgSameThreadWorker.totalTasks++;
66+
const result = await singleThreadWorker(staticRoute);
67+
ssgSameThreadWorker.activeTasks--;
68+
return result;
69+
},
70+
71+
terminate: async () => {},
72+
};
73+
return ssgSameThreadWorker;
74+
}
75+
5476
let terminateResolve: (() => void) | null = null;
5577
const mainTasks = new Map<string, WorkerMainTask>();
5678

5779
let workerFilePath: string | URL;
58-
let terminateTimeout: number | null = null;
5980

60-
// Launch the worker using the package's index module, which bootstraps the worker thread.
6181
if (typeof __filename === 'string') {
62-
// CommonJS path
63-
const ext = extname(__filename) || '.js';
64-
workerFilePath = join(dirname(__filename), `index${ext}`);
82+
workerFilePath = __filename;
6583
} else {
66-
// ESM path (import.meta.url)
67-
const thisUrl = new URL(import.meta.url);
68-
const pathname = thisUrl.pathname || '';
69-
let ext = '.js';
70-
if (pathname.endsWith('.ts')) {
71-
ext = '.ts';
72-
} else if (pathname.endsWith('.mjs')) {
73-
ext = '.mjs';
74-
}
84+
workerFilePath = import.meta.url;
85+
}
7586

76-
workerFilePath = new URL(`./index${ext}`, thisUrl);
87+
if (typeof workerFilePath === 'string' && workerFilePath.startsWith('file://')) {
88+
workerFilePath = new URL(workerFilePath);
7789
}
7890

7991
const nodeWorker = new Worker(workerFilePath, { workerData: opts });
80-
nodeWorker.unref();
92+
8193
const ssgWorker: StaticGeneratorWorker = {
8294
activeTasks: 0,
8395
totalTasks: 0,
@@ -104,9 +116,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
104116
terminateResolve = resolve;
105117
nodeWorker.postMessage(msg);
106118
});
107-
terminateTimeout = setTimeout(async () => {
108-
await nodeWorker.terminate();
109-
}, 1000) as unknown as number;
119+
await nodeWorker.terminate();
110120
},
111121
};
112122

@@ -136,11 +146,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
136146
});
137147

138148
nodeWorker.on('exit', (code) => {
139-
if (terminateTimeout) {
140-
clearTimeout(terminateTimeout);
141-
terminateTimeout = null;
142-
}
143-
if (code !== 0) {
149+
if (code !== 1) {
144150
console.error(`worker exit ${code}`);
145151
}
146152
});
@@ -194,15 +200,9 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
194200
console.error(e);
195201
}
196202
}
197-
198-
await Promise.all(promises);
199203
ssgWorkers.length = 0;
200204

201-
// On Windows, give extra time for all workers to fully exit
202-
// This prevents resource conflicts in back-to-back builds
203-
if (process.platform === 'win32') {
204-
await new Promise((resolve) => setTimeout(resolve, 300));
205-
}
205+
await Promise.all(promises);
206206
};
207207

208208
if (sitemapOutFile) {
@@ -214,11 +214,7 @@ export async function createNodeMainProcess(sys: System, opts: StaticGenerateOpt
214214
}
215215

216216
for (let i = 0; i < maxWorkers; i++) {
217-
ssgWorkers.push(createWorker());
218-
// On Windows, add delay between worker creation to avoid resource contention
219-
if (process.platform === 'win32' && i < maxWorkers - 1) {
220-
await new Promise((resolve) => setTimeout(resolve, 100));
221-
}
217+
ssgWorkers.push(createWorker(i));
222218
}
223219

224220
const mainCtx: MainContext = {

packages/qwik-city/src/static/node/node-system.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
import type { StaticGenerateOptions, System } from '../types';
33
import fs from 'node:fs';
44
import { dirname, join } from 'node:path';
5+
import { patchGlobalThis } from '../../middleware/node/node-fetch';
56
import { createNodeMainProcess } from './node-main';
67
import { createNodeWorkerProcess } from './node-worker';
78
import { normalizePath } from '../../utils/fs';
89

910
/** @public */
10-
export async function createSystem(
11-
opts: StaticGenerateOptions,
12-
threadId?: number
13-
): Promise<System> {
11+
export async function createSystem(opts: StaticGenerateOptions) {
12+
patchGlobalThis();
13+
1414
const createWriteStream = (filePath: string) => {
1515
return fs.createWriteStream(filePath, {
1616
flags: 'w',
@@ -29,13 +29,6 @@ export async function createSystem(
2929
};
3030

3131
const createLogger = async () => {
32-
if (threadId !== undefined) {
33-
return {
34-
debug: opts.log === 'debug' ? console.debug.bind(console, `[${threadId}]`) : () => {},
35-
error: console.error.bind(console, `[${threadId}]`),
36-
info: console.info.bind(console, `[${threadId}]`),
37-
};
38-
}
3932
return {
4033
debug: opts.log === 'debug' ? console.debug.bind(console) : () => {},
4134
error: console.error.bind(console),

packages/qwik-city/src/static/node/node-worker.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,5 @@ export async function createNodeWorkerProcess(
66
) {
77
parentPort?.on('message', async (msg: WorkerInputMessage) => {
88
parentPort?.postMessage(await onMessage(msg));
9-
if (msg.type === 'close') {
10-
parentPort?.close();
11-
}
129
});
1310
}

packages/qwik-city/src/static/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export interface System {
66
createMainProcess: (() => Promise<MainContext>) | null;
77
createWorkerProcess: (
88
onMessage: (msg: WorkerInputMessage) => Promise<WorkerOutputMessage>
9-
) => void | Promise<void>;
9+
) => void;
1010
createLogger: () => Promise<Logger>;
1111
getOptions: () => StaticGenerateOptions;
1212
ensureDir: (filePath: string) => Promise<void>;

0 commit comments

Comments
 (0)