Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/nitro-forward-externals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/nitro": minor
---

Forward string entries from Nitro's `externals.external` config to the workflow builder's esbuild `external` option.
14 changes: 14 additions & 0 deletions packages/nitro/src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,26 @@ import {
import type { Nitro } from 'nitro/types';
import { join } from 'pathe';

/**
* Forward string entries from Nitro's `externals.external` config to the
* workflow builder's esbuild `external` option. RegExp and function entries
* are skipped since esbuild's `external` only supports literal strings.
*/
function getNitroStringExternals(nitro: Nitro): string[] | undefined {
const externals = nitro.options.externals?.external?.filter(
(entry): entry is string => typeof entry === 'string'
);
return externals && externals.length > 0 ? externals : undefined;
}

export class VercelBuilder extends VercelBuildOutputAPIBuilder {
constructor(nitro: Nitro) {
super({
...createBaseBuilderConfig({
workingDir: nitro.options.rootDir,
dirs: ['.'], // Different apps that use nitro have different directories
runtime: nitro.options.workflow?.runtime,
externalPackages: getNitroStringExternals(nitro),
}),
buildTarget: 'vercel-build-output-api',
});
Expand All @@ -41,6 +54,7 @@ export class LocalBuilder extends BaseBuilder {
workingDir: nitro.options.rootDir,
watch: nitro.options.dev,
dirs: ['.'], // Different apps that use nitro have different directories
externalPackages: getNitroStringExternals(nitro),
}),
buildTarget: 'next', // Placeholder, not actually used
});
Expand Down
57 changes: 55 additions & 2 deletions packages/nitro/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { describe, expect, it } from 'vitest';
import { LocalBuilder, VercelBuilder } from './builders.js';
import nitroModule from './index.js';

function createNitroStub({ routing }: { routing: boolean }) {
function createNitroStub({
routing,
externals,
}: {
routing: boolean;
externals?: {
external?: Array<string | RegExp | ((id: string) => boolean)>;
};
}) {
return {
routing,
options: {
alias: {},
buildDir: '/tmp/.nitro',
dev: false,
externals: {},
externals: externals ?? {},
handlers: [],
preset: 'node-server',
rootDir: '/tmp/project',
Expand Down Expand Up @@ -47,3 +56,47 @@ describe('@workflow/nitro virtual handlers', () => {
);
});
});

describe('@workflow/nitro externals forwarding', () => {
for (const [label, Builder] of [
['VercelBuilder', VercelBuilder],
['LocalBuilder', LocalBuilder],
] as const) {
describe(label, () => {
it('leaves externalPackages undefined when nitro externals are empty', () => {
const nitro = createNitroStub({ routing: true });
const builder = new Builder(nitro) as any;
expect(builder.config.externalPackages).toBeUndefined();
});

it('forwards string entries from nitro.options.externals.external', () => {
const nitro = createNitroStub({
routing: true,
externals: { external: ['fsevents', 'pg'] },
});
const builder = new Builder(nitro) as any;
expect(builder.config.externalPackages).toEqual(['fsevents', 'pg']);
});

it('skips RegExp and function entries', () => {
const nitro = createNitroStub({
routing: true,
externals: {
external: [/pkg/, () => true, 'fsevents'],
},
});
const builder = new Builder(nitro) as any;
expect(builder.config.externalPackages).toEqual(['fsevents']);
});

it('leaves externalPackages undefined when all entries are non-strings', () => {
const nitro = createNitroStub({
routing: true,
externals: { external: [/pkg/, () => true] },
});
const builder = new Builder(nitro) as any;
expect(builder.config.externalPackages).toBeUndefined();
});
});
}
});
Loading