@@ -8,6 +8,7 @@ import { readFile, readdir } from "node:fs/promises";
88import { pathToFileURL } from "node:url" ;
99import * as path from "path" ;
1010import * as glob from "glob" ;
11+ import globby from "globby" ;
1112
1213import type { PackageJson } from "../../common/npmPackage" ;
1314import { lookUpDirSync } from "../../common/utils" ;
@@ -75,15 +76,35 @@ export function getApiExtractorConfigFilePath(commandLine: string): string {
7576}
7677
7778export function toPosixPath ( s : string ) {
78- return path . sep === "\\" ? s . replace ( / \\ / g, "/" ) : s ;
79+ return s . replace ( / \\ / g, "/" ) ;
7980}
8081
82+ /**
83+ * Promisified wrapper around the glob library.
84+ *
85+ * @param pattern - Glob pattern to match files
86+ * @param options - Options to pass to glob
87+ * @returns Promise resolving to array of matched file paths
88+ *
89+ * @remarks
90+ * When the environment variable `FLUID_BUILD_TEST_RANDOM_ORDER` is set to "true", results will be
91+ * randomly shuffled to expose code that incorrectly depends on glob result ordering. This should only
92+ * be used in test/CI environments.
93+ */
8194export async function globFn ( pattern : string , options : glob . IOptions = { } ) : Promise < string [ ] > {
8295 return new Promise ( ( resolve , reject ) => {
8396 glob . default ( pattern , options , ( err , matches ) => {
8497 if ( err ) {
8598 reject ( err ) ;
99+ return ;
86100 }
101+
102+ // Test mode: randomize order to expose ordering dependencies
103+ if ( isRandomOrderTestMode ( ) ) {
104+ resolve ( shuffleArray ( [ ...matches ] ) ) ;
105+ return ;
106+ }
107+
87108 resolve ( matches ) ;
88109 } ) ;
89110 } ) ;
@@ -97,3 +118,75 @@ export async function loadModule(modulePath: string, moduleType?: string) {
97118 }
98119 return require ( modulePath ) ;
99120}
121+
122+ /**
123+ * Shuffles an array in place using Fisher-Yates algorithm.
124+ * Used for testing order-independence when FLUID_BUILD_TEST_RANDOM_ORDER is set to "true".
125+ *
126+ * @param array - The array to shuffle
127+ * @returns The shuffled array (same reference, modified in place)
128+ */
129+ function shuffleArray < T > ( array : T [ ] ) : T [ ] {
130+ for ( let i = array . length - 1 ; i > 0 ; i -- ) {
131+ const j = Math . floor ( Math . random ( ) * ( i + 1 ) ) ;
132+ [ array [ i ] , array [ j ] ] = [ array [ j ] , array [ i ] ] ;
133+ }
134+ return array ;
135+ }
136+
137+ /**
138+ * Returns true if runtime order randomization is enabled for testing.
139+ * When enabled, glob functions will randomize their results to expose order dependencies.
140+ */
141+ function isRandomOrderTestMode ( ) : boolean {
142+ return process . env . FLUID_BUILD_TEST_RANDOM_ORDER === "true" ;
143+ }
144+
145+ /**
146+ * Options for {@link globWithGitignore}.
147+ */
148+ export interface GlobWithGitignoreOptions {
149+ /**
150+ * The working directory to use for relative patterns.
151+ */
152+ cwd : string ;
153+
154+ /**
155+ * Whether to apply gitignore rules to exclude files.
156+ * @defaultValue true
157+ */
158+ gitignore ?: boolean ;
159+ }
160+
161+ /**
162+ * Glob files with optional gitignore support. This function is used by LeafWithGlobInputOutputDoneFileTask
163+ * to get input and output files for tasks.
164+ *
165+ * @param patterns - Glob patterns to match files. Patterns should be relative paths (e.g., "src/**\/*.ts").
166+ * Absolute patterns are not recommended as they may behave unexpectedly with the cwd option.
167+ * @param options - Options for the glob operation.
168+ * @returns An array of absolute paths to all files that match the globs.
169+ *
170+ * @remarks
171+ * When the environment variable `FLUID_BUILD_TEST_RANDOM_ORDER` is set to "true", results will be
172+ * randomly shuffled to expose code that incorrectly depends on glob result ordering. This should only
173+ * be used in test/CI environments.
174+ */
175+ export async function globWithGitignore (
176+ patterns : readonly string [ ] ,
177+ options : GlobWithGitignoreOptions ,
178+ ) : Promise < string [ ] > {
179+ const { cwd, gitignore = true } = options ;
180+ const results = await globby ( [ ...patterns ] , {
181+ cwd,
182+ absolute : true ,
183+ gitignore,
184+ } ) ;
185+
186+ // Test mode: randomize order to expose ordering dependencies
187+ if ( isRandomOrderTestMode ( ) ) {
188+ return shuffleArray ( [ ...results ] ) ;
189+ }
190+
191+ return results ;
192+ }
0 commit comments