mirror of
https://github.com/vercel/commerce.git
synced 2025-07-27 04:01:23 +00:00
v1 convert to Agility
This commit is contained in:
51
framework/agility/agility.config.js
Normal file
51
framework/agility/agility.config.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const agilityContentSync = require("@agility/content-sync");
|
||||
const agilityFileSystem = require("@agility/content-sync/src/store-interface-filesystem");
|
||||
|
||||
const agilityConfig = {
|
||||
guid: process.env.AGILITY_GUID, //Set your guid here
|
||||
fetchAPIKey: process.env.AGILITY_API_FETCH_KEY, //Set your fetch apikey here
|
||||
previewAPIKey: process.env.AGILITY_API_PREVIEW_KEY, //set your preview apikey
|
||||
languageCode: "en-us", //the language for your website in Agility CMS
|
||||
channelName: "website", //the name of your channel in Agility CMS
|
||||
securityKey: process.env.AGILITY_SECURITY_KEY, //the website security key used to validate and generate preview keys
|
||||
};
|
||||
|
||||
const getSyncClient = ({ isPreview, isDevelopmentMode }) => {
|
||||
let cachePath = `node_modules/@agility/content-sync/cache/${
|
||||
isPreview ? "preview" : "live"
|
||||
}`;
|
||||
|
||||
if (!isDevelopmentMode) {
|
||||
//we are in prod and need to use a different directory that Vercel understands
|
||||
cachePath = `/tmp/agilitycache/${isPreview ? "preview" : "live"}`;
|
||||
}
|
||||
|
||||
console.log(`Agility CMS => Content cache path is ${cachePath}`);
|
||||
const apiKey = isPreview
|
||||
? agilityConfig.previewAPIKey
|
||||
: agilityConfig.fetchAPIKey;
|
||||
|
||||
if (!agilityConfig.guid) {
|
||||
console.log("Agility CMS => No GUID was provided.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return agilityContentSync.getSyncClient({
|
||||
guid: agilityConfig.guid,
|
||||
apiKey: apiKey,
|
||||
isPreview: isPreview,
|
||||
languages: [agilityConfig.languageCode],
|
||||
channels: [agilityConfig.channelName],
|
||||
store: {
|
||||
interface: agilityFileSystem,
|
||||
options: {
|
||||
rootPath: cachePath,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
agilityConfig,
|
||||
getSyncClient,
|
||||
};
|
299
framework/agility/agility.node.ts
Normal file
299
framework/agility/agility.node.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
import crypto from 'crypto'
|
||||
import { asyncForEach } from "./utils"
|
||||
|
||||
import { ModuleWithInit } from "@agility/types"
|
||||
|
||||
//Agility API stuff
|
||||
import { agilityConfig, getSyncClient } from './agility.config'
|
||||
import GlobalFooter from 'components/agility-global/GlobalFooter'
|
||||
import GlobalHeader from 'components/agility-global/GlobalHeader'
|
||||
|
||||
import moduleInitializers from "framework/module-data"
|
||||
|
||||
|
||||
const securityKey = agilityConfig.securityKey
|
||||
const channelName = agilityConfig.channelName
|
||||
const languageCode = agilityConfig.languageCode
|
||||
const isDevelopmentMode = process.env.NODE_ENV === "development"
|
||||
|
||||
interface AgilityPageProps {
|
||||
sitemapNode?: any,
|
||||
page?: any,
|
||||
dynamicPageItem?: any,
|
||||
pageTemplateName?:string|null,
|
||||
globalHeaderProps?:any,
|
||||
globalFooterProps?:any,
|
||||
languageCode?:string|null,
|
||||
channelName?:string|null,
|
||||
isPreview?:boolean,
|
||||
isDevelopmentMode?:boolean,
|
||||
notFound?:boolean
|
||||
}
|
||||
|
||||
const getAgilityPageProps = async ({ params, preview, locale }:any):Promise<AgilityPageProps> => {
|
||||
|
||||
let path = '/';
|
||||
if (params) {
|
||||
//build path by iterating through slugs
|
||||
path = '';
|
||||
params.slug.map((slug: string) => {
|
||||
path += '/' + slug
|
||||
})
|
||||
}
|
||||
|
||||
//TODO: use locale to determin LANGUAGECODE (pulled from config at this point...)
|
||||
|
||||
//determine if we are in preview mode
|
||||
const isPreview:boolean = (preview || isDevelopmentMode);
|
||||
|
||||
|
||||
|
||||
const agilitySyncClient = getSyncClient({
|
||||
isPreview: isPreview,
|
||||
isDevelopmentMode
|
||||
});
|
||||
|
||||
|
||||
//always sync to get latest
|
||||
|
||||
|
||||
if (! agilitySyncClient) {
|
||||
console.log("Agility CMS => Sync client could not be accessed.")
|
||||
return {
|
||||
notFound: true
|
||||
};
|
||||
}
|
||||
|
||||
if (!isDevelopmentMode) {
|
||||
console.log(`Agility CMS => Syncing ${isPreview ? "Preview" : "Live"} Mode`)
|
||||
await agilitySyncClient.runSync();
|
||||
}
|
||||
|
||||
console.log(`Agility CMS => Getting page props for '${path}'...`);
|
||||
|
||||
//get sitemap
|
||||
const sitemap = await agilitySyncClient.store.getSitemap({ channelName, languageCode });
|
||||
|
||||
if (sitemap === null) {
|
||||
console.warn("No sitemap found after sync.");
|
||||
}
|
||||
|
||||
let pageInSitemap = sitemap[path];
|
||||
let page: any = null;
|
||||
let dynamicPageItem: any = null;
|
||||
|
||||
if (path === '/') {
|
||||
let firstPagePathInSitemap = Object.keys(sitemap)[0];
|
||||
pageInSitemap = sitemap[firstPagePathInSitemap];
|
||||
}
|
||||
|
||||
if (pageInSitemap) {
|
||||
//get the page
|
||||
page = await agilitySyncClient.store.getPage({
|
||||
pageID: pageInSitemap.pageID,
|
||||
languageCode: languageCode
|
||||
});
|
||||
|
||||
} else {
|
||||
//Could not find page
|
||||
console.warn('page [' + path + '] not found in sitemap.');
|
||||
return handlePageNotFound();
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
console.warn('page [' + path + '] not found in getpage method.');
|
||||
}
|
||||
|
||||
|
||||
//if there is a dynamic page content id on this page, grab it...
|
||||
if (pageInSitemap.contentID > 0) {
|
||||
dynamicPageItem = await agilitySyncClient.store.getContentItem({
|
||||
contentID: pageInSitemap.contentID,
|
||||
languageCode: languageCode
|
||||
});
|
||||
}
|
||||
|
||||
//resolve the page template
|
||||
const pageTemplateName = page.templateName.replace(/[^0-9a-zA-Z]/g, '');
|
||||
|
||||
//resolve the modules per content zone
|
||||
await asyncForEach(Object.keys(page.zones), async (zoneName: string) => {
|
||||
|
||||
let modules: { moduleName: string; item: any }[] = [];
|
||||
|
||||
//grab the modules for this content zone
|
||||
const modulesForThisContentZone = page.zones[zoneName];
|
||||
|
||||
//loop through the zone's modules
|
||||
await asyncForEach(modulesForThisContentZone, async (moduleItem: { module: string, item: any }) => {
|
||||
|
||||
//find the react component to use for the module
|
||||
const moduleInitializer = moduleInitializers(moduleItem.module)
|
||||
|
||||
if (moduleInitializer) {
|
||||
//resolve any additional data for the modules
|
||||
|
||||
//we have some additional data in the module we'll need, execute that method now, so it can be included in SSG
|
||||
if (isDevelopmentMode) {
|
||||
console.log(`Agility CMS => Fetching additional data via getCustomInitialProps for ${moduleItem.module}...`);
|
||||
}
|
||||
|
||||
const moduleData = await moduleInitializer({
|
||||
item: moduleItem.item,
|
||||
agility: agilitySyncClient.store,
|
||||
languageCode,
|
||||
channelName,
|
||||
pageInSitemap,
|
||||
dynamicPageItem
|
||||
});
|
||||
|
||||
//if we have additional module data, then add it to the module props using 'customData'
|
||||
if (moduleData != null) {
|
||||
moduleItem.item.customData = moduleData;
|
||||
}
|
||||
}
|
||||
|
||||
modules.push({
|
||||
moduleName: moduleItem.module,
|
||||
item: moduleItem.item,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
//store as dictionary
|
||||
page.zones[zoneName] = modules;
|
||||
|
||||
})
|
||||
|
||||
//resolve data for other shared components
|
||||
const globalHeaderProps = await GlobalHeader.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName });
|
||||
const globalFooterProps = await GlobalFooter.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName });
|
||||
|
||||
return {
|
||||
sitemapNode: pageInSitemap,
|
||||
page,
|
||||
dynamicPageItem,
|
||||
pageTemplateName,
|
||||
globalHeaderProps,
|
||||
globalFooterProps,
|
||||
languageCode,
|
||||
channelName,
|
||||
isPreview,
|
||||
isDevelopmentMode
|
||||
}
|
||||
}
|
||||
|
||||
const getAgilityPaths = async () => {
|
||||
|
||||
console.log(`Agility CMS => Fetching sitemap for getAgilityPaths...`);
|
||||
|
||||
//determine if we are in preview mode
|
||||
const isPreview = isDevelopmentMode;
|
||||
|
||||
const agilitySyncClient = getSyncClient({
|
||||
isPreview,
|
||||
isDevelopmentMode
|
||||
});
|
||||
|
||||
//always sync to get latest
|
||||
|
||||
if (! agilitySyncClient) {
|
||||
console.log("Agility CMS => Sync client could not be accessed.")
|
||||
return [];
|
||||
}
|
||||
if (!isDevelopmentMode) {
|
||||
console.log(`Agility CMS => Syncing ${isPreview ? "Preview" : "Live"} Mode`)
|
||||
await agilitySyncClient.runSync();
|
||||
}
|
||||
|
||||
const sitemapFlat = await agilitySyncClient.store.getSitemap({
|
||||
channelName,
|
||||
languageCode
|
||||
})
|
||||
|
||||
|
||||
|
||||
if (!sitemapFlat) {
|
||||
console.warn("Agility CMS => No Site map found. Make sure your environment variables are setup correctly.")
|
||||
return []
|
||||
}
|
||||
|
||||
|
||||
|
||||
//returns an array of paths as a string (i.e. ['/home', '/posts'] )
|
||||
const paths = Object.keys(sitemapFlat)
|
||||
.filter(path => {
|
||||
const sitemapNode = sitemapFlat[path]
|
||||
return !sitemapNode.redirect
|
||||
&& !sitemapNode.isFolder
|
||||
})
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
|
||||
const validatePreview = async({ agilityPreviewKey, slug }: any) => {
|
||||
//Validate the preview key
|
||||
if (!agilityPreviewKey) {
|
||||
return {
|
||||
error: true,
|
||||
message: `Missing agilitypreviewkey.`
|
||||
}
|
||||
}
|
||||
|
||||
//sanitize incoming key (replace spaces with '+')
|
||||
if (agilityPreviewKey.indexOf(` `) > -1) {
|
||||
agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`);
|
||||
}
|
||||
|
||||
//compare the preview key being used
|
||||
const correctPreviewKey = generatePreviewKey();
|
||||
|
||||
if (agilityPreviewKey !== correctPreviewKey) {
|
||||
return {
|
||||
error: true,
|
||||
message: `Invalid agilitypreviewkey.`
|
||||
//message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...`
|
||||
}
|
||||
}
|
||||
|
||||
//return success
|
||||
return {
|
||||
error: false,
|
||||
message: null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const generatePreviewKey =() => {
|
||||
//the string we want to encode
|
||||
const str = `-1_${securityKey}_Preview`;
|
||||
|
||||
//build our byte array
|
||||
let data = [];
|
||||
for (var i = 0; i < str.length; ++i) {
|
||||
data.push(str.charCodeAt(i));
|
||||
data.push(0);
|
||||
}
|
||||
|
||||
//convert byte array to buffer
|
||||
const strBuffer = Buffer.from(data);
|
||||
|
||||
//encode it!
|
||||
const previewKey = crypto.createHash('sha512').update(strBuffer).digest('base64');
|
||||
return previewKey;
|
||||
}
|
||||
|
||||
function handlePageNotFound() {
|
||||
return {
|
||||
notFound: true
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
getAgilityPageProps,
|
||||
getAgilityPaths,
|
||||
validatePreview,
|
||||
generatePreviewKey
|
||||
}
|
||||
|
51
framework/agility/agility.sync.js
Normal file
51
framework/agility/agility.sync.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
THIS FILE IS ONLY EXECUTED LOCALLY
|
||||
WHEN DOING A LOCAL SYNC ON DEMAND
|
||||
IN DEVELOPMENT MODE
|
||||
*/
|
||||
|
||||
require("dotenv").config({
|
||||
path: `.env.local`,
|
||||
})
|
||||
|
||||
const { getSyncClient } = require('./agility.config')
|
||||
|
||||
|
||||
const runSync = async () => {
|
||||
|
||||
const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true })
|
||||
if (! agilitySyncClient) {
|
||||
console.log("Agility CMS => Sync client could not be accessed.")
|
||||
return;
|
||||
}
|
||||
|
||||
await agilitySyncClient.runSync();
|
||||
}
|
||||
|
||||
const clearSync = async () => {
|
||||
|
||||
const agilitySyncClient = getSyncClient({ isPreview: true, isDevelopmentMode: true })
|
||||
if (! agilitySyncClient) {
|
||||
console.log("Agility CMS => Sync client could not be accessed.")
|
||||
return;
|
||||
}
|
||||
await agilitySyncClient.clearSync();
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (process.argv[2]) {
|
||||
if (process.argv[2] === "clear") {
|
||||
//clear everything
|
||||
return clearSync();
|
||||
} else if (process.argv[2] === "sync") {
|
||||
//run the sync
|
||||
return runSync()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
clearSync,
|
||||
runSync
|
||||
}
|
24
framework/agility/types.ts
Normal file
24
framework/agility/types.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
export interface CustomInitPropsArg {
|
||||
item:any,
|
||||
agility:any,
|
||||
languageCode:any,
|
||||
channelName:any,
|
||||
pageInSitemap:any,
|
||||
dynamicPageItem?:any
|
||||
}
|
||||
|
||||
export interface CustomInitProps<T> {
|
||||
(props:CustomInitPropsArg): T;
|
||||
}
|
||||
|
||||
export interface ModuleWithInit<TProps, TInit> extends FC<TProps> {
|
||||
getCustomInitialProps:CustomInitProps<TInit>
|
||||
}
|
||||
|
||||
export interface URLField {
|
||||
href:string,
|
||||
target:string,
|
||||
text:string
|
||||
}
|
108
framework/agility/utils.js
Normal file
108
framework/agility/utils.js
Normal file
@@ -0,0 +1,108 @@
|
||||
const renderHTML = (html) => {
|
||||
if (!html) return { __html: "" };
|
||||
return { __html: cleanHTML(html) };
|
||||
}
|
||||
|
||||
const cleanHTML = (html) => {
|
||||
if (!html) return ""
|
||||
|
||||
//fix '~' in links in HTML
|
||||
return html.replace(/href="~\//gi, 'href="/')
|
||||
}
|
||||
|
||||
const asyncForEach = async (array, callback) => {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const expandContentList = async ({ agility, contentItems, languageCode, depth }) => {
|
||||
|
||||
asyncForEach(contentItems, async (contentItem) => {
|
||||
await expandContentItem({agility, contentItem, languageCode, depth})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
const expandContentItem = async ({ agility, contentItem, languageCode, depth = 1 }) => {
|
||||
if (!contentItem) return null;
|
||||
|
||||
|
||||
|
||||
const api = agility;
|
||||
|
||||
if (depth > 0) {
|
||||
//make this work for the .fields or the .customFields property...
|
||||
let fields = contentItem.fields;
|
||||
if (!fields) fields = contentItem.customFields;
|
||||
|
||||
for (const fieldName in fields) {
|
||||
const fieldValue = fields[fieldName];
|
||||
|
||||
if (fieldValue.contentid > 0) {
|
||||
//single linked item
|
||||
const childItem = await api.getContentItem({ contentID: fieldValue.contentid, languageCode, depth: depth - 1 });
|
||||
if (childItem != null) fields[fieldName] = childItem;
|
||||
} else if (fieldValue.sortids && fieldValue.sortids.split) {
|
||||
//multi linked item
|
||||
const sortIDAry = fieldValue.sortids.split(',');
|
||||
const childItems = [];
|
||||
for (const childItemID of sortIDAry) {
|
||||
const childItem = await api.getContentItem({ contentID: childItemID, languageCode, depth: depth - 1 });
|
||||
if (childItem != null) childItems.push(childItem);
|
||||
}
|
||||
fields[fieldName] = childItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
return contentItem;
|
||||
}
|
||||
|
||||
const expandLinkedList = async ({ agility, contentItem, languageCode, fieldName, sortIDField }) => {
|
||||
if (!contentItem) return null;
|
||||
|
||||
let fieldObj = contentItem.fields[fieldName]
|
||||
if (! fieldObj) throw Error(`The field ${fieldName} was not found on the content item.`)
|
||||
|
||||
const referenceName = fieldObj.referencename
|
||||
if (! referenceName) throw Error(`A referencename property was not found on the ${fieldName} value.`)
|
||||
|
||||
//grab the content list...
|
||||
let listItems = await agility.getContentList({referenceName, languageCode})
|
||||
if (listItems?.length > 0) {
|
||||
let sortIDs = contentItem.fields[sortIDField]
|
||||
if (sortIDs?.length > 0 && sortIDs?.split) {
|
||||
//if we have sort ids, assemble the values in the list based on those...
|
||||
const sortIDAry = sortIDs.split(',');
|
||||
const sortedItems = [];
|
||||
for (const idStr of sortIDAry) {
|
||||
const childContentID = parseInt(idStr)
|
||||
|
||||
const childItemIndex = listItems.findIndex(item => item.contentID === childContentID)
|
||||
if (childItemIndex >= 0) {
|
||||
sortedItems.push(listItems[childItemIndex]);
|
||||
listItems.splice(childItemIndex, 1)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
listItems = sortedItems.concat(listItems)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem.fields[fieldName] = listItems;
|
||||
return contentItem;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
renderHTML,
|
||||
cleanHTML,
|
||||
asyncForEach,
|
||||
expandContentItem,
|
||||
expandContentList,
|
||||
expandLinkedList
|
||||
}
|
@@ -35,9 +35,11 @@ async function getAllPages({
|
||||
>('/v3/content/pages')
|
||||
const pages = (data as RecursiveRequired<typeof data>) ?? []
|
||||
|
||||
return {
|
||||
const retPages = {
|
||||
pages: preview ? pages : pages.filter((p) => p.is_visible),
|
||||
}
|
||||
|
||||
return retPages
|
||||
}
|
||||
|
||||
export default getAllPages
|
||||
|
55
framework/module-data/BestsellingProductsData.ts
Normal file
55
framework/module-data/BestsellingProductsData.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { getConfig } from '@framework/api'
|
||||
import getAllProducts from '@framework/api/operations/get-all-products'
|
||||
|
||||
import rangeMap from '@lib/range-map'
|
||||
|
||||
const nonNullable = (v: any) => v
|
||||
|
||||
const BestsellingProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
||||
//TODO: pass the locale and preview mode as props...
|
||||
|
||||
|
||||
const locale = "en-US"
|
||||
const preview = false
|
||||
|
||||
const config = getConfig({ locale })
|
||||
|
||||
// Get Best Selling Products
|
||||
const { products: bestSellingProducts } = await getAllProducts({
|
||||
variables: { field: 'bestSellingProducts', first: 6 },
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
// Get Best Newest Products
|
||||
const { products: newestProducts } = await getAllProducts({
|
||||
variables: { field: 'newestProducts', first: 12 },
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
// These are the products that are going to be displayed in the landing.
|
||||
// We prefer to do the computation at buildtime/servertime
|
||||
const { bestSelling } = (() => {
|
||||
|
||||
// Create a copy of products that we can mutate
|
||||
const products = [...newestProducts]
|
||||
// If the lists of featured and best selling products don't have enough
|
||||
// products, then fill them with products from the products list, this
|
||||
// is useful for new commerce sites that don't have a lot of products
|
||||
return {
|
||||
bestSelling: rangeMap(
|
||||
6,
|
||||
(i) => bestSellingProducts[i] ?? products.shift()
|
||||
).filter(nonNullable),
|
||||
}
|
||||
})()
|
||||
|
||||
return {
|
||||
bestSelling
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default BestsellingProductsInit
|
34
framework/module-data/FeaturedProductsData.ts
Normal file
34
framework/module-data/FeaturedProductsData.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { getConfig } from '@framework/api'
|
||||
import getAllProducts from '@framework/api/operations/get-all-products'
|
||||
|
||||
|
||||
|
||||
import rangeMap from '@lib/range-map'
|
||||
|
||||
const nonNullable = (v: any) => v
|
||||
|
||||
const FeaturedProductsInit = async function ({ item, agility, languageCode, channelName, pageInSitemap, dynamicPageItem }: any) {
|
||||
//TODO: pass the locale and preview mode as props...
|
||||
|
||||
|
||||
const locale = "en-US"
|
||||
const preview = false
|
||||
|
||||
const config = getConfig({ locale })
|
||||
|
||||
// Get Featured Products
|
||||
const { products: featuredProducts } = await getAllProducts({
|
||||
variables: { field: 'featuredProducts', first: 6 },
|
||||
config,
|
||||
preview,
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
featured: featuredProducts
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default FeaturedProductsInit
|
22
framework/module-data/index.ts
Normal file
22
framework/module-data/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import {FC} from "react"
|
||||
import * as AgilityTypes from "@agility/types"
|
||||
|
||||
import BestsellingProductsData from "./BestsellingProductsData"
|
||||
import FeaturedProductsData from "./FeaturedProductsData"
|
||||
|
||||
const allModules:any =[
|
||||
{ name: "BestsellingProducts", init: BestsellingProductsData },
|
||||
{ name: "FeaturedProducts", init: FeaturedProductsData}
|
||||
]
|
||||
|
||||
/**
|
||||
* Find the data method for a module.
|
||||
* @param moduleName
|
||||
*/
|
||||
const getInitMethod = (moduleName:string):any => {
|
||||
const obj = allModules.find((m: { name: string }) => m.name.toLowerCase() === moduleName.toLowerCase())
|
||||
if (!obj) return null
|
||||
return obj.init
|
||||
}
|
||||
|
||||
export default getInitMethod
|
Reference in New Issue
Block a user