import type { Stats } from 'fs' import * as fs from 'fs/promises' import * as path from 'path' import { NATIVE_FILE_TYPE, PROJECT_ENTRYPOINT, RELEVANT_FILE_TYPES, type RelevantFileType, } from '@src/lib/constants' const shouldWrapExtension = (extension: string) => RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) && extension !== NATIVE_FILE_TYPE /// Get the current project file from the path. /// This is used for double-clicking on a file in the file explorer, /// or the command line args, or deep linking. export default async function getCurrentProjectFile( pathString: string ): Promise { // Fix for "." path, which is the current directory. let sourcePath = pathString === '.' ? process.cwd() : pathString // URL decode the path. sourcePath = decodeURIComponent(sourcePath) // If the path does not start with a slash, it is a relative path. // We need to convert it to an absolute path. sourcePath = path.isAbsolute(sourcePath) ? sourcePath : path.join(process.cwd(), sourcePath) let stats: Stats try { stats = await fs.stat(sourcePath) } catch (error) { return new Error( `Unable to access the path: ${sourcePath}. Error: ${error}` ) } // If the path is a directory, let's assume it is a project directory. if (stats.isDirectory()) { // Walk the directory and look for a kcl file. const files = await fs.readdir(sourcePath) const kclFiles = files.filter((file) => path.extname(file) === '.kcl') if (kclFiles.length === 0) { let projectFile = path.join(sourcePath, PROJECT_ENTRYPOINT) // Check if we have a main.kcl file in the project. try { await fs.access(projectFile) } catch { // Create the default file in the project. await fs.writeFile(projectFile, '') } return projectFile } // If a project entrypoint file exists, use it. // Otherwise, use the first kcl file in the project. const gotMain = files.filter((file) => file === PROJECT_ENTRYPOINT) if (gotMain.length === 0) { return path.join(sourcePath, kclFiles[0]) } return path.join(sourcePath, PROJECT_ENTRYPOINT) } // Check if the extension on what we are trying to open is a relevant file type. const extension = path.extname(sourcePath).slice(1).toLowerCase() if ( !RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) && extension !== 'toml' ) { return new Error( `File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${RELEVANT_FILE_TYPES.join( ', ' )}` ) } // We were given a file path, not a directory. // Let's get the parent directory of the file. const parent = path.dirname(sourcePath) // If we got an import model file, we need to check if we have a file in the project for // this import model. // TODO: once we have some sort of a load file into project it would make sense to stop creating these wrapper files // and let people save their own kcl file importing if (shouldWrapExtension(extension)) { const importFileName = path.basename(sourcePath) // Check if we have a file in the project for this import model. const kclWrapperFilename = `${importFileName}.kcl` const kclWrapperFilePath = path.join(parent, kclWrapperFilename) try { await fs.access(kclWrapperFilePath) } catch { // Create the file in the project with the default import content. const content = `// This file was automatically generated by the application when you // double-clicked on the model file. // You can edit this file to add your own content. // But we recommend you keep the import statement as it is. // For more information on the import statement, see the documentation at: // https://zoo.dev/docs/kcl/import import "${importFileName}" as model model` await fs.writeFile(kclWrapperFilePath, content) } return kclWrapperFilePath } return sourcePath }