@scalar/json-magic 0.1.0
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/.turbo/turbo-build.log +9 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/bundle/bundle.d.ts +292 -0
- package/dist/bundle/bundle.d.ts.map +1 -0
- package/dist/bundle/bundle.js +259 -0
- package/dist/bundle/bundle.js.map +7 -0
- package/dist/bundle/create-limiter.d.ts +21 -0
- package/dist/bundle/create-limiter.d.ts.map +1 -0
- package/dist/bundle/create-limiter.js +31 -0
- package/dist/bundle/create-limiter.js.map +7 -0
- package/dist/bundle/index.d.ts +2 -0
- package/dist/bundle/index.d.ts.map +1 -0
- package/dist/bundle/index.js +5 -0
- package/dist/bundle/index.js.map +7 -0
- package/dist/bundle/plugins/browser.d.ts +4 -0
- package/dist/bundle/plugins/browser.d.ts.map +1 -0
- package/dist/bundle/plugins/browser.js +9 -0
- package/dist/bundle/plugins/browser.js.map +7 -0
- package/dist/bundle/plugins/fetch-urls/index.d.ts +39 -0
- package/dist/bundle/plugins/fetch-urls/index.d.ts.map +1 -0
- package/dist/bundle/plugins/fetch-urls/index.js +48 -0
- package/dist/bundle/plugins/fetch-urls/index.js.map +7 -0
- package/dist/bundle/plugins/node.d.ts +5 -0
- package/dist/bundle/plugins/node.d.ts.map +1 -0
- package/dist/bundle/plugins/node.js +11 -0
- package/dist/bundle/plugins/node.js.map +7 -0
- package/dist/bundle/plugins/parse-json/index.d.ts +13 -0
- package/dist/bundle/plugins/parse-json/index.d.ts.map +1 -0
- package/dist/bundle/plugins/parse-json/index.js +22 -0
- package/dist/bundle/plugins/parse-json/index.js.map +7 -0
- package/dist/bundle/plugins/parse-yaml/index.d.ts +13 -0
- package/dist/bundle/plugins/parse-yaml/index.d.ts.map +1 -0
- package/dist/bundle/plugins/parse-yaml/index.js +23 -0
- package/dist/bundle/plugins/parse-yaml/index.js.map +7 -0
- package/dist/bundle/plugins/read-files/index.d.ts +29 -0
- package/dist/bundle/plugins/read-files/index.d.ts.map +1 -0
- package/dist/bundle/plugins/read-files/index.js +30 -0
- package/dist/bundle/plugins/read-files/index.js.map +7 -0
- package/dist/bundle/value-generator.d.ts +79 -0
- package/dist/bundle/value-generator.d.ts.map +1 -0
- package/dist/bundle/value-generator.js +55 -0
- package/dist/bundle/value-generator.js.map +7 -0
- package/dist/dereference/dereference.d.ts +45 -0
- package/dist/dereference/dereference.d.ts.map +1 -0
- package/dist/dereference/dereference.js +37 -0
- package/dist/dereference/dereference.js.map +7 -0
- package/dist/dereference/index.d.ts +2 -0
- package/dist/dereference/index.d.ts.map +1 -0
- package/dist/dereference/index.js +5 -0
- package/dist/dereference/index.js.map +7 -0
- package/dist/diff/apply.d.ts +35 -0
- package/dist/diff/apply.d.ts.map +1 -0
- package/dist/diff/apply.js +40 -0
- package/dist/diff/apply.js.map +7 -0
- package/dist/diff/diff.d.ts +56 -0
- package/dist/diff/diff.d.ts.map +1 -0
- package/dist/diff/diff.js +33 -0
- package/dist/diff/diff.js.map +7 -0
- package/dist/diff/index.d.ts +5 -0
- package/dist/diff/index.d.ts.map +1 -0
- package/dist/diff/index.js +9 -0
- package/dist/diff/index.js.map +7 -0
- package/dist/diff/merge.d.ts +43 -0
- package/dist/diff/merge.d.ts.map +1 -0
- package/dist/diff/merge.js +61 -0
- package/dist/diff/merge.js.map +7 -0
- package/dist/diff/trie.d.ts +64 -0
- package/dist/diff/trie.d.ts.map +1 -0
- package/dist/diff/trie.js +82 -0
- package/dist/diff/trie.js.map +7 -0
- package/dist/diff/utils.d.ts +63 -0
- package/dist/diff/utils.d.ts.map +1 -0
- package/dist/diff/utils.js +48 -0
- package/dist/diff/utils.js.map +7 -0
- package/dist/magic-proxy/index.d.ts +2 -0
- package/dist/magic-proxy/index.d.ts.map +1 -0
- package/dist/magic-proxy/index.js +6 -0
- package/dist/magic-proxy/index.js.map +7 -0
- package/dist/magic-proxy/proxy.d.ts +63 -0
- package/dist/magic-proxy/proxy.d.ts.map +1 -0
- package/dist/magic-proxy/proxy.js +108 -0
- package/dist/magic-proxy/proxy.js.map +7 -0
- package/dist/polyfills/index.d.ts +2 -0
- package/dist/polyfills/index.d.ts.map +1 -0
- package/dist/polyfills/index.js +25 -0
- package/dist/polyfills/index.js.map +7 -0
- package/dist/polyfills/path.d.ts +24 -0
- package/dist/polyfills/path.d.ts.map +1 -0
- package/dist/polyfills/path.js +174 -0
- package/dist/polyfills/path.js.map +7 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/dist/utils/escape-json-pointer.d.ts +7 -0
- package/dist/utils/escape-json-pointer.d.ts.map +1 -0
- package/dist/utils/escape-json-pointer.js +7 -0
- package/dist/utils/escape-json-pointer.js.map +7 -0
- package/dist/utils/get-segments-from-path.d.ts +5 -0
- package/dist/utils/get-segments-from-path.d.ts.map +1 -0
- package/dist/utils/get-segments-from-path.js +11 -0
- package/dist/utils/get-segments-from-path.js.map +7 -0
- package/dist/utils/is-json-object.d.ts +18 -0
- package/dist/utils/is-json-object.d.ts.map +1 -0
- package/dist/utils/is-json-object.js +16 -0
- package/dist/utils/is-json-object.js.map +7 -0
- package/dist/utils/is-object.d.ts +5 -0
- package/dist/utils/is-object.d.ts.map +1 -0
- package/dist/utils/is-object.js +5 -0
- package/dist/utils/is-object.js.map +7 -0
- package/dist/utils/is-yaml.d.ts +17 -0
- package/dist/utils/is-yaml.d.ts.map +1 -0
- package/dist/utils/is-yaml.js +7 -0
- package/dist/utils/is-yaml.js.map +7 -0
- package/dist/utils/json-path-utils.d.ts +23 -0
- package/dist/utils/json-path-utils.d.ts.map +1 -0
- package/dist/utils/json-path-utils.js +16 -0
- package/dist/utils/json-path-utils.js.map +7 -0
- package/dist/utils/normalize.d.ts +5 -0
- package/dist/utils/normalize.d.ts.map +1 -0
- package/dist/utils/normalize.js +28 -0
- package/dist/utils/normalize.js.map +7 -0
- package/dist/utils/unescape-json-pointer.d.ts +8 -0
- package/dist/utils/unescape-json-pointer.d.ts.map +1 -0
- package/dist/utils/unescape-json-pointer.js +7 -0
- package/dist/utils/unescape-json-pointer.js.map +7 -0
- package/esbuild.ts +13 -0
- package/package.json +65 -0
- package/src/bundle/bundle.test.ts +1843 -0
- package/src/bundle/bundle.ts +758 -0
- package/src/bundle/create-limiter.test.ts +28 -0
- package/src/bundle/create-limiter.ts +52 -0
- package/src/bundle/index.ts +2 -0
- package/src/bundle/plugins/browser.ts +4 -0
- package/src/bundle/plugins/fetch-urls/index.test.ts +147 -0
- package/src/bundle/plugins/fetch-urls/index.ts +94 -0
- package/src/bundle/plugins/node.ts +5 -0
- package/src/bundle/plugins/parse-json/index.test.ts +22 -0
- package/src/bundle/plugins/parse-json/index.ts +30 -0
- package/src/bundle/plugins/parse-yaml/index.test.ts +24 -0
- package/src/bundle/plugins/parse-yaml/index.ts +31 -0
- package/src/bundle/plugins/read-files/index.test.ts +35 -0
- package/src/bundle/plugins/read-files/index.ts +55 -0
- package/src/bundle/value-generator.test.ts +166 -0
- package/src/bundle/value-generator.ts +147 -0
- package/src/dereference/dereference.test.ts +137 -0
- package/src/dereference/dereference.ts +84 -0
- package/src/dereference/index.ts +2 -0
- package/src/diff/apply.test.ts +262 -0
- package/src/diff/apply.ts +78 -0
- package/src/diff/diff.test.ts +328 -0
- package/src/diff/diff.ts +94 -0
- package/src/diff/index.test.ts +150 -0
- package/src/diff/index.ts +5 -0
- package/src/diff/merge.test.ts +1109 -0
- package/src/diff/merge.ts +136 -0
- package/src/diff/trie.test.ts +30 -0
- package/src/diff/trie.ts +113 -0
- package/src/diff/utils.test.ts +169 -0
- package/src/diff/utils.ts +113 -0
- package/src/magic-proxy/index.ts +2 -0
- package/src/magic-proxy/proxy.test.ts +145 -0
- package/src/magic-proxy/proxy.ts +225 -0
- package/src/polyfills/index.ts +12 -0
- package/src/polyfills/path.ts +248 -0
- package/src/types.ts +1 -0
- package/src/utils/escape-json-pointer.test.ts +13 -0
- package/src/utils/escape-json-pointer.ts +8 -0
- package/src/utils/get-segments-from-path.test.ts +17 -0
- package/src/utils/get-segments-from-path.ts +17 -0
- package/src/utils/is-json-object.ts +31 -0
- package/src/utils/is-object.test.ts +27 -0
- package/src/utils/is-object.ts +4 -0
- package/src/utils/is-yaml.ts +18 -0
- package/src/utils/json-path-utils.test.ts +13 -0
- package/src/utils/json-path-utils.ts +38 -0
- package/src/utils/normalize.test.ts +91 -0
- package/src/utils/normalize.ts +34 -0
- package/src/utils/unescape-json-pointer.test.ts +23 -0
- package/src/utils/unescape-json-pointer.ts +9 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +16 -0
- package/vite.config.ts +8 -0
|
@@ -0,0 +1,1843 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
3
|
+
import fastify, { type FastifyInstance } from 'fastify'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
5
|
+
import {
|
|
6
|
+
bundle,
|
|
7
|
+
getNestedValue,
|
|
8
|
+
isLocalRef,
|
|
9
|
+
isRemoteUrl,
|
|
10
|
+
prefixInternalRef,
|
|
11
|
+
prefixInternalRefRecursive,
|
|
12
|
+
setValueAtPath,
|
|
13
|
+
} from './bundle'
|
|
14
|
+
import { fetchUrls } from './plugins/fetch-urls'
|
|
15
|
+
import { readFiles } from './plugins/read-files'
|
|
16
|
+
import { setTimeout } from 'node:timers/promises'
|
|
17
|
+
import { parseJson } from '@/bundle/plugins/parse-json'
|
|
18
|
+
import { parseYaml } from '@/bundle/plugins/parse-yaml'
|
|
19
|
+
import YAML from 'yaml'
|
|
20
|
+
import { getHash } from '@/bundle/value-generator'
|
|
21
|
+
|
|
22
|
+
describe('bundle', () => {
|
|
23
|
+
describe('external urls', () => {
|
|
24
|
+
let server: FastifyInstance
|
|
25
|
+
const PORT = 7289
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
server = fastify({ logger: false })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
await server.close()
|
|
33
|
+
await setTimeout(100)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('bundles external urls', async () => {
|
|
37
|
+
const url = `http://localhost:${PORT}`
|
|
38
|
+
|
|
39
|
+
const external = {
|
|
40
|
+
prop: 'I am external json prop',
|
|
41
|
+
}
|
|
42
|
+
server.get('/', (_, reply) => {
|
|
43
|
+
reply.send(external)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
await server.listen({ port: PORT })
|
|
47
|
+
|
|
48
|
+
const input = {
|
|
49
|
+
a: {
|
|
50
|
+
b: {
|
|
51
|
+
c: 'hello',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
d: {
|
|
55
|
+
'$ref': `http://localhost:${PORT}#/prop`,
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await bundle(input, {
|
|
60
|
+
plugins: [fetchUrls(), readFiles()],
|
|
61
|
+
treeShake: false,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
expect(input).toEqual({
|
|
65
|
+
'x-ext': {
|
|
66
|
+
[await getHash(url)]: {
|
|
67
|
+
...external,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
a: {
|
|
71
|
+
b: {
|
|
72
|
+
c: 'hello',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
d: {
|
|
76
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('bundles external urls from resolved external piece', async () => {
|
|
82
|
+
const url = `http://localhost:${PORT}`
|
|
83
|
+
const chunk2 = {
|
|
84
|
+
hey: 'hey',
|
|
85
|
+
nested: {
|
|
86
|
+
key: 'value',
|
|
87
|
+
},
|
|
88
|
+
internal: '#/nested/key',
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const chunk1 = {
|
|
92
|
+
a: {
|
|
93
|
+
hello: 'hello',
|
|
94
|
+
},
|
|
95
|
+
b: {
|
|
96
|
+
'$ref': `${url}/chunk2#`,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
server.get('/chunk1', (_, reply) => {
|
|
101
|
+
reply.send(chunk1)
|
|
102
|
+
})
|
|
103
|
+
server.get('/chunk2', (_, reply) => {
|
|
104
|
+
reply.send(chunk2)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
await server.listen({ port: PORT })
|
|
108
|
+
|
|
109
|
+
const input = {
|
|
110
|
+
a: {
|
|
111
|
+
b: {
|
|
112
|
+
c: {
|
|
113
|
+
'$ref': `${url}/chunk1#`,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
120
|
+
|
|
121
|
+
expect(input).toEqual({
|
|
122
|
+
'x-ext': {
|
|
123
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
124
|
+
...chunk1,
|
|
125
|
+
b: {
|
|
126
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk2`)}`,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
[await getHash(`${url}/chunk2`)]: {
|
|
130
|
+
...chunk2,
|
|
131
|
+
internal: '#/nested/key',
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
a: {
|
|
135
|
+
b: {
|
|
136
|
+
c: {
|
|
137
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should correctly handle only urls without a pointer', async () => {
|
|
145
|
+
const url = `http://localhost:${PORT}`
|
|
146
|
+
|
|
147
|
+
server.get('/', (_, reply) => {
|
|
148
|
+
reply.send({
|
|
149
|
+
a: 'a',
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
await server.listen({ port: PORT })
|
|
154
|
+
|
|
155
|
+
const input = {
|
|
156
|
+
a: {
|
|
157
|
+
b: {
|
|
158
|
+
'$ref': `${url}`,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
164
|
+
|
|
165
|
+
expect(input).toEqual({
|
|
166
|
+
'x-ext': {
|
|
167
|
+
[await getHash(url)]: {
|
|
168
|
+
a: 'a',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
a: {
|
|
172
|
+
b: {
|
|
173
|
+
$ref: `#/x-ext/${await getHash(url)}`,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('caches results for same resource', async () => {
|
|
180
|
+
const fn = vi.fn()
|
|
181
|
+
const url = `http://localhost:${PORT}`
|
|
182
|
+
|
|
183
|
+
server.get('/', (_, reply) => {
|
|
184
|
+
fn()
|
|
185
|
+
reply.send({
|
|
186
|
+
a: 'a',
|
|
187
|
+
b: 'b',
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
await server.listen({ port: PORT })
|
|
192
|
+
|
|
193
|
+
const input = {
|
|
194
|
+
a: {
|
|
195
|
+
'$ref': `${url}#/a`,
|
|
196
|
+
},
|
|
197
|
+
b: {
|
|
198
|
+
'$ref': `${url}#/b`,
|
|
199
|
+
},
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
203
|
+
|
|
204
|
+
expect(input).toEqual({
|
|
205
|
+
'x-ext': {
|
|
206
|
+
[await getHash(url)]: {
|
|
207
|
+
a: 'a',
|
|
208
|
+
b: 'b',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
a: {
|
|
212
|
+
$ref: `#/x-ext/${await getHash(url)}/a`,
|
|
213
|
+
},
|
|
214
|
+
b: {
|
|
215
|
+
$ref: `#/x-ext/${await getHash(url)}/b`,
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
// We expect the bundler to cache the result for the same url
|
|
220
|
+
expect(fn.mock.calls.length).toBe(1)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('handles correctly external nested refs', async () => {
|
|
224
|
+
const url = `http://localhost:${PORT}`
|
|
225
|
+
|
|
226
|
+
server.get('/nested/another-file.json', (_, reply) => {
|
|
227
|
+
reply.send({
|
|
228
|
+
c: 'c',
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
server.get('/nested/chunk1.json', (_, reply) => {
|
|
233
|
+
reply.send({
|
|
234
|
+
b: {
|
|
235
|
+
'$ref': './another-file.json#',
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await server.listen({ port: PORT })
|
|
241
|
+
|
|
242
|
+
const input = {
|
|
243
|
+
a: {
|
|
244
|
+
'$ref': `${url}/nested/chunk1.json#`,
|
|
245
|
+
},
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
249
|
+
|
|
250
|
+
expect(input).toEqual({
|
|
251
|
+
'x-ext': {
|
|
252
|
+
[await getHash(`${url}/nested/another-file.json`)]: {
|
|
253
|
+
c: 'c',
|
|
254
|
+
},
|
|
255
|
+
[await getHash(`${url}/nested/chunk1.json`)]: {
|
|
256
|
+
b: {
|
|
257
|
+
$ref: `#/x-ext/${await getHash(`${url}/nested/another-file.json`)}`,
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
a: {
|
|
262
|
+
$ref: `#/x-ext/${await getHash(`${url}/nested/chunk1.json`)}`,
|
|
263
|
+
},
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('does not merge paths when we use absolute urls', async () => {
|
|
268
|
+
const url = `http://localhost:${PORT}`
|
|
269
|
+
|
|
270
|
+
server.get('/top-level', (_, reply) => {
|
|
271
|
+
reply.send({
|
|
272
|
+
c: 'c',
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
server.get('/nested/chunk1.json', (_, reply) => {
|
|
277
|
+
reply.send({
|
|
278
|
+
b: {
|
|
279
|
+
'$ref': `${url}/top-level#`,
|
|
280
|
+
},
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
await server.listen({ port: PORT })
|
|
285
|
+
|
|
286
|
+
const input = {
|
|
287
|
+
a: {
|
|
288
|
+
'$ref': `${url}/nested/chunk1.json`,
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
293
|
+
|
|
294
|
+
expect(input).toEqual({
|
|
295
|
+
'x-ext': {
|
|
296
|
+
[await getHash(`${url}/top-level`)]: {
|
|
297
|
+
c: 'c',
|
|
298
|
+
},
|
|
299
|
+
[await getHash(`${url}/nested/chunk1.json`)]: {
|
|
300
|
+
b: {
|
|
301
|
+
$ref: `#/x-ext/${await getHash(`${url}/top-level`)}`,
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
a: {
|
|
306
|
+
$ref: `#/x-ext/${await getHash(`${url}/nested/chunk1.json`)}`,
|
|
307
|
+
},
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('bundles from a url input', async () => {
|
|
312
|
+
const url = `http://localhost:${PORT}`
|
|
313
|
+
|
|
314
|
+
server.get('/top-level', (_, reply) => {
|
|
315
|
+
reply.send({
|
|
316
|
+
c: 'c',
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
server.get('/nested/chunk1.json', (_, reply) => {
|
|
321
|
+
reply.send({
|
|
322
|
+
b: {
|
|
323
|
+
'$ref': `${url}/top-level#`,
|
|
324
|
+
},
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
server.get('/base/openapi.json', (_, reply) => {
|
|
329
|
+
reply.send({
|
|
330
|
+
a: {
|
|
331
|
+
$ref: '../nested/chunk1.json',
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
await server.listen({ port: PORT })
|
|
337
|
+
|
|
338
|
+
const output = await bundle(`${url}/base/openapi.json`, { plugins: [fetchUrls()], treeShake: false })
|
|
339
|
+
|
|
340
|
+
expect(output).toEqual({
|
|
341
|
+
'x-ext': {
|
|
342
|
+
[await getHash(`${url}/top-level`)]: {
|
|
343
|
+
c: 'c',
|
|
344
|
+
},
|
|
345
|
+
[await getHash(`${url}/nested/chunk1.json`)]: {
|
|
346
|
+
b: {
|
|
347
|
+
$ref: `#/x-ext/${await getHash(`${url}/top-level`)}`,
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
a: {
|
|
352
|
+
$ref: `#/x-ext/${await getHash(`${url}/nested/chunk1.json`)}`,
|
|
353
|
+
},
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
it('generated a map when we turn the urlMap on', async () => {
|
|
358
|
+
const url = `http://localhost:${PORT}`
|
|
359
|
+
|
|
360
|
+
server.get('/top-level', (_, reply) => {
|
|
361
|
+
reply.send({
|
|
362
|
+
c: 'c',
|
|
363
|
+
})
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
server.get('/nested/chunk1.json', (_, reply) => {
|
|
367
|
+
reply.send({
|
|
368
|
+
b: {
|
|
369
|
+
'$ref': `${url}/top-level#`,
|
|
370
|
+
},
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
server.get('/base/openapi.json', (_, reply) => {
|
|
375
|
+
reply.send({
|
|
376
|
+
a: {
|
|
377
|
+
$ref: '../nested/chunk1.json',
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
await server.listen({ port: PORT })
|
|
383
|
+
|
|
384
|
+
const output = await bundle(`${url}/base/openapi.json`, {
|
|
385
|
+
plugins: [fetchUrls()],
|
|
386
|
+
treeShake: false,
|
|
387
|
+
urlMap: true,
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
expect(output).toEqual({
|
|
391
|
+
'x-ext': {
|
|
392
|
+
[await getHash(`${url}/top-level`)]: {
|
|
393
|
+
c: 'c',
|
|
394
|
+
},
|
|
395
|
+
[await getHash(`${url}/nested/chunk1.json`)]: {
|
|
396
|
+
b: {
|
|
397
|
+
$ref: `#/x-ext/${await getHash(`${url}/top-level`)}`,
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
'x-ext-urls': {
|
|
402
|
+
[await getHash(`${url}/top-level`)]: `${url}/top-level`,
|
|
403
|
+
[await getHash(`${url}/nested/chunk1.json`)]: `${url}/nested/chunk1.json`,
|
|
404
|
+
},
|
|
405
|
+
a: {
|
|
406
|
+
$ref: `#/x-ext/${await getHash(`${url}/nested/chunk1.json`)}`,
|
|
407
|
+
},
|
|
408
|
+
})
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
it('prefixes the refs only once', async () => {
|
|
412
|
+
const url = `http://localhost:${PORT}`
|
|
413
|
+
|
|
414
|
+
const chunk2 = {
|
|
415
|
+
a: 'a',
|
|
416
|
+
b: {
|
|
417
|
+
'$ref': `${url}/chunk1#`,
|
|
418
|
+
},
|
|
419
|
+
}
|
|
420
|
+
const chunk1 = {
|
|
421
|
+
a: {
|
|
422
|
+
hello: 'hello',
|
|
423
|
+
},
|
|
424
|
+
b: {
|
|
425
|
+
'$ref': `${url}/chunk2#`,
|
|
426
|
+
},
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
server.get('/chunk1', (_, reply) => {
|
|
430
|
+
reply.send(chunk1)
|
|
431
|
+
})
|
|
432
|
+
server.get('/chunk2', (_, reply) => {
|
|
433
|
+
reply.send(chunk2)
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
await server.listen({ port: PORT })
|
|
437
|
+
|
|
438
|
+
const input = {
|
|
439
|
+
a: {
|
|
440
|
+
b: {
|
|
441
|
+
c: {
|
|
442
|
+
'$ref': `${url}/chunk1#`,
|
|
443
|
+
},
|
|
444
|
+
d: {
|
|
445
|
+
e: {
|
|
446
|
+
f: {
|
|
447
|
+
g: {
|
|
448
|
+
'$ref': `${url}/chunk1#`,
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
await bundle(input, { plugins: [fetchUrls()], treeShake: false })
|
|
458
|
+
|
|
459
|
+
expect(input).toEqual({
|
|
460
|
+
a: {
|
|
461
|
+
b: {
|
|
462
|
+
c: {
|
|
463
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
464
|
+
},
|
|
465
|
+
d: {
|
|
466
|
+
e: {
|
|
467
|
+
f: {
|
|
468
|
+
g: {
|
|
469
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
'x-ext': {
|
|
477
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
478
|
+
a: {
|
|
479
|
+
hello: 'hello',
|
|
480
|
+
},
|
|
481
|
+
b: {
|
|
482
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk2`)}`,
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
[await getHash(`${url}/chunk2`)]: {
|
|
486
|
+
a: 'a',
|
|
487
|
+
b: {
|
|
488
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
})
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
it('bundles array references', async () => {
|
|
496
|
+
const url = `http://localhost:${PORT}`
|
|
497
|
+
|
|
498
|
+
const chunk1 = {
|
|
499
|
+
a: {
|
|
500
|
+
hello: 'hello',
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
server.get('/chunk1', (_, reply) => {
|
|
505
|
+
reply.send(chunk1)
|
|
506
|
+
})
|
|
507
|
+
|
|
508
|
+
await server.listen({ port: PORT })
|
|
509
|
+
|
|
510
|
+
const input = {
|
|
511
|
+
a: [
|
|
512
|
+
{
|
|
513
|
+
$ref: `${url}/chunk1#`,
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
}
|
|
517
|
+
await bundle(input, {
|
|
518
|
+
plugins: [fetchUrls()],
|
|
519
|
+
treeShake: false,
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
expect(input).toEqual({
|
|
523
|
+
a: [
|
|
524
|
+
{
|
|
525
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
'x-ext': {
|
|
529
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
530
|
+
a: {
|
|
531
|
+
hello: 'hello',
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
})
|
|
536
|
+
})
|
|
537
|
+
|
|
538
|
+
it('bundles subpart of the document', async () => {
|
|
539
|
+
const url = `http://localhost:${PORT}`
|
|
540
|
+
|
|
541
|
+
const chunk1 = {
|
|
542
|
+
a: {
|
|
543
|
+
hello: 'hello',
|
|
544
|
+
},
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const fn = vi.fn()
|
|
548
|
+
|
|
549
|
+
server.get('/chunk1', (_, reply) => {
|
|
550
|
+
fn()
|
|
551
|
+
reply.send(chunk1)
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
await server.listen({ port: PORT })
|
|
555
|
+
|
|
556
|
+
const input = {
|
|
557
|
+
a: {
|
|
558
|
+
$ref: `${url}/chunk1#`,
|
|
559
|
+
},
|
|
560
|
+
b: {
|
|
561
|
+
$ref: `${url}/chunk1#`,
|
|
562
|
+
},
|
|
563
|
+
c: {
|
|
564
|
+
$ref: `${url}/chunk1#`,
|
|
565
|
+
},
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const cache = new Map()
|
|
569
|
+
|
|
570
|
+
// Bundle only partial
|
|
571
|
+
await bundle(input.b, {
|
|
572
|
+
plugins: [fetchUrls()],
|
|
573
|
+
treeShake: false,
|
|
574
|
+
root: input,
|
|
575
|
+
cache,
|
|
576
|
+
})
|
|
577
|
+
|
|
578
|
+
expect(input).toEqual({
|
|
579
|
+
a: {
|
|
580
|
+
$ref: `${url}/chunk1#`,
|
|
581
|
+
},
|
|
582
|
+
b: {
|
|
583
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
584
|
+
},
|
|
585
|
+
c: {
|
|
586
|
+
$ref: `${url}/chunk1#`,
|
|
587
|
+
},
|
|
588
|
+
'x-ext': {
|
|
589
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
590
|
+
a: {
|
|
591
|
+
hello: 'hello',
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
'x-ext-urls': {
|
|
596
|
+
[await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
|
|
597
|
+
},
|
|
598
|
+
})
|
|
599
|
+
|
|
600
|
+
// Bundle only partial
|
|
601
|
+
await bundle(input.c, {
|
|
602
|
+
plugins: [fetchUrls()],
|
|
603
|
+
treeShake: false,
|
|
604
|
+
root: input,
|
|
605
|
+
cache,
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
expect(input).toEqual({
|
|
609
|
+
a: {
|
|
610
|
+
$ref: `${url}/chunk1#`,
|
|
611
|
+
},
|
|
612
|
+
b: {
|
|
613
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
614
|
+
},
|
|
615
|
+
c: {
|
|
616
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
617
|
+
},
|
|
618
|
+
'x-ext': {
|
|
619
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
620
|
+
a: {
|
|
621
|
+
hello: 'hello',
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
'x-ext-urls': {
|
|
626
|
+
[await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
|
|
627
|
+
},
|
|
628
|
+
})
|
|
629
|
+
|
|
630
|
+
expect(fn).toHaveBeenCalledTimes(1)
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
it('always emits the url mappings when doing partial bundle', async () => {
|
|
634
|
+
const url = `http://localhost:${PORT}`
|
|
635
|
+
|
|
636
|
+
const chunk1 = {
|
|
637
|
+
a: {
|
|
638
|
+
hello: 'hello',
|
|
639
|
+
},
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
server.get('/chunk1', (_, reply) => {
|
|
643
|
+
reply.send(chunk1)
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
await server.listen({ port: PORT })
|
|
647
|
+
|
|
648
|
+
const input = {
|
|
649
|
+
a: {
|
|
650
|
+
$ref: `${url}/chunk1#`,
|
|
651
|
+
},
|
|
652
|
+
b: {
|
|
653
|
+
$ref: `${url}/chunk1#`,
|
|
654
|
+
},
|
|
655
|
+
c: {
|
|
656
|
+
$ref: `${url}/chunk1#`,
|
|
657
|
+
},
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const cache = new Map()
|
|
661
|
+
|
|
662
|
+
// Bundle only partial
|
|
663
|
+
await bundle(input.b, {
|
|
664
|
+
plugins: [fetchUrls()],
|
|
665
|
+
treeShake: false,
|
|
666
|
+
root: input,
|
|
667
|
+
cache,
|
|
668
|
+
urlMap: false, // Set the urlMapping to false
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
expect(input).toEqual({
|
|
672
|
+
a: {
|
|
673
|
+
$ref: `${url}/chunk1#`,
|
|
674
|
+
},
|
|
675
|
+
b: {
|
|
676
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
677
|
+
},
|
|
678
|
+
c: {
|
|
679
|
+
$ref: `${url}/chunk1#`,
|
|
680
|
+
},
|
|
681
|
+
'x-ext': {
|
|
682
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
683
|
+
a: {
|
|
684
|
+
hello: 'hello',
|
|
685
|
+
},
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
// It should still inject the mappings on the output document
|
|
689
|
+
'x-ext-urls': {
|
|
690
|
+
[await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
|
|
691
|
+
},
|
|
692
|
+
})
|
|
693
|
+
})
|
|
694
|
+
|
|
695
|
+
it('tree shakes the external documents correctly', async () => {
|
|
696
|
+
const url = `http://localhost:${PORT}`
|
|
697
|
+
|
|
698
|
+
const chunk1 = {
|
|
699
|
+
a: {
|
|
700
|
+
b: {
|
|
701
|
+
hello: 'hello',
|
|
702
|
+
g: {
|
|
703
|
+
$ref: '#/d/e',
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
c: 'c',
|
|
707
|
+
},
|
|
708
|
+
d: {
|
|
709
|
+
e: { message: 'I should be included' },
|
|
710
|
+
f: { message: 'I should be excluded on the final bundle' },
|
|
711
|
+
},
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
server.get('/chunk1', (_, reply) => {
|
|
715
|
+
reply.send(chunk1)
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
await server.listen({ port: PORT })
|
|
719
|
+
|
|
720
|
+
const input = {
|
|
721
|
+
a: {
|
|
722
|
+
$ref: `${url}/chunk1#/a/b`,
|
|
723
|
+
},
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
await bundle(input, { plugins: [fetchUrls()], treeShake: true })
|
|
727
|
+
|
|
728
|
+
expect(input).toEqual({
|
|
729
|
+
a: {
|
|
730
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/a/b`,
|
|
731
|
+
},
|
|
732
|
+
'x-ext': {
|
|
733
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
734
|
+
a: {
|
|
735
|
+
b: {
|
|
736
|
+
g: {
|
|
737
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/d/e`,
|
|
738
|
+
},
|
|
739
|
+
hello: 'hello',
|
|
740
|
+
},
|
|
741
|
+
},
|
|
742
|
+
d: {
|
|
743
|
+
e: { message: 'I should be included' },
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
it('tree shakes correctly when working with nested external refs', async () => {
|
|
751
|
+
const url = `http://localhost:${PORT}`
|
|
752
|
+
|
|
753
|
+
const chunk2 = {
|
|
754
|
+
a: {
|
|
755
|
+
b: {
|
|
756
|
+
hello: 'hello',
|
|
757
|
+
},
|
|
758
|
+
hi: 'hi',
|
|
759
|
+
},
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const chunk1 = {
|
|
763
|
+
a: {
|
|
764
|
+
b: {
|
|
765
|
+
hello: 'hello',
|
|
766
|
+
g: {
|
|
767
|
+
$ref: '#/d/e',
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
c: 'c',
|
|
771
|
+
external: {
|
|
772
|
+
$ref: './chunk2#/a/b',
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
d: {
|
|
776
|
+
e: { message: 'I should be included' },
|
|
777
|
+
f: { message: 'I should be excluded on the final bundle' },
|
|
778
|
+
},
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
server.get('/chunk1', (_, reply) => {
|
|
782
|
+
reply.send(chunk1)
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
server.get('/chunk2', (_, reply) => {
|
|
786
|
+
reply.send(chunk2)
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
await server.listen({ port: PORT })
|
|
790
|
+
|
|
791
|
+
const input = {
|
|
792
|
+
a: {
|
|
793
|
+
$ref: `${url}/chunk1#/a`,
|
|
794
|
+
},
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
await bundle(input, { plugins: [fetchUrls()], treeShake: true })
|
|
798
|
+
|
|
799
|
+
expect(input).toEqual({
|
|
800
|
+
a: {
|
|
801
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/a`,
|
|
802
|
+
},
|
|
803
|
+
'x-ext': {
|
|
804
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
805
|
+
a: {
|
|
806
|
+
b: {
|
|
807
|
+
g: {
|
|
808
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/d/e`,
|
|
809
|
+
},
|
|
810
|
+
hello: 'hello',
|
|
811
|
+
},
|
|
812
|
+
c: 'c',
|
|
813
|
+
'external': {
|
|
814
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk2`)}/a/b`,
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
d: {
|
|
818
|
+
e: {
|
|
819
|
+
'message': 'I should be included',
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
[await getHash(`${url}/chunk2`)]: {
|
|
824
|
+
a: {
|
|
825
|
+
b: {
|
|
826
|
+
hello: 'hello',
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
},
|
|
831
|
+
})
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
it('handles circular references when we treeshake', async () => {
|
|
835
|
+
const url = `http://localhost:${PORT}`
|
|
836
|
+
|
|
837
|
+
const chunk1 = {
|
|
838
|
+
a: {
|
|
839
|
+
b: {
|
|
840
|
+
hello: 'hello',
|
|
841
|
+
g: {
|
|
842
|
+
$ref: '#/a/external',
|
|
843
|
+
},
|
|
844
|
+
},
|
|
845
|
+
c: 'c',
|
|
846
|
+
external: {
|
|
847
|
+
$ref: '#/a/b',
|
|
848
|
+
},
|
|
849
|
+
},
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
server.get('/chunk1', (_, reply) => {
|
|
853
|
+
reply.send(chunk1)
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
await server.listen({ port: PORT })
|
|
857
|
+
|
|
858
|
+
const input = {
|
|
859
|
+
a: {
|
|
860
|
+
$ref: `${url}/chunk1#/a`,
|
|
861
|
+
},
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
await bundle(input, { plugins: [fetchUrls()], treeShake: true })
|
|
865
|
+
|
|
866
|
+
expect(input).toEqual({
|
|
867
|
+
a: {
|
|
868
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/a`,
|
|
869
|
+
},
|
|
870
|
+
'x-ext': {
|
|
871
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
872
|
+
a: {
|
|
873
|
+
b: {
|
|
874
|
+
g: {
|
|
875
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/a/external`,
|
|
876
|
+
},
|
|
877
|
+
hello: 'hello',
|
|
878
|
+
},
|
|
879
|
+
c: 'c',
|
|
880
|
+
external: {
|
|
881
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}/a/b`,
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
})
|
|
887
|
+
})
|
|
888
|
+
|
|
889
|
+
it('handles chunks', async () => {
|
|
890
|
+
const url = `http://localhost:${PORT}`
|
|
891
|
+
|
|
892
|
+
const chunk1 = {
|
|
893
|
+
description: 'Chunk 1',
|
|
894
|
+
someRef: {
|
|
895
|
+
$ref: '#/components/User',
|
|
896
|
+
},
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const chunk2 = {
|
|
900
|
+
description: 'Chunk 2',
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
server.get('/chunk1', (_, reply) => {
|
|
904
|
+
reply.send(chunk1)
|
|
905
|
+
})
|
|
906
|
+
server.get('/chunk2', (_, reply) => {
|
|
907
|
+
reply.send(chunk2)
|
|
908
|
+
})
|
|
909
|
+
|
|
910
|
+
await server.listen({ port: PORT })
|
|
911
|
+
|
|
912
|
+
const input = {
|
|
913
|
+
a: {
|
|
914
|
+
$ref: `${url}/chunk1#`,
|
|
915
|
+
$global: true,
|
|
916
|
+
},
|
|
917
|
+
b: {
|
|
918
|
+
$ref: `${url}/chunk2#`,
|
|
919
|
+
$global: true,
|
|
920
|
+
},
|
|
921
|
+
c: {
|
|
922
|
+
$ref: `${url}/chunk1#`,
|
|
923
|
+
$global: true,
|
|
924
|
+
},
|
|
925
|
+
components: {
|
|
926
|
+
User: {
|
|
927
|
+
id: 'number',
|
|
928
|
+
name: {
|
|
929
|
+
$ref: '#/a',
|
|
930
|
+
},
|
|
931
|
+
another: {
|
|
932
|
+
$ref: '#/b',
|
|
933
|
+
},
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// Bundle only partial
|
|
939
|
+
await bundle(input.a, {
|
|
940
|
+
plugins: [fetchUrls()],
|
|
941
|
+
treeShake: false,
|
|
942
|
+
root: input,
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
expect(input).toEqual({
|
|
946
|
+
a: {
|
|
947
|
+
$global: true,
|
|
948
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
949
|
+
},
|
|
950
|
+
b: {
|
|
951
|
+
$global: true,
|
|
952
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk2`)}`,
|
|
953
|
+
},
|
|
954
|
+
c: {
|
|
955
|
+
$global: true,
|
|
956
|
+
$ref: `${url}/chunk1#`,
|
|
957
|
+
},
|
|
958
|
+
components: {
|
|
959
|
+
User: {
|
|
960
|
+
another: {
|
|
961
|
+
$ref: '#/b',
|
|
962
|
+
},
|
|
963
|
+
id: 'number',
|
|
964
|
+
name: {
|
|
965
|
+
$ref: '#/a',
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
},
|
|
969
|
+
'x-ext': {
|
|
970
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
971
|
+
description: 'Chunk 1',
|
|
972
|
+
someRef: {
|
|
973
|
+
$ref: '#/components/User',
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
[await getHash(`${url}/chunk2`)]: {
|
|
977
|
+
description: 'Chunk 2',
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
'x-ext-urls': {
|
|
981
|
+
[await getHash(`${url}/chunk2`)]: `${url}/chunk2`,
|
|
982
|
+
[await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
|
|
983
|
+
},
|
|
984
|
+
})
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
it('when bundle partial document we ensure all the dependencies references are resolved', async () => {
|
|
988
|
+
const url = `http://localhost:${PORT}`
|
|
989
|
+
|
|
990
|
+
const chunk1 = {
|
|
991
|
+
a: {
|
|
992
|
+
hello: 'hello',
|
|
993
|
+
},
|
|
994
|
+
}
|
|
995
|
+
server.get('/chunk1', (_, reply) => {
|
|
996
|
+
reply.send(chunk1)
|
|
997
|
+
})
|
|
998
|
+
|
|
999
|
+
await server.listen({ port: PORT })
|
|
1000
|
+
|
|
1001
|
+
const input = {
|
|
1002
|
+
a: {
|
|
1003
|
+
$ref: `${url}/chunk1#`,
|
|
1004
|
+
},
|
|
1005
|
+
b: {
|
|
1006
|
+
a: 'a',
|
|
1007
|
+
someReference: {
|
|
1008
|
+
$ref: '#/a',
|
|
1009
|
+
},
|
|
1010
|
+
},
|
|
1011
|
+
c: {
|
|
1012
|
+
$ref: `${url}/chunk2#`,
|
|
1013
|
+
},
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const cache = new Map()
|
|
1017
|
+
|
|
1018
|
+
// Bundle only partial
|
|
1019
|
+
await bundle(input.b, {
|
|
1020
|
+
plugins: [fetchUrls()],
|
|
1021
|
+
treeShake: false,
|
|
1022
|
+
root: input,
|
|
1023
|
+
cache,
|
|
1024
|
+
})
|
|
1025
|
+
|
|
1026
|
+
expect(input).toEqual({
|
|
1027
|
+
a: {
|
|
1028
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
1029
|
+
},
|
|
1030
|
+
b: {
|
|
1031
|
+
a: 'a',
|
|
1032
|
+
someReference: {
|
|
1033
|
+
$ref: '#/a',
|
|
1034
|
+
},
|
|
1035
|
+
},
|
|
1036
|
+
c: {
|
|
1037
|
+
$ref: `${url}/chunk2#`,
|
|
1038
|
+
},
|
|
1039
|
+
'x-ext': {
|
|
1040
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
1041
|
+
a: {
|
|
1042
|
+
hello: 'hello',
|
|
1043
|
+
},
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
'x-ext-urls': {
|
|
1047
|
+
[await getHash(`${url}/chunk1`)]: 'http://localhost:7289/chunk1',
|
|
1048
|
+
},
|
|
1049
|
+
})
|
|
1050
|
+
})
|
|
1051
|
+
|
|
1052
|
+
it('should correctly handle nested chunk urls', async () => {
|
|
1053
|
+
const url = `http://localhost:${PORT}`
|
|
1054
|
+
|
|
1055
|
+
const chunk1 = {
|
|
1056
|
+
chunk1: 'chunk1',
|
|
1057
|
+
someRef: {
|
|
1058
|
+
$ref: '#/b',
|
|
1059
|
+
},
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const chunk2 = {
|
|
1063
|
+
chunk2: 'chunk2',
|
|
1064
|
+
someRef: {
|
|
1065
|
+
$ref: '#/c',
|
|
1066
|
+
},
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
const chunk3 = {
|
|
1070
|
+
chunk3: 'chunk3',
|
|
1071
|
+
}
|
|
1072
|
+
const external = {
|
|
1073
|
+
external: 'external',
|
|
1074
|
+
someChunk: {
|
|
1075
|
+
$ref: '/chunk3',
|
|
1076
|
+
$global: true,
|
|
1077
|
+
},
|
|
1078
|
+
}
|
|
1079
|
+
server.get('/chunk1', (_, reply) => {
|
|
1080
|
+
reply.send(chunk1)
|
|
1081
|
+
})
|
|
1082
|
+
server.get('/chunk2', (_, reply) => {
|
|
1083
|
+
reply.send(chunk2)
|
|
1084
|
+
})
|
|
1085
|
+
server.get('/external/chunk3', (_, reply) => {
|
|
1086
|
+
reply.send(chunk3)
|
|
1087
|
+
})
|
|
1088
|
+
server.get('/chunk3', (_, reply) => {
|
|
1089
|
+
reply.send(chunk3)
|
|
1090
|
+
})
|
|
1091
|
+
server.get('/external/document.json', (_, reply) => {
|
|
1092
|
+
reply.send(external)
|
|
1093
|
+
})
|
|
1094
|
+
|
|
1095
|
+
await server.listen({ port: PORT })
|
|
1096
|
+
|
|
1097
|
+
const input = {
|
|
1098
|
+
c: {
|
|
1099
|
+
$ref: `${url}/external/document.json`,
|
|
1100
|
+
},
|
|
1101
|
+
b: {
|
|
1102
|
+
$ref: `${url}/chunk2#`,
|
|
1103
|
+
$global: true,
|
|
1104
|
+
},
|
|
1105
|
+
a: {
|
|
1106
|
+
$ref: `${url}/chunk1#`,
|
|
1107
|
+
$global: true,
|
|
1108
|
+
},
|
|
1109
|
+
entry: {
|
|
1110
|
+
$ref: '#/a',
|
|
1111
|
+
},
|
|
1112
|
+
nonBundle: {
|
|
1113
|
+
$ref: `${url}/chunk1#`,
|
|
1114
|
+
},
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const cache = new Map()
|
|
1118
|
+
|
|
1119
|
+
// Bundle only partial
|
|
1120
|
+
await bundle(input.entry, {
|
|
1121
|
+
plugins: [fetchUrls()],
|
|
1122
|
+
treeShake: false,
|
|
1123
|
+
root: input,
|
|
1124
|
+
cache,
|
|
1125
|
+
urlMap: true,
|
|
1126
|
+
})
|
|
1127
|
+
|
|
1128
|
+
expect(input).toEqual({
|
|
1129
|
+
a: {
|
|
1130
|
+
$global: true,
|
|
1131
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk1`)}`,
|
|
1132
|
+
},
|
|
1133
|
+
b: {
|
|
1134
|
+
$global: true,
|
|
1135
|
+
$ref: `#/x-ext/${await getHash(`${url}/chunk2`)}`,
|
|
1136
|
+
},
|
|
1137
|
+
c: {
|
|
1138
|
+
$ref: `#/x-ext/${await getHash(`${url}/external/document.json`)}`,
|
|
1139
|
+
},
|
|
1140
|
+
|
|
1141
|
+
entry: {
|
|
1142
|
+
$ref: '#/a',
|
|
1143
|
+
},
|
|
1144
|
+
nonBundle: {
|
|
1145
|
+
$ref: `http://localhost:${PORT}/chunk1#`,
|
|
1146
|
+
},
|
|
1147
|
+
'x-ext': {
|
|
1148
|
+
[await getHash(`${url}/external/document.json`)]: {
|
|
1149
|
+
external: 'external',
|
|
1150
|
+
someChunk: {
|
|
1151
|
+
$ref: `#/x-ext/${await getHash(`${url}/external/chunk3`)}`,
|
|
1152
|
+
$global: true,
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
[await getHash(`${url}/chunk1`)]: {
|
|
1156
|
+
chunk1: 'chunk1',
|
|
1157
|
+
someRef: {
|
|
1158
|
+
$ref: '#/b',
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
[await getHash(`${url}/chunk2`)]: {
|
|
1162
|
+
chunk2: 'chunk2',
|
|
1163
|
+
someRef: {
|
|
1164
|
+
$ref: '#/c',
|
|
1165
|
+
},
|
|
1166
|
+
},
|
|
1167
|
+
[await getHash(`${url}/external/chunk3`)]: {
|
|
1168
|
+
chunk3: 'chunk3',
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
'x-ext-urls': {
|
|
1172
|
+
[await getHash(`${url}/chunk1`)]: `${url}/chunk1`,
|
|
1173
|
+
[await getHash(`${url}/chunk2`)]: `${url}/chunk2`,
|
|
1174
|
+
[await getHash(`${url}/external/chunk3`)]: `${url}/external/chunk3`,
|
|
1175
|
+
[await getHash(`${url}/external/document.json`)]: `${url}/external/document.json`,
|
|
1176
|
+
},
|
|
1177
|
+
})
|
|
1178
|
+
})
|
|
1179
|
+
|
|
1180
|
+
it('run success hook', async () => {
|
|
1181
|
+
const url = `http://localhost:${PORT}`
|
|
1182
|
+
|
|
1183
|
+
const chunk1 = {
|
|
1184
|
+
description: 'Chunk 1',
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
server.get('/chunk1', (_, reply) => {
|
|
1188
|
+
reply.send(chunk1)
|
|
1189
|
+
})
|
|
1190
|
+
|
|
1191
|
+
await server.listen({ port: PORT })
|
|
1192
|
+
|
|
1193
|
+
const input = {
|
|
1194
|
+
a: {
|
|
1195
|
+
$ref: `${url}/chunk1#`,
|
|
1196
|
+
},
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
const resolveStart = vi.fn()
|
|
1200
|
+
const resolveError = vi.fn()
|
|
1201
|
+
const resolveSuccess = vi.fn()
|
|
1202
|
+
|
|
1203
|
+
const refA = input.a
|
|
1204
|
+
|
|
1205
|
+
await bundle(input, {
|
|
1206
|
+
plugins: [fetchUrls()],
|
|
1207
|
+
treeShake: false,
|
|
1208
|
+
hooks: {
|
|
1209
|
+
onResolveStart(value) {
|
|
1210
|
+
resolveStart(value)
|
|
1211
|
+
},
|
|
1212
|
+
onResolveError(value) {
|
|
1213
|
+
resolveError(value)
|
|
1214
|
+
},
|
|
1215
|
+
onResolveSuccess(value) {
|
|
1216
|
+
resolveSuccess(value)
|
|
1217
|
+
},
|
|
1218
|
+
},
|
|
1219
|
+
})
|
|
1220
|
+
|
|
1221
|
+
expect(resolveStart).toHaveBeenCalledOnce()
|
|
1222
|
+
expect(resolveStart).toHaveBeenCalledWith(refA)
|
|
1223
|
+
expect(resolveSuccess).toHaveBeenCalledOnce()
|
|
1224
|
+
expect(resolveSuccess).toHaveBeenCalledWith(refA)
|
|
1225
|
+
expect(resolveError).not.toHaveBeenCalledOnce()
|
|
1226
|
+
})
|
|
1227
|
+
|
|
1228
|
+
it('run success hook', async () => {
|
|
1229
|
+
const url = `http://localhost:${PORT}`
|
|
1230
|
+
|
|
1231
|
+
server.get('/chunk1', (_, reply) => {
|
|
1232
|
+
reply.code(404).send()
|
|
1233
|
+
})
|
|
1234
|
+
|
|
1235
|
+
await server.listen({ port: PORT })
|
|
1236
|
+
|
|
1237
|
+
const input = {
|
|
1238
|
+
a: {
|
|
1239
|
+
$ref: `${url}/chunk1#`,
|
|
1240
|
+
},
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
const resolveStart = vi.fn()
|
|
1244
|
+
const resolveError = vi.fn()
|
|
1245
|
+
const resolveSuccess = vi.fn()
|
|
1246
|
+
|
|
1247
|
+
const refA = input.a
|
|
1248
|
+
|
|
1249
|
+
await bundle(input, {
|
|
1250
|
+
plugins: [fetchUrls()],
|
|
1251
|
+
treeShake: false,
|
|
1252
|
+
hooks: {
|
|
1253
|
+
onResolveStart(value) {
|
|
1254
|
+
resolveStart(value)
|
|
1255
|
+
},
|
|
1256
|
+
onResolveError(value) {
|
|
1257
|
+
resolveError(value)
|
|
1258
|
+
},
|
|
1259
|
+
onResolveSuccess(value) {
|
|
1260
|
+
resolveSuccess(value)
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
})
|
|
1264
|
+
|
|
1265
|
+
expect(resolveStart).toHaveBeenCalledOnce()
|
|
1266
|
+
expect(resolveStart).toHaveBeenCalledWith(refA)
|
|
1267
|
+
expect(resolveSuccess).not.toHaveBeenCalledOnce()
|
|
1268
|
+
expect(resolveError).toHaveBeenCalledOnce()
|
|
1269
|
+
expect(resolveError).toHaveBeenCalledWith(refA)
|
|
1270
|
+
})
|
|
1271
|
+
})
|
|
1272
|
+
|
|
1273
|
+
describe('local files', () => {
|
|
1274
|
+
it('resolves from local files', async () => {
|
|
1275
|
+
const chunk1 = { a: 'a', b: 'b' }
|
|
1276
|
+
const chunk1Path = randomUUID()
|
|
1277
|
+
|
|
1278
|
+
await fs.writeFile(chunk1Path, JSON.stringify(chunk1))
|
|
1279
|
+
|
|
1280
|
+
const input = {
|
|
1281
|
+
a: {
|
|
1282
|
+
'$ref': `./${chunk1Path}#/a`,
|
|
1283
|
+
},
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
1287
|
+
|
|
1288
|
+
await fs.rm(chunk1Path)
|
|
1289
|
+
|
|
1290
|
+
expect(input).toEqual({
|
|
1291
|
+
'x-ext': {
|
|
1292
|
+
[await getHash(chunk1Path)]: {
|
|
1293
|
+
...chunk1,
|
|
1294
|
+
},
|
|
1295
|
+
},
|
|
1296
|
+
a: {
|
|
1297
|
+
$ref: `#/x-ext/${await getHash(chunk1Path)}/a`,
|
|
1298
|
+
},
|
|
1299
|
+
})
|
|
1300
|
+
})
|
|
1301
|
+
|
|
1302
|
+
it('resolves external refs from resolved files', async () => {
|
|
1303
|
+
const chunk1 = { a: 'a', b: 'b' }
|
|
1304
|
+
const chunk1Path = randomUUID()
|
|
1305
|
+
|
|
1306
|
+
const chunk2 = { a: { '$ref': `./${chunk1Path}#` } }
|
|
1307
|
+
const chunk2Path = randomUUID()
|
|
1308
|
+
|
|
1309
|
+
await fs.writeFile(chunk1Path, JSON.stringify(chunk1))
|
|
1310
|
+
await fs.writeFile(chunk2Path, JSON.stringify(chunk2))
|
|
1311
|
+
|
|
1312
|
+
const input = {
|
|
1313
|
+
a: {
|
|
1314
|
+
'$ref': `./${chunk2Path}#`,
|
|
1315
|
+
},
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
1319
|
+
|
|
1320
|
+
await fs.rm(chunk1Path)
|
|
1321
|
+
await fs.rm(chunk2Path)
|
|
1322
|
+
|
|
1323
|
+
expect(input).toEqual({
|
|
1324
|
+
'x-ext': {
|
|
1325
|
+
[await getHash(chunk1Path)]: {
|
|
1326
|
+
...chunk1,
|
|
1327
|
+
},
|
|
1328
|
+
[await getHash(chunk2Path)]: {
|
|
1329
|
+
a: { $ref: `#/x-ext/${await getHash(chunk1Path)}` },
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
a: {
|
|
1333
|
+
$ref: `#/x-ext/${await getHash(chunk2Path)}`,
|
|
1334
|
+
},
|
|
1335
|
+
})
|
|
1336
|
+
})
|
|
1337
|
+
|
|
1338
|
+
it('resolves nested refs correctly', async () => {
|
|
1339
|
+
const c = {
|
|
1340
|
+
c: 'c',
|
|
1341
|
+
}
|
|
1342
|
+
const cName = randomUUID()
|
|
1343
|
+
|
|
1344
|
+
const b = {
|
|
1345
|
+
b: {
|
|
1346
|
+
'$ref': `./${cName}`,
|
|
1347
|
+
},
|
|
1348
|
+
}
|
|
1349
|
+
const bName = randomUUID()
|
|
1350
|
+
|
|
1351
|
+
await fs.mkdir('./nested')
|
|
1352
|
+
await fs.writeFile(`./nested/${bName}`, JSON.stringify(b))
|
|
1353
|
+
await fs.writeFile(`./nested/${cName}`, JSON.stringify(c))
|
|
1354
|
+
|
|
1355
|
+
const input = {
|
|
1356
|
+
a: {
|
|
1357
|
+
'$ref': `./nested/${bName}`,
|
|
1358
|
+
},
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
await bundle(input, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
1362
|
+
|
|
1363
|
+
await fs.rm(`./nested/${bName}`)
|
|
1364
|
+
await fs.rm(`./nested/${cName}`)
|
|
1365
|
+
await fs.rmdir('nested')
|
|
1366
|
+
|
|
1367
|
+
expect(input).toEqual({
|
|
1368
|
+
'x-ext': {
|
|
1369
|
+
[await getHash(`nested/${cName}`)]: {
|
|
1370
|
+
c: 'c',
|
|
1371
|
+
},
|
|
1372
|
+
[await getHash(`nested/${bName}`)]: {
|
|
1373
|
+
b: { $ref: `#/x-ext/${await getHash(`nested/${cName}`)}` },
|
|
1374
|
+
},
|
|
1375
|
+
},
|
|
1376
|
+
a: {
|
|
1377
|
+
$ref: `#/x-ext/${await getHash(`nested/${bName}`)}`,
|
|
1378
|
+
},
|
|
1379
|
+
})
|
|
1380
|
+
})
|
|
1381
|
+
|
|
1382
|
+
it('bundles from a file input', async () => {
|
|
1383
|
+
const c = {
|
|
1384
|
+
c: 'c',
|
|
1385
|
+
}
|
|
1386
|
+
const cName = randomUUID()
|
|
1387
|
+
|
|
1388
|
+
const b = {
|
|
1389
|
+
b: {
|
|
1390
|
+
'$ref': `./${cName}`,
|
|
1391
|
+
},
|
|
1392
|
+
}
|
|
1393
|
+
const bName = randomUUID()
|
|
1394
|
+
|
|
1395
|
+
await fs.mkdir('./nested')
|
|
1396
|
+
await fs.writeFile(`./nested/${bName}`, JSON.stringify(b))
|
|
1397
|
+
await fs.writeFile(`./nested/${cName}`, JSON.stringify(c))
|
|
1398
|
+
|
|
1399
|
+
const input = {
|
|
1400
|
+
a: {
|
|
1401
|
+
'$ref': `./${bName}`,
|
|
1402
|
+
},
|
|
1403
|
+
}
|
|
1404
|
+
const inputName = randomUUID()
|
|
1405
|
+
await fs.writeFile(`./nested/${inputName}`, JSON.stringify(input))
|
|
1406
|
+
|
|
1407
|
+
const result = await bundle(`./nested/${bName}`, { plugins: [fetchUrls(), readFiles()], treeShake: false })
|
|
1408
|
+
|
|
1409
|
+
await fs.rm(`./nested/${bName}`)
|
|
1410
|
+
await fs.rm(`./nested/${cName}`)
|
|
1411
|
+
await fs.rm(`./nested/${inputName}`)
|
|
1412
|
+
await fs.rmdir('nested')
|
|
1413
|
+
|
|
1414
|
+
expect(result).toEqual({
|
|
1415
|
+
'b': {
|
|
1416
|
+
'$ref': `#/x-ext/${await getHash(`nested/${cName}`)}`,
|
|
1417
|
+
},
|
|
1418
|
+
'x-ext': {
|
|
1419
|
+
[await getHash(`nested/${cName}`)]: {
|
|
1420
|
+
'c': 'c',
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
})
|
|
1424
|
+
})
|
|
1425
|
+
})
|
|
1426
|
+
|
|
1427
|
+
describe('json inputs', () => {
|
|
1428
|
+
it('should process json inputs', async () => {
|
|
1429
|
+
const result = await bundle('{ "openapi": "3.1", "info": { "title": "Simple API", "version": "1.0" } }', {
|
|
1430
|
+
treeShake: false,
|
|
1431
|
+
plugins: [parseJson()],
|
|
1432
|
+
})
|
|
1433
|
+
|
|
1434
|
+
expect(result).toEqual({
|
|
1435
|
+
openapi: '3.1',
|
|
1436
|
+
info: {
|
|
1437
|
+
title: 'Simple API',
|
|
1438
|
+
version: '1.0',
|
|
1439
|
+
},
|
|
1440
|
+
})
|
|
1441
|
+
})
|
|
1442
|
+
|
|
1443
|
+
it('should correctly resolve refs for json inputs', async () => {
|
|
1444
|
+
const chunk1 = { a: 'a', b: 'b' }
|
|
1445
|
+
const chunk1Path = randomUUID()
|
|
1446
|
+
|
|
1447
|
+
await fs.writeFile(chunk1Path, JSON.stringify(chunk1))
|
|
1448
|
+
|
|
1449
|
+
const input = JSON.stringify({
|
|
1450
|
+
a: {
|
|
1451
|
+
'$ref': `./${chunk1Path}#/a`,
|
|
1452
|
+
},
|
|
1453
|
+
})
|
|
1454
|
+
|
|
1455
|
+
const result = await bundle(input, { plugins: [readFiles(), parseJson()], treeShake: false })
|
|
1456
|
+
|
|
1457
|
+
await fs.rm(chunk1Path)
|
|
1458
|
+
|
|
1459
|
+
expect(result).toEqual({
|
|
1460
|
+
'x-ext': {
|
|
1461
|
+
[await getHash(chunk1Path)]: {
|
|
1462
|
+
...chunk1,
|
|
1463
|
+
},
|
|
1464
|
+
},
|
|
1465
|
+
a: {
|
|
1466
|
+
$ref: `#/x-ext/${await getHash(chunk1Path)}/a`,
|
|
1467
|
+
},
|
|
1468
|
+
})
|
|
1469
|
+
})
|
|
1470
|
+
})
|
|
1471
|
+
|
|
1472
|
+
describe('yaml inputs', () => {
|
|
1473
|
+
let server: FastifyInstance
|
|
1474
|
+
const port = 7229
|
|
1475
|
+
const url = `http://localhost:${port}`
|
|
1476
|
+
|
|
1477
|
+
beforeEach(() => {
|
|
1478
|
+
server = fastify({ logger: false })
|
|
1479
|
+
})
|
|
1480
|
+
|
|
1481
|
+
afterEach(async () => {
|
|
1482
|
+
await server.close()
|
|
1483
|
+
await setTimeout(100)
|
|
1484
|
+
})
|
|
1485
|
+
|
|
1486
|
+
it('should process yaml inputs', async () => {
|
|
1487
|
+
const result = await bundle('openapi: "3.1"\ninfo:\n title: Simple API\n version: "1.0"\n', {
|
|
1488
|
+
treeShake: false,
|
|
1489
|
+
plugins: [parseYaml()],
|
|
1490
|
+
})
|
|
1491
|
+
|
|
1492
|
+
expect(result).toEqual({
|
|
1493
|
+
openapi: '3.1',
|
|
1494
|
+
info: {
|
|
1495
|
+
title: 'Simple API',
|
|
1496
|
+
version: '1.0',
|
|
1497
|
+
},
|
|
1498
|
+
})
|
|
1499
|
+
})
|
|
1500
|
+
|
|
1501
|
+
it('should correctly resolve refs for yaml inputs', async () => {
|
|
1502
|
+
const chunk1 = { a: 'a', b: 'b' }
|
|
1503
|
+
const chunk1Path = randomUUID()
|
|
1504
|
+
|
|
1505
|
+
await fs.writeFile(chunk1Path, YAML.stringify(chunk1))
|
|
1506
|
+
|
|
1507
|
+
const input = YAML.stringify({
|
|
1508
|
+
a: {
|
|
1509
|
+
'$ref': `./${chunk1Path}#/a`,
|
|
1510
|
+
},
|
|
1511
|
+
})
|
|
1512
|
+
|
|
1513
|
+
const result = await bundle(input, { plugins: [parseYaml(), readFiles()], treeShake: false })
|
|
1514
|
+
|
|
1515
|
+
await fs.rm(chunk1Path)
|
|
1516
|
+
|
|
1517
|
+
expect(result).toEqual({
|
|
1518
|
+
'x-ext': {
|
|
1519
|
+
[await getHash(chunk1Path)]: {
|
|
1520
|
+
...chunk1,
|
|
1521
|
+
},
|
|
1522
|
+
},
|
|
1523
|
+
a: {
|
|
1524
|
+
$ref: `#/x-ext/${await getHash(chunk1Path)}/a`,
|
|
1525
|
+
},
|
|
1526
|
+
})
|
|
1527
|
+
})
|
|
1528
|
+
|
|
1529
|
+
it('should correctly load the document from an url even when yaml plugin is provided and it has high priority on the list', async () => {
|
|
1530
|
+
server.get('/', () => ({
|
|
1531
|
+
openapi: '3.1.1',
|
|
1532
|
+
info: {
|
|
1533
|
+
title: 'My API',
|
|
1534
|
+
},
|
|
1535
|
+
}))
|
|
1536
|
+
await server.listen({ port })
|
|
1537
|
+
const result = await bundle(url, {
|
|
1538
|
+
treeShake: false,
|
|
1539
|
+
plugins: [parseYaml(), fetchUrls()],
|
|
1540
|
+
})
|
|
1541
|
+
|
|
1542
|
+
expect(result).toEqual({
|
|
1543
|
+
'info': {
|
|
1544
|
+
'title': 'My API',
|
|
1545
|
+
},
|
|
1546
|
+
'openapi': '3.1.1',
|
|
1547
|
+
})
|
|
1548
|
+
})
|
|
1549
|
+
})
|
|
1550
|
+
|
|
1551
|
+
describe('bundle with a certain depth', () => {
|
|
1552
|
+
let server: FastifyInstance
|
|
1553
|
+
const PORT = 7299
|
|
1554
|
+
const url = `http://localhost:${PORT}`
|
|
1555
|
+
|
|
1556
|
+
beforeEach(() => {
|
|
1557
|
+
server = fastify({ logger: false })
|
|
1558
|
+
})
|
|
1559
|
+
|
|
1560
|
+
afterEach(async () => {
|
|
1561
|
+
await server.close()
|
|
1562
|
+
await setTimeout(100)
|
|
1563
|
+
})
|
|
1564
|
+
|
|
1565
|
+
it('bundles external urls', async () => {
|
|
1566
|
+
const external = {
|
|
1567
|
+
prop: 'I am external json prop',
|
|
1568
|
+
}
|
|
1569
|
+
server.get('/', (_, reply) => {
|
|
1570
|
+
reply.send(external)
|
|
1571
|
+
})
|
|
1572
|
+
|
|
1573
|
+
await server.listen({ port: PORT })
|
|
1574
|
+
|
|
1575
|
+
const input = {
|
|
1576
|
+
a: {
|
|
1577
|
+
b: {
|
|
1578
|
+
c: {
|
|
1579
|
+
d: {
|
|
1580
|
+
e: {
|
|
1581
|
+
// Deep ref
|
|
1582
|
+
'$ref': `http://localhost:${PORT}#/prop`,
|
|
1583
|
+
},
|
|
1584
|
+
},
|
|
1585
|
+
},
|
|
1586
|
+
},
|
|
1587
|
+
},
|
|
1588
|
+
d: {
|
|
1589
|
+
e: {
|
|
1590
|
+
'$ref': `http://localhost:${PORT}#/prop`,
|
|
1591
|
+
},
|
|
1592
|
+
},
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
await bundle(input, {
|
|
1596
|
+
plugins: [fetchUrls()],
|
|
1597
|
+
treeShake: false,
|
|
1598
|
+
depth: 2,
|
|
1599
|
+
})
|
|
1600
|
+
|
|
1601
|
+
expect(input).toEqual({
|
|
1602
|
+
'x-ext': {
|
|
1603
|
+
[await getHash(url)]: {
|
|
1604
|
+
...external,
|
|
1605
|
+
},
|
|
1606
|
+
},
|
|
1607
|
+
'x-ext-urls': {
|
|
1608
|
+
[await getHash(url)]: url,
|
|
1609
|
+
},
|
|
1610
|
+
a: {
|
|
1611
|
+
b: {
|
|
1612
|
+
c: {
|
|
1613
|
+
d: {
|
|
1614
|
+
e: {
|
|
1615
|
+
$ref: `${url}#/prop`,
|
|
1616
|
+
},
|
|
1617
|
+
},
|
|
1618
|
+
},
|
|
1619
|
+
},
|
|
1620
|
+
},
|
|
1621
|
+
d: {
|
|
1622
|
+
e: {
|
|
1623
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
1624
|
+
},
|
|
1625
|
+
},
|
|
1626
|
+
})
|
|
1627
|
+
})
|
|
1628
|
+
|
|
1629
|
+
it('will not do full bundle if we do specify a depth and reuse the same hash set', async () => {
|
|
1630
|
+
const external = {
|
|
1631
|
+
prop: 'I am external json prop',
|
|
1632
|
+
}
|
|
1633
|
+
server.get('/', (_, reply) => {
|
|
1634
|
+
reply.send(external)
|
|
1635
|
+
})
|
|
1636
|
+
|
|
1637
|
+
await server.listen({ port: PORT })
|
|
1638
|
+
|
|
1639
|
+
const input = {
|
|
1640
|
+
a: {
|
|
1641
|
+
b: {
|
|
1642
|
+
c: {
|
|
1643
|
+
d: {
|
|
1644
|
+
e: {
|
|
1645
|
+
// Deep ref
|
|
1646
|
+
'$ref': `http://localhost:${PORT}#/prop`,
|
|
1647
|
+
},
|
|
1648
|
+
},
|
|
1649
|
+
},
|
|
1650
|
+
},
|
|
1651
|
+
},
|
|
1652
|
+
d: {
|
|
1653
|
+
e: {
|
|
1654
|
+
'$ref': `http://localhost:${PORT}#/prop`,
|
|
1655
|
+
},
|
|
1656
|
+
},
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
const visitedNodes = new Set()
|
|
1660
|
+
|
|
1661
|
+
await bundle(input, {
|
|
1662
|
+
plugins: [fetchUrls()],
|
|
1663
|
+
treeShake: false,
|
|
1664
|
+
depth: 2,
|
|
1665
|
+
visitedNodes: visitedNodes,
|
|
1666
|
+
})
|
|
1667
|
+
|
|
1668
|
+
expect(input).toEqual({
|
|
1669
|
+
'x-ext': {
|
|
1670
|
+
[await getHash(url)]: {
|
|
1671
|
+
...external,
|
|
1672
|
+
},
|
|
1673
|
+
},
|
|
1674
|
+
'x-ext-urls': {
|
|
1675
|
+
[await getHash(url)]: url,
|
|
1676
|
+
},
|
|
1677
|
+
a: {
|
|
1678
|
+
b: {
|
|
1679
|
+
c: {
|
|
1680
|
+
d: {
|
|
1681
|
+
e: {
|
|
1682
|
+
$ref: `${url}#/prop`,
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1685
|
+
},
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
d: {
|
|
1689
|
+
e: {
|
|
1690
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
1691
|
+
},
|
|
1692
|
+
},
|
|
1693
|
+
})
|
|
1694
|
+
|
|
1695
|
+
// We run a full bundle on the root of the document without a depth
|
|
1696
|
+
await bundle(input, {
|
|
1697
|
+
plugins: [fetchUrls()],
|
|
1698
|
+
treeShake: false,
|
|
1699
|
+
visitedNodes: visitedNodes,
|
|
1700
|
+
urlMap: true,
|
|
1701
|
+
})
|
|
1702
|
+
|
|
1703
|
+
// Expect the input to be the same as before
|
|
1704
|
+
// because we are reusing the same hash set
|
|
1705
|
+
expect(input).toEqual({
|
|
1706
|
+
'x-ext': {
|
|
1707
|
+
[await getHash(url)]: {
|
|
1708
|
+
...external,
|
|
1709
|
+
},
|
|
1710
|
+
},
|
|
1711
|
+
'x-ext-urls': {
|
|
1712
|
+
[await getHash(url)]: url,
|
|
1713
|
+
},
|
|
1714
|
+
a: {
|
|
1715
|
+
b: {
|
|
1716
|
+
c: {
|
|
1717
|
+
d: {
|
|
1718
|
+
e: {
|
|
1719
|
+
$ref: `${url}#/prop`,
|
|
1720
|
+
},
|
|
1721
|
+
},
|
|
1722
|
+
},
|
|
1723
|
+
},
|
|
1724
|
+
},
|
|
1725
|
+
d: {
|
|
1726
|
+
e: {
|
|
1727
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
1728
|
+
},
|
|
1729
|
+
},
|
|
1730
|
+
})
|
|
1731
|
+
|
|
1732
|
+
// When we run a full bundle again without the same hash set we expect a full bundle
|
|
1733
|
+
await bundle(input, {
|
|
1734
|
+
plugins: [fetchUrls()],
|
|
1735
|
+
treeShake: false,
|
|
1736
|
+
urlMap: true,
|
|
1737
|
+
})
|
|
1738
|
+
|
|
1739
|
+
expect(input).toEqual({
|
|
1740
|
+
'x-ext': {
|
|
1741
|
+
[await getHash(url)]: {
|
|
1742
|
+
...external,
|
|
1743
|
+
},
|
|
1744
|
+
},
|
|
1745
|
+
'x-ext-urls': {
|
|
1746
|
+
[await getHash(url)]: url,
|
|
1747
|
+
},
|
|
1748
|
+
a: {
|
|
1749
|
+
b: {
|
|
1750
|
+
c: {
|
|
1751
|
+
d: {
|
|
1752
|
+
e: {
|
|
1753
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
1754
|
+
},
|
|
1755
|
+
},
|
|
1756
|
+
},
|
|
1757
|
+
},
|
|
1758
|
+
},
|
|
1759
|
+
d: {
|
|
1760
|
+
e: {
|
|
1761
|
+
$ref: `#/x-ext/${await getHash(url)}/prop`,
|
|
1762
|
+
},
|
|
1763
|
+
},
|
|
1764
|
+
})
|
|
1765
|
+
})
|
|
1766
|
+
})
|
|
1767
|
+
})
|
|
1768
|
+
|
|
1769
|
+
describe('isRemoteUrl', () => {
|
|
1770
|
+
it.each([
|
|
1771
|
+
['https://example.com/schema.json', true],
|
|
1772
|
+
['http://api.example.com/schemas/user.json', true],
|
|
1773
|
+
['file://some/path', false],
|
|
1774
|
+
['random-string', false],
|
|
1775
|
+
['#/components/schemas/User', false],
|
|
1776
|
+
['./local-schema.json', false],
|
|
1777
|
+
])('detects remote urls', (a, b) => {
|
|
1778
|
+
expect(isRemoteUrl(a)).toBe(b)
|
|
1779
|
+
})
|
|
1780
|
+
})
|
|
1781
|
+
|
|
1782
|
+
describe('isLocalRef', () => {
|
|
1783
|
+
it.each([
|
|
1784
|
+
['#/components/schemas/User', true],
|
|
1785
|
+
['https://example.com/schema.json', false],
|
|
1786
|
+
['./local-schema.json', false],
|
|
1787
|
+
])('detects local refs', (a, b) => {
|
|
1788
|
+
expect(isLocalRef(a)).toBe(b)
|
|
1789
|
+
})
|
|
1790
|
+
})
|
|
1791
|
+
|
|
1792
|
+
describe('getNestedValue', () => {
|
|
1793
|
+
it.each([
|
|
1794
|
+
[{ a: { b: { c: 'hello' } } }, ['a', 'b', 'c'], 'hello'],
|
|
1795
|
+
[{ a: { b: { c: 'hello' } } }, [], { a: { b: { c: 'hello' } } }],
|
|
1796
|
+
[{ foo: { bar: { baz: 42 } } }, ['foo', 'bar', 'baz'], 42],
|
|
1797
|
+
[{ foo: { bar: { baz: 42 } } }, ['foo', 'non-existing', 'baz'], undefined],
|
|
1798
|
+
])('gets nested value', (a, b, c) => {
|
|
1799
|
+
expect(getNestedValue(a, b)).toEqual(c)
|
|
1800
|
+
})
|
|
1801
|
+
})
|
|
1802
|
+
|
|
1803
|
+
describe('prefixInternalRef', () => {
|
|
1804
|
+
it.each([
|
|
1805
|
+
['#/hello', ['prefix'], '#/prefix/hello'],
|
|
1806
|
+
['#/a/b/c', ['prefixA', 'prefixB'], '#/prefixA/prefixB/a/b/c'],
|
|
1807
|
+
])('correctly prefix the internal refs', (a, b, c) => {
|
|
1808
|
+
expect(prefixInternalRef(a, b)).toEqual(c)
|
|
1809
|
+
})
|
|
1810
|
+
|
|
1811
|
+
it('throws when the ref is not internal', () => {
|
|
1812
|
+
expect(() => prefixInternalRef('http://example.com#/prefix', ['a', 'b'])).toThrowError()
|
|
1813
|
+
})
|
|
1814
|
+
})
|
|
1815
|
+
|
|
1816
|
+
describe('prefixInternalRefRecursive', () => {
|
|
1817
|
+
it.each([
|
|
1818
|
+
[
|
|
1819
|
+
{ a: { $ref: '#/a/b' }, b: { $ref: '#' } },
|
|
1820
|
+
['d', 'e', 'f'],
|
|
1821
|
+
{ a: { $ref: '#/d/e/f/a/b' }, b: { $ref: '#/d/e/f' } },
|
|
1822
|
+
],
|
|
1823
|
+
[
|
|
1824
|
+
{ a: { $ref: '#/a/b' }, b: { $ref: 'http://example.com#/external' } },
|
|
1825
|
+
['d', 'e', 'f'],
|
|
1826
|
+
{ a: { $ref: '#/d/e/f/a/b' }, b: { $ref: 'http://example.com#/external' } },
|
|
1827
|
+
],
|
|
1828
|
+
])('recursively prefixes any internal ref with the correct values', (a, b, c) => {
|
|
1829
|
+
prefixInternalRefRecursive(a, b)
|
|
1830
|
+
expect(a).toEqual(c)
|
|
1831
|
+
})
|
|
1832
|
+
})
|
|
1833
|
+
|
|
1834
|
+
describe('setValueAtPath', () => {
|
|
1835
|
+
it.each([
|
|
1836
|
+
[{}, '/a/b/c', { hello: 'hi' }, { a: { b: { c: { hello: 'hi' } } } }],
|
|
1837
|
+
[{ a: { b: 'b' } }, '/a/c', { hello: 'hi' }, { a: { b: 'b', c: { hello: 'hi' } } }],
|
|
1838
|
+
])('correctly sets a value at the specified path by creating new objects if necessary', (a, b, c, d) => {
|
|
1839
|
+
setValueAtPath(a, b, c)
|
|
1840
|
+
|
|
1841
|
+
expect(a).toEqual(d)
|
|
1842
|
+
})
|
|
1843
|
+
})
|