feat(server-dev): Updates to dev server manager.

This commit is contained in:
Sam Tolmay 2022-01-17 16:49:12 +02:00
parent b610a63a52
commit b4861d0892
No known key found for this signature in database
GPG Key ID: D004126FCD1A6DF0
13 changed files with 167 additions and 59 deletions

View File

@ -18,18 +18,35 @@ import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import lowdefyBuild from './processes/lowdefyBuild.mjs';
import nextBuild from './processes/nextBuild.mjs';
import installServer from './processes/installServer.mjs';
import reloadClients from './processes/reloadClients.mjs';
const argv = yargs(hideBin(process.argv)).argv;
async function getContext() {
const { configDirectory = process.cwd(), packageManager = 'npm', skipInstall } = argv;
const {
configDirectory = process.cwd(),
packageManager = 'npm',
skipInstall,
verbose = false,
} = argv;
const context = {
directories: {
config: path.resolve(configDirectory),
},
packageManager,
restartServer: () => {},
skipInstall,
restartServer: () => {},
shutdownServer: () => {},
verbose,
};
context.installServer = installServer(context);
context.lowdefyBuild = lowdefyBuild(context);
context.nextBuild = nextBuild(context);
context.reloadClients = reloadClients(context);
return context;
}

View File

@ -15,15 +15,11 @@
limitations under the License.
*/
import installServer from './processes/installServer.mjs';
import lowdefyBuild from './processes/lowdefyBuild.mjs';
import nextBuild from './processes/nextBuild.mjs';
async function initialBuild(context) {
await installServer(context);
await lowdefyBuild(context);
await installServer(context);
await nextBuild(context);
await context.installServer();
await context.lowdefyBuild();
await context.installServer();
await context.nextBuild();
}
export default initialBuild;

View File

@ -21,15 +21,17 @@ const args = {
yarn: ['install'],
};
async function installServer({ packageManager, skipInstall }) {
if (skipInstall) return;
console.log('Installing server');
await spawnProcess({
logger: console,
command: packageManager, // npm or yarn
args: args[packageManager],
silent: false,
});
function installServer({ packageManager, skipInstall, verbose }) {
return async () => {
if (skipInstall) return;
console.log('Installing server...');
await spawnProcess({
logger: console,
command: packageManager, // npm or yarn
args: args[packageManager],
silent: !verbose,
});
};
}
export default installServer;

View File

@ -16,21 +16,23 @@
import { spawnProcess } from '@lowdefy/node-utils';
async function runLowdefyBuild({ packageManager, directories }) {
await spawnProcess({
logger: console,
args: ['run', 'build:lowdefy'],
command: packageManager,
processOptions: {
env: {
...process.env,
LOWDEFY_BUILD_DIRECTORY: './build',
LOWDEFY_CONFIG_DIRECTORY: directories.config,
LOWDEFY_SERVER_DIRECTORY: process.cwd(),
function lowdefyBuild({ packageManager, directories }) {
return async () => {
await spawnProcess({
logger: console,
args: ['run', 'build:lowdefy'],
command: packageManager,
processOptions: {
env: {
...process.env,
LOWDEFY_BUILD_DIRECTORY: './build',
LOWDEFY_CONFIG_DIRECTORY: directories.config,
LOWDEFY_SERVER_DIRECTORY: process.cwd(),
},
},
},
silent: false,
});
silent: false,
});
};
}
export default runLowdefyBuild;
export default lowdefyBuild;

View File

@ -16,13 +16,16 @@
import { spawnProcess } from '@lowdefy/node-utils';
async function runNextBuild({ packageManager }) {
await spawnProcess({
logger: console,
args: ['run', 'build:next'],
command: packageManager,
silent: false,
});
function nextBuild({ packageManager, verbose }) {
return async () => {
console.log('Building next app...');
await spawnProcess({
logger: console,
args: ['run', 'build:next'],
command: packageManager,
silent: !verbose,
});
};
}
export default runNextBuild;
export default nextBuild;

View File

@ -0,0 +1,26 @@
/*
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 { writeFile } from '@lowdefy/node-utils';
function reloadClients(context) {
return async () => {
await writeFile({ filePath: './build/reload', content: `${Date.now()}` });
console.log('Reloaded');
};
}
export default reloadClients;

View File

@ -24,17 +24,22 @@ function startServerProcess({ context, handleExit }) {
silent: false,
});
context.serverProcess.on('exit', handleExit);
context.restartServer = async () => {
context.restartServer = () => {
console.log('Restarting server...');
context.serverProcess.kill();
startServerProcess({ context, handleExit });
};
context.shutdownServer = () => {
console.log('Shutting down server...');
context.serverProcess.kill();
};
}
async function startServer(context) {
return new Promise((resolve, reject) => {
function handleExit(code) {
if (code !== 0) {
// TODO: Shutdown server
context.shutdownServer && context.shutdownServer();
reject(new Error('Server error.'));
}
resolve();
@ -42,7 +47,7 @@ async function startServer(context) {
try {
startServerProcess({ context, handleExit });
} catch (error) {
// TODO: Shutdown server
context.shutdownServer && context.shutdownServer();
reject(error);
}
});

View File

@ -0,0 +1,21 @@
/*
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.
*/
async function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export default wait;

View File

@ -14,13 +14,12 @@
limitations under the License.
*/
import lowdefyBuild from '../processes/lowdefyBuild.mjs';
import setupWatcher from './setupWatcher.mjs';
async function configWatcher(context) {
const callback = async () => {
await lowdefyBuild(context);
context.restartServer();
await context.lowdefyBuild();
context.reloadClients({ type: 'soft' });
};
// TODO: Add ignored paths
return setupWatcher({ callback, watchPaths: [context.directories.config] });

View File

@ -0,0 +1,37 @@
/*
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 setupWatcher from './setupWatcher.mjs';
import wait from '../wait.mjs';
async function envWatcher(context) {
const callback = async () => {
console.log('.env file changed, restarting server...');
context.reloadClients({ type: 'hard' });
// Wait for clients to get reload event.
await wait(500);
context.restartServer();
};
// TODO: Add ignored paths
return setupWatcher({
callback,
watchPaths: [path.resolve(context.directories.config, '.env')],
watchDotfiles: true,
});
}
export default envWatcher;

View File

@ -17,16 +17,19 @@
import chokidar from 'chokidar';
import BatchChanges from '../BatchChanges.mjs';
function setupWatcher({ callback, watchPaths }) {
function setupWatcher({ callback, watchDotfiles = false, ignorePaths = [], watchPaths }) {
return new Promise((resolve) => {
// const { watch = [], watchIgnore = [] } = context.options;
// const resolvedWatchPaths = watch.map((pathName) => path.resolve(pathName));
const batchChanges = new BatchChanges({ fn: callback });
const defaultIgnorePaths = watchDotfiles
? []
: [
/(^|[/\\])\../, // ignore dotfiles
];
const configWatcher = chokidar.watch(watchPaths, {
ignored: [
/(^|[/\\])\../, // ignore dotfiles
],
ignored: [...defaultIgnorePaths, ...ignorePaths],
persistent: true,
ignoreInitial: true,
});

View File

@ -15,6 +15,7 @@
*/
import configWatcher from './configWatcher.mjs';
import envWatcher from './envWatcher.mjs';
/*
Config change
@ -57,7 +58,7 @@ Watch <server-dir>/build/plugins/*
*/
async function startWatchers(context) {
await Promise.all([configWatcher(context)]);
await Promise.all([configWatcher(context), envWatcher(context)]);
}
export default startWatchers;

View File

@ -28,13 +28,9 @@ async function writeFile({ filePath, content }) {
`Could not write file, file path should be a string, received ${JSON.stringify(filePath)}.`
);
}
if (filePath !== path.resolve(filePath)) {
throw new Error(
`Could not write file, file path was not resolved, received ${JSON.stringify(filePath)}.`
);
}
try {
await writeFilePromise(filePath, content);
await writeFilePromise(path.resolve(filePath), content);
} catch (error) {
if (error.code === 'ENOENT') {
await mkdirPromise(path.dirname(filePath), { recursive: true });