diff --git a/.pnp.cjs b/.pnp.cjs
index 58a97390e..1fd36ec3f 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -5700,6 +5700,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@lowdefy/operators-yaml", "workspace:packages/plugins/operators/operators-yaml"],
["@next/eslint-plugin-next", "npm:12.0.4"],
["chokidar", "npm:3.5.2"],
+ ["dotenv", "npm:14.2.0"],
+ ["js-yaml", "npm:4.1.0"],
["less", "npm:4.1.2"],
["less-loader", "virtual:003bebd8b7a948d12b44e2c11a621884feb1891eea3645171e827971487f79396db9f7422bc411ccf3f90877e94ec86f5c3da70b96efb5daddb2ee3b35eae5c6#npm:10.2.0"],
["next", "virtual:003bebd8b7a948d12b44e2c11a621884feb1891eea3645171e827971487f79396db9f7422bc411ccf3f90877e94ec86f5c3da70b96efb5daddb2ee3b35eae5c6#npm:12.0.3"],
@@ -10536,6 +10538,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["dotenv", [
+ ["npm:14.2.0", {
+ "packageLocation": "./.yarn/cache/dotenv-npm-14.2.0-b237345d26-85a0e44918.zip/node_modules/dotenv/",
+ "packageDependencies": [
+ ["dotenv", "npm:14.2.0"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["duplexer", [
["npm:0.1.2", {
"packageLocation": "./.yarn/cache/duplexer-npm-0.1.2-952c810235-62ba61a830.zip/node_modules/duplexer/",
diff --git a/.yarn/cache/dotenv-npm-14.2.0-b237345d26-85a0e44918.zip b/.yarn/cache/dotenv-npm-14.2.0-b237345d26-85a0e44918.zip
new file mode 100644
index 000000000..2f17715b4
Binary files /dev/null and b/.yarn/cache/dotenv-npm-14.2.0-b237345d26-85a0e44918.zip differ
diff --git a/packages/cli/src/commands/dev/runDevServer.js b/packages/cli/src/commands/dev/runDevServer.js
index b2ee9565f..fd19dbe9a 100644
--- a/packages/cli/src/commands/dev/runDevServer.js
+++ b/packages/cli/src/commands/dev/runDevServer.js
@@ -17,6 +17,7 @@
import { spawnProcess } from '@lowdefy/node-utils';
async function runDevServer({ context }) {
+ // TODO: Pass packageManager as option
await spawnProcess({
logger: context.print,
args: ['run', 'start'],
diff --git a/packages/server-dev/package.json b/packages/server-dev/package.json
index c533341dc..f397fcd8a 100644
--- a/packages/server-dev/package.json
+++ b/packages/server-dev/package.json
@@ -62,6 +62,8 @@
"@lowdefy/operators-uuid": "4.0.0-alpha.6",
"@lowdefy/operators-yaml": "4.0.0-alpha.6",
"chokidar": "3.5.2",
+ "dotenv": "14.2.0",
+ "js-yaml": "4.1.0",
"next": "12.0.3",
"next-auth": "4.0.0-beta.6",
"opener": "1.5.2",
diff --git a/packages/server-dev/src/components/Page.js b/packages/server-dev/src/components/Page.js
index 44aa76ce7..5276f284c 100644
--- a/packages/server-dev/src/components/Page.js
+++ b/packages/server-dev/src/components/Page.js
@@ -26,7 +26,7 @@ const LoadingBlock = () =>
Loading...
;
const Page = ({ lowdefy }) => {
const { data: pageConfig } = usePageConfig(lowdefy.pageId);
if (!pageConfig) {
- lowdefy._internal.router.push(`/404`);
+ lowdefy._internal.router.replace(`/404`);
return ;
}
return (
diff --git a/packages/server-dev/src/manager/getContext.mjs b/packages/server-dev/src/manager/getContext.mjs
index fee5983f1..4049afab3 100644
--- a/packages/server-dev/src/manager/getContext.mjs
+++ b/packages/server-dev/src/manager/getContext.mjs
@@ -19,11 +19,15 @@ import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
+import initialBuild from './processes/initialBuild.mjs';
+import installPlugins from './processes/installPlugins.mjs';
import lowdefyBuild from './processes/lowdefyBuild.mjs';
import nextBuild from './processes/nextBuild.mjs';
-import installPlugins from './processes/installPlugins.mjs';
-import startServerProcess from './processes/startServerProcess.mjs';
+import readDotEnv from './processes/readDotEnv.mjs';
import reloadClients from './processes/reloadClients.mjs';
+import restartServer from './processes/restartServer.mjs';
+import shutdownServer from './processes/shutdownServer.mjs';
+import startWatchers from './processes/startWatchers.mjs';
const argv = yargs(hideBin(process.argv)).argv;
@@ -38,25 +42,19 @@ async function getContext() {
server: process.cwd(),
},
packageManager,
- restartServer: () => {
- if (context.serverProcess) {
- console.log('Restarting server...');
- context.serverProcess.kill();
- startServerProcess(context);
- }
- },
- shutdownServer: () => {
- if (context.serverProcess) {
- console.log('Shutting down server...');
- context.serverProcess.kill();
- }
- },
verbose,
};
+
+ context.version = process.env.npm_package_version;
+ context.initialBuild = initialBuild(context);
context.installPlugins = installPlugins(context);
context.lowdefyBuild = lowdefyBuild(context);
context.nextBuild = nextBuild(context);
+ context.readDotEnv = readDotEnv(context);
context.reloadClients = reloadClients(context);
+ context.restartServer = restartServer(context);
+ context.shutdownServer = shutdownServer(context);
+ context.startWatchers = startWatchers(context);
return context;
}
diff --git a/packages/server-dev/src/manager/initialBuild.mjs b/packages/server-dev/src/manager/processes/initialBuild.mjs
similarity index 76%
rename from packages/server-dev/src/manager/initialBuild.mjs
rename to packages/server-dev/src/manager/processes/initialBuild.mjs
index 8b0d69a54..58d23711e 100644
--- a/packages/server-dev/src/manager/initialBuild.mjs
+++ b/packages/server-dev/src/manager/processes/initialBuild.mjs
@@ -15,10 +15,13 @@
limitations under the License.
*/
-async function initialBuild(context) {
- await context.lowdefyBuild();
- await context.installPlugins();
- await context.nextBuild();
+function initialBuild(context) {
+ return async () => {
+ await context.lowdefyBuild();
+ await context.installPlugins();
+ await context.nextBuild();
+ await context.readDotEnv();
+ };
}
export default initialBuild;
diff --git a/packages/server-dev/src/manager/processes/readDotEnv.mjs b/packages/server-dev/src/manager/processes/readDotEnv.mjs
new file mode 100644
index 000000000..77c2e8567
--- /dev/null
+++ b/packages/server-dev/src/manager/processes/readDotEnv.mjs
@@ -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 path from 'path';
+import dotenv from 'dotenv';
+import { readFile } from '@lowdefy/node-utils';
+
+function readDotEnv(context) {
+ return async () => {
+ const dotEnvPath = path.join(context.directories.config, '.env');
+ context.serverEnv = dotenv.parse(await readFile(dotEnvPath));
+ };
+}
+
+export default readDotEnv;
diff --git a/packages/server-dev/src/manager/processes/restartServer.mjs b/packages/server-dev/src/manager/processes/restartServer.mjs
new file mode 100644
index 000000000..0e69a3f5e
--- /dev/null
+++ b/packages/server-dev/src/manager/processes/restartServer.mjs
@@ -0,0 +1,29 @@
+/*
+ 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 startServerProcess from './startServerProcess.mjs';
+
+function shutdownServer(context) {
+ return async () => {
+ if (context.serverProcess) {
+ console.log('Restarting server...');
+ context.serverProcess.kill();
+ startServerProcess(context);
+ }
+ };
+}
+
+export default shutdownServer;
diff --git a/packages/server-dev/src/manager/processes/shutdownServer.mjs b/packages/server-dev/src/manager/processes/shutdownServer.mjs
new file mode 100644
index 000000000..f3e5e0b71
--- /dev/null
+++ b/packages/server-dev/src/manager/processes/shutdownServer.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.
+*/
+
+function shutdownServer(context) {
+ return async () => {
+ if (context.serverProcess) {
+ console.log('Shutting down server...');
+ context.serverProcess.kill();
+ }
+ };
+}
+
+export default shutdownServer;
diff --git a/packages/server-dev/src/manager/processes/startServerProcess.mjs b/packages/server-dev/src/manager/processes/startServerProcess.mjs
index 60f5a141a..5307c4eaa 100644
--- a/packages/server-dev/src/manager/processes/startServerProcess.mjs
+++ b/packages/server-dev/src/manager/processes/startServerProcess.mjs
@@ -14,7 +14,7 @@
limitations under the License.
*/
-import spawnProcess from '../spawnProcess.mjs';
+import spawnProcess from '../utils/spawnProcess.mjs';
function startServerProcess(context) {
context.serverProcess = spawnProcess({
@@ -22,6 +22,12 @@ function startServerProcess(context) {
command: context.packageManager,
args: ['run', 'next', 'start'],
silent: false,
+ processOptions: {
+ env: {
+ ...process.env,
+ ...context.serverEnv,
+ },
+ },
});
context.serverProcess.on('exit', (code) => {
if (code !== 0) {
diff --git a/packages/server-dev/src/manager/watchers/startWatchers.mjs b/packages/server-dev/src/manager/processes/startWatchers.mjs
similarity index 86%
rename from packages/server-dev/src/manager/watchers/startWatchers.mjs
rename to packages/server-dev/src/manager/processes/startWatchers.mjs
index a90886024..f1b354c6a 100644
--- a/packages/server-dev/src/manager/watchers/startWatchers.mjs
+++ b/packages/server-dev/src/manager/processes/startWatchers.mjs
@@ -14,8 +14,8 @@
limitations under the License.
*/
-import configWatcher from './configWatcher.mjs';
-import envWatcher from './envWatcher.mjs';
+import configWatcher from '../watchers/configWatcher.mjs';
+import envWatcher from '../watchers/envWatcher.mjs';
/*
Config change
@@ -31,7 +31,6 @@ Watch /package.json
- No need for Lowdefy build (confirm?)
- Trigger hard reload
- Restart server.
-
----------------------------------------
.env change
Watch /.env
@@ -48,7 +47,6 @@ Watch /build/config.json
- Next build.
- Trigger hard reload
- Restart server.
-
----------------------------------------
New plugin or icon used.
Watch /build/plugins/*
@@ -57,8 +55,10 @@ Watch /build/plugins/*
- Restart server.
*/
-async function startWatchers(context) {
- await Promise.all([configWatcher(context), envWatcher(context)]);
+function startWatchers(context) {
+ return async () => {
+ await Promise.all([configWatcher(context), envWatcher(context)]);
+ };
}
export default startWatchers;
diff --git a/packages/server-dev/src/manager/run.mjs b/packages/server-dev/src/manager/run.mjs
index 2597e21fc..1a276df39 100644
--- a/packages/server-dev/src/manager/run.mjs
+++ b/packages/server-dev/src/manager/run.mjs
@@ -18,14 +18,12 @@
import { wait } from '@lowdefy/helpers';
import opener from 'opener';
import getContext from './getContext.mjs';
-import initialBuild from './initialBuild.mjs';
import startServer from './processes/startServer.mjs';
-import startWatchers from './watchers/startWatchers.mjs';
async function run() {
const context = await getContext();
- await initialBuild(context);
- await startWatchers(context);
+ await context.initialBuild();
+ await context.startWatchers();
try {
const serverPromise = startServer(context);
await wait(800);
diff --git a/packages/server-dev/src/manager/BatchChanges.mjs b/packages/server-dev/src/manager/utils/BatchChanges.mjs
similarity index 98%
rename from packages/server-dev/src/manager/BatchChanges.mjs
rename to packages/server-dev/src/manager/utils/BatchChanges.mjs
index def45d671..ec1cb4d51 100644
--- a/packages/server-dev/src/manager/BatchChanges.mjs
+++ b/packages/server-dev/src/manager/utils/BatchChanges.mjs
@@ -13,6 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
+/* eslint-disable no-console */
class BatchChanges {
constructor({ fn, minDelay }) {
diff --git a/packages/server-dev/src/manager/utils/getLowdefyVersion.mjs b/packages/server-dev/src/manager/utils/getLowdefyVersion.mjs
new file mode 100644
index 000000000..d03a3cd99
--- /dev/null
+++ b/packages/server-dev/src/manager/utils/getLowdefyVersion.mjs
@@ -0,0 +1,51 @@
+/*
+ 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 { type } from '@lowdefy/helpers';
+import { readFile } from '@lowdefy/node-utils';
+import YAML from 'js-yaml';
+
+async function getLowdefyVersion(context) {
+ let lowdefyYaml = await readFile(path.join(context.directories.config, 'lowdefy.yaml'));
+ if (!lowdefyYaml) {
+ lowdefyYaml = await readFile(path.join(context.directories.config, 'lowdefy.yml'));
+ }
+ if (!lowdefyYaml) {
+ throw new Error(`Could not find "lowdefy.yaml" file.`);
+ }
+ let lowdefy;
+ try {
+ lowdefy = YAML.load(lowdefyYaml);
+ } catch (error) {
+ throw new Error(`Could not parse "lowdefy.yaml" file. Received error ${error.message}.`);
+ }
+ if (!lowdefy.lowdefy) {
+ throw new Error(
+ `No version specified in "lowdefy.yaml" file. Specify a version in the "lowdefy" field.`
+ );
+ }
+ if (!type.isString(lowdefy.lowdefy)) {
+ throw new Error(
+ `Version number specified in "lowdefy.yaml" file should be a string. Received ${JSON.stringify(
+ lowdefy.lowdefy
+ )}.`
+ );
+ }
+ return lowdefy.lowdefy;
+}
+
+export default getLowdefyVersion;
diff --git a/packages/server-dev/src/manager/watchers/setupWatcher.mjs b/packages/server-dev/src/manager/utils/setupWatcher.mjs
similarity index 83%
rename from packages/server-dev/src/manager/watchers/setupWatcher.mjs
rename to packages/server-dev/src/manager/utils/setupWatcher.mjs
index 4952b1997..ffb84b67e 100644
--- a/packages/server-dev/src/manager/watchers/setupWatcher.mjs
+++ b/packages/server-dev/src/manager/utils/setupWatcher.mjs
@@ -15,14 +15,20 @@
*/
import chokidar from 'chokidar';
-import BatchChanges from '../BatchChanges.mjs';
+import BatchChanges from './BatchChanges.mjs';
-function setupWatcher({ callback, watchDotfiles = false, ignorePaths = [], watchPaths }) {
+function setupWatcher({
+ callback,
+ watchDotfiles = false,
+ ignorePaths = [],
+ watchPaths,
+ minDelay = 500,
+}) {
return new Promise((resolve) => {
// const { watch = [], watchIgnore = [] } = context.options;
// const resolvedWatchPaths = watch.map((pathName) => path.resolve(pathName));
- const batchChanges = new BatchChanges({ fn: callback });
+ const batchChanges = new BatchChanges({ fn: callback, minDelay });
const defaultIgnorePaths = watchDotfiles
? []
: [
@@ -33,9 +39,9 @@ function setupWatcher({ callback, watchDotfiles = false, ignorePaths = [], watch
persistent: true,
ignoreInitial: true,
});
- configWatcher.on('add', (...args) => batchChanges.newChange(args));
- configWatcher.on('change', (...args) => batchChanges.newChange(args));
- configWatcher.on('unlink', (...args) => batchChanges.newChange(args));
+ configWatcher.on('add', (...args) => batchChanges.newChange(...args));
+ configWatcher.on('change', (...args) => batchChanges.newChange(...args));
+ configWatcher.on('unlink', (...args) => batchChanges.newChange(...args));
configWatcher.on('ready', () => resolve());
});
}
diff --git a/packages/server-dev/src/manager/spawnProcess.mjs b/packages/server-dev/src/manager/utils/spawnProcess.mjs
similarity index 100%
rename from packages/server-dev/src/manager/spawnProcess.mjs
rename to packages/server-dev/src/manager/utils/spawnProcess.mjs
diff --git a/packages/server-dev/src/manager/watchers/configWatcher.mjs b/packages/server-dev/src/manager/watchers/configWatcher.mjs
index f58c7e18d..18b88dc37 100644
--- a/packages/server-dev/src/manager/watchers/configWatcher.mjs
+++ b/packages/server-dev/src/manager/watchers/configWatcher.mjs
@@ -13,11 +13,24 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
+/* eslint-disable no-console */
-import setupWatcher from './setupWatcher.mjs';
+import getLowdefyVersion from '../utils/getLowdefyVersion.mjs';
+import setupWatcher from '../utils/setupWatcher.mjs';
async function configWatcher(context) {
- const callback = async () => {
+ const callback = async (filePaths) => {
+ const lowdefyYamlModified = filePaths
+ .flat()
+ .some((filePath) => filePath.includes('lowdefy.yaml') || filePath.includes('lowdefy.yml'));
+ if (lowdefyYamlModified) {
+ const lowdefyVersion = await getLowdefyVersion(context);
+ if (lowdefyVersion !== context.version) {
+ console.warn('Lowdefy version changed. You should restart your development server.');
+ process.exit();
+ }
+ }
+
await context.lowdefyBuild();
context.reloadClients();
};
diff --git a/packages/server-dev/src/manager/watchers/envWatcher.mjs b/packages/server-dev/src/manager/watchers/envWatcher.mjs
index bd36738d8..356b00c9b 100644
--- a/packages/server-dev/src/manager/watchers/envWatcher.mjs
+++ b/packages/server-dev/src/manager/watchers/envWatcher.mjs
@@ -13,13 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
*/
+/* eslint-disable no-console */
import path from 'path';
-import setupWatcher from './setupWatcher.mjs';
+import setupWatcher from '../utils/setupWatcher.mjs';
async function envWatcher(context) {
const callback = async () => {
- console.log('.env file changed, restarting server...');
+ console.warn('.env file changed.');
+ await context.readDotEnv();
context.restartServer();
};
return setupWatcher({
diff --git a/packages/utils/node-utils/src/readFile.js b/packages/utils/node-utils/src/readFile.js
index e78741883..a5df4bf98 100644
--- a/packages/utils/node-utils/src/readFile.js
+++ b/packages/utils/node-utils/src/readFile.js
@@ -27,14 +27,9 @@ async function readFile(filePath) {
`Could not read file, file path should be a string, received ${JSON.stringify(filePath)}.`
);
}
- if (filePath !== path.resolve(filePath)) {
- throw new Error(
- `Could not read file, file path was not resolved, received ${JSON.stringify(filePath)}.`
- );
- }
try {
// By specifying encoding, readFile returns a string instead of a buffer.
- const file = await readFilePromise(filePath, 'utf8');
+ const file = await readFilePromise(path.resolve(filePath), 'utf8');
return file;
} catch (error) {
if (error.code === 'ENOENT' || error.code === 'EISDIR') {
diff --git a/packages/utils/node-utils/src/readFile.test.js b/packages/utils/node-utils/src/readFile.test.js
index 6ee6a9d6d..71f9f33cf 100644
--- a/packages/utils/node-utils/src/readFile.test.js
+++ b/packages/utils/node-utils/src/readFile.test.js
@@ -35,9 +35,3 @@ test('readFile error id filepath is not a string', async () => {
'Could not read file, file path should be a string, received {}.'
);
});
-
-test('readFile errors if path is not already resolved', async () => {
- await expect(readFile('./readFile/readFile.txt')).rejects.toThrow(
- 'Could not read file, file path was not resolved, received "./readFile/readFile.txt".'
- );
-});
diff --git a/yarn.lock b/yarn.lock
index 096212225..8e249c0ab 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3917,6 +3917,8 @@ __metadata:
"@lowdefy/operators-yaml": 4.0.0-alpha.6
"@next/eslint-plugin-next": 12.0.4
chokidar: 3.5.2
+ dotenv: 14.2.0
+ js-yaml: 4.1.0
less: 4.1.2
less-loader: 10.2.0
next: 12.0.3
@@ -7945,6 +7947,13 @@ __metadata:
languageName: node
linkType: hard
+"dotenv@npm:14.2.0":
+ version: 14.2.0
+ resolution: "dotenv@npm:14.2.0"
+ checksum: 85a0e44918ef49e64c278f757dab50a156b9a6ca67f708876fd81d265e575e35b67387fc681d910df99368d6c1edca66cd546edeb0f7db3b499cb876c999233e
+ languageName: node
+ linkType: hard
+
"duplexer@npm:^0.1.1":
version: 0.1.2
resolution: "duplexer@npm:0.1.2"