Files
modeling-app/src/lib/getCurrentProjectFile.ts
Pierre Jacquier e78100eaac Assemblies: UX improvements around foreign file imports (#6159)
* WIP: Add point-and-click Import for geometry
Will eventually fix #6120
Right now the whole loop is there but the codemod doesn't work yet

* Better pathToNOde, log on non-working cm dispatch call

* Add workaround to updateModelingState not working

* Back to updateModelingState with a skip flag

* Better todo

* Change working from Import to Insert, cleanups

* Sister command in kclCommands to populate file options

* Improve path selector

* Unsure: move importAstMod to kclCommands onSubmit 😶

* Add e2e test

* Clean up for review

* Add native file menu entry and test

* No await yo lint said so

* WIP: UX improvements around foreign file imports
Fixes #6152

* @lrev-Dev's suggestion to remove a comment

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Update to scene.settled(cmdBar)

* Add partNNN default name for alias

* Lint

* Lint

* Fix unit tests

* Add sad path insert test
Thanks @Irev-Dev for the suggestion

* Add step insert test

* Lint

* Add test for second foreign import thru file tree click

* Add default value for local name alias

* Aligning tests

* Fix tests

* Add padding for filenames starting with a digit

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2025-04-09 07:47:57 -04:00

119 lines
3.9 KiB
TypeScript

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<string | Error> {
// 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
}