UNPKG

@sassoftware/vi-solution-extension-create

Version:

Creates a development environment for creating SAS Visual Investigator solution extensions.

264 lines (220 loc) 9.13 kB
#!/usr/bin/env node const shell = require("shelljs"); const prompt = require("prompt"); const fs = require("fs"); const path = require("path"); const ngPath = getNgPath(); // Display angular version before initialising creation prompt shell.exec(`${ngPath} version`); prompt.start(); const promptSchema = { properties: { projectName: { description: "Enter a name for this solution (e.g. my-great-solution)", type: "string", required: true }, sviHostname: { description: "Enter the name of the server to auto-deploy to (e.g. http://acme.server.na.sas.com", type: "string", required: true }, sviBearerToken: { description: "Will this project use an existing bearer token to upload the solution? (y/n)", type: "string", pattern: /^[YyNn]$/, // Accepts only Y or N default: "n", // Default value is N required: false }, sviUsername: { description: "Enter an admin username to be used to upload the solution", type: "string", // Only required if not using an existing bearer token. required: function () { return prompt.history("sviBearerToken").value.toLowerCase() === "n"; }, // Only display this prompt if the user does not have an existing bearer token. ask: function () { return prompt.history("sviBearerToken").value.toLowerCase() === "n"; } }, sviPassword: { description: "Enter the password for the admin user", type: "string", // Only required if not using an existing bearer token. required: function () { return prompt.history("sviBearerToken").value.toLowerCase() === "n"; }, // Only display this prompt if the user does not have an existing bearer token. ask: function () { return prompt.history("sviBearerToken").value.toLowerCase() === "n"; } }, includeMobile: { description: "Will this project also include mobile custom controls? (y/n)", type: "string", pattern: /^[YyNn]$/, // Accepts only Y or N default: "n", // Default value is N required: false } } }; prompt.get(promptSchema, function (err, result) { if (err) { log("Required inputs not provided"); log(err); return; } const { projectName, sviHostname, sviUsername, sviPassword } = result; const hasBearerToken = result.sviBearerToken.toLowerCase() === "y"; const includeMobile = result.includeMobile.toLowerCase() === "y"; executeNgCommand(`new ${projectName} --create-application false --routing false --style scss`); shell.cd(projectName); executeNgCommand("g library components"); executeNgCommand("g application elements --routing false --style scss"); executeNgCommand("add @angular/elements --project elements --skip-confirmation true"); executeNgCommand("add ngx-build-plus@^18.0.0 --project elements --skip-confirmation true"); removeUnnecessaryFiles(); addRequiredPackages(); modifyPackageJSON(); modifyAppModules(); modifyMainTypeScriptFile(); addPathToAngularJSON(); modifyEnvFile(includeMobile, sviHostname, sviUsername, sviPassword, hasBearerToken); if (includeMobile) { executeAndLogCommand("npx ng g @sassoftware/vi-solution-extension-angular-schematics:add-mobile"); } }); /* The main.ts file is not in the correct state for the purposes of this project after upgrading Angular version, this function resets it to the required state. */ function modifyMainTypeScriptFile() { const alterMainFileLogger = createLogger("Altering main.ts file"); alterMainFileLogger.start(); const mainFile = fs.readFileSync(path.resolve(__dirname, "./files/main.ts"), "utf-8"); fs.writeFileSync(`${process.cwd()}/projects/elements/src/main.ts`, mainFile); alterMainFileLogger.end(); } /* As part of the build step a "main" property is required instead of "browser"since upgrading the ngx-build-plus:browser builder version which is not added in the project setup and must be done as part of the scaffold. */ function addPathToAngularJSON() { const alterAngularJsonLogger = createLogger("Altering angular.json file"); const angularJson = fs.readFileSync(`${process.cwd()}/angular.json`, "utf8"); const config = JSON.parse(angularJson); if (config.projects && config.projects.elements) { // Add the 'main' property in architect.build.options if (!config.projects.elements.architect.build.options.main) { const browserValue = config.projects.elements.architect.build.options.browser; config.projects.elements.architect.build.options.main = browserValue || "projects/elements/src/main.ts"; } // Delete browser property if existing. if (config.projects.elements.architect.build.options.browser) { delete config.projects.elements.architect.build.options.browser; } const modifiedConfig = JSON.stringify(config, null, 2); fs.writeFileSync(`${process.cwd()}/angular.json`, modifiedConfig, "utf8"); alterAngularJsonLogger.end(); } } function removeUnnecessaryFiles() { const removeFilesLogger = createLogger("Removing unnecessary files created by ng new"); removeFilesLogger.start(); shell.rm("./projects/elements/src/app/app.component.*"); shell.rm("./projects/elements/src/app/app.config.*"); shell.rm("./projects/components/src/lib/components.*"); shell.rm("-rf", "./projects/environments"); shell.rm("-rf", "./projects/assets"); removeFilesLogger.end(); } function modifyPackageJSON() { const alterPackageJsonLogger = createLogger("Altering package.json structure"); alterPackageJsonLogger.start(); const packageJsonPath = `${process.cwd()}/package.json`; const pkg = require(packageJsonPath); delete pkg.scripts.serve; delete pkg.scripts.start; delete pkg.scripts.ng; delete pkg.scripts.test; pkg.scripts.build = "env-cmd ng build --configuration production --project elements --output-hashing none --single-bundle"; pkg.scripts.watch = "env-cmd ng build --configuration production --project elements --output-hashing none --single-bundle --watch --plugin @sassoftware/vi-solution-extension-upload/src/upload-bundle.ngx-plugin"; pkg.scripts["watch:debug"] = "env-cmd ng build --configuration development --project elements --output-hashing none --single-bundle --watch --plugin @sassoftware/vi-solution-extension-upload/src/upload-bundle.ngx-plugin"; pkg.scripts["create:solution-control"] = "env-cmd ng g @sassoftware/vi-solution-extension-angular-schematics:wc --project components"; pkg.scripts["add-mobile"] = "ng g @sassoftware/vi-solution-extension-angular-schematics:add-mobile"; fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2)); alterPackageJsonLogger.end(); } function modifyAppModules() { const alterAppModulesLogger = createLogger("Altering element and component app modules"); alterAppModulesLogger.start(); const componentsAppModule = fs.readFileSync( path.resolve(__dirname, "./files/app-modules/components.app.module.ts"), "utf-8" ); const elementsAppModule = fs.readFileSync( path.resolve(__dirname, "./files/app-modules/elements.app.module.ts"), "utf-8" ); fs.writeFileSync(`${process.cwd()}/projects/components/src/app.module.ts`, componentsAppModule); fs.writeFileSync(`${process.cwd()}/projects/elements/src/app/app.module.ts`, elementsAppModule); alterAppModulesLogger.end(); } function modifyEnvFile(includeMobile, sviHostname, sviUsername, sviPassword, hasBearerToken) { const envLogger = createLogger("Writing .env"); envLogger.start(); const environment = includeMobile ? ` SVI_HOSTNAME=${sviHostname} SVI_USERNAME=${sviUsername} SVI_PASSWORD=${sviPassword} SVI_BUNDLE_PATH=./dist/elements/main.js SMI_BUNDLE_PATH=./dist/mobile-elements/main.js ${hasBearerToken ? "SVI_BEARER_TOKEN=" : ""} `.trim() : ` SVI_HOSTNAME=${sviHostname} SVI_USERNAME=${sviUsername} SVI_PASSWORD=${sviPassword} SVI_BUNDLE_PATH=./dist/elements/main.js ${hasBearerToken ? "SVI_BEARER_TOKEN=" : ""} `.trim(); fs.writeFileSync(`${process.cwd()}/.env`, environment); envLogger.end(); } function addRequiredPackages() { executeNgCommand("add @sassoftware/vi-solution-extension-angular-schematics --skip-confirmation true"); executeAndLogCommand("npm i --save-dev @sassoftware/vi-solution-extension-upload"); executeAndLogCommand("npm i --save-dev env-cmd"); } function executeNgCommand(command) { return executeAndLogCommand(`node ${ngPath} ${command}`); } function executeAndLogCommand(command) { log(`Executing command: ${command}`); return shell.exec(command); } function log(message) { console.log(`----- ${message} -----`); } function createLogger(message) { return { start: () => log(`Start: ${message}.`), end: () => log(`End: ${message}.`) }; } function getNgPath() { // Default to using node_modules in base npx directory for @angular/cli const ngPath = require.resolve("@angular/cli/bin/ng"); if (!fs.existsSync(ngPath)) { console.error("Error: The required dependency `@angular/cli` could not be found.\n"); process.exit(1); } return ngPath; }