@tarojs/plugin-http 3.6.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@tarojs/plugin-http",
3
+ "version": "3.6.0-beta.2",
4
+ "description": "Taro 小程序端支持使用 web 请求 的插件",
5
+ "main": "index.js",
6
+ "files": [
7
+ "src",
8
+ "dist",
9
+ "index.js",
10
+ "package.json"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/NervJS/taro.git"
15
+ },
16
+ "keywords": [
17
+ "Taro"
18
+ ],
19
+ "author": "bigmeow <https://github.com/bigmeow>",
20
+ "license": "MIT",
21
+ "bugs": {
22
+ "url": "https://github.com/NervJS/taro/issues"
23
+ },
24
+ "homepage": "https://github.com/NervJS/taro#readme",
25
+ "dependencies": {
26
+ "@tarojs/runtime": "3.6.0-beta.2",
27
+ "@tarojs/service": "3.6.0-beta.2",
28
+ "@tarojs/shared": "3.6.0-beta.2"
29
+ },
30
+ "devDependencies": {
31
+ "@rollup/plugin-json": "^4.1.0",
32
+ "@rollup/plugin-node-resolve": "^15.0.1",
33
+ "rollup": "^3.8.1",
34
+ "tslib": "^2.4.0"
35
+ },
36
+ "scripts": {
37
+ "dev": "rollup -c -w --bundleConfigAsCjs",
38
+ "build": "rollup -c --bundleConfigAsCjs",
39
+ "test": "jest",
40
+ "test:ci": "jest --ci -i --coverage false"
41
+ }
42
+ }
@@ -0,0 +1,89 @@
1
+ const { delay } = require('./utils')
2
+
3
+ describe('cookie', () => {
4
+ const runtime = require('../../dist/runtime')
5
+
6
+ const cookie = new runtime.Cookie()
7
+ const url1 = 'http://sub.taro.com/p/a/t/h?query=string#hash'
8
+ const url2 = 'http://xxx.sub.taro.com/p/a/t/h?query=string#hash'
9
+ const url3 = 'http://sub2.taro.com/p/a/t/h?query=string#hash'
10
+ const url4 = 'https://sub.taro.com/p/a/t/h?query=string#hash'
11
+
12
+ it('key-value', () => {
13
+ cookie.setCookie('aaa=bbb', url1)
14
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb')
15
+ cookie.setCookie('ccc=ddd\nasdf', url1)
16
+ cookie.setCookie('eee=fff;asdf', url1)
17
+ cookie.setCookie('ggg=;asdf', url1)
18
+ cookie.setCookie('hhh\n=;asdf', url1)
19
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; ccc=ddd; eee=fff; ggg=')
20
+ cookie.setCookie('aaa=abc', url1)
21
+ cookie.setCookie('ccc=cba\nasdf', url1)
22
+ expect(cookie.getCookie(url1)).toBe('aaa=abc; ccc=cba; eee=fff; ggg=')
23
+ })
24
+
25
+ it('maxAge', async () => {
26
+ cookie.setCookie('aaa=bbb; max-age=1', url1)
27
+ await delay(1001)
28
+ expect(cookie.getCookie(url1)).toBe('ccc=cba; eee=fff; ggg=')
29
+ cookie.setCookie('ccc=ddd; max-age=-10', url1)
30
+ expect(cookie.getCookie(url1)).toBe('eee=fff; ggg=')
31
+ cookie.setCookie('eee=fff; max-age=', url1)
32
+ expect(cookie.getCookie(url1)).toBe('eee=fff; ggg=')
33
+ cookie.setCookie('eee=fff; max-age=abc', url1)
34
+ expect(cookie.getCookie(url1)).toBe('eee=fff; ggg=')
35
+ })
36
+
37
+ it('expires', async () => {
38
+ cookie.setCookie(`eee=fff; expires=${new Date(Date.now()).toUTCString()}`, url1)
39
+ expect(cookie.getCookie(url1)).toBe('ggg=')
40
+ cookie.setCookie(`ggg=fff; expires=${new Date(Date.now() + 1000).toUTCString()}`, url1)
41
+ expect(cookie.getCookie(url1)).toBe('ggg=fff')
42
+ await delay(1001)
43
+ expect(cookie.getCookie(url1)).toBe('')
44
+ })
45
+
46
+ it('max-age 优先于 expires', async () => {
47
+ cookie.setCookie(`aaa=bbb; max-age=1000; expires=${new Date(Date.now()).toUTCString()}`, url1)
48
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb')
49
+ cookie.setCookie(`aaa=bbb; max-age=0; expires=${new Date(Date.now() + 1000 * 1000).toUTCString()}`, url1)
50
+ expect(cookie.getCookie(url1)).toBe('')
51
+ })
52
+
53
+ it('domain', async () => {
54
+ cookie.setCookie('aaa=bbb; domain=taro.com', url1)
55
+ cookie.setCookie('abc=cba; domain=sub.taro.com', url1)
56
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba')
57
+ cookie.setCookie('ccc=ddd; domain=xxx.sub.taro.com', url1) // 不符合 domain,不写入 cookie
58
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba')
59
+ cookie.setCookie('eee=fff; domain=xxx.sub.taro.com', url2)
60
+ cookie.setCookie('ggg=; domain=sub2.taro.com', url3)
61
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba')
62
+ expect(cookie.getCookie(url2)).toBe('aaa=bbb; abc=cba; eee=fff')
63
+ expect(cookie.getCookie(url3)).toBe('aaa=bbb; ggg=')
64
+ })
65
+
66
+ it('path', async () => {
67
+ cookie.setCookie('eee=fff; path=/p/a/t/x', url1)
68
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba')
69
+ cookie.setCookie('ggg=hhh; path=/p/a/t', url1)
70
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba; ggg=hhh')
71
+ cookie.setCookie('iii=jjj; path=/p/a/x', url1)
72
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba; ggg=hhh')
73
+ cookie.setCookie('kkk=lll; path=/p/a', url1)
74
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba; ggg=hhh; kkk=lll')
75
+ })
76
+
77
+ it('secure', async () => {
78
+ cookie.setCookie('mmm=nnn; secure', url1)
79
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba; ggg=hhh; kkk=lll')
80
+ expect(cookie.getCookie(url4)).toBe('aaa=bbb; abc=cba; ggg=hhh; kkk=lll; mmm=nnn')
81
+ })
82
+
83
+ it('httpOnly', async () => {
84
+ cookie.setCookie('ooo=ppp; httpOnly', url1)
85
+ expect(cookie.getCookie(url1)).toBe('aaa=bbb; abc=cba; ggg=hhh; kkk=lll; ooo=ppp')
86
+ expect(cookie.getCookie(url4)).toBe('aaa=bbb; abc=cba; ggg=hhh; kkk=lll; mmm=nnn')
87
+ })
88
+
89
+ })
@@ -0,0 +1,20 @@
1
+ describe('DOM', () => {
2
+ process.env.FRAMEWORK = 'nerv'
3
+ const runtime = require('../../dist/runtime')
4
+ const document = runtime.document
5
+ global.document = runtime.document
6
+
7
+ describe('document', () => {
8
+
9
+ it('document setCookie', async () => {
10
+ expect(document.cookie).toBe('')
11
+ document.cookie = 'aaa=1111-2222-33-444-abcdefgasd; path=/; expires=Mon, 18 Jan 2038 19:14:07 GMT; secure;'
12
+ document.cookie = 'bbb=23123-aswe-4a7a-a740-f55dfd296b1d; path=/; expires=Mon, 18 Jan 2038 19:14:07 GMT'
13
+ document.cookie = 'ccc=69asd3d81234668942; path=/; expires=Mon, 18 Jan 2038 19:14:07 GMT'
14
+ expect(document.cookie).toBe(
15
+ 'aaa=1111-2222-33-444-abcdefgasd; bbb=23123-aswe-4a7a-a740-f55dfd296b1d; ccc=69asd3d81234668942'
16
+ )
17
+ })
18
+
19
+ })
20
+ })
@@ -0,0 +1 @@
1
+ process.env.TARO_ENV = 'weapp'
@@ -0,0 +1,8 @@
1
+
2
+ export const delay = (ms = 2) => {
3
+ return new Promise(resolve => {
4
+ setTimeout(() => {
5
+ resolve()
6
+ }, ms)
7
+ })
8
+ }
package/src/index.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { isArray, isString } from '@tarojs/shared'
2
+ import path from 'path'
3
+
4
+ import { name as packageName } from '../package.json'
5
+
6
+ import type { IPluginContext, TaroPlatformBase } from '@tarojs/service'
7
+
8
+ interface IOptions {
9
+ /** 支持 document.cookie 和 http 设置 cookie (默认false) */
10
+ enableCookie?: boolean
11
+ /** 禁用掉 FormData 全局对象 (默认true禁用) */
12
+ disabledFormData?: boolean
13
+ /** 禁用掉 Blob 全局对象 (默认true禁用) */
14
+ disabledBlob?: boolean
15
+ }
16
+
17
+ export default (ctx: IPluginContext, options: IOptions) => {
18
+ if (!['h5', 'rn'].includes(ctx.runOpts.options.platform)) {
19
+ ctx.modifyWebpackChain(({ chain }) => {
20
+ chain.plugin('definePlugin').tap((args) => {
21
+ args[0].ENABLE_COOKIE = options.enableCookie ?? false
22
+ return args
23
+ })
24
+
25
+ const runtimeAlia = `${packageName}/dist/runtime`
26
+ chain.resolve.alias.set(runtimeAlia, path.join(__dirname, 'runtime.js'))
27
+ // 注入相关全局BOM对象
28
+ chain.plugin('providerPlugin').tap((args) => {
29
+ args[0].XMLHttpRequest = [runtimeAlia, 'XMLHttpRequest']
30
+
31
+ // 实际上本runtime 没有实现 FormData 和 Blob 对象, 所以第三方库中的这2个对象会被替换成 undefined
32
+ // (axios这类请求库用到了这2个对象,所以要么实现它要么把它替换掉, 这里我们选择把它替换掉,这样可以确保除了上传以外的功能可以继续使用)
33
+ ;(options.disabledFormData ?? true) && (args[0].FormData ||= [runtimeAlia, 'FormData'])
34
+ ;(options.disabledBlob ?? true) && (args[0].Blob ||= [runtimeAlia, 'Blob'])
35
+
36
+ return args
37
+ })
38
+ })
39
+
40
+ ctx.registerMethod({
41
+ name: 'onSetupClose',
42
+ fn (platform: TaroPlatformBase) {
43
+ const injectedPath = `post:${packageName}/dist/runtime`
44
+ if (isArray(platform.runtimePath)) {
45
+ platform.runtimePath.push(injectedPath)
46
+ } else if (isString(platform.runtimePath)) {
47
+ platform.runtimePath = [platform.runtimePath, injectedPath]
48
+ }
49
+ },
50
+ })
51
+ }
52
+ }
@@ -0,0 +1,312 @@
1
+ /******************************************************************************
2
+ Copyright (c) 2019 wechat-miniprogram.
3
+ Reference and modify code by miniprogram-render/src/bom/cookie.js.
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14
+ PERFORMANCE OF THIS SOFTWARE.
15
+ ***************************************************************************** */
16
+ import { parseUrl } from '@tarojs/runtime'
17
+ import { getStorageSync, setStorage } from '@tarojs/taro'
18
+
19
+ const STORAGE_KEY = 'PAGE_COOKIE'
20
+ export class Cookie {
21
+ #map: any
22
+ constructor () {
23
+ this.#map = {} // 三维数组,domain - path - key
24
+ }
25
+
26
+ static parse (cookieStr: string) {
27
+ if (!cookieStr && typeof cookieStr !== 'string') return null
28
+
29
+ const cookieStrArr = cookieStr.trim().split(';')
30
+
31
+ // key-value
32
+ // eslint-disable-next-line no-control-regex
33
+ const parseKeyValue = /^([^=;\x00-\x1F]+)=([^;\n\r\0\x00-\x1F]*).*/.exec(cookieStrArr.shift()!)
34
+ if (!parseKeyValue) return null
35
+
36
+ const key = (parseKeyValue[1] || '').trim()
37
+ const value = (parseKeyValue[2] || '').trim()
38
+
39
+ // 其他字段
40
+ let path: string | null = null
41
+ let domain: string | null = null
42
+ let expires: number | null = null
43
+ let maxAge: number | null = null
44
+ let secure = false
45
+ let httpOnly = false
46
+
47
+ for (let item of cookieStrArr) {
48
+ item = item.trim()
49
+ if (!item) continue
50
+
51
+ let [key, value] = item.split('=')
52
+ key = (key || '').trim().toLowerCase()
53
+ value = (value || '').trim()
54
+
55
+ if (!key) continue
56
+
57
+ switch (key) {
58
+ case 'path':
59
+ if (value[0] === '/') path = value
60
+ break
61
+ case 'domain':
62
+ value = value.replace(/^\./, '').toLowerCase()
63
+ if (value) domain = value
64
+ break
65
+ case 'expires':
66
+ if (value) {
67
+ const timeStamp = Date.parse(value)
68
+ if (timeStamp) expires = timeStamp
69
+ }
70
+ break
71
+ case 'max-age':
72
+ if (/^-?[0-9]+$/.test(value)) maxAge = +value * 1000
73
+ break
74
+ case 'secure':
75
+ secure = true
76
+ break
77
+ case 'httponly':
78
+ httpOnly = true
79
+ break
80
+ default:
81
+ // ignore
82
+ break
83
+ }
84
+ }
85
+
86
+ return {
87
+ key,
88
+ value,
89
+ path,
90
+ domain,
91
+ expires,
92
+ maxAge,
93
+ secure,
94
+ httpOnly,
95
+ }
96
+ }
97
+
98
+ /**
99
+ * 判断 domain
100
+ */
101
+ $_checkDomain (host, cookieDomain) {
102
+ if (host === cookieDomain) return true
103
+
104
+ const index = host.indexOf(`.${cookieDomain}`)
105
+
106
+ return index > 0 && cookieDomain.length + index + 1 === host.length
107
+ }
108
+
109
+ /**
110
+ * 判断 path
111
+ */
112
+ $_checkPath (path, cookiePath) {
113
+ if (path === cookiePath) return true
114
+
115
+ cookiePath = cookiePath === '/' ? '' : cookiePath
116
+ return path.indexOf(`${cookiePath}/`) === 0
117
+ }
118
+
119
+ /**
120
+ * 判断过期
121
+ */
122
+ $_checkExpires (cookie) {
123
+ const now = Date.now()
124
+
125
+ // maxAge 优先
126
+ if (cookie.maxAge !== null) return cookie.createTime + cookie.maxAge > now
127
+
128
+ // 判断 expires
129
+ if (cookie.expires !== null) return cookie.expires > now
130
+
131
+ return true
132
+ }
133
+
134
+ /**
135
+ * 设置 cookie
136
+ */
137
+ setCookie (cookie, url) {
138
+ cookie = Cookie.parse(cookie)
139
+
140
+ if (!cookie) return
141
+
142
+ const { hostname, port, pathname } = parseUrl(url)
143
+ const host = (hostname || '') + (port ? ':' + port : '') || ''
144
+ const path = (pathname || '')[0] === '/' ? pathname : '/'
145
+
146
+ if (cookie.domain) {
147
+ // 判断 domain
148
+ if (!this.$_checkDomain(host, cookie.domain)) return
149
+ } else {
150
+ // 使用 host 作为默认的 domain
151
+ cookie.domain = host
152
+ }
153
+
154
+ // 需要设置 path 字段的情况,取 url 中除去最后一节的 path
155
+ if (!cookie.path || cookie.path[0] !== '/') {
156
+ const lastIndex = path.lastIndexOf('/')
157
+
158
+ cookie.path = lastIndex === 0 ? path : path.substr(0, lastIndex)
159
+ }
160
+
161
+ // 存入 cookie
162
+ const map = this.#map
163
+ const cookieDomain = cookie.domain
164
+ const cookiePath = cookie.path
165
+ const cookieKey = cookie.key
166
+
167
+ if (!map[cookieDomain]) map[cookieDomain] = {}
168
+ if (!map[cookieDomain][cookiePath]) map[cookieDomain][cookiePath] = {}
169
+
170
+ const oldCookie = map[cookieDomain][cookiePath][cookieKey]
171
+ cookie.createTime = (oldCookie && oldCookie.createTime) || Date.now()
172
+
173
+ if (this.$_checkExpires(cookie)) {
174
+ // 未过期
175
+ map[cookieDomain][cookiePath][cookieKey] = cookie
176
+ } else if (oldCookie) {
177
+ // 存在旧 cookie,且被设置为已过期
178
+ delete map[cookieDomain][cookiePath][cookieKey]
179
+ }
180
+
181
+ // 持久化 cookie
182
+ setStorage &&
183
+ setStorage({
184
+ key: STORAGE_KEY,
185
+ data: this.serialize(),
186
+ })
187
+ }
188
+
189
+ /**
190
+ * 拉取 cookie
191
+ */
192
+ getCookie (url: string, includeHttpOnly = false) {
193
+ const { protocol, hostname, port, pathname } = parseUrl(url)
194
+ const host = (hostname || '') + (port ? ':' + port : '') || ''
195
+ const path = (pathname || '')[0] === '/' ? pathname : '/'
196
+ const res: any[] = []
197
+
198
+ const map = this.#map
199
+ const domainList = Object.keys(map)
200
+
201
+ for (const domainItem of domainList) {
202
+ // 判断 domain
203
+ if (this.$_checkDomain(host, domainItem)) {
204
+ const domainMap = map[domainItem] || {}
205
+ const pathList = Object.keys(domainMap)
206
+
207
+ for (const pathItem of pathList) {
208
+ // 判断 path
209
+ if (this.$_checkPath(path, pathItem)) {
210
+ const pathMap = map[domainItem][pathItem] || {}
211
+
212
+ Object.keys(pathMap).forEach((key) => {
213
+ const cookie: any = pathMap[key]
214
+
215
+ if (!cookie) return
216
+
217
+ // 判断协议
218
+ if (cookie.secure && protocol !== 'https:' && protocol !== 'wss:') return
219
+ if (!includeHttpOnly && cookie.httpOnly && protocol && protocol !== 'http:') return
220
+
221
+ // 判断过期
222
+ if (this.$_checkExpires(cookie)) {
223
+ res.push(cookie)
224
+ } else {
225
+ // 过期,删掉
226
+ delete map[domainItem][pathItem][key]
227
+ }
228
+ })
229
+ }
230
+ }
231
+ }
232
+ }
233
+
234
+ return res
235
+ .sort((a, b) => {
236
+ const gap = a.createTime - b.createTime
237
+
238
+ if (!gap) {
239
+ return a.key < b.key ? -1 : 1
240
+ } else {
241
+ return gap
242
+ }
243
+ })
244
+ .map((cookie) => `${cookie.key}=${cookie.value}`)
245
+ .join('; ')
246
+ }
247
+
248
+ /**
249
+ * 序列化
250
+ */
251
+ serialize () {
252
+ try {
253
+ return JSON.stringify(this.#map)
254
+ } catch (err) {
255
+ // eslint-disable-next-line no-console
256
+ console.log('cannot serialize the cookie')
257
+ return ''
258
+ }
259
+ }
260
+
261
+ /**
262
+ * 反序列化
263
+ */
264
+ deserialize (str) {
265
+ let map = {}
266
+ try {
267
+ map = JSON.parse(str)
268
+ } catch (err) {
269
+ // eslint-disable-next-line no-console
270
+ console.log('cannot deserialize the cookie')
271
+ map = {}
272
+ }
273
+
274
+ // 合并 cookie
275
+ const domainList = Object.keys(map)
276
+
277
+ for (const domainItem of domainList) {
278
+ const domainMap = map[domainItem] || {}
279
+ const pathList = Object.keys(domainMap)
280
+
281
+ for (const pathItem of pathList) {
282
+ const pathMap = map[domainItem][pathItem] || {}
283
+
284
+ Object.keys(pathMap).forEach((key) => {
285
+ const cookie = pathMap[key]
286
+
287
+ if (!cookie) return
288
+
289
+ // 已存在则不覆盖
290
+ if (!this.#map[domainItem]) this.#map[domainItem] = {}
291
+ if (!this.#map[domainItem][pathItem]) this.#map[domainItem][pathItem] = {}
292
+ if (!this.#map[domainItem][pathItem][key]) this.#map[domainItem][pathItem][key] = cookie
293
+ })
294
+ }
295
+ }
296
+ }
297
+ }
298
+
299
+ /**
300
+ * 创建 cookie 实例并反序列化
301
+ * @returns
302
+ */
303
+ export function createCookieInstance () {
304
+ const cookieInstance = new Cookie()
305
+ try {
306
+ const cookie = getStorageSync(STORAGE_KEY)
307
+ if (cookie) cookieInstance.deserialize(cookie)
308
+ } catch (err) {
309
+ // ignore
310
+ }
311
+ return cookieInstance
312
+ }