Skip to content

Commit

Permalink
feat: allow plugins to specify if they should be transformed (#469)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
sheremet-va and antfu authored Jan 7, 2022
1 parent 51397ee commit f1b5474
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 5 deletions.
35 changes: 35 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,38 @@ Will call `.mockReset()` on all spies before each test

Will call `.mockRestore()` on all spies before each test

### transformMode

- **Type:** `{ web?, ssr? }`

Determine the transform method of modules

#### transformMode.ssr

- **Type:** `RegExp[]`
- **Default:** `[/\.([cm]?[jt]sx?|json)$/]`

Use SSR transform pipeline for the specified files.<br>
Vite plugins will receive `ssr: true` flag when processing those files.

#### transformMode.web

- **Type:** `RegExp[]`
- **Default:** *modules other than those specified in `transformMode.ssr`*

First do a normal transform pipeline (targeting browser), then then do a SSR rewrite to run the code in Node.<br>
Vite plugins will receive `ssr: false` flag when processing those files.

When you use JSX as component models other than React (e.g. Vue JSX or SolidJS), you might want to config as following to make `.tsx` / `.jsx` transformed as client-side components:

```ts
import { defineConfig } from 'vite'

export default defineConfig({
test: {
transformMode: {
web: [/\.[jt]sx$/],
},
},
})
```
17 changes: 17 additions & 0 deletions examples/vue-jsx/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "sb-vue",
"version": "0.1.0",
"private": true,
"scripts": {
"test": "vitest"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.0.1",
"@vitejs/plugin-vue-jsx": "^1.3.3",
"@vue/test-utils": "^2.0.0-rc.18",
"happy-dom": "*",
"vite": "^2.7.10",
"vue": "^3.2.26",
"vitest": "workspace:*"
}
}
30 changes: 30 additions & 0 deletions examples/vue-jsx/src/Case.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineComponent, ref, watchEffect } from 'vue'

export default defineComponent({
name: 'TestComponent',
props: {
value: String,
},
emits: ['update:value'],
setup(props, { emit }) {
const local = ref('')

watchEffect(() => {
emit('update:value', local)
})
watchEffect(() => {
local.value = props.value!
})

return {
local,
}
},
render() {
return (
<a-select v-model={[this.local, 'value']}>
<a-select-option value="aaa">aaa</a-select-option>
</a-select>
)
},
})
3 changes: 3 additions & 0 deletions examples/vue-jsx/test/__snapshots__/case.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Vitest Snapshot v1

exports[`mount component 1`] = `"<a-select-stub value=\\"test\\"></a-select-stub>"`;
16 changes: 16 additions & 0 deletions examples/vue-jsx/test/case.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { shallowMount } from '@vue/test-utils'
import { expect, test } from 'vitest'
import Case from '../src/Case'

test('mount component', () => {
const wrapper = shallowMount(Case, {
props: {
value: 'test',
},
global: {
stubs: ['a-select', 'a-select-option'],
},
})

expect(wrapper.html()).toMatchSnapshot()
})
19 changes: 19 additions & 0 deletions examples/vue-jsx/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"strictNullChecks": true,
"resolveJsonModule": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"outDir": "./dist",
"declaration": true,
"inlineSourceMap": true,
"jsx": "preserve"
},
"exclude": ["node_modules"]
}
14 changes: 14 additions & 0 deletions examples/vue-jsx/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Jsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
plugins: [Vue(), Jsx()],
test: {
global: true,
environment: 'happy-dom',
transformMode: {
web: [/.[tj]sx$/],
},
},
})
23 changes: 18 additions & 5 deletions packages/vitest/src/node/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,40 @@ export async function transformRequest(ctx: Vitest, id: string) {
return promiseMap.get(id)
}

function getTransformMode(ctx: Vitest, id: string) {
const withoutQuery = id.split('?')[0]

if (ctx.config.transformMode?.web?.some(r => withoutQuery.match(r)))
return 'web'
if (ctx.config.transformMode?.ssr?.some(r => withoutQuery.match(r)))
return 'ssr'

if (withoutQuery.match(/\.([cm]?[jt]sx?|json)$/))
return 'ssr'
return 'web'
}

async function _transformRequest(ctx: Vitest, id: string) {
let result: TransformResult | null = null

if (id.match(/\.(?:[cm]?[jt]sx?|json)$/)) {
result = await ctx.server.transformRequest(id, { ssr: true })
}
else {
const mode = getTransformMode(ctx, id)
if (mode === 'web') {
// for components like Vue, we want to use the client side
// plugins but then covert the code to be consumed by the server
result = await ctx.server.transformRequest(id)
if (result)
result = await ctx.server.ssrTransform(result.code, result.map, id)
}
else {
result = await ctx.server.transformRequest(id, { ssr: true })
}

if (result && !id.includes('node_modules'))
withInlineSourcemap(result)

if (result?.map && process.env.NODE_V8_COVERAGE)
ctx.visitedFilesMap.set(toFilePath(id, ctx.config.root), result.map as any)

// TODO: cache this result based on Vite's module graph
return result
}

Expand Down
21 changes: 21 additions & 0 deletions packages/vitest/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ export interface InlineConfig {
* @default '/__vitest__/'
*/
uiBase?: string

/**
* Determine the transform method of modules
*/
transformMode?: {
/**
* Use SSR transform pipeline for the specified files.
* Vite plugins will receive `ssr: true` flag when processing those files.
*
* @default [/\.([cm]?[jt]sx?|json)$/]
*/
ssr?: RegExp[]
/**
* First do a normal transform pipeline (targeting browser),
* then then do a SSR rewrite to run the code in Node.
* Vite plugins will receive `ssr: false` flag when processing those files.
*
* @default other than `ssr`
*/
web?: RegExp[]
}
}

export interface UserConfig extends InlineConfig {
Expand Down
Loading

0 comments on commit f1b5474

Please # to comment.