Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NextJS] Refactor sitemap-fetcher to make it extendable #865

Merged
merged 2 commits into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions samples/nextjs/scripts/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ generateConfig(configOverride);
*/
import './generate-component-factory';
/*
PAGE PROPS FACTORY PLUGINS GENERATION
PLUGINS GENERATION
*/
import './generate-page-props-factory-plugins';
import './generate-plugin';
66 changes: 0 additions & 66 deletions samples/nextjs/scripts/generate-page-props-factory-plugins.ts

This file was deleted.

87 changes: 87 additions & 0 deletions samples/nextjs/scripts/generate-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import fs from 'fs';
import path from 'path';
import { getItems } from './utils';

/*
PLUGINS GENERATION
NOTE: pluginName: the name of the plugin in the src/lib folder
Generates the `/src/temp/{pluginName}-plugins.ts` file, which exports list of plugins

Generating the plugins is optional, and it can be maintained manually if preferred.

The default convention uses the plugin's filename (without the extension) as the first part of the component
name. For example, the file `/lib/page-props-factory/plugins/exampleName.ts` would map to plugin `exampleNamePlugin`.
This can be customized in writePlugins().
*/

const sitemapFetcherPluginListPath = path.resolve('src/temp/sitemap-fetcher-plugins.ts');
const pagePropsFactoryPluginListPath = path.resolve('src/temp/page-props-factory-plugins.ts');
const sitemapFetcherPluginsRootPath = 'src/lib/sitemap-fetcher/plugins';
const pagePropsFactoryPluginsRootPath = 'src/lib/page-props-factory/plugins';

const paths = [
{
pluginListPath: sitemapFetcherPluginListPath,
pluginsRootPath: sitemapFetcherPluginsRootPath,
},
{
pluginListPath: pagePropsFactoryPluginListPath,
pluginsRootPath: pagePropsFactoryPluginsRootPath,
},
];

interface Paths {
pluginListPath: string;
pluginsRootPath: string;
}

interface PluginFile {
path: string;
name: string;
}

run(paths);

function run(paths: Paths[]) {
paths.forEach((path) => {
writePlugins(path.pluginListPath, path.pluginsRootPath);
});
}

/**
* Generates the plugins file and saves it to the filesystem.
* By convention, we expect to find plugins under src/lib/{pluginName}/plugins/** (subfolders are
* searched recursively). The filename, with extension and non-word characters
* stripped, is used to identify the plugin's JavaScript module definition (for adding
* new plugin to the factory).
* Modify this function to use a different convention.
*/
function writePlugins(pluginListPath: string, pluginsRootPath: string) {
const pluginName = pluginsRootPath.split('/')[2];
const plugins = getPluginList(pluginsRootPath, pluginName);

const fileContent = plugins
.map((plugin) => {
return `export { ${plugin.name} } from '${plugin.path}';`;
})
.join('\r\n')
.concat('\r\n');

console.log(`Writing ${pluginName} plugins to ${pluginListPath}`);
fs.writeFileSync(pluginListPath, fileContent, {
encoding: 'utf8',
});
}

function getPluginList(path: string, pluginName: string): PluginFile[] {
const plugins = getItems<PluginFile>({
path,
resolveItem: (path, name) => ({
path: `${path}/${name}`,
name: `${name.replace(/[^\w]+/g, '')}Plugin`,
}),
cb: (name) => console.debug(`Registering ${pluginName} plugin ${name}`),
});

return plugins;
}
77 changes: 0 additions & 77 deletions samples/nextjs/src/lib/sitemap-fetcher.ts

This file was deleted.

25 changes: 25 additions & 0 deletions samples/nextjs/src/lib/sitemap-fetcher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { GetStaticPathsContext } from 'next';
import { StaticPath } from '@sitecore-jss/sitecore-jss-nextjs';
import * as plugins from 'temp/sitemap-fetcher-plugins';

export interface SitemapFetcherPlugin {
/**
* A function which will be called during page props generation
*/
exec(context?: GetStaticPathsContext): Promise<StaticPath[]>;
}

export class SitecoreSitemapFetcher {
/**
* Generates SitecoreSitemap for given mode (Export / Disconnected Export / SSG)
* @param {GetStaticPathsContext} context
*/
async fetch(context?: GetStaticPathsContext): Promise<StaticPath[]> {
const pluginsList = Object.values(plugins);
const pluginsResults = await Promise.all(pluginsList.map((plugin) => plugin.exec(context)));
const results = pluginsResults.reduce((acc, cur) => [...acc, ...cur], []);
return results;
}
}

export const sitemapFetcher = new SitecoreSitemapFetcher();
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { DisconnectedSitemapService } from '@sitecore-jss/sitecore-jss-nextjs';
import { ManifestInstance } from '@sitecore-jss/sitecore-jss-dev-tools';
import { SitemapFetcherPlugin } from '..';

class DisconnectedSitemapServicePlugin implements SitemapFetcherPlugin {
_disconnectedSitemapService: DisconnectedSitemapService;

constructor() {
this._disconnectedSitemapService = new DisconnectedSitemapService(
this.getManifest() as unknown as ManifestInstance
);
}

/**
* Get sitecore-import.json manifest
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
getManifest() {
if (process.env.JSS_MODE !== 'disconnected') return null;

try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const manifest = require('sitecore/manifest/sitecore-import.json');

return manifest;
} catch (error) {
throw Error(
"[Disconnected Export] Please make sure you've started the disconnected proxy `npm run start:disconnected-proxy`"
);
}
}

async exec() {
// If we are in Export mode
if (process.env.EXPORT_MODE) {
// Disconnected Export mode
if (process.env.JSS_MODE === 'disconnected') {
return this._disconnectedSitemapService.fetchExportSitemap();
}
}

return [];
}
}

export const disconnectedSitemapServicePlugin = new DisconnectedSitemapServicePlugin();
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { GraphQLSitemapService } from '@sitecore-jss/sitecore-jss-nextjs';
import config from 'temp/config';
import { SitemapFetcherPlugin } from '..';
import { GetStaticPathsContext } from 'next';
import pkg from '../../../../package.json';
import { StaticPath } from '@sitecore-jss/sitecore-jss-nextjs';

class GraphqlSitemapServicePlugin implements SitemapFetcherPlugin {
_graphqlSitemapService: GraphQLSitemapService;

constructor() {
this._graphqlSitemapService = new GraphQLSitemapService({
endpoint: config.graphQLEndpoint,
apiKey: config.sitecoreApiKey,
siteName: config.jssAppName,
/*
The Sitemap Service needs a root item ID in order to fetch the list of pages for the current
app. If your Sitecore instance only has 1 JSS App, you can specify the root item ID here;
otherwise, the service will attempt to figure out the root item for the current JSS App using GraphQL and app name.
rootItemId: '{GUID}'
*/
});
}

async exec(context?: GetStaticPathsContext): Promise<StaticPath[]> {
if (process.env.EXPORT_MODE) {
// Disconnected Export mode
if (process.env.JSS_MODE !== 'disconnected') {
return this._graphqlSitemapService.fetchExportSitemap(pkg.config.language);
}
}
return this._graphqlSitemapService.fetchSSGSitemap(context?.locales || []);
}
}

export const graphqlSitemapServicePlugin = new GraphqlSitemapServicePlugin();