@webqit/webflo 0.8.47 → 0.8.51
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/docker/Dockerfile +25 -0
- package/docker/README.md +71 -0
- package/package.json +9 -8
- package/src/cmd/client.js +68 -97
- package/src/cmd/origins.js +2 -2
- package/src/cmd/server.js +1 -1
- package/src/modules/Router.js +130 -0
- package/src/modules/_FormData.js +60 -0
- package/src/modules/_Headers.js +88 -0
- package/src/modules/_MessageStream.js +191 -0
- package/src/modules/_NavigationEvent.js +89 -0
- package/src/modules/_Request.js +61 -0
- package/src/modules/_RequestHeaders.js +72 -0
- package/src/modules/_Response.js +56 -0
- package/src/modules/_ResponseHeaders.js +81 -0
- package/src/modules/_URL.js +111 -0
- package/src/modules/client/Cache.js +38 -0
- package/src/modules/client/Client.js +49 -20
- package/src/modules/client/Http.js +26 -11
- package/src/modules/client/NavigationEvent.js +20 -0
- package/src/modules/client/Router.js +30 -106
- package/src/modules/client/StdRequest.js +13 -15
- package/src/modules/client/Storage.js +56 -0
- package/src/modules/client/Url.js +1 -1
- package/src/modules/client/Worker.js +70 -66
- package/src/modules/client/WorkerClient.js +102 -0
- package/src/modules/client/WorkerComm.js +183 -0
- package/src/modules/client/effects/sounds.js +64 -0
- package/src/modules/server/NavigationEvent.js +38 -0
- package/src/modules/server/Router.js +53 -126
- package/src/modules/server/Server.js +192 -84
- package/src/modules/util.js +7 -7
- package/src/modules/NavigationEvent.js +0 -46
- package/src/modules/Response.js +0 -98
- package/src/modules/XURL.js +0 -125
- package/src/modules/client/ClientNavigationEvent.js +0 -10
- package/src/modules/client/Push.js +0 -84
- package/src/modules/server/ServerNavigationEvent.js +0 -10
- package/src/modules/server/StdIncomingMessage.js +0 -73
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Base installations
|
|
2
|
+
FROM node:12-alpine
|
|
3
|
+
RUN apk --no-cache add git
|
|
4
|
+
|
|
5
|
+
# We'll install npm packages at one-level higher than
|
|
6
|
+
# actuall app root, so that we can bind-mount host system's app root
|
|
7
|
+
# without overriding the container's node_modules directory
|
|
8
|
+
WORKDIR /home/www
|
|
9
|
+
RUN npm install
|
|
10
|
+
RUN npm install @webqit/webflo -g
|
|
11
|
+
RUN npm install @webqit/playui-cli -g
|
|
12
|
+
|
|
13
|
+
# Move one-level in, for the reasons above
|
|
14
|
+
WORKDIR /home/www/app
|
|
15
|
+
|
|
16
|
+
# To auto-start app (flags optional), we would add...
|
|
17
|
+
# CMD ["webflo", "start", "--env=dev", "--watch", "--http-only"]
|
|
18
|
+
|
|
19
|
+
# To build the image locally...
|
|
20
|
+
# docker build --no-cache -t webflo ./docker
|
|
21
|
+
|
|
22
|
+
# To publish to docker hub...
|
|
23
|
+
# docker login -u webqit
|
|
24
|
+
# docker tag webflo webqit/webflo
|
|
25
|
+
# docker push webqit/webflo
|
package/docker/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Webflo Container
|
|
2
|
+
This is simply a node.js container with the `@webqit/webflo` framework installed. Once started, any webflo app can be deployed into the container from any git repository.
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
This container image lives on Docker Hub and can be pulled to a local machine or a remote Virtual Machine (VM) on any cloud platform.
|
|
6
|
+
|
|
7
|
+
+ [To Use Locally](#to-use-locally)
|
|
8
|
+
+ [To Use In the Cloud](#to-use-in-the-cloud)
|
|
9
|
+
* [To Deploy An App From Any Repo](#to-deploy-an-app-from-any-repo)
|
|
10
|
+
|
|
11
|
+
### To Use Locally
|
|
12
|
+
Ensure you have docker installed on your computer and run the following command from any location on your terminal:
|
|
13
|
+
|
|
14
|
+
```shell
|
|
15
|
+
docker pull webqit/webflo:latest
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The above command pulls the `webqit/webflo` image to your local machine. (But this can be automatically done by docker on running any docker commands that reference the `webqit/webflo` image.)
|
|
19
|
+
|
|
20
|
+
Next is to use the following commands to start the container and the Webflo runtime. In each case, the first part of the command starts the container, while the second part (from `webflo start`) starts the application.
|
|
21
|
+
|
|
22
|
+
#### To Start
|
|
23
|
+
Start the container using `docker run`; map a port (e.g `80`) of your host machine to `3000` of the container (unless changed webflo expects to run on port `3000`); optionally, give your container a name; reference `webqit/webflo` as the image to use; and lastly, start webflo using `webflo start`.
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
docker run -d -p 80:3000 --name my-app webqit/webflo webflo start
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Visit [localhost](http://localhost) to view your app.
|
|
30
|
+
|
|
31
|
+
#### To Start In Dev Mode
|
|
32
|
+
Webflo's *dev* mode is the perfect mode for developing locally. All you do is append the `--env=dev --watch` flags to your webflo commands. [(Learn more)](#)
|
|
33
|
+
|
|
34
|
+
```shell
|
|
35
|
+
docker run -d -p 80:3000 --name my-app webqit/webflo webflo start --env=dev --watch
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
In *dev* mode, webflo automatically restarts as you make changes to your codebase. Since webflo now lives inside a container, you'll need to *bind* the directory of your source code on your host machine to the `/home/www/app` directory of the container.
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
docker run -d -v /Users/me/my-app:/home/www/app -p 80:3000 --name my-app webqit/webflo webflo start --env=dev --watch
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
docker run -d -v /Users/ox-harris/Documents/CODE/webqit/webqit.io:/home/www/app -p 80:3000 --name my-app webqit/webflo webflo start --env=dev --watch
|
|
45
|
+
|
|
46
|
+
### To Use In the Cloud
|
|
47
|
+
TODO
|
|
48
|
+
|
|
49
|
+
### To Deploy An App From Any Repo
|
|
50
|
+
Whether running locally or in the cloud, webflo can easily take your application from any git repo. This follows webflo's normal `deploy` command.
|
|
51
|
+
|
|
52
|
+
Simply point docker at your container (using `docker exec [container-name]`) and execute the `webflo deploy` command.
|
|
53
|
+
|
|
54
|
+
```shell
|
|
55
|
+
docker exec my-app webflo deploy https://github.com/me/my-app
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If you will need to install any npm dependencies, you would run `npm install` on the appropriate directory in your container.
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
docker exec my-app npm install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If you will need to run additional webflo commands (e.g `webflo restart` to restart the application), you would follow the same pattern above.
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
docker exec my-app webflo restart
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Extending this Build
|
|
71
|
+
TODO
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"vanila-javascript"
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://webqit.io/tooling/webflo",
|
|
15
|
-
"version": "0.8.
|
|
15
|
+
"version": "0.8.51",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -38,25 +38,26 @@
|
|
|
38
38
|
"@octokit/webhooks": "^7.15.1",
|
|
39
39
|
"@webqit/backpack": "^0.0.37",
|
|
40
40
|
"@webqit/browser-pie": "^0.0.16",
|
|
41
|
-
"@webqit/pseudo-browser": "^0.3.
|
|
41
|
+
"@webqit/pseudo-browser": "^0.3.35",
|
|
42
42
|
"@webqit/util": "^0.8.7",
|
|
43
|
-
"
|
|
43
|
+
"client-sessions": "^0.8.0",
|
|
44
44
|
"cookie": "^0.4.1",
|
|
45
|
+
"form-data-encoder": "^1.6.0",
|
|
46
|
+
"formdata-node": "^4.3.0",
|
|
45
47
|
"formidable": "^2.0.0-dev.20200131.2",
|
|
46
48
|
"is-glob": "^4.0.1",
|
|
47
49
|
"micromatch": "^3.1.10",
|
|
50
|
+
"mime-types": "^2.1.33",
|
|
48
51
|
"minimatch": "^3.0.4",
|
|
49
|
-
"
|
|
52
|
+
"node-fetch": "^2.6.1",
|
|
50
53
|
"pm2": "^5.1.0",
|
|
51
54
|
"simple-git": "^2.20.1",
|
|
55
|
+
"stream-slice": "^0.1.2",
|
|
52
56
|
"touch": "^3.1.0",
|
|
53
57
|
"undici": "^4.3.1",
|
|
54
|
-
"uuid": "^8.3.
|
|
58
|
+
"uuid": "^8.3.2",
|
|
55
59
|
"webpack": "^5.50.0"
|
|
56
60
|
},
|
|
57
|
-
"devDependencies": {
|
|
58
|
-
"nodemon": "^2.0.7"
|
|
59
|
-
},
|
|
60
61
|
"author": "Oxford Harrison <oxharris.dev@gmail.com>",
|
|
61
62
|
"maintainers": [
|
|
62
63
|
"Oxford Harrison <oxharris.dev@gmail.com>"
|
package/src/cmd/client.js
CHANGED
|
@@ -6,8 +6,8 @@ import Fs from 'fs';
|
|
|
6
6
|
import Url from 'url';
|
|
7
7
|
import Path from 'path';
|
|
8
8
|
import Webpack from 'webpack';
|
|
9
|
-
import
|
|
10
|
-
import
|
|
9
|
+
import { _beforeLast } from '@webqit/util/str/index.js';
|
|
10
|
+
import { _isObject, _isArray, _isEmpty } from '@webqit/util/js/index.js';
|
|
11
11
|
import * as DotJs from '@webqit/backpack/src/dotfiles/DotJs.js';
|
|
12
12
|
import * as client from '../config/client.js'
|
|
13
13
|
|
|
@@ -23,147 +23,94 @@ export const desc = {
|
|
|
23
23
|
* @build
|
|
24
24
|
*/
|
|
25
25
|
export async function build(Ui, flags = {}, layout = {}) {
|
|
26
|
+
|
|
26
27
|
const config = await client.read(flags, layout);
|
|
27
28
|
// Consistent forward slashing
|
|
28
29
|
const forwardSlash = str => str.replace(/\\/g, '/');
|
|
29
|
-
var
|
|
30
|
+
var modulesDir = forwardSlash(Url.fileURLToPath(Path.join(import.meta.url, '../../modules/client')));
|
|
31
|
+
|
|
32
|
+
var workerDirSplit = Path.resolve(layout.WORKER_DIR).replace(/\\/g, '/').split('/');
|
|
33
|
+
var workerParams = config.worker || {};
|
|
34
|
+
const workerBundlingConfig = workerParams.bundling || {};
|
|
35
|
+
|
|
30
36
|
var clientDirSplit = Path.resolve(layout.CLIENT_DIR).replace(/\\/g, '/').split('/');
|
|
31
37
|
var clientParams = config; // Yes root config object
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const clientBundlingConfig = clientParams.bundling || {};
|
|
39
|
+
|
|
34
40
|
var waiting;
|
|
35
41
|
|
|
36
42
|
// -------------------
|
|
37
43
|
// Create the Service Worker file
|
|
38
44
|
// -------------------
|
|
39
45
|
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
var workerBundlingConfig = workerParams.bundling || {};
|
|
43
|
-
if (!workerBundlingConfig.intermediate) {
|
|
44
|
-
workerBundlingConfig.intermediate = clientDirSplit.join('/') + '/worker.js';
|
|
45
|
-
}
|
|
46
|
-
if (!workerBundlingConfig.output) {
|
|
47
|
-
workerBundlingConfig.output = {
|
|
48
|
-
filename: 'worker.js',
|
|
49
|
-
path: Path.resolve(layout.PUBLIC_DIR),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
var workerBuild = {
|
|
54
|
-
imports: {},
|
|
55
|
-
code: [],
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// >> Import the Webflo Client file
|
|
59
|
-
var navigatorWorker = clientModulesDir + '/Worker.js';
|
|
60
|
-
workerBuild.imports[navigatorWorker] = 'Worker';
|
|
46
|
+
if (!_isEmpty(workerParams)) {
|
|
61
47
|
|
|
62
48
|
Ui.log('');
|
|
63
49
|
Ui.title(`SERVICE WORKER BUILD`);
|
|
64
|
-
// >> Routes mapping
|
|
65
|
-
buildRoutes(Ui, Path.resolve(layout.WORKER_DIR), workerBuild, 'Worker-Side Routing:');
|
|
66
50
|
|
|
67
|
-
|
|
51
|
+
const workerBuild = { imports: {}, code: [], };
|
|
52
|
+
workerBuild.imports[modulesDir + '/Worker.js'] = 'Worker';
|
|
53
|
+
|
|
54
|
+
// ------------------
|
|
55
|
+
// >> Routes mapping
|
|
56
|
+
buildRoutes(workerBuild, Ui, Path.resolve(layout.WORKER_DIR), 'Worker-Side Routing:');
|
|
68
57
|
workerBuild.code.push(``);
|
|
69
58
|
workerBuild.code.push(`// >> Worker Params`);
|
|
70
|
-
workerBuild
|
|
71
|
-
Object.keys(workerParams).forEach(name => {
|
|
72
|
-
if (name !== 'bundling') {
|
|
73
|
-
workerBuild.code.push(` ${name}: ${[ 'boolean', 'number' ].includes(typeof workerParams[name]) ? workerParams[name] : (Array.isArray(workerParams[name]) ? (!workerParams[name].length ? `[]` : `['${workerParams[name].join(`', '`)}']`) : `'${workerParams[name]}'`)},`);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
workerBuild.code.push(`};`);
|
|
77
|
-
|
|
78
|
-
// >> instantiation
|
|
59
|
+
buildParams(workerBuild, workerParams, 'params');
|
|
79
60
|
workerBuild.code.push(``);
|
|
80
61
|
workerBuild.code.push(`// >> Worker Instantiation`);
|
|
81
62
|
workerBuild.code.push(`Worker.call(null, layout, params);`);
|
|
63
|
+
// ------------------
|
|
82
64
|
|
|
65
|
+
// ------------------
|
|
83
66
|
// >> Write to file...
|
|
67
|
+
workerBundlingConfig.intermediate = workerBundlingConfig.intermediate || `${clientDirSplit.join('/')}/worker.js`;
|
|
68
|
+
workerBundlingConfig.output = workerBundlingConfig.output || {
|
|
69
|
+
filename: 'worker.js',
|
|
70
|
+
path: Path.resolve(layout.PUBLIC_DIR),
|
|
71
|
+
};
|
|
84
72
|
waiting = Ui.waiting(Ui.f`Writing the Service Worker file: ${workerBundlingConfig.intermediate}`);
|
|
85
73
|
waiting.start();
|
|
86
|
-
|
|
87
|
-
// Write
|
|
88
74
|
DotJs.write(workerBuild, workerBundlingConfig.intermediate, 'Service Worker File');
|
|
89
|
-
|
|
90
75
|
waiting.stop();
|
|
91
76
|
Ui.info(Ui.f`Service Worker file: ${workerBundlingConfig.intermediate}`);
|
|
92
|
-
|
|
77
|
+
// ------------------
|
|
93
78
|
}
|
|
94
79
|
|
|
95
80
|
// -------------------
|
|
96
81
|
// Create the Client file
|
|
97
82
|
// -------------------
|
|
98
83
|
|
|
99
|
-
var clientBundlingConfig = clientParams.bundling || {};
|
|
100
|
-
if (!clientBundlingConfig.intermediate) {
|
|
101
|
-
clientBundlingConfig.intermediate = clientDirSplit.join('/') + '/bundle.js';
|
|
102
|
-
}
|
|
103
|
-
if (!clientBundlingConfig.output) {
|
|
104
|
-
clientBundlingConfig.output = {
|
|
105
|
-
filename: 'bundle.js',
|
|
106
|
-
path: Path.resolve(layout.PUBLIC_DIR),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
var clientBuild = {
|
|
111
|
-
imports: {},
|
|
112
|
-
code: [],
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// >> Import the Webflo Client file
|
|
116
|
-
clientBuild.imports[clientModulesDir + '/Client.js'] = 'Client';
|
|
117
|
-
|
|
118
84
|
Ui.log('');
|
|
119
85
|
Ui.title(`CLIENT BUILD`);
|
|
120
|
-
// >> Routes mapping
|
|
121
|
-
buildRoutes(Ui, Path.resolve(layout.CLIENT_DIR), clientBuild, 'Client-Side Routing:');
|
|
122
86
|
|
|
123
|
-
|
|
87
|
+
const clientBuild = { imports: {}, code: [], };
|
|
88
|
+
clientBuild.imports[modulesDir + '/Client.js'] = 'Client';
|
|
89
|
+
|
|
90
|
+
// ------------------
|
|
91
|
+
// >> Routes mapping
|
|
92
|
+
buildRoutes(clientBuild, Ui, Path.resolve(layout.CLIENT_DIR), 'Client-Side Routing:');
|
|
124
93
|
clientBuild.code.push(``);
|
|
125
94
|
clientBuild.code.push(`// >> Client Params`);
|
|
126
|
-
clientBuild
|
|
127
|
-
clientBuild.code.push(`};`);
|
|
128
|
-
|
|
129
|
-
// >> Client Instantiation
|
|
95
|
+
buildParams(clientBuild, clientParams, 'params');
|
|
130
96
|
clientBuild.code.push(``);
|
|
131
97
|
clientBuild.code.push(`// >> Client Instantiation`);
|
|
132
98
|
clientBuild.code.push(`Client.call(null, layout, params);`);
|
|
133
|
-
|
|
134
|
-
// Service Worker registration code?
|
|
135
|
-
if (createWorker) {
|
|
136
|
-
if (workerParams.support_push) {
|
|
137
|
-
clientBuild.imports[clientModulesDir + '/Push.js'] = 'Push';
|
|
138
|
-
}
|
|
139
|
-
clientBuild.code.push(...[
|
|
140
|
-
``,
|
|
141
|
-
`// >> Service Worker Registration`,
|
|
142
|
-
`if ('serviceWorker' in navigator) {`,
|
|
143
|
-
` window.addEventListener('load', () => {`,
|
|
144
|
-
` navigator.serviceWorker.register('/${workerBundlingConfig.output.filename}', {scope: '${workerParams.scope}'}).then(async registration => {`,
|
|
145
|
-
` console.log('Service worker registered.');`,
|
|
146
|
-
` await /*SUPPORT_PUSH*/${workerParams.support_push} ? new Push(registration, {`,
|
|
147
|
-
` registration_url: '${workerParams.push_registration_url}',`,
|
|
148
|
-
` deregistration_url: '${workerParams.push_deregistration_url}',`,
|
|
149
|
-
` public_key: '${workerParams.push_public_key}',`,
|
|
150
|
-
` }) : null;`,
|
|
151
|
-
` });`,
|
|
152
|
-
` });`,
|
|
153
|
-
`}`,
|
|
154
|
-
``,
|
|
155
|
-
]);
|
|
156
|
-
}
|
|
99
|
+
// ------------------
|
|
157
100
|
|
|
101
|
+
// ------------------
|
|
158
102
|
// >> Write to file...
|
|
103
|
+
clientBundlingConfig.intermediate = clientBundlingConfig.intermediate || clientDirSplit.join('/') + '/bundle.js';
|
|
104
|
+
clientBundlingConfig.output = clientBundlingConfig.output || {
|
|
105
|
+
filename: 'bundle.js',
|
|
106
|
+
path: Path.resolve(layout.PUBLIC_DIR),
|
|
107
|
+
};
|
|
159
108
|
waiting = Ui.waiting(`Writing the client entry file: ${clientBundlingConfig.intermediate}`);
|
|
160
109
|
waiting.start();
|
|
161
|
-
|
|
162
|
-
// Write
|
|
163
110
|
DotJs.write(clientBuild, clientBundlingConfig.intermediate, 'Client Build File');
|
|
164
|
-
|
|
165
111
|
waiting.stop();
|
|
166
112
|
Ui.info(Ui.f`Client Build file: ${clientBundlingConfig.intermediate}`);
|
|
113
|
+
// ------------------
|
|
167
114
|
|
|
168
115
|
// -------------------
|
|
169
116
|
// Run webpack
|
|
@@ -230,7 +177,7 @@ const createBundle = (Ui, config, desc) => {
|
|
|
230
177
|
*
|
|
231
178
|
* @return void
|
|
232
179
|
*/
|
|
233
|
-
const buildRoutes = (Ui, entry,
|
|
180
|
+
const buildRoutes = (build, Ui, entry, desc) => {
|
|
234
181
|
// -------------------
|
|
235
182
|
// Helper functions
|
|
236
183
|
// -------------------
|
|
@@ -256,13 +203,16 @@ const buildRoutes = (Ui, entry, build, desc) => {
|
|
|
256
203
|
|
|
257
204
|
var indexCount = 0;
|
|
258
205
|
if (entry && Fs.existsSync(entry)) {
|
|
206
|
+
var clientDirname = entry.replace(/\\/g, '/').split('/').pop();
|
|
259
207
|
walk(entry, (file, ext) => {
|
|
260
208
|
//relativePath = relativePath.replace(/\\/g, '/');
|
|
261
209
|
if (file.replace(/\\/g, '/').endsWith('/index.js')) {
|
|
262
210
|
var relativePath = Path.relative(entry, file).replace(/\\/g, '/');
|
|
263
211
|
// Import code
|
|
264
212
|
var routeName = 'index' + (++ indexCount);
|
|
265
|
-
|
|
213
|
+
// IMPORTANT: we;re taking a step back here so that the parent-child relationship for
|
|
214
|
+
// the directories be involved
|
|
215
|
+
build.imports[`../${clientDirname}/${relativePath}`] = '* as ' + routeName;
|
|
266
216
|
// Definition code
|
|
267
217
|
var routePath = _beforeLast('/' + relativePath, '/index.js');
|
|
268
218
|
build.code.push(`layout['${routePath || '/'}'] = ${routeName};`);
|
|
@@ -275,3 +225,24 @@ const buildRoutes = (Ui, entry, build, desc) => {
|
|
|
275
225
|
Ui.log(`> (none)`);
|
|
276
226
|
}
|
|
277
227
|
};
|
|
228
|
+
|
|
229
|
+
const buildParams = (build, params, varName = null, indentation = 0) => {
|
|
230
|
+
if (varName) build.code.push(`const ${varName} = {`);
|
|
231
|
+
Object.keys(params).forEach(name => {
|
|
232
|
+
var _name = ` ${' '.repeat(indentation)}${(_isArray(params) ? '' : (name.includes(' ') ? `'${name}'` : name) + ': ')}`;
|
|
233
|
+
if ([ 'boolean', 'number' ].includes(typeof params[name])) {
|
|
234
|
+
build.code.push(`${_name}${params[name]},`);
|
|
235
|
+
} else if (_isArray(params[name])) {
|
|
236
|
+
build.code.push(`${_name}[`);
|
|
237
|
+
buildParams(build, params[name], null, indentation + 1);
|
|
238
|
+
build.code.push(` ${' '.repeat(indentation)}],`);
|
|
239
|
+
} else if (_isObject(params[name])) {
|
|
240
|
+
build.code.push(`${_name}{`);
|
|
241
|
+
buildParams(build, params[name], null, indentation + 1);
|
|
242
|
+
build.code.push(` ${' '.repeat(indentation)}},`);
|
|
243
|
+
} else {
|
|
244
|
+
build.code.push(`${_name}'${params[name]}',`);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
if (varName) build.code.push(`};`);
|
|
248
|
+
}
|
package/src/cmd/origins.js
CHANGED
|
@@ -64,9 +64,9 @@ export async function deploy(Ui, origin, flags = {}, layout = {}) {
|
|
|
64
64
|
Fs.mkdirSync(origin.deploy_path, {recursive: true});
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
git.cwd(origin.deploy_path);
|
|
67
|
+
await git.cwd(origin.deploy_path);
|
|
68
68
|
// Must come after git.cwd()
|
|
69
|
-
git.init();
|
|
69
|
+
await git.init();
|
|
70
70
|
|
|
71
71
|
const hosts = {
|
|
72
72
|
github: 'https://github.com',
|
package/src/cmd/server.js
CHANGED
|
@@ -61,7 +61,7 @@ export async function start(Ui, flags = {}, layout = {}) {
|
|
|
61
61
|
if (flags.env !== 'prod' && flags.watch) {
|
|
62
62
|
var nodemon, ecpt;
|
|
63
63
|
try {
|
|
64
|
-
nodemon = await import('nodemon');
|
|
64
|
+
nodemon = await import(Path.resolve('./node_modules/nodemon/lib/nodemon.js'));
|
|
65
65
|
} catch(e) {
|
|
66
66
|
ecpt = e;
|
|
67
67
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import _isString from '@webqit/util/js/isString.js';
|
|
6
|
+
import _isFunction from '@webqit/util/js/isFunction.js';
|
|
7
|
+
import _isArray from '@webqit/util/js/isArray.js';
|
|
8
|
+
import _arrFrom from '@webqit/util/arr/from.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ---------------------------
|
|
12
|
+
* The Router class
|
|
13
|
+
* ---------------------------
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export default class Router {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Constructs a new Router instance
|
|
20
|
+
* over route definitions.
|
|
21
|
+
*
|
|
22
|
+
* @param string|array path
|
|
23
|
+
* @param object layout
|
|
24
|
+
* @param object context
|
|
25
|
+
*
|
|
26
|
+
* @return void
|
|
27
|
+
*/
|
|
28
|
+
constructor(path, layout, context) {
|
|
29
|
+
this.path = _isArray(path) ? path : (path + '').split('/').filter(a => a);
|
|
30
|
+
this.layout = layout;
|
|
31
|
+
this.context = context;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Performs dynamic routing.
|
|
36
|
+
*
|
|
37
|
+
* @param array|string method
|
|
38
|
+
* @param Object event
|
|
39
|
+
* @param any arg
|
|
40
|
+
* @param function _default
|
|
41
|
+
*
|
|
42
|
+
* @return object
|
|
43
|
+
*/
|
|
44
|
+
async route(method, event, arg, _default) {
|
|
45
|
+
|
|
46
|
+
const $this = this;
|
|
47
|
+
|
|
48
|
+
// ----------------
|
|
49
|
+
// The loop
|
|
50
|
+
// ----------------
|
|
51
|
+
const next = async function(thisTick) {
|
|
52
|
+
const _this = { ...$this.context };
|
|
53
|
+
if (!thisTick.trail || thisTick.trail.length < thisTick.destination.length) {
|
|
54
|
+
thisTick = await $this.readTick(thisTick);
|
|
55
|
+
// -------------
|
|
56
|
+
_this.pathname = `/${thisTick.trail.join('/')}`;
|
|
57
|
+
_this.stepname = thisTick.trail[thisTick.trail.length - 1];
|
|
58
|
+
$this.finalizeHandlerContext(_this, thisTick);
|
|
59
|
+
// -------------
|
|
60
|
+
if (thisTick.exports) {
|
|
61
|
+
const methods = _arrFrom(thisTick.method);
|
|
62
|
+
const handler = _isFunction(thisTick.exports) && methods.includes('default') ? thisTick.exports : methods.reduce((_handler, name) => _handler || thisTick.exports[name.toLowerCase()], null);
|
|
63
|
+
if (handler) {
|
|
64
|
+
// -------------
|
|
65
|
+
// Dynamic response
|
|
66
|
+
// -------------
|
|
67
|
+
const _next = async (..._args) => {
|
|
68
|
+
const nextTick = { ...thisTick, arg: _args[0] };
|
|
69
|
+
if (_args.length > 1) {
|
|
70
|
+
var _url = _args[1], _request, requestInit = { ...(_args[2] || {}) };
|
|
71
|
+
if (_args[1] instanceof nextTick.event.Request) {
|
|
72
|
+
_request = _args[1];
|
|
73
|
+
_url = _request.url;
|
|
74
|
+
} else if (!_isString(_url)) {
|
|
75
|
+
throw new Error('Router redirect url must be a string!');
|
|
76
|
+
}
|
|
77
|
+
var newDestination = _url.startsWith('/') ? _url : $this.pathJoin(`/${thisTick.trail.join('/')}`, _url);
|
|
78
|
+
if (newDestination.startsWith('../')) {
|
|
79
|
+
throw new Error('Router redirect cannot traverse beyond the routing directory! (' + _url + ' >> ' + newDestination + ')');
|
|
80
|
+
}
|
|
81
|
+
if (requestInit.method) {
|
|
82
|
+
nextTick.method = requestInit.method;
|
|
83
|
+
if (_isArray(requestInit.method)) {
|
|
84
|
+
requestInit.method = requestInit.method[0];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (_request) {
|
|
88
|
+
nextTick.event = thisTick.event.retarget(_request, { ...requestInit, _proxy: { url: newDestination/** non-standard but works */ } });
|
|
89
|
+
} else {
|
|
90
|
+
nextTick.event = thisTick.event.retarget(newDestination, requestInit);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
nextTick.source = thisTick.destination.join('/');
|
|
94
|
+
nextTick.destination = newDestination.split('?').shift().split('/').map(a => a.trim()).filter(a => a);
|
|
95
|
+
nextTick.trail = _args[1].startsWith('/') ? [] : thisTick.trail.reduce((_commonRoot, _seg, i) => _commonRoot.length === i && _seg === nextTick.destination[i] ? _commonRoot.concat(_seg) : _commonRoot, []);
|
|
96
|
+
nextTick.trailOnFile = thisTick.trailOnFile.slice(0, nextTick.trail.length);
|
|
97
|
+
}
|
|
98
|
+
return next(nextTick);
|
|
99
|
+
};
|
|
100
|
+
// -------------
|
|
101
|
+
const nextPathname = thisTick.destination.slice(thisTick.trail.length);
|
|
102
|
+
_next.pathname = nextPathname.join('/');
|
|
103
|
+
_next.stepname = nextPathname[0];
|
|
104
|
+
// -------------
|
|
105
|
+
return await handler.call(_this, thisTick.event, thisTick.arg, _next/*next*/);
|
|
106
|
+
}
|
|
107
|
+
// Handler not found but exports found
|
|
108
|
+
return next(thisTick);
|
|
109
|
+
} else if ((thisTick.currentSegmentOnFile || {}).dirExists) {
|
|
110
|
+
// Exports not found but directory found
|
|
111
|
+
return next(thisTick);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// -------------
|
|
115
|
+
// Local file
|
|
116
|
+
// -------------
|
|
117
|
+
if (_default) {
|
|
118
|
+
return await _default.call(_this, thisTick.event, thisTick.arg);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return next({
|
|
123
|
+
destination: this.path,
|
|
124
|
+
event,
|
|
125
|
+
method,
|
|
126
|
+
arg,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _isTypeObject, _isNumeric } from '@webqit/util/js/index.js';
|
|
6
|
+
import { _before } from '@webqit/util/str/index.js';
|
|
7
|
+
import { wwwFormSet, wwwFormPathSerializeCallback } from './util.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The _Headers Mixin
|
|
11
|
+
*/
|
|
12
|
+
const _FormData = NativeFormData => class extends NativeFormData {
|
|
13
|
+
|
|
14
|
+
tee(callback = null) {
|
|
15
|
+
const formData1 = new this.constructor, formData2 = new this.constructor;
|
|
16
|
+
for (var [ name, value ] of this.entries()) {
|
|
17
|
+
const formDataType = formDataType(value);
|
|
18
|
+
if ((callback && callback(value, name, formDataType)) || (!callback && !formDataType)) {
|
|
19
|
+
formData1.append(name, value);
|
|
20
|
+
} else {
|
|
21
|
+
formData2.append(name, value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return [ formData1, formData2 ];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
json(data = {}, callback = null) {
|
|
28
|
+
if (arguments.length) {
|
|
29
|
+
Object.keys(data).forEach(key => {
|
|
30
|
+
wwwFormPathSerializeCallback(key, data[key], (_wwwFormPath, _value) => {
|
|
31
|
+
if (!callback || callback(_wwwFormPath, _value, _isTypeObject(_value))) {
|
|
32
|
+
this.append(_wwwFormPath, _value);
|
|
33
|
+
}
|
|
34
|
+
}, value => !formDataType(value));
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
var jsonBuild; // We'll dynamically determine if this should be an array or an object
|
|
39
|
+
for (var [ name, value ] of this.entries()) {
|
|
40
|
+
if (!jsonBuild) {
|
|
41
|
+
jsonBuild = _isNumeric(_before(name, '[')) ? [] : {};
|
|
42
|
+
}
|
|
43
|
+
wwwFormSet(jsonBuild, name, value);
|
|
44
|
+
}
|
|
45
|
+
return jsonBuild;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default _FormData;
|
|
51
|
+
|
|
52
|
+
export const formDataType = (value, list = null) => {
|
|
53
|
+
if (!_isTypeObject(value)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const toStringTag = value[Symbol.toStringTag];
|
|
57
|
+
return (list || [
|
|
58
|
+
'Uint8Array', 'Uint16Array', 'Uint32Array', 'ArrayBuffer', 'Blob', 'File', 'FormData', 'Stream'
|
|
59
|
+
]).reduce((_toStringTag, type) => _toStringTag || (toStringTag === type ? type : null), null);
|
|
60
|
+
};
|