UNPKG

genezio

Version:

Command line utility to interact with Genezio infrastructure.

233 lines (232 loc) 12.4 kB
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _DartBundler_instances, _DartBundler_getDartCompilePresignedUrl, _DartBundler_analyze, _DartBundler_compile, _DartBundler_downloadAndUnzipFromS3ToFolder, _DartBundler_castParameterToPropertyType, _DartBundler_getProperCast, _DartBundler_createRouterFileForClass, _DartBundler_uploadUserCodeToS3, _DartBundler_addLambdaRuntimeDependency, _DartBundler_copyNonDartFiles; import axios from "axios"; import fs from "fs"; import path from "path"; import Mustache from "mustache"; import { getCompileDartPresignedURL } from "../../requests/getCompileDartPresignedURL.js"; import { uploadContentToS3 } from "../../requests/uploadContentToS3.js"; import admZip from "adm-zip"; import { createTemporaryFolder, deleteFolder, writeToFile, zipDirectory, } from "../../utils/file.js"; import { checkIfDartIsInstalled } from "../../utils/dart.js"; import { debugLogger } from "../../utils/logging.js"; import { template } from "./dartMain.js"; import { default as fsExtra } from "fs-extra"; import { DART_COMPILATION_ENDPOINT } from "../../constants.js"; import { TriggerType } from "../../projectConfiguration/yaml/models.js"; import { spawnSync } from "child_process"; import { log } from "../../utils/logging.js"; import { runNewProcess } from "../../utils/process.js"; import { getAllFilesFromCurrentPath } from "../../utils/file.js"; import { AstNodeType, } from "../../models/genezioModels.js"; import { castArrayRecursivelyInitial, castMapRecursivelyInitial, } from "../../utils/dartAstCasting.js"; import { GENEZIO_NOT_ENOUGH_PERMISSION_FOR_FILE, UserError } from "../../errors.js"; export class DartBundler { constructor() { _DartBundler_instances.add(this); } async bundle(input) { // Create a temporary folder were we copy user code to prepare everything. const folderPath = input.genezioConfigurationFilePath; const inputTemporaryFolder = await createTemporaryFolder(input.configuration.name); let temporaryFolder = undefined; try { await fsExtra.copy(folderPath, inputTemporaryFolder); debugLogger.debug(`Copy files in temp folder ${inputTemporaryFolder}`); // Create the router class const userClass = input.projectConfiguration.classes.find((c) => c.path == input.path); if (!userClass) throw new UserError("Class not found while bundling"); __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_createRouterFileForClass).call(this, userClass, input.ast, inputTemporaryFolder); // Check if dart is installed await checkIfDartIsInstalled(); await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_addLambdaRuntimeDependency).call(this, inputTemporaryFolder); await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_analyze).call(this, inputTemporaryFolder); const archiveName = await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_uploadUserCodeToS3).call(this, input.projectConfiguration.name, userClass.name, inputTemporaryFolder); // Compile the Dart code on the server debugLogger.debug("Compiling Dart..."); const s3Zip = await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_compile).call(this, archiveName); debugLogger.debug("Compiling Dart finished."); if (s3Zip.success === false) { throw new UserError("Failed to upload code for compiling."); } temporaryFolder = await createTemporaryFolder(); try { debugLogger.debug(`Copy all non dart files to folder ${temporaryFolder}...`); await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_copyNonDartFiles).call(this, temporaryFolder); debugLogger.debug("Copy all non dart files to folder done."); debugLogger.debug("Downloading compiled code..."); await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_downloadAndUnzipFromS3ToFolder).call(this, s3Zip.downloadUrl, temporaryFolder); debugLogger.debug("Finished downloading compiled code..."); } catch (error) { await deleteFolder(temporaryFolder); throw error; } } finally { // remove temporary folder await deleteFolder(inputTemporaryFolder); } return { ...input, path: temporaryFolder, }; } } _DartBundler_instances = new WeakSet(), _DartBundler_getDartCompilePresignedUrl = async function _DartBundler_getDartCompilePresignedUrl(archiveName) { return (await getCompileDartPresignedURL(archiveName)).presignedURL; }, _DartBundler_analyze = async function _DartBundler_analyze(path) { const result = spawnSync("dart", ["analyze", "--no-fatal-warnings"], { cwd: path, }); if (result.status != 0) { log.info(result.stdout.toString().split("\n").slice(1).join("\n")); throw new UserError("Compilation error! Please check your code and try again."); } }, _DartBundler_compile = async function _DartBundler_compile(archiveName) { const url = DART_COMPILATION_ENDPOINT; const response = await axios({ method: "PUT", url: url, data: { archiveName }, maxContentLength: Infinity, maxBodyLength: Infinity, }); if (response.data.status === "error") { throw new UserError(response.data.error.message); } return response.data; }, _DartBundler_downloadAndUnzipFromS3ToFolder = async function _DartBundler_downloadAndUnzipFromS3ToFolder(s3ZipUrl, temporaryFolder) { const compiledZip = path.join(temporaryFolder, "compiled.zip"); return await axios({ url: s3ZipUrl, responseType: "arraybuffer", }) .then((response) => { fs.writeFileSync(compiledZip, response.data); }) .then(async () => { const zip = new admZip(compiledZip); try { zip.extractAllTo(temporaryFolder, true); } catch (error) { debugLogger.debug(`Failed to extract files: ${error}`); throw new Error("Failed to extract files. Please open an issue on GitHub"); } fs.unlinkSync(compiledZip); }); }, _DartBundler_castParameterToPropertyType = function _DartBundler_castParameterToPropertyType(node, variableName) { let implementation = ""; switch (node.type) { case AstNodeType.StringLiteral: implementation += `${variableName} as String`; break; case AstNodeType.DoubleLiteral: implementation += `${variableName} as double`; break; case AstNodeType.BooleanLiteral: implementation += `${variableName} as bool`; break; case AstNodeType.IntegerLiteral: implementation += `${variableName} as int`; break; case AstNodeType.PromiseType: implementation += __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_castParameterToPropertyType).call(this, node.generic, variableName); break; case AstNodeType.CustomNodeLiteral: implementation += `${node.rawValue}.fromJson(${variableName} as Map<String, dynamic>)`; break; case AstNodeType.ArrayType: implementation += castArrayRecursivelyInitial(node, variableName); break; case AstNodeType.MapType: implementation += castMapRecursivelyInitial(node, variableName); } return implementation; }, _DartBundler_getProperCast = function _DartBundler_getProperCast(mainClass, method, parameterType, index) { const type = mainClass.methods .find((m) => m.name == method.name) ?.params.find((p) => p.name == parameterType.name); if (!type) throw new UserError("Type not found"); return `${__classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_castParameterToPropertyType).call(this, type.paramType, `params[${index}]`)}`; }, _DartBundler_createRouterFileForClass = async function _DartBundler_createRouterFileForClass(classConfiguration, ast, folderPath) { const mainClass = ast.body?.find((element) => { return element.type === AstNodeType.ClassDefinition; }); const moustacheViewForMain = { classFileName: path.basename(classConfiguration.path, path.extname(classConfiguration.path)), className: classConfiguration.name, jsonRpcMethods: classConfiguration.methods .filter((m) => m.type === TriggerType.jsonrpc) .map((m) => ({ name: m.name, parameters: m.parameters.map((p, index) => ({ index, cast: __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_getProperCast).call(this, mainClass, m, p, index), })), })), cronMethods: classConfiguration.methods .filter((m) => m.type === TriggerType.cron) .map((m) => ({ name: m.name, })), httpMethods: classConfiguration.methods .filter((m) => m.type === TriggerType.http) .map((m) => ({ name: m.name, })), imports: ast.body?.map((element) => ({ name: element.path })), }; const routerFileContent = Mustache.render(template, moustacheViewForMain); await writeToFile(folderPath, "main.dart", routerFileContent); }, _DartBundler_uploadUserCodeToS3 = async function _DartBundler_uploadUserCodeToS3(projectName, className, userCodeFolderPath) { const random = Math.random().toString(36).substring(2); const archiveName = `${projectName}${className}${random}.zip`; const presignedUrl = await __classPrivateFieldGet(this, _DartBundler_instances, "m", _DartBundler_getDartCompilePresignedUrl).call(this, archiveName); const archiveDirectoryOutput = await createTemporaryFolder(); const archivePath = path.join(archiveDirectoryOutput, archiveName); try { await zipDirectory(userCodeFolderPath, archivePath, true); await uploadContentToS3(presignedUrl, archivePath); } finally { // remove temporary folder await deleteFolder(archiveDirectoryOutput); } return archiveName; }, _DartBundler_addLambdaRuntimeDependency = async function _DartBundler_addLambdaRuntimeDependency(path) { const success = await runNewProcess("dart pub add aws_lambda_dart_runtime:'^1.1.0'", path, false, false); if (!success) { throw new UserError("Error while adding aws_lambda_dart_runtime dependency"); } }, _DartBundler_copyNonDartFiles = async function _DartBundler_copyNonDartFiles(tempFolderPath) { const allNonJsFilesPaths = (await getAllFilesFromCurrentPath()).filter((file) => { // filter js files, node_modules and folders return (file.extension !== ".dart" && !file.path.includes(".git") && !fs.lstatSync(file.path).isDirectory()); }); // iterate over all non dart files and copy them to tmp folder await Promise.all(allNonJsFilesPaths.map((filePath) => { // create folder structure in tmp folder const folderPath = path.join(tempFolderPath, path.dirname(filePath.path)); if (!fs.existsSync(folderPath)) { fs.mkdirSync(folderPath, { recursive: true }); } // copy file to tmp folder const fileDestinationPath = path.join(tempFolderPath, filePath.path); return fs.promises.copyFile(filePath.path, fileDestinationPath).catch((error) => { if (error.code === "EACCES") { throw new UserError(GENEZIO_NOT_ENOUGH_PERMISSION_FOR_FILE(filePath.path)); } throw error; }); })); };