From 01c813863b970630eb6d5ef3753cc5997e497089 Mon Sep 17 00:00:00 2001
From: ota-meshi <otameshiyo23@gmail.com>
Date: Fri, 12 May 2023 09:39:45 +0900
Subject: [PATCH 1/3] Make to use `project: undefined` when parsing template
 script-let.

---
 src/html/parser.ts                 |   1 +
 src/script-setup/index.ts          |  12 ++-
 src/script-setup/parser-options.ts |   2 +-
 test/parser-options-project.js     | 129 +++++++++++++++++++++++++++++
 4 files changed, 136 insertions(+), 8 deletions(-)
 create mode 100644 test/parser-options-project.js

diff --git a/src/html/parser.ts b/src/html/parser.ts
index fada1477..baccda4c 100644
--- a/src/html/parser.ts
+++ b/src/html/parser.ts
@@ -302,6 +302,7 @@ export class Parser {
                     yield getParserLangFromSFC(doc)
                 },
             ),
+            project: undefined,
         }
         const scriptParserOptions = {
             ...this.baseParserOptions,
diff --git a/src/script-setup/index.ts b/src/script-setup/index.ts
index d5c3701a..d620cb98 100644
--- a/src/script-setup/index.ts
+++ b/src/script-setup/index.ts
@@ -516,14 +516,12 @@ function getScriptSetupCodeBlocks(
     const offsetLocationCalculator =
         linesAndColumns.createOffsetLocationCalculator(scriptSetupStartOffset)
 
-    const result = parseScript(
+    const { ast, visitorKeys } = parseScript(
         scriptCode,
-        parserOptions,
+        { ...parserOptions, project: undefined },
         offsetLocationCalculator,
     )
 
-    const { ast } = result
-
     // Holds the `import` and re-`export` statements.
     // All import and re-`export` statements are hoisted to the top.
     const importCodeBlocks = new CodeBlocks()
@@ -597,7 +595,7 @@ function getScriptSetupCodeBlocks(
                         }
                         fixNodeLocations(
                             body,
-                            result.visitorKeys,
+                            visitorKeys,
                             offsetLocationCalculator,
                         )
                         fixLocation(exportToken, offsetLocationCalculator)
@@ -695,7 +693,7 @@ function getScriptSetupCodeBlocks(
                         // restore
                         fixNodeLocations(
                             body,
-                            result.visitorKeys,
+                            visitorKeys,
                             offsetLocationCalculator,
                         )
                         for (const token of restoreTokens) {
@@ -826,7 +824,7 @@ function getScriptSetupCodeBlocks(
         let start = n.range[0]
         let end = n.range[1]
         traverseNodes(n, {
-            visitorKeys: result.visitorKeys,
+            visitorKeys,
             enterNode(c) {
                 start = Math.min(start, c.range[0])
                 end = Math.max(end, c.range[1])
diff --git a/src/script-setup/parser-options.ts b/src/script-setup/parser-options.ts
index 0a3b6865..183b20a4 100644
--- a/src/script-setup/parser-options.ts
+++ b/src/script-setup/parser-options.ts
@@ -16,7 +16,7 @@ export function getScriptSetupParserOptions(
 
     return {
         ...parserOptions,
-        ecmaVersion: espreeEcmaVersion,
+        ecmaVersion: espreeEcmaVersion || parserOptions.ecmaVersion,
     }
 }
 
diff --git a/test/parser-options-project.js b/test/parser-options-project.js
new file mode 100644
index 00000000..cc7dbbb7
--- /dev/null
+++ b/test/parser-options-project.js
@@ -0,0 +1,129 @@
+"use strict"
+
+const assert = require("assert")
+const { parseForESLint } = require("../src")
+const espree = require("espree")
+
+describe("use `project: undefined` when parsing template script-let", () => {
+    it("should be the project option is defined only once in Simple SFC.", () => {
+        let projectCount = 0
+        parseForESLint(
+            `<template>
+                <div v-bind:class="{}">
+                    <template v-for="item in items">
+                        {{ 'str' }}
+                        <button v-on:click="handler()"></button>
+                    </template>
+                    <MyComponent>
+                        <template v-slot="{a}">
+                            <div v-if="a">A</div>
+                        </template>
+                    </MyComponent>
+                </div>
+            </template>
+            <script>
+            export default {}
+            </script>
+            `,
+            {
+                project: true,
+                sourceType: "module",
+                ecmaVersion: 2018,
+                parser: {
+                    parseForESLint(code, options) {
+                        if (options.project) {
+                            projectCount++
+                        }
+
+                        return {
+                            ast: espree.parse(code, options),
+                        }
+                    },
+                },
+            },
+        )
+        assert.strictEqual(projectCount, 1)
+    })
+    it("should be the project option is defined only once in <script setup>.", () => {
+        let projectCount = 0
+        parseForESLint(
+            `<script setup>
+            let items = ["foo"]
+            </script>
+            <template>
+                <div v-bind:class="{}">
+                    <template v-for="item in items">
+                        {{ 'str' }}
+                        <button v-on:click="handler()"></button>
+                    </template>
+                    <MyComponent>
+                        <template v-slot="{a}">
+                            <div v-if="a">A</div>
+                        </template>
+                    </MyComponent>
+                </div>
+            </template>
+            `,
+            {
+                project: true,
+                sourceType: "module",
+                ecmaVersion: 2018,
+                parser: {
+                    parseForESLint(code, options) {
+                        if (options.project) {
+                            projectCount++
+                        }
+
+                        return {
+                            ast: espree.parse(code, options),
+                        }
+                    },
+                },
+            },
+        )
+        assert.strictEqual(projectCount, 1)
+    })
+
+    it("should be the project option is defined only once in <script setup> with <script>.", () => {
+        let projectCount = 0
+        parseForESLint(
+            `<script>
+            import { ref } from 'vue'
+            </script>
+            <script setup>
+            let items = ref(["foo"])
+            </script>
+            <template>
+                <div v-bind:class="{}">
+                    <template v-for="item in items">
+                        {{ 'str' }}
+                        <button v-on:click="handler()"></button>
+                    </template>
+                    <MyComponent>
+                        <template v-slot="{a}">
+                            <div v-if="a">A</div>
+                        </template>
+                    </MyComponent>
+                </div>
+            </template>
+            `,
+            {
+                project: true,
+                sourceType: "module",
+                ecmaVersion: 2018,
+                parser: {
+                    parseForESLint(code, options) {
+                        if (options.project) {
+                            projectCount++
+                        }
+
+                        return {
+                            ast: espree.parse(code, options),
+                        }
+                    },
+                },
+            },
+        )
+        assert.strictEqual(projectCount, 1)
+    })
+})

From 78db8a80d611e515588f82fe27f4528e637fde21 Mon Sep 17 00:00:00 2001
From: ota-meshi <otameshiyo23@gmail.com>
Date: Fri, 12 May 2023 09:48:06 +0900
Subject: [PATCH 2/3] fix for css v-bind

---
 src/index.ts                   | 1 +
 test/parser-options-project.js | 5 +++++
 2 files changed, 6 insertions(+)

diff --git a/src/index.ts b/src/index.ts
index 33eb1c31..703e44aa 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -165,6 +165,7 @@ function parseAsSFC(code: string, options: ParserOptions) {
                 yield "<template>"
                 yield getParserLangFromSFC(rootAST)
             }),
+            project: undefined,
         })
     }
     result.ast.templateBody = templateBody
diff --git a/test/parser-options-project.js b/test/parser-options-project.js
index cc7dbbb7..398835db 100644
--- a/test/parser-options-project.js
+++ b/test/parser-options-project.js
@@ -63,6 +63,11 @@ describe("use `project: undefined` when parsing template script-let", () => {
                     </MyComponent>
                 </div>
             </template>
+            <style scoped>
+            .a {
+                color: v-bind(color)
+            }
+            </style>
             `,
             {
                 project: true,

From 6353982af997e825265af32f4063743ec8513049 Mon Sep 17 00:00:00 2001
From: yosuke ota <otameshiyo23@gmail.com>
Date: Wed, 29 Jan 2025 13:26:08 +0900
Subject: [PATCH 3/3] add `projectService: undefined`

---
 src/common/parser-options.ts | 8 ++++++++
 src/html/parser.ts           | 1 +
 src/index.ts                 | 1 +
 src/script-setup/index.ts    | 2 +-
 src/script/index.ts          | 2 +-
 5 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/common/parser-options.ts b/src/common/parser-options.ts
index d28504e8..25656ab5 100644
--- a/src/common/parser-options.ts
+++ b/src/common/parser-options.ts
@@ -30,6 +30,7 @@ export interface ParserOptions {
     lib?: string[]
 
     project?: string | string[]
+    projectService?: boolean | ProjectServiceOptions
     projectFolderIgnoreList?: string[]
     tsconfigRootDir?: string
     extraFileExtensions?: string[]
@@ -55,6 +56,13 @@ export interface ParserOptions {
     >
 }
 
+interface ProjectServiceOptions {
+    allowDefaultProject?: string[]
+    defaultProject?: string
+    loadTypeScriptPlugins?: boolean
+    maximumDefaultProjectFileMatchCount_THIS_WILL_SLOW_DOWN_LINTING?: number
+}
+
 export function isSFCFile(parserOptions: ParserOptions) {
     if (parserOptions.filePath === "<input>") {
         return true
diff --git a/src/html/parser.ts b/src/html/parser.ts
index baccda4c..367eecc4 100644
--- a/src/html/parser.ts
+++ b/src/html/parser.ts
@@ -303,6 +303,7 @@ export class Parser {
                 },
             ),
             project: undefined,
+            projectService: undefined,
         }
         const scriptParserOptions = {
             ...this.baseParserOptions,
diff --git a/src/index.ts b/src/index.ts
index 703e44aa..3b45a182 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -166,6 +166,7 @@ function parseAsSFC(code: string, options: ParserOptions) {
                 yield getParserLangFromSFC(rootAST)
             }),
             project: undefined,
+            projectService: undefined,
         })
     }
     result.ast.templateBody = templateBody
diff --git a/src/script-setup/index.ts b/src/script-setup/index.ts
index d620cb98..d336b0f0 100644
--- a/src/script-setup/index.ts
+++ b/src/script-setup/index.ts
@@ -518,7 +518,7 @@ function getScriptSetupCodeBlocks(
 
     const { ast, visitorKeys } = parseScript(
         scriptCode,
-        { ...parserOptions, project: undefined },
+        { ...parserOptions, project: undefined, projectService: undefined },
         offsetLocationCalculator,
     )
 
diff --git a/src/script/index.ts b/src/script/index.ts
index b8531d30..73c28109 100644
--- a/src/script/index.ts
+++ b/src/script/index.ts
@@ -1302,7 +1302,7 @@ export function parseGenericExpression(
         const result = parseScriptFragmentWithOption(
             scriptLet,
             locationCalculator.getSubCalculatorShift(-14),
-            { ...parserOptions, project: undefined },
+            { ...parserOptions, project: undefined, projectService: undefined },
             {
                 preFixLocationProcess(preResult) {
                     const params = getParams(preResult)