diff --git a/packages/playground/blueprints/src/lib/resources.ts b/packages/playground/blueprints/src/lib/resources.ts index 053fac1137..67776ad75a 100644 --- a/packages/playground/blueprints/src/lib/resources.ts +++ b/packages/playground/blueprints/src/lib/resources.ts @@ -461,9 +461,13 @@ export class GitDirectoryResource extends Resource { const repoUrl = this.options?.corsProxy ? `${this.options.corsProxy}/${this.reference.url}` : this.reference.url; - const ref = `refs/heads/${this.reference.ref}`; + const ref = ['', 'HEAD'].includes(this.reference.ref) + ? 'HEAD' + : `refs/heads/${this.reference.ref}`; const allFiles = await listGitFiles(repoUrl, ref); - const filesToClone = listDescendantFiles(allFiles, this.reference.path); + + const requestedPath = this.reference.path.replace(/^\/+/, ''); + const filesToClone = listDescendantFiles(allFiles, requestedPath); let files = await sparseCheckout(repoUrl, ref, filesToClone); // Remove the path prefix from the cloned file names. files = Object.fromEntries( diff --git a/packages/playground/storage/src/lib/paths.spec.ts b/packages/playground/storage/src/lib/paths.spec.ts new file mode 100644 index 0000000000..d4f5fd43f0 --- /dev/null +++ b/packages/playground/storage/src/lib/paths.spec.ts @@ -0,0 +1,122 @@ +import { FileTree } from './git-sparse-checkout'; +import { listDescendantFiles } from './paths'; + +const testTree: FileTree[] = [ + { + name: 'packages', + type: 'folder', + children: [ + { + name: 'playground', + type: 'folder', + children: [ + { + name: 'storage', + type: 'folder', + children: [ + { + name: 'package.json', + type: 'file', + }, + { + name: 'STORAGE_README.md', + type: 'file', + }, + ], + }, + ], + }, + { + name: 'php-wasm', + type: 'folder', + children: [ + { + name: 'package.json', + type: 'file', + }, + ], + }, + ], + }, + { + name: 'wordpress', + type: 'folder', + children: [ + { + name: 'wp-content', + type: 'folder', + children: [ + { + name: 'plugins', + type: 'folder', + children: [ + { + name: 'blocky-formats', + type: 'folder', + children: [ + { + name: 'block.json', + type: 'file', + }, + { + name: 'index.php', + type: 'file', + }, + { + name: 'README.md', + type: 'file', + }, + ], + }, + ], + }, + ], + }, + { + name: 'index.php', + type: 'file', + }, + { + name: 'wp-includes', + type: 'folder', + children: [ + { + name: 'version.php', + type: 'file', + }, + ], + }, + { + name: 'wp-config.php', + type: 'file', + }, + ], + }, +]; + +describe('listDescendantFiles', () => { + it('should list all the descendant files from a subdirectory of a file tree', async () => { + const files = listDescendantFiles(testTree, 'packages/playground'); + expect(files).toEqual( + expect.arrayContaining([ + 'packages/playground/storage/package.json', + 'packages/playground/storage/STORAGE_README.md', + ]) + ); + }); + it.each(['.', '/', ''])( + 'should list all the descendant files when "%s" is passed as path', + async (path: string) => { + const files = listDescendantFiles( + [ + { + name: 'index.php', + type: 'file', + }, + ], + path + ); + expect(files).toEqual(expect.arrayContaining(['index.php'])); + } + ); +}); diff --git a/packages/playground/storage/src/lib/paths.ts b/packages/playground/storage/src/lib/paths.ts index 5b4a7fa5a8..47269c60c6 100644 --- a/packages/playground/storage/src/lib/paths.ts +++ b/packages/playground/storage/src/lib/paths.ts @@ -1,27 +1,31 @@ +import { normalizePath } from '@php-wasm/util'; import { FileTree } from './git-sparse-checkout'; export function listDescendantFiles(files: FileTree[], selectedPath: string) { - if (!selectedPath) { - return []; - } + selectedPath = normalizePath(selectedPath); + const isRoot = ['', '.', '/'].includes(selectedPath); - // Calculate the list of files to checkout based on the mapping - const descendants: string[] = []; - const segments = selectedPath.split('/'); let currentTree: FileTree[] | null = files; - for (const segment of segments) { - const file = currentTree?.find( - (file) => file.name === segment - ) as FileTree; - if (file?.type === 'folder') { - currentTree = file.children; - } else if (file) { - return [file.name]; - } else { - return []; + if (isRoot) { + selectedPath = ''; + } else { + const segments = selectedPath.split('/'); + for (const segment of segments) { + const file = currentTree?.find( + (file) => file.name === segment + ) as FileTree; + if (file?.type === 'folder') { + currentTree = file.children; + } else if (file) { + return [file.name]; + } else { + return []; + } } } + // Calculate the list of files to checkout based on the mapping + const descendants: string[] = []; const stack = [{ tree: currentTree, path: selectedPath }]; while (stack.length > 0) { const { tree, path } = stack.pop() as { @@ -29,13 +33,14 @@ export function listDescendantFiles(files: FileTree[], selectedPath: string) { path: string; }; for (const file of tree) { + const filePath = `${path}${path ? '/' : ''}${file.name}`; if (file.type === 'folder') { stack.push({ tree: file.children, - path: `${path}/${file.name}`, + path: filePath, }); } else { - descendants.push(`${path}/${file.name}`); + descendants.push(filePath); } } }