diff --git a/packages/server-dev/src/manager/getContext.mjs b/packages/server-dev/src/manager/getContext.mjs index 7e0f8e5ef..c0c28fa87 100644 --- a/packages/server-dev/src/manager/getContext.mjs +++ b/packages/server-dev/src/manager/getContext.mjs @@ -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; } diff --git a/packages/server-dev/src/manager/initialBuild.mjs b/packages/server-dev/src/manager/initialBuild.mjs index 32579b925..cd96934ef 100644 --- a/packages/server-dev/src/manager/initialBuild.mjs +++ b/packages/server-dev/src/manager/initialBuild.mjs @@ -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; diff --git a/packages/server-dev/src/manager/processes/installServer.mjs b/packages/server-dev/src/manager/processes/installServer.mjs index 8f13605f7..a949416e9 100644 --- a/packages/server-dev/src/manager/processes/installServer.mjs +++ b/packages/server-dev/src/manager/processes/installServer.mjs @@ -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; diff --git a/packages/server-dev/src/manager/processes/lowdefyBuild.mjs b/packages/server-dev/src/manager/processes/lowdefyBuild.mjs index eb7e6b211..b8b047550 100644 --- a/packages/server-dev/src/manager/processes/lowdefyBuild.mjs +++ b/packages/server-dev/src/manager/processes/lowdefyBuild.mjs @@ -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; diff --git a/packages/server-dev/src/manager/processes/nextBuild.mjs b/packages/server-dev/src/manager/processes/nextBuild.mjs index 16db81081..8a40201ae 100644 --- a/packages/server-dev/src/manager/processes/nextBuild.mjs +++ b/packages/server-dev/src/manager/processes/nextBuild.mjs @@ -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; diff --git a/packages/server-dev/src/manager/processes/reloadClients.mjs b/packages/server-dev/src/manager/processes/reloadClients.mjs new file mode 100644 index 000000000..ff8ee7189 --- /dev/null +++ b/packages/server-dev/src/manager/processes/reloadClients.mjs @@ -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; diff --git a/packages/server-dev/src/manager/processes/startServer.mjs b/packages/server-dev/src/manager/processes/startServer.mjs index 99b97ac12..92697bb29 100644 --- a/packages/server-dev/src/manager/processes/startServer.mjs +++ b/packages/server-dev/src/manager/processes/startServer.mjs @@ -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); } }); diff --git a/packages/server-dev/src/manager/wait.mjs b/packages/server-dev/src/manager/wait.mjs new file mode 100644 index 000000000..0014c7d99 --- /dev/null +++ b/packages/server-dev/src/manager/wait.mjs @@ -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; diff --git a/packages/server-dev/src/manager/watchers/configWatcher.mjs b/packages/server-dev/src/manager/watchers/configWatcher.mjs index ef2447d4c..73a2e9e1b 100644 --- a/packages/server-dev/src/manager/watchers/configWatcher.mjs +++ b/packages/server-dev/src/manager/watchers/configWatcher.mjs @@ -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] }); diff --git a/packages/server-dev/src/manager/watchers/envWatcher.mjs b/packages/server-dev/src/manager/watchers/envWatcher.mjs new file mode 100644 index 000000000..c15fc4c33 --- /dev/null +++ b/packages/server-dev/src/manager/watchers/envWatcher.mjs @@ -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; diff --git a/packages/server-dev/src/manager/watchers/setupWatcher.mjs b/packages/server-dev/src/manager/watchers/setupWatcher.mjs index 40cc66495..4952b1997 100644 --- a/packages/server-dev/src/manager/watchers/setupWatcher.mjs +++ b/packages/server-dev/src/manager/watchers/setupWatcher.mjs @@ -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, }); diff --git a/packages/server-dev/src/manager/watchers/startWatchers.mjs b/packages/server-dev/src/manager/watchers/startWatchers.mjs index 142b2b7a5..a90886024 100644 --- a/packages/server-dev/src/manager/watchers/startWatchers.mjs +++ b/packages/server-dev/src/manager/watchers/startWatchers.mjs @@ -15,6 +15,7 @@ */ import configWatcher from './configWatcher.mjs'; +import envWatcher from './envWatcher.mjs'; /* Config change @@ -57,7 +58,7 @@ Watch /build/plugins/* */ async function startWatchers(context) { - await Promise.all([configWatcher(context)]); + await Promise.all([configWatcher(context), envWatcher(context)]); } export default startWatchers; diff --git a/packages/utils/node-utils/src/writeFile.js b/packages/utils/node-utils/src/writeFile.js index e7c4b1356..d348db5b2 100644 --- a/packages/utils/node-utils/src/writeFile.js +++ b/packages/utils/node-utils/src/writeFile.js @@ -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 });