@flare-desktop/cli 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flare-desktop/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Command line interface for the flare desktop toolkit",
5
5
  "type": "module",
6
6
  "bin": {
Binary file
@@ -1,6 +1,6 @@
1
1
  import { setTimeout as delay } from "node:timers/promises";
2
2
 
3
- import { compileApplication, runHook } from "../lib/build.js";
3
+ import { compileApplication, ensureFrontendDirectory, runHook } from "../lib/build.js";
4
4
  import { FlareError } from "../lib/errors.js";
5
5
  import { loadProject, productName } from "../lib/project.js";
6
6
  import { run, startShell, terminate } from "../lib/process.js";
@@ -48,6 +48,12 @@ export async function devCommand(context) {
48
48
  process.once("SIGTERM", sigtermHandler);
49
49
 
50
50
  try {
51
+ await ensureFrontendDirectory({
52
+ projectDirectory: context.projectDirectory,
53
+ config,
54
+ logger: context.logger
55
+ });
56
+
51
57
  if (config.build.beforeDevCommand) {
52
58
  context.logger.info(`before dev command: ${config.build.beforeDevCommand}`);
53
59
  server = startShell(config.build.beforeDevCommand, { cwd: context.projectDirectory });
@@ -61,8 +67,8 @@ export async function devCommand(context) {
61
67
  const { binary } = await compileApplication({
62
68
  projectDirectory: context.projectDirectory,
63
69
  config,
64
- configFile: context.flags.config || "flare.config.json",
65
- environment: context.flags.environment,
70
+ configFile: context.flags.config || "flare.config.json",
71
+ environment: context.flags.environment,
66
72
  mode: "Debug",
67
73
  logger: context.logger
68
74
  });
@@ -6,6 +6,7 @@ import { placeholderIcon } from "../lib/icons.js";
6
6
  import { isValidAppName, toKebabCase } from "../lib/project.js";
7
7
  import { installSdk } from "../lib/sdk.js";
8
8
  import { writePng } from "../lib/png.js";
9
+ import { runShell } from "../lib/process.js";
9
10
  import { CORE_VERSION, MIN_CLI_VERSION } from "../release-versions.js";
10
11
  import {
11
12
  cmakeTemplate,
@@ -36,6 +37,7 @@ export async function initProject({
36
37
  template = "javascript",
37
38
  sdkMode = "source",
38
39
  sdkFetch,
40
+ installDependencies = false,
39
41
  force = false,
40
42
  logger
41
43
  }) {
@@ -121,12 +123,25 @@ export async function initProject({
121
123
 
122
124
  const written = [...files.map((entry) => entry.file), "icons/icon.png", "icons/32x32.png"];
123
125
 
126
+ if (installDependencies) {
127
+ if (logger) logger.info("installing project dependencies with npm");
128
+ let result;
129
+ try {
130
+ result = await runShell("npm install --no-audit --no-fund", { cwd: targetDirectory });
131
+ } catch (error) {
132
+ throw new FlareError(`npm install could not start in '${targetDirectory}': ${error.message}; run npm install manually`);
133
+ }
134
+ if (result.code !== 0) {
135
+ throw new FlareError(`npm install failed in '${targetDirectory}' (code ${result.code}); the project was kept, run npm install manually`);
136
+ }
137
+ }
138
+
124
139
  if (logger) {
125
140
  logger.info(`created project '${projectName}' in '${targetDirectory}'`);
126
141
  logger.info("");
127
142
  logger.info("next steps:");
128
143
  logger.step(`cd ${path.relative(process.cwd(), targetDirectory) || "."}`);
129
- logger.step("npm install");
144
+ if (!installDependencies) logger.step("npm install");
130
145
  logger.step("npm run dev");
131
146
  }
132
147
 
package/src/lib/build.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { existsSync } from "node:fs";
2
+
1
3
  import { buildDirectory, buildProject, configureProject, findExecutable } from "./cmake.js";
2
4
  import { FlareError } from "./errors.js";
3
5
  import { binaryName, frontendDirectory } from "./project.js";
@@ -10,14 +12,35 @@ export async function runHook(command, { cwd, env, logger, label }) {
10
12
  if (result.code !== 0) throw new FlareError(`${label} failed with code ${result.code}`);
11
13
  }
12
14
 
15
+ export async function ensureFrontendDirectory({ projectDirectory, config, logger }) {
16
+ const frontend = frontendDirectory(projectDirectory, config);
17
+ if (existsSync(frontend)) return;
18
+ if (!config.build.beforeBuildCommand) {
19
+ throw new FlareError(`frontend directory '${frontend}' is missing and no beforeBuildCommand is configured`);
20
+ }
21
+ logger.info("preparing frontend assets for CMake embedding");
22
+ await runHook(config.build.beforeBuildCommand, {
23
+ cwd: projectDirectory,
24
+ logger,
25
+ label: "frontend build command"
26
+ });
27
+ if (!existsSync(frontend)) {
28
+ throw new FlareError(`frontend build command did not create '${frontend}'`);
29
+ }
30
+ }
31
+
13
32
  export async function compileApplication({ projectDirectory, config, mode, env, logger, configFile, environment }) {
33
+ const frontend = frontendDirectory(projectDirectory, config);
34
+ if (!existsSync(frontend)) {
35
+ throw new FlareError(`frontend directory '${frontend}' is missing; run the frontend build command before compiling`);
36
+ }
14
37
  const directory = buildDirectory(projectDirectory, mode);
15
38
  await configureProject(projectDirectory, {
16
39
  directory,
17
40
  mode,
18
41
  env,
19
42
  logger,
20
- frontendDirectory: frontendDirectory(projectDirectory, config),
43
+ frontendDirectory: frontend,
21
44
  configFile,
22
45
  environment
23
46
  });
package/src/lib/cmake.js CHANGED
@@ -35,24 +35,37 @@ export function buildDirectory(projectDirectory, mode) {
35
35
  return path.join(projectDirectory, "build", mode === "Debug" ? "debug" : "release");
36
36
  }
37
37
 
38
+ export function msvcUtf8Arguments(directory, { platform = process.platform, cxxFlags = process.env.CXXFLAGS ?? "" } = {}) {
39
+ if (platform !== "win32") return [];
40
+ const cacheFile = path.join(directory, "CMakeCache.txt");
41
+ const cache = existsSync(cacheFile) ? readFileSync(cacheFile, "utf8") : "";
42
+ const existing = cache.match(/^CMAKE_CXX_FLAGS:STRING=(.*)$/m)?.[1] ?? cxxFlags;
43
+ if (/(?:^|\s)\/utf-8(?:\s|$)/i.test(existing)) return [];
44
+ return [`-DCMAKE_CXX_FLAGS:STRING=${[existing.trim(), "/utf-8"].filter(Boolean).join(" ")}`];
45
+ }
46
+
38
47
  export async function configureProject(projectDirectory, { directory, mode, env, logger, frontendDirectory, configFile = "flare.config.json", environment = "production" }) {
39
48
  if (!cmakeVersion()) throw new FlareError("cmake was not found in PATH");
40
49
  const outputDirectory = path.join(directory, "bin");
41
50
  const configuration = mode.toUpperCase();
51
+ const generator = process.env.CMAKE_GENERATOR ?? "";
52
+ const multiConfiguration = generator
53
+ ? /^(Visual Studio|Xcode|Ninja Multi-Config)/.test(generator)
54
+ : process.platform === "win32";
55
+ const prebuiltSdk = existsSync(path.join(projectDirectory, "vendor", "flare", "sdk-manifest.json"));
42
56
  const args = [
43
57
  "-S",
44
58
  projectDirectory,
45
59
  "-B",
46
60
  directory,
47
61
  ...generatorArguments(),
48
- `-DCMAKE_BUILD_TYPE=${mode}`,
62
+ ...(!multiConfiguration ? [`-DCMAKE_BUILD_TYPE=${mode}`] : []),
49
63
  `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${outputDirectory}`,
50
64
  `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_${configuration}=${outputDirectory}`,
51
65
  "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
52
66
  `-DFLARE_FRONTEND_DIR=${frontendDirectory ?? path.join(projectDirectory, "frontend")}`,
53
- "-DFLARE_BUILD_EXAMPLES=OFF",
54
- "-DFLARE_BUILD_TESTS=OFF",
55
- "-DFLARE_INSTALL=OFF"
67
+ ...(!prebuiltSdk ? ["-DFLARE_BUILD_EXAMPLES=OFF", "-DFLARE_BUILD_TESTS=OFF", "-DFLARE_INSTALL=OFF"] : []),
68
+ ...msvcUtf8Arguments(directory, { cxxFlags: env?.CXXFLAGS ?? process.env.CXXFLAGS ?? "" })
56
69
  ];
57
70
  logger.debug(`cmake ${args.join(" ")}`);
58
71
  const result = await run("cmake", args, { cwd: projectDirectory, env });
package/src/lib/icons.js CHANGED
@@ -1,23 +1,13 @@
1
+ import { readFileSync } from "node:fs";
2
+
1
3
  import { FlareError } from "./errors.js";
2
- import { resizePng, writePng } from "./png.js";
4
+ import { readPng, resizePng, writePng } from "./png.js";
5
+
6
+ let defaultMark;
3
7
 
4
8
  export function placeholderIcon(size = 512) {
5
- const data = Buffer.alloc(size * size * 4);
6
- const center = (size - 1) / 2;
7
- const radius = size * 0.46;
8
- for (let y = 0; y < size; y += 1) {
9
- for (let x = 0; x < size; x += 1) {
10
- const target = (y * size + x) * 4;
11
- const distance = Math.hypot(x - center, y - center) / radius;
12
- const glow = Math.max(0, Math.min(1, 1 - Math.abs(distance - 0.55) * 1.6));
13
- const edge = distance <= 1 ? 1 : 0;
14
- data[target] = edge ? Math.round(44 + 172 * glow) : 0;
15
- data[target + 1] = edge ? Math.round(35 + 132 * glow) : 0;
16
- data[target + 2] = edge ? Math.round(40 + 137 * glow) : 0;
17
- data[target + 3] = edge ? 255 : 0;
18
- }
19
- }
20
- return { width: size, height: size, data };
9
+ defaultMark ??= readPng(readFileSync(new URL("../assets/flare-mark.png", import.meta.url)));
10
+ return resizePng(defaultMark, size, size);
21
11
  }
22
12
 
23
13
  const ICO_SIZES = [16, 24, 32, 48, 64, 128, 256];
@@ -94,6 +94,9 @@ FetchContent_MakeAvailable(flare)`}
94
94
 
95
95
  add_executable(${name} source/main.cpp)
96
96
  target_link_libraries(${name} PRIVATE flare::core flare::assets)
97
+ if(MSVC)
98
+ target_compile_options(${name} PRIVATE /utf-8)
99
+ endif()
97
100
 
98
101
  find_program(FLARE_NODE_EXECUTABLE node REQUIRED)
99
102
  set(FLARE_CONFIG_FILE "flare.config.json" CACHE STRING "Source application configuration")