zile 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,222 @@
1
+ import path from 'node:path'
2
+ import { setupRepos } from '../test/utils.js'
3
+ import * as Packages from './Packages.js'
4
+
5
+ const cwd = path.resolve(import.meta.dirname, '..')
6
+
7
+ describe('Packages.find', () => {
8
+ function relative(absolutePaths: string[]): string[] {
9
+ return absolutePaths.map((p) => `/${path.relative(cwd, p)}`)
10
+ }
11
+
12
+ it('finds all packages under a given `cwd`', async () => {
13
+ const result = await Packages.find({
14
+ cwd,
15
+ })
16
+
17
+ expect(relative(result)).toMatchInlineSnapshot(`
18
+ [
19
+ "/",
20
+ "/test/repos/single-entrypoint",
21
+ "/test/repos/multiple-entrypoint",
22
+ "/test/repos/error-module-resolution",
23
+ "/test/repos/error-missing-entry-fields",
24
+ "/test/repos/error-main-not-exists",
25
+ "/test/repos/error-exports-string-not-exists",
26
+ "/test/repos/error-exports-object-not-exists",
27
+ "/test/repos/error-bin-string-not-exists",
28
+ "/test/repos/error-bin-object-not-exists",
29
+ "/test/repos/basic-object-exports",
30
+ "/test/repos/basic",
31
+ "/src/internal",
32
+ ]
33
+ `)
34
+ })
35
+
36
+ it('finds all packages under an alternate `cwd`', async () => {
37
+ const result = await Packages.find({
38
+ cwd: path.resolve(import.meta.dirname, '../test'),
39
+ })
40
+
41
+ expect(relative(result)).toMatchInlineSnapshot(`
42
+ [
43
+ "/test/repos/single-entrypoint",
44
+ "/test/repos/multiple-entrypoint",
45
+ "/test/repos/error-module-resolution",
46
+ "/test/repos/error-missing-entry-fields",
47
+ "/test/repos/error-main-not-exists",
48
+ "/test/repos/error-exports-string-not-exists",
49
+ "/test/repos/error-exports-object-not-exists",
50
+ "/test/repos/error-bin-string-not-exists",
51
+ "/test/repos/error-bin-object-not-exists",
52
+ "/test/repos/basic-object-exports",
53
+ "/test/repos/basic",
54
+ ]
55
+ `)
56
+ })
57
+
58
+ it('finds packages in specific directory', async () => {
59
+ const result = await Packages.find({
60
+ cwd,
61
+ includes: ['test/repos/basic'],
62
+ })
63
+
64
+ expect(relative(result)).toMatchInlineSnapshot(`
65
+ [
66
+ "/test/repos/basic",
67
+ ]
68
+ `)
69
+ })
70
+
71
+ it('finds packages with pattern ending in package.json', async () => {
72
+ const result = await Packages.find({
73
+ cwd,
74
+ includes: ['test/repos/basic/package.json'],
75
+ })
76
+
77
+ expect(relative(result)).toMatchInlineSnapshot(`
78
+ [
79
+ "/test/repos/basic",
80
+ ]
81
+ `)
82
+ })
83
+
84
+ it('finds packages with wildcard pattern', async () => {
85
+ const result = await Packages.find({
86
+ cwd,
87
+ includes: ['test/repos/*'],
88
+ })
89
+
90
+ expect(relative(result)).toMatchInlineSnapshot(`
91
+ [
92
+ "/test/repos/single-entrypoint",
93
+ "/test/repos/multiple-entrypoint",
94
+ "/test/repos/error-module-resolution",
95
+ "/test/repos/error-missing-entry-fields",
96
+ "/test/repos/error-main-not-exists",
97
+ "/test/repos/error-exports-string-not-exists",
98
+ "/test/repos/error-exports-object-not-exists",
99
+ "/test/repos/error-bin-string-not-exists",
100
+ "/test/repos/error-bin-object-not-exists",
101
+ "/test/repos/basic-object-exports",
102
+ "/test/repos/basic",
103
+ ]
104
+ `)
105
+ })
106
+
107
+ it('deduplicates results from multiple patterns', async () => {
108
+ const result = await Packages.find({
109
+ cwd,
110
+ includes: ['test/repos/basic', 'test/repos/basic/package.json'],
111
+ })
112
+
113
+ expect(relative(result)).toMatchInlineSnapshot(`
114
+ [
115
+ "/test/repos/basic",
116
+ ]
117
+ `)
118
+ })
119
+
120
+ it('returns empty array when no packages found', async () => {
121
+ const result = await Packages.find({
122
+ cwd,
123
+ includes: ['nonexistent'],
124
+ })
125
+
126
+ expect(relative(result)).toMatchInlineSnapshot(`[]`)
127
+ })
128
+
129
+ it('returns absolute paths', async () => {
130
+ const result = await Packages.find({
131
+ cwd,
132
+ includes: ['test/repos/basic'],
133
+ })
134
+
135
+ expect(result.length).toBeGreaterThan(0)
136
+ expect(path.isAbsolute(result[0])).toBe(true)
137
+ })
138
+
139
+ it('uses process.cwd() when no cwd provided', async () => {
140
+ const originalCwd = process.cwd()
141
+ const testDir = path.join(cwd, 'test/repos/basic')
142
+ process.chdir(testDir)
143
+
144
+ const result = await Packages.find()
145
+
146
+ process.chdir(originalCwd)
147
+
148
+ expect(relative(result)).toMatchInlineSnapshot(`
149
+ [
150
+ "/test/repos/basic",
151
+ ]
152
+ `)
153
+ })
154
+
155
+ it('uses default includes when not provided', async () => {
156
+ const result = await Packages.find({
157
+ cwd,
158
+ })
159
+
160
+ expect(relative(result)).toMatchInlineSnapshot(`
161
+ [
162
+ "/",
163
+ "/test/repos/single-entrypoint",
164
+ "/test/repos/multiple-entrypoint",
165
+ "/test/repos/error-module-resolution",
166
+ "/test/repos/error-missing-entry-fields",
167
+ "/test/repos/error-main-not-exists",
168
+ "/test/repos/error-exports-string-not-exists",
169
+ "/test/repos/error-exports-object-not-exists",
170
+ "/test/repos/error-bin-string-not-exists",
171
+ "/test/repos/error-bin-object-not-exists",
172
+ "/test/repos/basic-object-exports",
173
+ "/test/repos/basic",
174
+ "/src/internal",
175
+ ]
176
+ `)
177
+ })
178
+
179
+ it('excludes node_modules by default', async () => {
180
+ const result = await Packages.find({
181
+ cwd,
182
+ })
183
+
184
+ const hasNodeModules = relative(result).some((p) => p.includes('node_modules'))
185
+ expect(hasNodeModules).toBe(false)
186
+ })
187
+
188
+ it('can include node_modules without excludes', async () => {
189
+ const result = await Packages.find({
190
+ cwd,
191
+ includes: ['node_modules/@types/node'],
192
+ })
193
+
194
+ expect(relative(result)).toMatchInlineSnapshot(`
195
+ [
196
+ "/node_modules/@types/node",
197
+ ]
198
+ `)
199
+ })
200
+
201
+ it('handles excludes', async () => {
202
+ const result = await Packages.find({
203
+ cwd,
204
+ includes: ['test/**', '!test/repos'],
205
+ })
206
+
207
+ expect(relative(result)).toMatchInlineSnapshot(`[]`)
208
+ })
209
+ })
210
+
211
+ describe('Packages.build', () => {
212
+ it('builds all packages', async () => {
213
+ const { cwd } = await setupRepos()
214
+
215
+ const result = await Packages.build({
216
+ cwd,
217
+ includes: ['**', '!**/error-*', '!**/node_modules/**'],
218
+ })
219
+
220
+ expect(result).toMatchSnapshot()
221
+ })
222
+ })
@@ -0,0 +1,65 @@
1
+ import * as fs from 'node:fs/promises'
2
+ import * as path from 'node:path'
3
+ import * as Package from './Package.js'
4
+
5
+ /**
6
+ * Builds all packages in the given working directory.
7
+ *
8
+ * @param options - Options for building packages
9
+ */
10
+ export async function build(options: build.Options) {
11
+ const packages = await find(options)
12
+ return await Promise.all(packages.map((cwd) => Package.build({ cwd })))
13
+ }
14
+
15
+ export declare namespace build {
16
+ type Options = find.Options
17
+ }
18
+
19
+ /**
20
+ * Finds all packages (containing a `package.json`) matching
21
+ * the given working directory.
22
+ *
23
+ * @param options - Options for finding packages
24
+ * @returns Array of absolute paths to package directories
25
+ */
26
+ export async function find(options: find.Options = {}) {
27
+ const cwd = options.cwd ?? process.cwd()
28
+ const patterns = options.includes ?? ['**', '!**/node_modules/**']
29
+
30
+ // Separate includes and excludes from patterns
31
+ const includes: string[] = []
32
+ const excludes: string[] = []
33
+
34
+ for (const pattern of patterns) {
35
+ if (pattern.startsWith('!')) excludes.push(pattern.slice(1))
36
+ else includes.push(pattern)
37
+ }
38
+
39
+ const packageSet = new Set<string>()
40
+
41
+ for (const pattern of includes) {
42
+ // Find all package.json files matching the pattern
43
+ const globPattern = (() => {
44
+ if (path.basename(pattern) === 'package.json') return pattern
45
+ return path.join(pattern, 'package.json')
46
+ })()
47
+
48
+ for await (const file of fs.glob(globPattern, { cwd, exclude: excludes })) {
49
+ const dir = path.dirname(file)
50
+ const absolutePath = path.resolve(cwd, dir)
51
+ packageSet.add(absolutePath)
52
+ }
53
+ }
54
+
55
+ return Array.from(packageSet)
56
+ }
57
+
58
+ export declare namespace find {
59
+ type Options = {
60
+ /** Working directory to start searching from. @default process.cwd() */
61
+ cwd?: string | undefined
62
+ /** Glob patterns to include. @default ['**'] */
63
+ includes?: string[] | undefined
64
+ }
65
+ }