expo-modules-autolinking 0.3.4 → 0.5.2

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.
@@ -3,19 +3,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generatePackageListAsync = exports.resolveModuleAsync = void 0;
6
+ exports.formatArrayOfReactDelegateHandler = exports.generatePackageListAsync = exports.resolveModuleAsync = void 0;
7
7
  const fast_glob_1 = __importDefault(require("fast-glob"));
8
8
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
9
  const path_1 = __importDefault(require("path"));
10
- /**
11
- * Resolves module search result with additional details required for iOS platform.
12
- */
13
- async function resolveModuleAsync(packageName, revision, options) {
10
+ async function findPodspecFile(revision) {
14
11
  var _a;
15
- const [podspecFile] = await fast_glob_1.default('*/*.podspec', {
12
+ if ((_a = revision.config) === null || _a === void 0 ? void 0 : _a.iosPodspecPath()) {
13
+ return revision.config.iosPodspecPath();
14
+ }
15
+ const [podspecFile] = await (0, fast_glob_1.default)('*/*.podspec', {
16
16
  cwd: revision.path,
17
17
  ignore: ['**/node_modules/**'],
18
18
  });
19
+ return podspecFile;
20
+ }
21
+ /**
22
+ * Resolves module search result with additional details required for iOS platform.
23
+ */
24
+ async function resolveModuleAsync(packageName, revision, options) {
25
+ var _a, _b, _c;
26
+ const podspecFile = await findPodspecFile(revision);
19
27
  if (!podspecFile) {
20
28
  return null;
21
29
  }
@@ -26,6 +34,8 @@ async function resolveModuleAsync(packageName, revision, options) {
26
34
  podspecDir,
27
35
  flags: options.flags,
28
36
  modulesClassNames: (_a = revision.config) === null || _a === void 0 ? void 0 : _a.iosModulesClassNames(),
37
+ appDelegateSubscribers: (_b = revision.config) === null || _b === void 0 ? void 0 : _b.iosAppDelegateSubscribers(),
38
+ reactDelegateHandlers: (_c = revision.config) === null || _c === void 0 ? void 0 : _c.iosReactDelegateHandlers(),
29
39
  };
30
40
  }
31
41
  exports.resolveModuleAsync = resolveModuleAsync;
@@ -42,9 +52,17 @@ exports.generatePackageListAsync = generatePackageListAsync;
42
52
  * Generates the string to put into the generated package list.
43
53
  */
44
54
  async function generatePackageListFileContentAsync(modules, className) {
45
- const modulesToProvide = modules.filter((module) => module.modulesClassNames.length > 0);
46
- const pods = modulesToProvide.map((module) => module.podName);
47
- const classNames = [].concat(...modulesToProvide.map((module) => module.modulesClassNames));
55
+ const modulesToImport = modules.filter((module) => module.modulesClassNames.length ||
56
+ module.appDelegateSubscribers.length ||
57
+ module.reactDelegateHandlers.length);
58
+ const pods = modulesToImport.map((module) => module.podName);
59
+ const modulesClassNames = []
60
+ .concat(...modulesToImport.map((module) => module.modulesClassNames))
61
+ .filter(Boolean);
62
+ const appDelegateSubscribers = []
63
+ .concat(...modulesToImport.map((module) => module.appDelegateSubscribers))
64
+ .filter(Boolean);
65
+ const reactDelegateHandlerModules = modulesToImport.filter((module) => !!module.reactDelegateHandlers.length);
48
66
  return `/**
49
67
  * Automatically generated by expo-modules-autolinking.
50
68
  *
@@ -57,10 +75,40 @@ ${pods.map((podName) => `import ${podName}\n`).join('')}
57
75
  @objc(${className})
58
76
  public class ${className}: ModulesProvider {
59
77
  public override func getModuleClasses() -> [AnyModule.Type] {
60
- return [${classNames.map((className) => `\n ${className}.self`).join(',')}
61
- ]
78
+ return ${formatArrayOfClassNames(modulesClassNames)}
79
+ }
80
+
81
+ public override func getAppDelegateSubscribers() -> [ExpoAppDelegateSubscriber.Type] {
82
+ return ${formatArrayOfClassNames(appDelegateSubscribers)}
83
+ }
84
+
85
+ public override func getReactDelegateHandlers() -> [ExpoReactDelegateHandlerTupleType] {
86
+ return ${formatArrayOfReactDelegateHandler(reactDelegateHandlerModules)}
62
87
  }
63
88
  }
64
89
  `;
65
90
  }
91
+ /**
92
+ * Formats an array of class names to Swift's array containing these classes.
93
+ */
94
+ function formatArrayOfClassNames(classNames) {
95
+ const indent = ' ';
96
+ return `[${classNames.map((className) => `\n${indent.repeat(3)}${className}.self`).join(',')}
97
+ ${indent.repeat(2)}]`;
98
+ }
99
+ /**
100
+ * Formats an array of modules to Swift's array containing ReactDelegateHandlers
101
+ */
102
+ function formatArrayOfReactDelegateHandler(modules) {
103
+ const values = [];
104
+ for (const module of modules) {
105
+ for (const handler of module.reactDelegateHandlers) {
106
+ values.push(`(packageName: "${module.packageName}", handler: ${handler}.self)`);
107
+ }
108
+ }
109
+ const indent = ' ';
110
+ return `[${values.map((value) => `\n${indent.repeat(3)}${value}`).join(',')}
111
+ ${indent.repeat(2)}]`;
112
+ }
113
+ exports.formatArrayOfReactDelegateHandler = formatArrayOfReactDelegateHandler;
66
114
  //# sourceMappingURL=ios.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/platforms/ios.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,wDAA0B;AAC1B,gDAAwB;AAIxB;;GAEG;AACI,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAyB,EACzB,OAAsB;;IAEtB,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,mBAAI,CAAC,aAAa,EAAE;QAC9C,GAAG,EAAE,QAAQ,CAAC,IAAI;QAClB,MAAM,EAAE,CAAC,oBAAoB,CAAC;KAC/B,CAAC,CAAC;IAEH,IAAI,CAAC,WAAW,EAAE;QAChB,OAAO,IAAI,CAAC;KACb;IAED,MAAM,OAAO,GAAG,cAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,cAAI,CAAC,OAAO,CAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IAEvE,OAAO;QACL,OAAO;QACP,UAAU;QACV,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,iBAAiB,EAAE,MAAA,QAAQ,CAAC,MAAM,0CAAE,oBAAoB,EAAE;KAC3D,CAAC;AACJ,CAAC;AAvBD,gDAuBC;AAED;;GAEG;AACI,KAAK,UAAU,wBAAwB,CAC5C,OAA2B,EAC3B,UAAkB;IAElB,MAAM,SAAS,GAAG,cAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IACtE,MAAM,oBAAoB,GAAG,MAAM,mCAAmC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAE3F,MAAM,kBAAE,CAAC,UAAU,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;AACxD,CAAC;AARD,4DAQC;AAED;;GAEG;AACH,KAAK,UAAU,mCAAmC,CAChD,OAA2B,EAC3B,SAAiB;IAEjB,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAE5F,OAAO;;;;;;;;EAQP,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,SAAS;eACF,SAAS;;cAEV,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,WAAW,SAAS,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;;;;CAIjF,CAAC;AACF,CAAC","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs-extra';\nimport path from 'path';\n\nimport { ModuleDescriptor, PackageRevision, SearchOptions } from '../types';\n\n/**\n * Resolves module search result with additional details required for iOS platform.\n */\nexport async function resolveModuleAsync(\n packageName: string,\n revision: PackageRevision,\n options: SearchOptions\n): Promise<ModuleDescriptor | null> {\n const [podspecFile] = await glob('*/*.podspec', {\n cwd: revision.path,\n ignore: ['**/node_modules/**'],\n });\n\n if (!podspecFile) {\n return null;\n }\n\n const podName = path.basename(podspecFile, path.extname(podspecFile));\n const podspecDir = path.dirname(path.join(revision.path, podspecFile));\n\n return {\n podName,\n podspecDir,\n flags: options.flags,\n modulesClassNames: revision.config?.iosModulesClassNames(),\n };\n}\n\n/**\n * Generates Swift file that contains all autolinked Swift packages.\n */\nexport async function generatePackageListAsync(\n modules: ModuleDescriptor[],\n targetPath: string\n): Promise<void> {\n const className = path.basename(targetPath, path.extname(targetPath));\n const generatedFileContent = await generatePackageListFileContentAsync(modules, className);\n\n await fs.outputFile(targetPath, generatedFileContent);\n}\n\n/**\n * Generates the string to put into the generated package list.\n */\nasync function generatePackageListFileContentAsync(\n modules: ModuleDescriptor[],\n className: string\n): Promise<string> {\n const modulesToProvide = modules.filter((module) => module.modulesClassNames.length > 0);\n const pods = modulesToProvide.map((module) => module.podName);\n const classNames = [].concat(...modulesToProvide.map((module) => module.modulesClassNames));\n\n return `/**\n * Automatically generated by expo-modules-autolinking.\n *\n * This autogenerated class provides a list of classes of native Expo modules,\n * but only these that are written in Swift and use the new API for creating Expo modules.\n */\n\nimport ExpoModulesCore\n${pods.map((podName) => `import ${podName}\\n`).join('')}\n@objc(${className})\npublic class ${className}: ModulesProvider {\n public override func getModuleClasses() -> [AnyModule.Type] {\n return [${classNames.map((className) => `\\n ${className}.self`).join(',')}\n ]\n }\n}\n`;\n}\n"]}
1
+ {"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/platforms/ios.ts"],"names":[],"mappings":"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","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs-extra';\nimport path from 'path';\n\nimport { ModuleDescriptor, PackageRevision, SearchOptions } from '../types';\n\nasync function findPodspecFile(revision: PackageRevision): Promise<string | undefined> {\n if (revision.config?.iosPodspecPath()) {\n return revision.config.iosPodspecPath();\n }\n\n const [podspecFile] = await glob('*/*.podspec', {\n cwd: revision.path,\n ignore: ['**/node_modules/**'],\n });\n\n return podspecFile;\n}\n\n/**\n * Resolves module search result with additional details required for iOS platform.\n */\nexport async function resolveModuleAsync(\n packageName: string,\n revision: PackageRevision,\n options: SearchOptions\n): Promise<ModuleDescriptor | null> {\n const podspecFile = await findPodspecFile(revision);\n if (!podspecFile) {\n return null;\n }\n\n const podName = path.basename(podspecFile, path.extname(podspecFile));\n const podspecDir = path.dirname(path.join(revision.path, podspecFile));\n\n return {\n podName,\n podspecDir,\n flags: options.flags,\n modulesClassNames: revision.config?.iosModulesClassNames(),\n appDelegateSubscribers: revision.config?.iosAppDelegateSubscribers(),\n reactDelegateHandlers: revision.config?.iosReactDelegateHandlers(),\n };\n}\n\n/**\n * Generates Swift file that contains all autolinked Swift packages.\n */\nexport async function generatePackageListAsync(\n modules: ModuleDescriptor[],\n targetPath: string\n): Promise<void> {\n const className = path.basename(targetPath, path.extname(targetPath));\n const generatedFileContent = await generatePackageListFileContentAsync(modules, className);\n\n await fs.outputFile(targetPath, generatedFileContent);\n}\n\n/**\n * Generates the string to put into the generated package list.\n */\nasync function generatePackageListFileContentAsync(\n modules: ModuleDescriptor[],\n className: string\n): Promise<string> {\n const modulesToImport = modules.filter(\n (module) =>\n module.modulesClassNames.length ||\n module.appDelegateSubscribers.length ||\n module.reactDelegateHandlers.length\n );\n const pods = modulesToImport.map((module) => module.podName);\n\n const modulesClassNames = []\n .concat(...modulesToImport.map((module) => module.modulesClassNames))\n .filter(Boolean);\n\n const appDelegateSubscribers = []\n .concat(...modulesToImport.map((module) => module.appDelegateSubscribers))\n .filter(Boolean);\n\n const reactDelegateHandlerModules = modulesToImport.filter(\n (module) => !!module.reactDelegateHandlers.length\n );\n\n return `/**\n * Automatically generated by expo-modules-autolinking.\n *\n * This autogenerated class provides a list of classes of native Expo modules,\n * but only these that are written in Swift and use the new API for creating Expo modules.\n */\n\nimport ExpoModulesCore\n${pods.map((podName) => `import ${podName}\\n`).join('')}\n@objc(${className})\npublic class ${className}: ModulesProvider {\n public override func getModuleClasses() -> [AnyModule.Type] {\n return ${formatArrayOfClassNames(modulesClassNames)}\n }\n\n public override func getAppDelegateSubscribers() -> [ExpoAppDelegateSubscriber.Type] {\n return ${formatArrayOfClassNames(appDelegateSubscribers)}\n }\n\n public override func getReactDelegateHandlers() -> [ExpoReactDelegateHandlerTupleType] {\n return ${formatArrayOfReactDelegateHandler(reactDelegateHandlerModules)}\n }\n}\n`;\n}\n\n/**\n * Formats an array of class names to Swift's array containing these classes.\n */\nfunction formatArrayOfClassNames(classNames: string[]): string {\n const indent = ' ';\n return `[${classNames.map((className) => `\\n${indent.repeat(3)}${className}.self`).join(',')}\n${indent.repeat(2)}]`;\n}\n\n/**\n * Formats an array of modules to Swift's array containing ReactDelegateHandlers\n */\nexport function formatArrayOfReactDelegateHandler(modules: ModuleDescriptor[]): string {\n const values: string[] = [];\n for (const module of modules) {\n for (const handler of module.reactDelegateHandlers) {\n values.push(`(packageName: \"${module.packageName}\", handler: ${handler}.self)`);\n }\n }\n const indent = ' ';\n return `[${values.map((value) => `\\n${indent.repeat(3)}${value}`).join(',')}\n${indent.repeat(2)}]`;\n}\n"]}
package/build/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface SearchOptions {
5
5
  ignorePaths?: string[] | null;
6
6
  exclude?: string[] | null;
7
7
  platform: SupportedPlatform;
8
+ silent?: boolean;
8
9
  flags?: Record<string, any>;
9
10
  }
10
11
  export interface ResolveOptions extends SearchOptions {
@@ -15,6 +16,10 @@ export interface GenerateOptions extends ResolveOptions {
15
16
  namespace?: string;
16
17
  empty?: boolean;
17
18
  }
19
+ export interface PatchReactImportsOptions {
20
+ podsRoot: string;
21
+ dryRun: boolean;
22
+ }
18
23
  export declare type PackageRevision = {
19
24
  path: string;
20
25
  version: string;
@@ -41,5 +46,26 @@ export interface RawExpoModuleConfig {
41
46
  * Names of Swift native modules classes to put to the generated modules provider file.
42
47
  */
43
48
  modulesClassNames?: string[];
49
+ /**
50
+ * Names of Swift classes that hooks into `ExpoAppDelegate` to receive AppDelegate life-cycle events.
51
+ */
52
+ appDelegateSubscribers?: string[];
53
+ /**
54
+ * Names of Swift classes that implement `ExpoReactDelegateHandler` to hook React instance creation.
55
+ */
56
+ reactDelegateHandlers?: string[];
57
+ /**
58
+ * Podspec relative path.
59
+ */
60
+ podspecPath?: string;
61
+ };
62
+ /**
63
+ * Android-specific config.
64
+ */
65
+ android?: {
66
+ /**
67
+ * Full names (package + class name) of Kotlin native modules classes to put to the generated package provider file.
68
+ */
69
+ modulesClassNames?: string[];
44
70
  };
45
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import { ExpoModuleConfig } from './ExpoModuleConfig';\n\nexport type SupportedPlatform = 'ios' | 'android' | 'web';\n\nexport interface SearchOptions {\n // Available in the CLI\n searchPaths: string[];\n ignorePaths?: string[] | null;\n exclude?: string[] | null;\n platform: SupportedPlatform;\n\n // Scratched from project's config\n flags?: Record<string, any>;\n}\n\nexport interface ResolveOptions extends SearchOptions {\n json?: boolean;\n}\n\nexport interface GenerateOptions extends ResolveOptions {\n target: string;\n namespace?: string;\n empty?: boolean;\n}\n\nexport type PackageRevision = {\n path: string;\n version: string;\n config?: ExpoModuleConfig;\n duplicates?: PackageRevision[];\n};\n\nexport type SearchResults = {\n [moduleName: string]: PackageRevision;\n};\n\nexport type ModuleDescriptor = Record<string, any>;\n\n/**\n * Represents a raw config from `expo-module.json`.\n */\nexport interface RawExpoModuleConfig {\n /**\n * An array of supported platforms.\n */\n platforms?: SupportedPlatform[];\n\n /**\n * iOS-specific config.\n */\n ios?: {\n /**\n * Names of Swift native modules classes to put to the generated modules provider file.\n */\n modulesClassNames?: string[];\n };\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import { ExpoModuleConfig } from './ExpoModuleConfig';\n\nexport type SupportedPlatform = 'ios' | 'android' | 'web';\n\nexport interface SearchOptions {\n // Available in the CLI\n searchPaths: string[];\n ignorePaths?: string[] | null;\n exclude?: string[] | null;\n platform: SupportedPlatform;\n silent?: boolean;\n\n // Scratched from project's config\n flags?: Record<string, any>;\n}\n\nexport interface ResolveOptions extends SearchOptions {\n json?: boolean;\n}\n\nexport interface GenerateOptions extends ResolveOptions {\n target: string;\n namespace?: string;\n empty?: boolean;\n}\n\nexport interface PatchReactImportsOptions {\n podsRoot: string;\n dryRun: boolean;\n}\n\nexport type PackageRevision = {\n path: string;\n version: string;\n config?: ExpoModuleConfig;\n duplicates?: PackageRevision[];\n};\n\nexport type SearchResults = {\n [moduleName: string]: PackageRevision;\n};\n\nexport type ModuleDescriptor = Record<string, any>;\n\n/**\n * Represents a raw config from `expo-module.json`.\n */\nexport interface RawExpoModuleConfig {\n /**\n * An array of supported platforms.\n */\n platforms?: SupportedPlatform[];\n\n /**\n * iOS-specific config.\n */\n ios?: {\n /**\n * Names of Swift native modules classes to put to the generated modules provider file.\n */\n modulesClassNames?: string[];\n\n /**\n * Names of Swift classes that hooks into `ExpoAppDelegate` to receive AppDelegate life-cycle events.\n */\n appDelegateSubscribers?: string[];\n\n /**\n * Names of Swift classes that implement `ExpoReactDelegateHandler` to hook React instance creation.\n */\n reactDelegateHandlers?: string[];\n\n /**\n * Podspec relative path.\n */\n podspecPath?: string;\n };\n\n /**\n * Android-specific config.\n */\n android?: {\n /**\n * Full names (package + class name) of Kotlin native modules classes to put to the generated package provider file.\n */\n modulesClassNames?: string[];\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-autolinking",
3
- "version": "0.3.4",
3
+ "version": "0.5.2",
4
4
  "description": "Scripts that autolink Expo modules.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -44,8 +44,8 @@
44
44
  "chalk": "^4.1.0",
45
45
  "commander": "^7.2.0",
46
46
  "fast-glob": "^3.2.5",
47
- "find-up": "~5.0.0",
47
+ "find-up": "^5.0.0",
48
48
  "fs-extra": "^9.1.0"
49
49
  },
50
- "gitHead": "f93456e96091501a9b9f99246846cee29336a964"
50
+ "gitHead": "0546c63f2d7c25315dd709de8a1c7a3478d83564"
51
51
  }
@@ -166,6 +166,7 @@ if (rootProject instanceof ProjectDescriptor) {
166
166
  return
167
167
  }
168
168
 
169
+ println ''
169
170
  println 'Using expo modules'
170
171
 
171
172
  for (module in modules) {
@@ -181,8 +182,10 @@ if (rootProject instanceof ProjectDescriptor) {
181
182
  continue
182
183
  }
183
184
 
184
- println " ${Colors.GREEN}${module.name}${Colors.RESET} (${module.version})"
185
+ println " - ${Colors.GREEN}${module.name}${Colors.RESET} (${module.version})"
185
186
  }
187
+
188
+ println ''
186
189
  }
187
190
 
188
191
  // Adding dependencies
@@ -0,0 +1,6 @@
1
+ module React {
2
+ umbrella "../../Public/React-Core/React"
3
+
4
+ export *
5
+ module * { export * }
6
+ }
@@ -2,6 +2,7 @@ require_relative 'constants'
2
2
  require_relative 'package'
3
3
 
4
4
  # Require extensions to CocoaPods' classes
5
+ require_relative 'cocoapods/sandbox'
5
6
  require_relative 'cocoapods/target_definition'
6
7
  require_relative 'cocoapods/user_project_integrator'
7
8
 
@@ -0,0 +1,75 @@
1
+ # Overrides CocoaPods `Sandbox` class to patch podspecs on the fly
2
+ # See: https://github.com/CocoaPods/CocoaPods/blob/master/lib/cocoapods/sandbox.rb
3
+
4
+ require 'json'
5
+
6
+ module Pod
7
+ class Sandbox
8
+ private
9
+
10
+ _original_store_podspec = instance_method(:store_podspec)
11
+
12
+ public
13
+
14
+ define_method(:store_podspec) do |name, podspec, _external_source, json|
15
+ spec = _original_store_podspec.bind(self).(name, podspec, _external_source, json)
16
+ patched_spec = nil
17
+
18
+ # Patch `React-Core.podspec` for clang to generate correct submodules for swift integration
19
+ if name == 'React-Core'
20
+ spec_json = JSON.parse(spec.to_pretty_json)
21
+
22
+ # CocoaPods's default modulemap did not generate submodules correctly
23
+ # `ios/Pods/Headers/Public/React/React-Core.modulemap`
24
+ # ```
25
+ # module React {
26
+ # umbrella header "React-Core-umbrella.h"
27
+ #
28
+ # export *
29
+ # module * { export * }
30
+ # }
31
+ # ```
32
+ # clang will generate submodules for headers relative to the umbrella header directory.
33
+ # https://github.com/llvm/llvm-project/blob/2782cb8da0b3c180fa7c8627cb255a026f3d25a2/clang/lib/Lex/ModuleMap.cpp#L1133
34
+ # In this case, it is `ios/Pods/Headers/Public/React`.
35
+ # But React headers are placed in `ios/Pods/Headers/Public/React-Core/React`, so clang cannot find the headers and generate submodules.
36
+ # We patch `React-Core.podspec` to use custom modulemap and use `umbrella "../../Public/React-Core/React"` for clang to generate submodules correctly.
37
+ # Since CocoaPods generates the umbrella headers based on public headers,
38
+ # it is pretty safe to replace the umbrella header with the `umbrella` directory search inside the public headers directory.
39
+ spec_json['module_map'] ||= File.join(__dir__, '..', 'React-Core.modulemap')
40
+
41
+ # clang module does not support objc++.
42
+ # We should put Hermes headers inside private headers directory.
43
+ # Otherwise, clang will throw errors in building module.
44
+ hermes_subspec_index = spec_json['subspecs'].index { |subspec| subspec['name'] == 'Hermes' }
45
+ if hermes_subspec_index
46
+ spec_json['subspecs'][hermes_subspec_index]['private_header_files'] ||= [
47
+ 'ReactCommon/hermes/executor/*.h',
48
+ 'ReactCommon/hermes/inspector/*.h',
49
+ 'ReactCommon/hermes/inspector/chrome/*.h',
50
+ 'ReactCommon/hermes/inspector/detail/*.h',
51
+ ]
52
+ end
53
+
54
+ patched_spec = Specification.from_json(spec_json.to_json)
55
+
56
+ # Patch `ReactCommon.podspec` to define module
57
+ elsif name == 'ReactCommon'
58
+ spec_json = JSON.parse(podspec.to_pretty_json)
59
+ spec_json['pod_target_xcconfig']['DEFINES_MODULE'] = 'YES'
60
+ patched_spec = Specification.from_json(spec_json.to_json)
61
+ end
62
+
63
+ if patched_spec != nil
64
+ # Store the patched spec with original checksum and local saved file path
65
+ patched_spec.defined_in_file = spec.defined_in_file
66
+ patched_spec.instance_variable_set(:@checksum, spec.checksum)
67
+ @stored_podspecs[spec.name] = patched_spec
68
+ return patched_spec
69
+ end
70
+
71
+ return spec
72
+ end # define_method(:store_podspec)
73
+
74
+ end # class Sandbox
75
+ end # module Pod
@@ -0,0 +1,72 @@
1
+ # Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ module Expo
4
+ class ReactImportPatcher
5
+
6
+ public def initialize(installer, options)
7
+ @root = installer.sandbox.root
8
+ @module_dirs = get_module_dirs(installer)
9
+ @options = options
10
+ end
11
+
12
+ public def run!
13
+ args = [
14
+ 'node',
15
+ '--eval',
16
+ 'require(\'expo-modules-autolinking\')(process.argv.slice(1))',
17
+ 'patch-react-imports',
18
+ '--pods-root',
19
+ Shellwords.escape(File.expand_path(@root)),
20
+ ]
21
+
22
+ if @options[:dry_run]
23
+ args.append('--dry-run')
24
+ end
25
+
26
+ @module_dirs.each do |dir|
27
+ args.append(Shellwords.escape(File.expand_path(dir)))
28
+ end
29
+ Pod::UI.message "Executing ReactImportsPatcher node command: #{args.join(' ')}"
30
+
31
+ time_begin = Process.clock_gettime(Process::CLOCK_MONOTONIC)
32
+ system(*args)
33
+ elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_begin
34
+ Pod::UI.info "expo_patch_react_imports! took #{elapsed_time.round(4)} seconds to transform files."
35
+ end
36
+
37
+ private def get_module_dirs(installer)
38
+ unless installer.pods_project
39
+ Pod::UI.message '`pods_project` not found. This is expected when `:incremental_installation` is enabled in your project\'s Podfile.'
40
+ return []
41
+ end
42
+
43
+ result = []
44
+ installer.pods_project.development_pods.children.each do |pod|
45
+ if pod.is_a?(Xcodeproj::Project::Object::PBXFileReference) && pod.path.end_with?('.xcodeproj')
46
+ # Support generate_multiple_pod_projects or use_frameworks!
47
+ project = Xcodeproj::Project.open(File.join(installer.sandbox.root, pod.path))
48
+ groups = project.groups.select { |group| !(['Dependencies', 'Frameworks', 'Products'].include? group.name) }
49
+ groups.each do |group|
50
+ unless group.path.start_with? '../'
51
+ raise 'CocoaPods does not put pod subprojects inside nested directories'
52
+ end
53
+ result.append(group.path[3..])
54
+ end
55
+ else
56
+ unless pod.path.start_with? '../'
57
+ raise 'CocoaPods does not put pod subgroups inside nested directories'
58
+ end
59
+ result.append(pod.path[3..])
60
+ end
61
+ end
62
+
63
+ return result.select { |dir|
64
+ # Exclude known dirs unnecessary to patch and reduce processing time
65
+ !dir.include?('/react-native/') &&
66
+ !dir.end_with?('/react-native') &&
67
+ !dir.include?('/expo-')
68
+ }
69
+ end
70
+
71
+ end # class ReactImportPatcher
72
+ end # module Expo
@@ -20,6 +20,34 @@ export class ExpoModuleConfig {
20
20
  return this.rawConfig.ios?.modulesClassNames ?? [];
21
21
  }
22
22
 
23
+ /**
24
+ * Returns a list of names of Swift classes that receives AppDelegate life-cycle events.
25
+ */
26
+ iosAppDelegateSubscribers(): string[] {
27
+ return this.rawConfig.ios?.appDelegateSubscribers ?? [];
28
+ }
29
+
30
+ /**
31
+ * Returns a list of names of Swift classes that implement `ExpoReactDelegateHandler`.
32
+ */
33
+ iosReactDelegateHandlers(): string[] {
34
+ return this.rawConfig.ios?.reactDelegateHandlers ?? [];
35
+ }
36
+
37
+ /**
38
+ * Returns a podspec path defined by the module author.
39
+ */
40
+ iosPodspecPath(): string | undefined {
41
+ return this.rawConfig.ios?.podspecPath;
42
+ }
43
+
44
+ /**
45
+ * Returns a list of names of Kotlin native modules classes to put to the generated package provider file.
46
+ */
47
+ androidModulesClassNames() {
48
+ return this.rawConfig.android?.modulesClassNames ?? [];
49
+ }
50
+
23
51
  /**
24
52
  * Returns serializable raw config.
25
53
  */
@@ -0,0 +1,78 @@
1
+ // Copyright 2018-present 650 Industries. All rights reserved.
2
+
3
+ import glob from 'fast-glob';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+
7
+ import { PatchReactImportsOptions } from './types';
8
+
9
+ /**
10
+ * Remove all double-quoted react header imports
11
+ * @param dirs target directories to patch
12
+ * @param options PatchReactImportsOptions
13
+ */
14
+ export async function patchReactImportsAsync(dirs: string[], options: PatchReactImportsOptions) {
15
+ const headerSet = await generateReactHeaderSetAsync(
16
+ path.join(options.podsRoot, 'Headers', 'Public', 'React-Core', 'React')
17
+ );
18
+ await Promise.all(dirs.map((dir) => patchDirAsync(headerSet, dir, options.dryRun)));
19
+ }
20
+
21
+ /**
22
+ * Generate `React-Core` public header names as a set, will transform necessary headers based on this set.
23
+ */
24
+ async function generateReactHeaderSetAsync(reactHeaderDir: string): Promise<Set<string>> {
25
+ const files = await glob('*.h', { cwd: reactHeaderDir });
26
+ return new Set(files);
27
+ }
28
+
29
+ /**
30
+ * Patch imports from a file
31
+ * @param headerSet prebuilt React-Core header set
32
+ * @param file target patch file
33
+ * @param dryRun true if not writing changes to file
34
+ */
35
+ export async function patchFileAsync(headerSet: Set<string>, file: string, dryRun: boolean) {
36
+ let changed = false;
37
+ const content = await fs.readFile(file, 'utf-8');
38
+ const transformContent = content.replace(
39
+ /^#import\s+"(.+)"$/gm,
40
+ (match: string, headerName: string): string => {
41
+ // `#import "RCTBridge.h"` -> `#import <React/RCTBridge.h>`
42
+ if (headerSet.has(headerName)) {
43
+ changed = true;
44
+ return `#import <React/${headerName}>`;
45
+ }
46
+
47
+ // `#import "React/RCTBridge.h"` -> `#import <React/RCTBridge.h>`
48
+ if (headerName.startsWith('React/')) {
49
+ const name = headerName.substring(6);
50
+ if (headerSet.has(name)) {
51
+ changed = true;
52
+ return `#import <React/${name}>`;
53
+ }
54
+ }
55
+
56
+ // Otherwise, return original import
57
+ return match;
58
+ }
59
+ );
60
+
61
+ if (changed) {
62
+ console.log(`Patching imports for file: ${file}`);
63
+ }
64
+ if (!dryRun) {
65
+ await fs.writeFile(file, transformContent);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Patch imports from a directory
71
+ * @param headerSet prebuilt React-Core header set
72
+ * @param file target patch file
73
+ * @param dryRun true if not writing changes to file
74
+ */
75
+ async function patchDirAsync(headerSet: Set<string>, dir: string, dryRun: boolean) {
76
+ const files = await glob('**/*.{h,m,mm}', { cwd: dir, absolute: true });
77
+ return Promise.all(files.map((file) => patchFileAsync(headerSet, file, dryRun)));
78
+ }
@@ -127,13 +127,16 @@ export async function findModulesAsync(providedOptions: SearchOptions): Promise<
127
127
  if (options.searchPaths.length <= 1) {
128
128
  return results;
129
129
  }
130
- return filterToProjectDependencies(results);
130
+ return filterToProjectDependencies(results, providedOptions);
131
131
  }
132
132
 
133
133
  /**
134
134
  * Filters out packages that are not the dependencies of the project.
135
135
  */
136
- function filterToProjectDependencies(results: SearchResults) {
136
+ function filterToProjectDependencies(
137
+ results: SearchResults,
138
+ options: Pick<SearchOptions, 'silent'> = {}
139
+ ) {
137
140
  const filteredResults: SearchResults = {};
138
141
  const visitedPackages = new Set<string>();
139
142
 
@@ -160,11 +163,11 @@ function filterToProjectDependencies(results: SearchResults) {
160
163
  } else {
161
164
  try {
162
165
  dependencyPackageJsonPath = projectRequire.resolve(`${dependencyName}/package.json`);
163
- } catch (error) {
166
+ } catch (error: any) {
164
167
  // Some packages don't include package.json in its `exports` field,
165
168
  // but none of our packages do that, so it seems fine to just ignore that type of error.
166
169
  // Related issue: https://github.com/react-native-community/cli/issues/1168
167
- if (error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
170
+ if (!options.silent && error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
168
171
  console.warn(
169
172
  chalk.yellow(`⚠️ Cannot resolve the path to "${dependencyName}" package.`)
170
173
  );
@@ -285,6 +288,7 @@ export async function generatePackageListAsync(
285
288
  console.error(
286
289
  chalk.red(`Generating package list is not available for platform: ${options.platform}`)
287
290
  );
291
+ throw e;
288
292
  }
289
293
  }
290
294
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import commander from 'commander';
2
2
 
3
+ import { patchReactImportsAsync } from './ReactImportsPatcher';
3
4
  import {
4
5
  findModulesAsync,
5
6
  resolveModulesAsync,
@@ -33,6 +34,7 @@ function registerSearchCommand<OptionsType extends SearchOptions>(
33
34
  'The platform that the resulting modules must support. Available options: "ios", "android"',
34
35
  'ios'
35
36
  )
37
+ .option('--silent', 'Silence resolution warnings')
36
38
  .action(async (searchPaths, providedOptions) => {
37
39
  const options = await mergeLinkingOptionsAsync<OptionsType>({
38
40
  ...providedOptions,
@@ -53,6 +55,15 @@ function registerResolveCommand<OptionsType extends ResolveOptions>(
53
55
  return registerSearchCommand<OptionsType>(commandName, fn);
54
56
  }
55
57
 
58
+ // Register for `patch-react-imports` command
59
+ function registerPatchReactImportsCommand() {
60
+ return commander
61
+ .command('patch-react-imports [paths...]')
62
+ .requiredOption('--pods-root <podsRoot>', 'The path to `Pods` directory')
63
+ .option('--dry-run', 'Only list files without writing changes to the file system')
64
+ .action(patchReactImportsAsync);
65
+ }
66
+
56
67
  module.exports = async function (args: string[]) {
57
68
  // Searches for available expo modules.
58
69
  registerSearchCommand<SearchOptions & { json?: boolean }>('search', async (results, options) => {
@@ -101,6 +112,8 @@ module.exports = async function (args: string[]) {
101
112
  false
102
113
  );
103
114
 
115
+ registerPatchReactImportsCommand();
116
+
104
117
  await commander
105
118
  .version(require('expo-modules-autolinking/package.json').version)
106
119
  .description('CLI command that searches for Expo modules to autolink them.')
@@ -42,6 +42,7 @@ export async function resolveModuleAsync(
42
42
  return {
43
43
  projectName: convertPackageNameToProjectName(packageName),
44
44
  sourceDir,
45
+ modulesClassNames: revision.config?.androidModulesClassNames(),
45
46
  };
46
47
  }
47
48
 
@@ -57,32 +58,51 @@ async function generatePackageListFileContentAsync(
57
58
  modules.filter((module) => module.packageName !== 'expo')
58
59
  );
59
60
 
61
+ const modulesClasses = await findAndroidModules(modules);
62
+
60
63
  return `package ${namespace};
61
64
 
62
65
  import java.util.Arrays;
63
66
  import java.util.List;
64
67
  import expo.modules.core.interfaces.Package;
68
+ import expo.modules.kotlin.modules.Module;
69
+ import expo.modules.kotlin.ModulesProvider;
65
70
 
66
- public class ExpoModulesPackageList {
71
+ public class ExpoModulesPackageList implements ModulesProvider {
67
72
  private static class LazyHolder {
68
73
  static final List<Package> packagesList = Arrays.<Package>asList(
69
74
  ${packagesClasses.map((packageClass) => ` new ${packageClass}()`).join(',\n')}
70
75
  );
76
+
77
+ static final List<Class<? extends Module>> modulesList = Arrays.<Class<? extends Module>>asList(
78
+ ${modulesClasses.map((moduleClass) => ` ${moduleClass}.class`).join(',\n')}
79
+ );
71
80
  }
72
81
 
73
82
  public static List<Package> getPackageList() {
74
83
  return LazyHolder.packagesList;
75
84
  }
85
+
86
+ @Override
87
+ public List<Class<? extends Module>> getModulesList() {
88
+ return LazyHolder.modulesList;
89
+ }
76
90
  }
77
91
  `;
78
92
  }
79
93
 
94
+ function findAndroidModules(modules: ModuleDescriptor[]): string[] {
95
+ const modulesToProvide = modules.filter((module) => module.modulesClassNames.length > 0);
96
+ const classNames = [].concat(...modulesToProvide.map((module) => module.modulesClassNames));
97
+ return classNames;
98
+ }
99
+
80
100
  async function findAndroidPackagesAsync(modules: ModuleDescriptor[]): Promise<string[]> {
81
101
  const classes: string[] = [];
82
102
 
83
103
  await Promise.all(
84
104
  modules.map(async (module) => {
85
- const files = await glob('src/**/*Package.{java,kt}', {
105
+ const files = await glob('**/*Package.{java,kt}', {
86
106
  cwd: module.sourceDir,
87
107
  });
88
108