* 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>
119 lines
3.9 KiB
TypeScript
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
|
|
}
|