UNPKG

@amaui/style

Version:
257 lines (214 loc) 11.8 kB
import path from 'path'; import fg from 'fast-glob'; import fs from 'fs-extra'; import is from '@amaui/utils/is'; import getEnvironment from '@amaui/utils/getEnvironment'; import hash from '@amaui/utils/hash'; import merge from '@amaui/utils/merge'; import stringify from '@amaui/utils/stringify'; import to from '@amaui/utils/to'; import Try from '@amaui/utils/try'; import unique from '@amaui/utils/unique'; import AmauiNode from '@amaui/node'; import AmauiStyle from './AmauiStyle'; import AmauiTheme from './AmauiTheme'; import AmauiStyleSheetManager from './AmauiStyleSheetManager'; import { resetDefault, normalize } from './reset'; const optionsDefault = { mode: 'regular', amaui_style: { get: AmauiStyle.first.bind(AmauiStyle) }, amaui_theme: { get: AmauiTheme.first.bind(AmauiTheme) }, css: { file: { hash: true }, minify: true }, html: { insert: { comment: '<!-- amaui style insert -->' }, add: true }, rule: { rtl: false, prefix: true, sort: true }, log: true }; const env = getEnvironment(); const getValuePaths = async function (value) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { onlyDirectories: true }; const wd = process.cwd(); let paths = (is('array', value) ? value : [value]).map(item => (item === null || item === void 0 ? void 0 : item.url) || item).filter(Boolean).filter(item => is('string', item)).map(item => path.isAbsolute(item) ? item : path.join(wd, item)); paths = (await fg(paths, options)).map(filePath => path.resolve(filePath)); return unique(paths); }; const minify = value => { if (is('string', value)) return value.replace(/\n/g, '').replace(/ ?(\{|:|,|>|~) ?/g, '$1').replace(/;(\})/g, '$1'); return value; }; function css(value__) { let options_ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const options = merge(options_, optionsDefault, { copy: true }); // Amaui style let amauiStyle = options.amaui_style.value || is('function', options.amaui_style.get) && options.amaui_style.get(); if (amauiStyle === undefined) amauiStyle = new AmauiStyle(); // Amaui theme const amauiTheme = options.amaui_theme.value || is('function', options.amaui_theme.get) && options.amaui_theme.get(); // Make value if it's a function let value_ = is('function', value__) ? Try(() => value__(amauiTheme)) : value__; // For reset, add reset to value let method = 'css'; let priority = 'upper'; if (options.reset) { var _options$resetProps; method = 'reset'; // Default const valueDefault = merge(resetDefault, normalize); // Add reset defaults // user provided values override reset default values if ((_options$resetProps = options.resetProps) !== null && _options$resetProps !== void 0 && _options$resetProps.override) value_ = { ...valueDefault, ...value_ };else value_ = merge(value_, valueDefault); } // reset or pure update priority if (options.pure) method = 'pure'; if (options.reset || options.pure) priority = 'lower'; // Make an instance of amauiStyleSheetManager const amauiStyleSheetManager = new AmauiStyleSheetManager(value_, { mode: options.mode, pure: options.reset || options.pure, priority, amauiTheme, amauiStyle, rule: options.rule, style: { attributes: { method } } }); const responseManager = { ids: amauiStyleSheetManager.ids, amaui_style_sheet_manager: amauiStyleSheetManager, sheets: amauiStyleSheetManager.sheets, add: amauiStyleSheetManager.add.bind(amauiStyleSheetManager), props: amauiStyleSheetManager.props, update: amauiStyleSheetManager.update.bind(amauiStyleSheetManager), remove: amauiStyleSheetManager.remove.bind(amauiStyleSheetManager), addRule: amauiStyleSheetManager.sheets.static[0] && amauiStyleSheetManager.sheets.static[0].addRule.bind(amauiStyleSheetManager.sheets.static[0]) }; const response = { make: async () => { var _paths$folders$css; const wd = process.cwd(); // add const responseManagerValue = responseManager.add(); // All css let value = amauiStyleSheetManager.css; const minified = minify(value); if (options.css.minify) value = minified; // CSS // Make file name, only if at least 1 folder is provided // and it exists let made = false; const paths = { folders: { css: await getValuePaths(options.css.folders) }, files: { html: await getValuePaths(options.html.files, { onlyFiles: true }) } }; const madeFiles = { css: [] }; const fileHash = hash(minified, { withPrefix: false }); if ((_paths$folders$css = paths.folders.css) !== null && _paths$folders$css !== void 0 && _paths$folders$css.length) { var _options$css, _options$css$file, _options$css2, _options$css2$file; const folders = paths.folders.css; const name = "".concat(((_options$css = options.css) === null || _options$css === void 0 ? void 0 : (_options$css$file = _options$css.file) === null || _options$css$file === void 0 ? void 0 : _options$css$file.name) || env.amaui_methods.makeName.next().value).concat((_options$css2 = options.css) !== null && _options$css2 !== void 0 && (_options$css2$file = _options$css2.file) !== null && _options$css2$file !== void 0 && _options$css2$file.hash ? ".".concat(fileHash) : '', ".css"); // Clear folder if clear const make = async (path_, index) => { const folder = options.css.folders.find(item => path_ === (path.isAbsolute(item.url) ? item.url : path.join(wd, item.url))); if (folder) { const clear = folder.clear !== undefined ? folder.clear : options.css.clear; // Empty folder if clear if (clear) await fs.emptyDir(path_); // Make the file const nameWithPath = path.join(path_, name); await AmauiNode.file.add(nameWithPath, value); // Add to files if (index === 0 || !madeFiles.css.length) madeFiles.css.push(nameWithPath); if (options.log) console.log("Made \x1B[34m".concat(nameWithPath, "\x1B[0m \x1B[32m").concat(to(value, 'size'), "\x1B[0m")); } }; // Make files in all the folders if (options.log) console.log(); await Promise.all(folders.map((item, index) => make(item, index))); if (options.log) console.log(); made = true; } // HTML // Only if css is made and in folders // and html at least 1 file is provided and it exists // and html add: true if (made && paths.files.html.length && options.html.add) { const files = paths.files.html; const add = async path_ => { const file = options.html.files.find(item => path_ === (path.isAbsolute(item.url) ? item.url : path.join(wd, item.url))); if (file) { var _file$insert, _options$html, _options$html$insert, _options$html2; // Add path relative from the css file to the index path const valueHTML = await AmauiNode.file.get(path_, false); const values = valueHTML.split('\n'); const filesRelative = madeFiles.css.map(item => path.relative(path.dirname(path_), item)); // Add css files to folders // Start from insert or file.insert comment or if none exists // start from the first group of amaui inserted links, or // start from first the bottom of head const insert = ((_file$insert = file.insert) === null || _file$insert === void 0 ? void 0 : _file$insert.comment) || ((_options$html = options.html) === null || _options$html === void 0 ? void 0 : (_options$html$insert = _options$html.insert) === null || _options$html$insert === void 0 ? void 0 : _options$html$insert.comment); const indexInsertComment = values.findIndex(item => item.indexOf(insert) > -1); const indexAmauiInserts = values.findIndex(item => item.indexOf('<link') > -1 && item.indexOf("data-amaui='true'") > -1); const indexBottomHead = values.findIndex(item => item.indexOf('</head>') > -1); const indexBottomBody = values.findIndex(item => item.indexOf('</body>') > -1); const indexStart = indexInsertComment > -1 && indexInsertComment + 1 || indexAmauiInserts > -1 && indexAmauiInserts || indexBottomHead; const inBody = indexStart > indexBottomHead; // Adding order depends on if it's reset, pure or regular style const allLinks = values.slice(indexStart, inBody ? indexBottomBody : indexBottomHead).filter(item => item.indexOf('<link') > -1); const amauiLinks = allLinks.filter(item => item.indexOf("data-amaui='true'") > -1); const amauiLinksPure = amauiLinks.filter(item => item.indexOf("data-pure='true'") > -1); const amauiLinksNoReset = amauiLinks.filter(item => item.indexOf("data-reset='true'") === -1); const amauiLinkReset = values.find(item => item.indexOf('<link') > -1 && item.indexOf("data-reset='true'") > -1); const amauiLinkResetIndex = values.findIndex(item => item === amauiLinkReset); let indexInsert = amauiLinkResetIndex > -1 ? amauiLinkResetIndex + 1 : indexStart; if (!!amauiLinksNoReset.length && !options.reset) { if (!options.pure) { const amauiLinkLastIndex = values.findIndex(item => item === amauiLinksNoReset[amauiLinksNoReset.length - 1]); indexInsert = amauiLinkLastIndex + 1; } else { const amauiLinkFirstIndex = values.findIndex(item => item === amauiLinksNoReset[0]); if (!amauiLinksPure.length) indexInsert = amauiLinkFirstIndex;else { const amauiLinkPureLast = values.findIndex(item => item === amauiLinksPure[amauiLinksPure.length - 1]); indexInsert = amauiLinkPureLast + 1; } } } // Take whitespace padding from other styles or links, or title or body + tab space let padding = ''; const element = values[(inBody ? indexBottomBody : indexBottomHead) - 1]; const main = values[inBody ? indexBottomBody : indexBottomHead]; padding = element.split(/(?! )/)[0]; if (padding[0] !== ' ') padding = main.split(/(?! )/)[0]; if (padding[0] !== ' ') padding = ' '; filesRelative.forEach(file_ => values.splice(indexInsert, 0, "".concat(padding, "<link rel='stylesheet' href='").concat(file_, "' data-amaui='true'").concat(options.reset ? " data-reset='true'" : '').concat(options.pure ? " data-pure='true'" : '', " />"))); // if not pure and reset // get names and add (update) as a script global variable to window // add at the bottom of head if ((_options$html2 = options.html) !== null && _options$html2 !== void 0 && _options$html2.addNames && !options.pure && !options.reset) { delete responseManagerValue.ids; values.splice(indexBottomHead + 1, 0, "".concat(padding, "<script>\n").concat(padding, " if (!window.amauiStyleNames) window.amauiStyleNames = {};\n").concat(padding, "\n").concat(padding, " window.amauiStyleNames['").concat(fileHash, "'] = ").concat(stringify(responseManagerValue, padding.length + 2), ";\n").concat(padding, "</script>")); } // Update the file await AmauiNode.file.update(path_, values.join('\n')); } }; await Promise.all(files.map(item => add(item))); } } }; // Response return response; } export default css;