From 23f5a4ad902648ea005d19ed39985042860b8124 Mon Sep 17 00:00:00 2001 From: chouchouji <1305974212@qq.com> Date: Thu, 3 Jul 2025 12:03:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(mp-weixin):=20=E4=BF=AE=E5=A4=8D=20input=20?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=20keyboard-accessory=20=E6=97=B6=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=90=8E=E4=B8=A4=E8=80=85=E6=9C=AA=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=B1=82=E7=BA=A7=E7=9A=84Bug=20(question/20?= =?UTF-8?q?9237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/uni-mp-compiler/src/parserOptions.ts | 9 +- packages/uni-mp-vite/__tests__/input.spec.ts | 109 ++++++++++++++++++ packages/uni-mp-vite/src/index.ts | 3 +- packages/uni-mp-vite/src/plugins/input.ts | 74 ++++++++++++ 4 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 packages/uni-mp-vite/__tests__/input.spec.ts create mode 100644 packages/uni-mp-vite/src/plugins/input.ts diff --git a/packages/uni-mp-compiler/src/parserOptions.ts b/packages/uni-mp-compiler/src/parserOptions.ts index 5242979cec7..bb3cbaab63e 100644 --- a/packages/uni-mp-compiler/src/parserOptions.ts +++ b/packages/uni-mp-compiler/src/parserOptions.ts @@ -15,11 +15,10 @@ export const enum DOMNamespaces { export const parserOptions: ParserOptions = { isVoidTag(tag) { - // 开启 input 标签判断,会导致 编译失败 - // 微信小程序允许 Input 嵌套其他组件 https://ask.dcloud.net.cn/question/202776 - // if (tag === 'input') { - // return false - // } + // 微信小程序允许 input 嵌套其他组件 https://ask.dcloud.net.cn/question/202776 + if (tag === 'input') { + return false + } return isVoidTagRaw(tag) }, isNativeTag: (tag) => isHTMLTag(tag) || isSVGTag(tag), diff --git a/packages/uni-mp-vite/__tests__/input.spec.ts b/packages/uni-mp-vite/__tests__/input.spec.ts new file mode 100644 index 00000000000..030be14ac26 --- /dev/null +++ b/packages/uni-mp-vite/__tests__/input.spec.ts @@ -0,0 +1,109 @@ +import { formatInputTag } from '../src/plugins/input' + +describe('test input tag', () => { + test('with end input tag', () => { + expect( + formatInputTag(` + `) + ).toBe(` + `) + + expect( + formatInputTag( + `` + ) + ).toBe( + `` + ) + + expect(formatInputTag(``)).toBe( + `` + ) + + expect( + formatInputTag(` + `) + ).toBe(` + `) + }) + + test('no end input tag', () => { + expect(formatInputTag(``)).toBe( + `` + ) + + expect( + formatInputTag( + `` + ) + ).toBe( + `` + ) + + expect( + formatInputTag( + `` + ) + ).toBe( + `` + ) + + expect( + formatInputTag(` + `) + ).toBe(` + `) + }) +}) diff --git a/packages/uni-mp-vite/src/index.ts b/packages/uni-mp-vite/src/index.ts index c3c70300293..5614fc456e1 100644 --- a/packages/uni-mp-vite/src/index.ts +++ b/packages/uni-mp-vite/src/index.ts @@ -25,7 +25,7 @@ import { uniMainJsPlugin } from './plugins/mainJs' import { uniManifestJsonPlugin } from './plugins/manifestJson' import { uniPagesJsonPlugin } from './plugins/pagesJson' import { uniEntryPlugin } from './plugins/entry' - +import { uniInputAutoClosePlugin } from './plugins/input' import { uniRenderjsPlugin } from './plugins/renderjs' import { uniRuntimeHooksPlugin } from './plugins/runtimeHooks' import { uniSubpackagePlugin } from './plugins/subpackage' @@ -52,6 +52,7 @@ export default (options: UniMiniProgramPluginOptions) => { ) return [ + uniInputAutoClosePlugin(), ...(isEnableConsole() ? [uniHBuilderXConsolePlugin('uni.__f__')] : []), ...(process.env.UNI_APP_X === 'true' ? [ diff --git a/packages/uni-mp-vite/src/plugins/input.ts b/packages/uni-mp-vite/src/plugins/input.ts new file mode 100644 index 00000000000..0508ad308e9 --- /dev/null +++ b/packages/uni-mp-vite/src/plugins/input.ts @@ -0,0 +1,74 @@ +export function formatInputTag(code: string) { + const inputStartRegex = /]*)?>/g + let result = code + let match + + // 收集所有标签的位置 + const inputStarts: Array<{ + index: number + length: number + fullMatch: string + }> = [] + while ((match = inputStartRegex.exec(code)) !== null) { + inputStarts.push({ + index: match.index, + length: match[0].length, + fullMatch: match[0], + }) + } + + // 检查每个是否有对应的 + const toReplace: Array<{ + index: number + length: number + replacement: string + }> = [] + for (let i = 0; i < inputStarts.length; i++) { + const start = inputStarts[i] + const nextStart = inputStarts[i + 1]?.index || code.length + const substring = code.slice(start.index + start.length, nextStart) + + // 检查是否有对应的 + const hasClosingTag = substring.includes('') + + // 检查是否是自闭合形式(以/>结尾) + const isSelfClosing = start.fullMatch.endsWith('/>') + + if (!hasClosingTag && !isSelfClosing) { + // 需要转换的标签 + toReplace.push({ + index: start.index, + length: start.length, + replacement: start.fullMatch.replace(/>$/, ' />'), + }) + } + } + + // 从后往前替换,避免影响索引 + for (let i = toReplace.length - 1; i >= 0; i--) { + const item = toReplace[i] + result = + result.slice(0, item.index) + + item.replacement + + result.slice(item.index + item.length) + } + + return result +} + +export function uniInputAutoClosePlugin() { + return { + name: 'uni:mp-input-auto-close', + enforce: 'pre', + transform(code, id) { + if (!/\.(vue|nvue|uvue)$/.test(id)) { + return + } + if (!code.includes('