@react-native-community/cli-config 7.0.1

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 (62) hide show
  1. package/build/assign.d.ts +9 -0
  2. package/build/assign.d.ts.map +1 -0
  3. package/build/assign.js +39 -0
  4. package/build/assign.js.map +1 -0
  5. package/build/commands/config.d.ts +8 -0
  6. package/build/commands/config.d.ts.map +1 -0
  7. package/build/commands/config.js +32 -0
  8. package/build/commands/config.js.map +1 -0
  9. package/build/errors.d.ts +6 -0
  10. package/build/errors.d.ts.map +1 -0
  11. package/build/errors.js +49 -0
  12. package/build/errors.js.map +1 -0
  13. package/build/findAssets.d.ts +8 -0
  14. package/build/findAssets.d.ts.map +1 -0
  15. package/build/findAssets.js +53 -0
  16. package/build/findAssets.js.map +1 -0
  17. package/build/findDependencies.d.ts +5 -0
  18. package/build/findDependencies.d.ts.map +1 -0
  19. package/build/findDependencies.js +46 -0
  20. package/build/findDependencies.js.map +1 -0
  21. package/build/index.d.ts +7 -0
  22. package/build/index.d.ts.map +1 -0
  23. package/build/index.js +23 -0
  24. package/build/index.js.map +1 -0
  25. package/build/loadConfig.d.ts +7 -0
  26. package/build/loadConfig.d.ts.map +1 -0
  27. package/build/loadConfig.js +149 -0
  28. package/build/loadConfig.js.map +1 -0
  29. package/build/merge.d.ts +6 -0
  30. package/build/merge.d.ts.map +1 -0
  31. package/build/merge.js +30 -0
  32. package/build/merge.js.map +1 -0
  33. package/build/readConfigFromDisk.d.ts +12 -0
  34. package/build/readConfigFromDisk.d.ts.map +1 -0
  35. package/build/readConfigFromDisk.js +86 -0
  36. package/build/readConfigFromDisk.js.map +1 -0
  37. package/build/resolveReactNativePath.d.ts +6 -0
  38. package/build/resolveReactNativePath.d.ts.map +1 -0
  39. package/build/resolveReactNativePath.js +39 -0
  40. package/build/resolveReactNativePath.js.map +1 -0
  41. package/build/schema.d.ts +10 -0
  42. package/build/schema.d.ts.map +1 -0
  43. package/build/schema.js +177 -0
  44. package/build/schema.js.map +1 -0
  45. package/package.json +33 -0
  46. package/src/__tests__/__snapshots__/index-test.ts.snap +185 -0
  47. package/src/__tests__/findAssets-test.ts +51 -0
  48. package/src/__tests__/findDependencies-test.ts +23 -0
  49. package/src/__tests__/index-test.ts +350 -0
  50. package/src/assign.ts +27 -0
  51. package/src/commands/config.ts +30 -0
  52. package/src/errors.ts +31 -0
  53. package/src/findAssets.ts +28 -0
  54. package/src/findDependencies.ts +24 -0
  55. package/src/index.ts +5 -0
  56. package/src/loadConfig.ts +157 -0
  57. package/src/merge.ts +12 -0
  58. package/src/readConfigFromDisk.ts +68 -0
  59. package/src/resolveReactNativePath.ts +25 -0
  60. package/src/schema.ts +201 -0
  61. package/tsconfig.json +8 -0
  62. package/tsconfig.tsbuildinfo +1957 -0
@@ -0,0 +1,350 @@
1
+ import path from 'path';
2
+ import slash from 'slash';
3
+ import loadConfig from '..';
4
+ import {cleanup, writeFiles, getTempDirectory} from '../../../../jest/helpers';
5
+
6
+ let DIR = getTempDirectory('config_test');
7
+
8
+ jest.dontMock('@react-native-community/cli-tools');
9
+
10
+ const spy = jest.spyOn(global.console, 'warn').mockImplementation(jest.fn());
11
+
12
+ const iosPath = slash(
13
+ require.resolve('@react-native-community/cli-platform-ios'),
14
+ );
15
+ const androidPath = slash(
16
+ require.resolve('@react-native-community/cli-platform-android'),
17
+ );
18
+
19
+ const REACT_NATIVE_MOCK = {
20
+ 'node_modules/react-native/package.json': '{}',
21
+ 'node_modules/react-native/react-native.config.js': `
22
+ const ios = require("${iosPath}");
23
+ const android = require("${androidPath}");
24
+ module.exports = {
25
+ platforms: {
26
+ ios: {
27
+ linkConfig: ios.linkConfig,
28
+ projectConfig: ios.projectConfig,
29
+ dependencyConfig: ios.dependencyConfig,
30
+ },
31
+ android: {
32
+ linkConfig: android.linkConfig,
33
+ projectConfig: android.projectConfig,
34
+ dependencyConfig: android.dependencyConfig,
35
+ },
36
+ },
37
+ };
38
+ `,
39
+ };
40
+
41
+ // Removes string from all key/values within an object
42
+ const removeString = (config, str) =>
43
+ JSON.parse(
44
+ JSON.stringify(config, (_key, value) =>
45
+ // In certain cases, `str` (which is a temporary location) will be `/tmp`
46
+ // which is a symlink to `/private/tmp` on OS X. In this case, tests will fail.
47
+ // Following RegExp makes sure we strip the entire path.
48
+ typeof value === 'string'
49
+ ? slash(value.replace(new RegExp(`(.*)${str}`), '<<REPLACED>>'))
50
+ : value,
51
+ ),
52
+ );
53
+
54
+ beforeEach(async () => {
55
+ await cleanup(DIR);
56
+ jest.resetModules();
57
+ jest.clearAllMocks();
58
+ });
59
+
60
+ afterEach(async () => await cleanup(DIR));
61
+
62
+ test('should have a valid structure by default', () => {
63
+ DIR = getTempDirectory('config_test_structure');
64
+ writeFiles(DIR, {
65
+ 'react-native.config.js': `module.exports = {
66
+ reactNativePath: "."
67
+ }`,
68
+ });
69
+ const config = loadConfig(DIR);
70
+ expect(removeString(config, DIR)).toMatchSnapshot();
71
+ });
72
+
73
+ test('should return dependencies from package.json', () => {
74
+ DIR = getTempDirectory('config_test_deps');
75
+ writeFiles(DIR, {
76
+ ...REACT_NATIVE_MOCK,
77
+ 'node_modules/react-native-test/package.json': '{}',
78
+ 'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
79
+ '',
80
+ 'package.json': `{
81
+ "dependencies": {
82
+ "react-native": "0.0.1",
83
+ "react-native-test": "0.0.1"
84
+ }
85
+ }`,
86
+ });
87
+ const {dependencies} = loadConfig(DIR);
88
+ expect(removeString(dependencies, DIR)).toMatchSnapshot();
89
+ });
90
+
91
+ test('should read a config of a dependency and use it to load other settings', () => {
92
+ DIR = getTempDirectory('config_test_settings');
93
+ writeFiles(DIR, {
94
+ ...REACT_NATIVE_MOCK,
95
+ 'node_modules/react-native-test/package.json': '{}',
96
+ 'node_modules/react-native-test/ReactNativeTest.podspec': '',
97
+ 'node_modules/react-native-test/react-native.config.js': `module.exports = {
98
+ dependency: {
99
+ platforms: {
100
+ ios: {
101
+ project: "./customLocation/customProject.xcodeproj"
102
+ }
103
+ }
104
+ }
105
+ }`,
106
+ 'package.json': `{
107
+ "dependencies": {
108
+ "react-native": "0.0.1",
109
+ "react-native-test": "0.0.1"
110
+ }
111
+ }`,
112
+ });
113
+ const {dependencies} = loadConfig(DIR);
114
+ expect(
115
+ removeString(dependencies['react-native-test'], DIR),
116
+ ).toMatchSnapshot();
117
+ });
118
+
119
+ test('should merge project configuration with default values', () => {
120
+ DIR = getTempDirectory('config_test_merge');
121
+ writeFiles(DIR, {
122
+ ...REACT_NATIVE_MOCK,
123
+ 'node_modules/react-native-test/package.json': '{}',
124
+ 'node_modules/react-native-test/react-native.config.js': `module.exports = {
125
+ dependency: {
126
+ assets: ["foo", "baz"]
127
+ }
128
+ }`,
129
+ 'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
130
+ '',
131
+ 'package.json': `{
132
+ "dependencies": {
133
+ "react-native": "0.0.1",
134
+ "react-native-test": "0.0.1"
135
+ }
136
+ }`,
137
+ 'react-native.config.js': `module.exports = {
138
+ reactNativePath: ".",
139
+ dependencies: {
140
+ "react-native-test": {
141
+ platforms: {
142
+ ios: {
143
+ sourceDir: "./abc"
144
+ }
145
+ },
146
+ assets: ["foo"]
147
+ }
148
+ }
149
+ }`,
150
+ });
151
+ const {dependencies} = loadConfig(DIR);
152
+ expect(removeString(dependencies['react-native-test'], DIR)).toMatchSnapshot(
153
+ 'snapshoting `react-native-test` config',
154
+ );
155
+ });
156
+
157
+ test('should load commands from "react-native-foo" and "react-native-bar" packages', () => {
158
+ DIR = getTempDirectory('config_test_packages');
159
+ writeFiles(DIR, {
160
+ 'node_modules/react-native-foo/package.json': '{}',
161
+ 'node_modules/react-native-foo/react-native.config.js': `module.exports = {
162
+ commands: [
163
+ {
164
+ name: 'foo-command',
165
+ func: () => console.log('foo')
166
+ }
167
+ ]
168
+ }`,
169
+ 'node_modules/react-native-bar/package.json': '{}',
170
+ 'node_modules/react-native-bar/react-native.config.js': `module.exports = {
171
+ commands: [
172
+ {
173
+ name: 'bar-command',
174
+ func: () => console.log('bar')
175
+ }
176
+ ]
177
+ }`,
178
+ 'package.json': `{
179
+ "dependencies": {
180
+ "react-native-foo": "0.0.1",
181
+ "react-native-bar": "0.0.1"
182
+ }
183
+ }`,
184
+ });
185
+ const {commands} = loadConfig(DIR);
186
+ expect(commands).toMatchSnapshot();
187
+ });
188
+
189
+ test('should skip packages that have invalid configuration', () => {
190
+ process.env.FORCE_COLOR = '0'; // To disable chalk
191
+ DIR = getTempDirectory('config_test_skip');
192
+ writeFiles(DIR, {
193
+ 'node_modules/react-native/package.json': '{}',
194
+ 'node_modules/react-native/react-native.config.js': `module.exports = {
195
+ dependency: {
196
+ invalidProperty: 5
197
+ }
198
+ }`,
199
+ 'package.json': `{
200
+ "dependencies": {
201
+ "react-native": "0.0.1"
202
+ }
203
+ }`,
204
+ });
205
+ const {dependencies} = loadConfig(DIR);
206
+ expect(dependencies).toMatchSnapshot('dependencies config');
207
+ expect(spy.mock.calls[0][0]).toMatchSnapshot('logged warning');
208
+ });
209
+
210
+ test('does not use restricted "react-native" key to resolve config from package.json', () => {
211
+ DIR = getTempDirectory('config_test_restricted');
212
+ writeFiles(DIR, {
213
+ 'node_modules/react-native-netinfo/package.json': `{
214
+ "react-native": "src/index.js"
215
+ }`,
216
+ 'package.json': `{
217
+ "dependencies": {
218
+ "react-native-netinfo": "0.0.1"
219
+ }
220
+ }`,
221
+ });
222
+ const {dependencies} = loadConfig(DIR);
223
+ expect(dependencies).toHaveProperty('react-native-netinfo');
224
+ expect(spy).not.toHaveBeenCalled();
225
+ });
226
+
227
+ test('supports dependencies from user configuration with custom root and properties', () => {
228
+ DIR = getTempDirectory('config_test_custom_root');
229
+ const escapePathSeparator = (value: string) =>
230
+ path.sep === '\\' ? value.replace(/(\/|\\)/g, '\\\\') : value;
231
+
232
+ writeFiles(DIR, {
233
+ ...REACT_NATIVE_MOCK,
234
+ 'native-libs/local-lib/ios/LocalRNLibrary.xcodeproj/project.pbxproj': '',
235
+ 'react-native.config.js': `
236
+ const path = require('path');
237
+ const root = path.resolve('${escapePathSeparator(
238
+ DIR,
239
+ )}', 'native-libs', 'local-lib');
240
+
241
+ module.exports = {
242
+ dependencies: {
243
+ 'local-lib': {
244
+ root,
245
+ platforms: {
246
+ ios: {
247
+ podspecPath: "custom-path"
248
+ }
249
+ }
250
+ },
251
+ }
252
+ }`,
253
+ 'package.json': `{
254
+ "dependencies": {
255
+ "react-native": "0.0.1"
256
+ }
257
+ }`,
258
+ });
259
+
260
+ const {dependencies} = loadConfig(DIR);
261
+ expect(removeString(dependencies['local-lib'], DIR)).toMatchInlineSnapshot(`
262
+ Object {
263
+ "assets": Array [],
264
+ "hooks": Object {},
265
+ "name": "local-lib",
266
+ "params": Array [],
267
+ "platforms": Object {
268
+ "android": null,
269
+ "ios": Object {
270
+ "configurations": Array [],
271
+ "folder": "<<REPLACED>>/native-libs/local-lib",
272
+ "libraryFolder": "Libraries",
273
+ "pbxprojPath": "<<REPLACED>>/native-libs/local-lib/ios/LocalRNLibrary.xcodeproj/project.pbxproj",
274
+ "plist": Array [],
275
+ "podfile": null,
276
+ "podspecPath": "custom-path",
277
+ "projectName": "LocalRNLibrary.xcodeproj",
278
+ "projectPath": "<<REPLACED>>/native-libs/local-lib/ios/LocalRNLibrary.xcodeproj",
279
+ "scriptPhases": Array [],
280
+ "sharedLibraries": Array [],
281
+ "sourceDir": "<<REPLACED>>/native-libs/local-lib/ios",
282
+ },
283
+ },
284
+ "root": "<<REPLACED>>/native-libs/local-lib",
285
+ }
286
+ `);
287
+ });
288
+
289
+ test('should apply build types from dependency config', () => {
290
+ DIR = getTempDirectory('config_test_apply_dependency_config');
291
+ writeFiles(DIR, {
292
+ ...REACT_NATIVE_MOCK,
293
+ 'node_modules/react-native-test/package.json': '{}',
294
+ 'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
295
+ '',
296
+ 'node_modules/react-native-test/react-native.config.js': `module.exports = {
297
+ dependency: {
298
+ platforms: {
299
+ ios: {
300
+ configurations: ["debug"]
301
+ }
302
+ }
303
+ }
304
+ }`,
305
+ 'package.json': `{
306
+ "dependencies": {
307
+ "react-native": "0.0.1",
308
+ "react-native-test": "0.0.1"
309
+ }
310
+ }`,
311
+ });
312
+ const {dependencies} = loadConfig(DIR);
313
+ expect(
314
+ removeString(dependencies['react-native-test'], DIR),
315
+ ).toMatchSnapshot();
316
+ });
317
+
318
+ test('supports dependencies from user configuration with custom build type', () => {
319
+ DIR = getTempDirectory('config_test_apply_custom_build_config');
320
+ writeFiles(DIR, {
321
+ ...REACT_NATIVE_MOCK,
322
+ 'react-native.config.js': `module.exports = {
323
+ dependencies: {
324
+ 'react-native-test': {
325
+ platforms: {
326
+ ios: {
327
+ configurations: ["custom_build_type"]
328
+ }
329
+ }
330
+ },
331
+ }
332
+ }`,
333
+ 'node_modules/react-native-test/package.json': '{}',
334
+ 'node_modules/react-native-test/ios/HelloWorld.xcodeproj/project.pbxproj':
335
+ '',
336
+ 'node_modules/react-native-test/react-native.config.js':
337
+ 'module.exports = {}',
338
+ 'package.json': `{
339
+ "dependencies": {
340
+ "react-native": "0.0.1",
341
+ "react-native-test": "0.0.1"
342
+ }
343
+ }`,
344
+ });
345
+
346
+ const {dependencies} = loadConfig(DIR);
347
+ expect(
348
+ removeString(dependencies['react-native-test'], DIR),
349
+ ).toMatchSnapshot();
350
+ });
package/src/assign.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
3
+ *
4
+ * Similar to Object.assign(), but it doesn't execute getters. This allows us to have
5
+ * lazy properties on an object and still be able to merge them together
6
+ *
7
+ */
8
+ export default function assign(target: Object, ...sources: Object[]) {
9
+ sources.forEach((source) => {
10
+ let descriptors = Object.keys(source).reduce((acc, key) => {
11
+ const propertyDescriptor = Object.getOwnPropertyDescriptor(source, key);
12
+ if (propertyDescriptor !== undefined) {
13
+ acc[key] = propertyDescriptor;
14
+ }
15
+ return acc;
16
+ }, {} as PropertyDescriptorMap);
17
+ // by default, Object.assign copies enumerable Symbols too
18
+ Object.getOwnPropertySymbols(source).forEach((sym) => {
19
+ let descriptor = Object.getOwnPropertyDescriptor(source, sym);
20
+ if (descriptor && descriptor.enumerable) {
21
+ descriptors[sym.toString()] = descriptor;
22
+ }
23
+ });
24
+ Object.defineProperties(target, descriptors);
25
+ });
26
+ return target;
27
+ }
@@ -0,0 +1,30 @@
1
+ import {Config, Dependency} from '@react-native-community/cli-types';
2
+
3
+ function isValidRNDependency(config: Dependency) {
4
+ return (
5
+ Object.keys(config.platforms).filter((key) =>
6
+ Boolean(config.platforms[key]),
7
+ ).length !== 0 ||
8
+ (config.hooks && Object.keys(config.hooks).length !== 0) ||
9
+ (config.assets && config.assets.length !== 0) ||
10
+ (config.params && config.params.length !== 0)
11
+ );
12
+ }
13
+
14
+ function filterConfig(config: Config) {
15
+ const filtered = {...config};
16
+ Object.keys(filtered.dependencies).forEach((item) => {
17
+ if (!isValidRNDependency(filtered.dependencies[item])) {
18
+ delete filtered.dependencies[item];
19
+ }
20
+ });
21
+ return filtered;
22
+ }
23
+
24
+ export default {
25
+ name: 'config',
26
+ description: 'Print CLI configuration',
27
+ func: async (_argv: string[], ctx: Config) => {
28
+ console.log(JSON.stringify(filterConfig(ctx), null, 2));
29
+ },
30
+ };
package/src/errors.ts ADDED
@@ -0,0 +1,31 @@
1
+ import {CLIError} from '@react-native-community/cli-tools';
2
+ import {ValidationError} from 'joi';
3
+
4
+ export class JoiError extends CLIError {
5
+ constructor(joiError: ValidationError) {
6
+ const message = joiError.details
7
+ .map((error) => {
8
+ const name = error.path.join('.');
9
+ switch (error.type) {
10
+ case 'object.allowUnknown': {
11
+ const value = JSON.stringify(error.context && error.context.value);
12
+ return `
13
+ Unknown option ${name} with value "${value}" was found.
14
+ This is either a typing error or a user mistake. Fixing it will remove this message.
15
+ `;
16
+ }
17
+ default:
18
+ return error.message;
19
+ }
20
+ })
21
+ .join()
22
+ .trim();
23
+
24
+ super(message);
25
+ this.name = 'Config Validation Error';
26
+
27
+ if (Error.captureStackTrace) {
28
+ Error.captureStackTrace(this, JoiError);
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,28 @@
1
+ import glob from 'glob';
2
+ import path from 'path';
3
+
4
+ const findAssetsInFolder = (folder: string): string[] => {
5
+ const assets = glob.sync(path.join(folder, '**'), {nodir: true});
6
+ if (process.platform === 'win32') {
7
+ return assets.map((asset) => asset.split('/').join('\\'));
8
+ }
9
+ return assets;
10
+ };
11
+
12
+ /**
13
+ * Given an array of assets folders, e.g. ['Fonts', 'Images'],
14
+ * it globs in them to find all files that can be copied.
15
+ *
16
+ * It returns an array of absolute paths to files found.
17
+ */
18
+ export default function findAssets(
19
+ folder: string,
20
+ assets: Array<string>,
21
+ ): Array<string> {
22
+ return (assets || [])
23
+ .map((asset) => path.join(folder, asset))
24
+ .reduce(
25
+ (acc, assetPath) => acc.concat(findAssetsInFolder(assetPath)),
26
+ [] as string[],
27
+ );
28
+ }
@@ -0,0 +1,24 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+
4
+ /**
5
+ * Returns an array of dependencies from project's package.json
6
+ */
7
+ export default function findDependencies(root: string): Array<string> {
8
+ let pjson;
9
+
10
+ try {
11
+ pjson = JSON.parse(
12
+ fs.readFileSync(path.join(root, 'package.json'), 'UTF-8'),
13
+ );
14
+ } catch (e) {
15
+ return [];
16
+ }
17
+
18
+ const deps = [
19
+ ...Object.keys(pjson.dependencies || {}),
20
+ ...Object.keys(pjson.devDependencies || {}),
21
+ ];
22
+
23
+ return deps;
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import config from './commands/config';
2
+
3
+ export {default} from './loadConfig';
4
+
5
+ export const commands = [config];
@@ -0,0 +1,157 @@
1
+ import path from 'path';
2
+ import chalk from 'chalk';
3
+ import {
4
+ UserDependencyConfig,
5
+ ProjectConfig,
6
+ Dependency,
7
+ UserConfig,
8
+ Config,
9
+ } from '@react-native-community/cli-types';
10
+ import {
11
+ logger,
12
+ inlineString,
13
+ findProjectRoot,
14
+ resolveNodeModuleDir,
15
+ } from '@react-native-community/cli-tools';
16
+ import findDependencies from './findDependencies';
17
+ import resolveReactNativePath from './resolveReactNativePath';
18
+ import findAssets from './findAssets';
19
+ import {
20
+ readConfigFromDisk,
21
+ readDependencyConfigFromDisk,
22
+ } from './readConfigFromDisk';
23
+ import assign from './assign';
24
+ import merge from './merge';
25
+
26
+ function getDependencyConfig(
27
+ root: string,
28
+ dependencyName: string,
29
+ finalConfig: Config,
30
+ config: UserDependencyConfig,
31
+ userConfig: UserConfig,
32
+ isPlatform: boolean,
33
+ ): Dependency {
34
+ return merge(
35
+ {
36
+ root,
37
+ name: dependencyName,
38
+ platforms: Object.keys(finalConfig.platforms).reduce(
39
+ (dependency, platform) => {
40
+ const platformConfig = finalConfig.platforms[platform];
41
+ dependency[platform] =
42
+ // Linking platforms is not supported
43
+ isPlatform || !platformConfig
44
+ ? null
45
+ : platformConfig.dependencyConfig(
46
+ root,
47
+ config.dependency.platforms[platform],
48
+ );
49
+ return dependency;
50
+ },
51
+ {} as Config['platforms'],
52
+ ),
53
+ assets: findAssets(root, config.dependency.assets),
54
+ hooks: config.dependency.hooks,
55
+ params: config.dependency.params,
56
+ },
57
+ userConfig.dependencies[dependencyName] || {},
58
+ ) as Dependency;
59
+ }
60
+
61
+ /**
62
+ * Loads CLI configuration
63
+ */
64
+ function loadConfig(projectRoot: string = findProjectRoot()): Config {
65
+ let lazyProject: ProjectConfig;
66
+ const userConfig = readConfigFromDisk(projectRoot);
67
+
68
+ const initialConfig: Config = {
69
+ root: projectRoot,
70
+ get reactNativePath() {
71
+ return userConfig.reactNativePath
72
+ ? path.resolve(projectRoot, userConfig.reactNativePath)
73
+ : resolveReactNativePath(projectRoot);
74
+ },
75
+ dependencies: userConfig.dependencies,
76
+ commands: userConfig.commands,
77
+ get assets() {
78
+ return findAssets(projectRoot, userConfig.assets);
79
+ },
80
+ healthChecks: [],
81
+ platforms: userConfig.platforms,
82
+ get project() {
83
+ if (lazyProject) {
84
+ return lazyProject;
85
+ }
86
+
87
+ lazyProject = {};
88
+ for (const platform in finalConfig.platforms) {
89
+ const platformConfig = finalConfig.platforms[platform];
90
+ if (platformConfig) {
91
+ lazyProject[platform] = platformConfig.projectConfig(
92
+ projectRoot,
93
+ userConfig.project[platform] || {},
94
+ );
95
+ }
96
+ }
97
+
98
+ return lazyProject;
99
+ },
100
+ };
101
+
102
+ const finalConfig = Array.from(
103
+ new Set([
104
+ ...Object.keys(userConfig.dependencies),
105
+ ...findDependencies(projectRoot),
106
+ ]),
107
+ ).reduce((acc: Config, dependencyName) => {
108
+ const localDependencyRoot =
109
+ userConfig.dependencies[dependencyName] &&
110
+ userConfig.dependencies[dependencyName].root;
111
+ let root: string;
112
+ let config: UserDependencyConfig;
113
+ try {
114
+ root =
115
+ localDependencyRoot ||
116
+ resolveNodeModuleDir(projectRoot, dependencyName);
117
+ config = readDependencyConfigFromDisk(root);
118
+ } catch (error) {
119
+ logger.warn(
120
+ inlineString(`
121
+ Package ${chalk.bold(
122
+ dependencyName,
123
+ )} has been ignored because it contains invalid configuration.
124
+
125
+ Reason: ${chalk.dim(error.message)}`),
126
+ );
127
+ return acc;
128
+ }
129
+
130
+ const isPlatform = Object.keys(config.platforms).length > 0;
131
+
132
+ return assign({}, acc, {
133
+ dependencies: assign({}, acc.dependencies, {
134
+ get [dependencyName](): Dependency {
135
+ return getDependencyConfig(
136
+ root,
137
+ dependencyName,
138
+ finalConfig,
139
+ config,
140
+ userConfig,
141
+ isPlatform,
142
+ );
143
+ },
144
+ }),
145
+ commands: [...acc.commands, ...config.commands],
146
+ platforms: {
147
+ ...acc.platforms,
148
+ ...config.platforms,
149
+ },
150
+ healthChecks: [...acc.healthChecks, ...config.healthChecks],
151
+ }) as Config;
152
+ }, initialConfig);
153
+
154
+ return finalConfig;
155
+ }
156
+
157
+ export default loadConfig;
package/src/merge.ts ADDED
@@ -0,0 +1,12 @@
1
+ import deepmerge from 'deepmerge';
2
+
3
+ /**
4
+ * `deepmerge` concatenates arrays by default instead of overwriting them.
5
+ * We define custom merging function for arrays to change that behaviour
6
+ */
7
+ export default function merge<X, Y>(x: X, y: Y) {
8
+ return deepmerge(x, y, {
9
+ arrayMerge: (_destinationArray: any[], sourceArray: any[]): any[] =>
10
+ sourceArray,
11
+ });
12
+ }