import * as AdmZip from 'adm-zip'; import * as chalk from 'chalk'; import * as fs from 'fs'; import * as mustache from 'mustache'; import * as path from 'path'; import {Readable} from 'stream'; import {Template} from './types'; import * as globby from 'globby'; export class TemplateRenderer { constructor(private targetDir: string, private template: Template, private statusUpdater: (status: string) => void) { // globally disable HTML escaping (mustache.escape as any) = (text: string) => text; } public async render(substitutions: {[key: string]: string}) { process.chdir(this.targetDir); this.statusUpdater(chalk.gray(`Downloading ${this.template.name} template...`)); await this.downloadTemplate(); this.statusUpdater(chalk.gray(`Extracting template...`)); this.extract(); this.statusUpdater(chalk.gray(`Rendering template...`)); (await globby(['**/*'], {onlyFiles: true})).forEach((file) => { // exclude template.yml` if (file === 'template.yml') { fs.unlinkSync(file); return; } const f = mustache.render(file, substitutions); if (f !== file) { fs.renameSync(path.resolve(file), path.resolve(f)); file = f; } const transformed = mustache.render(fs.readFileSync(path.resolve(file)).toString(), substitutions); fs.writeFileSync(path.resolve(file), transformed); }); } private async downloadTemplate() { const response = await fetch(`https://s3.amazonaws.com/zaius-public/zdp/app-templates/${this.template.id}.zip`); if (response.ok && response.body) { const nodeStream = Readable.fromWeb(response.body); await this.pipe(nodeStream, fs.createWriteStream(path.resolve(`${this.template.name}.zip`))); } else { const errorText = await response.text(); throw new Error(`Failed to download template: ${response.statusText}\n${errorText}`); } } private extract() { const zipFile = path.resolve(`${this.template.name}.zip`); const zip = new AdmZip(zipFile); zip.extractAllTo(this.targetDir); fs.unlinkSync(zipFile); } private async pipe(input: NodeJS.ReadableStream, output: NodeJS.WritableStream) { return new Promise((resolve) => input.pipe(output).on('finish', resolve)); } }