Source

server/generate-routes.js

  1. /**
  2. * This name contins helpers for generating routes.
  3. * ```javascript
  4. * import { generateCommonRoutes } from "@quintype/framework/server/generate-routes";
  5. * ```
  6. * @category Server
  7. * @module generate-routes
  8. */
  9. // The below code dynamically generates routes based on the config
  10. // A section sect will generate three urls:
  11. // /sect, /sect/:storySlug, /sect/*/:storySlug
  12. const _ = require("lodash");
  13. let shownDeprecationWarning = false;
  14. function deprecationWarning() {
  15. if (!shownDeprecationWarning) {
  16. // eslint-disable-next-line no-console
  17. console.warn(
  18. "[WARNING] generateSectionPageRoutes and generateStoryPageRoutes are deprecated and will be removed in @quintype/framework v4. Please switch to generateCommonRoutes https://github.com/quintype/quintype-node-framework/blob/master/README.md#switching-to-generatecommonroutes"
  19. );
  20. shownDeprecationWarning = true;
  21. }
  22. }
  23. exports.generateSectionPageRoutes = function generateSectionPageRoutes(
  24. config,
  25. opts = {}
  26. ) {
  27. deprecationWarning();
  28. const sections = config.getDomainSections(opts.domainSlug);
  29. const sectionsById = _(sections).reduce((acc, section) => {
  30. acc[section.id] = section;
  31. return acc;
  32. }, {});
  33. return _(sections)
  34. .flatMap((section) => generateSectionPageRoute(section, sectionsById, opts))
  35. .value();
  36. };
  37. function generateSectionPageRoute(section, sectionsById, opts) {
  38. const params = {
  39. sectionId: section.id,
  40. ...(opts.layoutId && { layoutId: opts.layoutId }),
  41. ...(opts.preview && { preview: true }),
  42. };
  43. if (section.collection) params.collectionSlug = section.collection.slug;
  44. let { slug } = section;
  45. if (section["parent-id"]) {
  46. let currentSection = section;
  47. let depth = 0;
  48. while (currentSection["parent-id"] && depth++ < 5) {
  49. currentSection = sectionsById[currentSection["parent-id"]] || {
  50. slug: "invalid",
  51. };
  52. slug = `${currentSection.slug}/${slug}`;
  53. }
  54. }
  55. let routes = [];
  56. if (section["parent-id"] && opts.secWithoutParentPrefix)
  57. routes = [section.slug, slug];
  58. else routes = [slug];
  59. if (opts.addSectionPrefix)
  60. routes = _.flatMap(routes, (route) => [
  61. sectionPageRoute(route, params),
  62. addSectionPrefix(route, params),
  63. ]);
  64. else routes = _.flatMap(routes, (route) => [sectionPageRoute(route, params)]);
  65. return routes;
  66. }
  67. function addSectionPrefix(route, params) {
  68. return sectionPageRoute(`section/${route}`, params);
  69. }
  70. function sectionPageRoute(route, params) {
  71. return {
  72. pageType: "section-page",
  73. exact: true,
  74. path: `/${route}`,
  75. params,
  76. };
  77. }
  78. exports.generateStoryPageRoutes = function generateStoryPageRoutes(
  79. config,
  80. { withoutParentSection, domainSlug, skipPWA } = {}
  81. ) {
  82. deprecationWarning();
  83. const sections = config.getDomainSections(domainSlug);
  84. return _(sections)
  85. .filter((section) => withoutParentSection || !section["parent-id"])
  86. .flatMap((section) => [
  87. storyPageRoute(`/${section.slug}/:storySlug`, skipPWA || false),
  88. storyPageRoute(`/${section.slug}/*/:storySlug`, skipPWA || false),
  89. ])
  90. .value();
  91. };
  92. function storyPageRoute(path, skipPWA = false) {
  93. return {
  94. pageType: "story-page",
  95. exact: true,
  96. path,
  97. skipPWA,
  98. };
  99. }
  100. /**
  101. * This is used to generate all routes
  102. *
  103. * @param {Config} config The config object
  104. * @param {string} domainSlug The domainSlug (undefined if this is the main domain)
  105. * @param {Object} opts
  106. * @param {boolean} opts.allRoutes Generate all routes (default true)
  107. * @param {boolean} opts.sectionPageRoutes Generate section page routes (default *allRoutes*)
  108. * @param {boolean} opts.storyPageRoutes Generate story page routes (default *allRoutes*)
  109. * @param {boolean} opts.homePageRoute Generate home page route (default *allRoutes*)
  110. * @param {Object} opts.skipPWA
  111. * @param {boolean} opts.skipPWA.story Skips PWA for story pages
  112. * @param {boolean} opts.skipPWA.home Skips PWA for home pages
  113. * @param {boolean} opts.skipPWA.section Skips PWA for section pages
  114. * @return {Array<module:match-best-route~Route>} Array of created routes
  115. */
  116. exports.generateCommonRoutes = function generateSectionPageRoutes(
  117. config,
  118. domainSlug,
  119. {
  120. allRoutes = true,
  121. sectionPageRoutes = allRoutes,
  122. storyPageRoutes = allRoutes,
  123. homePageRoute = allRoutes,
  124. skipPWA = {},
  125. } = {}
  126. ) {
  127. const sections = config.getDomainSections(domainSlug);
  128. const sectionRoutes = sections.map((s) =>
  129. sectionToSectionRoute(config["sketches-host"], s, skipPWA.section || false)
  130. );
  131. const storyRoutes = sectionRoutes.map(({ path }) => ({
  132. path: `${path.replace(/^\/section\//, "/")}(/.*)?/:storySlug`,
  133. pageType: "story-page",
  134. exact: true,
  135. skipPWA: skipPWA.story || false,
  136. }));
  137. return [].concat(
  138. homePageRoute
  139. ? [
  140. {
  141. path: "/",
  142. pageType: "home-page",
  143. exact: true,
  144. skipPWA: skipPWA.home || false,
  145. params: {
  146. collectionSlug: config.getHomeCollectionSlug(domainSlug),
  147. },
  148. },
  149. ]
  150. : [],
  151. [
  152. {
  153. path: '/preview/:previewId/collection/home',
  154. pageType: 'home-page-preview',
  155. exact: true,
  156. skipPWA: true,
  157. params: {
  158. collectionSlug: config.getHomeCollectionSlug(domainSlug)
  159. }
  160. }
  161. ],
  162. [
  163. {
  164. path: '/preview/:previewId/collection/:collectionSlug',
  165. pageType: 'collection-page-preview',
  166. exact: true,
  167. skipPWA: skipPWA.home || false,
  168. params: {
  169. collectionSlug: config.getHomeCollectionSlug(domainSlug)
  170. }
  171. }
  172. ],
  173. sectionPageRoutes ? sectionRoutes : [],
  174. storyPageRoutes ? storyRoutes : []
  175. );
  176. };
  177. function sectionToSectionRoute(baseUrl, section, skipPWA = false) {
  178. const params = {
  179. sectionId: section.id,
  180. };
  181. if (section.collection && section.collection.slug) {
  182. params.collectionSlug = section.collection.slug;
  183. }
  184. try {
  185. const sectionUrl = section["section-url"];
  186. const relativeUrl =
  187. baseUrl && sectionUrl.startsWith(baseUrl)
  188. ? sectionUrl.slice(baseUrl.length)
  189. : new URL(section["section-url"]).pathname;
  190. return {
  191. path: relativeUrl,
  192. pageType: "section-page",
  193. exact: true,
  194. params,
  195. skipPWA,
  196. };
  197. } catch (e) {
  198. return {
  199. path: `/${section.slug}`,
  200. pageType: "section-page",
  201. exact: true,
  202. params,
  203. skipPWA,
  204. };
  205. }
  206. }