This repository has been archived by the owner on Apr 6, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(nuxt): support vue runtime compiler (#4762)
- Loading branch information
1 parent
7b5c755
commit 3fc9a75
Showing
16 changed files
with
295 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
node_modules | ||
*.log* | ||
.nuxt | ||
.nitro | ||
.cache | ||
.output | ||
.env | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<script> | ||
export default defineNuxtComponent({ | ||
template: '<div>hello, Helloworld.vue here ! </div>' | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export default defineNuxtComponent({ | ||
props: ['template', 'name'], | ||
|
||
/** | ||
* most of the time, vue compiler need at least a VNode, use h() to render the component | ||
*/ | ||
render () { | ||
return h({ | ||
props: ['name'], | ||
template: this.template | ||
}, { | ||
name: this.name | ||
}) | ||
} | ||
}) |
35 changes: 35 additions & 0 deletions
35
test/fixtures/runtime-compiler/components/ShowTemplate.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<template> | ||
<component :is="showIt" :name="name" /> | ||
</template> | ||
|
||
<script> | ||
export default defineNuxtComponent({ | ||
props: { | ||
template: { | ||
required: true, | ||
type: String | ||
}, | ||
name: { | ||
type: String, | ||
default: () => '(missing name prop)' | ||
} | ||
}, | ||
setup (props) { | ||
const showIt = h({ | ||
template: props.template, | ||
props: { | ||
name: { | ||
type: String, | ||
default: () => '(missing name prop)' | ||
} | ||
} | ||
}) | ||
return { | ||
showIt | ||
} | ||
} | ||
}) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// https://nuxt.com/docs/api/configuration/nuxt-config | ||
export default defineNuxtConfig({ | ||
experimental: { | ||
runtimeVueCompiler: true, | ||
externalVue: false | ||
}, | ||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite' | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"private": true, | ||
"name": "fixture-runtime-compiler", | ||
"scripts": { | ||
"build": "nuxi build" | ||
}, | ||
"dependencies": { | ||
"nuxt": "workspace:*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<script setup lang="ts"> | ||
import type { Component } from 'vue' | ||
import Helloworld from '../components/Helloworld.vue' | ||
const count = ref(0) | ||
const compTemplate = computed(() => ` | ||
<div class='border'> | ||
<div>hello i am defined in the setup of app.vue</div> | ||
<div>This component template is in a computed refreshed on count</div> | ||
count: <span class="count">${count.value}</span>. | ||
I dont recommend you to do this for performance issue, prefer passing props for mutable data. | ||
</div>` | ||
) | ||
const ComponentDefinedInSetup = computed(() => h({ | ||
template: compTemplate.value | ||
}) as Component) | ||
const { data, pending } = await useAsyncData('templates', async () => { | ||
const [interactiveComponent, templateString] = await Promise.all([ | ||
$fetch('/api/full-component'), | ||
$fetch('/api/template') | ||
]) | ||
return { | ||
interactiveComponent, | ||
templateString | ||
} | ||
}, {}) | ||
const Interactive = h({ | ||
template: data.value?.interactiveComponent.template, | ||
setup (props) { | ||
// eslint-disable-next-line no-new-func | ||
return new Function( | ||
'ref', | ||
'computed', | ||
'props', | ||
data.value?.interactiveComponent.setup ?? '' | ||
)(ref, computed, props) | ||
}, | ||
props: data.value?.interactiveComponent.props | ||
}) as Component | ||
</script> | ||
|
||
<template> | ||
<!-- Edit this file to play around with Nuxt but never commit changes! --> | ||
<div> | ||
<Helloworld id="hello-world" /> | ||
<ComponentDefinedInSetup id="component-defined-in-setup" /> | ||
<button id="increment-count" @click="count++"> | ||
{{ count }} | ||
</button> | ||
<template v-if="!pending"> | ||
<Name id="name" template="<div>I am the Name.ts component</div>" /> | ||
<show-template id="show-template" :template="data?.templateString ?? ''" name="John" /> | ||
<Interactive id="interactive" lastname="Doe" firstname="John" /> | ||
</template> | ||
</div> | ||
</template> | ||
<style> | ||
.border { | ||
border: 1px solid burlywood; | ||
} | ||
</style> |
Binary file not shown.
18 changes: 18 additions & 0 deletions
18
test/fixtures/runtime-compiler/server/api/full-component.get.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* sometimes, CMS wants to give full control on components. This might not be a good practice. | ||
* SO MAKE SURE TO SANITIZE ALL YOUR STRINGS | ||
*/ | ||
export default defineEventHandler(() => { | ||
return { | ||
props: ['lastname', 'firstname'], | ||
// don't forget to sanitize | ||
setup: ` | ||
const fullName = computed(() => props.lastname + ' ' + props.firstname); | ||
const count = ref(0); | ||
return {fullName, count} | ||
`, | ||
template: '<div>my name is {{ fullName }}, <button id="inc-interactive-count" @click="count++">click here</button> count: <span id="interactive-count">{{count}}</span>. I am defined by Interactive in the setup of App.vue. My full component definition is retrieved from the api </div>' | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** | ||
* mock the behavior of nuxt retrieving data from an api | ||
*/ | ||
|
||
export default defineEventHandler(() => { | ||
return '<div>Hello my name is : {{name}}, i am defined by ShowTemplate.vue and my template is retrieved from the API</div>' | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
// https://nuxt.com/docs/guide/concepts/typescript | ||
"extends": "./.nuxt/tsconfig.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { fileURLToPath } from 'node:url' | ||
import { isWindows } from 'std-env' | ||
import { describe, it, expect } from 'vitest' | ||
import { setup, $fetch } from '@nuxt/test-utils' | ||
import { expectNoClientErrors, renderPage } from './utils' | ||
const isWebpack = process.env.TEST_BUILDER === 'webpack' | ||
|
||
await setup({ | ||
rootDir: fileURLToPath(new URL('./fixtures/runtime-compiler', import.meta.url)), | ||
dev: process.env.TEST_ENV === 'dev', | ||
server: true, | ||
browser: true, | ||
setupTimeout: (isWindows ? 240 : 120) * 1000, | ||
nuxtConfig: { | ||
builder: isWebpack ? 'webpack' : 'vite' | ||
} | ||
}) | ||
|
||
describe('test basic config', () => { | ||
it('expect render page without any error or logs', async () => { | ||
await expectNoClientErrors('/') | ||
}) | ||
|
||
it('test HelloWorld.vue', async () => { | ||
const html = await $fetch('/') | ||
const { page } = await renderPage('/') | ||
|
||
expect(html).toContain('<div id="hello-world">hello, Helloworld.vue here ! </div>') | ||
expect(await page.locator('body').innerHTML()).toContain('<div id="hello-world">hello, Helloworld.vue here ! </div>') | ||
}) | ||
|
||
it('test Name.ts', async () => { | ||
const html = await $fetch('/') | ||
const { page } = await renderPage('/') | ||
|
||
expect(html).toContain('<div id="name">I am the Name.ts component</div>') | ||
expect(await page.locator('body').innerHTML()).toContain('<div id="name">I am the Name.ts component</div>') | ||
}) | ||
|
||
it('test ShowTemplate.ts', async () => { | ||
const html = await $fetch('/') | ||
const { page } = await renderPage('/') | ||
|
||
expect(html).toContain('<div id="show-template">Hello my name is : John, i am defined by ShowTemplate.vue and my template is retrieved from the API</div>') | ||
expect(await page.locator('body').innerHTML()).toContain('<div id="show-template">Hello my name is : John, i am defined by ShowTemplate.vue and my template is retrieved from the API</div>') | ||
}) | ||
|
||
it('test Interactive component.ts', async () => { | ||
const html = await $fetch('/') | ||
const { page } = await renderPage('/') | ||
|
||
expect(html).toContain('I am defined by Interactive in the setup of App.vue. My full component definition is retrieved from the api') | ||
expect(await page.locator('#interactive').innerHTML()).toContain('I am defined by Interactive in the setup of App.vue. My full component definition is retrieved from the api') | ||
const button = page.locator('#inc-interactive-count') | ||
await button.click() | ||
const count = page.locator('#interactive-count') | ||
expect(await count.innerHTML()).toBe('1') | ||
}) | ||
}) |