From d35a9dccc1bf7fde6d22b9609c2b7d12fc495602 Mon Sep 17 00:00:00 2001
From: Triumph-light <2495685883@qq.com>
Date: Tue, 12 May 2026 19:09:39 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=87=AA?=
=?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6props=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=94=AF=E6=8C=81,=20close:=20https://github?=
=?UTF-8?q?.com/mpx-ecology/language-tools/issues/86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../component-js-setup.mpx | 7 ++
.../component-options.mpx | 21 ++++++
.../components/reference-type-props/index.mpx | 21 ++++++
.../script-json/components/list.mpx | 6 ++
.../globalTypes/defineComponentTypes.ts | 22 ++++++
.../src/codegen/globalTypes/index.ts | 1 +
.../src/codegen/script/componentSelf.ts | 27 +++++--
.../language-core/src/codegen/script/index.ts | 15 ++--
.../src/codegen/script/jsonUsingComponents.ts | 70 +++++++++++--------
.../src/codegen/script/template.ts | 2 +-
.../src/codegen/template/element.ts | 65 +++++++++--------
.../src/codegen/template/elementProps.ts | 2 +-
.../src/codegen/template/templateChild.ts | 1 -
packages/language-core/src/plugins/mpx-tsx.ts | 9 ++-
14 files changed, 193 insertions(+), 76 deletions(-)
create mode 100644 inspect-extension/components/reference-type-props/component-js-setup.mpx
create mode 100644 inspect-extension/components/reference-type-props/component-options.mpx
create mode 100644 inspect-extension/components/reference-type-props/index.mpx
diff --git a/inspect-extension/components/reference-type-props/component-js-setup.mpx b/inspect-extension/components/reference-type-props/component-js-setup.mpx
new file mode 100644
index 0000000..4a77f41
--- /dev/null
+++ b/inspect-extension/components/reference-type-props/component-js-setup.mpx
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/inspect-extension/components/reference-type-props/component-options.mpx b/inspect-extension/components/reference-type-props/component-options.mpx
new file mode 100644
index 0000000..62c59c9
--- /dev/null
+++ b/inspect-extension/components/reference-type-props/component-options.mpx
@@ -0,0 +1,21 @@
+
+
+
\ No newline at end of file
diff --git a/inspect-extension/components/reference-type-props/index.mpx b/inspect-extension/components/reference-type-props/index.mpx
new file mode 100644
index 0000000..8b3ceab
--- /dev/null
+++ b/inspect-extension/components/reference-type-props/index.mpx
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/inspect-extension/components/script-json/components/list.mpx b/inspect-extension/components/script-json/components/list.mpx
index c40919b..5128e83 100644
--- a/inspect-extension/components/script-json/components/list.mpx
+++ b/inspect-extension/components/script-json/components/list.mpx
@@ -13,6 +13,12 @@
import { createComponent } from '@mpxjs/core'
createComponent({
+ properties:{
+ parentText: {
+ type: String,
+ value: 'parent text',
+ }
+ },
data: {
listData: ['手机', '电视', '电脑'],
},
diff --git a/packages/language-core/src/codegen/globalTypes/defineComponentTypes.ts b/packages/language-core/src/codegen/globalTypes/defineComponentTypes.ts
index e5405b1..f979de2 100644
--- a/packages/language-core/src/codegen/globalTypes/defineComponentTypes.ts
+++ b/packages/language-core/src/codegen/globalTypes/defineComponentTypes.ts
@@ -37,6 +37,27 @@ const globalTypes = () => `
: {}
`
+const runtimePropTypes = () => `
+type __VLS_ResolveProp = T extends { type: StringConstructor }
+ ? string
+ : T extends { type: NumberConstructor }
+ ? number
+ : T extends { type: BooleanConstructor }
+ ? boolean
+ : T extends { type: ArrayConstructor }
+ ? any[]
+ : T extends { type: ObjectConstructor }
+ ? Record
+ : T extends { type: import('@mpxjs/core').PropType }
+ ? V
+ : T extends import('@mpxjs/core').PropType
+ ? V
+ : unknown
+type __VLS_GetPropsType> = Partial<{
+ readonly [K in keyof T]: __VLS_ResolveProp
+}>
+`
+
const localTypes = (lib: MpxCompilerOptions['lib']) => `
// #region DefineComponent - local types
type Data = object | (() => object)
@@ -190,4 +211,5 @@ interface ReplaceWxComponentIns {
export const defineComponentTypesContents = {
globalTypes,
localTypes,
+ runtimePropTypes,
}
diff --git a/packages/language-core/src/codegen/globalTypes/index.ts b/packages/language-core/src/codegen/globalTypes/index.ts
index c02a715..8918d21 100644
--- a/packages/language-core/src/codegen/globalTypes/index.ts
+++ b/packages/language-core/src/codegen/globalTypes/index.ts
@@ -167,6 +167,7 @@ export function generateGlobalTypes({
: T[K]
}
${defineComponentTypesContents.globalTypes()}
+ ${defineComponentTypesContents.runtimePropTypes()}
}
` + defineComponentTypesContents.localTypes(lib)
diff --git a/packages/language-core/src/codegen/script/componentSelf.ts b/packages/language-core/src/codegen/script/componentSelf.ts
index 170fc4f..1f4b763 100644
--- a/packages/language-core/src/codegen/script/componentSelf.ts
+++ b/packages/language-core/src/codegen/script/componentSelf.ts
@@ -1,18 +1,37 @@
import type { Code } from '../../types'
import type { ScriptCodegenOptions } from './index'
-import { endOfLine } from '../utils'
+import { endOfLine, newLine } from '../utils'
+import { getSlotsPropertyName } from '../../utils/shared'
export function* generateComponentSelf(
options: ScriptCodegenOptions,
): Generator {
if (options.sfc.scriptSetup) {
+ yield `type __VLS_SelfBase = `
if (options.scriptSetupRanges?.defineExpose) {
- yield `const __VLS_self = typeof __VLS_defineExpose${endOfLine}`
+ yield `typeof __VLS_defineExpose`
} else {
- yield `const __VLS_self = {}${endOfLine}`
+ yield `{}`
}
+ yield endOfLine
+ yield `type __VLS_SelfComponent = __VLS_SelfBase & {${newLine}`
+ yield ` new (props: __VLS_PublicProps): __VLS_SelfBase & {${newLine}`
+ yield ` $props: __VLS_PublicProps${endOfLine}`
+ yield ` ${getSlotsPropertyName()}: __VLS_Slots${endOfLine}`
+ yield ` }${newLine}`
+ yield `}${endOfLine}`
+ yield `const __VLS_self = {} as __VLS_SelfComponent${endOfLine}`
} else {
- yield `const __VLS_self = typeof __VLS_defineComponent${endOfLine}`
+ yield `type __VLS_SelfProps = __VLS_GetPropsType<${newLine}`
+ yield ` NonNullable${newLine}`
+ yield `>${endOfLine}`
+ yield `type __VLS_SelfComponent = typeof __VLS_defineComponent & {${newLine}`
+ yield ` new (props: __VLS_SelfProps): typeof __VLS_defineComponent & {${newLine}`
+ yield ` $props: __VLS_SelfProps${endOfLine}`
+ yield ` ${getSlotsPropertyName()}: __VLS_Slots${endOfLine}`
+ yield ` }${newLine}`
+ yield `}${endOfLine}`
+ yield `const __VLS_self = {} as __VLS_SelfComponent${endOfLine}`
}
yield `export default __VLS_self${endOfLine}`
}
diff --git a/packages/language-core/src/codegen/script/index.ts b/packages/language-core/src/codegen/script/index.ts
index 1b1a196..41ef9b9 100644
--- a/packages/language-core/src/codegen/script/index.ts
+++ b/packages/language-core/src/codegen/script/index.ts
@@ -9,7 +9,10 @@ import { generateSrc } from './src'
import { generateTemplate } from './template'
import { codeFeatures } from '../codeFeatures'
import { generateComponentSelf } from './componentSelf'
-import { generateJsonPathCompletionImports } from './jsonUsingComponents'
+import {
+ generateJsonPathCompletionImports,
+ generateJsonUsingComponents,
+} from './jsonUsingComponents'
import {
endOfLine,
generateDefineComponent,
@@ -19,7 +22,6 @@ import {
import { generateGlobalTypes, getGlobalTypesFileName } from '../globalTypes'
import { ScriptCodegenContext, createScriptCodegenContext } from './context'
import { generateScriptSetup, generateScriptSetupImports } from './scriptSetup'
-// import { generateJsonUsingComponents } from './jsonUsingComponents'
export interface ScriptCodegenOptions {
ts: typeof ts
@@ -30,6 +32,7 @@ export interface ScriptCodegenOptions {
lang: string
scriptRanges: ScriptRanges | undefined
scriptSetupRanges: ScriptSetupRanges | undefined
+ scriptSetupImportComponentNames: Set
templateCodegen: (TemplateCodegenContext & { codes: Code[] }) | undefined
destructuredPropNames: Set
templateRefNames: Set
@@ -79,6 +82,10 @@ export function* generateScript(
options.scriptSetupRanges,
)
}
+ // Keep JSON path completion imports near the real import section so the
+ // generated script layout stays close to vue-language-tools.
+ yield* generateJsonPathCompletionImports(options, ctx)
+ yield* generateJsonUsingComponents(options, ctx)
if (options.sfc.script && options.scriptRanges) {
const { createComponentObj } = options.scriptRanges
const isCreateComponentRawObject =
@@ -177,12 +184,8 @@ export function* generateScript(
if (!ctx.generatedTemplate) {
yield* generateTemplate(options, ctx)
yield* generateComponentSelf(options)
- // yield* generateJsonUsingComponents(options, ctx)
}
- // 添加虚拟路径补全导入代码
- yield* generateJsonPathCompletionImports(options, ctx)
-
yield* ctx.localTypes.generate([...ctx.localTypes.getUsedNames()])
if (options.appendGlobalTypes) {
yield generateGlobalTypes(options.mpxCompilerOptions)
diff --git a/packages/language-core/src/codegen/script/jsonUsingComponents.ts b/packages/language-core/src/codegen/script/jsonUsingComponents.ts
index d8e2262..c3580a1 100644
--- a/packages/language-core/src/codegen/script/jsonUsingComponents.ts
+++ b/packages/language-core/src/codegen/script/jsonUsingComponents.ts
@@ -1,8 +1,9 @@
import type { Code } from '../../types'
import type { ScriptCodegenOptions } from './index'
import type { ScriptCodegenContext } from './context'
+import { camelize, capitalize } from '@mpxjs/language-shared'
import { endOfLine, newLine } from '../utils'
-import { codeFeatures } from '../codeFeatures'
+import { identifierRegex } from '../utils'
export function* generateJsonUsingComponents(
options: ScriptCodegenOptions,
@@ -11,34 +12,32 @@ export function* generateJsonUsingComponents(
const usingComponents = options.sfc.json?.usingComponents
if (!usingComponents?.size) {
+ yield `const __MPX_jsonComponents = {}${endOfLine}`
return
}
- yield `type __MPX_jsonComponents = {${newLine}`
+ yield `const __MPX_jsonComponents = {${newLine}`
for (const [componentName, componentPaths] of usingComponents) {
- for (const {
- text: componentPath,
- offset: componentPathOffset,
- // nameOffset: componentNameOffset,
- } of componentPaths) {
- yield `${componentName}: typeof import('`
-
- yield [
- componentPath,
- 'scriptSetup',
- componentPathOffset,
- codeFeatures.all,
- ]
+ const firstImportName = getUsingComponentImportName(componentName, 0)
+ yield `${firstImportName}`
+ if (componentPaths.length > 1) {
+ // Multiple resolved paths still need a single runtime value, so keep the
+ // first import as the value and widen its type to all candidate imports.
+ yield ` as `
+ for (let i = 0; i < componentPaths.length; i++) {
+ if (i) {
+ yield ` | `
+ }
+ yield `typeof ${getUsingComponentImportName(componentName, i)}`
+ }
}
-
- yield `)'${newLine}`
+ yield `,${newLine}`
}
yield `}${endOfLine}`
}
-// 新增函数:生成用于路径补全的虚拟 import 代码
export function* generateJsonPathCompletionImports(
options: ScriptCodegenOptions,
_ctx: ScriptCodegenContext,
@@ -52,20 +51,21 @@ export function* generateJsonPathCompletionImports(
// 为 usingComponents 生成虚拟 import
if (usingComponents?.size) {
- let index = 0
- for (const [, componentPaths] of usingComponents) {
- for (const {
- text: componentPath,
- offset: componentPathOffset,
- } of componentPaths) {
- // 生成虚拟 import 语句,带特殊标记以便识别
- yield `import __mpx_path_completion_${index++} from '`
+ for (const [componentName, componentPaths] of usingComponents) {
+ for (let i = 0; i < componentPaths.length; i++) {
+ const componentPathInfo = componentPaths[i]!
+ const importName = getUsingComponentImportName(componentName, i)
+
+ // Generate a virtual import with a stable identifier so the generated
+ // component map reads like normal source imports instead of index-based
+ // placeholders.
+ yield `import ${importName} from '`
// 传递原始路径及位置信息
yield [
- componentPath,
+ componentPathInfo.text,
'json_import',
- jsonStart + componentPathOffset,
+ jsonStart + componentPathInfo.offset,
{
// 仅启用补全功能,不参与语义分析等
completion: true,
@@ -91,7 +91,7 @@ export function* generateJsonPathCompletionImports(
yield [
page.text,
'json_import',
- page.offset,
+ jsonStart + page.offset,
{
completion: true,
navigation: true,
@@ -108,3 +108,15 @@ export function* generateJsonPathCompletionImports(
yield `${newLine}`
}
+
+export function getUsingComponentImportName(
+ componentName: string,
+ index: number,
+) {
+ const camelizedName = capitalize(camelize(componentName))
+ const baseName =
+ camelizedName && identifierRegex.test(camelizedName)
+ ? camelizedName
+ : `component_${index}`
+ return `${baseName}`
+}
diff --git a/packages/language-core/src/codegen/script/template.ts b/packages/language-core/src/codegen/script/template.ts
index f6b1b6c..6eb9137 100644
--- a/packages/language-core/src/codegen/script/template.ts
+++ b/packages/language-core/src/codegen/script/template.ts
@@ -65,7 +65,7 @@ function* generateTemplateElements(): Generator {
}
function* generateTemplateComponents(): Generator {
- const types: Code[] = [`typeof __MPX_ctx`]
+ const types: Code[] = [`typeof __MPX_ctx`, `typeof __MPX_jsonComponents`]
yield `type __VLS_LocalComponents =`
for (const type of types) {
diff --git a/packages/language-core/src/codegen/template/element.ts b/packages/language-core/src/codegen/template/element.ts
index babfdb1..9461d1b 100644
--- a/packages/language-core/src/codegen/template/element.ts
+++ b/packages/language-core/src/codegen/template/element.ts
@@ -4,7 +4,7 @@ import type { Code, MpxCodeInformation } from '../../types'
import * as CompilerDOM from '@vue/compiler-dom'
import { camelize, capitalize } from '@mpxjs/language-shared'
-import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared'
+import { hyphenateTag } from '../../utils/shared'
import { codeFeatures } from '../codeFeatures'
import {
endOfLine,
@@ -23,6 +23,7 @@ import { generateInterpolation } from './interpolation'
import { generatePropertyAccess } from './propertyAccess'
import { collectStyleScopedClassReferences } from './styleScopedClasses'
import { generateVSlot } from './vSlot'
+import { getUsingComponentImportName } from '../script/jsonUsingComponents'
const colonReg = /:/g
@@ -47,10 +48,17 @@ export function* generateComponent(
node.tag,
true,
)
- const matchImportName = possibleOriginalNames.find(name =>
- options.scriptSetupImportComponentNames.has(name),
+ const matchedUsingComponentName = possibleOriginalNames.find(name =>
+ options.usingComponents?.has(name),
)
- const componentOriginalVar = matchImportName ?? ctx.getInternalVariable()
+ const matchImportName =
+ possibleOriginalNames.find(name =>
+ options.scriptSetupImportComponentNames.has(name),
+ ) ??
+ (matchedUsingComponentName
+ ? getUsingComponentImportName(matchedUsingComponentName, 0)
+ : undefined)
+ const componentOriginalVar = ctx.getInternalVariable()
const componentFunctionalVar = ctx.getInternalVariable()
const componentVNodeVar = ctx.getInternalVariable()
const componentCtxVar = ctx.getInternalVariable()
@@ -98,32 +106,7 @@ export function* generateComponent(
}
}
- if (matchImportName) {
- // navigation support
- yield `/** @type {[`
- for (const tagOffset of tagOffsets) {
- yield `typeof `
- if (componentOriginalVar === node.tag) {
- yield [
- componentOriginalVar,
- 'template',
- tagOffset,
- ctx.codeFeatures.withoutHighlightAndCompletion,
- ]
- } else {
- const shouldCapitalize =
- matchImportName[0].toUpperCase() === matchImportName[0]
- yield* generateCamelized(
- shouldCapitalize ? capitalize(node.tag) : node.tag,
- 'template',
- tagOffset,
- ctx.codeFeatures.withoutHighlightAndCompletion,
- )
- }
- yield `, `
- }
- yield `]} */${endOfLine}`
- } else if (dynamicTagInfo) {
+ if (dynamicTagInfo) {
yield `const ${componentOriginalVar} = (`
yield* generateInterpolation(
options,
@@ -157,9 +140,7 @@ export function* generateComponent(
options.selfComponentName &&
possibleOriginalNames.includes(options.selfComponentName)
) {
- yield `typeof __VLS_self & (new () => { ` +
- getSlotsPropertyName() +
- `: __VLS_Slots }), `
+ yield `typeof __VLS_self, `
} else {
yield `void, `
}
@@ -209,6 +190,24 @@ export function* generateComponent(
)
yield `${endOfLine}`
}
+
+ if (matchImportName) {
+ // navigation support for resolved import
+ yield `/** @type {[`
+ for (const tagOffset of tagOffsets) {
+ yield `typeof `
+ const shouldCapitalize =
+ matchImportName[0].toUpperCase() === matchImportName[0]
+ yield* generateCamelized(
+ shouldCapitalize ? capitalize(node.tag) : node.tag,
+ 'template',
+ tagOffset,
+ ctx.codeFeatures.withoutHighlightAndCompletion,
+ )
+ yield `, `
+ }
+ yield `]} */${endOfLine}`
+ }
} else {
yield `const ${componentOriginalVar} = {} as any${endOfLine}`
}
diff --git a/packages/language-core/src/codegen/template/elementProps.ts b/packages/language-core/src/codegen/template/elementProps.ts
index 1037c8d..6af0772 100644
--- a/packages/language-core/src/codegen/template/elementProps.ts
+++ b/packages/language-core/src/codegen/template/elementProps.ts
@@ -437,7 +437,7 @@ function getPropsCodeInfo(
strictPropsCheck: boolean,
): MpxCodeInformation {
return ctx.resolveCodeFeatures({
- ...codeFeatures.withoutHighlightAndCompletion,
+ ...codeFeatures.withoutHighlight,
verification: strictPropsCheck || {
shouldReport(_source, code) {
// https://typescript.tv/errors/#ts2353
diff --git a/packages/language-core/src/codegen/template/templateChild.ts b/packages/language-core/src/codegen/template/templateChild.ts
index 1645a1e..d378d85 100644
--- a/packages/language-core/src/codegen/template/templateChild.ts
+++ b/packages/language-core/src/codegen/template/templateChild.ts
@@ -75,7 +75,6 @@ export function* generateTemplateChild(
yield* generateVSlot(options, ctx, node, slotDir)
} else if (
node.tagType === CompilerDOM.ElementTypes.ELEMENT ||
- node.tagType === CompilerDOM.ElementTypes.COMPONENT ||
node.tagType === CompilerDOM.ElementTypes.TEMPLATE
) {
yield* generateElement(options, ctx, node)
diff --git a/packages/language-core/src/plugins/mpx-tsx.ts b/packages/language-core/src/plugins/mpx-tsx.ts
index 67b324b..1046242 100644
--- a/packages/language-core/src/plugins/mpx-tsx.ts
+++ b/packages/language-core/src/plugins/mpx-tsx.ts
@@ -1,5 +1,6 @@
import type { Code, MpxLanguagePlugin, Sfc } from '../types'
import * as path from 'path-browserify'
+import { camelize, capitalize } from '@mpxjs/language-shared'
import { computed } from 'alien-signals'
import { generateScript } from '../codegen/script'
import { generateTemplate } from '../codegen/template'
@@ -186,6 +187,11 @@ function createTsx(
() => getScriptSetupRanges()?.defineProps?.name,
)
+ const getSelfComponentName = computed(() => {
+ const baseName = path.basename(fileName)
+ return capitalize(camelize(baseName.slice(0, baseName.lastIndexOf('.'))))
+ })
+
const getGeneratedTemplate = computed(() => {
if (getResolvedOptions().skipTemplateCheck || !sfc.template) {
return
@@ -205,7 +211,7 @@ function createTsx(
hasDefineSlots: setupHasDefineSlots(),
slotsAssignName: getSetupSlotsAssignName(),
propsAssignName: getSetupPropsAssignName(),
- selfComponentName: undefined,
+ selfComponentName: getSelfComponentName(),
})
let current = codegen.next()
@@ -232,6 +238,7 @@ function createTsx(
lang: getLang(),
scriptRanges: getScriptRanges(),
scriptSetupRanges: getScriptSetupRanges(),
+ scriptSetupImportComponentNames: getSetupImportComponentNames(),
templateCodegen: getGeneratedTemplate(),
destructuredPropNames: getSetupDestructuredPropNames(),
templateRefNames: getSetupTemplateRefNames(),
From f6557355982337e8051d13d1b453c900c8181776 Mon Sep 17 00:00:00 2001
From: Soon Wang
Date: Thu, 21 May 2026 14:42:23 +0800
Subject: [PATCH 2/3] fix: update component options and settings for improved
type handling and debugging
---
.vscode/launch.json | 3 ++-
inspect-extension/.vscode/settings.json | 2 +-
.../components/reference-type-props/component-options.mpx | 4 ++--
inspect-extension/components/reference-type-props/index.mpx | 6 ++++--
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 62b38f5..b6f7cdc 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -3,12 +3,13 @@
"version": "0.2.0",
"configurations": [
{
- "name": "Launch Client",
+ "name": "VSCode Extension",
"type": "extensionHost",
"request": "launch",
"autoAttachChildProcesses": true,
"runtimeExecutable": "${execPath}",
"args": [
+ "--disable-extension=mpxjs.mpx-official",
"--extensionDevelopmentPath=${workspaceRoot}/vscode",
"--folder-uri=${workspaceRoot}/inspect-extension"
],
diff --git a/inspect-extension/.vscode/settings.json b/inspect-extension/.vscode/settings.json
index 89b2275..77a2c6d 100644
--- a/inspect-extension/.vscode/settings.json
+++ b/inspect-extension/.vscode/settings.json
@@ -10,5 +10,5 @@
"mpx.format.script.prettier": true,
"mpx.format.template.prettier": true,
// 方便调试 TM,设置默认现代主题
- "workbench.colorTheme": "Default Dark Modern"
+ "workbench.colorTheme": "Dark Modern"
}
diff --git a/inspect-extension/components/reference-type-props/component-options.mpx b/inspect-extension/components/reference-type-props/component-options.mpx
index 62c59c9..2cb787c 100644
--- a/inspect-extension/components/reference-type-props/component-options.mpx
+++ b/inspect-extension/components/reference-type-props/component-options.mpx
@@ -1,4 +1,4 @@
-
\ No newline at end of file
diff --git a/inspect-extension/components/reference-type-props/index.mpx b/inspect-extension/components/reference-type-props/index.mpx
index 8b3ceab..8418071 100644
--- a/inspect-extension/components/reference-type-props/index.mpx
+++ b/inspect-extension/components/reference-type-props/index.mpx
@@ -1,6 +1,8 @@
+
-
+
+