@jungvonmatt/cssg-plugin-hugo 1.2.0 → 1.4.3

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 +122 -47
  3. package/package.json +4 -5
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.3](https://github.com/jungvonmatt/contentful-ssg/compare/v1.4.2...v1.4.3) (2022-01-24)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * 🐛 Don't deep merge settings ([298c15e](https://github.com/jungvonmatt/contentful-ssg/commit/298c15ed3c80dc222b42909db610c41e977a9110))
12
+
13
+
14
+
15
+
16
+
17
+ # [1.4.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.3.2...v1.4.0) (2022-01-19)
18
+
19
+ **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
20
+
21
+
22
+
23
+
24
+
25
+ ## [1.3.2](https://github.com/jungvonmatt/contentful-ssg/compare/v1.3.1...v1.3.2) (2022-01-14)
26
+
27
+ **Note:** Version bump only for package @jungvonmatt/cssg-plugin-hugo
28
+
29
+
30
+
31
+
32
+
33
+ # [1.3.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.2.0...v1.3.0) (2022-01-10)
34
+
35
+
36
+ ### Features
37
+
38
+ * 🎸 Consider menu reference in page node when building menu ([42499a4](https://github.com/jungvonmatt/contentful-ssg/commit/42499a4efb2f25275d8ea4fb74f72461aa441ba0))
39
+
40
+
41
+
42
+
43
+
6
44
  # [1.2.0](https://github.com/jungvonmatt/contentful-ssg/compare/v1.1.1...v1.2.0) (2021-12-21)
7
45
 
8
46
 
package/index.js CHANGED
@@ -1,5 +1,3 @@
1
- import mergeOptionsModule from 'merge-options';
2
-
3
1
  import { snakeCaseKeys } from '@jungvonmatt/contentful-ssg/lib/object';
4
2
  import mm from 'micromatch';
5
3
  import { existsSync } from 'fs';
@@ -7,8 +5,6 @@ import { outputFile } from 'fs-extra';
7
5
  import { readFile } from 'fs/promises';
8
6
  import path from 'path';
9
7
 
10
- const mergeOptions = mergeOptionsModule.bind({ ignoreUndefined: true });
11
-
12
8
  export const TYPE_CONTENT = 'content';
13
9
  export const TYPE_DATA = 'data';
14
10
 
@@ -19,12 +15,14 @@ const defaultOptions = {
19
15
  typeIdSettings: 'd-settings',
20
16
  translationStrategy: STRATEGY_DIRECTORY,
21
17
  typeIdI18n: 'd-i18n',
22
- menuDepth: 2,
23
- typeIdMenu: 'c-menu',
24
18
  languageConfig: true,
19
+ menuDepth: 0,
20
+ autoSubMenu: false,
21
+ typeIdMenu: 'c-menu',
25
22
  fieldIdHome: 'home',
26
23
  fieldIdSlug: 'slug',
27
24
  fieldIdParent: 'parent_page',
25
+ fieldIdMenu: 'menu',
28
26
  fieldIdMenuEntries: 'entries',
29
27
  fieldIdMenuHide: 'hide_in_menu',
30
28
  fieldIdMenuPos: 'menu_pos',
@@ -46,8 +44,11 @@ export default (args) => {
46
44
  const settingsEntries = Array.from(entryMap.values()).filter(
47
45
  (entry) => (entry?.sys?.contentType?.sys?.id ?? 'unknown') === options.typeIdSettings
48
46
  );
49
- const settingsFields = settingsEntries.map((entry) => entry?.fields ?? {});
50
- return [locale, mergeOptions(...settingsFields)];
47
+ const settingsFields = settingsEntries
48
+ .map((entry) => entry?.fields ?? {})
49
+ .reduce((result, fields) => ({ ...result, ...fields }), {});
50
+
51
+ return [locale, settingsFields];
51
52
  }
52
53
  )
53
54
  );
@@ -66,23 +67,85 @@ export default (args) => {
66
67
  return type;
67
68
  };
68
69
 
69
- const buildMenu = (transformContext, depth = 0) => {
70
+ const buildMenu = async (transformContext, depth = 0) => {
70
71
  const { entry, entryMap } = transformContext;
72
+ const entries = entry.fields?.[options.fieldIdMenuEntries] ?? [];
73
+ const nodes = entries
74
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
75
+ .map((node, index) => ({
76
+ identifier: node.sys.id,
77
+ weight: (index + 1) * -10,
78
+ params: {
79
+ id: node.sys.id,
80
+ // eslint-disable-next-line camelcase
81
+ content_type: node.sys.contentType.sys.id,
82
+ },
83
+ }));
84
+
85
+ // Resolve page entry
86
+ const resolvePageEntry = async (entry) => {
87
+ const id = entry?.sys?.id ?? 0;
88
+ const node = entryMap.get(id);
89
+ const contentType = node?.sys?.contentType?.sys?.id ?? '';
90
+ const pageId = node?.fields?.link_to_entry?.sys?.id;
91
+ if (contentType === 'page') {
92
+ return node;
93
+ }
71
94
 
72
- const entries = entry.fields?.entries ?? [];
73
- const nodes = entries.map((node, index) => ({
74
- identifier: node.sys.id,
75
- weight: (index + 1) * -10,
76
- params: {
77
- id: node.sys.id,
78
- // eslint-disable-next-line camelcase
79
- content_type: node.sys.contentType.sys.id,
80
- },
81
- }));
82
-
83
- const getChildnodes = (entry, depth) => {
95
+ if (pageId) {
96
+ return entryMap.get(pageId);
97
+ }
98
+
99
+ if (typeof options.resolvePage === 'function') {
100
+ return options.resolvePage(entry, entryMap);
101
+ }
102
+ };
103
+
104
+ const getChildnodesManual = async (entry, depth, ids = []) => {
84
105
  const id = entry?.sys?.id ?? 0;
85
- const contentType = entry?.sys?.contentType?.sys?.id ?? '';
106
+ const page = await resolvePageEntry(entry);
107
+ const contentType = page?.sys?.contentType?.sys?.id ?? '';
108
+ const menuId = page?.fields?.[options.fieldIdMenu]?.sys?.id;
109
+
110
+ if (
111
+ !id ||
112
+ !contentType ||
113
+ !menuId ||
114
+ !entryMap.has(menuId) ||
115
+ ids.includes(id) ||
116
+ depth <= 0
117
+ ) {
118
+ return [];
119
+ }
120
+
121
+ const menu = entryMap.get(menuId);
122
+ const subentries = menu?.fields?.[options.fieldIdMenuEntries] ?? [];
123
+
124
+ const collected = await Promise.all(
125
+ subentries.flatMap((node) => getChildnodesManual(node, depth - 1, [...ids, id]))
126
+ );
127
+
128
+ return [
129
+ ...subentries
130
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
131
+ .map((node, index) => ({
132
+ identifier: node.sys.id,
133
+ parent: id,
134
+ weight: (index + 1) * -10,
135
+ params: {
136
+ id: node.sys.id,
137
+ // eslint-disable-next-line camelcase
138
+ content_type: node.sys.contentType.sys.id,
139
+ },
140
+ })),
141
+ ...collected,
142
+ ];
143
+ };
144
+
145
+ const getChildnodesRecursive = async (entry, depth) => {
146
+ const id = entry?.sys?.id ?? 0;
147
+ const page = await resolvePageEntry(id);
148
+ const contentType = page?.sys?.contentType?.sys?.id ?? '';
86
149
 
87
150
  if (!id || !contentType || depth <= 0) {
88
151
  return [];
@@ -104,22 +167,34 @@ export default (args) => {
104
167
  (b?.fields?.[options.fieldIdMenuPos] ?? Number.MAX_SAFE_INTEGER)
105
168
  );
106
169
 
107
- return [
108
- ...sorted.map((node, index) => ({
109
- identifier: node.sys.id,
110
- parent: id,
111
- weight: (index + 1) * -10,
112
- params: {
113
- id: node.sys.id,
114
- // eslint-disable-next-line camelcase
115
- content_type: node.sys.contentType.sys.id,
116
- },
117
- })),
118
- ...sorted.flatMap((node) => getChildnodes(node, depth - 1)),
119
- ];
170
+ return Array.from(
171
+ await Promise.allSettled([
172
+ ...sorted
173
+ .filter((node) => node?.sys?.id && node?.sys?.contentType?.sys?.id)
174
+ .map((node, index) => ({
175
+ identifier: node.sys.id,
176
+ parent: id,
177
+ weight: (index + 1) * -10,
178
+ params: {
179
+ id: node.sys.id,
180
+ // eslint-disable-next-line camelcase
181
+ content_type: node.sys.contentType.sys.id,
182
+ },
183
+ })),
184
+ ...sorted.flatMap((node) => getChildnodesRecursive(node, depth - 1)),
185
+ ])
186
+ )
187
+ .map((a) => a.value)
188
+ .filter((v) => v);
120
189
  };
121
190
 
122
- return [...nodes, ...entries.flatMap((node) => getChildnodes(node, depth))];
191
+ // When autoSubMenu parameter is set, we collect child pages automatically
192
+ // Otherwise we look for dedicated menu entries in page nodes
193
+ const childentries = options.autoSubMenu
194
+ ? await Promise.all(entries.flatMap((node) => getChildnodesRecursive(node, depth)))
195
+ : await Promise.all(entries.flatMap((node) => getChildnodesManual(node, depth)));
196
+
197
+ return [...nodes, ...childentries].flat(Infinity).filter((v) => v);
123
198
  };
124
199
 
125
200
  return {
@@ -137,10 +212,10 @@ export default (args) => {
137
212
  // Initialize getSettings
138
213
  const getSettings = getSettingsHelper(runtimeContext);
139
214
  helper.getSettings = getSettings;
140
- // Write config toml according to locale settings in contentful
215
+ // Write config yaml according to locale settings in contentful
141
216
  if (options.languageConfig) {
142
217
  const rootDir = runtimeContext?.config?.rootDir ?? process.cwd();
143
- const dst = path.join(rootDir, 'config/_default/languages.toml');
218
+ const dst = path.join(rootDir, 'config/_default/languages.yaml');
144
219
  const languageConfig = Object.fromEntries(
145
220
  locales.map((locale) => {
146
221
  const { code, name: languageName } = locale;
@@ -166,7 +241,7 @@ export default (args) => {
166
241
  ];
167
242
  })
168
243
  );
169
- await outputFile(dst, converter.toml.stringify(languageConfig));
244
+ await outputFile(dst, converter.yaml.stringify(languageConfig));
170
245
  }
171
246
 
172
247
  // Find section pages and add them to the runtimeconfig
@@ -313,7 +388,8 @@ export default (args) => {
313
388
  // See https://gohugo.io/content-management/menus/
314
389
  if (options.typeIdMenu && contentTypeId === options.typeIdMenu) {
315
390
  const { name = 'main' } = entry.fields;
316
- const menu = buildMenu(transformContext, options.menuDepth);
391
+ const menu = await buildMenu(transformContext, options.menuDepth);
392
+
317
393
  runtimeContext.menus[locale.code][name] = menu;
318
394
  }
319
395
 
@@ -331,26 +407,25 @@ export default (args) => {
331
407
 
332
408
  async after(runtimeContext) {
333
409
  const contentDir = runtimeContext.config.directory;
334
- const { toml } = runtimeContext.converter;
410
+ const { yaml } = runtimeContext.converter;
335
411
  const i18n = runtimeContext?.i18n ?? {};
336
412
 
337
413
  await Promise.all(
338
414
  Object.entries(i18n).map(async ([localeCode, translations]) => {
339
- const dictionaryPath = path.join(contentDir, `../i18n/${localeCode}.toml`);
415
+ const dictionaryPath = path.join(contentDir, `../i18n/${localeCode}.yaml`);
340
416
  const oldContent = existsSync(dictionaryPath)
341
- ? toml.parse(await readFile(dictionaryPath, 'utf8'))
417
+ ? yaml.parse(await readFile(dictionaryPath, 'utf8'))
342
418
  : {};
343
419
 
344
- return outputFile(dictionaryPath, toml.stringify({ ...oldContent, ...translations }));
420
+ return outputFile(dictionaryPath, yaml.stringify({ ...oldContent, ...translations }));
345
421
  })
346
422
  );
347
423
 
348
- const { stringify } = await import('@jungvonmatt/contentful-ssg/converter/toml');
349
424
  const menus = runtimeContext?.menus ?? {};
350
425
  await Promise.all(
351
426
  Object.entries(menus).map(([localeCode, menuData]) => {
352
- const file = `config/_default/menus.${localeCode}.toml`;
353
- const data = stringify(menuData);
427
+ const file = `config/_default/menus.${localeCode}.yaml`;
428
+ const data = yaml.stringify(menuData);
354
429
  return outputFile(file, data);
355
430
  })
356
431
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungvonmatt/cssg-plugin-hugo",
3
- "version": "1.2.0",
3
+ "version": "1.4.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "typings": "./index.d.ts",
@@ -20,16 +20,15 @@
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
- "merge-options": "^3.0.4",
32
31
  "micromatch": "^4.0.4"
33
32
  },
34
- "gitHead": "9e0182ef7511c6ba80f6332e6f726eaadafca915"
33
+ "gitHead": "1c29946e6b7bf40fff7be2e473b62887329b559a"
35
34
  }