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
3 changes: 3 additions & 0 deletions docs/content/docs/reference/configuration/output.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ export default defineConfig({
|--------|------|---------|-------------|
| `type` | `'msw'` | required | Discriminator for MSW handler generation. |
| `path` | `String` | `undefined` | Output directory for this generator's mock files. Overrides the shared `mock.path` when set. When provided in `single` or `tags` mode, mock code is written to separate files (relative to `path`) instead of being inlined into the implementation file. |
| `operationResponses` | `Boolean` | `true` | Emit `get<Op>ResponseMock` factories in the MSW output. Set to `false` to generate handlers only, response fallbacks become `undefined`. No effect when a Faker generator also emits the factories, the handlers then import them from the `.faker` file. Honored in `split` and `tags-split` modes. |
| `delay` | `Number \| Function \| false` | `false` | Response delay in ms. |
| `delayFunctionLazyExecute` | `Boolean` | `false` | Execute delay function at runtime instead of at build time. |
| `baseUrl` | `String` | `''` | Base URL for the generated MSW handlers. |
Expand All @@ -631,6 +632,8 @@ export default defineConfig({

The Faker generator emits the same `get<Op>ResponseMock` factories MSW would emit, but without any `msw` dependency or HTTP handler code. Useful for tests or stories that only need fake response data.

In `split` and `tags-split` modes, configuring Faker alongside MSW moves the `get<Op>ResponseMock` factories to the `.faker` file. The `.msw` file only contains the handlers and imports (and re-exports) the factories instead of duplicating them. If the Faker generator is configured with `operationResponses: false` it emits no factories, so there is nothing to move and the `.msw` file keeps them inline.

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `type` | `'faker'` | required | Discriminator for Faker-only output. |
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ export interface CommonMockOptions {

export interface MswMockOptions extends CommonMockOptions {
type: typeof OutputMockType.MSW;
// Emit faker responses in MSW handler output, defaults to true. Disable to
// generate handlers only which require passing in mock responses, falls back
// to `undefined` if no mock response passed to handler.
operationResponses?: boolean;
// Base URL prefix for the generated MSW route matchers
baseUrl?: string;
// Response delay before MSW handlers resolve (false disables delay)
Expand Down
110 changes: 110 additions & 0 deletions packages/core/src/writers/generate-imports-for-builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,116 @@ describe('generateImportsForBuilder', () => {
});
});

describe('imports with explicit importPath', () => {
it('should group imports with the same importPath into a single dependency', () => {
const output = createMockOutput({ indexFiles: false });
const imports: GeneratorImport[] = [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
{
name: 'getUserResponseMock',
values: true,
importPath: './pets.faker',
},
];

const result = generateImportsForBuilder(output, imports, '../models');

expect(result).toEqual([
{
exports: [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
{
name: 'getUserResponseMock',
values: true,
importPath: './pets.faker',
},
],
dependency: './pets.faker',
},
]);
});

it('should separate imports with different importPaths into different dependencies', () => {
const output = createMockOutput({ indexFiles: false });
const imports: GeneratorImport[] = [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
{
name: 'getHealthResponseMock',
values: true,
importPath: './health.faker',
},
];

const result = generateImportsForBuilder(output, imports, '../models');

expect(result).toEqual([
{
exports: [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
],
dependency: './pets.faker',
},
{
exports: [
{
name: 'getHealthResponseMock',
values: true,
importPath: './health.faker',
},
],
dependency: './health.faker',
},
]);
});

it('should deduplicate imports with the same name and importPath', () => {
const output = createMockOutput({ indexFiles: false });
const imports: GeneratorImport[] = [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
];

const result = generateImportsForBuilder(output, imports, '../models');

expect(result).toEqual([
{
exports: [
{
name: 'getPetResponseMock',
values: true,
importPath: './pets.faker',
},
],
dependency: './pets.faker',
},
]);
});
});

describe('with importPath (package import specifier)', () => {
it('should use package import path with indexFiles', () => {
const output = createMockOutput({
Expand Down
25 changes: 17 additions & 8 deletions packages/core/src/writers/generate-imports-for-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,26 @@ export function generateImportsForBuilder(
);
}

const otherImports = uniqueBy(
const otherImportsMap = new Map<string, GeneratorImport[]>();
for (const imp of uniqueBy(
imports.filter(
(i): i is GeneratorImport & { importPath: string } => !!i.importPath,
),
(x) => x.name + x.importPath,
).map<GeneratorDependency>((i) => {
return {
exports: [i],
dependency: i.importPath,
};
});
(x) => `${x.name}|${x.importPath}`,
)) {
const existing = otherImportsMap.get(imp.importPath);
if (existing) {
existing.push(imp);
} else {
otherImportsMap.set(imp.importPath, [imp]);
}
}
const otherImports = [...otherImportsMap.entries()].map<GeneratorDependency>(
([dependency, exports]) => ({
exports,
dependency,
}),
);

return [...schemaImports, ...schemaFactoryDeps, ...otherImports];
}
Expand Down
Loading
Loading