@24i/bigscreen-sdk 0.9.9-alpha.2148 → 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.
Files changed (58) hide show
  1. package/package.json +1 -1
  2. package/packages/developer-tools/src/DeveloperConsole/utils/formatTime.ts +13 -0
  3. package/packages/developer-tools/src/DeveloperConsole/utils/index.ts +3 -0
  4. package/packages/developer-tools/src/DeveloperConsole/utils/stringify.ts +10 -0
  5. package/packages/developer-tools/src/DeveloperConsole/utils/styles.ts +24 -0
  6. package/packages/developer-tools/src/EnvironmentSelection/utils/format.ts +4 -0
  7. package/packages/developer-tools/src/TechnicalInfo/utils/eme01bSupport.ts +58 -0
  8. package/packages/developer-tools/src/TechnicalInfo/utils/emeSupport.ts +80 -0
  9. package/packages/developer-tools/src/TechnicalInfo/utils/formatTimezone.ts +15 -0
  10. package/packages/developer-tools/src/TechnicalInfo/utils/mseSupport.ts +14 -0
  11. package/packages/developer-tools/src/utils/reload.ts +6 -0
  12. package/packages/keyboard/src/utils/caret.ts +48 -0
  13. package/packages/keyboard/src/utils/generateFocusMatrixFromStringMatrix.ts +12 -0
  14. package/packages/keyboard/src/utils/input.ts +40 -0
  15. package/packages/list/utils/index.ts +1 -0
  16. package/packages/logger/src/utils/deviceInfo.ts +25 -0
  17. package/packages/logger/src/utils/index.ts +1 -0
  18. package/packages/utils/README.md +336 -0
  19. package/packages/utils/src/addClass.ts +9 -0
  20. package/packages/utils/src/counter.ts +47 -0
  21. package/packages/utils/src/debounce.ts +54 -0
  22. package/packages/utils/src/displayToggler.scss +3 -0
  23. package/packages/utils/src/displayToggler.ts +38 -0
  24. package/packages/utils/src/elementUtils.ts +58 -0
  25. package/packages/utils/src/generateUuid.ts +19 -0
  26. package/packages/utils/src/index.ts +35 -0
  27. package/packages/utils/src/memoryInfo.ts +21 -0
  28. package/packages/utils/src/nTimes.ts +9 -0
  29. package/packages/utils/src/noop.ts +1 -0
  30. package/packages/utils/src/offsetPosition.ts +56 -0
  31. package/packages/utils/src/removeClass.ts +9 -0
  32. package/packages/utils/src/scaledImage.ts +21 -0
  33. package/packages/utils/src/stopEvent.ts +10 -0
  34. package/packages/utils/src/timeConstants.ts +11 -0
  35. package/packages/utils/src/timers/createInterval.ts +19 -0
  36. package/packages/utils/src/timers/createTimeout.ts +22 -0
  37. package/packages/utils/src/timers/index.ts +4 -0
  38. package/packages/utils/src/timers/runAsync.ts +3 -0
  39. package/packages/utils/src/timers/types.ts +9 -0
  40. package/packages/utils/src/wait.ts +1 -0
  41. package/packages/utils/src/xhr/index.ts +11 -0
  42. package/packages/utils/src/xhr/xhrSend.ts +139 -0
  43. package/packages/utils/src/xhr/xhrSendRetry.ts +77 -0
  44. package/utils/create-export-maps/index.ts +3 -0
  45. package/utils/create-export-maps/src/__tests__/createExportMaps.spec.ts +50 -0
  46. package/utils/create-export-maps/src/createExportMaps.ts +61 -0
  47. package/utils/create-package/README.md +40 -0
  48. package/utils/create-package/src/createPackage.ts +72 -0
  49. package/utils/create-package/src/index.ts +3 -0
  50. package/utils/create-package/src/questionnaire/questions.ts +19 -0
  51. package/utils/create-package/src/settings/Settings.ts +5 -0
  52. package/utils/create-package/src/types.ts +4 -0
  53. package/utils/create-package/templates/typescript/README.md +9 -0
  54. package/utils/create-package/templates/typescript/exports.json +6 -0
  55. package/utils/create-package/templates/typescript/src/index.ts +0 -0
  56. package/utils/run-scripts/index.ts +3 -0
  57. package/utils/run-scripts/src/__tests__/runScripts.spec.ts +45 -0
  58. 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,9 @@
1
+ import { Reference } from '@24i/bigscreen-sdk/jsx';
2
+
3
+ export const removeClass = (
4
+ reference: Reference,
5
+ className: string,
6
+ ): void => {
7
+ if (!reference.current) return;
8
+ reference.current.classList.remove(className);
9
+ };
@@ -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,10 @@
1
+ export const stopEvent = (event: Event) => {
2
+ event.preventDefault();
3
+ event.stopPropagation();
4
+ };
5
+
6
+ export const stopEventImmediately = (event: Event) => {
7
+ event.preventDefault();
8
+ event.stopPropagation();
9
+ event.stopImmediatePropagation?.();
10
+ };
@@ -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,4 @@
1
+ export { createInterval } from './createInterval';
2
+ export { createTimeout } from './createTimeout';
3
+ export { runAsync } from './runAsync';
4
+ export type { TimedFunction } from './types';
@@ -0,0 +1,3 @@
1
+ export const runAsync = (callbackFn: () => void) => {
2
+ window.setTimeout(callbackFn, 0);
3
+ };
@@ -0,0 +1,9 @@
1
+ export interface TimedFunction {
2
+ set: (callbackFn: () => void, timeMs: number) => void,
3
+ clear: () => void,
4
+ isActive: () => boolean,
5
+ }
6
+
7
+ export type TimedObject = {
8
+ id: number | null,
9
+ } & TimedFunction;
@@ -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,3 @@
1
+ import { createExportMaps } from './src/createExportMaps';
2
+
3
+ createExportMaps();
@@ -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,3 @@
1
+ import { main } from './createPackage';
2
+
3
+ main();
@@ -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 };
@@ -0,0 +1,5 @@
1
+ const Settings = {
2
+ name: '',
3
+ };
4
+
5
+ export { Settings };
@@ -0,0 +1,4 @@
1
+ export interface IPackageOptions {
2
+ /** Package name */
3
+ name: string;
4
+ }