diff --git a/packages/uni-app-plus/dist/uni-app-service.es.js b/packages/uni-app-plus/dist/uni-app-service.es.js
index 0f1e2d085dda6821d9ff276a4700616d44740d41..3dff615e239c48fd01eab7af1614152201dcae12 100644
--- a/packages/uni-app-plus/dist/uni-app-service.es.js
+++ b/packages/uni-app-plus/dist/uni-app-service.es.js
@@ -5680,7 +5680,8 @@ var serviceContext = (function (vue) {
},
};
const API_CLOSE_AUTH_VIEW = 'closeAuthView';
- const API_GET_CHECK_BOX_STATE = 'getCheckBoxState';
+ const API_GET_CHECK_BOX_STATE = 'getCheckBoxState';
+ const API_GET_UNIVERIFY_MANAGER = 'getUniverifyManager';
const API_SHREA = 'share';
const SCENE = [
@@ -6386,7 +6387,7 @@ var serviceContext = (function (vue) {
let deviceId;
function deviceId$1 () {
- deviceId = deviceId || plus.runtime.getDCloudId();
+ deviceId = deviceId || plus.device.uuid;
return deviceId;
}
@@ -8985,7 +8986,7 @@ var serviceContext = (function (vue) {
errMsg: 'getLocation:ok',
});
}
- const getLocation = defineAsyncApi(API_GET_LOCATION, ({ type = 'wgs84', geocode = false, altitude = false }, { resolve, reject }) => {
+ const getLocation = defineAsyncApi(API_GET_LOCATION, ({ type = 'wgs84', geocode = false, altitude = false, highAccuracyExpireTime, }, { resolve, reject }) => {
plus.geolocation.getCurrentPosition((position) => {
getLocationSuccess(type, position, resolve);
}, (e) => {
@@ -8998,6 +8999,7 @@ var serviceContext = (function (vue) {
}, {
geocode: geocode,
enableHighAccuracy: altitude,
+ timeout: highAccuracyExpireTime,
});
}, GetLocationProtocol, GetLocationOptions);
@@ -9545,6 +9547,7 @@ var serviceContext = (function (vue) {
}
}, GetProviderProtocol);
+ let univerifyManager;
function getService(provider) {
return new Promise((resolve, reject) => {
plus.oauth.getServices((services) => {
@@ -9692,24 +9695,59 @@ var serviceContext = (function (vue) {
*/
function univerifyButtonsClickHandling(univerifyStyle, errorCallback) {
if (isPlainObject(univerifyStyle) &&
- univerifyStyle.buttons &&
- toTypeString(univerifyStyle.buttons.list) === '[object Array]' &&
- univerifyStyle.buttons.list.length > 0) {
+ isPlainObject(univerifyStyle.buttons) &&
+ toTypeString(univerifyStyle.buttons.list) === '[object Array]') {
univerifyStyle.buttons.list.forEach((button, index) => {
univerifyStyle.buttons.list[index].onclick = function () {
- _closeAuthView().then(() => {
- errorCallback({
- code: '30008',
- message: '用户点击了自定义按钮',
- index,
- provider: button.provider,
+ const res = {
+ code: '30008',
+ message: '用户点击了自定义按钮',
+ index,
+ provider: button.provider,
+ };
+ isPlainObject(univerifyManager)
+ ? univerifyManager._triggerUniverifyButtonsClick(res)
+ : _closeAuthView().then(() => {
+ errorCallback(res);
});
- });
};
});
}
return univerifyStyle;
- }
+ }
+ class UniverifyManager {
+ constructor() {
+ this.provider = 'univerify';
+ this.eventName = 'api.univerifyButtonsClick';
+ }
+ close() {
+ closeAuthView();
+ }
+ login(options) {
+ login(this._getOptions(options));
+ }
+ getCheckBoxState(options) {
+ getCheckBoxState(options);
+ }
+ preLogin(options) {
+ preLogin(this._getOptions(options));
+ }
+ onButtonsClick(callback) {
+ UniServiceJSBridge.on(this.eventName, callback);
+ }
+ offButtonsClick(callback) {
+ UniServiceJSBridge.off(this.eventName, callback);
+ }
+ _triggerUniverifyButtonsClick(res) {
+ UniServiceJSBridge.emit(this.eventName, res);
+ }
+ _getOptions(options = {}) {
+ return extend({}, options, { provider: this.provider });
+ }
+ }
+ const getUniverifyManager = defineSyncApi(API_GET_UNIVERIFY_MANAGER, () => {
+ return univerifyManager || (univerifyManager = new UniverifyManager());
+ });
const registerRuntime = defineSyncApi('registerRuntime', (runtime) => {
// @ts-expect-error
@@ -9859,7 +9897,7 @@ var serviceContext = (function (vue) {
});
}
catch (e) {
- console.error(e.message + '\n' + e.stack);
+ console.error(e.message + LINEFEED + e.stack);
}
}
}
@@ -12771,6 +12809,7 @@ var serviceContext = (function (vue) {
preLogin: preLogin,
closeAuthView: closeAuthView,
getCheckBoxState: getCheckBoxState,
+ getUniverifyManager: getUniverifyManager,
registerRuntime: registerRuntime,
share: share,
shareWithSystem: shareWithSystem,
diff --git a/packages/uni-cli-shared/package.json b/packages/uni-cli-shared/package.json
index fce58ec7b44cd29ee73cd48f5cf55eeb34b2243a..c5ebc7d577fa99d5e3d3fd75e2cc4a56fcd7e58e 100644
--- a/packages/uni-cli-shared/package.json
+++ b/packages/uni-cli-shared/package.json
@@ -23,6 +23,7 @@
"chokidar": "^3.5.2",
"compare-versions": "^3.6.0",
"debug": "^4.3.1",
+ "es-module-lexer": "^0.9.3",
"estree-walker": "^2.0.2",
"fast-glob": "^3.2.7",
"fs-extra": "^10.0.0",
diff --git a/packages/uni-cli-shared/src/checkUpdate.ts b/packages/uni-cli-shared/src/checkUpdate.ts
index 341d8b7bca51c2b53cb760671d821c0961759a41..0744d3b5c3557cfa6748dcadfae2105738a19cea 100644
--- a/packages/uni-cli-shared/src/checkUpdate.ts
+++ b/packages/uni-cli-shared/src/checkUpdate.ts
@@ -188,7 +188,7 @@ function writeCheckUpdateCache(
debugCheckUpdate('write:', filepath, updateCache)
try {
fs.outputFileSync(filepath, JSON.stringify(updateCache))
- } catch (e: any) {
+ } catch (e) {
debugCheckUpdate('write.error', e)
}
}
diff --git a/packages/uni-cli-shared/src/easycom.ts b/packages/uni-cli-shared/src/easycom.ts
index d650fc0f4d20beda2cce201e32c9bc70325b44c8..6fd95d37a69392bf1f4ad47db92c7e640185b796 100644
--- a/packages/uni-cli-shared/src/easycom.ts
+++ b/packages/uni-cli-shared/src/easycom.ts
@@ -24,7 +24,7 @@ interface EasycomCustom {
[key: string]: string
}
-const debugEasycom = debug('uni:easycom')
+const debugEasycom = debug('vite:uni:easycom')
const easycoms: EasycomMatcher[] = []
diff --git a/packages/uni-cli-shared/src/mp/imports.ts b/packages/uni-cli-shared/src/mp/imports.ts
new file mode 100644
index 0000000000000000000000000000000000000000..84523d2c99c28892ecf558765975dcd7d884e657
--- /dev/null
+++ b/packages/uni-cli-shared/src/mp/imports.ts
@@ -0,0 +1,48 @@
+import path from 'path'
+import { PluginContext } from 'rollup'
+import { init, parse as parseImports, ImportSpecifier } from 'es-module-lexer'
+import { extend } from '@vue/shared'
+import { EXTNAME_VUE, EXTNAME_VUE_RE } from '../constants'
+
+export async function findVueComponentImports(
+ source: string,
+ importer: string,
+ resolve: PluginContext['resolve']
+) {
+ await init
+ let imports: readonly ImportSpecifier[] = []
+ // strip UTF-8 BOM
+ if (source.charCodeAt(0) === 0xfeff) {
+ source = source.slice(1)
+ }
+ try {
+ imports = parseImports(source)[0]
+ } catch (e: any) {
+ console.error(e)
+ }
+ if (!imports.length) {
+ return []
+ }
+
+ const rewriteImports: ImportSpecifier[] = []
+ for (let i = 0; i < imports.length; i++) {
+ const importSpecifier = imports[i]
+ const { n } = importSpecifier
+ if (!n) {
+ continue
+ }
+ const extname = path.extname(n)
+ // 仅处理没有后缀,或后缀是.vue,.nvue的文件
+ if (extname && !EXTNAME_VUE.includes(extname)) {
+ continue
+ }
+ const res = await resolve(n, importer)
+ if (!res) {
+ continue
+ }
+ if (EXTNAME_VUE_RE.test(res.id)) {
+ rewriteImports.push(extend(importSpecifier, { n: res.id }))
+ }
+ }
+ return rewriteImports
+}
diff --git a/packages/uni-cli-shared/src/mp/index.ts b/packages/uni-cli-shared/src/mp/index.ts
index c475080bb89d804268a5f20b166ba6154f23b483..a86ec02958dab750ee291f34e2d21db2d149d414 100644
--- a/packages/uni-cli-shared/src/mp/index.ts
+++ b/packages/uni-cli-shared/src/mp/index.ts
@@ -1 +1,2 @@
export * from './event'
+export { findVueComponentImports } from './imports'
diff --git a/packages/uni-h5/dist/uni-h5.es.js b/packages/uni-h5/dist/uni-h5.es.js
index 5407a1c6d323fc80a9f8ffbbe3655567769308d2..7a6140206c3974de8e127b4f34bcbef415a4f0ff 100644
--- a/packages/uni-h5/dist/uni-h5.es.js
+++ b/packages/uni-h5/dist/uni-h5.es.js
@@ -17198,13 +17198,13 @@ function getJSONP(url, options, success, error) {
js.src = url + (url.indexOf("?") >= 0 ? "&" : "?") + callbackKey + "=" + callbackName;
document.body.appendChild(js);
}
-const getLocation = /* @__PURE__ */ defineAsyncApi(API_GET_LOCATION, ({ type, altitude }, { resolve, reject }) => {
+const getLocation = /* @__PURE__ */ defineAsyncApi(API_GET_LOCATION, ({ type, altitude, highAccuracyExpireTime }, { resolve, reject }) => {
const mapInfo = getMapInfo();
new Promise((resolve2, reject2) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((res) => resolve2(res.coords), reject2, {
enableHighAccuracy: altitude,
- timeout: 1e3 * 100
+ timeout: highAccuracyExpireTime || 1e3 * 100
});
} else {
reject2(new Error("device nonsupport geolocation"));
diff --git a/packages/uni-mp-compiler/__tests__/transformElement.spec.ts b/packages/uni-mp-compiler/__tests__/transformElement.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..617f8a1def2d02a4670464d1f7d9c384141fa4ed
--- /dev/null
+++ b/packages/uni-mp-compiler/__tests__/transformElement.spec.ts
@@ -0,0 +1,227 @@
+import { BindingTypes, ElementNode, RootNode } from '@vue/compiler-core'
+import { compile } from '../src'
+import { MPErrorCodes } from '../src/errors'
+import { CodegenRootNode, CompilerOptions } from '../src/options'
+import { BindingComponentTypes } from '../src/transform'
+
+function parseWithElementTransform(
+ template: string,
+ options: CompilerOptions = {}
+): {
+ code: string
+ preamble: string
+ root: RootNode
+ node: ElementNode
+} {
+ const { ast, code, preamble } = compile(`
${template}
`, options)
+ const node = (ast as any).children[0].children[0]
+ return {
+ code,
+ preamble,
+ root: ast,
+ node,
+ }
+}
+
+describe('compiler: element transform', () => {
+ test('import + resolve component', () => {
+ const { root, code } = parseWithElementTransform(``)
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Foo: { name: '_component_Foo', type: BindingComponentTypes.UNKNOWN },
+ })
+ expect(code).toContain(`if (!Math) {Math.max.call(Max, _component_Foo)}`)
+ })
+
+ test('import + resolve component multi', () => {
+ const { root, code } = parseWithElementTransform(
+ ``,
+ {
+ filename: `/foo/bar/Test.vue?vue&type=template`,
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_MAYBE_REF,
+ },
+ }
+ )
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Foo: { name: '_component_Foo', type: BindingComponentTypes.UNKNOWN },
+ Bar: { name: '_component_Bar', type: BindingComponentTypes.UNKNOWN },
+ Example: { name: '$setup["Example"]', type: BindingComponentTypes.SETUP },
+ Test: { name: '_component_Test', type: BindingComponentTypes.SELF },
+ })
+ expect(code).toContain(
+ `if (!Math) {Math.max.call(Max, _component_Foo, _component_Bar, $setup["Example"], _component_Test)}`
+ )
+ })
+
+ test('resolve implcitly self-referencing component', () => {
+ const { root, code } = parseWithElementTransform(``, {
+ filename: `/foo/bar/Example.vue?vue&type=template`,
+ })
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Example: { name: '_component_Example', type: BindingComponentTypes.SELF },
+ })
+ expect(code).toContain(
+ `if (!Math) {Math.max.call(Max, _component_Example)}`
+ )
+ })
+
+ test('resolve component from setup bindings', () => {
+ const { root, code } = parseWithElementTransform(``, {
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_MAYBE_REF,
+ },
+ })
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Example: { name: '$setup["Example"]', type: BindingComponentTypes.SETUP },
+ })
+ expect(code).toContain(`if (!Math) {Math.max.call(Max, $setup["Example"])}`)
+ })
+
+ test('resolve component from setup bindings (inline)', () => {
+ const { root, preamble } = parseWithElementTransform(``, {
+ inline: true,
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_MAYBE_REF,
+ },
+ })
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Example: { name: '_unref(Example)', type: BindingComponentTypes.SETUP },
+ })
+ expect(preamble).toContain(
+ `if (!Math) {Math.max.call(Max, _unref(Example))}`
+ )
+ })
+
+ test('resolve component from setup bindings (inline const)', () => {
+ const { root, preamble } = parseWithElementTransform(``, {
+ inline: true,
+ bindingMetadata: {
+ Example: BindingTypes.SETUP_CONST,
+ },
+ })
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Example: { name: 'Example', type: BindingComponentTypes.SETUP },
+ })
+ expect(preamble).toContain(`if (!Math) {Math.max.call(Max, Example)}`)
+ })
+
+ test('resolve namespaced component from setup bindings', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ bindingMetadata: {
+ Foo: BindingTypes.SETUP_MAYBE_REF,
+ },
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('resolve namespaced component from setup bindings (inline)', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ inline: true,
+ bindingMetadata: {
+ Foo: BindingTypes.SETUP_MAYBE_REF,
+ },
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('resolve namespaced component from setup bindings (inline const)', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ inline: true,
+ bindingMetadata: {
+ Foo: BindingTypes.SETUP_CONST,
+ },
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('do not resolve component from non-script-setup bindings', () => {
+ const bindingMetadata = {
+ Example: BindingTypes.SETUP_MAYBE_REF,
+ }
+ Object.defineProperty(bindingMetadata, '__isScriptSetup', { value: false })
+ const { root } = parseWithElementTransform(``, {
+ bindingMetadata,
+ })
+ expect((root as CodegenRootNode).bindingComponents).toEqual({
+ Example: {
+ name: '_component_Example',
+ type: BindingComponentTypes.UNKNOWN,
+ },
+ })
+ })
+
+ describe('dynamic component', () => {
+ test('static binding', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_DYNAMIC_COMPONENT_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('capitalized version w/ static binding', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_DYNAMIC_COMPONENT_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('dynamic binding', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_DYNAMIC_COMPONENT_NOT_SUPPORTED,
+ })
+ )
+ })
+
+ test('v-is', () => {
+ const onError = jest.fn()
+ parseWithElementTransform(``, {
+ onError,
+ })
+ expect(onError).toHaveBeenCalledTimes(1)
+ expect(onError).toHaveBeenCalledWith(
+ expect.objectContaining({
+ code: MPErrorCodes.X_V_IS_NOT_SUPPORTED,
+ })
+ )
+ })
+ })
+})
diff --git a/packages/uni-mp-compiler/__tests__/vFor.spec.ts b/packages/uni-mp-compiler/__tests__/vFor.spec.ts
index d8281b376d3642130baad71711ffb2fb153917e2..57fa2747d9854538020b4dc218cce986d2362f37 100644
--- a/packages/uni-mp-compiler/__tests__/vFor.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vFor.spec.ts
@@ -481,7 +481,10 @@ describe(`compiler: v-for`, () => {
test('should prefix v-for source w/ complex expression', () => {
const { node } = parseWithForTransform(
``,
- { prefixIdentifiers: true, skipTransformIdentifier: true }
+ {
+ prefixIdentifiers: true,
+ skipTransformIdentifier: true,
+ }
)
expect(node.vFor.source).toMatchObject({
type: NodeTypes.COMPOUND_EXPRESSION,
diff --git a/packages/uni-mp-compiler/__tests__/vIf.spec.ts b/packages/uni-mp-compiler/__tests__/vIf.spec.ts
index ce4f100aac5a44a5a8f1640c096369a0ac844fc6..e2459d7ffea4717e53909f2985d953de3e415c9c 100644
--- a/packages/uni-mp-compiler/__tests__/vIf.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vIf.spec.ts
@@ -61,13 +61,13 @@ describe(`compiler: v-if`, () => {
)
})
test(`component v-if`, () => {
- assert(
- ``,
- ``,
- `(_ctx, _cache) => {
- return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
-}`
- )
+ // assert(
+ // ``,
+ // ``,
+ // `(_ctx, _cache) => {
+ // return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
+ // }`
+ // )
})
test(`v-if + v-else`, () => {
assert(
diff --git a/packages/uni-mp-compiler/src/codegen.ts b/packages/uni-mp-compiler/src/codegen.ts
index 4c0d904d9dc476b922e94fc9d5d62d7af11483cd..c08cdc4f4f529faa7cbf45ddd01b5406c7103edd 100644
--- a/packages/uni-mp-compiler/src/codegen.ts
+++ b/packages/uni-mp-compiler/src/codegen.ts
@@ -10,13 +10,17 @@ import {
TextNode,
TO_DISPLAY_STRING,
} from '@vue/compiler-core'
+import { Expression } from '@babel/types'
import { default as babelGenerate } from '@babel/generator'
-import { CodegenOptions, CodegenScope } from './options'
+import { addImportDeclaration, matchEasycom } from '@dcloudio/uni-cli-shared'
+import { CodegenOptions, CodegenRootNode } from './options'
import { createObjectExpression } from './ast'
-import { Expression } from '@babel/types'
+
+import { BindingComponentTypes, TransformContext } from './transform'
interface CodegenContext extends CodegenOptions {
code: string
+ bindingComponents: TransformContext['bindingComponents']
indentLevel: number
push(code: string, node?: CodegenNode): void
indent(): void
@@ -25,8 +29,7 @@ interface CodegenContext extends CodegenOptions {
}
export function generate(
- ast: RootNode,
- scope: CodegenScope,
+ ast: CodegenRootNode,
options: CodegenOptions
): Omit {
const context = createCodegenContext(ast, options)
@@ -82,7 +85,7 @@ export function generate(
}
push(`return `)
- push(genBabelExpr(createObjectExpression(scope.properties)))
+ push(genBabelExpr(createObjectExpression(ast.scope.properties)))
if (useWithBlock) {
deindent()
push(`}`)
@@ -96,7 +99,7 @@ export function generate(
}
function createCodegenContext(
- ast: RootNode,
+ ast: CodegenRootNode,
{
mode = 'function',
prefixIdentifiers = mode === 'module',
@@ -114,6 +117,7 @@ function createCodegenContext(
scopeId,
runtimeGlobalName,
runtimeModuleName,
+ bindingComponents: ast.bindingComponents,
isTS,
code: ``,
indentLevel: 0,
@@ -142,8 +146,45 @@ function createCodegenContext(
return context
}
+function genComponentImports(
+ bindingComponents: TransformContext['bindingComponents'],
+ { push }: CodegenContext
+) {
+ const importDeclarations: string[] = []
+ Object.keys(bindingComponents).forEach((tag) => {
+ const { name, type } = bindingComponents[tag]
+ if (type === BindingComponentTypes.UNKNOWN) {
+ const source = matchEasycom(tag)
+ if (source) {
+ addImportDeclaration(importDeclarations, name, source)
+ }
+ }
+ })
+ importDeclarations.forEach((str) => push(str))
+}
+
+function genComponents(
+ bindingComponents: TransformContext['bindingComponents'],
+ { push }: CodegenContext
+) {
+ const components = Object.keys(bindingComponents).map(
+ (tag) => bindingComponents[tag].name
+ )
+ if (components.length) {
+ push(`if (!Math) {`)
+ push(`Math.max.call(Max, ${components.map((name) => name).join(', ')})`)
+ push(`}`)
+ }
+}
+
function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
- const { prefixIdentifiers, push, newline, runtimeGlobalName } = context
+ const {
+ prefixIdentifiers,
+ push,
+ newline,
+ runtimeGlobalName,
+ bindingComponents,
+ } = context
const VueBinding = runtimeGlobalName
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
if (ast.helpers.length > 0) {
@@ -155,6 +196,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
push(`const _Vue = ${VueBinding}\n`)
}
}
+ genComponentImports(bindingComponents, context)
+ genComponents(bindingComponents, context)
newline()
push(`return `)
}
@@ -164,7 +207,7 @@ function genModulePreamble(
context: CodegenContext,
inline?: boolean
) {
- const { push, newline, runtimeModuleName } = context
+ const { push, newline, runtimeModuleName, bindingComponents } = context
if (ast.helpers.length) {
push(
`import { ${ast.helpers
@@ -172,6 +215,8 @@ function genModulePreamble(
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
)
}
+ genComponentImports(bindingComponents, context)
+ genComponents(bindingComponents, context)
newline()
if (!inline) {
push(`export `)
diff --git a/packages/uni-mp-compiler/src/compile.ts b/packages/uni-mp-compiler/src/compile.ts
index 413f273a44ac088adc49bab6aa05b9cacaef0688..579a7a9f10ec4cbd6628ddee3e1de1e9249474a5 100644
--- a/packages/uni-mp-compiler/src/compile.ts
+++ b/packages/uni-mp-compiler/src/compile.ts
@@ -56,7 +56,16 @@ export function baseCompile(template: string, options: CompilerOptions = {}) {
),
})
)
- const result = extend(generate(ast, context.scope, options), { ast })
+ const result = extend(
+ generate(
+ extend(ast, {
+ scope: context.scope,
+ bindingComponents: context.bindingComponents,
+ }),
+ options
+ ),
+ { ast }
+ )
if (options.filename && options.miniProgram?.emitFile) {
genTemplate(ast, {
filename: options.filename,
diff --git a/packages/uni-mp-compiler/src/errors.ts b/packages/uni-mp-compiler/src/errors.ts
index 2b914944a3aceda42a7db74832667e99a8664670..c237dcc005ce45ea064ea5633dd9b7b3ac31502c 100644
--- a/packages/uni-mp-compiler/src/errors.ts
+++ b/packages/uni-mp-compiler/src/errors.ts
@@ -5,6 +5,9 @@ export const enum MPErrorCodes {
X_V_BIND_DYNAMIC_ARGUMENT,
X_V_BIND_MODIFIER_PROP,
X_V_BIND_MODIFIER_ATTR,
+ X_V_IS_NOT_SUPPORTED,
+ X_NOT_SUPPORTED,
+ X_DYNAMIC_COMPONENT_NOT_SUPPORTED,
}
export const errorMessages: Record = {
@@ -15,4 +18,8 @@ export const errorMessages: Record = {
'v-bind:[name]="" is not supported.',
[MPErrorCodes.X_V_BIND_MODIFIER_PROP]: 'v-bind .prop is not supported',
[MPErrorCodes.X_V_BIND_MODIFIER_ATTR]: 'v-bind .attr is not supported',
+ [MPErrorCodes.X_DYNAMIC_COMPONENT_NOT_SUPPORTED]:
+ ' is not supported',
+ [MPErrorCodes.X_NOT_SUPPORTED]: 'not supported: ',
+ [MPErrorCodes.X_V_IS_NOT_SUPPORTED]: 'v-is not supported',
}
diff --git a/packages/uni-mp-compiler/src/options.ts b/packages/uni-mp-compiler/src/options.ts
index dfdd0ba6af89f4dc08b38c583921c0d4885d3934..b7000abc5dced7657d777e84b3840073c37aaae1 100644
--- a/packages/uni-mp-compiler/src/options.ts
+++ b/packages/uni-mp-compiler/src/options.ts
@@ -1,10 +1,19 @@
import { ParserPlugin } from '@babel/parser'
import { Expression, ObjectProperty, SpreadElement } from '@babel/types'
-import { BindingMetadata, CompilerError } from '@vue/compiler-core'
+import { BindingMetadata, CompilerError, RootNode } from '@vue/compiler-core'
import IdentifierGenerator from './identifier'
-import { DirectiveTransform, NodeTransform } from './transform'
+import {
+ DirectiveTransform,
+ NodeTransform,
+ TransformContext,
+} from './transform'
import { VForOptions } from './transforms/vFor'
+export interface CodegenRootNode extends RootNode {
+ scope: CodegenScope
+ bindingComponents: TransformContext['bindingComponents']
+}
+
export interface ErrorHandlingOptions {
onWarn?: (warning: CompilerError) => void
onError?: (error: CompilerError) => void
diff --git a/packages/uni-mp-compiler/src/transform.ts b/packages/uni-mp-compiler/src/transform.ts
index cb515791504d549ccd6772e662e201b641853113..98edc936fe76ba07164127e18ca947f97205f3ce 100644
--- a/packages/uni-mp-compiler/src/transform.ts
+++ b/packages/uni-mp-compiler/src/transform.ts
@@ -60,6 +60,11 @@ export interface ErrorHandlingOptions {
onError?: (error: CompilerError) => void
}
+export const enum BindingComponentTypes {
+ SELF = 'self',
+ SETUP = 'setup',
+ UNKNOWN = 'unknown',
+}
export interface TransformContext
extends Required> {
selfName: string | null
@@ -68,6 +73,10 @@ export interface TransformContext
childIndex: number
helpers: Map
components: Set
+ bindingComponents: Record<
+ string,
+ { type: BindingComponentTypes; name: string }
+ >
identifiers: { [name: string]: number | undefined }
cached: number
scopes: {
@@ -269,6 +278,7 @@ export function createTransformContext(
childIndex: 0,
helpers: new Map(),
components: new Set(),
+ bindingComponents: Object.create(null),
cached: 0,
identifiers,
scope: rootScope,
diff --git a/packages/uni-mp-compiler/src/transforms/transformElement.ts b/packages/uni-mp-compiler/src/transforms/transformElement.ts
index a7e55b2897caa33e07e4b338a7ab9772b16284df..ba0695d86c51b19566bbbf0c36ab05a73e8d0209 100644
--- a/packages/uni-mp-compiler/src/transforms/transformElement.ts
+++ b/packages/uni-mp-compiler/src/transforms/transformElement.ts
@@ -1,3 +1,4 @@
+import { camelize, capitalize } from '@vue/shared'
import {
NodeTypes,
ElementTypes,
@@ -8,10 +9,19 @@ import {
TemplateLiteral,
Property,
ExpressionNode,
+ isCoreComponent,
+ BindingTypes,
+ UNREF,
+ toValidAssetId,
+ findDir,
} from '@vue/compiler-core'
import { errorMessages, MPErrorCodes } from '../errors'
-import { NodeTransform, TransformContext } from '../transform'
+import {
+ BindingComponentTypes,
+ NodeTransform,
+ TransformContext,
+} from '../transform'
export interface DirectiveTransformResult {
props: Property[]
@@ -31,7 +41,10 @@ export const transformElement: NodeTransform = (node, context) => {
) {
return
}
-
+ const isComponent = node.tagType === ElementTypes.COMPONENT
+ if (isComponent) {
+ processComponent(node, context)
+ }
const { props } = node
if (props.length > 0) {
processProps(node, context)
@@ -39,6 +52,122 @@ export const transformElement: NodeTransform = (node, context) => {
}
}
+function processComponent(node: ElementNode, context: TransformContext) {
+ const { tag } = node
+ if (context.bindingComponents[tag]) {
+ return
+ }
+
+ // 1. dynamic component
+ if (isComponentTag(tag)) {
+ return context.onError(
+ createCompilerError(
+ MPErrorCodes.X_DYNAMIC_COMPONENT_NOT_SUPPORTED,
+ node.loc,
+ errorMessages
+ )
+ )
+ }
+ if (findDir(node, 'is')) {
+ return context.onError(
+ createCompilerError(
+ MPErrorCodes.X_V_IS_NOT_SUPPORTED,
+ node.loc,
+ errorMessages
+ )
+ )
+ }
+ // TODO not supported
+ // const isProp = findProp(node, 'is')
+ // if (isProp) {
+ // }
+ // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)
+ const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag)
+ if (builtIn) {
+ return context.onError(
+ createCompilerError(
+ MPErrorCodes.X_NOT_SUPPORTED,
+ node.loc,
+ errorMessages,
+ tag
+ )
+ )
+ }
+ // 3. user component (from setup bindings)
+ const fromSetup = resolveSetupReference(tag, context)
+ if (fromSetup) {
+ return (context.bindingComponents[tag] = {
+ name: fromSetup,
+ type: BindingComponentTypes.SETUP,
+ })
+ }
+ const dotIndex = tag.indexOf('.')
+ if (dotIndex > 0) {
+ return context.onError(
+ createCompilerError(
+ MPErrorCodes.X_NOT_SUPPORTED,
+ node.loc,
+ errorMessages,
+ tag
+ )
+ )
+ }
+
+ // 4. Self referencing component (inferred from filename)
+ if (context.selfName && capitalize(camelize(tag)) === context.selfName) {
+ return (context.bindingComponents[tag] = {
+ name: toValidAssetId(tag, `component`),
+ type: BindingComponentTypes.SELF,
+ })
+ }
+
+ // 5. user component (resolve)
+ context.bindingComponents[tag] = {
+ name: toValidAssetId(tag, `component`),
+ type: BindingComponentTypes.UNKNOWN,
+ }
+}
+
+function resolveSetupReference(name: string, context: TransformContext) {
+ const bindings = context.bindingMetadata
+ if (!bindings || bindings.__isScriptSetup === false) {
+ return
+ }
+
+ const camelName = camelize(name)
+ const PascalName = capitalize(camelName)
+ const checkType = (type: BindingTypes) => {
+ if (bindings[name] === type) {
+ return name
+ }
+ if (bindings[camelName] === type) {
+ return camelName
+ }
+ if (bindings[PascalName] === type) {
+ return PascalName
+ }
+ }
+
+ const fromConst = checkType(BindingTypes.SETUP_CONST)
+ if (fromConst) {
+ return context.inline
+ ? // in inline mode, const setup bindings (e.g. imports) can be used as-is
+ fromConst
+ : `$setup[${JSON.stringify(fromConst)}]`
+ }
+
+ const fromMaybeRef =
+ checkType(BindingTypes.SETUP_LET) ||
+ checkType(BindingTypes.SETUP_REF) ||
+ checkType(BindingTypes.SETUP_MAYBE_REF)
+ if (fromMaybeRef) {
+ return context.inline
+ ? // setup scope bindings that may be refs need to be unrefed
+ `${context.helperString(UNREF)}(${fromMaybeRef})`
+ : `$setup[${JSON.stringify(fromMaybeRef)}]`
+ }
+}
+
function processProps(node: ElementNode, context: TransformContext) {
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
@@ -108,81 +237,11 @@ function processProps(node: ElementNode, context: TransformContext) {
if (directiveTransform) {
prop.exp = directiveTransform(prop, node, context).props[0]
.value as ExpressionNode
- // const { arg } = prop
- // if (arg && arg.type === NodeTypes.SIMPLE_EXPRESSION && prop.exp) {
- // const { content } = arg
- // if (content === 'class') {
- // hasClassBinding = true
- // processClass(prop, props, context)
- // } else if (content === 'style') {
- // hasStyleBinding = true
- // processStyle(prop, props, context)
- // }
- // }
}
}
}
- // remove static class and static style
- // if (hasClassBinding) {
- // const staticClassPropIndex = findStaticClassIndex(props)
- // if (staticClassPropIndex > -1) {
- // props.splice(staticClassPropIndex, 1)
- // }
- // }
- // if (hasStyleBinding) {
- // const staticStylePropIndex = findStaticStyleIndex(props)
- // if (staticStylePropIndex > -1) {
- // props.splice(staticStylePropIndex, 1)
- // }
- // }
}
function isComponentTag(tag: string) {
return tag[0].toLowerCase() + tag.slice(1) === 'component'
}
-
-// function findStaticClassIndex(props: (AttributeNode | DirectiveNode)[]) {
-// return props.findIndex((prop) => prop.name === 'class')
-// }
-// function findStaticStyleIndex(props: (AttributeNode | DirectiveNode)[]) {
-// return props.findIndex((prop) => prop.name === 'style')
-// }
-
-// function processClass(
-// classBindingProp: DirectiveNode,
-// props: (AttributeNode | DirectiveNode)[],
-// context: TransformContext
-// ) {
-// if (!classBindingProp.exp) {
-// return
-// }
-// const staticClassPropIndex = findStaticClassIndex(props)
-// const staticClass =
-// staticClassPropIndex > -1
-// ? (props[staticClassPropIndex] as AttributeNode).value
-// : ''
-// const expr = parseExpr(classBindingProp.exp, context)
-// if (!expr) {
-// return
-// }
-// console.log(staticClass)
-// if (isObjectExpression(expr)) {
-// classBindingProp.exp = createSimpleExpression(
-// genBabelExpr(createClassBindingArrayExpression(expr))
-// )
-// }
-// }
-// function processStyle(
-// styleBindingPropprop: DirectiveNode,
-// props: (AttributeNode | DirectiveNode)[],
-// context: TransformContext
-// ) {
-// const staticStylePropIndex = findStaticStyleIndex(props)
-// const staticStyle =
-// staticStylePropIndex > -1
-// ? (props[staticStylePropIndex] as AttributeNode).value
-// : ''
-// if (staticStyle) {
-// console.log(staticStyle)
-// }
-// }
diff --git a/packages/uni-mp-vite/__tests__/test.js b/packages/uni-mp-vite/__tests__/test.js
index 4ecc63e4a4c647fb6df88bcec69f53a59b050dd9..5a6048b7eec89a4288cef9b10a622f2dd145f9cf 100644
--- a/packages/uni-mp-vite/__tests__/test.js
+++ b/packages/uni-mp-vite/__tests__/test.js
@@ -14,3 +14,5 @@ console.log(
depth: null,
})
)
+
+// import a from 'a.vue'
diff --git a/packages/uni-mp-vite/package.json b/packages/uni-mp-vite/package.json
index 6cda5853752115657cd21e14a33557c425c7b355..06ea55ca8fff41452667f20ff4643fbee3834d87 100644
--- a/packages/uni-mp-vite/package.json
+++ b/packages/uni-mp-vite/package.json
@@ -18,5 +18,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "Apache-2.0",
+ "dependencies": {
+ "magic-string": "^0.25.7"
+ },
"gitHead": "1efa8efd0a9eddeabdba75c020d015ebf31b8177"
}
diff --git a/packages/uni-mp-vite/src/index.ts b/packages/uni-mp-vite/src/index.ts
index c958be4c5479d02d38911a3ab07da69817b59238..f518d86f156fe87f11c03ecdff703901c24edb6d 100644
--- a/packages/uni-mp-vite/src/index.ts
+++ b/packages/uni-mp-vite/src/index.ts
@@ -1,4 +1,5 @@
import { uniMiniProgramPlugin, UniMiniProgramPluginOptions } from './plugin'
+import { uniComponentPlugin } from './plugins/component'
import { uniMainJsPlugin } from './plugins/mainJs'
import { uniManifestJsonPlugin } from './plugins/manifestJson'
import { uniPagesJsonPlugin } from './plugins/pagesJson'
@@ -11,5 +12,6 @@ export default (options: UniMiniProgramPluginOptions) => {
uniPagesJsonPlugin(options),
uniVirtualPlugin(options),
uniMiniProgramPlugin(options),
+ uniComponentPlugin(),
]
}
diff --git a/packages/uni-mp-vite/src/plugins/component.ts b/packages/uni-mp-vite/src/plugins/component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3bf13f409c25125cffa9177d601a566d493ca022
--- /dev/null
+++ b/packages/uni-mp-vite/src/plugins/component.ts
@@ -0,0 +1,40 @@
+import path from 'path'
+import { Plugin } from 'vite'
+import {
+ EXTNAME_VUE,
+ parseVueRequest,
+ findVueComponentImports,
+} from '@dcloudio/uni-cli-shared'
+import MagicString from 'magic-string'
+import { virtualComponentPath } from './virtual'
+
+export function uniComponentPlugin(): Plugin {
+ return {
+ name: 'vite:uni-mp-component',
+ async transform(code, id) {
+ const { filename, query } = parseVueRequest(id)
+ if (query.vue) {
+ return null
+ }
+ if (!EXTNAME_VUE.includes(path.extname(filename))) {
+ return null
+ }
+ const vueComponentImports = await findVueComponentImports(
+ code,
+ id,
+ this.resolve
+ )
+ if (!vueComponentImports.length) {
+ return null
+ }
+ const s = new MagicString(code)
+ const rewriteImports: string[] = []
+ vueComponentImports.forEach(({ n, ss, se }) => {
+ s.remove(ss, se)
+ rewriteImports.push(`import('${virtualComponentPath(n!)}')`)
+ })
+ s.prepend(`if(!Math){${rewriteImports.join(';')}}`)
+ return s.toString()
+ },
+ }
+}
diff --git a/packages/uni-vue/src/componentOptions/hooks.ts b/packages/uni-vue/src/componentOptions/hooks.ts
index be0736d6a3b349a34660e9ab8084ac24a540cd3f..f33619d723305627385fc0d3b60ee8e3724b7426 100644
--- a/packages/uni-vue/src/componentOptions/hooks.ts
+++ b/packages/uni-vue/src/componentOptions/hooks.ts
@@ -1,5 +1,5 @@
import { invokeHook } from '@dcloudio/uni-core'
-import { ON_LOAD, ON_SHOW } from '@dcloudio/uni-shared'
+import { LINEFEED, ON_LOAD, ON_SHOW } from '@dcloudio/uni-shared'
import { isArray, isFunction } from '@vue/shared'
import {
@@ -49,7 +49,7 @@ export function initHooks(
invokeHook(publicThis, ON_SHOW)
})
} catch (e: any) {
- console.error(e.message + '\n' + e.stack)
+ console.error(e.message + LINEFEED + e.stack)
}
}
}
diff --git a/yarn.lock b/yarn.lock
index 71ab88f5631296c5f8a0a43d78bc5230411f857c..597a64ece02eec9515a4e736cfa9efb9ad9621ce 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4616,7 +4616,7 @@ es-abstract@^1.19.1:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
-es-module-lexer@^0.9.0:
+es-module-lexer@^0.9.0, es-module-lexer@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==