piral-cli
Version:
The standard CLI for creating and building a Piral instance or a Pilet.
173 lines (149 loc) • 5.14 kB
text/typescript
import { join, dirname, resolve, basename, isAbsolute } from 'path';
import { installNpmPackageFromOptionalRegistry } from './npm';
import { ForceOverwrite } from './enums';
import { createDirectory, createFileIfNotExists, updateExistingJson } from './io';
import { cliVersion, isWindows } from './info';
import { log, fail, getLogLevel } from './log';
import { Framework, SourceLanguage } from '../types';
interface TemplateFile {
path: string;
content: Buffer | string;
}
function getTemplatePackage(templatePackageName: string) {
const idx = templatePackageName.indexOf('@', 1);
const normalizedName =
idx > 0 && !isAbsolute(templatePackageName) ? templatePackageName.substring(0, idx) : templatePackageName;
try {
return require(normalizedName);
} catch (err) {
log('generalVerbose_0004', err);
fail(
'generalError_0002',
`Could not load the template "${templatePackageName}". Package "${normalizedName}" could not be resolved due to ${err?.message || 'an unspecified error'}.`,
);
}
}
async function getTemplateFiles(
templatePackageName: string,
registry: string,
root: string,
data: Record<string, any>,
forceOverwrite: ForceOverwrite,
): Promise<Array<TemplateFile>> {
// debug in monorepo such as "../templates/pilet-template-react/lib/index.js"
if (templatePackageName.startsWith('.')) {
templatePackageName = resolve(process.cwd(), templatePackageName);
} else {
if (templatePackageName.indexOf('@', 1) === -1) {
templatePackageName = `${templatePackageName}@latest`;
}
await installNpmPackageFromOptionalRegistry(templatePackageName, __dirname, registry);
}
const templateRunner = getTemplatePackage(templatePackageName);
const logLevel = getLogLevel();
const details = {
forceOverwrite,
cliVersion,
isWindows,
logLevel,
};
if (typeof templateRunner === 'function') {
return await templateRunner(root, data, details);
} else if ('default' in templateRunner && typeof templateRunner.default === 'function') {
return await templateRunner.default(root, data, details);
} else {
fail(
'generalError_0002',
`The provided template package "${templatePackageName}" does not export a template factory function.`,
);
}
}
function writeFiles(root: string, files: Array<TemplateFile>, forceOverwrite: ForceOverwrite) {
const rootPackage = resolve(root, 'package.json');
return Promise.all(
files
.filter((file) => {
if (typeof file.path !== 'string') {
log('generalWarning_0001', `The supplied file path ("${file.path}") is not a string. Skipping.`);
return false;
} else if (typeof file.content === 'undefined') {
log('generalWarning_0001', `The file "${file.path}" did not specify any content. Skipping.`);
return false;
}
return true;
})
.map((file) => {
const target = resolve(root, file.path);
const name = basename(target);
const dir = dirname(target);
if (target !== rootPackage) {
return createFileIfNotExists(dir, name, file.content, forceOverwrite);
} else {
return updateExistingJson(dir, name, JSON.parse(file.content.toString('utf8')));
}
}),
);
}
function getTemplatePackageName(type: 'piral' | 'pilet', template: string) {
if (template.indexOf('/') === -1 && !template.startsWith('.')) {
return `@smapiot/${type}-template-${template}`;
}
return template;
}
export function getPiralScaffoldData(
language: SourceLanguage,
root: string,
app: string,
packageName: Framework,
variables: Record<string, string>,
) {
const src = dirname(join(root, app));
return {
...variables,
root,
src,
language,
packageName,
reactVersion: parseInt(variables.reactVersion) || 18,
reactRouterVersion: parseInt(variables.reactRouterVersion) || 5,
} as const;
}
export async function scaffoldPiralSourceFiles(
template: string,
registry: string,
data: ReturnType<typeof getPiralScaffoldData>,
forceOverwrite: ForceOverwrite,
) {
const { src, root } = data;
const templatePackageName = getTemplatePackageName('piral', template);
await createDirectory(src);
const files = await getTemplateFiles(templatePackageName, registry, root, data, forceOverwrite);
await writeFiles(root, files, forceOverwrite);
}
export function getPiletScaffoldData(
language: SourceLanguage,
root: string,
sourceName: string,
variables: Record<string, string>,
) {
const src = join(root, 'src');
return {
...variables,
root,
src,
language,
sourceName,
} as const;
}
export async function scaffoldPiletSourceFiles(
template: string,
registry: string,
data: ReturnType<typeof getPiletScaffoldData>,
forceOverwrite: ForceOverwrite,
) {
const { src, root } = data;
const templatePackageName = getTemplatePackageName('pilet', template);
await createDirectory(src);
const files = await getTemplateFiles(templatePackageName, registry, root, data, forceOverwrite);
await writeFiles(root, files, forceOverwrite);
}