@jungvonmatt/contentful-migrations 6.2.6 → 7.0.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.
- package/README.md +22 -11
- package/cli.js +314 -171
- package/index.d.ts +9 -9
- package/lib/backend.js +146 -102
- package/lib/content.js +5 -5
- package/lib/contentful.js +90 -43
- package/lib/diff.js +46 -32
- package/lib/helpers/locale.d.ts +3 -3
- package/lib/helpers/validation.d.ts +94 -15
- package/lib/helpers/validation.js +4 -4
- package/lib/migration.js +64 -51
- package/package.json +54 -117
- package/lib/helpers/validation.test.js +0 -381
- package/lib/helpers/validation.utils.test.js +0 -45
package/lib/diff.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
const Diff = require(
|
|
2
|
-
const {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const Diff = require("diff");
|
|
2
|
+
const {
|
|
3
|
+
documentToPlainTextString,
|
|
4
|
+
} = require("@contentful/rich-text-plain-text-renderer");
|
|
5
|
+
const pc = require("picocolors");
|
|
6
|
+
const microdiff = require("microdiff").default;
|
|
5
7
|
const {
|
|
6
8
|
getContentId,
|
|
7
9
|
getContentTypeId,
|
|
@@ -16,13 +18,19 @@ const {
|
|
|
16
18
|
TYPE_ARRAY,
|
|
17
19
|
TYPE_BOOLEAN,
|
|
18
20
|
TYPE_LINK,
|
|
19
|
-
} = require(
|
|
21
|
+
} = require("./contentful");
|
|
20
22
|
|
|
21
23
|
const getNodeDate = (node) => {
|
|
22
24
|
const { sys } = node || {};
|
|
23
25
|
const { updatedAt } = sys || {};
|
|
24
26
|
const date = new Date(updatedAt);
|
|
25
|
-
const options = {
|
|
27
|
+
const options = {
|
|
28
|
+
hour: "2-digit",
|
|
29
|
+
minute: "2-digit",
|
|
30
|
+
year: "numeric",
|
|
31
|
+
month: "2-digit",
|
|
32
|
+
day: "numeric",
|
|
33
|
+
};
|
|
26
34
|
const { locale } = Intl.DateTimeFormat().resolvedOptions();
|
|
27
35
|
|
|
28
36
|
return date.toLocaleDateString(locale, options);
|
|
@@ -51,7 +59,7 @@ const diffString = (source, dest) => {
|
|
|
51
59
|
return unchangedColor(part.value);
|
|
52
60
|
});
|
|
53
61
|
|
|
54
|
-
return parts.join(
|
|
62
|
+
return parts.join("");
|
|
55
63
|
};
|
|
56
64
|
|
|
57
65
|
const getField = (fieldId, contentType) => {
|
|
@@ -69,7 +77,7 @@ const getFieldValue = (fieldId, node) => {
|
|
|
69
77
|
|
|
70
78
|
const normalizeArray = (array) =>
|
|
71
79
|
(array || []).map((val) => {
|
|
72
|
-
if (typeof val ===
|
|
80
|
+
if (typeof val === "string") {
|
|
73
81
|
return val;
|
|
74
82
|
}
|
|
75
83
|
|
|
@@ -80,13 +88,15 @@ const normalizeArray = (array) =>
|
|
|
80
88
|
|
|
81
89
|
const diff = (source, dest, contentTypes) => {
|
|
82
90
|
const id = getContentId(dest);
|
|
83
|
-
const contentType = (contentTypes || []).find(
|
|
91
|
+
const contentType = (contentTypes || []).find(
|
|
92
|
+
(ct) => getContentId(ct) === getContentTypeId(dest),
|
|
93
|
+
);
|
|
84
94
|
const { fields: contentTypeFields } = contentType || {};
|
|
85
95
|
const { fields: sourceFields } = source || {};
|
|
86
96
|
const { fields: destFields } = dest || {};
|
|
87
|
-
const diffResult =
|
|
97
|
+
const diffResult = microdiff(destFields || {}, sourceFields || {});
|
|
88
98
|
|
|
89
|
-
if (
|
|
99
|
+
if (diffResult.length === 0) {
|
|
90
100
|
return;
|
|
91
101
|
}
|
|
92
102
|
|
|
@@ -100,23 +110,27 @@ const diff = (source, dest, contentTypes) => {
|
|
|
100
110
|
const [fieldId] = path || [];
|
|
101
111
|
return fieldId;
|
|
102
112
|
})
|
|
103
|
-
.filter((v) => v)
|
|
113
|
+
.filter((v) => v),
|
|
104
114
|
),
|
|
105
115
|
];
|
|
106
116
|
|
|
107
117
|
// console.log(contentType);
|
|
108
118
|
const fieldData = changedFields.map((fieldId) => {
|
|
109
|
-
const { name = fieldId } =
|
|
119
|
+
const { name = fieldId } =
|
|
120
|
+
(contentTypeFields || []).find(({ id }) => id === fieldId) || {};
|
|
110
121
|
const field = getField(fieldId, contentType);
|
|
111
122
|
const { type, linkType } = field || {};
|
|
112
123
|
const sourceValue = getFieldValue(fieldId, source);
|
|
113
124
|
const destValue = getFieldValue(fieldId, dest);
|
|
114
125
|
if (type === TYPE_SYMBOL) {
|
|
115
|
-
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue ||
|
|
126
|
+
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue || ""}`, `${sourceValue || ""}`))}`;
|
|
116
127
|
}
|
|
117
128
|
if (type === TYPE_RICHTEXT) {
|
|
118
129
|
return `${pc.bold(name)}: ${pc.reset(
|
|
119
|
-
diffString(
|
|
130
|
+
diffString(
|
|
131
|
+
documentToPlainTextString(destValue || {}),
|
|
132
|
+
documentToPlainTextString(sourceValue || {}),
|
|
133
|
+
),
|
|
120
134
|
)}`;
|
|
121
135
|
}
|
|
122
136
|
if (type === TYPE_NUMBER) {
|
|
@@ -124,26 +138,26 @@ const diff = (source, dest, contentTypes) => {
|
|
|
124
138
|
return `${pc.bold(name)}: ${oldValueColor(`${destValue}`)} ${newValueColor(`${sourceValue}`)}`;
|
|
125
139
|
}
|
|
126
140
|
if (type === TYPE_TEXT) {
|
|
127
|
-
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue ||
|
|
141
|
+
return `${pc.bold(name)}: ${pc.reset(diffString(`${destValue || ""}`, `${sourceValue || ""}`))}`;
|
|
128
142
|
}
|
|
129
143
|
if (type === TYPE_DATE) {
|
|
130
|
-
return `${pc.bold(name)}: ${oldValueColor(`${destValue ||
|
|
144
|
+
return `${pc.bold(name)}: ${oldValueColor(`${destValue || ""}`)} ${newValueColor(`${sourceValue || ""}`)}`;
|
|
131
145
|
}
|
|
132
146
|
if (type === TYPE_LOCATION) {
|
|
133
|
-
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue ||
|
|
134
|
-
`${JSON.stringify(sourceValue ||
|
|
147
|
+
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue || "")}`)} ${newValueColor(
|
|
148
|
+
`${JSON.stringify(sourceValue || "")}`,
|
|
135
149
|
)}`;
|
|
136
150
|
}
|
|
137
151
|
if (type === TYPE_BOOLEAN) {
|
|
138
152
|
return `${pc.bold(name)}: ${oldValueColor(`${JSON.stringify(destValue)}`)} ${newValueColor(
|
|
139
|
-
`${JSON.stringify(sourceValue)}
|
|
153
|
+
`${JSON.stringify(sourceValue)}`,
|
|
140
154
|
)}`;
|
|
141
155
|
}
|
|
142
156
|
if (type === TYPE_LINK) {
|
|
143
157
|
const sourceValueId = getContentId(sourceValue || {});
|
|
144
158
|
const destValueId = getContentId(destValue || {});
|
|
145
|
-
return `${pc.bold(name)} (${linkType}): ${oldValueColor(destValueId ||
|
|
146
|
-
sourceValueId ||
|
|
159
|
+
return `${pc.bold(name)} (${linkType}): ${oldValueColor(destValueId || "deleted")} ${newValueColor(
|
|
160
|
+
sourceValueId || "empty",
|
|
147
161
|
)}`;
|
|
148
162
|
}
|
|
149
163
|
|
|
@@ -158,24 +172,24 @@ const diff = (source, dest, contentTypes) => {
|
|
|
158
172
|
const destArray = normalizeArray(destValue);
|
|
159
173
|
|
|
160
174
|
const array = Array(Math.max(sourceArray.length, destArray.length))
|
|
161
|
-
.fill(
|
|
175
|
+
.fill("")
|
|
162
176
|
.map((_, index) => {
|
|
163
177
|
if (sourceArray[index] === destArray[index]) {
|
|
164
178
|
return unchangedColor(sourceArray[index]);
|
|
165
179
|
}
|
|
166
180
|
|
|
167
181
|
if (sourceArray[index] && destArray[index]) {
|
|
168
|
-
return `${oldValueColor(destArray[index] ||
|
|
182
|
+
return `${oldValueColor(destArray[index] || "")} ${newValueColor(sourceArray[index])}`;
|
|
169
183
|
}
|
|
170
184
|
|
|
171
185
|
if (destArray[index]) {
|
|
172
186
|
return `${newValueColor(sourceArray[index])}`;
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
return `${oldValueColor(destArray[index] ||
|
|
189
|
+
return `${oldValueColor(destArray[index] || "")}`;
|
|
176
190
|
});
|
|
177
191
|
|
|
178
|
-
return `${pc.bold(name)}: [\n ${array.join(
|
|
192
|
+
return `${pc.bold(name)}: [\n ${array.join("\n ")}\n ]`;
|
|
179
193
|
}
|
|
180
194
|
|
|
181
195
|
return name;
|
|
@@ -185,20 +199,20 @@ const diff = (source, dest, contentTypes) => {
|
|
|
185
199
|
const sourceEnv = getEnvironmentId(source);
|
|
186
200
|
|
|
187
201
|
return {
|
|
188
|
-
type:
|
|
189
|
-
message: `${pc.reset(
|
|
202
|
+
type: "list",
|
|
203
|
+
message: `${pc.reset("Conflict on")} ${pc.cyan(name)}\n ${pc.reset(`${fieldData.join("\n ")}`)}\n`,
|
|
190
204
|
|
|
191
205
|
name: id,
|
|
192
206
|
choices: [
|
|
193
207
|
{
|
|
194
|
-
name: `Use ${oldValueColor(destEnv)} - updated on ${getNodeDate(dest)} ${pc.bold(
|
|
208
|
+
name: `Use ${oldValueColor(destEnv)} - updated on ${getNodeDate(dest)} ${pc.bold("(skip)")}`,
|
|
195
209
|
value: false,
|
|
196
|
-
short:
|
|
210
|
+
short: "skip",
|
|
197
211
|
},
|
|
198
212
|
{
|
|
199
|
-
name: `Use ${newValueColor(sourceEnv)} - updated on ${getNodeDate(source)} ${pc.bold(
|
|
213
|
+
name: `Use ${newValueColor(sourceEnv)} - updated on ${getNodeDate(source)} ${pc.bold("(overwrite)")}`,
|
|
200
214
|
value: true,
|
|
201
|
-
short:
|
|
215
|
+
short: "overwrite",
|
|
202
216
|
},
|
|
203
217
|
],
|
|
204
218
|
};
|
package/lib/helpers/locale.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Locale } from
|
|
2
|
-
import type Migration from
|
|
3
|
-
import type { MigrationContext } from
|
|
1
|
+
import type { Locale } from 'contentful-management/dist/typings/export-types';
|
|
2
|
+
import type Migration from 'contentful-migration';
|
|
3
|
+
import type { MigrationContext } from 'contentful-migration';
|
|
4
4
|
|
|
5
5
|
export interface LocaleHelpers {
|
|
6
6
|
getLocales(): Promise<Locale[]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type Migration from
|
|
2
|
-
import type { MigrationContext } from
|
|
1
|
+
import type Migration from 'contentful-migration';
|
|
2
|
+
import type { MigrationContext } from 'contentful-migration';
|
|
3
3
|
|
|
4
4
|
export type ValueMappingFunction<T extends string = string> = (values: T[]) => T[];
|
|
5
5
|
|
|
@@ -11,31 +11,110 @@ export interface AddValuesOptions {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// define some known mark and node values here to support code completion, but allow any string as well
|
|
14
|
-
export type RichTextMarks =
|
|
15
|
-
|
|
14
|
+
export type RichTextMarks =
|
|
15
|
+
| 'bold'
|
|
16
|
+
| 'italic'
|
|
17
|
+
| 'underline'
|
|
18
|
+
| 'code'
|
|
19
|
+
| 'superscript'
|
|
20
|
+
| 'subscript'
|
|
21
|
+
| 'strikethrough'
|
|
22
|
+
| (string & {});
|
|
23
|
+
export type RichTextNodeType =
|
|
24
|
+
| 'document'
|
|
25
|
+
| 'paragraph'
|
|
26
|
+
| 'heading-1'
|
|
27
|
+
| 'heading-2'
|
|
28
|
+
| 'heading-3'
|
|
29
|
+
| 'heading-4'
|
|
30
|
+
| 'heading-5'
|
|
31
|
+
| 'heading-6'
|
|
32
|
+
| 'ordered-list'
|
|
33
|
+
| 'unordered-list'
|
|
34
|
+
| 'list-item'
|
|
35
|
+
| 'hr'
|
|
36
|
+
| 'blockquote'
|
|
37
|
+
| 'embedded-entry-block'
|
|
38
|
+
| 'embedded-asset-block'
|
|
39
|
+
| 'embedded-resource-block'
|
|
40
|
+
| 'table'
|
|
41
|
+
| 'table-row'
|
|
42
|
+
| 'table-cell'
|
|
43
|
+
| 'table-header-cell'
|
|
44
|
+
| 'asset-hyperlink'
|
|
45
|
+
| 'embedded-entry-inline'
|
|
46
|
+
| 'embedded-resource-inline'
|
|
47
|
+
| 'entry-hyperlink'
|
|
48
|
+
| 'hyperlink'
|
|
49
|
+
| 'resource-hyperlink'
|
|
50
|
+
| (string & {});
|
|
16
51
|
|
|
17
|
-
export type RichTextLinkedNodeType = 'entry-hyperlink' | 'embedded-entry-block' | 'embedded-entry-inline'
|
|
52
|
+
export type RichTextLinkedNodeType = 'entry-hyperlink' | 'embedded-entry-block' | 'embedded-entry-inline';
|
|
18
53
|
|
|
19
54
|
export interface RichTextValidationHelpers {
|
|
20
55
|
addEnabledMarksValues(contentTypeId: string, fieldId: string, values: RichTextMarks | RichTextMarks[]): Promise<void>;
|
|
21
|
-
removeEnabledMarksValues(
|
|
22
|
-
|
|
56
|
+
removeEnabledMarksValues(
|
|
57
|
+
contentTypeId: string,
|
|
58
|
+
fieldId: string,
|
|
59
|
+
values: RichTextMarks | RichTextMarks[],
|
|
60
|
+
): Promise<void>;
|
|
61
|
+
modifyEnabledMarksValues(
|
|
62
|
+
contentTypeId: string,
|
|
63
|
+
fieldId: string,
|
|
64
|
+
valueMappingFunction: ValueMappingFunction<RichTextMarks>,
|
|
65
|
+
): Promise<void>;
|
|
23
66
|
|
|
24
|
-
addEnabledNodeTypeValues(
|
|
25
|
-
|
|
26
|
-
|
|
67
|
+
addEnabledNodeTypeValues(
|
|
68
|
+
contentTypeId: string,
|
|
69
|
+
fieldId: string,
|
|
70
|
+
values: RichTextNodeType | RichTextNodeType[],
|
|
71
|
+
): Promise<void>;
|
|
72
|
+
removeEnabledNodeTypeValues(
|
|
73
|
+
contentTypeId: string,
|
|
74
|
+
fieldId: string,
|
|
75
|
+
values: RichTextNodeType | RichTextNodeType[],
|
|
76
|
+
): Promise<void>;
|
|
77
|
+
modifyEnabledNodeTypeValues(
|
|
78
|
+
contentTypeId: string,
|
|
79
|
+
fieldId: string,
|
|
80
|
+
valueMappingFunction: ValueMappingFunction<RichTextNodeType>,
|
|
81
|
+
): Promise<void>;
|
|
27
82
|
|
|
28
|
-
addNodeContentTypeValues(
|
|
29
|
-
|
|
30
|
-
|
|
83
|
+
addNodeContentTypeValues(
|
|
84
|
+
contentTypeId: string,
|
|
85
|
+
fieldId: string,
|
|
86
|
+
nodeType: RichTextLinkedNodeType,
|
|
87
|
+
values: string | string[],
|
|
88
|
+
): Promise<void>;
|
|
89
|
+
removeNodeContentTypeValues(
|
|
90
|
+
contentTypeId: string,
|
|
91
|
+
fieldId: string,
|
|
92
|
+
nodeType: RichTextLinkedNodeType,
|
|
93
|
+
values: string | string[],
|
|
94
|
+
): Promise<void>;
|
|
95
|
+
modifyNodeContentTypeValues(
|
|
96
|
+
contentTypeId: string,
|
|
97
|
+
fieldId: string,
|
|
98
|
+
nodeType: RichTextLinkedNodeType,
|
|
99
|
+
valueMappingFunction: ValueMappingFunction,
|
|
100
|
+
): Promise<void>;
|
|
31
101
|
}
|
|
32
102
|
|
|
33
103
|
export interface ValidationHelpers {
|
|
34
104
|
addLinkContentTypeValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
35
105
|
removeLinkContentTypeValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
36
|
-
modifyLinkContentTypeValues(
|
|
106
|
+
modifyLinkContentTypeValues(
|
|
107
|
+
contentTypeId: string,
|
|
108
|
+
fieldId: string,
|
|
109
|
+
valueMappingFunction: ValueMappingFunction,
|
|
110
|
+
): Promise<void>;
|
|
37
111
|
|
|
38
|
-
addInValues(
|
|
112
|
+
addInValues(
|
|
113
|
+
contentTypeId: string,
|
|
114
|
+
fieldId: string,
|
|
115
|
+
values: string | string[],
|
|
116
|
+
options?: AddValuesOptions,
|
|
117
|
+
): Promise<void>;
|
|
39
118
|
removeInValues(contentTypeId: string, fieldId: string, values: string | string[]): Promise<void>;
|
|
40
119
|
modifyInValues(contentTypeId: string, fieldId: string, valueMappingFunction: ValueMappingFunction): Promise<void>;
|
|
41
120
|
|
|
@@ -172,7 +172,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
172
172
|
removeValidationValues,
|
|
173
173
|
contentTypeId,
|
|
174
174
|
fieldId,
|
|
175
|
-
values
|
|
175
|
+
values,
|
|
176
176
|
);
|
|
177
177
|
},
|
|
178
178
|
async modifyEnabledNodeTypeValues(contentTypeId, fieldId, valueMappingFunction) {
|
|
@@ -186,7 +186,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
186
186
|
addValidationValues,
|
|
187
187
|
contentTypeId,
|
|
188
188
|
fieldId,
|
|
189
|
-
values
|
|
189
|
+
values,
|
|
190
190
|
);
|
|
191
191
|
},
|
|
192
192
|
async removeNodeContentTypeValues(contentTypeId, fieldId, nodeType, values) {
|
|
@@ -195,7 +195,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
195
195
|
removeValidationValues,
|
|
196
196
|
contentTypeId,
|
|
197
197
|
fieldId,
|
|
198
|
-
values
|
|
198
|
+
values,
|
|
199
199
|
);
|
|
200
200
|
},
|
|
201
201
|
async modifyNodeContentTypeValues(contentTypeId, fieldId, nodeType, valueMappingFunction) {
|
|
@@ -205,7 +205,7 @@ const getValidationHelpers = (migration, context) => {
|
|
|
205
205
|
uniqueMappingFunction,
|
|
206
206
|
contentTypeId,
|
|
207
207
|
fieldId,
|
|
208
|
-
[]
|
|
208
|
+
[],
|
|
209
209
|
);
|
|
210
210
|
},
|
|
211
211
|
},
|
package/lib/migration.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const
|
|
3
|
-
const path = require(
|
|
4
|
-
const { stripIndent } = require(
|
|
5
|
-
const runMigration = require(
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const { format: oxfmt } = require("oxfmt");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { stripIndent } = require("common-tags");
|
|
5
|
+
const { runMigration } = require("contentful-migration");
|
|
6
6
|
const {
|
|
7
7
|
getContentTypes,
|
|
8
8
|
generateMigrationScript,
|
|
9
|
-
} = require(
|
|
10
|
-
const pc = require(
|
|
11
|
-
const { getEnvironment, getOrganizationId } = require(
|
|
9
|
+
} = require("contentful-cli/dist/lib/cmds/space_cmds/generate_cmds/migration");
|
|
10
|
+
const pc = require("picocolors");
|
|
11
|
+
const { getEnvironment, getOrganizationId } = require("./contentful");
|
|
12
12
|
|
|
13
|
-
const { confirm, STATE_SUCCESS, STATE_FAILURE } = require(
|
|
13
|
+
const { confirm, STATE_SUCCESS, STATE_FAILURE } = require("./config");
|
|
14
14
|
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
storeMigration,
|
|
17
|
+
getNewMigrations,
|
|
18
|
+
getVersionFromFile,
|
|
19
|
+
} = require("./backend");
|
|
16
20
|
|
|
17
21
|
const migrationHeader = stripIndent`/* eslint-env node */
|
|
18
22
|
const { withHelpers } = require('@jungvonmatt/contentful-migrations');
|
|
@@ -33,42 +37,30 @@ const migrationHeader = stripIndent`/* eslint-env node */
|
|
|
33
37
|
const createMigration = async (config) => {
|
|
34
38
|
let module = false;
|
|
35
39
|
try {
|
|
36
|
-
const { readPackageUp } = await import(
|
|
40
|
+
const { readPackageUp } = await import("read-package-up");
|
|
37
41
|
const data = await readPackageUp();
|
|
38
|
-
module = data?.packageJson?.type ===
|
|
42
|
+
module = data?.packageJson?.type === "module";
|
|
39
43
|
} catch (err) {
|
|
40
44
|
console.log(err);
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
const { directory } = config || {};
|
|
44
48
|
const timestamp = Date.now();
|
|
45
|
-
const filename = path.join(
|
|
49
|
+
const filename = path.join(
|
|
50
|
+
directory,
|
|
51
|
+
`${timestamp}-migration.${module ? "cjs" : "js"}`,
|
|
52
|
+
);
|
|
46
53
|
const content = stripIndent`${migrationHeader}
|
|
47
54
|
// Add your migration code here
|
|
48
55
|
})`;
|
|
49
56
|
|
|
50
|
-
await fs.outputFile(filename, await format(filename, content
|
|
57
|
+
await fs.outputFile(filename, await format(filename, content));
|
|
51
58
|
console.log(`Generated new migration file to ${pc.green(filename)}`);
|
|
52
59
|
};
|
|
53
60
|
|
|
54
|
-
const format = async (file, content
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return prettier.format(content, {
|
|
58
|
-
parser: 'babel',
|
|
59
|
-
...prettierOptions,
|
|
60
|
-
});
|
|
61
|
-
} catch (error) {
|
|
62
|
-
if (config?.verbose) {
|
|
63
|
-
console.log(pc.red('Error resolving prettier config. Using default settings.'));
|
|
64
|
-
console.log();
|
|
65
|
-
console.log(error);
|
|
66
|
-
console.log();
|
|
67
|
-
}
|
|
68
|
-
return prettier.format(content, {
|
|
69
|
-
parser: 'babel',
|
|
70
|
-
});
|
|
71
|
-
}
|
|
61
|
+
const format = async (file, content) => {
|
|
62
|
+
const { code } = await oxfmt(file, content, { singleQuote: true });
|
|
63
|
+
return code;
|
|
72
64
|
};
|
|
73
65
|
|
|
74
66
|
/**
|
|
@@ -80,51 +72,56 @@ const fetchMigration = async (config) => {
|
|
|
80
72
|
const client = await getEnvironment(config);
|
|
81
73
|
let module = false;
|
|
82
74
|
try {
|
|
83
|
-
const { readPackageUp } = await import(
|
|
75
|
+
const { readPackageUp } = await import("read-package-up");
|
|
84
76
|
const data = await readPackageUp();
|
|
85
|
-
module = data?.packageJson?.type ===
|
|
77
|
+
module = data?.packageJson?.type === "module";
|
|
86
78
|
} catch (err) {
|
|
87
79
|
console.log(err);
|
|
88
80
|
}
|
|
89
81
|
|
|
90
82
|
let timestamp = Date.now();
|
|
91
83
|
const contentTypes = contentType
|
|
92
|
-
? (
|
|
84
|
+
? (
|
|
85
|
+
await Promise.all(contentType.map((ct) => getContentTypes(client, ct)))
|
|
86
|
+
).flat()
|
|
93
87
|
: await getContentTypes(client);
|
|
94
88
|
|
|
95
89
|
const promises = contentTypes.map(async (entry) => {
|
|
96
|
-
const filename = path.join(
|
|
90
|
+
const filename = path.join(
|
|
91
|
+
directory,
|
|
92
|
+
`${timestamp++}-create-${entry.sys.id}-migration.${module ? "cjs" : "js"}`,
|
|
93
|
+
);
|
|
97
94
|
|
|
98
95
|
const content = await generateMigrationScript(client, [entry]);
|
|
99
96
|
// Fetch migration script generated by contentful
|
|
100
97
|
let modifiedContent = content.toString();
|
|
101
98
|
// Modify migration script to use our helpers
|
|
102
|
-
const testDefaultValueRegex = /(defaultValue\({\s*)[
|
|
99
|
+
const testDefaultValueRegex = /(defaultValue\({\s*)[^:]+([^)]+)/g;
|
|
103
100
|
if (testDefaultValueRegex.test(modifiedContent)) {
|
|
104
101
|
modifiedContent = content
|
|
105
102
|
.toString()
|
|
106
103
|
// Add call to utils.getDefaultLocale() to the top
|
|
107
104
|
.replace(
|
|
108
|
-
|
|
105
|
+
"module.exports = function (migration) {",
|
|
109
106
|
`
|
|
110
107
|
${migrationHeader}
|
|
111
108
|
const defaultLocale = await helpers.locale.getDefaultLocale();
|
|
112
|
-
|
|
109
|
+
`,
|
|
113
110
|
)
|
|
114
111
|
// Replace the default locale with defaultLocale.code so that the migration
|
|
115
112
|
// still works as expected when the locale is changed in contentful
|
|
116
|
-
.replace(testDefaultValueRegex,
|
|
113
|
+
.replace(testDefaultValueRegex, "$1[defaultLocale.code]$2")
|
|
117
114
|
// Add a closing parentheses as we wrap the migration function.
|
|
118
|
-
.replace(/};\s*$/g,
|
|
115
|
+
.replace(/};\s*$/g, "});");
|
|
119
116
|
} else {
|
|
120
117
|
// If we don't have a default value we just wrap the migration function with our withHelpers wrapper
|
|
121
118
|
modifiedContent = content
|
|
122
119
|
.toString()
|
|
123
|
-
.replace(
|
|
124
|
-
.replace(/};\s*$/g,
|
|
120
|
+
.replace("module.exports = function (migration) {", migrationHeader)
|
|
121
|
+
.replace(/};\s*$/g, "});");
|
|
125
122
|
}
|
|
126
123
|
|
|
127
|
-
await fs.outputFile(filename, await format(filename, modifiedContent
|
|
124
|
+
await fs.outputFile(filename, await format(filename, modifiedContent));
|
|
128
125
|
console.log(`Generated new migration file to ${pc.green(filename)}`);
|
|
129
126
|
});
|
|
130
127
|
|
|
@@ -145,7 +142,9 @@ const executeMigration = async (file, config) => {
|
|
|
145
142
|
const name = path.basename(file);
|
|
146
143
|
const version = getVersionFromFile(file);
|
|
147
144
|
if (!version) {
|
|
148
|
-
console.error(
|
|
145
|
+
console.error(
|
|
146
|
+
`Invalid migration file name "${name}". Must start with a timestamp.`,
|
|
147
|
+
);
|
|
149
148
|
process.exit(1);
|
|
150
149
|
}
|
|
151
150
|
|
|
@@ -163,7 +162,9 @@ const executeMigration = async (file, config) => {
|
|
|
163
162
|
options.host = config.host;
|
|
164
163
|
}
|
|
165
164
|
|
|
166
|
-
console.log(
|
|
165
|
+
console.log(
|
|
166
|
+
`\nRun migration ${pc.green(version)} in environment ${pc.green(environmentId)}`,
|
|
167
|
+
);
|
|
167
168
|
const proceed = await confirm(config);
|
|
168
169
|
if (!proceed) {
|
|
169
170
|
return;
|
|
@@ -172,8 +173,13 @@ const executeMigration = async (file, config) => {
|
|
|
172
173
|
await runMigration(options);
|
|
173
174
|
await storeMigration({ version, name, state: STATE_SUCCESS }, config);
|
|
174
175
|
} catch (error) {
|
|
175
|
-
const message = (error.errors || [error])
|
|
176
|
-
|
|
176
|
+
const message = (error.errors || [error])
|
|
177
|
+
.map((error) => error.message)
|
|
178
|
+
.join("\n");
|
|
179
|
+
await storeMigration(
|
|
180
|
+
{ version, name, state: STATE_FAILURE, message },
|
|
181
|
+
config,
|
|
182
|
+
);
|
|
177
183
|
|
|
178
184
|
throw error;
|
|
179
185
|
}
|
|
@@ -197,8 +203,15 @@ const runMigrations = async (config) => {
|
|
|
197
203
|
console.log(error.message);
|
|
198
204
|
throw error;
|
|
199
205
|
}
|
|
200
|
-
console.log(
|
|
201
|
-
|
|
206
|
+
console.log(
|
|
207
|
+
`Found ${pc.green(migrations.length)} unexecuted migrations in environment ${pc.green(environmentId)}`,
|
|
208
|
+
);
|
|
209
|
+
if (config?.verbose) {
|
|
210
|
+
for (const migration of migrations) {
|
|
211
|
+
console.log(` ${pc.green(migration)}`);
|
|
212
|
+
}
|
|
213
|
+
console.log();
|
|
214
|
+
}
|
|
202
215
|
const proceed = migrations.length === 0 || (await confirm(config));
|
|
203
216
|
if (!proceed) {
|
|
204
217
|
return;
|
|
@@ -214,7 +227,7 @@ const runMigrations = async (config) => {
|
|
|
214
227
|
}
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
console.log(pc.green(
|
|
230
|
+
console.log(pc.green("\nAll done"), "🚀");
|
|
218
231
|
};
|
|
219
232
|
|
|
220
233
|
module.exports.fetchMigration = fetchMigration;
|