@lexjs/web 2.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.
Files changed (63) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +800 -0
  3. package/dist/actions/core-actions.js +6 -0
  4. package/dist/actions/npm-actions.js +38 -0
  5. package/dist/actions/permission-actions.js +21 -0
  6. package/dist/api/browser/browser.js +111 -0
  7. package/dist/api/browser/browser.types.js +1 -0
  8. package/dist/api/matchers/matchers.js +9 -0
  9. package/dist/api/site/site.js +248 -0
  10. package/dist/api/site/site.types.js +1 -0
  11. package/dist/api/utils/extract-protocol.js +10 -0
  12. package/dist/api/utils/remove-protocol.js +8 -0
  13. package/dist/api/utils/return-type-guard.js +7 -0
  14. package/dist/api/utils/slash.js +21 -0
  15. package/dist/command/args/config-args.js +9 -0
  16. package/dist/command/args/data-args.js +110 -0
  17. package/dist/command/args/invalid-args.js +11 -0
  18. package/dist/command/args/query-args.js +121 -0
  19. package/dist/command/args/validate-args.js +126 -0
  20. package/dist/command/options.js +58 -0
  21. package/dist/config/config-file.js +66 -0
  22. package/dist/config/config-flags.js +42 -0
  23. package/dist/config/file-editor.js +26 -0
  24. package/dist/config/handle-config.js +12 -0
  25. package/dist/constants.js +15 -0
  26. package/dist/create-app/main.js +121 -0
  27. package/dist/create-app/paths.js +24 -0
  28. package/dist/create-app/schema/base.json +21 -0
  29. package/dist/create-app/schema/browsers.json +60 -0
  30. package/dist/create-app/schema/sites.json +71 -0
  31. package/dist/create-app/script-contents.js +18 -0
  32. package/dist/create-app/tree.js +25 -0
  33. package/dist/data/app-data.js +12 -0
  34. package/dist/data/config-data.js +35 -0
  35. package/dist/data/defaults.js +22 -0
  36. package/dist/data/initial-sites.js +36 -0
  37. package/dist/data/sort-config-data.js +17 -0
  38. package/dist/helpers/find-browser.js +9 -0
  39. package/dist/helpers/find-config-item.js +23 -0
  40. package/dist/helpers/find-nested.js +24 -0
  41. package/dist/helpers/find-profile.js +11 -0
  42. package/dist/helpers/find-site.js +15 -0
  43. package/dist/helpers/get-browser-name.js +9 -0
  44. package/dist/helpers/get-query-profiles.js +34 -0
  45. package/dist/main.js +19 -0
  46. package/dist/query/get-profiles-config.js +10 -0
  47. package/dist/query/get-query-browsers.js +33 -0
  48. package/dist/query/get-query-sites.js +49 -0
  49. package/dist/query/get-urls.js +85 -0
  50. package/dist/query/open-urls.js +30 -0
  51. package/dist/query/print-query.js +24 -0
  52. package/dist/query/query.js +19 -0
  53. package/dist/types/arg.types.js +1 -0
  54. package/dist/types/config.types.js +1 -0
  55. package/dist/types/primitive.type.js +1 -0
  56. package/dist/types/query.types.js +1 -0
  57. package/dist/types/utility.types.js +1 -0
  58. package/dist/utils/get-package-info.js +10 -0
  59. package/dist/utils/logger.js +15 -0
  60. package/dist/utils/parse-data.js +15 -0
  61. package/dist/utils/transform-text.js +7 -0
  62. package/dist/utils/yaml-files.js +6 -0
  63. package/package.json +88 -0
@@ -0,0 +1,6 @@
1
+ import { FileTree } from '@lexjs/filetree';
2
+ import { coreActions } from '@lexjs/filetree/actions';
3
+ import { paths } from '../create-app/paths.js';
4
+ import { tree } from '../create-app/tree.js';
5
+ const fileTree = new FileTree(paths.root, tree);
6
+ export const useCoreActions = fileTree.use(coreActions);
@@ -0,0 +1,38 @@
1
+ import { FileTree } from '@lexjs/filetree';
2
+ import { $, ExecaError } from 'execa';
3
+ import { logger } from '../utils/logger.js';
4
+ export const npmCommands = {
5
+ install: 'install',
6
+ link: 'link',
7
+ };
8
+ const dirActions = FileTree.dirActions((targetDir) => ({
9
+ async [npmCommands.install](deps) {
10
+ const currentDir = process.cwd();
11
+ process.chdir(targetDir.path);
12
+ try {
13
+ await $ `npm install ${deps.join(' ')}`;
14
+ }
15
+ catch (error) {
16
+ if (error instanceof Error || error instanceof ExecaError) {
17
+ logger.error(error.message);
18
+ }
19
+ }
20
+ process.chdir(currentDir);
21
+ },
22
+ async [npmCommands.link](deps) {
23
+ const currentDir = process.cwd();
24
+ process.chdir(targetDir.path);
25
+ try {
26
+ await Promise.all(deps.map((dependency) => $ `npm link ${dependency}`));
27
+ }
28
+ catch (error) {
29
+ if (error instanceof Error || error instanceof ExecaError) {
30
+ logger.error(error.message);
31
+ }
32
+ }
33
+ process.chdir(currentDir);
34
+ },
35
+ }));
36
+ export const npmActions = {
37
+ dir: dirActions,
38
+ };
@@ -0,0 +1,21 @@
1
+ import { FileTree } from '@lexjs/filetree';
2
+ import { $, ExecaError } from 'execa';
3
+ import { logger } from '../utils/logger.js';
4
+ const dirActions = FileTree.dirActions((targetDir) => ({
5
+ async x(fileName) {
6
+ const currentPath = process.cwd();
7
+ process.chdir(targetDir.path);
8
+ try {
9
+ await $ `chmod +x ${fileName}`;
10
+ }
11
+ catch (error) {
12
+ if (error instanceof Error || error instanceof ExecaError) {
13
+ logger.error(error.message);
14
+ }
15
+ }
16
+ process.chdir(currentPath);
17
+ },
18
+ }));
19
+ export const permissionActions = {
20
+ dir: dirActions,
21
+ };
@@ -0,0 +1,111 @@
1
+ import open, { apps, openApp } from 'open';
2
+ import { returnTypeGuard } from '../utils/return-type-guard.js';
3
+ export class Browser {
4
+ #browserName;
5
+ #config;
6
+ constructor(browserName, config) {
7
+ this.#browserName = browserName;
8
+ this.#config = config;
9
+ }
10
+ async open(url, options = {}) {
11
+ const urls = this.getUrls(url);
12
+ if (this.#browserName == null) {
13
+ if (urls.length > 0) {
14
+ await Promise.all(urls.map(async (link) => {
15
+ await open(link);
16
+ }));
17
+ }
18
+ return;
19
+ }
20
+ const browserAppName = this.getBrowserAppName(this.#browserName);
21
+ if (urls.length > 0) {
22
+ await Promise.all(urls.map(async (link) => {
23
+ await this.handleOpen(async (browserArguments) => {
24
+ await open(link, {
25
+ app: {
26
+ name: browserAppName,
27
+ arguments: browserArguments,
28
+ },
29
+ });
30
+ }, options);
31
+ }));
32
+ return;
33
+ }
34
+ this.handleOpen(async (browserArguments) => {
35
+ await openApp(browserAppName, {
36
+ arguments: browserArguments,
37
+ });
38
+ }, options);
39
+ }
40
+ async handleOpen(handler, options) {
41
+ const { profile: profileValue, incognito = false } = options;
42
+ const profiles = this.getProfiles(profileValue);
43
+ if (profiles.length > 0) {
44
+ await Promise.all(profiles.map(async (profile) => {
45
+ const browserArguments = this.getBrowserArguments(incognito, profile);
46
+ await handler(browserArguments);
47
+ }));
48
+ return;
49
+ }
50
+ await handler(this.getBrowserArguments(incognito));
51
+ }
52
+ getUrls(value) {
53
+ if (typeof value === 'string') {
54
+ return [value];
55
+ }
56
+ if (Array.isArray(value) &&
57
+ value.every((url) => typeof url === 'string')) {
58
+ return value;
59
+ }
60
+ return [];
61
+ }
62
+ getProfiles(profileValue) {
63
+ if (typeof profileValue === 'string') {
64
+ return [profileValue];
65
+ }
66
+ if (Array.isArray(profileValue) &&
67
+ profileValue.every((value) => typeof value === 'string')) {
68
+ return profileValue;
69
+ }
70
+ const { profiles } = this.#config ?? {};
71
+ if (profileValue != null &&
72
+ profileValue instanceof Function &&
73
+ profiles != null) {
74
+ const result = returnTypeGuard(profileValue, profiles);
75
+ if (result != null) {
76
+ return Array.isArray(result) ? result : [result];
77
+ }
78
+ }
79
+ return [];
80
+ }
81
+ getBrowserAppName(browserName) {
82
+ switch (browserName.toLowerCase()) {
83
+ case 'chrome':
84
+ return apps.chrome;
85
+ case 'firefox':
86
+ return apps.firefox;
87
+ case 'edge':
88
+ return apps.edge;
89
+ default:
90
+ return browserName;
91
+ }
92
+ }
93
+ getBrowserArguments(incognito, profileDirectory) {
94
+ const browserName = this.#browserName;
95
+ const browserArguments = [];
96
+ if (profileDirectory != null) {
97
+ browserArguments.push(`--profile-directory=${profileDirectory}`);
98
+ }
99
+ if (browserName != null && incognito) {
100
+ let incognitoValue = 'incognito';
101
+ if (browserName === 'edge') {
102
+ incognitoValue = 'inprivate';
103
+ }
104
+ else if (browserName === 'firefox' || browserName === 'opera') {
105
+ incognitoValue = 'private';
106
+ }
107
+ browserArguments.push(`--${incognitoValue}`);
108
+ }
109
+ return browserArguments;
110
+ }
111
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export const matchers = {
2
+ url: /^(https?:\/\/)?(\b([A-Za-z0-9]+([A-Za-z0-9-])*([A-Za-z0-9])*\b)\.)+[a-z]{2,}\/?/i,
3
+ port: /:(\d{1,5})/,
4
+ protocol: /^https?:\/\//,
5
+ slash: {
6
+ leading: /^\//,
7
+ trailing: /\/$/,
8
+ },
9
+ };
@@ -0,0 +1,248 @@
1
+ import { matchers } from '../matchers/matchers.js';
2
+ import { extractProtocol } from '../utils/extract-protocol.js';
3
+ import { removeProtocol } from '../utils/remove-protocol.js';
4
+ import { returnTypeGuard } from '../utils/return-type-guard.js';
5
+ import { slash } from '../utils/slash.js';
6
+ export class Site {
7
+ #baseUrl;
8
+ #config;
9
+ #delimiter = ' ';
10
+ constructor(baseUrl, config = {}) {
11
+ this.#baseUrl = baseUrl;
12
+ this.#config = config;
13
+ if (config.delimiter != null) {
14
+ this.#delimiter = config.delimiter;
15
+ }
16
+ }
17
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
18
+ /* PUBLIC API */
19
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
20
+ /**
21
+ * Creates base URLs with a protocol and a port, if provided
22
+ */
23
+ getBaseUrls(options = {}) {
24
+ const { port: portValue, unsecureHttp } = options;
25
+ const emptyPortsArray = Array.isArray(portValue) && portValue.length === 0;
26
+ if (portValue == null || emptyPortsArray) {
27
+ const baseUrl = this.getUrlWithProtocol(this.#baseUrl, unsecureHttp);
28
+ return [this.getHref(baseUrl)];
29
+ }
30
+ const ports = this.getUniquePorts(portValue);
31
+ return (ports
32
+ // add ports
33
+ .reduce((result, port) => [
34
+ ...result,
35
+ ...this.getUrlWithPort(this.#baseUrl, port).filter((url) => !result.includes(url)),
36
+ ], [])
37
+ // add protocol
38
+ .map((url) => this.getUrlWithProtocol(url, unsecureHttp))
39
+ .map((url) => this.getHref(url)));
40
+ }
41
+ /**
42
+ * Creates search URLs.
43
+ *
44
+ * - If search value is not provided or is empty,
45
+ * then URLs are created by using the base URL
46
+ */
47
+ search(searchValue, options = {}) {
48
+ const { queryPath, port, split, unsecureHttp } = options;
49
+ return searchValue == null || searchValue.trim() === ''
50
+ ? this.getBaseUrls({ port, unsecureHttp })
51
+ : this.getSearchUrls(searchValue, {
52
+ queryPath,
53
+ port,
54
+ split,
55
+ unsecureHttp,
56
+ });
57
+ }
58
+ /**
59
+ * Creates URLs for the provided resource
60
+ */
61
+ resource(resourceValue, options = {}) {
62
+ const { path: pathValue, port, unsecureHttp } = options;
63
+ const baseUrls = this.getBaseUrls({ port, unsecureHttp });
64
+ const resources = this.getResourceValues(resourceValue);
65
+ const paths = this.getResourceValues(pathValue);
66
+ const resourceUrls = baseUrls.reduce((result, baseUrl) => [
67
+ ...result,
68
+ ...resources
69
+ .map((resource) => slash.trailing.add(baseUrl) + slash.leading.remove(resource))
70
+ .filter((url) => !result.includes(url)),
71
+ ], []);
72
+ if (paths.length > 0) {
73
+ return resourceUrls.reduce((result, url) => [
74
+ ...result,
75
+ ...paths.map((path) => (path.startsWith('?') ? url : slash.trailing.add(url)) +
76
+ slash.leading.remove(path)),
77
+ ], []);
78
+ }
79
+ return resourceUrls;
80
+ }
81
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
82
+ /* PRIVATE METHODS */
83
+ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
84
+ /**
85
+ * Creates final URLs with search keywords
86
+ */
87
+ getSearchUrls(searchValue, options) {
88
+ const { queryPath, port, split, unsecureHttp } = options;
89
+ const keywords = this.getKeywords(searchValue);
90
+ const urls = this.getSearchPathUrls({
91
+ queryPath,
92
+ port,
93
+ unsecureHttp,
94
+ });
95
+ if (split) {
96
+ return keywords.reduce((result, keyword) => [
97
+ ...result,
98
+ ...urls.map((url) => this.getHref(url + keyword)),
99
+ ], []);
100
+ }
101
+ return urls.map((url) => this.getHref(url + keywords.join(this.#delimiter)));
102
+ }
103
+ /**
104
+ * Creates URLs by adding the `queryPath` property to the site's
105
+ * base URL that can be used to query search keywords.
106
+ *
107
+ * For example `https://google.com/search?q=`
108
+ */
109
+ getSearchPathUrls(options) {
110
+ const { queryPath, port, unsecureHttp } = options;
111
+ const baseUrls = this.getBaseUrls({ port, unsecureHttp });
112
+ const searchPaths = this.getSearchPathValues(queryPath);
113
+ return baseUrls.reduce((result, baseUrl) => [
114
+ ...result,
115
+ ...searchPaths.map((sp) => (baseUrl.endsWith('=') ? baseUrl : slash.trailing.add(baseUrl)) +
116
+ (sp.startsWith('?') ? sp : slash.leading.remove(sp))),
117
+ ], []);
118
+ }
119
+ /**
120
+ * Splits the search value into an array of keyword strings
121
+ */
122
+ getKeywords(searchValue) {
123
+ return searchValue
124
+ .trim()
125
+ .split(' ')
126
+ .filter((keyword) => keyword.length > 0);
127
+ }
128
+ /**
129
+ * Creates a URL with a protocol.
130
+ *
131
+ * @param unsecureHttp
132
+ * If provided and URL string includes a protocol,
133
+ * the URL's protocol will be overridden
134
+ */
135
+ getUrlWithProtocol(url, unsecureHttp) {
136
+ const protocol = `http${unsecureHttp ? '' : 's'}://`;
137
+ const hasProtocol = matchers.protocol.test(url);
138
+ return hasProtocol && unsecureHttp == null
139
+ ? url
140
+ : `${protocol}${removeProtocol(url)}`;
141
+ }
142
+ /**
143
+ * Creates a URL with a port
144
+ */
145
+ getUrlWithPort(url, port) {
146
+ const protocol = extractProtocol(url) ?? '';
147
+ const noProtocolUrl = removeProtocol(url);
148
+ function hasPort() {
149
+ return matchers.port.test(url);
150
+ }
151
+ function buildUrl() {
152
+ const matches = noProtocolUrl.match(matchers.port);
153
+ // provided URL includes a port
154
+ if (matches != null) {
155
+ const [, currentPort] = matches;
156
+ const colon = ':';
157
+ const i = noProtocolUrl.indexOf(colon);
158
+ // provided port is not part of the url
159
+ if (currentPort !== port.toString() && i >= 0) {
160
+ const beforePort = noProtocolUrl.substring(0, i);
161
+ const afterPort = noProtocolUrl.substring(i + colon.length + currentPort.length);
162
+ return `${protocol}${beforePort}:${port}${afterPort}`;
163
+ }
164
+ // provided port is already part of the url
165
+ return url;
166
+ }
167
+ // provided URL does not include a port
168
+ const i = noProtocolUrl.indexOf('/');
169
+ const host = i > 0 ? noProtocolUrl.substring(0, i) : noProtocolUrl;
170
+ const path = i > 0 ? noProtocolUrl.substring(i) : '';
171
+ return `${protocol}${host}:${port}${path}`;
172
+ }
173
+ const urlWithPort = buildUrl();
174
+ return hasPort() && urlWithPort !== url
175
+ ? [url, urlWithPort]
176
+ : [urlWithPort];
177
+ }
178
+ /**
179
+ * Returns an array of `queryPath` values provided for the current `search` call
180
+ *
181
+ * - If `queryPath` is not provided or is invalid, defaults to the site's
182
+ * main `search` value
183
+ *
184
+ * - If site does not have a main `search` value, default to the
185
+ * site's root (effectively querying the base url)
186
+ */
187
+ getSearchPathValues(queryPath) {
188
+ if (typeof queryPath === 'string') {
189
+ return [queryPath];
190
+ }
191
+ if (Array.isArray(queryPath) &&
192
+ queryPath.every((sp) => typeof sp === 'string')) {
193
+ return queryPath;
194
+ }
195
+ const { query: configQuery } = this.#config;
196
+ if (queryPath != null && queryPath instanceof Function) {
197
+ const result = returnTypeGuard(queryPath, configQuery);
198
+ if (result != null) {
199
+ return Array.isArray(result) ? result : [result];
200
+ }
201
+ }
202
+ // fallback to the site's root (base url)
203
+ let defaultSearchPath = '/';
204
+ // set the default to the site config's main value, if it exists
205
+ if (configQuery != null) {
206
+ if (typeof configQuery === 'string') {
207
+ defaultSearchPath = configQuery;
208
+ }
209
+ else if (configQuery instanceof Object && 'main' in configQuery) {
210
+ defaultSearchPath = configQuery.main;
211
+ }
212
+ }
213
+ return [defaultSearchPath];
214
+ }
215
+ /**
216
+ * Returns an array of resources for the current `resource` call.
217
+ *
218
+ * - If resource is not provided or is invalid, returns an empty array
219
+ */
220
+ getResourceValues(resourceValue) {
221
+ if (typeof resourceValue === 'string') {
222
+ return [resourceValue];
223
+ }
224
+ if (Array.isArray(resourceValue) &&
225
+ resourceValue.every((r) => typeof r === 'string')) {
226
+ return resourceValue;
227
+ }
228
+ if (resourceValue != null && resourceValue instanceof Function) {
229
+ const result = returnTypeGuard(resourceValue, this.#config.resources);
230
+ if (result != null) {
231
+ return Array.isArray(result) ? result : [result];
232
+ }
233
+ }
234
+ return [];
235
+ }
236
+ /**
237
+ * Returns an array of unique port numbers
238
+ */
239
+ getUniquePorts(ports) {
240
+ return [...new Set(Array.isArray(ports) ? ports : [ports])];
241
+ }
242
+ /**
243
+ * Returns a property formatted URL string
244
+ */
245
+ getHref(url) {
246
+ return new URL(url).href;
247
+ }
248
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Return the protocol or an empty string
3
+ * if the url string has no protocol
4
+ */
5
+ export function extractProtocol(url) {
6
+ const delimiter = '://';
7
+ return url.startsWith('http') && url.includes(delimiter)
8
+ ? url.substring(0, url.indexOf(delimiter) + delimiter.length)
9
+ : null;
10
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Return a string without https:// or http://
3
+ */
4
+ export function removeProtocol(url) {
5
+ return url.startsWith('https://') || url.startsWith('http://')
6
+ ? url.split('://')[1]
7
+ : url;
8
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Higher-order function that returns the callback's
3
+ * return value or undefined for TypeScript purposes.
4
+ */
5
+ export function returnTypeGuard(callback, ...args) {
6
+ return callback(...args);
7
+ }
@@ -0,0 +1,21 @@
1
+ import { matchers } from '../matchers/matchers.js';
2
+ export const slash = {
3
+ leading: {
4
+ add(value) {
5
+ return matchers.slash.leading.test(value) ? value : `/${value}`;
6
+ },
7
+ remove(value) {
8
+ return matchers.slash.leading.test(value) ? value.substring(1) : value;
9
+ },
10
+ },
11
+ trailing: {
12
+ add(value) {
13
+ return matchers.slash.trailing.test(value) ? value : `${value}/`;
14
+ },
15
+ remove(value) {
16
+ return matchers.slash.trailing.test(value)
17
+ ? value.substring(0, value.length - 1)
18
+ : value;
19
+ },
20
+ },
21
+ };
@@ -0,0 +1,9 @@
1
+ import yargs from 'yargs';
2
+ import { hideBin } from 'yargs/helpers';
3
+ import { ConfigOptions as Options } from '../options.js';
4
+ export const configArgs = yargs(hideBin(process.argv))
5
+ .option(Options.Config, {
6
+ type: 'boolean',
7
+ })
8
+ .help(false)
9
+ .parseSync();
@@ -0,0 +1,110 @@
1
+ import { configProfileFlags } from '../../config/config-flags.js';
2
+ import { sitesData, browsersData, getProfilesData } from '../../data/config-data.js';
3
+ import { queryOptions } from '../options.js';
4
+ import { queryArgs } from './query-args.js';
5
+ /**
6
+ * Returns a combined list of values that were supplied to the CLI
7
+ * as standard options and custom flags
8
+ */
9
+ function combineArgLists(optionArg, customArgs = []) {
10
+ const argList = [...customArgs];
11
+ if (optionArg != null) {
12
+ argList.push(...(Array.isArray(optionArg) ? optionArg : [optionArg]));
13
+ }
14
+ return argList;
15
+ }
16
+ /**
17
+ * Returns a unique list of non-nullable args
18
+ */
19
+ function getUniqueList(optionArg, customArgs = [], removeEmptyArg = true) {
20
+ const list = combineArgLists(optionArg, customArgs);
21
+ const uniqueList = [...new Set(list)];
22
+ return removeEmptyArg ? uniqueList.filter((arg) => arg !== '') : uniqueList;
23
+ }
24
+ /**
25
+ * Returns a list of arg options supplied to the CLI that are
26
+ * specific to config data and do not match standard args
27
+ */
28
+ function getCustomArgs(data) {
29
+ const customFlags = Object.keys(queryArgs).filter((key) => !queryOptions.includes(key));
30
+ return Object.entries(data)
31
+ .map(([key, { alias }]) => {
32
+ if (alias != null) {
33
+ return Array.isArray(alias) ? [key, ...alias] : [key, alias];
34
+ }
35
+ return key;
36
+ })
37
+ .flat()
38
+ .filter((nameOrAlias) => customFlags.includes(nameOrAlias));
39
+ }
40
+ export const dataArgs = {
41
+ /**
42
+ * Returns a unique list of browser args provided to the CLI
43
+ *
44
+ * @param removeEmptyArg
45
+ * If true, removes the empty value from the list
46
+ */
47
+ browser: function getBrowserArgs(removeEmptyArg = true) {
48
+ const customArgs = getCustomArgs(browsersData);
49
+ return getUniqueList(queryArgs.browser, customArgs, removeEmptyArg);
50
+ },
51
+ /**
52
+ * Returns a unique list of profile args provided to the CLI.
53
+ *
54
+ * @param browserName
55
+ * If value is provided, returns profile args for that browser name,
56
+ * otherwise returns all profile args supplied to the CLI
57
+ *
58
+ * @param removeEmptyArg
59
+ * If true, removes an empty arg value from the list
60
+ */
61
+ profile: function getProfileArgs(browserName, removeEmptyArg = true) {
62
+ const { profile } = queryArgs;
63
+ if (browserName == null) {
64
+ const list = [];
65
+ // push option arg values (--profile <name>) to the list
66
+ if (profile != null) {
67
+ list.push(...(Array.isArray(profile) ? profile : [profile]));
68
+ }
69
+ // push custom args (--profileName) to the list
70
+ Object.keys(browsersData).forEach((arg) => {
71
+ const profilesData = getProfilesData(arg);
72
+ const customArgs = getCustomArgs(profilesData);
73
+ // filter out short aliases
74
+ const profileArgs = customArgs.filter((profileArg) => configProfileFlags.includes(profileArg));
75
+ list.push(...profileArgs);
76
+ });
77
+ return [
78
+ ...new Set(removeEmptyArg ? list.filter((arg) => arg !== '') : list),
79
+ ];
80
+ }
81
+ const profilesData = getProfilesData(browserName);
82
+ const customArgs = getCustomArgs(profilesData);
83
+ // filter out short aliases
84
+ const profileArgs = customArgs.filter((profileArg) => configProfileFlags.includes(profileArg));
85
+ return getUniqueList(profile, profileArgs, removeEmptyArg);
86
+ },
87
+ /**
88
+ * Returns a unique list of site args provided to the CLI
89
+ *
90
+ * @param removeEmptyArg
91
+ * If true, removes the empty value from the list
92
+ */
93
+ site: function getSiteArgs(removeEmptyArg = true) {
94
+ const customArgs = getCustomArgs(sitesData);
95
+ return getUniqueList(queryArgs.site, customArgs, removeEmptyArg);
96
+ },
97
+ /**
98
+ * Returns a unique list of port args provided to the CLI
99
+ *
100
+ * @param removeEmptyArg
101
+ * If true, removes the empty value from the list
102
+ */
103
+ port: function getPortArgs() {
104
+ const ports = Array.isArray(queryArgs.port)
105
+ ? queryArgs.port
106
+ : [queryArgs.port];
107
+ const uniquePorts = [...new Set(ports)];
108
+ return uniquePorts.filter((port) => port != null);
109
+ },
110
+ };
@@ -0,0 +1,11 @@
1
+ import { configFlags } from '../../config/config-flags.js';
2
+ import { queryOptions, yargsOptions } from '../options.js';
3
+ import { queryArgs } from './query-args.js';
4
+ /**
5
+ * A list of args that do not match CLI and yargs' options,
6
+ * or the custom flags derived from the config's site, browser,
7
+ * and profile values. Does not check against config options.
8
+ */
9
+ export const invalidArgs = Object.keys(queryArgs)
10
+ .filter((key) => !yargsOptions.includes(key))
11
+ .filter((key) => ![...queryOptions, ...configFlags].includes(key));