Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
],
Expand Down
2 changes: 1 addition & 1 deletion inspect-extension/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"mpx.format.script.prettier": true,
"mpx.format.template.prettier": true,
// 方便调试 TM,设置默认现代主题
"workbench.colorTheme": "Default Dark Modern"
"workbench.colorTheme": "Dark Modern"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
defineProps<{
msg: string
count?: number
}>()
defineExpose({})
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script>
import { createComponent } from "@mpxjs/core";

createComponent({
properties: {
parentText: {
type: String,
value: '123'
},
name: {
type: Number
}
},
})
</script>

<script name="json">
module.exports = {
"component": true,
}
</script>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
23 changes: 23 additions & 0 deletions inspect-extension/components/reference-type-props/index.mpx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<!-- 预期报错:Type 'number' is not assignable to type 'string'.ts-plugin(2322) -->
<component-options parentText="{{ 123 }}"></component-options>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<!-- 预期报错:Type 'string' is not assignable to type 'number'. -->
<component-js-setup count="{{ 'hello' }}"></component-js-setup>
</template>

<script lang="ts">
import { createComponent } from '@mpxjs/core'

createComponent({
})
</script>

<script name="json">
module.exports = {
"component": true,
"usingComponents": {
"component-options": "./component-options.mpx",
"component-js-setup": "./component-js-setup.mpx"
}
}
</script>
6 changes: 6 additions & 0 deletions inspect-extension/components/script-json/components/list.mpx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
import { createComponent } from '@mpxjs/core'

createComponent({
properties:{
parentText: {
type: String,
value: 'parent text',
}
},
data: {
listData: ['手机', '电视', '电脑'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ const globalTypes = () => `
: {}
`

const runtimePropTypes = () => `
type __VLS_ResolveProp<T> = 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<string, any>
: T extends { type: import('@mpxjs/core').PropType<infer V> }
? V
: T extends import('@mpxjs/core').PropType<infer V>
? V
: unknown
type __VLS_GetPropsType<T extends Record<string, any>> = Partial<{
readonly [K in keyof T]: __VLS_ResolveProp<T[K]>
}>
`

const localTypes = (lib: MpxCompilerOptions['lib']) => `
// #region DefineComponent - local types
type Data = object | (() => object)
Expand Down Expand Up @@ -190,4 +211,5 @@ interface ReplaceWxComponentIns {
export const defineComponentTypesContents = {
globalTypes,
localTypes,
runtimePropTypes,
}
1 change: 1 addition & 0 deletions packages/language-core/src/codegen/globalTypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export function generateGlobalTypes({
: T[K]
}
${defineComponentTypesContents.globalTypes()}
${defineComponentTypesContents.runtimePropTypes()}
}
` + defineComponentTypesContents.localTypes(lib)

Expand Down
27 changes: 23 additions & 4 deletions packages/language-core/src/codegen/script/componentSelf.ts
Original file line number Diff line number Diff line change
@@ -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<Code> {
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<typeof __VLS_defineComponent['$rawOptions']['properties']>${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}`
}
15 changes: 9 additions & 6 deletions packages/language-core/src/codegen/script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -30,6 +32,7 @@ export interface ScriptCodegenOptions {
lang: string
scriptRanges: ScriptRanges | undefined
scriptSetupRanges: ScriptSetupRanges | undefined
scriptSetupImportComponentNames: Set<string>
templateCodegen: (TemplateCodegenContext & { codes: Code[] }) | undefined
destructuredPropNames: Set<string>
templateRefNames: Set<string>
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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)
Expand Down
74 changes: 43 additions & 31 deletions packages/language-core/src/codegen/script/jsonUsingComponents.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion packages/language-core/src/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function* generateTemplateElements(): Generator<Code> {
}

function* generateTemplateComponents(): Generator<Code> {
const types: Code[] = [`typeof __MPX_ctx`]
const types: Code[] = [`typeof __MPX_ctx`, `typeof __MPX_jsonComponents`]

yield `type __VLS_LocalComponents =`
for (const type of types) {
Expand Down
Loading
Loading