├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dist └── index.wxs ├── example ├── .gitignore ├── app.js ├── app.json ├── app.wxss ├── pages │ ├── dynamic │ │ ├── index.js │ │ ├── index.json │ │ └── index.wxml │ ├── index │ │ ├── index.js │ │ ├── index.json │ │ └── index.wxml │ ├── multiple │ │ ├── comp1 │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ └── index.wxml │ │ ├── comp2 │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ └── index.wxml │ │ ├── index.js │ │ ├── index.json │ │ └── index.wxml │ └── vertical │ │ ├── index.js │ │ ├── index.json │ │ └── index.wxml ├── project.config.json ├── sitemap.json └── weapp-scroll.wxs ├── package-lock.json ├── package.json ├── preview.gif ├── rollup.config.js ├── src ├── Easing.ts ├── Utils.ts ├── core.ts ├── index.ts ├── options.ts └── wxs.d.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.wxs linguist-language=js -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Xieyuhang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Weapp Scroll 2 | Weapp Scroll 是基于 [WXS](https://developers.weixin.qq.com/miniprogram/dev/reference/wxs/01wxs-module.html) 实现的平滑滚动容器,有一些计算的逻辑参考了 [better-scroll](https://github.com/ustbhuangyi/better-scroll)。 3 | 4 | 在微信小程序中,使用 WXS 相对于逻辑层来处理交互事件拥有较好的性能,相关官方文档:[WXS响应事件](https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html)。 5 | 6 | [微信小程序中基于WXS实现回弹的平滑滚动容器](https://juejin.cn/post/7119278260315242503) 7 | 8 | ## 效果预览 9 | 10 | 11 | ## 使用 12 | 1. 拷贝 `dist/index.wxs` 文件至项目中并重命名为 `weapp-scroll.wxs` 13 | 2. 在需要使用的页面或组件使用 `` 标签引入 `weapp-scroll.wxs` 14 | 15 | 下面是创建横向滚动的简单的示例: 16 | 17 | ```html 18 | 19 | 20 | 21 | 30 | 31 | {{ item }} 32 | 33 | 34 | ``` 35 | 36 | ## 示例 37 | 项目中 example 目录是一个小程序项目,里面包含了一些使用示例 38 | 39 | + `example/pages/index`,横向滚动 40 | + `example/pages/vertical`,竖向滚动 41 | + `example/pages/dynamic`,容器子元素动态变化 42 | + `example/pages/multiple`,同一页面使用多次 43 | 44 | ## 选项 45 | 46 | | 属性 | 默认值 | 描述 | 47 | | ------------------------ | ------------------------------------------------- | ------------------------------------------------------------ | 48 | | enableScrollX | 滑动容器的宽度大于容器的宽度时为 true,否则 false | 是否启用横向的滚动 | 49 | | enableScrollY | 滑动容器的高度大于容器的高度时为 true,否则 false | 是否启用竖向的滚动 | 50 | | slidingContainerSelector | .content | 滑动容器的选择器 | 51 | | damping | 0.3 | 滑动超出边界后,超出边界的阻尼系数,取值在 [0, 1],值约小,阻力越大 | 52 | | bounceDuration | 800 | 滑动超出边界后进行反弹,设置反弹动画的持续时间 | 53 | 54 | ## 事件 55 | ### onScroll 56 | 在滑动容器滑动时触发 57 | ```ts 58 | interface ScrollCallback { 59 | (data: { 60 | // 滑动容器的 translateX 61 | x: number 62 | // 滑动容器的 translateY 63 | y: number 64 | // 滑动容器在 X 方向的最大滑动距离,正值 65 | maxScrollDistanceX: number 66 | // 滑动容器在 Y 方向的最大滑动距离,正值 67 | maxScrollDistanceY: number 68 | }): void 69 | } 70 | 71 | function onScroll(callback: ScrollCallback): () => void 72 | ``` 73 | 74 | 示例 75 | ```js 76 | // 在页面中任意引入到的 *.wxs 文件 77 | var weappScroll = require('./weapp-scroll.wxs') 78 | 79 | var cancel = weappScroll.onScroll(function (data) { 80 | console.log(data.x) 81 | }) 82 | 83 | // 取消监听 84 | cancel() 85 | ``` 86 | 87 | ## License 88 | 89 | [MIT](https://github.com/haiya6/weapp-scroll/blob/main/LICENSE) 90 | -------------------------------------------------------------------------------- /dist/index.wxs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Easing = { 4 | v1: function (t) { 5 | return 1 + --t * t * t * t * t; 6 | }, 7 | v2: function (t) { 8 | return t * (2 - t); 9 | }, 10 | v3: function (t) { 11 | return 1 - --t * t * t * t; 12 | } 13 | }; 14 | 15 | var Utils = { 16 | isUndefined: function (val) { 17 | return typeof val === 'undefined'; 18 | }, 19 | clamp: function (min, max, val) { 20 | return Math.max(min, Math.min(max, val)); 21 | }, 22 | clonePos: function (pos) { 23 | return { 24 | x: pos.x, 25 | y: pos.y 26 | }; 27 | }, 28 | isSamePos: function (p1, p2) { 29 | return p1.x === p2.x && p1.y === p2.y; 30 | } 31 | }; 32 | 33 | var options = { 34 | enableScrollX: false, 35 | enableScrollY: false, 36 | slidingContainerSelector: '.content', 37 | damping: 0.3, 38 | deceleration: 0.0015, 39 | momentumDuration: 1800, 40 | momentumOutBoundsDuration: 400, 41 | bounceDuration: 800 42 | }; 43 | function setOption(userOptions, key) { 44 | if (!Utils.isUndefined(userOptions[key])) { 45 | options[key] = userOptions[key]; 46 | } 47 | } 48 | function resolveOptions(userOptions) { 49 | setOption(userOptions, 'slidingContainerSelector'); 50 | setOption(userOptions, 'damping'); 51 | setOption(userOptions, 'bounceDuration'); 52 | } 53 | 54 | var ownerInstance; 55 | var containerRect; 56 | var slidingContainerInstance; 57 | var slidingContainerRect; 58 | var canScrollX = false; 59 | var canScrollY = false; 60 | var minTranslateX; 61 | var maxTranslateX = 0; 62 | var minTranslateY; 63 | var maxTranslateY = 0; 64 | var pos = { x: 0, y: 0 }; 65 | var startTouch = { clientX: 0, clientY: 0 }; 66 | var startPos = { x: 0, y: 0 }; 67 | var startTimeStamp = 0; 68 | var effect = null; 69 | var onScrollCallbacks = []; 70 | function setTranslate(pos0) { 71 | if (Utils.isSamePos(pos0, pos)) 72 | return; 73 | slidingContainerInstance.setStyle({ 74 | transform: 'translateX(' + pos0.x + 'px) translateY(' + pos0.y + 'px)' 75 | }); 76 | pos.x = pos0.x; 77 | pos.y = pos0.y; 78 | onScrollCallbacks.forEach(function (cb) { return cb({ 79 | x: pos.x, 80 | y: pos.y, 81 | maxScrollDistanceX: minTranslateX * -1, 82 | maxScrollDistanceY: minTranslateY * -1 83 | }); }); 84 | } 85 | function moveFromTo(fromPos, toPos, duration, timing, onComplete) { 86 | var aborted = false; 87 | var completed = false; 88 | fromPos = Utils.clonePos(fromPos); 89 | toPos = Utils.clonePos(toPos); 90 | if (duration === 0) { 91 | setTranslate(toPos); 92 | ownerInstance.requestAnimationFrame(function () { 93 | completed = true; 94 | onComplete && onComplete(); 95 | }); 96 | } 97 | else { 98 | var startTime = Date.now(); 99 | var disX = toPos.x - fromPos.x; 100 | var disY = toPos.y - fromPos.y; 101 | var progressX = 1; 102 | var progressY = 1; 103 | var rAFHandler = function rAFHandler() { 104 | if (aborted) 105 | return; 106 | var curPos = Utils.clonePos(fromPos); 107 | if (canScrollX) { 108 | progressX = timing.x(Utils.clamp(0, 1, (Date.now() - startTime) / duration)); 109 | curPos.x = disX * progressX + fromPos.x; 110 | } 111 | if (canScrollY) { 112 | progressY = timing.y(Utils.clamp(0, 1, (Date.now() - startTime) / duration)); 113 | curPos.y = disY * progressY + fromPos.y; 114 | } 115 | setTranslate(curPos); 116 | if (progressX < 1 || progressY < 1) { 117 | ownerInstance.requestAnimationFrame(rAFHandler); 118 | } 119 | else { 120 | completed = true; 121 | onComplete && onComplete(); 122 | } 123 | }; 124 | ownerInstance.requestAnimationFrame(rAFHandler); 125 | } 126 | if (effect) 127 | effect(); 128 | effect = function abort() { 129 | if (!completed && !aborted) { 130 | aborted = true; 131 | } 132 | }; 133 | } 134 | function positionCorrection(pos) { 135 | var correctedPos = { 136 | x: Utils.clamp(minTranslateX, maxTranslateX, pos.x), 137 | y: Utils.clamp(minTranslateY, maxTranslateY, pos.y) 138 | }; 139 | if (!Utils.isSamePos(correctedPos, pos)) { 140 | moveFromTo(pos, correctedPos, options.bounceDuration, { x: Easing.v3, y: Easing.v3 }); 141 | } 142 | } 143 | function setup(_options, _, _ownerInstance, instance) { 144 | resolveOptions(_options); 145 | ownerInstance = _ownerInstance; 146 | containerRect = instance.getBoundingClientRect(); 147 | slidingContainerInstance = ownerInstance.selectComponent(options.slidingContainerSelector); 148 | slidingContainerRect = slidingContainerInstance.getBoundingClientRect(); 149 | if (slidingContainerRect.width > containerRect.width) { 150 | canScrollX = Utils.isUndefined(_options.enableScrollX) ? true : _options.enableScrollX; 151 | minTranslateX = (slidingContainerRect.width - containerRect.width) * -1; 152 | } 153 | if (slidingContainerRect.height > containerRect.height) { 154 | canScrollY = Utils.isUndefined(_options.enableScrollY) ? true : _options.enableScrollY; 155 | minTranslateY = (slidingContainerRect.height - containerRect.height) * -1; 156 | } 157 | } 158 | function touchstart(event) { 159 | startTouch.clientX = event.changedTouches[0].clientX; 160 | startTouch.clientY = event.changedTouches[0].clientY; 161 | startPos.x = pos.x; 162 | startPos.y = pos.y; 163 | startTimeStamp = event.timeStamp; 164 | if (effect) { 165 | effect(); 166 | effect = null; 167 | } 168 | } 169 | function touchmove(event) { 170 | var deltaX = event.changedTouches[0].clientX - startTouch.clientX; 171 | var deltaY = event.changedTouches[0].clientY - startTouch.clientY; 172 | var x = startPos.x; 173 | var y = startPos.y; 174 | if (canScrollX) { 175 | x += deltaX; 176 | if (x > maxTranslateX) { 177 | x = maxTranslateX + options.damping * (x - maxTranslateX); 178 | } 179 | else if (x < minTranslateX) { 180 | x = minTranslateX + options.damping * (x - minTranslateX); 181 | } 182 | } 183 | if (canScrollY) { 184 | y += deltaY; 185 | if (y > maxTranslateY) { 186 | y = maxTranslateY + options.damping * (y - maxTranslateY); 187 | } 188 | else if (y < minTranslateY) { 189 | y = minTranslateY + options.damping * (y - minTranslateY); 190 | } 191 | } 192 | setTranslate({ x: x, y: y }); 193 | } 194 | function touchend(event) { 195 | var minMovingDistance = 15; 196 | var maxMovingDuration = 300; 197 | var finalPos = { x: pos.x, y: pos.y }; 198 | if (Utils.isSamePos(finalPos, startPos)) 199 | return; 200 | var eventDuration = event.timeStamp - startTimeStamp; 201 | if (eventDuration > maxMovingDuration) { 202 | positionCorrection(finalPos); 203 | return; 204 | } 205 | var distanceX = Math.abs(finalPos.x - startPos.x); 206 | var distanceY = Math.abs(finalPos.y - startPos.y); 207 | var durationX = 0; 208 | var durationY = 0; 209 | var timingX = Easing.v1; 210 | var timingY = Easing.v1; 211 | var calculateMomentum = function calculateMomentum(start, end, distance) { 212 | var speed = distance / eventDuration; 213 | var dir = end - start > 0 ? 1 : -1; 214 | var duration = Math.min(options.momentumDuration, (speed * 2) / options.deceleration); 215 | var delta = Math.pow(speed, 2) / options.deceleration * dir; 216 | return { 217 | duration: duration, 218 | delta: delta 219 | }; 220 | }; 221 | if (canScrollX 222 | && distanceX > minMovingDistance 223 | && finalPos.x <= maxTranslateX 224 | && finalPos.x >= minTranslateX) { 225 | var result = calculateMomentum(startPos.x, pos.x, distanceX); 226 | durationX = result.duration; 227 | finalPos.x += result.delta; 228 | if (finalPos.x > maxTranslateX || finalPos.x < minTranslateX) { 229 | durationX = options.momentumOutBoundsDuration; 230 | timingX = Easing.v2; 231 | var beyondDis = containerRect.width / 6; 232 | if (finalPos.x > maxTranslateX) { 233 | finalPos.x = maxTranslateX + beyondDis; 234 | } 235 | else { 236 | finalPos.x = minTranslateX + beyondDis * -1; 237 | } 238 | } 239 | } 240 | if (canScrollY 241 | && distanceY > minMovingDistance 242 | && finalPos.y >= minTranslateY 243 | && finalPos.y <= maxTranslateY) { 244 | var result = calculateMomentum(startPos.y, pos.y, distanceY); 245 | durationY = result.duration; 246 | finalPos.y += result.delta; 247 | if (finalPos.y > maxTranslateY || finalPos.y < minTranslateY) { 248 | durationY = options.momentumOutBoundsDuration; 249 | timingY = Easing.v2; 250 | var beyondDis = containerRect.height / 6; 251 | if (finalPos.y > maxTranslateY) { 252 | finalPos.y = maxTranslateY + beyondDis; 253 | } 254 | else { 255 | finalPos.y = minTranslateY + beyondDis * -1; 256 | } 257 | } 258 | } 259 | moveFromTo(pos, finalPos, Math.max(durationX, durationY), { x: timingX, y: timingY }, function () { 260 | positionCorrection(finalPos); 261 | }); 262 | } 263 | function onScroll(callback) { 264 | onScrollCallbacks.push(callback); 265 | return function cancel() { 266 | var idx = onScrollCallbacks.indexOf(callback); 267 | if (idx !== 1) { 268 | onScrollCallbacks.splice(idx, 1); 269 | } 270 | }; 271 | } 272 | 273 | var index = { 274 | setup: setup, 275 | touchstart: touchstart, 276 | touchmove: touchmove, 277 | touchend: touchend, 278 | onScroll: onScroll 279 | }; 280 | 281 | module.exports = index; 282 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | project.private.config.json 3 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | App({}) 2 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/vertical/index", 5 | "pages/dynamic/index", 6 | "pages/multiple/index" 7 | ], 8 | "window": { 9 | "backgroundTextStyle": "light", 10 | "navigationBarBackgroundColor": "#fff", 11 | "navigationBarTitleText": "Weapp Scroll", 12 | "navigationBarTextStyle": "black" 13 | }, 14 | "sitemapLocation": "sitemap.json" 15 | } -------------------------------------------------------------------------------- /example/app.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | padding: 0 32rpx; 3 | } 4 | 5 | page, view { 6 | box-sizing: border-box; 7 | } 8 | 9 | .label { 10 | font-size: 32rpx; 11 | line-height: 32rpx; 12 | color: #000; 13 | margin: 30rpx 0; 14 | } 15 | 16 | .container { 17 | width: 100%; 18 | height: 300rpx; 19 | overflow: hidden; 20 | } 21 | 22 | .container.vertical { 23 | height: 100vh; 24 | } 25 | 26 | .container .content { 27 | display: inline-block; 28 | white-space: nowrap; 29 | } 30 | 31 | .container.vertical .content { 32 | display: block; 33 | } 34 | 35 | .container .content .item { 36 | display: inline-flex; 37 | justify-content: center; 38 | align-items: center; 39 | width: 200rpx; 40 | height: 300rpx; 41 | border: 1px solid; 42 | } 43 | 44 | .container.vertical .content .item { 45 | width: 100%; 46 | display: flex; 47 | } 48 | 49 | .button { 50 | margin: 30rpx 0; 51 | } -------------------------------------------------------------------------------- /example/pages/dynamic/index.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | count: 1, 4 | weappScrollOptions: { 5 | slidingContainerSelector: '.content' 6 | } 7 | }, 8 | onLoad() { 9 | setTimeout(() => { 10 | this.setData({ 11 | count: 10, 12 | // 结构改变后,需要重新触发 scroll.setup 来进行相关初始化 13 | // 这里目的就是使用 setData 重设置 weappScrollOptions,从而触发 change:_ 监听,重新执行 scroll.setup 方法 14 | // 可设置下面这行代码,或 'weappScrollOptions.t': Date.now() 15 | weappScrollOptions: this.data.weappScrollOptions 16 | }) 17 | }, 0); 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /example/pages/dynamic/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | }, 4 | "disableScroll": true 5 | } -------------------------------------------------------------------------------- /example/pages/dynamic/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 动态添加元素后需要手动触发更新 4 | 13 | 14 | {{ item }} 15 | 16 | 17 | -------------------------------------------------------------------------------- /example/pages/index/index.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | weappsScrollOptions: { 4 | slidingContainerSelector: '.content', 5 | } 6 | }, 7 | toVertical() { 8 | wx.navigateTo({ 9 | url: '/pages/vertical/index' 10 | }) 11 | }, 12 | toDynamic() { 13 | wx.navigateTo({ 14 | url: '/pages/dynamic/index' 15 | }) 16 | }, 17 | toMultiple() { 18 | wx.navigateTo({ 19 | url: '/pages/multiple/index' 20 | }) 21 | } 22 | }) 23 | -------------------------------------------------------------------------------- /example/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "disableScroll": true 3 | } -------------------------------------------------------------------------------- /example/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | var weappScroll = require('../../weapp-scroll.wxs') 5 | 6 | var cancel = weappScroll.onScroll(function(data) { 7 | console.log('x:', data.x, 'y:', data.y) 8 | }) 9 | 10 | // 取消监听 11 | // cancel() 12 | 13 | 14 | 横向滚动 15 | 24 | 25 | {{ item }} 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/pages/multiple/comp1/index.js: -------------------------------------------------------------------------------- 1 | Page({}) 2 | -------------------------------------------------------------------------------- /example/pages/multiple/comp1/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /example/pages/multiple/comp1/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | {{ item }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/pages/multiple/comp2/index.js: -------------------------------------------------------------------------------- 1 | Page({}) 2 | -------------------------------------------------------------------------------- /example/pages/multiple/comp2/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /example/pages/multiple/comp2/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | {{ item }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/pages/multiple/index.js: -------------------------------------------------------------------------------- 1 | Page({}) 2 | -------------------------------------------------------------------------------- /example/pages/multiple/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "comp1": "./comp1/index", 4 | "comp2": "./comp2/index" 5 | }, 6 | "disableScroll": true 7 | } -------------------------------------------------------------------------------- /example/pages/multiple/index.wxml: -------------------------------------------------------------------------------- 1 | 页面中多个滚动容器需要用组件隔离 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/pages/vertical/index.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | 3 | }) -------------------------------------------------------------------------------- /example/pages/vertical/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "disableScroll": true 3 | } -------------------------------------------------------------------------------- /example/pages/vertical/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | {{ item }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "appid": "", 3 | "compileType": "miniprogram", 4 | "libVersion": "2.25.0", 5 | "packOptions": { 6 | "ignore": [], 7 | "include": [] 8 | }, 9 | "setting": { 10 | "urlCheck": true, 11 | "coverView": true, 12 | "es6": true, 13 | "postcss": true, 14 | "lazyloadPlaceholderEnable": false, 15 | "preloadBackgroundData": false, 16 | "minified": true, 17 | "autoAudits": false, 18 | "uglifyFileName": false, 19 | "uploadWithSourceMap": true, 20 | "enhance": true, 21 | "showShadowRootInWxmlPanel": true, 22 | "packNpmManually": false, 23 | "packNpmRelationList": [], 24 | "minifyWXSS": true, 25 | "useStaticServer": true, 26 | "showES6CompileOption": false, 27 | "checkInvalidKey": true, 28 | "babelSetting": { 29 | "ignore": [], 30 | "disablePlugins": [], 31 | "outputPath": "" 32 | }, 33 | "disableUseStrict": false, 34 | "useCompilerPlugins": false, 35 | "minifyWXML": true 36 | }, 37 | "condition": {}, 38 | "editorSetting": { 39 | "tabIndent": "insertSpaces", 40 | "tabSize": 2 41 | }, 42 | "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html" 43 | } -------------------------------------------------------------------------------- /example/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /example/weapp-scroll.wxs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Easing = { 4 | v1: function (t) { 5 | return 1 + --t * t * t * t * t; 6 | }, 7 | v2: function (t) { 8 | return t * (2 - t); 9 | }, 10 | v3: function (t) { 11 | return 1 - --t * t * t * t; 12 | } 13 | }; 14 | 15 | var Utils = { 16 | isUndefined: function (val) { 17 | return typeof val === 'undefined'; 18 | }, 19 | clamp: function (min, max, val) { 20 | return Math.max(min, Math.min(max, val)); 21 | }, 22 | clonePos: function (pos) { 23 | return { 24 | x: pos.x, 25 | y: pos.y 26 | }; 27 | }, 28 | isSamePos: function (p1, p2) { 29 | return p1.x === p2.x && p1.y === p2.y; 30 | } 31 | }; 32 | 33 | var options = { 34 | enableScrollX: false, 35 | enableScrollY: false, 36 | slidingContainerSelector: '.content', 37 | damping: 0.3, 38 | deceleration: 0.0015, 39 | momentumDuration: 1800, 40 | momentumOutBoundsDuration: 400, 41 | bounceDuration: 800 42 | }; 43 | function setOption(userOptions, key) { 44 | if (!Utils.isUndefined(userOptions[key])) { 45 | options[key] = userOptions[key]; 46 | } 47 | } 48 | function resolveOptions(userOptions) { 49 | setOption(userOptions, 'slidingContainerSelector'); 50 | setOption(userOptions, 'damping'); 51 | setOption(userOptions, 'bounceDuration'); 52 | } 53 | 54 | var ownerInstance; 55 | var containerRect; 56 | var slidingContainerInstance; 57 | var slidingContainerRect; 58 | var canScrollX = false; 59 | var canScrollY = false; 60 | var minTranslateX; 61 | var maxTranslateX = 0; 62 | var minTranslateY; 63 | var maxTranslateY = 0; 64 | var pos = { x: 0, y: 0 }; 65 | var startTouch = { clientX: 0, clientY: 0 }; 66 | var startPos = { x: 0, y: 0 }; 67 | var startTimeStamp = 0; 68 | var effect = null; 69 | var onScrollCallbacks = []; 70 | function setTranslate(pos0) { 71 | if (Utils.isSamePos(pos0, pos)) 72 | return; 73 | slidingContainerInstance.setStyle({ 74 | transform: 'translateX(' + pos0.x + 'px) translateY(' + pos0.y + 'px)' 75 | }); 76 | pos.x = pos0.x; 77 | pos.y = pos0.y; 78 | onScrollCallbacks.forEach(function (cb) { return cb({ 79 | x: pos.x, 80 | y: pos.y, 81 | maxScrollDistanceX: minTranslateX * -1, 82 | maxScrollDistanceY: minTranslateY * -1 83 | }); }); 84 | } 85 | function moveFromTo(fromPos, toPos, duration, timing, onComplete) { 86 | var aborted = false; 87 | var completed = false; 88 | fromPos = Utils.clonePos(fromPos); 89 | toPos = Utils.clonePos(toPos); 90 | if (duration === 0) { 91 | setTranslate(toPos); 92 | ownerInstance.requestAnimationFrame(function () { 93 | completed = true; 94 | onComplete && onComplete(); 95 | }); 96 | } 97 | else { 98 | var startTime = Date.now(); 99 | var disX = toPos.x - fromPos.x; 100 | var disY = toPos.y - fromPos.y; 101 | var progressX = 1; 102 | var progressY = 1; 103 | var rAFHandler = function rAFHandler() { 104 | if (aborted) 105 | return; 106 | var curPos = Utils.clonePos(fromPos); 107 | if (canScrollX) { 108 | progressX = timing.x(Utils.clamp(0, 1, (Date.now() - startTime) / duration)); 109 | curPos.x = disX * progressX + fromPos.x; 110 | } 111 | if (canScrollY) { 112 | progressY = timing.y(Utils.clamp(0, 1, (Date.now() - startTime) / duration)); 113 | curPos.y = disY * progressY + fromPos.y; 114 | } 115 | setTranslate(curPos); 116 | if (progressX < 1 || progressY < 1) { 117 | ownerInstance.requestAnimationFrame(rAFHandler); 118 | } 119 | else { 120 | completed = true; 121 | onComplete && onComplete(); 122 | } 123 | }; 124 | ownerInstance.requestAnimationFrame(rAFHandler); 125 | } 126 | if (effect) 127 | effect(); 128 | effect = function abort() { 129 | if (!completed && !aborted) { 130 | aborted = true; 131 | } 132 | }; 133 | } 134 | function positionCorrection(pos) { 135 | var correctedPos = { 136 | x: Utils.clamp(minTranslateX, maxTranslateX, pos.x), 137 | y: Utils.clamp(minTranslateY, maxTranslateY, pos.y) 138 | }; 139 | if (!Utils.isSamePos(correctedPos, pos)) { 140 | moveFromTo(pos, correctedPos, options.bounceDuration, { x: Easing.v3, y: Easing.v3 }); 141 | } 142 | } 143 | function setup(_options, _, _ownerInstance, instance) { 144 | resolveOptions(_options); 145 | ownerInstance = _ownerInstance; 146 | containerRect = instance.getBoundingClientRect(); 147 | slidingContainerInstance = ownerInstance.selectComponent(options.slidingContainerSelector); 148 | slidingContainerRect = slidingContainerInstance.getBoundingClientRect(); 149 | if (slidingContainerRect.width > containerRect.width) { 150 | canScrollX = Utils.isUndefined(_options.enableScrollX) ? true : _options.enableScrollX; 151 | minTranslateX = (slidingContainerRect.width - containerRect.width) * -1; 152 | } 153 | if (slidingContainerRect.height > containerRect.height) { 154 | canScrollY = Utils.isUndefined(_options.enableScrollY) ? true : _options.enableScrollY; 155 | minTranslateY = (slidingContainerRect.height - containerRect.height) * -1; 156 | } 157 | } 158 | function touchstart(event) { 159 | startTouch.clientX = event.changedTouches[0].clientX; 160 | startTouch.clientY = event.changedTouches[0].clientY; 161 | startPos.x = pos.x; 162 | startPos.y = pos.y; 163 | startTimeStamp = event.timeStamp; 164 | if (effect) { 165 | effect(); 166 | effect = null; 167 | } 168 | } 169 | function touchmove(event) { 170 | var deltaX = event.changedTouches[0].clientX - startTouch.clientX; 171 | var deltaY = event.changedTouches[0].clientY - startTouch.clientY; 172 | var x = startPos.x; 173 | var y = startPos.y; 174 | if (canScrollX) { 175 | x += deltaX; 176 | if (x > maxTranslateX) { 177 | x = maxTranslateX + options.damping * (x - maxTranslateX); 178 | } 179 | else if (x < minTranslateX) { 180 | x = minTranslateX + options.damping * (x - minTranslateX); 181 | } 182 | } 183 | if (canScrollY) { 184 | y += deltaY; 185 | if (y > maxTranslateY) { 186 | y = maxTranslateY + options.damping * (y - maxTranslateY); 187 | } 188 | else if (y < minTranslateY) { 189 | y = minTranslateY + options.damping * (y - minTranslateY); 190 | } 191 | } 192 | setTranslate({ x: x, y: y }); 193 | } 194 | function touchend(event) { 195 | var minMovingDistance = 15; 196 | var maxMovingDuration = 300; 197 | var finalPos = { x: pos.x, y: pos.y }; 198 | if (Utils.isSamePos(finalPos, startPos)) 199 | return; 200 | var eventDuration = event.timeStamp - startTimeStamp; 201 | if (eventDuration > maxMovingDuration) { 202 | positionCorrection(finalPos); 203 | return; 204 | } 205 | var distanceX = Math.abs(finalPos.x - startPos.x); 206 | var distanceY = Math.abs(finalPos.y - startPos.y); 207 | var durationX = 0; 208 | var durationY = 0; 209 | var timingX = Easing.v1; 210 | var timingY = Easing.v1; 211 | var calculateMomentum = function calculateMomentum(start, end, distance) { 212 | var speed = distance / eventDuration; 213 | var dir = end - start > 0 ? 1 : -1; 214 | var duration = Math.min(options.momentumDuration, (speed * 2) / options.deceleration); 215 | var delta = Math.pow(speed, 2) / options.deceleration * dir; 216 | return { 217 | duration: duration, 218 | delta: delta 219 | }; 220 | }; 221 | if (canScrollX 222 | && distanceX > minMovingDistance 223 | && finalPos.x <= maxTranslateX 224 | && finalPos.x >= minTranslateX) { 225 | var result = calculateMomentum(startPos.x, pos.x, distanceX); 226 | durationX = result.duration; 227 | finalPos.x += result.delta; 228 | if (finalPos.x > maxTranslateX || finalPos.x < minTranslateX) { 229 | durationX = options.momentumOutBoundsDuration; 230 | timingX = Easing.v2; 231 | var beyondDis = containerRect.width / 6; 232 | if (finalPos.x > maxTranslateX) { 233 | finalPos.x = maxTranslateX + beyondDis; 234 | } 235 | else { 236 | finalPos.x = minTranslateX + beyondDis * -1; 237 | } 238 | } 239 | } 240 | if (canScrollY 241 | && distanceY > minMovingDistance 242 | && finalPos.y >= minTranslateY 243 | && finalPos.y <= maxTranslateY) { 244 | var result = calculateMomentum(startPos.y, pos.y, distanceY); 245 | durationY = result.duration; 246 | finalPos.y += result.delta; 247 | if (finalPos.y > maxTranslateY || finalPos.y < minTranslateY) { 248 | durationY = options.momentumOutBoundsDuration; 249 | timingY = Easing.v2; 250 | var beyondDis = containerRect.height / 6; 251 | if (finalPos.y > maxTranslateY) { 252 | finalPos.y = maxTranslateY + beyondDis; 253 | } 254 | else { 255 | finalPos.y = minTranslateY + beyondDis * -1; 256 | } 257 | } 258 | } 259 | moveFromTo(pos, finalPos, Math.max(durationX, durationY), { x: timingX, y: timingY }, function () { 260 | positionCorrection(finalPos); 261 | }); 262 | } 263 | function onScroll(callback) { 264 | onScrollCallbacks.push(callback); 265 | return function cancel() { 266 | var idx = onScrollCallbacks.indexOf(callback); 267 | if (idx !== 1) { 268 | onScrollCallbacks.splice(idx, 1); 269 | } 270 | }; 271 | } 272 | 273 | var index = { 274 | setup: setup, 275 | touchstart: touchstart, 276 | touchmove: touchmove, 277 | touchend: touchend, 278 | onScroll: onScroll 279 | }; 280 | 281 | module.exports = index; 282 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weapp-scroll", 3 | "version": "1.0.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "weapp-scroll", 9 | "version": "1.0.4", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@rollup/plugin-typescript": "^8.3.3", 13 | "miniprogram-api-typings": "^3.5.0", 14 | "rollup": "^2.76.0", 15 | "rollup-plugin-copy": "^3.4.0", 16 | "tslib": "^2.4.0", 17 | "typescript": "^4.7.4" 18 | } 19 | }, 20 | "node_modules/@nodelib/fs.scandir": { 21 | "version": "2.1.5", 22 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 23 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 24 | "dev": true, 25 | "dependencies": { 26 | "@nodelib/fs.stat": "2.0.5", 27 | "run-parallel": "^1.1.9" 28 | }, 29 | "engines": { 30 | "node": ">= 8" 31 | } 32 | }, 33 | "node_modules/@nodelib/fs.stat": { 34 | "version": "2.0.5", 35 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 36 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 37 | "dev": true, 38 | "engines": { 39 | "node": ">= 8" 40 | } 41 | }, 42 | "node_modules/@nodelib/fs.walk": { 43 | "version": "1.2.8", 44 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 45 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 46 | "dev": true, 47 | "dependencies": { 48 | "@nodelib/fs.scandir": "2.1.5", 49 | "fastq": "^1.6.0" 50 | }, 51 | "engines": { 52 | "node": ">= 8" 53 | } 54 | }, 55 | "node_modules/@rollup/plugin-typescript": { 56 | "version": "8.3.3", 57 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.3.tgz", 58 | "integrity": "sha512-55L9SyiYu3r/JtqdjhwcwaECXP7JeJ9h1Sg1VWRJKIutla2MdZQodTgcCNybXLMCnqpNLEhS2vGENww98L1npg==", 59 | "dev": true, 60 | "dependencies": { 61 | "@rollup/pluginutils": "^3.1.0", 62 | "resolve": "^1.17.0" 63 | }, 64 | "engines": { 65 | "node": ">=8.0.0" 66 | }, 67 | "peerDependencies": { 68 | "rollup": "^2.14.0", 69 | "tslib": "*", 70 | "typescript": ">=3.7.0" 71 | }, 72 | "peerDependenciesMeta": { 73 | "tslib": { 74 | "optional": true 75 | } 76 | } 77 | }, 78 | "node_modules/@rollup/pluginutils": { 79 | "version": "3.1.0", 80 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 81 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 82 | "dev": true, 83 | "dependencies": { 84 | "@types/estree": "0.0.39", 85 | "estree-walker": "^1.0.1", 86 | "picomatch": "^2.2.2" 87 | }, 88 | "engines": { 89 | "node": ">= 8.0.0" 90 | }, 91 | "peerDependencies": { 92 | "rollup": "^1.20.0||^2.0.0" 93 | } 94 | }, 95 | "node_modules/@types/estree": { 96 | "version": "0.0.39", 97 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 98 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 99 | "dev": true 100 | }, 101 | "node_modules/@types/fs-extra": { 102 | "version": "8.1.2", 103 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", 104 | "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", 105 | "dev": true, 106 | "dependencies": { 107 | "@types/node": "*" 108 | } 109 | }, 110 | "node_modules/@types/glob": { 111 | "version": "7.2.0", 112 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", 113 | "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", 114 | "dev": true, 115 | "dependencies": { 116 | "@types/minimatch": "*", 117 | "@types/node": "*" 118 | } 119 | }, 120 | "node_modules/@types/minimatch": { 121 | "version": "3.0.5", 122 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", 123 | "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", 124 | "dev": true 125 | }, 126 | "node_modules/@types/node": { 127 | "version": "18.0.3", 128 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz", 129 | "integrity": "sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==", 130 | "dev": true 131 | }, 132 | "node_modules/array-union": { 133 | "version": "2.1.0", 134 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 135 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 136 | "dev": true, 137 | "engines": { 138 | "node": ">=8" 139 | } 140 | }, 141 | "node_modules/balanced-match": { 142 | "version": "1.0.2", 143 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 144 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 145 | "dev": true 146 | }, 147 | "node_modules/brace-expansion": { 148 | "version": "1.1.11", 149 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 150 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 151 | "dev": true, 152 | "dependencies": { 153 | "balanced-match": "^1.0.0", 154 | "concat-map": "0.0.1" 155 | } 156 | }, 157 | "node_modules/braces": { 158 | "version": "3.0.2", 159 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 160 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 161 | "dev": true, 162 | "dependencies": { 163 | "fill-range": "^7.0.1" 164 | }, 165 | "engines": { 166 | "node": ">=8" 167 | } 168 | }, 169 | "node_modules/colorette": { 170 | "version": "1.4.0", 171 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", 172 | "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", 173 | "dev": true 174 | }, 175 | "node_modules/concat-map": { 176 | "version": "0.0.1", 177 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 178 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 179 | "dev": true 180 | }, 181 | "node_modules/dir-glob": { 182 | "version": "3.0.1", 183 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 184 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 185 | "dev": true, 186 | "dependencies": { 187 | "path-type": "^4.0.0" 188 | }, 189 | "engines": { 190 | "node": ">=8" 191 | } 192 | }, 193 | "node_modules/estree-walker": { 194 | "version": "1.0.1", 195 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 196 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 197 | "dev": true 198 | }, 199 | "node_modules/fast-glob": { 200 | "version": "3.2.11", 201 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", 202 | "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", 203 | "dev": true, 204 | "dependencies": { 205 | "@nodelib/fs.stat": "^2.0.2", 206 | "@nodelib/fs.walk": "^1.2.3", 207 | "glob-parent": "^5.1.2", 208 | "merge2": "^1.3.0", 209 | "micromatch": "^4.0.4" 210 | }, 211 | "engines": { 212 | "node": ">=8.6.0" 213 | } 214 | }, 215 | "node_modules/fastq": { 216 | "version": "1.13.0", 217 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 218 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 219 | "dev": true, 220 | "dependencies": { 221 | "reusify": "^1.0.4" 222 | } 223 | }, 224 | "node_modules/fill-range": { 225 | "version": "7.0.1", 226 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 227 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 228 | "dev": true, 229 | "dependencies": { 230 | "to-regex-range": "^5.0.1" 231 | }, 232 | "engines": { 233 | "node": ">=8" 234 | } 235 | }, 236 | "node_modules/fs-extra": { 237 | "version": "8.1.0", 238 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 239 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 240 | "dev": true, 241 | "dependencies": { 242 | "graceful-fs": "^4.2.0", 243 | "jsonfile": "^4.0.0", 244 | "universalify": "^0.1.0" 245 | }, 246 | "engines": { 247 | "node": ">=6 <7 || >=8" 248 | } 249 | }, 250 | "node_modules/fs.realpath": { 251 | "version": "1.0.0", 252 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 253 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 254 | "dev": true 255 | }, 256 | "node_modules/fsevents": { 257 | "version": "2.3.2", 258 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 259 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 260 | "dev": true, 261 | "hasInstallScript": true, 262 | "optional": true, 263 | "os": [ 264 | "darwin" 265 | ], 266 | "engines": { 267 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 268 | } 269 | }, 270 | "node_modules/function-bind": { 271 | "version": "1.1.1", 272 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 273 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 274 | "dev": true 275 | }, 276 | "node_modules/glob": { 277 | "version": "7.2.3", 278 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 279 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 280 | "dev": true, 281 | "dependencies": { 282 | "fs.realpath": "^1.0.0", 283 | "inflight": "^1.0.4", 284 | "inherits": "2", 285 | "minimatch": "^3.1.1", 286 | "once": "^1.3.0", 287 | "path-is-absolute": "^1.0.0" 288 | }, 289 | "engines": { 290 | "node": "*" 291 | }, 292 | "funding": { 293 | "url": "https://github.com/sponsors/isaacs" 294 | } 295 | }, 296 | "node_modules/glob-parent": { 297 | "version": "5.1.2", 298 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 299 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 300 | "dev": true, 301 | "dependencies": { 302 | "is-glob": "^4.0.1" 303 | }, 304 | "engines": { 305 | "node": ">= 6" 306 | } 307 | }, 308 | "node_modules/globby": { 309 | "version": "10.0.1", 310 | "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", 311 | "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", 312 | "dev": true, 313 | "dependencies": { 314 | "@types/glob": "^7.1.1", 315 | "array-union": "^2.1.0", 316 | "dir-glob": "^3.0.1", 317 | "fast-glob": "^3.0.3", 318 | "glob": "^7.1.3", 319 | "ignore": "^5.1.1", 320 | "merge2": "^1.2.3", 321 | "slash": "^3.0.0" 322 | }, 323 | "engines": { 324 | "node": ">=8" 325 | } 326 | }, 327 | "node_modules/graceful-fs": { 328 | "version": "4.2.10", 329 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 330 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 331 | "dev": true 332 | }, 333 | "node_modules/has": { 334 | "version": "1.0.3", 335 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 336 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 337 | "dev": true, 338 | "dependencies": { 339 | "function-bind": "^1.1.1" 340 | }, 341 | "engines": { 342 | "node": ">= 0.4.0" 343 | } 344 | }, 345 | "node_modules/ignore": { 346 | "version": "5.2.0", 347 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 348 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 349 | "dev": true, 350 | "engines": { 351 | "node": ">= 4" 352 | } 353 | }, 354 | "node_modules/inflight": { 355 | "version": "1.0.6", 356 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 357 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 358 | "dev": true, 359 | "dependencies": { 360 | "once": "^1.3.0", 361 | "wrappy": "1" 362 | } 363 | }, 364 | "node_modules/inherits": { 365 | "version": "2.0.4", 366 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 367 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 368 | "dev": true 369 | }, 370 | "node_modules/is-core-module": { 371 | "version": "2.9.0", 372 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 373 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 374 | "dev": true, 375 | "dependencies": { 376 | "has": "^1.0.3" 377 | }, 378 | "funding": { 379 | "url": "https://github.com/sponsors/ljharb" 380 | } 381 | }, 382 | "node_modules/is-extglob": { 383 | "version": "2.1.1", 384 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 385 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 386 | "dev": true, 387 | "engines": { 388 | "node": ">=0.10.0" 389 | } 390 | }, 391 | "node_modules/is-glob": { 392 | "version": "4.0.3", 393 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 394 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 395 | "dev": true, 396 | "dependencies": { 397 | "is-extglob": "^2.1.1" 398 | }, 399 | "engines": { 400 | "node": ">=0.10.0" 401 | } 402 | }, 403 | "node_modules/is-number": { 404 | "version": "7.0.0", 405 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 406 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 407 | "dev": true, 408 | "engines": { 409 | "node": ">=0.12.0" 410 | } 411 | }, 412 | "node_modules/is-plain-object": { 413 | "version": "3.0.1", 414 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", 415 | "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", 416 | "dev": true, 417 | "engines": { 418 | "node": ">=0.10.0" 419 | } 420 | }, 421 | "node_modules/jsonfile": { 422 | "version": "4.0.0", 423 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 424 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 425 | "dev": true, 426 | "optionalDependencies": { 427 | "graceful-fs": "^4.1.6" 428 | } 429 | }, 430 | "node_modules/merge2": { 431 | "version": "1.4.1", 432 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 433 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 434 | "dev": true, 435 | "engines": { 436 | "node": ">= 8" 437 | } 438 | }, 439 | "node_modules/micromatch": { 440 | "version": "4.0.5", 441 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 442 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 443 | "dev": true, 444 | "dependencies": { 445 | "braces": "^3.0.2", 446 | "picomatch": "^2.3.1" 447 | }, 448 | "engines": { 449 | "node": ">=8.6" 450 | } 451 | }, 452 | "node_modules/minimatch": { 453 | "version": "3.1.2", 454 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 455 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 456 | "dev": true, 457 | "dependencies": { 458 | "brace-expansion": "^1.1.7" 459 | }, 460 | "engines": { 461 | "node": "*" 462 | } 463 | }, 464 | "node_modules/miniprogram-api-typings": { 465 | "version": "3.5.0", 466 | "resolved": "https://registry.npmjs.org/miniprogram-api-typings/-/miniprogram-api-typings-3.5.0.tgz", 467 | "integrity": "sha512-cS/uXJTMGKujwoaeEFFFKqpCf7qp3R7iuQZWagZAwhKA3Z7U/xlhOcI3i1wqN+uo4YODg3AG1j2zCe1PDa9Wtg==", 468 | "dev": true 469 | }, 470 | "node_modules/once": { 471 | "version": "1.4.0", 472 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 473 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 474 | "dev": true, 475 | "dependencies": { 476 | "wrappy": "1" 477 | } 478 | }, 479 | "node_modules/path-is-absolute": { 480 | "version": "1.0.1", 481 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 482 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 483 | "dev": true, 484 | "engines": { 485 | "node": ">=0.10.0" 486 | } 487 | }, 488 | "node_modules/path-parse": { 489 | "version": "1.0.7", 490 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 491 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 492 | "dev": true 493 | }, 494 | "node_modules/path-type": { 495 | "version": "4.0.0", 496 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 497 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 498 | "dev": true, 499 | "engines": { 500 | "node": ">=8" 501 | } 502 | }, 503 | "node_modules/picomatch": { 504 | "version": "2.3.1", 505 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 506 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 507 | "dev": true, 508 | "engines": { 509 | "node": ">=8.6" 510 | }, 511 | "funding": { 512 | "url": "https://github.com/sponsors/jonschlinkert" 513 | } 514 | }, 515 | "node_modules/queue-microtask": { 516 | "version": "1.2.3", 517 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 518 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 519 | "dev": true, 520 | "funding": [ 521 | { 522 | "type": "github", 523 | "url": "https://github.com/sponsors/feross" 524 | }, 525 | { 526 | "type": "patreon", 527 | "url": "https://www.patreon.com/feross" 528 | }, 529 | { 530 | "type": "consulting", 531 | "url": "https://feross.org/support" 532 | } 533 | ] 534 | }, 535 | "node_modules/resolve": { 536 | "version": "1.22.1", 537 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 538 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 539 | "dev": true, 540 | "dependencies": { 541 | "is-core-module": "^2.9.0", 542 | "path-parse": "^1.0.7", 543 | "supports-preserve-symlinks-flag": "^1.0.0" 544 | }, 545 | "bin": { 546 | "resolve": "bin/resolve" 547 | }, 548 | "funding": { 549 | "url": "https://github.com/sponsors/ljharb" 550 | } 551 | }, 552 | "node_modules/reusify": { 553 | "version": "1.0.4", 554 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 555 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 556 | "dev": true, 557 | "engines": { 558 | "iojs": ">=1.0.0", 559 | "node": ">=0.10.0" 560 | } 561 | }, 562 | "node_modules/rollup": { 563 | "version": "2.76.0", 564 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.76.0.tgz", 565 | "integrity": "sha512-9jwRIEY1jOzKLj3nsY/yot41r19ITdQrhs+q3ggNWhr9TQgduHqANvPpS32RNpzGklJu3G1AJfvlZLi/6wFgWA==", 566 | "dev": true, 567 | "bin": { 568 | "rollup": "dist/bin/rollup" 569 | }, 570 | "engines": { 571 | "node": ">=10.0.0" 572 | }, 573 | "optionalDependencies": { 574 | "fsevents": "~2.3.2" 575 | } 576 | }, 577 | "node_modules/rollup-plugin-copy": { 578 | "version": "3.4.0", 579 | "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", 580 | "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", 581 | "dev": true, 582 | "dependencies": { 583 | "@types/fs-extra": "^8.0.1", 584 | "colorette": "^1.1.0", 585 | "fs-extra": "^8.1.0", 586 | "globby": "10.0.1", 587 | "is-plain-object": "^3.0.0" 588 | }, 589 | "engines": { 590 | "node": ">=8.3" 591 | } 592 | }, 593 | "node_modules/run-parallel": { 594 | "version": "1.2.0", 595 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 596 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 597 | "dev": true, 598 | "funding": [ 599 | { 600 | "type": "github", 601 | "url": "https://github.com/sponsors/feross" 602 | }, 603 | { 604 | "type": "patreon", 605 | "url": "https://www.patreon.com/feross" 606 | }, 607 | { 608 | "type": "consulting", 609 | "url": "https://feross.org/support" 610 | } 611 | ], 612 | "dependencies": { 613 | "queue-microtask": "^1.2.2" 614 | } 615 | }, 616 | "node_modules/slash": { 617 | "version": "3.0.0", 618 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 619 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 620 | "dev": true, 621 | "engines": { 622 | "node": ">=8" 623 | } 624 | }, 625 | "node_modules/supports-preserve-symlinks-flag": { 626 | "version": "1.0.0", 627 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 628 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 629 | "dev": true, 630 | "engines": { 631 | "node": ">= 0.4" 632 | }, 633 | "funding": { 634 | "url": "https://github.com/sponsors/ljharb" 635 | } 636 | }, 637 | "node_modules/to-regex-range": { 638 | "version": "5.0.1", 639 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 640 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 641 | "dev": true, 642 | "dependencies": { 643 | "is-number": "^7.0.0" 644 | }, 645 | "engines": { 646 | "node": ">=8.0" 647 | } 648 | }, 649 | "node_modules/tslib": { 650 | "version": "2.4.0", 651 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 652 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", 653 | "dev": true 654 | }, 655 | "node_modules/typescript": { 656 | "version": "4.7.4", 657 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 658 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 659 | "dev": true, 660 | "bin": { 661 | "tsc": "bin/tsc", 662 | "tsserver": "bin/tsserver" 663 | }, 664 | "engines": { 665 | "node": ">=4.2.0" 666 | } 667 | }, 668 | "node_modules/universalify": { 669 | "version": "0.1.2", 670 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 671 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 672 | "dev": true, 673 | "engines": { 674 | "node": ">= 4.0.0" 675 | } 676 | }, 677 | "node_modules/wrappy": { 678 | "version": "1.0.2", 679 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 680 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 681 | "dev": true 682 | } 683 | }, 684 | "dependencies": { 685 | "@nodelib/fs.scandir": { 686 | "version": "2.1.5", 687 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 688 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 689 | "dev": true, 690 | "requires": { 691 | "@nodelib/fs.stat": "2.0.5", 692 | "run-parallel": "^1.1.9" 693 | } 694 | }, 695 | "@nodelib/fs.stat": { 696 | "version": "2.0.5", 697 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 698 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 699 | "dev": true 700 | }, 701 | "@nodelib/fs.walk": { 702 | "version": "1.2.8", 703 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 704 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 705 | "dev": true, 706 | "requires": { 707 | "@nodelib/fs.scandir": "2.1.5", 708 | "fastq": "^1.6.0" 709 | } 710 | }, 711 | "@rollup/plugin-typescript": { 712 | "version": "8.3.3", 713 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.3.tgz", 714 | "integrity": "sha512-55L9SyiYu3r/JtqdjhwcwaECXP7JeJ9h1Sg1VWRJKIutla2MdZQodTgcCNybXLMCnqpNLEhS2vGENww98L1npg==", 715 | "dev": true, 716 | "requires": { 717 | "@rollup/pluginutils": "^3.1.0", 718 | "resolve": "^1.17.0" 719 | } 720 | }, 721 | "@rollup/pluginutils": { 722 | "version": "3.1.0", 723 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 724 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 725 | "dev": true, 726 | "requires": { 727 | "@types/estree": "0.0.39", 728 | "estree-walker": "^1.0.1", 729 | "picomatch": "^2.2.2" 730 | } 731 | }, 732 | "@types/estree": { 733 | "version": "0.0.39", 734 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 735 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 736 | "dev": true 737 | }, 738 | "@types/fs-extra": { 739 | "version": "8.1.2", 740 | "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", 741 | "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", 742 | "dev": true, 743 | "requires": { 744 | "@types/node": "*" 745 | } 746 | }, 747 | "@types/glob": { 748 | "version": "7.2.0", 749 | "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", 750 | "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", 751 | "dev": true, 752 | "requires": { 753 | "@types/minimatch": "*", 754 | "@types/node": "*" 755 | } 756 | }, 757 | "@types/minimatch": { 758 | "version": "3.0.5", 759 | "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", 760 | "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", 761 | "dev": true 762 | }, 763 | "@types/node": { 764 | "version": "18.0.3", 765 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz", 766 | "integrity": "sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ==", 767 | "dev": true 768 | }, 769 | "array-union": { 770 | "version": "2.1.0", 771 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 772 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 773 | "dev": true 774 | }, 775 | "balanced-match": { 776 | "version": "1.0.2", 777 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 778 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 779 | "dev": true 780 | }, 781 | "brace-expansion": { 782 | "version": "1.1.11", 783 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 784 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 785 | "dev": true, 786 | "requires": { 787 | "balanced-match": "^1.0.0", 788 | "concat-map": "0.0.1" 789 | } 790 | }, 791 | "braces": { 792 | "version": "3.0.2", 793 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 794 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 795 | "dev": true, 796 | "requires": { 797 | "fill-range": "^7.0.1" 798 | } 799 | }, 800 | "colorette": { 801 | "version": "1.4.0", 802 | "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", 803 | "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", 804 | "dev": true 805 | }, 806 | "concat-map": { 807 | "version": "0.0.1", 808 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 809 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 810 | "dev": true 811 | }, 812 | "dir-glob": { 813 | "version": "3.0.1", 814 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 815 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 816 | "dev": true, 817 | "requires": { 818 | "path-type": "^4.0.0" 819 | } 820 | }, 821 | "estree-walker": { 822 | "version": "1.0.1", 823 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 824 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 825 | "dev": true 826 | }, 827 | "fast-glob": { 828 | "version": "3.2.11", 829 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", 830 | "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", 831 | "dev": true, 832 | "requires": { 833 | "@nodelib/fs.stat": "^2.0.2", 834 | "@nodelib/fs.walk": "^1.2.3", 835 | "glob-parent": "^5.1.2", 836 | "merge2": "^1.3.0", 837 | "micromatch": "^4.0.4" 838 | } 839 | }, 840 | "fastq": { 841 | "version": "1.13.0", 842 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 843 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 844 | "dev": true, 845 | "requires": { 846 | "reusify": "^1.0.4" 847 | } 848 | }, 849 | "fill-range": { 850 | "version": "7.0.1", 851 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 852 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 853 | "dev": true, 854 | "requires": { 855 | "to-regex-range": "^5.0.1" 856 | } 857 | }, 858 | "fs-extra": { 859 | "version": "8.1.0", 860 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 861 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 862 | "dev": true, 863 | "requires": { 864 | "graceful-fs": "^4.2.0", 865 | "jsonfile": "^4.0.0", 866 | "universalify": "^0.1.0" 867 | } 868 | }, 869 | "fs.realpath": { 870 | "version": "1.0.0", 871 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 872 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 873 | "dev": true 874 | }, 875 | "fsevents": { 876 | "version": "2.3.2", 877 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 878 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 879 | "dev": true, 880 | "optional": true 881 | }, 882 | "function-bind": { 883 | "version": "1.1.1", 884 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 885 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 886 | "dev": true 887 | }, 888 | "glob": { 889 | "version": "7.2.3", 890 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 891 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 892 | "dev": true, 893 | "requires": { 894 | "fs.realpath": "^1.0.0", 895 | "inflight": "^1.0.4", 896 | "inherits": "2", 897 | "minimatch": "^3.1.1", 898 | "once": "^1.3.0", 899 | "path-is-absolute": "^1.0.0" 900 | } 901 | }, 902 | "glob-parent": { 903 | "version": "5.1.2", 904 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 905 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 906 | "dev": true, 907 | "requires": { 908 | "is-glob": "^4.0.1" 909 | } 910 | }, 911 | "globby": { 912 | "version": "10.0.1", 913 | "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", 914 | "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", 915 | "dev": true, 916 | "requires": { 917 | "@types/glob": "^7.1.1", 918 | "array-union": "^2.1.0", 919 | "dir-glob": "^3.0.1", 920 | "fast-glob": "^3.0.3", 921 | "glob": "^7.1.3", 922 | "ignore": "^5.1.1", 923 | "merge2": "^1.2.3", 924 | "slash": "^3.0.0" 925 | } 926 | }, 927 | "graceful-fs": { 928 | "version": "4.2.10", 929 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 930 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", 931 | "dev": true 932 | }, 933 | "has": { 934 | "version": "1.0.3", 935 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 936 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 937 | "dev": true, 938 | "requires": { 939 | "function-bind": "^1.1.1" 940 | } 941 | }, 942 | "ignore": { 943 | "version": "5.2.0", 944 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 945 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", 946 | "dev": true 947 | }, 948 | "inflight": { 949 | "version": "1.0.6", 950 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 951 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 952 | "dev": true, 953 | "requires": { 954 | "once": "^1.3.0", 955 | "wrappy": "1" 956 | } 957 | }, 958 | "inherits": { 959 | "version": "2.0.4", 960 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 961 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 962 | "dev": true 963 | }, 964 | "is-core-module": { 965 | "version": "2.9.0", 966 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 967 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 968 | "dev": true, 969 | "requires": { 970 | "has": "^1.0.3" 971 | } 972 | }, 973 | "is-extglob": { 974 | "version": "2.1.1", 975 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 976 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 977 | "dev": true 978 | }, 979 | "is-glob": { 980 | "version": "4.0.3", 981 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 982 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 983 | "dev": true, 984 | "requires": { 985 | "is-extglob": "^2.1.1" 986 | } 987 | }, 988 | "is-number": { 989 | "version": "7.0.0", 990 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 991 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 992 | "dev": true 993 | }, 994 | "is-plain-object": { 995 | "version": "3.0.1", 996 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", 997 | "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", 998 | "dev": true 999 | }, 1000 | "jsonfile": { 1001 | "version": "4.0.0", 1002 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 1003 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 1004 | "dev": true, 1005 | "requires": { 1006 | "graceful-fs": "^4.1.6" 1007 | } 1008 | }, 1009 | "merge2": { 1010 | "version": "1.4.1", 1011 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1012 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1013 | "dev": true 1014 | }, 1015 | "micromatch": { 1016 | "version": "4.0.5", 1017 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1018 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1019 | "dev": true, 1020 | "requires": { 1021 | "braces": "^3.0.2", 1022 | "picomatch": "^2.3.1" 1023 | } 1024 | }, 1025 | "minimatch": { 1026 | "version": "3.1.2", 1027 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1028 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1029 | "dev": true, 1030 | "requires": { 1031 | "brace-expansion": "^1.1.7" 1032 | } 1033 | }, 1034 | "miniprogram-api-typings": { 1035 | "version": "3.5.0", 1036 | "resolved": "https://registry.npmjs.org/miniprogram-api-typings/-/miniprogram-api-typings-3.5.0.tgz", 1037 | "integrity": "sha512-cS/uXJTMGKujwoaeEFFFKqpCf7qp3R7iuQZWagZAwhKA3Z7U/xlhOcI3i1wqN+uo4YODg3AG1j2zCe1PDa9Wtg==", 1038 | "dev": true 1039 | }, 1040 | "once": { 1041 | "version": "1.4.0", 1042 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1043 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1044 | "dev": true, 1045 | "requires": { 1046 | "wrappy": "1" 1047 | } 1048 | }, 1049 | "path-is-absolute": { 1050 | "version": "1.0.1", 1051 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1052 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1053 | "dev": true 1054 | }, 1055 | "path-parse": { 1056 | "version": "1.0.7", 1057 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1058 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1059 | "dev": true 1060 | }, 1061 | "path-type": { 1062 | "version": "4.0.0", 1063 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1064 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1065 | "dev": true 1066 | }, 1067 | "picomatch": { 1068 | "version": "2.3.1", 1069 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1070 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1071 | "dev": true 1072 | }, 1073 | "queue-microtask": { 1074 | "version": "1.2.3", 1075 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1076 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1077 | "dev": true 1078 | }, 1079 | "resolve": { 1080 | "version": "1.22.1", 1081 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", 1082 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", 1083 | "dev": true, 1084 | "requires": { 1085 | "is-core-module": "^2.9.0", 1086 | "path-parse": "^1.0.7", 1087 | "supports-preserve-symlinks-flag": "^1.0.0" 1088 | } 1089 | }, 1090 | "reusify": { 1091 | "version": "1.0.4", 1092 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1093 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1094 | "dev": true 1095 | }, 1096 | "rollup": { 1097 | "version": "2.76.0", 1098 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.76.0.tgz", 1099 | "integrity": "sha512-9jwRIEY1jOzKLj3nsY/yot41r19ITdQrhs+q3ggNWhr9TQgduHqANvPpS32RNpzGklJu3G1AJfvlZLi/6wFgWA==", 1100 | "dev": true, 1101 | "requires": { 1102 | "fsevents": "~2.3.2" 1103 | } 1104 | }, 1105 | "rollup-plugin-copy": { 1106 | "version": "3.4.0", 1107 | "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz", 1108 | "integrity": "sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ==", 1109 | "dev": true, 1110 | "requires": { 1111 | "@types/fs-extra": "^8.0.1", 1112 | "colorette": "^1.1.0", 1113 | "fs-extra": "^8.1.0", 1114 | "globby": "10.0.1", 1115 | "is-plain-object": "^3.0.0" 1116 | } 1117 | }, 1118 | "run-parallel": { 1119 | "version": "1.2.0", 1120 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 1121 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 1122 | "dev": true, 1123 | "requires": { 1124 | "queue-microtask": "^1.2.2" 1125 | } 1126 | }, 1127 | "slash": { 1128 | "version": "3.0.0", 1129 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 1130 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 1131 | "dev": true 1132 | }, 1133 | "supports-preserve-symlinks-flag": { 1134 | "version": "1.0.0", 1135 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1136 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1137 | "dev": true 1138 | }, 1139 | "to-regex-range": { 1140 | "version": "5.0.1", 1141 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1142 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1143 | "dev": true, 1144 | "requires": { 1145 | "is-number": "^7.0.0" 1146 | } 1147 | }, 1148 | "tslib": { 1149 | "version": "2.4.0", 1150 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 1151 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", 1152 | "dev": true 1153 | }, 1154 | "typescript": { 1155 | "version": "4.7.4", 1156 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 1157 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 1158 | "dev": true 1159 | }, 1160 | "universalify": { 1161 | "version": "0.1.2", 1162 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1163 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 1164 | "dev": true 1165 | }, 1166 | "wrappy": { 1167 | "version": "1.0.2", 1168 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1169 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1170 | "dev": true 1171 | } 1172 | } 1173 | } 1174 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weapp-scroll", 3 | "version": "1.0.5", 4 | "description": "微信小程序带回弹效果的滚动容器,可适用在安卓、PC场景", 5 | "main": "dist/index.wxs", 6 | "miniprogram": "dist", 7 | "scripts": { 8 | "dev": "rollup -c rollup.config.js --watch", 9 | "build": "rollup -c rollup.config.js" 10 | }, 11 | "keywords": [ 12 | "WXS", 13 | "微信小程序", 14 | "滚动容器" 15 | ], 16 | "author": "xieyuhang", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@rollup/plugin-typescript": "^8.3.3", 20 | "miniprogram-api-typings": "^3.5.0", 21 | "rollup": "^2.76.0", 22 | "rollup-plugin-copy": "^3.4.0", 23 | "tslib": "^2.4.0", 24 | "typescript": "^4.7.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieyhn/weapp-scroll/051f25f9ae8302270b4498dbdf58f0645daa02d2/preview.gif -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'rollup' 2 | import typescript from '@rollup/plugin-typescript' 3 | import copy from 'rollup-plugin-copy' 4 | 5 | export default defineConfig({ 6 | input: 'src/index.ts', 7 | output: { 8 | file: 'dist/index.wxs', 9 | format: 'cjs', 10 | exports: 'default' 11 | }, 12 | plugins: [ 13 | typescript(), 14 | copy({ 15 | targets: [ 16 | { src: 'dist/index.wxs', dest: 'example/', rename: 'weapp-scroll.wxs' } 17 | ], 18 | hook: 'writeBundle' 19 | }) 20 | ] 21 | }) 22 | -------------------------------------------------------------------------------- /src/Easing.ts: -------------------------------------------------------------------------------- 1 | export type EasingFunction = (t: number) => number 2 | 3 | var Easing = { 4 | v1: function (t: number) { 5 | return 1 + --t * t * t * t * t 6 | }, 7 | v2: function(t: number) { 8 | return t * (2 - t) 9 | }, 10 | v3: function(t: number) { 11 | return 1 - --t * t * t * t 12 | } 13 | } 14 | 15 | export default Easing 16 | -------------------------------------------------------------------------------- /src/Utils.ts: -------------------------------------------------------------------------------- 1 | import type { Pos } from './core' 2 | 3 | var Utils = { 4 | isUndefined(val: unknown): val is undefined { 5 | return typeof val === 'undefined' 6 | }, 7 | 8 | clamp(min: number, max: number, val: number) { 9 | return Math.max(min, Math.min(max, val)) 10 | }, 11 | 12 | clonePos(pos: Pos): Pos { 13 | return { 14 | x: pos.x, 15 | y: pos.y 16 | } 17 | }, 18 | 19 | isSamePos(p1: Pos, p2: Pos) { 20 | return p1.x === p2.x && p1.y === p2.y 21 | } 22 | } 23 | 24 | export default Utils -------------------------------------------------------------------------------- /src/core.ts: -------------------------------------------------------------------------------- 1 | import Easing, { EasingFunction } from './Easing' 2 | import Utils from './Utils' 3 | import options, { resolveOptions, Options } from './options' 4 | 5 | type Rect = WechatMiniprogram.BoundingClientRectCallbackResult 6 | 7 | export interface Pos { 8 | x: number 9 | y: number 10 | } 11 | 12 | interface ScrollCallback { 13 | (data: { 14 | x: number 15 | y: number 16 | maxScrollDistanceX: number 17 | maxScrollDistanceY: number 18 | }): void 19 | } 20 | 21 | var ownerInstance: ComponentDescriptor 22 | /** 23 | * 最外层容器 BoundingClientRect 24 | */ 25 | var containerRect: Rect 26 | /** 27 | * 滑动容器实例,通过此实例设置 CSS 属性 28 | */ 29 | var slidingContainerInstance: ComponentDescriptor 30 | /** 31 | * 滑动容器 BoundingClientRect 32 | */ 33 | var slidingContainerRect: Rect 34 | /** 35 | * 标记 X 方向是否能滚动 36 | */ 37 | var canScrollX = false 38 | /** 39 | * 标记 Y 方向是否能滚动 40 | */ 41 | var canScrollY = false 42 | /** 43 | * X方向的最小、最大滚动距离。如 -200 至 0(手势往右时,元素左移,translateX 为负值) 44 | */ 45 | var minTranslateX: number 46 | var maxTranslateX = 0 47 | /** 48 | * Y方向的最小,最大滚动距离。如 -200 至 0(手势往下时,元素上移,translateY 为负值) 49 | */ 50 | var minTranslateY: number 51 | var maxTranslateY = 0 52 | /** 53 | * 滑动元素的位置信息,即滑动元素上的 CSS translate 值 54 | * 触摸动作需要以此为基础进行变换 55 | */ 56 | var pos: Pos = { x: 0, y: 0 } 57 | /** 58 | * 记录触摸开始时,手指的位置 59 | */ 60 | var startTouch = { clientX: 0, clientY: 0 } 61 | /** 62 | * 记录每次触摸开啥时,滑动容器位置 63 | */ 64 | var startPos: Pos = { x: 0, y: 0 } 65 | /** 66 | * 记录触摸开始时的时间戳 67 | */ 68 | var startTimeStamp = 0 69 | /** 70 | * 记录触摸开始前需要清除的 effect 71 | */ 72 | var effect: (() => void) | null = null 73 | 74 | /** 75 | * callbacks 76 | */ 77 | var onScrollCallbacks: ScrollCallback[] = [] 78 | 79 | /** 80 | * 设置样式 81 | */ 82 | function setTranslate(pos0: Pos) { 83 | if (Utils.isSamePos(pos0, pos)) return 84 | 85 | slidingContainerInstance.setStyle({ 86 | transform: 'translateX(' + pos0.x + 'px) translateY(' + pos0.y + 'px)' 87 | }) 88 | pos.x = pos0.x 89 | pos.y = pos0.y 90 | onScrollCallbacks.forEach(cb => cb({ 91 | x: pos.x, 92 | y: pos.y, 93 | maxScrollDistanceX: minTranslateX * -1, 94 | maxScrollDistanceY: minTranslateY * -1 95 | })) 96 | } 97 | 98 | /** 99 | * @param fromPos 100 | * @param toPos 101 | * @param duration 持续时长,单位ms 102 | * @param timing 103 | * @param onComplete 动画完成的回调 104 | */ 105 | function moveFromTo( 106 | fromPos: Pos, 107 | toPos: Pos, 108 | duration: number, 109 | timing: { 110 | x: EasingFunction, 111 | y: EasingFunction 112 | }, 113 | onComplete?: () => void 114 | ) { 115 | var aborted = false 116 | var completed = false 117 | fromPos = Utils.clonePos(fromPos) 118 | toPos = Utils.clonePos(toPos) 119 | 120 | if (duration === 0) { 121 | setTranslate(toPos) 122 | ownerInstance.requestAnimationFrame(function() { 123 | completed = true 124 | onComplete && onComplete() 125 | }) 126 | } else { 127 | var startTime = Date.now() 128 | var disX = toPos.x - fromPos.x 129 | var disY = toPos.y - fromPos.y 130 | var progressX = 1 131 | var progressY = 1 132 | var rAFHandler = function rAFHandler() { 133 | if (aborted) return 134 | var curPos = Utils.clonePos(fromPos) 135 | if (canScrollX) { 136 | progressX = timing.x(Utils.clamp(0, 1, (Date.now() - startTime) / duration)) 137 | curPos.x = disX * progressX + fromPos.x 138 | } 139 | if (canScrollY) { 140 | progressY = timing.y(Utils.clamp(0, 1, (Date.now() - startTime) / duration)) 141 | curPos.y = disY * progressY + fromPos.y 142 | } 143 | setTranslate(curPos) 144 | 145 | if (progressX < 1 || progressY < 1) { 146 | ownerInstance.requestAnimationFrame(rAFHandler) 147 | } else { 148 | completed = true 149 | onComplete && onComplete() 150 | } 151 | } 152 | ownerInstance.requestAnimationFrame(rAFHandler) 153 | } 154 | 155 | if (effect) effect() 156 | effect = function abort() { 157 | if (!completed && !aborted) { 158 | aborted = true 159 | } 160 | } 161 | } 162 | 163 | /** 164 | * 超出边界后就行位置修正,即回弹效果 165 | */ 166 | function positionCorrection(pos: Pos) { 167 | var correctedPos: Pos = { 168 | x: Utils.clamp(minTranslateX, maxTranslateX, pos.x), 169 | y: Utils.clamp(minTranslateY, maxTranslateY, pos.y) 170 | } 171 | if (!Utils.isSamePos(correctedPos, pos)) { 172 | moveFromTo(pos, correctedPos, options.bounceDuration, { x: Easing.v3, y: Easing.v3 }) 173 | } 174 | } 175 | 176 | export function setup(_options: Options, _: any, _ownerInstance: ComponentDescriptor, instance: ComponentDescriptor) { 177 | resolveOptions(_options) 178 | 179 | ownerInstance = _ownerInstance 180 | containerRect = instance.getBoundingClientRect() 181 | slidingContainerInstance = ownerInstance.selectComponent(options.slidingContainerSelector)! 182 | slidingContainerRect = slidingContainerInstance.getBoundingClientRect() 183 | 184 | if (slidingContainerRect.width > containerRect.width) { 185 | // canScrollX 186 | canScrollX = Utils.isUndefined(_options.enableScrollX) ? true : _options.enableScrollX 187 | minTranslateX = (slidingContainerRect.width - containerRect.width) * -1 188 | } 189 | if (slidingContainerRect.height > containerRect.height) { 190 | // canScrollY 191 | canScrollY = Utils.isUndefined(_options.enableScrollY) ? true : _options.enableScrollY 192 | minTranslateY = (slidingContainerRect.height - containerRect.height) * -1 193 | } 194 | } 195 | 196 | // touchstart 197 | export function touchstart(event: WechatMiniprogram.TouchEvent) { 198 | startTouch.clientX = event.changedTouches[0].clientX 199 | startTouch.clientY = event.changedTouches[0].clientY 200 | 201 | startPos.x = pos.x 202 | startPos.y = pos.y 203 | 204 | startTimeStamp = event.timeStamp 205 | 206 | if (effect) { 207 | effect() 208 | effect = null 209 | } 210 | } 211 | 212 | // touchmove 213 | export function touchmove(event: WechatMiniprogram.TouchEvent) { 214 | var deltaX = event.changedTouches[0].clientX - startTouch.clientX 215 | var deltaY = event.changedTouches[0].clientY - startTouch.clientY 216 | 217 | var x = startPos.x 218 | var y = startPos.y 219 | 220 | if (canScrollX) { 221 | x += deltaX 222 | 223 | if (x > maxTranslateX) { 224 | // 手指右滑导致元素左侧超出,超出部分添加阻尼行为 225 | x = maxTranslateX + options.damping * (x - maxTranslateX) 226 | } else if (x < minTranslateX) { 227 | // 手指左滑导致元素右侧超出,超出部分添加阻尼行为 228 | x = minTranslateX + options.damping * (x - minTranslateX) 229 | } 230 | } 231 | 232 | if (canScrollY) { 233 | y += deltaY 234 | 235 | if (y > maxTranslateY) { 236 | // 手指下滑导致元素顶部超出,超出部分添加阻尼行为 237 | y = maxTranslateY + options.damping * (y - maxTranslateY) 238 | } else if (y < minTranslateY) { 239 | // 手指上滑导致元素底部超出,超出部分添加阻尼行为 240 | y = minTranslateY + options.damping * (y - minTranslateY) 241 | } 242 | } 243 | 244 | setTranslate({ x, y }) 245 | } 246 | 247 | // touchend 248 | export function touchend(event: WechatMiniprogram.TouchEvent) { 249 | var minMovingDistance = 15 250 | var maxMovingDuration = 300 251 | var finalPos: Pos = { x: pos.x, y: pos.y } 252 | 253 | // 未移动 254 | if (Utils.isSamePos(finalPos, startPos)) return 255 | 256 | var eventDuration = event.timeStamp - startTimeStamp 257 | 258 | if (eventDuration > maxMovingDuration) { 259 | positionCorrection(finalPos) 260 | return 261 | } 262 | 263 | // 移动距离 264 | var distanceX = Math.abs(finalPos.x - startPos.x) 265 | var distanceY = Math.abs(finalPos.y - startPos.y) 266 | 267 | // 动量动画持续时间 268 | var durationX = 0 269 | var durationY = 0 270 | 271 | // 动量动画函数 272 | var timingX = Easing.v1 273 | var timingY = Easing.v1 274 | 275 | var calculateMomentum = function calculateMomentum(start: number, end: number, distance: number) { 276 | var speed = distance / eventDuration 277 | var dir = end - start > 0 ? 1 : -1 278 | var duration = Math.min(options.momentumDuration, (speed * 2) / options.deceleration) 279 | var delta = Math.pow(speed, 2) / options.deceleration * dir 280 | 281 | return { 282 | duration: duration, 283 | delta: delta 284 | } 285 | } 286 | 287 | if ( 288 | canScrollX 289 | && distanceX > minMovingDistance 290 | && finalPos.x <= maxTranslateX 291 | && finalPos.x >= minTranslateX 292 | ) { 293 | var result = calculateMomentum(startPos.x, pos.x, distanceX) 294 | 295 | durationX = result.duration 296 | finalPos.x += result.delta 297 | 298 | if (finalPos.x > maxTranslateX || finalPos.x < minTranslateX) { 299 | durationX = options.momentumOutBoundsDuration 300 | timingX = Easing.v2 301 | var beyondDis = containerRect.width / 6 302 | if (finalPos.x > maxTranslateX) { 303 | finalPos.x = maxTranslateX + beyondDis 304 | } else { 305 | finalPos.x = minTranslateX + beyondDis * -1 306 | } 307 | } 308 | } 309 | 310 | if ( 311 | canScrollY 312 | && distanceY > minMovingDistance 313 | && finalPos.y >= minTranslateY 314 | && finalPos.y <= maxTranslateY 315 | ) { 316 | // 在边界中,可以进行动量动画 317 | var result = calculateMomentum(startPos.y, pos.y, distanceY) 318 | 319 | durationY = result.duration 320 | finalPos.y += result.delta 321 | 322 | if (finalPos.y > maxTranslateY || finalPos.y < minTranslateY) { 323 | durationY = options.momentumOutBoundsDuration 324 | timingY = Easing.v2 325 | var beyondDis = containerRect.height / 6 326 | if (finalPos.y > maxTranslateY) { 327 | finalPos.y = maxTranslateY + beyondDis 328 | } else { 329 | finalPos.y = minTranslateY + beyondDis * -1 330 | } 331 | } 332 | } 333 | 334 | moveFromTo(pos, finalPos, Math.max(durationX, durationY), { x: timingX, y: timingY }, function() { 335 | positionCorrection(finalPos) 336 | }) 337 | } 338 | 339 | export function onScroll(callback: ScrollCallback) { 340 | onScrollCallbacks.push(callback) 341 | 342 | return function cancel() { 343 | var idx = onScrollCallbacks.indexOf(callback) 344 | if (idx !== 1) { 345 | onScrollCallbacks.splice(idx, 1) 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { setup, touchstart, touchmove, touchend, onScroll } from './core' 2 | 3 | export default { 4 | setup, 5 | touchstart, 6 | touchmove, 7 | touchend, 8 | onScroll 9 | } 10 | -------------------------------------------------------------------------------- /src/options.ts: -------------------------------------------------------------------------------- 1 | import Utils from './Utils' 2 | 3 | export interface Options { 4 | // 启用X方向滚动 5 | enableScrollX: boolean 6 | // 启用Y方向滚动 7 | enableScrollY: boolean 8 | // 滑动容器选择器 9 | slidingContainerSelector: string 10 | // 阻尼因子,超出边界后添加效果,值越小,阻力越大 11 | damping: number 12 | // 动量减速度 13 | deceleration: number 14 | // 动量动画默认的持续时间 15 | momentumDuration: number 16 | // 加上动量若超出容器后,动量动画持续时间 17 | momentumOutBoundsDuration: number 18 | // 回弹时长 19 | bounceDuration: number 20 | } 21 | 22 | var options: Options = { 23 | enableScrollX: false, 24 | enableScrollY: false, 25 | slidingContainerSelector: '.content', 26 | damping: 0.3, 27 | deceleration: 0.0015, 28 | momentumDuration: 1800, 29 | momentumOutBoundsDuration: 400, 30 | bounceDuration: 800 31 | } 32 | 33 | function setOption(userOptions: Partial, key: K) { 34 | if (!Utils.isUndefined(userOptions[key])) { 35 | options[key] = userOptions[key]! 36 | } 37 | } 38 | 39 | export function resolveOptions(userOptions: Partial) { 40 | setOption(userOptions, 'slidingContainerSelector') 41 | setOption(userOptions, 'damping') 42 | setOption(userOptions, 'bounceDuration') 43 | } 44 | 45 | export default options 46 | -------------------------------------------------------------------------------- /src/wxs.d.ts: -------------------------------------------------------------------------------- 1 | declare interface ComponentDescriptor { 2 | 3 | selectComponent(sel: string): ComponentDescriptor | undefined 4 | 5 | selectAllComponents(sels: string[]): ComponentDescriptor[] 6 | 7 | setStyle(style: WechatMiniprogram.IAnyObject | string): void 8 | 9 | addClass(className: string): void 10 | 11 | removeClass(className: string): void 12 | 13 | hasClass(className: string): void 14 | 15 | requestAnimationFrame(cb: Function): void 16 | 17 | getBoundingClientRect(): WechatMiniprogram.BoundingClientRectCallbackResult 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES3", 4 | "module": "ESNext", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitAny": true, 8 | "strictNullChecks": true, 9 | "noImplicitThis": true, 10 | "skipLibCheck": true, 11 | "baseUrl": ".", 12 | "removeComments": true, 13 | "types": [ 14 | "miniprogram-api-typings" 15 | ] 16 | }, 17 | "include": [ 18 | "src/**/*.ts" 19 | ] 20 | } 21 | --------------------------------------------------------------------------------