protected function installProjectFiles(
        $recipe,
        $sourceRoot,
        $destinationRoot,
        $filePatterns,
        $registrationKey,
        $name = 'project'
    ) {
        // load composer json data
        $composerFile = new JsonFile(Factory::getComposerFile(), null, $this->io);
        $composerData = $composerFile->read();
        $installedFiles = isset($composerData['extra'][$registrationKey])
            ? $composerData['extra'][$registrationKey]
            : [];

        // Load all project files
        $fileIterator = $this->getFileIterator($sourceRoot, $filePatterns);
        $any = false;
        foreach ($fileIterator as $path => $info) {
            // Write header on first file
            if (!$any) {
                $this->io->write("Installing {$name} files for recipe <info>{$recipe}</info>:");
                $any = true;
            }

            // Install this file
            $relativePath = $this->installProjectFile($sourceRoot, $destinationRoot, $path, $installedFiles);

            // Add file to installed (even if already exists)
            if (!in_array($relativePath, $installedFiles)) {
                $installedFiles[] = $relativePath;
            }
        }

        // If any files are written, modify composer.json with newly installed files
        if ($installedFiles) {
            sort($installedFiles);
            if (!isset($composerData['extra'])) {
                $composerData['extra'] = [];
            }
            $composerData['extra'][$registrationKey] = $installedFiles;
            $composerFile->write($composerData);
        }
    }