create-crossbind 2.0.0-beta.62 → 2.0.0-beta.63

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.
Files changed (76) hide show
  1. package/README.md +3 -1
  2. package/package.json +1 -1
  3. package/src/index.js +1 -1
  4. package/src/manifest.json +44 -6
  5. package/templates/backend-nodejs-native/README.md +47 -0
  6. package/templates/backend-nodejs-native/_gitignore +3 -0
  7. package/templates/backend-nodejs-native/crossbind.config.mjs +14 -0
  8. package/templates/backend-nodejs-native/e2e/run.mjs +14 -0
  9. package/templates/backend-nodejs-native/package.json +29 -0
  10. package/templates/backend-nodejs-native/src/index.js +5 -0
  11. package/templates/backend-nodejs-native/src/index.mjs +5 -0
  12. package/templates/backend-nodejs-native/src/native/native.cpp +9 -0
  13. package/templates/backend-nodejs-native/src/native/native.h +12 -0
  14. package/templates/backend-nodejs-standalone/README.md +33 -0
  15. package/templates/backend-nodejs-standalone/_gitignore +1 -0
  16. package/templates/backend-nodejs-standalone/e2e/run.mjs +14 -0
  17. package/templates/backend-nodejs-standalone/package.json +23 -0
  18. package/templates/backend-nodejs-standalone/src/index.js +6 -0
  19. package/templates/backend-nodejs-standalone/src/index.mjs +5 -0
  20. package/templates/backend-nodejs-wasm/README.md +0 -3
  21. package/templates/backend-nodejs-wasm/package.json +2 -2
  22. package/templates/cloud-cloudflare-worker/Readme.md +0 -3
  23. package/templates/cloud-cloudflare-worker/package.json +1 -1
  24. package/templates/desktop-electron/README.md +53 -0
  25. package/templates/desktop-electron/_gitignore +6 -0
  26. package/templates/desktop-electron/crossbind.config.mjs +14 -0
  27. package/templates/desktop-electron/e2e/crossbind.spec.cjs +30 -0
  28. package/templates/desktop-electron/electron-builder.yml +11 -0
  29. package/templates/desktop-electron/package.json +33 -0
  30. package/templates/desktop-electron/playwright.config.cjs +15 -0
  31. package/templates/desktop-electron/pnpm-workspace.yaml +2 -0
  32. package/templates/desktop-electron/src/index.html +16 -0
  33. package/templates/desktop-electron/src/main.js +29 -0
  34. package/templates/desktop-electron/src/native/native.cpp +9 -0
  35. package/templates/desktop-electron/src/native/native.h +12 -0
  36. package/templates/desktop-electron/src/preload.js +5 -0
  37. package/templates/desktop-electron/src/renderer.js +5 -0
  38. package/templates/lib-cmake/README.md +2 -2
  39. package/templates/lib-cmake/package.json +1 -1
  40. package/templates/lib-prebuilt/AGENTS.md +3 -0
  41. package/templates/lib-prebuilt/README.md +22 -10
  42. package/templates/lib-prebuilt/edge/wasm +1 -0
  43. package/templates/lib-prebuilt/edge/wasm.d.ts +1 -0
  44. package/templates/lib-prebuilt/node/napi +1 -0
  45. package/templates/lib-prebuilt/node/napi.d.ts +1 -0
  46. package/templates/lib-prebuilt/node/wasm +1 -0
  47. package/templates/lib-prebuilt/node/wasm.d.ts +1 -0
  48. package/templates/lib-prebuilt/package.json +15 -3
  49. package/templates/lib-source/README.md +2 -2
  50. package/templates/lib-source/package.json +1 -1
  51. package/templates/mobile-reactnative-cli/README.md +0 -3
  52. package/templates/mobile-reactnative-cli/__mocks__/native-header.js +5 -0
  53. package/templates/mobile-reactnative-cli/__tests__/App.test.tsx +1 -1
  54. package/templates/mobile-reactnative-cli/ci/crossbind-snapshot/cache.json +3 -3
  55. package/templates/mobile-reactnative-cli/jest.config.js +3 -0
  56. package/templates/mobile-reactnative-cli/package.json +8 -8
  57. package/templates/mobile-reactnative-expo/README.md +12 -3
  58. package/templates/mobile-reactnative-expo/package-lock.json +184 -176
  59. package/templates/mobile-reactnative-expo/package.json +12 -11
  60. package/templates/mobile-reactnative-expo/src/app/index.tsx +1 -2
  61. package/templates/native-executable/README.md +50 -0
  62. package/templates/native-executable/_gitignore +3 -0
  63. package/templates/native-executable/crossbind.config.mjs +14 -0
  64. package/templates/native-executable/e2e/run.mjs +27 -0
  65. package/templates/native-executable/package.json +26 -0
  66. package/templates/native-executable/src/native/main.cpp +10 -0
  67. package/templates/web-react-rspack/README.md +0 -3
  68. package/templates/web-react-rspack/package.json +3 -3
  69. package/templates/web-react-vite/README.md +0 -3
  70. package/templates/web-react-vite/package.json +2 -2
  71. package/templates/web-svelte-vite/README.md +0 -3
  72. package/templates/web-svelte-vite/package.json +2 -2
  73. package/templates/web-vanilla/README.md +0 -3
  74. package/templates/web-vanilla/package.json +2 -2
  75. package/templates/web-vue-vite/README.md +0 -3
  76. package/templates/web-vue-vite/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  **Create crossbind Applications**
3
3
 
4
4
  <a href="https://www.npmjs.com/package/create-crossbind">
5
- <img alt="NPM version" src="https://img.shields.io/npm/v/create-crossbind?style=for-the-badge" />
5
+ <img alt="NPM version" src="https://img.shields.io/npm/v/create-crossbind/beta?style=for-the-badge" />
6
6
  </a>
7
7
  <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE">
8
8
  <img alt="License" src="https://img.shields.io/github/license/crossbind/crossbind?style=for-the-badge" />
@@ -51,6 +51,8 @@ Every prompt can be preselected positionally, which is what CI and scripted setu
51
51
 
52
52
  ```bash
53
53
  npm create crossbind@beta -- my-app Web React Vite
54
+ npm create crossbind@beta -- my-app Desktop Electron
55
+ npm create crossbind@beta -- my-tool Native Executable
54
56
  npm create crossbind@beta -- my-lib Library Prebuilt
55
57
  ```
56
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-crossbind",
3
- "version": "2.0.0-beta.62",
3
+ "version": "2.0.0-beta.63",
4
4
  "description": "Create crossbind Applications",
5
5
  "homepage": "https://github.com/crossbind/crossbind/tree/main/tooling/create-app#readme",
6
6
  "repository": {
package/src/index.js CHANGED
@@ -80,7 +80,7 @@ function runSteps(entry, scripts, pm) {
80
80
  }
81
81
  if (entry.kind === 'lib') return [`${pm} run build`];
82
82
  if (has('dev')) return [`${pm} run dev`];
83
- if (has('start')) return [`${pm} start`];
83
+ if (has('start')) return has('build') ? [`${pm} run build`, `${pm} start`] : [`${pm} start`];
84
84
  if (has('preview')) return [`${pm} run build`, `${pm} run preview`];
85
85
  if (has('build')) return [`${pm} run build`];
86
86
  return [];
package/src/manifest.json CHANGED
@@ -46,7 +46,8 @@
46
46
  "label": "Native CLI",
47
47
  "kind": "app",
48
48
  "source": "examples/mobile-reactnative-cli",
49
- "nativeFolders": true
49
+ "nativeFolders": true,
50
+ "workspaceOnly": ["eslint.config.mjs"]
50
51
  },
51
52
  {
52
53
  "key": "mobile-reactnative-expo",
@@ -58,6 +59,17 @@
58
59
  "nativeFolders": false,
59
60
  "keepLockfile": true
60
61
  },
62
+ {
63
+ "key": "desktop-electron",
64
+ "group": "Desktop",
65
+ "framework": null,
66
+ "label": "Electron",
67
+ "kind": "app",
68
+ "source": "examples/desktop-electron",
69
+ "allowBuilds": {
70
+ "electron-winstaller": false
71
+ }
72
+ },
61
73
  {
62
74
  "key": "backend-nodejs-wasm",
63
75
  "group": "Backend",
@@ -66,6 +78,23 @@
66
78
  "kind": "app",
67
79
  "source": "examples/backend-nodejs-wasm"
68
80
  },
81
+ {
82
+ "key": "backend-nodejs-native",
83
+ "group": "Backend",
84
+ "framework": "Node.js",
85
+ "label": "Node-API",
86
+ "kind": "app",
87
+ "source": "examples/backend-nodejs-native",
88
+ "workspaceOnly": ["ci"]
89
+ },
90
+ {
91
+ "key": "backend-nodejs-standalone",
92
+ "group": "Backend",
93
+ "framework": "Node.js",
94
+ "label": "Standalone",
95
+ "kind": "app",
96
+ "source": "examples/backend-nodejs-standalone"
97
+ },
69
98
  {
70
99
  "key": "cloud-cloudflare-worker",
71
100
  "group": "Cloud",
@@ -73,10 +102,18 @@
73
102
  "label": "Cloudflare Worker",
74
103
  "kind": "app",
75
104
  "source": "examples/cloud-cloudflare-worker",
76
- "allowBuilds": [
77
- "esbuild",
78
- "workerd"
79
- ]
105
+ "allowBuilds": {
106
+ "esbuild": true,
107
+ "workerd": true
108
+ }
109
+ },
110
+ {
111
+ "key": "native-executable",
112
+ "group": "Native",
113
+ "framework": null,
114
+ "label": "Executable",
115
+ "kind": "app",
116
+ "source": "examples/native-executable"
80
117
  },
81
118
  {
82
119
  "key": "lib-prebuilt",
@@ -84,7 +121,8 @@
84
121
  "framework": null,
85
122
  "label": "Prebuilt",
86
123
  "kind": "lib",
87
- "source": "examples/lib-prebuilt-matrix"
124
+ "source": "examples/lib-prebuilt-matrix",
125
+ "workspaceOnly": ["LICENSE"]
88
126
  },
89
127
  {
90
128
  "key": "lib-source",
@@ -0,0 +1,47 @@
1
+ # @crossbind/example-backend-nodejs-native
2
+ **crossbind Node.js native addon sample**
3
+
4
+ The same C++ code as
5
+ [backend-nodejs-wasm](https://github.com/crossbind/crossbind/tree/main/examples/backend-nodejs-wasm),
6
+ built as a Node-API addon instead of WebAssembly. `initNative()` resolves the same module shape on
7
+ both builds.
8
+
9
+ The app builds its own addon: crossbind generates the bindings of `src/native/native.h` and links
10
+ the matrix library's archives into one addon per platform. To use a library without building
11
+ anything, see
12
+ [backend-nodejs-standalone](https://github.com/crossbind/crossbind/tree/main/examples/backend-nodejs-standalone),
13
+ which loads the addons the same library ships prebuilt.
14
+
15
+ # Getting Started
16
+
17
+ >**Note**: Make sure you have completed the [crossbind - Prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) instructions.
18
+ The bindings and the Linux and Windows addons build in Docker; the macOS addons build on a macOS
19
+ host with Xcode's command line tools.
20
+
21
+ ## Setup
22
+
23
+ Install the dependencies:
24
+
25
+ ```bash
26
+ pnpm install
27
+ ```
28
+
29
+ Inside the crossbind repository, build the library's archives for the desktop platforms first:
30
+ `pnpm --filter @crossbind/example-lib-prebuilt-matrix run build:desktop`.
31
+
32
+ ## Get Started
33
+
34
+ Build the addons of this machine's platform, for arm64 and x64, and their loader:
35
+
36
+ ```bash
37
+ pnpm run build
38
+ ```
39
+
40
+ `pnpm run build:desktop` builds them for every desktop platform instead: `linux`, `linuxmusl` and
41
+ `win32`, plus `darwin` on a Mac.
42
+
43
+ Run it:
44
+
45
+ ```bash
46
+ node src/index.js
47
+ ```
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ .crossbind
3
+ dist
@@ -0,0 +1,14 @@
1
+ import Matrix from '@crossbind/example-lib-prebuilt-matrix/crossbind.config.js';
2
+
3
+ export default {
4
+ general: {
5
+ name: 'crossbind-example-backend-nodejs-native',
6
+ },
7
+ dependencies: [
8
+ Matrix,
9
+ ],
10
+ paths: {
11
+ config: import.meta.url,
12
+ output: 'dist',
13
+ },
14
+ };
@@ -0,0 +1,14 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+
4
+ // The loader picks the addon of this machine, from CommonJS and from ESM alike.
5
+ const EXPECTED = '= 6*J₃';
6
+
7
+ for (const entry of ['src/index.js', 'src/index.mjs']) {
8
+ const out = execFileSync(process.execPath, [entry], { cwd: resolve(import.meta.dirname, '..'), encoding: 'utf8' });
9
+ process.stdout.write(out);
10
+ if (!out.includes(EXPECTED)) {
11
+ console.error(`FAIL: ${entry} did not print "${EXPECTED}"`);
12
+ process.exit(1);
13
+ }
14
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@crossbind/example-backend-nodejs-native",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "description": "crossbind - Node.js native addon sample",
6
+ "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/backend-nodejs-native#readme",
7
+ "repository": "https://github.com/crossbind/crossbind.git",
8
+ "license": "MIT",
9
+ "scripts": {
10
+ "build": "crossbind build -p host -e node -b release",
11
+ "build:desktop": "crossbind build -p darwin,linux,linuxmusl,win32 -e node -b release",
12
+ "e2e:prod": "node e2e/run.mjs"
13
+ },
14
+ "dependencies": {
15
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.63"
16
+ },
17
+ "devDependencies": {
18
+ "@crossbind/core-embind-napi": "^2.0.0-beta.63",
19
+ "crossbind": "^2.0.0-beta.63"
20
+ },
21
+ "keywords": [
22
+ "crossbind-sample",
23
+ "node",
24
+ "node-api",
25
+ "addon",
26
+ "bind",
27
+ "c++"
28
+ ]
29
+ }
@@ -0,0 +1,5 @@
1
+ const initNative = require('../dist/crossbind-example-backend-nodejs-native.native.cjs');
2
+
3
+ initNative().then(({ Native }) => {
4
+ console.log(`Matrix multiplier with c++ => ${Native.sample()}`);
5
+ });
@@ -0,0 +1,5 @@
1
+ import initNative from '../dist/crossbind-example-backend-nodejs-native.native.cjs';
2
+
3
+ initNative().then(({ Native }) => {
4
+ console.log(`Matrix multiplier with c++ => ${Native.sample()}`);
5
+ });
@@ -0,0 +1,9 @@
1
+ #include "native.h"
2
+ #include <Matrix.h>
3
+
4
+ std::string Native::sample() {
5
+ auto firstMatrix = std::make_shared<Matrix>(9, 1);
6
+ auto secondMatrix = std::make_shared<Matrix>(9, 2);
7
+ auto resultStr = std::to_string(firstMatrix->multiple(secondMatrix)->get(0));
8
+ return "J₃ * (2*J₃) = " + resultStr + "*J₃";
9
+ }
@@ -0,0 +1,12 @@
1
+ #ifndef _NATIVE_H
2
+ #define _NATIVE_H
3
+
4
+ #include <string>
5
+ #include <memory>
6
+
7
+ class Native {
8
+ public:
9
+ static std::string sample();
10
+ };
11
+
12
+ #endif
@@ -0,0 +1,33 @@
1
+ # @crossbind/example-backend-nodejs-standalone
2
+ **crossbind Node.js sample on a standalone Node-API package**
3
+
4
+ The app builds nothing: it installs the matrix library,
5
+ [@crossbind/example-lib-prebuilt-matrix](https://github.com/crossbind/crossbind/tree/main/examples/lib-prebuilt-matrix),
6
+ which ships prebuilt Node-API addons for macOS, Linux (glibc and musl) and Windows next to its
7
+ WebAssembly and mobile builds. It needs neither Docker nor a compiler, and crossbind is not one of
8
+ its dependencies.
9
+
10
+ ```js
11
+ import { initNative, Matrix } from '@crossbind/example-lib-prebuilt-matrix/node/napi';
12
+
13
+ await initNative();
14
+ const product = new Matrix(9, 1).multiple(new Matrix(9, 2));
15
+ ```
16
+
17
+ Every name the library binds is ready once `initNative()` resolves, and the same import from
18
+ `/node/wasm` runs its WebAssembly build instead. A `@crossbind/port-<name>-standalone-napi` package
19
+ works the same way from its package root, e.g. `@crossbind/port-zlib-standalone-napi`. In an app of
20
+ your own, `npm install @crossbind/example-lib-prebuilt-matrix@beta` is the whole setup. To build an
21
+ addon from C++ of your own instead, start from
22
+ [backend-nodejs-native](https://github.com/crossbind/crossbind/tree/main/examples/backend-nodejs-native).
23
+
24
+ # Getting Started
25
+
26
+ ```bash
27
+ pnpm install
28
+ pnpm start
29
+ ```
30
+
31
+ Inside the crossbind repository the library comes from the workspace, so build its addons first
32
+ (in Docker, and the macOS ones on a Mac):
33
+ `pnpm --filter @crossbind/example-lib-prebuilt-matrix run build:desktop`.
@@ -0,0 +1 @@
1
+ node_modules
@@ -0,0 +1,14 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+
4
+ // The library's addon for this machine loads from CommonJS and from ESM alike.
5
+ const EXPECTED = '= 6*J₃';
6
+
7
+ for (const entry of ['src/index.js', 'src/index.mjs']) {
8
+ const out = execFileSync(process.execPath, [entry], { cwd: resolve(import.meta.dirname, '..'), encoding: 'utf8' });
9
+ process.stdout.write(out);
10
+ if (!out.includes(EXPECTED)) {
11
+ console.error(`FAIL: ${entry} did not print "${EXPECTED}"`);
12
+ process.exit(1);
13
+ }
14
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@crossbind/example-backend-nodejs-standalone",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "description": "crossbind - Node.js sample on a standalone Node-API package, nothing to build",
6
+ "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/backend-nodejs-standalone#readme",
7
+ "repository": "https://github.com/crossbind/crossbind.git",
8
+ "license": "MIT",
9
+ "scripts": {
10
+ "start": "node src/index.mjs",
11
+ "e2e:prod": "node e2e/run.mjs"
12
+ },
13
+ "dependencies": {
14
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.63"
15
+ },
16
+ "keywords": [
17
+ "crossbind-sample",
18
+ "node",
19
+ "node-api",
20
+ "standalone",
21
+ "c++"
22
+ ]
23
+ }
@@ -0,0 +1,6 @@
1
+ const matrix = require('@crossbind/example-lib-prebuilt-matrix/node/napi');
2
+
3
+ matrix.initNative().then(() => {
4
+ const product = new matrix.Matrix(9, 1).multiple(new matrix.Matrix(9, 2));
5
+ console.log(`Matrix multiplier with c++ => J₃ * (2*J₃) = ${product.get(0)}*J₃`);
6
+ });
@@ -0,0 +1,5 @@
1
+ import { initNative, Matrix } from '@crossbind/example-lib-prebuilt-matrix/node/napi';
2
+
3
+ await initNative();
4
+ const product = new Matrix(9, 1).multiple(new Matrix(9, 2));
5
+ console.log(`Matrix multiplier with c++ => J₃ * (2*J₃) = ${product.get(0)}*J₃`);
@@ -1,9 +1,6 @@
1
1
  # @crossbind/example-backend-nodejs-wasm
2
2
  **crossbind Node.js webassembly sample**
3
3
 
4
- <a href="https://www.npmjs.com/package/@crossbind/example-backend-nodejs-wasm">
5
- <img alt="NPM version" src="https://img.shields.io/npm/v/@crossbind/example-backend-nodejs-wasm?style=for-the-badge" />
6
- </a>
7
4
  <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE">
8
5
  <img alt="License" src="https://img.shields.io/github/license/crossbind/crossbind?style=for-the-badge" />
9
6
  </a>
@@ -10,10 +10,10 @@
10
10
  "build": "crossbind build -p wasm -a wasm32 -r st -e node -b release"
11
11
  },
12
12
  "dependencies": {
13
- "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.62"
13
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.63"
14
14
  },
15
15
  "devDependencies": {
16
- "crossbind": "^2.0.0-beta.62"
16
+ "crossbind": "^2.0.0-beta.63"
17
17
  },
18
18
  "keywords": [
19
19
  "crossbind-sample",
@@ -1,9 +1,6 @@
1
1
  # @crossbind/example-cloud-cloudflare-worker
2
2
  **crossbind Cloudflare Worker sample**
3
3
 
4
- <a href="https://www.npmjs.com/package/@crossbind/example-cloud-cloudflare-worker">
5
- <img alt="NPM version" src="https://img.shields.io/npm/v/@crossbind/example-cloud-cloudflare-worker?style=for-the-badge" />
6
- </a>
7
4
  <a href="https://github.com/crossbind/crossbind/blob/main/LICENSE">
8
5
  <img alt="License" src="https://img.shields.io/github/license/crossbind/crossbind?style=for-the-badge" />
9
6
  </a>
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {},
25
25
  "devDependencies": {
26
- "crossbind": "^2.0.0-beta.62",
26
+ "crossbind": "^2.0.0-beta.63",
27
27
  "wrangler": "^4.136.3",
28
28
  "@playwright/test": "^1.63.0"
29
29
  },
@@ -0,0 +1,53 @@
1
+ # @crossbind/example-desktop-electron
2
+ **crossbind Electron sample**
3
+
4
+ An Electron app whose main process runs C++: crossbind builds `src/native` and the matrix library
5
+ into a Node-API addon, the main process loads it (`src/main.js`), and the window asks it for results
6
+ over IPC (`src/preload.js`), so the window keeps Electron's sandbox and has no Node.js.
7
+
8
+ Node-API is ABI-stable: the addon Node.js loads loads in Electron too, with no rebuild per Electron
9
+ version and nothing for `electron-rebuild` to do.
10
+
11
+ # Getting Started
12
+
13
+ >**Note**: Make sure you have completed the [crossbind - Prerequisites](https://crossbind.dev/docs/guide/getting-started/prerequisites) instructions.
14
+ The bindings and the Linux and Windows addons build in Docker; the macOS addons build on a macOS
15
+ host with Xcode's command line tools.
16
+
17
+ ## Setup
18
+
19
+ Install the dependencies:
20
+
21
+ ```bash
22
+ pnpm install
23
+ ```
24
+
25
+ Inside the crossbind repository, build the library's archives for the desktop platforms first:
26
+ `pnpm --filter @crossbind/example-lib-prebuilt-matrix run build:desktop`.
27
+
28
+ ## Get Started
29
+
30
+ Build the addon of this machine's platform, for arm64 and x64, and start the app:
31
+
32
+ ```bash
33
+ pnpm run build
34
+ pnpm start
35
+ ```
36
+
37
+ `pnpm run build:desktop` builds the addons of macOS, Linux and Windows instead.
38
+
39
+ ## Package
40
+
41
+ ```bash
42
+ pnpm run package
43
+ ```
44
+
45
+ [electron-builder](https://www.electron.build) writes the app to `out/`; `npx electron-builder`
46
+ makes the installers. `electron-builder.yml` keeps `dist/` outside the asar archive: Electron loads
47
+ the addon from there as it is, and native code cannot read a library's data, such as `proj.db`,
48
+ inside the archive. Rename the app there (`productName`).
49
+
50
+ ## Test
51
+
52
+ `pnpm run e2e:dev` opens the app from its sources with Playwright, `pnpm run e2e:prod` packages it
53
+ and opens the packaged app. On Linux they need a display, e.g. `xvfb-run -a pnpm run e2e:prod`.
@@ -0,0 +1,6 @@
1
+ node_modules
2
+ .crossbind
3
+ dist
4
+ out
5
+ test-results
6
+ playwright-report
@@ -0,0 +1,14 @@
1
+ import Matrix from '@crossbind/example-lib-prebuilt-matrix/crossbind.config.js';
2
+
3
+ export default {
4
+ general: {
5
+ name: 'crossbind-example-desktop-electron',
6
+ },
7
+ dependencies: [
8
+ Matrix,
9
+ ],
10
+ paths: {
11
+ config: import.meta.url,
12
+ output: 'dist',
13
+ },
14
+ };
@@ -0,0 +1,30 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const { test: base, expect, _electron: electron } = require('@playwright/test');
4
+
5
+ const test = base.extend({ packaged: [false, { option: true }] });
6
+
7
+ const APP_DIR = path.resolve(__dirname, '..');
8
+ // A CI Linux has no setuid sandbox helper, and the test needs no sandbox.
9
+ const ARGS = process.platform === 'linux' ? ['--no-sandbox'] : [];
10
+
11
+ // The same Electron runs the app.asar `electron-builder --dir` wrote last, and reads what the archive
12
+ // keeps unpacked from the app.asar.unpacked beside it, as the packaged executable does.
13
+ function packagedApp() {
14
+ const out = path.join(APP_DIR, 'out');
15
+ const archives = fs.existsSync(out)
16
+ ? fs.readdirSync(out, { recursive: true }).filter((file) => path.basename(file) === 'app.asar').map((file) => path.join(out, file))
17
+ : [];
18
+ if (archives.length === 0) throw new Error('no out/**/app.asar - run `electron-builder --dir` first');
19
+ return archives.reduce((newest, file) => (fs.statSync(file).mtimeMs > fs.statSync(newest).mtimeMs ? file : newest));
20
+ }
21
+
22
+ test('check string receiving from c++', async ({ packaged }) => {
23
+ const app = await electron.launch({ args: [...ARGS, packaged ? packagedApp() : APP_DIR] });
24
+ try {
25
+ const window = await app.firstWindow();
26
+ await expect(window.locator('#cppMessage')).toHaveText('J₃ * (2*J₃) = 6*J₃');
27
+ } finally {
28
+ await app.close();
29
+ }
30
+ });
@@ -0,0 +1,11 @@
1
+ # crossbind's output stays outside the asar archive: Electron loads the addon from there as it is,
2
+ # and native code cannot read files inside the archive.
3
+ productName: crossbind Electron sample
4
+ directories:
5
+ output: out
6
+ files:
7
+ - src/**
8
+ - dist/**
9
+ - '!dist/prebuilt'
10
+ asarUnpack:
11
+ - dist/**
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@crossbind/example-desktop-electron",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "description": "crossbind - Electron desktop app sample",
6
+ "homepage": "https://github.com/crossbind/crossbind/tree/main/examples/desktop-electron#readme",
7
+ "repository": "https://github.com/crossbind/crossbind.git",
8
+ "license": "MIT",
9
+ "main": "src/main.js",
10
+ "scripts": {
11
+ "build": "crossbind build -p host -e node -b release",
12
+ "build:desktop": "crossbind build -p darwin,linux,win32 -e node -b release",
13
+ "start": "electron .",
14
+ "package": "electron-builder --dir",
15
+ "e2e:dev": "playwright test --project=dev",
16
+ "e2e:prod": "electron-builder --dir && playwright test --project=packaged"
17
+ },
18
+ "devDependencies": {
19
+ "@crossbind/core-embind-napi": "^2.0.0-beta.63",
20
+ "@crossbind/example-lib-prebuilt-matrix": "^2.0.0-beta.63",
21
+ "@playwright/test": "^1.63.0",
22
+ "crossbind": "^2.0.0-beta.63",
23
+ "electron": "^44.5.1",
24
+ "electron-builder": "^26.15.3"
25
+ },
26
+ "keywords": [
27
+ "crossbind-sample",
28
+ "electron",
29
+ "desktop",
30
+ "node-api",
31
+ "c++"
32
+ ]
33
+ }
@@ -0,0 +1,15 @@
1
+ const { defineConfig } = require('@playwright/test');
2
+
3
+ // dev runs the app from its sources, packaged runs what `electron-builder --dir` wrote to out/.
4
+ module.exports = defineConfig({
5
+ testDir: './e2e',
6
+ timeout: 60 * 1000,
7
+ forbidOnly: !!process.env.CI,
8
+ retries: process.env.CI ? 2 : 0,
9
+ workers: 1,
10
+ reporter: 'list',
11
+ projects: [
12
+ { name: 'dev', use: { packaged: false } },
13
+ { name: 'packaged', use: { packaged: true } },
14
+ ],
15
+ });
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ electron-winstaller: false
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+
3
+ <html>
4
+
5
+ <head>
6
+ <meta charset="utf-8">
7
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
8
+ <title>crossbind Electron sample</title>
9
+ <script src="./renderer.js" defer></script>
10
+ </head>
11
+
12
+ <body>
13
+ <p>Matrix multiplier with c++ =&gt; <span id="cppMessage">loading ...</span></p>
14
+ </body>
15
+
16
+ </html>
@@ -0,0 +1,29 @@
1
+ const path = require('node:path');
2
+ const { app, BrowserWindow, ipcMain } = require('electron');
3
+ const initNative = require('../dist/crossbind-example-desktop-electron.native.cjs');
4
+
5
+ // The addon runs in the main process, the one with Node.js; the window asks it over IPC.
6
+ ipcMain.handle('native:sample', async () => {
7
+ const { Native } = await initNative();
8
+ return Native.sample();
9
+ });
10
+
11
+ function createWindow() {
12
+ const window = new BrowserWindow({
13
+ width: 640,
14
+ height: 360,
15
+ webPreferences: { preload: path.join(__dirname, 'preload.js') },
16
+ });
17
+ window.loadFile(path.join(__dirname, 'index.html'));
18
+ }
19
+
20
+ app.whenReady().then(() => {
21
+ createWindow();
22
+ app.on('activate', () => {
23
+ if (BrowserWindow.getAllWindows().length === 0) createWindow();
24
+ });
25
+ });
26
+
27
+ app.on('window-all-closed', () => {
28
+ if (process.platform !== 'darwin') app.quit();
29
+ });
@@ -0,0 +1,9 @@
1
+ #include "native.h"
2
+ #include <Matrix.h>
3
+
4
+ std::string Native::sample() {
5
+ auto firstMatrix = std::make_shared<Matrix>(9, 1);
6
+ auto secondMatrix = std::make_shared<Matrix>(9, 2);
7
+ auto resultStr = std::to_string(firstMatrix->multiple(secondMatrix)->get(0));
8
+ return "J₃ * (2*J₃) = " + resultStr + "*J₃";
9
+ }