Skip to content

Commit

Permalink
fix(vite): set ssr.noExternal even if not in project package.json (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mcous authored Oct 18, 2024
1 parent f4b1508 commit b5e0f3d
Show file tree
Hide file tree
Showing 4 changed files with 277 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/__tests__/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export const IS_JSDOM = window.navigator.userAgent.includes('jsdom')

export const IS_HAPPYDOM = !IS_JSDOM // right now it's happy or js

export const IS_JEST = Boolean(process.env.JEST_WORKER_ID)

export const IS_SVELTE_5 = SVELTE_VERSION >= '5'

export const MODE_LEGACY = 'legacy'
Expand Down
227 changes: 227 additions & 0 deletions src/__tests__/vite-plugin.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { beforeEach, describe, expect, test, vi } from 'vitest'

import { svelteTesting } from '../vite.js'
import { IS_JEST } from './utils.js'

describe.skipIf(IS_JEST)('vite plugin', () => {
beforeEach(() => {
vi.stubEnv('VITEST', '1')
})

test('does not modify config if disabled', () => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: false,
noExternal: false,
})

const result = {}
subject.config(result)

expect(result).toEqual({})
})

test('does not modify config if not Vitest', () => {
vi.stubEnv('VITEST', '')

const subject = svelteTesting()

const result = {}
subject.config(result)

expect(result).toEqual({})
})

test.each([
{
config: () => ({ resolve: { conditions: ['node'] } }),
expectedConditions: ['browser', 'node'],
},
{
config: () => ({ resolve: { conditions: ['svelte', 'node'] } }),
expectedConditions: ['svelte', 'browser', 'node'],
},
])(
'adds browser condition if necessary',
({ config, expectedConditions }) => {
const subject = svelteTesting({
resolveBrowser: true,
autoCleanup: false,
noExternal: false,
})

const result = config()
subject.config(result)

expect(result).toEqual({
resolve: {
conditions: expectedConditions,
},
})
}
)

test.each([
{
config: () => ({}),
expectedConditions: [],
},
{
config: () => ({ resolve: { conditions: [] } }),
expectedConditions: [],
},
{
config: () => ({ resolve: { conditions: ['svelte'] } }),
expectedConditions: ['svelte'],
},
])(
'skips browser condition if possible',
({ config, expectedConditions }) => {
const subject = svelteTesting({
resolveBrowser: true,
autoCleanup: false,
noExternal: false,
})

const result = config()
subject.config(result)

expect(result).toEqual({
resolve: {
conditions: expectedConditions,
},
})
}
)

test.each([
{
config: () => ({}),
expectedSetupFiles: [expect.stringMatching(/src\/vitest.js$/u)],
},
{
config: () => ({ test: { setupFiles: [] } }),
expectedSetupFiles: [expect.stringMatching(/src\/vitest.js$/u)],
},
{
config: () => ({ test: { setupFiles: 'other-file.js' } }),
expectedSetupFiles: [
'other-file.js',
expect.stringMatching(/src\/vitest.js$/u),
],
},
])('adds cleanup', ({ config, expectedSetupFiles }) => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: true,
noExternal: false,
})

const result = config()
subject.config(result)

expect(result).toEqual({
test: {
setupFiles: expectedSetupFiles,
},
})
})

test('skips cleanup in global mode', () => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: true,
noExternal: false,
})

const result = { test: { globals: true } }
subject.config(result)

expect(result).toEqual({
test: {
globals: true,
},
})
})

test.each([
{
config: () => ({ ssr: { noExternal: [] } }),
expectedNoExternal: ['@testing-library/svelte'],
},
{
config: () => ({}),
expectedNoExternal: ['@testing-library/svelte'],
},
{
config: () => ({ ssr: { noExternal: 'other-file.js' } }),
expectedNoExternal: ['other-file.js', '@testing-library/svelte'],
},
{
config: () => ({ ssr: { noExternal: /other/u } }),
expectedNoExternal: [/other/u, '@testing-library/svelte'],
},
])('adds noExternal rule', ({ config, expectedNoExternal }) => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: false,
noExternal: true,
})

const result = config()
subject.config(result)

expect(result).toEqual({
ssr: {
noExternal: expectedNoExternal,
},
})
})

test.each([
{
config: () => ({ ssr: { noExternal: true } }),
expectedNoExternal: true,
},
{
config: () => ({ ssr: { noExternal: '@testing-library/svelte' } }),
expectedNoExternal: '@testing-library/svelte',
},
{
config: () => ({ ssr: { noExternal: /svelte/u } }),
expectedNoExternal: /svelte/u,
},
])('skips noExternal if able', ({ config, expectedNoExternal }) => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: false,
noExternal: true,
})

const result = config()
subject.config(result)

expect(result).toEqual({
ssr: {
noExternal: expectedNoExternal,
},
})
})

test('bails on noExternal if input is unexpected', () => {
const subject = svelteTesting({
resolveBrowser: false,
autoCleanup: false,
noExternal: true,
})

const result = { ssr: { noExternal: false } }
subject.config(result)

expect(result).toEqual({
ssr: {
noExternal: false,
},
})
})
})
48 changes: 47 additions & 1 deletion src/vite.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import { fileURLToPath } from 'node:url'
* Ensures Svelte is imported correctly in tests
* and that the DOM is cleaned up after each test.
*
* @param {{resolveBrowser?: boolean, autoCleanup?: boolean}} options
* @param {{resolveBrowser?: boolean, autoCleanup?: boolean, noExternal?: boolean}} options
* @returns {import('vite').Plugin}
*/
export const svelteTesting = ({
resolveBrowser = true,
autoCleanup = true,
noExternal = true,
} = {}) => ({
name: 'vite-plugin-svelte-testing-library',
config: (config) => {
Expand All @@ -27,6 +28,10 @@ export const svelteTesting = ({
if (autoCleanup) {
addAutoCleanup(config)
}

if (noExternal) {
addNoExternal(config)
}
},
})

Expand Down Expand Up @@ -64,6 +69,10 @@ const addAutoCleanup = (config) => {
const test = config.test ?? {}
let setupFiles = test.setupFiles ?? []

if (test.globals) {
return
}

if (typeof setupFiles === 'string') {
setupFiles = [setupFiles]
}
Expand All @@ -73,3 +82,40 @@ const addAutoCleanup = (config) => {
test.setupFiles = setupFiles
config.test = test
}

/**
* Add `@testing-library/svelte` to Vite's noExternal rules, if not present.
*
* This ensures `@testing-library/svelte` is processed by `@sveltejs/vite-plugin-svelte`
* in certain monorepo setups.
*/
const addNoExternal = (config) => {
const ssr = config.ssr ?? {}
let noExternal = ssr.noExternal ?? []

if (noExternal === true) {
return
}

if (typeof noExternal === 'string' || noExternal instanceof RegExp) {
noExternal = [noExternal]
}

if (!Array.isArray(noExternal)) {
return
}

for (const rule of noExternal) {
if (typeof rule === 'string' && rule === '@testing-library/svelte') {
return
}

if (rule instanceof RegExp && rule.test('@testing-library/svelte')) {
return
}
}

noExternal.push('@testing-library/svelte')
ssr.noExternal = noExternal
config.ssr = ssr
}
1 change: 1 addition & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default defineConfig({
setupFiles: ['./src/__tests__/_vitest-setup.js'],
mockReset: true,
unstubGlobals: true,
unstubEnvs: true,
coverage: {
provider: 'v8',
include: ['src/**/*'],
Expand Down

0 comments on commit b5e0f3d

Please sign in to comment.