@callstack/brownfield-cli 3.13.1 → 3.14.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/brownfield/commands/packageAndroid.d.ts.map +1 -1
  3. package/dist/brownfield/commands/packageAndroid.js +3 -1
  4. package/dist/brownfield/commands/packageIos.d.ts +1 -0
  5. package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
  6. package/dist/brownfield/commands/packageIos.js +37 -4
  7. package/dist/brownfield/commands/publishAndroid.d.ts.map +1 -1
  8. package/dist/brownfield/commands/publishAndroid.js +3 -1
  9. package/dist/brownfield/utils/createLocalSpmPackage.d.ts +2 -1
  10. package/dist/brownfield/utils/createLocalSpmPackage.d.ts.map +1 -1
  11. package/dist/brownfield/utils/createLocalSpmPackage.js +8 -2
  12. package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts +9 -0
  13. package/dist/brownfield/utils/emitExpoSupportXcframeworks.d.ts.map +1 -0
  14. package/dist/brownfield/utils/emitExpoSupportXcframeworks.js +70 -0
  15. package/dist/brownfield/utils/normalizeCopiedXcframework.d.ts +3 -0
  16. package/dist/brownfield/utils/normalizeCopiedXcframework.d.ts.map +1 -0
  17. package/dist/brownfield/utils/normalizeCopiedXcframework.js +64 -0
  18. package/dist/brownfield/utils/prepareLocalSpmArtifacts.d.ts.map +1 -1
  19. package/dist/brownfield/utils/prepareLocalSpmArtifacts.js +2 -63
  20. package/dist/brownfield/utils/project.d.ts.map +1 -1
  21. package/dist/brownfield/utils/project.js +20 -9
  22. package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts +14 -0
  23. package/dist/brownfield/utils/supportsPrebuiltExpo.d.ts.map +1 -0
  24. package/dist/brownfield/utils/supportsPrebuiltExpo.js +20 -0
  25. package/dist/brownie/config.js +1 -1
  26. package/dist/config.d.ts +4 -1
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js +10 -7
  29. package/dist/expoPluginConfig.d.ts +74 -0
  30. package/dist/expoPluginConfig.d.ts.map +1 -0
  31. package/dist/expoPluginConfig.js +103 -0
  32. package/dist/types.d.ts +94 -3
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +12 -6
  35. package/schema.json +98 -9
  36. package/src/brownfield/commands/packageAndroid.ts +4 -1
  37. package/src/brownfield/commands/packageIos.ts +53 -4
  38. package/src/brownfield/commands/publishAndroid.ts +4 -1
  39. package/src/brownfield/utils/createLocalSpmPackage.ts +15 -1
  40. package/src/brownfield/utils/emitExpoSupportXcframeworks.ts +132 -0
  41. package/src/brownfield/utils/normalizeCopiedXcframework.ts +90 -0
  42. package/src/brownfield/utils/prepareLocalSpmArtifacts.ts +7 -90
  43. package/src/brownfield/utils/project.ts +33 -14
  44. package/src/brownfield/utils/supportsPrebuiltExpo.ts +34 -0
  45. package/src/brownie/config.ts +1 -1
  46. package/src/config.ts +12 -8
  47. package/src/expoPluginConfig.ts +195 -0
  48. package/src/types.ts +112 -3
package/src/config.ts CHANGED
@@ -10,13 +10,13 @@ import { findProjectRoot } from './brownfield/utils/paths.js';
10
10
  import BrownfieldSchema from '../schema.json' with { type: 'json' };
11
11
  import { logger } from '@rock-js/tools';
12
12
 
13
- const CONFIG_BASE_NAME = 'brownfield';
14
- const JS_CONFIG_FILE_NAME = `${CONFIG_BASE_NAME}.config.js`;
15
- const JSON_CONFIG_FILE_NAME = `${CONFIG_BASE_NAME}.config.json`;
13
+ export const CONFIG_BASE_NAME = 'brownfield';
14
+ export const JS_CONFIG_FILE_NAME = `${CONFIG_BASE_NAME}.config.js`;
15
+ export const JSON_CONFIG_FILE_NAME = `${CONFIG_BASE_NAME}.config.json`;
16
16
 
17
17
  const SEPARATOR = '\n● ';
18
18
 
19
- const ajv = new Ajv({ allErrors: true });
19
+ const ajv = new Ajv({ allErrors: true, allowUnionTypes: true });
20
20
  const validateBrownfieldConfig = ajv.compile(BrownfieldSchema);
21
21
 
22
22
  export function validateBrownfieldCLIConfig(config: unknown): void {
@@ -29,7 +29,7 @@ export function validateBrownfieldCLIConfig(config: unknown): void {
29
29
 
30
30
  export function loadBrownfieldConfig(
31
31
  projectRoot: string = findProjectRoot()
32
- ): BrownfieldConfig {
32
+ ): BrownfieldConfig | null {
33
33
  const require = createRequire(path.join(projectRoot, 'package.json'));
34
34
 
35
35
  const jsConfigFilePath = path.join(projectRoot, JS_CONFIG_FILE_NAME);
@@ -41,7 +41,7 @@ export function loadBrownfieldConfig(
41
41
  [
42
42
  fs.existsSync(jsConfigFilePath),
43
43
  fs.existsSync(jsonConfigFilePath),
44
- packageJson[CONFIG_BASE_NAME],
44
+ CONFIG_BASE_NAME in packageJson,
45
45
  ].filter(Boolean).length > 1
46
46
  ) {
47
47
  throw new Error('Project has multiple Brownfield configuration files');
@@ -55,7 +55,11 @@ export function loadBrownfieldConfig(
55
55
  return require(jsonConfigFilePath) as BrownfieldConfig;
56
56
  }
57
57
 
58
- return packageJson[CONFIG_BASE_NAME] || {};
58
+ if (CONFIG_BASE_NAME in packageJson) {
59
+ return (packageJson[CONFIG_BASE_NAME] as BrownfieldConfig) ?? {};
60
+ }
61
+
62
+ return null;
59
63
  }
60
64
 
61
65
  type BrownfieldPlatform = 'android' | 'ios';
@@ -77,7 +81,7 @@ export function mergeBrownfieldConfigWithOptions<T extends object>(
77
81
  options: T,
78
82
  platform: BrownfieldPlatform
79
83
  ): T {
80
- const reactNativeBrownfieldConfig = loadBrownfieldConfig();
84
+ const reactNativeBrownfieldConfig = loadBrownfieldConfig() ?? {};
81
85
 
82
86
  validateBrownfieldCLIConfig(reactNativeBrownfieldConfig);
83
87
 
@@ -0,0 +1,195 @@
1
+ import type { BrownfieldConfig } from './types.js';
2
+
3
+ export { loadBrownfieldConfig } from './config.js';
4
+
5
+ /**
6
+ * Legacy Expo config plugin props shape accepted via app.json plugins tuple.
7
+ */
8
+ export type BrownfieldPluginProps = {
9
+ debug?: boolean;
10
+ ios?: {
11
+ frameworkName?: string;
12
+ bundleIdentifier?: string;
13
+ buildSettings?: Record<string, string | boolean | number>;
14
+ deploymentTarget?: string;
15
+ frameworkVersion?: string;
16
+ };
17
+ android?: {
18
+ moduleName?: string;
19
+ packageName?: string;
20
+ minSdkVersion?: number;
21
+ targetSdkVersion?: number;
22
+ compileSdkVersion?: number;
23
+ groupId?: string;
24
+ artifactId?: string;
25
+ version?: string;
26
+ useLocalGradlePlugin?: boolean;
27
+ };
28
+ };
29
+
30
+ export type ResolvedBrownfieldPluginAndroidConfig = {
31
+ moduleName: string;
32
+ packageName: string;
33
+ minSdkVersion: number;
34
+ targetSdkVersion: number;
35
+ compileSdkVersion: number;
36
+ groupId: string;
37
+ artifactId: string;
38
+ version: string;
39
+ useLocalGradlePlugin: boolean;
40
+ };
41
+
42
+ export type ResolvedBrownfieldPluginIosConfig = {
43
+ frameworkName: string;
44
+ bundleIdentifier: string;
45
+ buildSettings: Record<string, string | boolean | number>;
46
+ deploymentTarget: string;
47
+ frameworkVersion: string;
48
+ };
49
+
50
+ export type ResolvedBrownfieldPluginConfig = {
51
+ debug: boolean;
52
+ ios: ResolvedBrownfieldPluginIosConfig | null;
53
+ android: ResolvedBrownfieldPluginAndroidConfig | null;
54
+ };
55
+
56
+ type BrownfieldExpoConfig = {
57
+ ios?: {
58
+ bundleIdentifier?: string;
59
+ };
60
+ android?: {
61
+ package?: string;
62
+ };
63
+ };
64
+
65
+ const CONFIG_FILE_PLUGIN_OVERLAP_ERROR =
66
+ 'Brownfield configuration is defined in both a brownfield config file and app.json plugin options. ' +
67
+ 'Use only one source: either brownfield.config.js, brownfield.config.json, package.json#brownfield, or the plugin options in app.json.';
68
+
69
+ /**
70
+ * Checks if the plugin props are non-empty.
71
+ * @param props - The plugin props to check.
72
+ * @returns True if the plugin props are non-empty, false otherwise.
73
+ */
74
+ function isPluginPropsNonEmpty(props: BrownfieldPluginProps): boolean {
75
+ if (props.debug !== undefined) {
76
+ return true;
77
+ }
78
+
79
+ if (props.ios && Object.keys(props.ios).length > 0) {
80
+ return true;
81
+ }
82
+
83
+ if (props.android && Object.keys(props.android).length > 0) {
84
+ return true;
85
+ }
86
+
87
+ return false;
88
+ }
89
+
90
+ /**
91
+ * Converts the file config to plugin props.
92
+ * @param fileConfig - The file config to convert to plugin props.
93
+ * @returns The plugin props.
94
+ */
95
+ function fileConfigToPluginProps(
96
+ fileConfig: BrownfieldConfig
97
+ ): BrownfieldPluginProps {
98
+ const props: BrownfieldPluginProps = {};
99
+
100
+ if (fileConfig.verbose !== undefined) {
101
+ props.debug = fileConfig.verbose;
102
+ }
103
+
104
+ if (fileConfig.android) {
105
+ props.android = {
106
+ moduleName: fileConfig.android.moduleName,
107
+ ...fileConfig.android.expo,
108
+ };
109
+ }
110
+
111
+ if (fileConfig.ios) {
112
+ props.ios = {
113
+ frameworkName: fileConfig.ios.scheme,
114
+ ...fileConfig.ios.expo,
115
+ };
116
+ }
117
+
118
+ return props;
119
+ }
120
+
121
+ /**
122
+ * Asserts that there is no overlap between the file config and the plugin props.
123
+ * @param fileConfig - The loaded file config, or null when no config source exists.
124
+ * @param pluginProps - The plugin props to check for overlap.
125
+ * @throws An error if there is overlap.
126
+ * @returns void
127
+ */
128
+ export function assertNoConfigFilePluginOverlap(
129
+ fileConfig: BrownfieldConfig | null,
130
+ pluginProps: BrownfieldPluginProps
131
+ ): void {
132
+ if (fileConfig === null) {
133
+ return;
134
+ }
135
+
136
+ if (isPluginPropsNonEmpty(pluginProps)) {
137
+ throw new Error(CONFIG_FILE_PLUGIN_OVERLAP_ERROR);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Resolves the plugin config from the file config and the plugin props.
143
+ * @param pluginProps - The plugin props to resolve.
144
+ * @param fileConfig - The file config to resolve.
145
+ * @param expoConfig - The Expo plugin config to resolve.
146
+ * @returns The resolved plugin config.
147
+ */
148
+ export function resolveBrownfieldPluginConfig(
149
+ pluginProps: BrownfieldPluginProps,
150
+ fileConfig: BrownfieldConfig | null,
151
+ expoConfig: BrownfieldExpoConfig
152
+ ): ResolvedBrownfieldPluginConfig {
153
+ const effectiveProps =
154
+ fileConfig !== null ? fileConfigToPluginProps(fileConfig) : pluginProps;
155
+
156
+ const androidPackage = expoConfig.android?.package;
157
+ /**
158
+ * Below: android.moduleName may be provided in the fully-qualified Gradle format
159
+ * (e.g. :BrownfieldLib — this is shown in the CLI example usage).
160
+ * The Expo prebuild side expects a raw module folder/name (it later
161
+ * prepends : itself and uses it as a path), so a leading : is removed.
162
+ */
163
+ const androidModuleName = (
164
+ effectiveProps.android?.moduleName ?? 'brownfieldlib'
165
+ ).replace(/^:/, '');
166
+
167
+ return {
168
+ debug: effectiveProps.debug ?? false,
169
+ ios: expoConfig.ios
170
+ ? {
171
+ frameworkName: effectiveProps.ios?.frameworkName ?? 'BrownfieldLib',
172
+ bundleIdentifier:
173
+ effectiveProps.ios?.bundleIdentifier ??
174
+ `${expoConfig.ios.bundleIdentifier}.brownfield`,
175
+ buildSettings: effectiveProps.ios?.buildSettings ?? {},
176
+ deploymentTarget: effectiveProps.ios?.deploymentTarget ?? '15.0',
177
+ frameworkVersion: effectiveProps.ios?.frameworkVersion ?? '1',
178
+ }
179
+ : null,
180
+ android: androidPackage
181
+ ? {
182
+ moduleName: androidModuleName,
183
+ packageName: effectiveProps.android?.packageName ?? androidPackage,
184
+ minSdkVersion: effectiveProps.android?.minSdkVersion ?? 24,
185
+ targetSdkVersion: effectiveProps.android?.targetSdkVersion ?? 35,
186
+ compileSdkVersion: effectiveProps.android?.compileSdkVersion ?? 35,
187
+ groupId: effectiveProps.android?.groupId ?? androidPackage,
188
+ artifactId: effectiveProps.android?.artifactId ?? androidModuleName,
189
+ version: effectiveProps.android?.version ?? '0.0.1-SNAPSHOT',
190
+ useLocalGradlePlugin:
191
+ effectiveProps.android?.useLocalGradlePlugin ?? false,
192
+ }
193
+ : null,
194
+ };
195
+ }
package/src/types.ts CHANGED
@@ -7,6 +7,9 @@ import type { BuildFlags as AppleBuildFlags } from '@rock-js/platform-apple-help
7
7
  export type Platform = 'swift' | 'kotlin';
8
8
 
9
9
  export type BrownfieldCommonOptions = Partial<{
10
+ /**
11
+ * Enables verbose CLI logging.
12
+ */
10
13
  verbose: boolean;
11
14
  }>;
12
15
 
@@ -15,13 +18,24 @@ export type BrownfieldConfigMetadata = Partial<{
15
18
  }>;
16
19
 
17
20
  export type BrownieConfig = {
21
+ /**
22
+ * The output path to generate Kotlin Brownie store files at.
23
+ */
18
24
  kotlin?: string;
25
+
26
+ /**
27
+ * The package name for the Kotlin source code.
28
+ */
19
29
  kotlinPackageName?: string;
20
30
  };
21
31
 
22
- export type PackageIosOptions = AppleBuildFlags & {
32
+ export type PackageIosOptions = Omit<AppleBuildFlags, 'archive' | 'local'> & {
23
33
  /** Set when `--use-prebuilt-rn-core` is passed; omitted when the flag is absent (Rock applies RN version defaults). */
24
34
  usePrebuiltRnCore?: boolean;
35
+
36
+ /** Set when `--use-prebuilt-expo` is passed; omitted when the flag is absent (Rock applies Expo version defaults). */
37
+ usePrebuiltExpo?: boolean;
38
+
25
39
  /** When set, generate a local Swift Package Manager manifest next to the packaged XCFramework outputs. */
26
40
  addSpmPackage?: boolean;
27
41
  };
@@ -33,19 +47,114 @@ export type BrownfieldPublishAndroidOptions = BrownfieldCommonOptions &
33
47
  export type BrownfieldPackageIosOptions = BrownfieldCommonOptions &
34
48
  Partial<PackageIosOptions>;
35
49
 
50
+ /**
51
+ * Expo config plugin options for Android prebuild scaffolding.
52
+ */
53
+ export type BrownfieldExpoAndroidConfig = {
54
+ /**
55
+ * The package name for the Android library module.
56
+ */
57
+ packageName?: string;
58
+
59
+ /**
60
+ * Minimum SDK version for the Android library.
61
+ */
62
+ minSdkVersion?: number;
63
+
64
+ /**
65
+ * Target SDK version for the Android library.
66
+ */
67
+ targetSdkVersion?: number;
68
+
69
+ /**
70
+ * Compile SDK version for the Android library.
71
+ */
72
+ compileSdkVersion?: number;
73
+
74
+ /**
75
+ * Maven group ID used when publishing the AAR.
76
+ */
77
+ groupId?: string;
78
+
79
+ /**
80
+ * Maven artifact ID used when publishing the AAR.
81
+ */
82
+ artifactId?: string;
83
+
84
+ /**
85
+ * Maven version used when publishing the AAR.
86
+ */
87
+ version?: string;
88
+
89
+ /**
90
+ * When true, load the Brownfield Gradle plugin from
91
+ * `@callstack/react-native-brownfield/gradle-plugin/brownfield` via
92
+ * `includeBuild` instead of adding the Maven classpath dependency.
93
+ * Disabled by default.
94
+ */
95
+ useLocalGradlePlugin?: boolean;
96
+ };
97
+
98
+ /**
99
+ * Expo config plugin options for iOS prebuild scaffolding.
100
+ */
101
+ export type BrownfieldExpoIosConfig = {
102
+ /**
103
+ * The bundle identifier for the framework.
104
+ */
105
+ bundleIdentifier?: string;
106
+
107
+ /**
108
+ * Custom build settings applied when building the framework.
109
+ */
110
+ buildSettings?: Record<string, string | boolean | number>;
111
+
112
+ /**
113
+ * Minimum iOS deployment target for the generated framework.
114
+ */
115
+ deploymentTarget?: string;
116
+
117
+ /**
118
+ * Framework version used for Apple build settings.
119
+ */
120
+ frameworkVersion?: string;
121
+ };
122
+
36
123
  export type BrownfieldAndroidConfig = Omit<
37
124
  Partial<PackageAarFlags> & Partial<PublishLocalAarFlags>,
38
125
  keyof BrownfieldCommonOptions
39
- >;
126
+ > & {
127
+ /**
128
+ * Expo config plugin options for Android Expo prebuild phase.
129
+ */
130
+ expo?: BrownfieldExpoAndroidConfig;
131
+ };
132
+
40
133
  export type BrownfieldIosConfig = Omit<
41
134
  Partial<PackageIosOptions>,
42
135
  keyof BrownfieldCommonOptions
43
- >;
136
+ > & {
137
+ /**
138
+ * Expo config plugin options for iOS Expo prebuild phase.
139
+ */
140
+ expo?: BrownfieldExpoIosConfig;
141
+ };
44
142
 
45
143
  export type BrownfieldConfig = BrownfieldConfigMetadata &
46
144
  BrownfieldCommonOptions &
47
145
  Partial<{
146
+ /**
147
+ * Brownfield Android configuration.
148
+ */
48
149
  android: BrownfieldAndroidConfig;
150
+
151
+ /**
152
+ * Brownfield iOS configuration.
153
+ */
49
154
  ios: BrownfieldIosConfig;
155
+
156
+ /**
157
+ * Brownie (state library) configuration.
158
+ */
50
159
  brownie: BrownieConfig;
51
160
  }>;