@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.
- package/LICENSE +16 -0
- package/README.md +800 -0
- package/dist/actions/core-actions.js +6 -0
- package/dist/actions/npm-actions.js +38 -0
- package/dist/actions/permission-actions.js +21 -0
- package/dist/api/browser/browser.js +111 -0
- package/dist/api/browser/browser.types.js +1 -0
- package/dist/api/matchers/matchers.js +9 -0
- package/dist/api/site/site.js +248 -0
- package/dist/api/site/site.types.js +1 -0
- package/dist/api/utils/extract-protocol.js +10 -0
- package/dist/api/utils/remove-protocol.js +8 -0
- package/dist/api/utils/return-type-guard.js +7 -0
- package/dist/api/utils/slash.js +21 -0
- package/dist/command/args/config-args.js +9 -0
- package/dist/command/args/data-args.js +110 -0
- package/dist/command/args/invalid-args.js +11 -0
- package/dist/command/args/query-args.js +121 -0
- package/dist/command/args/validate-args.js +126 -0
- package/dist/command/options.js +58 -0
- package/dist/config/config-file.js +66 -0
- package/dist/config/config-flags.js +42 -0
- package/dist/config/file-editor.js +26 -0
- package/dist/config/handle-config.js +12 -0
- package/dist/constants.js +15 -0
- package/dist/create-app/main.js +121 -0
- package/dist/create-app/paths.js +24 -0
- package/dist/create-app/schema/base.json +21 -0
- package/dist/create-app/schema/browsers.json +60 -0
- package/dist/create-app/schema/sites.json +71 -0
- package/dist/create-app/script-contents.js +18 -0
- package/dist/create-app/tree.js +25 -0
- package/dist/data/app-data.js +12 -0
- package/dist/data/config-data.js +35 -0
- package/dist/data/defaults.js +22 -0
- package/dist/data/initial-sites.js +36 -0
- package/dist/data/sort-config-data.js +17 -0
- package/dist/helpers/find-browser.js +9 -0
- package/dist/helpers/find-config-item.js +23 -0
- package/dist/helpers/find-nested.js +24 -0
- package/dist/helpers/find-profile.js +11 -0
- package/dist/helpers/find-site.js +15 -0
- package/dist/helpers/get-browser-name.js +9 -0
- package/dist/helpers/get-query-profiles.js +34 -0
- package/dist/main.js +19 -0
- package/dist/query/get-profiles-config.js +10 -0
- package/dist/query/get-query-browsers.js +33 -0
- package/dist/query/get-query-sites.js +49 -0
- package/dist/query/get-urls.js +85 -0
- package/dist/query/open-urls.js +30 -0
- package/dist/query/print-query.js +24 -0
- package/dist/query/query.js +19 -0
- package/dist/types/arg.types.js +1 -0
- package/dist/types/config.types.js +1 -0
- package/dist/types/primitive.type.js +1 -0
- package/dist/types/query.types.js +1 -0
- package/dist/types/utility.types.js +1 -0
- package/dist/utils/get-package-info.js +10 -0
- package/dist/utils/logger.js +15 -0
- package/dist/utils/parse-data.js +15 -0
- package/dist/utils/transform-text.js +7 -0
- package/dist/utils/yaml-files.js +6 -0
- 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,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,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));
|