@villedemontreal/general-utils 5.21.1 → 6.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/dist/config/configs.js +2 -2
- package/dist/config/configs.js.map +1 -1
- package/dist/src/apiError.test.js +0 -2
- package/dist/src/apiError.test.js.map +1 -1
- package/dist/src/collectionUtils.d.ts +3 -3
- package/dist/src/collectionUtils.d.ts.map +1 -1
- package/dist/src/collectionUtils.js +0 -1
- package/dist/src/collectionUtils.js.map +1 -1
- package/dist/src/config/constants.js +2 -2
- package/dist/src/config/constants.js.map +1 -1
- package/dist/src/dateUtils.d.ts +7 -8
- package/dist/src/dateUtils.d.ts.map +1 -1
- package/dist/src/dateUtils.js +106 -24
- package/dist/src/dateUtils.js.map +1 -1
- package/dist/src/dateUtils.test.js +5 -6
- package/dist/src/dateUtils.test.js.map +1 -1
- package/dist/src/logLevel.js +2 -2
- package/dist/src/logLevel.js.map +1 -1
- package/dist/src/orderBy.d.ts +1 -1
- package/dist/src/stringUtils.d.ts.map +1 -1
- package/dist/src/stringUtils.js +2 -2
- package/dist/src/stringUtils.js.map +1 -1
- package/dist/src/timer.d.ts +2 -2
- package/dist/src/timer.d.ts.map +1 -1
- package/dist/src/timer.js +10 -5
- package/dist/src/timer.js.map +1 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +23 -29
- package/dist/src/utils.js.map +1 -1
- package/dist/src/utils.test.js +13 -21
- package/dist/src/utils.test.js.map +1 -1
- package/dist/test-data/test_throwNotManaged/fcd4d925-6d8f-4578-b0fc-23d83daeb137.d.ts +2 -0
- package/dist/test-data/test_throwNotManaged/fcd4d925-6d8f-4578-b0fc-23d83daeb137.d.ts.map +1 -0
- package/dist/test-data/test_throwNotManaged/{c5ab12da-db6a-49f4-8259-c0a46f48de28.js → fcd4d925-6d8f-4578-b0fc-23d83daeb137.js} +1 -1
- package/dist/test-data/test_throwNotManaged/{c5ab12da-db6a-49f4-8259-c0a46f48de28.js.map → fcd4d925-6d8f-4578-b0fc-23d83daeb137.js.map} +1 -1
- package/dist/tests-resources/exec/execTest.js +0 -1
- package/dist/tests-resources/exec/execTest.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -21
- package/src/apiError.test.ts +0 -2
- package/src/collectionUtils.ts +3 -8
- package/src/config/constants.ts +2 -2
- package/src/dateUtils.test.ts +5 -6
- package/src/dateUtils.ts +120 -26
- package/src/logLevel.ts +2 -2
- package/src/orderBy.ts +1 -1
- package/src/stringUtils.ts +2 -2
- package/src/timer.ts +10 -5
- package/src/utils.test.ts +13 -21
- package/src/utils.ts +24 -28
- package/dist/test-data/test_throwNotManaged/c5ab12da-db6a-49f4-8259-c0a46f48de28.d.ts +0 -2
- package/dist/test-data/test_throwNotManaged/c5ab12da-db6a-49f4-8259-c0a46f48de28.d.ts.map +0 -1
package/src/dateUtils.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
2
|
import * as _ from 'lodash';
|
|
3
|
-
import
|
|
3
|
+
import { DateTime } from 'luxon';
|
|
4
4
|
import { getValueDescription, getValueDescriptionWithType, utils } from '.';
|
|
5
5
|
import { getCartesianProduct } from './collectionUtils';
|
|
6
6
|
import {
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
const VALID_DATE_UTC_REPRESENTATION = '2018-07-31T12:34:56.789Z';
|
|
23
23
|
const VALID_DATE = new Date(VALID_DATE_UTC_REPRESENTATION);
|
|
24
24
|
|
|
25
|
-
// tslint:disable:max-func-body-length
|
|
26
25
|
describe('Date Utility', () => {
|
|
27
26
|
describe('#isDateBetween', () => {
|
|
28
27
|
it('should support open ranges', () => {
|
|
@@ -41,7 +40,7 @@ describe('Date Utility', () => {
|
|
|
41
40
|
|
|
42
41
|
describe('#isDateCompatible', () => {
|
|
43
42
|
const dateRepr = '2018-09-12T21:45:12.243Z';
|
|
44
|
-
const dateValues = [dateRepr, new Date(dateRepr),
|
|
43
|
+
const dateValues = [dateRepr, new Date(dateRepr), DateTime.fromISO(dateRepr)];
|
|
45
44
|
|
|
46
45
|
getCartesianProduct(dateValues, dateValues).forEach((dateParameters) => {
|
|
47
46
|
const parameter1 = dateParameters[0];
|
|
@@ -55,9 +54,9 @@ describe('Date Utility', () => {
|
|
|
55
54
|
});
|
|
56
55
|
|
|
57
56
|
const date1Repr = '2018-09-12T12:34:56.789Z';
|
|
58
|
-
const date1Values = [date1Repr, new Date(date1Repr),
|
|
57
|
+
const date1Values = [date1Repr, new Date(date1Repr), DateTime.fromISO(date1Repr)];
|
|
59
58
|
const date2Repr = '2018-09-12T21:45:12.243Z';
|
|
60
|
-
const date2Values = [date2Repr, new Date(date2Repr),
|
|
59
|
+
const date2Values = [date2Repr, new Date(date2Repr), DateTime.fromISO(date2Repr)];
|
|
61
60
|
|
|
62
61
|
getCartesianProduct(date1Values, date2Values).forEach((dateRangeParameter) => {
|
|
63
62
|
const dateRangeLowBoundary = dateRangeParameter[0];
|
|
@@ -158,7 +157,7 @@ describe('Date Utility', () => {
|
|
|
158
157
|
let failed = false;
|
|
159
158
|
try {
|
|
160
159
|
getSafeDate(value);
|
|
161
|
-
} catch
|
|
160
|
+
} catch {
|
|
162
161
|
failed = true;
|
|
163
162
|
}
|
|
164
163
|
assert.isTrue(failed);
|
package/src/dateUtils.ts
CHANGED
|
@@ -1,41 +1,107 @@
|
|
|
1
1
|
import { DateTime, Zone } from 'luxon';
|
|
2
|
-
import * as moment from 'moment';
|
|
3
|
-
import { Moment } from 'moment';
|
|
4
2
|
import { getValueDescription, utils } from '.';
|
|
5
3
|
import { isDate, isNil } from 'lodash';
|
|
6
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Internal helper to convert a DateDefinition to a Luxon DateTime.
|
|
7
|
+
*/
|
|
8
|
+
function toDateTime(value: DateDefinition): DateTime {
|
|
9
|
+
if (value instanceof DateTime) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (isDate(value)) {
|
|
13
|
+
return DateTime.fromJSDate(value);
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === 'string') {
|
|
16
|
+
const dt = DateTime.fromISO(value);
|
|
17
|
+
if (dt.isValid) {
|
|
18
|
+
return dt;
|
|
19
|
+
}
|
|
20
|
+
// Fallback for some non-standard ISO formats that moment handled
|
|
21
|
+
return DateTime.fromFormat(value, 'yyyy/MM/dd');
|
|
22
|
+
}
|
|
23
|
+
return DateTime.invalid('Unsupported date definition');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Internal helper to convert a DateDefinition to a UTC Luxon DateTime.
|
|
28
|
+
*/
|
|
29
|
+
function toUtcDateTime(value: DateDefinition, formats?: string | string[]): DateTime {
|
|
30
|
+
if (value instanceof DateTime) {
|
|
31
|
+
return value.toUTC();
|
|
32
|
+
}
|
|
33
|
+
if (isDate(value)) {
|
|
34
|
+
return DateTime.fromJSDate(value, { zone: 'utc' });
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === 'string') {
|
|
37
|
+
if (formats) {
|
|
38
|
+
const formatList = Array.isArray(formats) ? formats : [formats];
|
|
39
|
+
for (const format of formatList) {
|
|
40
|
+
// Map moment format to luxon format if needed, but here we assume luxon compatible formats are passed or we'll handle them in parseDate
|
|
41
|
+
const dt = DateTime.fromFormat(value, format, { zone: 'utc' });
|
|
42
|
+
if (dt.isValid) {
|
|
43
|
+
return dt;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const dt = DateTime.fromISO(value, { zone: 'utc' });
|
|
48
|
+
if (dt.isValid) {
|
|
49
|
+
return dt;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return DateTime.invalid('Unsupported date definition');
|
|
53
|
+
}
|
|
54
|
+
|
|
7
55
|
export function isDateEqual(value: DateDefinition, expectedDate: DateDefinition) {
|
|
8
|
-
const
|
|
9
|
-
const
|
|
56
|
+
const dt = toDateTime(value);
|
|
57
|
+
const expectedDt = toDateTime(expectedDate);
|
|
10
58
|
|
|
11
|
-
return
|
|
59
|
+
return dt.toMillis() === expectedDt.toMillis();
|
|
12
60
|
}
|
|
13
61
|
|
|
14
|
-
/** @see https://
|
|
62
|
+
/** @see https://moment.github.io/luxon/#/ */
|
|
15
63
|
export function isDateBetween(
|
|
16
64
|
value: DateDefinition,
|
|
17
65
|
expectedDate: DateRangeDefinition,
|
|
18
66
|
inclusivity: '()' | '[)' | '(]' | '[]' = '[]',
|
|
19
67
|
) {
|
|
20
|
-
const
|
|
68
|
+
const dt = toDateTime(value);
|
|
21
69
|
const from = expectedDate[0];
|
|
22
70
|
const to = expectedDate[1];
|
|
23
|
-
|
|
71
|
+
|
|
72
|
+
const fromDt = from !== null && from !== undefined ? toDateTime(from) : null;
|
|
73
|
+
const toDt = to !== null && to !== undefined ? toDateTime(to) : null;
|
|
74
|
+
|
|
75
|
+
if (!fromDt) {
|
|
76
|
+
if (!toDt) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
24
79
|
if (inclusivity[1] === ')') {
|
|
25
|
-
return
|
|
80
|
+
return dt.toMillis() < toDt.toMillis();
|
|
26
81
|
}
|
|
27
|
-
return
|
|
82
|
+
return dt.toMillis() <= toDt.toMillis();
|
|
28
83
|
}
|
|
29
|
-
if (
|
|
84
|
+
if (!toDt) {
|
|
30
85
|
if (inclusivity[0] === '(') {
|
|
31
|
-
return
|
|
86
|
+
return dt.toMillis() > fromDt.toMillis();
|
|
32
87
|
}
|
|
33
|
-
return
|
|
88
|
+
return dt.toMillis() >= fromDt.toMillis();
|
|
34
89
|
}
|
|
35
|
-
|
|
90
|
+
|
|
91
|
+
const leftInclusive = inclusivity[0] === '[';
|
|
92
|
+
const rightInclusive = inclusivity[1] === ']';
|
|
93
|
+
|
|
94
|
+
const afterFrom = leftInclusive
|
|
95
|
+
? dt.toMillis() >= fromDt.toMillis()
|
|
96
|
+
: dt.toMillis() > fromDt.toMillis();
|
|
97
|
+
const beforeTo = rightInclusive
|
|
98
|
+
? dt.toMillis() <= toDt.toMillis()
|
|
99
|
+
: dt.toMillis() < toDt.toMillis();
|
|
100
|
+
|
|
101
|
+
return afterFrom && beforeTo;
|
|
36
102
|
}
|
|
37
103
|
|
|
38
|
-
export type DateDefinition = string | Date |
|
|
104
|
+
export type DateDefinition = string | Date | DateTime;
|
|
39
105
|
export type DateRangeDefinition = [DateDefinition, DateDefinition];
|
|
40
106
|
export type CompatibleDateDefinition = DateDefinition | DateRangeDefinition;
|
|
41
107
|
|
|
@@ -73,14 +139,14 @@ export function isDateRange(value: any[]): boolean {
|
|
|
73
139
|
* Returns a "safe" date from the given definition.
|
|
74
140
|
*
|
|
75
141
|
* - `String` values are not considered "safe" since they can contain anything, including invalid dates.
|
|
76
|
-
* - `
|
|
142
|
+
* - `DateTime` values are not considered "safe" since they tolerate exceptions and advanced
|
|
77
143
|
* features that `Date` doesn't support.
|
|
78
144
|
*/
|
|
79
145
|
export function getSafeDate(dateDefinition: DateDefinition): Date {
|
|
80
146
|
let result: Date;
|
|
81
147
|
|
|
82
148
|
if (dateDefinition !== undefined && dateDefinition !== null) {
|
|
83
|
-
result =
|
|
149
|
+
result = toUtcDateTime(dateDefinition).toJSDate();
|
|
84
150
|
}
|
|
85
151
|
|
|
86
152
|
if (!result || !utils.isValidDate(result)) {
|
|
@@ -127,13 +193,23 @@ export function isDateCompatible(value: DateDefinition, expectedDate: Compatible
|
|
|
127
193
|
|
|
128
194
|
export type TimeUnitSymbol = 'ms' | 's' | 'm' | 'h' | 'd' | 'w';
|
|
129
195
|
|
|
196
|
+
const TIME_UNIT_MAPPING: Record<TimeUnitSymbol, string> = {
|
|
197
|
+
ms: 'milliseconds',
|
|
198
|
+
s: 'seconds',
|
|
199
|
+
m: 'minutes',
|
|
200
|
+
h: 'hours',
|
|
201
|
+
d: 'days',
|
|
202
|
+
w: 'weeks',
|
|
203
|
+
};
|
|
204
|
+
|
|
130
205
|
export function getDateRangeAround(
|
|
131
206
|
value: DateDefinition,
|
|
132
207
|
marginValue: number,
|
|
133
208
|
marginUnit: TimeUnitSymbol,
|
|
134
209
|
): DateRangeDefinition {
|
|
135
|
-
const
|
|
136
|
-
|
|
210
|
+
const dt = toDateTime(value);
|
|
211
|
+
const unit = TIME_UNIT_MAPPING[marginUnit];
|
|
212
|
+
return [dt.minus({ [unit]: marginValue }), dt.plus({ [unit]: marginValue })];
|
|
137
213
|
}
|
|
138
214
|
|
|
139
215
|
/**
|
|
@@ -159,13 +235,13 @@ export function isValidIso8601Date(representation: string): boolean {
|
|
|
159
235
|
}
|
|
160
236
|
|
|
161
237
|
/**
|
|
162
|
-
* Format used to represent dates, and which is compatible with
|
|
238
|
+
* Format used to represent dates, and which is compatible with Luxon.
|
|
163
239
|
* Note: It produces ISO-compatible dates, and which also works well with T-SQL.
|
|
164
240
|
* @see `#parseDate`
|
|
165
241
|
* @see `#formatDate`
|
|
166
|
-
* @see https://
|
|
242
|
+
* @see https://moment.github.io/luxon/#/formatting?id=table-of-tokens
|
|
167
243
|
*/
|
|
168
|
-
export const DEFAULT_DATE_FORMAT =
|
|
244
|
+
export const DEFAULT_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
|
|
169
245
|
|
|
170
246
|
/**
|
|
171
247
|
* Parses the given date representation using the provided format (or the default ISO format).
|
|
@@ -176,7 +252,17 @@ export function parseDate(
|
|
|
176
252
|
format: string | string[] = DEFAULT_DATE_FORMAT,
|
|
177
253
|
): Date {
|
|
178
254
|
const formats: string[] = format instanceof Array ? format : [format];
|
|
179
|
-
|
|
255
|
+
|
|
256
|
+
// Map moment tokens to luxon tokens if they look like moment formats
|
|
257
|
+
const mappedFormats = formats.map((f) =>
|
|
258
|
+
f
|
|
259
|
+
.replace(/Y/g, 'y')
|
|
260
|
+
.replace(/D/g, 'd')
|
|
261
|
+
.replace(/A/g, 'a')
|
|
262
|
+
.replace(/(^|[^Z])Z($|[^Z])/, '$1ZZ$2'),
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return toUtcDateTime(representation, mappedFormats).toJSDate();
|
|
180
266
|
}
|
|
181
267
|
|
|
182
268
|
/**
|
|
@@ -185,9 +271,16 @@ export function parseDate(
|
|
|
185
271
|
* @see `#parseDate`
|
|
186
272
|
*/
|
|
187
273
|
export function formatDate(date: DateDefinition, format: string = DEFAULT_DATE_FORMAT) {
|
|
274
|
+
// Map moment tokens to luxon tokens if they look like moment formats
|
|
275
|
+
const mappedFormat = format
|
|
276
|
+
.replace(/Y/g, 'y')
|
|
277
|
+
.replace(/D/g, 'd')
|
|
278
|
+
.replace(/A/g, 'a')
|
|
279
|
+
.replace(/(^|[^Z])Z($|[^Z])/, '$1ZZ$2');
|
|
280
|
+
|
|
188
281
|
return (
|
|
189
|
-
|
|
190
|
-
.
|
|
282
|
+
toDateTime(date)
|
|
283
|
+
.toFormat(mappedFormat)
|
|
191
284
|
// Ensure that 'Z' is used instead of '+00:00' at the end of UTC dates:
|
|
192
285
|
.replace(/[+-]00:?00$/, 'Z')
|
|
193
286
|
);
|
|
@@ -199,7 +292,8 @@ export function formatDate(date: DateDefinition, format: string = DEFAULT_DATE_F
|
|
|
199
292
|
* @see `#formatDate`
|
|
200
293
|
*/
|
|
201
294
|
export function formatUtcDate(date: DateDefinition, format: string = DEFAULT_DATE_FORMAT) {
|
|
202
|
-
|
|
295
|
+
const dt = toDateTime(date).toUTC();
|
|
296
|
+
return formatDate(dt, format);
|
|
203
297
|
}
|
|
204
298
|
|
|
205
299
|
/**
|
package/src/logLevel.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// ==========================================
|
|
2
2
|
// The Log levels
|
|
3
3
|
//
|
|
4
|
-
// We defined
|
|
5
|
-
// in the "@
|
|
4
|
+
// We defined those in this library and not
|
|
5
|
+
// in the "@villee3montreal/logger"
|
|
6
6
|
// library because they are used here, and we
|
|
7
7
|
// want to prevent circular dependencies as much as
|
|
8
8
|
// possible.
|
package/src/orderBy.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as _ from 'lodash';
|
|
|
6
6
|
* to type the orderBys found in its querystring.
|
|
7
7
|
*
|
|
8
8
|
* Note that a `httpUtils.getOrderBys(...)` utility is provided by
|
|
9
|
-
* the "@
|
|
9
|
+
* the "@villedemontreal/http-request" library to
|
|
10
10
|
* easily parse and type such orderBys from an Express request.
|
|
11
11
|
*
|
|
12
12
|
* @see https://confluence.montreal.ca/pages/viewpage.action?spaceKey=AES&title=REST+API#RESTAPI-Tridelarequ%C3%AAte
|
package/src/stringUtils.ts
CHANGED
|
@@ -6,7 +6,6 @@ import * as _ from 'lodash';
|
|
|
6
6
|
* @param value Value which to add the prefix before.
|
|
7
7
|
* @param prefix Prefix to add before the value.
|
|
8
8
|
*/
|
|
9
|
-
// tslint:disable-next-line:no-shadowed-variable
|
|
10
9
|
export function prefix(value: string, prefix: string): string {
|
|
11
10
|
let result: string = value;
|
|
12
11
|
|
|
@@ -25,7 +24,6 @@ export function prefix(value: string, prefix: string): string {
|
|
|
25
24
|
* @param value Value which to add the suffix after.
|
|
26
25
|
* @param suffix Suffix to add after the value.
|
|
27
26
|
*/
|
|
28
|
-
// tslint:disable-next-line:no-shadowed-variable
|
|
29
27
|
export function suffix(value: string, suffix: string): string {
|
|
30
28
|
let result: string = value;
|
|
31
29
|
|
|
@@ -80,8 +78,10 @@ export function deDuplicateChars(
|
|
|
80
78
|
|
|
81
79
|
let regExCharsToReplace = '(';
|
|
82
80
|
for (const char of continuousCharsToReplace) {
|
|
81
|
+
// eslint-disable-next-line @/prefer-template
|
|
83
82
|
regExCharsToReplace += (char !== '\\' ? char : '\\\\') + '|';
|
|
84
83
|
}
|
|
84
|
+
// eslint-disable-next-line @/prefer-template
|
|
85
85
|
regExCharsToReplace = regExCharsToReplace.slice(0, -1) + ')+';
|
|
86
86
|
|
|
87
87
|
const regEx = new RegExp(regExCharsToReplace, 'g');
|
package/src/timer.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A Timer object. Can be used to compute time elapsed
|
|
@@ -27,7 +27,7 @@ export class Timer {
|
|
|
27
27
|
*/
|
|
28
28
|
public getMillisecondsElapsed(): number {
|
|
29
29
|
const end = process.hrtime(this.start);
|
|
30
|
-
const millisecs = Math.round(end[0] * 1000 +
|
|
30
|
+
const millisecs = Math.round(end[0] * 1000 + end[1] / 1000000);
|
|
31
31
|
|
|
32
32
|
return millisecs;
|
|
33
33
|
}
|
|
@@ -38,12 +38,17 @@ export class Timer {
|
|
|
38
38
|
* this duration.
|
|
39
39
|
*
|
|
40
40
|
* @param format the format to use for the resulting string. The
|
|
41
|
-
* default format is "HH:mm:ss.SSS". Have a look at the
|
|
41
|
+
* default format is "HH:mm:ss.SSS". Have a look at the Luxon library
|
|
42
42
|
* documentation to see how to define a custom format :
|
|
43
|
-
* https://
|
|
43
|
+
* https://moment.github.io/luxon/#/formatting?id=table-of-tokens
|
|
44
44
|
*/
|
|
45
45
|
public toString(format = 'HH:mm:ss.SSS'): string {
|
|
46
46
|
const millisecs = this.getMillisecondsElapsed();
|
|
47
|
-
|
|
47
|
+
const mappedFormat = format
|
|
48
|
+
.replace(/Y/g, 'y')
|
|
49
|
+
.replace(/D/g, 'd')
|
|
50
|
+
.replace(/A/g, 'a')
|
|
51
|
+
.replace(/Z/g, 'ZZ');
|
|
52
|
+
return DateTime.fromMillis(millisecs, { zone: 'utc' }).toFormat(mappedFormat);
|
|
48
53
|
}
|
|
49
54
|
}
|
package/src/utils.test.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// Utils functions unit tests
|
|
3
|
-
//
|
|
4
|
-
// Disabling the "function length" rule is OK for the test files.
|
|
5
|
-
// tslint:disable:max-func-body-length
|
|
6
|
-
// tslint:disable: prefer-template
|
|
7
|
-
// ==========================================
|
|
1
|
+
/* eslint-disable @/prefer-template */
|
|
8
2
|
|
|
9
3
|
import { assert } from 'chai';
|
|
10
4
|
import * as fs from 'fs-extra';
|
|
@@ -14,7 +8,6 @@ import { ExecError, utils, Utils } from './utils';
|
|
|
14
8
|
|
|
15
9
|
// A regular "function" is required here for the call to "this.timeout(...)"
|
|
16
10
|
// @see https://github.com/mochajs/mocha/issues/2018
|
|
17
|
-
// tslint:disable-next-line:only-arrow-functions ter-prefer-arrow-callback
|
|
18
11
|
describe("App's utilities functions", function () {
|
|
19
12
|
this.beforeAll(() => {
|
|
20
13
|
constants.appRoot = constants.libRoot; // because our lib is within a monorepo
|
|
@@ -728,7 +721,7 @@ describe("App's utilities functions", function () {
|
|
|
728
721
|
// ==========================================
|
|
729
722
|
// deleteFile()
|
|
730
723
|
// ==========================================
|
|
731
|
-
describe('deleteFile()',
|
|
724
|
+
describe('deleteFile()', () => {
|
|
732
725
|
it('Safe file', async () => {
|
|
733
726
|
const tempFilePath = constants.testDataDirPath + '/tmp' + new Date().getTime();
|
|
734
727
|
assert.isFalse(fs.existsSync(tempFilePath));
|
|
@@ -749,7 +742,7 @@ describe("App's utilities functions", function () {
|
|
|
749
742
|
let error = false;
|
|
750
743
|
try {
|
|
751
744
|
await utils.deleteFile(tempFilePath);
|
|
752
|
-
} catch
|
|
745
|
+
} catch {
|
|
753
746
|
error = true;
|
|
754
747
|
}
|
|
755
748
|
assert.isTrue(error);
|
|
@@ -759,7 +752,7 @@ describe("App's utilities functions", function () {
|
|
|
759
752
|
// ==========================================
|
|
760
753
|
// deleteDir()
|
|
761
754
|
// ==========================================
|
|
762
|
-
describe('deleteDir()',
|
|
755
|
+
describe('deleteDir()', () => {
|
|
763
756
|
it('Safe dir', async () => {
|
|
764
757
|
const tempFilePath = constants.testDataDirPath + '/tmp' + new Date().getTime();
|
|
765
758
|
assert.isFalse(fs.existsSync(tempFilePath));
|
|
@@ -783,7 +776,7 @@ describe("App's utilities functions", function () {
|
|
|
783
776
|
let error = false;
|
|
784
777
|
try {
|
|
785
778
|
await utils.deleteDir(tempFilePath);
|
|
786
|
-
} catch
|
|
779
|
+
} catch {
|
|
787
780
|
error = true;
|
|
788
781
|
}
|
|
789
782
|
assert.isTrue(error);
|
|
@@ -793,7 +786,7 @@ describe("App's utilities functions", function () {
|
|
|
793
786
|
// ==========================================
|
|
794
787
|
// clearDir()
|
|
795
788
|
// ==========================================
|
|
796
|
-
describe('clearDir()',
|
|
789
|
+
describe('clearDir()', () => {
|
|
797
790
|
it('Safe dir', async () => {
|
|
798
791
|
if (!fs.existsSync(constants.testDataDirPath)) {
|
|
799
792
|
fs.mkdirSync(constants.testDataDirPath);
|
|
@@ -832,7 +825,7 @@ describe("App's utilities functions", function () {
|
|
|
832
825
|
let error = false;
|
|
833
826
|
try {
|
|
834
827
|
await utils.clearDir(tempFilePath);
|
|
835
|
-
} catch
|
|
828
|
+
} catch {
|
|
836
829
|
error = true;
|
|
837
830
|
}
|
|
838
831
|
assert.isTrue(error);
|
|
@@ -842,7 +835,7 @@ describe("App's utilities functions", function () {
|
|
|
842
835
|
// ==========================================
|
|
843
836
|
// getDefinedOrNull()
|
|
844
837
|
// ==========================================
|
|
845
|
-
describe('getDefinedOrNull()',
|
|
838
|
+
describe('getDefinedOrNull()', () => {
|
|
846
839
|
it('Defined stays untouches', async () => {
|
|
847
840
|
let res = utils.getDefinedOrNull('abc');
|
|
848
841
|
assert.strictEqual(res, 'abc');
|
|
@@ -995,7 +988,7 @@ describe("App's utilities functions", function () {
|
|
|
995
988
|
try {
|
|
996
989
|
await utils.tsc([tsFilePath]);
|
|
997
990
|
assert.fail();
|
|
998
|
-
} catch
|
|
991
|
+
} catch {
|
|
999
992
|
// ok
|
|
1000
993
|
}
|
|
1001
994
|
});
|
|
@@ -1004,7 +997,7 @@ describe("App's utilities functions", function () {
|
|
|
1004
997
|
try {
|
|
1005
998
|
utils.throwNotManaged('my message', 'oups' as never);
|
|
1006
999
|
assert.fail();
|
|
1007
|
-
} catch
|
|
1000
|
+
} catch {
|
|
1008
1001
|
/* ok */
|
|
1009
1002
|
}
|
|
1010
1003
|
});
|
|
@@ -1020,22 +1013,21 @@ describe("App's utilities functions", function () {
|
|
|
1020
1013
|
}
|
|
1021
1014
|
});
|
|
1022
1015
|
it('is not object', () => {
|
|
1023
|
-
|
|
1016
|
+
const values = [
|
|
1024
1017
|
[],
|
|
1025
1018
|
[123],
|
|
1026
|
-
// tslint:disable-next-line:prefer-array-literal
|
|
1027
1019
|
[],
|
|
1028
1020
|
null,
|
|
1029
1021
|
undefined,
|
|
1030
1022
|
'',
|
|
1031
1023
|
' ',
|
|
1032
1024
|
123,
|
|
1033
|
-
,
|
|
1034
1025
|
new Date(),
|
|
1035
1026
|
() => {
|
|
1036
1027
|
/* ok */
|
|
1037
1028
|
},
|
|
1038
|
-
] as any
|
|
1029
|
+
] as any[];
|
|
1030
|
+
for (const value of values) {
|
|
1039
1031
|
assert.isFalse(utils.isObjectStrict(value));
|
|
1040
1032
|
}
|
|
1041
1033
|
});
|
package/src/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChildProcess, spawn, StdioOptions } from 'child_process';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import getPort from 'get-port';
|
|
4
5
|
import { isArray, isDate, isEqual, isFunction, isNil, isObject, isString, trimEnd } from 'lodash';
|
|
5
6
|
import * as pathUtils from 'path';
|
|
6
7
|
import { rimraf } from 'rimraf';
|
|
@@ -19,7 +20,7 @@ export class Utils {
|
|
|
19
20
|
* @param ms The number of milliseconds to sleep for.
|
|
20
21
|
*/
|
|
21
22
|
public async sleep(ms: number): Promise<void> {
|
|
22
|
-
await new Promise((resolve
|
|
23
|
+
await new Promise<void>((resolve) => {
|
|
23
24
|
setTimeout(resolve, ms);
|
|
24
25
|
});
|
|
25
26
|
}
|
|
@@ -108,6 +109,7 @@ export class Utils {
|
|
|
108
109
|
// If there were decimals but "0" only, it is
|
|
109
110
|
// still considered as an Integer, and Number(value)
|
|
110
111
|
// still have stripped those decimals....
|
|
112
|
+
// eslint-disable-next-line @/prefer-template
|
|
111
113
|
if ((asNumber + '').indexOf('.') > -1) {
|
|
112
114
|
return false;
|
|
113
115
|
}
|
|
@@ -131,6 +133,7 @@ export class Utils {
|
|
|
131
133
|
let strClean = str;
|
|
132
134
|
|
|
133
135
|
if (typeof strClean === 'number') {
|
|
136
|
+
// eslint-disable-next-line @/prefer-template
|
|
134
137
|
strClean = str + '';
|
|
135
138
|
} else if (typeof strClean !== 'string') {
|
|
136
139
|
return false;
|
|
@@ -223,7 +226,7 @@ export class Utils {
|
|
|
223
226
|
|
|
224
227
|
try {
|
|
225
228
|
return rimraf(dirPath);
|
|
226
|
-
} catch
|
|
229
|
+
} catch {
|
|
227
230
|
// ==========================================
|
|
228
231
|
// Try recursively as rimraf may sometimes
|
|
229
232
|
// fail in infrequent situations...
|
|
@@ -239,35 +242,29 @@ export class Utils {
|
|
|
239
242
|
*
|
|
240
243
|
* You can't clear a root directory using this function.
|
|
241
244
|
*/
|
|
242
|
-
public async clearDir(dirPath: string) {
|
|
245
|
+
public async clearDir(dirPath: string): Promise<void> {
|
|
243
246
|
if (!this.isSafeToDelete(dirPath)) {
|
|
244
247
|
throw new Error("Unsafe dir to clear. A dir to clear can't be at the root.");
|
|
245
248
|
}
|
|
246
249
|
// NOTE: I had to replace the globby module with fs.readdir, because globby was not
|
|
247
250
|
// listing the folders any more!
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
await this.deleteDir(filePath);
|
|
258
|
-
} else {
|
|
259
|
-
await this.deleteFile(filePath);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
resolve();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
251
|
+
const paths = await fs.promises.readdir(dirPath);
|
|
252
|
+
for (const path of paths) {
|
|
253
|
+
const filePath = pathUtils.join(dirPath, path);
|
|
254
|
+
if (fs.lstatSync(filePath).isDirectory()) {
|
|
255
|
+
await this.deleteDir(filePath);
|
|
256
|
+
} else {
|
|
257
|
+
await this.deleteFile(filePath);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
265
260
|
}
|
|
266
261
|
|
|
267
262
|
protected get tscCompilerOptions(): string[] {
|
|
268
263
|
if (!this.tscCompilerOptionsParams) {
|
|
269
264
|
this.tscCompilerOptionsParams = [];
|
|
270
|
-
const compilerOptions = tsconfig.load_file_sync(
|
|
265
|
+
const compilerOptions = tsconfig.load_file_sync(
|
|
266
|
+
path.join(constants.appRoot, 'tsconfig.json'),
|
|
267
|
+
);
|
|
271
268
|
|
|
272
269
|
for (const key of Object.keys(compilerOptions)) {
|
|
273
270
|
// ==========================================
|
|
@@ -286,7 +283,7 @@ export class Utils {
|
|
|
286
283
|
compilerOptions[key] = false;
|
|
287
284
|
}
|
|
288
285
|
|
|
289
|
-
this.tscCompilerOptionsParams.push(
|
|
286
|
+
this.tscCompilerOptionsParams.push(`--${key}`);
|
|
290
287
|
this.tscCompilerOptionsParams.push(compilerOptions[key]);
|
|
291
288
|
}
|
|
292
289
|
}
|
|
@@ -451,7 +448,7 @@ export class Utils {
|
|
|
451
448
|
if (/[^A-Za-z0-9_/.:=-]/.test(a)) {
|
|
452
449
|
a = a.replace(/\\\\/g, '\\');
|
|
453
450
|
a = a.replace(/\\/g, '\\\\');
|
|
454
|
-
a =
|
|
451
|
+
a = `'${a.replace(/'/g, "'\\''")}'`;
|
|
455
452
|
a = a
|
|
456
453
|
.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
|
|
457
454
|
.replace(/\\'''/g, "\\'") // remove non-escaped single-quote if there are enclosed between 2 escaped
|
|
@@ -463,7 +460,7 @@ export class Utils {
|
|
|
463
460
|
|
|
464
461
|
public shellescapeArgumentForWindowsCmd(a: string) {
|
|
465
462
|
if (/[^A-Za-z0-9_/\\.$:=-]/.test(a)) {
|
|
466
|
-
a =
|
|
463
|
+
a = `"${a.replace(/"/g, '""')}"`;
|
|
467
464
|
a = a
|
|
468
465
|
.replace('\n', '\\n') // handle new lines
|
|
469
466
|
.replace('\t', '\\t'); // handle tabs
|
|
@@ -484,7 +481,7 @@ export class Utils {
|
|
|
484
481
|
outputHandler: dataHandler,
|
|
485
482
|
useShellOption,
|
|
486
483
|
disableConsoleOutputs: !dataHandler,
|
|
487
|
-
}).then((
|
|
484
|
+
}).then(() => {
|
|
488
485
|
// nothing, returns void
|
|
489
486
|
});
|
|
490
487
|
}
|
|
@@ -622,7 +619,6 @@ export class Utils {
|
|
|
622
619
|
/**
|
|
623
620
|
* Error thrown when a process launched with `exec()` fails.
|
|
624
621
|
*/
|
|
625
|
-
// tslint:disable-next-line: max-classes-per-file
|
|
626
622
|
export class ExecError extends Error {
|
|
627
623
|
constructor(
|
|
628
624
|
message: string,
|
|
@@ -638,7 +634,7 @@ export function getValueDescription(value: any): string {
|
|
|
638
634
|
|
|
639
635
|
export function getValueDescriptionWithType(value: any): string {
|
|
640
636
|
const valueType = isObject(value) ? value.constructor.name : typeof value;
|
|
641
|
-
return getValueDescription(value)
|
|
637
|
+
return `${getValueDescription(value)} (${valueType})`;
|
|
642
638
|
}
|
|
643
639
|
|
|
644
640
|
export const utils: Utils = new Utils();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"c5ab12da-db6a-49f4-8259-c0a46f48de28.d.ts","sourceRoot":"","sources":["../../../test-data/test_throwNotManaged/c5ab12da-db6a-49f4-8259-c0a46f48de28.ts"],"names":[],"mappings":""}
|