moonflower 1.4.9 → 1.5.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/dist/openapi/analyzerModule/analyzerModule.cjs +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.cjs.map +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.d.ts +3 -2
- package/dist/openapi/analyzerModule/analyzerModule.d.ts.map +1 -1
- package/dist/openapi/analyzerModule/analyzerModule.mjs +163 -125
- package/dist/openapi/analyzerModule/analyzerModule.mjs.map +1 -1
- package/dist/openapi/analyzerModule/analyzerWorker.cjs +2 -0
- package/dist/openapi/analyzerModule/analyzerWorker.cjs.map +1 -0
- package/dist/openapi/analyzerModule/analyzerWorker.d.ts +2 -0
- package/dist/openapi/analyzerModule/analyzerWorker.d.ts.map +1 -0
- package/dist/openapi/analyzerModule/analyzerWorker.mjs +44 -0
- package/dist/openapi/analyzerModule/analyzerWorker.mjs.map +1 -0
- package/dist/openapi/analyzerModule/nodeParsers.cjs +1 -1
- package/dist/openapi/analyzerModule/nodeParsers.cjs.map +1 -1
- package/dist/openapi/analyzerModule/nodeParsers.d.ts.map +1 -1
- package/dist/openapi/analyzerModule/nodeParsers.mjs +199 -264
- package/dist/openapi/analyzerModule/nodeParsers.mjs.map +1 -1
- package/dist/openapi/analyzerModule/test/TestCase.d.ts +1 -0
- package/dist/openapi/analyzerModule/test/TestCase.d.ts.map +1 -1
- package/dist/openapi/analyzerModule/test/workerGlobalSetup.d.ts +3 -0
- package/dist/openapi/analyzerModule/test/workerGlobalSetup.d.ts.map +1 -0
- package/dist/openapi/analyzerModule/workerPaths.d.ts +2 -0
- package/dist/openapi/analyzerModule/workerPaths.d.ts.map +1 -0
- package/dist/openapi/analyzerModule/workerPool.cjs +2 -0
- package/dist/openapi/analyzerModule/workerPool.cjs.map +1 -0
- package/dist/openapi/analyzerModule/workerPool.d.ts +33 -0
- package/dist/openapi/analyzerModule/workerPool.d.ts.map +1 -0
- package/dist/openapi/analyzerModule/workerPool.mjs +46 -0
- package/dist/openapi/analyzerModule/workerPool.mjs.map +1 -0
- package/package.json +1 -1
- package/src/openapi/analyzerModule/analyzerModule.ts +142 -12
- package/src/openapi/analyzerModule/analyzerWorker.ts +73 -0
- package/src/openapi/analyzerModule/nodeParsers.ts +4 -104
- package/src/openapi/analyzerModule/test/TestCase.ts +1 -0
- package/src/openapi/analyzerModule/test/openApiAnalyzer.zod.spec.data.ts +6 -0
- package/src/openapi/analyzerModule/test/openApiAnalyzer.zod.spec.ts +8 -0
- package/src/openapi/analyzerModule/test/workerGlobalSetup.ts +25 -0
- package/src/openapi/analyzerModule/workerPaths.ts +4 -0
- package/src/openapi/analyzerModule/workerPool.ts +92 -0
- package/src/test/app.spec.ts +10 -1
- package/vite.config.ts +5 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Node, Project, ts } from 'ts-morph'
|
|
2
|
+
import { parentPort } from 'worker_threads'
|
|
3
|
+
|
|
4
|
+
import { parseEndpoint } from './parseEndpoint'
|
|
5
|
+
import { WorkerResult, WorkerTask } from './workerPool'
|
|
6
|
+
|
|
7
|
+
const OPERATIONS = ['get', 'post', 'put', 'delete', 'del', 'patch']
|
|
8
|
+
const OPERATIONS_PATTERN = OPERATIONS.join('|')
|
|
9
|
+
|
|
10
|
+
let project: Project | null = null
|
|
11
|
+
let currentTsconfigPath: string | null = null
|
|
12
|
+
|
|
13
|
+
function getProject(tsconfigPath: string): Project {
|
|
14
|
+
if (!project || currentTsconfigPath !== tsconfigPath) {
|
|
15
|
+
project = new Project({
|
|
16
|
+
tsConfigFilePath: tsconfigPath,
|
|
17
|
+
skipFileDependencyResolution: true,
|
|
18
|
+
})
|
|
19
|
+
currentTsconfigPath = tsconfigPath
|
|
20
|
+
}
|
|
21
|
+
return project
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
parentPort!.on('message', (task: WorkerTask) => {
|
|
25
|
+
try {
|
|
26
|
+
const proj = getProject(task.tsconfigPath)
|
|
27
|
+
|
|
28
|
+
let sourceFile = proj.getSourceFile(task.sourceFilePath)
|
|
29
|
+
if (!sourceFile) {
|
|
30
|
+
sourceFile = proj.addSourceFileAtPath(task.sourceFilePath)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const routerPattern = new RegExp(`${task.routerName}\\.(?:${OPERATIONS_PATTERN})`)
|
|
34
|
+
let index = 0
|
|
35
|
+
let targetNode: Node<ts.Node> | undefined
|
|
36
|
+
|
|
37
|
+
sourceFile.forEachChild((node) => {
|
|
38
|
+
if (targetNode) return
|
|
39
|
+
if (routerPattern.test(node.getText())) {
|
|
40
|
+
if (index === task.endpointIndex) {
|
|
41
|
+
targetNode = node
|
|
42
|
+
}
|
|
43
|
+
index++
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
if (!targetNode) {
|
|
48
|
+
const result: WorkerResult = {
|
|
49
|
+
taskId: task.taskId,
|
|
50
|
+
error: `Endpoint not found: routerName=${task.routerName} index=${task.endpointIndex} in ${task.sourceFilePath}`,
|
|
51
|
+
}
|
|
52
|
+
parentPort!.postMessage(result)
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const t1 = performance.now()
|
|
57
|
+
const { endpoint, sectionTimings } = parseEndpoint(targetNode, task.sourceFilePath)
|
|
58
|
+
|
|
59
|
+
const result: WorkerResult = {
|
|
60
|
+
taskId: task.taskId,
|
|
61
|
+
endpoint,
|
|
62
|
+
sectionTimings,
|
|
63
|
+
timing: performance.now() - t1,
|
|
64
|
+
}
|
|
65
|
+
parentPort!.postMessage(result)
|
|
66
|
+
} catch (err) {
|
|
67
|
+
const result: WorkerResult = {
|
|
68
|
+
taskId: task.taskId,
|
|
69
|
+
error: String(err),
|
|
70
|
+
}
|
|
71
|
+
parentPort!.postMessage(result)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
@@ -278,112 +278,12 @@ const isZodCallExpression = (node: Node): boolean => {
|
|
|
278
278
|
const getZodCallShape = (node: Node): ShapeOfType['shape'] => {
|
|
279
279
|
const callExpression = node.asKind(SyntaxKind.CallExpression)!
|
|
280
280
|
const returnType = callExpression.getReturnType()
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return 'number'
|
|
285
|
-
}
|
|
286
|
-
if (typeName === 'ZodString') {
|
|
287
|
-
return 'string'
|
|
288
|
-
}
|
|
289
|
-
if (typeName === 'ZodBoolean') {
|
|
290
|
-
return 'boolean'
|
|
291
|
-
}
|
|
292
|
-
if (typeName === 'ZodBigInt') {
|
|
293
|
-
return 'bigint'
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (typeName === 'ZodObject') {
|
|
297
|
-
const argNode = callExpression.getFirstChildByKind(SyntaxKind.SyntaxList)?.getFirstChild()
|
|
298
|
-
const objectLiteral = argNode?.asKind(SyntaxKind.ObjectLiteralExpression)
|
|
299
|
-
if (!objectLiteral) {
|
|
300
|
-
return 'unknown_zod_object'
|
|
301
|
-
}
|
|
302
|
-
const syntaxList = objectLiteral.getFirstChildByKind(SyntaxKind.SyntaxList)
|
|
303
|
-
if (!syntaxList) {
|
|
304
|
-
return []
|
|
305
|
-
}
|
|
306
|
-
const properties = syntaxList.getChildrenOfKind(SyntaxKind.PropertyAssignment)
|
|
307
|
-
return properties.map((prop) => {
|
|
308
|
-
const identifier = prop.getFirstChildByKind(SyntaxKind.Identifier)!.getText()
|
|
309
|
-
const valueNode = prop.getLastChild()!
|
|
310
|
-
return {
|
|
311
|
-
role: 'property' as const,
|
|
312
|
-
identifier,
|
|
313
|
-
shape: isZodCallExpression(valueNode)
|
|
314
|
-
? getZodCallShape(valueNode)
|
|
315
|
-
: getValidatorPropertyShape(valueNode),
|
|
316
|
-
optional: false,
|
|
317
|
-
}
|
|
318
|
-
})
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (typeName === 'ZodArray') {
|
|
322
|
-
const argNode = callExpression.getFirstChildByKind(SyntaxKind.SyntaxList)?.getFirstChild()
|
|
323
|
-
if (argNode) {
|
|
324
|
-
const elementShape = isZodCallExpression(argNode)
|
|
325
|
-
? getZodCallShape(argNode)
|
|
326
|
-
: getValidatorPropertyShape(argNode)
|
|
327
|
-
return [
|
|
328
|
-
{
|
|
329
|
-
role: 'array' as const,
|
|
330
|
-
shape: elementShape,
|
|
331
|
-
optional: false,
|
|
332
|
-
},
|
|
333
|
-
]
|
|
334
|
-
}
|
|
335
|
-
// Handle chained form: z.string().array()
|
|
336
|
-
const propertyAccess = callExpression.getFirstChildByKind(SyntaxKind.PropertyAccessExpression)
|
|
337
|
-
const receiverCall = propertyAccess?.getFirstChildByKind(SyntaxKind.CallExpression)
|
|
338
|
-
if (receiverCall && isZodCallExpression(receiverCall)) {
|
|
339
|
-
return [
|
|
340
|
-
{
|
|
341
|
-
role: 'array' as const,
|
|
342
|
-
shape: getZodCallShape(receiverCall),
|
|
343
|
-
optional: false,
|
|
344
|
-
},
|
|
345
|
-
]
|
|
346
|
-
}
|
|
347
|
-
return 'unknown_zod_array'
|
|
281
|
+
const outputProp = returnType.getProperty('_output')
|
|
282
|
+
if (outputProp) {
|
|
283
|
+
return getProperTypeShape(outputProp.getTypeAtLocation(callExpression), callExpression)
|
|
348
284
|
}
|
|
349
|
-
|
|
350
|
-
if (typeName === 'ZodEnum') {
|
|
351
|
-
const typeArgs = returnType.getTypeArguments()
|
|
352
|
-
if (typeArgs.length > 0) {
|
|
353
|
-
const enumType = typeArgs[0]
|
|
354
|
-
const properties = enumType.getProperties()
|
|
355
|
-
const shapes: ShapeOfUnionEntry[] = properties.map((prop) => ({
|
|
356
|
-
role: 'union_entry' as const,
|
|
357
|
-
shape: getProperTypeShape(prop.getTypeAtLocation(callExpression), callExpression, []),
|
|
358
|
-
optional: false,
|
|
359
|
-
}))
|
|
360
|
-
if (shapes.length === 1) {
|
|
361
|
-
return shapes[0].shape
|
|
362
|
-
}
|
|
363
|
-
if (shapes.length > 1) {
|
|
364
|
-
return [
|
|
365
|
-
{
|
|
366
|
-
role: 'union' as const,
|
|
367
|
-
shape: shapes,
|
|
368
|
-
optional: false,
|
|
369
|
-
},
|
|
370
|
-
]
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return 'unknown_zod_enum'
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (typeName === 'ZodOptional') {
|
|
377
|
-
const innerCallExpression = callExpression
|
|
378
|
-
.getFirstChildByKind(SyntaxKind.PropertyAccessExpression)
|
|
379
|
-
?.getFirstChildByKind(SyntaxKind.CallExpression)
|
|
380
|
-
if (innerCallExpression && isZodCallExpression(innerCallExpression)) {
|
|
381
|
-
return getZodCallShape(innerCallExpression)
|
|
382
|
-
}
|
|
383
|
-
return 'unknown_zod_optional'
|
|
384
|
-
}
|
|
385
|
-
|
|
386
285
|
const fileName = node.getSourceFile().getFilePath().split('/').pop()
|
|
286
|
+
const typeName = returnType.getSymbol()?.getName() ?? ''
|
|
387
287
|
Logger.warn(`[${fileName}] Unknown zod type: ${typeName}`)
|
|
388
288
|
return 'unknown_zod'
|
|
389
289
|
}
|
|
@@ -11,6 +11,7 @@ export const TestCase = {
|
|
|
11
11
|
parsesZodQueryNumberArray: 'parses-zod-query-number-array',
|
|
12
12
|
parsesZodQueryObjectArray: 'parses-zod-query-object-array',
|
|
13
13
|
parsesZodQueryOptionalArray: 'parses-zod-query-optional-array',
|
|
14
|
+
parsesZodPipe: 'parses-zod-pipe',
|
|
14
15
|
parsesReturnRecordStringUnknown: 'parses-return-record-string-unknown',
|
|
15
16
|
parsesReturnObjectWithRecordProperty: 'parses-return-object-with-record-property',
|
|
16
17
|
parsesBufferReturnedFromFunction: 'parses-buffer-returned-from-function',
|
|
@@ -147,3 +147,9 @@ router.get(`/test/${TestCase.parsesZodQueryOptionalArray}`, (ctx) => {
|
|
|
147
147
|
tags: z.array(z.string()).optional(),
|
|
148
148
|
})
|
|
149
149
|
})
|
|
150
|
+
|
|
151
|
+
router.post(`/test/${TestCase.parsesZodPipe}`, (ctx) => {
|
|
152
|
+
useRequestBody(ctx, {
|
|
153
|
+
value: z.string().pipe(z.coerce.number()),
|
|
154
|
+
})
|
|
155
|
+
})
|
|
@@ -315,6 +315,14 @@ describe('OpenApi Analyzer (Zod Validator)', () => {
|
|
|
315
315
|
])
|
|
316
316
|
expect(endpoint.requestQuery[0].optional).toEqual(true)
|
|
317
317
|
})
|
|
318
|
+
|
|
319
|
+
it('parses zod pipe validators', () => {
|
|
320
|
+
const endpoint = analyzeEndpointById(TestCase.parsesZodPipe)
|
|
321
|
+
|
|
322
|
+
expect(endpoint.objectBody[0].identifier).toEqual('value')
|
|
323
|
+
expect(endpoint.objectBody[0].signature).toEqual('number')
|
|
324
|
+
expect(endpoint.objectBody[0].optional).toEqual(false)
|
|
325
|
+
})
|
|
318
326
|
})
|
|
319
327
|
})
|
|
320
328
|
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { buildSync } from 'esbuild'
|
|
2
|
+
import { existsSync, unlinkSync } from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const workerSrc = path.join(__dirname, '..', 'analyzerWorker.ts')
|
|
8
|
+
const workerOut = path.join(__dirname, '..', 'analyzerWorker.test.mjs')
|
|
9
|
+
|
|
10
|
+
export function setup() {
|
|
11
|
+
buildSync({
|
|
12
|
+
entryPoints: [workerSrc],
|
|
13
|
+
outfile: workerOut,
|
|
14
|
+
bundle: true,
|
|
15
|
+
format: 'esm',
|
|
16
|
+
platform: 'node',
|
|
17
|
+
external: ['ts-morph', '@ts-morph/common', 'typescript', 'worker_threads'],
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function teardown() {
|
|
22
|
+
if (existsSync(workerOut)) {
|
|
23
|
+
unlinkSync(workerOut)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import os from 'os'
|
|
2
|
+
import { Worker } from 'worker_threads'
|
|
3
|
+
|
|
4
|
+
import { EndpointData } from '../types'
|
|
5
|
+
import { SectionTiming } from './parseEndpoint'
|
|
6
|
+
|
|
7
|
+
export type WorkerTask = {
|
|
8
|
+
taskId: string
|
|
9
|
+
tsconfigPath: string
|
|
10
|
+
sourceFilePath: string
|
|
11
|
+
routerName: string
|
|
12
|
+
endpointIndex: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type WorkerResultSuccess = {
|
|
16
|
+
taskId: string
|
|
17
|
+
endpoint: EndpointData
|
|
18
|
+
sectionTimings: SectionTiming[]
|
|
19
|
+
timing: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type WorkerResultError = {
|
|
23
|
+
taskId: string
|
|
24
|
+
error: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type WorkerResult = WorkerResultSuccess | WorkerResultError
|
|
28
|
+
|
|
29
|
+
export class WorkerPool {
|
|
30
|
+
private workers: Worker[]
|
|
31
|
+
private idle: Worker[]
|
|
32
|
+
private queue: Array<{ task: WorkerTask; resolve: (r: WorkerResult) => void }> = []
|
|
33
|
+
private pending = new Map<string, (r: WorkerResult) => void>()
|
|
34
|
+
|
|
35
|
+
constructor(workerUrl: URL) {
|
|
36
|
+
const size = Math.max(1, Math.min(os.cpus().length - 1, 8))
|
|
37
|
+
this.workers = Array.from({ length: size }, () => {
|
|
38
|
+
const worker = new Worker(workerUrl)
|
|
39
|
+
worker.on('message', (result: WorkerResult) => {
|
|
40
|
+
const resolve = this.pending.get(result.taskId)
|
|
41
|
+
if (resolve) {
|
|
42
|
+
this.pending.delete(result.taskId)
|
|
43
|
+
resolve(result)
|
|
44
|
+
}
|
|
45
|
+
this.idle.push(worker)
|
|
46
|
+
this.flush()
|
|
47
|
+
})
|
|
48
|
+
worker.on('error', (err) => {
|
|
49
|
+
// Find any pending task for this worker and reject it
|
|
50
|
+
// (worker crashed — shouldn't happen, but handle gracefully)
|
|
51
|
+
for (const [taskId, resolve] of this.pending) {
|
|
52
|
+
resolve({ taskId, error: String(err) })
|
|
53
|
+
this.pending.delete(taskId)
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
this.idle.push(worker)
|
|
57
|
+
this.flush()
|
|
58
|
+
})
|
|
59
|
+
return worker
|
|
60
|
+
})
|
|
61
|
+
this.idle = [...this.workers]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
run(task: WorkerTask): Promise<WorkerResult> {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
if (this.idle.length > 0) {
|
|
67
|
+
const worker = this.idle.pop()!
|
|
68
|
+
this.pending.set(task.taskId, resolve)
|
|
69
|
+
worker.postMessage(task)
|
|
70
|
+
} else {
|
|
71
|
+
this.queue.push({ task, resolve })
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
runAll(tasks: WorkerTask[]): Promise<WorkerResult[]> {
|
|
77
|
+
return Promise.all(tasks.map((t) => this.run(t)))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
terminate() {
|
|
81
|
+
this.workers.forEach((w) => w.terminate())
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private flush() {
|
|
85
|
+
while (this.queue.length > 0 && this.idle.length > 0) {
|
|
86
|
+
const { task, resolve } = this.queue.shift()!
|
|
87
|
+
const worker = this.idle.pop()!
|
|
88
|
+
this.pending.set(task.taskId, resolve)
|
|
89
|
+
worker.postMessage(task)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/test/app.spec.ts
CHANGED
|
@@ -3,13 +3,22 @@ import Koa from 'koa'
|
|
|
3
3
|
import * as os from 'os'
|
|
4
4
|
import * as path from 'path'
|
|
5
5
|
import request from 'supertest'
|
|
6
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
6
|
+
import { beforeAll, describe, expect, it, vi } from 'vitest'
|
|
7
7
|
|
|
8
8
|
import { generateOpenApiSpec } from '../openapi/generatorModule/generatorModule'
|
|
9
9
|
import { initOpenApiEngine } from '../openapi/initOpenApiEngine'
|
|
10
10
|
import { OpenApiManager } from '../openapi/manager/OpenApiManager'
|
|
11
11
|
import { app } from './app'
|
|
12
12
|
|
|
13
|
+
// The OpenAPI engine runs the TypeScript analyzer lazily on first request, and any request that
|
|
14
|
+
// reaches the initOpenApiEngine middleware awaits its readiness. Requests that match a route never
|
|
15
|
+
// reach it, but unmatched ones (e.g. the 405 test) do, so the first such request pays the full
|
|
16
|
+
// analyzer cold-start cost. That can exceed a single test's default 5s timeout in CI. Warm the
|
|
17
|
+
// engine up once here so no individual test bears that cost.
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
await request(app.callback()).get('/api-json')
|
|
20
|
+
}, 60_000)
|
|
21
|
+
|
|
13
22
|
describe('TestAppRouter', () => {
|
|
14
23
|
it('includes content type header', async () => {
|
|
15
24
|
const response = await request(app.callback()).get('/test/hello')
|
package/vite.config.ts
CHANGED
|
@@ -24,6 +24,7 @@ const entries = {
|
|
|
24
24
|
'validators/BuiltInValidators': resolve(__dirname, 'src/validators/BuiltInValidators.ts'),
|
|
25
25
|
'validators/ParamWrappers': resolve(__dirname, 'src/validators/ParamWrappers.ts'),
|
|
26
26
|
'cli/cli': resolve(__dirname, 'src/cli/cli.ts'),
|
|
27
|
+
'openapi/analyzerModule/analyzerWorker': resolve(__dirname, 'src/openapi/analyzerModule/analyzerWorker.ts'),
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export const baseViteConfig: ViteUserConfig = {
|
|
@@ -50,7 +51,10 @@ export const baseViteConfig: ViteUserConfig = {
|
|
|
50
51
|
'fs/promises',
|
|
51
52
|
'assert',
|
|
52
53
|
'crypto',
|
|
54
|
+
'os',
|
|
53
55
|
'perf_hooks',
|
|
56
|
+
'url',
|
|
57
|
+
'worker_threads',
|
|
54
58
|
'@ts-morph/common',
|
|
55
59
|
'typescript',
|
|
56
60
|
'ts-morph',
|
|
@@ -90,6 +94,7 @@ export const baseViteConfig: ViteUserConfig = {
|
|
|
90
94
|
},
|
|
91
95
|
test: {
|
|
92
96
|
globals: true,
|
|
97
|
+
globalSetup: 'src/openapi/analyzerModule/test/workerGlobalSetup.ts',
|
|
93
98
|
setupFiles: 'src/setupTests.ts',
|
|
94
99
|
include: ['src/**/*.spec.ts'],
|
|
95
100
|
coverage: {
|