forest-cli
Version:
The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.
218 lines • 17.7 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const languages_1 = __importDefault(require("../../utils/languages"));
const abstract_dumper_1 = __importDefault(require("./abstract-dumper"));
class AgentNodeJs extends abstract_dumper_1.default {
constructor(context) {
const { assertPresent, env, isLinuxOs, buildDatabaseUrl, isDatabaseLocal, lodash, strings, toValidPackageName, logger, } = context;
assertPresent({
env,
isLinuxOs,
buildDatabaseUrl,
isDatabaseLocal,
lodash,
strings,
toValidPackageName,
logger,
});
super(context);
this.DEFAULT_PORT = 3310;
this.templateFolder = 'agent-nodejs';
this.env = env;
this.isLinuxOs = isLinuxOs;
this.buildDatabaseUrl = buildDatabaseUrl;
this.isDatabaseLocal = isDatabaseLocal;
this.lodash = lodash;
this.strings = strings;
this.toValidPackageName = toValidPackageName;
}
writePackageJson(language, dbDialect, appName, isDemo = false) {
const dependencies = {
dotenv: '^16.0.1',
'@forestadmin/agent': '^1.0.0',
};
if (isDemo) {
dependencies['@forestadmin/datasource-demo-fintech'] = '^1.0.1';
}
else if (dbDialect === 'mongodb') {
dependencies['@forestadmin/datasource-mongo'] = '^1.0.0';
}
else {
dependencies['@forestadmin/datasource-sql'] = '^1.0.0';
}
// The embedded workflow executor runs Forest Admin workflows in-process. It uses an in-memory
// run store, so it ships with every project regardless of the datasource and needs no config.
dependencies['@forestadmin/workflow-executor'] = '^1.0.0';
if (!isDemo && dbDialect) {
if (dbDialect.includes('postgres')) {
dependencies.pg = '^8.8.0';
}
else if (dbDialect === 'mysql') {
dependencies.mysql2 = '^3.0.1';
}
else if (dbDialect === 'mariadb') {
dependencies.mariadb = '^3.0.2';
}
else if (dbDialect === 'mssql') {
dependencies.tedious = '^18.6.1';
}
}
let scripts = {
start: 'node ./index.js',
'start:watch': 'nodemon ./index.js',
};
const devDependencies = {
nodemon: '^3.1.10',
};
const nodemonConfig = {
ignore: ['./forestadmin-schema.json'],
};
if (language === languages_1.default.Typescript) {
scripts = {
build: 'tsc',
start: 'node ./dist/index.js',
'start:watch': 'nodemon ./index.ts',
};
devDependencies.typescript = '^4.9.4';
devDependencies['ts-node'] = '^10.9.1';
nodemonConfig.ignore.push('./typings.ts');
}
const pkg = {
name: this.toValidPackageName(appName),
version: '0.0.1',
private: true,
scripts,
nodemonConfig,
dependencies,
devDependencies,
};
this.writeFile('package.json', `${JSON.stringify(pkg, null, 2)}\n`);
}
writeTsConfigJson() {
this.writeFile('tsconfig.json', `${JSON.stringify({
compilerOptions: {
experimentalDecorators: true,
target: 'ES2020',
module: 'CommonJS',
moduleResolution: 'Node',
esModuleInterop: true,
declaration: true,
declarationMap: true,
inlineSourceMap: true,
noImplicitOverride: true,
stripInternal: true,
outDir: 'dist',
skipLibCheck: true,
},
'ts-node': {
transpileOnly: true,
},
}, null, 2)}\n`);
}
writeIndex(language, dbDialect, dbSchema, isDemo = false) {
const isMongo = dbDialect === 'mongodb';
const context = {
isDemo,
isMongo,
isMySQL: dbDialect === 'mysql',
isMSSQL: dbDialect === 'mssql',
isMariaDB: dbDialect === 'mariadb',
dbSchema,
forestServerUrl: this.env.FOREST_URL_IS_DEFAULT ? false : this.env.FOREST_SERVER_URL,
};
this.copyHandleBarsTemplate(`${language.name}/index.hbs`, `index.${language.fileExtension}`, context);
}
writeDotEnv(dbConfig, appPort, forestEnvSecret, forestAuthSecret, isDemo = false) {
const context = {
isDemo,
isMongo: dbConfig.dbDialect === 'mongodb',
dbUrl: '',
dbSchema: dbConfig.dbSchema,
dbSslMode: dbConfig.dbSslMode ?? 'disabled',
appPort,
forestServerUrl: this.env.FOREST_URL_IS_DEFAULT ? false : this.env.FOREST_SERVER_URL,
forestEnvSecret,
forestAuthSecret,
hasDockerDbUrl: false,
dockerDbUrl: '',
};
// Demo runs on an in-memory dummy datasource: no DATABASE_URL.
if (!isDemo) {
const dbUrl = this.buildDatabaseUrl(dbConfig);
context.dbUrl = dbUrl;
if (!this.isLinuxOs) {
context.hasDockerDbUrl = true;
context.dockerDbUrl = dbUrl.replace('localhost', 'host.docker.internal');
}
}
this.copyHandleBarsTemplate('common/env.hbs', '.env', context);
}
writeGitignore(language) {
this.writeFile('.gitignore', `node_modules\n.env\n${language === languages_1.default.Typescript ? 'dist\n' : ''}`);
}
writeTypings() {
this.writeFile('typings.ts', '/* eslint-disable */\nexport type Schema = any;\n');
}
writeDockerignore(language) {
this.writeFile('.dockerignore', `node_modules\nnpm-debug.log\n.env\n${language === languages_1.default.Typescript ? 'dist\n' : ''}`);
}
writeDockerfile(language) {
this.copyHandleBarsTemplate(`${language.name}/Dockerfile.hbs`, 'Dockerfile');
}
writeDockerCompose(config) {
const forestServerUrl = this.env.FOREST_URL_IS_DEFAULT ? false : `\${FOREST_SERVER_URL}`;
let forestExtraHost = '';
if (forestServerUrl) {
try {
forestExtraHost = new URL(this.env.FOREST_SERVER_URL).hostname;
}
catch (error) {
throw new Error(`Invalid value for FOREST_SERVER_URL: "${this.env.FOREST_SERVER_URL}"`);
}
}
this.copyHandleBarsTemplate(`${config.language.name}/docker-compose.hbs`, 'docker-compose.yml', {
containerName: this.lodash.snakeCase(config.appConfig.appName),
forestExtraHost,
isLinuxOs: this.isLinuxOs,
network: this.isLinuxOs && this.isDatabaseLocal(config.dbConfig) ? 'host' : null,
});
}
// Demo projects ship a pre-configured forest-layout.json so the panel is curated
// out of the box. This only writes the file; applying it (via the `forest layout`
// commands) is a separate onboarding step.
writeLayoutFile() {
this.copyHandleBarsTemplate('common/forest-layout.json', 'forest-layout.json');
this.copyWorkflowBpmnSidecars();
}
// The curated workflows ship their compiled BPMN as portable sidecar files
// (workflows/<id>.bpmn). `forest layout apply --with-workflows` re-uploads each
// one to the target environment's own S3 bucket and links the fresh version, so
// the layout file carries no (non-portable) bpmnAwsS3Identifier of its own.
copyWorkflowBpmnSidecars() {
this.copyTemplateDirectory('common/workflows', 'workflows');
}
async createFiles(dumpConfig) {
const { isDemo } = dumpConfig;
this.writePackageJson(dumpConfig.language, dumpConfig.dbConfig.dbDialect, dumpConfig.appConfig.appName, isDemo);
if (dumpConfig.language === languages_1.default.Typescript) {
this.writeTsConfigJson();
}
this.writeIndex(dumpConfig.language, dumpConfig.dbConfig.dbDialect, dumpConfig.dbConfig.dbSchema, isDemo);
this.writeDotEnv(dumpConfig.dbConfig, dumpConfig.appConfig.appPort || this.DEFAULT_PORT, dumpConfig.forestEnvSecret, dumpConfig.forestAuthSecret, isDemo);
this.writeTypings();
this.writeGitignore(dumpConfig.language);
this.writeDockerignore(dumpConfig.language);
this.writeDockerfile(dumpConfig.language);
// The demo project has no database, so no docker-compose DB service is needed.
if (!isDemo)
this.writeDockerCompose(dumpConfig);
// Demo projects also get a pre-configured layout file.
if (isDemo)
this.writeLayoutFile();
}
}
exports.default = AgentNodeJs;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdlbnQtbm9kZWpzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZpY2VzL2R1bXBlcnMvYWdlbnQtbm9kZWpzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0Esc0VBQThDO0FBQzlDLHdFQUErQztBQVcvQyxNQUFxQixXQUFZLFNBQVEseUJBQWM7SUFtQnJELFlBQVksT0FBTztRQUNqQixNQUFNLEVBQ0osYUFBYSxFQUNiLEdBQUcsRUFDSCxTQUFTLEVBQ1QsZ0JBQWdCLEVBQ2hCLGVBQWUsRUFDZixNQUFNLEVBQ04sT0FBTyxFQUNQLGtCQUFrQixFQUNsQixNQUFNLEdBQ1AsR0FBRyxPQUFPLENBQUM7UUFFWixhQUFhLENBQUM7WUFDWixHQUFHO1lBQ0gsU0FBUztZQUNULGdCQUFnQjtZQUNoQixlQUFlO1lBQ2YsTUFBTTtZQUNOLE9BQU87WUFDUCxrQkFBa0I7WUFDbEIsTUFBTTtTQUNQLENBQUMsQ0FBQztRQUVILEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQXhDQSxpQkFBWSxHQUFHLElBQUksQ0FBQztRQWNsQixtQkFBYyxHQUFHLGNBQWMsQ0FBQztRQTRCakQsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztRQUMzQixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDekMsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7UUFDdkMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLGtCQUFrQixDQUFDO0lBQy9DLENBQUM7SUFFRCxnQkFBZ0IsQ0FBQyxRQUFrQixFQUFFLFNBQWlCLEVBQUUsT0FBZSxFQUFFLE1BQU0sR0FBRyxLQUFLO1FBQ3JGLE1BQU0sWUFBWSxHQUErQjtZQUMv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