@mason-api/cli
Version:
CLI assistant for Mason builder
148 lines (139 loc) • 4.88 kB
JavaScript
import { getAPIKey } from './login';
import colors from 'colors/safe';
import fs from 'fs';
import { fetchDatasource, fetchProject } from '../utils';
import generate, { getDatasourceIds } from '@mason-api/generator';
import _ from 'lodash';
export const command = 'generate <format> <project> [component] [pages..]';
export const desc = 'Generate template out of a component page';
export const builder = (yargs) => {
yargs
.positional('format', {
describe: 'Target file format for compilation',
choices: ['html', 'ejs', 'pug', 'php', 'twig', 'erb', 'haml', 'handlebars', 'mustache'],
})
.positional('project', {
describe: 'ID of a project that contains the component',
})
.positional('component', {
describe: 'ID of a component (processes all components by default)',
default: '*',
})
.positional('pages', {
describe: 'List of pages to generate (process all pages by default)',
default: '*',
})
.option('embed-data', {
alias: 'e',
type: 'array',
describe: 'Unroll loops and fill interpolations',
})
.option('data-prefix', {
alias: 'p',
describe: 'Prefix for all top level variables',
})
.option('output', {
alias: 'o',
describe: 'Folder to output the file to',
default: '.',
})
.option('file', {
alias: 'f',
describe: 'File name of an output file (defaults to page name)',
});
};
export const handler = async (argv) => {
const apiKey = argv.k || await getAPIKey();
if (!apiKey) {
return null;
}
const verbose = !!(argv.verbose || argv.f || argv.o);
Object.assign(argv, _.mapKeys(argv, (value, key) => _.camelCase(key)));
if (verbose) {
console.log(` - Fetching project ${colors.bold(argv.project)}...`);
}
let json;
try {
json = await fetchProject(apiKey, argv.project, verbose, argv.verbose);
} catch (e) {
return console.log(colors.red(`Could not fetch project ${colors.bold(argv.project)} for api key ${colors.bold(apiKey)}`));
}
if (verbose && argv.component !== '*') {
console.log(` - Finding component ${colors.bold(argv.component)}...`);
}
const project = _.find(json.projects, ['id', argv.project]);
const components = argv.component === '*' ? json.components : _.find(json.components, ['id', argv.component]);
if (argv.component && !components.length) {
return console.log(colors.red(`Could not fetch component ${colors.bold(argv.component)} in project ${colors.bold(argv.project)}`));
}
const datasourceIds = [];
_.forEach(components, (comp) => {
_.forEach(_.get(comp, 'config.data'), (page, name) => {
_.forEach(getDatasourceIds(comp.config, name), (datasourceId) => {
if (!_.includes(datasourceIds, datasourceId)) {
datasourceIds.push(datasourceId);
}
});
});
});
if (argv.embedData) {
await Promise.all(_.map(datasourceIds, async (datasourceId) => {
if (_.includes(argv.embedData, datasourceId)) {
const datasource = _.find(project.datasources, ['id', datasourceId]);
if (verbose) {
console.log(` - Fetching datasource ${colors.bold(datasourceId)}...`);
}
datasource.data = await fetchDatasource(datasource, argv.verbose);
}
}));
}
_.forEach(components, (component) => {
const pages = argv.pages[0] === '*' ? Object.keys(component.config.data) : argv.pages;
return pages.map(async (page) => {
if (verbose) {
console.log(` - Generating ${colors.bold(argv.format)} template for ${colors.bold(page)} page of ${colors.bold(component.name)}...`);
}
let template;
try {
template = generate(component.config, page, {
datasources: project.datasources,
embedData: argv.format === 'html' ? true : argv.embedData,
format: argv.format,
});
} catch (e) {
console.log('Failed to generate file');
if (verbose) {
console.log(e);
} else {
console.log('Set -v option to see the details of exceptions');
}
}
if (argv.f || argv.o) {
const dir = _.join(_.compact([
argv.o,
argv.pages[0] === '*' ? _.snakeCase(component.name) : null,
]), '/');
const path = `${dir}/${argv.f || page}.${argv.format}`;
if (verbose) {
console.log(` - Writing file ${colors.bold(path)}...`);
}
mkdirRecursiveSync(dir);
fs.writeFileSync(path, template, { encoding: 'utf8', flag: 'w' });
}
});
});
};
function mkdirRecursiveSync(path) {
const paths = path.split('/');
let fullPath = '';
paths.forEach((path) => {
if (fullPath === '') {
fullPath = path;
} else {
fullPath = `${fullPath}/${path}`;
}
if (!fs.existsSync(fullPath)) {
fs.mkdirSync(fullPath);
}
});
}