UNPKG

@figma/code-connect

Version:

A tool for connecting your design system components in code with your design system in Figma

189 lines 11 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSwiftParserDir = getSwiftParserDir; const child_process_1 = require("child_process"); const logging_1 = require("../common/logging"); const get_file_if_exists_1 = require("./get_file_if_exists"); const path_1 = __importDefault(require("path")); const fs_1 = require("fs"); // Find the location of the Code Connect Swift package on disk, so that we can // call `swift run figma-swift` from the correct location. This requires parsing // the output of xcodebuild for .xcodeproj projects, or parsing Package.swift // for SPM projects. // // As this is a first party parser, we do this in TypeScript and call it as part // of our code. For third party parsers, logic like this would need to be // implemented in a script/binary which the user points Code Connect to. async function getSwiftParserDir(cwd, xcodeprojPath, swiftPackagePath, sourcePackagesPath) { let figmaPackageDir; let xcodeprojFile; let packageSwiftFile; // // Check for the supported project types giving precedence top the user provided path if (xcodeprojPath) { xcodeprojFile = xcodeprojPath.replace(/\s/g, '\\ '); } else if (swiftPackagePath) { packageSwiftFile = path_1.default.dirname(swiftPackagePath).replace(/\s/g, '\\ '); } else { xcodeprojFile = (0, get_file_if_exists_1.getFileIfExists)(cwd, '*.xcodeproj').replace(/\s/g, '\\ '); packageSwiftFile = (0, get_file_if_exists_1.getFileIfExists)(cwd, 'Package.swift').replace(/\s/g, '\\ '); } if (!(xcodeprojFile || packageSwiftFile)) { (0, logging_1.exitWithError)('No supported project found. Supported project types are .xcodeproj or Package.swift. You can specify the location of your .xcodeproj file with the `xcodeprojPath` config option.'); } if (xcodeprojFile) { // Use xcodebuild to get the build settings, so we can find where the Code // Connect Swift package is installed const result = (0, child_process_1.spawnSync)('xcodebuild', ['-project', xcodeprojFile, '-showBuildSettings'], { cwd, encoding: 'utf-8', }); if (result.error) { throw result.error; } const buildSettings = result.stdout; // Extract the source and version of the Code Connect Swift package from the // xcodebuild output, which can be in any of the following formats depending // on how it is installed: // - Figma: https://github.com/figma/code-connect @ 0.1.2 // - Figma: /path/to/code-connect @ local // - Figma: /path/to/code-connect const figmaPackageMatch = buildSettings.match(/\s+Figma: ([^\s]*)(?: @ (.*))?/); if (!figmaPackageMatch) { (0, logging_1.exitWithError)('Code Connect Swift package not found. Please add a dependency to the Code Connect package at https://github.com/figma/code-connect to your project.'); } // Find the package's location on disk, to compile and run the parser binary from const [_, figmaPackageSource, figmaPackageVersion] = figmaPackageMatch; // The package version is `local` if installed via the "Add package" dialog, // or undefined if installed via the "Frameworks" section directly (which we // do for our test project, because the dialog won't allow ancestors to be // added) const isLocalFigmaPackage = figmaPackageVersion === 'local' || figmaPackageVersion === undefined; if (isLocalFigmaPackage) { // If the version is 'local', the package is installed from a local checkout, // and the path on disk is the source output by xcodebuild. figmaPackageDir = figmaPackageSource; } else if (sourcePackagesPath) { logging_1.logger.info(`Using custom DerivedData path: ${sourcePackagesPath}`); // If a custom path is supplied, use it. figmaPackageDir = `${sourcePackagesPath}/checkouts/code-connect`; } else { // Otherwise the SourcePackages will typically be located in Xcode's DerivedData directory, // at ~/Users/{username}/Library/Developer/Xcode/DerivedData/{project}-{xxx}/SourcePackages, or in the Project's root // at {project}/DerivedData/{project}/SourcePackages (depending on how the user's project is configured). let hasFoundSourcePackagesDir = false; // 1. First look in the USER_LIBRARY_DIR (e.g. /Users/{username}/Library) const userLibraryDirectoryMatch = buildSettings.match(/\s+USER_LIBRARY_DIR = (.*)/); const userLibraryDirectory = userLibraryDirectoryMatch ? userLibraryDirectoryMatch[1] : undefined; logging_1.logger.info('Finding Code Connect Swift package'); // Find the project's name using the build settings const projectNameMatch = buildSettings.match(/\s+PROJECT_NAME = (.*)/); const projectName = projectNameMatch ? projectNameMatch[1] : undefined; // We can't proceed without the project name (however, this should always exist) if (!projectName) { (0, logging_1.exitWithError)('PROJECT_NAME not found in xcodebuild output'); } // 1. From the folders in the user library directory use a regex to determine the folder name of the project defined by "ProjectName-" and a 28 character hash // e.g. project-fbybcbnivxfbfeefownexgukzwxd if (userLibraryDirectory) { // Default to Xcode's default Dervied Data location (e.g. ~/Users/{username}/Library/Developer/Xcode/DerivedData/project-fbybcbnivxfbfeefownexgukzwxd/SourcePackages) const root = `${userLibraryDirectory}/Developer/Xcode/DerivedData`; const projectFolderRegex = new RegExp(`${projectName}-[a-zA-Z0-9]{28}`); const derivedDataFolders = (0, fs_1.readdirSync)(root); const projectFolder = derivedDataFolders.find((folder) => projectFolderRegex.test(folder)); // If the project folder is found, use it to find the Code Connect package if (projectFolder) { figmaPackageDir = `${root}/${projectFolder}/SourcePackages/checkouts/code-connect`; hasFoundSourcePackagesDir = true; } else { logging_1.logger.warn('Package not found in user library directory'); } } // 2. If SourcePackages couldn't be found in ~/Users/{username}/Library/Developer/Xcode/DerivedData/{project}-{xxx}/SourcePackages, attempt // to find it in the project root {project}/DerivedData/{project}/SourcePackages if (!hasFoundSourcePackagesDir) { const rootDir = buildSettings.match(/\s+LOCROOT = (.*)/); if (rootDir) { figmaPackageDir = `${rootDir[1]}/DerivedData/${projectName}/SourcePackages/checkouts/code-connect`; hasFoundSourcePackagesDir = true; } else { logging_1.logger.warn('Package not found in project root'); } } // 3. Otherwise, the package may be installed to // <DerviedData>/SourcePackages/checkouts/code-connect. We find the // DerivedData location from the BUILD_DIR (which points to // <DerivedData>/Build/Products). if (!hasFoundSourcePackagesDir) { const buildDir = buildSettings.match(/\s+BUILD_DIR = (.*)/); if (buildDir) { figmaPackageDir = `${buildDir[1]}/../../SourcePackages/checkouts/code-connect`; } else { logging_1.logger.warn('Package not found in build directory'); } } } } else if (packageSwiftFile) { const swiftPackageDir = swiftPackagePath ? path_1.default.dirname(swiftPackagePath) : undefined; const packageDir = swiftPackageDir || cwd; // Use the Swift command to determine if the package is installed locally or from Git try { const result = (0, child_process_1.spawnSync)('swift', ['package', '--package-path', packageDir, 'describe', '--type', 'json'], { cwd, encoding: 'utf-8', }); if (result.error) { throw result.error; } const packageInfo = JSON.parse(result.stdout); const codeConnectPackage = packageInfo.dependencies.find((p) => p.identity === 'code-connect') ?? // Our local directory is called figmadoc, so this is what swift outputs as the identity packageInfo.dependencies.find((p) => p.identity === 'figmadoc'); if (!codeConnectPackage) { (0, logging_1.exitWithError)('Code Connect Swift package not found in Package.swift. Please add a dependency to https://github.com/figma/code-connect to your Package.swift file.'); } // We can run directly from the directory of the package swift file figmaPackageDir = packageDir; } catch (e) { (0, logging_1.exitWithError)(`Error calling Swift command: ${e}`); } } if (!figmaPackageDir) { (0, logging_1.exitWithError)('Figma package could not be found'); } // Check if the figmaPackageDir exists, otherwise we can't proceed, // as we require the code-connect package to continue if (!(0, fs_1.existsSync)(figmaPackageDir)) { (0, logging_1.exitWithError)(`Figma package directory not found at ${figmaPackageDir}`); } // We need to ensure that the directory is writable, so we can run the swift run command // otherwise an "invalid access" error will be thrown by swift. try { const packageFile = path_1.default.join(figmaPackageDir, 'Package.resolved'); const accessCheck = (0, child_process_1.spawnSync)('test', ['-w', packageFile]); if (accessCheck.status !== 0) { // Directory is not writable, attempt to make it writable (0, child_process_1.spawnSync)('chmod', ['-R', '755', packageFile]); } logging_1.logger.info(`Directory enabled for swift run command`); } catch (e) { logging_1.logger.warn(`Unable to verify or modify directory permissions: ${e}`); } logging_1.logger.info(`Found Code Connect Swift package at ${figmaPackageDir}, building parser binary. This may take a few minutes if this is the first time you've run Code Connect.`); return figmaPackageDir; } //# sourceMappingURL=get_swift_parser_dir.js.map