From f3effdda5434bc33f00d4c940b3548a0476e3382 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Tue, 24 Aug 2021 16:47:43 +0800 Subject: [PATCH] feat: add uni-automator --- packages/shims-node.d.ts | 1 + packages/uni-app-plus/build.json | 1 + .../uni-app-plus/dist/uni-app-service.es.js | 12 +- .../uni-app-plus/dist/uni-app-view.umd.js | 2 +- packages/uni-app-plus/lib/automator.js | 1131 +++++++++++++ packages/uni-app-plus/lib/uni.automator.js | 415 +++++ packages/uni-app-plus/package.json | 5 +- packages/uni-app-plus/src/view/index.ts | 2 + .../lib/template/__uniappautomator.js | 530 ++++++ .../lib/template/__uniappview.html | 3 +- packages/uni-app-vite/src/plugins/template.ts | 5 + packages/uni-automator/dist/environment.js | 44 + packages/uni-automator/dist/index.js | 1467 +++++++++++++++++ packages/uni-automator/dist/teardown.js | 13 + packages/uni-automator/lib/uni.plugin.js | 56 + packages/uni-automator/package.json | 41 + packages/uni-automator/src/uni.plugin.ts | 47 + packages/uni-automator/tsconfig.json | 11 + .../webpack/config/module/rules/fileLoader.ts | 16 + .../src/webpack/config/module/rules/index.ts | 2 + packages/uni-cli-shared/src/constants.ts | 1 + packages/uni-cli-shared/src/env/provide.ts | 4 +- packages/uni-h5/lib/automator.js | 1001 +++++++++++ packages/uni-h5/lib/uni.automator.js | 133 ++ packages/uni-h5/package.json | 5 +- packages/uni-mp-baidu/lib/automator.js | 488 ++++++ packages/uni-mp-baidu/lib/uni.automator.js | 556 +++++++ packages/uni-mp-baidu/package.json | 4 + packages/uni-mp-weixin/lib/automator.js | 489 ++++++ packages/uni-mp-weixin/lib/uni.automator.js | 361 ++++ packages/uni-mp-weixin/package.json | 4 + packages/uni-shared/dist/uni-shared.cjs.js | 56 + packages/uni-shared/dist/uni-shared.d.ts | 2 + packages/uni-shared/dist/uni-shared.es.js | 59 +- .../src/hbx/formatLog.ts | 2 +- packages/uni-shared/src/hbx/index.ts | 1 + packages/uni-shared/src/index.ts | 1 + packages/uni-stat/dist/uni-stat.cjs.js | 74 +- packages/uni-stat/dist/uni-stat.es.js | 74 +- packages/vite-plugin-uni/src/cli/index.ts | 4 + packages/vite-plugin-uni/src/cli/utils.ts | 42 +- .../src/config/optimizeDeps.ts | 1 + .../src/configResolved/plugins/resolveId.ts | 1 + scripts/build.js | 2 +- yarn.lock | 113 +- 45 files changed, 7144 insertions(+), 138 deletions(-) create mode 100644 packages/uni-app-plus/lib/automator.js create mode 100644 packages/uni-app-plus/lib/uni.automator.js create mode 100644 packages/uni-app-vite/lib/template/__uniappautomator.js create mode 100644 packages/uni-automator/dist/environment.js create mode 100644 packages/uni-automator/dist/index.js create mode 100644 packages/uni-automator/dist/teardown.js create mode 100644 packages/uni-automator/lib/uni.plugin.js create mode 100644 packages/uni-automator/package.json create mode 100644 packages/uni-automator/src/uni.plugin.ts create mode 100644 packages/uni-automator/tsconfig.json create mode 100644 packages/uni-cli-nvue/src/webpack/config/module/rules/fileLoader.ts create mode 100644 packages/uni-h5/lib/automator.js create mode 100644 packages/uni-h5/lib/uni.automator.js create mode 100644 packages/uni-mp-baidu/lib/automator.js create mode 100644 packages/uni-mp-baidu/lib/uni.automator.js create mode 100644 packages/uni-mp-weixin/lib/automator.js create mode 100644 packages/uni-mp-weixin/lib/uni.automator.js rename packages/{uni-cli-shared => uni-shared}/src/hbx/formatLog.ts (98%) create mode 100644 packages/uni-shared/src/hbx/index.ts diff --git a/packages/shims-node.d.ts b/packages/shims-node.d.ts index ab79ca175..0b13dc33b 100644 --- a/packages/shims-node.d.ts +++ b/packages/shims-node.d.ts @@ -14,5 +14,6 @@ declare namespace NodeJS { UNI_NVUE_COMPILER: 'uni-app' | 'weex' | 'vue' UNI_NVUE_STYLE_COMPILER: 'uni-app' | 'weex' UNI_APP_CODE_SPLITING?: 'true' + UNI_AUTOMATOR_WS_ENDPOINT?: string } } diff --git a/packages/uni-app-plus/build.json b/packages/uni-app-plus/build.json index 95cbf7eb1..d3d447a31 100644 --- a/packages/uni-app-plus/build.json +++ b/packages/uni-app-plus/build.json @@ -4,6 +4,7 @@ "src/service/index.ts": "dist/uni-app-service.es.js" }, "output": { + "freeze": false, "name": "serviceContext", "format": "iife", "banner": "export function createServiceContext(weex, plus, instanceContext){\nconst Vue = instanceContext.Vue;\nlet setTimeout = instanceContext.setTimeout;\nlet clearTimeout = instanceContext.clearTimeout;\nlet setInterval = instanceContext.setInterval;\nlet clearInterval = instanceContext.clearInterval;\nconst __uniConfig = instanceContext.__uniConfig;\nconst __uniRoutes = instanceContext.__uniRoutes;\n", 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 be8a1cda2..b6ea0c536 100644 --- a/packages/uni-app-plus/dist/uni-app-service.es.js +++ b/packages/uni-app-plus/dist/uni-app-service.es.js @@ -2157,14 +2157,14 @@ var serviceContext = (function (vue) { return querySelectorAll(this, selector); } - var wxInstance = /*#__PURE__*/Object.freeze({ + var wxInstance = { __proto__: null, createSelectorQuery: createSelectorQuery$1, createMediaQueryObserver: createMediaQueryObserver$1, createIntersectionObserver: createIntersectionObserver$1, selectComponent: selectComponent, selectAllComponents: selectAllComponents - }); + }; function getOpenerEventChannel() { // TODO App @@ -12042,7 +12042,7 @@ var serviceContext = (function (vue) { }); }, PreloadPageProtocol); - var uni$1 = /*#__PURE__*/Object.freeze({ + var uni$1 = { __proto__: null, navigateTo: navigateTo, switchTab: switchTab, @@ -12212,7 +12212,7 @@ var serviceContext = (function (vue) { reLaunch: reLaunch, unPreloadPage: unPreloadPage, preloadPage: preloadPage - }); + }; const UniServiceJSBridge$1 = /*#__PURE__*/ extend(ServiceJSBridge, { publishHandler, @@ -18952,7 +18952,7 @@ var serviceContext = (function (vue) { constants: constants_1 }; - var pako_esm = /*#__PURE__*/Object.freeze({ + var pako_esm = { __proto__: null, Deflate: Deflate_1, Inflate: Inflate_1, @@ -18964,7 +18964,7 @@ var serviceContext = (function (vue) { inflate: inflate_1, inflateRaw: inflateRaw_1, ungzip: ungzip_1 - }); + }; return index; diff --git a/packages/uni-app-plus/dist/uni-app-view.umd.js b/packages/uni-app-plus/dist/uni-app-view.umd.js index 0eedc472b..2def7ab11 100644 --- a/packages/uni-app-plus/dist/uni-app-view.umd.js +++ b/packages/uni-app-plus/dist/uni-app-view.umd.js @@ -1 +1 @@ -!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";var e={exports:{}},t={exports:{}},n={exports:{}},r=n.exports={version:"2.6.12"};"number"==typeof __e&&(__e=r);var i={exports:{}},a=i.exports=void 0!==a&&a.Math==Math?a:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=a);var o=n.exports,s=i.exports,l="__core-js_shared__",u=s[l]||(s[l]={});(t.exports=function(e,t){return u[e]||(u[e]=void 0!==t?t:{})})("versions",[]).push({version:o.version,mode:"window",copyright:"© 2020 Denis Pushkarev (zloirock.ru)"});var c=0,d=Math.random(),h=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++c+d).toString(36))},p=t.exports("wks"),f=h,v=i.exports.Symbol,g="function"==typeof v;(e.exports=function(e){return p[e]||(p[e]=g&&v[e]||(g?v:f)("Symbol."+e))}).store=p;var m={},y=function(e){return"object"==typeof e?null!==e:"function"==typeof e},_=y,b=function(e){if(!_(e))throw TypeError(e+" is not an object!");return e},w=function(e){try{return!!e()}catch(t){return!0}},x=!w((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})),S=y,T=i.exports.document,E=S(T)&&S(T.createElement),k=function(e){return E?T.createElement(e):{}},C=!x&&!w((function(){return 7!=Object.defineProperty(k("div"),"a",{get:function(){return 7}}).a})),M=y,O=b,I=C,L=function(e,t){if(!M(e))return e;var n,r;if(t&&"function"==typeof(n=e.toString)&&!M(r=n.call(e)))return r;if("function"==typeof(n=e.valueOf)&&!M(r=n.call(e)))return r;if(!t&&"function"==typeof(n=e.toString)&&!M(r=n.call(e)))return r;throw TypeError("Can't convert object to primitive value")},N=Object.defineProperty;m.f=x?Object.defineProperty:function(e,t,n){if(O(e),t=L(t,!0),O(n),I)try{return N(e,t,n)}catch(r){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e};var A=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}},P=m,R=A,B=x?function(e,t,n){return P.f(e,t,R(1,n))}:function(e,t,n){return e[t]=n,e},D=e.exports("unscopables"),$=Array.prototype;null==$[D]&&B($,D,{});var F={},W={}.toString,j=function(e){return W.call(e).slice(8,-1)},V=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e},z=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==j(e)?e.split(""):Object(e)},H=V,q=function(e){return z(H(e))},U={exports:{}},Y={}.hasOwnProperty,X=function(e,t){return Y.call(e,t)},G=t.exports("native-function-to-string",Function.toString),J=i.exports,K=B,Z=X,Q=h("src"),ee=G,te="toString",ne=(""+ee).split(te);n.exports.inspectSource=function(e){return ee.call(e)},(U.exports=function(e,t,n,r){var i="function"==typeof n;i&&(Z(n,"name")||K(n,"name",t)),e[t]!==n&&(i&&(Z(n,Q)||K(n,Q,e[t]?""+e[t]:ne.join(String(t)))),e===J?e[t]=n:r?e[t]?e[t]=n:K(e,t,n):(delete e[t],K(e,t,n)))})(Function.prototype,te,(function(){return"function"==typeof this&&this[Q]||ee.call(this)}));var re=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e},ie=re,ae=i.exports,oe=n.exports,se=B,le=U.exports,ue=function(e,t,n){if(ie(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}},ce=function(e,t,n){var r,i,a,o,s=e&ce.F,l=e&ce.G,u=e&ce.S,c=e&ce.P,d=e&ce.B,h=l?ae:u?ae[t]||(ae[t]={}):(ae[t]||{}).prototype,p=l?oe:oe[t]||(oe[t]={}),f=p.prototype||(p.prototype={});for(r in l&&(n=t),n)a=((i=!s&&h&&void 0!==h[r])?h:n)[r],o=d&&i?ue(a,ae):c&&"function"==typeof a?ue(Function.call,a):a,h&&le(h,r,a,e&ce.U),p[r]!=a&&se(p,r,o),c&&f[r]!=a&&(f[r]=a)};ae.core=oe,ce.F=1,ce.G=2,ce.S=4,ce.P=8,ce.B=16,ce.W=32,ce.U=64,ce.R=128;var de,he=ce,pe=Math.ceil,fe=Math.floor,ve=function(e){return isNaN(e=+e)?0:(e>0?fe:pe)(e)},ge=ve,me=Math.min,ye=ve,_e=Math.max,be=Math.min,we=q,xe=function(e){return e>0?me(ge(e),9007199254740991):0},Se=function(e,t){return(e=ye(e))<0?_e(e+t,0):be(e,t)},Te=t.exports("keys"),Ee=h,ke=function(e){return Te[e]||(Te[e]=Ee(e))},Ce=X,Me=q,Oe=(de=!1,function(e,t,n){var r,i=we(e),a=xe(i.length),o=Se(n,a);if(de&&t!=t){for(;a>o;)if((r=i[o++])!=r)return!0}else for(;a>o;o++)if((de||o in i)&&i[o]===t)return de||o||0;return!de&&-1}),Ie=ke("IE_PROTO"),Le="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(","),Ne=function(e,t){var n,r=Me(e),i=0,a=[];for(n in r)n!=Ie&&Ce(r,n)&&a.push(n);for(;t.length>i;)Ce(r,n=t[i++])&&(~Oe(a,n)||a.push(n));return a},Ae=Le,Pe=Object.keys||function(e){return Ne(e,Ae)},Re=m,Be=b,De=Pe,$e=x?Object.defineProperties:function(e,t){Be(e);for(var n,r=De(t),i=r.length,a=0;i>a;)Re.f(e,n=r[a++],t[n]);return e},Fe=i.exports.document,We=Fe&&Fe.documentElement,je=b,Ve=$e,ze=Le,He=ke("IE_PROTO"),qe=function(){},Ue=function(){var e,t=k("iframe"),n=ze.length;for(t.style.display="none",We.appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" - + diff --git a/packages/uni-app-vite/src/plugins/template.ts b/packages/uni-app-vite/src/plugins/template.ts index a8172e233..57efb6bcc 100644 --- a/packages/uni-app-vite/src/plugins/template.ts +++ b/packages/uni-app-vite/src/plugins/template.ts @@ -27,10 +27,15 @@ function genViewHtml(bundle: OutputBundle) { ? `` : '' + const automatorCode = process.env.UNI_AUTOMATOR_WS_ENDPOINT + ? `` + : '' + return viewHtmlStr .toString() .replace('', wxsCode) .replace('', renderjsCode) + .replace('', automatorCode) .replace( '/*__uniConfig*/', `var __uniConfig = ${JSON.stringify(__uniConfig)}` diff --git a/packages/uni-automator/dist/environment.js b/packages/uni-automator/dist/environment.js new file mode 100644 index 000000000..f4fd45a63 --- /dev/null +++ b/packages/uni-automator/dist/environment.js @@ -0,0 +1,44 @@ +'use strict'; + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var NodeEnvironment = _interopDefault(require('jest-environment-node')); +var Automator = _interopDefault(require('./index.js')); + +const automator = new Automator(); +class UniAutomatorEnvironment extends NodeEnvironment { + constructor(config, context) { + super(config); + if (process.env.UNI_AUTOMATOR_CONFIG) { + this.launchOptions = require(process.env.UNI_AUTOMATOR_CONFIG); + } + else { + this.launchOptions = config.testEnvironmentOptions; + } + } + async setup() { + await super.setup(); + const globalThis = global; + if (!globalThis.__init__) { + globalThis.__init__ = true; + // 必须启用runInBand,否则会launch多次 + this.launchOptions.platform = + this.launchOptions.platform || process.env.UNI_PLATFORM; + globalThis.program = await automator.launch(this.launchOptions); + if (this.launchOptions.devtools && this.launchOptions.devtools.remote) { + await globalThis.program.remote(true); + } + } + else { + if (!globalThis.program) { + throw Error(`Program init failed`); + } + } + this.global.program = globalThis.program; + } + async teardown() { + await super.teardown(); + } +} + +module.exports = UniAutomatorEnvironment; diff --git a/packages/uni-automator/dist/index.js b/packages/uni-automator/dist/index.js new file mode 100644 index 000000000..8495752b3 --- /dev/null +++ b/packages/uni-automator/dist/index.js @@ -0,0 +1,1467 @@ +'use strict'; + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var fs = _interopDefault(require('fs')); +var path = _interopDefault(require('path')); +var debug = _interopDefault(require('debug')); +var isRelative = _interopDefault(require('licia/isRelative')); +var WebSocket = _interopDefault(require('ws')); +var events = require('events'); +var uuid = _interopDefault(require('licia/uuid')); +var stringify = _interopDefault(require('licia/stringify')); +var dateFormat = _interopDefault(require('licia/dateFormat')); +var waitUntil = _interopDefault(require('licia/waitUntil')); +var fs$1 = _interopDefault(require('licia/fs')); +var isFn = _interopDefault(require('licia/isFn')); +var trim = _interopDefault(require('licia/trim')); +var isStr = _interopDefault(require('licia/isStr')); +var startWith = _interopDefault(require('licia/startWith')); +var isNum = _interopDefault(require('licia/isNum')); +var sleep$1 = _interopDefault(require('licia/sleep')); +var isUndef = _interopDefault(require('licia/isUndef')); +var address = _interopDefault(require('address')); +var defaultGateway = _interopDefault(require('default-gateway')); +var getPort = _interopDefault(require('licia/getPort')); +var child_process = require('child_process'); +var toStr = _interopDefault(require('licia/toStr')); + +class Transport extends events.EventEmitter { + constructor(ws) { + super(); + this.ws = ws; + this.ws.addEventListener("message", (event) => { + this.emit("message", event.data); + }); + this.ws.addEventListener("close", () => { + this.emit("close"); + }); + } + send(message) { + this.ws.send(message); + } + close() { + this.ws.close(); + } +} + +const CLOSE_ERR_TIP = "Connection closed"; +class Connection extends events.EventEmitter { + constructor(transport, puppet, namespace) { + super(); + this.puppet = puppet; + this.namespace = namespace; + this.callbacks = new Map(); + this.transport = transport; + this.debug = debug("automator:protocol:" + this.namespace); + this.onMessage = (msg) => { + this.debug(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} ◀ RECV ${msg}`); + const { id, method, error, result, params } = JSON.parse(msg); + if (!id) { + return this.puppet.emit(method, params); + } + const { callbacks } = this; + if (id && callbacks.has(id)) { + const promise = callbacks.get(id); + callbacks.delete(id); + error ? promise.reject(Error(error.message)) : promise.resolve(result); + } + }; + this.onClose = () => { + this.callbacks.forEach((promise) => { + promise.reject(Error(CLOSE_ERR_TIP)); + }); + }; + this.transport.on("message", this.onMessage); + this.transport.on("close", this.onClose); + } + send(method, params = {}, reflect = true) { + if (reflect && this.puppet.adapter.has(method)) { + return this.puppet.adapter.send(this, method, params); + } + const id = uuid(); + const data = stringify({ + id, + method, + params, + }); + this.debug(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} SEND ► ${data}`); + return new Promise((resolve, reject) => { + try { + this.transport.send(data); + } + catch (e) { + reject(Error(CLOSE_ERR_TIP)); + } + this.callbacks.set(id, { + resolve, + reject, + }); + }); + } + dispose() { + this.transport.close(); + } + static createDevtoolConnection(url, puppet) { + return new Promise((resolve, reject) => { + const ws = new WebSocket(url); + ws.addEventListener("open", () => { + resolve(new Connection(new Transport(ws), puppet, "devtool")); + }); + ws.addEventListener("error", reject); + }); + } + static createRuntimeConnection(port, puppet, timeout) { + return new Promise((resolve, reject) => { + debug("automator:runtime")(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} port=${port}`); + const wss = new WebSocket.Server({ + port, + }); + waitUntil(async () => { + if (puppet.runtimeConnection) { + return true; + } + }, timeout, 1e3).catch(() => { + wss.close(); + reject("Failed to connect to runtime, please make sure the project is running"); + }); + wss.on("connection", function connection(ws) { + debug("automator:runtime")(`${dateFormat("yyyy-mm-dd HH:MM:ss:l")} connected`); + const connection = new Connection(new Transport(ws), puppet, "runtime"); + // 可能会被重新连接,刷新成最新的 + puppet.setRuntimeConnection(connection); + resolve(connection); + }); + puppet.setRuntimeServer(wss); + }); + } +} + +const qrCodeTerminal = require("qrcode-terminal"); +const QrCodeReader = require("qrcode-reader"); +const isWin = /^win/.test(process.platform); +function printQrCode(content) { + return new Promise((resolve) => { + qrCodeTerminal.generate(content, { + small: true, + }, (qrcode) => { + process.stdout.write(qrcode); + resolve(); + }); + }); +} +function toArray(str) { + if (isStr(str)) { + return [true, [str]]; + } + return [false, str]; +} +async function invokeManyToMany(fn, str) { + const [isSingle, strArr] = toArray(str); + const result = await fn(strArr); + return isSingle ? result[0] : result; +} +async function resolvePort(port, defaultPort) { + const newPort = await getPort(port || defaultPort); + if (port && newPort !== port) { + throw Error(`Port ${port} is in use, please specify another port`); + } + return newPort; +} +function getWsEndpoint(port) { + let host; + try { + // This can only return an IPv4 address + const result = defaultGateway.v4.sync(); + host = address.ip(result && result.interface); + if (host) { + // Check if the address is a private ip + // https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces + if (!/^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(host)) { + // Address is not private, so we will discard it + host = undefined; + } + } + } + catch (_e) { + // ignored + } + return "ws://" + (host || "localhost") + ":" + port; +} + +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +function __decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} + +var TYPES; +(function (TYPES) { + TYPES["RUNTIME"] = "runtime"; + TYPES["DEVTOOL"] = "devtool"; +})(TYPES || (TYPES = {})); +function wrapper(type, descriptor) { + const method = descriptor.value; + descriptor.value = async function (params) { + const fn = await (method === null || method === void 0 ? void 0 : method.call(this, params)); + return fn(type); + }; + return descriptor; +} +function runtime(target, propertyName, descriptor) { + return wrapper(TYPES.RUNTIME, descriptor); +} +function devtool(target, propertyName, descriptor) { + return wrapper(TYPES.DEVTOOL, descriptor); +} +class Base { + constructor(puppet) { + this.puppet = puppet; + } + invoke(method, params) { + return async (type) => { + if (!this.puppet.devtoolConnection) { + return this.puppet.runtimeConnection.send(method, params); + } + return (type === TYPES.DEVTOOL + ? this.puppet.devtoolConnection + : this.puppet.runtimeConnection).send(method, params); + }; + } + on(method, listener) { + this.puppet.on(method, listener); + } +} + +class Element extends Base { + constructor(puppet, options) { + super(puppet); + this.id = options.elementId; + this.pageId = options.pageId; + this.nodeId = options.nodeId; + this.videoId = options.videoId; + } + async getData(params) { + return this.invokeMethod("Element.getData", params); + } + async setData(params) { + return this.invokeMethod("Element.setData", params); + } + async callMethod(params) { + return this.invokeMethod("Element.callMethod", params); + } + async getElement(params) { + return this.invokeMethod("Element.getElement", params); + } + async getElements(params) { + return this.invokeMethod("Element.getElements", params); + } + async getOffset() { + return this.invokeMethod("Element.getOffset"); + } + async getHTML(params) { + return this.invokeMethod("Element.getHTML", params); + } + async getAttributes(params) { + return this.invokeMethod("Element.getAttributes", params); + } + async getStyles(params) { + return this.invokeMethod("Element.getStyles", params); + } + async getDOMProperties(params) { + return this.invokeMethod("Element.getDOMProperties", params); + } + async getProperties(params) { + return this.invokeMethod("Element.getProperties", params); + } + async tap() { + return this.invokeMethod("Element.tap"); + } + async longpress() { + return this.invokeMethod("Element.longpress"); + } + async touchstart(params) { + return this.invokeMethod("Element.touchstart", params); + } + async touchmove(params) { + return this.invokeMethod("Element.touchmove", params); + } + async touchend(params) { + return this.invokeMethod("Element.touchend", params); + } + async triggerEvent(params) { + return this.invokeMethod("Element.triggerEvent", params); + } + async callFunction(params) { + return this.invokeMethod("Element.callFunction", params); + } + async callContextMethod(params) { + return this.invokeMethod("Element.callContextMethod", params); + } + invokeMethod(method, params = {}) { + params.elementId = this.id; + params.pageId = this.pageId; + this.nodeId && (params.nodeId = this.nodeId); + this.videoId && (params.videoId = this.videoId); + return this.invoke(method, params); + } +} +__decorate([ + runtime +], Element.prototype, "getData", null); +__decorate([ + runtime +], Element.prototype, "setData", null); +__decorate([ + runtime +], Element.prototype, "callMethod", null); +__decorate([ + devtool +], Element.prototype, "getElement", null); +__decorate([ + devtool +], Element.prototype, "getElements", null); +__decorate([ + devtool +], Element.prototype, "getOffset", null); +__decorate([ + devtool +], Element.prototype, "getHTML", null); +__decorate([ + devtool +], Element.prototype, "getAttributes", null); +__decorate([ + devtool +], Element.prototype, "getStyles", null); +__decorate([ + devtool +], Element.prototype, "getDOMProperties", null); +__decorate([ + devtool +], Element.prototype, "getProperties", null); +__decorate([ + devtool +], Element.prototype, "tap", null); +__decorate([ + devtool +], Element.prototype, "longpress", null); +__decorate([ + devtool +], Element.prototype, "touchstart", null); +__decorate([ + devtool +], Element.prototype, "touchmove", null); +__decorate([ + devtool +], Element.prototype, "touchend", null); +__decorate([ + devtool +], Element.prototype, "triggerEvent", null); +__decorate([ + devtool +], Element.prototype, "callFunction", null); +__decorate([ + devtool +], Element.prototype, "callContextMethod", null); + +const util = require("util"); +class Element$1 { + constructor(puppet, options, elementMap) { + this.puppet = puppet; + this.id = options.elementId; + this.pageId = options.pageId; + this.nodeId = options.nodeId || null; + this.videoId = options.videoId || null; + this.tagName = options.tagName; + this.nvue = options.nvue; + this.elementMap = elementMap; + // 统一格式化为 page + if (this.tagName === "body" || this.tagName === "page-body") { + this.tagName = "page"; + } + this.api = new Element(puppet, options); + } + toJSON() { + return JSON.stringify({ + id: this.id, + tagName: this.tagName, + pageId: this.pageId, + nodeId: this.nodeId, + videoId: this.videoId, + }); + } + toString() { + return this.toJSON(); + } + [util.inspect.custom]() { + return this.toJSON(); + } + async $(selector) { + try { + const element = await this.api.getElement({ selector }); + return Element$1.create(this.puppet, Object.assign({}, element, { + pageId: this.pageId, + }), this.elementMap); + } + catch (e) { + return null; + } + } + async $$(selector) { + const { elements } = await this.api.getElements({ selector }); + return elements.map((elem) => Element$1.create(this.puppet, Object.assign({}, elem, { + pageId: this.pageId, + }), this.elementMap)); + } + async size() { + const [width, height] = await this.domProperty([ + "offsetWidth", + "offsetHeight", + ]); + return { + width, + height, + }; + } + async offset() { + const { left, top } = await this.api.getOffset(); + return { + left, + top, + }; + } + async text() { + return this.domProperty("innerText"); + } + async attribute(name) { + if (!isStr(name)) { + throw Error("name must be a string"); + } + return (await this.api.getAttributes({ names: [name] })).attributes[0]; + } + async value() { + return this.property("value"); + } + async property(name) { + if (!isStr(name)) { + throw Error("name must be a string"); + } + if (this.puppet.checkProperty) { + let props = this.publicProps; + if (!props) { + this.publicProps = props = await this._property("__propPublic"); + } + if (!props[name]) { + throw Error(`${this.tagName}.${name} not exists`); + } + } + return this._property(name); + } + async html() { + return (await this.api.getHTML({ type: "inner" })).html; + } + async outerHtml() { + return (await this.api.getHTML({ type: "outer" })).html; + } + async style(name) { + if (!isStr(name)) { + throw Error("name must be a string"); + } + return (await this.api.getStyles({ names: [name] })).styles[0]; + } + async tap() { + return this.api.tap(); + } + async longpress() { + if (this.nvue) { + return this.api.longpress(); + } + await this.touchstart(); + await sleep$1(350); + return this.touchend(); + } + async trigger(type, detail) { + const event = { + type, + }; + if (!isUndef(detail)) { + event.detail = detail; + } + return this.api.triggerEvent(event); + } + async touchstart(options) { + return this.api.touchstart(options); + } + async touchmove(options) { + return this.api.touchmove(options); + } + async touchend(options) { + return this.api.touchend(options); + } + async domProperty(name) { + return invokeManyToMany(async (names) => (await this.api.getDOMProperties({ names })).properties, name); + } + _property(name) { + return invokeManyToMany(async (names) => (await this.api.getProperties({ names })).properties, name); + } + send(method, params) { + params.elementId = this.id; + params.pageId = this.pageId; + this.nodeId && (params.nodeId = this.nodeId); + this.videoId && (params.videoId = this.videoId); + return this.puppet.send(method, params); + } + async callFunction(functionName, ...args) { + return (await this.api.callFunction({ + functionName, + args, + })).result; + } + static create(puppet, options, elementMap) { + let element = elementMap.get(options.elementId); + if (element) { + return element; + } + let ElementClass; + if (options.nodeId) { + ElementClass = CustomElement; + } + else { + switch (options.tagName) { + case "input": + ElementClass = InputElement; + break; + case "textarea": + ElementClass = TextareaElement; + break; + case "scroll-view": + ElementClass = ScrollViewElement; + break; + case "swiper": + ElementClass = SwiperElement; + break; + case "movable-view": + ElementClass = MovableViewElement; + break; + case "switch": + ElementClass = SwitchElement; + break; + case "slider": + ElementClass = SliderElement; + break; + case "video": + ElementClass = ContextElement; + break; + default: + ElementClass = Element$1; + } + } + element = new ElementClass(puppet, options, elementMap); + elementMap.set(options.elementId, element); + return element; + } +} +class CustomElement extends Element$1 { + async setData(data) { + return this.api.setData({ data }); + } + async data(path) { + const data = {}; + if (path) { + data.path = path; + } + return (await this.api.getData(data)).data; + } + async callMethod(method, ...args) { + return (await this.api.callMethod({ + method, + args, + })).result; + } +} +class InputElement extends Element$1 { + async input(value) { + return this.callFunction("input.input", value); + } +} +class TextareaElement extends Element$1 { + async input(value) { + return this.callFunction("textarea.input", value); + } +} +class ScrollViewElement extends Element$1 { + async scrollTo(x, y) { + return this.callFunction("scroll-view.scrollTo", x, y); + } + async property(name) { + if (name === "scrollTop") { + return this.callFunction("scroll-view.scrollTop"); + } + else if (name === "scrollLeft") { + return this.callFunction("scroll-view.scrollLeft"); + } + return super.property(name); + } + async scrollWidth() { + return this.callFunction("scroll-view.scrollWidth"); + } + async scrollHeight() { + return this.callFunction("scroll-view.scrollHeight"); + } +} +class SwiperElement extends Element$1 { + async swipeTo(index) { + return this.callFunction("swiper.swipeTo", index); + } +} +class MovableViewElement extends Element$1 { + async moveTo(x, y) { + return this.callFunction("movable-view.moveTo", x, y); + } + async property(name) { + if (name === "x") { + return this._property("_translateX"); + } + else if (name === "y") { + return this._property("_translateY"); + } + return super.property(name); + } +} +class SwitchElement extends Element$1 { + async tap() { + return this.callFunction("switch.tap"); + } +} +class SliderElement extends Element$1 { + async slideTo(value) { + return this.callFunction("slider.slideTo", value); + } +} +class ContextElement extends Element$1 { + async callContextMethod(method, ...args) { + const result = await this.api.callContextMethod({ + method, + args, + }); + return result; + } +} + +class Page extends Base { + constructor(puppet, options) { + super(puppet); + this.id = options.id; + } + async getData(params) { + return this.invokeMethod("Page.getData", params); + } + async setData(params) { + return this.invokeMethod("Page.setData", params); + } + async callMethod(params) { + return this.invokeMethod("Page.callMethod", params); + } + async getElement(params) { + return this.invokeMethod("Page.getElement", params); + } + async getElements(params) { + return this.invokeMethod("Page.getElements", params); + } + async getWindowProperties(params) { + return this.invokeMethod("Page.getWindowProperties", params); + } + invokeMethod(method, params = {}) { + params.pageId = this.id; + return this.invoke(method, params); + } +} +__decorate([ + runtime +], Page.prototype, "getData", null); +__decorate([ + runtime +], Page.prototype, "setData", null); +__decorate([ + runtime +], Page.prototype, "callMethod", null); +__decorate([ + devtool +], Page.prototype, "getElement", null); +__decorate([ + devtool +], Page.prototype, "getElements", null); +__decorate([ + devtool +], Page.prototype, "getWindowProperties", null); + +const util$1 = require("util"); +class Page$1 { + constructor(puppet, options) { + this.puppet = puppet; + this.id = options.id; + this.path = options.path; + this.query = options.query; + this.elementMap = new Map(); + this.api = new Page(puppet, options); + } + toJSON() { + return JSON.stringify({ id: this.id, path: this.path, query: this.query }); + } + toString() { + return this.toJSON(); + } + [util$1.inspect.custom]() { + return this.toJSON(); + } + async waitFor(condition) { + if (isNum(condition)) { + return await sleep$1(condition); + } + else if (isFn(condition)) { + return waitUntil(condition); + } + else if (isStr(condition)) { + return waitUntil(async () => { + const elms = await this.$$(condition); + return elms.length > 0; + }); + } + } + async $(selector) { + try { + const page = await this.api.getElement({ selector }); + return Element$1.create(this.puppet, Object.assign({ + selector, + }, page, { + pageId: this.id, + }), this.elementMap); + } + catch (t) { + return null; + } + } + async $$(selector) { + const { elements } = await this.api.getElements({ selector }); + return elements.map((elem) => Element$1.create(this.puppet, Object.assign({ + selector, + }, elem, { + pageId: this.id, + }), this.elementMap)); + } + async data(path) { + const payload = {}; + if (path) { + payload.path = path; + } + return (await this.api.getData(payload)).data; + } + async setData(data) { + return this.api.setData({ data }); + } + async size() { + const [width, height] = await this.windowProperty([ + "document.documentElement.scrollWidth", + "document.documentElement.scrollHeight", + ]); + return { + width, + height, + }; + } + async callMethod(method, ...args) { + return (await this.api.callMethod({ + method, + args, + })).result; + } + async scrollTop() { + return this.windowProperty("document.documentElement.scrollTop"); + } + async windowProperty(names) { + const isSingle = isStr(names); + if (isSingle) { + names = [names]; + } + const { properties } = await this.api.getWindowProperties({ + names: names, + }); + return isSingle ? properties[0] : properties; + } + static create(puppet, options, pageMap) { + let page = pageMap.get(options.id); + if (page) { + //update query (部分页面id被锁定,如tabBar页面) + page.query = options.query; + return page; + } + page = new Page$1(puppet, options); + pageMap.set(options.id, page); + return page; + } +} + +class App extends Base { + async getPageStack() { + return this.invoke("App.getPageStack"); + } + async callUniMethod(params) { + return this.invoke("App.callUniMethod", params); + } + async getCurrentPage() { + return this.invoke("App.getCurrentPage"); + } + async mockUniMethod(params) { + return this.invoke("App.mockUniMethod", params); + } + async callFunction(params) { + return this.invoke("App.callFunction", params); + } + async captureScreenshot(params) { + return this.invoke("App.captureScreenshot", params); + } + async exit() { + return this.invoke("App.exit"); + } + async addBinding(params) { + return this.invoke("App.addBinding", params); + } + async enableLog() { + return this.invoke("App.enableLog"); + } + onLogAdded(listener) { + return this.on("App.logAdded", listener); + } + onBindingCalled(listener) { + return this.on("App.bindingCalled", listener); + } + onExceptionThrown(listener) { + return this.on("App.exceptionThrown", listener); + } +} +__decorate([ + runtime +], App.prototype, "getPageStack", null); +__decorate([ + runtime +], App.prototype, "callUniMethod", null); +__decorate([ + runtime +], App.prototype, "getCurrentPage", null); +__decorate([ + runtime +], App.prototype, "mockUniMethod", null); +__decorate([ + devtool +], App.prototype, "callFunction", null); +__decorate([ + devtool +], App.prototype, "captureScreenshot", null); +__decorate([ + devtool +], App.prototype, "exit", null); +__decorate([ + devtool +], App.prototype, "addBinding", null); +__decorate([ + devtool +], App.prototype, "enableLog", null); + +class Tool extends Base { + async getInfo() { + return this.invoke("Tool.getInfo"); + } + async enableRemoteDebug(params) { + return this.invoke("Tool.enableRemoteDebug"); + } + async close() { + return this.invoke("Tool.close"); + } + async getTestAccounts() { + return this.invoke("Tool.getTestAccounts"); + } + onRemoteDebugConnected(listener) { + this.puppet.once("Tool.onRemoteDebugConnected", listener); + // mp-baidu + this.puppet.once("Tool.onPreviewConnected", listener); + } +} +__decorate([ + devtool +], Tool.prototype, "getInfo", null); +__decorate([ + devtool +], Tool.prototype, "enableRemoteDebug", null); +__decorate([ + devtool +], Tool.prototype, "close", null); +__decorate([ + devtool +], Tool.prototype, "getTestAccounts", null); + +function sleep(timeout) { + return new Promise((e) => setTimeout(e, timeout)); +} +function isFnStr(str) { + return (isStr(str) && + ((str = trim(str)), startWith(str, "function") || startWith(str, "() =>"))); +} +class Program extends events.EventEmitter { + constructor(puppet, options) { + super(); + this.puppet = puppet; + this.options = options; + this.pageMap = new Map(); + this.appBindings = new Map(); + this.appApi = new App(puppet); + this.toolApi = new Tool(puppet); + this.appApi.onLogAdded((msg) => { + this.emit("console", msg); + }); + this.appApi.onBindingCalled(({ name, args }) => { + try { + const fn = this.appBindings.get(name); + fn && fn(...args); + } + catch (t) { } + }); + this.appApi.onExceptionThrown((error) => { + this.emit("exception", error); + }); + } + async pageStack() { + return (await this.appApi.getPageStack()).pageStack.map((page) => Page$1.create(this.puppet, page, this.pageMap)); + } + async navigateTo(url) { + return this.changeRoute("navigateTo", url); + } + async redirectTo(url) { + return this.changeRoute("redirectTo", url); + } + async navigateBack() { + return this.changeRoute("navigateBack"); + } + async reLaunch(url) { + return this.changeRoute("reLaunch", url); + } + async switchTab(url) { + return this.changeRoute("switchTab", url); + } + async currentPage() { + const { id, path, query } = await this.appApi.getCurrentPage(); + return Page$1.create(this.puppet, { id, path, query }, this.pageMap); + } + async systemInfo() { + return this.callUniMethod("getSystemInfoSync"); + } + async callUniMethod(method, ...args) { + return (await this.appApi.callUniMethod({ method, args })).result; + } + async mockUniMethod(method, result, ...args) { + if (isFn(result) || isFnStr(result)) { + return this.appApi.mockUniMethod({ + method, + functionDeclaration: result.toString(), + args, + }); + } + return this.appApi.mockUniMethod({ method, result }); + } + async restoreUniMethod(method) { + return this.appApi.mockUniMethod({ method }); + } + async evaluate(appFunction, // tslint:disable-line + ...args) { + return (await this.appApi.callFunction({ + functionDeclaration: appFunction.toString(), + args, + })).result; + } + async pageScrollTo(scrollTop) { + await this.callUniMethod("pageScrollTo", { + scrollTop, + duration: 0, + }); + } + async close() { + try { + await this.appApi.exit(); + } + catch (t) { } + await sleep(1e3); + this.puppet.disposeRuntimeServer(); + await this.toolApi.close(); + this.disconnect(); + } + async teardown() { + return this[this.options.teardown === "disconnect" ? "disconnect" : "close"](); + } + async remote(auto) { + if (!this.puppet.devtools.remote) { + return console.warn(`Failed to enable remote, ${this.puppet.devtools.name} is unimplemented`); + } + const { qrCode } = await this.toolApi.enableRemoteDebug({ auto }); + qrCode && (await printQrCode(qrCode)); + const connectedPromise = new Promise((resolve) => { + this.toolApi.onRemoteDebugConnected(async () => { + await sleep(1e3); + resolve(); + }); + }); + const runtimePromise = new Promise((resolve) => { + this.puppet.setRemoteRuntimeConnectionCallback(() => { + resolve(); + }); + }); + return Promise.all([connectedPromise, runtimePromise]); + } + disconnect() { + this.puppet.dispose(); + } + on(event, listener) { + if (event === "console") { + this.appApi.enableLog(); + } + super.on(event, listener); + return this; + } + async exposeFunction(name, bindingFunction) { + if (this.appBindings.has(name)) { + throw Error(`Failed to expose function with name ${name}: already exists!`); + } + this.appBindings.set(name, bindingFunction); + await this.appApi.addBinding({ name }); + } + async checkVersion() { } + async screenshot(options) { + const { data } = await this.appApi.captureScreenshot({ + fullPage: options === null || options === void 0 ? void 0 : options.fullPage, + }); + if (!(options === null || options === void 0 ? void 0 : options.path)) + return data; + await fs$1.writeFile(options.path, data, "base64"); + } + async testAccounts() { + return (await this.toolApi.getTestAccounts()).accounts; + } + async changeRoute(method, url) { + await this.callUniMethod(method, { + url, + }); + await sleep(3e3); + return this.currentPage(); + } +} + +class Adapter { + constructor(options) { + this.options = options; + } + has(method) { + return !!this.options[method]; + } + send(connection, method, params) { + const option = this.options[method]; + if (!option) { + return Promise.reject(Error(`adapter for ${method} not found`)); + } + const reflect = option.reflect; + if (!reflect) { + return Promise.reject(Error(`${method}'s reflect is required`)); + } + if (option.params) { + params = option.params(params); + } + if (typeof reflect === "function") { + return reflect(connection.send.bind(connection), params); + } + else { + method = reflect; + } + return connection.send(method, params); + } +} + +const debugPuppet = debug("automator:puppet"); +const AUTOMATOR_JSON_FILE = ".automator.json"; +function tryRequire(path) { + try { + return require(path); + } + catch (e) { } +} +function resolveAutomatorJson(projectPath, platform, mode) { + let json; + let jsonPath; + if (process.env.UNI_OUTPUT_DIR) { + jsonPath = path.join(process.env.UNI_OUTPUT_DIR, `../.automator/${platform}`, AUTOMATOR_JSON_FILE); + json = tryRequire(jsonPath); + } + else { + jsonPath = path.join(projectPath, `dist/${mode}/.automator/${platform}`, AUTOMATOR_JSON_FILE); + json = tryRequire(jsonPath); + if (!json) { + jsonPath = path.join(projectPath, `unpackage/dist/${mode}/.automator/${platform}`, AUTOMATOR_JSON_FILE); + json = tryRequire(jsonPath); + } + } + debugPuppet(`${jsonPath}=>${JSON.stringify(json)}`); + return json; +} +function equalWsEndpoint(projectPath, port, platform, mode) { + const json = resolveAutomatorJson(projectPath, platform, mode); + if (!json || !json.wsEndpoint) { + return false; + } + const version = require("../package.json").version; + if (json.version !== version) { + debugPuppet(`unmet=>${json.version}!==${version}`); + return false; + } + const wsEndpoint = getWsEndpoint(port); + debugPuppet(`wsEndpoint=>${wsEndpoint}`); + return json.wsEndpoint === wsEndpoint; +} +class Puppet extends events.EventEmitter { + constructor(platform, target) { + super(); + if (target) { + this.target = target; + } + else { + this.target = require(`@dcloudio/uni-${platform === "app" ? "app-plus" : platform}/lib/uni.automator.js`); + } + if (!this.target) { + throw Error("puppet is not provided"); + } + this.platform = platform; + this.adapter = new Adapter(this.target.adapter || {}); + } + setCompiler(compiler) { + this.compiler = compiler; + } + setRuntimeServer(wss) { + this.wss = wss; + } + setRemoteRuntimeConnectionCallback(callback) { + this.remoteRuntimeConnectionCallback = callback; + } + setRuntimeConnection(connection) { + this.runtimeConnection = connection; + if (this.remoteRuntimeConnectionCallback) { + this.remoteRuntimeConnectionCallback(); + this.remoteRuntimeConnectionCallback = null; + } + } + setDevtoolConnection(connection) { + this.devtoolConnection = connection; + } + disposeRuntimeServer() { + this.wss && this.wss.close(); + } + disposeRuntime() { + this.runtimeConnection.dispose(); + } + disposeDevtool() { + this.compiler && this.compiler.stop(); + this.devtoolConnection && this.devtoolConnection.dispose(); + } + dispose() { + this.disposeRuntime(); + this.disposeDevtool(); + this.disposeRuntimeServer(); + } + send(method, params) { + return this.runtimeConnection.send(method, params); + } + validateProject(projectPath) { + const required = this.target.devtools.required; + if (!required) { + return true; + } + return !required.find((file) => !fs.existsSync(path.join(projectPath, file))); + } + validateDevtools(opions) { + const validate = this.target.devtools.validate; + if (validate) { + return validate(opions, this); + } + return Promise.resolve(opions); + } + createDevtools(devtoolsProjectPath, options, timeout) { + const create = this.target.devtools.create; + if (create) { + options.timeout = timeout; + return create(devtoolsProjectPath, options, this); + } + return Promise.resolve(); + } + shouldCompile(projectPath, port, options, devtoolsOptions) { + this.compiled = true; + const shouldCompile = this.target.shouldCompile; + if (shouldCompile) { + this.compiled = shouldCompile(options, devtoolsOptions); + } + else { + if (options.compile === true) { + this.compiled = true; + } + else { + //自动检测 + this.compiled = !equalWsEndpoint(projectPath, port, this.platform, this.mode); + } + } + return this.compiled; + } + get checkProperty() { + return this.platform === "mp-weixin"; + } + get devtools() { + return this.target.devtools; + } + get mode() { + const mode = this.target.mode; + if (mode) { + return mode; + } + return process.env.NODE_ENV === "production" ? "build" : "dev"; + } +} + +const debugCompiler = debug("automator:compiler"); +const SIGNAL_DONE = "DONE Build complete"; +const SIGNAL_DONE_H5 = "- Network"; +const SIGNAL_DONE_VITE_H5 = "> Network"; +const PATH_RE = /The\s+(.*)\s+directory is ready/; +class Compiler { + constructor(puppet) { + this.puppet = puppet; + this.puppet.setCompiler(this); + } + compile(options) { + const mode = this.puppet.mode; + const platform = this.puppet.platform; + let silent = options.silent; + const autoPort = options.port; + const autoHost = options.host; + const npmScript = `${mode}:${platform}`; + const projectPath = options.projectPath; + const [command, cliArgs] = this.getSpawnArgs(options, npmScript); + cliArgs.push("--auto-port"); + cliArgs.push(toStr(autoPort)); + if (autoHost) { + cliArgs.push("--auto-host"); + cliArgs.push(autoHost); + } + const cliOptions = { + cwd: options.cliPath, + env: Object.assign(Object.assign({}, process.env), { NODE_ENV: mode === "build" ? "production" : "development" }), + }; + return new Promise((resolve, reject) => { + const onError = (err) => { + reject(err); + }; + const onStdoutData = (data) => { + const msg = data.toString().trim(); + !silent && console.log(msg); + if (msg.includes(SIGNAL_DONE_H5) || msg.includes(SIGNAL_DONE_VITE_H5)) { + const networkUrl = msg.match(/Network:(.*)/)[1].trim(); + // H5 DONE + debugCompiler(`url: ${networkUrl}`); + resolve({ path: networkUrl }); + } + else if (msg.includes(SIGNAL_DONE)) { + const matches = msg.match(PATH_RE); + let outputDir = ""; + if (matches && matches.length > 1) { + outputDir = path.join(projectPath, matches[1]); + } + else { + outputDir = path.join(projectPath, `dist/${mode}/${platform}`); + if (!fs.existsSync(outputDir)) { + outputDir = path.join(projectPath, `unpackage/dist/${mode}/${platform}`); + } + } + silent = true; // 编译已完成 + this.stop(); + resolve({ + path: outputDir, + }); + } + }; + debugCompiler(`${command} ${cliArgs.join(" ")} %o`, cliOptions); + this.cliProcess = child_process.spawn(command, cliArgs, cliOptions); + this.cliProcess.on("error", onError); + this.cliProcess.stdout.on("data", onStdoutData); + this.cliProcess.stderr.on("data", onStdoutData); + }); + } + stop() { + this.cliProcess && this.cliProcess.kill("SIGTERM"); + } + getSpawnArgs(options, npmScript) { + let pkg; + const cliPath = options.cliPath; + try { + pkg = require(path.join(cliPath, "package.json")); + } + catch (e) { } + if (pkg && pkg.scripts && pkg.scripts[npmScript]) { + return [ + process.env.UNI_NPM_PATH || + (/^win/.test(process.platform) ? "npm.cmd" : "npm"), + ["run", npmScript, "--"], + ]; + } + process.env.UNI_INPUT_DIR = options.projectPath; + process.env.UNI_OUTPUT_DIR = path.join(options.projectPath, `unpackage/dist/${this.puppet.mode}/${this.puppet.platform}`); + return [ + process.env.UNI_NODE_PATH || "node", + [path.join(cliPath, "bin/uniapp-cli.js")], + ]; + } +} + +const PORT = 9520; +const TIMEOUT = 6e4; +function exit(msg) { + throw Error(msg); +} +class Launcher { + async launch(options) { + let devtools = (options.platform === "app" + ? options.app || options["app-plus"] + : options[options.platform]) || {}; + this.puppet = new Puppet(options.platform, devtools.puppet); + // 1.校验参数 + const { port, cliPath, timeout, projectPath } = await this.validate(options); + devtools = await this.puppet.validateDevtools(devtools); + // 2.编译 + let shouldCompile = this.puppet.shouldCompile(projectPath, port, options, devtools); + let devtoolsProjectPath = process.env.UNI_OUTPUT_DIR || projectPath; + if (!shouldCompile) { + if (!this.puppet.validateProject(devtoolsProjectPath)) { + devtoolsProjectPath = path.join(projectPath, "dist/" + this.puppet.mode + "/" + this.puppet.platform); + if (!this.puppet.validateProject(devtoolsProjectPath)) { + devtoolsProjectPath = path.join(projectPath, "unpackage/dist/" + this.puppet.mode + "/" + this.puppet.platform); + if (!this.puppet.validateProject(devtoolsProjectPath)) { + shouldCompile = true; + } + } + } + } + if (shouldCompile) { + this.puppet.compiled = options.compile = true; + this.compiler = new Compiler(this.puppet); + const compilerResult = await this.compiler.compile({ + host: options.host, + port, + cliPath, + projectPath, + silent: !!options.silent, + }); + if (compilerResult.path) { + devtoolsProjectPath = compilerResult.path; + } + } + const promises = []; + // 3.runtime + promises.push(this.createRuntimeConnection(port, timeout)); + // 4.devtool + promises.push(this.puppet.createDevtools(devtoolsProjectPath, devtools, timeout)); + return new Promise((resolve, reject) => { + Promise.all(promises) + .then(([runtimeConnection, devtoolConnection]) => { + runtimeConnection && + this.puppet.setRuntimeConnection(runtimeConnection); + devtoolConnection && + this.puppet.setDevtoolConnection(devtoolConnection); + debug("automator:program")("ready"); + const teardown = devtools.teardown || "disconnect"; + resolve(new Program(this.puppet, { teardown, port })); + }) + .catch((err) => reject(err)); + }); + } + resolveCliPath(cliPath) { + if (!cliPath) { + return cliPath; + } + try { + const { dependencies, devDependencies } = require(path.join(cliPath, "package.json")); + if (hasCliDeps(devDependencies) || hasCliDeps(dependencies)) { + return cliPath; + } + } + catch (e) { } + } + resolveProjectPath(projectPath, options) { + if (!projectPath) { + projectPath = process.env.UNI_INPUT_DIR || process.cwd(); + } + if (isRelative(projectPath)) { + projectPath = path.resolve(projectPath); + } + if (!fs.existsSync(projectPath)) { + exit(`Project path ${projectPath} doesn't exist`); + } + return projectPath; + } + async validate(options) { + const projectPath = this.resolveProjectPath(options.projectPath, options); + let cliPath = process.env.UNI_CLI_PATH || options.cliPath; + cliPath = this.resolveCliPath(cliPath || ""); + !cliPath && (cliPath = this.resolveCliPath(process.cwd())); + !cliPath && (cliPath = this.resolveCliPath(projectPath)); + if (!cliPath) { + throw Error("cliPath is not provided"); + } + const port = await resolvePort(options.port || PORT); + const timeout = options.timeout || TIMEOUT; + return { + port, + cliPath, + timeout, + projectPath, + }; + } + async createRuntimeConnection(port, timeout) { + return Connection.createRuntimeConnection(port, this.puppet, timeout); + } +} +function hasCliDeps(deps) { + if (!deps) { + return false; + } + return !!(deps["@dcloudio/vue-cli-plugin-uni"] || deps["@dcloudio/vite-plugin-uni"]); +} + +class Automator { + constructor() { + this.launcher = new Launcher(); + } + // async connect(options: IConnectOptions) { + // return this.launcher.connect(options); + // } + async launch(options) { + return this.launcher.launch(options); + } +} + +module.exports = Automator; diff --git a/packages/uni-automator/dist/teardown.js b/packages/uni-automator/dist/teardown.js new file mode 100644 index 000000000..a2a3de34d --- /dev/null +++ b/packages/uni-automator/dist/teardown.js @@ -0,0 +1,13 @@ +'use strict'; + +async function teardown() { + const program = global.program; + program && program.teardown(); + await new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 3000); + }); +} + +module.exports = teardown; diff --git a/packages/uni-automator/lib/uni.plugin.js b/packages/uni-automator/lib/uni.plugin.js new file mode 100644 index 000000000..e1e40abed --- /dev/null +++ b/packages/uni-automator/lib/uni.plugin.js @@ -0,0 +1,56 @@ +'use strict' +var __importDefault = + (this && this.__importDefault) || + function (mod) { + return mod && mod.__esModule ? mod : { default: mod } + } +Object.defineProperty(exports, '__esModule', { value: true }) +const fs_extra_1 = __importDefault(require('fs-extra')) +const path_1 = __importDefault(require('path')) +const uni_cli_shared_1 = require('@dcloudio/uni-cli-shared') +exports.default = [ + uni_cli_shared_1.defineUniMainJsPlugin((opts) => { + return { + name: 'vite:uni-automator', + enforce: 'pre', + configResolved() { + if (!process.env.UNI_AUTOMATOR_WS_ENDPOINT) { + return + } + const pkg = JSON.parse( + fs_extra_1.default.readFileSync( + path_1.default.resolve(__dirname, '../package.json'), + 'utf8' + ) + ) + const automatorJson = JSON.stringify({ + version: pkg.version, + wsEndpoint: process.env.UNI_AUTOMATOR_WS_ENDPOINT, + }) + fs_extra_1.default.outputFileSync( + path_1.default.resolve( + process.env.UNI_OUTPUT_DIR, + '../.automator/' + process.env.UNI_PLATFORM + '/.automator.json' + ), + automatorJson + ) + }, + transform(code, id) { + if (!process.env.UNI_AUTOMATOR_WS_ENDPOINT) { + return null + } + if (opts.filter(id)) { + const platform = process.env.UNI_PLATFORM + return { + code: + code + + `;import '@dcloudio/uni-${ + platform === 'app' ? 'app-plus' : platform + }/lib/automator.js';`, + map: null, + } + } + }, + } + }), +] diff --git a/packages/uni-automator/package.json b/packages/uni-automator/package.json new file mode 100644 index 000000000..01b1dc7c8 --- /dev/null +++ b/packages/uni-automator/package.json @@ -0,0 +1,41 @@ +{ + "name": "@dcloudio/uni-automator", + "version": "3.0.0-alpha-3000020210813002", + "description": "@dcloudio/uni-automator", + "main": "dist/index.js", + "files": [ + "dist", + "lib" + ], + "sideEffects": false, + "repository": { + "type": "git", + "url": "git+https://github.com/dcloudio/uni-app.git", + "directory": "packages/uni-automator" + }, + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/dcloudio/uni-app/issues" + }, + "uni-app": { + "name": "uniAutomator", + "apply": [ + "app", + "h5", + "mp-weixin" + ] + }, + "gitHead": "d5896d19315a106039411c16bbf8b804a865450d", + "dependencies": { + "address": "^1.1.2", + "cross-env": "^7.0.3", + "debug": "^4.3.2", + "default-gateway": "^6.0.3", + "fs-extra": "^10.0.0", + "licia": "^1.29.0", + "postcss-selector-parser": "^6.0.6", + "qrcode-reader": "^1.0.4", + "qrcode-terminal": "^0.12.0", + "ws": "^8.2.0" + } +} diff --git a/packages/uni-automator/src/uni.plugin.ts b/packages/uni-automator/src/uni.plugin.ts new file mode 100644 index 000000000..b2bacc8bb --- /dev/null +++ b/packages/uni-automator/src/uni.plugin.ts @@ -0,0 +1,47 @@ +import fs from 'fs-extra' +import path from 'path' +import { defineUniMainJsPlugin } from '@dcloudio/uni-cli-shared' + +export default [ + defineUniMainJsPlugin((opts) => { + return { + name: 'vite:uni-automator', + enforce: 'pre', + configResolved() { + if (!process.env.UNI_AUTOMATOR_WS_ENDPOINT) { + return + } + const pkg = JSON.parse( + fs.readFileSync(path.resolve(__dirname, '../package.json'), 'utf8') + ) + const automatorJson = JSON.stringify({ + version: pkg.version, + wsEndpoint: process.env.UNI_AUTOMATOR_WS_ENDPOINT, + }) + fs.outputFileSync( + path.resolve( + process.env.UNI_OUTPUT_DIR, + '../.automator/' + process.env.UNI_PLATFORM + '/.automator.json' + ), + automatorJson + ) + }, + transform(code, id) { + if (!process.env.UNI_AUTOMATOR_WS_ENDPOINT) { + return null + } + if (opts.filter(id)) { + const platform = process.env.UNI_PLATFORM + return { + code: + code + + `;import '@dcloudio/uni-${ + platform === 'app' ? 'app-plus' : platform + }/lib/automator.js';`, + map: null, + } + } + }, + } + }), +] diff --git a/packages/uni-automator/tsconfig.json b/packages/uni-automator/tsconfig.json new file mode 100644 index 000000000..bdf8fda0a --- /dev/null +++ b/packages/uni-automator/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.node.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "src", + "../shims-node.d.ts", + "../shims-uni-app.d.ts" + ] +} diff --git a/packages/uni-cli-nvue/src/webpack/config/module/rules/fileLoader.ts b/packages/uni-cli-nvue/src/webpack/config/module/rules/fileLoader.ts new file mode 100644 index 000000000..b0deec434 --- /dev/null +++ b/packages/uni-cli-nvue/src/webpack/config/module/rules/fileLoader.ts @@ -0,0 +1,16 @@ +import { RuleSetRule } from 'webpack' + +export function createFileLoader(): RuleSetRule { + return { + test: /\.(png|jpg|gif|ttf|eot|woff|woff2)$/i, + use: [ + { + loader: 'file-loader', + options: { + publicPath: 'assets', + outputPath: 'assets', + }, + }, + ], + } +} diff --git a/packages/uni-cli-nvue/src/webpack/config/module/rules/index.ts b/packages/uni-cli-nvue/src/webpack/config/module/rules/index.ts index f03686ad8..347bc6892 100644 --- a/packages/uni-cli-nvue/src/webpack/config/module/rules/index.ts +++ b/packages/uni-cli-nvue/src/webpack/config/module/rules/index.ts @@ -1,11 +1,13 @@ import { RuleSetRule } from 'webpack' import { createBabelLoader } from './babelLoader' import { createCssLoaders } from './cssLoader' +import { createFileLoader } from './fileLoader' import { createRecyclableLoader } from './recyclableLoader' import { createTemplateLoader } from './templateLoader' import { createVueLoader } from './vueLoader' export function createRules(options: NVueCompilerOptions): RuleSetRule[] { const rules = [ + createFileLoader(), createVueLoader(options), createBabelLoader(), createRecyclableLoader(), diff --git a/packages/uni-cli-shared/src/constants.ts b/packages/uni-cli-shared/src/constants.ts index 4a382346d..4a3801506 100644 --- a/packages/uni-cli-shared/src/constants.ts +++ b/packages/uni-cli-shared/src/constants.ts @@ -21,6 +21,7 @@ export const COMMON_EXCLUDE = [ /\/@vue\//, /\/vue-router\//, /\/vuex\//, + /\/vue-i18n\//, /\/@dcloudio\/uni-h5-vue/, /\/@dcloudio\/uni-shared/, /\/@dcloudio\/uni-h5\/style/, diff --git a/packages/uni-cli-shared/src/env/provide.ts b/packages/uni-cli-shared/src/env/provide.ts index 7094e16eb..6d96b4fb3 100644 --- a/packages/uni-cli-shared/src/env/provide.ts +++ b/packages/uni-cli-shared/src/env/provide.ts @@ -1,9 +1,9 @@ import path from 'path' -const libDir = path.resolve(__dirname, '../lib') +const libDir = path.resolve(__dirname, '../../lib') export function initProvide() { const cryptoDefine = [path.join(libDir, 'crypto.js'), 'default'] return { - __f__: [path.join(__dirname, '../hbx/formatLog.js'), 'formatLog'], + __f__: ['@dcloudio/uni-shared', 'formatAppLog'], crypto: cryptoDefine, 'window.crypto': cryptoDefine, 'global.crypto': cryptoDefine, diff --git a/packages/uni-h5/lib/automator.js b/packages/uni-h5/lib/automator.js new file mode 100644 index 000000000..3f5ee394f --- /dev/null +++ b/packages/uni-h5/lib/automator.js @@ -0,0 +1,1001 @@ +function initPage(adapter) { + return { + 'Page.getElement': function (params) { + return adapter.querySelector( + adapter.getDocument(params.pageId), + params.selector + ) + }, + 'Page.getElements': function (params) { + return adapter.querySelectorAll( + adapter.getDocument(params.pageId), + params.selector + ) + }, + 'Page.getWindowProperties': function (params) { + return adapter.queryProperties( + adapter.getWindow(params.pageId), + params.names + ) + }, + } +} + +function initElement(adapter) { + var getEl = function (params) { + return adapter.getEl(params.elementId, params.pageId) + } + return { + 'Element.getElement': function (params) { + return adapter.querySelector(getEl(params), params.selector) + }, + 'Element.getElements': function (params) { + return adapter.querySelectorAll(getEl(params), params.selector) + }, + 'Element.getDOMProperties': function (params) { + return adapter.queryProperties(getEl(params), params.names) + }, + 'Element.getProperties': function (params) { + var el = getEl(params) + var ctx = el.__vue__ || el.attr || {} + return adapter.queryProperties(ctx, params.names) + }, + 'Element.getOffset': function (params) { + return adapter.getOffset(getEl(params)) + }, + 'Element.getAttributes': function (params) { + return adapter.queryAttributes(getEl(params), params.names) + }, + 'Element.getStyles': function (params) { + return adapter.queryStyles(getEl(params), params.names) + }, + 'Element.getHTML': function (params) { + return adapter.queryHTML(getEl(params), params.type) + }, + 'Element.tap': function (params) { + return adapter.dispatchTapEvent(getEl(params)) + }, + 'Element.longpress': function (params) { + return adapter.dispatchLongpressEvent(getEl(params)) + }, + 'Element.touchstart': function (params) { + return adapter.dispatchTouchEvent(getEl(params), 'touchstart', params) + }, + 'Element.touchmove': function (params) { + return adapter.dispatchTouchEvent(getEl(params), 'touchmove', params) + }, + 'Element.touchend': function (params) { + return adapter.dispatchTouchEvent(getEl(params), 'touchend', params) + }, + 'Element.callFunction': function (params) { + return adapter.callFunction( + getEl(params), + params.functionName, + params.args + ) + }, + 'Element.triggerEvent': function (params) { + return adapter.triggerEvent(getEl(params), params.type, params.detail) + }, + } +} + +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + +function __spreadArrays() { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) + s += arguments[i].length + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j] + return r +} + +// Unique ID creation requires a high quality random # generator. In the browser we therefore +// require the crypto API and do not support built-in fallback to lower quality random number +// generators (like Math.random()). +// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also, +// find the complete implementation of crypto (msCrypto) on IE11. +var getRandomValues = + (typeof crypto != 'undefined' && + crypto.getRandomValues && + crypto.getRandomValues.bind(crypto)) || + (typeof msCrypto != 'undefined' && + typeof msCrypto.getRandomValues == 'function' && + msCrypto.getRandomValues.bind(msCrypto)) +var rnds8 = new Uint8Array(16) // eslint-disable-line no-undef + +function rng() { + if (!getRandomValues) { + throw new Error( + 'crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported' + ) + } + + return getRandomValues(rnds8) +} + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +var byteToHex = [] + +for (var i = 0; i < 256; ++i) { + byteToHex[i] = (i + 0x100).toString(16).substr(1) +} + +function bytesToUuid(buf, offset) { + var i = offset || 0 + var bth = byteToHex // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4 + + return [ + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + '-', + bth[buf[i++]], + bth[buf[i++]], + '-', + bth[buf[i++]], + bth[buf[i++]], + '-', + bth[buf[i++]], + bth[buf[i++]], + '-', + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + bth[buf[i++]], + ].join('') +} + +function v4(options, buf, offset) { + var i = (buf && offset) || 0 + + if (typeof options == 'string') { + buf = options === 'binary' ? new Array(16) : null + options = null + } + + options = options || {} + var rnds = options.random || (options.rng || rng)() // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + + rnds[6] = (rnds[6] & 0x0f) | 0x40 + rnds[8] = (rnds[8] & 0x3f) | 0x80 // Copy bytes to buffer, if provided + + if (buf) { + for (var ii = 0; ii < 16; ++ii) { + buf[i + ii] = rnds[ii] + } + } + + return buf || bytesToUuid(rnds) +} + +var hasOwnProperty = Object.prototype.hasOwnProperty +var hasOwn = function (val, key) { + return hasOwnProperty.call(val, key) +} +var isUndef = function (v) { + return v === undefined || v === null +} +var isArray = Array.isArray +var isPromise = function (obj) { + return ( + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function' + ) +} +var cacheStringFunction = function (fn) { + var cache = Object.create(null) + return function (str) { + var hit = cache[str] + return hit || (cache[str] = fn(str)) + } +} +var camelizeRE = /-(\w)/g +var camelize = cacheStringFunction(function (str) { + return str.replace(camelizeRE, function (_, c) { + return c ? c.toUpperCase() : '' + }) +}) +var capitalize = cacheStringFunction(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) +}) +var PATH_RE = + /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g +function getPaths(path, data) { + if (isArray(path)) { + return path + } + if (data && hasOwn(data, path)) { + return [path] + } + var res = [] + path.replace(PATH_RE, function (match, p1, offset, string) { + res.push(offset ? string.replace(/\\(\\)?/g, '$1') : p1 || match) + return string + }) + return res +} +function getDataByPath(data, path) { + var paths = getPaths(path, data) + var dataPath + for (dataPath = paths.shift(); !isUndef(dataPath); ) { + if (null == (data = data[dataPath])) { + return + } + dataPath = paths.shift() + } + return data +} +function getVmNodeId(vm) { + //@ts-ignore + { + return vm._uid + } +} + +var elementMap = new Map() +function getElId(element) { + var elementId = element._id + if (!elementId) { + elementId = v4() + element._id = elementId + elementMap.set(elementId, { id: elementId, element: element }) + } + return elementId +} +function isValidEl(el) { + if (el) { + var tagName = el.tagName + return tagName.indexOf('UNI-') === 0 || tagName === 'BODY' + } + return false +} +function transEl(el) { + if (!isValidEl(el)) { + throw Error('no such element') + } + var elem = { + elementId: getElId(el), + tagName: el.tagName.toLocaleLowerCase().replace('uni-', ''), + } + var vm = el.__vue__ + if (vm) { + if (vm.$parent && vm.$parent.$el === el) { + vm = vm.$parent + } + if (vm && !vm.$options.isReserved) { + elem.nodeId = getVmNodeId(vm) + } + } + if (elem.tagName === 'video') { + elem.videoId = elem.nodeId + } + return elem +} +function formatHTML(html) { + return html + .replace(/\n/g, '') + .replace( + /(]*>)(]*>[^<]*<\/span>)(.*?<\/uni-text>)/g, + '$1$3' + ) + .replace(/<\/?[^>]*>/g, function (replacement) { + if (-1 < replacement.indexOf('' + } else if ('' === replacement) { + return '' + } else if ( + 0 !== replacement.indexOf(' { + if (selector.type === 'tag') { + const value = selector.value + if (value === 'page') { + //@ts-ignore + { + selector.value = 'uni-page-body' + } + } else { + selector.value = 'uni-' + value + } + } + }) +} +function transSelector(method) { + return { + reflect: async (send, params) => send(method, params, false), + params(params) { + if (params.selector) { + params.selector = parser(transform).processSync(params.selector) + } + return params + }, + } +} +const methods = [ + 'Page.getElement', + 'Page.getElements', + 'Element.getElement', + 'Element.getElements', +] +function initAdapter(adapter) { + methods.forEach((method) => { + adapter[method] = transSelector(method) + }) +} + +const debugDevtools = debug('automator:devtool') +async function validateDevtools(options) { + options.options = options.options || {} + if (options.executablePath && !options.options.executablePath) { + options.options.executablePath = options.executablePath + } + options.options.defaultViewport = Object.assign( + { + width: 375, + height: 667, + deviceScaleFactor: 2, + hasTouch: true, + isMobile: true, + }, + options.options.defaultViewport || {} + ) + if (!options.teardown) { + options.teardown = + options.options.headless === false ? 'disconnect' : 'close' + } + return options +} +let browser +let page +async function createDevtools(url, options, puppet) { + browser = await puppeteer.launch(options.options) + const process = browser.process() + if (process) { + debugDevtools('%s %o', process.spawnfile, options.options) + } else { + debugDevtools('%o', options.options) + } + page = await browser.newPage() + page.on('console', (msg) => { + puppet.emit('App.logAdded', { type: msg.type(), args: [msg.text()] }) + }) + page.on('pageerror', (err) => { + puppet.emit('App.exceptionThrown', err) + }) + await page.goto(options.url || url) + await page.waitFor(1000) +} +const adapter = { + 'Tool.close': { + reflect: async () => { + await browser.close() + }, + }, + 'App.exit': { + reflect: async () => {}, + }, + 'App.enableLog': { + reflect: () => Promise.resolve(), + }, + 'App.captureScreenshot': { + reflect: async (send, params) => { + const data = await page.screenshot({ + encoding: 'base64', + fullPage: !!params.fullPage, + }) + debugDevtools(`App.captureScreenshot ${data.length}`) + return { + data, + } + }, + }, +} +initAdapter(adapter) +const puppet = { + devtools: { + name: 'google chrome', + paths: [], + validate: validateDevtools, + create: createDevtools, + }, + shouldCompile(options, devtoolsOptions) { + if (devtoolsOptions.url) { + return false + } + return true + }, + adapter, +} + +module.exports = puppet diff --git a/packages/uni-h5/package.json b/packages/uni-h5/package.json index 588e61fbe..124477eff 100644 --- a/packages/uni-h5/package.json +++ b/packages/uni-h5/package.json @@ -6,9 +6,12 @@ "module": "./dist/uni-h5.es.js", "files": [ "dist", + "lib", "style" ], - "sideEffects": false, + "sideEffects": [ + "lib/automator.js" + ], "repository": { "type": "git", "url": "git+https://github.com/dcloudio/uni-app.git", diff --git a/packages/uni-mp-baidu/lib/automator.js b/packages/uni-mp-baidu/lib/automator.js new file mode 100644 index 000000000..748c83f0e --- /dev/null +++ b/packages/uni-mp-baidu/lib/automator.js @@ -0,0 +1,488 @@ +var hasOwnProperty = Object.prototype.hasOwnProperty +var hasOwn = function (val, key) { + return hasOwnProperty.call(val, key) +} +var isUndef = function (v) { + return v === undefined || v === null +} +var isArray = Array.isArray +var isPromise = function (obj) { + return ( + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function' + ) +} +var cacheStringFunction = function (fn) { + var cache = Object.create(null) + return function (str) { + var hit = cache[str] + return hit || (cache[str] = fn(str)) + } +} +var camelizeRE = /-(\w)/g +var camelize = cacheStringFunction(function (str) { + return str.replace(camelizeRE, function (_, c) { + return c ? c.toUpperCase() : '' + }) +}) +var capitalize = cacheStringFunction(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) +}) +var PATH_RE = + /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g +function getPaths(path, data) { + if (isArray(path)) { + return path + } + if (data && hasOwn(data, path)) { + return [path] + } + var res = [] + path.replace(PATH_RE, function (match, p1, offset, string) { + res.push(offset ? string.replace(/\\(\\)?/g, '$1') : p1 || match) + return string + }) + return res +} +function getDataByPath(data, path) { + var paths = getPaths(path, data) + var dataPath + for (dataPath = paths.shift(); !isUndef(dataPath); ) { + if (null == (data = data[dataPath])) { + return + } + dataPath = paths.shift() + } + return data +} + +function getPageId(page) { + if (page.__wxWebviewId__) { + //mp-weixin + return page.__wxWebviewId__ + } + if (page.privateProperties) { + //mp-baidu + return page.privateProperties.slaveId + } + if (page.$page) { + //h5 and app-plus + return page.$page.id + } +} +function getPagePath(page) { + return page.route || page.uri +} +function getPageQuery(page) { + return page.options || (page.$page && page.$page.options) || {} +} +function parsePage(page) { + return { + id: getPageId(page), + path: getPagePath(page), + query: getPageQuery(page), + } +} +function getPageById(id) { + return getCurrentPages().find(function (page) { + return getPageId(page) === id + }) +} +function getPageVm(id) { + var page = getPageById(id) + return page && page.$vm +} +function getNodeId(scope) { + return scope.__wxExparserNodeId__ || scope.nodeId || scope.id +} +function matchNodeId(vm, nodeId) { + return vm.$scope && getNodeId(vm.$scope) === nodeId +} +function findComponentVm(vm, nodeId) { + var res + if (vm) { + if (matchNodeId(vm, nodeId)) { + res = vm + } else { + vm.$children.find(function (child) { + res = findComponentVm(child, nodeId) + return res + }) + } + } + return res +} +function getComponentVm(pageId, nodeId) { + var pageVm = getPageVm(pageId) + return pageVm && findComponentVm(pageVm, nodeId) +} +function getData(vm, path) { + var data + if (vm) { + data = path ? getDataByPath(vm.$data, path) : Object.assign({}, vm.$data) + } + return Promise.resolve({ data: data }) +} +function setData(vm, data) { + if (vm) { + Object.keys(data).forEach(function (name) { + vm[name] = data[name] + }) + } + return Promise.resolve() +} +var CALL_METHOD_ERROR +;(function (CALL_METHOD_ERROR) { + CALL_METHOD_ERROR['VM_NOT_EXISTS'] = 'VM_NOT_EXISTS' + CALL_METHOD_ERROR['METHOD_NOT_EXISTS'] = 'METHOD_NOT_EXISTS' +})(CALL_METHOD_ERROR || (CALL_METHOD_ERROR = {})) +function callMethod(vm, method, args) { + return new Promise(function (resolve, reject) { + if (!vm) { + return reject(CALL_METHOD_ERROR.VM_NOT_EXISTS) + } + if (!vm[method]) { + return reject(CALL_METHOD_ERROR.VM_NOT_EXISTS) + } + var ret = vm[method].apply(vm, args) + isPromise(ret) + ? ret.then(function (res) { + resolve({ result: res }) + }) + : resolve({ result: ret }) + }) +} + +var SYNC_APIS = [ + 'stopRecord', + 'getRecorderManager', + 'pauseVoice', + 'stopVoice', + 'pauseBackgroundAudio', + 'stopBackgroundAudio', + 'getBackgroundAudioManager', + 'createAudioContext', + 'createInnerAudioContext', + 'createVideoContext', + 'createCameraContext', + 'createMapContext', + 'canIUse', + 'startAccelerometer', + 'stopAccelerometer', + 'startCompass', + 'stopCompass', + 'hideToast', + 'hideLoading', + 'showNavigationBarLoading', + 'hideNavigationBarLoading', + 'navigateBack', + 'createAnimation', + 'pageScrollTo', + 'createSelectorQuery', + 'createCanvasContext', + 'createContext', + 'drawCanvas', + 'hideKeyboard', + 'stopPullDownRefresh', + 'arrayBufferToBase64', + 'base64ToArrayBuffer', +] +var originUni = {} +var SYNC_API_RE = /Sync$/ +var MOCK_API_BLACKLIST_RE = /^on|^off/ +function isSyncApi(method) { + return SYNC_API_RE.test(method) || SYNC_APIS.indexOf(method) !== -1 +} +function canIMock(method) { + return !MOCK_API_BLACKLIST_RE.test(method) +} +var App = { + getPageStack: function () { + return Promise.resolve({ + pageStack: getCurrentPages().map(function (page) { + return parsePage(page) + }), + }) + }, + getCurrentPage: function () { + var pages = getCurrentPages() + var len = pages.length + return new Promise(function (resolve, reject) { + if (!len) { + reject(Error('getCurrentPages().length=0')) + } else { + resolve(parsePage(pages[len - 1])) + } + }) + }, + callUniMethod: function (params) { + var method = params.method + var args = params.args + return new Promise(function (resolve, reject) { + if (!uni[method]) { + return reject(Error('uni.' + method + ' not exists')) + } + if (isSyncApi(method)) { + return resolve({ + result: uni[method].apply(uni, args), + }) + } + var params = [ + Object.assign({}, args[0] || {}, { + success: function (result) { + var timeout = method === 'pageScrollTo' ? 350 : 0 + setTimeout(function () { + resolve({ result: result }) + }, timeout) + }, + fail: function (res) { + reject(Error(res.errMsg.replace(method + ':fail ', ''))) + }, + }), + ] + uni[method].apply(uni, params) + }) + }, + mockUniMethod: function (params) { + var method = params.method + if (!uni[method]) { + throw Error('uni.' + method + ' not exists') + } + if (!canIMock(method)) { + throw Error("You can't mock uni." + method) + } + // TODO getOwnPropertyDescriptor? + var result = params.result + if (isUndef(result)) { + // restoreUniMethod + if (originUni[method]) { + uni[method] = originUni[method] + delete originUni[method] + } + return Promise.resolve() + } + var mockFn = isSyncApi(method) + ? function () { + return result + } + : function (params) { + setTimeout(function () { + var isFail = result.errMsg && result.errMsg.indexOf(':fail') !== -1 + if (isFail) { + params.fail && params.fail(result) + } else { + params.success && params.success(result) + } + params.complete && params.complete(result) + }, 4) + } + // mockFn.origin = originUni[method] || uni[method]; + if (!originUni[method]) { + originUni[method] = uni[method] + } + uni[method] = mockFn + return Promise.resolve() + }, +} + +var Page = { + getData: function (params) { + return getData(getPageVm(params.pageId), params.path) + }, + setData: function (params) { + return setData(getPageVm(params.pageId), params.data) + }, + callMethod: function (params) { + var _a + var err = + ((_a = {}), + (_a[CALL_METHOD_ERROR.VM_NOT_EXISTS] = + 'Page[' + params.pageId + '] not exists'), + (_a[CALL_METHOD_ERROR.METHOD_NOT_EXISTS] = + 'page.' + params.method + ' not exists'), + _a) + return new Promise(function (resolve, reject) { + callMethod(getPageVm(params.pageId), params.method, params.args) + .then(function (res) { + return resolve(res) + }) + .catch(function (type) { + reject(Error(err[type])) + }) + }) + }, +} + +function getNodeId$1(params) { + return params.nodeId || params.elementId +} +var Element = { + getData: function (params) { + return getData( + getComponentVm(params.pageId, getNodeId$1(params)), + params.path + ) + }, + setData: function (params) { + return setData( + getComponentVm(params.pageId, getNodeId$1(params)), + params.data + ) + }, + callMethod: function (params) { + var _a + var nodeId = getNodeId$1(params) + var err = + ((_a = {}), + (_a[CALL_METHOD_ERROR.VM_NOT_EXISTS] = + 'Component[' + params.pageId + ':' + nodeId + '] not exists'), + (_a[CALL_METHOD_ERROR.METHOD_NOT_EXISTS] = + 'component.' + params.method + ' not exists'), + _a) + return new Promise(function (resolve, reject) { + callMethod( + getComponentVm(params.pageId, nodeId), + params.method, + params.args + ) + .then(function (res) { + return resolve(res) + }) + .catch(function (type) { + reject(Error(err[type])) + }) + }) + }, +} + +// Unique ID creation requires a high quality random # generator. In the browser we therefore +// require the crypto API and do not support built-in fallback to lower quality random number +// generators (like Math.random()). +// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also, +// find the complete implementation of crypto (msCrypto) on IE11. +var getRandomValues = + (typeof crypto != 'undefined' && + crypto.getRandomValues && + crypto.getRandomValues.bind(crypto)) || + (typeof msCrypto != 'undefined' && + typeof msCrypto.getRandomValues == 'function' && + msCrypto.getRandomValues.bind(msCrypto)) + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +var byteToHex = [] + +for (var i = 0; i < 256; ++i) { + byteToHex[i] = (i + 0x100).toString(16).substr(1) +} + +var BUILITIN = [ + 'movable-view', + 'picker', + 'ad', + 'button', + 'checkbox-group', + 'checkbox', + 'form', + 'icon', + 'label', + 'movable-area', + 'navigator', + 'picker-view-column', + 'picker-view', + 'progress', + 'radio-group', + 'radio', + 'rich-text', + 'u-slider', + 'swiper-item', + 'swiper', + 'switch', +] +var BUILITIN_ALIAS = BUILITIN.map(function (tag) { + return capitalize(camelize(tag)) +}) + +var Api = {} +Object.keys(App).forEach(function (method) { + Api['App.' + method] = App[method] +}) +Object.keys(Page).forEach(function (method) { + Api['Page.' + method] = Page[method] +}) +Object.keys(Element).forEach(function (method) { + Api['Element.' + method] = Element[method] +}) +var wsEndpoint = process.env.UNI_AUTOMATOR_WS_ENDPOINT +var socketTask +function send(data) { + socketTask.send({ data: JSON.stringify(data) }) +} +function onMessage(res) { + var _a = JSON.parse(res.data), + id = _a.id, + method = _a.method, + params = _a.params + var data = { id: id } + var fn = Api[method] + if (!fn) { + if (!fn) { + data.error = { + message: method + ' unimplemented', + } + return send(data) + } + } + try { + fn(params) + .then(function (res) { + res && (data.result = res) + }) + .catch(function (err) { + data.error = { + message: err.message, + } + }) + .finally(function () { + send(data) + }) + } catch (err) { + data.error = { + message: err.message, + } + send(data) + } +} +function initRuntimeAutomator(options) { + if (options === void 0) { + options = {} + } + socketTask = uni.connectSocket({ + url: wsEndpoint, + complete: function () {}, + }) + socketTask.onMessage(onMessage) + socketTask.onOpen(function (res) { + options.success && options.success() + console.log('已开启自动化测试...') + }) + socketTask.onError(function (res) { + console.log('automator.onError', res) + }) + socketTask.onClose(function () { + options.fail && options.fail({ errMsg: '$$initRuntimeAutomator:fail' }) + console.log('automator.onClose') + }) +} +//@ts-ignore +{ + //@ts-ignore + swan.$$initRuntimeAutomator = initRuntimeAutomator + setTimeout(function () { + //@ts-ignore + swan.$$initRuntimeAutomator() + }, 500) +} diff --git a/packages/uni-mp-baidu/lib/uni.automator.js b/packages/uni-mp-baidu/lib/uni.automator.js new file mode 100644 index 000000000..744e20604 --- /dev/null +++ b/packages/uni-mp-baidu/lib/uni.automator.js @@ -0,0 +1,556 @@ +'use strict' + +function _interopDefault(ex) { + return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex +} + +var os = _interopDefault(require('os')) +var path = _interopDefault(require('path')) +var debug = _interopDefault(require('debug')) +var isWindows = _interopDefault(require('licia/isWindows')) +var fs = _interopDefault(require('fs')) +var child_process = _interopDefault(require('child_process')) +var sleep = _interopDefault(require('licia/sleep')) +var toStr = _interopDefault(require('licia/toStr')) +var waitUntil = _interopDefault(require('licia/waitUntil')) +var concat = _interopDefault(require('licia/concat')) +var getPort = _interopDefault(require('licia/getPort')) +var dateFormat = _interopDefault(require('licia/dateFormat')) +require('address') +require('default-gateway') +require('licia/isStr') +var WebSocket = _interopDefault(require('ws')) +var events = require('events') +var uuid = _interopDefault(require('licia/uuid')) +var stringify = _interopDefault(require('licia/stringify')) + +const TAGNAME_REGEX = /(^[a-z][a-z0-9-]*)/i +const NAVIGATOR_SELECTOR_REGEX = /^navigator/i +const NAVIGATOR_TAGNAME_REGEX = /^swan-nav$/i +var TYPES +;(function (TYPES) { + TYPES['SELECTOR'] = 'selector' + TYPES['TAGNAME'] = 'tagName' +})(TYPES || (TYPES = {})) +const argsProcessors = { + [TYPES.SELECTOR]: [ + { + test: NAVIGATOR_SELECTOR_REGEX, + processor: (str) => str.replace(NAVIGATOR_SELECTOR_REGEX, 'nav'), + }, + { + test: TAGNAME_REGEX, + processor: (str) => `swan-${str}`, + }, + ], + [TYPES.TAGNAME]: [ + { + test: NAVIGATOR_TAGNAME_REGEX, + processor: (str) => + str.replace(NAVIGATOR_TAGNAME_REGEX, 'swan-navigator'), + }, + { + test: TAGNAME_REGEX, + processor: (str) => str.toLocaleLowerCase().replace('swan-', ''), + }, + ], +} +const getProcess = (arg) => { + return (str) => { + const processors = (argsProcessors[arg] || []).filter((processor) => + processor.test.test(str) + ) + for (const processor of processors) { + str = processor.processor(str) + } + return str + } +} +const processSingleSelector = getProcess(TYPES.SELECTOR) +const processTagName = getProcess(TYPES.TAGNAME) +const processSelector = (selector) => + selector + .split(' ') + .map((selector) => processSingleSelector(selector)) + .join(' ') +const addElementId = (params) => + Object.assign({}, params, { + type: 'id', + info: { + id: params.elementId, + }, + }) + +const qrCodeTerminal = require('qrcode-terminal') +const QrCodeReader = require('qrcode-reader') +const isWin = /^win/.test(process.platform) +async function resolvePort(port, defaultPort) { + const newPort = await getPort(port || defaultPort) + if (port && newPort !== port) { + throw Error(`Port ${port} is in use, please specify another port`) + } + return newPort +} + +class Transport extends events.EventEmitter { + constructor(ws) { + super() + this.ws = ws + this.ws.addEventListener('message', (event) => { + this.emit('message', event.data) + }) + this.ws.addEventListener('close', () => { + this.emit('close') + }) + } + send(message) { + this.ws.send(message) + } + close() { + this.ws.close() + } +} + +const CLOSE_ERR_TIP = 'Connection closed' +class Connection extends events.EventEmitter { + constructor(transport, puppet, namespace) { + super() + this.puppet = puppet + this.namespace = namespace + this.callbacks = new Map() + this.transport = transport + this.debug = debug('automator:protocol:' + this.namespace) + this.onMessage = (msg) => { + this.debug(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} ◀ RECV ${msg}`) + const { id, method, error, result, params } = JSON.parse(msg) + if (!id) { + return this.puppet.emit(method, params) + } + const { callbacks } = this + if (id && callbacks.has(id)) { + const promise = callbacks.get(id) + callbacks.delete(id) + error ? promise.reject(Error(error.message)) : promise.resolve(result) + } + } + this.onClose = () => { + this.callbacks.forEach((promise) => { + promise.reject(Error(CLOSE_ERR_TIP)) + }) + } + this.transport.on('message', this.onMessage) + this.transport.on('close', this.onClose) + } + send(method, params = {}, reflect = true) { + if (reflect && this.puppet.adapter.has(method)) { + return this.puppet.adapter.send(this, method, params) + } + const id = uuid() + const data = stringify({ + id, + method, + params, + }) + this.debug(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} SEND ► ${data}`) + return new Promise((resolve, reject) => { + try { + this.transport.send(data) + } catch (e) { + reject(Error(CLOSE_ERR_TIP)) + } + this.callbacks.set(id, { + resolve, + reject, + }) + }) + } + dispose() { + this.transport.close() + } + static createDevtoolConnection(url, puppet) { + return new Promise((resolve, reject) => { + const ws = new WebSocket(url) + ws.addEventListener('open', () => { + resolve(new Connection(new Transport(ws), puppet, 'devtool')) + }) + ws.addEventListener('error', reject) + }) + } + static createRuntimeConnection(port, puppet, timeout) { + return new Promise((resolve, reject) => { + debug('automator:runtime')( + `${dateFormat('yyyy-mm-dd HH:MM:ss:l')} port=${port}` + ) + const wss = new WebSocket.Server({ + port, + }) + waitUntil( + async () => { + if (puppet.runtimeConnection) { + return true + } + }, + timeout, + 1e3 + ).catch(() => { + wss.close() + reject( + 'Failed to connect to runtime, please make sure the project is running' + ) + }) + wss.on('connection', function connection(ws) { + debug('automator:runtime')( + `${dateFormat('yyyy-mm-dd HH:MM:ss:l')} connected` + ) + const connection = new Connection(new Transport(ws), puppet, 'runtime') + // 可能会被重新连接,刷新成最新的 + puppet.setRuntimeConnection(connection) + resolve(connection) + }) + puppet.setRuntimeServer(wss) + }) + } +} + +const debugDevtools = debug('automator:devtool') +function resolveDevtoolsPath(cliPath, puppet) { + const paths = puppet.devtools.paths.slice(0) + if (cliPath) { + paths.unshift(cliPath) + } + for (const cliPath of paths) { + if (fs.existsSync(cliPath)) { + return cliPath + } + } + throw Error( + `${puppet.devtools.name} not found, please specify executablePath option` + ) +} +async function validateDevtools(options, puppet) { + const cliPath = resolveDevtoolsPath(options.executablePath, puppet) + let port = options.port || puppet.devtools.defaultPort + if (options.launch !== false) { + try { + port = await resolvePort(port) + } catch (e) { + // console.log(`Port ${port} is in use, try to connect directly`); + options.launch = false + } + } else { + const newPort = await getPort(port) + if (port === newPort) { + options.launch = true + // console.log(`try to launch ${this.puppet.devtools.name}`); + } + } + return Object.assign(Object.assign({}, options), { port, cliPath }) +} +async function connectTool(options, puppet) { + let connection + try { + connection = await Connection.createDevtoolConnection( + options.wsEndpoint, + puppet + ) + } catch (e) { + throw Error( + `Failed connecting to ${options.wsEndpoint}, check if target project window is opened with automation enabled` + ) + } + return connection +} +async function createDevtools(projectPath, options, puppet) { + const { + port, + cliPath, + timeout, + cwd = '', + account = '', + args = [], + launch = true, + } = options + let launchFailed = false + let connectFailed = false + if (launch !== false) { + const spawnOptions = { + stdio: 'ignore', + } + cwd && (spawnOptions.cwd = cwd) + let spawnArgs = concat(args, []) + //@ts-ignore + { + spawnArgs = concat(spawnArgs, ['--auto']) + } + spawnArgs = concat(spawnArgs, [projectPath, '--auto-port', toStr(port)]) + account && (spawnArgs = concat(spawnArgs, ['--auto-account', account])) + try { + debugDevtools('%s %o %o', cliPath, spawnArgs, spawnOptions) + const cliProcess = child_process.spawn(cliPath, spawnArgs, spawnOptions) + cliProcess.on('error', (err) => { + launchFailed = true + }) + cliProcess.on('exit', () => { + setTimeout(() => { + connectFailed = true + }, 15e3) + }) + // TODO unref? + cliProcess.unref() + } catch (err) { + launchFailed = false + } + } else { + setTimeout(() => { + connectFailed = true + }, 15e3) + } + const connection = await waitUntil( + async () => { + try { + if (launchFailed || connectFailed) { + return true + } + const connection = await connectTool( + { wsEndpoint: `ws://127.0.0.1:${port}` }, + puppet + ) + return connection + } catch (err) {} + }, + timeout, + 1e3 + ) + if (launchFailed) { + throw Error( + `Failed to launch ${puppet.devtools.name}, please make sure cliPath is correctly specified` + ) + } + if (connectFailed) { + throw Error( + `Failed to launch ${puppet.devtools.name} , please make sure http port is open` + ) + } + await sleep(5e3) + debugDevtools(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} connected`) + return connection +} + +const paths = [] +;['', '-rc'].forEach((v) => { + if (isWindows) { + paths.push( + path.join(os.homedir(), `AppData/Local/Programs/swan-ide-gui${v}/cli.bat`) + ) + paths.push(`C:/Program Files/swan-ide-gui${v}/cli.bat`) + } else { + paths.push(`/Applications/百度开发者工具${v}.app/Contents/MacOS/cli`) + } +}) +const puppet = { + devtools: { + name: 'Baidu DevTools', + remote: true, + automator: true, + paths, + required: ['project.swan.json', 'app.json', 'app.js'], + defaultPort: 9430, + validate: validateDevtools, + async create(projectPath, options, puppet) { + const connection = await createDevtools(projectPath, options, puppet) + if (!puppet.compiled) { + debug('automator:devtool')('initRuntimeAutomator') + connection.send('smartapp.swan', { + api: '$$initRuntimeAutomator', + params: [], + }) + } else { + debug('automator:devtool')('Waiting for runtime automator') + } + return connection + }, + }, + adapter: { + 'Tool.enableRemoteDebug': { + reflect: async (send) => { + return { qrCode: (await send('Tool.enablePreview')).url } + }, + }, + 'App.exit': { + reflect: async () => Promise.resolve(), + }, + // "App.callUniMethod": { + // reflect: "smartapp.swan", + // params: (params) => + // Object.assign( + // { + // api: params.method, + // params: params.args, + // }, + // params + // ), + // }, + 'Page.getElement': { + reflect: async (send, params) => { + return (await send('Page.getElements', params)).elements[0] + }, + }, + 'Page.getElements': { + reflect: async (send, params) => { + return { + elements: ( + await send( + 'smartapp.element.getBySelector', + Object.assign(Object.assign({}, params), { + properties: ['id', 'tagName'], + selector: processSelector(params.selector), + }) + ) + ).map((element) => { + const properties = element.properties + return { + elementId: properties.id, + nodeId: properties.id, + tagName: processTagName(properties.tagName), + } + }), + } + }, + }, + 'Page.getWindowProperties': { + reflect: async (send, params) => { + const properties = params.names.map((name) => + name.replace('document.documentElement.', '') + ) + const elem = ( + await send('smartapp.element.getBySelector', { + properties, + selector: 'html', + }) + )[0] + return { + properties: properties.map((name) => elem.properties[name]), + } + }, + }, + 'Element.getHTML': { + reflect: async (send, params) => { + const names = [params.type + 'HTML'] + return { + html: ( + await send( + 'Element.getDOMProperties', + Object.assign(Object.assign({}, params), { names }) + ) + ).properties[0], + } + }, + }, + 'Element.getElement': { + reflect: async (send, params) => { + return (await send('Element.getElements', params)).elements[0] + }, + }, + 'Element.getElements': { + reflect: async (send, params) => { + const { elements } = await send( + 'Page.getElements', + Object.assign(Object.assign({}, params), { + selector: `#${params.elementId} ${params.selector}`, + }) + ) + elements.forEach((element) => { + element.nodeId = element.id + }) + return { elements } + }, + }, + 'Element.getAttributes': { + reflect: async (send, params) => { + const attributes = [] + for (const attribute of params.names) { + attributes.push( + await send( + 'smartapp.element.getAttribute', + Object.assign({ attribute }, params) + ) + ) + } + return { + attributes, + } + }, + params: addElementId, + }, + 'Element.getStyles': { + reflect: async (send, params) => { + const styles = [] + for (const style of params.names) { + styles.push( + await send( + 'smartapp.element.getComputedStyle', + Object.assign({ style }, params) + ) + ) + } + return { + styles, + } + }, + params: addElementId, + }, + 'Element.getDOMProperties': { + reflect: async (send, params) => { + const properties = [] + for (const property of params.names) { + properties.push( + await send( + 'smartapp.element.getProperty', + Object.assign({ property }, params) + ) + ) + } + return { + properties, + } + }, + params: addElementId, + }, + 'Element.getProperties': { + reflect: async (send, params) => { + const properties = [] + for (const attribute of params.names) { + properties.push( + await send( + 'smartapp.element.getAttribute', + Object.assign({ attribute }, params) + ) + ) + } + return { + properties, + } + }, + params: addElementId, + }, + 'Element.getOffset': { + reflect: async (send, params) => ({ + left: await send( + 'smartapp.element.getProperty', + Object.assign({ property: 'offsetLeft' }, params) + ), + top: await send( + 'smartapp.element.getProperty', + Object.assign({ property: 'offsetTop' }, params) + ), + }), + params: addElementId, + }, + 'Element.tap': { + reflect: 'smartapp.element.touch', + params: addElementId, + }, + }, +} + +module.exports = puppet diff --git a/packages/uni-mp-baidu/package.json b/packages/uni-mp-baidu/package.json index abdaa51ae..22cc5bba1 100644 --- a/packages/uni-mp-baidu/package.json +++ b/packages/uni-mp-baidu/package.json @@ -3,6 +3,10 @@ "version": "3.0.0-alpha-3000020210813002", "description": "uni-app mp-baidu", "main": "dist/index.js", + "files": [ + "dist", + "lib" + ], "repository": { "type": "git", "url": "git+https://github.com/dcloudio/uni-app.git", diff --git a/packages/uni-mp-weixin/lib/automator.js b/packages/uni-mp-weixin/lib/automator.js new file mode 100644 index 000000000..894696fec --- /dev/null +++ b/packages/uni-mp-weixin/lib/automator.js @@ -0,0 +1,489 @@ +var hasOwnProperty = Object.prototype.hasOwnProperty +var hasOwn = function (val, key) { + return hasOwnProperty.call(val, key) +} +var isUndef = function (v) { + return v === undefined || v === null +} +var isArray = Array.isArray +var isPromise = function (obj) { + return ( + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function' + ) +} +var cacheStringFunction = function (fn) { + var cache = Object.create(null) + return function (str) { + var hit = cache[str] + return hit || (cache[str] = fn(str)) + } +} +var camelizeRE = /-(\w)/g +var camelize = cacheStringFunction(function (str) { + return str.replace(camelizeRE, function (_, c) { + return c ? c.toUpperCase() : '' + }) +}) +var capitalize = cacheStringFunction(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) +}) +var PATH_RE = + /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g +function getPaths(path, data) { + if (isArray(path)) { + return path + } + if (data && hasOwn(data, path)) { + return [path] + } + var res = [] + path.replace(PATH_RE, function (match, p1, offset, string) { + res.push(offset ? string.replace(/\\(\\)?/g, '$1') : p1 || match) + return string + }) + return res +} +function getDataByPath(data, path) { + var paths = getPaths(path, data) + var dataPath + for (dataPath = paths.shift(); !isUndef(dataPath); ) { + if (null == (data = data[dataPath])) { + return + } + dataPath = paths.shift() + } + return data +} + +function getPageId(page) { + if (page.__wxWebviewId__) { + //mp-weixin + return page.__wxWebviewId__ + } + if (page.privateProperties) { + //mp-baidu + return page.privateProperties.slaveId + } + if (page.$page) { + //h5 and app-plus + return page.$page.id + } +} +function getPagePath(page) { + return page.route || page.uri +} +function getPageQuery(page) { + return page.options || (page.$page && page.$page.options) || {} +} +function parsePage(page) { + return { + id: getPageId(page), + path: getPagePath(page), + query: getPageQuery(page), + } +} +function getPageById(id) { + return getCurrentPages().find(function (page) { + return getPageId(page) === id + }) +} +function getPageVm(id) { + var page = getPageById(id) + return page && page.$vm +} +function getNodeId(scope) { + return scope.__wxExparserNodeId__ || scope.nodeId || scope.id +} +function matchNodeId(vm, nodeId) { + return vm.$scope && getNodeId(vm.$scope) === nodeId +} +function findComponentVm(vm, nodeId) { + var res + if (vm) { + if (matchNodeId(vm, nodeId)) { + res = vm + } else { + vm.$children.find(function (child) { + res = findComponentVm(child, nodeId) + return res + }) + } + } + return res +} +function getComponentVm(pageId, nodeId) { + var pageVm = getPageVm(pageId) + return pageVm && findComponentVm(pageVm, nodeId) +} +function getData(vm, path) { + var data + if (vm) { + data = path ? getDataByPath(vm.$data, path) : Object.assign({}, vm.$data) + } + return Promise.resolve({ data: data }) +} +function setData(vm, data) { + if (vm) { + Object.keys(data).forEach(function (name) { + vm[name] = data[name] + }) + } + return Promise.resolve() +} +var CALL_METHOD_ERROR +;(function (CALL_METHOD_ERROR) { + CALL_METHOD_ERROR['VM_NOT_EXISTS'] = 'VM_NOT_EXISTS' + CALL_METHOD_ERROR['METHOD_NOT_EXISTS'] = 'METHOD_NOT_EXISTS' +})(CALL_METHOD_ERROR || (CALL_METHOD_ERROR = {})) +function callMethod(vm, method, args) { + return new Promise(function (resolve, reject) { + if (!vm) { + return reject(CALL_METHOD_ERROR.VM_NOT_EXISTS) + } + if (!vm[method]) { + return reject(CALL_METHOD_ERROR.VM_NOT_EXISTS) + } + var ret = vm[method].apply(vm, args) + isPromise(ret) + ? ret.then(function (res) { + resolve({ result: res }) + }) + : resolve({ result: ret }) + }) +} + +var SYNC_APIS = [ + 'stopRecord', + 'getRecorderManager', + 'pauseVoice', + 'stopVoice', + 'pauseBackgroundAudio', + 'stopBackgroundAudio', + 'getBackgroundAudioManager', + 'createAudioContext', + 'createInnerAudioContext', + 'createVideoContext', + 'createCameraContext', + 'createMapContext', + 'canIUse', + 'startAccelerometer', + 'stopAccelerometer', + 'startCompass', + 'stopCompass', + 'hideToast', + 'hideLoading', + 'showNavigationBarLoading', + 'hideNavigationBarLoading', + 'navigateBack', + 'createAnimation', + 'pageScrollTo', + 'createSelectorQuery', + 'createCanvasContext', + 'createContext', + 'drawCanvas', + 'hideKeyboard', + 'stopPullDownRefresh', + 'arrayBufferToBase64', + 'base64ToArrayBuffer', +] +var originUni = {} +var SYNC_API_RE = /Sync$/ +var MOCK_API_BLACKLIST_RE = /^on|^off/ +function isSyncApi(method) { + return SYNC_API_RE.test(method) || SYNC_APIS.indexOf(method) !== -1 +} +function canIMock(method) { + return !MOCK_API_BLACKLIST_RE.test(method) +} +var App = { + getPageStack: function () { + return Promise.resolve({ + pageStack: getCurrentPages().map(function (page) { + return parsePage(page) + }), + }) + }, + getCurrentPage: function () { + var pages = getCurrentPages() + var len = pages.length + return new Promise(function (resolve, reject) { + if (!len) { + reject(Error('getCurrentPages().length=0')) + } else { + resolve(parsePage(pages[len - 1])) + } + }) + }, + callUniMethod: function (params) { + var method = params.method + var args = params.args + return new Promise(function (resolve, reject) { + if (!uni[method]) { + return reject(Error('uni.' + method + ' not exists')) + } + if (isSyncApi(method)) { + return resolve({ + result: uni[method].apply(uni, args), + }) + } + var params = [ + Object.assign({}, args[0] || {}, { + success: function (result) { + var timeout = method === 'pageScrollTo' ? 350 : 0 + setTimeout(function () { + resolve({ result: result }) + }, timeout) + }, + fail: function (res) { + reject(Error(res.errMsg.replace(method + ':fail ', ''))) + }, + }), + ] + uni[method].apply(uni, params) + }) + }, + mockUniMethod: function (params) { + var method = params.method + if (!uni[method]) { + throw Error('uni.' + method + ' not exists') + } + if (!canIMock(method)) { + throw Error("You can't mock uni." + method) + } + // TODO getOwnPropertyDescriptor? + var result = params.result + if (isUndef(result)) { + // restoreUniMethod + if (originUni[method]) { + uni[method] = originUni[method] + delete originUni[method] + } + return Promise.resolve() + } + var mockFn = isSyncApi(method) + ? function () { + return result + } + : function (params) { + setTimeout(function () { + var isFail = result.errMsg && result.errMsg.indexOf(':fail') !== -1 + if (isFail) { + params.fail && params.fail(result) + } else { + params.success && params.success(result) + } + params.complete && params.complete(result) + }, 4) + } + // mockFn.origin = originUni[method] || uni[method]; + if (!originUni[method]) { + originUni[method] = uni[method] + } + uni[method] = mockFn + return Promise.resolve() + }, +} + +var Page = { + getData: function (params) { + return getData(getPageVm(params.pageId), params.path) + }, + setData: function (params) { + return setData(getPageVm(params.pageId), params.data) + }, + callMethod: function (params) { + var _a + var err = + ((_a = {}), + (_a[CALL_METHOD_ERROR.VM_NOT_EXISTS] = + 'Page[' + params.pageId + '] not exists'), + (_a[CALL_METHOD_ERROR.METHOD_NOT_EXISTS] = + 'page.' + params.method + ' not exists'), + _a) + return new Promise(function (resolve, reject) { + callMethod(getPageVm(params.pageId), params.method, params.args) + .then(function (res) { + return resolve(res) + }) + .catch(function (type) { + reject(Error(err[type])) + }) + }) + }, +} + +function getNodeId$1(params) { + return params.nodeId || params.elementId +} +var Element = { + getData: function (params) { + return getData( + getComponentVm(params.pageId, getNodeId$1(params)), + params.path + ) + }, + setData: function (params) { + return setData( + getComponentVm(params.pageId, getNodeId$1(params)), + params.data + ) + }, + callMethod: function (params) { + var _a + var nodeId = getNodeId$1(params) + var err = + ((_a = {}), + (_a[CALL_METHOD_ERROR.VM_NOT_EXISTS] = + 'Component[' + params.pageId + ':' + nodeId + '] not exists'), + (_a[CALL_METHOD_ERROR.METHOD_NOT_EXISTS] = + 'component.' + params.method + ' not exists'), + _a) + return new Promise(function (resolve, reject) { + callMethod( + getComponentVm(params.pageId, nodeId), + params.method, + params.args + ) + .then(function (res) { + return resolve(res) + }) + .catch(function (type) { + reject(Error(err[type])) + }) + }) + }, +} + +// Unique ID creation requires a high quality random # generator. In the browser we therefore +// require the crypto API and do not support built-in fallback to lower quality random number +// generators (like Math.random()). +// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also, +// find the complete implementation of crypto (msCrypto) on IE11. +var getRandomValues = + (typeof crypto != 'undefined' && + crypto.getRandomValues && + crypto.getRandomValues.bind(crypto)) || + (typeof msCrypto != 'undefined' && + typeof msCrypto.getRandomValues == 'function' && + msCrypto.getRandomValues.bind(msCrypto)) + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +var byteToHex = [] + +for (var i = 0; i < 256; ++i) { + byteToHex[i] = (i + 0x100).toString(16).substr(1) +} + +var BUILITIN = [ + 'movable-view', + 'picker', + 'ad', + 'button', + 'checkbox-group', + 'checkbox', + 'form', + 'icon', + 'label', + 'movable-area', + 'navigator', + 'picker-view-column', + 'picker-view', + 'progress', + 'radio-group', + 'radio', + 'rich-text', + 'u-slider', + 'swiper-item', + 'swiper', + 'switch', +] +var BUILITIN_ALIAS = BUILITIN.map(function (tag) { + return capitalize(camelize(tag)) +}) + +var Api = {} +Object.keys(App).forEach(function (method) { + Api['App.' + method] = App[method] +}) +Object.keys(Page).forEach(function (method) { + Api['Page.' + method] = Page[method] +}) +Object.keys(Element).forEach(function (method) { + Api['Element.' + method] = Element[method] +}) +var wsEndpoint = process.env.UNI_AUTOMATOR_WS_ENDPOINT +var socketTask +function send(data) { + socketTask.send({ data: JSON.stringify(data) }) +} +function onMessage(res) { + var _a = JSON.parse(res.data), + id = _a.id, + method = _a.method, + params = _a.params + var data = { id: id } + var fn = Api[method] + if (!fn) { + if (!fn) { + data.error = { + message: method + ' unimplemented', + } + return send(data) + } + } + try { + fn(params) + .then(function (res) { + res && (data.result = res) + }) + .catch(function (err) { + data.error = { + message: err.message, + } + }) + .finally(function () { + send(data) + }) + } catch (err) { + data.error = { + message: err.message, + } + send(data) + } +} +function initRuntimeAutomator(options) { + if (options === void 0) { + options = {} + } + socketTask = uni.connectSocket({ + url: wsEndpoint, + complete: function () {}, + }) + socketTask.onMessage(onMessage) + socketTask.onOpen(function (res) { + options.success && options.success() + console.log('已开启自动化测试...') + }) + socketTask.onError(function (res) { + console.log('automator.onError', res) + }) + socketTask.onClose(function () { + options.fail && options.fail({ errMsg: '$$initRuntimeAutomator:fail' }) + console.log('automator.onClose') + }) +} +//@ts-ignore +{ + //@ts-ignore + wx.$$initRuntimeAutomator = initRuntimeAutomator + setTimeout(function () { + //@ts-ignore + wx.$$initRuntimeAutomator() + }, 500) + //@ts-ignore +} diff --git a/packages/uni-mp-weixin/lib/uni.automator.js b/packages/uni-mp-weixin/lib/uni.automator.js new file mode 100644 index 000000000..98a27b90e --- /dev/null +++ b/packages/uni-mp-weixin/lib/uni.automator.js @@ -0,0 +1,361 @@ +'use strict' + +function _interopDefault(ex) { + return ex && typeof ex === 'object' && 'default' in ex ? ex['default'] : ex +} + +var debug = _interopDefault(require('debug')) +var isWindows = _interopDefault(require('licia/isWindows')) +require('address') +require('default-gateway') +require('licia/isStr') +var getPort = _interopDefault(require('licia/getPort')) +var fs = _interopDefault(require('fs')) +var child_process = _interopDefault(require('child_process')) +var sleep = _interopDefault(require('licia/sleep')) +var toStr = _interopDefault(require('licia/toStr')) +var waitUntil = _interopDefault(require('licia/waitUntil')) +var concat = _interopDefault(require('licia/concat')) +var dateFormat = _interopDefault(require('licia/dateFormat')) +var WebSocket = _interopDefault(require('ws')) +var events = require('events') +var uuid = _interopDefault(require('licia/uuid')) +var stringify = _interopDefault(require('licia/stringify')) + +const qrCodeTerminal = require('qrcode-terminal') +const QrCodeReader = require('qrcode-reader') +const isWin = /^win/.test(process.platform) +function decodeQrCode(qrCode) { + const buffer = new Buffer(qrCode, 'base64') + return new Promise(async (resolve, reject) => { + const img = await require('jimp').read(buffer) + const qrCodeReader = new QrCodeReader() + qrCodeReader.callback = function (error, value) { + if (error) { + return reject(error) + } + resolve(value.result) + } + qrCodeReader.decode(img.bitmap) + }) +} +async function resolvePort(port, defaultPort) { + const newPort = await getPort(port || defaultPort) + if (port && newPort !== port) { + throw Error(`Port ${port} is in use, please specify another port`) + } + return newPort +} + +class Transport extends events.EventEmitter { + constructor(ws) { + super() + this.ws = ws + this.ws.addEventListener('message', (event) => { + this.emit('message', event.data) + }) + this.ws.addEventListener('close', () => { + this.emit('close') + }) + } + send(message) { + this.ws.send(message) + } + close() { + this.ws.close() + } +} + +const CLOSE_ERR_TIP = 'Connection closed' +class Connection extends events.EventEmitter { + constructor(transport, puppet, namespace) { + super() + this.puppet = puppet + this.namespace = namespace + this.callbacks = new Map() + this.transport = transport + this.debug = debug('automator:protocol:' + this.namespace) + this.onMessage = (msg) => { + this.debug(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} ◀ RECV ${msg}`) + const { id, method, error, result, params } = JSON.parse(msg) + if (!id) { + return this.puppet.emit(method, params) + } + const { callbacks } = this + if (id && callbacks.has(id)) { + const promise = callbacks.get(id) + callbacks.delete(id) + error ? promise.reject(Error(error.message)) : promise.resolve(result) + } + } + this.onClose = () => { + this.callbacks.forEach((promise) => { + promise.reject(Error(CLOSE_ERR_TIP)) + }) + } + this.transport.on('message', this.onMessage) + this.transport.on('close', this.onClose) + } + send(method, params = {}, reflect = true) { + if (reflect && this.puppet.adapter.has(method)) { + return this.puppet.adapter.send(this, method, params) + } + const id = uuid() + const data = stringify({ + id, + method, + params, + }) + this.debug(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} SEND ► ${data}`) + return new Promise((resolve, reject) => { + try { + this.transport.send(data) + } catch (e) { + reject(Error(CLOSE_ERR_TIP)) + } + this.callbacks.set(id, { + resolve, + reject, + }) + }) + } + dispose() { + this.transport.close() + } + static createDevtoolConnection(url, puppet) { + return new Promise((resolve, reject) => { + const ws = new WebSocket(url) + ws.addEventListener('open', () => { + resolve(new Connection(new Transport(ws), puppet, 'devtool')) + }) + ws.addEventListener('error', reject) + }) + } + static createRuntimeConnection(port, puppet, timeout) { + return new Promise((resolve, reject) => { + debug('automator:runtime')( + `${dateFormat('yyyy-mm-dd HH:MM:ss:l')} port=${port}` + ) + const wss = new WebSocket.Server({ + port, + }) + waitUntil( + async () => { + if (puppet.runtimeConnection) { + return true + } + }, + timeout, + 1e3 + ).catch(() => { + wss.close() + reject( + 'Failed to connect to runtime, please make sure the project is running' + ) + }) + wss.on('connection', function connection(ws) { + debug('automator:runtime')( + `${dateFormat('yyyy-mm-dd HH:MM:ss:l')} connected` + ) + const connection = new Connection(new Transport(ws), puppet, 'runtime') + // 可能会被重新连接,刷新成最新的 + puppet.setRuntimeConnection(connection) + resolve(connection) + }) + puppet.setRuntimeServer(wss) + }) + } +} + +const debugDevtools = debug('automator:devtool') +function resolveDevtoolsPath(cliPath, puppet) { + const paths = puppet.devtools.paths.slice(0) + if (cliPath) { + paths.unshift(cliPath) + } + for (const cliPath of paths) { + if (fs.existsSync(cliPath)) { + return cliPath + } + } + throw Error( + `${puppet.devtools.name} not found, please specify executablePath option` + ) +} +async function validateDevtools(options, puppet) { + const cliPath = resolveDevtoolsPath(options.executablePath, puppet) + let port = options.port || puppet.devtools.defaultPort + if (options.launch !== false) { + try { + port = await resolvePort(port) + } catch (e) { + // console.log(`Port ${port} is in use, try to connect directly`); + options.launch = false + } + } else { + const newPort = await getPort(port) + if (port === newPort) { + options.launch = true + // console.log(`try to launch ${this.puppet.devtools.name}`); + } + } + return Object.assign(Object.assign({}, options), { port, cliPath }) +} +async function connectTool(options, puppet) { + let connection + try { + connection = await Connection.createDevtoolConnection( + options.wsEndpoint, + puppet + ) + } catch (e) { + throw Error( + `Failed connecting to ${options.wsEndpoint}, check if target project window is opened with automation enabled` + ) + } + return connection +} +async function createDevtools(projectPath, options, puppet) { + const { + port, + cliPath, + timeout, + cwd = '', + account = '', + args = [], + launch = true, + } = options + let launchFailed = false + let connectFailed = false + if (launch !== false) { + const spawnOptions = { + stdio: 'ignore', + } + //@ts-ignore + { + spawnOptions.detached = true + } + cwd && (spawnOptions.cwd = cwd) + let spawnArgs = concat(args, []) + //@ts-ignore + { + spawnArgs = concat(spawnArgs, ['auto', '--project']) + } + spawnArgs = concat(spawnArgs, [projectPath, '--auto-port', toStr(port)]) + account && (spawnArgs = concat(spawnArgs, ['--auto-account', account])) + try { + debugDevtools('%s %o %o', cliPath, spawnArgs, spawnOptions) + const cliProcess = child_process.spawn(cliPath, spawnArgs, spawnOptions) + cliProcess.on('error', (err) => { + launchFailed = true + }) + cliProcess.on('exit', () => { + setTimeout(() => { + connectFailed = true + }, 15e3) + }) + // TODO unref? + cliProcess.unref() + } catch (err) { + launchFailed = false + } + } else { + setTimeout(() => { + connectFailed = true + }, 15e3) + } + const connection = await waitUntil( + async () => { + try { + if (launchFailed || connectFailed) { + return true + } + const connection = await connectTool( + { wsEndpoint: `ws://127.0.0.1:${port}` }, + puppet + ) + return connection + } catch (err) {} + }, + timeout, + 1e3 + ) + if (launchFailed) { + throw Error( + `Failed to launch ${puppet.devtools.name}, please make sure cliPath is correctly specified` + ) + } + if (connectFailed) { + throw Error( + `Failed to launch ${puppet.devtools.name} , please make sure http port is open` + ) + } + await sleep(5e3) + debugDevtools(`${dateFormat('yyyy-mm-dd HH:MM:ss:l')} connected`) + return connection +} + +function wrapper(fnStr) { + if (fnStr[fnStr.length - 1] === '}') { + return fnStr.replace('{', '{\nvar uni = wx;\n') + } + return fnStr.replace('=>', '=>{\nvar uni = wx;\nreturn ') + '}' +} +const puppet = { + devtools: { + name: 'Wechat web devTools', + remote: true, + automator: true, + paths: [ + isWindows + ? 'C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat' + : '/Applications/wechatwebdevtools.app/Contents/MacOS/cli', + ], + required: ['project.config.json', 'app.json', 'app.js'], + defaultPort: 9420, + validate: validateDevtools, + async create(projectPath, options, puppet) { + const connection = await createDevtools(projectPath, options, puppet) + if (!puppet.compiled) { + debug('automator:devtool')('initRuntimeAutomator') + connection.send('App.callWxMethod', { + method: '$$initRuntimeAutomator', + args: [], + }) + } else { + debug('automator:devtool')('Waiting for runtime automator') + } + return connection + }, + }, + adapter: { + 'Tool.enableRemoteDebug': { + reflect: async (send, params) => { + let { qrCode } = await send('Tool.enableRemoteDebug', params, false) + qrCode && (qrCode = await decodeQrCode(qrCode)) + return { qrCode } + }, + }, + // "App.callUniMethod": { + // reflect: "App.callWxMethod", + // }, + 'App.callFunction': { + reflect: async (send, params) => { + return send( + 'App.callFunction', + Object.assign(Object.assign({}, params), { + functionDeclaration: wrapper(params.functionDeclaration), + }), + false + ) + }, + }, + 'Element.getHTML': { + reflect: async (send, params) => { + return { html: (await send('Element.getWXML', params, false)).wxml } + }, + }, + }, +} + +module.exports = puppet diff --git a/packages/uni-mp-weixin/package.json b/packages/uni-mp-weixin/package.json index 916fde0e0..d0928e38c 100644 --- a/packages/uni-mp-weixin/package.json +++ b/packages/uni-mp-weixin/package.json @@ -3,6 +3,10 @@ "version": "3.0.0-alpha-3000020210813002", "description": "uni-app mp-weixin", "main": "dist/index.js", + "files": [ + "dist", + "lib" + ], "repository": { "type": "git", "url": "git+https://github.com/dcloudio/uni-app.git", diff --git a/packages/uni-shared/dist/uni-shared.cjs.js b/packages/uni-shared/dist/uni-shared.cjs.js index ac178bb7d..8101a82d8 100644 --- a/packages/uni-shared/dist/uni-shared.cjs.js +++ b/packages/uni-shared/dist/uni-shared.cjs.js @@ -344,6 +344,61 @@ function parseUrl(url) { }; } +function isDebugMode() { + // @ts-expect-error + return typeof __channelId__ === 'string' && __channelId__; +} +function jsonStringifyReplacer(k, p) { + switch (shared.toRawType(p)) { + case 'Function': + return 'function() { [native code] }'; + default: + return p; + } +} +function normalizeLog(type, filename, args) { + if (isDebugMode()) { + args.push(filename.replace('at ', 'uni-app:///')); + return console[type].apply(console, args); + } + const msgs = args.map(function (v) { + const type = shared.toTypeString(v).toLowerCase(); + if (type === '[object object]' || type === '[object array]') { + try { + v = + '---BEGIN:JSON---' + + JSON.stringify(v, jsonStringifyReplacer) + + '---END:JSON---'; + } + catch (e) { + v = type; + } + } + else { + if (v === null) { + v = '---NULL---'; + } + else if (v === undefined) { + v = '---UNDEFINED---'; + } + else { + const vType = shared.toRawType(v).toUpperCase(); + if (vType === 'NUMBER' || vType === 'BOOLEAN') { + v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---'; + } + else { + v = String(v); + } + } + } + return v; + }); + return msgs.join('---COMMA---') + ' ' + filename; +} +function formatAppLog(type, filename, ...args) { + console[type](normalizeLog(type, filename, args)); +} + function plusReady(callback) { if (typeof callback !== 'function') { return; @@ -1122,6 +1177,7 @@ exports.debounce = debounce; exports.decode = decode; exports.decodedQuery = decodedQuery; exports.defaultRpx2Unit = defaultRpx2Unit; +exports.formatAppLog = formatAppLog; exports.formatDateTime = formatDateTime; exports.formatLog = formatLog; exports.getCustomDataset = getCustomDataset; diff --git a/packages/uni-shared/dist/uni-shared.d.ts b/packages/uni-shared/dist/uni-shared.d.ts index 86bda04e8..bb7113be4 100644 --- a/packages/uni-shared/dist/uni-shared.d.ts +++ b/packages/uni-shared/dist/uni-shared.d.ts @@ -165,6 +165,8 @@ export declare const EventModifierFlags: { self: number; }; +export declare function formatAppLog(type: 'log' | 'info' | 'debug' | 'warn' | 'error', filename: string, ...args: unknown[]): void; + export declare function formatDateTime({ date, mode }: { date?: Date | undefined; mode?: string | undefined; diff --git a/packages/uni-shared/dist/uni-shared.es.js b/packages/uni-shared/dist/uni-shared.es.js index dd2bebcf2..58b311538 100644 --- a/packages/uni-shared/dist/uni-shared.es.js +++ b/packages/uni-shared/dist/uni-shared.es.js @@ -1,4 +1,4 @@ -import { isHTMLTag, isSVGTag, hyphenate, camelize, extend, isString, isPlainObject, isArray, capitalize } from '@vue/shared'; +import { isHTMLTag, isSVGTag, hyphenate, camelize, extend, isString, isPlainObject, isArray, toTypeString, toRawType, capitalize } from '@vue/shared'; const BUILT_IN_TAGS = [ 'ad', @@ -340,6 +340,61 @@ function parseUrl(url) { }; } +function isDebugMode() { + // @ts-expect-error + return typeof __channelId__ === 'string' && __channelId__; +} +function jsonStringifyReplacer(k, p) { + switch (toRawType(p)) { + case 'Function': + return 'function() { [native code] }'; + default: + return p; + } +} +function normalizeLog(type, filename, args) { + if (isDebugMode()) { + args.push(filename.replace('at ', 'uni-app:///')); + return console[type].apply(console, args); + } + const msgs = args.map(function (v) { + const type = toTypeString(v).toLowerCase(); + if (type === '[object object]' || type === '[object array]') { + try { + v = + '---BEGIN:JSON---' + + JSON.stringify(v, jsonStringifyReplacer) + + '---END:JSON---'; + } + catch (e) { + v = type; + } + } + else { + if (v === null) { + v = '---NULL---'; + } + else if (v === undefined) { + v = '---UNDEFINED---'; + } + else { + const vType = toRawType(v).toUpperCase(); + if (vType === 'NUMBER' || vType === 'BOOLEAN') { + v = '---BEGIN:' + vType + '---' + v + '---END:' + vType + '---'; + } + else { + v = String(v); + } + } + } + return v; + }); + return msgs.join('---COMMA---') + ' ' + filename; +} +function formatAppLog(type, filename, ...args) { + console[type](normalizeLog(type, filename, args)); +} + function plusReady(callback) { if (typeof callback !== 'function') { return; @@ -1019,4 +1074,4 @@ function getEnvLocale() { return (lang && lang.replace(/[.:].*/, '')) || 'en'; } -export { ACTION_TYPE_ADD_EVENT, ACTION_TYPE_ADD_WXS_EVENT, ACTION_TYPE_CREATE, ACTION_TYPE_EVENT, ACTION_TYPE_INSERT, ACTION_TYPE_PAGE_CREATE, ACTION_TYPE_PAGE_CREATED, ACTION_TYPE_PAGE_SCROLL, ACTION_TYPE_REMOVE, ACTION_TYPE_REMOVE_ATTRIBUTE, ACTION_TYPE_REMOVE_EVENT, ACTION_TYPE_SET_ATTRIBUTE, ACTION_TYPE_SET_TEXT, ATTR_CHANGE_PREFIX, ATTR_CLASS, ATTR_INNER_HTML, ATTR_STYLE, ATTR_TEXT_CONTENT, ATTR_V_OWNER_ID, ATTR_V_RENDERJS, ATTR_V_SHOW, BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, EventChannel, EventModifierFlags, JSON_PROTOCOL, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_ADD_TO_FAVORITES, ON_APP_ENTER_BACKGROUND, ON_APP_ENTER_FOREGROUND, ON_BACK_PRESS, ON_ERROR, ON_HIDE, ON_KEYBOARD_HEIGHT_CHANGE, ON_LAUNCH, ON_LOAD, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, ON_PAGE_NOT_FOUND, ON_PAGE_SCROLL, ON_PULL_DOWN_REFRESH, ON_REACH_BOTTOM, ON_REACH_BOTTOM_DISTANCE, ON_READY, ON_RESIZE, ON_SHARE_APP_MESSAGE, ON_SHARE_TIMELINE, ON_SHOW, ON_TAB_ITEM_TAP, ON_THEME_CHANGE, ON_UNHANDLE_REJECTION, ON_UNLOAD, ON_WEB_INVOKE_APP_SERVICE, ON_WXS_INVOKE_CALL_METHOD, PLUS_RE, PRIMARY_COLOR, RENDERJS_MODULES, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, WXS_MODULES, WXS_PROTOCOL, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, createUniEvent, debounce, decode, decodedQuery, defaultRpx2Unit, formatDateTime, formatLog, getCustomDataset, getEnvLocale, getLen, getValueByDataPath, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isRootHook, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeEventType, normalizeTarget, once, parseEventName, parseQuery, parseUrl, passive, plusReady, removeLeadingSlash, resolveOwnerEl, resolveOwnerVm, sanitise, scrollTo, stringifyQuery, updateElementStyle }; +export { ACTION_TYPE_ADD_EVENT, ACTION_TYPE_ADD_WXS_EVENT, ACTION_TYPE_CREATE, ACTION_TYPE_EVENT, ACTION_TYPE_INSERT, ACTION_TYPE_PAGE_CREATE, ACTION_TYPE_PAGE_CREATED, ACTION_TYPE_PAGE_SCROLL, ACTION_TYPE_REMOVE, ACTION_TYPE_REMOVE_ATTRIBUTE, ACTION_TYPE_REMOVE_EVENT, ACTION_TYPE_SET_ATTRIBUTE, ACTION_TYPE_SET_TEXT, ATTR_CHANGE_PREFIX, ATTR_CLASS, ATTR_INNER_HTML, ATTR_STYLE, ATTR_TEXT_CONTENT, ATTR_V_OWNER_ID, ATTR_V_RENDERJS, ATTR_V_SHOW, BACKGROUND_COLOR, BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, DATA_RE, EventChannel, EventModifierFlags, JSON_PROTOCOL, NAVBAR_HEIGHT, NODE_TYPE_COMMENT, NODE_TYPE_ELEMENT, NODE_TYPE_PAGE, NODE_TYPE_TEXT, ON_ADD_TO_FAVORITES, ON_APP_ENTER_BACKGROUND, ON_APP_ENTER_FOREGROUND, ON_BACK_PRESS, ON_ERROR, ON_HIDE, ON_KEYBOARD_HEIGHT_CHANGE, ON_LAUNCH, ON_LOAD, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED, ON_PAGE_NOT_FOUND, ON_PAGE_SCROLL, ON_PULL_DOWN_REFRESH, ON_REACH_BOTTOM, ON_REACH_BOTTOM_DISTANCE, ON_READY, ON_RESIZE, ON_SHARE_APP_MESSAGE, ON_SHARE_TIMELINE, ON_SHOW, ON_TAB_ITEM_TAP, ON_THEME_CHANGE, ON_UNHANDLE_REJECTION, ON_UNLOAD, ON_WEB_INVOKE_APP_SERVICE, ON_WXS_INVOKE_CALL_METHOD, PLUS_RE, PRIMARY_COLOR, RENDERJS_MODULES, RESPONSIVE_MIN_WIDTH, SCHEME_RE, SELECTED_COLOR, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, UNI_SSR_TITLE, UniBaseNode, UniCommentNode, UniElement, UniEvent, UniInputElement, UniNode, UniTextAreaElement, UniTextNode, WEB_INVOKE_APPSERVICE, WXS_MODULES, WXS_PROTOCOL, addFont, cache, cacheStringFunction, callOptions, createRpx2Unit, createUniEvent, debounce, decode, decodedQuery, defaultRpx2Unit, formatAppLog, formatDateTime, formatLog, getCustomDataset, getEnvLocale, getLen, getValueByDataPath, initCustomDataset, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, isRootHook, isServiceCustomElement, isServiceNativeTag, normalizeDataset, normalizeEventType, normalizeTarget, once, parseEventName, parseQuery, parseUrl, passive, plusReady, removeLeadingSlash, resolveOwnerEl, resolveOwnerVm, sanitise, scrollTo, stringifyQuery, updateElementStyle }; diff --git a/packages/uni-cli-shared/src/hbx/formatLog.ts b/packages/uni-shared/src/hbx/formatLog.ts similarity index 98% rename from packages/uni-cli-shared/src/hbx/formatLog.ts rename to packages/uni-shared/src/hbx/formatLog.ts index 9ceb62c33..980e6bcb0 100644 --- a/packages/uni-cli-shared/src/hbx/formatLog.ts +++ b/packages/uni-shared/src/hbx/formatLog.ts @@ -54,7 +54,7 @@ export function normalizeLog( return msgs.join('---COMMA---') + ' ' + filename } -export function formatLog( +export function formatAppLog( type: 'log' | 'info' | 'debug' | 'warn' | 'error', filename: string, ...args: unknown[] diff --git a/packages/uni-shared/src/hbx/index.ts b/packages/uni-shared/src/hbx/index.ts new file mode 100644 index 000000000..a37c2fece --- /dev/null +++ b/packages/uni-shared/src/hbx/index.ts @@ -0,0 +1 @@ +export { formatAppLog } from './formatLog' diff --git a/packages/uni-shared/src/index.ts b/packages/uni-shared/src/index.ts index d21a767ce..e9b0ec0c3 100644 --- a/packages/uni-shared/src/index.ts +++ b/packages/uni-shared/src/index.ts @@ -2,6 +2,7 @@ export * from './vue' export * from './log' export * from './dom' export * from './url' +export * from './hbx' export * from './plus' export * from './tags' export * from './vdom' diff --git a/packages/uni-stat/dist/uni-stat.cjs.js b/packages/uni-stat/dist/uni-stat.cjs.js index 24d3c7c16..b2db3147c 100644 --- a/packages/uni-stat/dist/uni-stat.cjs.js +++ b/packages/uni-stat/dist/uni-stat.cjs.js @@ -240,41 +240,29 @@ const getRoute = () => { var pages = getCurrentPages(); var page = pages[pages.length - 1]; if (!page) return '' + // TODO 需要确认如果不用 $vm ,其他平台会不会出错 let _self = page.$vm; if (getPlatformName() === 'bd') { return _self.$mp && _self.$mp.page.is } else { - return ( - (_self.$scope && _self.$scope.route) || - (_self.$mp && _self.$mp.page.route) - ) + return _self.route || (_self.$mp && _self.$mp.page.route) } }; const getPageRoute = (self) => { - var pages = getCurrentPages(); - var page = pages[pages.length - 1]; - if (!page) return '' - let _self = page.$vm; + let route = getRoute(); let query = self._query; let str = query && JSON.stringify(query) !== '{}' ? '?' + JSON.stringify(query) : ''; // clear self._query = ''; - if (getPlatformName() === 'bd') { - return _self.$mp && _self.$mp.page.is + str - } else { - return ( - (_self.$scope && _self.$scope.route + str) || - (_self.$mp && _self.$mp.page.route + str) - ) - } + return route + str }; const getPageTypes = (self) => { if ( - self.mpType === 'page' || + self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self.$options.mpType === 'page' ) { @@ -401,7 +389,7 @@ const requestData = (done) => { }); }; -const titleJsons = process.env.UNI_STAT_PAGES_TITLE; +const titleJsons = process.env.UNI_STAT_TITLE_JSON; const statConfig = { appid: process.env.UNI_APP_ID, }; @@ -505,11 +493,11 @@ class Util { } getLastTime(); - this._lastPageRoute = route; const time = getResidenceTime('page'); + // 停留时间 if (time.overtime) { let options = { - path: this._lastPageRoute, + path: route, scene: this.statData.sc, }; this._sendReportRequest(options); @@ -521,11 +509,13 @@ class Util { if (!this.__licationHide) { getLastTime(); const time = getResidenceTime('page'); + const route = getPageRoute(this); this._sendPageRequest({ - url: this._lastPageRoute, + url: route, urlref: this._lastPageRoute, urlref_ts: time.residenceTime, }); + this._lastPageRoute = route; this._navigationBarTitle = { config: '', page: '', @@ -665,7 +655,6 @@ class Util { data.ttn = title.page; data.ttpj = title.config; data.ttc = title.report; - let requestData = this._reportingRequestData; if (getPlatformName() === 'n') { requestData = uni.getStorageSync('__UNI__STAT__DATA') || {}; @@ -735,9 +724,6 @@ class Util { uni.request({ url: STAT_URL, method: 'POST', - // header: { - // 'content-type': 'application/json' // 默认值 - // }, data: optionsData, success: () => { // if (process.env.NODE_ENV === 'development') { @@ -853,19 +839,17 @@ class Stat extends Util { report(options, self) { this.self = self; - // if (process.env.NODE_ENV === 'development') { - // console.log('report init'); - // } setPageResidenceTime(); this.__licationShow = true; this._sendReportRequest(options, true); } load(options, self) { - if (!self.$scope && !self.$mp) { - const page = getCurrentPages(); - self.$scope = page[page.length - 1]; - } + // if (!self.$scope && !self.$mp) { + // const page = getCurrentPages() + // console.log(); + // self.$scope = page[page.length - 1] + // } this.self = self; this._query = options; } @@ -929,12 +913,15 @@ const stat = Stat$1.getInstance(); let isHide = false; const lifecycle = { onLaunch(options) { + console.log('report onLaunch init'); stat.report(options, this); }, onReady() { + console.log('report onReady init'); stat.ready(this); }, onLoad(options) { + console.log('report onLoad init'); stat.load(options, this); // 重写分享,获取分享上报事件 if (this.$scope && this.$scope.onShareAppMessage) { @@ -946,14 +933,17 @@ const lifecycle = { } }, onShow() { + console.log('report onShow init'); isHide = false; stat.show(this); }, onHide() { + console.log('report onHide init'); isHide = true; stat.hide(this); }, onUnload() { + console.log('report onUnload init'); if (isHide) { isHide = false; return @@ -961,20 +951,28 @@ const lifecycle = { stat.hide(this); }, onError(e) { + console.log('report onError init'); stat.error(e); }, }; function main() { - if (process.env.NODE_ENV === 'development') { - uni.report = function (type, options) {}; - } else { - const Vue = require('vue') - ;(Vue.default || Vue).mixin(lifecycle); + console.log('stat onload ----'); + setTimeout(() => { + getApp().$.appContext.app.mixin(lifecycle); uni.report = function (type, options) { stat.sendEvent(type, options); }; - } + }, 1); + // if (process.env.NODE_ENV === 'development') { + // uni.report = function (type, options) {} + // } else { + // const Vue = require('vue') + // ;(Vue.default || Vue).mixin(lifecycle) + // uni.report = function (type, options) { + // stat.sendEvent(type, options) + // } + // } } main(); diff --git a/packages/uni-stat/dist/uni-stat.es.js b/packages/uni-stat/dist/uni-stat.es.js index 408ae3343..e37d1ab86 100644 --- a/packages/uni-stat/dist/uni-stat.es.js +++ b/packages/uni-stat/dist/uni-stat.es.js @@ -238,41 +238,29 @@ const getRoute = () => { var pages = getCurrentPages(); var page = pages[pages.length - 1]; if (!page) return '' + // TODO 需要确认如果不用 $vm ,其他平台会不会出错 let _self = page.$vm; if (getPlatformName() === 'bd') { return _self.$mp && _self.$mp.page.is } else { - return ( - (_self.$scope && _self.$scope.route) || - (_self.$mp && _self.$mp.page.route) - ) + return _self.route || (_self.$mp && _self.$mp.page.route) } }; const getPageRoute = (self) => { - var pages = getCurrentPages(); - var page = pages[pages.length - 1]; - if (!page) return '' - let _self = page.$vm; + let route = getRoute(); let query = self._query; let str = query && JSON.stringify(query) !== '{}' ? '?' + JSON.stringify(query) : ''; // clear self._query = ''; - if (getPlatformName() === 'bd') { - return _self.$mp && _self.$mp.page.is + str - } else { - return ( - (_self.$scope && _self.$scope.route + str) || - (_self.$mp && _self.$mp.page.route + str) - ) - } + return route + str }; const getPageTypes = (self) => { if ( - self.mpType === 'page' || + self.$mpType === 'page' || (self.$mp && self.$mp.mpType === 'page') || self.$options.mpType === 'page' ) { @@ -399,7 +387,7 @@ const requestData = (done) => { }); }; -const titleJsons = process.env.UNI_STAT_PAGES_TITLE; +const titleJsons = process.env.UNI_STAT_TITLE_JSON; const statConfig = { appid: process.env.UNI_APP_ID, }; @@ -503,11 +491,11 @@ class Util { } getLastTime(); - this._lastPageRoute = route; const time = getResidenceTime('page'); + // 停留时间 if (time.overtime) { let options = { - path: this._lastPageRoute, + path: route, scene: this.statData.sc, }; this._sendReportRequest(options); @@ -519,11 +507,13 @@ class Util { if (!this.__licationHide) { getLastTime(); const time = getResidenceTime('page'); + const route = getPageRoute(this); this._sendPageRequest({ - url: this._lastPageRoute, + url: route, urlref: this._lastPageRoute, urlref_ts: time.residenceTime, }); + this._lastPageRoute = route; this._navigationBarTitle = { config: '', page: '', @@ -663,7 +653,6 @@ class Util { data.ttn = title.page; data.ttpj = title.config; data.ttc = title.report; - let requestData = this._reportingRequestData; if (getPlatformName() === 'n') { requestData = uni.getStorageSync('__UNI__STAT__DATA') || {}; @@ -733,9 +722,6 @@ class Util { uni.request({ url: STAT_URL, method: 'POST', - // header: { - // 'content-type': 'application/json' // 默认值 - // }, data: optionsData, success: () => { // if (process.env.NODE_ENV === 'development') { @@ -851,19 +837,17 @@ class Stat extends Util { report(options, self) { this.self = self; - // if (process.env.NODE_ENV === 'development') { - // console.log('report init'); - // } setPageResidenceTime(); this.__licationShow = true; this._sendReportRequest(options, true); } load(options, self) { - if (!self.$scope && !self.$mp) { - const page = getCurrentPages(); - self.$scope = page[page.length - 1]; - } + // if (!self.$scope && !self.$mp) { + // const page = getCurrentPages() + // console.log(); + // self.$scope = page[page.length - 1] + // } this.self = self; this._query = options; } @@ -927,12 +911,15 @@ const stat = Stat$1.getInstance(); let isHide = false; const lifecycle = { onLaunch(options) { + console.log('report onLaunch init'); stat.report(options, this); }, onReady() { + console.log('report onReady init'); stat.ready(this); }, onLoad(options) { + console.log('report onLoad init'); stat.load(options, this); // 重写分享,获取分享上报事件 if (this.$scope && this.$scope.onShareAppMessage) { @@ -944,14 +931,17 @@ const lifecycle = { } }, onShow() { + console.log('report onShow init'); isHide = false; stat.show(this); }, onHide() { + console.log('report onHide init'); isHide = true; stat.hide(this); }, onUnload() { + console.log('report onUnload init'); if (isHide) { isHide = false; return @@ -959,20 +949,28 @@ const lifecycle = { stat.hide(this); }, onError(e) { + console.log('report onError init'); stat.error(e); }, }; function main() { - if (process.env.NODE_ENV === 'development') { - uni.report = function (type, options) {}; - } else { - const Vue = require('vue') - ;(Vue.default || Vue).mixin(lifecycle); + console.log('stat onload ----'); + setTimeout(() => { + getApp().$.appContext.app.mixin(lifecycle); uni.report = function (type, options) { stat.sendEvent(type, options); }; - } + }, 1); + // if (process.env.NODE_ENV === 'development') { + // uni.report = function (type, options) {} + // } else { + // const Vue = require('vue') + // ;(Vue.default || Vue).mixin(lifecycle) + // uni.report = function (type, options) { + // stat.sendEvent(type, options) + // } + // } } main(); diff --git a/packages/vite-plugin-uni/src/cli/index.ts b/packages/vite-plugin-uni/src/cli/index.ts index 11b9dc368..15e537f95 100644 --- a/packages/vite-plugin-uni/src/cli/index.ts +++ b/packages/vite-plugin-uni/src/cli/index.ts @@ -21,6 +21,8 @@ export interface CliOptions { logLevel?: LogLevel l?: LogLevel clearScreen?: boolean + autoHost?: string + autoPort?: number } cli @@ -34,6 +36,8 @@ cli .option('--clearScreen', `[boolean] allow/disable clear screen when logging`) .option('-d, --debug [feat]', `[string | boolean] show debug logs`) .option('-f, --filter ', `[string] filter debug logs`) + .option('--autoHost', `[string] specify automator hostname`) + .option('--autoPort', `[number] specify automator port`) cli .command('') diff --git a/packages/vite-plugin-uni/src/cli/utils.ts b/packages/vite-plugin-uni/src/cli/utils.ts index da21b09dc..7426be25a 100644 --- a/packages/vite-plugin-uni/src/cli/utils.ts +++ b/packages/vite-plugin-uni/src/cli/utils.ts @@ -1,4 +1,5 @@ import fs from 'fs' +import os from 'os' import path from 'path' import { BuildOptions, InlineConfig } from 'vite' @@ -74,15 +75,9 @@ export function initEnv(type: 'dev' | 'build', options: CliOptions) { } process.env.UNI_OUTPUT_DIR = (options as BuildOptions).outDir! } - // tips - // if (isInHBuilderX() && options.platform === 'app') { - // return ( - // console.error( - // `当前项目 Vue 版本为3,暂不支持编译至 App 端,近期将升级支持。` - // ), - // process.exit(1) - // ) - // } + + initAutomator(options) + if (process.env.NODE_ENV === 'production') { if (!(options as BuildOptions).minify) { ;(options as BuildOptions).minify = 'terser' @@ -113,6 +108,31 @@ export function initEnv(type: 'dev' | 'build', options: CliOptions) { console.log(M['compiling']) } +function initAutomator({ autoHost, autoPort }: CliOptions) { + if (!autoPort) { + return + } + process.env.UNI_AUTOMATOR_WS_ENDPOINT = + 'ws://' + (autoHost || resolveHostname()) + ':' + autoPort +} + +function resolveHostname() { + const interfaces = os.networkInterfaces() + const keys = Object.keys(interfaces) + for (const key of keys) { + const interfaceInfos = interfaces[key] + if (!interfaceInfos) { + continue + } + for (const info of interfaceInfos) { + if (info.family === 'IPv4' && !info.address.includes('127.0.0.1')) { + return info.address + } + } + } + return 'localhost' +} + export function cleanOptions(options: CliOptions) { const ret = { ...options } delete ret['--'] @@ -128,5 +148,9 @@ export function cleanOptions(options: CliOptions) { delete ret.logLevel delete ret.l delete ret.clearScreen + + delete ret.autoHost + delete ret.autoPort + return ret } diff --git a/packages/vite-plugin-uni/src/config/optimizeDeps.ts b/packages/vite-plugin-uni/src/config/optimizeDeps.ts index cf91672b0..debddb002 100644 --- a/packages/vite-plugin-uni/src/config/optimizeDeps.ts +++ b/packages/vite-plugin-uni/src/config/optimizeDeps.ts @@ -8,6 +8,7 @@ export function createOptimizeDeps( exclude: [ 'vue', 'vuex', + 'vue-i18n', 'vue-router', '@dcloudio/uni-app', '@dcloudio/uni-components', diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/resolveId.ts b/packages/vite-plugin-uni/src/configResolved/plugins/resolveId.ts index 196ef6bfc..da1fea636 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/resolveId.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/resolveId.ts @@ -12,6 +12,7 @@ const debugResolve = debug('vite:uni:resolve') const BUILT_IN_MODULES = { 'vue-router': 'dist/vue-router.esm-bundler.js', vuex: 'dist/vuex.esm-bundler.js', + 'vue-i18n': 'dist/vue-i18n.esm-bundler.js', '@dcloudio/uni-app': 'dist/uni-app.es.js', '@dcloudio/uni-stat': 'dist/uni-stat.es.js', '@dcloudio/uni-cloud': 'dist/uni-cloud.es.js', diff --git a/scripts/build.js b/scripts/build.js index eb9aaf7c6..7d52f8c7b 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -57,7 +57,7 @@ async function build(target) { const types = target.endsWith('-shared') || pkg.types // if building a specific format, do not remove dist. // if (!formats && bundler !== 'vite') { - if (target !== 'uni-cloud') { + if (!['uni-cloud', 'uni-automator'].includes(target)) { await fs.remove(`${pkgDir}/dist`) } // } diff --git a/yarn.lock b/yarn.lock index d27ac9e50..19fa6db37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1848,16 +1848,16 @@ "@rushstack/node-core-library" "3.40.0" "@microsoft/api-extractor@^7.13.2": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.18.5.tgz#cc2804d7c8b9d0f1e63fd85d0448569b767db102" - integrity sha512-NUGS6WxexziEnroHUOI3KKVmMX02god7SLA8Y4a5GKCL5k7AHuHFqP2bpd5Otx2odfbdj15ObO7FU/XA3Oxh8w== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.18.6.tgz#4a0a05df51f91ed18a9cea23c7cca4063dce65e9" + integrity sha512-632kwS+YEozh7Xl4dz/VxHmqD31ugGTEXUr2if1+8klWiUt8qw6RmsIux5oGAEuu2L+Dzl9Y+/P02ZG3GyzjTA== dependencies: "@microsoft/api-extractor-model" "7.13.5" "@microsoft/tsdoc" "0.13.2" "@microsoft/tsdoc-config" "~0.15.2" "@rushstack/node-core-library" "3.40.0" "@rushstack/rig-package" "0.2.13" - "@rushstack/ts-command-line" "4.8.1" + "@rushstack/ts-command-line" "4.9.0" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" @@ -2168,10 +2168,10 @@ resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.8.1.tgz#c233a0226112338e58e7e4fd219247b4e7cec883" - integrity sha512-rmxvYdCNRbyRs+DYAPye3g6lkCkWHleqO40K8UPvUAzFqEuj6+YCVssBiOmrUDCoM5gaegSNT0wFDYhz24DWtw== +"@rushstack/ts-command-line@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rushstack/ts-command-line/-/ts-command-line-4.9.0.tgz#781ba42cff73cae097b6d5241b6441e7cc2fe6e0" + integrity sha512-kmT8t+JfnvphISF1C5WwY56RefjwgajhSjs9J4ckvAFXZDXR6F5cvF5/RTh7fGCzIomg8esy2PHO/b52zFoZvA== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -2427,9 +2427,9 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*": - version "16.6.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50" - integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA== + version "16.7.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.7.1.tgz#c6b9198178da504dfca1fd0be9b2e1002f1586f0" + integrity sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A== "@types/node@10.17.13": version "10.17.13" @@ -2437,9 +2437,9 @@ integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== "@types/node@^14.14.20": - version "14.17.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.10.tgz#93f4b095af275a0427114579c10ec7aa696729d7" - integrity sha512-09x2d6kNBwjHgyh3jOUE2GE4DFoxDriDvWdu6mFhMP1ysynGYazt4ecZmJlL6/fe4Zi2vtYvTvtL7epjQQrBhA== + version "14.17.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.11.tgz#82d266d657aec5ff01ca59f2ffaff1bb43f7bf0f" + integrity sha512-n2OQ+0Bz6WEsUjrvcHD1xZ8K+Kgo4cn9/w94s1bJS690QMUWfJPW/m7CCb7gPkA1fcYwL2UpjXP/rq/Eo41m6w== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2962,6 +2962,11 @@ add-stream@^1.0.0: resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" integrity sha1-anmQQ3ynNtXhKI25K9MmbV9csqo= +address@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" + integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== + agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -3168,13 +3173,13 @@ atob@^2.1.2: integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== autoprefixer@^10.2.5: - version "10.3.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.1.tgz#954214821d3aa06692406c6a0a9e9d401eafbed2" - integrity sha512-L8AmtKzdiRyYg7BUXJTzigmhbQRCXFKz6SA1Lqo0+AR2FBbQ4aTAPFSDlOutnFkjhiz8my4agGXog1xlMjPJ6A== + version "10.3.2" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.3.2.tgz#836e4b4f59eb6876c41012c1c937be74035f3ec8" + integrity sha512-RHKq0YCvhxAn9987n0Gl6lkzLd39UKwCkUPMFE0cHhxU0SvcTjBxWG/CtkZ4/HvbqK9U5V8j03nAcGBlX3er/Q== dependencies: - browserslist "^4.16.6" - caniuse-lite "^1.0.30001243" - colorette "^1.2.2" + browserslist "^4.16.8" + caniuse-lite "^1.0.30001251" + colorette "^1.3.0" fraction.js "^4.1.1" normalize-range "^0.1.2" postcss-value-parser "^4.1.0" @@ -3444,7 +3449,7 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.16.7: +browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.16.7, browserslist@^4.16.8: version "4.16.8" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.8.tgz#cb868b0b554f137ba6e33de0ecff2eda403c4fb0" integrity sha512-sc2m9ohR/49sWEbPj14ZSSZqp+kbi16aLao42Hmn3Z8FpjuMaq2xCA2l4zl9ITfyzvnvyE0hcg62YkIGKxgaNQ== @@ -3569,7 +3574,7 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001243, caniuse-lite@^1.0.30001251: +caniuse-lite@^1.0.30001251: version "1.0.30001251" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz#6853a606ec50893115db660f82c094d18f096d85" integrity sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A== @@ -4011,9 +4016,9 @@ core-util-is@1.0.2, core-util-is@~1.0.0: integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -4052,6 +4057,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -4061,7 +4073,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4167,7 +4179,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -4217,6 +4229,13 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -4367,9 +4386,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.811: - version "1.3.813" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.813.tgz#751a007d71c00faed8b5e9edaf3634c14b9c5a1f" - integrity sha512-YcSRImHt6JZZ2sSuQ4Bzajtk98igQ0iKkksqlzZLzbh4p0OIyJRSvUbsgqfcR8txdfsoYCc4ym306t4p2kP/aw== + version "1.3.814" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.814.tgz#418fad80c3276a46103ca72a21a8290620d83c4a" + integrity sha512-0mH03cyjh6OzMlmjauGg0TLd87ErIJqWiYxMcOLKf5w6p0YEOl7DJAj7BDlXEFmCguY5CQaKVOiMjAMODO2XDw== elliptic@^6.5.3: version "6.5.4" @@ -4500,9 +4519,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" esbuild@^0.12.17: - version "0.12.21" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.21.tgz#7ff32a9ac73ce4310f9cb61ea4c3da9756570d46" - integrity sha512-7hyXbU3g94aREufI/5nls7Xcc+RGQeZWZApm6hoBaFvt2BPtpT4TjFMQ9Tb1jU8XyBGz00ShmiyflCogphMHFQ== + version "0.12.22" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.22.tgz#6031a1257b8d0307d306bed673b79c3668607f51" + integrity sha512-yWCr9RoFehpqoe/+MwZXJpYOEIt7KOEvNnjIeMZpMSyQt+KCBASM3y7yViiN5dJRphf1wGdUz1+M4rTtWd/ulA== escalade@^3.1.1: version "3.1.1" @@ -6749,6 +6768,11 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +licia@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/licia/-/licia-1.29.0.tgz#2651d620ac44b8626cd0b027f0fb2e221b5bd264" + integrity sha512-YM+LbyX596y9jtj2ewBndq3PNFOG+Ul006B3Mc19Fq9OduZ/xWUbwrBkb521J6XKHlt4jJcb49E6f2AkA4vc9w== + lilconfig@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" @@ -8134,7 +8158,7 @@ postcss-selector-parser@^5.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.6: version "6.0.6" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== @@ -8325,6 +8349,16 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qrcode-reader@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/qrcode-reader/-/qrcode-reader-1.0.4.tgz#95d9bb9e8130800361a96cb5a43124ad1d9e06b8" + integrity sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ== + +qrcode-terminal@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" + integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== + qs@6.7.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" @@ -8820,9 +8854,9 @@ rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.8.2: estree-walker "^0.6.1" rollup@^2.35.1, rollup@^2.38.5: - version "2.56.2" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.56.2.tgz#a045ff3f6af53ee009b5f5016ca3da0329e5470f" - integrity sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ== + version "2.56.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.56.3.tgz#b63edadd9851b0d618a6d0e6af8201955a77aeff" + integrity sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg== optionalDependencies: fsevents "~2.3.2" @@ -10260,6 +10294,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== +ws@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.0.tgz#0b738cd484bfc9303421914b11bb4011e07615bb" + integrity sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" -- GitLab