@rpcbase/test 0.307.0 → 0.309.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.
Files changed (83) hide show
  1. package/README.md +2 -2
  2. package/dist/clearDatabase.d.ts +2 -0
  3. package/dist/clearDatabase.d.ts.map +1 -0
  4. package/dist/clearDatabase.js +12 -0
  5. package/dist/clearDatabase.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +785 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/coverage/collect.d.ts +5 -0
  11. package/dist/coverage/collect.d.ts.map +1 -0
  12. package/dist/coverage/collect.js +113 -0
  13. package/dist/coverage/collect.js.map +1 -0
  14. package/dist/coverage/config-loader.d.ts +11 -0
  15. package/dist/coverage/config-loader.d.ts.map +1 -0
  16. package/dist/coverage/config-loader.js +292 -0
  17. package/dist/coverage/config-loader.js.map +1 -0
  18. package/dist/coverage/config.d.ts +3 -0
  19. package/dist/coverage/config.d.ts.map +1 -0
  20. package/dist/coverage/config.js +110 -0
  21. package/dist/coverage/config.js.map +1 -0
  22. package/dist/coverage/files.d.ts +4 -0
  23. package/dist/coverage/files.d.ts.map +1 -0
  24. package/dist/coverage/files.js +52 -0
  25. package/dist/coverage/files.js.map +1 -0
  26. package/dist/coverage/fixtures.d.ts +5 -0
  27. package/dist/coverage/fixtures.d.ts.map +1 -0
  28. package/dist/coverage/fixtures.js +42 -0
  29. package/dist/coverage/fixtures.js.map +1 -0
  30. package/dist/coverage/global-setup.d.ts +3 -0
  31. package/dist/coverage/global-setup.d.ts.map +1 -0
  32. package/dist/coverage/global-setup.js +18 -0
  33. package/dist/coverage/global-setup.js.map +1 -0
  34. package/dist/coverage/index.d.ts +10 -0
  35. package/dist/coverage/index.d.ts.map +1 -0
  36. package/dist/coverage/index.js +62 -0
  37. package/dist/coverage/index.js.map +1 -0
  38. package/dist/coverage/report.d.ts +7 -0
  39. package/dist/coverage/report.d.ts.map +1 -0
  40. package/dist/coverage/report.js +524 -0
  41. package/dist/coverage/report.js.map +1 -0
  42. package/dist/coverage/reporter.d.ts +16 -0
  43. package/dist/coverage/reporter.d.ts.map +1 -0
  44. package/dist/coverage/reporter.js +57 -0
  45. package/dist/coverage/reporter.js.map +1 -0
  46. package/dist/coverage/types.d.ts +47 -0
  47. package/dist/coverage/types.d.ts.map +1 -0
  48. package/dist/coverage/v8-tracker.d.ts +6 -0
  49. package/dist/coverage/v8-tracker.d.ts.map +1 -0
  50. package/dist/coverage/v8-tracker.js +166 -0
  51. package/dist/coverage/v8-tracker.js.map +1 -0
  52. package/dist/defineConfig.d.ts +3 -0
  53. package/dist/defineConfig.d.ts.map +1 -0
  54. package/dist/index.d.ts +11 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +61 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/serverCoverage.d.ts +8 -0
  59. package/dist/serverCoverage.d.ts.map +1 -0
  60. package/dist/serverCoverage.js +42 -0
  61. package/dist/serverCoverage.js.map +1 -0
  62. package/dist/vitest.config.d.ts +3 -0
  63. package/dist/vitest.config.d.ts.map +1 -0
  64. package/dist/vitest.config.js +83 -0
  65. package/dist/vitest.config.js.map +1 -0
  66. package/package.json +25 -14
  67. package/index.d.ts +0 -64
  68. package/src/clearDatabase.js +0 -19
  69. package/src/cli.js +0 -501
  70. package/src/coverage/collect.js +0 -134
  71. package/src/coverage/config-loader.js +0 -206
  72. package/src/coverage/config.js +0 -134
  73. package/src/coverage/files.js +0 -55
  74. package/src/coverage/fixtures.js +0 -37
  75. package/src/coverage/global-setup.js +0 -19
  76. package/src/coverage/index.js +0 -30
  77. package/src/coverage/report.js +0 -600
  78. package/src/coverage/reporter.js +0 -65
  79. package/src/coverage/v8-tracker.js +0 -205
  80. package/src/defineConfig.js +0 -129
  81. package/src/index.js +0 -49
  82. package/src/vitest.config.mjs +0 -107
  83. /package/{src → dist}/register-tty.cjs +0 -0
@@ -1,19 +0,0 @@
1
- import mongoose from "mongoose"
2
-
3
-
4
- export async function clearDatabase(dbName) {
5
- const { DB_PORT, APP_NAME } = process.env
6
-
7
- const connectionString = `mongodb://localhost:${DB_PORT}/${APP_NAME}-${dbName}`
8
-
9
- // console.log("will clear DB:", connectionString)
10
-
11
- const connection = await mongoose
12
- .createConnection(connectionString)
13
- .asPromise()
14
-
15
- // Drop the database
16
- await connection.dropDatabase()
17
-
18
- await connection.close()
19
- }
package/src/cli.js DELETED
@@ -1,501 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "child_process"
4
- import fs from "fs"
5
- import fsPromises from "fs/promises"
6
- import path from "path"
7
- import { createRequire } from "module"
8
- import { fileURLToPath } from "url"
9
-
10
- import { createCoverageConfig } from "./coverage/config.js"
11
- import { isInsideAnyRoot, resolveCollectCoverageRoots } from "./coverage/collect.js"
12
- import { loadCoverageOptions } from "./coverage/config-loader.js"
13
- import { removeCoverageFiles } from "./coverage/files.js"
14
- import { CoverageThresholdError, generateCoverageReport } from "./coverage/report.js"
15
-
16
-
17
- const require = createRequire(import.meta.url)
18
- const moduleDir = path.dirname(fileURLToPath(import.meta.url))
19
-
20
-
21
- const VITEST_COVERAGE_CANDIDATES = ["src/coverage.json"]
22
-
23
- const COMBINED_COVERAGE_ENV_VAR = "RB_TEST_COMBINED_COVERAGE"
24
-
25
- const isAider = process.env.IS_AIDER === "yes"
26
-
27
- if (process.env.IS_AIDER !== undefined && process.env.IS_AIDER !== "yes") {
28
- console.warn("Warning: IS_AIDER is set to a value other than 'yes'.")
29
- }
30
-
31
- async function runTests() {
32
- const userArgs = process.argv.slice(2)
33
-
34
- const playwrightCoverage = await loadPlaywrightCoverageConfig()
35
- const vitestCoverage = await loadVitestCoverageConfig()
36
- const combinedCoverage = resolveCombinedCoverage(playwrightCoverage, vitestCoverage)
37
-
38
- if (combinedCoverage?.enabled) {
39
- await cleanCoverageArtifacts(combinedCoverage.config)
40
- }
41
-
42
- let testError = null
43
-
44
- try {
45
- await runVitest(vitestCoverage, combinedCoverage?.config ?? null)
46
- console.log("\nRunning Playwright Tests...")
47
- await runPlaywright(userArgs)
48
- } catch (error) {
49
- testError = error
50
- }
51
-
52
- if (combinedCoverage?.enabled) {
53
- try {
54
- await finalizeCoverage(combinedCoverage.config)
55
- } catch (error) {
56
- if (!testError) {
57
- testError = error
58
- }
59
- }
60
- }
61
-
62
- if (testError) {
63
- throw testError
64
- }
65
- }
66
-
67
- runTests()
68
- .then(() => process.exit(0))
69
- .catch((error) => {
70
- if (!(error instanceof CoverageThresholdError)) {
71
- console.error(error?.stack ?? String(error))
72
- }
73
- process.exit(1)
74
- })
75
-
76
- async function runVitest(coverage, combinedConfig) {
77
- const vitestArgs = ["run", "--passWithNoTests"]
78
- const vitestConfig = resolveVitestConfig()
79
-
80
- if (vitestConfig) {
81
- vitestArgs.push("--config", vitestConfig)
82
- }
83
- const launcher = resolveVitestLauncher()
84
- const env = withRegisterShim(process.env)
85
-
86
- if (coverage?.enabled) {
87
- env.NODE_V8_COVERAGE = resolveNodeCoverageDir(combinedConfig ?? coverage.config)
88
- }
89
-
90
- await spawnWithLogs({
91
- name: "Vitest",
92
- launcher,
93
- args: vitestArgs,
94
- env,
95
- successMessage: "Vitest suite passed!",
96
- failureMessage: "Vitest failed",
97
- })
98
-
99
- if (coverage?.enabled) {
100
- await convertNodeCoverage({
101
- config: combinedConfig ?? coverage.config,
102
- nodeCoverageDir: resolveNodeCoverageDir(combinedConfig ?? coverage.config),
103
- })
104
- }
105
- }
106
-
107
- function runPlaywright(userArgs) {
108
- // Determine config file path
109
- const configPath = fs.existsSync(
110
- path.join(process.cwd(), "playwright.config.ts"),
111
- )
112
- ? path.join(process.cwd(), "playwright.config.ts")
113
- : path.join(moduleDir, "playwright.config.ts")
114
-
115
- const hasCustomConfig = userArgs.some((arg) => {
116
- if (arg === "--config" || arg === "-c") {
117
- return true
118
- }
119
-
120
- return arg.startsWith("--config=")
121
- })
122
-
123
- const playwrightArgs = ["test"]
124
-
125
- if (!hasCustomConfig) {
126
- playwrightArgs.push("--config", configPath)
127
- }
128
-
129
- playwrightArgs.push(...userArgs)
130
-
131
- ensureJsxRuntimeShim(process.cwd())
132
- const launcher = resolvePlaywrightLauncher()
133
- const env = withRegisterShim(process.env)
134
- env[COMBINED_COVERAGE_ENV_VAR] = "1"
135
-
136
- return spawnWithLogs({
137
- name: "Playwright",
138
- launcher,
139
- args: playwrightArgs,
140
- env,
141
- successMessage: "Playwright suite passed!",
142
- failureMessage: "Playwright failed",
143
- })
144
- }
145
-
146
- function resolvePlaywrightLauncher() {
147
- const cliPath = resolveCliPath()
148
- if (cliPath) {
149
- return {
150
- command: process.execPath,
151
- args: [cliPath],
152
- }
153
- }
154
-
155
- const localBin = path.resolve(process.cwd(), "node_modules/.bin/playwright")
156
- if (fs.existsSync(localBin)) {
157
- return {
158
- command: localBin,
159
- args: [],
160
- }
161
- }
162
-
163
- return {
164
- command: "playwright",
165
- args: [],
166
- }
167
- }
168
-
169
- function resolveCliPath() {
170
- const searchRoots = [process.cwd(), moduleDir]
171
-
172
- for (const base of searchRoots) {
173
- try {
174
- const pkgPath = require.resolve("@playwright/test/package.json", { paths: [base] })
175
- const cliPath = path.join(path.dirname(pkgPath), "cli.js")
176
- if (fs.existsSync(cliPath)) {
177
- return cliPath
178
- }
179
- } catch (_error) {
180
- // continue searching
181
- }
182
- }
183
-
184
- return null
185
- }
186
-
187
- function resolveVitestLauncher() {
188
- const searchRoots = [process.cwd(), moduleDir]
189
-
190
- for (const base of searchRoots) {
191
- try {
192
- const pkgPath = require.resolve("vitest/package.json", { paths: [base] })
193
- const pkgDir = path.dirname(pkgPath)
194
- const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
195
- const binPath = typeof pkgJson.bin === "string" ? pkgJson.bin : pkgJson.bin?.vitest
196
- if (binPath) {
197
- return {
198
- command: process.execPath,
199
- args: [path.join(pkgDir, binPath)],
200
- }
201
- }
202
- } catch (_error) {
203
- // continue searching
204
- }
205
- }
206
-
207
- const localBin = path.resolve(process.cwd(), "node_modules/.bin/vitest")
208
- if (fs.existsSync(localBin)) {
209
- return {
210
- command: localBin,
211
- args: [],
212
- }
213
- }
214
-
215
- return {
216
- command: "vitest",
217
- args: [],
218
- }
219
- }
220
-
221
- function resolveVitestConfig() {
222
- const userConfig = findVitestConfig(process.cwd())
223
- if (userConfig) {
224
- return userConfig
225
- }
226
-
227
- const bundledConfig = path.join(moduleDir, "vitest.config.mjs")
228
- return fs.existsSync(bundledConfig) ? bundledConfig : null
229
- }
230
-
231
- function findVitestConfig(baseDir) {
232
- const candidates = [
233
- "vitest.config.ts",
234
- "vitest.config.js",
235
- "vitest.config.mjs",
236
- ]
237
-
238
- for (const file of candidates) {
239
- const fullPath = path.join(baseDir, file)
240
- if (fs.existsSync(fullPath)) {
241
- return fullPath
242
- }
243
- }
244
-
245
- return null
246
- }
247
-
248
- async function loadVitestCoverageConfig() {
249
- const options = await loadCoverageOptions({
250
- optional: true,
251
- candidates: VITEST_COVERAGE_CANDIDATES,
252
- defaultTestResultsDir: "test-results-vitest",
253
- })
254
- if (!options) {
255
- return null
256
- }
257
-
258
- const config = createCoverageConfig(options)
259
-
260
- return {
261
- config,
262
- enabled: config.coverageEnabled,
263
- }
264
- }
265
-
266
- async function loadPlaywrightCoverageConfig() {
267
- const options = await loadCoverageOptions({ optional: true })
268
- if (!options) {
269
- return null
270
- }
271
-
272
- const config = createCoverageConfig(options)
273
-
274
- return {
275
- config,
276
- enabled: config.coverageEnabled,
277
- }
278
- }
279
-
280
- function resolveCombinedCoverage(playwrightCoverage, vitestCoverage) {
281
- if (playwrightCoverage?.enabled) {
282
- return playwrightCoverage
283
- }
284
-
285
- if (vitestCoverage?.enabled) {
286
- return vitestCoverage
287
- }
288
-
289
- return null
290
- }
291
-
292
- async function cleanCoverageArtifacts(config) {
293
- await removeCoverageFiles(config)
294
- await fsPromises.rm(config.coverageReportDir, { recursive: true, force: true })
295
- await fsPromises.rm(path.join(config.testResultsRoot, "node-coverage"), { recursive: true, force: true })
296
- }
297
-
298
- function resolveNodeCoverageDir(config) {
299
- return path.join(config.testResultsRoot, "node-coverage", "vitest")
300
- }
301
-
302
- async function convertNodeCoverage(coverage) {
303
- const { config, nodeCoverageDir } = coverage
304
-
305
- const entries = await fsPromises.readdir(nodeCoverageDir).catch(() => [])
306
- const scripts = []
307
- const scriptRoots = resolveCollectCoverageRoots(config.collectCoverageFrom, config.rootDir)
308
-
309
- for (const entry of entries) {
310
- if (!entry.endsWith(".json")) {
311
- continue
312
- }
313
-
314
- const fullPath = path.join(nodeCoverageDir, entry)
315
- const payload = await readJson(fullPath)
316
- const results = Array.isArray(payload?.result) ? payload.result : []
317
-
318
- for (const script of results) {
319
- const normalized = normalizeNodeScriptUrl(script.url, config.rootDir)
320
- if (!normalized) {
321
- continue
322
- }
323
-
324
- if (isNodeModulesPath(normalized.absolutePath)) {
325
- continue
326
- }
327
-
328
- if (!isInsideAnyRoot(normalized.absolutePath, scriptRoots)) {
329
- continue
330
- }
331
-
332
- const source = await fsPromises.readFile(normalized.absolutePath, "utf8").catch(() => "")
333
-
334
- scripts.push({
335
- absolutePath: normalized.absolutePath,
336
- relativePath: normalized.relativePath,
337
- source,
338
- functions: script.functions ?? [],
339
- url: script.url,
340
- })
341
- }
342
- }
343
-
344
- if (scripts.length === 0) {
345
- return
346
- }
347
-
348
- const outDir = path.join(config.testResultsRoot, "vitest")
349
- await fsPromises.mkdir(outDir, { recursive: true })
350
- const outputFile = path.join(outDir, config.coverageFileName)
351
- await fsPromises.writeFile(outputFile, JSON.stringify({ testId: "vitest", scripts }, null, 2), "utf8")
352
- }
353
-
354
- async function finalizeCoverage(config) {
355
- try {
356
- await generateCoverageReport(config)
357
- } catch (error) {
358
- if (error instanceof CoverageThresholdError) {
359
- console.error(error.message)
360
- }
361
- throw error
362
- }
363
- }
364
-
365
- async function readJson(filePath) {
366
- try {
367
- const raw = await fsPromises.readFile(filePath, "utf8")
368
- return JSON.parse(raw)
369
- } catch {
370
- return null
371
- }
372
- }
373
-
374
- function normalizeNodeScriptUrl(rawUrl, rootDir) {
375
- if (!rawUrl || rawUrl.startsWith("node:")) {
376
- return null
377
- }
378
-
379
- let absolutePath = null
380
-
381
- try {
382
- if (rawUrl.startsWith("file://")) {
383
- absolutePath = fileURLToPath(rawUrl)
384
- }
385
- } catch (_err) {
386
- // ignore invalid URLs
387
- }
388
-
389
- if (!absolutePath && path.isAbsolute(rawUrl)) {
390
- absolutePath = rawUrl
391
- }
392
-
393
- if (!absolutePath) {
394
- return null
395
- }
396
-
397
- const normalized = path.normalize(absolutePath)
398
-
399
- return {
400
- absolutePath: normalized,
401
- relativePath: path.relative(rootDir, normalized),
402
- }
403
- }
404
-
405
- function isNodeModulesPath(filePath) {
406
- return path
407
- .normalize(String(filePath ?? ""))
408
- .split(path.sep)
409
- .includes("node_modules")
410
- }
411
-
412
- function spawnWithLogs({ name, launcher, args, env, successMessage, failureMessage }) {
413
- return new Promise((resolve, reject) => {
414
- const stdoutBuffer = []
415
- const stderrBuffer = []
416
-
417
- const child = spawn(
418
- launcher.command,
419
- [...(launcher.args || []), ...args],
420
- {
421
- shell: false,
422
- env,
423
- },
424
- )
425
-
426
- child.stdout.on("data", (data) => {
427
- if (!isAider) {
428
- process.stdout.write(data)
429
- }
430
- stdoutBuffer.push(data.toString())
431
- })
432
-
433
- child.stderr.on("data", (data) => {
434
- if (!isAider) {
435
- process.stderr.write(data)
436
- }
437
- stderrBuffer.push(data.toString())
438
- })
439
-
440
- child.on("close", (code) => {
441
- if (code === 0) {
442
- if (successMessage) {
443
- console.log(successMessage)
444
- }
445
- resolve()
446
- } else {
447
- console.error(failureMessage || `${name} failed:`)
448
-
449
- if (isAider) {
450
- if (stdoutBuffer.length > 0) {
451
- console.error(stdoutBuffer.join(""))
452
- }
453
-
454
- if (stderrBuffer.length > 0) {
455
- console.error(stderrBuffer.join(""))
456
- }
457
- }
458
-
459
- reject(new Error(`${name} failed with exit code: ${code}`))
460
- }
461
- })
462
-
463
- child.on("error", (error) => {
464
- console.error(`Error spawning ${name}:`, error)
465
- reject(error)
466
- })
467
- })
468
- }
469
-
470
- function withRegisterShim(baseEnv) {
471
- const nodeOptions = appendNodeRequire(baseEnv.NODE_OPTIONS, path.join(moduleDir, "register-tty.cjs"))
472
- return {
473
- ...baseEnv,
474
- NODE_OPTIONS: nodeOptions,
475
- }
476
- }
477
-
478
- function ensureJsxRuntimeShim(projectRoot) {
479
- const shimDir = path.join(projectRoot, "node_modules", "playwright")
480
- fs.mkdirSync(shimDir, { recursive: true })
481
- const shims = [
482
- { file: "jsx-runtime.js", target: "react/jsx-runtime" },
483
- { file: "jsx-dev-runtime.js", target: "react/jsx-dev-runtime" },
484
- ]
485
-
486
- for (const { file, target } of shims) {
487
- const filePath = path.join(shimDir, file)
488
- if (!fs.existsSync(filePath)) {
489
- const content = `export * from "${target}";\nexport { default } from "${target}";\n`
490
- fs.writeFileSync(filePath, content, "utf8")
491
- }
492
- }
493
- }
494
-
495
- function appendNodeRequire(existing, modulePath) {
496
- const flag = `--require=${modulePath}`
497
- if (!existing || existing.length === 0) {
498
- return flag
499
- }
500
- return `${existing} ${flag}`
501
- }
@@ -1,134 +0,0 @@
1
- import path from "node:path"
2
-
3
- import picomatch from "picomatch"
4
-
5
-
6
- export function createCollectCoverageMatcher(patterns, rootDir) {
7
- const normalizedRoot = path.resolve(String(rootDir ?? ""))
8
-
9
- const includeMatchers = []
10
- const excludeMatchers = []
11
-
12
- if (Array.isArray(patterns)) {
13
- for (const pattern of patterns) {
14
- const raw = String(pattern ?? "").trim()
15
- if (!raw) {
16
- continue
17
- }
18
-
19
- const isExclude = raw.startsWith("!")
20
- const body = toPosix(isExclude ? raw.slice(1) : raw)
21
- if (!body) {
22
- continue
23
- }
24
-
25
- const matcher = picomatch(body, { dot: true })
26
- if (isExclude) {
27
- excludeMatchers.push(matcher)
28
- } else {
29
- includeMatchers.push(matcher)
30
- }
31
- }
32
- }
33
-
34
- if (includeMatchers.length === 0) {
35
- return () => false
36
- }
37
-
38
- return (absolutePath) => {
39
- const normalizedAbsolute = path.resolve(String(absolutePath ?? ""))
40
- if (!normalizedAbsolute) {
41
- return false
42
- }
43
-
44
- const relativePosix = toPosix(path.relative(normalizedRoot, normalizedAbsolute))
45
- const absolutePosix = toPosix(normalizedAbsolute)
46
- const candidates = new Set([absolutePosix, relativePosix])
47
-
48
- if (relativePosix) {
49
- candidates.add(`./${relativePosix}`)
50
- }
51
-
52
- const candidateList = Array.from(candidates)
53
- const included = includeMatchers.some((matcher) => candidateList.some((candidate) => matcher(candidate)))
54
- if (!included) {
55
- return false
56
- }
57
-
58
- return !excludeMatchers.some((matcher) => candidateList.some((candidate) => matcher(candidate)))
59
- }
60
- }
61
-
62
- export function toPosix(input) {
63
- return String(input ?? "").split(path.sep).join("/")
64
- }
65
-
66
- export function isInsideAnyRoot(absolutePath, roots) {
67
- const normalizedAbsolute = path.resolve(String(absolutePath ?? ""))
68
- return (Array.isArray(roots) ? roots : []).some((root) => {
69
- const normalizedRoot = path.resolve(String(root ?? ""))
70
- const relative = path.relative(normalizedRoot, normalizedAbsolute)
71
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))
72
- })
73
- }
74
-
75
- export function resolveCollectCoverageRoots(patterns, rootDir) {
76
- const resolvedRootDir = path.resolve(String(rootDir ?? ""))
77
- const roots = new Set([resolvedRootDir])
78
-
79
- if (!Array.isArray(patterns)) {
80
- return Array.from(roots)
81
- }
82
-
83
- for (const pattern of patterns) {
84
- const raw = String(pattern ?? "").trim()
85
- if (!raw) {
86
- continue
87
- }
88
-
89
- const cleaned = raw.startsWith("!") ? raw.slice(1) : raw
90
- if (!cleaned) {
91
- continue
92
- }
93
-
94
- const prefix = staticGlobPrefix(cleaned)
95
- if (!prefix) {
96
- continue
97
- }
98
-
99
- roots.add(path.resolve(resolvedRootDir, prefix))
100
- }
101
-
102
- return Array.from(roots)
103
- }
104
-
105
- function staticGlobPrefix(pattern) {
106
- const normalized = String(pattern ?? "").trim()
107
- if (!normalized) {
108
- return null
109
- }
110
-
111
- const firstGlobIndex = findFirstGlobIndex(normalized)
112
- const prefix = firstGlobIndex === -1 ? normalized : normalized.slice(0, firstGlobIndex)
113
-
114
- if (!prefix) {
115
- return null
116
- }
117
-
118
- if (prefix.endsWith("/") || prefix.endsWith(path.sep)) {
119
- return prefix
120
- }
121
-
122
- const posix = prefix.split(path.sep).join("/")
123
- return path.posix.dirname(posix)
124
- }
125
-
126
- function findFirstGlobIndex(value) {
127
- for (let i = 0; i < value.length; i += 1) {
128
- const char = value[i]
129
- if (char === "*" || char === "?" || char === "[" || char === "{") {
130
- return i
131
- }
132
- }
133
- return -1
134
- }