UNPKG

@miyagi/core

Version:

miyagi is a component development tool for JavaScript template engines.

331 lines (280 loc) 8.58 kB
/** * Module for sanitizing the user configuration and merging it with the default configuration * @module initConfig */ import deepMerge from "deepmerge"; import log from "../logger.js"; import appConfig from "../default-config.js"; import { t, available as langAvailable } from "../i18n/index.js"; import fs from "fs"; import path from "path"; const { defaultUserConfig } = appConfig; /** * @param {string} path - unsanitized directory or file path * @returns {string} the given path sanitized */ function sanitizePath(path) { if (path === null) return 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 {object} root0 * @param {string} root0.src * @param {boolean} [root0.defer] * @param {boolean} [root0.async] * @param {string} [root0.type] * @param {string} [root0.position] * @returns {object} */ function getJsFileObject({ src, defer, async, type, 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) { if (!Array.isArray(strOrArrOrObj)) { log("warn", "config.assets.js is not an array."); return []; } let files = strOrArrOrObj.map((entry) => typeof entry === "string" ? getJsFileObject({ src: entry }) : entry, ); if (files.length > 0 && manifest.file && manifest.content) { files = files.map((file) => { const manifestEntry = getPathFromManifest(file.src, manifest, root); if (manifestEntry) { return { ...file, src: path.join(path.dirname(manifest.file), manifestEntry), }; } 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|null} [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) { if (!Array.isArray(strOrArrOrObj)) { log("warn", "config.assets.css is not an array."); return []; } let files = strOrArrOrObj.filter((entry) => typeof entry === "string"); if (files.length > 0 && manifest.content && manifest.file) { files = files.map((file) => { const manifestEntry = getPathFromManifest(file, manifest, root); if (manifestEntry) { return path.join(path.dirname(manifest.file), manifestEntry); } 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) { if (!Array.isArray(strOrArrOrObj)) { log("warn", "config.assets.folder is not an array."); return []; } return strOrArrOrObj .filter((entry) => typeof entry === "string") .map(sanitizePath); } /** * @param {object} [userConfig] the unmerged user configuration * @returns {object} the user configuration merged with the default configuration */ export default (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) { let manifest = {}; if (config.assets.manifest) { try { const manifestContent = fs.readFileSync( path.resolve( path.join(config.assets.root || "", config.assets.manifest), ), { encoding: "utf8" }, ); manifest.file = config.assets.manifest; manifest.content = JSON.parse(manifestContent); // eslint-disable-next-line no-unused-vars } catch (e) { log( "warn", t("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 (Array.isArray(config.assets.customProperties.files)) { config.assets.customProperties.files = config.assets.customProperties.files.filter( (entry) => typeof entry === "string", ); if (manifest?.content) { config.assets.customProperties.files = config.assets.customProperties.files.map((file) => { const manifestEntry = getPathFromManifest( file, manifest, config.assets.root, ); if (manifestEntry) { return path.join(path.dirname(manifest.file), manifestEntry); } else { return file; } }); } } else { log("warn", "config.assets.customProperties.files is not an array."); 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 (typeof config.ui.theme.logo === "string") { const { logo } = config.ui.theme; config.ui.theme.logo = { light: logo, dark: logo, }; } else { if (config.ui.theme.logo.light && !config.ui.theme.logo.dark) { config.ui.theme.logo.dark = config.ui.theme.logo.light; } else if (config.ui.theme.logo.dark && !config.ui.theme.logo.light) { config.ui.theme.logo.light = config.ui.theme.logo.dark; } } if (config.ui.theme.logo.light) { config.ui.theme.logo.light = sanitizePath(config.ui.theme.logo.light); } if (config.ui.theme.logo.dark) { config.ui.theme.logo.dark = sanitizePath(config.ui.theme.logo.dark); } } const merged = deepMerge(defaultUserConfig, config); merged.components.folder = sanitizePath(merged.components.folder); // do this later as otherwise the deepMerge would do concatenation which we do not want if (config.files) { if (config.files.mocks) { if (config.files.mocks.extension) { merged.files.mocks.extension = arrayfy(config.files.mocks.extension); if (merged.files.mocks.extension.length === 1) { merged.files.mocks.extension.push( defaultUserConfig.files.mocks.extension[1], ); } } } } if (!langAvailable.includes(merged.ui.lang)) { merged.ui.lang = "en"; } return merged; }; /** * @param {string} file * @param {object} manifest * @param {string} root * @returns {string|null} */ function getPathFromManifest(file, manifest, root = "") { const entry = Object.entries(manifest.content).find(([key]) => { return ( path.resolve(root, path.dirname(manifest.file), sanitizePath(key)) === path.resolve(root, sanitizePath(file)) ); }); return entry ? entry[1] : null; }