crossbind 2.0.0-beta.67 → 2.0.0-beta.69

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 (61) hide show
  1. package/README.md +33 -24
  2. package/package.json +69 -59
  3. package/src/actions/addExtension.js +191 -0
  4. package/src/actions/androidGradle.js +69 -25
  5. package/src/actions/appInstaller.js +69 -153
  6. package/src/actions/appSwift.js +66 -0
  7. package/src/actions/appleSigning.js +32 -14
  8. package/src/actions/buildApp.js +229 -67
  9. package/src/actions/buildNode.js +6 -1
  10. package/src/actions/buildWasm.js +0 -9
  11. package/src/actions/createInterface.js +17 -8
  12. package/src/actions/createLib.js +5 -7
  13. package/src/actions/extensionApi.js +298 -0
  14. package/src/actions/extensionCommands.js +41 -0
  15. package/src/actions/extensionsInApp.js +19 -0
  16. package/src/actions/getCmakeParameters.js +2 -3
  17. package/src/actions/gradleProject.js +199 -16
  18. package/src/actions/iosDevice.js +200 -0
  19. package/src/actions/iosSimulator.js +3 -3
  20. package/src/actions/licenses.js +83 -34
  21. package/src/actions/linuxPackages.js +48 -0
  22. package/src/actions/mavenVerification.js +37 -0
  23. package/src/actions/publish.js +220 -0
  24. package/src/actions/uploadKey.js +134 -0
  25. package/src/actions/windowsSigning.js +1 -1
  26. package/src/actions/xcodeProject.js +63 -17
  27. package/src/actions/xcodeTargets.js +122 -0
  28. package/src/actions/xcodebuild.js +65 -0
  29. package/src/app/index.d.ts +126 -133
  30. package/src/app/index.js +126 -188
  31. package/src/assets/configure/install.sh +0 -0
  32. package/src/assets/toolchain-digests.json +20 -20
  33. package/src/bin.js +79 -26
  34. package/src/index.js +5 -1
  35. package/src/program.js +35 -4
  36. package/src/state/index.js +2 -1
  37. package/src/state/loadConfig.js +16 -5
  38. package/src/utils/appApiHeader.js +4 -0
  39. package/src/utils/appExtensions.js +761 -0
  40. package/src/utils/appFiles.js +58 -0
  41. package/src/utils/appLaunches.js +249 -0
  42. package/src/utils/appOptions.js +144 -95
  43. package/src/utils/appTargets.js +168 -0
  44. package/src/utils/ask.js +38 -0
  45. package/src/utils/bridgeTools.js +66 -0
  46. package/src/utils/configChanges.js +30 -0
  47. package/src/utils/configWriter.js +181 -0
  48. package/src/utils/extensionSteps.js +135 -0
  49. package/src/utils/hermes.js +24 -0
  50. package/src/utils/keychain.js +54 -0
  51. package/src/utils/openUrl.js +12 -0
  52. package/src/utils/password.js +34 -0
  53. package/src/utils/plist.js +5 -0
  54. package/src/utils/requiredReasonApis.js +77 -0
  55. package/src/utils/resolveAppHost.js +7 -0
  56. package/src/utils/secretFile.js +23 -0
  57. package/src/utils/systemConfig.js +19 -0
  58. package/src/utils/systemKeys.js +16 -0
  59. package/vitest.config.js +6 -1
  60. package/src/actions/appUpdate.js +0 -68
  61. package/src/actions/extensions.js +0 -7
package/src/program.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  } from './actions/cloudCommands.js';
7
7
  import { cloudUrl } from './utils/cloudAccount.js';
8
8
  import flattenConfigForTable from './utils/flattenConfigForTable.js';
9
- import loadSystemConfig, { systemConfigPath } from './utils/systemConfig.js';
9
+ import loadSystemConfig, { extensionSettingsOf, systemConfigPath } from './utils/systemConfig.js';
10
10
  import systemKeys from './utils/systemKeys.js';
11
11
  import writeJson from './utils/writeJson.js';
12
12
 
@@ -19,10 +19,14 @@ export const handleFatal = (e) => {
19
19
  console.error(e);
20
20
  } else {
21
21
  let message = e?.message || String(e);
22
- if (e?.cause?.message) message += `\n caused by: ${e.cause.message.split('\n')[0]}`;
22
+ // A cause the message tells already, as an extension's failed step does, is told once.
23
+ const cause = e?.cause?.message?.split('\n')[0];
24
+ if (cause && !message.includes(cause)) message += `\n caused by: ${cause}`;
23
25
  console.error(message);
24
26
  }
25
- process.exit(1);
27
+ // Writes to a pipe are asynchronous, so exiting at once would cut what is still queued, such as the failed
28
+ // command's output run.js printed: exit after both streams have written it.
29
+ process.stdout.write('', () => process.stderr.write('', () => process.exit(1)));
26
30
  };
27
31
 
28
32
  export const createProgram = () => new Command()
@@ -109,12 +113,30 @@ function listSystemKeys() {
109
113
  console.table(systemKeys);
110
114
  }
111
115
 
116
+ // An extension's own setting, as its name and the setting's name it: demo-store.TOKEN.
117
+ function extensionSetting(text) {
118
+ const [, extension, key] = /^([a-z][a-z0-9-]*)\.([A-Za-z][A-Za-z0-9_]*)$/.exec(text) ?? [];
119
+ return extension ? { extension, key: key.toUpperCase() } : null;
120
+ }
121
+
122
+ // crossbind's settings, and each extension's own by its name and the setting's.
123
+ function shownSettings(system) {
124
+ const { extensions = {}, ...settings } = system;
125
+ const owned = Object.keys(extensions).flatMap((name) => Object.entries(extensionSettingsOf(system, name)).map(([key, value]) => [`${name}.${key}`, value]));
126
+ return { ...settings, ...Object.fromEntries(owned) };
127
+ }
128
+
112
129
  async function getSystemConfig(systemKey) {
113
130
  if (!systemKey) {
114
131
  await listSystemConfig('system');
115
132
  return;
116
133
  }
117
134
  const system = loadSystemConfig();
135
+ const own = extensionSetting(systemKey);
136
+ if (own) {
137
+ console.table({ [`${own.extension}.${own.key}`]: { value: extensionSettingsOf(system, own.extension)[own.key] ?? 'undefined' } });
138
+ return;
139
+ }
118
140
  const key = systemKey.toUpperCase();
119
141
  const output = {};
120
142
  if (key in system) {
@@ -141,6 +163,15 @@ function setSystemConfig(key, value) {
141
163
  }
142
164
 
143
165
  function deleteSystemConfig(key) {
166
+ const own = extensionSetting(key);
167
+ if (own) {
168
+ const system = loadSystemConfig();
169
+ const { [own.key]: deleted, ...kept } = extensionSettingsOf(system, own.extension);
170
+ if (deleted === undefined) throw new Error(`Configuration ${own.extension}.${own.key} is not set.`);
171
+ const { [own.extension]: emptied, ...others } = system.extensions;
172
+ writeJson(systemConfigPath(), { ...system, extensions: Object.keys(kept).length > 0 ? { ...others, [own.extension]: kept } : others });
173
+ return;
174
+ }
144
175
  if (!systemKeys[key]) {
145
176
  throw new Error(`Configuration ${key} is not available. Please choose from the following available configurations: ${Object.keys(systemKeys).join(', ')}`);
146
177
  }
@@ -155,7 +186,7 @@ async function listSystemConfig(type) {
155
186
  : (await import('./state/index.js')).default.config;
156
187
  if (type === 'all' || type === 'system') {
157
188
  console.log('System Configuration');
158
- console.table(systemConfig);
189
+ console.table(shownSettings(systemConfig));
159
190
  }
160
191
 
161
192
  if (type === 'all') {
@@ -7,6 +7,7 @@ import {
7
7
  import { findCargoModuleImportsIn, writeCargoMarker } from '../utils/cargoImport.js';
8
8
  import { exportWasiSdkPath } from '../utils/wasiToolchain.js';
9
9
  import loadConfig, { assertBinarySelection } from './loadConfig.js';
10
+ import { configChangesOf } from '../utils/configChanges.js';
10
11
  import refreshConanDependencies from './refreshConanDependencies.js';
11
12
 
12
13
  const cacheDir = `${process.cwd()}/.crossbind`;
@@ -25,7 +26,7 @@ await initProcessState();
25
26
 
26
27
  async function initProcessState() {
27
28
  state.cache = loadCacheState();
28
- state.config = await loadConfig();
29
+ state.config = await loadConfig(undefined, undefined, { changes: configChangesOf() });
29
30
  assertBinarySelection(state.config);
30
31
  refreshConanDependencies(state.config);
31
32
  exportWasiSdkPath(state.config.system);
@@ -14,6 +14,8 @@ import logger from '../utils/logger.js';
14
14
  import { RUNTIME_ENVS } from '../utils/targets.js';
15
15
  import { normalizeConanDependencies } from '../utils/conanDependencies.js';
16
16
  import isSourceCmakePackage from '../utils/isSourceCmakePackage.js';
17
+ import { listedExtension } from '../utils/appExtensions.js';
18
+ import { mergePatch } from '../utils/configChanges.js';
17
19
  // import getCmakeParameters from './getCmakeParameters.js';
18
20
 
19
21
  // For the project being built only: dependency builds load published configs that may still set
@@ -28,8 +30,14 @@ export function assertBinarySelection(config) {
28
30
  }
29
31
  }
30
32
 
31
- export default async function loadConfig(configDir = process.cwd(), configName = 'crossbind.config') {
32
- const config = await loadJs(configDir, configName) || {};
33
+ // The export each loaded config was filled from, as crossbind read it, which an extension's api shows.
34
+ const exports = new WeakMap();
35
+ export const exportOf = (config) => exports.get(config) ?? null;
36
+
37
+ // The project's config, with the changes a publisher makes for its build, as a JSON merge patch of the export.
38
+ export default async function loadConfig(configDir = process.cwd(), configName = 'crossbind.config', { changes = null } = {}) {
39
+ const exported = await loadJs(configDir, configName) || {};
40
+ const config = changes ? mergePatch(exported, changes) : exported;
33
41
  if (config.excludedDependencies?.length) {
34
42
  logger.info('crossbind: `excludedDependencies` in crossbind.config.js is no longer read — declare exclusions in crossbind.overrides.js as { <name>: { exclude: true } }.');
35
43
  }
@@ -85,6 +93,7 @@ export default async function loadConfig(configDir = process.cwd(), configName =
85
93
  output.paths.systemConfig = systemConfigPath();
86
94
  output.system = loadSystemConfig();
87
95
 
96
+ exports.set(output, config);
88
97
  return output;
89
98
  }
90
99
 
@@ -135,6 +144,7 @@ export function getFilledConfig(config, options = { isDepend: false }) {
135
144
  target: config.target || {},
136
145
  // An app's identity: its bundle id, on iOS the Apple team that signs it, the name it shows and its desktop window.
137
146
  app: config.app || {},
147
+ // What an app's host builds in beside its own extensions.
138
148
  extensions: config.extensions || [],
139
149
  package: null,
140
150
  functions: config.functions || {},
@@ -199,9 +209,10 @@ export function getFilledConfig(config, options = { isDepend: false }) {
199
209
 
200
210
  newConfig.allDependencies = flattenDependencies(newConfig.dependencies);
201
211
 
202
- newConfig.extensions?.forEach(e => {
203
- e?.loadConfig?.after(newConfig);
204
- });
212
+ if (!Array.isArray(newConfig.extensions)) throw new Error('crossbind: "extensions" must be a list of extensions, as their packages\' crossbind.extension.js gives them.');
213
+ newConfig.extensions = newConfig.extensions.map((extension, i) => listedExtension(extension, `extensions[${i}]`, newConfig.paths.base));
214
+ const twice = newConfig.extensions.find(({ name }, i) => newConfig.extensions.findIndex((other) => other.name === name) !== i);
215
+ if (twice) throw new Error(`crossbind: "extensions" lists two extensions named ${twice.name}.`);
205
216
 
206
217
  if (newConfig.target.runtime !== 'mt' && newConfig.allDependencies.some((d) => d?.target?.runtime === 'mt')) {
207
218
  newConfig.target.runtime = 'mt';
@@ -12,6 +12,10 @@ export function compilesAsApp(target) {
12
12
  return state.config.target?.runtimeEnv === 'app' && runtimeEnvsOf(target.platform).includes('app');
13
13
  }
14
14
 
15
+ // What an app's own code compiles with: CROSSBIND_APP, and on iOS its debug information, from which the archive's dSYM
16
+ // gives App Store Connect its files and lines; Android's NDK keeps that by default.
17
+ export const appCompileOptions = (target) => (compilesAsApp(target) ? ['-DCROSSBIND_APP', ...(target.platform === 'ios' ? ['-g'] : [])] : []);
18
+
15
19
  export function appApiHeader() {
16
20
  const header = `${resolveAppHostRoot()}/cpp/include/crossbind/app.h`;
17
21
  if (!fs.existsSync(header)) {