@jungvonmatt/cssg-plugin-hugo 1.0.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/index.js +165 -2
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -3,6 +3,47 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.3.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.2.0...v1.3.0) (2022-01-10)
7
+
8
+
9
+ ### Features
10
+
11
+ * 🎸 Consider menu reference in page node when building menu ([42499a4](https://github.com/jungvonmatt/contentful-ssg/commit/42499a4efb2f25275d8ea4fb74f72461aa441ba0))
12
+
13
+
14
+
15
+
16
+
17
+ # [1.2.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.1.1...v1.2.0) (2021-12-21)
18
+
19
+
20
+ ### Features
21
+
22
+ * 🎸 Generates hugo's menu.toml ([27e95a3](https://github.com/jungvonmatt/contentful-ssg/commit/27e95a33452b1aa5158ee89070889383cee4b6c5))
23
+
24
+
25
+
26
+
27
+
28
+ # [1.1.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.0.6...v1.1.0) (2021-12-02)
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * 🐛 Adds <rootDir>/data to managed directories ([d67de2b](https://github.com/jungvonmatt/contentful-ssg/commit/d67de2b4a78771076e5fd1d4e7beec0c1fb65a7d))
34
+
35
+
36
+
37
+
38
+
39
+ ## [1.0.6](https://github.com/jungvonmatt/contentful-ssg/compare/v1.0.5...v1.0.6) (2021-12-02)
40
+
41
+ **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
42
+
43
+
44
+
45
+
46
+
6
47
  ## [1.0.5](https://github.com/jungvonmatt/contentful-ssg/compare/v1.0.4...v1.0.5) (2021-11-30)
7
48
 
8
49
  **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
package/index.js CHANGED
@@ -20,9 +20,16 @@ const defaultOptions = {
20
20
  translationStrategy: STRATEGY_DIRECTORY,
21
21
  typeIdI18n: 'd-i18n',
22
22
  languageConfig: true,
23
+ menuDepth: 0,
24
+ autoSubMenu: false,
25
+ typeIdMenu: 'c-menu',
23
26
  fieldIdHome: 'home',
24
27
  fieldIdSlug: 'slug',
25
28
  fieldIdParent: 'parent_page',
29
+ fieldIdMenu: 'menu',
30
+ fieldIdMenuEntries: 'entries',
31
+ fieldIdMenuHide: 'hide_in_menu',
32
+ fieldIdMenuPos: 'menu_pos',
26
33
  typeConfig: {
27
34
  [TYPE_CONTENT]: ['page'],
28
35
  },
@@ -61,7 +68,143 @@ export default (args) => {
61
68
  return type;
62
69
  };
63
70
 
71
+ const buildMenu = async (transformContext, depth = 0) => {
72
+ const { entry, entryMap } = transformContext;
73
+ const entries = entry.fields?.[options.fieldIdMenuEntries] ?? [];
74
+ const nodes = entries
75
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
76
+ .map((node, index) => ({
77
+ identifier: node.sys.id,
78
+ weight: (index + 1) * -10,
79
+ params: {
80
+ id: node.sys.id,
81
+ // eslint-disable-next-line camelcase
82
+ content_type: node.sys.contentType.sys.id,
83
+ },
84
+ }));
85
+
86
+ // Resolve page entry
87
+ const resolvePageEntry = async (entry) => {
88
+ const id = entry?.sys?.id ?? 0;
89
+ const node = entryMap.get(id);
90
+ const contentType = node?.sys?.contentType?.sys?.id ?? '';
91
+ const pageId = node?.fields?.link_to_entry?.sys?.id;
92
+ if (contentType === 'page') {
93
+ return node;
94
+ }
95
+
96
+ if (pageId) {
97
+ return entryMap.get(pageId);
98
+ }
99
+
100
+ if (typeof options.resolvePage === 'function') {
101
+ return options.resolvePage(entry, entryMap);
102
+ }
103
+ };
104
+
105
+ const getChildnodesManual = async (entry, depth, ids = []) => {
106
+ const id = entry?.sys?.id ?? 0;
107
+ const page = await resolvePageEntry(entry);
108
+ const contentType = page?.sys?.contentType?.sys?.id ?? '';
109
+ const menuId = page?.fields?.[options.fieldIdMenu]?.sys?.id;
110
+
111
+ if (
112
+ !id ||
113
+ !contentType ||
114
+ !menuId ||
115
+ !entryMap.has(menuId) ||
116
+ ids.includes(id) ||
117
+ depth <= 0
118
+ ) {
119
+ return [];
120
+ }
121
+
122
+ const menu = entryMap.get(menuId);
123
+ const subentries = menu?.fields?.[options.fieldIdMenuEntries] ?? [];
124
+
125
+ const collected = await Promise.all(
126
+ subentries.flatMap((node) => getChildnodesManual(node, depth - 1, [...ids, id]))
127
+ );
128
+
129
+ return [
130
+ ...subentries
131
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
132
+ .map((node, index) => ({
133
+ identifier: node.sys.id,
134
+ parent: id,
135
+ weight: (index + 1) * -10,
136
+ params: {
137
+ id: node.sys.id,
138
+ // eslint-disable-next-line camelcase
139
+ content_type: node.sys.contentType.sys.id,
140
+ },
141
+ })),
142
+ ...collected,
143
+ ];
144
+ };
145
+
146
+ const getChildnodesRecursive = async (entry, depth) => {
147
+ const id = entry?.sys?.id ?? 0;
148
+ const page = await resolvePageEntry(id);
149
+ const contentType = page?.sys?.contentType?.sys?.id ?? '';
150
+
151
+ if (!id || !contentType || depth <= 0) {
152
+ return [];
153
+ }
154
+
155
+ const childnodes = [...entryMap.values()].filter(
156
+ (entry) => (entry?.fields?.[options.fieldIdParent]?.sys?.id ?? '') === id
157
+ );
158
+
159
+ // Filter childnodes based on hide_in_menu field
160
+ const filtered = childnodes.filter(
161
+ (entry) => !(entry?.fields?.[options.fieldIdMenuHide] ?? false)
162
+ );
163
+
164
+ // Sort based on menuPos field
165
+ const sorted = [...filtered].sort(
166
+ (a, b) =>
167
+ (a?.fields?.[options.fieldIdMenuPos] ?? Number.MAX_SAFE_INTEGER) -
168
+ (b?.fields?.[options.fieldIdMenuPos] ?? Number.MAX_SAFE_INTEGER)
169
+ );
170
+
171
+ return Array.from(
172
+ await Promise.allSettled([
173
+ ...sorted
174
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
175
+ .map((node, index) => ({
176
+ identifier: node.sys.id,
177
+ parent: id,
178
+ weight: (index + 1) * -10,
179
+ params: {
180
+ id: node.sys.id,
181
+ // eslint-disable-next-line camelcase
182
+ content_type: node.sys.contentType.sys.id,
183
+ },
184
+ })),
185
+ ...sorted.flatMap((node) => getChildnodesRecursive(node, depth - 1)),
186
+ ])
187
+ )
188
+ .map((a) => a.value)
189
+ .filter((v) => v);
190
+ };
191
+
192
+ // When autoSubMenu parameter is set, we collect child pages automatically
193
+ // Otherwise we look for dedicated menu entries in page nodes
194
+ const childentries = options.autoSubMenu
195
+ ? await Promise.all(entries.flatMap((node) => getChildnodesRecursive(node, depth)))
196
+ : await Promise.all(entries.flatMap((node) => getChildnodesManual(node, depth)));
197
+
198
+ return [...nodes, ...childentries].flat(Infinity).filter((v) => v);
199
+ };
200
+
64
201
  return {
202
+ config(prev) {
203
+ const { managedDirectories } = prev || {};
204
+
205
+ return { ...prev, managedDirectories: [...(managedDirectories || []), 'data'] };
206
+ },
207
+
65
208
  // Before hook
66
209
  async before(runtimeContext) {
67
210
  const { helper, converter, data, localized } = runtimeContext;
@@ -119,8 +262,9 @@ export default (args) => {
119
262
  );
120
263
 
121
264
  const i18n = Object.fromEntries(locales.map((locale) => [locale.code, {}]));
265
+ const menus = Object.fromEntries(locales.map((locale) => [locale.code, {}]));
122
266
 
123
- return { ...runtimeContext, helper, localized: enhancedLocalized, i18n };
267
+ return { ...runtimeContext, helper, localized: enhancedLocalized, i18n, menus };
124
268
  },
125
269
 
126
270
  /**
@@ -225,7 +369,7 @@ export default (args) => {
225
369
  },
226
370
 
227
371
  async transform(transformContext, runtimeContext) {
228
- const { content, id, contentTypeId, locale } = transformContext;
372
+ const { content, id, contentTypeId, locale, entry } = transformContext;
229
373
 
230
374
  const type = getEntryType(transformContext);
231
375
 
@@ -241,6 +385,15 @@ export default (args) => {
241
385
  return undefined;
242
386
  }
243
387
 
388
+ // Automatically build hugo menus
389
+ // See https://gohugo.io/content-management/menus/
390
+ if (options.typeIdMenu && contentTypeId === options.typeIdMenu) {
391
+ const { name = 'main' } = entry.fields;
392
+ const menu = await buildMenu(transformContext, options.menuDepth);
393
+
394
+ runtimeContext.menus[locale.code][name] = menu;
395
+ }
396
+
244
397
  if (type === TYPE_CONTENT) {
245
398
  return {
246
399
  ...snakeCaseKeys({
@@ -268,6 +421,16 @@ export default (args) => {
268
421
  return outputFile(dictionaryPath, toml.stringify({ ...oldContent, ...translations }));
269
422
  })
270
423
  );
424
+
425
+ const { stringify } = await import('@jungvonmatt/contentful-ssg/converter/toml');
426
+ const menus = runtimeContext?.menus ?? {};
427
+ await Promise.all(
428
+ Object.entries(menus).map(([localeCode, menuData]) => {
429
+ const file = `config/_default/menus.${localeCode}.toml`;
430
+ const data = stringify(menuData);
431
+ return outputFile(file, data);
432
+ })
433
+ );
271
434
  },
272
435
  };
273
436
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungvonmatt/cssg-plugin-hugo",
3
- "version": "1.0.5",
3
+ "version": "1.3.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "typings": "./index.d.ts",
@@ -26,10 +26,10 @@
26
26
  },
27
27
  "homepage": "https://github.com/jungvonmatt/contentful-ssg#readme",
28
28
  "dependencies": {
29
- "@jungvonmatt/contentful-ssg": "^1.0.5",
29
+ "@jungvonmatt/contentful-ssg": "^1.1.0",
30
30
  "fs-extra": "^10.0.0",
31
31
  "merge-options": "^3.0.4",
32
32
  "micromatch": "^4.0.4"
33
33
  },
34
- "gitHead": "03741b4add20ce95e1cd539bc46b7630cddcc2c3"
34
+ "gitHead": "2cfe041c72ed9a583e5e45e0fad165772ff37a79"
35
35
  }