#!/usr/bin/env node import {Command} from 'commander'; import path from 'path'; import fetch from "node-fetch"; import fs from "fs"; import http from "http"; import https from "https"; import StreamZip from "node-stream-zip"; import {fileURLToPath} from 'url'; import memFs from 'mem-fs'; import editor from 'mem-fs-editor'; export const defaultGitRepo = 'git+https://git.devdevdev.life/axe-web-public/create-block.git#master'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const isDev = process.env.NODE_ENV === 'development'; // Check README file in case you get "missing files" error. const blocksRegistry = isDev ? 'https://axe-web-blocks-registry.captain.devdevdev.life' : 'https://axe-web-blocks-registry.captain.devdevdev.life'; const blocksDirectory = isDev ? 'blocks/' : ''; if (isDev) { console.log(`Development Mode Active`); } try { const blockName = await init(); console.log(`🎉 Done! \n\nCheck the "${blocksDirectory}${blockName}" directory. \n`); } catch (e) { console.log(`❌ Fail.`); } async function init() { const program = new Command(); program .name('create-block') .description('AXE-WEB Platform Blocks'); const promise = new Promise((resolve, reject) => { program.command('pull') .argument('', 'Provide a full name of required block, for example: @axe-web/hero-block') .action(async (blockName, options) => { console.log('📦 Block to download:', blockName) const status = await getBlockSourceFiles(blockName); if (status) { resolve(blockName); } else { reject(); } }); }); program.parse(); return promise; } async function getBlockSourceFiles(blockName) { const queryString = new URLSearchParams(); queryString.append('blockName', blockName); queryString.append('includeDevConfig', 'true'); const response = await fetch(`${blocksRegistry}?${queryString.toString()}`); const responseData = await response.json(); if (!responseData || !responseData.name) { console.log("⚠️ Block not found, please contact administrator."); return; } if (responseData.status && responseData.status !== 200) { console.log("⚠️ [ERROR]", responseData.message || "Server side error."); throw new Error("⚠️ [ERROR]", responseData.message || "Server side error."); } console.log(`⬇️ Downloading v${responseData.version}`); const zipFile = await downloadFile(responseData.downloadUrl, responseData.name + '.zip'); // Download, Extract and Remove downloaded file. try { const zip = new StreamZip.async({file: zipFile}); await zip.extract(null, `${blocksDirectory}${responseData.name}/src`); await zip.close(); await fs.promises.access(zipFile, fs.constants.W_OK); await fs.promises.unlink(zipFile); } catch (e) { throw e; } await createTechnicalFiles({ title: responseData.title, name: responseData.name, blockFilename: responseData.name, blockGroupName: responseData.project, version: responseData.version, devToolSource: defaultGitRepo, }, __dirname, `${blocksDirectory}${responseData.name}`); if (responseData.config.design_files) { await downloadDesignFiles(responseData.name, responseData.config.design_files); } await createDataFiles(responseData.name, responseData.config.data_sources); await createConfigFile(responseData.name, responseData.config); return true; } async function createConfigFile(blockName, config = {}) { const obj = { blockName, baseView: config.baseView, remToPx: config.remToPx, }; const cssAssets = config.assets.filter(item => item.type === 'css').map(item => item.url); if (cssAssets.length) { obj.cssUrl = cssAssets; } const jsAssets = config.assets.filter(item => item.type === 'js').map(item => item.url); if (jsAssets.length) { obj.jsUrl = jsAssets; } await setupPath(`${blocksDirectory}${blockName}/config`); await fs.promises.writeFile(`${blocksDirectory}${blockName}/config/default.json`, JSON.stringify(obj, null, 2)); } async function downloadDesignFiles(blockName = '', files) { for (let url of files) { const fileName = url.split('/').pop(); await setupPath(`${blocksDirectory}${blockName}/design`); await downloadFile(url, `${blocksDirectory}${blockName}/design/${fileName}`) } } async function createDataFiles(blockName, dataSources = []) { for (let source of dataSources) { await setupPath(`${blocksDirectory}${blockName}/data`); await fs.promises.writeFile(`${blocksDirectory}${blockName}/data/${source.name}.json`, JSON.stringify(source.data, null, 2)); if (typeof source.preview_images !== 'undefined' && source.preview_images.length) { for (let preview_image of source.preview_images) { await setupPath(`${blocksDirectory}${blockName}/design/preview`); await downloadFile(preview_image.url, `${blocksDirectory}${blockName}/design/preview/${source.name}.${preview_image.width}.${preview_image.extension}`) } } } } // // Helpers // async function setupPath(path) { if (!fs.existsSync(path)) { await fs.promises.mkdir(path); } } async function downloadFile(url, fileName) { const file = fs.createWriteStream(fileName); return new Promise((resolve, reject) => { const protocol = url.startsWith('https://') ? https : http; protocol.get(url, function (response) { response.pipe(file); // Loading Indicator. const loadingInterval = setInterval(() => console.log('🕐 Download in progress...'), 3000); // after download completed close filestream file.on("finish", () => { clearInterval(loadingInterval); file.close(); resolve(fileName); }); }); }) } export async function createTechnicalFiles(data, baseDir, distPath) { const pathDist = distPath; //path.join(baseDir, distPath); const generatorsPath = path.join(baseDir, 'generators/block/templates'); const store = memFs.create(); const filesystem = editor.create(store); const files = ['package.json', 'README.md', '.editorconfig', {from: 'gitignore', to: '.gitignore'}, 'block.json']; for (let file of files) { const from = typeof file !== 'string' ? `${generatorsPath}/${file.from}` : `${generatorsPath}/${file}`; const to = typeof file !== 'string' ? `${pathDist}/${file.to}` : `${pathDist}/${file}`; await filesystem.copyTplAsync(from, to, data); } return filesystem.commit(); // Promise }