diff --git a/packages/cli/src/glob.rescript.test.ts b/packages/cli/src/glob.rescript.test.ts new file mode 100644 index 00000000..f2043817 --- /dev/null +++ b/packages/cli/src/glob.rescript.test.ts @@ -0,0 +1,101 @@ +import chokidar from 'chokidar'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; + +/** Uses chokidar to collect files matching a glob pattern (same as CLI) */ +function getMatchedFiles(pattern: string): Promise { + return new Promise((resolve) => { + const files: string[] = []; + const watcher = chokidar.watch(pattern, { persistent: false }); + watcher.on('add', (filePath) => files.push(filePath)); + watcher.on('ready', () => { + watcher.close(); + resolve(files); + }); + }); +} + +describe('srcDir glob pattern matching', () => { + let testDir: string; + let srcDir: string; + let testsDir: string; + + beforeEach(() => { + // Create a temporary directory structure + testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pgtyped-test-')); + srcDir = path.join(testDir, 'src'); + testsDir = path.join(testDir, '__tests__'); + + // Create directories + fs.mkdirSync(srcDir, { recursive: true }); + fs.mkdirSync(testsDir, { recursive: true }); + fs.mkdirSync(path.join(srcDir, 'queries'), { recursive: true }); + fs.mkdirSync(path.join(testsDir, 'queries'), { recursive: true }); + + // Create test SQL files + fs.writeFileSync( + path.join(srcDir, 'queries', 'users.sql'), + '/* @name GetUsers */\nSELECT * FROM users;', + ); + fs.writeFileSync( + path.join(testsDir, 'queries', 'test.sql'), + '/* @name GetTestData */\nSELECT * FROM test_data;', + ); + }); + + afterEach(() => { + // Cleanup + fs.rmSync(testDir, { recursive: true, force: true }); + }); + + test('simple srcDir pattern finds SQL files', async () => { + const srcDirPattern = path.join(testDir, 'src'); + const include = '**/*.sql'; + const pattern = `${srcDirPattern}/**/${include}`; + + const files = await getMatchedFiles(pattern); + expect(files.length).toBe(1); + expect(files[0]).toContain('users.sql'); + }); + + test('parentheses extglob syntax (src|__tests__) finds files in both directories', async () => { + const srcDirPattern = `${testDir}/(src|__tests__)`; + const include = '**/*.sql'; + const pattern = `${srcDirPattern}/**/${include}`; + + const files = await getMatchedFiles(pattern); + expect(files.length).toBe(2); + }); + + test('brace syntax {src,__tests__} finds files in both directories', async () => { + const srcDirPattern = `${testDir}/{src,__tests__}`; + const include = '**/*.sql'; + const pattern = `${srcDirPattern}/**/${include}`; + + const files = await getMatchedFiles(pattern); + expect(files.length).toBe(2); + }); + + test('srcDir with embedded wildcards (src|__tests__)/**/* works', async () => { + // The user's config had srcDir: "./(src|__tests__)/**/*" + const srcDirWithWildcard = `${testDir}/(src|__tests__)/**/*`; + const include = '**/*.sql'; + const pattern = `${srcDirWithWildcard}/**/${include}`; + + const files = await getMatchedFiles(pattern); + expect(files.length).toBe(2); + }); + + test('empty directory returns empty array without error', async () => { + const emptyDir = path.join(testDir, 'empty'); + fs.mkdirSync(emptyDir, { recursive: true }); + + const srcDirPattern = emptyDir; + const include = '**/*.sql'; + const pattern = `${srcDirPattern}/**/${include}`; + + const files = await getMatchedFiles(pattern); + expect(files.length).toBe(0); + }); +}); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 9ed21362..ad0c4a8f 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -3,7 +3,6 @@ import { startup } from 'pgtyped-rescript-query'; import { AsyncQueue } from '@pgtyped/wire'; import chokidar from 'chokidar'; -import { globSync } from 'glob'; import nun from 'nunjucks'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; @@ -17,6 +16,19 @@ import WorkerPool from 'piscina'; nun.configure({ autoescape: false }); +/** Uses chokidar to collect files matching a glob pattern */ +function getMatchedFiles(pattern: string): Promise { + return new Promise((resolve) => { + const files: string[] = []; + const watcher = chokidar.watch(pattern, { persistent: false }); + watcher.on('add', (filePath) => files.push(filePath)); + watcher.on('ready', () => { + watcher.close(); + resolve(files); + }); + }); +} + interface TransformJob { files: string[]; transform: TransformConfig; @@ -113,10 +125,10 @@ async function main( .on('change', cb); } else { /** - * If the user didn't provide the -f paramter, we're using the list of files we got from glob. - * If he did, we're using glob file list to detect if his provided file should be used with this transform. + * If the user didn't provide the -f parameter, we're using the list of files we got from chokidar. + * If he did, we're using the file list to detect if his provided file should be used with this transform. */ - let fileList = globSync(pattern); + let fileList = await getMatchedFiles(pattern); if (fileOverride) { fileList = fileList.includes(fileOverride) ? [fileOverride] : []; if (fileList.length > 0) {