cht-conf 4.4.1 → 4.5.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/.editorconfig +15 -0
- package/CHANGELOG.md +7 -0
- package/README.md +9 -1
- package/package.json +27 -26
- package/src/bin/shell-completion.js +5 -5
- package/src/cli/supported-actions.js +2 -2
- package/src/cli/usage.js +2 -0
- package/src/fn/backup-all-forms.js +1 -1
- package/src/fn/compile-app-settings.js +41 -14
- package/src/fn/compress-pngs.js +10 -8
- package/src/fn/compress-svgs.js +5 -2
- package/src/fn/create-users.js +6 -3
- package/src/fn/csv-to-docs.js +73 -52
- package/src/fn/delete-contacts.js +2 -0
- package/src/fn/edit-contacts.js +14 -11
- package/src/fn/fetch-csvs-from-google-drive.js +3 -3
- package/src/fn/fetch-forms-from-google-drive.js +3 -3
- package/src/fn/initialise-project-layout.js +14 -8
- package/src/fn/merge-contacts.js +10 -2
- package/src/fn/move-contacts.js +2 -0
- package/src/fn/upload-custom-translations.js +29 -22
- package/src/fn/upload-docs.js +30 -13
- package/src/fn/upload-privacy-policies.js +4 -1
- package/src/fn/upload-sms-from-csv.js +11 -11
- package/src/fn/watch-project.js +212 -204
- package/src/lib/api.js +22 -22
- package/src/lib/args-form-filter.js +1 -1
- package/src/lib/attachments-from-dir.js +3 -1
- package/src/lib/check-cht-conf-dependency-version.js +4 -2
- package/src/lib/check-for-updates.js +4 -3
- package/src/lib/compilation/compile-contact-summary.js +12 -2
- package/src/lib/compilation/compile-tasks-and-targets.js +5 -2
- package/src/lib/compilation/package-lib.js +5 -2
- package/src/lib/compilation/validate-declarative-schema.js +49 -32
- package/src/lib/convert-forms.js +27 -9
- package/src/lib/exec-promise.js +27 -19
- package/src/lib/fetch-document-list.js +2 -2
- package/src/lib/generate-users-csv.js +1 -1
- package/src/lib/get-api-url.js +4 -1
- package/src/lib/google-auth.js +15 -5
- package/src/lib/hierarchy-operations/delete-hierarchy.js +4 -1
- package/src/lib/hierarchy-operations/hierarchy-data-source.js +7 -2
- package/src/lib/hierarchy-operations/index.js +16 -4
- package/src/lib/hierarchy-operations/jsdocFolder.js +4 -1
- package/src/lib/hierarchy-operations/lineage-constraints.js +12 -6
- package/src/lib/hierarchy-operations/replace-lineage.js +20 -18
- package/src/lib/insert-or-replace.js +6 -2
- package/src/lib/log.js +6 -2
- package/src/lib/main.js +4 -3
- package/src/lib/nools-utils.js +5 -3
- package/src/lib/parse-purge.js +4 -1
- package/src/lib/parse-targets.js +3 -1
- package/src/lib/polyfill.js +3 -1
- package/src/lib/progress-bar.js +26 -13
- package/src/lib/skip-fn.js +5 -3
- package/src/lib/sync-fs.js +24 -12
- package/src/lib/upload-forms.js +17 -7
- package/src/lib/validation/form/can-generate-xform.js +3 -1
- package/src/lib/validation/form/check-xpaths-exist.js +8 -3
- package/src/lib/validation/form/deprecated-appearance.js +4 -2
- package/src/lib/validation/form/has-instance-id.js +4 -1
- package/src/lib/validation/form/no-required-notes.js +4 -1
- package/src/lib/warn-upload-overwrite.js +6 -3
package/src/fn/watch-project.js
CHANGED
|
@@ -26,269 +26,277 @@ const { execute: uploadCustomTranslations, TRANSLATIONS_DIR_PATH } = require('./
|
|
|
26
26
|
const { execute: uploadResources, RESOURCES_DIR_PATH, RESOURCE_CONFIG_PATH } = require('./upload-resources');
|
|
27
27
|
|
|
28
28
|
const watcherEvents = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
CreateEvent: 'create',
|
|
30
|
+
DeleteEvent: 'delete',
|
|
31
|
+
UpdateEvent: 'update'
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const runValidation = (validation, forms) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
if(environment.skipValidate) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
return validation(forms);
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
const uploadInitialState = async (api) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
await uploadResources();
|
|
43
|
+
await runValidation(validateAppForms, environment.extraArgs);
|
|
44
|
+
await runValidation(validateContactForms, environment.extraArgs);
|
|
45
|
+
await runValidation(validateTrainingForms, environment.extraArgs);
|
|
46
|
+
await uploadAppForms(environment.extraArgs);
|
|
47
|
+
await uploadContactForms(environment.extraArgs);
|
|
48
|
+
await uploadTrainingForms(environment.extraArgs);
|
|
49
|
+
await uploadCustomTranslations();
|
|
50
|
+
await uploadAppSettings(api);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
let fsEventsSubscription;
|
|
54
54
|
let eventQueue;
|
|
55
55
|
|
|
56
56
|
const cleanUp = async () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
if (fsEventsSubscription) {
|
|
58
|
+
await fsEventsSubscription.unsubscribe();
|
|
59
|
+
fsEventsSubscription = undefined;
|
|
60
|
+
}
|
|
61
|
+
if (eventQueue) {
|
|
62
|
+
await eventQueue.clear();
|
|
63
|
+
eventQueue = undefined;
|
|
64
|
+
}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
const waitForKillSignal = () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
process.on('SIGINT', async () => {
|
|
70
|
+
await cleanUp();
|
|
71
|
+
resolve();
|
|
73
72
|
});
|
|
73
|
+
});
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
const deleteForm = (fileName, formDir) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
const absFormDirPath = path.join(environment.pathToProject, formDir);
|
|
82
|
-
const formFiles = fs.readdirSync(absFormDirPath).map(file => (uploadForms.formFileMatcher(file) || convertForms.formFileMatcher(file))).filter(_form => _form === form);
|
|
83
|
-
const canDelete = formFiles.length <= 0;
|
|
84
|
-
if (canDelete) {
|
|
85
|
-
eventQueue.enqueue(async () => {
|
|
86
|
-
await deleteForms([form]);
|
|
87
|
-
return fileName;
|
|
88
|
-
});
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
warn(`You have to delete all form files (.xlsx, .properties.json, .xml) associated with the ${form} form to delete it from ${environment.apiUrl}`);
|
|
77
|
+
const form = uploadForms.formFileMatcher(fileName) || convertForms.formFileMatcher(fileName);
|
|
78
|
+
if (!form) {
|
|
92
79
|
return false;
|
|
80
|
+
}
|
|
81
|
+
const absFormDirPath = path.join(environment.pathToProject, formDir);
|
|
82
|
+
const formFiles = fs
|
|
83
|
+
.readdirSync(absFormDirPath)
|
|
84
|
+
.map(file => (uploadForms.formFileMatcher(file) || convertForms.formFileMatcher(file)))
|
|
85
|
+
.filter(_form => _form === form);
|
|
86
|
+
const canDelete = formFiles.length <= 0;
|
|
87
|
+
if (canDelete) {
|
|
88
|
+
eventQueue.enqueue(async () => {
|
|
89
|
+
await deleteForms([form]);
|
|
90
|
+
return fileName;
|
|
91
|
+
});
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
warn(
|
|
95
|
+
`You have to delete all form files (.xlsx, .properties.json, .xml) associated with the ${form} form `
|
|
96
|
+
+ `to delete it from ${environment.apiUrl}`
|
|
97
|
+
);
|
|
98
|
+
return false;
|
|
93
99
|
};
|
|
94
100
|
|
|
95
101
|
const getFormCommandContext = (parsedPath) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (parsedPath.dir === path.join(environment.pathToProject, APP_FORMS_PATH)) {
|
|
103
|
+
return {
|
|
104
|
+
path: APP_FORMS_PATH,
|
|
105
|
+
validateForms: validateAppForms,
|
|
106
|
+
uploadForms: uploadAppForms,
|
|
107
|
+
convertForms: convertAppForms,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
if (parsedPath.dir === path.join(environment.pathToProject, TRAINING_FORMS_PATH)) {
|
|
112
|
+
return {
|
|
113
|
+
path: TRAINING_FORMS_PATH,
|
|
114
|
+
validateForms: validateTrainingForms,
|
|
115
|
+
uploadForms: uploadTrainingForms,
|
|
116
|
+
convertForms: convertTrainingForms,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
if (parsedPath.dir === path.join(environment.pathToProject, CONTACT_FORMS_PATH)) {
|
|
121
|
+
return {
|
|
122
|
+
path: CONTACT_FORMS_PATH,
|
|
123
|
+
validateForms: validateContactForms,
|
|
124
|
+
uploadForms: uploadContactForms,
|
|
125
|
+
convertForms: convertContactForm,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
122
128
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
if (parsedPath.dir === path.join(environment.pathToProject, COLLECT_FORMS_PATH)) {
|
|
130
|
+
return {
|
|
131
|
+
path: COLLECT_FORMS_PATH,
|
|
132
|
+
validateForms: validateCollectForms,
|
|
133
|
+
uploadForms: uploadCollectForms,
|
|
134
|
+
convertForms: convertCollectForms,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
return;
|
|
133
139
|
};
|
|
134
140
|
|
|
135
141
|
const processForm = (eventType, fileName, commandContext) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
if (eventType === watcherEvents.DeleteEvent && deleteForm(fileName, commandContext.path)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
let form = uploadForms.formFileMatcher(fileName);
|
|
146
|
+
if (form) {
|
|
147
|
+
eventQueue.enqueue(async () => {
|
|
148
|
+
await runValidation(commandContext.validateForms, [form]);
|
|
149
|
+
await commandContext.uploadForms([form]);
|
|
150
|
+
return fileName;
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
form = convertForms.formFileMatcher(fileName);
|
|
156
|
+
if (form) {
|
|
157
|
+
eventQueue.enqueue(async () => {
|
|
158
|
+
await commandContext.convertForms([form]);
|
|
159
|
+
return fileName;
|
|
160
|
+
});
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
158
164
|
};
|
|
159
165
|
|
|
160
166
|
const getFormMediaCommandContext = (parsedPath) => {
|
|
161
|
-
|
|
167
|
+
if (parsedPath.dir.startsWith(path.join(environment.pathToProject, APP_FORMS_PATH))
|
|
162
168
|
&& path.parse(parsedPath.dir).dir.endsWith(APP_FORMS_PATH)) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
+
// Check if the directory's immediate parent is forms/app
|
|
170
|
+
return {
|
|
171
|
+
validateForms: validateAppForms,
|
|
172
|
+
uploadForms: uploadAppForms,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
169
175
|
|
|
170
|
-
|
|
176
|
+
if (parsedPath.dir.startsWith(path.join(environment.pathToProject, TRAINING_FORMS_PATH))
|
|
171
177
|
&& path.parse(parsedPath.dir).dir.endsWith(TRAINING_FORMS_PATH)) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
// Check if the directory's immediate parent is forms/training
|
|
179
|
+
return {
|
|
180
|
+
validateForms: validateTrainingForms,
|
|
181
|
+
uploadForms: uploadTrainingForms,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
178
184
|
|
|
179
|
-
|
|
185
|
+
return;
|
|
180
186
|
};
|
|
181
187
|
|
|
182
188
|
const processFormMedia = (formMediaDir, fileName, commandContext) => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
const form = uploadForms.formMediaMatcher(formMediaDir);
|
|
190
|
+
if (form) {
|
|
191
|
+
eventQueue.enqueue(async () => {
|
|
192
|
+
await runValidation(commandContext.validateForms,[form]);
|
|
193
|
+
await commandContext.uploadForms([form]);
|
|
194
|
+
return fileName;
|
|
195
|
+
});
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
193
199
|
};
|
|
194
200
|
|
|
195
201
|
const processConfigFiles = (api, fileName) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
202
|
+
if (fileName === APP_SETTINGS_JSON_PATH) {
|
|
203
|
+
eventQueue.enqueue(async () => {
|
|
204
|
+
await uploadAppSettings(api);
|
|
205
|
+
return fileName;
|
|
206
|
+
});
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
if (configFileMatcher(fileName)) {
|
|
210
|
+
eventQueue.enqueue(async () => {
|
|
211
|
+
await compileAppSettings();
|
|
212
|
+
return fileName;
|
|
213
|
+
});
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
211
217
|
};
|
|
212
218
|
|
|
213
219
|
const watchProject = {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
watch: async (api, blockFn, callback = {}, uploadStateOnStart = false) => {
|
|
221
|
+
if (uploadStateOnStart) {
|
|
222
|
+
await uploadInitialState(api);
|
|
223
|
+
info('Initial State Uploaded');
|
|
224
|
+
}
|
|
219
225
|
|
|
220
|
-
|
|
226
|
+
eventQueue = new Queue({ concurrent: 1, start: true });
|
|
221
227
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
fsEventsSubscription = await watcher.subscribe(environment.pathToProject, async (err, events) => {
|
|
229
|
+
if (err) {
|
|
230
|
+
error(err);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
for (const event of events) {
|
|
234
|
+
const changePath = event.path;
|
|
235
|
+
const parsedPath = path.parse(changePath);
|
|
236
|
+
const fileName = parsedPath.base;
|
|
231
237
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
const formContext = getFormCommandContext(parsedPath);
|
|
239
|
+
if (formContext) {
|
|
240
|
+
processForm(event.type, fileName, formContext);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
237
243
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
const formMediaContext = getFormMediaCommandContext(parsedPath);
|
|
245
|
+
if (formMediaContext) {
|
|
246
|
+
const dirName = path.parse(parsedPath.dir).base;
|
|
247
|
+
processFormMedia(dirName, fileName, formMediaContext);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
244
250
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
251
|
+
if (parsedPath.dir === path.join(environment.pathToProject, TRANSLATIONS_DIR_PATH)) {
|
|
252
|
+
eventQueue.enqueue(async () => {
|
|
253
|
+
await uploadCustomTranslations();
|
|
254
|
+
return fileName;
|
|
255
|
+
});
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
252
258
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
if (parsedPath.dir === path.join(environment.pathToProject, APP_SETTINGS_DIR_PATH)) {
|
|
260
|
+
processConfigFiles(api, fileName);
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
257
263
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
264
|
+
if (parsedPath.dir === environment.pathToProject && parsedPath.base !== RESOURCE_CONFIG_PATH) {
|
|
265
|
+
processConfigFiles(api, fileName);
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
262
268
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
269
|
+
if (parsedPath.base === RESOURCE_CONFIG_PATH
|
|
270
|
+
|| parsedPath.dir === path.join(environment.pathToProject, RESOURCES_DIR_PATH)
|
|
271
|
+
) {
|
|
272
|
+
eventQueue.enqueue(async () => {
|
|
273
|
+
await uploadResources();
|
|
274
|
+
return fileName;
|
|
275
|
+
});
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
272
280
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
281
|
+
eventQueue.on('resolve', file => {
|
|
282
|
+
if (callback) {
|
|
283
|
+
callback(file);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
eventQueue.on('reject', err => error('watch-mode: Error while processing change,', err));
|
|
279
287
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
288
|
+
info('watching', environment.pathToProject, 'for changes');
|
|
289
|
+
await blockFn();
|
|
290
|
+
},
|
|
291
|
+
close: async () => {
|
|
292
|
+
info('stopping watchers');
|
|
293
|
+
await cleanUp();
|
|
294
|
+
}
|
|
287
295
|
};
|
|
288
296
|
|
|
289
297
|
const api = require('../lib/api');
|
|
290
298
|
module.exports = {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
299
|
+
watchProject,
|
|
300
|
+
requiresInstance: true,
|
|
301
|
+
execute: () => watchProject.watch(api(), waitForKillSignal, false, true)
|
|
294
302
|
};
|
package/src/lib/api.js
CHANGED
|
@@ -210,28 +210,28 @@ const api = {
|
|
|
210
210
|
headers: { 'Content-Type': 'application/xml' },
|
|
211
211
|
body: formXml,
|
|
212
212
|
})
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
213
|
+
.then(resp => {
|
|
214
|
+
try {
|
|
215
|
+
return JSON.parse(resp);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
throw new Error('Invalid JSON response validating XForm against the API: ' + resp);
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
.catch(err => {
|
|
221
|
+
if (err.statusCode === 404) {
|
|
222
|
+
// The endpoint doesn't exist in the API (old CHT version), so
|
|
223
|
+
// we assume the form is valid but return special JSON
|
|
224
|
+
// highlighting the situation, and remembering the lack
|
|
225
|
+
// of the endpoint so next call there is no need
|
|
226
|
+
// to call the missed endpoint again
|
|
227
|
+
this._formsValidateEndpointFound = false;
|
|
228
|
+
return {ok: true, formsValidateEndpointFound: false};
|
|
229
|
+
}
|
|
230
|
+
if (err.statusCode === 400 && err.error) {
|
|
231
|
+
throw new Error(JSON.parse(err.error).error);
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
});
|
|
235
235
|
},
|
|
236
236
|
|
|
237
237
|
async getCompressibleTypes () {
|
|
@@ -3,7 +3,9 @@ const fs = require('./sync-fs');
|
|
|
3
3
|
const warn = require('./log').warn;
|
|
4
4
|
|
|
5
5
|
module.exports = dir => {
|
|
6
|
-
if(!fs.exists(dir))
|
|
6
|
+
if(!fs.exists(dir)) {
|
|
7
|
+
return warn('Cannot build list of attachments from non-existent dir:', dir);
|
|
8
|
+
}
|
|
7
9
|
|
|
8
10
|
const attachments = {};
|
|
9
11
|
fs.recurseFiles(dir)
|
|
@@ -19,8 +19,8 @@ module.exports = projectDir => {
|
|
|
19
19
|
const majorRunningVersion = semver.major(runningVersion);
|
|
20
20
|
let upgradeDowngradeLocalMsg = '';
|
|
21
21
|
let upgradeDowngradeProjectMsg = '';
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const satisfiesLessThanMajorRunningVersion = semver.satisfies(projectVersion, `<${majorRunningVersion}.x`);
|
|
23
|
+
const satisifiesGreaterThanRunningVersion = semver.satisfies(projectVersion, `>${runningVersion}`);
|
|
24
24
|
|
|
25
25
|
if(satisfiesLessThanMajorRunningVersion || satisifiesGreaterThanRunningVersion) {
|
|
26
26
|
|
|
@@ -34,6 +34,7 @@ module.exports = projectDir => {
|
|
|
34
34
|
upgradeDowngradeProjectMsg = 'downgrade';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/* eslint-disable max-len */
|
|
37
38
|
throw new Error(`Your cht-conf version is incompatible with the project's cht-conf version:
|
|
38
39
|
Your local cht-conf version: ${runningVersion}
|
|
39
40
|
The project cht-conf version: ${projectVersion}
|
|
@@ -44,6 +45,7 @@ module.exports = projectDir => {
|
|
|
44
45
|
npm i -g cht-conf@${projectVersion}
|
|
45
46
|
and try again, or ${upgradeDowngradeProjectMsg} the project cht-conf version to ${runningVersion}, or ignore this warning with --skip-dependency-check
|
|
46
47
|
`);
|
|
48
|
+
/* eslint-enable max-len */
|
|
47
49
|
|
|
48
50
|
}
|
|
49
51
|
};
|
|
@@ -6,7 +6,9 @@ module.exports = (options) => {
|
|
|
6
6
|
const current = require('../../package').version;
|
|
7
7
|
info(`Current version: ${current}. Checking NPM for updates…`);
|
|
8
8
|
|
|
9
|
-
if (!options)
|
|
9
|
+
if (!options) {
|
|
10
|
+
options = {};
|
|
11
|
+
}
|
|
10
12
|
|
|
11
13
|
return request
|
|
12
14
|
.get('https://registry.npmjs.org/cht-conf')
|
|
@@ -33,6 +35,5 @@ module.exports = (options) => {
|
|
|
33
35
|
} else {
|
|
34
36
|
warn(`Could not check NPM for updates. Error: ${err.message}`);
|
|
35
37
|
}
|
|
36
|
-
})
|
|
37
|
-
;
|
|
38
|
+
});
|
|
38
39
|
};
|
|
@@ -9,8 +9,18 @@ module.exports = async (projectDir, options) => {
|
|
|
9
9
|
const freeformPathExists = fs.exists(freeformPath);
|
|
10
10
|
const structuredPathExists = fs.exists(structuredPath);
|
|
11
11
|
|
|
12
|
-
if (!freeformPathExists && !structuredPathExists)
|
|
13
|
-
|
|
12
|
+
if (!freeformPathExists && !structuredPathExists) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`Could not find contact-summary javascript at either of ${freeformPath} or ${structuredPath}. `
|
|
15
|
+
+ 'Please create one xor other of these files.'
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
if (freeformPathExists && structuredPathExists) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Found contact-summary javascript at both ${freeformPath} and ${structuredPath}. `
|
|
21
|
+
+ 'Only one of these files should exist.'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
14
24
|
|
|
15
25
|
const baseEslintPath = path.join(__dirname, '../../contact-summary/.eslintrc');
|
|
16
26
|
const pathToDeclarativeLib = path.join(__dirname, '../../contact-summary/lib.js');
|
|
@@ -22,7 +22,10 @@ const compileTasksAndTargets = async (projectDir, options = {}) => {
|
|
|
22
22
|
|
|
23
23
|
if (legacyRules !== undefined) {
|
|
24
24
|
if (findMissingDeclarativeFiles(projectDir).length !== DECLARATIVE_NOOLS_FILES.length) {
|
|
25
|
-
throw new Error(
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Both legacy and declarative files found. '
|
|
27
|
+
+ `You should either have rules.nools.js xor ${DECLARATIVE_NOOLS_FILES} files.`
|
|
28
|
+
);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
const rules = options.minifyScripts ? nools.minify(legacyRules) : legacyRules;
|
|
@@ -54,4 +57,4 @@ const compileDeclarativeFiles = async (projectDir, options) => {
|
|
|
54
57
|
return pack(projectDir, pathToDeclarativeLib, baseEslintPath, options);
|
|
55
58
|
};
|
|
56
59
|
|
|
57
|
-
module.exports = compileTasksAndTargets;
|
|
60
|
+
module.exports = compileTasksAndTargets;
|
|
@@ -67,7 +67,8 @@ module.exports = (pathToProject, entry, baseEslintPath, options = {}) => {
|
|
|
67
67
|
failOnError: false,
|
|
68
68
|
failOnWarning: false,
|
|
69
69
|
}),
|
|
70
|
-
|
|
70
|
+
// Ignore all optional deps of moment.js
|
|
71
|
+
new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/ }),
|
|
71
72
|
],
|
|
72
73
|
});
|
|
73
74
|
|
|
@@ -89,7 +90,9 @@ module.exports = (pathToProject, entry, baseEslintPath, options = {}) => {
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
if (stats.hasWarnings()) {
|
|
92
|
-
const hasWarningsRelatedToLinting = stats
|
|
93
|
+
const hasWarningsRelatedToLinting = stats
|
|
94
|
+
.toJson().warnings
|
|
95
|
+
.some(warning => warning.message.includes('warnings potentially fixable'));
|
|
93
96
|
const shouldHalt = options.haltOnWebpackWarning ||
|
|
94
97
|
options.haltOnLintMessage && hasWarningsRelatedToLinting;
|
|
95
98
|
|