Repository: antfu/unplugin-vue2-script-setup
Branch: main
Commit: 1aaaf584be97
Files: 103
Total size: 109.5 KB
Directory structure:
gitextract_7eas0mg5/
├── .eslintignore
├── .eslintrc.json
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .npmrc
├── .tazerc.json
├── LICENSE
├── README.md
├── esbuild.d.ts
├── examples/
│ └── vue-cli/
│ ├── .npmrc
│ ├── babel.config.js
│ ├── package.json
│ ├── public/
│ │ └── index.html
│ ├── src/
│ │ ├── App.vue
│ │ ├── components/
│ │ │ └── HelloWorld.vue
│ │ ├── main.ts
│ │ └── shims-vue.d.ts
│ ├── tsconfig.json
│ └── vue.config.cjs
├── index.d.ts
├── jest.js
├── nuxt.d.ts
├── package.json
├── playground/
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── Async.vue
│ │ ├── Bar.vue
│ │ ├── ButtonTest.vue
│ │ ├── Foo.vue
│ │ ├── HelloWorld.vue
│ │ ├── main.ts
│ │ └── shims-vue.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── pnpm-workspace.yaml
├── ref-macros.d.ts
├── rollup.config.mjs
├── rollup.d.ts
├── scripts/
│ └── postbuild.ts
├── shims.d.ts
├── src/
│ ├── core/
│ │ ├── babel.ts
│ │ ├── identifiers.ts
│ │ ├── index.ts
│ │ ├── macros.ts
│ │ ├── options.ts
│ │ ├── parseSFC.ts
│ │ ├── transform.ts
│ │ ├── transformScriptSetup.ts
│ │ ├── transformSfcRefSugar.ts
│ │ └── utils.ts
│ ├── esbuild.ts
│ ├── index.ts
│ ├── lib.ts
│ ├── nuxt.ts
│ ├── rollup.ts
│ ├── types.ts
│ ├── vite.ts
│ └── webpack.ts
├── test/
│ ├── __snapshots__/
│ │ └── transform.test.ts.snap
│ ├── errors.test.ts
│ ├── fixtures/
│ │ ├── AsyncImport.vue
│ │ ├── ComponentsDirectives.vue
│ │ ├── ComponentsDirectivesLocal.vue
│ │ ├── ComponentsLocal.vue
│ │ ├── DynamicStyle.vue
│ │ ├── Empty.vue
│ │ ├── Enum.vue
│ │ ├── HtmlTag.vue
│ │ ├── HtmlTag2.vue
│ │ ├── JSLongComment.vue
│ │ ├── Macros.vue
│ │ ├── MacrosDefineExpose.vue
│ │ ├── MacrosPure.vue
│ │ ├── MacrosType.vue
│ │ ├── MacrosType2.vue
│ │ ├── MacrosType3.vue
│ │ ├── MacrosType4.vue
│ │ ├── MacrosType5.vue
│ │ ├── MacrosType6.vue
│ │ ├── MacrosTypeAny.vue
│ │ ├── Object1.vue
│ │ ├── ObjectDestructure.vue
│ │ ├── Pug1.vue
│ │ ├── RefSugar.ts
│ │ ├── RefSugar.vue
│ │ ├── RefSugarScriptSetup.vue
│ │ ├── ScriptLessThanOrEqualTo.vue
│ │ ├── ScriptOnly.vue
│ │ ├── TemplateOnly.vue
│ │ ├── TemplateOptionalChaining.vue
│ │ ├── VFor.vue
│ │ └── VariableBinding.vue
│ ├── identifiers.test.ts
│ ├── nativeTag.test.ts
│ ├── transform.test.ts
│ └── transform_filter.test.ts
├── tsconfig.json
├── types.d.ts
├── vite.d.ts
└── webpack.d.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
dist
node_modules
*.vue
================================================
FILE: .eslintrc.json
================================================
{
"extends": "@antfu"
}
================================================
FILE: .github/FUNDING.yml
================================================
github: [antfu]
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: lts/*
registry-url: https://registry.npmjs.org/
- run: npm i -g pnpm @antfu/ni
- run: nci
- run: nr test --if-present
# - run: npm publish --access public
# env:
# NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
- run: npx conventional-github-releaser -p angular
env:
CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node: [18.x]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Install pnpm
uses: pnpm/action-setup@v2
- name: Set node version to ${{ matrix.node }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
cache: pnpm
- uses: actions/checkout@v2
- name: Install
run: pnpm i
- name: Lint
run: pnpm run lint
- name: Build
run: pnpm run build
- name: Test
run: pnpm run test
- name: Build Examples
run: pnpm run build:examples
================================================
FILE: .gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE
.idea
temp.ts
.output
================================================
FILE: .npmrc
================================================
ignore-workspace-root-check=true
strict-peer-dependencies=false
================================================
FILE: .tazerc.json
================================================
{
"exclude": [
"vue",
"htmlparser2"
]
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# unplugin-vue2-script-setup
[](https://www.npmjs.com/package/unplugin-vue2-script-setup)
Bring [`<script setup>`](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to Vue 2. Works for Vite, Nuxt, Vue CLI, Webpack, esbuild and more, powered by [unplugin](https://github.com/unjs/unplugin).
> ⚠️ With the release of [Vue 2.7](https://blog.vuejs.org/posts/vue-2-7-naruto.html), which has Composition API and `<script setup>` built-in, **you no longer need this plugin**. Thereby this plugin has entered maintenance mode and will only support Vue 2.6 or earlier. This project will reach End of Life by the end of 2022.
## Install
```bash
npm i -D unplugin-vue2-script-setup
npm i @vue/composition-api
```
Install [`@vue/composition-api`](https://github.com/vuejs/composition-api) in your App's entry (it enables the `setup()` hook):
```ts
import Vue from 'vue'
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
```
<details>
<summary>Vite</summary><br>
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import { createVuePlugin as Vue2 } from 'vite-plugin-vue2'
import ScriptSetup from 'unplugin-vue2-script-setup/vite'
export default defineConfig({
plugins: [
Vue2(),
ScriptSetup({ /* options */ }),
],
})
```
Example: [`playground/`](./playground/)
<br></details>
<details>
<summary>Nuxt</summary><br>
> It's built-in in [Nuxt Bridge](https://github.com/nuxt/bridge).
</details>
<details>
<summary>Vue CLI</summary><br>
```ts
// vue.config.js
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default
module.exports = {
parallel: false, // disable thread-loader, which is not compactible with this plugin
configureWebpack: {
plugins: [
ScriptSetup({ /* options */ }),
],
},
}
```
Example: [`examples/vue-cli`](./examples/vue-cli)
###### TypeScript
To use TypeScript with Vue CLI, install `@vue/cli-plugin-typescript` but disable the type check:
```bash
npm i -D @vue/cli-plugin-typescript vue-tsc
```
```ts
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default
module.exports = {
parallel: false,
configureWebpack: {
plugins: [
ScriptSetup({ /* options */ }),
],
},
chainWebpack(config) {
// disable type check and let `vue-tsc` handles it
config.plugins.delete('fork-ts-checker')
},
}
```
And then use [`vue-tsc`](https://github.com/johnsoncodehk/volar) to do the type check at build time:
```jsonc
// package.json
{
"scripts": {
"dev": "vue-cli-service serve",
"build": "vue-tsc --noEmit && vue-cli-service build"
}
}
```
<br></details>
<details>
<summary>Webpack</summary><br>
```ts
// webpack.config.js
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default
module.exports = {
/* ... */
plugins: [
ScriptSetup({ /* options */ }),
]
}
```
<br></details>
<details>
<summary>Rollup</summary><br>
```ts
// rollup.config.js
import Vue from 'rollup-plugin-vue'
import ScriptSetup from 'unplugin-vue2-script-setup/rollup'
export default {
plugins: [
Vue(),
ScriptSetup({ /* options */ }),
]
}
```
<br></details>
<details>
<summary>esbuild</summary><br>
```ts
// esbuild.config.js
import { build } from 'esbuild'
import ScriptSetup from 'unplugin-vue2-script-setup/esbuild'
build({
/* ... */
plugins: [
ScriptSetup({
/* options */
}),
],
})
```
<br></details>
<details>
<summary>Jest</summary><br>
```bash
npm i -D vue-jest
```
```ts
// jest.config.js
module.exports = {
transform: {
'.*\\.(vue)$': 'unplugin-vue2-script-setup/jest',
},
}
```
<br></details>
<details>
<summary>JavaScript API</summary><br>
```ts
import { transform } from 'unplugin-vue2-script-setup'
const Vue2SFC = await transform(`
<template>
<!-- ... -->
</template>
<script setup>
// ...
</script>
`)
```
<br></details>
## IDE
We recommend using [VS Code](https://code.visualstudio.com/) with [Volar](https://github.com/johnsoncodehk/volar) to get the best experience (You might want to disable Vetur if you have it).
When using Volar, you need to install `@vue/runtime-dom` as devDependencies to make it work on Vue 2.
```bash
npm i -D @vue/runtime-dom
```
[Learn more](https://github.com/johnsoncodehk/volar#using)
###### Global Types
If the global types are missing for your IDE, update your `tsconfig.json` with:
```jsonc
{
"compilerOptions": {
"types": [
"unplugin-vue2-script-setup/types"
]
}
}
```
###### Support Vue 2 template
Volar preferentially supports Vue 3. Vue 3 and Vue 2 template has some different. You need to set the `experimentalCompatMode` option to support Vue 2 template.
```jsonc
{
"compilerOptions": {
// ...
},
"vueCompilerOptions": {
"target": 2
}
}
```
###### ESLint
If you are using ESLint, you might get `@typescript-eslint/no-unused-vars` warning with `<script setup>`. You can disable it and add `noUnusedLocals: true` in your `tsconfig.json`, Volar will infer the real missing locals correctly for you.
## Configurations
<details>
<summary>
Ref Sugar (take 2)
</summary>
In v0.5.x, we shipped the **experimental** [Ref Sugar (take 2)](https://github.com/vuejs/rfcs/discussions/369) implementation based on Vue 3's [`@vue/reactivity-transform`](https://github.com/vuejs/vue-next/tree/master/packages/reactivity-transform) package. Notice the syntax is not settled yet and might be changed in the future updates. **Use at your own risk!**
To enabled it, pass the option:
```ts
ScriptSetup({
reactivityTransform: true
})
```
To get TypeScript support, update your `tsconfig.json` with:
```jsonc
{
"compilerOptions": {
"types": [
"unplugin-vue2-script-setup/types",
"unplugin-vue2-script-setup/ref-macros"
]
}
}
```
</details>
## Recommendations
If you enjoy using `<script setup>`, you might also want to try [`unplugin-auto-import`](https://github.com/antfu/unplugin-auto-import) to improve the DX even further.
## Progress
- [x] PoC
- [x] Components registration
- [x] Compile time macros `defineProps` `defineEmits` `withDefaults` `defineExpose`
- [x] Global types
- [x] Merge with normal scripts
- [x] [Ref Sugar (take 2)](https://github.com/vuejs/rfcs/discussions/369)
- [x] `<template lang="pug">` support
- [x] Vite plugin
- [x] Webpack plugin
- [x] Nuxt module
- [ ] ~~Top-level await~~ (not supported)
## How?
<details>
<summary>
👀
</summary>

It's made possible by transforming the `<script setup>` syntax back to normal `<script>` and let the Vue 2 SFC compiler handle the rest.
<br></details>
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2021 [Anthony Fu](https://github.com/antfu)
================================================
FILE: esbuild.d.ts
================================================
export { default } from './dist/esbuild'
================================================
FILE: examples/vue-cli/.npmrc
================================================
shamefully-hoist=true
================================================
FILE: examples/vue-cli/babel.config.js
================================================
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
}
================================================
FILE: examples/vue-cli/package.json
================================================
{
"name": "vue-cli",
"private": true,
"scripts": {
"type-check": "vue-tsc --noEmit",
"dev": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@vue/composition-api": "^1.7.1",
"core-js": "^3.32.0",
"vue": "~2.6.14"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^5.0.8",
"@vue/cli-plugin-typescript": "^5.0.8",
"@vue/cli-service": "^5.0.8",
"typescript": "^5.1.6",
"unplugin-vue2-script-setup": "workspace:*",
"vue-template-compiler": "~2.6.14",
"vue-tsc": "^1.8.8"
}
}
================================================
FILE: examples/vue-cli/public/index.html
================================================
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
================================================
FILE: examples/vue-cli/src/App.vue
================================================
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
</script>
<template>
<div id="app">
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
================================================
FILE: examples/vue-cli/src/components/HelloWorld.vue
================================================
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
================================================
FILE: examples/vue-cli/src/main.ts
================================================
import Vue from 'vue'
import VueCompositionAPI, { createApp, h } from '@vue/composition-api'
import App from './App.vue'
Vue.config.productionTip = false
Vue.use(VueCompositionAPI)
const app = createApp({
render: () => h(App),
})
app.mount('#app')
================================================
FILE: examples/vue-cli/src/shims-vue.d.ts
================================================
/// <reference types="unplugin-vue2-script-setup/shims.js" />
================================================
FILE: examples/vue-cli/tsconfig.json
================================================
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"types": [
"webpack-env"
],
"lib": [
"esnext",
"dom",
"dom.iterable"
]
},
"include": [
"src"
],
"exclude": [
"node_modules"
],
"vueCompilerOptions": {
"target": 2
}
}
================================================
FILE: examples/vue-cli/vue.config.cjs
================================================
const { defineConfig } = require('@vue/cli-service')
const ScriptSetup = require('unplugin-vue2-script-setup/webpack').default
module.exports = defineConfig({
configureWebpack: {
plugins: [
ScriptSetup({
reactivityTransform: true,
}),
],
},
parallel: false,
chainWebpack(config) {
// disable type check and let `vue-tsc` handles it
config.plugins.delete('fork-ts-checker')
// disable cache for testing, you should remove this in production
config.module.rule('vue').uses.delete('cache-loader')
config.module.rule('js').uses.delete('cache-loader')
config.module.rule('ts').uses.delete('cache-loader')
config.module.rule('tsx').uses.delete('cache-loader')
},
})
================================================
FILE: index.d.ts
================================================
export { default } from './dist/index'
================================================
FILE: jest.js
================================================
const { transform } = require('./dist/index')
function requireVueJest() {
const names = ['@vue/vue2-jest', 'vue-jest']
for (const name of names) {
try {
return require(name)
}
catch (e) {
// Try next module
}
}
throw new Error(`Cannot find a Jest transformer for Vue SFC, you should install one of these packages: ${names.join(', ')}`)
}
module.exports = {
async process(source, filename, ...args) {
const transformed = await transform(source, filename)
const code = transformed ? transformed.code : source
return requireVueJest().process.call(this, code, filename, ...args)
},
}
================================================
FILE: nuxt.d.ts
================================================
export { default } from './dist/nuxt'
================================================
FILE: package.json
================================================
{
"name": "unplugin-vue2-script-setup",
"version": "0.11.4",
"packageManager": "pnpm@8.6.11",
"description": "Bring <script setup> to Vue 2",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unplugin/unplugin-vue2-script-setup#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/unplugin/unplugin-vue2-script-setup.git"
},
"bugs": {
"url": "https://github.com/unplugin/unplugin-vue2-script-setup/issues"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./*": "./*",
"./esbuild": {
"import": "./dist/esbuild.mjs",
"require": "./dist/esbuild.js"
},
"./jest": "./jest.js",
"./nuxt": {
"import": "./dist/nuxt.mjs",
"require": "./dist/nuxt.js"
},
"./rollup": {
"import": "./dist/rollup.mjs",
"require": "./dist/rollup.js"
},
"./types": {
"import": "./dist/types.mjs",
"require": "./dist/types.js"
},
"./vite": {
"import": "./dist/vite.mjs",
"require": "./dist/vite.js"
},
"./webpack": {
"import": "./dist/webpack.mjs",
"require": "./dist/webpack.js"
}
},
"main": "dist/index.js",
"files": [
"dist",
"jest.js",
"*.d.ts"
],
"scripts": {
"build": "rimraf dist && rollup -c",
"dev": "rollup -c --watch",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"build:examples": "pnpm -r --filter=!unplugin-vue2-script-setup run build",
"play": "npm -C playground run dev",
"prepublishOnly": "nr build",
"release": "bumpp --commit --push --tag && pnpm publish",
"test": "vitest",
"test:update": "vitest -u"
},
"peerDependencies": {
"@vue/composition-api": "*",
"@vue/runtime-dom": "^3.2.31",
"pug": "^3.0.2"
},
"peerDependenciesMeta": {
"pug": {
"optional": true
}
},
"dependencies": {
"@antfu/utils": "^0.7.5",
"@babel/core": "^7.22.9",
"@babel/generator": "^7.22.9",
"@babel/parser": "^7.22.7",
"@babel/traverse": "^7.22.8",
"@babel/types": "^7.22.5",
"@rollup/pluginutils": "^5.0.2",
"@vue/compiler-core": "^3.3.4",
"@vue/compiler-dom": "^3.3.4",
"@vue/reactivity-transform": "^3.3.4",
"@vue/shared": "^3.3.4",
"defu": "^6.1.2",
"magic-string": "^0.30.2",
"unplugin": "^1.4.0"
},
"devDependencies": {
"@antfu/eslint-config": "^0.40.0",
"@antfu/ni": "^0.21.5",
"@rollup/plugin-alias": "^5.0.0",
"@rollup/plugin-commonjs": "^25.0.3",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.1.0",
"@types/babel__core": "^7.20.1",
"@types/estree": "^1.0.1",
"@types/node": "^18.17.1",
"@types/pug": "^2.0.6",
"@types/ws": "^8.5.5",
"@vue/composition-api": "^1.7.1",
"@vue/runtime-dom": "^3.3.4",
"bumpp": "^9.1.1",
"esbuild": "^0.18.17",
"eslint": "^8.46.0",
"fast-glob": "^3.3.1",
"pug": "^3.0.2",
"rimraf": "^5.0.1",
"rollup": "^3.27.0",
"rollup-plugin-dts": "^5.3.1",
"rollup-plugin-esbuild": "^5.0.0",
"rollup-plugin-typescript2": "^0.35.0",
"typescript": "^5.1.6",
"vite": "^4.4.8",
"vitest": "^0.33.0",
"webpack": "^5.88.2"
},
"pnpm": {
"overrides": {
"unplugin-vue2-script-setup": "workspace:*"
}
}
}
================================================
FILE: playground/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
================================================
FILE: playground/package.json
================================================
{
"private": true,
"scripts": {
"dev": "vite --open",
"build": "vite build"
},
"dependencies": {
"@vue/composition-api": "^1.7.1",
"vue": "^2.6.14"
},
"devDependencies": {
"vite": "^4.4.8",
"vite-plugin-inspect": "^0.7.35",
"vite-plugin-vue2": "^2.0.3",
"vue-template-compiler": "~2.6.14"
}
}
================================================
FILE: playground/src/App.vue
================================================
<script setup lang="ts">
import { defineAsyncComponent } from '@vue/composition-api'
import ButtonTest from './ButtonTest.vue';
import HelloWorld from './HelloWorld.vue'
const AsyncComponent = defineAsyncComponent(() => import('./Async.vue'))
function onUpdate(e: any) {
// eslint-disable-next-line no-console
console.log(e)
}
</script>
<template>
<div>
<ButtonTest />
<HelloWorld name="Vue 2" @update="onUpdate" />
<AsyncComponent />
</div>
</template>
================================================
FILE: playground/src/Async.vue
================================================
<template>
<div>Async Component</div>
</template>
================================================
FILE: playground/src/Bar.vue
================================================
<template>
<div>Bar</div>
</template>
================================================
FILE: playground/src/ButtonTest.vue
================================================
<script setup lang="ts">
import Button from './Foo.vue';
import button from './Foo.vue';
</script>
<template>
<div>
<button>{{ Button }}</button>
<Button>{{ button }}</Button>
</div>
</template>
================================================
FILE: playground/src/Foo.vue
================================================
<script setup lang="ts">
</script>
<template>
<div>
Button Component: <slot></slot>
</div>
</template>
================================================
FILE: playground/src/HelloWorld.vue
================================================
<script lang="ts">
/* eslint-disable import/first */
export default {
name: 'App',
}
</script>
<script setup lang="ts">
import { watch } from '@vue/composition-api'
import Foo from './Foo.vue'
import Bar from './Bar.vue'
withDefaults(defineProps<{ msg: string; name: string | number }>(), { msg: 'Hello' })
const emit = defineEmits<{
(event: 'update', value: number): void
}>()
let count = $ref(0)
// eslint-disable-next-line prefer-const
let doubled = $computed(() => count * 2)
function inc() {
count += 1
}
function dec() {
count -= 1
}
const decText = '<b>Dec</b>'
watch(count, value => emit('update', value))
</script>
<template>
<div>
<h3>{{ msg }}, {{ name }}</h3>
<button @click="inc">
Inc
</button>
<div>{{ count }} x 2 = {{ doubled }}</div>
<button @click="dec()" v-html="decText" />
<component :is="count > 2 ? Foo : Bar" />
</div>
</template>
================================================
FILE: playground/src/main.ts
================================================
import Vue from 'vue'
import VueCompositionAPI, { createApp, h } from '@vue/composition-api'
import App from './App.vue'
Vue.use(VueCompositionAPI)
const app = createApp({ render: () => h(App) })
app.mount('#app')
================================================
FILE: playground/src/shims-vue.d.ts
================================================
/// <reference types="unplugin-vue2-script-setup/shims.js" />
/// <reference types="unplugin-vue2-script-setup/ref-macros.js" />
================================================
FILE: playground/tsconfig.json
================================================
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"types": [
"webpack-env"
],
"lib": [
"esnext",
"dom",
"dom.iterable"
]
},
"include": [
"src"
],
"exclude": [
"node_modules"
],
"vueCompilerOptions": {
"experimentalCompatMode": 2
}
}
================================================
FILE: playground/vite.config.ts
================================================
import { defineConfig } from 'vite'
import { createVuePlugin as Vue2 } from 'vite-plugin-vue2'
import Inspect from 'vite-plugin-inspect'
import { unplugin } from '../src'
const ScriptSetup = unplugin.vite
export default defineConfig({
plugins: [
Vue2(),
Inspect(),
ScriptSetup({
reactivityTransform: true,
}),
],
})
================================================
FILE: pnpm-workspace.yaml
================================================
- packages
- playground
- examples/*
================================================
FILE: ref-macros.d.ts
================================================
import type {
ComputedRef,
Ref,
ShallowUnwrapRef,
UnwrapRef,
WritableComputedOptions,
WritableComputedRef,
} from '@vue/composition-api'
declare const RefMarker: unique symbol
type RefValue<T> = T & { [RefMarker]?: any }
declare const ComputedRefMarker: unique symbol
type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
declare const WritableComputedRefMarker: unique symbol
type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: any }
type ToRawRefs<T extends object> = {
[K in keyof T]: T[K] extends ComputedRefValue<infer V>
? ComputedRefValue<V>
: T[K] extends WritableComputedRefValue<infer V>
? WritableComputedRef<V>
: T[K] extends RefValue<infer V>
? Ref<V>
: T[K] extends object
? T[K] extends
| Function
| Map<any, any>
| Set<any>
| WeakMap<any, any>
| WeakSet<any>
? T[K]
: ToRawRefs<T[K]>
: T[K];
}
/**
* Vue ref transform macro for binding refs as reactive variables.
*/
declare function _$<T>(arg: ComputedRef<T>): ComputedRefValue<T>
declare function _$<T>(
arg: WritableComputedRef<T>
): WritableComputedRefValue<T>
declare function _$<T>(arg: Ref<T>): RefValue<T>
declare function _$<T extends object>(arg?: T): ShallowUnwrapRef<T>
/**
* Vue ref transform macro for accessing underlying refs of reactive varaibles.
*/
declare function _$$<T>(value: T): ComputedRef<T>
declare function _$$<T>(
value: WritableComputedRefValue<T>
): WritableComputedRef<T>
declare function _$$<T>(value: RefValue<T>): Ref<T>
declare function _$$<T extends object>(arg: T): ToRawRefs<T>
declare function _$ref<T>(arg?: T | Ref<T>): RefValue<UnwrapRef<T>>
declare function _$shallowRef<T>(arg?: T): RefValue<T>
declare function _$computed<T>(
getter: () => T,
// debuggerOptions?: DebuggerOptions
): ComputedRefValue<T>
declare function _$computed<T>(
options: WritableComputedOptions<T>,
// debuggerOptions?: DebuggerOptions
): WritableComputedRefValue<T>
declare global {
const $: typeof _$
const $$: typeof _$$
const $ref: typeof _$ref
const $shallowRef: typeof _$shallowRef
const $computed: typeof _$computed
}
================================================
FILE: rollup.config.mjs
================================================
// @ts-check
import * as fs from 'node:fs'
import ts from 'rollup-plugin-esbuild'
import dts from 'rollup-plugin-dts'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import alias from '@rollup/plugin-alias'
/** @type {import('./package.json')} */
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8'))
const entries = {
index: 'src/index.ts',
webpack: 'src/webpack.ts',
vite: 'src/vite.ts',
rollup: 'src/rollup.ts',
esbuild: 'src/esbuild.ts',
nuxt: 'src/nuxt.ts',
types: 'src/types.ts',
}
const external = [
...Object.keys(pkg.dependencies),
...Object.keys(pkg.peerDependencies),
'esbuild',
'rollup',
'vite',
'webpack',
'@nuxt/kit',
]
/** @type {import('rollup').RollupOptions[]} */
export default [
{
input: entries,
external,
plugins: [
alias({
entries: [
{ find: /^node:(.+)$/, replacement: '$1' },
],
}),
resolve({
preferBuiltins: true,
}),
json(),
commonjs(),
ts(),
],
onwarn({ code, message }) {
if(code === 'EMPTY_BUNDLE') return
console.error(message)
},
output:[
{
dir: 'dist',
format: 'esm',
sourcemap: 'inline',
entryFileNames: "[name].mjs",
},
{
dir: 'dist',
format: 'cjs',
exports: 'named',
sourcemap: 'inline',
entryFileNames: "[name].js",
},
]
},
{
input: entries,
external,
plugins: [
dts({ respectExternal: true }),
],
output: [
{
dir: 'dist',
entryFileNames: "[name].d.mts",
},
{
dir: 'dist',
entryFileNames: "[name].d.ts",
},
],
},
]
================================================
FILE: rollup.d.ts
================================================
export { default } from './dist/rollup'
================================================
FILE: scripts/postbuild.ts
================================================
import { basename, resolve } from 'node:path'
import { promises as fs } from 'node:fs'
import fg from 'fast-glob'
async function run() {
// fix cjs exports
const files = await fg('*.js', {
ignore: ['chunk-*'],
absolute: true,
cwd: resolve(__dirname, '../dist'),
})
for (const file of files) {
console.log('[postbuild]', basename(file))
const name = basename(file, '.js')
let code = await fs.readFile(file, 'utf8')
code = code.replace('exports.default =', 'module.exports =')
code += 'exports.default = module.exports;'
await fs.writeFile(file, code)
await fs.writeFile(`${name}.d.ts`, `import './shims'\nexport { default } from './dist/${name}'\n`)
}
}
run()
================================================
FILE: shims.d.ts
================================================
// workaround for Volar to infer the ref type in <template>
// https://github.com/johnsoncodehk/volar/issues/404
declare module '@vue/runtime-dom' {
export * from '@vue/runtime-dom/dist/runtime-dom'
export { defineComponent, PropType, ObjectDirective, FunctionDirective } from '@vue/composition-api'
}
================================================
FILE: src/core/babel.ts
================================================
import * as babel from '@babel/core'
import { parse, parseExpression } from '@babel/parser'
import g from '@babel/generator'
import * as babel_traverse from '@babel/traverse'
export const t: typeof babel['types'] = ((babel as any).default || babel).types
export const generate: typeof g = ((g as any).default || g)
export const traverse = ((babel_traverse as any)?.default?.default as null) ?? babel_traverse?.default ?? babel_traverse
export { parseExpression, parse }
================================================
FILE: src/core/identifiers.ts
================================================
import type {
Expression,
File,
PrivateName,
SpreadElement,
Statement,
TSType,
} from '@babel/types'
import type { ParseResult } from '@babel/parser'
import { t, traverse } from './babel'
export function getIdentifierDeclarations(nodes: Statement[]) {
let result!: Set<string>
let programScopeUid: number
traverse(t.file(t.program(nodes)), {
Program(path) {
result = new Set(Object.keys(path.scope.bindings))
programScopeUid = (path.scope as any).uid
},
// FIXME: babel bug, temporary add TSEnumDeclaration and TSModuleDeclaration logic
TSEnumDeclaration(path) {
if ((path.scope as any).uid === programScopeUid)
result.add(path.node.id.name)
},
TSModuleDeclaration(path) {
if ((path.scope as any).uid === programScopeUid) {
const id = path.node.id
if (id.type === 'Identifier')
result.add(id.name)
}
},
})
return Array.from(result)
}
/**
* @deprecated use `getFileGlobals` instead
*/
export function getIdentifierUsages(node?: Expression | TSType | SpreadElement | PrivateName | Statement | null, identifiers = new Set<string>()) {
if (!node)
return identifiers
if (node.type === 'BlockStatement') {
node.body.forEach(child => getIdentifierUsages(child, identifiers))
}
else if (node.type === 'ExpressionStatement') {
getIdentifierUsages(node.expression, identifiers)
}
else if (node.type === 'Identifier') {
identifiers.add(node.name)
}
else if (node.type === 'MemberExpression' || node.type === 'OptionalMemberExpression') {
getIdentifierUsages(node.object, identifiers)
if (node.computed)
getIdentifierUsages(node.property, identifiers)
}
else if (node.type === 'CallExpression' || node.type === 'OptionalCallExpression') {
getIdentifierUsages(node.callee as Expression, identifiers)
node.arguments.forEach(arg => getIdentifierUsages(arg as Expression, identifiers))
}
else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
getIdentifierUsages(node.left, identifiers)
getIdentifierUsages(node.right, identifiers)
}
else if (node.type === 'UnaryExpression') {
getIdentifierUsages(node.argument, identifiers)
}
else if (node.type === 'ForOfStatement' || node.type === 'ForInStatement') {
getIdentifierUsages(node.right, identifiers)
}
else if (node.type === 'ConditionalExpression') {
getIdentifierUsages(node.test, identifiers)
getIdentifierUsages(node.consequent, identifiers)
getIdentifierUsages(node.alternate, identifiers)
}
else if (node.type === 'ObjectExpression') {
node.properties.forEach((prop) => {
if (prop.type === 'ObjectProperty') {
if (prop.computed)
getIdentifierUsages(prop.key, identifiers)
getIdentifierUsages(prop.value as Expression, identifiers)
}
else if (prop.type === 'SpreadElement') {
getIdentifierUsages(prop, identifiers)
}
})
}
else if (node.type === 'ArrayExpression') {
node.elements.forEach(element => getIdentifierUsages(element, identifiers))
}
else if (node.type === 'SpreadElement' || node.type === 'ReturnStatement') {
getIdentifierUsages(node.argument, identifiers)
}
else if (node.type === 'NewExpression') {
getIdentifierUsages(node.callee as Expression, identifiers)
node.arguments.forEach(arg => getIdentifierUsages(arg as Expression, identifiers))
}
else if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') {
getIdentifierUsages(node.body, identifiers)
}
else if (node.type === 'TemplateLiteral') {
node.expressions.forEach(expr => getIdentifierUsages(expr, identifiers))
}
// else {
// console.log(node)
// }
return identifiers
}
export function getFileGlobals(result: ParseResult<File>) {
let globals!: Set<string>
let programScopeUid: number
traverse(result, {
Program(path) {
globals = new Set(Object.keys((path.scope as any).globals))
programScopeUid = (path.scope as any).uid
},
// FIXME: babel bug, temporary add TSEnumDeclaration and TSModuleDeclaration logic
TSEnumDeclaration(path) {
if ((path.scope as any).uid === programScopeUid)
globals.delete(path.node.id.name)
},
TSModuleDeclaration(path) {
if ((path.scope as any).uid === programScopeUid) {
const id = path.node.id
if (id.type === 'Identifier')
globals.delete(id.name)
}
},
})
return Array.from(globals)
}
================================================
FILE: src/core/index.ts
================================================
export * from './transform'
export * from './transformScriptSetup'
export * from '../types'
================================================
FILE: src/core/macros.ts
================================================
// modified from https://github.com/vuejs/vue-next/blob/main/packages/compiler-sfc/src/compileScript.ts
import type {
CallExpression,
Node,
ObjectExpression,
ObjectProperty,
Statement,
TSFunctionType,
TSInterfaceBody,
TSType,
TSTypeLiteral,
} from '@babel/types'
import { parseExpression, t } from './babel'
// Special compiler macros
const DEFINE_PROPS = 'defineProps'
const DEFINE_EMITS = 'defineEmits'
const DEFINE_EXPOSE = 'defineExpose'
const WITH_DEFAULTS = 'withDefaults'
const DEFINE_SLOTS = 'defineSlots'
export interface PropTypeData {
key: string
type: string[] | string
required: boolean
}
export function applyMacros(nodes: Statement[]) {
let hasDefinePropsCall = false
let hasDefineEmitCall = false
let hasDefineSlotsCall = false
let propsRuntimeDecl: Node | undefined
let propsRuntimeDefaults: Node | undefined
let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
let propsTypeDeclRaw: Node | undefined
let emitsRuntimeDecl: Node | undefined
let emitsTypeDecl:
| TSFunctionType
| TSTypeLiteral
| TSInterfaceBody
| undefined
let emitsTypeDeclRaw: Node | undefined
let exposeDecl: CallExpression['arguments'][number] | undefined
// props/emits declared via types
const typeDeclaredProps: Record<string, PropTypeData> = {}
// record declared types for runtime props type generation
const declaredTypes: Record<string, string[]> = {}
function error(
msg: string,
_node: Node,
): never {
throw new Error(msg)
}
function processDefineProps(node: Node): boolean {
if (!isCallOf(node, DEFINE_PROPS))
return false
if (hasDefinePropsCall)
error(`duplicate ${DEFINE_PROPS}() call`, node)
hasDefinePropsCall = true
propsRuntimeDecl = node.arguments[0]
// call has type parameters - infer runtime types from it
if (node.typeParameters) {
if (propsRuntimeDecl) {
error(
`${DEFINE_PROPS}() cannot accept both type and non-type arguments `
+ 'at the same time. Use one or the other.',
node,
)
}
propsTypeDeclRaw = node.typeParameters.params[0]
propsTypeDecl = resolveQualifiedType(
propsTypeDeclRaw,
node => node.type === 'TSTypeLiteral',
) as TSTypeLiteral | TSInterfaceBody | undefined
if (!propsTypeDecl) {
error(
`type argument passed to ${DEFINE_PROPS}() must be a literal type, `
+ 'or a reference to an interface or literal type.',
propsTypeDeclRaw,
)
}
}
return true
}
function processWithDefaults(node: Node): boolean {
if (!isCallOf(node, WITH_DEFAULTS))
return false
if (processDefineProps(node.arguments[0])) {
if (propsRuntimeDecl) {
error(
`${WITH_DEFAULTS} can only be used with type-based `
+ `${DEFINE_PROPS} declaration.`,
node,
)
}
propsRuntimeDefaults = node.arguments[1]
}
else {
error(
`${WITH_DEFAULTS}' first argument must be a ${DEFINE_PROPS} call.`,
node.arguments[0] || node,
)
}
return true
}
function processDefineEmits(node: Node): boolean {
if (!isCallOf(node, DEFINE_EMITS))
return false
if (hasDefineEmitCall)
error(`duplicate ${DEFINE_EMITS}() call`, node)
hasDefineEmitCall = true
emitsRuntimeDecl = node.arguments[0]
if (node.typeParameters) {
if (emitsRuntimeDecl) {
error(
`${DEFINE_EMITS}() cannot accept both type and non-type arguments `
+ 'at the same time. Use one or the other.',
node,
)
}
emitsTypeDeclRaw = node.typeParameters.params[0]
emitsTypeDecl = resolveQualifiedType(
emitsTypeDeclRaw,
node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral',
) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
if (!emitsTypeDecl) {
error(
`type argument passed to ${DEFINE_EMITS}() must be a function type, `
+ 'a literal type with call signatures, or a reference to the above types.',
emitsTypeDeclRaw,
)
}
}
return true
}
function resolveQualifiedType(
node: Node,
qualifier: (node: Node) => boolean,
) {
if (qualifier(node))
return node
if (
node.type === 'TSTypeReference'
&& node.typeName.type === 'Identifier'
) {
const refName = node.typeName.name
const isQualifiedType = (node: Node): Node | undefined => {
if (
node.type === 'TSInterfaceDeclaration'
&& node.id.name === refName
)
return node.body
else if (
node.type === 'TSTypeAliasDeclaration'
&& node.id.name === refName
&& qualifier(node.typeAnnotation)
)
return node.typeAnnotation
else if (node.type === 'ExportNamedDeclaration' && node.declaration)
return isQualifiedType(node.declaration)
}
for (const node of nodes) {
const qualified = isQualifiedType(node)
if (qualified)
return qualified
}
}
}
function processDefineExpose(node: Node): boolean {
if (!isCallOf(node, DEFINE_EXPOSE))
return false
if (exposeDecl)
error(`duplicate ${DEFINE_EXPOSE}() call`, node)
if (node.arguments.length !== 1)
error(`${DEFINE_EXPOSE}() requires one argument`, node)
exposeDecl = node.arguments[0]
return true
}
function processDefineSlots(
node: Node,
): boolean {
if (!isCallOf(node, DEFINE_SLOTS))
return false
if (hasDefineSlotsCall)
error(`duplicate ${DEFINE_SLOTS}() call`, node)
hasDefineSlotsCall = true
if (node.arguments.length > 0)
error(`${DEFINE_SLOTS}() cannot accept arguments`, node)
return true
}
function genRuntimeProps(props: Record<string, PropTypeData>) {
const keys = Object.keys(props)
if (!keys.length)
return undefined
// check defaults. If the default object is an object literal with only
// static properties, we can directly generate more optimzied default
// decalrations. Otherwise we will have to fallback to runtime merging.
const hasStaticDefaults = propsRuntimeDefaults
&& propsRuntimeDefaults.type === 'ObjectExpression'
&& propsRuntimeDefaults.properties.every(
node => node.type === 'ObjectProperty' && !node.computed,
)
return t.objectExpression(
Object.entries(props).map(([key, value]) => {
const prop = hasStaticDefaults
? (propsRuntimeDefaults as ObjectExpression).properties.find((node: any) => node.key.name === key) as ObjectProperty
: undefined
if (prop)
value.required = false
const entries = Object.entries(value).map(([key, value]) => key === 'type'
? t.objectProperty(t.identifier(key), typeof value === 'string' ? t.identifier(value) : t.arrayExpression(value.map((i: any) => t.identifier(i))) as any)
: t.objectProperty(t.identifier(key), parseExpression(JSON.stringify(value)) as any),
)
if (prop)
entries.push(t.objectProperty(t.identifier('default'), prop.value as any))
return t.objectProperty(
t.identifier(key),
t.objectExpression(entries),
)
}),
)
}
function getProps() {
if (propsRuntimeDecl)
return propsRuntimeDecl
if (propsTypeDecl) {
extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes)
return genRuntimeProps(typeDeclaredProps)
}
}
function throwIfAwait(node: Node) {
if (node.type === 'AwaitExpression')
error('top-level await is not supported in Vue 2', node)
}
nodes = nodes
.map((raw: Node) => {
let node = raw
if (raw.type === 'ExpressionStatement')
node = raw.expression
if (node.type === 'VariableDeclaration' && !node.declare) {
const total = node.declarations.length
for (let i = 0; i < total; i++) {
const decl = node.declarations[i]
if (decl.init) {
if (processDefineEmits(decl.init))
decl.init = t.memberExpression(t.identifier('__ctx'), t.identifier('emit'))
else if (processDefineSlots(decl.init))
decl.init = t.memberExpression(t.identifier('__ctx'), t.identifier('slots'))
else if (processDefineProps(decl.init) || processWithDefaults(decl.init))
decl.init = t.identifier('__props') as any
else
throwIfAwait(decl.init)
}
}
}
if (processWithDefaults(node) || processDefineEmits(node) || processDefineProps(node) || processDefineExpose(node) || processDefineSlots(node))
return null
throwIfAwait(node)
return raw
})
.filter(Boolean) as Statement[]
return {
nodes,
props: getProps(),
expose: exposeDecl,
}
}
function isCallOf(
node: Node | null | undefined,
test: string | ((id: string) => boolean),
): node is CallExpression {
return !!(
node
&& node.type === 'CallExpression'
&& node.callee.type === 'Identifier'
&& (typeof test === 'string'
? node.callee.name === test
: test(node.callee.name))
)
}
function extractRuntimeProps(
node: TSTypeLiteral | TSInterfaceBody,
props: Record<string, PropTypeData>,
declaredTypes: Record<string, string[]>,
) {
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
for (const m of members) {
if (
(m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature')
&& m.key.type === 'Identifier'
) {
let type: string[] | undefined
if (m.type === 'TSMethodSignature') {
type = ['Function']
}
else if (m.typeAnnotation) {
type = inferRuntimeType(
m.typeAnnotation.typeAnnotation,
declaredTypes,
)
}
props[m.key.name] = {
key: m.key.name,
required: !m.optional,
type: type?.length === 1 ? type[0] : type || 'null',
}
}
}
}
function inferRuntimeType(
node: TSType,
declaredTypes: Record<string, string[]>,
): string[] {
switch (node.type) {
case 'TSStringKeyword':
return ['String']
case 'TSNumberKeyword':
return ['Number']
case 'TSBooleanKeyword':
return ['Boolean']
case 'TSObjectKeyword':
return ['Object']
case 'TSTypeLiteral':
// TODO (nice to have) generate runtime property validation
return ['Object']
case 'TSFunctionType':
return ['Function']
case 'TSArrayType':
case 'TSTupleType':
// TODO (nice to have) generate runtime element type/length checks
return ['Array']
case 'TSLiteralType':
switch (node.literal.type) {
case 'StringLiteral':
return ['String']
case 'BooleanLiteral':
return ['Boolean']
case 'NumericLiteral':
case 'BigIntLiteral':
return ['Number']
default:
return ['null']
}
case 'TSTypeReference':
if (node.typeName.type === 'Identifier') {
if (declaredTypes[node.typeName.name])
return declaredTypes[node.typeName.name]
switch (node.typeName.name) {
case 'Array':
case 'Function':
case 'Object':
case 'Set':
case 'Map':
case 'WeakSet':
case 'WeakMap':
return [node.typeName.name]
case 'Record':
case 'Partial':
case 'Readonly':
case 'Pick':
case 'Omit':
case 'Exclude':
case 'Extract':
case 'Required':
case 'InstanceType':
return ['Object']
}
}
return ['null']
case 'TSParenthesizedType':
return inferRuntimeType(node.typeAnnotation, declaredTypes)
case 'TSUnionType':
return [
...new Set(
[].concat(
...(node.types.map(t => inferRuntimeType(t, declaredTypes)) as any),
),
),
]
case 'TSIntersectionType':
return ['Object']
default:
return ['null'] // no runtime check
}
}
================================================
FILE: src/core/options.ts
================================================
import type { ResolvedOptions, ScriptSetupTransformOptions } from '../types'
export function resolveOptions(options: ScriptSetupTransformOptions = {}): ResolvedOptions {
return Object.assign(
{},
{
sourceMap: true,
reactivityTransform: false,
importHelpersFrom: '@vue/composition-api',
astTransforms: {},
},
options,
)
}
================================================
FILE: src/core/parseSFC.ts
================================================
/* eslint-disable one-var */
/* eslint-disable @typescript-eslint/no-namespace */
import { notNullish, partition } from '@antfu/utils'
import type { Program } from '@babel/types'
import type { ParserPlugin } from '@babel/parser'
import type {
AttributeNode,
DirectiveNode,
ExpressionNode,
PlainElementNode,
RootNode,
TemplateChildNode,
} from '@vue/compiler-core'
import { baseParse } from '@vue/compiler-core'
import { parserOptions } from '@vue/compiler-dom'
import { camelize } from '@vue/shared'
import type {
ParsedSFC,
ScriptSetupTransformOptions,
ScriptTagMeta,
} from '../types'
import { getFileGlobals } from './identifiers'
import { parse } from './babel'
import { exhaustiveCheckReturnUndefined, pascalize } from './utils'
namespace NodeTypes {
export const ROOT = 0,
ELEMENT = 1,
TEXT = 2,
COMMENT = 3,
SIMPLE_EXPRESSION = 4,
INTERPOLATION = 5,
ATTRIBUTE = 6,
DIRECTIVE = 7,
COMPOUND_EXPRESSION = 8,
IF = 9,
IF_BRANCH = 10,
FOR = 11,
TEXT_CALL = 12,
VNODE_CALL = 13,
JS_CALL_EXPRESSION = 14,
JS_OBJECT_EXPRESSION = 15,
JS_PROPERTY = 16,
JS_ARRAY_EXPRESSION = 17,
JS_FUNCTION_EXPRESSION = 18,
JS_CONDITIONAL_EXPRESSION = 19,
JS_CACHE_EXPRESSION = 20,
JS_BLOCK_STATEMENT = 21,
JS_TEMPLATE_LITERAL = 22,
JS_IF_STATEMENT = 23,
JS_ASSIGNMENT_EXPRESSION = 24,
JS_SEQUENCE_EXPRESSION = 25,
JS_RETURN_STATEMENT = 26
}
namespace ElementTypes {
export const ELEMENT = 0,
COMPONENT = 1,
SLOT = 2,
TEMPLATE = 3
}
const BUILD_IN_DIRECTIVES = new Set([
'if',
'else',
'else-if',
'for',
'once',
'model',
'on',
'bind',
'slot',
'slot-scope',
'key',
'ref',
'text',
'html',
'show',
'pre',
'cloak',
// 'el',
// 'ref',
])
function getComponents(node: TemplateChildNode): string[] {
const current
= node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.COMPONENT
? [node.tag]
: node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.ELEMENT
? [node.tag]
: []
const children
= node.type === NodeTypes.IF
? node.branches
: node.type === NodeTypes.ELEMENT
|| node.type === NodeTypes.IF_BRANCH
|| node.type === NodeTypes.FOR
? node.children
: node.type === NodeTypes.TEXT
|| node.type === NodeTypes.COMMENT
|| node.type === NodeTypes.COMPOUND_EXPRESSION
|| node.type === NodeTypes.TEXT_CALL
|| node.type === NodeTypes.INTERPOLATION
? []
: exhaustiveCheckReturnUndefined(node) ?? []
return [...current, ...children.flatMap(getComponents)]
}
function getDirectiveNames(node: TemplateChildNode): string[] {
if (node.type === NodeTypes.ELEMENT) {
const directives = node.props.flatMap(x =>
x.type === NodeTypes.DIRECTIVE ? [x.name] : [],
)
return [...directives, ...node.children.flatMap(getDirectiveNames)]
}
else if (node.type === NodeTypes.IF) {
return node.branches.flatMap(getDirectiveNames)
}
else if (node.type === NodeTypes.IF_BRANCH || node.type === NodeTypes.FOR) {
return node.children.flatMap(getDirectiveNames)
}
else if (
node.type === NodeTypes.INTERPOLATION
|| node.type === NodeTypes.COMPOUND_EXPRESSION
|| node.type === NodeTypes.TEXT
|| node.type === NodeTypes.COMMENT
|| node.type === NodeTypes.TEXT_CALL
) {
return []
}
else {
exhaustiveCheckReturnUndefined(node)
return []
}
}
function getFreeVariablesForText(input: string): string[] {
const inputWithPrefix = input.trimStart()[0] === '{' ? `(${input})` : input
return getFileGlobals(parse(inputWithPrefix))
}
function getFreeVariablesForPropsNode(
node: AttributeNode | DirectiveNode,
): string[] {
if (node.type === NodeTypes.DIRECTIVE) {
const arg = node.arg === undefined ? [] : getFreeVariablesForNode(node.arg)
const exp = node.exp === undefined ? [] : getFreeVariablesForNode(node.exp)
return [...arg, ...exp]
}
return []
}
function getFreeVariablesForNode(
node: TemplateChildNode | ExpressionNode,
): string[] {
if (node.type === NodeTypes.SIMPLE_EXPRESSION) {
return node.isStatic ? [] : getFreeVariablesForText(node.content)
}
else if (node.type === NodeTypes.COMPOUND_EXPRESSION) {
return node.children.flatMap(x =>
typeof x !== 'object' ? [] : getFreeVariablesForNode(x),
)
}
else if (node.type === NodeTypes.INTERPOLATION) {
return getFreeVariablesForNode(node.content)
}
else if (node.type === NodeTypes.ELEMENT) {
const children = node.children.flatMap(getFreeVariablesForNode)
const directiveProps = node.props.flatMap(x =>
x.type === NodeTypes.DIRECTIVE ? [x] : [],
)
const attributeProps = node.props.flatMap(x =>
x.type === NodeTypes.ATTRIBUTE ? [x] : [],
)
const refNode = attributeProps.find(
node => node.name === 'ref' && node.value !== undefined,
)
const refIdentifier = refNode?.value?.content
const vSlotNode = directiveProps.find(node => node.name === 'slot')
const vSlotArgIdentifiers
= vSlotNode?.arg === undefined ? [] : getFreeVariablesForNode(vSlotNode.arg)
// TODO: Variable shadowing
const vSlotExpVariableShadowingIdentifiers: string[] = []
const vForNode = directiveProps.find(node => node.name === 'for')
const vForIdentifiers
= vForNode?.exp?.type === NodeTypes.SIMPLE_EXPRESSION
? getFreeVariablesForText(
vForNode.exp.content.replace(/^.*\s(?:in|of)\s/, ''),
)
: []
// TODO: Variable shadowing
const vForExpVariableShadowingIdentifiers: string[] = []
const props = directiveProps
.filter(({ name }) => name !== 'slot' && name !== 'for')
.flatMap(getFreeVariablesForPropsNode)
const shadowingIdentifiers = new Set([
...vSlotExpVariableShadowingIdentifiers,
...vForExpVariableShadowingIdentifiers,
])
return [
...vSlotArgIdentifiers,
refIdentifier,
...vForIdentifiers,
...[...children, ...props].filter(x => !shadowingIdentifiers.has(x)),
].filter(notNullish)
}
else if (node.type === NodeTypes.FOR) {
// If we use `baseCompiler`, we need add variable shadowing here
// But we use `baseParse` now. So this branch will never be reached.
// `NodeTypes.IF` and `NodeTypes.IF_BRANCH` will never be reached, also.
// const { keyAlias, valueAlias } = node
return [node.source, ...node.children].flatMap(getFreeVariablesForNode)
}
else if (node.type === NodeTypes.IF) {
return (node.branches ?? []).flatMap(getFreeVariablesForNode)
}
else if (node.type === NodeTypes.IF_BRANCH) {
return [node.condition, ...node.children]
.filter(notNullish)
.flatMap(getFreeVariablesForNode)
}
else if (
node.type === NodeTypes.TEXT
|| node.type === NodeTypes.COMMENT
|| node.type === NodeTypes.TEXT_CALL
) {
return []
}
else {
exhaustiveCheckReturnUndefined(node)
return []
}
}
export function findReferencesForSFC(
templateNode: RootNode | PlainElementNode | undefined,
) {
const components = templateNode?.children.flatMap(getComponents) ?? []
const directives = templateNode?.children.flatMap(getDirectiveNames) ?? []
const identifiers
= templateNode?.children.flatMap(getFreeVariablesForNode) ?? []
return {
components,
directives,
identifiers,
}
}
function getBabelParserOptions(lang: string | null | undefined) {
lang = lang || 'js'
const pluginsDict: Record<string, Array<ParserPlugin>> = {
js: [],
ts: ['typescript'],
jsx: ['jsx'],
tsx: ['jsx', 'typescript'],
}
const plugins = pluginsDict[lang]
if (plugins === undefined)
throw new SyntaxError(`Unsupported script language: ${lang}`)
return {
sourceType: 'module' as const,
plugins,
}
}
export async function parseSFC(
code: string,
id?: string,
options?: ScriptSetupTransformOptions,
): Promise<ParsedSFC> {
const elementChildren = baseParse(code, parserOptions).children.flatMap(x =>
x.type === NodeTypes.ELEMENT && x.tagType === ElementTypes.ELEMENT
? [x]
: [],
)
const templateNode = elementChildren.find(x => x.tag === 'template')
const [scriptSetupChildNodes, scriptChildNodes] = partition(
elementChildren.filter(x => x.tag === 'script'),
x => x.props.some(p => p.type === NodeTypes.ATTRIBUTE && p.name === 'setup'),
)
const getScriptTagMeta = (
n: PlainElementNode | undefined,
astTransforms: (ast: Program) => Program = x => x,
): ScriptTagMeta => {
if (n === undefined) {
const content = ''
const ast = parse(content, {
sourceType: 'module',
plugins: [],
}).program
return {
start: 0,
end: 0,
contentStart: 0,
contentEnd: 0,
content,
attrs: {},
found: false,
ast,
}
}
const attrs = Object.fromEntries(
n.props.flatMap(x =>
!(
x.type === NodeTypes.ATTRIBUTE && typeof x.value?.content === 'string'
)
? []
: [[x.name, x.value.content]],
),
)
const content = n.children[0]?.loc.source ?? ''
const contentStart = n.children[0]?.loc.start.offset ?? 0
const contentEnd = n.children[0]?.loc.end.offset ?? 0
const ast = astTransforms(
parse(content, getBabelParserOptions(attrs.lang)).program,
)
return {
start: n.loc.start.offset,
end: n.loc.end.offset,
contentStart,
contentEnd,
content,
attrs,
found: true,
ast,
}
}
const scriptSetup = getScriptTagMeta(
scriptSetupChildNodes[0],
options?.astTransforms?.scriptSetup,
)
const script = getScriptTagMeta(
scriptChildNodes[0],
options?.astTransforms?.script,
)
if (
script.found
&& scriptSetup.found
&& scriptSetup.attrs.lang !== script.attrs.lang
) {
throw new SyntaxError(
'<script setup> language must be the same as <script>',
)
}
const codeOfTemplate
= templateNode == null
? undefined
: templateNode.props.some(
p =>
p.type === NodeTypes.ATTRIBUTE
&& p.name === 'lang'
&& p.value?.type === NodeTypes.TEXT
&& p.value.content === 'pug',
)
? baseParse(
(await import('pug')).compile(
templateNode.children.map(x => x.loc.source).join(''),
{
filename: id,
},
)(),
parserOptions,
)
: templateNode
const result = codeOfTemplate
? findReferencesForSFC(codeOfTemplate)
: undefined
return {
id,
template: {
tags: new Set(result?.components),
components: new Set(result?.components.map(pascalize)),
directives: new Set(
result?.directives
.filter(x => !BUILD_IN_DIRECTIVES.has(x))
.map(camelize),
),
identifiers: new Set(result?.identifiers),
},
scriptSetup,
script,
parserOptions: getBabelParserOptions(
script.attrs.lang ?? scriptSetup.attrs.lang,
),
extraDeclarations: [],
}
}
================================================
FILE: src/core/transform.ts
================================================
import MagicString from 'magic-string'
import { shouldTransform as shouldTransformRefSugar, transform as transformRef } from '@vue/reactivity-transform'
import type { ResolvedOptions, ScriptSetupTransformOptions, TransformResult } from '../types'
import { parseSFC } from './parseSFC'
import { transformScriptSetup } from './transformScriptSetup'
import { transformSfcRefSugar } from './transformSfcRefSugar'
import { resolveOptions } from './options'
export const scriptSetupRE = /<script\s+(.*\s+)?setup(\s+.*)?\s*>/
export function shouldTransform(code: string, id: string, options?: ScriptSetupTransformOptions): boolean {
// avoid transforming twice
if (code.includes('export default __sfc_main'))
return false
return (options?.reactivityTransform && shouldTransformRefSugar(code)) || scriptSetupRE.test(code)
}
export async function transform(input: string, id: string, options?: ScriptSetupTransformOptions): Promise<TransformResult> {
if (!shouldTransform(input, id, options))
return null
const resolved = resolveOptions(options)
if (id.endsWith('.vue') || id.includes('.vue?vue'))
return await transformVue(input, id, resolved)
else
return transformNonVue(input, id, resolved)
}
function transformNonVue(input: string, id: string, options: ResolvedOptions): TransformResult {
if (options.reactivityTransform && shouldTransformRefSugar(input)) {
return transformRef(input, {
filename: id,
sourceMap: options.sourceMap,
importHelpersFrom: options.importHelpersFrom,
}) as any
}
return null
}
async function transformVue(input: string, id: string, options: ResolvedOptions): Promise<TransformResult> {
const s = new MagicString(input)
const sfc = await parseSFC(input, id)
if (options.reactivityTransform)
transformSfcRefSugar(sfc, options)
const { code } = transformScriptSetup(sfc, options)
const attributes = {
...sfc.script.attrs,
...sfc.scriptSetup.attrs,
}
delete attributes.setup
const attr = Object.entries(attributes)
.map(([key, value]) => value ? `${key}="${value}"` : key)
.join(' ')
if (code) {
const block = `<script ${attr}>\n${code}\n</script>`
s.remove(sfc.script.start, sfc.script.end)
if (sfc.scriptSetup.start !== sfc.scriptSetup.end) {
s.overwrite(
sfc.scriptSetup.start,
sfc.scriptSetup.end,
block,
)
}
else {
s.prependLeft(0, `${block}\n`)
}
}
return {
code: s.toString(),
map: options.sourceMap
? s.generateMap({
source: id,
includeContent: true,
}) as any
: null,
}
}
================================================
FILE: src/core/transformScriptSetup.ts
================================================
import { capitalize } from '@vue/shared'
import type { Node, ObjectExpression, Statement } from '@babel/types'
import { notNullish, partition, uniq } from '@antfu/utils'
import { parserOptions } from '@vue/compiler-dom'
import type { ParsedSFC, ScriptSetupTransformOptions } from '../types'
import { applyMacros } from './macros'
import { getIdentifierDeclarations } from './identifiers'
import { generate, t } from './babel'
import { pascalize } from './utils'
function isAsyncImport(node: Statement) {
if (t.isVariableDeclaration(node)) {
const declaration = node.declarations[0]
return (
declaration !== undefined
&& t.isCallExpression(declaration.init)
&& t.isIdentifier(declaration.init.callee)
&& declaration.init.callee.name === 'defineAsyncComponent'
)
}
return false
}
export function transformScriptSetup(
sfc: ParsedSFC,
options?: ScriptSetupTransformOptions,
) {
const { scriptSetup, script, template } = sfc
const { nodes: body, props, expose } = applyMacros(scriptSetup.ast.body)
const [hoisted, setupBody] = partition(
body,
n =>
isAsyncImport(n)
|| t.isImportDeclaration(n)
|| t.isExportNamedDeclaration(n)
|| n.type.startsWith('TS'),
)
// get all identifiers in `<script setup>` and `<script>`
const declarationArray = uniq([
...getIdentifierDeclarations(hoisted),
...getIdentifierDeclarations(setupBody),
...getIdentifierDeclarations(script.ast.body),
]).filter(notNullish)
// filter out identifiers that are used in `<template>`
const returns: ObjectExpression['properties'] = declarationArray
.filter(i => template.identifiers.has(i))
.map((i) => {
const id = t.identifier(i)
return t.objectProperty(id, id, false, true)
})
const nonNativeTags = new Set(
Array.from(template.tags)
.filter(tag => !parserOptions.isNativeTag!(tag))
.map(pascalize),
)
const components = Array.from(nonNativeTags)
.map(
component =>
declarationArray.find(declare => declare === component)
?? declarationArray.find(declare => pascalize(declare) === component),
)
.filter(notNullish)
const directiveDeclaration = Array.from(template.directives)
.map((directive) => {
const identifier = declarationArray.find(
declaration => declaration === `v${capitalize(directive)}`,
)
if (identifier === undefined)
return undefined
return { identifier, directive }
})
.filter(notNullish)
// append `<script setup>` imports to `<script>`
const __sfc = t.identifier('__sfc_main')
let hasBody = false
const bodyNodes = script.ast.body.map((node: Node) => {
// replace `export default` with a temproray variable
// `const __sfc_main = { ... }`
if (node.type === 'ExportDefaultDeclaration') {
hasBody = true
return t.variableDeclaration('const', [
t.variableDeclarator(__sfc, node.declaration as any),
])
}
return node
})
let ast = t.program([
...sfc.extraDeclarations,
...hoisted,
...bodyNodes,
] as Statement[])
// inject `const __sfc_main = {}` if `<script>` has default export
if (!hasBody) {
ast.body.push(
t.variableDeclaration('const', [
t.variableDeclarator(__sfc, t.objectExpression([])),
]),
)
}
// inject props function
// `__sfc_main.props = { ... }`
if (props) {
hasBody = true
ast.body.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(__sfc, t.identifier('props')),
props as any,
),
) as any,
)
}
// inject setup function
// `__sfc_main.setup = () => {}`
if (body.length) {
hasBody = true
const returnExpr = expose
? t.callExpression(
t.memberExpression(t.identifier('Object'), t.identifier('assign')),
[t.objectExpression(returns), expose],
)
: t.objectExpression(returns)
const returnStatement = t.returnStatement(returnExpr)
ast.body.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(__sfc, t.identifier('setup')),
t.arrowFunctionExpression(
[t.identifier('__props'), t.identifier('__ctx')],
t.blockStatement([...setupBody, returnStatement as any]),
),
),
) as any,
)
}
// inject components
// `__sfc_main.components = Object.assign({ ... }, __sfc_main.components)`
if (components.length) {
hasBody = true
const componentsObject = t.objectExpression(
components.map((i) => {
const id = t.identifier(i)
return t.objectProperty(id, id, false, true)
}),
)
ast.body.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(__sfc, t.identifier('components')),
t.callExpression(
t.memberExpression(t.identifier('Object'), t.identifier('assign')),
[
componentsObject,
t.memberExpression(__sfc, t.identifier('components')),
],
),
),
) as any,
)
}
// inject directives
// `__sfc_main.directives = Object.assign({ ... }, __sfc_main.directives)`
if (directiveDeclaration.length) {
hasBody = true
const directivesObject = t.objectExpression(
directiveDeclaration.map(({ directive, identifier }) =>
t.objectProperty(
t.identifier(directive),
t.identifier(identifier),
false,
false,
),
),
)
ast.body.push(
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(__sfc, t.identifier('directives')),
t.callExpression(
t.memberExpression(t.identifier('Object'), t.identifier('assign')),
[
directivesObject,
t.memberExpression(__sfc, t.identifier('directives')),
],
),
),
) as any,
)
}
if (!hasBody && !options?.astTransforms) {
return {
ast: null,
code: '',
}
}
// re-export
// `export default __sfc_main`
ast.body.push(t.exportDefaultDeclaration(__sfc) as any)
ast = options?.astTransforms?.post?.(ast, sfc) || ast
return {
ast,
code: generate(ast).code,
}
}
================================================
FILE: src/core/transformSfcRefSugar.ts
================================================
import { shouldTransform, transformAST } from '@vue/reactivity-transform'
import MagicString from 'magic-string'
import type { ParsedSFC, ResolvedOptions } from '../types'
import { parse, t } from './babel'
export function transformSfcRefSugar(sfc: ParsedSFC, options: ResolvedOptions) {
const importedHelpers = new Set<string>()
for (const script of [sfc.script, sfc.scriptSetup]) {
if (shouldTransform(script.content)) {
const s = new MagicString(script.content)
const { importedHelpers: imports } = transformAST(script.ast, s)
Array.from(imports).forEach(helper => importedHelpers.add(helper))
script.content = s.toString()
script.ast = parse(script.content, sfc.parserOptions).program
}
}
if (importedHelpers.size) {
sfc.extraDeclarations = [
t.importDeclaration(
Array.from(importedHelpers).map(i => t.importSpecifier(t.identifier(`_${i}`), t.identifier(i))),
t.stringLiteral(options.importHelpersFrom),
),
]
}
}
================================================
FILE: src/core/utils.ts
================================================
import { camelize, capitalize } from '@vue/shared'
export const pascalize = (str: string) => capitalize(camelize(str))
export const isNotNil = <T>(value: T): value is NonNullable<T> => value != null
export function exhaustiveCheckReturnUndefined(_param: never) {
return undefined as never
}
================================================
FILE: src/esbuild.ts
================================================
import unplugin from '.'
export default unplugin.esbuild
================================================
FILE: src/index.ts
================================================
import { createUnplugin } from 'unplugin'
import { createFilter } from '@rollup/pluginutils'
import type { PluginOptions } from './types'
import { transform } from './core'
export * from './core'
export const unplugin = createUnplugin<PluginOptions>((options = {}) => {
const filter = createFilter(
options.include || (options.reactivityTransform ? [/\.vue$/, /\.vue\?vue/, /\.[jt]sx?$/] : [/\.vue$/, /\.vue\?vue/]),
options.exclude || [/node_modules/, /\.git/, /\.nuxt/],
)
return {
name: 'unplugin-vue2-script-setup',
enforce: 'pre',
transformInclude(id) {
return filter(id)
},
async transform(code, id) {
try {
return await transform(code, id, options)
}
catch (e: any) {
this.error(e)
}
},
}
})
export default unplugin
================================================
FILE: src/lib.ts
================================================
export * from './core'
================================================
FILE: src/nuxt.ts
================================================
import defu from 'defu'
import type { PluginOptions } from './types'
import unplugin from '.'
function scriptSetupModule(this: any, inlineOptions: PluginOptions = {}) {
const options = defu(inlineOptions, this.nuxt.options.scriptSetup)
// install webpack plugin
this.extendBuild((config: any) => {
config.plugins = config.plugins || []
config.plugins.unshift(unplugin.webpack(options))
})
// install vite plugin
this.nuxt.hook('vite:extend', async (vite: any) => {
vite.config.plugins = vite.config.plugins || []
vite.config.plugins.push(unplugin.vite(options))
})
}
export default scriptSetupModule
================================================
FILE: src/rollup.ts
================================================
import unplugin from '.'
export default unplugin.rollup
================================================
FILE: src/types.ts
================================================
import type { ParserOptions } from '@babel/parser'
import type { Node, Program } from '@babel/types'
import type { FilterPattern } from '@rollup/pluginutils'
export interface ScriptTagMeta {
start: number
end: number
contentStart: number
contentEnd: number
content: string
attrs: Record<string, string>
found: boolean
ast: Program
}
export interface ParsedSFC {
id?: string
template: {
/** foo-bar -> FooBar */
components: Set<string>
tags: Set<string>
/** v-foo-bar -> fooBar */
directives: Set<string>
identifiers: Set<string>
}
scriptSetup: ScriptTagMeta
script: ScriptTagMeta
parserOptions: ParserOptions
extraDeclarations: Node[]
}
export interface ScriptSetupTransformOptions {
astTransforms?: {
script?: (ast: Program) => Program
scriptSetup?: (ast: Program) => Program
post?: (ast: Program, sfc: ParsedSFC) => Program
}
reactivityTransform?: boolean
importHelpersFrom?: string
sourceMap?: boolean
}
export interface PluginOptions extends ScriptSetupTransformOptions {
include?: FilterPattern
exclude?: FilterPattern
}
export type ResolvedOptions = Required<ScriptSetupTransformOptions>
export interface SourceMap {
file: string
mappings: string
names: string[]
sources: string[]
sourcesContent: string[]
version: number
toString(): string
toUrl(): string
}
export type TransformResult = {
code: string
readonly map: SourceMap | null
} | null
================================================
FILE: src/vite.ts
================================================
import unplugin from '.'
export default unplugin.vite
================================================
FILE: src/webpack.ts
================================================
import unplugin from '.'
export default unplugin.webpack
================================================
FILE: test/__snapshots__/transform.test.ts.snap
================================================
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`transform > fixtures > playground/src/App.vue 1`] = `
"<script lang=\\"ts\\">
import { defineAsyncComponent } from '@vue/composition-api';
import ButtonTest from './ButtonTest.vue';
import HelloWorld from './HelloWorld.vue';
const AsyncComponent = defineAsyncComponent(() => import('./Async.vue'));
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
function onUpdate(e: any) {
// eslint-disable-next-line no-console
console.log(e);
}
return {
onUpdate
};
};
__sfc_main.components = Object.assign({
ButtonTest,
HelloWorld,
AsyncComponent
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<div>
<ButtonTest />
<HelloWorld name=\\"Vue 2\\" @update=\\"onUpdate\\" />
<AsyncComponent />
</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/Async.vue 1`] = `
"<template>
<div>Async Component</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/Bar.vue 1`] = `
"<template>
<div>Bar</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/ButtonTest.vue 1`] = `
"<script lang=\\"ts\\">
import Button from './Foo.vue';
import button from './Foo.vue';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
return {
Button,
button
};
};
__sfc_main.components = Object.assign({
Button
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<div>
<button>{{ Button }}</button>
<Button>{{ button }}</Button>
</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/Foo.vue 1`] = `
"<script lang=\\"ts\\">
const __sfc_main = {};
export default __sfc_main;
</script>
<template>
<div>
Button Component: <slot></slot>
</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/HelloWorld.vue 1`] = `
"
<script lang=\\"ts\\">
import { ref as _ref, computed as _computed } from \\"@vue/composition-api\\";
import { watch } from '@vue/composition-api';
import Foo from './Foo.vue';
import Bar from './Bar.vue';
const __sfc_main = {
name: 'App'
};
__sfc_main.props = {
msg: {
key: \\"msg\\",
required: false,
type: String,
default: 'Hello'
},
name: {
key: \\"name\\",
required: true,
type: [String, Number]
}
};
__sfc_main.setup = (__props, __ctx) => {
const emit = __ctx.emit;
let count = _ref(0);
// eslint-disable-next-line prefer-const
let doubled = _computed(() => count.value * 2);
function inc() {
count.value += 1;
}
function dec() {
count.value -= 1;
}
const decText = '<b>Dec</b>';
watch(count.value, value => emit('update', value));
return {
Foo,
Bar,
count,
doubled,
inc,
dec,
decText
};
};
export default __sfc_main;
</script>
<template>
<div>
<h3>{{ msg }}, {{ name }}</h3>
<button @click=\\"inc\\">
Inc
</button>
<div>{{ count }} x 2 = {{ doubled }}</div>
<button @click=\\"dec()\\" v-html=\\"decText\\" />
<component :is=\\"count > 2 ? Foo : Bar\\" />
</div>
</template>
"
`;
exports[`transform > fixtures > playground/src/main.ts 1`] = `
"import Vue from 'vue'
import VueCompositionAPI, { createApp, h } from '@vue/composition-api'
import App from './App.vue'
Vue.use(VueCompositionAPI)
const app = createApp({ render: () => h(App) })
app.mount('#app')
"
`;
exports[`transform > fixtures > playground/src/shims-vue.d.ts 1`] = `
"/// <reference types=\\"unplugin-vue2-script-setup/shims.js\\" />
/// <reference types=\\"unplugin-vue2-script-setup/ref-macros.js\\" />
"
`;
exports[`transform > fixtures > test/fixtures/AsyncImport.vue 1`] = `
"<script lang=\\"ts\\">
import { defineAsyncComponent } from '@vue/composition-api';
const ScriptOnly = defineAsyncComponent(() => import('./ScriptOnly.vue'));
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
return {};
};
__sfc_main.components = Object.assign({
ScriptOnly
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<ScriptOnly />
</template>
"
`;
exports[`transform > fixtures > test/fixtures/ComponentsDirectives.vue 1`] = `
"<template>
<div>
<FooView
v-if=\\"now > 0\\"
ref=\\"fooView\\"
v-foo-bar=\\"message0\\"
v-d0-demo:foo.a.b=\\"message1\\"
v-d1-modifiers.a=\\"message2\\"
v-d2-modifiers-no-value.b.c
v-d3-arg:click=\\"message3\\"
v-d4-arg-no-value:click
v-d5-arg-dynamic:[direction1+direction2.length].c=\\"message4\\"
v-d6-arg-dynamic-no-value:[direction3]
v-d6-arg-dynamic-no-value:shouldNotUsed
>
<template #[slotName]=\\"{ foo = 1 }\\">
<div>
good {{ foo }}
</div>
</template>
<template v-slot:default=\\"bar\\">
<div>
good {{ bar }}
</div>
</template>
</FooView>
<button v-else-if=\\"now === 0\\" :[propNamePrefix+propName]=\\"1\\" @[eventPrefix.value+eventName]=\\"console.log($event)\\"></button>
<router-view v-else></router-view>
<img src=\\"https://vuejs.org/images/logo.svg\\">
</div>
</template>
<script lang=\\"ts\\">
import { ref } from '@vue/runtime-dom';
import FooView from './FooView.vue';
import { vFooBar, vDemo as vD0Demo, vD1Modifiers, vD2ModifiersNoValue, vD3Arg, vD4ArgNoValue, vD5ArgDynamic, vD6ArgDynamicNoValue } from './directive';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const fooView = ref<null | InstanceType<typeof FooView>>(null);
const message0 = ref('hello');
const message1 = ref('hello');
const message2 = ref('hello');
const message3 = ref('hello');
const message4 = ref('hello');
const direction1 = ref('top');
const direction2 = ref('top');
const direction3 = ref('top');
const shouldNotUsed = ref('');
const propNamePrefix = 'vue-';
const propName = 'color';
const eventPrefix = {
value: 'vue-'
};
const eventName = '';
const console = globalThis.console;
const now = Date.now();
const slotName = 'footer';
return {
fooView,
message0,
message1,
message2,
message3,
message4,
direction1,
direction2,
direction3,
propNamePrefix,
propName,
eventPrefix,
eventName,
console,
now,
slotName
};
};
__sfc_main.components = Object.assign({
FooView
}, __sfc_main.components);
__sfc_main.directives = Object.assign({
fooBar: vFooBar,
d0Demo: vD0Demo,
d1Modifiers: vD1Modifiers,
d2ModifiersNoValue: vD2ModifiersNoValue,
d3Arg: vD3Arg,
d4ArgNoValue: vD4ArgNoValue,
d5ArgDynamic: vD5ArgDynamic,
d6ArgDynamicNoValue: vD6ArgDynamicNoValue
}, __sfc_main.directives);
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/ComponentsDirectivesLocal.vue 1`] = `
"
<script lang=\\"ts\\">
import type { ObjectDirective } from '@vue/composition-api';
// enables v-focus in templates
const vFocus: ObjectDirective<HTMLInputElement, null> = {
inserted: (el, binding, vnode, oldVnode) => {
el.focus();
}
};
const __sfc_main = {};
__sfc_main.directives = Object.assign({
focus: vFocus
}, __sfc_main.directives);
export default __sfc_main;
</script>
<template>
<input v-focus=\\"null\\">
</template>
"
`;
exports[`transform > fixtures > test/fixtures/ComponentsLocal.vue 1`] = `
"
<script lang=\\"tsx\\">
const HelloComponent = ({
name
}: {
name: string;
}) => <span>Hello, {name}</span>;
const __sfc_main = {};
__sfc_main.components = Object.assign({
HelloComponent
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<HelloComponent name=\\"Evan You\\" />
</template>
"
`;
exports[`transform > fixtures > test/fixtures/DynamicStyle.vue 1`] = `
"<template>
<div :style=\\"{ color, border: '1px' }\\" />
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const color = computed(() => 'red');
return {
color
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/Empty.vue 1`] = `""`;
exports[`transform > fixtures > test/fixtures/Enum.vue 1`] = `
"<template>
<div>{{ MyEnum }}</div>
</template>
<script lang=\\"ts\\">
enum MyEnum {
test = 'true',
}
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
return {
MyEnum
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/HtmlTag.vue 1`] = `
"<script lang=\\"ts\\">
import Enum from './Enum.vue';
import { ref } from '@vue/composition-api';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
let p = \\"hello word\\";
let Div = ref(\\"hello word\\");
let h3 = 'test';
let H3 = '';
return {
p,
Div,
h3,
H3
};
};
__sfc_main.components = Object.assign({
Enum
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<div>
<Enum />
<h3></h3>
{{ H3 }}
{{ h3 }}
{{ Div }}
<p>{{ p }}</p>
</div>
</template>
"
`;
exports[`transform > fixtures > test/fixtures/HtmlTag2.vue 1`] = `
"<script lang=\\"ts\\">
import Button from './DynamicStyle.vue';
import button from './DynamicStyle.vue';
import { defineAsyncComponent } from '@vue/composition-api';
const footer = defineAsyncComponent(() => import('./ScriptOnly.vue'));
import { ref } from '@vue/composition-api';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const p = ref(\\"hello word\\");
return {
Button,
button,
p
};
};
__sfc_main.components = Object.assign({
Button
}, __sfc_main.components);
export default __sfc_main;
</script>
<template>
<div>
<p>{{ p }}</p>
<button>{{ Button }}</button>
<Button>{{ button }}</Button>
<footer>FOOTER</footer>
</div>
</template>
"
`;
exports[`transform > fixtures > test/fixtures/JSLongComment.vue 1`] = `
"<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const a = 1;
////////////////
return {
a
};
};
export default __sfc_main;
</script>
<template>
<div>{{ a }}</div>
</template>
"
`;
exports[`transform > fixtures > test/fixtures/Macros.vue 1`] = `
"<template>
<div @click=\\"emit(props.msg)\\">{{ msg }}</div>
</template>
<script lang=\\"js\\">
const __sfc_main = {};
__sfc_main.props = {
msg: String
};
__sfc_main.setup = (__props, __ctx) => {
const props = __props;
const emit = __ctx.emit;
return {
props,
emit
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosDefineExpose.vue 1`] = `
"<template>
<div>{{ a }}</div>
<div>{{ text?.length ?? textLengthDefault }}</div>
</template>
<script lang=\\"ts\\">
import { ref } from '@vue/composition-api';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const a = ref(1);
const b = 1;
const text = 'hello';
const textLengthDefault = 0;
return Object.assign({
a,
text,
textLengthDefault
}, {
b
});
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosPure.vue 1`] = `
"<template>
<div>{{ msg }}</div>
</template>
<script >
const __sfc_main = {};
__sfc_main.props = {
msg: String
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType.vue 1`] = `
"<template>
<div @click=\\"emit('update', props.msg)\\">
{{ msg }}
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.props = {
msg: {
key: \\"msg\\",
required: false,
type: String,
default: 'Hello'
},
value: {
key: \\"value\\",
required: true,
type: [Number, String]
},
data: {
key: \\"data\\",
required: false,
type: Object
}
};
__sfc_main.setup = (__props, __ctx) => {
const props = __props;
const emit = __ctx.emit;
return {
props,
emit
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType2.vue 1`] = `
"<template>
<div @click=\\"emit(props.msg)\\">
{{ msg }}
</div>
</template>
<script lang=\\"ts\\">
export interface Props {
msg: string;
value: number | string;
data?: {
value: boolean;
};
arr?: [number, string, {}];
any: any;
}
const __sfc_main = {};
__sfc_main.props = {
msg: {
key: \\"msg\\",
required: false,
type: String,
default: 'Hello'
},
value: {
key: \\"value\\",
required: true,
type: [Number, String]
},
data: {
key: \\"data\\",
required: false,
type: Object
},
arr: {
key: \\"arr\\",
required: false,
type: Array
},
any: {
key: \\"any\\",
required: true,
type: null
}
};
__sfc_main.setup = (__props, __ctx) => {
const props = __props;
const emit = __ctx.emit;
return {
props,
emit
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType3.vue 1`] = `
"<template>
<div @click=\\"emit('update', msg);emit('update', msg + value);\\">
{{ msg }}
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.props = {
msg: {
key: \\"msg\\",
required: false,
type: String,
default: 'Hello'
},
value: {
key: \\"value\\",
required: true,
type: [Number, String]
},
data: {
key: \\"data\\",
required: false,
type: Object
}
};
__sfc_main.setup = (__props, __ctx) => {
const emit = __ctx.emit;
const value = 'bar';
return {
emit,
value
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType4.vue 1`] = `
"<script lang=\\"ts\\">
type Test = number[];
const __sfc_main = {};
__sfc_main.props = {
test: {
key: \\"test\\",
required: false,
type: null,
default: () => []
}
};
__sfc_main.setup = (__props, __ctx) => {
return {};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType5.vue 1`] = `
"<script lang=\\"ts\\">
const __sfc_main = {};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosType6.vue 1`] = `
"<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const slots = __ctx.slots;
return {
slots
};
};
export default __sfc_main;
</script>
<template>
<template v-if=\\"slots.default\\">
<slot name=\\"default\\" msg=\\"hello\\"></slot>
</template>
<div v-else>
fallback
</div>
</template>
"
`;
exports[`transform > fixtures > test/fixtures/MacrosTypeAny.vue 1`] = `
"<template>
<div>
{{ value }}
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.props = {
value: {
key: \\"value\\",
required: true,
type: null
}
};
__sfc_main.setup = (__props, __ctx) => {
const props = __props;
return {};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/Object1.vue 1`] = `
"<template>
<div>
{{ data }} {{ title }}
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const data = {
title: 'Template Only'
};
return {
data
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/ObjectDestructure.vue 1`] = `
"<template>
<div></div>
</template>
<script >
import { toRefs, reactive } from 'vue';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const state = reactive({
data: 'hello'
});
const {
data
} = toRefs(state);
return {};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/Pug1.vue 1`] = `
"<template lang=\\"pug\\">
.root
span {{ data }} {{ title }}
.p-1(@click=\\"foo\\")
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const data = {
title: 'Template Only'
};
function foo() {}
return {
data,
foo
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/RefSugar.ts 1`] = `
"import { ref as _ref, computed as _computed } from '@vue/composition-api'
/* eslint-disable unused-imports/no-unused-vars */
/* eslint-disable prefer-const */
import { ref } from '@vue/composition-api'
let ref1 = (ref('hello'))
ref1.value = 'world'
let ref1Raw = (ref1)
let ref2 = _ref('hello')
ref2.value = 'world'
let ref2Raw = (ref2)
let computed1 = _computed(() => ref2.value += ' str')
let computed1Raw = (computed1)
"
`;
exports[`transform > fixtures > test/fixtures/RefSugar.vue 1`] = `
"<template>
<div>
{{ msg }}
{{ msg2 }}
{{ a }}
</div>
</template>
<script lang=\\"ts\\">
import { ref as _ref } from \\"@vue/composition-api\\";
/* eslint-disable prefer-const */
import { defineComponent, ref } from '@vue/composition-api';
const __sfc_main = defineComponent({
setup() {
let msg = ref('hello world');
return {
msg: msg.value,
msgRef: msg
};
}
});
__sfc_main.setup = (__props, __ctx) => {
let msg2 = _ref('hello world');
let a: number;
a = 2;
return {
msg2,
a
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/RefSugarScriptSetup.vue 1`] = `
"<template>
<div>
{{ msg }}
</div>
</template>
<script lang=\\"ts\\">
import { ref as _ref } from \\"@vue/composition-api\\";
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
/* eslint-disable prefer-const */
let msg = _ref('hello world');
return {
msg
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/ScriptLessThanOrEqualTo.vue 1`] = `
"<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const val: boolean = 1 <= 2;
return {
val
};
};
export default __sfc_main;
</script>
<template>
<div>{{ val }}</div>
</template>
"
`;
exports[`transform > fixtures > test/fixtures/ScriptOnly.vue 1`] = `
"<script lang=\\"ts\\">
// <script setup> in comment should not be matched
import { defineComponent } from '@vue/composition-api';
const __sfc_main = defineComponent({
name: 'Hi'
});
export default __sfc_main;
</script>
<!-- <script setup> -->
"
`;
exports[`transform > fixtures > test/fixtures/TemplateOnly.vue 1`] = `
"<template>
<div>
<div v-for=\\"(item, index) in items\\" :key=\\"item\\">
{{ index }}: {{ item }}
</div>
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const items = [{
name: 'Item 1',
value: 1
}, {
name: 'Item 2',
value: 2
}, {
name: 'Item 3',
value: 3
}, {
name: 'Item 4',
value: 4
}];
return {
items
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/TemplateOptionalChaining.vue 1`] = `
"<template>
<div @click=\\"callback?.()\\">
{{ text?.length ?? textLengthDefault?.[index] }}
{{ classes?.[0] }}
{{ arrayObj.data.value[i] }}
</div>
</template>
<script lang=\\"ts\\">
import { ref } from '@vue/composition-api';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const text = 'hello';
const textLengthDefault = '';
const index = ref(0);
const callback = (undefined as undefined | (() => void));
const classes = (undefined as undefined | string[]);
const arrayObj = {
data: {
value: [1, 2, 3]
}
};
const i = 0;
return {
text,
textLengthDefault,
index,
callback,
classes,
arrayObj,
i
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/VFor.vue 1`] = `
"<template>
<div>
<div v-for=\\"(item, index) in items\\" :key=\\"item\\">
{{ item }}
</div>
<div v-for=\\"{ cargo_key, in_of } in items2\\">
{{ cargo_key }}
</div>
</div>
</template>
<script lang=\\"ts\\">
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const items = [{
name: 'Item 1',
value: 1
}, {
name: 'Item 2',
value: 2
}, {
name: 'Item 3',
value: 3
}, {
name: 'Item 4',
value: 4
}];
const items2 = [{
cargo_key: 'cargo_key',
in_of: 'in'
}];
const index = 0;
return {
items,
items2
};
};
export default __sfc_main;
</script>
"
`;
exports[`transform > fixtures > test/fixtures/VariableBinding.vue 1`] = `
"<script lang=\\"ts\\">
/* eslint-disable no-console */
import { reactive } from '@vue/composition-api';
const __sfc_main = {};
__sfc_main.setup = (__props, __ctx) => {
const state = reactive({
value: ''
});
function showEvent(event: MouseEvent) {
console.log(event);
}
const emit = __ctx.emit;
return {
state,
showEvent,
emit
};
};
export default __sfc_main;
</script>
<template>
<div
@click=\\"
state.value = \`x: \${$event.x}\`
showEvent($event)
emit('foo')
\\"
>
2333
</div>
</template>
"
`;
================================================
FILE: test/errors.test.ts
================================================
import { describe, expect, it, vi } from 'vitest'
import { transform as t } from '../src'
describe('errors', () => {
it('langs', async () => {
await expect(() =>
t(`
<script setup>
const a = 1
</script>
<script lang="ts">
export default {}
</script>
`, 'Lang.vue')).rejects.toThrowError('<script setup> language must be the same as <script>')
})
it('defineProps', async () => {
await expect(() =>
t(`
<script setup>
defineProps()
const a = defineProps()
</script>
`, 'DefineProps.vue'))
.rejects.toThrowError('duplicate defineProps() call')
})
it('top-level await', async () => {
await expect(() =>
t(`
<script setup>
defineProps()
await something()
</script>
`, 'TopLevel.vue'))
.rejects.toThrowError('top-level await is not supported in Vue 2')
await expect(() =>
t(`
<script setup>
defineProps()
const {data} = await something()
</script>
`, 'TopLevel.vue'))
.rejects.toThrowError('top-level await is not supported in Vue 2')
})
it('ref sugar', async () => {
const consoleWarnMock = vi.spyOn(console, 'warn')
await t(`
<script setup>
defineProps()
const a = async () => {
await something()
}
</script>
`, 'App.vue')
consoleWarnMock.mockRestore()
})
})
================================================
FILE: test/fixtures/AsyncImport.vue
================================================
<script setup lang="ts">
import { defineAsyncComponent } from '@vue/composition-api'
const ScriptOnly = defineAsyncComponent(() => import('./ScriptOnly.vue'))
</script>
<template>
<ScriptOnly />
</template>
================================================
FILE: test/fixtures/ComponentsDirectives.vue
================================================
<template>
<div>
<FooView
v-if="now > 0"
ref="fooView"
v-foo-bar="message0"
v-d0-demo:foo.a.b="message1"
v-d1-modifiers.a="message2"
v-d2-modifiers-no-value.b.c
v-d3-arg:click="message3"
v-d4-arg-no-value:click
v-d5-arg-dynamic:[direction1+direction2.length].c="message4"
v-d6-arg-dynamic-no-value:[direction3]
v-d6-arg-dynamic-no-value:shouldNotUsed
>
<template #[slotName]="{ foo = 1 }">
<div>
good {{ foo }}
</div>
</template>
<template v-slot:default="bar">
<div>
good {{ bar }}
</div>
</template>
</FooView>
<button v-else-if="now === 0" :[propNamePrefix+propName]="1" @[eventPrefix.value+eventName]="console.log($event)"></button>
<router-view v-else></router-view>
<img src="https://vuejs.org/images/logo.svg">
</div>
</template>
<script setup lang="ts">
import { ref } from '@vue/runtime-dom'
import FooView from './FooView.vue'
import { vFooBar, vDemo as vD0Demo, vD1Modifiers, vD2ModifiersNoValue, vD3Arg, vD4ArgNoValue, vD5ArgDynamic, vD6ArgDynamicNoValue } from './directive'
const fooView = ref<null | InstanceType<typeof FooView>>(null)
const message0 = ref('hello')
const message1 = ref('hello')
const message2 = ref('hello')
const message3 = ref('hello')
const message4 = ref('hello')
const direction1 = ref('top')
const direction2 = ref('top')
const direction3 = ref('top')
const shouldNotUsed = ref('')
const propNamePrefix = 'vue-'
const propName = 'color'
const eventPrefix = { value: 'vue-' }
const eventName = ''
const console = globalThis.console
const now = Date.now()
const slotName = 'footer'
</script>
================================================
FILE: test/fixtures/ComponentsDirectivesLocal.vue
================================================
<script lang="ts">
import type { ObjectDirective } from '@vue/composition-api'
// enables v-focus in templates
const vFocus: ObjectDirective<HTMLInputElement, null> = {
inserted: (el, binding, vnode, oldVnode) => {
el.focus()
},
}
</script>
<script setup lang="ts"></script>
<template>
<input v-focus="null">
</template>
================================================
FILE: test/fixtures/ComponentsLocal.vue
================================================
<script lang="tsx">
const HelloComponent = ({ name }: { name: string }) => <span>Hello, {name}</span>
</script>
<script setup lang="tsx"></script>
<template>
<HelloComponent name="Evan You" />
</template>
================================================
FILE: test/fixtures/DynamicStyle.vue
================================================
<template>
<div :style="{ color, border: '1px' }" />
</template>
<script setup lang="ts">
const color = computed(() => 'red')
</script>
================================================
FILE: test/fixtures/Empty.vue
================================================
================================================
FILE: test/fixtures/Enum.vue
================================================
<template>
<div>{{ MyEnum }}</div>
</template>
<script setup lang="ts">
enum MyEnum {
test = 'true',
}
</script>
================================================
FILE: test/fixtures/HtmlTag.vue
================================================
<script setup lang="ts">
import Enum from './Enum.vue'
import { ref } from '@vue/composition-api';
let p = "hello word";
let Div = ref("hello word");
let h3 = 'test'
let H3 = ''
</script>
<template>
<div>
<Enum />
<h3></h3>
{{ H3 }}
{{ h3 }}
{{ Div }}
<p>{{ p }}</p>
</div>
</template>
================================================
FILE: test/fixtures/HtmlTag2.vue
================================================
<script setup lang="ts">
import Button from './DynamicStyle.vue';
import button from './DynamicStyle.vue';
import { defineAsyncComponent } from '@vue/composition-api'
const footer = defineAsyncComponent(() => import('./ScriptOnly.vue'))
import { ref } from '@vue/composition-api';
const p = ref("hello word");
</script>
<template>
<div>
<p>{{ p }}</p>
<button>{{ Button }}</button>
<Button>{{ button }}</Button>
<footer>FOOTER</footer>
</div>
</template>
================================================
FILE: test/fixtures/JSLongComment.vue
================================================
<script setup lang="ts">
const a = 1
////////////////
</script>
<template>
<div>{{ a }}</div>
</template>
================================================
FILE: test/fixtures/Macros.vue
================================================
<template>
<div @click="emit(props.msg)">{{ msg }}</div>
</template>
<script setup lang="js">
const props = defineProps({
msg: String,
})
const emit = defineEmits(['msg', 'update'])
</script>
================================================
FILE: test/fixtures/MacrosDefineExpose.vue
================================================
<template>
<div>{{ a }}</div>
<div>{{ text?.length ?? textLengthDefault }}</div>
</template>
<script setup lang="ts">
import { ref } from '@vue/composition-api'
const a = ref(1)
const b = 1
const text = 'hello'
const textLengthDefault = 0
defineExpose({ b })
</script>
================================================
FILE: test/fixtures/MacrosPure.vue
================================================
<template>
<div>{{ msg }}</div>
</template>
<script setup>
defineEmits()
defineProps({
msg: String,
})
</script>
================================================
FILE: test/fixtures/MacrosType.vue
================================================
<template>
<div @click="emit('update', props.msg)">
{{ msg }}
</div>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{ msg: string; value: number | string; data?: { value: boolean } }>(),
{ msg: 'Hello' },
)
const emit = defineEmits(['update'])
</script>
================================================
FILE: test/fixtures/MacrosType2.vue
================================================
<template>
<div @click="emit(props.msg)">
{{ msg }}
</div>
</template>
<script setup lang="ts">
export interface Props {
msg: string
value: number | string
data?: { value: boolean }
arr?: [number, string, {}]
any: any
}
const props = withDefaults(
defineProps<Props>(),
{ msg: 'Hello' },
)
const emit = defineEmits<{
(msg: string): void
(msg: number): number
}>()
</script>
================================================
FILE: test/fixtures/MacrosType3.vue
================================================
<template>
<div @click="emit('update', msg);emit('update', msg + value);">
{{ msg }}
</div>
</template>
<script setup lang="ts">
withDefaults(
defineProps<{ msg: string; value: number | string; data?: { value: boolean } }>(),
{ msg: 'Hello' },
)
const emit = defineEmits(['update'])
const value = 'bar'
</script>
================================================
FILE: test/fixtures/MacrosType4.vue
================================================
<script setup lang="ts">
type Test = number[]
withDefaults(
defineProps<{ test: Test }>(),
{ test: () => [] },
)
</script>
================================================
FILE: test/fixtures/MacrosType5.vue
================================================
<script setup lang="ts">
defineSlots<{
default: { msg: string }
}>()
</script>
================================================
FILE: test/fixtures/MacrosType6.vue
================================================
<script setup lang="ts">
const slots = defineSlots<{
default: { msg: string }
}>()
</script>
<template>
<template v-if="slots.default">
<slot name="default" msg="hello"></slot>
</template>
<div v-else>
fallback
</div>
</template>
================================================
FILE: test/fixtures/MacrosTypeAny.vue
================================================
<template>
<div>
{{ value }}
</div>
</template>
<script setup lang="ts">
const props = defineProps<{ value: any }>()
</script>
================================================
FILE: test/fixtures/Object1.vue
================================================
<template>
<div>
{{ data }} {{ title }}
</div>
</template>
<script setup lang="ts">
const data = {
title: 'Template Only',
}
</script>
================================================
FILE: test/fixtures/ObjectDestructure.vue
================================================
<template>
<div></div>
</template>
<script setup>
import { toRefs, reactive } from 'vue'
const state = reactive({ data: 'hello' })
const { data } = toRefs(state)
</script>
================================================
FILE: test/fixtures/Pug1.vue
================================================
<template lang="pug">
.root
span {{ data }} {{ title }}
.p-1(@click="foo")
</template>
<script setup lang="ts">
const data = {
title: 'Template Only',
}
function foo() {}
</script>
================================================
FILE: test/fixtures/RefSugar.ts
================================================
/* eslint-disable unused-imports/no-unused-vars */
/* eslint-disable prefer-const */
import { ref } from '@vue/composition-api'
let ref1 = $(ref('hello'))
ref1 = 'world'
let ref1Raw = $$(ref1)
let ref2 = $ref('hello')
ref2 = 'world'
let ref2Raw = $$(ref2)
let computed1 = $computed(() => ref2 += ' str')
let computed1Raw = $$(computed1)
================================================
FILE: test/fixtures/RefSugar.vue
================================================
<template>
<div>
{{ msg }}
{{ msg2 }}
{{ a }}
</div>
</template>
<script lang="ts">
/* eslint-disable prefer-const */
import { defineComponent, ref } from '@vue/composition-api'
export default defineComponent({
setup() {
let msg = $(ref('hello world'))
return {
msg,
msgRef: $$(msg),
}
},
})
</script>
<script setup lang="ts">
let msg2 = $ref('hello world')
let a: number
a = 2
</script>
================================================
FILE: test/fixtures/RefSugarScriptSetup.vue
================================================
<template>
<div>
{{ msg }}
</div>
</template>
<script setup lang="ts">
/* eslint-disable prefer-const */
let msg = $ref('hello world')
</script>
================================================
FILE: test/fixtures/ScriptLessThanOrEqualTo.vue
================================================
<script setup lang="ts">
const val: boolean = 1 <= 2
</script>
<template>
<div>{{ val }}</div>
</template>
================================================
FILE: test/fixtures/ScriptOnly.vue
================================================
<script lang="ts">
// <script setup> in comment should not be matched
import { defineComponent } from '@vue/composition-api'
export default defineComponent({
name: 'Hi',
})
</script>
<!-- <script setup> -->
================================================
FILE: test/fixtures/TemplateOnly.vue
================================================
<template>
<div>
<div v-for="(item, index) in items" :key="item">
{{ index }}: {{ item }}
</div>
</div>
</template>
<script setup lang="ts">
const items = [
{ name: 'Item 1', value: 1 },
{ name: 'Item 2', value: 2 },
{ name: 'Item 3', value: 3 },
{ name: 'Item 4', value: 4 },
]
</script>
================================================
FILE: test/fixtures/TemplateOptionalChaining.vue
================================================
<template>
<div @click="callback?.()">
{{ text?.length ?? textLengthDefault?.[index] }}
{{ classes?.[0] }}
{{ arrayObj.data.value[i] }}
</div>
</template>
<script setup lang="ts">
import { ref } from '@vue/composition-api'
const text = 'hello'
const textLengthDefault = ''
const index = ref(0)
const callback = undefined as undefined | (() => void)
const classes = undefined as undefined | string[]
const arrayObj = { data: { value: [1, 2, 3] } }
const i = 0
</script>
================================================
FILE: test/fixtures/VFor.vue
================================================
<template>
<div>
<div v-for="(item, index) in items" :key="item">
{{ item }}
</div>
<div v-for="{ cargo_key, in_of } in items2">
{{ cargo_key }}
</div>
</div>
</template>
<script setup lang="ts">
const items = [
{ name: 'Item 1', value: 1 },
{ name: 'Item 2', value: 2 },
{ name: 'Item 3', value: 3 },
{ name: 'Item 4', value: 4 },
]
const items2 = [
{ cargo_key: 'cargo_key', in_of: 'in' },
]
const index = 0
</script>
================================================
FILE: test/fixtures/VariableBinding.vue
================================================
<script setup lang="ts">
/* eslint-disable no-console */
import { reactive } from '@vue/composition-api'
const state = reactive({ value: '' })
function showEvent(event: MouseEvent) {
console.log(event)
}
const emit = defineEmits<{
(event: 'foo'): void
}>()
</script>
<template>
<div
@click="
state.value = `x: ${$event.x}`
showEvent($event)
emit('foo')
"
>
2333
</div>
</template>
================================================
FILE: test/identifiers.test.ts
================================================
/* eslint-disable no-template-curly-in-string */
import { parse } from '@babel/parser'
import { describe, expect, it } from 'vitest'
import { getIdentifierDeclarations, getIdentifierUsages } from '../src/core/identifiers'
describe('identifiers', () => {
describe('should identifier declarations', () => {
const cases: [string, string[]][] = [
['var a = 1', ['a']],
['import { foo, t as bar } from "z"', ['foo', 'bar']],
['import foo from "z"', ['foo']],
['import Button from \'./DynamicStyle.vue\'', ['Button']],
['import button from \'./DynamicStyle.vue\'', ['button']],
['import * as foo from "z"', ['foo']],
['function foo(bar) {const a = z}', ['foo']],
['console.log(foo)', []],
['var { data } = toRefs(state)', ['data']],
['const { data, ...args } = bar', ['data', 'args']],
['const { foo: bar } = bar', ['bar']],
['const { foo: { a, b: c, d: { e: [f] } } } = { bar }', ['a', 'c', 'f']],
['let [a, b,, ...c] = bar', ['a', 'b', 'c']],
['let [a, b, [c, {d}],, ...e] = bar', ['a', 'b', 'c', 'd', 'e']],
['class A extends B {}', ['A']],
]
for (const [input, output] of cases) {
it(input, () => {
const ast = parse(input, {
sourceType: 'module',
})
expect(new Set(getIdentifierDeclarations(ast.program.body))).toEqual(new Set(output))
})
}
})
describe('should identifier usages', () => {
const cases: [string, string[]][] = [
['foo', ['foo']],
['foo.bar', ['foo']],
['foo(bar, console.log, ...args)', ['foo', 'bar', 'console', 'args']],
['foo(bar())', ['foo', 'bar']],
['for (let x in foo) {}', ['foo']],
['for (let [x, idx] of foo) {}', ['foo']],
['a + b', ['a', 'b']],
['a ? "" : b < c', ['a', 'b', 'c']],
['a == b && a === c || d != e', ['a', 'b', 'c', 'd', 'e']],
['({ a, b, ...args, [c]: 1, d: e, f: { g } })', ['a', 'b', 'args', 'c', 'e', 'g']],
['!a', ['a']],
['!!c', ['c']],
['[a,b,[c,{d}],...args]', ['a', 'b', 'c', 'd', 'args']],
['new Foo(a,[b,,c])', ['Foo', 'a', 'b', 'c']],
['new RC.Foo()', ['RC']],
['() => foo(bar)', ['foo', 'bar']],
['() => { foo() + bar; a }', ['foo', 'bar', 'a']],
['(function () { foo() + bar })', ['foo', 'bar']],
['function foobar() { return foo() + bar }', ['foo', 'bar']],
['`${foo}bar`', ['foo']],
['`${foo(zag)}` + bar', ['foo', 'zag', 'bar']],
]
for (const [input, output] of cases) {
it(input, () => {
const nodes = parse(input).program.body
const i = new Set<string>()
nodes.forEach(node => getIdentifierUsages(node, i))
expect(i).toEqual(new Set(output))
})
}
})
})
================================================
FILE: test/nativeTag.test.ts
================================================
import { describe, expect, it } from 'vitest'
import { transform } from '../src'
describe('filter native tags as vue components', () => {
describe('no components output', () => {
const cases: string[] = [
`
<script setup lang="ts">
import button from './DynamicStyle.vue';
</script>
<template>
<button>{{ Button }}</button>
</template>
`,
`
<script setup lang="ts">
let p='hello'
let Div='hello'
</script>
<template>
<div>
<p>{{ p }}</p>
<Button>{{ Div }}</Button>
</div>
</template>
`,
`
<script setup lang="ts">
let svg='hello'
</script>
<template>
<div>
<p>{{ svg }}</p>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 283.46 283.46">
<defs>
<style>
.cls-1{fill:#231815;}
@media (prefers-color-scheme: dark) { .cls-1{fill:#ffffff;} }
</style>
</defs>
<path class="cls-1" d="M144.89,89.86c-33.46,0-54.44,14.56-66.14,26.76a86,86,0,0,0-23.69,58.94c0,22.64,8.81,43.48,24.8,58.67,15.7,14.92,36.9,23.14,59.68,23.14,23.81,0,46-8.49,62.49-23.91,17-15.9,26.37-37.93,26.37-62C228.4,120.37,185.94,89.86,144.89,89.86Zm.49,153.67a61.49,61.49,0,0,1-46.45-20.4c-12.33-13.76-18.85-32.64-18.85-54.62,0-20.7,6.07-37.67,17.57-49.07,10.11-10,24.39-15.62,40.19-15.74,19,0,35.22,6.56,46.76,19,12.6,13.58,19.27,34,19.27,58.95C203.87,224.39,174.49,243.53,145.38,243.53Z"/>
<polygon class="cls-1" points="198.75 74.96 179.45 74.96 142.09 37.83 104.51 74.96 86.14 74.96 138.09 24.25 146.81 24.25 198.75 74.96"/>
</svg>
</div>
</template>
`,
]
for (const input of cases) {
it(input, async () => {
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
expect(result?.code.includes('__sfc_main.components')).toEqual(false)
})
}
})
it('capitalized native tags as components', async () => {
const input = `
<script setup lang="ts">
import Button from './DynamicStyle.vue';
</script>
<template>
<Button>{{ Button }}</Button>
</template>
`
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
expect(result?.code.includes('__sfc_main.components = Object.assign({\n Button\n}, __sfc_main.components);')).toEqual(true)
})
it('keep non-native components', async () => {
const input = `
<script setup lang="ts">
import Button from './DynamicStyle.vue';
import DynamicStyle from './DynamicStyle.vue';
let p='hello'
</script>
<template>
<dynamic-style/>
<p>{{ p }}</p>
<button>{{ Button }}</button>
</template>
`
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
expect(result?.code.includes('__sfc_main.components = Object.assign({\n DynamicStyle\n}, __sfc_main.components);')).toEqual(true)
})
})
================================================
FILE: test/transform.test.ts
================================================
import { resolve } from 'node:path'
import { promises as fs } from 'node:fs'
import fg from 'fast-glob'
import { describe, expect, it } from 'vitest'
import { transform } from '../src'
describe('transform', () => {
describe('fixtures', () => {
const root = resolve(__dirname, '..')
const files = fg.sync([
'test/fixtures/*.{vue,js,ts}',
'playground/src/*.{vue,js,ts}',
], {
cwd: root,
onlyFiles: true,
})
for (const file of files) {
it(file.replace(/\\/g, '/'), async () => {
const fixture = await fs.readFile(resolve(root, file), 'utf-8')
const result = (await transform(fixture, file, { reactivityTransform: true }))?.code || fixture
expect(result).toMatchSnapshot()
const result2 = (await transform(result, file, { reactivityTransform: true }))?.code || result
expect(result).toEqual(result2)
})
}
})
})
================================================
FILE: test/transform_filter.test.ts
================================================
import { } from 'node:test'
import { describe, expect, it } from 'vitest'
import { scriptSetupRE } from '../src/core'
describe('transform filter', () => {
describe('look for what needs to be converted by regular ', () => {
const cases: string[] = [
`<script lang="tsx"
setup>
import HelloWorld from './HelloWorld.vue'
</setup>`,
'<script lang="ts" setup>',
`<script
lang="ts"
setup>
import HelloWorld from './HelloWorld.vue'
</script>`,
`<script
setup>
import HelloWorld from './HelloWorld.vue'
</script>`,
`<script setup>
import HelloWorld from './HelloWorld.vue'
</script>`,
`<script setup lang="tsx" >
import HelloWorld from './HelloWorld.vue'
</script>
`,
`<script setup
lang="ts">
import HelloWorld from './HelloWorld.vue'
</script>
`,
`<script
setup
lang="ts">
import HelloWorld from './HelloWorld.vue'
</script>
`,
`<script setup
lang="ts">
import HelloWorld from './HelloWorld.vue'
</script>`,
]
for (const input of cases) {
it(input, () => {
expect(scriptSetupRE.test(input)).toEqual(true)
})
}
})
describe('filter what is not needed by regular ', () => {
const cases: string[] = [
`<scriptlang="ts"
setup>
import HelloWorld from './HelloWorld.vue'
</script>
`,
`<script lang="ts">
import HelloWorld from './HelloWorld.vue'
</script>`,
`<script>
import HelloWorld from './HelloWorld.vue'
</script>
`,
]
for (const input of cases) {
it(input, () => {
expect(scriptSetupRE.test(input)).toEqual(false)
})
}
})
})
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2017",
"module": "esnext",
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"jsx": "preserve",
"strictNullChecks": true,
"isolatedModules": true,
"resolveJsonModule": true
}
}
================================================
FILE: types.d.ts
================================================
export { default } from './dist/types'
================================================
FILE: vite.d.ts
================================================
export { default } from './dist/vite'
================================================
FILE: webpack.d.ts
================================================
export { default } from './dist/webpack'
gitextract_7eas0mg5/ ├── .eslintignore ├── .eslintrc.json ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .tazerc.json ├── LICENSE ├── README.md ├── esbuild.d.ts ├── examples/ │ └── vue-cli/ │ ├── .npmrc │ ├── babel.config.js │ ├── package.json │ ├── public/ │ │ └── index.html │ ├── src/ │ │ ├── App.vue │ │ ├── components/ │ │ │ └── HelloWorld.vue │ │ ├── main.ts │ │ └── shims-vue.d.ts │ ├── tsconfig.json │ └── vue.config.cjs ├── index.d.ts ├── jest.js ├── nuxt.d.ts ├── package.json ├── playground/ │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── Async.vue │ │ ├── Bar.vue │ │ ├── ButtonTest.vue │ │ ├── Foo.vue │ │ ├── HelloWorld.vue │ │ ├── main.ts │ │ └── shims-vue.d.ts │ ├── tsconfig.json │ └── vite.config.ts ├── pnpm-workspace.yaml ├── ref-macros.d.ts ├── rollup.config.mjs ├── rollup.d.ts ├── scripts/ │ └── postbuild.ts ├── shims.d.ts ├── src/ │ ├── core/ │ │ ├── babel.ts │ │ ├── identifiers.ts │ │ ├── index.ts │ │ ├── macros.ts │ │ ├── options.ts │ │ ├── parseSFC.ts │ │ ├── transform.ts │ │ ├── transformScriptSetup.ts │ │ ├── transformSfcRefSugar.ts │ │ └── utils.ts │ ├── esbuild.ts │ ├── index.ts │ ├── lib.ts │ ├── nuxt.ts │ ├── rollup.ts │ ├── types.ts │ ├── vite.ts │ └── webpack.ts ├── test/ │ ├── __snapshots__/ │ │ └── transform.test.ts.snap │ ├── errors.test.ts │ ├── fixtures/ │ │ ├── AsyncImport.vue │ │ ├── ComponentsDirectives.vue │ │ ├── ComponentsDirectivesLocal.vue │ │ ├── ComponentsLocal.vue │ │ ├── DynamicStyle.vue │ │ ├── Empty.vue │ │ ├── Enum.vue │ │ ├── HtmlTag.vue │ │ ├── HtmlTag2.vue │ │ ├── JSLongComment.vue │ │ ├── Macros.vue │ │ ├── MacrosDefineExpose.vue │ │ ├── MacrosPure.vue │ │ ├── MacrosType.vue │ │ ├── MacrosType2.vue │ │ ├── MacrosType3.vue │ │ ├── MacrosType4.vue │ │ ├── MacrosType5.vue │ │ ├── MacrosType6.vue │ │ ├── MacrosTypeAny.vue │ │ ├── Object1.vue │ │ ├── ObjectDestructure.vue │ │ ├── Pug1.vue │ │ ├── RefSugar.ts │ │ ├── RefSugar.vue │ │ ├── RefSugarScriptSetup.vue │ │ ├── ScriptLessThanOrEqualTo.vue │ │ ├── ScriptOnly.vue │ │ ├── TemplateOnly.vue │ │ ├── TemplateOptionalChaining.vue │ │ ├── VFor.vue │ │ └── VariableBinding.vue │ ├── identifiers.test.ts │ ├── nativeTag.test.ts │ ├── transform.test.ts │ └── transform_filter.test.ts ├── tsconfig.json ├── types.d.ts ├── vite.d.ts └── webpack.d.ts
SYMBOL INDEX (50 symbols across 16 files)
FILE: examples/vue-cli/vue.config.cjs
method chainWebpack (line 13) | chainWebpack(config) {
FILE: jest.js
function requireVueJest (line 3) | function requireVueJest() {
method process (line 17) | async process(source, filename, ...args) {
FILE: ref-macros.d.ts
type RefValue (line 11) | type RefValue<T> = T & { [RefMarker]?: any }
type ComputedRefValue (line 14) | type ComputedRefValue<T> = T & { [ComputedRefMarker]?: any }
type WritableComputedRefValue (line 17) | type WritableComputedRefValue<T> = T & { [WritableComputedRefMarker]?: a...
type ToRawRefs (line 19) | type ToRawRefs<T extends object> = {
FILE: rollup.config.mjs
method onwarn (line 52) | onwarn({ code, message }) {
FILE: scripts/postbuild.ts
function run (line 5) | async function run() {
FILE: src/core/identifiers.ts
function getIdentifierDeclarations (line 12) | function getIdentifierDeclarations(nodes: Statement[]) {
function getIdentifierUsages (line 39) | function getIdentifierUsages(node?: Expression | TSType | SpreadElement ...
function getFileGlobals (line 110) | function getFileGlobals(result: ParseResult<File>) {
FILE: src/core/macros.ts
constant DEFINE_PROPS (line 17) | const DEFINE_PROPS = 'defineProps'
constant DEFINE_EMITS (line 18) | const DEFINE_EMITS = 'defineEmits'
constant DEFINE_EXPOSE (line 19) | const DEFINE_EXPOSE = 'defineExpose'
constant WITH_DEFAULTS (line 20) | const WITH_DEFAULTS = 'withDefaults'
constant DEFINE_SLOTS (line 21) | const DEFINE_SLOTS = 'defineSlots'
type PropTypeData (line 23) | interface PropTypeData {
function applyMacros (line 29) | function applyMacros(nodes: Statement[]) {
function isCallOf (line 319) | function isCallOf(
function extractRuntimeProps (line 333) | function extractRuntimeProps(
function inferRuntimeType (line 363) | function inferRuntimeType(
FILE: src/core/options.ts
function resolveOptions (line 3) | function resolveOptions(options: ScriptSetupTransformOptions = {}): Reso...
FILE: src/core/parseSFC.ts
constant BUILD_IN_DIRECTIVES (line 63) | const BUILD_IN_DIRECTIVES = new Set([
function getComponents (line 85) | function getComponents(node: TemplateChildNode): string[] {
function getDirectiveNames (line 111) | function getDirectiveNames(node: TemplateChildNode): string[] {
function getFreeVariablesForText (line 139) | function getFreeVariablesForText(input: string): string[] {
function getFreeVariablesForPropsNode (line 144) | function getFreeVariablesForPropsNode(
function getFreeVariablesForNode (line 155) | function getFreeVariablesForNode(
function findReferencesForSFC (line 244) | function findReferencesForSFC(
function getBabelParserOptions (line 259) | function getBabelParserOptions(lang: string | null | undefined) {
function parseSFC (line 275) | async function parseSFC(
FILE: src/core/transform.ts
function shouldTransform (line 11) | function shouldTransform(code: string, id: string, options?: ScriptSetup...
function transform (line 18) | async function transform(input: string, id: string, options?: ScriptSetu...
function transformNonVue (line 28) | function transformNonVue(input: string, id: string, options: ResolvedOpt...
function transformVue (line 39) | async function transformVue(input: string, id: string, options: Resolved...
FILE: src/core/transformScriptSetup.ts
function isAsyncImport (line 11) | function isAsyncImport(node: Statement) {
function transformScriptSetup (line 26) | function transformScriptSetup(
FILE: src/core/transformSfcRefSugar.ts
function transformSfcRefSugar (line 6) | function transformSfcRefSugar(sfc: ParsedSFC, options: ResolvedOptions) {
FILE: src/core/utils.ts
function exhaustiveCheckReturnUndefined (line 7) | function exhaustiveCheckReturnUndefined(_param: never) {
FILE: src/index.ts
method transformInclude (line 17) | transformInclude(id) {
method transform (line 20) | async transform(code, id) {
FILE: src/nuxt.ts
function scriptSetupModule (line 5) | function scriptSetupModule(this: any, inlineOptions: PluginOptions = {}) {
FILE: src/types.ts
type ScriptTagMeta (line 5) | interface ScriptTagMeta {
type ParsedSFC (line 16) | interface ParsedSFC {
type ScriptSetupTransformOptions (line 32) | interface ScriptSetupTransformOptions {
type PluginOptions (line 43) | interface PluginOptions extends ScriptSetupTransformOptions {
type ResolvedOptions (line 48) | type ResolvedOptions = Required<ScriptSetupTransformOptions>
type SourceMap (line 50) | interface SourceMap {
type TransformResult (line 61) | type TransformResult = {
Condensed preview — 103 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (124K chars).
[
{
"path": ".eslintignore",
"chars": 24,
"preview": "dist\nnode_modules\n*.vue\n"
},
{
"path": ".eslintrc.json",
"chars": 26,
"preview": "{\n \"extends\": \"@antfu\"\n}\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 16,
"preview": "github: [antfu]\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 690,
"preview": "name: Release\n\non:\n push:\n tags:\n - 'v*'\n\njobs:\n release:\n runs-on: ubuntu-latest\n permissions:\n co"
},
{
"path": ".github/workflows/test.yml",
"chars": 868,
"preview": "name: Test\n\non:\n push:\n branches:\n - main\n - master\n\n pull_request:\n branches:\n - main\n - ma"
},
{
"path": ".gitignore",
"chars": 1194,
"preview": "# Created by .ignore support plugin (hsz.mobi)\n### Node template\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-e"
},
{
"path": ".npmrc",
"chars": 64,
"preview": "ignore-workspace-root-check=true\nstrict-peer-dependencies=false\n"
},
{
"path": ".tazerc.json",
"chars": 52,
"preview": "{\n \"exclude\": [\n \"vue\",\n \"htmlparser2\"\n ]\n}\n"
},
{
"path": "LICENSE",
"chars": 1094,
"preview": "MIT License\n\nCopyright (c) 2021 Anthony Fu <https://github.com/antfu>\n\nPermission is hereby granted, free of charge, to "
},
{
"path": "README.md",
"chars": 7067,
"preview": "# unplugin-vue2-script-setup\n\n[\n</script>\n<template>\n <div class=\"hello\">\n <h1>{{ msg }}</"
},
{
"path": "examples/vue-cli/src/main.ts",
"chars": 253,
"preview": "import Vue from 'vue'\nimport VueCompositionAPI, { createApp, h } from '@vue/composition-api'\nimport App from './App.vue'"
},
{
"path": "examples/vue-cli/src/shims-vue.d.ts",
"chars": 62,
"preview": "/// <reference types=\"unplugin-vue2-script-setup/shims.js\" />\n"
},
{
"path": "examples/vue-cli/tsconfig.json",
"chars": 529,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"esnext\",\n \"module\": \"esnext\",\n \"strict\": true,\n \"jsx\": \"preserve\",\n "
},
{
"path": "examples/vue-cli/vue.config.cjs",
"chars": 728,
"preview": "const { defineConfig } = require('@vue/cli-service')\nconst ScriptSetup = require('unplugin-vue2-script-setup/webpack').d"
},
{
"path": "index.d.ts",
"chars": 39,
"preview": "export { default } from './dist/index'\n"
},
{
"path": "jest.js",
"chars": 634,
"preview": "const { transform } = require('./dist/index')\n\nfunction requireVueJest() {\n const names = ['@vue/vue2-jest', 'vue-jest'"
},
{
"path": "nuxt.d.ts",
"chars": 38,
"preview": "export { default } from './dist/nuxt'\n"
},
{
"path": "package.json",
"chars": 3434,
"preview": "{\n \"name\": \"unplugin-vue2-script-setup\",\n \"version\": \"0.11.4\",\n \"packageManager\": \"pnpm@8.6.11\",\n \"description\": \"Br"
},
{
"path": "playground/index.html",
"chars": 314,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <link rel=\"icon\" href=\"/favicon.ico\" />\n <meta name="
},
{
"path": "playground/package.json",
"chars": 339,
"preview": "{\n \"private\": true,\n \"scripts\": {\n \"dev\": \"vite --open\",\n \"build\": \"vite build\"\n },\n \"dependencies\": {\n \"@v"
},
{
"path": "playground/src/App.vue",
"chars": 478,
"preview": "<script setup lang=\"ts\">\nimport { defineAsyncComponent } from '@vue/composition-api'\nimport ButtonTest from './ButtonTes"
},
{
"path": "playground/src/Async.vue",
"chars": 52,
"preview": "<template>\n <div>Async Component</div>\n</template>\n"
},
{
"path": "playground/src/Bar.vue",
"chars": 40,
"preview": "<template>\n <div>Bar</div>\n</template>\n"
},
{
"path": "playground/src/ButtonTest.vue",
"chars": 208,
"preview": "<script setup lang=\"ts\">\nimport Button from './Foo.vue';\nimport button from './Foo.vue';\n</script>\n\n<template>\n <div>\n "
},
{
"path": "playground/src/Foo.vue",
"chars": 112,
"preview": "<script setup lang=\"ts\">\n\n</script>\n<template>\n <div>\n Button Component: <slot></slot>\n </div>\n</template>\n"
},
{
"path": "playground/src/HelloWorld.vue",
"chars": 902,
"preview": "<script lang=\"ts\">\n/* eslint-disable import/first */\nexport default {\n name: 'App',\n}\n</script>\n<script setup lang=\"ts\""
},
{
"path": "playground/src/main.ts",
"chars": 217,
"preview": "import Vue from 'vue'\nimport VueCompositionAPI, { createApp, h } from '@vue/composition-api'\nimport App from './App.vue'"
},
{
"path": "playground/src/shims-vue.d.ts",
"chars": 129,
"preview": "/// <reference types=\"unplugin-vue2-script-setup/shims.js\" />\n/// <reference types=\"unplugin-vue2-script-setup/ref-macro"
},
{
"path": "playground/tsconfig.json",
"chars": 545,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"esnext\",\n \"module\": \"esnext\",\n \"strict\": true,\n \"jsx\": \"preserve\",\n "
},
{
"path": "playground/vite.config.ts",
"chars": 344,
"preview": "import { defineConfig } from 'vite'\nimport { createVuePlugin as Vue2 } from 'vite-plugin-vue2'\nimport Inspect from 'vite"
},
{
"path": "pnpm-workspace.yaml",
"chars": 41,
"preview": "- packages\n - playground\n - examples/*\n"
},
{
"path": "ref-macros.d.ts",
"chars": 2266,
"preview": "import type {\n ComputedRef,\n Ref,\n ShallowUnwrapRef,\n UnwrapRef,\n WritableComputedOptions,\n WritableComputedRef,\n}"
},
{
"path": "rollup.config.mjs",
"chars": 1796,
"preview": "// @ts-check\nimport * as fs from 'node:fs'\nimport ts from 'rollup-plugin-esbuild'\nimport dts from 'rollup-plugin-dts'\nim"
},
{
"path": "rollup.d.ts",
"chars": 40,
"preview": "export { default } from './dist/rollup'\n"
},
{
"path": "scripts/postbuild.ts",
"chars": 711,
"preview": "import { basename, resolve } from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport fg from 'fast-glob'\n\nasync"
},
{
"path": "shims.d.ts",
"chars": 306,
"preview": "// workaround for Volar to infer the ref type in <template>\n// https://github.com/johnsoncodehk/volar/issues/404\ndeclare"
},
{
"path": "src/core/babel.ts",
"chars": 471,
"preview": "import * as babel from '@babel/core'\nimport { parse, parseExpression } from '@babel/parser'\nimport g from '@babel/genera"
},
{
"path": "src/core/identifiers.ts",
"chars": 4568,
"preview": "import type {\n Expression,\n File,\n PrivateName,\n SpreadElement,\n Statement,\n TSType,\n} from '@babel/types'\nimport "
},
{
"path": "src/core/index.ts",
"chars": 92,
"preview": "export * from './transform'\nexport * from './transformScriptSetup'\nexport * from '../types'\n"
},
{
"path": "src/core/macros.ts",
"chars": 12283,
"preview": "// modified from https://github.com/vuejs/vue-next/blob/main/packages/compiler-sfc/src/compileScript.ts\n\nimport type {\n "
},
{
"path": "src/core/options.ts",
"chars": 366,
"preview": "import type { ResolvedOptions, ScriptSetupTransformOptions } from '../types'\n\nexport function resolveOptions(options: Sc"
},
{
"path": "src/core/parseSFC.ts",
"chars": 11182,
"preview": "/* eslint-disable one-var */\n/* eslint-disable @typescript-eslint/no-namespace */\nimport { notNullish, partition } from "
},
{
"path": "src/core/transform.ts",
"chars": 2624,
"preview": "import MagicString from 'magic-string'\nimport { shouldTransform as shouldTransformRefSugar, transform as transformRef } "
},
{
"path": "src/core/transformScriptSetup.ts",
"chars": 6398,
"preview": "import { capitalize } from '@vue/shared'\nimport type { Node, ObjectExpression, Statement } from '@babel/types'\nimport { "
},
{
"path": "src/core/transformSfcRefSugar.ts",
"chars": 1005,
"preview": "import { shouldTransform, transformAST } from '@vue/reactivity-transform'\nimport MagicString from 'magic-string'\nimport "
},
{
"path": "src/core/utils.ts",
"chars": 296,
"preview": "import { camelize, capitalize } from '@vue/shared'\n\nexport const pascalize = (str: string) => capitalize(camelize(str))\n"
},
{
"path": "src/esbuild.ts",
"chars": 58,
"preview": "import unplugin from '.'\n\nexport default unplugin.esbuild\n"
},
{
"path": "src/index.ts",
"chars": 813,
"preview": "import { createUnplugin } from 'unplugin'\nimport { createFilter } from '@rollup/pluginutils'\nimport type { PluginOptions"
},
{
"path": "src/lib.ts",
"chars": 23,
"preview": "export * from './core'\n"
},
{
"path": "src/nuxt.ts",
"chars": 634,
"preview": "import defu from 'defu'\nimport type { PluginOptions } from './types'\nimport unplugin from '.'\n\nfunction scriptSetupModul"
},
{
"path": "src/rollup.ts",
"chars": 57,
"preview": "import unplugin from '.'\n\nexport default unplugin.rollup\n"
},
{
"path": "src/types.ts",
"chars": 1457,
"preview": "import type { ParserOptions } from '@babel/parser'\nimport type { Node, Program } from '@babel/types'\nimport type { Filte"
},
{
"path": "src/vite.ts",
"chars": 55,
"preview": "import unplugin from '.'\n\nexport default unplugin.vite\n"
},
{
"path": "src/webpack.ts",
"chars": 58,
"preview": "import unplugin from '.'\n\nexport default unplugin.webpack\n"
},
{
"path": "test/__snapshots__/transform.test.ts.snap",
"chars": 20890,
"preview": "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html\n\nexports[`transform > fixtures > playground/src/App.vue 1`"
},
{
"path": "test/errors.test.ts",
"chars": 1255,
"preview": "import { describe, expect, it, vi } from 'vitest'\nimport { transform as t } from '../src'\n\ndescribe('errors', () => {\n "
},
{
"path": "test/fixtures/AsyncImport.vue",
"chars": 210,
"preview": "<script setup lang=\"ts\">\nimport { defineAsyncComponent } from '@vue/composition-api'\nconst ScriptOnly = defineAsyncCompo"
},
{
"path": "test/fixtures/ComponentsDirectives.vue",
"chars": 1703,
"preview": "<template>\n <div>\n <FooView\n v-if=\"now > 0\"\n ref=\"fooView\"\n v-foo-bar=\"message0\"\n v-d0-demo:foo."
},
{
"path": "test/fixtures/ComponentsDirectivesLocal.vue",
"chars": 331,
"preview": "<script lang=\"ts\">\nimport type { ObjectDirective } from '@vue/composition-api'\n// enables v-focus in templates\nconst vFo"
},
{
"path": "test/fixtures/ComponentsLocal.vue",
"chars": 207,
"preview": "<script lang=\"tsx\">\nconst HelloComponent = ({ name }: { name: string }) => <span>Hello, {name}</span>\n</script>\n<script "
},
{
"path": "test/fixtures/DynamicStyle.vue",
"chars": 139,
"preview": "<template>\n <div :style=\"{ color, border: '1px' }\" />\n</template>\n\n<script setup lang=\"ts\">\nconst color = computed(() ="
},
{
"path": "test/fixtures/Empty.vue",
"chars": 0,
"preview": ""
},
{
"path": "test/fixtures/Enum.vue",
"chars": 118,
"preview": "<template>\n <div>{{ MyEnum }}</div>\n</template>\n\n<script setup lang=\"ts\">\nenum MyEnum {\n test = 'true',\n}\n</script>\n"
},
{
"path": "test/fixtures/HtmlTag.vue",
"chars": 316,
"preview": "<script setup lang=\"ts\">\nimport Enum from './Enum.vue'\nimport { ref } from '@vue/composition-api';\n\nlet p = \"hello word\""
},
{
"path": "test/fixtures/HtmlTag2.vue",
"chars": 479,
"preview": "<script setup lang=\"ts\">\nimport Button from './DynamicStyle.vue';\nimport button from './DynamicStyle.vue';\nimport { defi"
},
{
"path": "test/fixtures/JSLongComment.vue",
"chars": 108,
"preview": "<script setup lang=\"ts\">\nconst a = 1\n////////////////\n</script>\n<template>\n <div>{{ a }}</div>\n</template>\n"
},
{
"path": "test/fixtures/Macros.vue",
"chars": 198,
"preview": "<template>\n <div @click=\"emit(props.msg)\">{{ msg }}</div>\n</template>\n\n<script setup lang=\"js\">\nconst props = definePro"
},
{
"path": "test/fixtures/MacrosDefineExpose.vue",
"chars": 275,
"preview": "<template>\n <div>{{ a }}</div>\n <div>{{ text?.length ?? textLengthDefault }}</div>\n</template>\n\n<script setup lang=\"ts"
},
{
"path": "test/fixtures/MacrosPure.vue",
"chars": 118,
"preview": "<template>\n <div>{{ msg }}</div>\n</template>\n\n<script setup>\ndefineEmits()\ndefineProps({\n msg: String,\n})\n</script>\n"
},
{
"path": "test/fixtures/MacrosType.vue",
"chars": 298,
"preview": "<template>\n <div @click=\"emit('update', props.msg)\">\n {{ msg }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nconst"
},
{
"path": "test/fixtures/MacrosType2.vue",
"chars": 403,
"preview": "<template>\n <div @click=\"emit(props.msg)\">\n {{ msg }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nexport interfac"
},
{
"path": "test/fixtures/MacrosType3.vue",
"chars": 327,
"preview": "<template>\n <div @click=\"emit('update', msg);emit('update', msg + value);\">\n {{ msg }}\n </div>\n</template>\n\n<script"
},
{
"path": "test/fixtures/MacrosType4.vue",
"chars": 127,
"preview": "<script setup lang=\"ts\">\ntype Test = number[]\nwithDefaults(\n defineProps<{ test: Test }>(),\n { test: () => [] },\n)\n</s"
},
{
"path": "test/fixtures/MacrosType5.vue",
"chars": 81,
"preview": "<script setup lang=\"ts\">\ndefineSlots<{\n default: { msg: string }\n}>()\n</script>\n"
},
{
"path": "test/fixtures/MacrosType6.vue",
"chars": 248,
"preview": "<script setup lang=\"ts\">\nconst slots = defineSlots<{\n default: { msg: string }\n}>()\n</script>\n<template>\n <template v-"
},
{
"path": "test/fixtures/MacrosTypeAny.vue",
"chars": 136,
"preview": "<template>\n <div>\n {{ value }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nconst props = defineProps<{ value: any"
},
{
"path": "test/fixtures/Object1.vue",
"chars": 146,
"preview": "<template>\n <div>\n {{ data }} {{ title }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\nconst data = {\n title: 'Te"
},
{
"path": "test/fixtures/ObjectDestructure.vue",
"chars": 175,
"preview": "<template>\n <div></div>\n</template>\n\n<script setup>\nimport { toRefs, reactive } from 'vue'\nconst state = reactive({ dat"
},
{
"path": "test/fixtures/Pug1.vue",
"chars": 189,
"preview": "<template lang=\"pug\">\n.root\n span {{ data }} {{ title }}\n .p-1(@click=\"foo\")\n</template>\n\n<script setup lang=\"ts\">\ncon"
},
{
"path": "test/fixtures/RefSugar.ts",
"chars": 340,
"preview": "/* eslint-disable unused-imports/no-unused-vars */\n/* eslint-disable prefer-const */\nimport { ref } from '@vue/compositi"
},
{
"path": "test/fixtures/RefSugar.vue",
"chars": 438,
"preview": "<template>\n <div>\n {{ msg }}\n {{ msg2 }}\n {{ a }}\n </div>\n</template>\n\n<script lang=\"ts\">\n/* eslint-disable p"
},
{
"path": "test/fixtures/RefSugarScriptSetup.vue",
"chars": 155,
"preview": "<template>\n <div>\n {{ msg }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\n/* eslint-disable prefer-const */\n\nlet m"
},
{
"path": "test/fixtures/ScriptLessThanOrEqualTo.vue",
"chars": 110,
"preview": "<script setup lang=\"ts\">\nconst val: boolean = 1 <= 2\n</script>\n\n<template>\n <div>{{ val }}</div>\n</template>\n"
},
{
"path": "test/fixtures/ScriptOnly.vue",
"chars": 211,
"preview": "<script lang=\"ts\">\n// <script setup> in comment should not be matched\nimport { defineComponent } from '@vue/composition-"
},
{
"path": "test/fixtures/TemplateOnly.vue",
"chars": 316,
"preview": "<template>\n <div>\n <div v-for=\"(item, index) in items\" :key=\"item\">\n {{ index }}: {{ item }}\n </div>\n </div"
},
{
"path": "test/fixtures/TemplateOptionalChaining.vue",
"chars": 487,
"preview": "<template>\n <div @click=\"callback?.()\">\n {{ text?.length ?? textLengthDefault?.[index] }}\n {{ classes?.[0] }}\n "
},
{
"path": "test/fixtures/VFor.vue",
"chars": 465,
"preview": "<template>\n <div>\n <div v-for=\"(item, index) in items\" :key=\"item\">\n {{ item }}\n </div>\n <div v-for=\"{ ca"
},
{
"path": "test/fixtures/VariableBinding.vue",
"chars": 421,
"preview": "<script setup lang=\"ts\">\n/* eslint-disable no-console */\nimport { reactive } from '@vue/composition-api'\nconst state = r"
},
{
"path": "test/identifiers.test.ts",
"chars": 2763,
"preview": "/* eslint-disable no-template-curly-in-string */\nimport { parse } from '@babel/parser'\nimport { describe, expect, it } f"
},
{
"path": "test/nativeTag.test.ts",
"chars": 3081,
"preview": "import { describe, expect, it } from 'vitest'\nimport { transform } from '../src'\n\ndescribe('filter native tags as vue co"
},
{
"path": "test/transform.test.ts",
"chars": 914,
"preview": "import { resolve } from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport fg from 'fast-glob'\nimport { describ"
},
{
"path": "test/transform_filter.test.ts",
"chars": 1887,
"preview": "import { } from 'node:test'\nimport { describe, expect, it } from 'vitest'\nimport { scriptSetupRE } from '../src/core'\n\nd"
},
{
"path": "tsconfig.json",
"chars": 344,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2017\",\n \"module\": \"esnext\",\n \"lib\": [\"esnext\", \"dom\"],\n \"moduleResolu"
},
{
"path": "types.d.ts",
"chars": 39,
"preview": "export { default } from './dist/types'\n"
},
{
"path": "vite.d.ts",
"chars": 38,
"preview": "export { default } from './dist/vite'\n"
},
{
"path": "webpack.d.ts",
"chars": 41,
"preview": "export { default } from './dist/webpack'\n"
}
]
About this extraction
This page contains the full source code of the antfu/unplugin-vue2-script-setup GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 103 files (109.5 KB), approximately 32.9k tokens, and a symbol index with 50 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.