Skip to content

Commit

Permalink
fix: context provided by defineCustomBlocksVisitor (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
ota-meshi authored Oct 8, 2023
1 parent cbf1481 commit 7fd3735
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 53 deletions.
100 changes: 56 additions & 44 deletions src/sfc/custom-block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,26 +213,40 @@ export function createCustomBlockSharedContext({
parserOptions: any
}) {
let sourceCode: SourceCode
let scopeManager: ScopeManager
let currentNode: any
return {
serCurrentNode(node: any) {
currentNode = node
},
context: {
getAncestors: () => getAncestors(currentNode),

getAncestors: () => getSourceCode().getAncestors(currentNode),
getDeclaredVariables: (...args: any[]) =>
// @ts-expect-error -- ignore
getScopeManager().getDeclaredVariables(...args),
getScope: () => getScope(getScopeManager(), currentNode),
getSourceCode().getDeclaredVariables(...args),
getScope: () => getSourceCode().getScope(currentNode),
markVariableAsUsed: (name: string) =>
markVariableAsUsed(
getScopeManager(),
currentNode,
parserOptions,
name,
),
getSourceCode().markVariableAsUsed(name, currentNode),
get parserServices() {
return getSourceCode().parserServices
},
getSourceCode,
get sourceCode() {
return getSourceCode()
},
},
}

function getSourceCode(): SourceCode {
if (sourceCode) {
return sourceCode
}

const scopeManager = getScopeManager()

// eslint-disable-next-line @typescript-eslint/no-require-imports
const originalSourceCode = new (require("eslint").SourceCode)({
text,
ast: parsedResult.ast,
parserServices: {
customBlock,
parseCustomBlockElement(
Expand All @@ -251,44 +265,43 @@ export function createCustomBlockSharedContext({
? { parseError: parsedResult.error }
: {}),
},
getSourceCode,
get sourceCode() {
return getSourceCode()
},
},
}
scopeManager,
visitorKeys: parsedResult.visitorKeys,
})

function getSourceCode() {
return (
sourceCode ||
// eslint-disable-next-line @typescript-eslint/no-require-imports
(sourceCode = new (require("eslint").SourceCode)({
text,
ast: parsedResult.ast,
parserServices: parsedResult.services,
scopeManager: getScopeManager(),
visitorKeys: parsedResult.visitorKeys,
}))
)
const polyfills = {
markVariableAsUsed: (name: string, node: any) =>
markVariableAsUsed(scopeManager, node, parsedResult.ast, name),
getScope: (node: any) => getScope(scopeManager, node),
getAncestors: (node: any) => getAncestors(node),
getDeclaredVariables: (...args: any[]) =>
// @ts-expect-error -- ignore
scopeManager.getDeclaredVariables(...args),
}

return (sourceCode = new Proxy(originalSourceCode, {
get(_target, prop) {
return originalSourceCode[prop] || (polyfills as any)[prop]
},
}))
}

function getScopeManager() {
if (parsedResult.scopeManager || scopeManager) {
return parsedResult.scopeManager || scopeManager
if (parsedResult.scopeManager) {
return parsedResult.scopeManager
}

const ecmaVersion = getEcmaVersionIfUseEspree(parserOptions) || 2022
const ecmaFeatures = parserOptions.ecmaFeatures || {}
const sourceType = parserOptions.sourceType || "script"
scopeManager = getEslintScope().analyze(parsedResult.ast, {
return getEslintScope().analyze(parsedResult.ast, {
ignoreEval: true,
nodejsScope: false,
impliedStrict: ecmaFeatures.impliedStrict,
ecmaVersion,
sourceType,
fallback: getFallbackKeys,
})
return scopeManager
}
}

Expand Down Expand Up @@ -349,20 +362,19 @@ function getScope(scopeManager: ScopeManager, currentNode: Node) {
function markVariableAsUsed(
scopeManager: ScopeManager,
currentNode: Node,
parserOptions: any,
program: Node,
name: string,
) {
const hasGlobalReturn =
parserOptions.ecmaFeatures && parserOptions.ecmaFeatures.globalReturn
const specialScope =
hasGlobalReturn || parserOptions.sourceType === "module"
const currentScope = getScope(scopeManager, currentNode)

// Special Node.js scope means we need to start one level deeper
const initialScope =
currentScope.type === "global" && specialScope
? currentScope.childScopes[0]
: currentScope
let initialScope = currentScope
if (
currentScope.type === "global" &&
currentScope.childScopes.length > 0 &&
// top-level scopes refer to a `Program` node
currentScope.childScopes[0].block === program
) {
initialScope = currentScope.childScopes[0]
}

for (let scope: Scope | null = initialScope; scope; scope = scope.upper) {
const variable = scope.variables.find(
Expand Down
58 changes: 49 additions & 9 deletions test/define-custom-blocks-visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const LINTER_CONFIG = {
"test-no-number-literal": "error",
"test-no-forbidden-key": "error",
"test-no-parsing-error": "error",
"test-no-parsing-error2": "error",
},
}
const noNumberLiteralRule = {
Expand Down Expand Up @@ -61,6 +62,30 @@ const noNoForbiddenKeyRule = {
},
}
const noParsingErrorRule = {
create(context) {
const parseError = context.getSourceCode().parserServices.parseError
if (parseError) {
let loc = undefined
if ("column" in parseError && "lineNumber" in parseError) {
loc = {
line: parseError.lineNumber,
column: parseError.column,
}
}
return {
Program(node) {
context.report({
node,
loc,
message: parseError.message,
})
},
}
}
return {}
},
}
const noParsingErrorRule2 = {
create(context) {
const parseError = context.parserServices.parseError
if (parseError) {
Expand Down Expand Up @@ -131,10 +156,10 @@ function createLinter(target = "json") {
...noParsingErrorRule,
}),
)
linter.defineRule("test-no-parsing-error", (context) =>
linter.defineRule("test-no-parsing-error2", (context) =>
context.parserServices.defineCustomBlocksVisitor(context, jsonParser, {
target,
...noParsingErrorRule,
...noParsingErrorRule2,
}),
)
linter.defineRule("test-no-program-exit", (context) =>
Expand Down Expand Up @@ -297,22 +322,37 @@ describe("parserServices.defineCustomBlocksVisitor tests", () => {

const messages = linter.verify(code, LINTER_CONFIG)

assert.strictEqual(messages.length, 3)
assert.strictEqual(messages.length, 6)
assert.strictEqual(messages[0].message, "Unexpected token ':'.")
assert.strictEqual(messages[0].line, 3)
assert.strictEqual(messages[0].column, 6)
assert.strictEqual(messages[1].message, "Unexpected token ':'.")
assert.strictEqual(messages[1].line, 3)
assert.strictEqual(messages[1].column, 6)
assert.strictEqual(
messages[1].message,
messages[2].message,
"Expected to be an expression, but got empty.",
)
assert.strictEqual(messages[1].line, 5)
assert.strictEqual(messages[1].column, 19)
assert.strictEqual(messages[2].line, 5)
assert.strictEqual(messages[2].column, 19)
assert.strictEqual(
messages[2].message,
messages[3].message,
"Expected to be an expression, but got empty.",
)
assert.strictEqual(messages[2].line, 6)
assert.strictEqual(messages[2].column, 19)
assert.strictEqual(messages[3].line, 5)
assert.strictEqual(messages[3].column, 19)
assert.strictEqual(
messages[4].message,
"Expected to be an expression, but got empty.",
)
assert.strictEqual(messages[4].line, 6)
assert.strictEqual(messages[4].column, 19)
assert.strictEqual(
messages[5].message,
"Expected to be an expression, but got empty.",
)
assert.strictEqual(messages[5].line, 6)
assert.strictEqual(messages[5].column, 19)
})

it("should work even if error.", () => {
Expand Down

0 comments on commit 7fd3735

Please sign in to comment.