Skip to content

Commit 73f3a6b

Browse files
committed
feat(component-api-style): add scriptSetupVapor
1 parent 72b186c commit 73f3a6b

File tree

3 files changed

+209
-11
lines changed

3 files changed

+209
-11
lines changed

docs/rules/component-api-style.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ since: v7.18.0
1414

1515
This rule aims to make the API style you use to define Vue components consistent in your project.
1616

17-
For example, if you want to allow only `<script setup>` and Composition API.
17+
For example, if you want to allow only `<script setup>` and Composition API.
1818
(This is the default for this rule.)
1919

2020
<eslint-code-block :rules="{'vue/component-api-style': ['error']}">
@@ -74,17 +74,44 @@ export default {
7474
```json
7575
{
7676
"vue/component-api-style": ["error",
77-
["script-setup", "composition"] // "script-setup", "composition", "composition-vue2", or "options"
77+
["script-setup", "composition"] // "script-setup", "script-setup-vapor", "composition", "composition-vue2", or "options"
7878
]
7979
}
8080
```
8181

8282
- Array options ... Defines the API styles you want to allow. Default is `["script-setup", "composition"]`. You can use the following values.
8383
- `"script-setup"` ... If set, allows [`<script setup>`](https://vuejs.org/api/sfc-script-setup.html).
84+
- `"script-setup-vapor"` ... If set, allows [`<script setup vapor>`](https://vuejs.org/api/sfc-script-setup.html) (Vue 3.6+).
8485
- `"composition"` ... If set, allows [Composition API](https://vuejs.org/api/#composition-api) (not `<script setup>`).
8586
- `"composition-vue2"` ... If set, allows [Composition API for Vue 2](https://github.com/vuejs/composition-api) (not `<script setup>`). In particular, it allows `render`, `renderTracked` and `renderTriggered` alongside `setup`.
8687
- `"options"` ... If set, allows Options API.
8788

89+
### `["script-setup-vapor"]`
90+
91+
<eslint-code-block :rules="{'vue/component-api-style': ['error', ['script-setup-vapor']]}">
92+
93+
```vue
94+
<!-- ✓ GOOD -->
95+
<script setup vapor>
96+
import { ref } from 'vue'
97+
const msg = ref('Hello World!')
98+
</script>
99+
```
100+
101+
</eslint-code-block>
102+
103+
<eslint-code-block :rules="{'vue/component-api-style': ['error', ['script-setup-vapor']]}">
104+
105+
```vue
106+
<!-- ✗ BAD -->
107+
<script setup>
108+
import { ref } from 'vue'
109+
const msg = ref('Hello World!')
110+
</script>
111+
```
112+
113+
</eslint-code-block>
114+
88115
### `["options"]`
89116

90117
<eslint-code-block :rules="{'vue/component-api-style': ['error', ['options']]}">

lib/rules/component-api-style.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
const utils = require('../utils')
88

99
/**
10-
* @typedef { 'script-setup' | 'composition' | 'composition-vue2' | 'options' } PreferOption
10+
* @typedef { 'script-setup' | 'script-setup-vapor' | 'composition' | 'composition-vue2' | 'options' } PreferOption
1111
*
1212
* @typedef {PreferOption[]} UserPreferOption
1313
*
1414
* @typedef {object} NormalizeOptions
1515
* @property {object} allowsSFC
1616
* @property {boolean} [allowsSFC.scriptSetup]
17+
* @property {boolean} [allowsSFC.scriptSetupVapor]
1718
* @property {boolean} [allowsSFC.composition]
1819
* @property {boolean} [allowsSFC.compositionVue2]
1920
* @property {boolean} [allowsSFC.options]
@@ -26,6 +27,7 @@ const utils = require('../utils')
2627
/** @type {PreferOption[]} */
2728
const STYLE_OPTIONS = [
2829
'script-setup',
30+
'script-setup-vapor',
2931
'composition',
3032
'composition-vue2',
3133
'options'
@@ -48,6 +50,10 @@ function parseOptions(options) {
4850
opts.allowsSFC.scriptSetup = true
4951
break
5052
}
53+
case 'script-setup-vapor': {
54+
opts.allowsSFC.scriptSetupVapor = true
55+
break
56+
}
5157
case 'composition': {
5258
opts.allowsSFC.composition = true
5359
opts.allowsOther.composition = true
@@ -143,6 +149,7 @@ const LIFECYCLE_HOOK_OPTIONS = new Set([
143149
/**
144150
* @param {object} allowsOpt
145151
* @param {boolean} [allowsOpt.scriptSetup]
152+
* @param {boolean} [allowsOpt.scriptSetupVapor]
146153
* @param {boolean} [allowsOpt.composition]
147154
* @param {boolean} [allowsOpt.compositionVue2]
148155
* @param {boolean} [allowsOpt.options]
@@ -152,6 +159,9 @@ function buildAllowedPhrase(allowsOpt) {
152159
if (allowsOpt.scriptSetup) {
153160
phrases.push('`<script setup>`')
154161
}
162+
if (allowsOpt.scriptSetupVapor) {
163+
phrases.push('`<script setup vapor>`')
164+
}
155165
if (allowsOpt.composition) {
156166
phrases.push('Composition API')
157167
}
@@ -169,13 +179,36 @@ function buildAllowedPhrase(allowsOpt) {
169179
/**
170180
* @param {object} allowsOpt
171181
* @param {boolean} [allowsOpt.scriptSetup]
182+
* @param {boolean} [allowsOpt.scriptSetupVapor]
172183
* @param {boolean} [allowsOpt.composition]
173184
* @param {boolean} [allowsOpt.compositionVue2]
174185
* @param {boolean} [allowsOpt.options]
175186
*/
176187
function isPreferScriptSetup(allowsOpt) {
177188
if (
178189
!allowsOpt.scriptSetup ||
190+
allowsOpt.scriptSetupVapor ||
191+
allowsOpt.composition ||
192+
allowsOpt.compositionVue2 ||
193+
allowsOpt.options
194+
) {
195+
return false
196+
}
197+
return true
198+
}
199+
200+
/**
201+
* @param {object} allowsOpt
202+
* @param {boolean} [allowsOpt.scriptSetup]
203+
* @param {boolean} [allowsOpt.scriptSetupVapor]
204+
* @param {boolean} [allowsOpt.composition]
205+
* @param {boolean} [allowsOpt.compositionVue2]
206+
* @param {boolean} [allowsOpt.options]
207+
*/
208+
function isPreferScriptSetupVapor(allowsOpt) {
209+
if (
210+
!allowsOpt.scriptSetupVapor ||
211+
allowsOpt.scriptSetup ||
179212
allowsOpt.composition ||
180213
allowsOpt.compositionVue2 ||
181214
allowsOpt.options
@@ -219,10 +252,14 @@ module.exports = {
219252
messages: {
220253
disallowScriptSetup:
221254
'`<script setup>` is not allowed in your project. Use {{allowedApis}} instead.',
255+
disallowScriptSetupVapor:
256+
'`<script setup vapor>` is not allowed in your project. Use {{allowedApis}} instead.',
222257
disallowComponentOption:
223258
'{{disallowedApi}} is not allowed in your project. {{optionPhrase}} is part of the {{disallowedApi}}. Use {{allowedApis}} instead.',
224259
disallowComponentOptionPreferScriptSetup:
225-
'{{disallowedApi}} is not allowed in your project. Use `<script setup>` instead.'
260+
'{{disallowedApi}} is not allowed in your project. Use `<script setup>` instead.',
261+
disallowComponentOptionPreferScriptSetupVapor:
262+
'{{disallowedApi}} is not allowed in your project. Use `<script setup vapor>` instead.'
226263
}
227264
},
228265
/** @param {RuleContext} context */
@@ -232,13 +269,35 @@ module.exports = {
232269
return utils.compositingVisitors(
233270
{
234271
Program() {
235-
if (options.allowsSFC.scriptSetup) {
272+
const { scriptSetup, scriptSetupVapor } = options.allowsSFC
273+
if (scriptSetup && scriptSetupVapor) {
274+
return
275+
}
276+
const scriptSetupElement = utils.getScriptSetupElement(context)
277+
if (!scriptSetupElement) {
278+
return
279+
}
280+
281+
const hasVapor = utils.hasAttribute(scriptSetupElement, 'vapor')
282+
283+
// <script setup vapor>
284+
if (hasVapor) {
285+
if (!scriptSetupVapor) {
286+
context.report({
287+
node: scriptSetupElement.startTag,
288+
messageId: 'disallowScriptSetupVapor',
289+
data: {
290+
allowedApis: buildAllowedPhrase(options.allowsSFC)
291+
}
292+
})
293+
}
236294
return
237295
}
238-
const scriptSetup = utils.getScriptSetupElement(context)
239-
if (scriptSetup) {
296+
297+
// <script setup>
298+
if (!scriptSetup) {
240299
context.report({
241-
node: scriptSetup.startTag,
300+
node: scriptSetupElement.startTag,
242301
messageId: 'disallowScriptSetup',
243302
data: {
244303
allowedApis: buildAllowedPhrase(options.allowsSFC)
@@ -290,9 +349,11 @@ module.exports = {
290349
if (disallowApi) {
291350
context.report({
292351
node: prop.key,
293-
messageId: isPreferScriptSetup(allows)
294-
? 'disallowComponentOptionPreferScriptSetup'
295-
: 'disallowComponentOption',
352+
messageId: isPreferScriptSetupVapor(allows)
353+
? 'disallowComponentOptionPreferScriptSetupVapor'
354+
: isPreferScriptSetup(allows)
355+
? 'disallowComponentOptionPreferScriptSetup'
356+
: 'disallowComponentOption',
296357
data: {
297358
disallowedApi: disallowApi.apiName,
298359
optionPhrase: buildOptionPhrase(name),

tests/lib/rules/component-api-style.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ tester.run('component-api-style', rule, {
103103
`,
104104
options: [['script-setup']]
105105
},
106+
{
107+
filename: 'test.vue',
108+
code: `
109+
<script setup vapor>
110+
import { ref } from 'vue'
111+
const msg = ref('Hello World!')
112+
</script>
113+
`,
114+
options: [['script-setup-vapor']]
115+
},
116+
{
117+
filename: 'test.vue',
118+
code: `
119+
<script setup vapor>
120+
import { ref } from 'vue'
121+
const msg = ref('Hello World!')
122+
</script>
123+
`,
124+
options: [['script-setup', 'script-setup-vapor']]
125+
},
106126
{
107127
filename: 'test.js',
108128
code: `
@@ -845,6 +865,96 @@ tester.run('component-api-style', rule, {
845865
column: 9
846866
}
847867
]
868+
},
869+
{
870+
filename: 'test.vue',
871+
code: `
872+
<script setup>
873+
import { ref } from 'vue'
874+
const msg = ref('Hello World!')
875+
</script>
876+
`,
877+
options: [['script-setup-vapor']],
878+
errors: [
879+
{
880+
message:
881+
'`<script setup>` is not allowed in your project. Use `<script setup vapor>` instead.',
882+
line: 2,
883+
column: 7,
884+
endLine: 2,
885+
endColumn: 21
886+
}
887+
]
888+
},
889+
{
890+
filename: 'test.vue',
891+
code: `
892+
<script setup vapor>
893+
import { ref } from 'vue'
894+
const msg = ref('Hello World!')
895+
</script>
896+
`,
897+
options: [['script-setup']],
898+
errors: [
899+
{
900+
message:
901+
'`<script setup vapor>` is not allowed in your project. Use `<script setup>` instead.',
902+
line: 2,
903+
column: 7,
904+
endLine: 2,
905+
endColumn: 27
906+
}
907+
]
908+
},
909+
{
910+
filename: 'test.vue',
911+
code: `
912+
<script>
913+
export default {
914+
data () {
915+
return {
916+
msg: 'Hello World!'
917+
}
918+
}
919+
}
920+
</script>
921+
`,
922+
options: [['script-setup-vapor']],
923+
errors: [
924+
{
925+
message:
926+
'Options API is not allowed in your project. Use `<script setup vapor>` instead.',
927+
line: 4,
928+
column: 9,
929+
endLine: 4,
930+
endColumn: 13
931+
}
932+
]
933+
},
934+
{
935+
filename: 'test.vue',
936+
code: `
937+
<script>
938+
export default {
939+
data () {
940+
return {
941+
msg: 'Hello World!'
942+
}
943+
}
944+
}
945+
</script>
946+
`,
947+
options: [['script-setup-vapor', 'script-setup']],
948+
errors: [
949+
{
950+
message:
951+
'Options API is not allowed in your project. `data` option is part of the Options API. Use `<script setup>` or `<script setup vapor>` instead.',
952+
line: 4,
953+
column: 9,
954+
endLine: 4,
955+
endColumn: 13
956+
}
957+
]
848958
}
849959
]
850960
})

0 commit comments

Comments
 (0)