@miyagi/core
Version:
miyagi is a component development tool for JavaScript template engines.
200 lines (186 loc) • 5.54 kB
JavaScript
import { writeFile, mkdir } from "node:fs/promises";
import path from "path";
import jsonToYaml from "js-yaml";
import * as helpers from "../helpers.js";
import { t } from "../i18n/index.js";
/**
* Module for creating component files based on the configuration cli params
* @param {object} root
* @param {object} root.component
* @param {Array} root.fileTypes
* @returns {Promise<string>}
*/
export default async function componentGenerator({ component, fileTypes }) {
try {
await createComponentFolder(component);
await createComponentFiles(
global.config.files,
component,
fileTypes,
global.config.components.folder,
);
return Promise.resolve(
t("generator.component.done").replace("{{component}}", component),
);
} catch (err) {
return Promise.reject(err);
}
}
/**
* Returns an array with file names
* @param {object} fileNames - an object with file names for the component
* @param {Array} fileTypes
* @returns {Array} all file paths that should be created
*/
function getFiles(fileNames, fileTypes) {
return fileTypes.map((fileType) => fileNames[fileType]);
}
/**
* Returns the dummy content for a component file
* @param {string} fileType - the file type that should be created
* @param {object} filesConfig - the files object from the user congiguration object
* @param {string} componentPath
* @returns {string} dummy file content based on the given file type
*/
function getDummyFileContent(fileType, filesConfig, componentPath) {
let str;
switch (fileType) {
case "mocks":
{
const data = {
$variants: [
{
$name: "",
},
],
};
if (["yaml", "yml"].includes(filesConfig.mocks.extension[0])) {
str = jsonToYaml.dump(data);
} else {
str = `${JSON.stringify(data, null, 2)}\n`;
}
}
break;
case "schema":
{
const id = path.join("/", componentPath);
if (["yaml", "yml"].includes(filesConfig.schema.extension)) {
str = `$schema: "http://json-schema.org/draft-07/schema"
$id: "${id}"
additionalProperties: false
properties:
required:
`;
} else {
str = `${JSON.stringify(
{
$schema: "http://json-schema.org/draft-07/schema",
$id: id,
additionalProperties: false,
properties: {},
required: [],
},
null,
2,
)}\n`;
}
}
break;
default:
str = "";
}
return str;
}
/**
* Creates the component files
* @param {object} filesConfig - the files configuration from the user configuration object
* @param {string} componentPath - the path of the component folder
* @param {Array} fileTypes
* @param {string} componentFolder
* @returns {Promise} gets resolved when all files have been created
*/
async function createComponentFiles(
filesConfig,
componentPath,
fileTypes,
componentFolder,
) {
const componentName = path.basename(componentPath);
const fileNames = getFileNames(filesConfig, componentName);
const files = getFiles(fileNames, fileTypes);
const entries = Object.entries(fileNames);
for (const [type, file] of entries) {
if (files.includes(file)) {
const fullFilePath = path.join(
process.env.INIT_CWD || process.cwd(),
componentPath,
file,
);
try {
await writeFile(
fullFilePath,
getDummyFileContent(
type,
filesConfig,
path.relative(componentFolder, componentPath),
),
{ flag: "wx" },
);
} catch (err) {
return Promise.reject(err.message);
}
}
}
}
/**
* Returns an object with the file names for a given component name
* @param {object} filesConfig - the files configuration from the user configuration object
* @param {string} componentName - the name of the component
* @returns {object} all file names based on the user configuration
*/
function getFileNames(filesConfig, componentName) {
return {
tpl: `${helpers.getResolvedFileName(
filesConfig.templates.name,
componentName,
)}.${filesConfig.templates.extension}`,
mocks: `${filesConfig.mocks.name}.${filesConfig.mocks.extension[0]}`,
docs: "README.md",
css: `${helpers.getResolvedFileName(filesConfig.css.name, componentName)}.${
filesConfig.css.extension
}`,
js: `${helpers.getResolvedFileName(filesConfig.js.name, componentName)}.${
filesConfig.js.extension
}`,
schema: `${filesConfig.schema.name}.${filesConfig.schema.extension}`,
};
}
/**
* Creates the component folder
* @param {string} folder - component folder path that should be created
* @returns {Promise} gets resolved when the folder has been created
*/
async function createComponentFolder(folder) {
try {
await mkdir(
/*
* When using `yarn/npm miyagi new …`, `process.env.INIT_CWD` equals
* the current working directory, so also subdirectories of where
* the package.json is located. In this case `process.cwd()` always
* equals the root directory though.
* When using node directly, `process.env.INIT_CWD` is not available,
* but `process.cwd()` is always the current working directory, so
* also subdirectories.
* So, if INIT_CWD is available, we know it is the directory the user
* cd'ed into, if it not available, then we use process.cwd(), which
* in that case is also the directory the user cd'ed into.
* It is important that we let the user create a component from their
* current working directory, so they can benefit from autocompletion.
*/
path.join(process.env.INIT_CWD || process.cwd(), folder),
{ recursive: true },
);
} catch (err) {
return err;
}
}