Skip to content

Commit

Permalink
disallow composables after await
Browse files Browse the repository at this point in the history
  • Loading branch information
ktsn committed Jun 13, 2023
1 parent df52bc6 commit f1c96d3
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 11 deletions.
52 changes: 42 additions & 10 deletions src/rules/composable-placement.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Rule } from 'eslint'
import * as ESTree from 'estree'
import assert from 'assert'
import { AST } from 'vue-eslint-parser'

const composableNameRE = /^use[A-Z0-9]/
Expand Down Expand Up @@ -86,21 +87,52 @@ export default {
)
}

const functionStack: { node: Rule.Node; afterAwait: boolean }[] = []

return {
':function'(node: Rule.Node) {
functionStack.push({
node,
afterAwait: false,
})
},

':function:exit'(node: Rule.Node) {
const last = functionStack[functionStack.length - 1]
assert(last?.node === node)
functionStack.pop()
},

AwaitExpression() {
if (functionStack.length > 0) {
functionStack[functionStack.length - 1]!.afterAwait = true
}
},

CallExpression(node) {
if (getCalleeName(node.callee)?.match(composableNameRE)) {
const scope = context.sourceCode.getScope(node)
const block = scope.block as Rule.Node
if (!getCalleeName(node.callee)?.match(composableNameRE)) {
return
}

const { afterAwait } = functionStack[functionStack.length - 1] ?? {
afterAwait: false,
}
if (afterAwait) {
context.report({
node,
message: 'Composable function must not be placed after await.',
})
}

const isComposableScope = isComposableFunction(block)
const isSetupScope = isSetupOption(block)
const isScriptSetupRoot =
inScriptSetup(node) && block.type === 'Program'
const scope = context.sourceCode.getScope(node)
const block = scope.block as Rule.Node

if (isComposableScope || isSetupScope || isScriptSetupRoot) {
return
}
const isComposableScope = isComposableFunction(block)
const isSetupScope = isSetupOption(block)
const isScriptSetupRoot =
inScriptSetup(node) && block.type === 'Program'

if (!isComposableScope && !isSetupScope && !isScriptSetupRoot) {
context.report({
node,
message:
Expand Down
48 changes: 48 additions & 0 deletions test/composable-placement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ describe('vue-composable/composable-placement', () => {
}
`,
},
{
code: `
import { useFoo } from './foo'
export async function useBar() {
useFoo()
await fetch()
}
`,
},
{
code: `
import { defineComponent } from 'vue'
Expand All @@ -60,6 +70,19 @@ describe('vue-composable/composable-placement', () => {
})
`,
},
{
code: `
import { defineComponent } from 'vue'
import { useFoo } from './foo'
export default defineComponent({
async setup() {
useFoo()
await fetch()
}
})
`,
},
{
code: `
import { useFoo } from './foo'
Expand Down Expand Up @@ -117,6 +140,17 @@ describe('vue-composable/composable-placement', () => {
code: `
import { useFoo } from './foo'
export async function useBar() {
await fetch()
useFoo()
}
`,
errors: [{}],
},
{
code: `
import { useFoo } from './foo'
export function useBar() {
function baz() {
useFoo()
Expand All @@ -138,6 +172,20 @@ describe('vue-composable/composable-placement', () => {
`,
errors: [{}],
},
{
code: `
import { defineComponent } from 'vue'
import { useFoo } from './foo'
export default defineComponent({
async setup() {
await fetch()
useFoo()
}
})
`,
errors: [{}],
},
],
})

Expand Down
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "node",
"strict": true
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}

0 comments on commit f1c96d3

Please sign in to comment.