"use strict" ; import * as vscode from "vscode" ; import * as path from "path" ; import * as utils from "./utils" ; import { TelemetryContext } from "./telemetry" ; import { FileNames , ScaffoldType , PlatformType , TemplateTag } from "./constants" ; import { IoTWorkbenchSettings } from "./IoTSettings" ; import { FileUtility } from "./FileUtility" ; import { ProjectTemplate , ProjectTemplateType , TemplatesType } from "./Models/Interfaces/ProjectTemplate" ; import { RemoteExtension } from "./Models/RemoteExtension" ; import { ResourceNotFoundError } from "./common/Error/OperationFailedErrors/ResourceNotFoundError" ; import { OperationCanceledError } from "./common/Error/OperationCanceledError" ; import { TypeNotSupportedError } from "./common/Error/SystemErrors/TypeNotSupportedError" ; const importLazy = require ( "import-lazy" ) ; const ioTWorkspaceProjectModule = importLazy ( ( ) => require ( "./Models/IoTWorkspaceProject" ) ) ( ) ; const ioTContainerizedProjectModule = importLazy ( ( ) => require ( "./Models/IoTContainerizedProject" ) ) ( ) ; const constants = { defaultProjectName : "IoTproject" , noDeviceMessage : "$(issue-opened) My device is not in the list..." , embeddedLinuxProjectName : "Embedded Linux Project" } ; export class ProjectInitializer { async InitializeProject ( context , channel , telemetryContext ) { RemoteExtension . ensureLocalBeforeRunCommand ( "init project" , context ) ; let openInNewWindow = false ; if ( vscode . workspace . workspaceFolders && vscode . workspace . workspaceFolders . length > 0 ) { openInNewWindow = true ; } await vscode . window . withProgress ( { title : "Project initialization" , location : vscode . ProgressLocation . Window } , async ( progress ) => { progress . report ( { message : "Updating a list of available template" } ) ; const scaffoldType = ScaffoldType . Local ; const projectPath = await this . generateProjectFolder ( telemetryContext , scaffoldType ) ; if ( ! projectPath ) { throw new OperationCanceledError ( `Project initialization cancelled: Project name input cancelled.` ) ; } const platformSelection = await utils . selectPlatform ( scaffoldType , context ) ; if ( ! platformSelection ) { throw new OperationCanceledError ( `Project initialization cancelled: Platform selection cancelled.` ) ; } else { telemetryContext . properties . platform = platformSelection . label ; } const templateJson = await utils . getTemplateJson ( context , scaffoldType ) ; let templateName ; if ( platformSelection . label === PlatformType . Arduino ) { const templateSelection = await this . selectTemplate ( templateJson , PlatformType . Arduino ) ; if ( ! templateSelection ) { throw new OperationCanceledError ( `Project initialization cancelled: Project template selection cancelled.` ) ; } else { telemetryContext . properties . template = templateSelection . label ; if ( templateSelection . label === constants . noDeviceMessage ) { await utils . takeNoDeviceSurvey ( telemetryContext , context ) ; return ; } } templateName = templateSelection . label ; } else { templateName = constants . embeddedLinuxProjectName ; } const template = templateJson . templates . find ( ( template ) => { return template . platform === platformSelection . label && template . name === templateName ; } ) ; if ( ! template ) { throw new ResourceNotFoundError ( "initialize iot project" , `project template in template json file with the given template name ${ templateName } \
            and platform ${ platformSelection . label }` , "" ) ; } const projectTemplateType = utils . getEnumKeyByEnumValue ( ProjectTemplateType , template . type ) ; const resourceRootPath = context . asAbsolutePath ( path . join ( FileNames . resourcesFolderName , FileNames . templatesFolderName ) ) ; const templateFolder = path . join ( resourceRootPath , template . path ) ; const templateFilesInfo = await utils . getTemplateFilesInfo ( templateFolder ) ; let project ; if ( template . platform === PlatformType . EmbeddedLinux ) { project = new ioTContainerizedProjectModule . IoTContainerizedProject ( context , channel , telemetryContext , projectPath ) ; } else if ( template . platform === PlatformType . Arduino ) { project = new ioTWorkspaceProjectModule . IoTWorkspaceProject ( context , channel , telemetryContext , projectPath ) ; } else { throw new TypeNotSupportedError ( "platform" , `${ template . platform }` ) ; } await project . create ( templateFilesInfo , projectTemplateType , template . boardId , openInNewWindow ) ; } ) ; } private async selectTemplate ( templateJson , platform ) { const result = templateJson . templates . filter ( ( template ) => { return template . platform === platform && template . tag === TemplateTag . General ; } ) ; const projectTemplateList = [ ] ; result . forEach ( ( element ) => { projectTemplateList . push ( { label : element . name , description : element . description , detail : element . detail } ) ; } ) ; const templateSelection = await vscode . window . showQuickPick ( projectTemplateList , { ignoreFocusOut : true , matchOnDescription : true , matchOnDetail : true , placeHolder : "Select a project template" } ) ; return templateSelection ; } private async generateProjectFolder ( telemetryContext , scaffoldType ) { const settings = await IoTWorkbenchSettings . getInstance ( ) ; const workbench = settings . getWorkbenchPath ( ) ; const projectRootPath = path . join ( workbench , "projects" ) ; if ( ! ( await FileUtility . directoryExists ( scaffoldType , projectRootPath ) ) ) { await FileUtility . mkdirRecursively ( scaffoldType , projectRootPath ) ; } let counter = 0 ; const name = constants . defaultProjectName ; let candidateName = name ; while ( true ) { const projectPath = path . join ( projectRootPath , candidateName ) ; const isValid = await this . isProjectPathValid ( scaffoldType , projectPath ) ; if ( isValid ) { break ; } counter ++ ; candidateName = `${ name }_${ counter }` ; } const projectName = await vscode . window . showInputBox ( { value : candidateName , prompt : "Input project name." , ignoreFocusOut : true , validateInput : async ( projectName ) => { if ( ! /^([a-z0-9_]|[a-z0-9_][-a-z0-9_.]*[a-z0-9_])(\.ino)?$/i . test ( projectName ) ) { return 'Project name can only contain letters, numbers, "-" and ".", and cannot start or end with "-" or ".".' ; } const projectPath = path . join ( projectRootPath , projectName ) ; const isProjectNameValid = await this . isProjectPathValid ( scaffoldType , projectPath ) ; if ( isProjectNameValid ) { return ; } else { return `${ projectPath } exists, please choose another name.` ; } } } ) ; if ( projectName ) { const projectNameMd5 = utils . getHashFromString ( projectName ) ; telemetryContext . properties . projectName = projectNameMd5 ; } const projectPath = projectName ? path . join ( projectRootPath , projectName ) : undefined ; return projectPath ; } private async isProjectPathValid ( scaffoldType , projectPath ) { const projectPathExists = await FileUtility . fileExists ( scaffoldType , projectPath ) ; const projectDirectoryExists = await FileUtility . directoryExists ( scaffoldType , projectPath ) ; return ! projectPathExists && ! projectDirectoryExists ; } }