eslint-plugin-vue-setup-order 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/index.cjs +382 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +359 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 David KulhΓ‘nek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# eslint-plugin-vue-setup-order
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/eslint-plugin-vue-setup-order)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
ESLint plugin to enforce consistent order of statements in Vue 3 `<script setup>`.
|
|
7
|
+
|
|
8
|
+
## π¦ Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -D eslint-plugin-vue-setup-order
|
|
12
|
+
# or
|
|
13
|
+
pnpm add -D eslint-plugin-vue-setup-order
|
|
14
|
+
# or
|
|
15
|
+
yarn add -D eslint-plugin-vue-setup-order
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## π Usage
|
|
19
|
+
|
|
20
|
+
### ESLint Flat Config (ESLint 9+)
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
// eslint.config.js
|
|
24
|
+
import vueSetupOrder from 'eslint-plugin-vue-setup-order';
|
|
25
|
+
|
|
26
|
+
export default [
|
|
27
|
+
vueSetupOrder.configs.flat.recommended,
|
|
28
|
+
// or manually:
|
|
29
|
+
{
|
|
30
|
+
plugins: {
|
|
31
|
+
'vue-setup-order': vueSetupOrder,
|
|
32
|
+
},
|
|
33
|
+
rules: {
|
|
34
|
+
'vue-setup-order/order': 'error',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Legacy Config (.eslintrc)
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
// .eslintrc.js
|
|
44
|
+
module.exports = {
|
|
45
|
+
plugins: ['vue-setup-order'],
|
|
46
|
+
extends: ['plugin:vue-setup-order/recommended'],
|
|
47
|
+
// or manually:
|
|
48
|
+
rules: {
|
|
49
|
+
'vue-setup-order/order': 'error',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## π Expected Order
|
|
55
|
+
|
|
56
|
+
The plugin enforces the following order in `<script setup>`:
|
|
57
|
+
|
|
58
|
+
1. Imports - import statements
|
|
59
|
+
2. Type declarations - type, interface, enum (TypeScript)
|
|
60
|
+
3. Define macros - defineProps, defineEmits, defineOptions, defineSlots, defineExpose, defineModel
|
|
61
|
+
4. Composables - useXxx() calls (useRouter, useStore, custom composables)
|
|
62
|
+
5. Reactive state - ref, reactive, shallowRef, etc.
|
|
63
|
+
6. Computed - computed properties
|
|
64
|
+
7. Watchers - watch, watchEffect
|
|
65
|
+
8. Lifecycle hooks - onMounted, onBeforeMount, etc.
|
|
66
|
+
9. Functions - function declarations and arrow functions
|
|
67
|
+
10. Provide - provide calls
|
|
68
|
+
|
|
69
|
+
## β
Examples
|
|
70
|
+
|
|
71
|
+
### β Incorrect
|
|
72
|
+
|
|
73
|
+
```vue
|
|
74
|
+
<script setup>
|
|
75
|
+
import { ref, computed } from 'vue';
|
|
76
|
+
|
|
77
|
+
function increment() {
|
|
78
|
+
// β function before ref
|
|
79
|
+
count.value++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const count = ref(0);
|
|
83
|
+
|
|
84
|
+
onMounted(() => {}); // β lifecycle before computed
|
|
85
|
+
|
|
86
|
+
const doubled = computed(() => count.value * 2);
|
|
87
|
+
</script>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### β
Correct
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<script setup lang="ts">
|
|
94
|
+
import { ref, computed, onMounted } from 'vue';
|
|
95
|
+
import { useRouter } from 'vue-router';
|
|
96
|
+
|
|
97
|
+
type Props = {
|
|
98
|
+
title: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
interface Emits {
|
|
102
|
+
(e: 'update', value: string): void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const props = defineProps<Props>();
|
|
106
|
+
const emit = defineEmits<Emits>();
|
|
107
|
+
|
|
108
|
+
const router = useRouter();
|
|
109
|
+
|
|
110
|
+
const count = ref(0);
|
|
111
|
+
|
|
112
|
+
const doubled = computed(() => count.value * 2);
|
|
113
|
+
|
|
114
|
+
onMounted(() => {
|
|
115
|
+
console.log('mounted');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
function increment() {
|
|
119
|
+
count.value++;
|
|
120
|
+
}
|
|
121
|
+
</script>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## βοΈ Options
|
|
125
|
+
|
|
126
|
+
### `groupBlankLines`
|
|
127
|
+
|
|
128
|
+
Add blank lines between different categories (default: `true`).
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
{
|
|
132
|
+
'vue-setup-order/order': ['error', {
|
|
133
|
+
groupBlankLines: true,
|
|
134
|
+
}]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `order`
|
|
139
|
+
|
|
140
|
+
Customize the order of categories. You can specify a custom array of category names to enforce your preferred order.
|
|
141
|
+
|
|
142
|
+
**Available categories:**
|
|
143
|
+
|
|
144
|
+
- `import` - Import statements
|
|
145
|
+
- `types` - TypeScript type declarations (type, interface, enum)
|
|
146
|
+
- `defineProps`, `defineEmits`, `defineOptions`, `defineSlots`, `defineExpose`, `defineModel`, `withDefaults` - Vue define macros
|
|
147
|
+
- `composable` - Composable functions (useXxx)
|
|
148
|
+
- `ref`, `reactive`, `computed`, `watch`, `watchEffect` - Vue reactivity APIs
|
|
149
|
+
- `onMounted`, `onBeforeMount`, etc. - Lifecycle hooks
|
|
150
|
+
- `function` - Function declarations
|
|
151
|
+
- `provide` - Provide calls
|
|
152
|
+
|
|
153
|
+
**Example: Custom order**
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
{
|
|
157
|
+
'vue-setup-order/order': ['error', {
|
|
158
|
+
order: [
|
|
159
|
+
'import',
|
|
160
|
+
'types',
|
|
161
|
+
'defineProps',
|
|
162
|
+
'defineEmits',
|
|
163
|
+
'composable',
|
|
164
|
+
'ref',
|
|
165
|
+
'reactive',
|
|
166
|
+
'computed',
|
|
167
|
+
'watch',
|
|
168
|
+
'lifecycle',
|
|
169
|
+
'function',
|
|
170
|
+
'provide'
|
|
171
|
+
]
|
|
172
|
+
}]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Example: Functions before reactive state**
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
{
|
|
180
|
+
'vue-setup-order/order': ['error', {
|
|
181
|
+
order: ['import', 'types', 'defineProps', 'function', 'ref', 'computed']
|
|
182
|
+
}]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## π§ Auto-fix
|
|
187
|
+
|
|
188
|
+
This rule supports auto-fix! Run ESLint with `--fix` flag:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npx eslint --fix "src/**/*.vue"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## π License
|
|
195
|
+
|
|
196
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => index_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/utils/constants.ts
|
|
28
|
+
var CATEGORY_IMPORT = 0;
|
|
29
|
+
var CATEGORY_TYPES = 1;
|
|
30
|
+
var CATEGORY_DEFINE = 2;
|
|
31
|
+
var CATEGORY_COMPOSABLE = 3;
|
|
32
|
+
var CATEGORY_REACTIVE = 4;
|
|
33
|
+
var CATEGORY_COMPUTED = 5;
|
|
34
|
+
var CATEGORY_WATCH = 6;
|
|
35
|
+
var CATEGORY_LIFECYCLE = 7;
|
|
36
|
+
var CATEGORY_FUNCTION = 8;
|
|
37
|
+
var CATEGORY_PROVIDE = 9;
|
|
38
|
+
var CATEGORY_UNKNOWN = 99;
|
|
39
|
+
var DEFAULT_ORDER = {
|
|
40
|
+
// Imports
|
|
41
|
+
import: CATEGORY_IMPORT,
|
|
42
|
+
// Types
|
|
43
|
+
types: CATEGORY_TYPES,
|
|
44
|
+
// Define macros
|
|
45
|
+
defineOptions: CATEGORY_DEFINE,
|
|
46
|
+
defineProps: CATEGORY_DEFINE,
|
|
47
|
+
defineEmits: CATEGORY_DEFINE,
|
|
48
|
+
defineSlots: CATEGORY_DEFINE,
|
|
49
|
+
defineExpose: CATEGORY_DEFINE,
|
|
50
|
+
defineModel: CATEGORY_DEFINE,
|
|
51
|
+
withDefaults: CATEGORY_DEFINE,
|
|
52
|
+
// Composables
|
|
53
|
+
composable: CATEGORY_COMPOSABLE,
|
|
54
|
+
// Reactive state
|
|
55
|
+
ref: CATEGORY_REACTIVE,
|
|
56
|
+
reactive: CATEGORY_REACTIVE,
|
|
57
|
+
shallowRef: CATEGORY_REACTIVE,
|
|
58
|
+
shallowReactive: CATEGORY_REACTIVE,
|
|
59
|
+
toRef: CATEGORY_REACTIVE,
|
|
60
|
+
toRefs: CATEGORY_REACTIVE,
|
|
61
|
+
customRef: CATEGORY_REACTIVE,
|
|
62
|
+
readonly: CATEGORY_REACTIVE,
|
|
63
|
+
shallowReadonly: CATEGORY_REACTIVE,
|
|
64
|
+
// Computed
|
|
65
|
+
computed: CATEGORY_COMPUTED,
|
|
66
|
+
// Watchers
|
|
67
|
+
watch: CATEGORY_WATCH,
|
|
68
|
+
watchEffect: CATEGORY_WATCH,
|
|
69
|
+
watchPostEffect: CATEGORY_WATCH,
|
|
70
|
+
watchSyncEffect: CATEGORY_WATCH,
|
|
71
|
+
// Lifecycle hooks
|
|
72
|
+
onBeforeMount: CATEGORY_LIFECYCLE,
|
|
73
|
+
onMounted: CATEGORY_LIFECYCLE,
|
|
74
|
+
onBeforeUpdate: CATEGORY_LIFECYCLE,
|
|
75
|
+
onUpdated: CATEGORY_LIFECYCLE,
|
|
76
|
+
onBeforeUnmount: CATEGORY_LIFECYCLE,
|
|
77
|
+
onUnmounted: CATEGORY_LIFECYCLE,
|
|
78
|
+
onActivated: CATEGORY_LIFECYCLE,
|
|
79
|
+
onDeactivated: CATEGORY_LIFECYCLE,
|
|
80
|
+
onErrorCaptured: CATEGORY_LIFECYCLE,
|
|
81
|
+
onRenderTracked: CATEGORY_LIFECYCLE,
|
|
82
|
+
onRenderTriggered: CATEGORY_LIFECYCLE,
|
|
83
|
+
onServerPrefetch: CATEGORY_LIFECYCLE,
|
|
84
|
+
// Functions
|
|
85
|
+
function: CATEGORY_FUNCTION,
|
|
86
|
+
// Provide
|
|
87
|
+
provide: CATEGORY_PROVIDE,
|
|
88
|
+
// Unknown
|
|
89
|
+
unknown: CATEGORY_UNKNOWN
|
|
90
|
+
};
|
|
91
|
+
var ORDER_NAMES = {
|
|
92
|
+
[CATEGORY_IMPORT]: "imports",
|
|
93
|
+
[CATEGORY_TYPES]: "type declarations",
|
|
94
|
+
[CATEGORY_DEFINE]: "define macros",
|
|
95
|
+
[CATEGORY_COMPOSABLE]: "composables",
|
|
96
|
+
[CATEGORY_REACTIVE]: "reactive state",
|
|
97
|
+
[CATEGORY_COMPUTED]: "computed properties",
|
|
98
|
+
[CATEGORY_WATCH]: "watchers",
|
|
99
|
+
[CATEGORY_LIFECYCLE]: "lifecycle hooks",
|
|
100
|
+
[CATEGORY_FUNCTION]: "functions",
|
|
101
|
+
[CATEGORY_PROVIDE]: "provide",
|
|
102
|
+
[CATEGORY_UNKNOWN]: "other"
|
|
103
|
+
};
|
|
104
|
+
var COMPOSABLE_PATTERN = /^use[A-Z]/;
|
|
105
|
+
|
|
106
|
+
// src/utils/analyze.ts
|
|
107
|
+
function getCalleeName(node, sourceCode) {
|
|
108
|
+
if (!node) return null;
|
|
109
|
+
if (node.type === "Identifier") {
|
|
110
|
+
return node.name;
|
|
111
|
+
}
|
|
112
|
+
if (node.type === "MemberExpression") {
|
|
113
|
+
return sourceCode.getText(node);
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
function isComposable(name) {
|
|
118
|
+
return name !== null && COMPOSABLE_PATTERN.test(name);
|
|
119
|
+
}
|
|
120
|
+
function analyzeStatement(statement, sourceCode, orderConfig) {
|
|
121
|
+
const result = {
|
|
122
|
+
node: statement,
|
|
123
|
+
order: orderConfig.unknown ?? CATEGORY_UNKNOWN,
|
|
124
|
+
name: "unknown",
|
|
125
|
+
category: "unknown"
|
|
126
|
+
};
|
|
127
|
+
if (statement.type === "ImportDeclaration") {
|
|
128
|
+
result.order = orderConfig.import;
|
|
129
|
+
result.name = "import";
|
|
130
|
+
result.category = "import";
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
const statementType = statement.type;
|
|
134
|
+
if (statementType === "TSTypeAliasDeclaration" || statementType === "TSInterfaceDeclaration" || statementType === "TSEnumDeclaration") {
|
|
135
|
+
result.order = orderConfig.types ?? CATEGORY_TYPES;
|
|
136
|
+
result.name = statementType === "TSTypeAliasDeclaration" ? "type" : statementType === "TSInterfaceDeclaration" ? "interface" : "enum";
|
|
137
|
+
result.category = "types";
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
if (statement.type === "VariableDeclaration") {
|
|
144
|
+
const declaration = statement.declarations[0];
|
|
145
|
+
const init = declaration?.init;
|
|
146
|
+
if (!init) {
|
|
147
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
148
|
+
result.name = "variable";
|
|
149
|
+
result.category = "reactive";
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
if (init.type === "CallExpression") {
|
|
153
|
+
const calleeName = getCalleeName(init.callee, sourceCode);
|
|
154
|
+
if (calleeName) {
|
|
155
|
+
if (isComposable(calleeName)) {
|
|
156
|
+
result.order = orderConfig.composable;
|
|
157
|
+
result.name = calleeName;
|
|
158
|
+
result.category = "composable";
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
if (orderConfig[calleeName] !== void 0) {
|
|
162
|
+
result.order = orderConfig[calleeName];
|
|
163
|
+
result.name = calleeName;
|
|
164
|
+
result.category = calleeName;
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
169
|
+
result.name = calleeName || "variable";
|
|
170
|
+
result.category = "reactive";
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") {
|
|
174
|
+
result.order = orderConfig.function ?? CATEGORY_FUNCTION;
|
|
175
|
+
result.name = declaration.id?.type === "Identifier" ? declaration.id.name : "arrow function";
|
|
176
|
+
result.category = "function";
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
180
|
+
result.name = "variable";
|
|
181
|
+
result.category = "reactive";
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
if (statement.type === "FunctionDeclaration") {
|
|
185
|
+
result.order = orderConfig.function ?? CATEGORY_FUNCTION;
|
|
186
|
+
result.name = statement.id?.name || "function";
|
|
187
|
+
result.category = "function";
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
if (statement.type === "ExpressionStatement") {
|
|
191
|
+
const expr = statement.expression;
|
|
192
|
+
if (expr.type === "CallExpression") {
|
|
193
|
+
const calleeName = getCalleeName(expr.callee, sourceCode);
|
|
194
|
+
if (calleeName && orderConfig[calleeName] !== void 0) {
|
|
195
|
+
result.order = orderConfig[calleeName];
|
|
196
|
+
result.name = calleeName;
|
|
197
|
+
result.category = calleeName;
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;
|
|
201
|
+
result.name = calleeName || "call";
|
|
202
|
+
result.category = "unknown";
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;
|
|
206
|
+
result.name = "expression";
|
|
207
|
+
result.category = "unknown";
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/utils/source-code.ts
|
|
214
|
+
function getNodeTextWithComments(node, sourceCode) {
|
|
215
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
216
|
+
let start = node.range[0];
|
|
217
|
+
if (comments.length > 0) {
|
|
218
|
+
start = comments[0].range[0];
|
|
219
|
+
}
|
|
220
|
+
return sourceCode.text.slice(start, node.range[1]);
|
|
221
|
+
}
|
|
222
|
+
function getFullRange(node, sourceCode) {
|
|
223
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
224
|
+
let start = node.range[0];
|
|
225
|
+
if (comments.length > 0) {
|
|
226
|
+
start = comments[0].range[0];
|
|
227
|
+
}
|
|
228
|
+
let end = node.range[1];
|
|
229
|
+
const textAfter = sourceCode.text.slice(end, end + 2);
|
|
230
|
+
if (textAfter.startsWith("\r\n")) {
|
|
231
|
+
end += 2;
|
|
232
|
+
} else if (textAfter.startsWith("\n")) {
|
|
233
|
+
end += 1;
|
|
234
|
+
}
|
|
235
|
+
return [start, end];
|
|
236
|
+
}
|
|
237
|
+
function generateSortedCode(statements, sourceCode, addBlankLines = true) {
|
|
238
|
+
const parts = [];
|
|
239
|
+
let lastOrder = -1;
|
|
240
|
+
for (const statement of statements) {
|
|
241
|
+
const text = getNodeTextWithComments(statement.node, sourceCode).trim();
|
|
242
|
+
if (addBlankLines && lastOrder !== -1 && statement.order !== lastOrder) {
|
|
243
|
+
parts.push("");
|
|
244
|
+
}
|
|
245
|
+
parts.push(text);
|
|
246
|
+
lastOrder = statement.order;
|
|
247
|
+
}
|
|
248
|
+
return parts.join("\n");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/rules/order.ts
|
|
252
|
+
var rule = {
|
|
253
|
+
meta: {
|
|
254
|
+
type: "suggestion",
|
|
255
|
+
docs: {
|
|
256
|
+
description: "Enforce consistent order of statements in Vue 3 <script setup>",
|
|
257
|
+
category: "Stylistic Issues",
|
|
258
|
+
recommended: true,
|
|
259
|
+
url: "https://github.com/d2a8k3u/eslint-plugin-vue-setup-order#readme"
|
|
260
|
+
},
|
|
261
|
+
fixable: "code",
|
|
262
|
+
schema: [
|
|
263
|
+
{
|
|
264
|
+
type: "object",
|
|
265
|
+
properties: {
|
|
266
|
+
order: {
|
|
267
|
+
type: "array",
|
|
268
|
+
items: { type: "string" },
|
|
269
|
+
description: "Custom order of categories"
|
|
270
|
+
},
|
|
271
|
+
groupBlankLines: {
|
|
272
|
+
type: "boolean",
|
|
273
|
+
default: true,
|
|
274
|
+
description: "Add blank lines between different categories"
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
additionalProperties: false
|
|
278
|
+
}
|
|
279
|
+
],
|
|
280
|
+
messages: {
|
|
281
|
+
wrongOrder: "'{{current}}' ({{currentCategory}}) should come before '{{previous}}' ({{previousCategory}})"
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
create(context) {
|
|
285
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
286
|
+
const options = context.options[0] || {};
|
|
287
|
+
const groupBlankLines = false !== options.groupBlankLines;
|
|
288
|
+
const orderConfig = { ...DEFAULT_ORDER };
|
|
289
|
+
if (options.order && Array.isArray(options.order)) {
|
|
290
|
+
options.order.forEach((category, index) => {
|
|
291
|
+
const originalOrder = DEFAULT_ORDER[category];
|
|
292
|
+
if (originalOrder !== void 0) {
|
|
293
|
+
Object.keys(DEFAULT_ORDER).forEach((key) => {
|
|
294
|
+
if (DEFAULT_ORDER[key] === originalOrder) {
|
|
295
|
+
orderConfig[key] = index;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
orderConfig[category] = index;
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
Program(node) {
|
|
304
|
+
const statements = [];
|
|
305
|
+
for (const statement of node.body) {
|
|
306
|
+
const analyzed = analyzeStatement(statement, sourceCode, orderConfig);
|
|
307
|
+
if (analyzed) {
|
|
308
|
+
statements.push(analyzed);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (statements.length < 2) return;
|
|
312
|
+
let firstError = null;
|
|
313
|
+
for (let i = 1; i < statements.length; i++) {
|
|
314
|
+
const prev = statements[i - 1];
|
|
315
|
+
const curr = statements[i];
|
|
316
|
+
if (curr.order < prev.order) {
|
|
317
|
+
firstError = { index: i, current: curr, previous: prev };
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (!firstError) return;
|
|
322
|
+
context.report({
|
|
323
|
+
node: firstError.current.node,
|
|
324
|
+
messageId: "wrongOrder",
|
|
325
|
+
data: {
|
|
326
|
+
current: firstError.current.name,
|
|
327
|
+
currentCategory: ORDER_NAMES[firstError.current.order] || "unknown",
|
|
328
|
+
previous: firstError.previous.name,
|
|
329
|
+
previousCategory: ORDER_NAMES[firstError.previous.order] || "unknown"
|
|
330
|
+
},
|
|
331
|
+
fix(fixer) {
|
|
332
|
+
const sorted = [...statements].sort((a, b) => {
|
|
333
|
+
if (a.order !== b.order) {
|
|
334
|
+
return a.order - b.order;
|
|
335
|
+
}
|
|
336
|
+
return statements.indexOf(a) - statements.indexOf(b);
|
|
337
|
+
});
|
|
338
|
+
const newCode = generateSortedCode(sorted, sourceCode, groupBlankLines);
|
|
339
|
+
const firstRange = getFullRange(statements[0].node, sourceCode);
|
|
340
|
+
const lastRange = getFullRange(statements[statements.length - 1].node, sourceCode);
|
|
341
|
+
return fixer.replaceTextRange([firstRange[0], lastRange[1]], newCode + "\n");
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
var order_default = rule;
|
|
349
|
+
|
|
350
|
+
// src/index.ts
|
|
351
|
+
var plugin = {
|
|
352
|
+
meta: {
|
|
353
|
+
name: "eslint-plugin-vue-setup-order",
|
|
354
|
+
version: "1.0.0"
|
|
355
|
+
},
|
|
356
|
+
rules: {
|
|
357
|
+
order: order_default
|
|
358
|
+
},
|
|
359
|
+
configs: {
|
|
360
|
+
recommended: {
|
|
361
|
+
plugins: ["vue-setup-order"],
|
|
362
|
+
rules: {
|
|
363
|
+
"vue-setup-order/order": "error"
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
// Flat config format for ESLint 9+
|
|
367
|
+
flat: {
|
|
368
|
+
recommended: {
|
|
369
|
+
plugins: {
|
|
370
|
+
get "vue-setup-order"() {
|
|
371
|
+
return plugin;
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
rules: {
|
|
375
|
+
"vue-setup-order/order": "error"
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
var index_default = plugin;
|
|
382
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/constants.ts","../src/utils/analyze.ts","../src/utils/source-code.ts","../src/rules/order.ts"],"sourcesContent":["import orderRule from './rules/order';\n\nconst plugin = {\n meta: {\n name: 'eslint-plugin-vue-setup-order',\n version: '1.0.0',\n },\n rules: {\n order: orderRule,\n },\n configs: {\n recommended: {\n plugins: ['vue-setup-order'],\n rules: {\n 'vue-setup-order/order': 'error',\n },\n },\n // Flat config format for ESLint 9+\n flat: {\n recommended: {\n plugins: {\n get 'vue-setup-order'() {\n return plugin;\n },\n },\n rules: {\n 'vue-setup-order/order': 'error',\n },\n },\n },\n },\n};\n\nexport default plugin;\n","import type { OrderConfig } from '../types';\n\nexport const CATEGORY_IMPORT = 0;\nexport const CATEGORY_TYPES = 1;\nexport const CATEGORY_DEFINE = 2;\nexport const CATEGORY_COMPOSABLE = 3;\nexport const CATEGORY_REACTIVE = 4;\nexport const CATEGORY_COMPUTED = 5;\nexport const CATEGORY_WATCH = 6;\nexport const CATEGORY_LIFECYCLE = 7;\nexport const CATEGORY_FUNCTION = 8;\nexport const CATEGORY_PROVIDE = 9;\nexport const CATEGORY_UNKNOWN = 99;\n\nexport const DEFAULT_ORDER: OrderConfig = {\n // Imports\n import: CATEGORY_IMPORT,\n\n // Types\n types: CATEGORY_TYPES,\n\n // Define macros\n defineOptions: CATEGORY_DEFINE,\n defineProps: CATEGORY_DEFINE,\n defineEmits: CATEGORY_DEFINE,\n defineSlots: CATEGORY_DEFINE,\n defineExpose: CATEGORY_DEFINE,\n defineModel: CATEGORY_DEFINE,\n withDefaults: CATEGORY_DEFINE,\n\n // Composables\n composable: CATEGORY_COMPOSABLE,\n\n // Reactive state\n ref: CATEGORY_REACTIVE,\n reactive: CATEGORY_REACTIVE,\n shallowRef: CATEGORY_REACTIVE,\n shallowReactive: CATEGORY_REACTIVE,\n toRef: CATEGORY_REACTIVE,\n toRefs: CATEGORY_REACTIVE,\n customRef: CATEGORY_REACTIVE,\n readonly: CATEGORY_REACTIVE,\n shallowReadonly: CATEGORY_REACTIVE,\n\n // Computed\n computed: CATEGORY_COMPUTED,\n\n // Watchers\n watch: CATEGORY_WATCH,\n watchEffect: CATEGORY_WATCH,\n watchPostEffect: CATEGORY_WATCH,\n watchSyncEffect: CATEGORY_WATCH,\n\n // Lifecycle hooks\n onBeforeMount: CATEGORY_LIFECYCLE,\n onMounted: CATEGORY_LIFECYCLE,\n onBeforeUpdate: CATEGORY_LIFECYCLE,\n onUpdated: CATEGORY_LIFECYCLE,\n onBeforeUnmount: CATEGORY_LIFECYCLE,\n onUnmounted: CATEGORY_LIFECYCLE,\n onActivated: CATEGORY_LIFECYCLE,\n onDeactivated: CATEGORY_LIFECYCLE,\n onErrorCaptured: CATEGORY_LIFECYCLE,\n onRenderTracked: CATEGORY_LIFECYCLE,\n onRenderTriggered: CATEGORY_LIFECYCLE,\n onServerPrefetch: CATEGORY_LIFECYCLE,\n\n // Functions\n function: CATEGORY_FUNCTION,\n\n // Provide\n provide: CATEGORY_PROVIDE,\n\n // Unknown\n unknown: CATEGORY_UNKNOWN,\n};\n\nexport const ORDER_NAMES: Record<number, string> = {\n [CATEGORY_IMPORT]: 'imports',\n [CATEGORY_TYPES]: 'type declarations',\n [CATEGORY_DEFINE]: 'define macros',\n [CATEGORY_COMPOSABLE]: 'composables',\n [CATEGORY_REACTIVE]: 'reactive state',\n [CATEGORY_COMPUTED]: 'computed properties',\n [CATEGORY_WATCH]: 'watchers',\n [CATEGORY_LIFECYCLE]: 'lifecycle hooks',\n [CATEGORY_FUNCTION]: 'functions',\n [CATEGORY_PROVIDE]: 'provide',\n [CATEGORY_UNKNOWN]: 'other',\n};\n\nexport const COMPOSABLE_PATTERN = /^use[A-Z]/;\n","import type { Rule } from 'eslint';\nimport type { Expression, ModuleDeclaration, Statement, Super } from 'estree';\nimport type { AnalyzedStatement, OrderConfig } from '../types';\nimport {\n CATEGORY_FUNCTION,\n CATEGORY_REACTIVE,\n CATEGORY_TYPES,\n CATEGORY_UNKNOWN,\n COMPOSABLE_PATTERN,\n} from './constants';\n\n/**\n * Retrieves the name of the callee from the provided node (Expression or Super).\n *\n * @param {Expression | Super | null | undefined} node - The node representing a callee, which can be an Expression or Super. May be null or undefined.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to extract the callee name in case it's a MemberExpression.\n * @return {string | null} The name of the callee as a string if resolved, or null if the node is null, undefined, or not resolvable.\n */\nexport function getCalleeName(\n node: Expression | Super | null | undefined,\n sourceCode: Rule.RuleContext['sourceCode'],\n): string | null {\n if (!node) return null;\n\n if (node.type === 'Identifier') {\n return node.name;\n }\n\n if (node.type === 'MemberExpression') {\n return sourceCode.getText(node);\n }\n\n return null;\n}\n\n/**\n * Determines if the provided name is composable based on a predefined pattern.\n *\n * @param {string | null} name - The name to be tested against the composable pattern. Can be null.\n * @return {boolean} Returns true if the name matches the composable pattern and is not null; otherwise, returns false.\n */\nexport function isComposable(name: string | null): boolean {\n return name !== null && COMPOSABLE_PATTERN.test(name);\n}\n\n/**\n * Analyzes a given statement or module declaration to categorize, name, and assign an order based on the specified configuration.\n *\n * @param {Statement | ModuleDeclaration} statement - The statement or module declaration to be analyzed.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code context to extract details about the statement.\n * @param {OrderConfig} orderConfig - Configuration object that defines ordering and categorization rules.\n * @return {AnalyzedStatement | null} An object containing analysis details of the statement, or null for certain export declarations.\n */\nexport function analyzeStatement(\n statement: Statement | ModuleDeclaration,\n sourceCode: Rule.RuleContext['sourceCode'],\n orderConfig: OrderConfig,\n): AnalyzedStatement | null {\n const result: AnalyzedStatement = {\n node: statement as Statement,\n order: orderConfig.unknown ?? CATEGORY_UNKNOWN,\n name: 'unknown',\n category: 'unknown',\n };\n\n if (statement.type === 'ImportDeclaration') {\n result.order = orderConfig.import;\n result.name = 'import';\n result.category = 'import';\n return result;\n }\n\n const statementType = (statement as { type: string }).type;\n if (\n statementType === 'TSTypeAliasDeclaration' ||\n statementType === 'TSInterfaceDeclaration' ||\n statementType === 'TSEnumDeclaration'\n ) {\n result.order = orderConfig.types ?? CATEGORY_TYPES;\n result.name =\n statementType === 'TSTypeAliasDeclaration'\n ? 'type'\n : statementType === 'TSInterfaceDeclaration'\n ? 'interface'\n : 'enum';\n result.category = 'types';\n return result;\n }\n\n if (\n statement.type === 'ExportNamedDeclaration' ||\n statement.type === 'ExportDefaultDeclaration' ||\n statement.type === 'ExportAllDeclaration'\n ) {\n return null;\n }\n\n if (statement.type === 'VariableDeclaration') {\n const declaration = statement.declarations[0];\n const init = declaration?.init;\n\n if (!init) {\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (init.type === 'CallExpression') {\n const calleeName = getCalleeName(init.callee, sourceCode);\n\n if (calleeName) {\n if (isComposable(calleeName)) {\n result.order = orderConfig.composable;\n result.name = calleeName;\n result.category = 'composable';\n return result;\n }\n\n if (orderConfig[calleeName] !== undefined) {\n result.order = orderConfig[calleeName];\n result.name = calleeName;\n result.category = calleeName;\n return result;\n }\n }\n\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = calleeName || 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') {\n result.order = orderConfig.function ?? CATEGORY_FUNCTION;\n result.name = declaration.id?.type === 'Identifier' ? declaration.id.name : 'arrow function';\n result.category = 'function';\n return result;\n }\n\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (statement.type === 'FunctionDeclaration') {\n result.order = orderConfig.function ?? CATEGORY_FUNCTION;\n result.name = statement.id?.name || 'function';\n result.category = 'function';\n return result;\n }\n\n if (statement.type === 'ExpressionStatement') {\n const expr = statement.expression;\n\n if (expr.type === 'CallExpression') {\n const calleeName = getCalleeName(expr.callee, sourceCode);\n\n if (calleeName && orderConfig[calleeName] !== undefined) {\n result.order = orderConfig[calleeName];\n result.name = calleeName;\n result.category = calleeName;\n return result;\n }\n\n result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;\n result.name = calleeName || 'call';\n result.category = 'unknown';\n return result;\n }\n\n result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;\n result.name = 'expression';\n result.category = 'unknown';\n return result;\n }\n\n return result;\n}\n","import type { Rule } from 'eslint';\nimport type { Statement } from 'estree';\n\n/**\n * Retrieves the combined text of a given AST node and its preceding comments from the source code.\n *\n * @param {Statement} node - The AST node whose text and preceding comments are to be retrieved.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object containing the text and comments.\n * @return {string} The concatenated text of the node and its preceding comments.\n */\nexport function getNodeTextWithComments(node: Statement, sourceCode: Rule.RuleContext['sourceCode']): string {\n const comments = sourceCode.getCommentsBefore(node);\n let start = node.range![0];\n\n if (comments.length > 0) {\n start = comments[0].range![0];\n }\n\n return sourceCode.text.slice(start, node.range![1]);\n}\n\n/**\n * Determines the full range of a given statement node, including its preceding comments\n * and any trailing newlines immediately following the node.\n *\n * @param {Statement} node - The statement node for which to calculate the full range.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to retrieve comments and text.\n * @return {[number, number]} The start and end positions of the full range in the source code.\n */\nexport function getFullRange(node: Statement, sourceCode: Rule.RuleContext['sourceCode']): [number, number] {\n const comments = sourceCode.getCommentsBefore(node);\n let start = node.range![0];\n\n if (comments.length > 0) {\n start = comments[0].range![0];\n }\n\n let end = node.range![1];\n const textAfter = sourceCode.text.slice(end, end + 2);\n\n if (textAfter.startsWith('\\r\\n')) {\n end += 2;\n } else if (textAfter.startsWith('\\n')) {\n end += 1;\n }\n\n return [start, end];\n}\n\n/**\n * Generates a sorted string representation of code statements, optionally adding blank lines between different categories.\n *\n * @param {Array<{ node: Statement, order: number }>} statements - An array of objects containing a statement node and its corresponding order.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to extract the textual representation of the statement nodes.\n * @param {boolean} [addBlankLines=true] - A flag indicating whether to add blank lines between statements of differing orders.\n * @return {string} A sorted and formatted code string based on the given statements and options.\n */\nexport function generateSortedCode(\n statements: Array<{ node: Statement; order: number }>,\n sourceCode: Rule.RuleContext['sourceCode'],\n addBlankLines: boolean = true,\n): string {\n const parts: string[] = [];\n let lastOrder = -1;\n\n for (const statement of statements) {\n const text = getNodeTextWithComments(statement.node, sourceCode).trim();\n\n if (addBlankLines && lastOrder !== -1 && statement.order !== lastOrder) {\n parts.push('');\n }\n\n parts.push(text);\n lastOrder = statement.order;\n }\n\n return parts.join('\\n');\n}\n","import type { Rule } from 'eslint';\nimport type { Program } from 'estree';\nimport type { AnalyzedStatement, OrderConfig, PluginOptions } from '../types';\nimport { analyzeStatement } from '../utils/analyze';\nimport { DEFAULT_ORDER, ORDER_NAMES } from '../utils/constants';\nimport { generateSortedCode, getFullRange } from '../utils/source-code';\n\n/**\n * Represents an ESLint rule module designed to enforce a consistent order of statements\n * in the `<script setup>` block of Vue 3 components.\n *\n * The rule provides configuration options for defining custom ordering of statement categories\n * and for including blank lines between different categories. The rule reports and optionally\n * fixes statement order violations.\n */\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Enforce consistent order of statements in Vue 3 <script setup>',\n category: 'Stylistic Issues',\n recommended: true,\n url: 'https://github.com/d2a8k3u/eslint-plugin-vue-setup-order#readme',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n order: {\n type: 'array',\n items: { type: 'string' },\n description: 'Custom order of categories',\n },\n groupBlankLines: {\n type: 'boolean',\n default: true,\n description: 'Add blank lines between different categories',\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n wrongOrder: \"'{{current}}' ({{currentCategory}}) should come before '{{previous}}' ({{previousCategory}})\",\n },\n },\n\n create(context: Rule.RuleContext) {\n const sourceCode = context.sourceCode ?? context.getSourceCode();\n const options: PluginOptions = context.options[0] || {};\n const groupBlankLines = false !== options.groupBlankLines;\n const orderConfig: OrderConfig = { ...DEFAULT_ORDER };\n\n if (options.order && Array.isArray(options.order)) {\n options.order.forEach((category, index) => {\n const originalOrder = DEFAULT_ORDER[category];\n if (originalOrder !== undefined) {\n Object.keys(DEFAULT_ORDER).forEach((key) => {\n if (DEFAULT_ORDER[key] === originalOrder) {\n orderConfig[key] = index;\n }\n });\n }\n orderConfig[category] = index;\n });\n }\n\n return {\n Program(node: Program) {\n const statements: AnalyzedStatement[] = [];\n\n for (const statement of node.body) {\n const analyzed = analyzeStatement(statement, sourceCode, orderConfig);\n if (analyzed) {\n statements.push(analyzed);\n }\n }\n\n if (statements.length < 2) return;\n\n let firstError: {\n index: number;\n current: AnalyzedStatement;\n previous: AnalyzedStatement;\n } | null = null;\n\n for (let i = 1; i < statements.length; i++) {\n const prev = statements[i - 1];\n const curr = statements[i];\n\n if (curr.order < prev.order) {\n firstError = { index: i, current: curr, previous: prev };\n break;\n }\n }\n\n if (!firstError) return;\n\n context.report({\n node: firstError.current.node,\n messageId: 'wrongOrder',\n data: {\n current: firstError.current.name,\n currentCategory: ORDER_NAMES[firstError.current.order] || 'unknown',\n previous: firstError.previous.name,\n previousCategory: ORDER_NAMES[firstError.previous.order] || 'unknown',\n },\n fix(fixer) {\n const sorted = [...statements].sort((a, b) => {\n if (a.order !== b.order) {\n return a.order - b.order;\n }\n\n return statements.indexOf(a) - statements.indexOf(b);\n });\n\n const newCode = generateSortedCode(sorted, sourceCode, groupBlankLines);\n const firstRange = getFullRange(statements[0].node, sourceCode);\n const lastRange = getFullRange(statements[statements.length - 1].node, sourceCode);\n\n return fixer.replaceTextRange([firstRange[0], lastRange[1]], newCode + '\\n');\n },\n });\n },\n };\n },\n};\n\nexport default rule;\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as eslint from 'eslint';
|
|
2
|
+
|
|
3
|
+
declare const plugin: {
|
|
4
|
+
meta: {
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
};
|
|
8
|
+
rules: {
|
|
9
|
+
order: eslint.Rule.RuleModule;
|
|
10
|
+
};
|
|
11
|
+
configs: {
|
|
12
|
+
recommended: {
|
|
13
|
+
plugins: string[];
|
|
14
|
+
rules: {
|
|
15
|
+
'vue-setup-order/order': string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
flat: {
|
|
19
|
+
recommended: {
|
|
20
|
+
plugins: {
|
|
21
|
+
readonly 'vue-setup-order': any;
|
|
22
|
+
};
|
|
23
|
+
rules: {
|
|
24
|
+
'vue-setup-order/order': string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { plugin as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as eslint from 'eslint';
|
|
2
|
+
|
|
3
|
+
declare const plugin: {
|
|
4
|
+
meta: {
|
|
5
|
+
name: string;
|
|
6
|
+
version: string;
|
|
7
|
+
};
|
|
8
|
+
rules: {
|
|
9
|
+
order: eslint.Rule.RuleModule;
|
|
10
|
+
};
|
|
11
|
+
configs: {
|
|
12
|
+
recommended: {
|
|
13
|
+
plugins: string[];
|
|
14
|
+
rules: {
|
|
15
|
+
'vue-setup-order/order': string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
flat: {
|
|
19
|
+
recommended: {
|
|
20
|
+
plugins: {
|
|
21
|
+
readonly 'vue-setup-order': any;
|
|
22
|
+
};
|
|
23
|
+
rules: {
|
|
24
|
+
'vue-setup-order/order': string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { plugin as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// src/utils/constants.ts
|
|
2
|
+
var CATEGORY_IMPORT = 0;
|
|
3
|
+
var CATEGORY_TYPES = 1;
|
|
4
|
+
var CATEGORY_DEFINE = 2;
|
|
5
|
+
var CATEGORY_COMPOSABLE = 3;
|
|
6
|
+
var CATEGORY_REACTIVE = 4;
|
|
7
|
+
var CATEGORY_COMPUTED = 5;
|
|
8
|
+
var CATEGORY_WATCH = 6;
|
|
9
|
+
var CATEGORY_LIFECYCLE = 7;
|
|
10
|
+
var CATEGORY_FUNCTION = 8;
|
|
11
|
+
var CATEGORY_PROVIDE = 9;
|
|
12
|
+
var CATEGORY_UNKNOWN = 99;
|
|
13
|
+
var DEFAULT_ORDER = {
|
|
14
|
+
// Imports
|
|
15
|
+
import: CATEGORY_IMPORT,
|
|
16
|
+
// Types
|
|
17
|
+
types: CATEGORY_TYPES,
|
|
18
|
+
// Define macros
|
|
19
|
+
defineOptions: CATEGORY_DEFINE,
|
|
20
|
+
defineProps: CATEGORY_DEFINE,
|
|
21
|
+
defineEmits: CATEGORY_DEFINE,
|
|
22
|
+
defineSlots: CATEGORY_DEFINE,
|
|
23
|
+
defineExpose: CATEGORY_DEFINE,
|
|
24
|
+
defineModel: CATEGORY_DEFINE,
|
|
25
|
+
withDefaults: CATEGORY_DEFINE,
|
|
26
|
+
// Composables
|
|
27
|
+
composable: CATEGORY_COMPOSABLE,
|
|
28
|
+
// Reactive state
|
|
29
|
+
ref: CATEGORY_REACTIVE,
|
|
30
|
+
reactive: CATEGORY_REACTIVE,
|
|
31
|
+
shallowRef: CATEGORY_REACTIVE,
|
|
32
|
+
shallowReactive: CATEGORY_REACTIVE,
|
|
33
|
+
toRef: CATEGORY_REACTIVE,
|
|
34
|
+
toRefs: CATEGORY_REACTIVE,
|
|
35
|
+
customRef: CATEGORY_REACTIVE,
|
|
36
|
+
readonly: CATEGORY_REACTIVE,
|
|
37
|
+
shallowReadonly: CATEGORY_REACTIVE,
|
|
38
|
+
// Computed
|
|
39
|
+
computed: CATEGORY_COMPUTED,
|
|
40
|
+
// Watchers
|
|
41
|
+
watch: CATEGORY_WATCH,
|
|
42
|
+
watchEffect: CATEGORY_WATCH,
|
|
43
|
+
watchPostEffect: CATEGORY_WATCH,
|
|
44
|
+
watchSyncEffect: CATEGORY_WATCH,
|
|
45
|
+
// Lifecycle hooks
|
|
46
|
+
onBeforeMount: CATEGORY_LIFECYCLE,
|
|
47
|
+
onMounted: CATEGORY_LIFECYCLE,
|
|
48
|
+
onBeforeUpdate: CATEGORY_LIFECYCLE,
|
|
49
|
+
onUpdated: CATEGORY_LIFECYCLE,
|
|
50
|
+
onBeforeUnmount: CATEGORY_LIFECYCLE,
|
|
51
|
+
onUnmounted: CATEGORY_LIFECYCLE,
|
|
52
|
+
onActivated: CATEGORY_LIFECYCLE,
|
|
53
|
+
onDeactivated: CATEGORY_LIFECYCLE,
|
|
54
|
+
onErrorCaptured: CATEGORY_LIFECYCLE,
|
|
55
|
+
onRenderTracked: CATEGORY_LIFECYCLE,
|
|
56
|
+
onRenderTriggered: CATEGORY_LIFECYCLE,
|
|
57
|
+
onServerPrefetch: CATEGORY_LIFECYCLE,
|
|
58
|
+
// Functions
|
|
59
|
+
function: CATEGORY_FUNCTION,
|
|
60
|
+
// Provide
|
|
61
|
+
provide: CATEGORY_PROVIDE,
|
|
62
|
+
// Unknown
|
|
63
|
+
unknown: CATEGORY_UNKNOWN
|
|
64
|
+
};
|
|
65
|
+
var ORDER_NAMES = {
|
|
66
|
+
[CATEGORY_IMPORT]: "imports",
|
|
67
|
+
[CATEGORY_TYPES]: "type declarations",
|
|
68
|
+
[CATEGORY_DEFINE]: "define macros",
|
|
69
|
+
[CATEGORY_COMPOSABLE]: "composables",
|
|
70
|
+
[CATEGORY_REACTIVE]: "reactive state",
|
|
71
|
+
[CATEGORY_COMPUTED]: "computed properties",
|
|
72
|
+
[CATEGORY_WATCH]: "watchers",
|
|
73
|
+
[CATEGORY_LIFECYCLE]: "lifecycle hooks",
|
|
74
|
+
[CATEGORY_FUNCTION]: "functions",
|
|
75
|
+
[CATEGORY_PROVIDE]: "provide",
|
|
76
|
+
[CATEGORY_UNKNOWN]: "other"
|
|
77
|
+
};
|
|
78
|
+
var COMPOSABLE_PATTERN = /^use[A-Z]/;
|
|
79
|
+
|
|
80
|
+
// src/utils/analyze.ts
|
|
81
|
+
function getCalleeName(node, sourceCode) {
|
|
82
|
+
if (!node) return null;
|
|
83
|
+
if (node.type === "Identifier") {
|
|
84
|
+
return node.name;
|
|
85
|
+
}
|
|
86
|
+
if (node.type === "MemberExpression") {
|
|
87
|
+
return sourceCode.getText(node);
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
function isComposable(name) {
|
|
92
|
+
return name !== null && COMPOSABLE_PATTERN.test(name);
|
|
93
|
+
}
|
|
94
|
+
function analyzeStatement(statement, sourceCode, orderConfig) {
|
|
95
|
+
const result = {
|
|
96
|
+
node: statement,
|
|
97
|
+
order: orderConfig.unknown ?? CATEGORY_UNKNOWN,
|
|
98
|
+
name: "unknown",
|
|
99
|
+
category: "unknown"
|
|
100
|
+
};
|
|
101
|
+
if (statement.type === "ImportDeclaration") {
|
|
102
|
+
result.order = orderConfig.import;
|
|
103
|
+
result.name = "import";
|
|
104
|
+
result.category = "import";
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
const statementType = statement.type;
|
|
108
|
+
if (statementType === "TSTypeAliasDeclaration" || statementType === "TSInterfaceDeclaration" || statementType === "TSEnumDeclaration") {
|
|
109
|
+
result.order = orderConfig.types ?? CATEGORY_TYPES;
|
|
110
|
+
result.name = statementType === "TSTypeAliasDeclaration" ? "type" : statementType === "TSInterfaceDeclaration" ? "interface" : "enum";
|
|
111
|
+
result.category = "types";
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (statement.type === "VariableDeclaration") {
|
|
118
|
+
const declaration = statement.declarations[0];
|
|
119
|
+
const init = declaration?.init;
|
|
120
|
+
if (!init) {
|
|
121
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
122
|
+
result.name = "variable";
|
|
123
|
+
result.category = "reactive";
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
if (init.type === "CallExpression") {
|
|
127
|
+
const calleeName = getCalleeName(init.callee, sourceCode);
|
|
128
|
+
if (calleeName) {
|
|
129
|
+
if (isComposable(calleeName)) {
|
|
130
|
+
result.order = orderConfig.composable;
|
|
131
|
+
result.name = calleeName;
|
|
132
|
+
result.category = "composable";
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
if (orderConfig[calleeName] !== void 0) {
|
|
136
|
+
result.order = orderConfig[calleeName];
|
|
137
|
+
result.name = calleeName;
|
|
138
|
+
result.category = calleeName;
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
143
|
+
result.name = calleeName || "variable";
|
|
144
|
+
result.category = "reactive";
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") {
|
|
148
|
+
result.order = orderConfig.function ?? CATEGORY_FUNCTION;
|
|
149
|
+
result.name = declaration.id?.type === "Identifier" ? declaration.id.name : "arrow function";
|
|
150
|
+
result.category = "function";
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
result.order = orderConfig.ref ?? CATEGORY_REACTIVE;
|
|
154
|
+
result.name = "variable";
|
|
155
|
+
result.category = "reactive";
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
if (statement.type === "FunctionDeclaration") {
|
|
159
|
+
result.order = orderConfig.function ?? CATEGORY_FUNCTION;
|
|
160
|
+
result.name = statement.id?.name || "function";
|
|
161
|
+
result.category = "function";
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
if (statement.type === "ExpressionStatement") {
|
|
165
|
+
const expr = statement.expression;
|
|
166
|
+
if (expr.type === "CallExpression") {
|
|
167
|
+
const calleeName = getCalleeName(expr.callee, sourceCode);
|
|
168
|
+
if (calleeName && orderConfig[calleeName] !== void 0) {
|
|
169
|
+
result.order = orderConfig[calleeName];
|
|
170
|
+
result.name = calleeName;
|
|
171
|
+
result.category = calleeName;
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;
|
|
175
|
+
result.name = calleeName || "call";
|
|
176
|
+
result.category = "unknown";
|
|
177
|
+
return result;
|
|
178
|
+
}
|
|
179
|
+
result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;
|
|
180
|
+
result.name = "expression";
|
|
181
|
+
result.category = "unknown";
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/utils/source-code.ts
|
|
188
|
+
function getNodeTextWithComments(node, sourceCode) {
|
|
189
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
190
|
+
let start = node.range[0];
|
|
191
|
+
if (comments.length > 0) {
|
|
192
|
+
start = comments[0].range[0];
|
|
193
|
+
}
|
|
194
|
+
return sourceCode.text.slice(start, node.range[1]);
|
|
195
|
+
}
|
|
196
|
+
function getFullRange(node, sourceCode) {
|
|
197
|
+
const comments = sourceCode.getCommentsBefore(node);
|
|
198
|
+
let start = node.range[0];
|
|
199
|
+
if (comments.length > 0) {
|
|
200
|
+
start = comments[0].range[0];
|
|
201
|
+
}
|
|
202
|
+
let end = node.range[1];
|
|
203
|
+
const textAfter = sourceCode.text.slice(end, end + 2);
|
|
204
|
+
if (textAfter.startsWith("\r\n")) {
|
|
205
|
+
end += 2;
|
|
206
|
+
} else if (textAfter.startsWith("\n")) {
|
|
207
|
+
end += 1;
|
|
208
|
+
}
|
|
209
|
+
return [start, end];
|
|
210
|
+
}
|
|
211
|
+
function generateSortedCode(statements, sourceCode, addBlankLines = true) {
|
|
212
|
+
const parts = [];
|
|
213
|
+
let lastOrder = -1;
|
|
214
|
+
for (const statement of statements) {
|
|
215
|
+
const text = getNodeTextWithComments(statement.node, sourceCode).trim();
|
|
216
|
+
if (addBlankLines && lastOrder !== -1 && statement.order !== lastOrder) {
|
|
217
|
+
parts.push("");
|
|
218
|
+
}
|
|
219
|
+
parts.push(text);
|
|
220
|
+
lastOrder = statement.order;
|
|
221
|
+
}
|
|
222
|
+
return parts.join("\n");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/rules/order.ts
|
|
226
|
+
var rule = {
|
|
227
|
+
meta: {
|
|
228
|
+
type: "suggestion",
|
|
229
|
+
docs: {
|
|
230
|
+
description: "Enforce consistent order of statements in Vue 3 <script setup>",
|
|
231
|
+
category: "Stylistic Issues",
|
|
232
|
+
recommended: true,
|
|
233
|
+
url: "https://github.com/d2a8k3u/eslint-plugin-vue-setup-order#readme"
|
|
234
|
+
},
|
|
235
|
+
fixable: "code",
|
|
236
|
+
schema: [
|
|
237
|
+
{
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
order: {
|
|
241
|
+
type: "array",
|
|
242
|
+
items: { type: "string" },
|
|
243
|
+
description: "Custom order of categories"
|
|
244
|
+
},
|
|
245
|
+
groupBlankLines: {
|
|
246
|
+
type: "boolean",
|
|
247
|
+
default: true,
|
|
248
|
+
description: "Add blank lines between different categories"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
additionalProperties: false
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
messages: {
|
|
255
|
+
wrongOrder: "'{{current}}' ({{currentCategory}}) should come before '{{previous}}' ({{previousCategory}})"
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
create(context) {
|
|
259
|
+
const sourceCode = context.sourceCode ?? context.getSourceCode();
|
|
260
|
+
const options = context.options[0] || {};
|
|
261
|
+
const groupBlankLines = false !== options.groupBlankLines;
|
|
262
|
+
const orderConfig = { ...DEFAULT_ORDER };
|
|
263
|
+
if (options.order && Array.isArray(options.order)) {
|
|
264
|
+
options.order.forEach((category, index) => {
|
|
265
|
+
const originalOrder = DEFAULT_ORDER[category];
|
|
266
|
+
if (originalOrder !== void 0) {
|
|
267
|
+
Object.keys(DEFAULT_ORDER).forEach((key) => {
|
|
268
|
+
if (DEFAULT_ORDER[key] === originalOrder) {
|
|
269
|
+
orderConfig[key] = index;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
orderConfig[category] = index;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
Program(node) {
|
|
278
|
+
const statements = [];
|
|
279
|
+
for (const statement of node.body) {
|
|
280
|
+
const analyzed = analyzeStatement(statement, sourceCode, orderConfig);
|
|
281
|
+
if (analyzed) {
|
|
282
|
+
statements.push(analyzed);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (statements.length < 2) return;
|
|
286
|
+
let firstError = null;
|
|
287
|
+
for (let i = 1; i < statements.length; i++) {
|
|
288
|
+
const prev = statements[i - 1];
|
|
289
|
+
const curr = statements[i];
|
|
290
|
+
if (curr.order < prev.order) {
|
|
291
|
+
firstError = { index: i, current: curr, previous: prev };
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (!firstError) return;
|
|
296
|
+
context.report({
|
|
297
|
+
node: firstError.current.node,
|
|
298
|
+
messageId: "wrongOrder",
|
|
299
|
+
data: {
|
|
300
|
+
current: firstError.current.name,
|
|
301
|
+
currentCategory: ORDER_NAMES[firstError.current.order] || "unknown",
|
|
302
|
+
previous: firstError.previous.name,
|
|
303
|
+
previousCategory: ORDER_NAMES[firstError.previous.order] || "unknown"
|
|
304
|
+
},
|
|
305
|
+
fix(fixer) {
|
|
306
|
+
const sorted = [...statements].sort((a, b) => {
|
|
307
|
+
if (a.order !== b.order) {
|
|
308
|
+
return a.order - b.order;
|
|
309
|
+
}
|
|
310
|
+
return statements.indexOf(a) - statements.indexOf(b);
|
|
311
|
+
});
|
|
312
|
+
const newCode = generateSortedCode(sorted, sourceCode, groupBlankLines);
|
|
313
|
+
const firstRange = getFullRange(statements[0].node, sourceCode);
|
|
314
|
+
const lastRange = getFullRange(statements[statements.length - 1].node, sourceCode);
|
|
315
|
+
return fixer.replaceTextRange([firstRange[0], lastRange[1]], newCode + "\n");
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
var order_default = rule;
|
|
323
|
+
|
|
324
|
+
// src/index.ts
|
|
325
|
+
var plugin = {
|
|
326
|
+
meta: {
|
|
327
|
+
name: "eslint-plugin-vue-setup-order",
|
|
328
|
+
version: "1.0.0"
|
|
329
|
+
},
|
|
330
|
+
rules: {
|
|
331
|
+
order: order_default
|
|
332
|
+
},
|
|
333
|
+
configs: {
|
|
334
|
+
recommended: {
|
|
335
|
+
plugins: ["vue-setup-order"],
|
|
336
|
+
rules: {
|
|
337
|
+
"vue-setup-order/order": "error"
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
// Flat config format for ESLint 9+
|
|
341
|
+
flat: {
|
|
342
|
+
recommended: {
|
|
343
|
+
plugins: {
|
|
344
|
+
get "vue-setup-order"() {
|
|
345
|
+
return plugin;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
rules: {
|
|
349
|
+
"vue-setup-order/order": "error"
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
var index_default = plugin;
|
|
356
|
+
export {
|
|
357
|
+
index_default as default
|
|
358
|
+
};
|
|
359
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/constants.ts","../src/utils/analyze.ts","../src/utils/source-code.ts","../src/rules/order.ts","../src/index.ts"],"sourcesContent":["import type { OrderConfig } from '../types';\n\nexport const CATEGORY_IMPORT = 0;\nexport const CATEGORY_TYPES = 1;\nexport const CATEGORY_DEFINE = 2;\nexport const CATEGORY_COMPOSABLE = 3;\nexport const CATEGORY_REACTIVE = 4;\nexport const CATEGORY_COMPUTED = 5;\nexport const CATEGORY_WATCH = 6;\nexport const CATEGORY_LIFECYCLE = 7;\nexport const CATEGORY_FUNCTION = 8;\nexport const CATEGORY_PROVIDE = 9;\nexport const CATEGORY_UNKNOWN = 99;\n\nexport const DEFAULT_ORDER: OrderConfig = {\n // Imports\n import: CATEGORY_IMPORT,\n\n // Types\n types: CATEGORY_TYPES,\n\n // Define macros\n defineOptions: CATEGORY_DEFINE,\n defineProps: CATEGORY_DEFINE,\n defineEmits: CATEGORY_DEFINE,\n defineSlots: CATEGORY_DEFINE,\n defineExpose: CATEGORY_DEFINE,\n defineModel: CATEGORY_DEFINE,\n withDefaults: CATEGORY_DEFINE,\n\n // Composables\n composable: CATEGORY_COMPOSABLE,\n\n // Reactive state\n ref: CATEGORY_REACTIVE,\n reactive: CATEGORY_REACTIVE,\n shallowRef: CATEGORY_REACTIVE,\n shallowReactive: CATEGORY_REACTIVE,\n toRef: CATEGORY_REACTIVE,\n toRefs: CATEGORY_REACTIVE,\n customRef: CATEGORY_REACTIVE,\n readonly: CATEGORY_REACTIVE,\n shallowReadonly: CATEGORY_REACTIVE,\n\n // Computed\n computed: CATEGORY_COMPUTED,\n\n // Watchers\n watch: CATEGORY_WATCH,\n watchEffect: CATEGORY_WATCH,\n watchPostEffect: CATEGORY_WATCH,\n watchSyncEffect: CATEGORY_WATCH,\n\n // Lifecycle hooks\n onBeforeMount: CATEGORY_LIFECYCLE,\n onMounted: CATEGORY_LIFECYCLE,\n onBeforeUpdate: CATEGORY_LIFECYCLE,\n onUpdated: CATEGORY_LIFECYCLE,\n onBeforeUnmount: CATEGORY_LIFECYCLE,\n onUnmounted: CATEGORY_LIFECYCLE,\n onActivated: CATEGORY_LIFECYCLE,\n onDeactivated: CATEGORY_LIFECYCLE,\n onErrorCaptured: CATEGORY_LIFECYCLE,\n onRenderTracked: CATEGORY_LIFECYCLE,\n onRenderTriggered: CATEGORY_LIFECYCLE,\n onServerPrefetch: CATEGORY_LIFECYCLE,\n\n // Functions\n function: CATEGORY_FUNCTION,\n\n // Provide\n provide: CATEGORY_PROVIDE,\n\n // Unknown\n unknown: CATEGORY_UNKNOWN,\n};\n\nexport const ORDER_NAMES: Record<number, string> = {\n [CATEGORY_IMPORT]: 'imports',\n [CATEGORY_TYPES]: 'type declarations',\n [CATEGORY_DEFINE]: 'define macros',\n [CATEGORY_COMPOSABLE]: 'composables',\n [CATEGORY_REACTIVE]: 'reactive state',\n [CATEGORY_COMPUTED]: 'computed properties',\n [CATEGORY_WATCH]: 'watchers',\n [CATEGORY_LIFECYCLE]: 'lifecycle hooks',\n [CATEGORY_FUNCTION]: 'functions',\n [CATEGORY_PROVIDE]: 'provide',\n [CATEGORY_UNKNOWN]: 'other',\n};\n\nexport const COMPOSABLE_PATTERN = /^use[A-Z]/;\n","import type { Rule } from 'eslint';\nimport type { Expression, ModuleDeclaration, Statement, Super } from 'estree';\nimport type { AnalyzedStatement, OrderConfig } from '../types';\nimport {\n CATEGORY_FUNCTION,\n CATEGORY_REACTIVE,\n CATEGORY_TYPES,\n CATEGORY_UNKNOWN,\n COMPOSABLE_PATTERN,\n} from './constants';\n\n/**\n * Retrieves the name of the callee from the provided node (Expression or Super).\n *\n * @param {Expression | Super | null | undefined} node - The node representing a callee, which can be an Expression or Super. May be null or undefined.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to extract the callee name in case it's a MemberExpression.\n * @return {string | null} The name of the callee as a string if resolved, or null if the node is null, undefined, or not resolvable.\n */\nexport function getCalleeName(\n node: Expression | Super | null | undefined,\n sourceCode: Rule.RuleContext['sourceCode'],\n): string | null {\n if (!node) return null;\n\n if (node.type === 'Identifier') {\n return node.name;\n }\n\n if (node.type === 'MemberExpression') {\n return sourceCode.getText(node);\n }\n\n return null;\n}\n\n/**\n * Determines if the provided name is composable based on a predefined pattern.\n *\n * @param {string | null} name - The name to be tested against the composable pattern. Can be null.\n * @return {boolean} Returns true if the name matches the composable pattern and is not null; otherwise, returns false.\n */\nexport function isComposable(name: string | null): boolean {\n return name !== null && COMPOSABLE_PATTERN.test(name);\n}\n\n/**\n * Analyzes a given statement or module declaration to categorize, name, and assign an order based on the specified configuration.\n *\n * @param {Statement | ModuleDeclaration} statement - The statement or module declaration to be analyzed.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code context to extract details about the statement.\n * @param {OrderConfig} orderConfig - Configuration object that defines ordering and categorization rules.\n * @return {AnalyzedStatement | null} An object containing analysis details of the statement, or null for certain export declarations.\n */\nexport function analyzeStatement(\n statement: Statement | ModuleDeclaration,\n sourceCode: Rule.RuleContext['sourceCode'],\n orderConfig: OrderConfig,\n): AnalyzedStatement | null {\n const result: AnalyzedStatement = {\n node: statement as Statement,\n order: orderConfig.unknown ?? CATEGORY_UNKNOWN,\n name: 'unknown',\n category: 'unknown',\n };\n\n if (statement.type === 'ImportDeclaration') {\n result.order = orderConfig.import;\n result.name = 'import';\n result.category = 'import';\n return result;\n }\n\n const statementType = (statement as { type: string }).type;\n if (\n statementType === 'TSTypeAliasDeclaration' ||\n statementType === 'TSInterfaceDeclaration' ||\n statementType === 'TSEnumDeclaration'\n ) {\n result.order = orderConfig.types ?? CATEGORY_TYPES;\n result.name =\n statementType === 'TSTypeAliasDeclaration'\n ? 'type'\n : statementType === 'TSInterfaceDeclaration'\n ? 'interface'\n : 'enum';\n result.category = 'types';\n return result;\n }\n\n if (\n statement.type === 'ExportNamedDeclaration' ||\n statement.type === 'ExportDefaultDeclaration' ||\n statement.type === 'ExportAllDeclaration'\n ) {\n return null;\n }\n\n if (statement.type === 'VariableDeclaration') {\n const declaration = statement.declarations[0];\n const init = declaration?.init;\n\n if (!init) {\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (init.type === 'CallExpression') {\n const calleeName = getCalleeName(init.callee, sourceCode);\n\n if (calleeName) {\n if (isComposable(calleeName)) {\n result.order = orderConfig.composable;\n result.name = calleeName;\n result.category = 'composable';\n return result;\n }\n\n if (orderConfig[calleeName] !== undefined) {\n result.order = orderConfig[calleeName];\n result.name = calleeName;\n result.category = calleeName;\n return result;\n }\n }\n\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = calleeName || 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') {\n result.order = orderConfig.function ?? CATEGORY_FUNCTION;\n result.name = declaration.id?.type === 'Identifier' ? declaration.id.name : 'arrow function';\n result.category = 'function';\n return result;\n }\n\n result.order = orderConfig.ref ?? CATEGORY_REACTIVE;\n result.name = 'variable';\n result.category = 'reactive';\n return result;\n }\n\n if (statement.type === 'FunctionDeclaration') {\n result.order = orderConfig.function ?? CATEGORY_FUNCTION;\n result.name = statement.id?.name || 'function';\n result.category = 'function';\n return result;\n }\n\n if (statement.type === 'ExpressionStatement') {\n const expr = statement.expression;\n\n if (expr.type === 'CallExpression') {\n const calleeName = getCalleeName(expr.callee, sourceCode);\n\n if (calleeName && orderConfig[calleeName] !== undefined) {\n result.order = orderConfig[calleeName];\n result.name = calleeName;\n result.category = calleeName;\n return result;\n }\n\n result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;\n result.name = calleeName || 'call';\n result.category = 'unknown';\n return result;\n }\n\n result.order = orderConfig.unknown ?? CATEGORY_UNKNOWN;\n result.name = 'expression';\n result.category = 'unknown';\n return result;\n }\n\n return result;\n}\n","import type { Rule } from 'eslint';\nimport type { Statement } from 'estree';\n\n/**\n * Retrieves the combined text of a given AST node and its preceding comments from the source code.\n *\n * @param {Statement} node - The AST node whose text and preceding comments are to be retrieved.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object containing the text and comments.\n * @return {string} The concatenated text of the node and its preceding comments.\n */\nexport function getNodeTextWithComments(node: Statement, sourceCode: Rule.RuleContext['sourceCode']): string {\n const comments = sourceCode.getCommentsBefore(node);\n let start = node.range![0];\n\n if (comments.length > 0) {\n start = comments[0].range![0];\n }\n\n return sourceCode.text.slice(start, node.range![1]);\n}\n\n/**\n * Determines the full range of a given statement node, including its preceding comments\n * and any trailing newlines immediately following the node.\n *\n * @param {Statement} node - The statement node for which to calculate the full range.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to retrieve comments and text.\n * @return {[number, number]} The start and end positions of the full range in the source code.\n */\nexport function getFullRange(node: Statement, sourceCode: Rule.RuleContext['sourceCode']): [number, number] {\n const comments = sourceCode.getCommentsBefore(node);\n let start = node.range![0];\n\n if (comments.length > 0) {\n start = comments[0].range![0];\n }\n\n let end = node.range![1];\n const textAfter = sourceCode.text.slice(end, end + 2);\n\n if (textAfter.startsWith('\\r\\n')) {\n end += 2;\n } else if (textAfter.startsWith('\\n')) {\n end += 1;\n }\n\n return [start, end];\n}\n\n/**\n * Generates a sorted string representation of code statements, optionally adding blank lines between different categories.\n *\n * @param {Array<{ node: Statement, order: number }>} statements - An array of objects containing a statement node and its corresponding order.\n * @param {Rule.RuleContext['sourceCode']} sourceCode - The source code object used to extract the textual representation of the statement nodes.\n * @param {boolean} [addBlankLines=true] - A flag indicating whether to add blank lines between statements of differing orders.\n * @return {string} A sorted and formatted code string based on the given statements and options.\n */\nexport function generateSortedCode(\n statements: Array<{ node: Statement; order: number }>,\n sourceCode: Rule.RuleContext['sourceCode'],\n addBlankLines: boolean = true,\n): string {\n const parts: string[] = [];\n let lastOrder = -1;\n\n for (const statement of statements) {\n const text = getNodeTextWithComments(statement.node, sourceCode).trim();\n\n if (addBlankLines && lastOrder !== -1 && statement.order !== lastOrder) {\n parts.push('');\n }\n\n parts.push(text);\n lastOrder = statement.order;\n }\n\n return parts.join('\\n');\n}\n","import type { Rule } from 'eslint';\nimport type { Program } from 'estree';\nimport type { AnalyzedStatement, OrderConfig, PluginOptions } from '../types';\nimport { analyzeStatement } from '../utils/analyze';\nimport { DEFAULT_ORDER, ORDER_NAMES } from '../utils/constants';\nimport { generateSortedCode, getFullRange } from '../utils/source-code';\n\n/**\n * Represents an ESLint rule module designed to enforce a consistent order of statements\n * in the `<script setup>` block of Vue 3 components.\n *\n * The rule provides configuration options for defining custom ordering of statement categories\n * and for including blank lines between different categories. The rule reports and optionally\n * fixes statement order violations.\n */\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Enforce consistent order of statements in Vue 3 <script setup>',\n category: 'Stylistic Issues',\n recommended: true,\n url: 'https://github.com/d2a8k3u/eslint-plugin-vue-setup-order#readme',\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n order: {\n type: 'array',\n items: { type: 'string' },\n description: 'Custom order of categories',\n },\n groupBlankLines: {\n type: 'boolean',\n default: true,\n description: 'Add blank lines between different categories',\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n wrongOrder: \"'{{current}}' ({{currentCategory}}) should come before '{{previous}}' ({{previousCategory}})\",\n },\n },\n\n create(context: Rule.RuleContext) {\n const sourceCode = context.sourceCode ?? context.getSourceCode();\n const options: PluginOptions = context.options[0] || {};\n const groupBlankLines = false !== options.groupBlankLines;\n const orderConfig: OrderConfig = { ...DEFAULT_ORDER };\n\n if (options.order && Array.isArray(options.order)) {\n options.order.forEach((category, index) => {\n const originalOrder = DEFAULT_ORDER[category];\n if (originalOrder !== undefined) {\n Object.keys(DEFAULT_ORDER).forEach((key) => {\n if (DEFAULT_ORDER[key] === originalOrder) {\n orderConfig[key] = index;\n }\n });\n }\n orderConfig[category] = index;\n });\n }\n\n return {\n Program(node: Program) {\n const statements: AnalyzedStatement[] = [];\n\n for (const statement of node.body) {\n const analyzed = analyzeStatement(statement, sourceCode, orderConfig);\n if (analyzed) {\n statements.push(analyzed);\n }\n }\n\n if (statements.length < 2) return;\n\n let firstError: {\n index: number;\n current: AnalyzedStatement;\n previous: AnalyzedStatement;\n } | null = null;\n\n for (let i = 1; i < statements.length; i++) {\n const prev = statements[i - 1];\n const curr = statements[i];\n\n if (curr.order < prev.order) {\n firstError = { index: i, current: curr, previous: prev };\n break;\n }\n }\n\n if (!firstError) return;\n\n context.report({\n node: firstError.current.node,\n messageId: 'wrongOrder',\n data: {\n current: firstError.current.name,\n currentCategory: ORDER_NAMES[firstError.current.order] || 'unknown',\n previous: firstError.previous.name,\n previousCategory: ORDER_NAMES[firstError.previous.order] || 'unknown',\n },\n fix(fixer) {\n const sorted = [...statements].sort((a, b) => {\n if (a.order !== b.order) {\n return a.order - b.order;\n }\n\n return statements.indexOf(a) - statements.indexOf(b);\n });\n\n const newCode = generateSortedCode(sorted, sourceCode, groupBlankLines);\n const firstRange = getFullRange(statements[0].node, sourceCode);\n const lastRange = getFullRange(statements[statements.length - 1].node, sourceCode);\n\n return fixer.replaceTextRange([firstRange[0], lastRange[1]], newCode + '\\n');\n },\n });\n },\n };\n },\n};\n\nexport default rule;\n","import orderRule from './rules/order';\n\nconst plugin = {\n meta: {\n name: 'eslint-plugin-vue-setup-order',\n version: '1.0.0',\n },\n rules: {\n order: orderRule,\n },\n configs: {\n recommended: {\n plugins: ['vue-setup-order'],\n rules: {\n 'vue-setup-order/order': 'error',\n },\n },\n // Flat config format for ESLint 9+\n flat: {\n recommended: {\n plugins: {\n get 'vue-setup-order'() {\n return plugin;\n },\n },\n rules: {\n 'vue-setup-order/order': 'error',\n },\n },\n },\n },\n};\n\nexport default plugin;\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "eslint-plugin-vue-setup-order",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ESLint plugin to enforce consistent order of statements in Vue 3 <script setup>",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"eslint",
|
|
7
|
+
"eslintplugin",
|
|
8
|
+
"eslint-plugin",
|
|
9
|
+
"vue",
|
|
10
|
+
"vue3",
|
|
11
|
+
"script-setup",
|
|
12
|
+
"order",
|
|
13
|
+
"formatting",
|
|
14
|
+
"code-style"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/d2a8k3u/eslint-plugin-vue-setup-order#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/d2a8k3u/eslint-plugin-vue-setup-order/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/d2a8k3u/eslint-plugin-vue-setup-order.git"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "David Kulhanek (https://github.com/d2a8k3u)",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"require": "./dist/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.cjs",
|
|
34
|
+
"module": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch",
|
|
42
|
+
"lint": "eslint src tests",
|
|
43
|
+
"lint:fix": "eslint src tests --fix",
|
|
44
|
+
"prepublishOnly": "npm run build && npm run test:run",
|
|
45
|
+
"release": "npm run build && changeset publish",
|
|
46
|
+
"format": "prettier --write .",
|
|
47
|
+
"format:check": "prettier --check .",
|
|
48
|
+
"test": "vitest",
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"test:run": "vitest run",
|
|
51
|
+
"typecheck": "tsc --noEmit"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
55
|
+
"@changesets/cli": "^2.29.8",
|
|
56
|
+
"@types/eslint": "^8.56.2",
|
|
57
|
+
"@types/node": "^20.11.5",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
|
59
|
+
"@typescript-eslint/parser": "^6.19.1",
|
|
60
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
61
|
+
"eslint": "^8.57.1",
|
|
62
|
+
"prettier": "^3.2.4",
|
|
63
|
+
"prettier-plugin-organize-imports": "^4.1.0",
|
|
64
|
+
"tsup": "^8.0.1",
|
|
65
|
+
"typescript": "~5.3.3",
|
|
66
|
+
"vitest": "^4.0.16"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"eslint": ">=8.0.0"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=18.0.0"
|
|
73
|
+
}
|
|
74
|
+
}
|