@buzola/codegen 0.0.1

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 (76) hide show
  1. package/dist/__tests__/codegen.test.d.ts +2 -0
  2. package/dist/__tests__/codegen.test.d.ts.map +1 -0
  3. package/dist/__tests__/codegen.test.js +208 -0
  4. package/dist/__tests__/codegen.test.js.map +1 -0
  5. package/dist/__tests__/conventions.test.d.ts +2 -0
  6. package/dist/__tests__/conventions.test.d.ts.map +1 -0
  7. package/dist/__tests__/conventions.test.js +80 -0
  8. package/dist/__tests__/conventions.test.js.map +1 -0
  9. package/dist/__tests__/page-extractor.test.d.ts +2 -0
  10. package/dist/__tests__/page-extractor.test.d.ts.map +1 -0
  11. package/dist/__tests__/page-extractor.test.js +82 -0
  12. package/dist/__tests__/page-extractor.test.js.map +1 -0
  13. package/dist/__tests__/resolve.test.d.ts +2 -0
  14. package/dist/__tests__/resolve.test.d.ts.map +1 -0
  15. package/dist/__tests__/resolve.test.js +110 -0
  16. package/dist/__tests__/resolve.test.js.map +1 -0
  17. package/dist/__tests__/tree-builder.test.d.ts +2 -0
  18. package/dist/__tests__/tree-builder.test.d.ts.map +1 -0
  19. package/dist/__tests__/tree-builder.test.js +474 -0
  20. package/dist/__tests__/tree-builder.test.js.map +1 -0
  21. package/dist/cli.d.ts +3 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +40 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/config.d.ts +20 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +20 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/conventions.d.ts +53 -0
  30. package/dist/conventions.d.ts.map +1 -0
  31. package/dist/conventions.js +82 -0
  32. package/dist/conventions.js.map +1 -0
  33. package/dist/generate.d.ts +28 -0
  34. package/dist/generate.d.ts.map +1 -0
  35. package/dist/generate.js +46 -0
  36. package/dist/generate.js.map +1 -0
  37. package/dist/generator/codegen.d.ts +19 -0
  38. package/dist/generator/codegen.d.ts.map +1 -0
  39. package/dist/generator/codegen.js +162 -0
  40. package/dist/generator/codegen.js.map +1 -0
  41. package/dist/generator/page-extractor.d.ts +21 -0
  42. package/dist/generator/page-extractor.d.ts.map +1 -0
  43. package/dist/generator/page-extractor.js +23 -0
  44. package/dist/generator/page-extractor.js.map +1 -0
  45. package/dist/generator/scanner.d.ts +11 -0
  46. package/dist/generator/scanner.d.ts.map +1 -0
  47. package/dist/generator/scanner.js +34 -0
  48. package/dist/generator/scanner.js.map +1 -0
  49. package/dist/generator/tree-builder.d.ts +54 -0
  50. package/dist/generator/tree-builder.d.ts.map +1 -0
  51. package/dist/generator/tree-builder.js +355 -0
  52. package/dist/generator/tree-builder.js.map +1 -0
  53. package/dist/index.d.ts +17 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +9 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/resolve.d.ts +49 -0
  58. package/dist/resolve.d.ts.map +1 -0
  59. package/dist/resolve.js +41 -0
  60. package/dist/resolve.js.map +1 -0
  61. package/package.json +34 -0
  62. package/src/__tests__/codegen.test.ts +229 -0
  63. package/src/__tests__/conventions.test.ts +93 -0
  64. package/src/__tests__/page-extractor.test.ts +95 -0
  65. package/src/__tests__/resolve.test.ts +120 -0
  66. package/src/__tests__/tree-builder.test.ts +554 -0
  67. package/src/cli.ts +43 -0
  68. package/src/config.ts +35 -0
  69. package/src/conventions.ts +116 -0
  70. package/src/generate.ts +67 -0
  71. package/src/generator/codegen.ts +213 -0
  72. package/src/generator/page-extractor.ts +48 -0
  73. package/src/generator/scanner.ts +45 -0
  74. package/src/generator/tree-builder.ts +449 -0
  75. package/src/index.ts +23 -0
  76. package/src/resolve.ts +75 -0
@@ -0,0 +1,229 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { generateRouteModule } from '../generator/codegen.js'
3
+
4
+ describe('generateRouteModule', () => {
5
+ it('emits BuzolaPersistentParams when persistentParams is provided', () => {
6
+ const output = generateRouteModule({
7
+ tree: [],
8
+ routesDir: '/tmp/routes',
9
+ outputPath: '/tmp/buzola.gen.ts',
10
+ persistentParams: ['lang'],
11
+ })
12
+
13
+ expect(output).toContain('interface BuzolaPersistentParams')
14
+ expect(output).toContain('lang: true;')
15
+ })
16
+
17
+ it('quotes hyphenated persistentParams keys', () => {
18
+ const output = generateRouteModule({
19
+ tree: [],
20
+ routesDir: '/tmp/routes',
21
+ outputPath: '/tmp/buzola.gen.ts',
22
+ persistentParams: ['create-new-project', 'source', 'utm-medium'],
23
+ })
24
+
25
+ expect(output).toContain("'create-new-project': true;")
26
+ expect(output).toContain('source: true;')
27
+ expect(output).toContain("'utm-medium': true;")
28
+ })
29
+
30
+ it('quotes hyphenated page param keys in BuzolaPageMap', () => {
31
+ const output = generateRouteModule({
32
+ tree: [{
33
+ segment: 'users',
34
+ fullPath: '/users',
35
+ filePath: '/tmp/routes/users.tsx',
36
+ exportName: 'default',
37
+ isLayout: false,
38
+ isIndex: false,
39
+ isNotFound: false,
40
+ isPathlessGroup: false,
41
+ pageExports: [{
42
+ pageId: 'users',
43
+ exportName: 'default',
44
+ routePattern: '/users',
45
+ params: [
46
+ { name: 'create-new-project', optional: true, array: false },
47
+ { name: 'source', optional: false, array: false },
48
+ ],
49
+ }],
50
+ children: [],
51
+ }],
52
+ routesDir: '/tmp/routes',
53
+ outputPath: '/tmp/buzola.gen.ts',
54
+ })
55
+
56
+ expect(output).toContain("'create-new-project'?: string")
57
+ expect(output).toContain('source: string')
58
+ })
59
+
60
+ it('does not emit BuzolaPersistentParams without persistentParams', () => {
61
+ const output = generateRouteModule({
62
+ tree: [],
63
+ routesDir: '/tmp/routes',
64
+ outputPath: '/tmp/buzola.gen.ts',
65
+ })
66
+
67
+ expect(output).not.toContain('BuzolaPersistentParams')
68
+ })
69
+
70
+ it('emits BuzolaPageMap interface', () => {
71
+ const output = generateRouteModule({
72
+ tree: [],
73
+ routesDir: '/tmp/routes',
74
+ outputPath: '/tmp/buzola.gen.ts',
75
+ })
76
+
77
+ expect(output).toContain('interface BuzolaPageMap')
78
+ })
79
+
80
+ it('emits pageRegistry export', () => {
81
+ const output = generateRouteModule({
82
+ tree: [],
83
+ routesDir: '/tmp/routes',
84
+ outputPath: '/tmp/buzola.gen.ts',
85
+ })
86
+
87
+ expect(output).toContain('export const pageRegistry')
88
+ })
89
+
90
+ it('generates correct lazy import for default export', () => {
91
+ const output = generateRouteModule({
92
+ tree: [{
93
+ segment: 'users',
94
+ fullPath: '/users',
95
+ filePath: '/tmp/routes/users.tsx',
96
+ exportName: 'default',
97
+ isLayout: false,
98
+ isIndex: false,
99
+ isNotFound: false,
100
+ isPathlessGroup: false,
101
+ pageExports: [{ pageId: 'users', exportName: 'default', routePattern: '/users', params: [] }],
102
+ children: [],
103
+ }],
104
+ routesDir: '/tmp/routes',
105
+ outputPath: '/tmp/buzola.gen.ts',
106
+ })
107
+
108
+ expect(output).toContain('.then(m => ({ default: m.default.component }))')
109
+ })
110
+
111
+ it('generates correct lazy import for named export', () => {
112
+ const output = generateRouteModule({
113
+ tree: [{
114
+ segment: 'users',
115
+ fullPath: '/users',
116
+ isLayout: false,
117
+ isIndex: false,
118
+ isNotFound: false,
119
+ isPathlessGroup: false,
120
+ children: [{
121
+ segment: 'detail',
122
+ fullPath: '/users/detail',
123
+ filePath: '/tmp/routes/users.tsx',
124
+ exportName: 'detail',
125
+ isLayout: false,
126
+ isIndex: false,
127
+ isNotFound: false,
128
+ isPathlessGroup: false,
129
+ pageExports: [{ pageId: 'users/detail', exportName: 'detail', routePattern: '/users/detail', params: [] }],
130
+ children: [],
131
+ }],
132
+ }],
133
+ routesDir: '/tmp/routes',
134
+ outputPath: '/tmp/buzola.gen.ts',
135
+ })
136
+
137
+ expect(output).toContain('.then(m => ({ default: m.detail.component }))')
138
+ expect(output).toContain("'users/detail': '/users/detail'")
139
+ })
140
+
141
+ it('generates matchPath when routePattern differs from file position', () => {
142
+ const output = generateRouteModule({
143
+ tree: [{
144
+ segment: 'login',
145
+ fullPath: '/login',
146
+ filePath: '/tmp/routes/login/_layout.tsx',
147
+ isLayout: true,
148
+ isIndex: false,
149
+ isNotFound: false,
150
+ isPathlessGroup: false,
151
+ children: [{
152
+ segment: '',
153
+ fullPath: '/login',
154
+ filePath: '/tmp/routes/login/index.tsx',
155
+ exportName: 'default',
156
+ isLayout: false,
157
+ isIndex: true,
158
+ isNotFound: false,
159
+ isPathlessGroup: false,
160
+ pageExports: [{ pageId: 'login', exportName: 'default', routePattern: '/', params: [] }],
161
+ children: [],
162
+ }],
163
+ }],
164
+ routesDir: '/tmp/routes',
165
+ outputPath: '/tmp/buzola.gen.ts',
166
+ })
167
+
168
+ expect(output).toContain("matchPath: '/'")
169
+ expect(output).toContain('isIndex: true')
170
+ })
171
+
172
+ it('does not generate matchPath when routePattern matches file position', () => {
173
+ const output = generateRouteModule({
174
+ tree: [{
175
+ segment: 'users',
176
+ fullPath: '/users',
177
+ filePath: '/tmp/routes/users.tsx',
178
+ exportName: 'default',
179
+ isLayout: false,
180
+ isIndex: false,
181
+ isNotFound: false,
182
+ isPathlessGroup: false,
183
+ pageExports: [{ pageId: 'users', exportName: 'default', routePattern: '/users', params: [] }],
184
+ children: [],
185
+ }],
186
+ routesDir: '/tmp/routes',
187
+ outputPath: '/tmp/buzola.gen.ts',
188
+ })
189
+
190
+ expect(output).not.toContain('matchPath')
191
+ })
192
+
193
+ it('generates separate imports for default and named exports from same file', () => {
194
+ const output = generateRouteModule({
195
+ tree: [{
196
+ segment: 'users',
197
+ fullPath: '/users',
198
+ filePath: '/tmp/routes/users.tsx',
199
+ exportName: 'default',
200
+ isLayout: false,
201
+ isIndex: false,
202
+ isNotFound: false,
203
+ isPathlessGroup: false,
204
+ pageExports: [{ pageId: 'users', exportName: 'default', routePattern: '/users', params: [] }],
205
+ children: [{
206
+ segment: 'detail',
207
+ fullPath: '/users/detail',
208
+ filePath: '/tmp/routes/users.tsx',
209
+ exportName: 'detail',
210
+ isLayout: false,
211
+ isIndex: false,
212
+ isNotFound: false,
213
+ isPathlessGroup: false,
214
+ pageExports: [{ pageId: 'users/detail', exportName: 'detail', routePattern: '/users/detail', params: [] }],
215
+ children: [],
216
+ }],
217
+ }],
218
+ routesDir: '/tmp/routes',
219
+ outputPath: '/tmp/buzola.gen.ts',
220
+ })
221
+
222
+ // Two separate import lines for the same file
223
+ expect(output).toContain('m.default.component')
224
+ expect(output).toContain('m.detail.component')
225
+ // Both in page registry
226
+ expect(output).toContain("'users': '/users'")
227
+ expect(output).toContain("'users/detail': '/users/detail'")
228
+ })
229
+ })
@@ -0,0 +1,93 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { isRouteFile, parseDirName, parseFileName, parseRouteFile } from '../conventions.js'
3
+
4
+ describe('isRouteFile', () => {
5
+ it('accepts .tsx files', () => {
6
+ expect(isRouteFile('index.tsx')).toBe(true)
7
+ })
8
+
9
+ it('accepts .jsx files', () => {
10
+ expect(isRouteFile('_layout.jsx')).toBe(true)
11
+ })
12
+
13
+ it('rejects .css files', () => {
14
+ expect(isRouteFile('style.css')).toBe(false)
15
+ })
16
+ })
17
+
18
+ describe('parseFileName', () => {
19
+ it('parses layout files', () => {
20
+ const result = parseFileName('_layout')
21
+ expect(result.isLayout).toBe(true)
22
+ expect(result.segment).toBe('')
23
+ })
24
+
25
+ it('parses not-found files', () => {
26
+ const result = parseFileName('_404')
27
+ expect(result.isNotFound).toBe(true)
28
+ expect(result.segment).toBe('')
29
+ })
30
+
31
+ it('parses index files', () => {
32
+ const result = parseFileName('index')
33
+ expect(result.isIndex).toBe(true)
34
+ expect(result.segment).toBe('')
35
+ })
36
+
37
+ it('parses regular page files', () => {
38
+ const result = parseFileName('about')
39
+ expect(result.segment).toBe('about')
40
+ expect(result.isLayout).toBe(false)
41
+ expect(result.isIndex).toBe(false)
42
+ expect(result.isNotFound).toBe(false)
43
+ })
44
+
45
+ it('treats bracket names as plain file names', () => {
46
+ const result = parseFileName('[userId]')
47
+ expect(result.segment).toBe('[userId]')
48
+ expect(result.isLayout).toBe(false)
49
+ expect(result.isIndex).toBe(false)
50
+ expect(result.isNotFound).toBe(false)
51
+ })
52
+ })
53
+
54
+ describe('parseDirName', () => {
55
+ it('parses pathless groups', () => {
56
+ const result = parseDirName('(auth)')
57
+ expect(result.isPathlessGroup).toBe(true)
58
+ expect(result.segment).toBe('')
59
+ })
60
+
61
+ it('parses regular directories', () => {
62
+ const result = parseDirName('users')
63
+ expect(result.segment).toBe('users')
64
+ expect(result.isPathlessGroup).toBe(false)
65
+ })
66
+
67
+ it('treats bracket names as plain directory names', () => {
68
+ const result = parseDirName('[userId]')
69
+ expect(result.segment).toBe('[userId]')
70
+ expect(result.isPathlessGroup).toBe(false)
71
+ })
72
+ })
73
+
74
+ describe('parseRouteFile', () => {
75
+ it('parses root index', () => {
76
+ const result = parseRouteFile('index.tsx')
77
+ expect(result.isIndex).toBe(true)
78
+ expect(result.segment).toBe('')
79
+ })
80
+
81
+ it('parses nested layout', () => {
82
+ const result = parseRouteFile('users/_layout.tsx')
83
+ expect(result.isLayout).toBe(true)
84
+ expect(result.segment).toBe('users')
85
+ })
86
+
87
+ it('parses regular nested file', () => {
88
+ const result = parseRouteFile('users/detail.tsx')
89
+ expect(result.segment).toBe('users/detail')
90
+ expect(result.isLayout).toBe(false)
91
+ expect(result.isIndex).toBe(false)
92
+ })
93
+ })
@@ -0,0 +1,95 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import { extractPages } from '../generator/page-extractor.js'
3
+
4
+ describe('extractPages', () => {
5
+ it('extracts default page export', async () => {
6
+ const mockModule = {
7
+ default: {
8
+ __buzolaPage: true,
9
+ component: () => null,
10
+ route: '/users/:userId',
11
+ paramsMeta: [
12
+ { name: 'userId', optional: false, array: false },
13
+ { name: 'tab', optional: true, array: false },
14
+ ],
15
+ },
16
+ }
17
+
18
+ const pages = await extractPages('/fake/path.tsx', async () => mockModule as Record<string, unknown>)
19
+
20
+ expect(pages).toHaveLength(1)
21
+ expect(pages[0].exportName).toBe('default')
22
+ expect(pages[0].isDefault).toBe(true)
23
+ expect(pages[0].route).toBe('/users/:userId')
24
+ expect(pages[0].params).toEqual([
25
+ { name: 'userId', optional: false, array: false },
26
+ { name: 'tab', optional: true, array: false },
27
+ ])
28
+ })
29
+
30
+ it('extracts named page exports', async () => {
31
+ const mockModule = {
32
+ detail: {
33
+ __buzolaPage: true,
34
+ component: () => null,
35
+ route: '/project/:id',
36
+ paramsMeta: [{ name: 'id', optional: false, array: false }],
37
+ },
38
+ list: {
39
+ __buzolaPage: true,
40
+ component: () => null,
41
+ route: '/projects',
42
+ paramsMeta: [],
43
+ },
44
+ }
45
+
46
+ const pages = await extractPages('/fake/path.tsx', async () => mockModule as Record<string, unknown>)
47
+
48
+ expect(pages).toHaveLength(2)
49
+ expect(pages[0].exportName).toBe('detail')
50
+ expect(pages[0].isDefault).toBe(false)
51
+ expect(pages[1].exportName).toBe('list')
52
+ })
53
+
54
+ it('skips non-page exports', async () => {
55
+ const mockModule = {
56
+ default: {
57
+ __buzolaPage: true,
58
+ component: () => null,
59
+ route: '/about',
60
+ paramsMeta: [],
61
+ },
62
+ helperFunction: () => 'not a page',
63
+ SOME_CONSTANT: 42,
64
+ }
65
+
66
+ const pages = await extractPages('/fake/path.tsx', async () => mockModule as Record<string, unknown>)
67
+
68
+ expect(pages).toHaveLength(1)
69
+ expect(pages[0].exportName).toBe('default')
70
+ })
71
+
72
+ it('returns empty for module without pages', async () => {
73
+ const mockModule = {
74
+ default: () => null,
75
+ }
76
+
77
+ const pages = await extractPages('/fake/path.tsx', async () => mockModule as Record<string, unknown>)
78
+ expect(pages).toEqual([])
79
+ })
80
+
81
+ it('handles page without route', async () => {
82
+ const mockModule = {
83
+ default: {
84
+ __buzolaPage: true,
85
+ component: () => null,
86
+ paramsMeta: [],
87
+ },
88
+ }
89
+
90
+ const pages = await extractPages('/fake/path.tsx', async () => mockModule as Record<string, unknown>)
91
+
92
+ expect(pages).toHaveLength(1)
93
+ expect(pages[0].route).toBeUndefined()
94
+ })
95
+ })
@@ -0,0 +1,120 @@
1
+ import { describe, expect, it } from 'bun:test'
2
+ import * as fs from 'node:fs'
3
+ import * as os from 'node:os'
4
+ import * as path from 'node:path'
5
+ import { buildVirtualSource, resolveOptions, resolveVirtualModuleId } from '../resolve.js'
6
+
7
+ function makeTempDir(): string {
8
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'buzola-resolve-test-'))
9
+ }
10
+
11
+ describe('resolveVirtualModuleId', () => {
12
+ it('returns defaults when name is undefined', () => {
13
+ expect(resolveVirtualModuleId(undefined)).toEqual({
14
+ pluginName: 'buzola',
15
+ id: 'virtual:buzola/routes',
16
+ })
17
+ })
18
+
19
+ it('includes name in plugin name and virtual module ID', () => {
20
+ expect(resolveVirtualModuleId('admin')).toEqual({
21
+ pluginName: 'buzola:admin',
22
+ id: 'virtual:buzola/admin/routes',
23
+ })
24
+ })
25
+ })
26
+
27
+ describe('buildVirtualSource', () => {
28
+ it('strips trailing .ts and re-exports routes + pageRegistry', () => {
29
+ const source = buildVirtualSource('/abs/path/buzola.gen.ts')
30
+ expect(source).toContain('export { routes, pageRegistry } from')
31
+ expect(source).toContain('"/abs/path/buzola.gen"')
32
+ expect(source).not.toContain('.gen.ts"')
33
+ })
34
+
35
+ it('escapes the import path via JSON.stringify', () => {
36
+ const source = buildVirtualSource('/path "with" quotes/buzola.gen.ts')
37
+ const match = source.match(/from (.+?);\n$/)
38
+ expect(match).not.toBeNull()
39
+ expect(JSON.parse(match![1])).toBe('/path "with" quotes/buzola.gen')
40
+ })
41
+ })
42
+
43
+ describe('resolveOptions', () => {
44
+ it('uses built-in defaults when input is empty', async () => {
45
+ const root = makeTempDir()
46
+ try {
47
+ const result = await resolveOptions({ root })
48
+ expect(result.root).toBe(root)
49
+ expect(result.routesDir).toBe(path.join(root, 'src/routes'))
50
+ expect(result.outputPath).toBe(path.join(root, 'src/buzola.gen.ts'))
51
+ expect(result.persistentParams).toBeUndefined()
52
+ } finally {
53
+ fs.rmSync(root, { recursive: true, force: true })
54
+ }
55
+ })
56
+
57
+ it('input values override defaults', async () => {
58
+ const root = makeTempDir()
59
+ try {
60
+ const result = await resolveOptions({
61
+ root,
62
+ routesDir: 'app/routes',
63
+ output: 'gen/routes.gen.ts',
64
+ persistentParams: ['lang'],
65
+ })
66
+ expect(result.routesDir).toBe(path.join(root, 'app/routes'))
67
+ expect(result.outputPath).toBe(path.join(root, 'gen/routes.gen.ts'))
68
+ expect(result.persistentParams).toEqual(['lang'])
69
+ } finally {
70
+ fs.rmSync(root, { recursive: true, force: true })
71
+ }
72
+ })
73
+
74
+ it('reads buzola.config.ts when input field is absent', async () => {
75
+ const root = makeTempDir()
76
+ try {
77
+ fs.writeFileSync(
78
+ path.join(root, 'buzola.config.ts'),
79
+ `export default {
80
+ routesDir: 'configured/routes',
81
+ output: 'configured/out.gen.ts',
82
+ persistentParams: ['theme'],
83
+ }`,
84
+ )
85
+ const result = await resolveOptions({ root })
86
+ expect(result.routesDir).toBe(path.join(root, 'configured/routes'))
87
+ expect(result.outputPath).toBe(path.join(root, 'configured/out.gen.ts'))
88
+ expect(result.persistentParams).toEqual(['theme'])
89
+ } finally {
90
+ fs.rmSync(root, { recursive: true, force: true })
91
+ }
92
+ })
93
+
94
+ it('input overrides config file per-field', async () => {
95
+ const root = makeTempDir()
96
+ try {
97
+ fs.writeFileSync(
98
+ path.join(root, 'buzola.config.ts'),
99
+ `export default { routesDir: 'configured', output: 'configured.gen.ts' }`,
100
+ )
101
+ const result = await resolveOptions({ root, routesDir: 'override' })
102
+ expect(result.routesDir).toBe(path.join(root, 'override'))
103
+ expect(result.outputPath).toBe(path.join(root, 'configured.gen.ts'))
104
+ } finally {
105
+ fs.rmSync(root, { recursive: true, force: true })
106
+ }
107
+ })
108
+
109
+ it('returns absolute paths', async () => {
110
+ const root = makeTempDir()
111
+ try {
112
+ const result = await resolveOptions({ root, routesDir: './nested/routes' })
113
+ expect(path.isAbsolute(result.routesDir)).toBe(true)
114
+ expect(path.isAbsolute(result.outputPath)).toBe(true)
115
+ expect(path.isAbsolute(result.root)).toBe(true)
116
+ } finally {
117
+ fs.rmSync(root, { recursive: true, force: true })
118
+ }
119
+ })
120
+ })