@jungvonmatt/cssg-plugin-hugo 1.1.0 → 1.4.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 +38 -0
  2. package/index.js +165 -9
  3. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -3,6 +3,44 @@
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.4.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.3.2...v1.4.0) (2022-01-19)
7
+
8
+ **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
9
+
10
+
11
+
12
+
13
+
14
+ ## [1.3.2](https://github.com/jungvonmatt/contentful-ssg/compare/v1.3.1...v1.3.2) (2022-01-14)
15
+
16
+ **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
17
+
18
+
19
+
20
+
21
+
22
+ # [1.3.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.2.0...v1.3.0) (2022-01-10)
23
+
24
+
25
+ ### Features
26
+
27
+ * 🎸 Consider menu reference in page node when building menu ([42499a4](https://github.com/jungvonmatt/contentful-ssg/commit/42499a4efb2f25275d8ea4fb74f72461aa441ba0))
28
+
29
+
30
+
31
+
32
+
33
+ # [1.2.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.1.1...v1.2.0) (2021-12-21)
34
+
35
+
36
+ ### Features
37
+
38
+ * 🎸 Generates hugo's menu.toml ([27e95a3](https://github.com/jungvonmatt/contentful-ssg/commit/27e95a33452b1aa5158ee89070889383cee4b6c5))
39
+
40
+
41
+
42
+
43
+
6
44
  # [1.1.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.0.6...v1.1.0) (2021-12-02)
7
45
 
8
46
 
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,6 +68,136 @@ 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 {
65
202
  config(prev) {
66
203
  const { managedDirectories } = prev || {};
@@ -76,10 +213,10 @@ export default (args) => {
76
213
  // Initialize getSettings
77
214
  const getSettings = getSettingsHelper(runtimeContext);
78
215
  helper.getSettings = getSettings;
79
- // Write config toml according to locale settings in contentful
216
+ // Write config yaml according to locale settings in contentful
80
217
  if (options.languageConfig) {
81
218
  const rootDir = runtimeContext?.config?.rootDir ?? process.cwd();
82
- const dst = path.join(rootDir, 'config/_default/languages.toml');
219
+ const dst = path.join(rootDir, 'config/_default/languages.yaml');
83
220
  const languageConfig = Object.fromEntries(
84
221
  locales.map((locale) => {
85
222
  const { code, name: languageName } = locale;
@@ -105,7 +242,7 @@ export default (args) => {
105
242
  ];
106
243
  })
107
244
  );
108
- await outputFile(dst, converter.toml.stringify(languageConfig));
245
+ await outputFile(dst, converter.yaml.stringify(languageConfig));
109
246
  }
110
247
 
111
248
  // Find section pages and add them to the runtimeconfig
@@ -125,8 +262,9 @@ export default (args) => {
125
262
  );
126
263
 
127
264
  const i18n = Object.fromEntries(locales.map((locale) => [locale.code, {}]));
265
+ const menus = Object.fromEntries(locales.map((locale) => [locale.code, {}]));
128
266
 
129
- return { ...runtimeContext, helper, localized: enhancedLocalized, i18n };
267
+ return { ...runtimeContext, helper, localized: enhancedLocalized, i18n, menus };
130
268
  },
131
269
 
132
270
  /**
@@ -231,7 +369,7 @@ export default (args) => {
231
369
  },
232
370
 
233
371
  async transform(transformContext, runtimeContext) {
234
- const { content, id, contentTypeId, locale } = transformContext;
372
+ const { content, id, contentTypeId, locale, entry } = transformContext;
235
373
 
236
374
  const type = getEntryType(transformContext);
237
375
 
@@ -247,6 +385,15 @@ export default (args) => {
247
385
  return undefined;
248
386
  }
249
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
+
250
397
  if (type === TYPE_CONTENT) {
251
398
  return {
252
399
  ...snakeCaseKeys({
@@ -261,17 +408,26 @@ export default (args) => {
261
408
 
262
409
  async after(runtimeContext) {
263
410
  const contentDir = runtimeContext.config.directory;
264
- const { toml } = runtimeContext.converter;
411
+ const { yaml } = runtimeContext.converter;
265
412
  const i18n = runtimeContext?.i18n ?? {};
266
413
 
267
414
  await Promise.all(
268
415
  Object.entries(i18n).map(async ([localeCode, translations]) => {
269
- const dictionaryPath = path.join(contentDir, `../i18n/${localeCode}.toml`);
416
+ const dictionaryPath = path.join(contentDir, `../i18n/${localeCode}.yaml`);
270
417
  const oldContent = existsSync(dictionaryPath)
271
- ? toml.parse(await readFile(dictionaryPath, 'utf8'))
418
+ ? yaml.parse(await readFile(dictionaryPath, 'utf8'))
272
419
  : {};
273
420
 
274
- return outputFile(dictionaryPath, toml.stringify({ ...oldContent, ...translations }));
421
+ return outputFile(dictionaryPath, yaml.stringify({ ...oldContent, ...translations }));
422
+ })
423
+ );
424
+
425
+ const menus = runtimeContext?.menus ?? {};
426
+ await Promise.all(
427
+ Object.entries(menus).map(([localeCode, menuData]) => {
428
+ const file = `config/_default/menus.${localeCode}.yaml`;
429
+ const data = yaml.stringify(menuData);
430
+ return outputFile(file, data);
275
431
  })
276
432
  );
277
433
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungvonmatt/cssg-plugin-hugo",
3
- "version": "1.1.0",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "typings": "./index.d.ts",
@@ -20,16 +20,16 @@
20
20
  },
21
21
  "keywords": [],
22
22
  "author": "Ben Zörb",
23
- "license": "ISC",
23
+ "license": "MIT",
24
24
  "bugs": {
25
25
  "url": "https://github.com/jungvonmatt/contentful-ssg/issues"
26
26
  },
27
27
  "homepage": "https://github.com/jungvonmatt/contentful-ssg#readme",
28
28
  "dependencies": {
29
- "@jungvonmatt/contentful-ssg": "^1.1.0",
29
+ "@jungvonmatt/contentful-ssg": "^1.4.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": "95f58f6323beda2a6ba1b3cb6fecd07ff6d6cc7f"
34
+ "gitHead": "0d323f9884ae8fa48049e25bcd127954ee7988b8"
35
35
  }