lowdefy 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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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,12 +18,16 @@ import installServer from '../../utils/installServer.js';
18
18
  import resetServerPackageJson from '../../utils/resetServerPackageJson.js';
19
19
  import runLowdefyBuild from '../../utils/runLowdefyBuild.js';
20
20
  import runNextBuild from '../../utils/runNextBuild.js';
21
+ const serverPackages = {
22
+ e2e: '@lowdefy/server-e2e'
23
+ };
21
24
  async function build({ context }) {
22
- context.print.info('Starting build.');
25
+ context.logger.info('Starting build.');
23
26
  const directory = context.directories.server;
27
+ const packageName = serverPackages[context.options.server] ?? '@lowdefy/server';
24
28
  await getServer({
25
29
  context,
26
- packageName: '@lowdefy/server',
30
+ packageName,
27
31
  directory
28
32
  });
29
33
  await resetServerPackageJson({
@@ -55,6 +59,8 @@ async function build({ context }) {
55
59
  await context.sendTelemetry({
56
60
  sendTypes: true
57
61
  });
58
- context.print.succeed(`Build successful.`);
62
+ context.logger.info({
63
+ spin: 'succeed'
64
+ }, 'Build successful.');
59
65
  }
60
66
  export default build;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,13 +13,17 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import addCustomPluginsAsDeps from '../../utils/addCustomPluginsAsDeps.js';
16
+ import checkPortAvailable from '../../utils/checkPortAvailable.js';
16
17
  import installServer from '../../utils/installServer.js';
17
18
  import resetServerPackageJson from '../../utils/resetServerPackageJson.js';
18
19
  import runDevServer from './runDevServer.js';
19
20
  import getServer from '../../utils/getServer.js';
20
21
  async function dev({ context }) {
21
22
  const directory = context.directories.dev;
22
- context.print.info('Starting development server.');
23
+ context.logger.info('Starting development server.');
24
+ await checkPortAvailable({
25
+ port: context.options.port
26
+ });
23
27
  await getServer({
24
28
  context,
25
29
  packageName: '@lowdefy/server-dev',
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,7 +13,7 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { spawnProcess } from '@lowdefy/node-utils';
16
- import createStdOutLineHandler from '../../utils/createStdOutLineHandler.js';
16
+ import { createStdOutLineHandler } from '@lowdefy/logger/cli';
17
17
  async function runDevServer({ context, directory }) {
18
18
  await spawnProcess({
19
19
  args: [
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -22,15 +22,17 @@ async function init({ context }) {
22
22
  if (fileExists) {
23
23
  throw new Error('Cannot initialize a Lowdefy project, a "lowdefy.yaml" file already exists');
24
24
  }
25
- context.print.log('Initializing Lowdefy project.');
25
+ context.logger.info('Initializing Lowdefy project.');
26
26
  await writeFile(lowdefyFilePath, lowdefyFile({
27
27
  version: context.cliVersion
28
28
  }));
29
- context.print.log("Created 'lowdefy.yaml'.");
29
+ context.logger.info("Created 'lowdefy.yaml'.");
30
30
  await writeFile(path.resolve('./.gitignore'), `.lowdefy/**
31
31
  .env`);
32
- context.print.log("Created '.gitignore'.");
32
+ context.logger.info("Created '.gitignore'.");
33
33
  await context.sendTelemetry();
34
- context.print.succeed('Project initialized.');
34
+ context.logger.info({
35
+ spin: 'succeed'
36
+ }, 'Project initialized.');
35
37
  }
36
38
  export default init;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -16,14 +16,16 @@
16
16
  import url from 'url';
17
17
  import { readFile, writeFile } from '@lowdefy/node-utils';
18
18
  async function initDocker({ context }) {
19
- context.print.log('Initializing Docker deployment.');
19
+ context.logger.info('Initializing Docker deployment.');
20
20
  const dockerfile = await readFile(url.fileURLToPath(new URL('./Dockerfile', import.meta.url)));
21
21
  await writeFile(path.join(context.directories.config, 'Dockerfile'), dockerfile);
22
- context.print.log("Created 'Dockerfile'.");
22
+ context.logger.info("Created 'Dockerfile'.");
23
23
  const dockerignore = await readFile(url.fileURLToPath(new URL('./.dockerignore', import.meta.url)));
24
24
  await writeFile(path.join(context.directories.config, '.dockerignore'), dockerignore);
25
- context.print.log("Created '.dockerignore'.");
25
+ context.logger.info("Created '.dockerignore'.");
26
26
  await context.sendTelemetry();
27
- context.print.succeed('Docker deployment initialized.');
27
+ context.logger.info({
28
+ spin: 'succeed'
29
+ }, 'Docker deployment initialized.');
28
30
  }
29
31
  export default initDocker;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -16,14 +16,16 @@
16
16
  import url from 'url';
17
17
  import { readFile, writeFile } from '@lowdefy/node-utils';
18
18
  async function initVercel({ context }) {
19
- context.print.log('Initializing Vercel deployment.');
19
+ context.logger.info('Initializing Vercel deployment.');
20
20
  const installScript = await readFile(url.fileURLToPath(new URL('./vercel.install.sh', import.meta.url)));
21
21
  await writeFile(path.join(context.directories.config, 'deploy', 'vercel.install.sh'), installScript);
22
- context.print.log("Created 'vercel.install.sh'.");
22
+ context.logger.info("Created 'vercel.install.sh'.");
23
23
  const readMe = await readFile(url.fileURLToPath(new URL('./README.md', import.meta.url)));
24
24
  await writeFile(path.join(context.directories.config, 'deploy', 'README.md'), readMe);
25
- context.print.log("Created 'README.md'.");
25
+ context.logger.info("Created 'README.md'.");
26
26
  await context.sendTelemetry();
27
- context.print.succeed('Vercel deployment initialized.');
27
+ context.logger.info({
28
+ spin: 'succeed'
29
+ }, 'Vercel deployment initialized.');
28
30
  }
29
31
  export default initVercel;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,7 +13,7 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { spawnProcess } from '@lowdefy/node-utils';
16
- import createStdOutLineHandler from '../../utils/createStdOutLineHandler.js';
16
+ import { createStdOutLineHandler } from '@lowdefy/logger/cli';
17
17
  async function runStart({ context, directory }) {
18
18
  await spawnProcess({
19
19
  args: [
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -12,9 +12,13 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import runStart from './runStart.js';
15
+ */ import checkPortAvailable from '../../utils/checkPortAvailable.js';
16
+ import runStart from './runStart.js';
16
17
  // TODO: Handle "spawn yarn ENOENT" error if no built server exists.
17
18
  async function build({ context }) {
19
+ await checkPortAvailable({
20
+ port: context.options.port
21
+ });
18
22
  context.sendTelemetry({
19
23
  sendTypes: true
20
24
  });
@@ -22,7 +26,9 @@ async function build({ context }) {
22
26
  context,
23
27
  directory: context.directories.server
24
28
  });
25
- context.print.succeed('Started server.');
29
+ context.logger.info({
30
+ spin: 'succeed'
31
+ }, 'Started server.');
26
32
  await serverProcess;
27
33
  }
28
34
  export default build;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /*
3
- Copyright 2020-2024 Lowdefy, Inc
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.
package/dist/program.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -42,7 +42,9 @@ const options = {
42
42
  watch: new Option('--watch <paths...>', 'A list of paths to files or directories that should be watched for changes. Globs are supported. Specify each path to watch separated by spaces.'),
43
43
  watchIgnore: new Option('--watch-ignore <paths...>', 'A list of paths to files or directories that should be ignored by the file watcher. Globs are supported. Specify each path to watch separated by spaces.')
44
44
  };
45
- program.command('build').description('Build a Lowdefy production app.').usage('[options]').addOption(options.configDirectory).addOption(options.disableTelemetry).addOption(options.logLevel).option('--no-next-build', 'Do not build the Next.js server.').addOption(options.refResolver).addOption(options.serverDirectory).action(runCommand({
45
+ program.command('build').description('Build a Lowdefy production app.').usage('[options]').addOption(options.configDirectory).addOption(options.disableTelemetry).addOption(options.logLevel).option('--no-next-build', 'Do not build the Next.js server.').addOption(options.refResolver).addOption(new Option('--server <server>', 'Server package variant. Use "e2e" for @lowdefy/server-e2e.').choices([
46
+ 'e2e'
47
+ ])).addOption(options.serverDirectory).action(runCommand({
46
48
  cliVersion,
47
49
  handler: build
48
50
  }));
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,13 +13,16 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { execSync } from 'child_process';
16
- function checkPnpmIsInstalled({ print, pnpmCmd }) {
16
+ function checkPnpmIsInstalled({ logger, pnpmCmd }) {
17
+ const ui = logger?.ui ?? logger ?? {
18
+ error: (message)=>console.error(message)
19
+ };
17
20
  try {
18
21
  execSync(`${pnpmCmd} --version`, {
19
22
  stdio: 'ignore'
20
23
  });
21
24
  } catch (e) {
22
- print.error(`
25
+ ui.error(`
23
26
  -------------------------------------------------------------
24
27
  The package manager "pnpm" is required to run Lowdefy.
25
28
  Install pnpm as describe here:
@@ -0,0 +1,37 @@
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
+ */ import net from 'net';
16
+ function checkPortAvailable({ port }) {
17
+ return new Promise((resolve, reject)=>{
18
+ const server = net.createServer();
19
+ server.on('error', (error)=>{
20
+ if (error.code === 'EADDRINUSE') {
21
+ reject(new Error(`[Server Error] Port ${port} is already in use.
22
+
23
+ To use a different port, either:
24
+ - Run with the --port flag: pnpx lowdefy dev --port 3001
25
+ - Set the port in your lowdefy.yaml:
26
+ cli:
27
+ port: 3001`));
28
+ } else {
29
+ reject(error);
30
+ }
31
+ });
32
+ server.listen(port, ()=>{
33
+ server.close(()=>resolve());
34
+ });
35
+ });
36
+ }
37
+ export default checkPortAvailable;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,7 +13,7 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import axios from 'axios';
16
- import createPrint from './createPrint.js';
16
+ import createCliLogger from '@lowdefy/logger/cli';
17
17
  async function logError({ error, context = {} }) {
18
18
  try {
19
19
  await axios.request({
@@ -37,11 +37,11 @@ async function logError({ error, context = {} }) {
37
37
  }
38
38
  }
39
39
  async function errorHandler({ context, error }) {
40
- const print = createPrint({
40
+ const logger = context?.logger ?? createCliLogger({
41
41
  logLevel: 'info'
42
42
  });
43
- print.error(error.message);
44
- if (!context.disableTelemetry) {
43
+ logger.error(error);
44
+ if (!context?.disableTelemetry) {
45
45
  await logError({
46
46
  context,
47
47
  error
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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,10 +14,12 @@
14
14
  limitations under the License.
15
15
  */ import path from 'path';
16
16
  import { get, type } from '@lowdefy/helpers';
17
+ import { ConfigError } from '@lowdefy/errors';
17
18
  import { readFile } from '@lowdefy/node-utils';
18
19
  import YAML from 'yaml';
19
20
  async function getLowdefyYaml({ configDirectory, requiresLowdefyYaml }) {
20
- let lowdefyYaml = await readFile(path.join(configDirectory, 'lowdefy.yaml'));
21
+ const filePath = 'lowdefy.yaml';
22
+ let lowdefyYaml = await readFile(path.join(configDirectory, filePath));
21
23
  if (!lowdefyYaml) {
22
24
  lowdefyYaml = await readFile(path.join(configDirectory, 'lowdefy.yml'));
23
25
  }
@@ -33,13 +35,19 @@ async function getLowdefyYaml({ configDirectory, requiresLowdefyYaml }) {
33
35
  try {
34
36
  lowdefy = YAML.parse(lowdefyYaml);
35
37
  } catch (error) {
36
- throw new Error(`Could not parse "lowdefy.yaml" file. Received error ${error.message}.`);
38
+ throw new ConfigError('Could not parse YAML.', {
39
+ cause: error,
40
+ filePath
41
+ });
37
42
  }
38
43
  if (!lowdefy.lowdefy) {
39
44
  throw new Error('No version specified in "lowdefy.yaml" file. Specify a version in the "lowdefy" field.');
40
45
  }
41
46
  if (!type.isString(lowdefy.lowdefy)) {
42
- throw new Error(`Version number specified in "lowdefy.yaml" file should be a string. Received ${JSON.stringify(lowdefy.lowdefy)}.`);
47
+ throw new ConfigError('Version number specified in "lowdefy.yaml" file should be a string.', {
48
+ received: lowdefy.lowdefy,
49
+ filePath
50
+ });
43
51
  }
44
52
  // TODO: Validate plugins
45
53
  return {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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 { cleanDirectory, readFile } from '@lowdefy/node-utils';
18
18
  import fetchNpmTarball from './fetchNpmTarball.js';
19
19
  async function getServer({ context, packageName, directory }) {
20
20
  if (context.lowdefyVersion === 'local') {
21
- context.print.warn(`Running local ${packageName}.`);
21
+ context.logger.warn(`Running local ${packageName}.`);
22
22
  return;
23
23
  }
24
24
  let fetchServer = false;
@@ -28,18 +28,20 @@ async function getServer({ context, packageName, directory }) {
28
28
  const serverPackageConfig = JSON.parse(await readFile(path.join(directory, 'package.json')));
29
29
  if (serverPackageConfig.version !== context.lowdefyVersion) {
30
30
  fetchServer = true;
31
- context.print.warn(`Removing ${packageName} with version ${serverPackageConfig.version}`);
31
+ context.logger.warn(`Removing ${packageName} with version ${serverPackageConfig.version}`);
32
32
  await cleanDirectory(directory);
33
33
  }
34
34
  }
35
35
  if (fetchServer) {
36
- context.print.spin(`Fetching ${packageName} from npm.`);
36
+ context.logger.info({
37
+ spin: 'start'
38
+ }, `Fetching ${packageName} from npm.`);
37
39
  await fetchNpmTarball({
38
40
  packageName,
39
41
  version: context.lowdefyVersion,
40
42
  directory
41
43
  });
42
- context.print.log(`Fetched ${packageName} from npm.`);
44
+ context.logger.info(`Fetched ${packageName} from npm.`);
43
45
  }
44
46
  }
45
47
  export default getServer;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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,7 +14,14 @@
14
14
  limitations under the License.
15
15
  */ import { spawnProcess } from '@lowdefy/node-utils';
16
16
  async function installServer({ context, directory }) {
17
- context.print.spin('Installing dependencies.');
17
+ // Skip dependency installation for local development (monorepo already has deps installed)
18
+ if (context.lowdefyVersion === 'local') {
19
+ context.logger.info('Skipping dependency installation for local development.');
20
+ return;
21
+ }
22
+ context.logger.info({
23
+ spin: 'start'
24
+ }, 'Installing dependencies.');
18
25
  try {
19
26
  await spawnProcess({
20
27
  command: context.pnpmCmd,
@@ -22,7 +29,7 @@ async function installServer({ context, directory }) {
22
29
  'install',
23
30
  '--no-frozen-lockfile'
24
31
  ],
25
- stdOutLineHandler: (line)=>context.print.debug(line),
32
+ stdOutLineHandler: (line)=>context.logger.debug(line),
26
33
  processOptions: {
27
34
  cwd: directory,
28
35
  // 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
@@ -30,9 +37,9 @@ async function installServer({ context, directory }) {
30
37
  }
31
38
  });
32
39
  } catch (error) {
33
- console.log(error);
40
+ console.error(error);
34
41
  throw new Error('Dependency installation failed.');
35
42
  }
36
- context.print.log('Dependencies install successfully.');
43
+ context.logger.info('Dependencies install successfully.');
37
44
  }
38
45
  export default installServer;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -13,9 +13,11 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { spawnProcess } from '@lowdefy/node-utils';
16
- import createStdOutLineHandler from './createStdOutLineHandler.js';
16
+ import { createStdOutLineHandler } from '@lowdefy/logger/cli';
17
17
  async function runLowdefyBuild({ context, directory }) {
18
- context.print.spin('Running Lowdefy build.');
18
+ context.logger.info({
19
+ spin: 'start'
20
+ }, 'Running Lowdefy build.');
19
21
  try {
20
22
  await spawnProcess({
21
23
  args: [
@@ -41,6 +43,6 @@ async function runLowdefyBuild({ context, directory }) {
41
43
  } catch (error) {
42
44
  throw new Error('Lowdefy build failed.');
43
45
  }
44
- context.print.log('Lowdefy build successful.');
46
+ context.logger.info('Lowdefy build successful.');
45
47
  }
46
48
  export default runLowdefyBuild;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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,14 +18,16 @@ function createStdOutLineHandler({ context }) {
18
18
  // Matches next build output of form: ┌ λ / 261 B 403 kB
19
19
  const match = line.match(/┌ λ \/\s*\d* [a-zA-Z]*\s*(\d* [a-zA-Z]*)/u);
20
20
  if (match) {
21
- context.print.info(`Home page first load JS size: ${match[1]}.`);
21
+ context.logger.info(`Home page first load JS size: ${match[1]}.`);
22
22
  }
23
- context.print.debug(line);
23
+ context.logger.debug(line);
24
24
  }
25
25
  return stdOutLineHandler;
26
26
  }
27
27
  async function runNextBuild({ context, directory }) {
28
- context.print.spin('Running Next build.');
28
+ context.logger.info({
29
+ spin: 'start'
30
+ }, 'Running Next build.');
29
31
  try {
30
32
  await spawnProcess({
31
33
  command: context.pnpmCmd,
@@ -39,16 +41,16 @@ async function runNextBuild({ context, directory }) {
39
41
  processOptions: {
40
42
  // 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
41
43
  shell: process.platform === 'win32',
42
- cwd: directory
43
- },
44
- env: {
45
- ...process.env,
46
- NEXT_TELEMETRY_DISABLED: context.options.disableTelemetry ? '1' : undefined
44
+ cwd: directory,
45
+ env: {
46
+ ...process.env,
47
+ NEXT_TELEMETRY_DISABLED: context.options.disableTelemetry ? '1' : undefined
48
+ }
47
49
  }
48
50
  });
49
51
  } catch (error) {
50
52
  throw new Error('Next build failed.');
51
53
  }
52
- context.print.log('Next build successful.');
54
+ context.logger.info('Next build successful.');
53
55
  }
54
56
  export default runNextBuild;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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.
@@ -15,7 +15,7 @@
15
15
  */ import path from 'path';
16
16
  import { type } from '@lowdefy/helpers';
17
17
  import checkPnpmIsInstalled from './checkPnpmIsInstalled.js';
18
- import createPrint from './createPrint.js';
18
+ import { createCliLogger } from '@lowdefy/logger/cli';
19
19
  import getCliJson from './getCliJson.js';
20
20
  import getDirectories from './getDirectories.js';
21
21
  import getLowdefyYaml from './getLowdefyYaml.js';
@@ -38,18 +38,21 @@ async function startUp({ context, options = {}, command }) {
38
38
  const { appId } = await getCliJson(context);
39
39
  context.appId = appId;
40
40
  context.options = getOptions(context);
41
- context.print = createPrint({
41
+ context.logger = createCliLogger({
42
42
  logLevel: context.options.logLevel
43
43
  });
44
44
  context.directories = getDirectories(context);
45
45
  context.pnpmCmd = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
46
- checkPnpmIsInstalled(context);
46
+ checkPnpmIsInstalled({
47
+ logger: context.logger,
48
+ pnpmCmd: context.pnpmCmd
49
+ });
47
50
  await validateVersion(context);
48
51
  context.sendTelemetry = getSendTelemetry(context);
49
52
  if (type.isNone(lowdefyVersion)) {
50
- context.print.log(`Running 'lowdefy ${context.command}'.`);
53
+ context.logger.info(`Running 'lowdefy ${context.command}'.`);
51
54
  } else {
52
- context.print.log(`Running 'lowdefy ${context.command}'. Lowdefy app version ${lowdefyVersion}.`);
55
+ context.logger.info(`Running 'lowdefy ${context.command}'. Lowdefy app version ${lowdefyVersion}.`);
53
56
  }
54
57
  return context;
55
58
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
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,7 +14,10 @@
14
14
  limitations under the License.
15
15
  */ import axios from 'axios';
16
16
  import semver from 'semver';
17
- async function validateVersion({ cliVersion, lowdefyVersion, print, requiresLowdefyYaml }) {
17
+ async function validateVersion({ cliVersion, lowdefyVersion, logger, requiresLowdefyYaml }) {
18
+ const ui = logger?.ui ?? logger ?? {
19
+ warn: (message)=>console.warn(message)
20
+ };
18
21
  if (!requiresLowdefyYaml) {
19
22
  return;
20
23
  }
@@ -39,7 +42,7 @@ async function validateVersion({ cliVersion, lowdefyVersion, print, requiresLowd
39
42
  ---------------------------------------------------`);
40
43
  }
41
44
  if (isExperimentalVersion(cliVersion) || isExperimentalVersion(lowdefyVersion)) {
42
- print.warn(`
45
+ ui.warn(`
43
46
  ---------------------------------------------------
44
47
  You are using an experimental version of Lowdefy.
45
48
  Features may change at any time.
@@ -51,7 +54,7 @@ async function validateVersion({ cliVersion, lowdefyVersion, print, requiresLowd
51
54
  const packageInfo = await axios.get(registryUrl);
52
55
  const latestVersion = packageInfo.data['dist-tags'].latest;
53
56
  if (cliVersion !== latestVersion) {
54
- print.warn(`
57
+ ui.warn(`
55
58
  -------------------------------------------------------------
56
59
  You are not using the latest version of the Lowdefy CLI.
57
60
  Please update to version ${latestVersion}.
@@ -59,7 +62,7 @@ async function validateVersion({ cliVersion, lowdefyVersion, print, requiresLowd
59
62
  -------------------------------------------------------------`);
60
63
  }
61
64
  if (lowdefyVersion && lowdefyVersion !== latestVersion) {
62
- print.warn(`
65
+ ui.warn(`
63
66
  -------------------------------------------------------------
64
67
  Your app is not using the latest Lowdefy version, ${lowdefyVersion}.
65
68
  Please update your app to version ${latestVersion}.
@@ -68,7 +71,7 @@ async function validateVersion({ cliVersion, lowdefyVersion, print, requiresLowd
68
71
  -------------------------------------------------------------`);
69
72
  }
70
73
  } catch (error) {
71
- print.warn('Failed to check for latest Lowdefy version.');
74
+ ui.warn('Failed to check for latest Lowdefy version.');
72
75
  }
73
76
  }
74
77
  function isExperimentalVersion(version) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lowdefy",
3
- "version": "4.5.1",
3
+ "version": "4.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy CLI",
6
6
  "homepage": "https://lowdefy.com",
@@ -32,14 +32,15 @@
32
32
  ],
33
33
  "exports": "./dist/index.js",
34
34
  "dependencies": {
35
- "@lowdefy/helpers": "4.5.1",
36
- "@lowdefy/node-utils": "4.5.1",
35
+ "@lowdefy/errors": "4.6.0",
36
+ "@lowdefy/helpers": "4.6.0",
37
+ "@lowdefy/logger": "4.6.0",
38
+ "@lowdefy/node-utils": "4.6.0",
37
39
  "axios": "1.8.2",
38
40
  "commander": "11.1.0",
39
41
  "decompress": "4.2.1",
40
42
  "decompress-targz": "4.1.1",
41
43
  "dotenv": "16.3.1",
42
- "ora": "7.0.1",
43
44
  "semver": "7.5.4",
44
45
  "uuid": "9.0.1",
45
46
  "yaml": "2.3.4"
@@ -61,6 +62,6 @@
61
62
  "build": "swc src --out-dir dist --config-file ../../.swcrc --delete-dir-on-start --copy-files",
62
63
  "clean": "rm -rf dist && rm -rf .lowdefy",
63
64
  "start": "node ./dist/index.js",
64
- "test": "FORCE_COLOR=3 node --experimental-vm-modules node_modules/jest/bin/jest.js"
65
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
65
66
  }
66
67
  }
@@ -1,105 +0,0 @@
1
- /*
2
- Copyright 2020-2024 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
- */ import ora from 'ora';
16
- const reset = '\x1b[0m';
17
- const red = (text)=>`\x1b[31m${text}${reset}`;
18
- const green = (text)=>`\x1b[32m${text}${reset}`;
19
- const yellow = (text)=>`\x1b[33m${text}${reset}`;
20
- const blue = (text)=>`\x1b[34m${text}${reset}`;
21
- const dim = (text)=>`\x1b[2m${text}${reset}`;
22
- function getTime() {
23
- const time = new Date(Date.now());
24
- const h = time.getHours();
25
- const m = time.getMinutes();
26
- const s = time.getSeconds();
27
- return `${h > 9 ? '' : '0'}${h}:${m > 9 ? '' : '0'}${m}:${s > 9 ? '' : '0'}${s}`;
28
- }
29
- // Same levels as pino with added custom levels
30
- const logLevelValues = {
31
- error: 50,
32
- warn: 40,
33
- succeed: 33,
34
- spin: 32,
35
- log: 31,
36
- info: 30,
37
- debug: 20
38
- };
39
- function filterLevels(logger, level) {
40
- const levelValue = logLevelValues[level];
41
- Object.keys(logger).forEach((key)=>{
42
- if (logLevelValues[key] < levelValue) {
43
- logger[key] = ()=>{};
44
- }
45
- });
46
- return logger;
47
- }
48
- function createOraPrint({ logLevel }) {
49
- const spinner = ora({
50
- spinner: 'random',
51
- prefixText: ()=>dim(getTime()),
52
- color: 'blue'
53
- });
54
- return filterLevels({
55
- error: (text)=>spinner.fail(red(text)),
56
- info: (text)=>spinner.info(blue(text)),
57
- log: (text)=>spinner.stopAndPersist({
58
- symbol: '∙',
59
- text
60
- }),
61
- spin: (text)=>spinner.start(text),
62
- succeed: (text)=>spinner.succeed(green(text)),
63
- warn: (text)=>spinner.warn(yellow(text)),
64
- debug: (text)=>{
65
- if (spinner.isSpinning) {
66
- spinner.stopAndPersist({
67
- symbol: '∙'
68
- });
69
- }
70
- spinner.stopAndPersist({
71
- symbol: dim('+'),
72
- text: dim(text)
73
- });
74
- }
75
- }, logLevel);
76
- }
77
- function createBasicPrint({ logLevel = 'info' }) {
78
- const { error, info, log, warn, debug } = console;
79
- return filterLevels({
80
- error,
81
- info,
82
- log,
83
- spin: log,
84
- succeed: log,
85
- warn,
86
- debug
87
- }, logLevel);
88
- }
89
- // Memoise print so that error handler can get the same spinner object
90
- let print;
91
- function createPrint({ logLevel }) {
92
- if (print) return print;
93
- if (process.env.CI === 'true' || process.env.CI === '1') {
94
- print = createBasicPrint({
95
- logLevel
96
- });
97
- return print;
98
- }
99
- print = createOraPrint({
100
- logLevel
101
- });
102
- return print;
103
- }
104
- export { createOraPrint, createBasicPrint };
105
- export default createPrint;
@@ -1,26 +0,0 @@
1
- /*
2
- Copyright 2020-2024 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
- */ function createStdOutLineHandler({ context }) {
16
- function stdOutLineHandler(line) {
17
- try {
18
- const { print, msg } = JSON.parse(line);
19
- context.print[print](msg);
20
- } catch (error) {
21
- context.print.log(line);
22
- }
23
- }
24
- return stdOutLineHandler;
25
- }
26
- export default createStdOutLineHandler;