@jungvonmatt/contentful-ssg 0.17.0 → 1.0.0-alpha.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 (134) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +113 -117
  3. package/dist/__test__/mock.d.ts +28 -0
  4. package/dist/__test__/mock.js +83 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +85 -0
  7. package/dist/converter/index.d.ts +7 -0
  8. package/dist/converter/index.js +42 -0
  9. package/dist/converter/json.d.ts +3 -0
  10. package/dist/converter/json.js +2 -0
  11. package/dist/converter/markdown.d.ts +3 -0
  12. package/dist/converter/markdown.js +17 -0
  13. package/dist/converter/toml.d.ts +3 -0
  14. package/dist/converter/toml.js +3 -0
  15. package/dist/converter/yaml.d.ts +3 -0
  16. package/dist/converter/yaml.js +27 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +130 -0
  19. package/dist/lib/array.d.ts +4 -0
  20. package/dist/lib/array.js +25 -0
  21. package/dist/lib/config.d.ts +3 -0
  22. package/dist/lib/config.js +132 -0
  23. package/dist/lib/contentful.d.ts +40 -0
  24. package/dist/lib/contentful.js +145 -0
  25. package/dist/lib/create-require.d.ts +2 -0
  26. package/dist/lib/create-require.js +4 -0
  27. package/dist/lib/error.d.ts +9 -0
  28. package/dist/lib/error.js +17 -0
  29. package/dist/lib/file-manager.d.ts +16 -0
  30. package/dist/lib/file-manager.js +55 -0
  31. package/dist/lib/hook-manager.d.ts +15 -0
  32. package/dist/lib/hook-manager.js +94 -0
  33. package/dist/lib/object.d.ts +11 -0
  34. package/dist/lib/object.js +40 -0
  35. package/dist/lib/stats.d.ts +18 -0
  36. package/dist/lib/stats.js +74 -0
  37. package/dist/lib/ui.d.ts +6 -0
  38. package/dist/lib/ui.js +88 -0
  39. package/dist/lib/utils.d.ts +4 -0
  40. package/dist/lib/utils.js +31 -0
  41. package/dist/mapper/map-date-field.d.ts +1 -0
  42. package/dist/mapper/map-date-field.js +6 -0
  43. package/dist/mapper/map-entry.d.ts +4 -0
  44. package/dist/mapper/map-entry.js +42 -0
  45. package/dist/mapper/map-field.d.ts +2 -0
  46. package/dist/mapper/map-field.js +24 -0
  47. package/dist/mapper/map-meta-fields.d.ts +9 -0
  48. package/dist/mapper/map-meta-fields.js +8 -0
  49. package/dist/mapper/map-reference-field.d.ts +11 -0
  50. package/dist/mapper/map-reference-field.js +71 -0
  51. package/dist/mapper/map-rich-text-field.d.ts +7 -0
  52. package/dist/mapper/map-rich-text-field.js +47 -0
  53. package/dist/tasks/fetch.d.ts +2 -0
  54. package/dist/tasks/fetch.js +12 -0
  55. package/dist/tasks/localize.d.ts +6 -0
  56. package/dist/tasks/localize.js +48 -0
  57. package/dist/tasks/setup.d.ts +2 -0
  58. package/dist/tasks/setup.js +26 -0
  59. package/dist/tasks/transform.d.ts +2 -0
  60. package/dist/tasks/transform.js +5 -0
  61. package/dist/tasks/write.d.ts +2 -0
  62. package/dist/tasks/write.js +33 -0
  63. package/dist/types.d.ts +194 -0
  64. package/dist/types.js +1 -0
  65. package/package.json +115 -91
  66. package/src/__test__/fixtures/assets.json +47 -0
  67. package/src/__test__/fixtures/content_types.json +181 -0
  68. package/src/__test__/fixtures/entries.json +783 -0
  69. package/src/__test__/fixtures/locales.json +35 -0
  70. package/src/__test__/fixtures/richtext.json +135 -0
  71. package/src/__test__/mock.ts +106 -0
  72. package/src/cli.ts +134 -0
  73. package/src/converter/index.test.ts +38 -0
  74. package/src/converter/index.ts +60 -0
  75. package/src/converter/json.test.ts +25 -0
  76. package/src/converter/json.ts +16 -0
  77. package/src/converter/markdown.test.ts +58 -0
  78. package/{lib/converter/markdown.js → src/converter/markdown.ts} +8 -10
  79. package/src/converter/toml.test.ts +74 -0
  80. package/src/converter/toml.ts +17 -0
  81. package/src/converter/yaml.test.ts +51 -0
  82. package/src/converter/yaml.ts +51 -0
  83. package/src/index.test.ts +89 -0
  84. package/src/index.ts +161 -0
  85. package/src/lib/array.test.ts +105 -0
  86. package/src/lib/array.ts +84 -0
  87. package/src/lib/config.ts +183 -0
  88. package/src/lib/contentful.test.ts +251 -0
  89. package/{lib/contentful.js → src/lib/contentful.ts} +99 -122
  90. package/src/lib/create-require.ts +6 -0
  91. package/src/lib/error.ts +19 -0
  92. package/src/lib/file-manager.test.ts +73 -0
  93. package/src/lib/file-manager.ts +79 -0
  94. package/src/lib/hook-manager.test.ts +169 -0
  95. package/src/lib/hook-manager.ts +161 -0
  96. package/src/lib/object.test.ts +56 -0
  97. package/src/lib/object.ts +102 -0
  98. package/src/lib/stats.test.ts +78 -0
  99. package/src/lib/stats.ts +112 -0
  100. package/src/lib/ui.test.ts +58 -0
  101. package/src/lib/ui.ts +128 -0
  102. package/src/lib/utils.test.ts +85 -0
  103. package/src/lib/utils.ts +57 -0
  104. package/src/mapper/map-date-field.ts +12 -0
  105. package/src/mapper/map-entry.test.ts +191 -0
  106. package/src/mapper/map-entry.ts +77 -0
  107. package/src/mapper/map-field.test.ts +577 -0
  108. package/src/mapper/map-field.ts +57 -0
  109. package/src/mapper/map-meta-fields.ts +17 -0
  110. package/src/mapper/map-reference-field.ts +122 -0
  111. package/src/mapper/map-rich-text-field.ts +107 -0
  112. package/src/tasks/fetch.test.ts +32 -0
  113. package/src/tasks/fetch.ts +17 -0
  114. package/src/tasks/localize.test.ts +83 -0
  115. package/src/tasks/localize.ts +95 -0
  116. package/src/tasks/setup.test.ts +33 -0
  117. package/src/tasks/setup.ts +31 -0
  118. package/src/tasks/transform.test.ts +26 -0
  119. package/src/tasks/transform.ts +11 -0
  120. package/src/tasks/write.test.ts +94 -0
  121. package/src/tasks/write.ts +49 -0
  122. package/src/types.ts +271 -0
  123. package/index.js +0 -90
  124. package/lib/array.js +0 -48
  125. package/lib/config.js +0 -142
  126. package/lib/converter/index.js +0 -63
  127. package/lib/converter/json.js +0 -20
  128. package/lib/converter/toml.js +0 -22
  129. package/lib/converter/yaml.js +0 -40
  130. package/lib/dump.js +0 -337
  131. package/lib/presets/grow.js +0 -88
  132. package/lib/transform/localize.js +0 -59
  133. package/lib/transform/mapper.js +0 -293
  134. package/lib/utils.js +0 -150
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 Jung von Matt
3
+ Copyright (c) 2021 Jung von Matt TECH (https://www.jvm.com/)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![NPM version][npm-image]][npm-url] [![Build Status][ci-image]][ci-url] [![dependencies Status][depstat-image]][depstat-url] [![devDependencies Status][devdepstat-image]][devdepstat-url] [![Coverage][coveralls-image]][coveralls-url]
1
+ [![NPM version][npm-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coverage][coveralls-image]][coveralls-url]
2
2
 
3
3
  # JvM Contentful export for static site generators
4
4
 
@@ -12,7 +12,7 @@ When a `.gitignore` file is found only ignored files are removed.
12
12
  ### Install
13
13
 
14
14
  ```bash
15
- npm i @jungvonmatt/contentful-ssg
15
+ npm install --save-dev @jungvonmatt/contentful-ssg
16
16
  ```
17
17
 
18
18
  ## Commands
@@ -31,29 +31,114 @@ npx cssg init
31
31
 
32
32
  Initializes contentful-ssg and stores the config values in the `contentful-ssg.config.js` file.
33
33
 
34
+ Contentful SSG ships with built in typescript support. Add `--typescript` to generate a typescript configuration file.
35
+
36
+ ```bash
37
+ npx cssg init --typescript
38
+ ```
39
+
34
40
  <!-- prettier-ignore -->
35
41
  #### Configuration values
36
42
 
37
- | Name | Type | Default | Description |
38
- | ------------------ | ------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
39
- | accessToken | `String` | `undefined` | Content Delivery API - access token |
40
- | previewAccessToken | `String` | `undefined` | Content Preview API - access token |
41
- | spaceId | `String` | `undefined` | Contentful Space id |
42
- | environmentId | `String` | `'master'` | Contentful Environment id |
43
- | format | `String`\|`Function`\|`Object` | `'yaml'` | File format ( `yaml`, `toml`, `md`, `json`) You can add a function returning the format or you can add a mapping object like `{yaml: [glob pattern]}` ([pattern](https://github.com/micromatch/micromatch) should match the directory) |
44
- | directory | `String` | `'./content'` | Base directory for content files. |
45
- | typeConfig | `Object` | `undefined` | Pass a map with e.g. grow's blueprint config ({<contenttypeid>: {$path: '...', $view: '...'}}) |
46
- | preset | `String` | `undefined` | Pass `grow` to enable generator specific addons |
47
- | validate | `Function` | `undefined` | Pass `function(content, { requiredFields, requiredFieldMissing, entry, contentType, locale, ... }){...}` to validate an entry. Return a 'falsy' value to skip the entry completely. Without a validate function entries where a required field is missing are skipped. |
48
- | transform | `Function` | `undefined` | Pass `function(content, { entry, contentType, locale, helper, ... }){...}` to modify the stored object. Return `undefined` to skip the entry completely. (no file will be written) |
49
- | mapDirectory | `Function` | `undefined` | Pass `function(contentType, { locale, helper })` to customize the directory per content-type relative to the base directory. |
50
- | mapFilename | `Function` | `undefined` | Pass `function(data, { locale, contentType, entry, format, helper })` to customize the filename per entry |
51
- | mapAssetLink | `Function` | `undefined` | Pass `function(asset){...}` to customize how asset links are stored |
52
- | mapEntryLink | `Function` | `undefined` | Pass `function(entry){...}` to customize how entry links are stored |
53
- | mapMetaFields | `Function` | `undefined` | Pass `function(entry, {contentType, spaceId, locale, helper, ... }){...}` to customize the meta fields per entry |
54
- | richTextRenderer | `Object`\|`Function` | `Boolean` | `{}` We use the contentful [`rich-text-html-renderer`](https://github.com/contentful/rich-text/tree/master/packages/rich-text-html-renderer) to render the html.<br/> You can pass a [configuration object](https://github.com/contentful/rich-text/tree/master/packages/rich-text-html-renderer#usage)<br/> or you can pass `function(document){...}` to use your own richtext renderer or you can turn it off to get a mirrored version of the JSON output |
55
- | before | `Function` | `undefined` | Runs `function(context){...}` before processing the content right after pulling data from contentful |
56
- | after | `Function` | `undefined` | Runs `function(context){...}` after processing the content right before the cleanup |
43
+ | Name | Type | Default | Description |
44
+ | ------------------ | ------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | accessToken | `String` | `undefined` | Content Delivery API - access token |
46
+ | previewAccessToken | `String` | `undefined` | Content Preview API - access token |
47
+ | spaceId | `String` | `undefined` | Contentful Space id |
48
+ | environmentId | `String` | `'master'` | Contentful Environment id |
49
+ | format | `String`\|`Function`\|`Object` | `'yaml'` | File format ( `yaml`, `toml`, `md`, `json`) You can add a function returning the format or you can add a mapping object like `{yaml: [glob pattern]}` ([pattern](https://github.com/micromatch/micromatch) should match the directory) |
50
+ | directory | `String` | `'./content'` | Base directory for content files. |
51
+ | validate | `Function` | `undefined` | Pass `function(transformContext, runtimeContext){...}` to validate an entry. Return `false` to skip the entry completely. Without a validate function entries with a missing required field are skipped. |
52
+ | transform | `Function` | `undefined` | Pass `function(transformContext, runtimeContext){...}` to modify the stored object. Return `undefined` to skip the entry completely. (no file will be written) |
53
+ | mapDirectory | `Function` | `undefined` | Pass `function(transformContext, runtimeContext, defaultValue){...}` to customize the directory per content-type relative to the base directory. |
54
+ | mapFilename | `Function` | `undefined` | Pass `function(transformContext, runtimeContext, defaultValue){...}` to customize the filename per entry |
55
+ | mapAssetLink | `Function` | `undefined` | Pass `function(transformContext, runtimeContext, defaultValue){...}` to customize how asset links are stored |
56
+ | mapEntryLink | `Function` | `undefined` | Pass `function(transformContext, runtimeContext, defaultValue){...}` to customize how entry links are stored |
57
+ | mapMetaFields | `Function` | `undefined` | Pass `function(transformContext, runtimeContext, defaultValue){...}` to customize the meta fields per entry |
58
+ | richTextRenderer | `Boolean`\|`Object`\|`Function` | `{}` | We use the contentful [`rich-text-html-renderer`](https://github.com/contentful/rich-text/tree/master/packages/rich-text-html-renderer) to render the html.<br/> You can pass a [configuration object](https://github.com/contentful/rich-text/tree/master/packages/rich-text-html-renderer#usage)<br/> or you can pass `function(document){...}` to use your own richtext renderer or you can turn it off by passing `false` to get a mirrored version of the JSON output |
59
+ | before | `Function` | `undefined` | Runs `function(runtimeContext){...}` before processing the content right after pulling data from contentful |
60
+ | after | `Function` | `undefined` | Runs `function(runtimeContext){...}` after processing the content right before the cleanup |
61
+
62
+
63
+ ### Runtime Hooks
64
+ **before**
65
+ ```js
66
+ import
67
+ (runtimeContext) => {
68
+ // Do things before processing the localized contentful entries
69
+ // The return value should be an object which is merged with the runtime context.
70
+ return { key: 'test' };
71
+ }
72
+ ```
73
+
74
+ **after**
75
+ ```js
76
+ (runtimeContext) => {
77
+ // Do things after processing the localized contentful entries before cleanup
78
+ // We have access to values added to the context in the before hook
79
+ console.log(runtimeContext.key) // -> 'test'
80
+ }
81
+ ```
82
+
83
+ ### Transform Hooks
84
+ **transform**
85
+ ```js
86
+ (transformContext, runtimeContext) => {
87
+ const {content} = transformContext;
88
+ // modify content and
89
+ // return object
90
+ return content;
91
+ }
92
+ ```
93
+
94
+ **mapFilename**
95
+ ```js
96
+ (transformContext, runtimeContext, defaultValue) => {
97
+ // customize the filename on entry level
98
+ // return string
99
+ return defaultValue;
100
+ }
101
+ ```
102
+
103
+ **mapDirectory**
104
+ ```js
105
+ (transformContext, runtimeContext, defaultValue) => {
106
+ // customize the directory on entry level
107
+ // return string
108
+ return defaultValue;
109
+ }
110
+ ```
111
+
112
+ **mapAssetLink**
113
+ ```js
114
+ (transformContext, runtimeContext, defaultValue) => {
115
+ const {asset} = transformContext;
116
+ // customize the asset representation in front matter
117
+ // return object
118
+ return { ...defaultValue, ... };
119
+ }
120
+ ```
121
+
122
+ **mapEntryLink**
123
+ ```js
124
+ (transformContext, runtimeContext, defaultValue) => {
125
+ const {entry} = transformContext;
126
+ // customize how the entry is added to your front matter
127
+ // return object
128
+ return { ...defaultValue, ... };
129
+ }
130
+ ```
131
+
132
+ **mapMetaFields**
133
+ ```js
134
+ (transformContext, runtimeContext, defaultValue) => {
135
+ const {entry} = transformContext;
136
+ // customize how the sys meta data is added to your front matter
137
+ // return object
138
+ return { ...defaultValue, ... };
139
+ }
140
+ ```
141
+
57
142
 
58
143
  #### Helper functions
59
144
 
@@ -63,9 +148,9 @@ Get values from linked pages to e.g. build an url out of parent slugs.
63
148
 
64
149
  ```js
65
150
  {
66
- transform: (content, options) => {
67
- const { helper } = options;
68
- const slugs = helper.collectValues('fields.slug', {
151
+ transform: (context) => {
152
+ const { utils } = context;
153
+ const slugs = utils.collectValues('fields.slug', {
69
154
  linkField: 'fields.parentPage',
70
155
  });
71
156
 
@@ -74,8 +159,6 @@ Get values from linked pages to e.g. build an url out of parent slugs.
74
159
  }
75
160
  ```
76
161
 
77
- _contentful-ssg.config.js_
78
-
79
162
  ###### collectParentValues
80
163
 
81
164
  The same as collectValues just without the value from the current entry
@@ -92,104 +175,17 @@ npx cssg fetch
92
175
 
93
176
  ### Grow
94
177
 
95
- ```js
96
- const path = require('path');
97
-
98
- module.exports = {
99
- spaceId: '...',
100
- environmentId: '...',
101
- accessToken: '...',
102
- previewAccessToken: '...',
103
- directory: 'content',
104
- preset: 'grow',
105
- mapDirectory: (contentType) => {
106
- switch (contentType.substr(0, 2)) {
107
- case 't-':
108
- return contentType;
109
- case 'o-':
110
- return path.join('partials/organisms', contentType);
111
- case 'm-':
112
- return path.join('partials/molecules', contentType);
113
- case 'a-':
114
- return path.join('partials/atoms', contentType);
115
- default:
116
- return path.join('partials', contentType);
117
- }
118
- },
119
- typeConfig: {
120
- 't-home': {
121
- view: '/views/t-home.html',
122
- path: '/{locale}/',
123
- },
124
- 't-article': {
125
- view: '/views/t-article.html',
126
- path: '/{locale}/{category}/{slug}/',
127
- },
128
- },
129
- };
130
- ```
178
+ See [`cssg-plugin-grow`](../cssg-plugin-grow)
131
179
 
132
- ### Hugo
133
180
 
134
- ```js
135
- const path = require('path');
136
-
137
- module.exports = {
138
- spaceId: '...',
139
- environmentId: '...',
140
- accessToken: '...',
141
- previewAccessToken: '...',
142
- directory: 'content',
143
- format: 'md',
144
- mapDirectory: function (contentType, { locale, helper }) {
145
- if (contentType === 't-home') {
146
- return '/';
147
- } else if (contentType.substr(0, 2) === 't-') {
148
- return 'pages';
149
- } else {
150
- return path.join('headlessBundles', contentType);
151
- }
152
- },
153
- mapFilename: function (data, { locale, contentType, entry, format, helper }) {
154
- if (contentType === 't-home') {
155
- return path.join('_index.' + locale.code + '.' + format);
156
- } else {
157
- return path.join(entry.sys.id + '/index.' + locale.code + '.' + format);
158
- }
159
- },
160
- transform: (content, options) => {
161
- const { helper } = options;
162
- const slugs = helper.collectValues('fields.slug', {
163
- linkField: 'fields.parentPage',
164
- });
165
- if (content.contentType === 't-home') {
166
- return { ...content };
167
- } else if (content.contentType.substr(0, 2) === 't-') {
168
- return { ...content, url: slugs.join('/') };
169
- } else {
170
- return { ...content, headless: true };
171
- }
172
- },
173
- };
174
- ```
175
-
176
- ## Demo
177
-
178
- ![Demo](https://github.com/jungvonmatt/contentful-ssg/blob/main/demo.gif?raw=true)
181
+ ### Hugo
179
182
 
180
- ## Can I contribute?
183
+ See [`cssg-plugin-hugo`](../cssg-plugin-hugo)
181
184
 
182
- Of course. We appreciate all of our [contributors](https://github.com/jungvonmatt/contentful-ssg/graphs/contributors) and
183
- welcome contributions to improve the project further. If you're uncertain whether an addition should be made, feel
184
- free to open up an issue and we can discuss it.
185
185
 
186
186
  [npm-url]: https://www.npmjs.com/package/@jungvonmatt/contentful-ssg
187
187
  [npm-image]: https://img.shields.io/npm/v/@jungvonmatt/contentful-ssg.svg
188
188
  [ci-url]: https://github.com/jungvonmatt/contentful-ssg/actions?workflow=Tests
189
189
  [ci-image]: https://github.com/jungvonmatt/contentful-ssg/workflows/Tests/badge.svg
190
- [depstat-url]: https://david-dm.org/jungvonmatt/contentful-ssg
191
- [depstat-image]: https://img.shields.io/david/jungvonmatt/contentful-ssg.svg
192
- [devdepstat-url]: https://david-dm.org/jungvonmatt/contentful-ssg?type=dev
193
- [devdepstat-image]: https://img.shields.io/david/dev/jungvonmatt/contentful-ssg.svg
194
190
  [coveralls-url]: https://coveralls.io/github/jungvonmatt/contentful-ssg?branch=main
195
191
  [coveralls-image]: https://coveralls.io/repos/github/jungvonmatt/contentful-ssg/badge.svg?branch=main
@@ -0,0 +1,28 @@
1
+ import type { Config, RuntimeContext, TransformContext } from '../types.js';
2
+ export declare const readFixture: (file: any) => Promise<any>;
3
+ export declare const readFixtureSync: (file: any) => any;
4
+ export declare const getContent: () => Promise<{
5
+ entries: any;
6
+ assets: any;
7
+ contentTypes: any;
8
+ locales: any;
9
+ assetLink: {
10
+ sys: {
11
+ id: string;
12
+ type: string;
13
+ linkType: string;
14
+ };
15
+ };
16
+ entryLink: {
17
+ sys: {
18
+ id: string;
19
+ type: string;
20
+ linkType: string;
21
+ };
22
+ };
23
+ entry: any;
24
+ asset: any;
25
+ }>;
26
+ export declare const getConfig: (fixture?: Partial<Config>) => Config;
27
+ export declare const getRuntimeContext: (fixture?: Partial<RuntimeContext>) => RuntimeContext;
28
+ export declare const getTransformContext: (fixture?: Partial<TransformContext>) => TransformContext;
@@ -0,0 +1,83 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { FIELD_TYPE_LINK, getFieldSettings, LINK_TYPE_ASSET, LINK_TYPE_ENTRY, } from '../lib/contentful.js';
4
+ import { FileManager } from '../lib/file-manager.js';
5
+ import { HookManager } from '../lib/hook-manager.js';
6
+ const cache = new Map();
7
+ export const readFixture = async (file) => {
8
+ if (!cache.has(file)) {
9
+ const content = await fs.readJSON(path.join(__dirname, 'fixtures', file));
10
+ cache.set(file, content);
11
+ }
12
+ return cache.get(file);
13
+ };
14
+ export const readFixtureSync = (file) => {
15
+ if (!cache.has(file)) {
16
+ const content = fs.readJSONSync(path.join(__dirname, 'fixtures', file));
17
+ cache.set(file, content);
18
+ }
19
+ return cache.get(file);
20
+ };
21
+ export const getContent = async () => {
22
+ const assets = await readFixture('assets.json');
23
+ const entries = await readFixture('entries.json');
24
+ const locales = await readFixture('locales.json');
25
+ const contentTypes = await readFixture('content_types.json');
26
+ const [entry] = entries;
27
+ const [asset] = assets;
28
+ const assetLink = {
29
+ sys: {
30
+ id: 'asset-id',
31
+ type: FIELD_TYPE_LINK,
32
+ linkType: LINK_TYPE_ASSET,
33
+ },
34
+ };
35
+ const entryLink = {
36
+ sys: {
37
+ id: 'entry-id',
38
+ type: FIELD_TYPE_LINK,
39
+ linkType: LINK_TYPE_ENTRY,
40
+ },
41
+ };
42
+ return { entries, assets, contentTypes, locales, assetLink, entryLink, entry, asset };
43
+ };
44
+ export const getConfig = (fixture = {}) => ({
45
+ directory: 'test',
46
+ plugins: [],
47
+ ...fixture,
48
+ });
49
+ export const getRuntimeContext = (fixture = {}) => {
50
+ const assets = readFixtureSync('assets.json');
51
+ const entries = readFixtureSync('entries.json');
52
+ const locales = readFixtureSync('locales.json');
53
+ const contentTypes = readFixtureSync('content_types.json');
54
+ const fieldSettings = getFieldSettings(contentTypes);
55
+ const { code: defaultLocale } = locales.find((locale) => locale.default) || locales[0];
56
+ const result = {
57
+ config: getConfig(),
58
+ localized: new Map(),
59
+ data: {
60
+ assets,
61
+ entries,
62
+ contentTypes,
63
+ locales,
64
+ fieldSettings,
65
+ },
66
+ defaultLocale,
67
+ ...fixture,
68
+ };
69
+ const hooks = new HookManager(result, result.config);
70
+ const fileManager = new FileManager({ directory: '/testbase' });
71
+ fileManager.cleanup = jest.fn();
72
+ fileManager.initialize = jest.fn();
73
+ fileManager.deleteFile = jest.fn();
74
+ fileManager.writeFile = jest.fn();
75
+ return { ...result, hooks, fileManager };
76
+ };
77
+ export const getTransformContext = (fixture = {}) => ({
78
+ assets: [],
79
+ entries: [],
80
+ assetMap: new Map(),
81
+ entryMap: new Map(),
82
+ ...fixture,
83
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ import path from 'path';
3
+ import chalk from 'chalk';
4
+ import { existsSync } from 'fs';
5
+ import { outputFile } from 'fs-extra';
6
+ import prettier from 'prettier';
7
+ import { Command } from 'commander';
8
+ import dotenv from 'dotenv';
9
+ import dotenvExpand from 'dotenv-expand';
10
+ import { logError, confirm, askAll, askMissing } from './lib/ui.js';
11
+ import { omitKeys } from './lib/object.js';
12
+ import { getConfig, getEnvironmentConfig } from './lib/config.js';
13
+ import { run } from './index.js';
14
+ const env = dotenv.config();
15
+ dotenvExpand(env);
16
+ const parseArgs = (cmd) => ({
17
+ environment: cmd.env,
18
+ preview: Boolean(cmd.preview),
19
+ verbose: Boolean(cmd.verbose),
20
+ });
21
+ const errorHandler = (error, silence) => {
22
+ if (!silence) {
23
+ const { errors } = error;
24
+ logError(error);
25
+ (errors || []).forEach((error) => {
26
+ logError(error);
27
+ });
28
+ }
29
+ process.exit(1);
30
+ };
31
+ const actionRunner = (fn, log = true) => (...args) => fn(...args).catch((error) => errorHandler(error, !log));
32
+ const program = new Command();
33
+ program
34
+ .command('init')
35
+ .description('Initialize contentful-ssg')
36
+ .option('--typescript', 'Initialize typescript config')
37
+ .action(actionRunner(async (cmd) => {
38
+ const useTypescript = Boolean(cmd?.typescript ?? false);
39
+ const config = await getConfig(parseArgs(cmd || {}));
40
+ const verified = await askAll(config);
41
+ const environmentConfig = getEnvironmentConfig();
42
+ const filePath = path.join(process.cwd(), `contentful-ssg.config.${useTypescript ? 'ts' : 'js'}`);
43
+ const prettierOptions = await prettier.resolveConfig(filePath);
44
+ if (verified.directory?.startsWith('/')) {
45
+ verified.directory = path.relative(process.cwd(), verified.directory);
46
+ }
47
+ const environmentKeys = Object.keys(environmentConfig).filter((key) => environmentConfig[key] === verified[key]);
48
+ const cleanedConfig = omitKeys(verified, 'preview', 'verbose', 'rootDir', 'resolvedPlugins', 'host', 'managementToken', ...environmentKeys);
49
+ let content = '';
50
+ if (useTypescript) {
51
+ content = prettier.format(`import {Config} from '@jungvonmatt/contentful-ssg';
52
+ export default <Config>${JSON.stringify(cleanedConfig)}`, {
53
+ parser: 'typescript',
54
+ ...prettierOptions,
55
+ });
56
+ }
57
+ else {
58
+ content = prettier.format(`module.exports = ${JSON.stringify(cleanedConfig)}`, {
59
+ parser: 'babel',
60
+ ...prettierOptions,
61
+ });
62
+ }
63
+ let writeFile = true;
64
+ if (existsSync(filePath)) {
65
+ writeFile = await confirm(`Config file already exists. Overwrite?\n\n${chalk.reset(content)}`);
66
+ }
67
+ else {
68
+ writeFile = await confirm(`Please verify your settings:\n\n${chalk.reset(content)}`, true);
69
+ }
70
+ if (writeFile) {
71
+ await outputFile(filePath, content);
72
+ console.log(`\nConfiguration saved to ${chalk.cyan(path.relative(process.cwd(), filePath))}`);
73
+ }
74
+ }));
75
+ program
76
+ .command('fetch')
77
+ .description('Fetch content objects')
78
+ .option('-p, --preview', 'Fetch with preview mode')
79
+ .option('-v, --verbose', 'Verbose output')
80
+ .action(actionRunner(async (cmd) => {
81
+ const config = await getConfig(parseArgs(cmd || {}));
82
+ const verified = await askMissing(config);
83
+ return run(verified);
84
+ }));
85
+ program.parse(process.argv);
@@ -0,0 +1,7 @@
1
+ import { KeyValueMap } from '../types.js';
2
+ export declare const TYPE_JSON = "json";
3
+ export declare const TYPE_YAML = "yaml";
4
+ export declare const TYPE_MARKDOWN = "markdown";
5
+ export declare const TYPE_TOML = "toml";
6
+ export declare const stringify: <T = KeyValueMap<any>>(obj: T, format?: string) => string;
7
+ export declare const parse: (str: string, format?: string) => KeyValueMap<any>;
@@ -0,0 +1,42 @@
1
+ import { stringify as stringifyJson, parse as parseJson } from './json.js';
2
+ import { stringify as stringifyMarkdown, parse as parseMarkdown } from './markdown.js';
3
+ import { stringify as stringifyToml, parse as parseToml } from './toml.js';
4
+ import { stringify as stringifyYaml, parse as parseYaml } from './yaml.js';
5
+ export const TYPE_JSON = 'json';
6
+ export const TYPE_YAML = 'yaml';
7
+ export const TYPE_MARKDOWN = 'markdown';
8
+ export const TYPE_TOML = 'toml';
9
+ export const stringify = (obj, format = 'yaml') => {
10
+ switch (format) {
11
+ case 'yml':
12
+ case TYPE_YAML:
13
+ return stringifyYaml(obj);
14
+ case 'md':
15
+ case TYPE_MARKDOWN:
16
+ return stringifyMarkdown(obj);
17
+ case TYPE_JSON:
18
+ return stringifyJson(obj);
19
+ case TYPE_TOML:
20
+ return stringifyToml(obj);
21
+ default:
22
+ throw new Error(`Format ${JSON.stringify(format)} is not supported`);
23
+ }
24
+ };
25
+ export const parse = (str, format = 'yaml') => {
26
+ switch (format) {
27
+ case 'yml':
28
+ case TYPE_YAML:
29
+ return parseYaml(str);
30
+ case 'md':
31
+ case TYPE_MARKDOWN: {
32
+ const { data } = parseMarkdown(str);
33
+ return data;
34
+ }
35
+ case TYPE_JSON:
36
+ return parseJson(str);
37
+ case TYPE_TOML:
38
+ return parseToml(str);
39
+ default:
40
+ throw new Error(`Format "${format}" is not supported`);
41
+ }
42
+ };
@@ -0,0 +1,3 @@
1
+ import { KeyValueMap } from '../types';
2
+ export declare const stringify: <T = KeyValueMap<any>>(obj: T) => string;
3
+ export declare const parse: <T = KeyValueMap<any>>(string: string) => T;
@@ -0,0 +1,2 @@
1
+ export const stringify = (obj) => JSON.stringify(obj, null, ' ');
2
+ export const parse = (string) => JSON.parse(string);
@@ -0,0 +1,3 @@
1
+ import { KeyValueMap } from '../types.js';
2
+ export declare const stringify: <T = KeyValueMap<any>>(obj: T, content?: string) => string;
3
+ export declare const parse: <T = KeyValueMap<any>>(string: string) => T;
@@ -0,0 +1,17 @@
1
+ import matter from 'gray-matter';
2
+ import { stringify as stringifyYaml, parse as parseYaml } from './yaml.js';
3
+ export const stringify = (obj, content = '') => {
4
+ let frontMatter = '';
5
+ frontMatter += '---\n';
6
+ frontMatter += stringifyYaml(obj);
7
+ frontMatter += `---\n${content}`;
8
+ return frontMatter;
9
+ };
10
+ export const parse = (string) => {
11
+ const data = matter(string, {
12
+ engines: {
13
+ yaml: (string) => parseYaml(string),
14
+ },
15
+ });
16
+ return data;
17
+ };
@@ -0,0 +1,3 @@
1
+ import { KeyValueMap } from '../types';
2
+ export declare const stringify: <T = KeyValueMap<any>>(obj: T) => string;
3
+ export declare const parse: <T = KeyValueMap<any>>(string: string) => T;
@@ -0,0 +1,3 @@
1
+ import TOML from '@iarna/toml';
2
+ export const stringify = (obj) => TOML.stringify(obj);
3
+ export const parse = (string) => TOML.parse(string);
@@ -0,0 +1,3 @@
1
+ import { KeyValueMap } from '../types';
2
+ export declare const stringify: <T = KeyValueMap<any>>(obj: T) => string;
3
+ export declare const parse: <T = KeyValueMap<any>>(obj: string) => T;
@@ -0,0 +1,27 @@
1
+ import yaml from 'js-yaml';
2
+ const getPredicate = (type) => (data) => typeof data === 'string' && data.startsWith(`${type} `);
3
+ const getRepresent = (type) => (data) => typeof data === 'string' ? data.replace(`${type} `, '') : JSON.stringify(data);
4
+ const getConstruct = (type) => (data) => typeof data === 'string' ? `${type} ${data}` : `${type} ${typeof data}`;
5
+ const growYamlConstructors = [
6
+ '!g.csv',
7
+ '!g.doc',
8
+ '!g.json',
9
+ '!g.static',
10
+ '!g.string',
11
+ '!g.url',
12
+ '!g.yaml',
13
+ ];
14
+ const growYamlTypes = growYamlConstructors.map((type) => new yaml.Type(type, {
15
+ kind: 'scalar',
16
+ predicate: getPredicate(type),
17
+ represent: getRepresent(type),
18
+ construct: getConstruct(type),
19
+ }));
20
+ export const stringify = (obj) => {
21
+ const GROW_SCHEMA = yaml.DEFAULT_SCHEMA.extend(growYamlTypes);
22
+ return yaml.dump(obj, { schema: GROW_SCHEMA });
23
+ };
24
+ export const parse = (obj) => {
25
+ const GROW_SCHEMA = yaml.DEFAULT_SCHEMA.extend(growYamlTypes);
26
+ return yaml.load(obj, { schema: GROW_SCHEMA });
27
+ };
@@ -0,0 +1,2 @@
1
+ import type { Config } from './types.js';
2
+ export declare const run: (config: Config) => Promise<void>;