@miyagi/core
Version:
miyagi is a component development tool for JavaScript template engines.
374 lines (320 loc) • 10.1 kB
JavaScript
/**
* Module for sanitizing the user configuration and merging it with the default configuration
*
* @module initConfig
*/
const deepMerge = require("deepmerge");
const log = require("../logger.js");
const appConfig = require("../config.json");
const fs = require("fs");
const path = require("path");
const { defaultUserConfig } = appConfig;
/**
* @param {string} path - unsanitized directory or file path
* @returns {string} the given path sanitized
*/
function sanitizePath(path) {
let sanitizedPath = path;
if (sanitizedPath.startsWith("./")) {
sanitizedPath = sanitizedPath.slice(2);
} else if (sanitizedPath.startsWith("/")) {
sanitizedPath = sanitizedPath.slice(1);
}
if (sanitizedPath === "." || sanitizedPath === "/") {
sanitizedPath = "";
}
if (sanitizedPath.endsWith("/")) {
sanitizedPath = sanitizedPath.slice(0, -1);
}
return sanitizedPath;
}
/**
* @param {string|Array} strOrArr - file path or array of file paths
* @returns {Array} the given file path in an array or simply the given array
*/
function arrayfy(strOrArr) {
return Array.isArray(strOrArr) ? strOrArr : [strOrArr];
}
/**
* @param {any} obj - any value provided by the user
* @returns {boolean} is true if the given object is a real object
*/
function objectIsRealObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function getJsFileObject({
src,
defer = false,
async = false,
type = null,
position = "head",
}) {
return {
src,
defer,
async,
type,
position,
};
}
/**
* @param {string|Array|object} strOrArrOrObj - user assets files, either one file as string, an array of files or an object with strings or array for each NODE_ENV
* @param {object} manifest - manifest object
* @param {string} manifest.file - manifest file path
* @param {object} manifest.content - parsed json content of manifest file
* @param {string} root
* @returns {string[]} converts the given object to an array of asset file path strings
*/
function getJsFilesArray(strOrArrOrObj, manifest, root) {
let files = strOrArrOrObj;
if (typeof files === "string") {
files = [getJsFileObject({ src: files })];
} else if (Array.isArray(files)) {
files = files.map((entry) =>
typeof entry === "string" ? getJsFileObject({ src: entry }) : entry
);
} else if (objectIsRealObject(files)) {
const nodeEnv = process.env.NODE_ENV;
if (files[nodeEnv]) {
files = arrayfy(files[nodeEnv]).map((entry) =>
typeof entry === "string" ? getJsFileObject({ src: entry }) : entry
);
} else if (files.src) {
files = [files];
} else {
files = [];
log(
"warn",
appConfig.messages.nodeEnvAndKeysDontMatchCssOrJs
.replace("{{nodeEnv}}", nodeEnv)
.replace(/{{assetType}}/g, "js")
);
}
}
if (files.length > 0 && manifest) {
files = files.map((file) => {
const manifestEntry = Object.entries(manifest.content).find(([key]) => {
return (
path.resolve(root, path.dirname(manifest.file), sanitizePath(key)) ===
path.resolve(root, sanitizePath(file.src))
);
});
if (manifestEntry) {
return {
...file,
src: path.join(path.dirname(manifest.file), manifestEntry[1]),
};
} else {
return file;
}
});
}
return files
.filter((file) => typeof file.src === "string")
.map((file) => ({
...file,
src: sanitizePath(file.src),
}));
}
/**
* @param {string|Array|object} strOrArrOrObj - user assets files, either one file as string, an array of files or an object with strings or array for each NODE_ENV
* @param {object} manifest - manifest object
* @param {string} manifest.file - manifest file path
* @param {object} manifest.content - parsed json content of manifest file
* @param {string} root
* @returns {string[]} converts the given object to an array of asset file path strings
*/
function getCssFilesArray(strOrArrOrObj, manifest, root) {
let files = strOrArrOrObj;
if (typeof files === "string") {
files = [files];
} else if (objectIsRealObject(files)) {
const nodeEnv = process.env.NODE_ENV;
if (files[nodeEnv]) {
files = arrayfy(files[nodeEnv]);
} else {
files = [];
log(
"warn",
appConfig.messages.nodeEnvAndKeysDontMatchCssOrJs
.replace("{{nodeEnv}}", nodeEnv)
.replace(/{{assetType}}/g, "css")
);
}
}
if (files.length > 0 && manifest) {
files = files.map((file) => {
const manifestEntry = Object.entries(manifest.content).find(([key]) => {
return (
path.resolve(root, path.dirname(manifest.file), sanitizePath(key)) ===
path.resolve(root, sanitizePath(file))
);
});
if (manifestEntry) {
return path.relative(
root,
path.join(
path.dirname(path.join(root, manifest.file)),
manifestEntry[1]
)
);
} else {
return file;
}
});
}
return files.map(sanitizePath);
}
/**
* @param {string|Array|object} strOrArrOrObj
* @returns {string[]} the given param converted to an array of asset file path strings
*/
function getAssetFoldersArray(strOrArrOrObj) {
let folders = strOrArrOrObj;
if (typeof folders === "string") {
folders = [folders];
} else if (objectIsRealObject(folders)) {
const nodeEnv = process.env.NODE_ENV;
if (folders[nodeEnv]) {
folders = arrayfy(folders[nodeEnv]);
} else {
folders = [];
log(
"warn",
appConfig.messages.nodeEnvAndKeysDontMatchAssetFolders.replace(
"{{nodeEnv}}",
nodeEnv
)
);
}
}
return folders.map(sanitizePath);
}
/**
* @param {object} [userConfig] the unmerged user configuration
* @returns {object} the user configuration merged with the default configuration
*/
module.exports = (userConfig = {}) => {
const config = { ...userConfig };
if (config.build) {
if (config.build.basePath) {
if (!config.build.basePath.startsWith("/")) {
config.build.basePath = `/${config.build.basePath}`;
}
if (!config.build.basePath.endsWith("/")) {
config.build.basePath = `${config.build.basePath}/`;
}
}
}
if (config.assets) {
const nodeEnv = process.env.NODE_ENV;
let manifest;
if (config.assets.root) {
if (
objectIsRealObject(config.assets.root) &&
config.assets.root[nodeEnv]
) {
config.assets.root = config.assets.root[nodeEnv];
}
} else {
config.assets.root = "";
}
if (config.assets.manifest) {
try {
const manifestContent = fs.readFileSync(
path.resolve(path.join(config.assets.root, config.assets.manifest)),
"utf-8"
);
manifest = {
file: config.assets.manifest,
content: JSON.parse(manifestContent),
};
} catch (e) {
log(
"warn",
appConfig.messages.manifestNotFound.replace(
"{{manifest}}",
config.assets.manifest
)
);
}
}
if (config.assets.folder) {
config.assets.folder = getAssetFoldersArray(config.assets.folder);
}
if (config.assets.css) {
config.assets.css = getCssFilesArray(
config.assets.css,
manifest,
config.assets.root
);
}
if (config.assets.js) {
config.assets.js = getJsFilesArray(
config.assets.js,
manifest,
config.assets.root
);
}
if (!config.assets.customProperties) {
config.assets.customProperties = {};
}
if (config.assets.customProperties.files) {
if (config.assets.customProperties.files[process.env.NODE_ENV]) {
config.assets.customProperties.files = arrayfy(
config.assets.customProperties.files[process.env.NODE_ENV]
);
} else {
config.assets.customProperties.files = arrayfy(
config.assets.customProperties.files
);
}
} else {
config.assets.customProperties.files = [];
}
}
if (config.components) {
if (config.components.ignores) {
config.components.ignores = arrayfy(config.components.ignores).map(
sanitizePath
);
}
}
if (!config.ui) config.ui = {};
if (!config.ui.theme) config.ui.theme = {};
if (!config.ui.theme.light) config.ui.theme.light = {};
if (!config.ui.theme.dark) config.ui.theme.dark = {};
if (config.ui.theme.logo) {
if (!config.ui.theme.light.logo) {
config.ui.theme.light.logo = config.ui.theme.logo;
}
if (!config.ui.theme.dark.logo) {
config.ui.theme.dark.logo = config.ui.theme.logo;
}
delete config.ui.theme.logo;
}
if (!config.ui.theme.light.logo && config.ui.theme.dark.logo) {
config.ui.theme.light.logo = config.ui.theme.dark.logo;
} else if (!config.ui.theme.dark.logo && config.ui.theme.light.logo) {
config.ui.theme.dark.logo = config.ui.theme.light.logo;
}
if (config.ui.theme.content || config.ui.theme.navigation) {
config.ui.theme.light.content = config.ui.theme.content;
config.ui.theme.light.navigation = config.ui.theme.navigation;
log(
"warn",
"Please note that `config.ui.theme.content` and `config.ui.theme.navigation` are deprecated and are going to be removed in future versions. Please use `config.ui.theme.(light|dark).content` and `config.ui.theme.(light|dark).navigation` instead."
);
delete config.ui.theme.content;
delete config.ui.theme.navigation;
}
if (config.ui.theme.light.logo) {
config.ui.theme.light.logo = sanitizePath(config.ui.theme.light.logo);
}
if (config.ui.theme.dark.logo) {
config.ui.theme.dark.logo = sanitizePath(config.ui.theme.dark.logo);
}
const merged = deepMerge(defaultUserConfig, config);
merged.components.folder = sanitizePath(merged.components.folder);
return merged;
};