UNPKG

patternplate-server

Version:

Programmatically serve atomic patterns via a REST API

187 lines (150 loc) 6.22 kB
import {basename, dirname, resolve, relative} from 'path'; import {debuglog, inspect} from 'util'; import chalk from 'chalk'; import exists from 'path-exists'; import {merge, omit, pick} from 'lodash'; import throat from 'throat'; import getEnvironments, {defaultEnvironment} from './get-environments'; import getDependentPatterns from './get-dependent-patterns'; import getStaticCacheItem from './get-static-cache-item.js'; import getMatchingEnvironments from './get-matching-environments'; import readTree from '../filesystem/read-tree'; const envDebug = debuglog('environments'); const debug = debuglog('get-patterns'); const defaults = { isEnvironment: false, filters: {}, log() {} }; async function getPatterns(options, cache, cmds = ['read', 'transform']) { const settings = {...defaults, ...options}; const { id, base, config, factory, transforms, filters, log, isEnvironment } = settings; const path = resolve(base, id); const staticCacheRoot = resolve(process.cwd(), '.cache'); config.log = log; // No patterns to find here if (!await exists(path)) { debug(`Expected path ${path} for pattern ${id} does not exist.`); return []; } const search = await exists(resolve(path, 'pattern.json')) ? resolve(path, 'pattern.json') : path; // Get all pattern ids const paths = await readTree(search, options.cache); const patternIDs = paths .filter(item => basename(item) === 'pattern.json') .filter(item => isEnvironment ? true : !item.includes('@environments')) .map(item => dirname(item)) .map(item => relative(options.base, item)); // read and transform patterns at a concurrency of 5 return await Promise.all(patternIDs.map(throat(5, async patternID => { // try to use the static cache const cached = cache.config.static ? await getStaticCacheItem({ id: patternID, base: staticCacheRoot, filters, cache }) : null; if (cached) { return cached; } // load user environments const userEnvironments = await getEnvironments(base, { cache, log }); const free = typeof filters.environments === 'undefined' || filters.environments.length === 0; // get environments that match this pattern const matchingEnvironments = free ? getMatchingEnvironments(patternID, userEnvironments) : userEnvironments.filter(({name}) => filters.environments.includes(name)); // get the available environment names for this pattern const environmentNames = matchingEnvironments .map(env => env.name); if (environmentNames.length > 0) { log.debug(`Applying environments ${chalk.bold(environmentNames.join(', '))} to ${chalk.bold(patternID)}`); } // merge environment configs // fall back to default environment if none is matching const environmentsConfig = matchingEnvironments .reduce((results, environmentConfig) => { const {environment} = environmentConfig; const misplacedKeys = omit(environment, Object.keys(config)); const misplacedKeyNames = Object.keys(misplacedKeys); if (misplacedKeys.length > 0) { log.warn([ `${chalk.yellow('[⚠ Deprecation ⚠ ]')} Found unexpected keys ${misplacedKeyNames} in environment`, `${environmentConfig.name}.environment. Placing keys other than ${Object.keys(config)} in`, `${environmentConfig.name}.environment is deprecated, move the keys to`, `${environmentConfig.name}.environment.transforms` ].join(' ')); } // directly stuff mismatching keys into transforms config to retain previous behaviour return omit(merge({}, results, omit(environment, misplacedKeyNames), {transforms: misplacedKeys}), Object.keys(misplacedKeys).concat(Object.keys(defaultEnvironment))); }, defaultEnvironment); envDebug('applying env config to pattern %s', patternID); envDebug('%s', inspect(environmentsConfig, {depth: null})); // merge the determined environments config onto the pattern config const patternConfiguration = merge({}, config, environmentsConfig, { environments: environmentNames }); // Initialize the pattern object const initStart = new Date(); const filterString = JSON.stringify(filters); const filterStamp = chalk.grey(`[${filterString}]`); log.debug(`Initializing pattern "${patternID}" with filters: ${filterStamp}`); const pattern = await factory(patternID, base, patternConfiguration, transforms, filters); log.debug(`Initialized pattern "${patternID}" ${chalk.grey(`[${new Date() - initStart}ms]`)}`); // Inject information about available environments const availableEnvironments = userEnvironments .map(env => pick(env, ['name', 'displayName'])); // Select environments that should be displayed const demoEnvironments = userEnvironments .filter(env => env.display) .map(env => pick(env, ['name', 'displayName'])); pattern.manifest.availableEnvironments = availableEnvironments.length ? availableEnvironments : [pick(defaultEnvironment, ['name', 'displayName'])]; pattern.manifest.demoEnvironments = demoEnvironments.length ? demoEnvironments : [pick(defaultEnvironment, ['name', 'displayName'])]; // Determine dependening patterns const gettingDepending = await getDependentPatterns(patternID, base, {cache}); // Exit if we do not have to read if (!cmds.includes('read')) { // Inject depending pattern information pattern.manifest.dependentPatterns = await gettingDepending; return pattern; } // Read the pattern files const readStart = new Date(); log.debug(`Reading pattern "${patternID}"`); await pattern.read(); // Inject depending pattern information pattern.manifest.dependentPatterns = await gettingDepending; log.debug(`Read pattern "${patternID}" ${chalk.grey(`[${new Date() - readStart}ms]`)}`); // Exit if we do not have to transform if (!cmds.includes('transform')) { return pattern; } // Transform pattern sources const transformStart = new Date(); log.debug(`Transforming pattern "${patternID}"`); const transformed = await pattern.transform(!isEnvironment, isEnvironment); log.debug(`Transformed pattern "${patternID}" ${chalk.grey(`[${new Date() - transformStart}ms]`)}`); return transformed; }))); } export default getPatterns;