eslint-plugin-import-x
eslint-plugin-import-x copied to clipboard
Vue SFC and imports are not gracefully handled per-block when using `script` and `script setup`
Given the following code:
<script lang="ts">
console.log('a')
import 'vue'
console.log('b')
</script>
<script setup lang="ts">
console.log('c')
import 'vue'
console.log('d')
</script>
The import rules fail to handle both of the script blocks as distinct scripts. Specifically in this case, import-x/first will not produce the expected results and attempt to group all imports under a same script block.
I did a hacky patch via pnpm to overcome this issue in the project I'm using, I'm not sure it's the "proper" way but it did the trick just fine in my case. I've provided the patch below.
I'm afraid I don't have enough time to clean/polish/test the patch and file it as a PR myself, sorry. I hope it'll be useful nonetheless! 😅
eslint-plugin-import-x.patch
diff --git a/lib/rules/first.js b/lib/rules/first.js
index 330f6612d409e7f2cc3f0fe86d3cb4b9ae7f2c7a..0e08cc0d13d026e17e528fb43e1942bfe2c97ed1 100644
--- a/lib/rules/first.js
+++ b/lib/rules/first.js
@@ -35,12 +35,42 @@ module.exports = (0, utils_1.createRule)({
},
defaultOptions: [],
create(context) {
+ // [Cynthia] Begin patch: Vue support
+ const __cyn_isVue = context.filename.endsWith('.vue')
+ // [Cynthia] End patch: Vue support
+
return {
Program(n) {
- const body = n.body;
- if (!body?.length) {
- return;
+ // [Cynthia] Begin patch: Vue support
+ if (!n.body?.length) return
+
+ const __cyn_bodies = []
+ if (__cyn_isVue) {
+ const __cyn_docFrag = context.sourceCode.parserServices.getDocumentFragment()
+ const __cyn_scriptRanges = __cyn_docFrag.children
+ .filter((c) => c.type === 'VElement' && c.name === 'script')
+ .map((c) => c.range)
+
+ for (const __cyn_range of __cyn_scriptRanges) {
+ const __cyn_body = []
+ for (const __cyn_node of n.body) {
+ if (__cyn_node.range[0] > __cyn_range[0] && __cyn_node.range[1] < __cyn_range[1]) {
+ __cyn_body.push(__cyn_node)
+ }
+ }
+
+ if (__cyn_body.length) {
+ __cyn_bodies.push(__cyn_body)
+ }
+ }
+ } else {
+ __cyn_bodies.push(n.body)
}
+
+ for (const body of __cyn_bodies) {
+ ;(() => { // IIFE to void undesired `return` and make them behave as `continue`
+ // [Cynthia] End patch: Vue support
+
const absoluteFirst = context.options[0] === 'absolute-first';
const { sourceCode } = context;
const originSourceCode = sourceCode.getText();
@@ -147,6 +177,11 @@ module.exports = (0, utils_1.createRule)({
fix,
});
}
+
+ // [Cynthia] Begin patch: Vue support
+ })();
+ }
+ // [Cynthia] End patch: Vue support
},
};
},