|
1 | 1 | import fs from 'fs' |
2 | 2 | import path from 'path' |
| 3 | +import { execFileSync } from 'child_process' |
| 4 | +import ts from 'typescript' |
3 | 5 | import jscc from 'rollup-plugin-jscc' |
4 | 6 | import { compileScript, compileTemplate, parse } from '@vue/compiler-sfc' |
| 7 | +import { |
| 8 | + H5_BUILT_IN_TAGS, |
| 9 | + TAGS, |
| 10 | + isH5CustomElement, |
| 11 | + isH5NativeTag, |
| 12 | +} from '../../uni-shared/src/tags' |
5 | 13 | import { preprocess } from '../../uni-preprocess' |
6 | 14 |
|
7 | 15 | const root = path.resolve(__dirname, '..') |
@@ -137,3 +145,109 @@ describe('Web 框架组件入口', () => { |
137 | 145 | expect(entry).not.toContain('uni-components/src/index-vapor') |
138 | 146 | }) |
139 | 147 | }) |
| 148 | + |
| 149 | +describe('框架构建的结构标签分类', () => { |
| 150 | + // 直接读取构建配置中的函数,避免测试复制分类逻辑或执行配置的源码同步副作用。 |
| 151 | + const source = fs.readFileSync(path.join(root, 'vite.config.ts'), 'utf8') |
| 152 | + const ast = ts.createSourceFile( |
| 153 | + 'vite.config.ts', |
| 154 | + source, |
| 155 | + ts.ScriptTarget.Latest, |
| 156 | + true |
| 157 | + ) |
| 158 | + const functions = ast.statements |
| 159 | + .filter( |
| 160 | + (node) => |
| 161 | + ts.isFunctionDeclaration(node) && |
| 162 | + ['realIsH5NativeTag', 'realIsH5CustomElement'].includes( |
| 163 | + node.name?.text || '' |
| 164 | + ) |
| 165 | + ) |
| 166 | + .map((node) => node.getText(ast)) |
| 167 | + .join('\n') |
| 168 | + const code = ts.transpileModule(functions, {}).outputText |
| 169 | + const getOptions = (isX: boolean, isX_VAPOR: boolean) => |
| 170 | + new Function( |
| 171 | + 'TAGS', |
| 172 | + 'isH5NativeTag', |
| 173 | + 'isH5CustomElement', |
| 174 | + 'isX', |
| 175 | + 'isX_VAPOR', |
| 176 | + `${code}; return { isNativeTag: realIsH5NativeTag, isCustomElement: realIsH5CustomElement }` |
| 177 | + )(TAGS, isH5NativeTag, isH5CustomElement, isX, isX_VAPOR) |
| 178 | + |
| 179 | + test('SFC 构建配置使用框架专用标签判断', () => { |
| 180 | + expect(source).toContain('isNativeTag: realIsH5NativeTag') |
| 181 | + expect(source).toContain('isCustomElement: realIsH5CustomElement') |
| 182 | + }) |
| 183 | + |
| 184 | + test.each([false, true])('非 Vapor 分类不变(X=%s)', (isX) => { |
| 185 | + const options = getOptions(isX, false) |
| 186 | + for (const tag of [ |
| 187 | + ...TAGS, |
| 188 | + ...H5_BUILT_IN_TAGS, |
| 189 | + 'div', |
| 190 | + 'span', |
| 191 | + 'svg', |
| 192 | + 'UnknownComponent', |
| 193 | + ]) { |
| 194 | + expect(options.isNativeTag(tag)).toBe(isH5NativeTag(tag)) |
| 195 | + expect(options.isCustomElement(tag)).toBe(isH5CustomElement(tag, isX)) |
| 196 | + } |
| 197 | + }) |
| 198 | + |
| 199 | + test('Vapor 仅将结构标签改为普通元素', () => { |
| 200 | + const options = getOptions(true, true) |
| 201 | + for (const tag of TAGS) { |
| 202 | + expect(options.isNativeTag(tag)).toBe(true) |
| 203 | + expect(options.isCustomElement(tag)).toBe(false) |
| 204 | + } |
| 205 | + for (const tag of [...H5_BUILT_IN_TAGS, 'div', 'svg', 'UnknownComponent']) { |
| 206 | + expect(options.isNativeTag(tag)).toBe(isH5NativeTag(tag)) |
| 207 | + expect(options.isCustomElement(tag)).toBe(isH5CustomElement(tag, true)) |
| 208 | + } |
| 209 | + }) |
| 210 | + |
| 211 | + test('实际 Vapor 编译保留 slot、ref、动态属性和真正自定义元素', () => { |
| 212 | + const result = JSON.parse( |
| 213 | + execFileSync( |
| 214 | + process.execPath, |
| 215 | + [ |
| 216 | + '-e', |
| 217 | + ` |
| 218 | + const fs = require('fs'); |
| 219 | + const { code, tags } = JSON.parse(fs.readFileSync(0, 'utf8')); |
| 220 | + require('./packages/uni-cli-shared/dist/hbx/alias').initWebVaporAliases(); |
| 221 | + const { compile } = require('@dcloudio/compiler-vapor-web'); |
| 222 | + const { isH5NativeTag, isH5CustomElement } = require('./packages/uni-shared/dist/uni-shared.cjs.js'); |
| 223 | + const options = new Function('TAGS', 'isH5NativeTag', 'isH5CustomElement', 'isX', 'isX_VAPOR', |
| 224 | + code + '; return { isNativeTag: realIsH5NativeTag, isCustomElement: realIsH5CustomElement }' |
| 225 | + )(tags, isH5NativeTag, isH5CustomElement, true, true); |
| 226 | + process.stdout.write(JSON.stringify([ |
| 227 | + compile('<uni-page-wrapper ref="wrapper" v-bind="props"><uni-page-body><slot /></uni-page-body></uni-page-wrapper>', options).code, |
| 228 | + compile('<uni-layout><uni-main v-if="visible"><div>{{msg}}</div></uni-main><uni-input :value="value" /></uni-layout>', options).code |
| 229 | + ])); |
| 230 | + `, |
| 231 | + ], |
| 232 | + { |
| 233 | + cwd: path.resolve(root, '../..'), |
| 234 | + input: JSON.stringify({ code, tags: TAGS }), |
| 235 | + encoding: 'utf8', |
| 236 | + } |
| 237 | + ) |
| 238 | + ) as string[] |
| 239 | + expect(result[0]).toContain('_template("<uni-page-wrapper') |
| 240 | + expect(result[0]).toContain('<uni-page-body>') |
| 241 | + expect(result[0]).not.toContain('createPlainElement') |
| 242 | + expect(result[0]).toContain('createSlot') |
| 243 | + expect(result[0]).toContain('setDynamicProps') |
| 244 | + expect(result[0]).toContain('wrapper') |
| 245 | + expect(result[1]).toContain('_template("<uni-layout') |
| 246 | + expect(result[1]).toContain('_template("<uni-main') |
| 247 | + expect(result[1]).toContain('_createPlainElement("uni-input"') |
| 248 | + expect(result[1]).toContain('createIf') |
| 249 | + for (const output of result) { |
| 250 | + expect(output).not.toContain('resolveComponent') |
| 251 | + } |
| 252 | + }) |
| 253 | +}) |
0 commit comments