Skip to content

Commit

Permalink
fix: use conditional exports (#134)
Browse files Browse the repository at this point in the history
* fix: use conditional exports

* chore: keep "dist" as build directory

* chore: remove custom webpackFinal config

* chore: prevent browser-style resolution in jsdom tests

* chore: remove old code

* chore: use "index.ts"

* fix: use environment-based tsconfig.json

* chore: remove unused imports

* fix: abstract "api.use()" to "applyRequestHandlers"

* read getWorker export

* add types for getWorker

---------

Co-authored-by: Yann Braga <yannbf@gmail.com>
  • Loading branch information
kettanaito and yannbf committed Jan 29, 2024
1 parent bf62ea8 commit 37dfc82
Show file tree
Hide file tree
Showing 20 changed files with 619 additions and 198 deletions.
12 changes: 0 additions & 12 deletions packages/docs/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const path = require('path')
/**
* @type {import('@storybook/react-webpack5').StorybookConfig}
*/
Expand All @@ -11,17 +10,6 @@ const config = {
'@storybook/preset-create-react-app',
],
staticDirs: ['../public'],
webpackFinal: async (config) => {
config.resolve = config.resolve || {}
config.resolve.alias = {
...config.resolve.alias,
'msw/native': require.resolve(
path.resolve(__dirname, '../../../node_modules/msw/lib/native/index.mjs')
),
'msw-storybook-addon': require.resolve('../../msw-addon/dist'),
}
return config
},
framework: {
name: '@storybook/react-webpack5',
options: {},
Expand Down
8 changes: 7 additions & 1 deletion packages/docs/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ module.exports = {
'jsx',
'node',
],
moduleNameMapper: { '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy' },
moduleNameMapper: {
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
'^msw-storybook-addon$': '<rootDir>/../msw-addon/dist/index.node.js',
},
resetMocks: true,
resetModules: false,
restoreMocks: false,
setupFiles: ['./src/fetch-polyfill.js'],
setupFilesAfterEnv: ['./src/setupTests.js'],
testEnvironment: 'jsdom',
testEnvironmentOptions: {
customExportConditions: [''],
},
transform: {
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': 'react-scripts/config/jest/babelTransform.js',
'^.+\\.css$': 'react-scripts/config/jest/cssTransform.js',
Expand Down
15 changes: 6 additions & 9 deletions packages/docs/src/demos/fetch/AddonOnNode.test.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @jest-environment jsdom
*/
import { render, screen } from '@testing-library/react'
import { composeStories } from '@storybook/react'

Expand All @@ -16,9 +19,7 @@ describe('Running msw-addon on node', () => {

it('renders film cards for each film', async () => {
// Story + msw addon decorator, which resets and applies the server handlers based on story parameters
render(
mswDecorator(MockedSuccess, { parameters: MockedSuccess.parameters })
)
render(mswDecorator(MockedSuccess, { parameters: MockedSuccess.parameters }))

expect(screen.getByText(/fetching star wars data/i)).toBeInTheDocument()

Expand All @@ -35,13 +36,9 @@ describe('Running msw-addon on node', () => {

it('renders error message if it cannot load the films', async () => {
// Story + msw addon decorator, which resets and applies the server handlers based on story parameters
render(
mswDecorator(MockedError, { parameters: MockedError.parameters })
)
render(mswDecorator(MockedError, { parameters: MockedError.parameters }))

const errorMsgNode = await screen.findByText(
/could not fetch star wars data/i
)
const errorMsgNode = await screen.findByText(/could not fetch star wars data/i)
expect(errorMsgNode).toBeInTheDocument()
})
})
1 change: 1 addition & 0 deletions packages/msw-addon/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# production
/dist
/build

# misc
.DS_Store
Expand Down
29 changes: 23 additions & 6 deletions packages/msw-addon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@
"name": "msw-storybook-addon",
"description": "Mock API requests in Storybook with Mock Service Worker.",
"version": "1.6.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"main": "./dist/index.browser.js",
"types": "./dist/index.browser.d.ts",
"exports": {
"browser": {
"types": "./dist/index.browser.d.ts",
"default": "./dist/index.browser.js"
},
"react-native": {
"types": "./dist/index.react-native.d.ts",
"default": "./dist/index.react-native.js"
},
"node": {
"types": "./dist/index.node.d.ts",
"default": "./dist/index.node.js"
}
},
"scripts": {
"dev": "yarn build --watch",
"build": "tsc",
"prepublishOnly": "yarn build",
"clean": "rimraf ./dist",
"build": "tsup",
"prepublishOnly": "yarn clean && yarn build",
"release": "auto shipit"
},
"files": [
Expand Down Expand Up @@ -39,8 +54,10 @@
"devDependencies": {
"@auto-it/released": "^10.32.2",
"auto": "^10.32.2",
"typescript": "^5.2.2",
"msw": "^2.0.9"
"msw": "^2.0.9",
"rimraf": "^5.0.5",
"tsup": "^8.0.1",
"typescript": "^5.2.2"
},
"peerDependencies": {
"msw": "^2.0.0"
Expand Down
34 changes: 34 additions & 0 deletions packages/msw-addon/src/applyRequestHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { RequestHandler } from 'msw'
import { api } from '@build-time/initialize'
import type { Context } from './decorator'

export function applyRequestHandlers(
handlersListOrObject: Context['parameters']['msw']
): void {
if (handlersListOrObject == null) {
return
}

if (Array.isArray(handlersListOrObject) && handlersListOrObject.length > 0) {
// Support an Array of request handlers (backwards compatability).
api.use(...handlersListOrObject)
return
}

if ('handlers' in handlersListOrObject && handlersListOrObject.handlers) {
// Support an Array named request handlers handlers
// or an Object of named request handlers with named arrays of handlers
const handlers = Object.values(handlersListOrObject.handlers)
.filter(Boolean)
.reduce<RequestHandler[]>(
(handlers, handlersList) => handlers.concat(handlersList),
[]
)

if (handlers.length > 0) {
api.use(...handlers)
}

return
}
}
22 changes: 22 additions & 0 deletions packages/msw-addon/src/decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { RequestHandler } from 'msw'
import { applyRequestHandlers } from './applyRequestHandlers'

export type MswParameters = {
msw?:
| RequestHandler[]
| {
handlers: RequestHandler[] | Record<string, RequestHandler>
}
}

export type Context = {
parameters: MswParameters
}

export const mswDecorator = <Story extends (...args: any[]) => any>(
storyFn: Story,
context: Context
) => {
applyRequestHandlers(context.parameters.msw)
return storyFn()
}
4 changes: 3 additions & 1 deletion packages/msw-addon/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './mswDecorator';
export { initialize, InitializeOptions, getWorker } from '@build-time/initialize'
export * from './decorator'
export * from './loader'
27 changes: 27 additions & 0 deletions packages/msw-addon/src/initialize.browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { RequestHandler } from 'msw'
import type { SetupWorker } from 'msw/browser'
import { setupWorker } from 'msw/browser'

export let api: SetupWorker

export type InitializeOptions = Parameters<SetupWorker['start']>[0]

export function initialize(
options?: InitializeOptions,
initialHandlers: RequestHandler[] = []
): SetupWorker {
const worker = setupWorker(...initialHandlers)
worker.start(options)
api = worker
return worker
}

export function getWorker(): SetupWorker {
if (api === undefined) {
throw new Error(
`[MSW] Failed to retrieve the worker: no active worker found. Did you forget to call "initialize"?`
)
}

return api
}
16 changes: 16 additions & 0 deletions packages/msw-addon/src/initialize.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { RequestHandler, SetupApi, LifeCycleEventsMap } from 'msw'
import type { SetupWorker } from 'msw/browser'
import type { SetupServer } from 'msw/node'

export declare var api: SetupApi<LifeCycleEventsMap>

export type InitializeOptions =
| Parameters<SetupServer['listen']>[0]
| Parameters<SetupWorker['start']>[0]

export declare function initialize(
options?: InitializeOptions,
initialHandlers?: RequestHandler[]
): SetupApi<LifeCycleEventsMap>

export declare function getWorker(): typeof api
27 changes: 27 additions & 0 deletions packages/msw-addon/src/initialize.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { RequestHandler } from 'msw'
import type { SetupServer } from 'msw/node'
import { setupServer } from 'msw/node'

export let api: SetupServer

export type InitializeOptions = Parameters<SetupServer['listen']>[0]

export function initialize(
options?: InitializeOptions,
initialHandlers: RequestHandler[] = []
): SetupServer {
const server = setupServer(...initialHandlers)
server.listen(options)
api = server
return server
}

export function getWorker(): SetupServer {
if (api === undefined) {
throw new Error(
`[MSW] Failed to retrieve the worker: no active worker found. Did you forget to call "initialize"?`
)
}

return api
}
27 changes: 27 additions & 0 deletions packages/msw-addon/src/initialize.react-native.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { RequestHandler } from 'msw'
import type { SetupServer } from 'msw/node'
import { setupServer } from 'msw/native'

export let api: SetupServer

export type InitializeOptions = Parameters<SetupServer['listen']>[0]

export function initialize(
options?: InitializeOptions,
initialHandlers: RequestHandler[] = []
): SetupServer {
const server = setupServer(...initialHandlers)
server.listen(options)
api = server
return server
}

export function getWorker(): SetupServer {
if (api === undefined) {
throw new Error(
`[MSW] Failed to retrieve the worker: no active worker found. Did you forget to call "initialize"?`
)
}

return api
}
18 changes: 18 additions & 0 deletions packages/msw-addon/src/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Context } from './decorator'
import { applyRequestHandlers } from './applyRequestHandlers'

export const mswLoader = async (context: Context) => {
applyRequestHandlers(context.parameters.msw)

if (
typeof window !== 'undefined' &&
'navigator' in window &&
navigator.serviceWorker.controller
) {
// No need to rely on the MSW Promise exactly
// since only 1 worker can control 1 scope at a time.
await navigator.serviceWorker.ready
}

return {}
}

0 comments on commit 37dfc82

Please sign in to comment.