@24i/bigscreen-sdk 0.9.9-alpha.2147 → 0.9.9-alpha.2149
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/package.json +3 -4
- package/packages/developer-tools/src/DeveloperConsole/utils/formatTime.ts +13 -0
- package/packages/developer-tools/src/DeveloperConsole/utils/index.ts +3 -0
- package/packages/developer-tools/src/DeveloperConsole/utils/stringify.ts +10 -0
- package/packages/developer-tools/src/DeveloperConsole/utils/styles.ts +24 -0
- package/packages/developer-tools/src/EnvironmentSelection/utils/format.ts +4 -0
- package/packages/developer-tools/src/TechnicalInfo/utils/eme01bSupport.ts +58 -0
- package/packages/developer-tools/src/TechnicalInfo/utils/emeSupport.ts +80 -0
- package/packages/developer-tools/src/TechnicalInfo/utils/formatTimezone.ts +15 -0
- package/packages/developer-tools/src/TechnicalInfo/utils/mseSupport.ts +14 -0
- package/packages/developer-tools/src/utils/reload.ts +6 -0
- package/packages/keyboard/src/utils/caret.ts +48 -0
- package/packages/keyboard/src/utils/generateFocusMatrixFromStringMatrix.ts +12 -0
- package/packages/keyboard/src/utils/input.ts +40 -0
- package/packages/list/utils/index.ts +1 -0
- package/packages/logger/src/utils/deviceInfo.ts +25 -0
- package/packages/logger/src/utils/index.ts +1 -0
- package/packages/utils/README.md +336 -0
- package/packages/utils/src/addClass.ts +9 -0
- package/packages/utils/src/counter.ts +47 -0
- package/packages/utils/src/debounce.ts +54 -0
- package/packages/utils/src/displayToggler.scss +3 -0
- package/packages/utils/src/displayToggler.ts +38 -0
- package/packages/utils/src/elementUtils.ts +58 -0
- package/packages/utils/src/generateUuid.ts +19 -0
- package/packages/utils/src/index.ts +35 -0
- package/packages/utils/src/memoryInfo.ts +21 -0
- package/packages/utils/src/nTimes.ts +9 -0
- package/packages/utils/src/noop.ts +1 -0
- package/packages/utils/src/offsetPosition.ts +56 -0
- package/packages/utils/src/removeClass.ts +9 -0
- package/packages/utils/src/scaledImage.ts +21 -0
- package/packages/utils/src/stopEvent.ts +10 -0
- package/packages/utils/src/timeConstants.ts +11 -0
- package/packages/utils/src/timers/createInterval.ts +19 -0
- package/packages/utils/src/timers/createTimeout.ts +22 -0
- package/packages/utils/src/timers/index.ts +4 -0
- package/packages/utils/src/timers/runAsync.ts +3 -0
- package/packages/utils/src/timers/types.ts +9 -0
- package/packages/utils/src/wait.ts +1 -0
- package/packages/utils/src/xhr/index.ts +11 -0
- package/packages/utils/src/xhr/xhrSend.ts +139 -0
- package/packages/utils/src/xhr/xhrSendRetry.ts +77 -0
- package/utils/create-export-maps/index.ts +3 -0
- package/utils/create-export-maps/src/__tests__/createExportMaps.spec.ts +50 -0
- package/utils/create-export-maps/src/createExportMaps.ts +61 -0
- package/utils/create-package/README.md +40 -0
- package/utils/create-package/src/createPackage.ts +72 -0
- package/utils/create-package/src/index.ts +3 -0
- package/utils/create-package/src/questionnaire/questions.ts +19 -0
- package/utils/create-package/src/settings/Settings.ts +5 -0
- package/utils/create-package/src/types.ts +4 -0
- package/utils/create-package/templates/typescript/README.md +9 -0
- package/utils/create-package/templates/typescript/exports.json +6 -0
- package/utils/create-package/templates/typescript/src/index.ts +0 -0
- package/utils/run-scripts/index.ts +3 -0
- package/utils/run-scripts/src/__tests__/runScripts.spec.ts +45 -0
- package/utils/run-scripts/src/runScripts.ts +20 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Reference } from '@24i/bigscreen-sdk/jsx';
|
|
2
|
+
import { isRtl } from '@24i/bigscreen-sdk/i18n';
|
|
3
|
+
|
|
4
|
+
type WritableCSSKeys = Exclude<(keyof CSSStyleDeclaration), 'length' | 'parentRule'>;
|
|
5
|
+
|
|
6
|
+
const isCssSupported = <T extends WritableCSSKeys>(
|
|
7
|
+
property: T, value: CSSStyleDeclaration[T],
|
|
8
|
+
) => {
|
|
9
|
+
const el = document.createElement('div');
|
|
10
|
+
if (el.style[property] === undefined) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
el.style[property] = value;
|
|
14
|
+
return !!el.style[property];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let useTransform = false;
|
|
18
|
+
let transformKey = '';
|
|
19
|
+
if (isCssSupported('transform', 'translate3d(0px, 0px, 0px)')) {
|
|
20
|
+
useTransform = true;
|
|
21
|
+
transformKey = 'transform';
|
|
22
|
+
} else if (isCssSupported('webkitTransform', 'translate3d(0px, 0px, 0px)')) {
|
|
23
|
+
useTransform = true;
|
|
24
|
+
transformKey = 'webkitTransform';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function offsetPosition(
|
|
28
|
+
reference: Reference,
|
|
29
|
+
{ x, y }: { x?: number | string, y?: number | string },
|
|
30
|
+
rtl: boolean = isRtl(),
|
|
31
|
+
): void {
|
|
32
|
+
if (!reference.current) return;
|
|
33
|
+
|
|
34
|
+
const tx = typeof x === 'number' ? `${rtl ? -x : x}px` : (x || 0);
|
|
35
|
+
const ty = typeof y === 'number' ? `${y}px` : (y || 0);
|
|
36
|
+
if (useTransform) {
|
|
37
|
+
reference.current.style[transformKey] = `translate3d(${tx}, ${ty}, 0)`;
|
|
38
|
+
} else {
|
|
39
|
+
reference.current.style.top = ty;
|
|
40
|
+
reference.current.style.left = tx;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line no-redeclare
|
|
45
|
+
namespace offsetPosition {
|
|
46
|
+
export const getTransformKey = () => transformKey;
|
|
47
|
+
export const usesTransform = () => useTransform;
|
|
48
|
+
export const forceTransform = () => {
|
|
49
|
+
useTransform = true;
|
|
50
|
+
};
|
|
51
|
+
export const forceTopLeft = () => {
|
|
52
|
+
useTransform = false;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { offsetPosition };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const DEFAULT_SCALING_ENDPOINT = 'http://imageresize.24i.com';
|
|
2
|
+
|
|
3
|
+
type Dimensions = {
|
|
4
|
+
height: number,
|
|
5
|
+
width: number,
|
|
6
|
+
fit?: 'cover' | 'contain' | 'inside',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const attention = (fit: Dimensions['fit']) => (
|
|
10
|
+
fit === 'cover' ? '&a=attention' : ''
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const scaledImage = (
|
|
14
|
+
url: string,
|
|
15
|
+
{ height, width, fit = 'cover' }: Dimensions,
|
|
16
|
+
scalingEndpoint: string = DEFAULT_SCALING_ENDPOINT,
|
|
17
|
+
) => (
|
|
18
|
+
`${scalingEndpoint}?url=${url}&h=${height}&w=${width}&fit=${fit}${attention(fit)}`
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export { scaledImage, Dimensions };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const SECONDS_PER_MINUTE = 60;
|
|
2
|
+
export const MINUTES_PER_HALF_HOUR = 30;
|
|
3
|
+
export const MINUTES_PER_HOUR = 60;
|
|
4
|
+
export const HOURS_PER_DAY = 24;
|
|
5
|
+
export const DAYS_PER_WEEK = 7;
|
|
6
|
+
|
|
7
|
+
export const SECOND_IN_MS = 1000;
|
|
8
|
+
export const MINUTE_IN_MS = SECOND_IN_MS * SECONDS_PER_MINUTE;
|
|
9
|
+
export const HALF_HOUR_IN_MS = MINUTE_IN_MS * MINUTES_PER_HALF_HOUR;
|
|
10
|
+
export const HOUR_IN_MS = MINUTE_IN_MS * MINUTES_PER_HOUR;
|
|
11
|
+
export const DAY_IN_MS = HOUR_IN_MS * HOURS_PER_DAY;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TimedObject, TimedFunction } from './types';
|
|
2
|
+
|
|
3
|
+
export const createInterval = () => {
|
|
4
|
+
const intervalObject: TimedObject = {
|
|
5
|
+
id: null,
|
|
6
|
+
set: (callbackFn: () => void, timeMs: number) => {
|
|
7
|
+
intervalObject.clear();
|
|
8
|
+
intervalObject.id = window.setInterval(callbackFn, timeMs);
|
|
9
|
+
},
|
|
10
|
+
clear: () => {
|
|
11
|
+
if (intervalObject.id !== null) {
|
|
12
|
+
window.clearInterval(intervalObject.id);
|
|
13
|
+
intervalObject.id = null;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
isActive: () => intervalObject.id !== null,
|
|
17
|
+
};
|
|
18
|
+
return intervalObject as TimedFunction;
|
|
19
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TimedObject, TimedFunction } from './types';
|
|
2
|
+
|
|
3
|
+
export const createTimeout = () => {
|
|
4
|
+
const timeoutObject: TimedObject = {
|
|
5
|
+
id: null,
|
|
6
|
+
set: (callbackFn: () => void, timeMs: number) => {
|
|
7
|
+
timeoutObject.clear();
|
|
8
|
+
timeoutObject.id = window.setTimeout(() => {
|
|
9
|
+
timeoutObject.id = null;
|
|
10
|
+
callbackFn();
|
|
11
|
+
}, timeMs);
|
|
12
|
+
},
|
|
13
|
+
clear: () => {
|
|
14
|
+
if (timeoutObject.id !== null) {
|
|
15
|
+
window.clearTimeout(timeoutObject.id);
|
|
16
|
+
timeoutObject.id = null;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
isActive: () => timeoutObject.id !== null,
|
|
20
|
+
};
|
|
21
|
+
return timeoutObject as TimedFunction;
|
|
22
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const wait = (waitMs: number) => new Promise((r) => setTimeout(r, waitMs));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export {
|
|
2
|
+
xhrSend,
|
|
3
|
+
type XhrOptions, type XhrResponse,
|
|
4
|
+
} from './xhrSend';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
xhrSendRetry, defaultRetryConfig,
|
|
8
|
+
type RetryConfig, type RSOptions, type ShouldRetryParam,
|
|
9
|
+
} from './xhrSendRetry';
|
|
10
|
+
|
|
11
|
+
export { mockXhr } from './__mocks__/xhr';
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple wrap "xhrSend" function over XMLHttpRequest.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const HttpStatusCode = {
|
|
6
|
+
OK: 200,
|
|
7
|
+
MULTIPLE_CHOICES: 300,
|
|
8
|
+
UNAUTHORIZED: 401,
|
|
9
|
+
FORBIDDEN: 403,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type HttpMethods = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
13
|
+
|
|
14
|
+
export type XhrOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* HTTP request method, e.g. POST.
|
|
17
|
+
*/
|
|
18
|
+
method: HttpMethods,
|
|
19
|
+
/**
|
|
20
|
+
* The object with HTTP request headers.
|
|
21
|
+
*/
|
|
22
|
+
headers: { [key: string]: string },
|
|
23
|
+
/**
|
|
24
|
+
* The number of milliseconds a request can take before automatically being terminated.
|
|
25
|
+
* Value 0 means there is no timeout.
|
|
26
|
+
*/
|
|
27
|
+
timeoutInMs: number,
|
|
28
|
+
/**
|
|
29
|
+
* The body of data to be sent in the XHR request, a string (e.g. JSON-encoded).
|
|
30
|
+
*/
|
|
31
|
+
body?: string,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type XhrResponse = {
|
|
35
|
+
/**
|
|
36
|
+
* The body content of response.
|
|
37
|
+
*/
|
|
38
|
+
dataText: string,
|
|
39
|
+
/**
|
|
40
|
+
* The object corresponding to the given JSON body text.
|
|
41
|
+
*/
|
|
42
|
+
dataJson: Object | undefined,
|
|
43
|
+
/**
|
|
44
|
+
* The status code of the response (e.g., 200 for a success).
|
|
45
|
+
*/
|
|
46
|
+
status: number,
|
|
47
|
+
/**
|
|
48
|
+
* The status message corresponding to the status code (e.g., "OK" for 200).
|
|
49
|
+
*/
|
|
50
|
+
statusText: string
|
|
51
|
+
/**
|
|
52
|
+
* The state whether the response was successful (status in the range 200-299) or not.
|
|
53
|
+
*/
|
|
54
|
+
ok: boolean,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const defaultOptions: XhrOptions = {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
headers: {},
|
|
60
|
+
timeoutInMs: 0,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Sends HTTP request
|
|
65
|
+
*
|
|
66
|
+
* @param url The URL to access.
|
|
67
|
+
* @param options The optional options object.
|
|
68
|
+
* @param options.method [options.method='GET'] HTTP request method.
|
|
69
|
+
* @param options.timeoutInMs [options.timeoutInMs=0] The number of milliseconds
|
|
70
|
+
* a request can take before automatically being terminated.
|
|
71
|
+
* @param options.headers [options.headers={}] The object with HTTP request headers.
|
|
72
|
+
* @param options.body [options.body] The body of data to be sent in the XHR request,
|
|
73
|
+
* a string (e.g. JSON-encoded).
|
|
74
|
+
* @returns Promise rejects with TypeError. Resolves with XhrResponse object.
|
|
75
|
+
*/
|
|
76
|
+
function xhrSend(url: string, options: Partial<XhrOptions> = {}): Promise<XhrResponse | TypeError> {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const method = options.method || defaultOptions.method;
|
|
79
|
+
const headers = options.headers || defaultOptions.headers;
|
|
80
|
+
const timeoutInMs = options.timeoutInMs !== undefined
|
|
81
|
+
? options.timeoutInMs : defaultOptions.timeoutInMs;
|
|
82
|
+
const { body } = options;
|
|
83
|
+
|
|
84
|
+
const xhr = new XMLHttpRequest();
|
|
85
|
+
|
|
86
|
+
function onLoad() {
|
|
87
|
+
const dataText = 'response' in xhr ? xhr.response : xhr.responseText;
|
|
88
|
+
const dataJson = parseJson(dataText);
|
|
89
|
+
const status = xhr.status === undefined ? HttpStatusCode.OK : xhr.status;
|
|
90
|
+
const statusText = xhr.statusText === undefined ? 'OK' : xhr.statusText;
|
|
91
|
+
const ok = status >= HttpStatusCode.OK && status < HttpStatusCode.MULTIPLE_CHOICES;
|
|
92
|
+
resolve({ dataText, dataJson, status, statusText, ok });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function onError() {
|
|
96
|
+
const error = new TypeError('Network request failed');
|
|
97
|
+
reject(error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function onTimeout() {
|
|
101
|
+
const error = new TypeError('Network request failed - timeout');
|
|
102
|
+
error.name = 'TimeoutError';
|
|
103
|
+
reject(error);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function onAbort() {
|
|
107
|
+
const error = new TypeError('Aborted');
|
|
108
|
+
error.name = 'AbortedError';
|
|
109
|
+
reject(error);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
xhr.onload = onLoad;
|
|
113
|
+
xhr.onerror = onError;
|
|
114
|
+
xhr.ontimeout = onTimeout;
|
|
115
|
+
xhr.onabort = onAbort;
|
|
116
|
+
xhr.open(method, url, true);
|
|
117
|
+
|
|
118
|
+
xhr.timeout = timeoutInMs;
|
|
119
|
+
|
|
120
|
+
const keys = Object.keys(headers);
|
|
121
|
+
for (let i = 0; i < keys.length; i++) {
|
|
122
|
+
const name = keys[i];
|
|
123
|
+
const value = headers[name];
|
|
124
|
+
xhr.setRequestHeader(name, value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
xhr.send(typeof body === 'undefined' ? null : body);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function parseJson(text: string): string | undefined {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(text);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { xhrSend };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { xhrSend, XhrResponse, XhrOptions } from './xhrSend';
|
|
2
|
+
|
|
3
|
+
export type RetryConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* The number of times to retry the request. Defaults to 0.
|
|
6
|
+
*/
|
|
7
|
+
retry?: number,
|
|
8
|
+
/**
|
|
9
|
+
* The amount of time in milliseconds to initially delay the retry. Defaults to 1000.
|
|
10
|
+
*/
|
|
11
|
+
retryDelay?: number,
|
|
12
|
+
/**
|
|
13
|
+
* The number of retries already attempted.
|
|
14
|
+
*/
|
|
15
|
+
currentRetryAttempt?: number,
|
|
16
|
+
/**
|
|
17
|
+
* Function to invoke which determines if you should retry
|
|
18
|
+
*/
|
|
19
|
+
shouldRetry?: (data: ShouldRetryParam) => boolean,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RSOptions = {
|
|
23
|
+
retryConfig: RetryConfig;
|
|
24
|
+
} & XhrOptions;
|
|
25
|
+
|
|
26
|
+
export type ShouldRetryParam = {
|
|
27
|
+
options: Partial<RSOptions>,
|
|
28
|
+
response?: XhrResponse,
|
|
29
|
+
error?: any,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function shouldRetryRequest(data: ShouldRetryParam) {
|
|
33
|
+
const { error, response } = data;
|
|
34
|
+
if (error) return true;
|
|
35
|
+
if (!response || !response.ok) return true;
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const defaultRetryConfig = {
|
|
40
|
+
retry: 0,
|
|
41
|
+
retryDelay: 1000,
|
|
42
|
+
currentRetryAttempt: 0,
|
|
43
|
+
shouldRetry: shouldRetryRequest,
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
export async function xhrSendRetry(url: string, options: Partial<RSOptions> = {}):
|
|
47
|
+
Promise<XhrResponse | TypeError> {
|
|
48
|
+
const config = options.retryConfig || {} as RetryConfig;
|
|
49
|
+
config.retry = config.retry ?? defaultRetryConfig.retry;
|
|
50
|
+
config.retryDelay = config.retryDelay ?? defaultRetryConfig.retryDelay;
|
|
51
|
+
config.currentRetryAttempt = config.currentRetryAttempt
|
|
52
|
+
?? defaultRetryConfig.currentRetryAttempt;
|
|
53
|
+
|
|
54
|
+
options.retryConfig = config;
|
|
55
|
+
let response;
|
|
56
|
+
let error;
|
|
57
|
+
try {
|
|
58
|
+
response = await xhrSend(url, options) as XhrResponse;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
error = e;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (config.currentRetryAttempt === config.retry) {
|
|
64
|
+
if (response) return response;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const shouldRetryFn = config.shouldRetry || defaultRetryConfig.shouldRetry;
|
|
69
|
+
const shouldRetry = shouldRetryFn({ options, response, error });
|
|
70
|
+
if (shouldRetry) {
|
|
71
|
+
await new Promise((res) => window.setTimeout(res, config.retryDelay));
|
|
72
|
+
config.currentRetryAttempt += 1;
|
|
73
|
+
return xhrSendRetry(url, options);
|
|
74
|
+
}
|
|
75
|
+
if (response) return response;
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
|
+
import { createExportMaps } from '../createExportMaps';
|
|
3
|
+
|
|
4
|
+
jest.mock('fs', () => ({
|
|
5
|
+
readdirSync: jest.fn(() => [
|
|
6
|
+
'adobe-heartbeat',
|
|
7
|
+
'animations',
|
|
8
|
+
'.DS-STORE',
|
|
9
|
+
]),
|
|
10
|
+
lstatSync: jest.fn((name: string) => ({
|
|
11
|
+
isDirectory: () => name.indexOf('.DS-STORE') === -1
|
|
12
|
+
})),
|
|
13
|
+
existsSync: jest.fn(() => true),
|
|
14
|
+
writeFileSync: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
jest.mock('../../../../packages/adobe-heartbeat/exports.json', () => ({
|
|
18
|
+
name: 'adobe-heartbeat',
|
|
19
|
+
entryPoints: {
|
|
20
|
+
'/': 'src/index.ts'
|
|
21
|
+
},
|
|
22
|
+
}));
|
|
23
|
+
jest.mock('../../../../packages/animations/exports.json', () => ({
|
|
24
|
+
name: 'animations',
|
|
25
|
+
entryPoints: {
|
|
26
|
+
'/': 'src/index.ts',
|
|
27
|
+
'mock': 'src/__mocks__/JSAnimations.ts'
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.mock('../../../../package.json', () => ({
|
|
32
|
+
name: 'mock-root-package',
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe('createExportMaps', () => {
|
|
36
|
+
it('should add all exports to the package.json', () => {
|
|
37
|
+
createExportMaps();
|
|
38
|
+
expect(writeFileSync).toHaveBeenCalledWith(
|
|
39
|
+
expect.stringContaining('package.json'),
|
|
40
|
+
JSON.stringify({
|
|
41
|
+
name: 'mock-root-package',
|
|
42
|
+
exports: {
|
|
43
|
+
'./adobe-heartbeat': './packages/adobe-heartbeat/src/index.ts',
|
|
44
|
+
'./animations/mock': './packages/animations/src/__mocks__/JSAnimations.ts',
|
|
45
|
+
'./animations': './packages/animations/src/index.ts',
|
|
46
|
+
}
|
|
47
|
+
}, null, 4),
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import packageJson from '../../../package.json';
|
|
4
|
+
|
|
5
|
+
const packagesPath = resolve('./packages');
|
|
6
|
+
|
|
7
|
+
const allExports: Record<string, string> = {};
|
|
8
|
+
|
|
9
|
+
type ExportsDefinition = {
|
|
10
|
+
name: string,
|
|
11
|
+
entryPoints: Record<string, string>,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const handleExport = ({ name, entryPoints }: ExportsDefinition) => {
|
|
15
|
+
let hasRootExport = false;
|
|
16
|
+
Object.entries(entryPoints).forEach(([exportAlias, exportPath]) => {
|
|
17
|
+
if (exportAlias === '/') {
|
|
18
|
+
hasRootExport = true;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const parsedAlias = `/${exportAlias}`;
|
|
22
|
+
allExports[`${name}${parsedAlias}`] = `${name}/${exportPath}`;
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Typescript resolves aliases from top. Root export would overtake any other pathed export.
|
|
26
|
+
* Therefore root export has to be last in the generated list.
|
|
27
|
+
*/
|
|
28
|
+
if (hasRootExport) {
|
|
29
|
+
allExports[name] = `${name}/${entryPoints['/']}`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const loadAllExports = () => {
|
|
34
|
+
const fileNames = fs.readdirSync(packagesPath);
|
|
35
|
+
fileNames.forEach((fileName) => {
|
|
36
|
+
const packagePath = resolve(packagesPath, fileName);
|
|
37
|
+
const isDirectory = fs.lstatSync(packagePath).isDirectory();
|
|
38
|
+
if (isDirectory) {
|
|
39
|
+
const exportsPath = resolve(packagePath, 'exports.json');
|
|
40
|
+
if (fs.existsSync(exportsPath)) {
|
|
41
|
+
// eslint-disable-next-line import/no-dynamic-require, global-require
|
|
42
|
+
const exports = require(exportsPath);
|
|
43
|
+
handleExport(exports);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const createPackageJsonMapping = () => {
|
|
50
|
+
const exports: Record<string, string> = {};
|
|
51
|
+
Object.entries(allExports).forEach(([exportAlias, exportPath]) => {
|
|
52
|
+
exports[`./${exportAlias}`] = `./packages/${exportPath}`;
|
|
53
|
+
});
|
|
54
|
+
(packageJson.exports as Record<string, string>) = exports;
|
|
55
|
+
fs.writeFileSync(resolve('./package.json'), JSON.stringify(packageJson, null, 4));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const createExportMaps = () => {
|
|
59
|
+
loadAllExports();
|
|
60
|
+
createPackageJsonMapping();
|
|
61
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: README
|
|
3
|
+
title: Create Package
|
|
4
|
+
hide_title: true
|
|
5
|
+
sidebar_label: Create Package
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Package Initializer
|
|
9
|
+
An initializer for packages in `SmartApps BIGscreen SDK` monorepo helps to set up
|
|
10
|
+
folder structure with basic config files like eslint, tsconfig etc.
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
Originally designed as [npm initializer](https://docs.npmjs.com/cli/init#description),
|
|
14
|
+
now degraded to the local script without any ambition to be published.
|
|
15
|
+
|
|
16
|
+
Go to SmartApps BIGscreen SDK monorepo root folder and type:
|
|
17
|
+
```
|
|
18
|
+
$ npm run create-package -- <my-package-name>
|
|
19
|
+
```
|
|
20
|
+
You will be prompted for basic package information.
|
|
21
|
+
|
|
22
|
+
## What it does
|
|
23
|
+
1. Create folder `./packages/<my-package-name>`
|
|
24
|
+
2. Guide you through a questionnaire to setup the project
|
|
25
|
+
* e.g. auto-suggested 24i package name in form: `@24i/smartapps-bigscreenen-<my-package-name>`
|
|
26
|
+
3. Copy the template files (src, tsconfig, eslintrc, jest.config, readme, etc.)
|
|
27
|
+
4. Generate package.json with all the project details
|
|
28
|
+
5. Create symlink in docs `./docs/packages/<my-package-name>/README.md`
|
|
29
|
+
|
|
30
|
+
## Motivation
|
|
31
|
+
Every time that I start a new package inside SmartApps BIGscreen SDK monorepo, I hate
|
|
32
|
+
to go to other project folder, copy files like eslintrc, tsconfig, create folder
|
|
33
|
+
structure, etc.
|
|
34
|
+
|
|
35
|
+
With this in mind, the motivation to build this package started as a DRY (Do not
|
|
36
|
+
repeat yourself) thing.
|
|
37
|
+
|
|
38
|
+
This package is intended to automate the initialization of new packages and with
|
|
39
|
+
this have a new folder with everything ready to work. Behind this, also to ensure
|
|
40
|
+
we are using the unified setup for each package inside SmartApps BIGscreen SDK monorepo.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as fse from 'fs-extra';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import minimist from 'minimist';
|
|
8
|
+
import { promptPackageDetails } from './questionnaire/questions';
|
|
9
|
+
import { Settings } from './settings/Settings';
|
|
10
|
+
import { IPackageOptions } from './types';
|
|
11
|
+
|
|
12
|
+
async function isDirectoryEmpty(dirPath: string) {
|
|
13
|
+
const files = await fs.promises.readdir(dirPath);
|
|
14
|
+
return !files.length;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function copyTemplateFiles(templateDir: string, targetDir: string) {
|
|
18
|
+
return fse.copy(templateDir, targetDir);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function updateExportsJsonFile(filePath: string, options: IPackageOptions) {
|
|
22
|
+
let data = fs.readFileSync(filePath, 'utf8');
|
|
23
|
+
data = data.replace(/("name": ")(.*)(")/, `$1${options.name}$3`);
|
|
24
|
+
fs.writeFileSync(filePath, data);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseArguments(rawArgv: Array<string>) {
|
|
28
|
+
const argv = minimist(rawArgv.slice(2));
|
|
29
|
+
const dirName = argv._[0];
|
|
30
|
+
|
|
31
|
+
if (!dirName) {
|
|
32
|
+
console.error('Error: Missing package name');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
dirName,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function main() {
|
|
42
|
+
const { dirName: destDirName } = parseArguments(process.argv);
|
|
43
|
+
const destDir = path.join(process.cwd(), 'packages/', destDirName);
|
|
44
|
+
if (fs.existsSync(destDir)) {
|
|
45
|
+
console.error(`Error: Target directory "${destDir}" already exists`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
49
|
+
|
|
50
|
+
const isDirEmpty = await isDirectoryEmpty(destDir);
|
|
51
|
+
if (!isDirEmpty) {
|
|
52
|
+
console.error(`Error: Target directory "${destDir}" is not empty.`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const settings = { ...Settings };
|
|
57
|
+
settings.name = `${Settings.name}${destDirName}`.toLowerCase();
|
|
58
|
+
|
|
59
|
+
const details = await promptPackageDetails(settings);
|
|
60
|
+
const templateDir = path.join(__dirname, '..', 'templates/typescript');
|
|
61
|
+
|
|
62
|
+
await copyTemplateFiles(templateDir, destDir);
|
|
63
|
+
const exportsJsonFile = path.join(destDir, 'exports.json');
|
|
64
|
+
updateExportsJsonFile(exportsJsonFile, details);
|
|
65
|
+
|
|
66
|
+
// create symlink in docs to README.md
|
|
67
|
+
const docsSymlinkTarget = path.join('../../../packages/', destDirName, '/README.md');
|
|
68
|
+
const docsSymlinkPath = path.join(process.cwd(), 'docs/packages/', destDirName, '/README.md');
|
|
69
|
+
await fse.ensureSymlink(docsSymlinkTarget, docsSymlinkPath);
|
|
70
|
+
|
|
71
|
+
console.log(`${chalk.green('Success')}, package has been initialized!`);
|
|
72
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as inquirer from 'inquirer';
|
|
2
|
+
|
|
3
|
+
interface IPromptPackageDetailsDefaults {
|
|
4
|
+
/** Package name */
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function promptPackageDetails(defaultValues: IPromptPackageDetailsDefaults) {
|
|
9
|
+
return inquirer.prompt([
|
|
10
|
+
{
|
|
11
|
+
type: 'input',
|
|
12
|
+
name: 'name',
|
|
13
|
+
message: 'Package name:',
|
|
14
|
+
default: defaultValues.name,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { promptPackageDetails };
|