2
0
mirror of https://github.com/lowdefy/lowdefy.git synced 2025-03-19 15:01:06 +08:00

feat: Add server manager and file watcher in reload event stream.

This commit is contained in:
Sam Tolmay 2021-12-01 17:50:40 +02:00
parent a556eabdbb
commit 8474aaf63c
No known key found for this signature in database
GPG Key ID: D004126FCD1A6DF0
17 changed files with 239 additions and 26 deletions

26
.pnp.cjs generated

@ -5659,6 +5659,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@lowdefy/engine", "workspace:packages/engine"],
["@lowdefy/helpers", "workspace:packages/utils/helpers"],
["@lowdefy/layout", "workspace:packages/layout"],
["@lowdefy/node-utils", "workspace:packages/utils/node-utils"],
["@lowdefy/operators-change-case", "workspace:packages/plugins/operators/operators-change-case"],
["@lowdefy/operators-diff", "workspace:packages/plugins/operators/operators-diff"],
["@lowdefy/operators-js", "workspace:packages/plugins/operators/operators-js"],
@ -5667,6 +5668,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@lowdefy/operators-uuid", "workspace:packages/plugins/operators/operators-uuid"],
["@lowdefy/operators-yaml", "workspace:packages/plugins/operators/operators-yaml"],
["@next/eslint-plugin-next", "npm:12.0.4"],
["chokidar", "npm:3.5.2"],
["less", "npm:4.1.2"],
["less-loader", "virtual:003bebd8b7a948d12b44e2c11a621884feb1891eea3645171e827971487f79396db9f7422bc411ccf3f90877e94ec86f5c3da70b96efb5daddb2ee3b35eae5c6#npm:10.2.0"],
["next", "virtual:003bebd8b7a948d12b44e2c11a621884feb1891eea3645171e827971487f79396db9f7422bc411ccf3f90877e94ec86f5c3da70b96efb5daddb2ee3b35eae5c6#npm:12.0.3"],
@ -5675,7 +5677,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["react", "npm:18.0.0-alpha-327d5c484-20211106"],
["react-dom", "virtual:573fe255dffc9c89f4f7aa60da718603753ee98acc55d6772bbd0ebdcf07f9183fb8e54b4f3f2246c538a14ead402db8d2e076039c667d1538702638a0cc87b8#npm:18.0.0-alpha-327d5c484-20211106"],
["react-icons", "virtual:003bebd8b7a948d12b44e2c11a621884feb1891eea3645171e827971487f79396db9f7422bc411ccf3f90877e94ec86f5c3da70b96efb5daddb2ee3b35eae5c6#npm:4.3.1"],
["swr", "virtual:b951ea20ab6cada5f665e8389a50d828047e6b6f10e6ebaddde1e74a94868ec6ec703ff140742f295ef663cf92da1bc80fe9bbeaab30196cba0e992f38cd19ea#npm:1.0.1"]
["swr", "virtual:b951ea20ab6cada5f665e8389a50d828047e6b6f10e6ebaddde1e74a94868ec6ec703ff140742f295ef663cf92da1bc80fe9bbeaab30196cba0e992f38cd19ea#npm:1.0.1"],
["yargs", "npm:17.3.0"]
],
"linkType": "SOFT",
}]
@ -22916,6 +22919,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["yargs-parser", "npm:20.2.9"]
],
"linkType": "HARD",
}],
["npm:17.3.0", {
"packageLocation": "./.yarn/cache/yargs-npm-17.3.0-d4a72039e2-2b68733868.zip/node_modules/yargs/",
"packageDependencies": [
["yargs", "npm:17.3.0"],
["cliui", "npm:7.0.4"],
["escalade", "npm:3.1.1"],
["get-caller-file", "npm:2.0.5"],
["require-directory", "npm:2.1.1"],
["string-width", "npm:4.2.3"],
["y18n", "npm:5.0.8"],
["yargs-parser", "npm:21.0.0"]
],
"linkType": "HARD",
}]
]],
["yargs-parser", [
@ -22932,6 +22949,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["yargs-parser", "npm:20.2.9"]
],
"linkType": "HARD",
}],
["npm:21.0.0", {
"packageLocation": "./.yarn/cache/yargs-parser-npm-21.0.0-d564c0a5d4-1e205fca1c.zip/node_modules/yargs-parser/",
"packageDependencies": [
["yargs-parser", "npm:21.0.0"]
],
"linkType": "HARD",
}]
]],
["yauzl", [

Binary file not shown.

Binary file not shown.

@ -14,7 +14,7 @@
limitations under the License.
*/
import spawnProcess from '../../utils/spawnProcess.js';
import { spawnProcess } from '@lowdefy/node-utils';
const args = {
npm: ['install', '--legacy-peer-deps'],
@ -25,7 +25,7 @@ async function installServer({ context }) {
context.print.spin(`Running ${context.packageManager} install.`);
try {
await spawnProcess({
context,
logger: context.print,
command: context.packageManager, // npm or yarn
args: args[context.packageManager],
processOptions: {

@ -14,13 +14,13 @@
limitations under the License.
*/
import spawnProcess from '../../utils/spawnProcess.js';
import { spawnProcess } from '@lowdefy/node-utils';
async function runLowdefyBuild({ context }) {
context.print.log('Running Lowdefy build.');
try {
await spawnProcess({
context,
logger: context.print,
command: context.packageManager, // npm or yarn
args: ['run', 'build:lowdefy'],
processOptions: {

@ -14,13 +14,13 @@
limitations under the License.
*/
import spawnProcess from '../../utils/spawnProcess.js';
import { spawnProcess } from '@lowdefy/node-utils';
async function runNextBuild({ context }) {
context.print.log('Running Next build.');
try {
await spawnProcess({
context,
logger: context.print,
command: context.packageManager, // npm or yarn
args: ['run', 'build:next'],
processOptions: {

@ -14,17 +14,15 @@
limitations under the License.
*/
import spawnProcess from '../../utils/spawnProcess.js';
import { spawnProcess } from '@lowdefy/node-utils';
async function runStart({ context }) {
context.print.spin(`Running "${context.packageManager} run start".`);
await spawnProcess({
context,
command: context.packageManager, // npm or yarn
logger: context.print,
args: ['run', 'start'],
processOptions: {
cwd: context.directories.server,
},
command: context.packageManager, // npm or yarn
processOptions: { cwd: context.directories.server },
silent: false,
});
}

@ -27,6 +27,7 @@
},
"files": [
"src/*",
"public/*",
"next.config.js",
".eslintrc.yaml"
],
@ -34,7 +35,7 @@
"build:lowdefy": "lowdefy-build",
"build:next": "next build",
"dev": "next dev",
"start": "next start",
"start": "node src/manager/run.mjs",
"lint": "next lint",
"next": "next"
},
@ -52,6 +53,7 @@
"@lowdefy/engine": "4.0.0-alpha.5",
"@lowdefy/helpers": "4.0.0-alpha.5",
"@lowdefy/layout": "4.0.0-alpha.5",
"@lowdefy/node-utils": "4.0.0-alpha.5",
"@lowdefy/operators-change-case": "4.0.0-alpha.5",
"@lowdefy/operators-diff": "4.0.0-alpha.5",
"@lowdefy/operators-js": "4.0.0-alpha.5",
@ -59,12 +61,14 @@
"@lowdefy/operators-nunjucks": "4.0.0-alpha.5",
"@lowdefy/operators-uuid": "4.0.0-alpha.5",
"@lowdefy/operators-yaml": "4.0.0-alpha.5",
"chokidar": "3.5.2",
"next": "12.0.3",
"next-auth": "4.0.0-beta.6",
"react": "18.0.0-alpha-327d5c484-20211106",
"react-dom": "18.0.0-alpha-327d5c484-20211106",
"react-icons": "4.3.1",
"swr": "1.0.1"
"swr": "1.0.1",
"yargs": "17.3.0"
},
"devDependencies": {
"@next/eslint-plugin-next": "12.0.4",

@ -42,6 +42,11 @@ const Reload = ({ children }) => {
console.log(event);
mutateCache();
};
sse.addEventListener('tick', (event) => {
console.log('tick event listener');
console.log(event);
mutateCache();
});
sse.onerror = (error) => {
console.log('ERROR');
console.error(error);

@ -0,0 +1,50 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
class BatchChanges {
constructor({ fn, context, minDelay }) {
this.fn = fn;
this.context = context;
this.delay = minDelay || 500;
this.minDelay = minDelay || 500;
this._call = this._call.bind(this);
}
newChange() {
this.delay = this.minDelay;
this._startTimer();
}
_startTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(this._call, this.delay);
}
async _call() {
try {
await this.fn();
} catch (error) {
this.context.print.error(error.message, { timestamp: true });
this.delay *= 2;
this.context.print.warn(`Retrying in ${this.delay / 1000}s.`, { timestamp: true });
this._startTimer();
}
}
}
export default BatchChanges;

@ -0,0 +1,50 @@
#!/usr/bin/env node
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { writeFile } from '@lowdefy/node-utils';
import startServer from './startServer.mjs';
const argv = yargs(hideBin(process.argv)).argv;
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const tick = async () => {
const filePath = path.resolve('./build/tick.json');
console.log(filePath);
for (let i = 0; i < 10; i++) {
await sleep(5000);
console.log('tick', i);
await writeFile({
filePath,
content: JSON.stringify({ tick: i }),
});
}
};
async function run() {
tick();
await startServer({
packageManager: argv.packageManager || 'npm',
});
}
run();

@ -0,0 +1,28 @@
/*
Copyright 2020-2021 Lowdefy, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { spawnProcess } from '@lowdefy/node-utils';
async function startServer({ packageManager }) {
await spawnProcess({
logger: console,
args: ['run', 'next', 'start'],
command: packageManager || 'npm',
silent: false,
});
}
export default startServer;

@ -14,21 +14,47 @@
limitations under the License.
*/
// TODO: Send keep-alive comment event: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#examples
import chokidar from 'chokidar';
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const handler = async (req, res) => {
console.log('Update');
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
res.setHeader('Cache-Control', 'no-cache, no-transform');
res.setHeader('X-Accel-Buffering', 'no');
res.setHeader('Connection', 'keep-alive');
for (let i = 0; i < 10; i++) {
console.log('Sending event', i);
res.write(`data: Hello seq ${i}\n\n`);
await sleep(1500);
}
res.end('done\n');
const watcher = chokidar.watch(['./build/tick.json'], {
ignored: [
/(^|[/\\])\../, // ignore dotfiles
],
persistent: true,
ignoreInitial: true,
});
const reload = () => {
try {
console.log('reload');
res.write(`event: tick\ndata: ${JSON.stringify({ hello: true })}\n\n`);
} catch (e) {
console.log(e);
}
};
watcher.on('add', () => reload());
watcher.on('change', () => reload());
watcher.on('unlink', () => reload());
// TODO: This isn't working.
req.on('close', () => {
console.log('req closed');
watcher.close().then(() => {
console.log('watcher closed');
});
});
await sleep(7000);
};
export default handler;

@ -27,6 +27,7 @@
},
"files": [
"src/*",
"public/*",
"next.config.js",
".eslintrc.yaml"
],

@ -18,6 +18,7 @@ import cleanDirectory from './cleanDirectory.js';
import getConfigFromEnv from './getConfigFromEnv.js';
import getFileExtension, { getFileSubExtension } from './getFileExtension.js';
import getSecretsFromEnv from './getSecretsFromEnv.js';
import spawnProcess from './spawnProcess.js';
import readFile from './readFile.js';
import writeFile from './writeFile.js';
@ -27,6 +28,7 @@ export {
getFileExtension,
getFileSubExtension,
getSecretsFromEnv,
spawnProcess,
readFile,
writeFile,
};

@ -16,7 +16,7 @@
import { spawn } from 'child_process';
async function spawnProcess({ context, command, args, processOptions, silent }) {
async function spawnProcess({ logger, command, args, processOptions, silent }) {
return new Promise((resolve, reject) => {
const process = spawn(command, args, processOptions);
@ -27,7 +27,7 @@ async function spawnProcess({ context, command, args, processOptions, silent })
.split('\n')
.forEach((line) => {
if (line) {
context.print.log(line);
logger.log(line);
}
});
}
@ -40,7 +40,7 @@ async function spawnProcess({ context, command, args, processOptions, silent })
.split('\n')
.forEach((line) => {
if (line) {
context.print.warn(line);
logger.warn(line);
}
});
}

@ -3884,6 +3884,7 @@ __metadata:
"@lowdefy/engine": 4.0.0-alpha.5
"@lowdefy/helpers": 4.0.0-alpha.5
"@lowdefy/layout": 4.0.0-alpha.5
"@lowdefy/node-utils": 4.0.0-alpha.5
"@lowdefy/operators-change-case": 4.0.0-alpha.5
"@lowdefy/operators-diff": 4.0.0-alpha.5
"@lowdefy/operators-js": 4.0.0-alpha.5
@ -3892,6 +3893,7 @@ __metadata:
"@lowdefy/operators-uuid": 4.0.0-alpha.5
"@lowdefy/operators-yaml": 4.0.0-alpha.5
"@next/eslint-plugin-next": 12.0.4
chokidar: 3.5.2
less: 4.1.2
less-loader: 10.2.0
next: 12.0.3
@ -3901,6 +3903,7 @@ __metadata:
react-dom: 18.0.0-alpha-327d5c484-20211106
react-icons: 4.3.1
swr: 1.0.1
yargs: 17.3.0
languageName: unknown
linkType: soft
@ -16829,7 +16832,7 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard
"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0":
"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
version: 4.2.3
resolution: "string-width@npm:4.2.3"
dependencies:
@ -18485,6 +18488,28 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard
"yargs-parser@npm:^21.0.0":
version: 21.0.0
resolution: "yargs-parser@npm:21.0.0"
checksum: 1e205fca1cb7a36a1585e2b94a64e641c12741b53627d338e12747f4dca3c3610cdd9bb235040621120548dd74c3ef03a8168d52a1eabfedccbe4a62462b6731
languageName: node
linkType: hard
"yargs@npm:17.3.0":
version: 17.3.0
resolution: "yargs@npm:17.3.0"
dependencies:
cliui: ^7.0.2
escalade: ^3.1.1
get-caller-file: ^2.0.5
require-directory: ^2.1.1
string-width: ^4.2.3
y18n: ^5.0.5
yargs-parser: ^21.0.0
checksum: 2b687338684bf9645e9389ffdbe813fc5a2ddfede299d46fbe5ac80eb9a391e558b97861ba44d2256936ebe9d7f8135f6a38af1c76a5685eac4061008b2df57a
languageName: node
linkType: hard
"yargs@npm:^16.1.0, yargs@npm:^16.2.0":
version: 16.2.0
resolution: "yargs@npm:16.2.0"