expo-modules-macros 0.27.1 → 0.29.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 (44) hide show
  1. package/.github/workflows/linux.yml +3 -7
  2. package/.github/workflows/publish.yml +2 -3
  3. package/.github/workflows/swift.yml +7 -13
  4. package/.github/workflows/windows.yml +3 -10
  5. package/AGENTS.md +10 -7
  6. package/Package.swift +110 -0
  7. package/README.md +31 -8
  8. package/apple/ExpoModulesMacros +0 -0
  9. package/apple/Sources/ExpoModulesCLI/CLI.swift +1 -1
  10. package/apple/Sources/{ExpoModulesMacros → ExpoModulesCLI/Commands}/Expand/MacroExpander.swift +1 -1
  11. package/apple/Sources/ExpoModulesCLI/Commands/ScanExports/ExportedSurface.swift +21 -4
  12. package/apple/Sources/ExpoModulesCLI/Commands/ScanExports/InheritedMembers.swift +96 -0
  13. package/apple/Sources/ExpoModulesCLI/Commands/ScanExports/ScanExports.swift +3 -2
  14. package/apple/Sources/ExpoModulesCLI/Commands/ScanExports/SurfaceVisitor.swift +42 -11
  15. package/apple/Sources/ExpoModulesMacros/main.swift +6 -0
  16. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/ExpoModuleMacro.swift +12 -6
  17. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/RecordMacro.swift +29 -71
  18. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/SharedObjectMacro.swift +30 -3
  19. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/UnionMacro.swift +6 -4
  20. package/apple/Sources/ExpoModulesMacrosCore/PluginServer.swift +40 -0
  21. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/ProvidedMacros.swift +7 -7
  22. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/DecorateModuleBuilder.swift +44 -9
  23. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/JSConstructor.swift +15 -2
  24. package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/MacroHelpers.swift +2 -3
  25. package/apple/Sources/ExpoModulesMacrosCore/Support/SendableConformance.swift +36 -0
  26. package/apple/Sources/ExpoModulesMacrosTool/main.swift +27 -0
  27. package/build/types.d.ts +11 -3
  28. package/package.json +5 -5
  29. package/apple/Package.swift +0 -66
  30. package/apple/Sources/ExpoModulesMacros/Plugin.swift +0 -36
  31. /package/{apple/Package.resolved → Package.resolved} +0 -0
  32. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/EventMacro.swift +0 -0
  33. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/ExpoModulesOptimizedMacro.swift +0 -0
  34. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/ExpoViewMacro.swift +0 -0
  35. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/JSMacro.swift +0 -0
  36. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Macros/ViewPropsMacro.swift +0 -0
  37. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/ClosureArguments.swift +0 -0
  38. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/FreeFormTypes.swift +0 -0
  39. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/JSNameCollisions.swift +0 -0
  40. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/OptimizedFunctionHelpers.swift +0 -0
  41. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/Receiver.swift +0 -0
  42. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/TupleTypes.swift +0 -0
  43. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/TypeAliases.swift +0 -0
  44. /package/apple/Sources/{ExpoModulesMacros → ExpoModulesMacrosCore}/Support/TypeConformanceAssertion.swift +0 -0
@@ -0,0 +1,36 @@
1
+ import SwiftSyntax
2
+ import SwiftSyntaxBuilder
3
+
4
+ /// The `@unchecked Sendable` conformance that `@ExpoModule` and `@SharedObject` add to their class, or
5
+ /// `nil` when the class already conforms. The compiler leaves `Sendable` out of `protocols` when the
6
+ /// class already conforms, also through a superclass. The inheritance clause is checked too, so that
7
+ /// `expand`, which passes every declared conformance, gives the same result for a class that spells
8
+ /// out its own conformance.
9
+ ///
10
+ /// A module or shared object is called from more than one thread: `.withAsync` runs a function on
11
+ /// the concurrent thread pool, and native code calls events and closures from any thread. The class
12
+ /// can't be checked `Sendable`, because `Module` and `SharedObject` aren't `Sendable`, so
13
+ /// `@unchecked Sendable` is the only conformance it can have.
14
+ internal func uncheckedSendableExtension(
15
+ for type: some TypeSyntaxProtocol,
16
+ of declaration: some DeclGroupSyntax,
17
+ conformingTo protocols: [TypeSyntax]
18
+ ) -> ExtensionDeclSyntax? {
19
+ guard protocols.contains(where: { $0.trimmedDescription == "Sendable" }) else {
20
+ return nil
21
+ }
22
+ let inherited = declaration.inheritanceClause?.inheritedTypes ?? []
23
+ if inherited.contains(where: { isSendable($0.type) }) {
24
+ return nil
25
+ }
26
+ let conformance: DeclSyntax = "extension \(type.trimmed): @unchecked Sendable {}"
27
+ return conformance.cast(ExtensionDeclSyntax.self)
28
+ }
29
+
30
+ /// Whether an inherited type is `Sendable`, with or without attributes such as `@unchecked`.
31
+ private func isSendable(_ type: TypeSyntax) -> Bool {
32
+ if let attributed = type.as(AttributedTypeSyntax.self) {
33
+ return isSendable(attributed.baseType)
34
+ }
35
+ return baseIdentifier(of: type) == "Sendable"
36
+ }
@@ -0,0 +1,27 @@
1
+ import ExpoModulesCLI
2
+ import ExpoModulesMacrosCore
3
+
4
+ #if canImport(Darwin)
5
+ import Darwin
6
+ #elseif canImport(Glibc)
7
+ import Glibc
8
+ #elseif os(Windows)
9
+ import CRT
10
+ #endif
11
+
12
+ // The binary that this package ships as `ExpoModulesMacros`: the compiler plugin, which CocoaPods
13
+ // builds load, and the scanner CLI, which `expo-modules-autolinking` and `expo-modules-cli` run. The
14
+ // compiler always launches a plugin executable without arguments and speaks the plugin protocol over
15
+ // stdin, so any argument means a scanner invocation (`ExpoModulesMacros scan-modules <path>...`);
16
+ // with none, this starts the plugin server. The CLI gets `expandMacros`, so `expand` runs the macros
17
+ // of this executable.
18
+ let arguments = Array(CommandLine.arguments.dropFirst())
19
+ if arguments.isEmpty {
20
+ try runPluginServer()
21
+ } else {
22
+ let expander = MacroExpander(
23
+ attributeNames: Set(ProvidedMacro.all.map(\.name)),
24
+ expand: expandMacros
25
+ )
26
+ exit(ScannerCLI.run(arguments: arguments, expander: expander))
27
+ }
package/build/types.d.ts CHANGED
@@ -281,16 +281,24 @@ export interface ExportedSharedObject extends ExportedAccess, ExportedAvailabili
281
281
  * which every JS object inherits.
282
282
  */
283
283
  initializer?: ExportedInitializer;
284
- /** What the class inherits from `SharedRef<T>`, or absent when its superclass isn't `SharedRef`. */
284
+ /**
285
+ * What the class inherits from `SharedRef<T>`, directly or through another scanned `@SharedObject`
286
+ * class, or absent when it doesn't inherit `SharedRef`.
287
+ */
285
288
  sharedRef?: ExportedSharedRef;
289
+ /**
290
+ * The class's `@JS` members include the ones it inherits from scanned `@SharedObject` superclasses,
291
+ * the inherited ones first. An own member replaces an inherited one with the same JS name.
292
+ */
286
293
  functions: ExportedFunction[];
287
294
  properties: ExportedProperty[];
288
295
  events: ExportedEvent[];
289
296
  file: string;
290
297
  }
291
298
  /**
292
- * The `SharedRef<T>` superclass of a shared object. Only the superclass written on the class is read,
293
- * so a class that inherits `SharedRef` through another subclass has none.
299
+ * The `SharedRef<T>` superclass of a shared object. A class that inherits `SharedRef` through another
300
+ * `@SharedObject` class reports the `T` of that class. A class that inherits it through a class the
301
+ * scan doesn't report has none.
294
302
  */
295
303
  export interface ExportedSharedRef {
296
304
  /** The `T` in `SharedRef<T>`: the native type the object holds, which JS never sees. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-macros",
3
- "version": "0.27.1",
3
+ "version": "0.29.0",
4
4
  "description": "Swift macro plugin for Expo modules",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -26,9 +26,9 @@
26
26
  "typescript": "^5.4.0"
27
27
  },
28
28
  "optionalDependencies": {
29
- "@expo/modules-macros-linux-arm64": "0.27.1",
30
- "@expo/modules-macros-linux-x64": "0.27.1",
31
- "@expo/modules-macros-win32-arm64": "0.27.1",
32
- "@expo/modules-macros-win32-x64": "0.27.1"
29
+ "@expo/modules-macros-linux-arm64": "0.29.0",
30
+ "@expo/modules-macros-linux-x64": "0.29.0",
31
+ "@expo/modules-macros-win32-arm64": "0.29.0",
32
+ "@expo/modules-macros-win32-x64": "0.29.0"
33
33
  }
34
34
  }
@@ -1,66 +0,0 @@
1
- // swift-tools-version: 6.2
2
- // The swift-tools-version declares the minimum version of Swift required to build this package.
3
-
4
- import CompilerPluginSupport
5
- import Foundation
6
- import PackageDescription
7
-
8
- let package = Package(
9
- name: "ExpoModulesMacros",
10
- platforms: [.macOS(.v13)],
11
- dependencies: [
12
- .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "604.0.0-latest")
13
- ],
14
- targets: [
15
- // The plugin executable doubles as the scanner CLI: the compiler launches it without arguments
16
- // and speaks the plugin protocol over stdin, while an invocation with arguments dispatches into
17
- // `ScannerCLI` (see the entry point in Plugin.swift). Sharing the binary keeps the package to a
18
- // single shipped executable; the scanner adds little on top of the SwiftSyntax the macros
19
- // already link.
20
- .macro(
21
- name: "ExpoModulesMacros",
22
- dependencies: [
23
- "ExpoModulesCLI",
24
- .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
25
- .product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
26
- // For `expand`, which runs the macros in process.
27
- .product(name: "SwiftSyntaxMacroExpansion", package: "swift-syntax"),
28
- .product(name: "SwiftParser", package: "swift-syntax"),
29
- .product(name: "SwiftParserDiagnostics", package: "swift-syntax"),
30
- .product(name: "SwiftBasicFormat", package: "swift-syntax"),
31
- ]
32
- ),
33
- .target(
34
- name: "ExpoModulesCLI",
35
- dependencies: [
36
- .product(name: "SwiftSyntax", package: "swift-syntax"),
37
- .product(name: "SwiftParser", package: "swift-syntax"),
38
- .product(name: "SwiftIfConfig", package: "swift-syntax"),
39
- ]
40
- ),
41
- ]
42
- )
43
-
44
- // The Tests directory is excluded from the published npm package,
45
- // so only declare the test target when building from the repository.
46
- if FileManager.default.fileExists(atPath: Context.packageDirectory + "/Tests") {
47
- package.targets.append(
48
- .testTarget(
49
- name: "ExpoModulesMacrosTests",
50
- dependencies: [
51
- "ExpoModulesMacros",
52
- "ExpoModulesCLI",
53
- .product(name: "SwiftSyntaxMacrosGenericTestSupport", package: "swift-syntax"),
54
- ]
55
- )
56
- )
57
- package.targets.append(
58
- .testTarget(
59
- name: "ExpoModulesCLITests",
60
- dependencies: [
61
- "ExpoModulesCLI",
62
- .product(name: "SwiftParser", package: "swift-syntax"),
63
- ]
64
- )
65
- )
66
- }
@@ -1,36 +0,0 @@
1
- import ExpoModulesCLI
2
- import SwiftCompilerPlugin
3
- import SwiftSyntaxMacros
4
-
5
- #if canImport(Darwin)
6
- import Darwin
7
- #elseif canImport(Glibc)
8
- import Glibc
9
- #elseif os(Windows)
10
- import CRT
11
- #endif
12
-
13
- struct ExpoModulesMacrosPlugin: CompilerPlugin {
14
- let providingMacros: [Macro.Type] = ProvidedMacro.all.map(\.type)
15
- }
16
-
17
- /// The executable doubles as the scanner CLI. The compiler always launches a plugin executable
18
- /// without arguments and speaks the plugin protocol over stdin, so any argument means a scanner
19
- /// invocation (`ExpoModulesMacros scan-modules <path>...`); with none, this starts the plugin
20
- /// server exactly as `@main` on the `CompilerPlugin` type would. The CLI gets `expandMacros`, so
21
- /// `expand` runs the macros of this executable.
22
- @main
23
- enum EntryPoint {
24
- static func main() throws {
25
- let arguments = Array(CommandLine.arguments.dropFirst())
26
- if arguments.isEmpty {
27
- try ExpoModulesMacrosPlugin.main()
28
- } else {
29
- let expander = MacroExpander(
30
- attributeNames: Set(ProvidedMacro.all.map(\.name)),
31
- expand: expandMacros
32
- )
33
- exit(ScannerCLI.run(arguments: arguments, expander: expander))
34
- }
35
- }
36
- }
File without changes