Fix: Vue text interpolation in code blocks. (#2430)

* Update Vue 3 depenency and docs. Remove Vue 2.

* Fix minor lint issue

* Remove Vue 2 reference

* Swap button positions

* Update Vue2 links

* Send HTML with _blank to avoid quirks mode

* Support Vue text interpolation in code blocks

Fix #1812
This commit is contained in:
John Hildenbiddle 2024-05-19 11:49:33 -05:00 committed by GitHub
parent 0070e26da5
commit 8ef9a3cd03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 260 additions and 407 deletions

View File

@ -956,7 +956,7 @@ window.$docsify = {
- Type: `Object`
Creates and registers global [Vue components](https://vuejs.org/v2/guide/components.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site.
Creates and registers global [Vue](https://vuejs.org/guide/essentials/component-basics.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site.
```js
window.$docsify = {
@ -989,7 +989,7 @@ window.$docsify = {
- Type: `Object`
Specifies [Vue options](https://vuejs.org/v2/api/#Options-Data) for use with Vue content not explicitly mounted with [vueMounts](#mounting-dom-elements), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used.
Specifies global Vue options for use with Vue content not explicitly mounted with [vueMounts](#mounting-dom-elements), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used.
```js
window.$docsify = {
@ -1023,7 +1023,7 @@ window.$docsify = {
- Type: `Object`
Specifies DOM elements to mount as [Vue instances](https://vuejs.org/v2/guide/instance.html) and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site.
Specifies DOM elements to mount as Vue instances and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site.
```js
window.$docsify = {

View File

@ -141,7 +141,7 @@ frontend:
2. Using the user interface, import your docsify project from one of the three supported Git providers (GitHub, GitLab, or Bitbucket).
3. Navigate to the projects production environment in Stormkit or create a new environment if needed.
4. Verify the build command in your Stormkit configuration. By default, Stormkit CI will run `npm run build` but you can specify a custom build command on this page.
5. Set output folder to `docs`
5. Set output folder to `docs`
6. Click the “Deploy Now” button to deploy your site.
Read more in the [Stormkit Documentation](https://stormkit.io/docs).

View File

@ -224,7 +224,13 @@
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-nginx.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-php.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
<!-- <script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script> -->
<!-- Vue: Development -->
<!-- <script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script> -->
<!-- <script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.runtime.global.js"></script> -->
<!-- Vue: Production -->
<script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<!-- <script src="//cdn.jsdelivr.net/npm/vue@3/dist/vue.runtime.global.prod.js"></script> -->
</body>
</html>

View File

@ -1,20 +1,12 @@
# Vue compatibility
Docsify allows Vue content to be added directly to your markdown pages. This can greatly simplify working with data and adding reactivity to your site.
Docsify allows [Vue.js](https://vuejs.org) content to be added directly to your markdown pages. This can greatly simplify working with data and adding reactivity to your site.
To get started, add Vue [2.x](https://vuejs.org) or [3.x](https://v3.vuejs.org) to your `index.html` file. Choose the production version for your live site or the development version for helpful console warnings and [Vue.js devtools](https://github.com/vuejs/vue-devtools) support.
Vue [template syntax](https://vuejs.org/guide/essentials/template-syntax) can be used to add dynamic content to your pages. Vue content becomes more interesting when [data](#data), [computed properties](#computed-properties), [methods](#methods), and [lifecycle hooks](#lifecycle-hooks) are used. These options can be specified as [global options](#global-options) or within DOM [mounts](#mounts) and [components](#components).
#### Vue 2.x
## Setup
```html
<!-- Production -->
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
<!-- Development -->
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
```
#### Vue 3.x
To get started, add Vue.js to your `index.html` file. Choose the production version for your live site or the development version for helpful console warnings and [Vue.js devtools](https://github.com/vuejs/vue-devtools) support.
```html
<!-- Production -->
@ -26,7 +18,7 @@ To get started, add Vue [2.x](https://vuejs.org) or [3.x](https://v3.vuejs.org)
## Template syntax
Vue [template syntax](https://vuejs.org/v2/guide/syntax.html) is used to create dynamic content. With no additional configuration, this syntax offers several useful features like support for [JavaScript expressions](https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions) and Vue [directives](https://vuejs.org/v2/guide/syntax.html#Directives) for loops and conditional rendering.
Vue [template syntax](https://vuejs.org/guide/essentials/template-syntax) offers several useful features like support for [JavaScript expressions](https://vuejs.org/guide/essentials/template-syntax.html#using-javascript-expressions) and Vue [directives](https://vuejs.org/guide/essentials/template-syntax.html#directives) for loops and conditional rendering.
```markdown
<!-- Hide in docsify, show elsewhere (e.g. GitHub) -->
@ -53,9 +45,29 @@ Vue [template syntax](https://vuejs.org/v2/guide/syntax.html) is used to create
[View output on GitHub](https://github.com/docsifyjs/docsify/blob/develop/docs/vue.md#template-syntax)
Vue content becomes more interesting when [data](#data), [computed properties](#computed-properties), [methods](#methods), and [lifecycle hooks](#lifecycle-hooks) are used. These options can be specified as [global options](#global-options) or within DOM [mounts](#mounts) and [components](#components).
## Code Blocks
### Data
Docsify ignores Vue template syntax within code blocks by default:
````markdown
```
{{ message}}
```
````
To process Vue template syntax within a code block, wrap the code block in an element with a `v-template` attribute:
````markdown
<div v-template>
```
{{ message}}
```
</div>
````
## Data
```js
{
@ -74,23 +86,18 @@ Vue content becomes more interesting when [data](#data), [computed properties](#
<!-- Show message in docsify, hide elsewhere (e.g. GitHub) -->
<p v-text="message"></p>
<!-- Show message in docsify, show text elsewhere (e.g. GitHub) -->
<p v-text="message">Text for GitHub</p>
```
<!-- prettier-ignore-end -->
<output data-lang="output">
{{ message }}
<p>{{ message }}</p>
<p v-text="message"></p>
<p v-text="message">Text for GitHub</p>
</output>
[View output on GitHub](https://github.com/docsifyjs/docsify/blob/develop/docs/vue.md#data)
### Computed properties
## Computed properties
```js
{
@ -123,7 +130,7 @@ Good {{ timeOfDay }}!
</output>
### Methods
## Methods
```js
{
@ -148,7 +155,7 @@ Good {{ timeOfDay }}!
<p><button @click="hello">Say Hello</button></p>
</output>
### Lifecycle Hooks
## Lifecycle Hooks
```js
{
@ -193,7 +200,7 @@ Good {{ timeOfDay }}!
## Global options
Use `vueGlobalOptions` to specify [Vue options](https://vuejs.org/v2/api/#Options-Data) for use with Vue content not explicitly mounted with [vueMounts](#mounts), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used.
Use `vueGlobalOptions` to specify global Vue options for use with Vue content not explicitly mounted with [vueMounts](#mounts), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used.
```js
window.$docsify = {
@ -209,17 +216,17 @@ window.$docsify = {
```markdown
<p>
<button @click="count -= 1">-</button>
{{ count }}
<button @click="count += 1">+</button>
{{ count }}
<button @click="count -= 1">-</button>
</p>
```
<output data-lang="output">
<p>
<button @click="count -= 1">-</button>
{{ count }}
<button @click="count += 1">+</button>
{{ count }}
<button @click="count -= 1">-</button>
</p>
</output>
@ -227,9 +234,9 @@ Notice the behavior when multiple global counters are rendered:
<output data-lang="output">
<p>
<button @click="count -= 1">-</button>
{{ count }}
<button @click="count += 1">+</button>
{{ count }}
<button @click="count -= 1">-</button>
</p>
</output>
@ -237,7 +244,7 @@ Changes made to one counter affect the both counters. This is because both insta
## Mounts
Use `vueMounts` to specify DOM elements to mount as [Vue instances](https://vuejs.org/v2/guide/instance.html) and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site.
Use `vueMounts` to specify DOM elements to mount as Vue instances and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site.
```js
window.$docsify = {
@ -255,21 +262,21 @@ window.$docsify = {
```markdown
<div id="counter">
<button @click="count -= 1">-</button>
{{ count }}
<button @click="count += 1">+</button>
{{ count }}
<button @click="count -= 1">-</button>
</div>
```
<output id="counter">
<button @click="count -= 1">-</button>
{{ count }}
<button @click="count += 1">+</button>
{{ count }}
<button @click="count -= 1">-</button>
</output>
## Components
Use `vueComponents` to create and register global [Vue components](https://vuejs.org/v2/guide/components.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site.
Use `vueComponents` to create and register global [Vue components](https://vuejs.org/guide/essentials/component-basics.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site.
```js
window.$docsify = {
@ -307,17 +314,6 @@ Vue content can mounted using a `<script>` tag in your markdown pages.
!> Only the first `<script>` tag in a markdown file is executed. If you wish to mount multiple Vue instances using a script tag, all instances must be mounted within the first script tag in your markdown.
```html
<!-- Vue 2.x -->
<script>
new Vue({
el: '#example',
// Options...
});
</script>
```
```html
<!-- Vue 3.x -->
<script>
Vue.createApp({
// Options...

324
package-lock.json generated
View File

@ -22,7 +22,7 @@
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.11.5",
"@eslint/js": "^8.43.0",
"@playwright/test": "^1.39.0",
"@playwright/test": "^1.44.0",
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-node-resolve": "^15.1.0",
"@rollup/plugin-replace": "^5.0.2",
@ -55,8 +55,7 @@
"rollup": "^3.25.3",
"serve-handler": "^6.1.2",
"stylus": "^0.60.0",
"vue2": "npm:vue@^2.6.12",
"vue3": "npm:vue@^3.0.0",
"vue": "^3.4.27",
"xhr-mock": "^2.5.1"
}
},
@ -507,9 +506,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
"integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
"version": "7.24.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz",
"integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@ -2718,12 +2717,12 @@
}
},
"node_modules/@playwright/test": {
"version": "1.40.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.1.tgz",
"integrity": "sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw==",
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz",
"integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==",
"dev": true,
"dependencies": {
"playwright": "1.40.1"
"playwright": "1.44.0"
},
"bin": {
"playwright": "cli.js"
@ -3248,42 +3247,61 @@
"dev": true
},
"node_modules/@vue/compiler-core": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.11.tgz",
"integrity": "sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
"integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/shared": "3.3.11",
"@babel/parser": "^7.24.4",
"@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-core/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.11.tgz",
"integrity": "sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
"integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-core": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "2.7.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz",
"integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
"integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.18.4",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
"@babel/parser": "^7.24.4",
"@vue/compiler-core": "3.4.27",
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.10",
"postcss": "^8.4.38",
"source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-sfc/node_modules/postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true,
"funding": [
{
@ -3302,82 +3320,69 @@
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.11.tgz",
"integrity": "sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
"integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-dom": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/reactivity": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.11.tgz",
"integrity": "sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
"integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
"dev": true,
"dependencies": {
"@vue/shared": "3.3.11"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.11.tgz",
"integrity": "sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
"@vue/shared": "3.3.11",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5"
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.11.tgz",
"integrity": "sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
"integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
"dev": true,
"dependencies": {
"@vue/reactivity": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/reactivity": "3.4.27",
"@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.11.tgz",
"integrity": "sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
"integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
"dev": true,
"dependencies": {
"@vue/runtime-core": "3.3.11",
"@vue/shared": "3.3.11",
"csstype": "^3.1.2"
"@vue/runtime-core": "3.4.27",
"@vue/shared": "3.4.27",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.11.tgz",
"integrity": "sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
"integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
"dev": true,
"dependencies": {
"@vue/compiler-ssr": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-ssr": "3.4.27",
"@vue/shared": "3.4.27"
},
"peerDependencies": {
"vue": "3.3.11"
"vue": "3.4.27"
}
},
"node_modules/@vue/shared": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.11.tgz",
"integrity": "sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
"integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==",
"dev": true
},
"node_modules/abab": {
@ -10918,15 +10923,12 @@
}
},
"node_modules/magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
"version": "0.30.10",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
"integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
},
"engines": {
"node": ">=12"
}
},
"node_modules/make-dir": {
@ -11995,12 +11997,12 @@
}
},
"node_modules/playwright": {
"version": "1.40.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.1.tgz",
"integrity": "sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw==",
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz",
"integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==",
"dev": true,
"dependencies": {
"playwright-core": "1.40.1"
"playwright-core": "1.44.0"
},
"bin": {
"playwright": "cli.js"
@ -12013,9 +12015,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.40.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.1.tgz",
"integrity": "sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ==",
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz",
"integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@ -13893,9 +13895,9 @@
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@ -15159,17 +15161,16 @@
}
},
"node_modules/vue": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.11.tgz",
"integrity": "sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==",
"version": "3.4.27",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
"integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
"dev": true,
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-sfc": "3.3.11",
"@vue/runtime-dom": "3.3.11",
"@vue/server-renderer": "3.3.11",
"@vue/shared": "3.3.11"
"@vue/compiler-dom": "3.4.27",
"@vue/compiler-sfc": "3.4.27",
"@vue/runtime-dom": "3.4.27",
"@vue/server-renderer": "3.4.27",
"@vue/shared": "3.4.27"
},
"peerDependencies": {
"typescript": "*"
@ -15180,133 +15181,6 @@
}
}
},
"node_modules/vue/node_modules/@vue/compiler-sfc": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz",
"integrity": "sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==",
"dev": true,
"peer": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-ssr": "3.3.11",
"@vue/reactivity-transform": "3.3.11",
"@vue/shared": "3.3.11",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.32",
"source-map-js": "^1.0.2"
}
},
"node_modules/vue/node_modules/postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/vue2": {
"name": "vue",
"version": "2.7.15",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz",
"integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==",
"dev": true,
"dependencies": {
"@vue/compiler-sfc": "2.7.15",
"csstype": "^3.1.0"
}
},
"node_modules/vue3": {
"name": "vue",
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.3.11.tgz",
"integrity": "sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-sfc": "3.3.11",
"@vue/runtime-dom": "3.3.11",
"@vue/server-renderer": "3.3.11",
"@vue/shared": "3.3.11"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/vue3/node_modules/@vue/compiler-sfc": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.11.tgz",
"integrity": "sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.5",
"@vue/compiler-core": "3.3.11",
"@vue/compiler-dom": "3.3.11",
"@vue/compiler-ssr": "3.3.11",
"@vue/reactivity-transform": "3.3.11",
"@vue/shared": "3.3.11",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.32",
"source-map-js": "^1.0.2"
}
},
"node_modules/vue3/node_modules/postcss": {
"version": "8.4.32",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
"integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",

View File

@ -75,7 +75,7 @@
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.11.5",
"@eslint/js": "^8.43.0",
"@playwright/test": "^1.39.0",
"@playwright/test": "^1.44.0",
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-node-resolve": "^15.1.0",
"@rollup/plugin-replace": "^5.0.2",
@ -108,8 +108,7 @@
"rollup": "^3.25.3",
"serve-handler": "^6.1.2",
"stylus": "^0.60.0",
"vue2": "npm:vue@^2.6.12",
"vue3": "npm:vue@^3.0.0",
"vue": "^3.4.27",
"xhr-mock": "^2.5.1"
},
"keywords": [

View File

@ -49,7 +49,7 @@ export const testConfig = {
route: '/_blank.html',
handle(req, res, next) {
res.setHeader('Content-Type', 'text/html');
res.end('');
res.end('<!DOCTYPE html><html><body></body></html>');
next();
},
},

View File

@ -11,5 +11,5 @@ export const highlightCodeCompiler = ({ renderer }) =>
lang
);
return /* html */ `<pre v-pre data-lang="${lang}"><code class="lang-${lang}" tabindex="0">${text}</code></pre>`;
return /* html */ `<pre data-lang="${lang}"><code class="lang-${lang}" tabindex="0">${text}</code></pre>`;
});

View File

@ -202,7 +202,7 @@ export function Render(Base) {
const isVueAttr = 'data-isvue';
const isSkipElm =
// Is an invalid tag
mountElm.matches('pre, script') ||
mountElm.matches('pre, :not([v-template]):has(pre), script') ||
// Is a mounted instance
isMountedVue(mountElm) ||
// Has mounted instance(s)

View File

@ -2,10 +2,7 @@ import stripIndent from 'common-tags/lib/stripIndent/index.js';
import docsifyInit from '../helpers/docsify-init.js';
import { test, expect } from './fixtures/docsify-init-fixture.js';
const vueURLs = [
'/node_modules/vue2/dist/vue.js',
'/node_modules/vue3/dist/vue.global.js',
];
const vueURL = '/node_modules/vue/dist/vue.global.js';
test.describe('Vue.js Compatibility', () => {
function getSharedConfig() {
@ -74,14 +71,8 @@ test.describe('Vue.js Compatibility', () => {
},
}
const vueMountElm = '#vuescript';
const vueVersion = Number(window.Vue.version.charAt(0));
if (vueVersion === 2) {
new Vue(vueConfig).$mount(vueMountElm);
}
else if (vueVersion === 3) {
Vue.createApp(vueConfig).mount(vueMountElm);
}
Vue.createApp(vueConfig).mount(vueMountElm);
</script>
`,
},
@ -91,126 +82,113 @@ test.describe('Vue.js Compatibility', () => {
}
// Tests
// ---------------------------------------------------------------------------
for (const vueURL of vueURLs) {
const vueVersion = Number(vueURL.match(/vue(\d+)/)[1]); // 2|3
test.describe(`Vue v${vueVersion}`, () => {
test(`Parse templates and render content when import Vue v${vueVersion} resources`, async ({
page,
}) => {
const docsifyInitConfig = {
config: {},
markdown: {
homepage: stripIndent`
// ----------------------------------------------------------------------------
test(`Parse templates and render content when import Vue resources`, async ({
page,
}) => {
const docsifyInitConfig = {
config: {},
markdown: {
homepage: stripIndent`
<div id="vuefor"><span v-for="i in 5">{{ i }}</span></div>
`,
},
};
},
};
docsifyInitConfig.scriptURLs = vueURL;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
});
for (const executeScript of [true, undefined]) {
test(`renders content when executeScript is ${executeScript}`, async ({
page,
}) => {
const docsifyInitConfig = getSharedConfig();
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
});
docsifyInitConfig.config.executeScript = executeScript;
docsifyInitConfig.scriptURLs = vueURL;
for (const executeScript of [true, undefined]) {
test(`renders content when executeScript is ${executeScript}`, async ({
page,
}) => {
const docsifyInitConfig = getSharedConfig();
await docsifyInit(docsifyInitConfig);
docsifyInitConfig.config.executeScript = executeScript;
docsifyInitConfig.scriptURLs = vueURL;
// Static
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vueglobaloptions p')).toHaveText(
'vueglobaloptions'
);
await expect(page.locator('#vueglobaloptions > span')).toHaveText(
'0'
);
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
await expect(page.locator('#vuemounts > span')).toHaveText('0');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
await expect(page.locator('#vuescript > span')).toHaveText('0');
await docsifyInit(docsifyInitConfig);
// Reactive
await page.click('#vuecomponent');
await expect(page.locator('#vuecomponent')).toHaveText('1');
await page.click('#vueglobaloptions button');
await expect(page.locator('#vueglobaloptions > span')).toHaveText(
'1'
);
await page.click('#vuemounts button');
await expect(page.locator('#vuemounts > span')).toHaveText('1');
await page.click('#vuescript button');
await expect(page.locator('#vuescript > span')).toHaveText('1');
});
}
// Static
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vueglobaloptions p')).toHaveText(
'vueglobaloptions'
);
await expect(page.locator('#vueglobaloptions > span')).toHaveText('0');
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
await expect(page.locator('#vuemounts > span')).toHaveText('0');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
await expect(page.locator('#vuescript > span')).toHaveText('0');
test(`ignores content when Vue is not present`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
await docsifyInit(docsifyInitConfig);
await page.evaluate(() => 'Vue' in window === false);
await expect(page.locator('#vuefor')).toHaveText('{{ i }}');
await expect(page.locator('#vuecomponent')).toHaveText('---');
await expect(page.locator('#vueglobaloptions p')).toHaveText('---');
await expect(page.locator('#vuemounts p')).toHaveText('---');
await expect(page.locator('#vuescript p')).toHaveText('---');
});
test(`ignores content when vueGlobalOptions is undefined`, async ({
page,
}) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.vueGlobalOptions = undefined;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vuecomponent')).toHaveText('0');
// eslint-disable-next-line playwright/prefer-web-first-assertions
expect(await page.locator('#vueglobaloptions p').innerText()).toBe('');
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
});
test(`ignores content when vueMounts is undefined`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.vueMounts['#vuemounts'] = undefined;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vueglobaloptions p')).toHaveText(
'vueglobaloptions'
);
await expect(page.locator('#vuemounts p')).toHaveText(
'vueglobaloptions'
);
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
});
test(`ignores <script> when executeScript is false`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.executeScript = false;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuescript p')).toHaveText(
'vueglobaloptions'
);
});
// Reactive
await page.click('#vuecomponent');
await expect(page.locator('#vuecomponent')).toHaveText('1');
await page.click('#vueglobaloptions button');
await expect(page.locator('#vueglobaloptions > span')).toHaveText('1');
await page.click('#vuemounts button');
await expect(page.locator('#vuemounts > span')).toHaveText('1');
await page.click('#vuescript button');
await expect(page.locator('#vuescript > span')).toHaveText('1');
});
}
test(`ignores content when Vue is not present`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
await docsifyInit(docsifyInitConfig);
await page.evaluate(() => 'Vue' in window === false);
await expect(page.locator('#vuefor')).toHaveText('{{ i }}');
await expect(page.locator('#vuecomponent')).toHaveText('---');
await expect(page.locator('#vueglobaloptions p')).toHaveText('---');
await expect(page.locator('#vuemounts p')).toHaveText('---');
await expect(page.locator('#vuescript p')).toHaveText('---');
});
test(`ignores content when vueGlobalOptions is undefined`, async ({
page,
}) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.vueGlobalOptions = undefined;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vuecomponent')).toHaveText('0');
// eslint-disable-next-line playwright/prefer-web-first-assertions
expect(await page.locator('#vueglobaloptions p').innerText()).toBe('');
await expect(page.locator('#vuemounts p')).toHaveText('vuemounts');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
});
test(`ignores content when vueMounts is undefined`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.vueMounts['#vuemounts'] = undefined;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuefor')).toHaveText('12345');
await expect(page.locator('#vuecomponent')).toHaveText('0');
await expect(page.locator('#vueglobaloptions p')).toHaveText(
'vueglobaloptions'
);
await expect(page.locator('#vuemounts p')).toHaveText('vueglobaloptions');
await expect(page.locator('#vuescript p')).toHaveText('vuescript');
});
test(`ignores <script> when executeScript is false`, async ({ page }) => {
const docsifyInitConfig = getSharedConfig();
docsifyInitConfig.config.executeScript = false;
docsifyInitConfig.scriptURLs = vueURL;
await docsifyInit(docsifyInitConfig);
await expect(page.locator('#vuescript p')).toHaveText('vueglobaloptions');
});
});