genezio
Version:
Command line utility to interact with Genezio infrastructure.
233 lines (232 loc) • 12.4 kB
JavaScript
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;
});
}));
};