diff --git a/vscode-extension/src/main/ts/commands/createNewProject.ts b/vscode-extension/src/main/ts/commands/createNewProject.ts index 4dd8f9df7..b6836bff5 100644 --- a/vscode-extension/src/main/ts/commands/createNewProject.ts +++ b/vscode-extension/src/main/ts/commands/createNewProject.ts @@ -18,17 +18,28 @@ import * as fs from "fs"; import * as path from "path"; import selectWorkspaceSDK from "./selectWorkspaceSDK"; import validateFrameworkSDK from "../utils/validateFrameworkSDK"; +import validateRoyale from "../utils/validateRoyale"; +import validateFlex from "../utils/validateFlex"; +import validateFeathers from "../utils/validateFeathers"; const FILE_ASCONFIG_JSON = "asconfig.json"; const FILE_SETTINGS_JSON = "settings.json"; const FILE_LAUNCH_JSON = "launch.json"; const FILE_EXTENSION_AS = ".as"; +const FILE_EXTENSION_MXML = ".mxml"; const FILE_EXTENSION_SWF = ".swf"; interface WorkspaceFolderQuickPickItem extends vscode.QuickPickItem { workspaceFolder?: vscode.WorkspaceFolder; } +interface ProjectQuickPickItem { + flex?: boolean; + royale?: boolean; + feathers?: boolean; + mobile?: boolean; +} + export function createNewProject() { var workspaceFolders = vscode.workspace.workspaceFolders; if (workspaceFolders == null) { @@ -128,6 +139,59 @@ async function createNewProjectAtUri(uri: vscode.Uri): Promise { return Promise.resolve(false); } + const isFeathers = validateFeathers(sdkPath); + const isRoyale = validateRoyale(sdkPath); + const isFlex = !isFeathers && !isRoyale && validateFlex(sdkPath); + let projectType: ProjectQuickPickItem = {}; + + if (isFlex) { + await vscode.window + .showQuickPick( + [ + { label: "Apache Flex Desktop", flex: true }, + { label: "Apache Flex Mobile", flex: true, mobile: true }, + { label: "ActionScript Desktop" }, + { label: "ActionScript Mobile", mobile: true }, + ] as (vscode.QuickPickItem & ProjectQuickPickItem)[], + { + title: "Select a project type…", + } + ) + .then((result) => (projectType = result)); + } else if (isRoyale) { + projectType = { royale: true }; + } else if (isFeathers) { + await vscode.window + .showQuickPick( + [ + { label: "Feathers SDK Desktop", feathers: true }, + { label: "Feathers SDK Mobile", feathers: true, mobile: true }, + { label: "ActionScript Desktop" }, + { label: "ActionScript Mobile", mobile: true }, + ] as (vscode.QuickPickItem & ProjectQuickPickItem)[], + { + title: "Select a project type…", + } + ) + .then((result) => (projectType = result)); + } else { + await vscode.window + .showQuickPick( + [ + { label: "ActionScript Desktop" }, + { label: "ActionScript Mobile", mobile: true }, + ] as (vscode.QuickPickItem & ProjectQuickPickItem)[], + { + title: "Select a project type…", + } + ) + .then((result) => (projectType = result)); + } + + if (!projectType) { + return; + } + fs.mkdirSync(projectRoot, { recursive: true }); const srcPath = path.resolve(projectRoot, "src"); fs.mkdirSync(srcPath, { recursive: true }); @@ -144,8 +208,9 @@ async function createNewProjectAtUri(uri: vscode.Uri): Promise { encoding: "utf8", }); - const launchJsonPath = path.resolve(vscodePath, FILE_LAUNCH_JSON); - const launchJsonContents = `{ + if (!isRoyale) { + const launchJsonPath = path.resolve(vscodePath, FILE_LAUNCH_JSON); + const launchJsonContents = `{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 @@ -158,7 +223,8 @@ async function createNewProjectAtUri(uri: vscode.Uri): Promise { } ] }`; - fs.writeFileSync(launchJsonPath, launchJsonContents, { encoding: "utf8" }); + fs.writeFileSync(launchJsonPath, launchJsonContents, { encoding: "utf8" }); + } // generate the main class name from the project name let mainClassName = path @@ -173,44 +239,52 @@ async function createNewProjectAtUri(uri: vscode.Uri): Promise { } const swfFileName = `${mainClassName}${FILE_EXTENSION_SWF}`; const descriptorFileName = `${mainClassName}-app.xml`; - const mainClassFileName = `${mainClassName}${FILE_EXTENSION_AS}`; - - const descriptorTemplatePath = sdkPath - ? path.resolve(sdkPath, "templates/air/descriptor-template.xml") - : undefined; - const airDescriptorContents = createAirDescriptor( - descriptorTemplatePath, - swfFileName, - mainClassName, - mainClassName - ); - if (airDescriptorContents) { - const descriptorOutputPath = path.resolve(srcPath, descriptorFileName); - fs.writeFileSync(descriptorOutputPath, airDescriptorContents, { - encoding: "utf8", - }); - } - const asconfigContents = createAsconfigJson( - mainClassName, - swfFileName, - airDescriptorContents ? descriptorFileName : null, - false - ); - const asconfigPath = path.resolve(projectRoot, FILE_ASCONFIG_JSON); - fs.writeFileSync(asconfigPath, asconfigContents, { encoding: "utf8" }); + let hasAirDescriptor = false; + if (!isRoyale) { + const descriptorTemplatePath = sdkPath + ? path.resolve(sdkPath, "templates/air/descriptor-template.xml") + : undefined; + const airDescriptorContents = createAirDescriptor( + descriptorTemplatePath, + projectType, + swfFileName, + mainClassName, + mainClassName + ); + if (airDescriptorContents) { + hasAirDescriptor = true; + const descriptorOutputPath = path.resolve(srcPath, descriptorFileName); + fs.writeFileSync(descriptorOutputPath, airDescriptorContents, { + encoding: "utf8", + }); + } + } - const mainClassOutputPath = path.resolve(srcPath, `${mainClassFileName}`); - const mainClassContents = createMainClassAS3(mainClassName); - fs.writeFileSync(mainClassOutputPath, mainClassContents, { + let asconfigJsonContents: string | undefined = undefined; + if (projectType.royale) { + asconfigJsonContents = createAsconfigJsonRoyale(mainClassName); + } else { + asconfigJsonContents = createAsconfigJson( + mainClassName, + swfFileName, + hasAirDescriptor ? descriptorFileName : null, + projectType.mobile + ); + } + const asconfigJsonPath = path.resolve(projectRoot, FILE_ASCONFIG_JSON); + fs.writeFileSync(asconfigJsonPath, asconfigJsonContents, { encoding: "utf8", }); + createSourceFiles(projectType, srcPath, mainClassName); + return Promise.resolve(true); } function createAirDescriptor( descriptorTemplatePath: string, + projectType: ProjectQuickPickItem, swfFileName: string, applicationId: string, fileName: string @@ -234,10 +308,12 @@ function createAirDescriptor( /.*<\/filename>(?!\s*-->)/, `${fileName}` ); - descriptorContents = descriptorContents.replace( - //, - `true` - ); + if (!projectType.flex && !projectType.royale && !projectType.feathers) { + descriptorContents = descriptorContents.replace( + //, + `true` + ); + } return descriptorContents; } @@ -264,10 +340,51 @@ function createAsconfigJson( `; } -function createMainClassAS3(mainClassName: string): string { - return `package +function createAsconfigJsonRoyale(mainClassName: string): string { + return `{ + "config": "royale", + "compilerOptions": { + "targets": [ + "JSRoyale" + ], + "source-path": [ + "src" + ], + "library-path": [ + "libs" + ] + }, + "mainClass": "${mainClassName}" +} +`; +} + +function createSourceFiles( + projectType: ProjectQuickPickItem, + srcPath: string, + mainClassName: string +) { + if (projectType.flex) { + if (projectType.mobile) { + createSourceFilesFlexMobile(srcPath, mainClassName); + } else { + createSourceFilesFlexDesktop(srcPath, mainClassName); + } + } else if (projectType.royale) { + createSourceFilesRoyaleBasic(srcPath, mainClassName); + } else if (projectType.feathers) { + createSourceFilesFeathers(srcPath, mainClassName); + } else { + createSourceFilesAS3(srcPath, mainClassName); + } +} + +function createSourceFilesAS3(srcPath: string, mainClassName: string) { + const mainClassContents = `package { import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; @@ -278,6 +395,9 @@ function createMainClassAS3(mainClassName: string): string { { super(); + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + var textField:TextField = new TextField(); textField.autoSize = TextFieldAutoSize.LEFT; textField.defaultTextFormat = new TextFormat("_sans", 12, 0x0000cc); @@ -287,4 +407,126 @@ function createMainClassAS3(mainClassName: string): string { } } `; + const mainClassOutputPath = path.resolve( + srcPath, + `${mainClassName}${FILE_EXTENSION_AS}` + ); + fs.writeFileSync(mainClassOutputPath, mainClassContents, { + encoding: "utf8", + }); +} + +function createSourceFilesFlexDesktop(srcPath: string, mainClassName: string) { + const mainClassContents = ` + + + + + + + +`; + const mainClassOutputPath = path.resolve( + srcPath, + `${mainClassName}${FILE_EXTENSION_MXML}` + ); + fs.writeFileSync(mainClassOutputPath, mainClassContents, { + encoding: "utf8", + }); +} + +function createSourceFilesFlexMobile(srcPath: string, mainClassName: string) { + const mainClassContents = ` + + + + + +`; + const mainClassOutputPath = path.resolve( + srcPath, + `${mainClassName}${FILE_EXTENSION_MXML}` + ); + fs.writeFileSync(mainClassOutputPath, mainClassContents, { + encoding: "utf8", + }); + + const viewsPath = path.resolve(srcPath, "views"); + fs.mkdirSync(viewsPath, { recursive: true }); + const homeViewClassContents = ` + + + + + + + +`; + const homeViewClassOutputPath = path.resolve( + srcPath, + `views/HomeView${FILE_EXTENSION_MXML}` + ); + fs.writeFileSync(homeViewClassOutputPath, homeViewClassContents, { + encoding: "utf8", + }); +} + +function createSourceFilesRoyaleBasic(srcPath: string, mainClassName: string) { + const mainClassContents = ` + + + + + + + + + + + + + + + + + + +`; + const mainClassOutputPath = path.resolve( + srcPath, + `${mainClassName}${FILE_EXTENSION_MXML}` + ); + fs.writeFileSync(mainClassOutputPath, mainClassContents, { + encoding: "utf8", + }); +} + +function createSourceFilesFeathers(srcPath: string, mainClassName: string) { + const mainClassContents = ` + + + + + + + +`; + const mainClassOutputPath = path.resolve( + srcPath, + `${mainClassName}${FILE_EXTENSION_MXML}` + ); + fs.writeFileSync(mainClassOutputPath, mainClassContents, { + encoding: "utf8", + }); } diff --git a/vscode-extension/src/main/ts/utils/validateFeathers.ts b/vscode-extension/src/main/ts/utils/validateFeathers.ts new file mode 100644 index 000000000..a4c002eac --- /dev/null +++ b/vscode-extension/src/main/ts/utils/validateFeathers.ts @@ -0,0 +1,85 @@ +/* +Copyright 2016-2023 Bowler Hat LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import * as fs from "fs"; +import * as path from "path"; +import validateFrameworkSDK from "./validateFrameworkSDK"; + +const XML_VERSION_START = ""; +const XML_VERSION_END = ""; +const PATH_SDK_DESCRIPTION_FLEX = "flex-sdk-description.xml"; +const PATH_MANIFEST_FEATHERS = "frameworks/projects/feathers/manifest.xml"; + +export default function validateFeathers( + sdkPath: string, + minVersion?: string +): boolean { + if (!validateFrameworkSDK(sdkPath)) { + return false; + } + const feathersManifestPath = path.join(sdkPath, PATH_MANIFEST_FEATHERS); + if (!fs.existsSync(feathersManifestPath)) { + return false; + } + const flexDescriptionPath = path.join(sdkPath, PATH_SDK_DESCRIPTION_FLEX); + if (!fs.existsSync(flexDescriptionPath)) { + return false; + } + if (!minVersion) { + return true; + } + const flexDescription = fs.readFileSync(flexDescriptionPath, "utf8"); + const versionString = readBetween( + flexDescription, + XML_VERSION_START, + XML_VERSION_END + ); + const versionParts = versionString.split("-")[0].split("."); + const minVersionParts = minVersion.split("-")[0].split("."); + const major = versionParts.length > 0 ? versionParts[0] : 0; + const minor = versionParts.length > 1 ? versionParts[1] : 0; + const revision = versionParts.length > 2 ? versionParts[2] : 0; + const minMajor = minVersionParts.length > 0 ? minVersionParts[0] : 0; + const minMinor = minVersionParts.length > 1 ? minVersionParts[1] : 0; + const minRevision = minVersionParts.length > 2 ? minVersionParts[2] : 0; + if (major > minMajor) { + return true; + } else if (major == minMajor) { + if (minor > minMinor) { + return true; + } else if (minor == minMinor) { + if (revision >= minRevision) { + return true; + } + } + } + return false; +} + +function readBetween( + fileContents: string, + startText: string, + endText: string +): string { + let startIndex = fileContents.indexOf(startText); + if (startIndex !== -1) { + startIndex += startText.length; + let endIndex = fileContents.indexOf(endText, startIndex + 1); + if (endIndex !== -1) { + return fileContents.substr(startIndex, endIndex - startIndex); + } + } + return null; +} diff --git a/vscode-extension/src/main/ts/utils/validateFlex.ts b/vscode-extension/src/main/ts/utils/validateFlex.ts new file mode 100644 index 000000000..fa19489ae --- /dev/null +++ b/vscode-extension/src/main/ts/utils/validateFlex.ts @@ -0,0 +1,80 @@ +/* +Copyright 2016-2023 Bowler Hat LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import * as fs from "fs"; +import * as path from "path"; +import validateFrameworkSDK from "./validateFrameworkSDK"; + +const XML_VERSION_START = ""; +const XML_VERSION_END = ""; +const PATH_SDK_DESCRIPTION_FLEX = "flex-sdk-description.xml"; + +export default function validateFlex( + sdkPath: string, + minVersion?: string +): boolean { + if (!validateFrameworkSDK(sdkPath)) { + return false; + } + const flexDescriptionPath = path.join(sdkPath, PATH_SDK_DESCRIPTION_FLEX); + if (!fs.existsSync(flexDescriptionPath)) { + return false; + } + if (!minVersion) { + return true; + } + const flexDescription = fs.readFileSync(flexDescriptionPath, "utf8"); + const versionString = readBetween( + flexDescription, + XML_VERSION_START, + XML_VERSION_END + ); + const versionParts = versionString.split("-")[0].split("."); + const minVersionParts = minVersion.split("-")[0].split("."); + const major = versionParts.length > 0 ? versionParts[0] : 0; + const minor = versionParts.length > 1 ? versionParts[1] : 0; + const revision = versionParts.length > 2 ? versionParts[2] : 0; + const minMajor = minVersionParts.length > 0 ? minVersionParts[0] : 0; + const minMinor = minVersionParts.length > 1 ? minVersionParts[1] : 0; + const minRevision = minVersionParts.length > 2 ? minVersionParts[2] : 0; + if (major > minMajor) { + return true; + } else if (major == minMajor) { + if (minor > minMinor) { + return true; + } else if (minor == minMinor) { + if (revision >= minRevision) { + return true; + } + } + } + return false; +} + +function readBetween( + fileContents: string, + startText: string, + endText: string +): string { + let startIndex = fileContents.indexOf(startText); + if (startIndex !== -1) { + startIndex += startText.length; + let endIndex = fileContents.indexOf(endText, startIndex + 1); + if (endIndex !== -1) { + return fileContents.substr(startIndex, endIndex - startIndex); + } + } + return null; +}