@lowdefy/server-dev 4.5.1 → 4.6.0
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/{manager/utils/createLogger.mjs → lib/build/app.js} +4 -18
- package/lib/build/auth.js +19 -0
- package/lib/build/config.js +19 -0
- package/lib/build/logger.js +19 -0
- package/lib/client/App.js +25 -21
- package/lib/client/BuildErrorPage.js +91 -0
- package/lib/client/BuildingPage.js +74 -0
- package/lib/client/InstallingPluginsPage.js +50 -0
- package/lib/client/Page.js +20 -2
- package/lib/client/Reload.js +1 -1
- package/lib/client/RestartingPage.js +3 -1
- package/lib/client/auth/Auth.js +2 -2
- package/lib/client/auth/AuthConfigured.js +2 -2
- package/lib/client/auth/AuthNotConfigured.js +1 -1
- package/lib/client/setPageId.js +1 -1
- package/lib/client/utils/request.js +2 -5
- package/lib/client/utils/useMutateCache.js +16 -3
- package/lib/client/utils/usePageConfig.js +51 -5
- package/lib/client/utils/useRootConfig.js +1 -1
- package/lib/client/utils/waitForRestartedServer.js +1 -1
- package/lib/server/apiWrapper.js +48 -8
- package/lib/server/auth/getAuthOptions.js +2 -2
- package/lib/server/auth/getMockSession.js +67 -0
- package/lib/server/auth/getServerSession.js +10 -3
- package/lib/server/fileCache.js +1 -1
- package/lib/server/jitPageBuilder.js +162 -0
- package/lib/server/log/createHandleError.js +47 -0
- package/lib/server/log/createLogger.js +4 -4
- package/lib/server/log/logRequest.js +1 -1
- package/lib/server/pageCache.mjs +61 -0
- package/lib/server/pageCache.test.mjs +80 -0
- package/manager/getContext.mjs +26 -4
- package/manager/processes/checkMockUserWarning.mjs +42 -0
- package/manager/processes/initialBuild.mjs +2 -1
- package/manager/processes/installPlugins.mjs +3 -3
- package/manager/processes/lowdefyBuild.mjs +16 -5
- package/manager/processes/nextBuild.mjs +27 -8
- package/manager/processes/readDotEnv.mjs +1 -1
- package/manager/processes/reloadClients.mjs +1 -1
- package/manager/processes/restartServer.mjs +3 -3
- package/manager/processes/shutdownServer.mjs +2 -2
- package/manager/processes/startServer.mjs +32 -15
- package/manager/processes/startWatchers.mjs +1 -1
- package/manager/run.mjs +4 -12
- package/manager/utils/BatchChanges.mjs +1 -1
- package/manager/utils/checkPortAvailable.mjs +41 -0
- package/manager/utils/createCustomPluginTypesMap.mjs +1 -1
- package/manager/utils/getLowdefyVersion.mjs +8 -7
- package/manager/utils/getNextBin.mjs +1 -1
- package/manager/utils/setupWatcher.mjs +1 -1
- package/manager/watchers/envWatcher.mjs +1 -1
- package/manager/watchers/lowdefyBuildWatcher.mjs +26 -5
- package/manager/watchers/nextBuildWatcher.mjs +12 -2
- package/next.config.js +3 -2
- package/package.json +33 -29
- package/package.original.json +32 -28
- package/pages/404.js +1 -1
- package/pages/[pageId].js +1 -1
- package/pages/_app.js +17 -4
- package/pages/_document.js +3 -3
- package/pages/api/auth/[...nextauth].js +13 -2
- package/pages/api/client-error.js +45 -0
- package/pages/api/dev-tools.js +1 -1
- package/pages/api/endpoints/[endpointId].js +1 -1
- package/pages/api/js/[env].js +42 -0
- package/pages/api/page/[pageId].js +40 -2
- package/pages/api/ping.js +1 -1
- package/pages/api/reload.js +6 -3
- package/pages/api/request/[pageId]/[requestId].js +6 -3
- package/pages/api/root.js +1 -1
- package/pages/index.js +1 -1
- package/lib/server/log/logError.js +0 -40
- package/manager/utils/createStdOutLineHandler.mjs +0 -33
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFile } from 'fs/promises';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
|
|
20
|
+
function checkMockUserWarning(context) {
|
|
21
|
+
return async () => {
|
|
22
|
+
// Check env var first
|
|
23
|
+
if (process.env.LOWDEFY_DEV_USER) {
|
|
24
|
+
context.logger.warn('Mock user active - login bypassed');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check auth.json after build
|
|
29
|
+
try {
|
|
30
|
+
const authJsonPath = path.join(context.directories.build, 'auth.json');
|
|
31
|
+
const authJsonContent = await readFile(authJsonPath, 'utf8');
|
|
32
|
+
const authJson = JSON.parse(authJsonContent);
|
|
33
|
+
if (authJson.dev?.mockUser) {
|
|
34
|
+
context.logger.warn('Mock user active - login bypassed');
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
// auth.json may not exist if auth is not configured
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default checkMockUserWarning;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
Copyright 2020-
|
|
3
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
4
4
|
|
|
5
5
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
you may not use this file except in compliance with the License.
|
|
@@ -19,6 +19,7 @@ function initialBuild(context) {
|
|
|
19
19
|
return async () => {
|
|
20
20
|
context.readDotEnv();
|
|
21
21
|
await context.lowdefyBuild();
|
|
22
|
+
await context.checkMockUserWarning();
|
|
22
23
|
await context.installPlugins();
|
|
23
24
|
await context.nextBuild();
|
|
24
25
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -18,7 +18,7 @@ import { spawnProcess } from '@lowdefy/node-utils';
|
|
|
18
18
|
|
|
19
19
|
function installPlugins({ logger, packageManagerCmd }) {
|
|
20
20
|
return async () => {
|
|
21
|
-
logger.info({
|
|
21
|
+
logger.info({ spin: 'start' }, 'Installing plugins...');
|
|
22
22
|
await spawnProcess({
|
|
23
23
|
processOptions: {
|
|
24
24
|
// https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2#command-injection-via-args-parameter-of-child_processspawn-without-shell-option-enabled-on-windows-cve-2024-27980---high
|
|
@@ -28,7 +28,7 @@ function installPlugins({ logger, packageManagerCmd }) {
|
|
|
28
28
|
args: ['install', '--no-frozen-lockfile'],
|
|
29
29
|
stdOutLineHandler: (line) => logger.debug(line),
|
|
30
30
|
});
|
|
31
|
-
logger.info(
|
|
31
|
+
logger.info('Installed plugins.');
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -14,21 +14,32 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import
|
|
17
|
+
import { shallowBuild } from '@lowdefy/build/dev';
|
|
18
18
|
import createCustomPluginTypesMap from '../utils/createCustomPluginTypesMap.mjs';
|
|
19
19
|
|
|
20
|
+
function formatDuration(ms) {
|
|
21
|
+
if (ms < 1000) return `${ms}ms`;
|
|
22
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
function lowdefyBuild({ directories, logger, options }) {
|
|
21
26
|
return async () => {
|
|
22
|
-
logger.info({
|
|
27
|
+
logger.info({ spin: 'start' }, 'Building config...');
|
|
28
|
+
const startTime = Date.now();
|
|
23
29
|
const customTypesMap = await createCustomPluginTypesMap({ directories, logger });
|
|
24
|
-
|
|
30
|
+
|
|
31
|
+
const result = await shallowBuild({
|
|
25
32
|
customTypesMap,
|
|
26
33
|
directories,
|
|
27
34
|
logger,
|
|
28
35
|
refResolver: options.refResolver,
|
|
29
36
|
stage: 'dev',
|
|
30
37
|
});
|
|
31
|
-
|
|
38
|
+
|
|
39
|
+
// Return result so getContext can store registries
|
|
40
|
+
const duration = Date.now() - startTime;
|
|
41
|
+
logger.info({ spin: 'succeed' }, `Built config in ${formatDuration(duration)}.`);
|
|
42
|
+
return result;
|
|
32
43
|
};
|
|
33
44
|
}
|
|
34
45
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -14,17 +14,36 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { BuildError } from '@lowdefy/errors';
|
|
17
18
|
import { spawnProcess } from '@lowdefy/node-utils';
|
|
18
19
|
|
|
20
|
+
function formatDuration(ms) {
|
|
21
|
+
if (ms < 1000) return `${ms}ms`;
|
|
22
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
function nextBuild({ bin, logger }) {
|
|
20
26
|
return async () => {
|
|
21
|
-
logger.info({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
logger.info({ spin: 'start' }, 'Building app...');
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
const errorLines = [];
|
|
30
|
+
try {
|
|
31
|
+
await spawnProcess({
|
|
32
|
+
command: 'node',
|
|
33
|
+
args: [bin.next, 'build'],
|
|
34
|
+
stdOutLineHandler: (line) => logger.debug(line),
|
|
35
|
+
stdErrLineHandler: (line) => {
|
|
36
|
+
logger.debug(line);
|
|
37
|
+
errorLines.push(line);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
} catch (err) {
|
|
41
|
+
if (errorLines.length > 0) {
|
|
42
|
+
errorLines.forEach((line) => logger.error(line));
|
|
43
|
+
}
|
|
44
|
+
throw new BuildError('Next.js build failed. See above for details.');
|
|
45
|
+
}
|
|
46
|
+
logger.info({ spin: 'succeed' }, `Built app in ${formatDuration(Date.now() - startTime)}.`);
|
|
28
47
|
};
|
|
29
48
|
}
|
|
30
49
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -19,9 +19,9 @@ import startServer from './startServer.mjs';
|
|
|
19
19
|
function restartServer(context) {
|
|
20
20
|
return () => {
|
|
21
21
|
context.shutdownServer();
|
|
22
|
-
context.logger.info({
|
|
22
|
+
context.logger.info({ spin: 'start' }, 'Restarting server...');
|
|
23
23
|
startServer(context);
|
|
24
|
-
context.logger.info({
|
|
24
|
+
context.logger.info({ spin: 'succeed' }, 'Restarted server.');
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -21,7 +21,7 @@ function shutdownServer(context) {
|
|
|
21
21
|
`Existing next server with pid ${context.nextServer.pid}, killed: ${context.nextServer.killed}`
|
|
22
22
|
);
|
|
23
23
|
if (!context.nextServer.killed) {
|
|
24
|
-
context.logger.info({
|
|
24
|
+
context.logger.info({ spin: 'start' }, 'Shutting down server...');
|
|
25
25
|
context.nextServer.kill();
|
|
26
26
|
context.logger.debug(
|
|
27
27
|
`Killed next server with pid ${context.nextServer.pid}, killed: ${context.nextServer.killed}`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -14,26 +14,43 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
|
|
17
|
+
import { spawn } from 'child_process';
|
|
18
|
+
|
|
19
|
+
function createStdErrLineHandler({ context }) {
|
|
20
|
+
const port = context.options.port;
|
|
21
|
+
return function stdErrLineHandler(line) {
|
|
22
|
+
if (line.includes('EADDRINUSE')) {
|
|
23
|
+
context.logger.error(
|
|
24
|
+
`Port ${port} is already in use. Stop the other process or use a different port with --port.`
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
context.logger.error(line);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
19
31
|
|
|
20
32
|
function startServer(context) {
|
|
21
33
|
context.shutdownServer();
|
|
22
34
|
|
|
23
|
-
const nextServer =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
env: {
|
|
30
|
-
...process.env,
|
|
31
|
-
LOWDEFY_DIRECTORY_CONFIG: context.directories.config,
|
|
32
|
-
PORT: context.options.port,
|
|
33
|
-
},
|
|
35
|
+
const nextServer = spawn('node', [context.bin.next, 'start'], {
|
|
36
|
+
stdio: ['ignore', 'inherit', 'pipe'],
|
|
37
|
+
env: {
|
|
38
|
+
...process.env,
|
|
39
|
+
LOWDEFY_DIRECTORY_CONFIG: context.directories.config,
|
|
40
|
+
PORT: context.options.port,
|
|
34
41
|
},
|
|
35
|
-
returnProcess: true,
|
|
36
42
|
});
|
|
43
|
+
|
|
44
|
+
const stdErrLineHandler = createStdErrLineHandler({ context });
|
|
45
|
+
nextServer.stderr.on('data', (data) => {
|
|
46
|
+
data
|
|
47
|
+
.toString('utf8')
|
|
48
|
+
.split('\n')
|
|
49
|
+
.forEach((line) => {
|
|
50
|
+
if (line) stdErrLineHandler(line);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
37
54
|
context.logger.debug(`Started next server with pid ${nextServer.pid}.`);
|
|
38
55
|
nextServer.on('exit', (code, signal) => {
|
|
39
56
|
context.logger.debug(`nextServer exit ${nextServer.pid}, signal: ${signal}, code: ${code}`);
|
package/manager/run.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/*
|
|
3
|
-
Copyright 2020-
|
|
3
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
4
4
|
|
|
5
5
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
6
|
you may not use this file except in compliance with the License.
|
|
@@ -19,6 +19,7 @@ import { wait } from '@lowdefy/helpers';
|
|
|
19
19
|
import opener from 'opener';
|
|
20
20
|
import getContext from './getContext.mjs';
|
|
21
21
|
import startServer from './processes/startServer.mjs';
|
|
22
|
+
import checkPortAvailable from './utils/checkPortAvailable.mjs';
|
|
22
23
|
|
|
23
24
|
/*
|
|
24
25
|
The run script does the following:
|
|
@@ -76,25 +77,16 @@ The run script does the following:
|
|
|
76
77
|
pinging the /api/ping route, until it detects a new server has started, and then reloads the window.
|
|
77
78
|
*/
|
|
78
79
|
|
|
79
|
-
/* TODO:
|
|
80
|
-
Not killing server on errors properly
|
|
81
|
-
when:
|
|
82
|
-
- initial build fails
|
|
83
|
-
*/
|
|
84
|
-
|
|
85
80
|
const context = await getContext();
|
|
86
81
|
|
|
87
82
|
try {
|
|
88
|
-
|
|
89
|
-
await context.initialBuild();
|
|
90
|
-
} catch (error) {
|
|
91
|
-
context.logger.error(error);
|
|
92
|
-
}
|
|
83
|
+
await context.initialBuild();
|
|
93
84
|
|
|
94
85
|
// We are not waiting for the startWatchers promise to resolve (all watchers have fired the ready event)
|
|
95
86
|
// because chokidar sometimes doesn't fire this event, and it seems like there isn't an issue with not waiting.
|
|
96
87
|
context.startWatchers();
|
|
97
88
|
|
|
89
|
+
await checkPortAvailable(context.options.port);
|
|
98
90
|
startServer(context);
|
|
99
91
|
await wait(800);
|
|
100
92
|
if (process.env.LOWDEFY_SERVER_DEV_OPEN_BROWSER === 'true') {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { BuildError } from '@lowdefy/errors';
|
|
18
|
+
import net from 'net';
|
|
19
|
+
|
|
20
|
+
function checkPortAvailable(port) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const server = net.createServer();
|
|
23
|
+
server.once('error', (err) => {
|
|
24
|
+
if (err.code === 'EADDRINUSE') {
|
|
25
|
+
reject(
|
|
26
|
+
new BuildError(
|
|
27
|
+
`Port ${port} is already in use. Stop the other process or use a different port with --port.`
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
reject(err);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
server.once('listening', () => {
|
|
35
|
+
server.close(() => resolve());
|
|
36
|
+
});
|
|
37
|
+
server.listen(port);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default checkPortAvailable;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import { type } from '@lowdefy/helpers';
|
|
19
19
|
import { readFile } from '@lowdefy/node-utils';
|
|
20
|
+
import { ConfigError } from '@lowdefy/errors';
|
|
20
21
|
import YAML from 'yaml';
|
|
21
22
|
|
|
22
23
|
async function getLowdefyVersion(context) {
|
|
23
|
-
|
|
24
|
+
const filePath = 'lowdefy.yaml';
|
|
25
|
+
let lowdefyYaml = await readFile(path.join(context.directories.config, filePath));
|
|
24
26
|
if (!lowdefyYaml) {
|
|
25
27
|
lowdefyYaml = await readFile(path.join(context.directories.config, 'lowdefy.yml'));
|
|
26
28
|
}
|
|
@@ -31,7 +33,7 @@ async function getLowdefyVersion(context) {
|
|
|
31
33
|
try {
|
|
32
34
|
lowdefy = YAML.parse(lowdefyYaml);
|
|
33
35
|
} catch (error) {
|
|
34
|
-
throw new
|
|
36
|
+
throw new ConfigError('Could not parse YAML.', { cause: error, filePath });
|
|
35
37
|
}
|
|
36
38
|
if (!lowdefy.lowdefy) {
|
|
37
39
|
throw new Error(
|
|
@@ -39,10 +41,9 @@ async function getLowdefyVersion(context) {
|
|
|
39
41
|
);
|
|
40
42
|
}
|
|
41
43
|
if (!type.isString(lowdefy.lowdefy)) {
|
|
42
|
-
throw new
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
)}.`
|
|
44
|
+
throw new ConfigError(
|
|
45
|
+
'Version number specified in "lowdefy.yaml" file should be a string.',
|
|
46
|
+
{ received: lowdefy.lowdefy, filePath }
|
|
46
47
|
);
|
|
47
48
|
}
|
|
48
49
|
return lowdefy.lowdefy;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import fs from 'fs';
|
|
17
18
|
import path from 'path';
|
|
18
19
|
import getLowdefyVersion from '../utils/getLowdefyVersion.mjs';
|
|
19
20
|
import setupWatcher from '../utils/setupWatcher.mjs';
|
|
@@ -23,9 +24,13 @@ function lowdefyBuildWatcher(context) {
|
|
|
23
24
|
path.isAbsolute(item) ? item : path.resolve(context.directories.config, item);
|
|
24
25
|
|
|
25
26
|
const callback = async (filePaths) => {
|
|
26
|
-
const
|
|
27
|
+
const changedFiles = filePaths
|
|
27
28
|
.flat()
|
|
28
|
-
.
|
|
29
|
+
.map((filePath) => path.relative(context.directories.config, filePath));
|
|
30
|
+
|
|
31
|
+
const lowdefyYamlModified = changedFiles.some(
|
|
32
|
+
(filePath) => filePath === 'lowdefy.yaml' || filePath === 'lowdefy.yml'
|
|
33
|
+
);
|
|
29
34
|
if (lowdefyYamlModified) {
|
|
30
35
|
const lowdefyVersion = await getLowdefyVersion(context);
|
|
31
36
|
if (lowdefyVersion !== context.version && lowdefyVersion !== 'local') {
|
|
@@ -35,8 +40,24 @@ function lowdefyBuildWatcher(context) {
|
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
try {
|
|
44
|
+
const isSkeletonChange =
|
|
45
|
+
lowdefyYamlModified ||
|
|
46
|
+
changedFiles.some((f) => !f.startsWith('pages/') && !f.startsWith('./'));
|
|
47
|
+
|
|
48
|
+
if (isSkeletonChange) {
|
|
49
|
+
await context.lowdefyBuild();
|
|
50
|
+
} else {
|
|
51
|
+
// Page-only changes: write signal file so the server invalidates its page cache
|
|
52
|
+
const invalidatePath = path.join(context.directories.build, 'invalidatePages');
|
|
53
|
+
fs.writeFileSync(invalidatePath, String(Date.now()));
|
|
54
|
+
context.logger.info('Page files changed, invalidated all pages.');
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
context.logger.error(error);
|
|
58
|
+
} finally {
|
|
59
|
+
await context.reloadClients();
|
|
60
|
+
}
|
|
40
61
|
};
|
|
41
62
|
return setupWatcher({
|
|
42
63
|
callback,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -86,7 +86,7 @@ async function nextBuildWatcher(context) {
|
|
|
86
86
|
})
|
|
87
87
|
);
|
|
88
88
|
if (!build) {
|
|
89
|
-
context.logger.info({
|
|
89
|
+
context.logger.info({ spin: 'succeed' }, 'Reloaded app.');
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -94,6 +94,16 @@ async function nextBuildWatcher(context) {
|
|
|
94
94
|
if (install) {
|
|
95
95
|
context.logger.warn('Plugin dependencies have changed and will be reinstalled.');
|
|
96
96
|
await context.installPlugins();
|
|
97
|
+
// Rebuild Lowdefy artifacts (blocks.js, icons.js, styles.less, etc.)
|
|
98
|
+
// so newly installed packages are included in the Next.js bundle.
|
|
99
|
+
await context.lowdefyBuild();
|
|
100
|
+
// Re-hash all tracked files to avoid detecting our own build output
|
|
101
|
+
// changes as new changes on the next watcher callback.
|
|
102
|
+
await Promise.all(
|
|
103
|
+
trackedFiles.map(async (filePath) => {
|
|
104
|
+
hashes[filePath] = await sha1(filePath);
|
|
105
|
+
})
|
|
106
|
+
);
|
|
97
107
|
}
|
|
98
108
|
await context.nextBuild();
|
|
99
109
|
context.restartServer();
|
package/next.config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const withLess = require('next-with-less');
|
|
2
2
|
const lowdefyConfig = require('./build/config.json');
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const nextConfig = withLess({
|
|
5
5
|
basePath: lowdefyConfig.basePath,
|
|
6
6
|
// reactStrictMode: true,
|
|
7
7
|
webpack: (config, { isServer }) => {
|
|
@@ -19,7 +19,6 @@ module.exports = withLess({
|
|
|
19
19
|
}
|
|
20
20
|
return config;
|
|
21
21
|
},
|
|
22
|
-
swcMinify: false,
|
|
23
22
|
compress: false,
|
|
24
23
|
outputFileTracing: false,
|
|
25
24
|
poweredByHeader: false,
|
|
@@ -29,3 +28,5 @@ module.exports = withLess({
|
|
|
29
28
|
ignoreDuringBuilds: true,
|
|
30
29
|
},
|
|
31
30
|
});
|
|
31
|
+
|
|
32
|
+
module.exports = nextConfig;
|