UNPKG

@communities-webruntime/services

Version:

If you would like to run Lightning Web Runtime without the CLI, we expose some of our programmatic APIs available in Node.js. If you're looking for the CLI documentation [you can find that here](https://www.npmjs.com/package/@communities-webruntime/cli).

226 lines 7.66 kB
/** * Copyright (c) 2019, salesforce.com, inc. * All rights reserved. * SPDX-License-Identifier: MIT * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT */ import crypto from 'crypto'; import { existsSync } from 'fs'; import { dirname } from 'path'; import { unparse as unparseUUID } from 'uuid-parse'; import { ContextService } from '../context/context-service.js'; import { readFolder, readJsonFile } from '../utils/files.js'; import { findInTree } from '../utils/tree.js'; import { assert } from '../utils/assert.js'; import { validate as validateMetadata } from './metadata-validation.js'; import { getBrandingTokenMap as getBrandingTokenMapFromProperties } from './process-branding.js'; const { getContext } = ContextService; /** * Get all the context template routes */ function getRoutes() { const { routes } = getContext(); if (typeof routes === 'object') { return routes; } return readJsonFile(routes); } /** * Get the context template theme */ function getTheme() { const { theme } = getContext(); if (typeof theme === 'object') { return theme; } return readJsonFile(theme); } /** * Get all the context template labels */ function getLabels() { const { labels } = getContext(); if (typeof labels === 'object') { return labels; } return (labels && readJsonFile(labels)) || {}; } /** * Get all the context template locales */ function getLocales() { const { locales } = getContext(); if (typeof locales === 'object') { return locales; } return locales && existsSync(locales) ? readJsonFile(locales) : []; } /** * Get all the context template branding properties */ function getBrandingProperties() { const { theme: themeConfig } = getContext(); let brandingProperties; const theme = getTheme(); if (typeof themeConfig === 'object') { brandingProperties = (theme && theme.branding) || []; } else { const dir = dirname(themeConfig); brandingProperties = (theme && theme.branding && readJsonFile(`${dir}/${theme.branding}`)) || []; } return brandingProperties; } function getBrandingTokenMap() { return getBrandingTokenMapFromProperties(getBrandingProperties()); } /** * Compute components and regions uuids for design mode. * * Ids are a hash of the concatenation of the view name * and the cmp/region (deep) string representation. * * The resulting 128 bits hash is unparsed as a UUID. * * The view is mutated in place. * * @param {object} view the view to add uuids to * @returns {object} the mutated view */ function updateViewWithUuids(view) { // compute a uuid for a given region or component function uuid(node) { return unparseUUID(crypto .createHash('md5') .update(`${view.devName}:${JSON.stringify(node, (key, value) => { // filter out uuid and region properties cause they would impact the hash return ['uuid', 'region'].includes(key) ? undefined : value; })}`) .digest()); } // update a region recursively function updateRegionWithUuids(region) { // First, generate region key region.uuid = region.key ? region.key : uuid(region); if (region.components) { region.components.forEach((cmp) => { cmp.region = region.slotName; if (cmp.regions) { // recursively generate keys for each inner component region cmp.regions.forEach(updateRegionWithUuids); } // generate a key for the component itself cmp.uuid = uuid(cmp); }); } } // if the view has a corresponding component and this component has inner regions, generate // keys for each region if (view.component && view.component.regions) { view.component.regions.forEach(updateRegionWithUuids); } return view; } function getView(devName) { const { views, isDesignMode } = getContext(); const view = typeof views === 'object' ? views[devName] : readJsonFile(`${views}/${devName}.json`); assert(view, `Cannot find view '${devName}'`); // Compute components and regions ids for design mode. // Ids are a hash of the concatenation of the view name // and the cmp/region (deep) string representation if (isDesignMode) { return updateViewWithUuids(view); } return view; } function getAllViews(routes = getRoutes(), theme = getTheme()) { // using `[].concat(routes)` allows the caller to pass either a single route (the current route for example) // or an array of routes const routesArr = [].concat(routes || []); // get all route views const routeViewDevNames = routesArr.filter((route) => route.view).map((route) => route.view); const routeViews = routeViewDevNames.map(getView); // get all the theme layout views const themeLayoutViewDevNames = routeViews .filter((view) => view.themeLayoutType) .map((view) => view.themeLayoutType) .filter((themeLayoutType) => { return theme.themeLayouts[themeLayoutType] && theme.themeLayouts[themeLayoutType].view; }) .map((themeLayoutType) => theme.themeLayouts[themeLayoutType].view); // filter out unique views const allUniqueViewIds = [...new Set(routeViewDevNames.concat(themeLayoutViewDevNames))]; return allUniqueViewIds.map(getView); } /** * Get all the HTML partials from the template's partials folder. * Result is in format of `{[fileNameWithoutExtension] : stringContentOfFile }` */ function getPartials() { const extension = '.html'; const { partials } = getContext(); if (typeof partials === 'object') { return partials; } const fileMap = readFolder(partials, extension); return Object.keys(fileMap).reduce((result, key) => { result[key.slice(0, -extension.length)] = fileMap[key]; return result; }, {}); } /** * Get all views which are routed */ function getViewToThemeLayoutMap() { const viewToThemeLayoutMap = {}; const theme = getTheme(); getRoutes().forEach((route) => { const view = getView(route.view); const themeLayout = theme.themeLayouts[view.themeLayoutType]; viewToThemeLayoutMap[route.view] = themeLayout.view; }); return viewToThemeLayoutMap; } function validate(schema = 'communities') { return validateMetadata({ routes: getRoutes(), labels: getLabels(), locales: getLocales(), brandingProperties: getBrandingProperties(), theme: getTheme(), views: getAllViews(), partials: getPartials(), }, schema); } /** * Returns the component with the given UUID among all the views. * * This is only available in design mode. * * @param uuid the UUID of the component to find * @returns the component, or `undefined` if the component cannot be found * @throws an error is not in design mode */ function getComponent(uuid) { const { isDesignMode } = getContext(); assert(isDesignMode, 'Can only get components by UUIDs in design mode'); return getAllViews().reduce((c, view) => { return c || findInTree(view, (cmp) => cmp && cmp.uuid === uuid); }, undefined); } export const MetadataService = { getRoutes, getTheme, getBrandingProperties, getBrandingTokenMap, getComponent, getLabels, getLocales, getView, getViewToThemeLayoutMap, getAllViews, getPartials, validate, }; //# sourceMappingURL=metadata-service.js.map