Skip to content

Commit e2ff04a

Browse files
authored
Merge pull request #8385 from ianlet/fix/deno-production-builds
fix(qwik-city): support Deno as package manager for production builds
2 parents 9bf82cc + 0314b61 commit e2ff04a

File tree

6 files changed

+62
-32
lines changed

6 files changed

+62
-32
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@builder.io/qwik': patch
3+
'@builder.io/qwik-city': patch
4+
---
5+
6+
FIX: support Deno as package manager for production builds. The Vite plugin now recognizes Deno as a Node-compatible runtime for manifest passing, and SSG delegates to the Node implementation instead of stubbing out.

packages/qwik-city/src/static/deno/index.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ export async function generate(opts: StaticGenerateOptions) {
2121
export type { StaticGenerateOptions, StaticGenerateRenderOptions, StaticGenerateResult };
2222

2323
function getEntryModulePath() {
24-
if (isDeno()) {
25-
return './deno.mjs';
26-
}
27-
if (isNode() || isBun()) {
24+
if (isNode() || isBun() || isDeno()) {
2825
if (isCjs()) {
2926
return './node.cjs';
3027
}

packages/qwik/src/optimizer/src/plugins/vite.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
OptimizerOptions,
88
OptimizerSystem,
99
QwikManifest,
10+
SystemEnvironment,
1011
TransformModule,
1112
} from '../types';
1213
import { type BundleGraphAdder } from './bundle-graph';
@@ -191,7 +192,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any {
191192
pluginOpts.input = viteConfig.build?.lib.entry;
192193
}
193194
}
194-
if (sys.env === 'node' || sys.env === 'bun') {
195+
if (hasNodeCompat(sys.env)) {
195196
const fs: typeof import('fs') = await sys.dynamicImport('node:fs');
196197

197198
try {
@@ -551,7 +552,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any {
551552
);
552553

553554
const sys = qwikPlugin.getSys();
554-
if (tmpClientManifestPath && (sys.env === 'node' || sys.env === 'bun')) {
555+
if (tmpClientManifestPath && hasNodeCompat(sys.env)) {
555556
// Client build should write the manifest to a tmp dir
556557
const fs: typeof import('fs') = await sys.dynamicImport('node:fs');
557558
await fs.promises.writeFile(tmpClientManifestPath, clientManifestStr);
@@ -566,7 +567,7 @@ export function qwikVite(qwikViteOpts: QwikVitePluginOptions = {}): any {
566567
// ssr build
567568

568569
const sys = qwikPlugin.getSys();
569-
if (sys.env === 'node' || sys.env === 'bun') {
570+
if (hasNodeCompat(sys.env)) {
570571
const outputs = Object.keys(rollupBundle);
571572

572573
// In order to simplify executing the server script with a common script
@@ -758,7 +759,7 @@ const findQwikRoots = async (
758759
packageJsonDir: string
759760
): Promise<QwikPackages[]> => {
760761
const paths = new Map<string, string>();
761-
if (sys.env === 'node' || sys.env === 'bun') {
762+
if (hasNodeCompat(sys.env)) {
762763
const fs: typeof import('fs') = await sys.dynamicImport('node:fs');
763764
let prevPackageJsonDir: string | undefined;
764765
do {
@@ -819,6 +820,9 @@ export const isNotNullable = <T>(v: T): v is NonNullable<T> => {
819820
return v != null;
820821
};
821822

823+
/** Whether the runtime supports Node standard library APIs (node:fs, node:os, etc.). */
824+
const hasNodeCompat = (env: SystemEnvironment) => env === 'node' || env === 'bun' || env === 'deno';
825+
822826
const VITE_CLIENT_MODULE = `@builder.io/qwik/vite-client`;
823827
const CLIENT_DEV_INPUT = 'entry.dev';
824828

packages/qwik/src/optimizer/src/plugins/vite.unit.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ const chunkInfoMocks = [
2222
},
2323
] as Rollup.PreRenderedChunk[];
2424

25-
function mockOptimizerOptions(): OptimizerOptions {
25+
function mockOptimizerOptions(env: 'node' | 'deno' = 'node'): OptimizerOptions {
2626
return {
2727
sys: {
2828
cwd: () => process.cwd(),
29-
env: 'node',
29+
env,
3030
os: process.platform,
3131
dynamicImport: async (path) => import(path),
3232
strictDynamicImport: async (path) => import(path),
@@ -417,6 +417,51 @@ test('should use build.outDir config when assetsDir is _astro', async () => {
417417
assert.equal(c.build.outDir, normalizePath(resolve(cwd, `dist/`)));
418418
});
419419

420+
test('command: build, mode: production (deno)', async () => {
421+
const initOpts = {
422+
optimizerOptions: mockOptimizerOptions('deno'),
423+
};
424+
const plugin = getPlugin(initOpts);
425+
const c = (await plugin.config.call(
426+
configHookPluginContext,
427+
{},
428+
{ command: 'build', mode: 'production' }
429+
))!;
430+
const opts = await plugin.api?.getOptions();
431+
432+
assert.deepEqual(opts.target, 'client');
433+
assert.deepEqual(opts.buildMode, 'production');
434+
assert.deepEqual(opts.resolveQwikBuild, true);
435+
436+
// Deno should produce the same config shape as Node
437+
const build = c.build!;
438+
assert.deepEqual(build.outDir, normalizePath(resolve(cwd, 'dist')));
439+
assert.deepEqual(build.dynamicImportVarsOptions?.exclude, [/./]);
440+
assert.deepEqual(build.ssr, undefined);
441+
});
442+
443+
test('command: build, --ssr entry.server.tsx (deno)', async () => {
444+
const initOpts = {
445+
optimizerOptions: mockOptimizerOptions('deno'),
446+
};
447+
const plugin = getPlugin(initOpts);
448+
const c = (await plugin.config.call(
449+
configHookPluginContext,
450+
{ build: { ssr: resolve(cwd, 'src', 'entry.server.tsx') } },
451+
{ command: 'build', mode: '' }
452+
))!;
453+
const opts = await plugin.api?.getOptions();
454+
455+
assert.deepEqual(opts.target, 'ssr');
456+
assert.deepEqual(opts.buildMode, 'development');
457+
assert.deepEqual(opts.entryStrategy, { type: 'hoist' });
458+
459+
const build = c.build!;
460+
assert.deepEqual(build.outDir, normalizePath(resolve(cwd, 'server')));
461+
assert.deepEqual(build.ssr, true);
462+
assert.deepEqual(c.publicDir, false);
463+
});
464+
420465
test('command: build, --mode lib', async () => {
421466
const initOpts = {
422467
optimizerOptions: mockOptimizerOptions(),

scripts/qwik-city.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export async function buildQwikCity(config: BuildConfig) {
3535
buildMiddlewareFirebase(config),
3636
buildStatic(config),
3737
buildStaticNode(config),
38-
buildStaticDeno(config),
3938
]);
4039

4140
await buildRuntime(config);
@@ -637,19 +636,6 @@ async function buildStatic(config: BuildConfig) {
637636
});
638637
}
639638

640-
async function buildStaticDeno(config: BuildConfig) {
641-
const entryPoints = [join(config.srcQwikCityDir, 'static', 'deno', 'index.ts')];
642-
643-
await build({
644-
entryPoints,
645-
outfile: join(config.distQwikCityPkgDir, 'static', 'deno.mjs'),
646-
bundle: true,
647-
platform: 'neutral',
648-
format: 'esm',
649-
plugins: [resolveRequestHandler('../middleware/request-handler/index.mjs')],
650-
});
651-
}
652-
653639
async function buildStaticNode(config: BuildConfig) {
654640
const entryPoints = [join(config.srcQwikCityDir, 'static', 'node', 'index.ts')];
655641

0 commit comments

Comments
 (0)