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-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..2cb787c
--- /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..8418071
--- /dev/null
+++ b/inspect-extension/components/reference-type-props/index.mpx
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
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..83aa732 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 `: ${firstImportName} 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,24 +51,25 @@ 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,
- navigation: true, // 需要导航功能来支持跳转
+ navigation: false,
semantic: false,
verification: false,
structure: false,
@@ -91,10 +91,10 @@ export function* generateJsonPathCompletionImports(
yield [
page.text,
'json_import',
- page.offset,
+ jsonStart + page.offset,
{
completion: true,
- navigation: true,
+ navigation: false,
semantic: false,
verification: false,
structure: false,
@@ -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 index && baseName === camelizedName ? `${baseName}_${index}` : 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..78d62d4 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,
@@ -47,15 +47,18 @@ export function* generateComponent(
node.tag,
true,
)
- const matchImportName = possibleOriginalNames.find(name =>
- options.scriptSetupImportComponentNames.has(name),
- )
- const componentOriginalVar = matchImportName ?? ctx.getInternalVariable()
+ const componentOriginalVar = ctx.getInternalVariable()
const componentFunctionalVar = ctx.getInternalVariable()
const componentVNodeVar = ctx.getInternalVariable()
const componentCtxVar = ctx.getInternalVariable()
const isComponentTag = node.tag.toLowerCase() === 'component'
+ ctx.templateNodeTags.push({
+ name: node.tag,
+ startTagOffset: tagOffsets[0],
+ endTagOffset: tagOffsets[1],
+ })
+
ctx.currentComponent?.childTypes.push(`typeof ${componentVNodeVar}`)
ctx.currentComponent = {
ctxVar: componentCtxVar,
@@ -98,32 +101,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 +135,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, `
}
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(),