-
-
Notifications
You must be signed in to change notification settings - Fork 12
feat: add support for page-meta component in Mini Program #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,11 @@ | ||||||||||||||||||||||||||||||||
| import type { Node } from '@babel/types' | ||||||||||||||||||||||||||||||||
| import { isMp } from '@uni-helper/uni-env' | ||||||||||||||||||||||||||||||||
| import type { AttributeNode, DirectiveNode, ElementNode, SimpleExpressionNode } from '@vue/compiler-core' | ||||||||||||||||||||||||||||||||
| import type { | ||||||||||||||||||||||||||||||||
| AttributeNode, | ||||||||||||||||||||||||||||||||
| DirectiveNode, | ||||||||||||||||||||||||||||||||
| ElementNode, | ||||||||||||||||||||||||||||||||
| SimpleExpressionNode, | ||||||||||||||||||||||||||||||||
| } from '@vue/compiler-core' | ||||||||||||||||||||||||||||||||
| import { babelParse, walkAST } from 'ast-kit' | ||||||||||||||||||||||||||||||||
| import MagicString from 'magic-string' | ||||||||||||||||||||||||||||||||
| import { kebabCase } from 'scule' | ||||||||||||||||||||||||||||||||
|
|
@@ -71,7 +76,9 @@ export class Context { | |||||||||||||||||||||||||||||||
| // layout name is empty | ||||||||||||||||||||||||||||||||
| if (!pageLayoutName) | ||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||
| pageLayout = this.layouts.find(l => l.name === (pageLayoutName as string)) | ||||||||||||||||||||||||||||||||
| pageLayout = this.layouts.find( | ||||||||||||||||||||||||||||||||
| l => l.name === (pageLayoutName as string), | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| // can not find layout | ||||||||||||||||||||||||||||||||
| if (!pageLayout) | ||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||
|
|
@@ -100,18 +107,58 @@ export class Context { | |||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // 检查是否有 page-meta 组件 | ||||||||||||||||||||||||||||||||
| let pageMetaNodes: ElementNode[] = [] | ||||||||||||||||||||||||||||||||
| if (sfc.template?.ast) { | ||||||||||||||||||||||||||||||||
| pageMetaNodes = sfc.template.ast.children.filter( | ||||||||||||||||||||||||||||||||
| v => | ||||||||||||||||||||||||||||||||
| v.type === 1 | ||||||||||||||||||||||||||||||||
| && (kebabCase(v.tag) === 'page-meta' || v.tag === 'page-meta'), | ||||||||||||||||||||||||||||||||
| ) as ElementNode[] | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (disabled) { | ||||||||||||||||||||||||||||||||
| // find dynamic layout | ||||||||||||||||||||||||||||||||
| const uniLayoutNode = sfc.template?.ast.children.find(v => v.type === 1 && kebabCase(v.tag) === 'uni-layout') as ElementNode | ||||||||||||||||||||||||||||||||
| const uniLayoutNode = sfc.template?.ast!.children.find( | ||||||||||||||||||||||||||||||||
| v => v.type === 1 && kebabCase(v.tag) === 'uni-layout', | ||||||||||||||||||||||||||||||||
| ) as ElementNode | ||||||||||||||||||||||||||||||||
| // not found | ||||||||||||||||||||||||||||||||
| if (!uniLayoutNode) | ||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ms.overwrite(uniLayoutNode.loc.start.offset, uniLayoutNode.loc.end.offset, this.generateDynamicLayout(uniLayoutNode)) | ||||||||||||||||||||||||||||||||
| ms.overwrite( | ||||||||||||||||||||||||||||||||
| uniLayoutNode.loc.start.offset, | ||||||||||||||||||||||||||||||||
| uniLayoutNode.loc.end.offset, | ||||||||||||||||||||||||||||||||
| this.generateDynamicLayout(uniLayoutNode), | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||
| if (sfc.template?.loc.start.offset && sfc.template?.loc.end.offset) | ||||||||||||||||||||||||||||||||
| ms.overwrite(sfc.template?.loc.start.offset, sfc.template?.loc.end.offset, `\n<layout-${pageLayout?.kebabName}-uni ${pageLayoutProps.join(' ')}>${sfc.template.content}</layout-${pageLayout?.kebabName}-uni>\n`) | ||||||||||||||||||||||||||||||||
| if (sfc.template?.loc.start.offset && sfc.template?.loc.end.offset) { | ||||||||||||||||||||||||||||||||
| // 提取 page-meta 组件内容 | ||||||||||||||||||||||||||||||||
| const pageMetaContent = pageMetaNodes | ||||||||||||||||||||||||||||||||
| .map(node => node.loc.source) | ||||||||||||||||||||||||||||||||
| .join('\n') | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // 从原内容中移除 page-meta 组件 | ||||||||||||||||||||||||||||||||
| let contentWithoutPageMeta = sfc.template.content | ||||||||||||||||||||||||||||||||
| for (const node of pageMetaNodes) { | ||||||||||||||||||||||||||||||||
| contentWithoutPageMeta = contentWithoutPageMeta.replace( | ||||||||||||||||||||||||||||||||
| node.loc.source, | ||||||||||||||||||||||||||||||||
| '', | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // 在布局外部添加 page-meta | ||||||||||||||||||||||||||||||||
| ms.overwrite( | ||||||||||||||||||||||||||||||||
| sfc.template?.loc.start.offset, | ||||||||||||||||||||||||||||||||
| sfc.template?.loc.end.offset, | ||||||||||||||||||||||||||||||||
| `\n${pageMetaContent}<layout-${ | ||||||||||||||||||||||||||||||||
| pageLayout?.kebabName | ||||||||||||||||||||||||||||||||
| }-uni ${pageLayoutProps.join( | ||||||||||||||||||||||||||||||||
| ' ', | ||||||||||||||||||||||||||||||||
| )}>${contentWithoutPageMeta}</layout-${pageLayout?.kebabName}-uni>\n`, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (ms.hasChanged()) { | ||||||||||||||||||||||||||||||||
|
|
@@ -155,42 +202,70 @@ export default { | |||||||||||||||||||||||||||||||
| v => v.type === 6 && v.name === 'name' && v.value?.content, | ||||||||||||||||||||||||||||||||
| ) as AttributeNode | ||||||||||||||||||||||||||||||||
| const dynamicLayoutNameBind = node.props.find( | ||||||||||||||||||||||||||||||||
| v => v.type === 7 && v.name === 'bind' && v.arg?.type === 4 && v.arg?.content === 'name' && v.exp?.type === 4 && v.exp.content, | ||||||||||||||||||||||||||||||||
| v => | ||||||||||||||||||||||||||||||||
| v.type === 7 | ||||||||||||||||||||||||||||||||
| && v.name === 'bind' | ||||||||||||||||||||||||||||||||
| && v.arg?.type === 4 | ||||||||||||||||||||||||||||||||
| && v.arg?.content === 'name' | ||||||||||||||||||||||||||||||||
| && v.exp?.type === 4 | ||||||||||||||||||||||||||||||||
| && v.exp.content, | ||||||||||||||||||||||||||||||||
| ) as DirectiveNode | ||||||||||||||||||||||||||||||||
| const slotsSource = node.children.map(v => v.loc.source).join('\n') | ||||||||||||||||||||||||||||||||
| const nodeProps = node.props.filter(prop => !(prop === dynamicLayoutNameBind || prop === staticLayoutNameBind)).map(v => v.loc.source) | ||||||||||||||||||||||||||||||||
| const nodeProps = node.props | ||||||||||||||||||||||||||||||||
| .filter( | ||||||||||||||||||||||||||||||||
| prop => | ||||||||||||||||||||||||||||||||
| !(prop === dynamicLayoutNameBind || prop === staticLayoutNameBind), | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| .map(v => v.loc.source) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!(staticLayoutNameBind || dynamicLayoutNameBind)) | ||||||||||||||||||||||||||||||||
| console.warn('[vite-plugin-uni-layouts] Dynamic layout not found name bind') | ||||||||||||||||||||||||||||||||
| if (!(staticLayoutNameBind || dynamicLayoutNameBind)) { | ||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||
| '[vite-plugin-uni-layouts] Dynamic layout not found name bind', | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+221
to
+225
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid runtime crash when no name bind is present. You warn if neither static nor dynamic name is bound, but later dereference dynamicLayoutNameBind.exp unconditionally. Bail out early by returning slotsSource. - if (!(staticLayoutNameBind || dynamicLayoutNameBind)) {
- console.warn(
- '[vite-plugin-uni-layouts] Dynamic layout not found name bind',
- )
- }
+ if (!(staticLayoutNameBind || dynamicLayoutNameBind)) {
+ console.warn(
+ '[vite-plugin-uni-layouts] Dynamic layout not found name bind',
+ )
+ return slotsSource
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (isMp) { | ||||||||||||||||||||||||||||||||
| const props: string[] = [...nodeProps] | ||||||||||||||||||||||||||||||||
| if (staticLayoutNameBind) { | ||||||||||||||||||||||||||||||||
| const layout = staticLayoutNameBind.value?.content | ||||||||||||||||||||||||||||||||
| return `<layout-${layout}-uni ${props.join(' ')}>${slotsSource}</layout-${layout}-uni>` | ||||||||||||||||||||||||||||||||
| return `<layout-${layout}-uni ${props.join( | ||||||||||||||||||||||||||||||||
| ' ', | ||||||||||||||||||||||||||||||||
| )}>${slotsSource}</layout-${layout}-uni>` | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const bind = (dynamicLayoutNameBind.exp as SimpleExpressionNode).content | ||||||||||||||||||||||||||||||||
| const defaultSlot = node.children.filter((v) => { | ||||||||||||||||||||||||||||||||
| if (v.type === 1 && v.tagType === 3) { | ||||||||||||||||||||||||||||||||
| const slot = v.props.find(v => v.type === 7 && v.name === 'slot' && v.arg?.type === 4) as any | ||||||||||||||||||||||||||||||||
| const slot = v.props.find( | ||||||||||||||||||||||||||||||||
| v => v.type === 7 && v.name === 'slot' && v.arg?.type === 4, | ||||||||||||||||||||||||||||||||
| ) as any | ||||||||||||||||||||||||||||||||
| if (slot) | ||||||||||||||||||||||||||||||||
| return slot.arg.content === 'default' | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return v | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
| const defaultSlotSource = defaultSlot.map(v => v.loc.source).join('\n') | ||||||||||||||||||||||||||||||||
| const layouts = this.layouts.map((layout, index) => `<layout-${layout.kebabName}-uni v-${index === 0 ? 'if' : 'else-if'}="${bind} ==='${layout.kebabName}'" ${props.join(' ')}>${slotsSource}</layout-${layout.kebabName}-uni>`) | ||||||||||||||||||||||||||||||||
| const layouts = this.layouts.map( | ||||||||||||||||||||||||||||||||
| (layout, index) => `<layout-${layout.kebabName}-uni v-${ | ||||||||||||||||||||||||||||||||
| index === 0 ? 'if' : 'else-if' | ||||||||||||||||||||||||||||||||
| }="${bind} ==='${layout.kebabName}'" ${props.join( | ||||||||||||||||||||||||||||||||
| ' ', | ||||||||||||||||||||||||||||||||
| )}>${slotsSource}</layout-${layout.kebabName}-uni>`, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| layouts.push(`<template v-else>${defaultSlotSource}</template>`) | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return layouts.join('\n') | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||
| const props: string[] = [...nodeProps] | ||||||||||||||||||||||||||||||||
| if (staticLayoutNameBind) | ||||||||||||||||||||||||||||||||
| props.push(`is="layout-${staticLayoutNameBind.value?.content}-uni"`) | ||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||
| props.push(`:is="\`layout-\${${(dynamicLayoutNameBind.exp as SimpleExpressionNode).content}}-uni\`"`) | ||||||||||||||||||||||||||||||||
| if (staticLayoutNameBind) { props.push(`is="layout-${staticLayoutNameBind.value?.content}-uni"`) } | ||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||
| props.push( | ||||||||||||||||||||||||||||||||
| `:is="\`layout-\${${ | ||||||||||||||||||||||||||||||||
| (dynamicLayoutNameBind.exp as SimpleExpressionNode).content | ||||||||||||||||||||||||||||||||
| }}-uni\`"`, | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+261
to
+268
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ESLint max-statements-per-line and guard dynamic bind access. Split the one-liner if/else and avoid undefined access. This also resolves the reported ESLint error. As per ESLint hint. - if (staticLayoutNameBind) { props.push(`is="layout-${staticLayoutNameBind.value?.content}-uni"`) }
- else {
- props.push(
- `:is="\`layout-\${${
- (dynamicLayoutNameBind.exp as SimpleExpressionNode).content
- }}-uni\`"`,
- )
- }
+ if (staticLayoutNameBind) {
+ props.push(`is="layout-${staticLayoutNameBind.value?.content}-uni"`)
+ }
+ else {
+ const exp = (dynamicLayoutNameBind!.exp as SimpleExpressionNode).content
+ props.push(`:is="\`layout-\${${exp}}-uni\`"`)
+ }📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 261-261: This line has 2 statements. Maximum allowed is 1. (style/max-statements-per-line) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
| return `<component ${props.join(' ')}>${slotsSource}</component>` | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix offset truthiness bug and guard reinjection; handle absence of page-meta cleanly.
Using truthy checks can skip when start.offset === 0. Also, only reflow page-meta for MP; otherwise, keep existing wrapper behavior without the page-meta preface.
Apply this refactor:
🤖 Prompt for AI Agents