expo-modules-macros 0.31.0 → 0.32.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.
- package/.github/workflows/{swift.yml → macos.yml} +12 -3
- package/AGENTS.md +2 -1
- package/README.md +5 -5
- package/apple/ExpoModulesMacros +0 -0
- package/apple/Sources/ExpoModulesMacrosCore/Macros/EventMacro.swift +12 -2
- package/apple/Sources/ExpoModulesMacrosCore/Macros/ExpoModuleMacro.swift +12 -0
- package/apple/Sources/ExpoModulesMacrosCore/Macros/JSMacro.swift +124 -0
- package/apple/Sources/ExpoModulesMacrosCore/Macros/RecordMacro.swift +68 -0
- package/apple/Sources/ExpoModulesMacrosCore/Macros/SharedObjectMacro.swift +79 -1
- package/apple/Sources/ExpoModulesMacrosCore/Macros/UnionMacro.swift +101 -5
- package/apple/Sources/ExpoModulesMacrosCore/Macros/ViewPropsMacro.swift +585 -23
- package/apple/Sources/ExpoModulesMacrosCore/Support/JSNameCollisions.swift +111 -2
- package/apple/Sources/ExpoModulesMacrosCore/Support/MacroHelpers.swift +13 -0
- package/package.json +5 -5
|
@@ -40,6 +40,94 @@ internal func diagnoseJSNameCollisions(
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/// The JavaScript object that a set of `@JS` members binds onto, for `diagnoseReservedJSNames`.
|
|
44
|
+
internal enum JSBindingTarget {
|
|
45
|
+
/// The module object of an `@ExpoModule` class.
|
|
46
|
+
case module
|
|
47
|
+
/// The prototype of a shared object class, which holds the instance members.
|
|
48
|
+
case prototype
|
|
49
|
+
/// The constructor of a shared object class, which holds the static members.
|
|
50
|
+
case constructor
|
|
51
|
+
|
|
52
|
+
/// The names that JavaScript already gives a meaning on this object, with what binding a member
|
|
53
|
+
/// under one would break.
|
|
54
|
+
var reservedNames: [String: String] {
|
|
55
|
+
let proto = "setting '__proto__' on an object replaces its prototype instead of adding a member"
|
|
56
|
+
switch self {
|
|
57
|
+
case .module:
|
|
58
|
+
return ["__proto__": proto]
|
|
59
|
+
case .prototype:
|
|
60
|
+
return [
|
|
61
|
+
"__proto__": proto,
|
|
62
|
+
"constructor": "a member named 'constructor' replaces the prototype's reference to the class",
|
|
63
|
+
]
|
|
64
|
+
case .constructor:
|
|
65
|
+
return [
|
|
66
|
+
"__proto__": proto,
|
|
67
|
+
"prototype": "a static member named 'prototype' replaces the prototype that holds the instance members",
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// Reports each `@JS` member whose JavaScript name JavaScript already gives a meaning on the object that
|
|
74
|
+
/// the member binds onto, such as `constructor` on a shared object's prototype.
|
|
75
|
+
internal func diagnoseReservedJSNames(
|
|
76
|
+
functions: [JSFunction],
|
|
77
|
+
properties: [JSProperty],
|
|
78
|
+
on target: JSBindingTarget,
|
|
79
|
+
in context: some MacroExpansionContext
|
|
80
|
+
) {
|
|
81
|
+
let members = functions.map(BoundMember.init(function:)) + properties.map(BoundMember.init(property:))
|
|
82
|
+
for member in members {
|
|
83
|
+
guard let reason = target.reservedNames[member.jsName] else {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
context.diagnose(
|
|
87
|
+
Diagnostic(
|
|
88
|
+
node: member.nameToken,
|
|
89
|
+
message: ReservedJSNameMessage(
|
|
90
|
+
"The JavaScript name '\(member.jsName)' can't be bound: \(reason). Give the \(member.kind) another name with \(member.renaming)."
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Reports each `@Event` property of a class body whose JavaScript event name an earlier `@Event`
|
|
98
|
+
/// property already uses. Both properties emit under that name, so a listener can't tell which one fired.
|
|
99
|
+
internal func diagnoseEventNameCollisions(in memberBlock: MemberBlockSyntax, in context: some MacroExpansionContext) {
|
|
100
|
+
var firstEvents: [String: TokenSyntax] = [:]
|
|
101
|
+
|
|
102
|
+
for member in memberBlock.members {
|
|
103
|
+
guard let varDecl = member.decl.as(VariableDeclSyntax.self),
|
|
104
|
+
let attribute = varDecl.attributes.firstAttribute(named: "Event"),
|
|
105
|
+
let jsName = eventJSName(of: varDecl, attribute: attribute),
|
|
106
|
+
let nameToken = varDecl.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier
|
|
107
|
+
else {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
guard let first = firstEvents[jsName] else {
|
|
111
|
+
firstEvents[jsName] = nameToken
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
context.diagnose(
|
|
115
|
+
Diagnostic(
|
|
116
|
+
node: nameToken,
|
|
117
|
+
message: EventNameCollisionMessage(
|
|
118
|
+
"The event name '\(jsName)' is already used by the earlier @Event '\(first.text)'. Both properties emit the same event, so a JavaScript listener can't tell them apart. Give one of them another name with @Event(\"...\")."
|
|
119
|
+
),
|
|
120
|
+
notes: [
|
|
121
|
+
Note(
|
|
122
|
+
node: Syntax(first),
|
|
123
|
+
message: JSNameCollisionNote("'\(jsName)' is first used here", id: "event-name-collision-first")
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
43
131
|
private struct BoundMember {
|
|
44
132
|
let jsName: String
|
|
45
133
|
/// How the message names the member, such as `@JS function`.
|
|
@@ -74,11 +162,32 @@ private struct JSNameCollisionMessage: DiagnosticMessage {
|
|
|
74
162
|
}
|
|
75
163
|
}
|
|
76
164
|
|
|
77
|
-
private struct
|
|
165
|
+
private struct ReservedJSNameMessage: DiagnosticMessage {
|
|
166
|
+
let message: String
|
|
167
|
+
let diagnosticID = MessageID(domain: "ExpoModulesMacros", id: "js-reserved-name")
|
|
168
|
+
let severity: DiagnosticSeverity = .error
|
|
169
|
+
|
|
170
|
+
init(_ message: String) {
|
|
171
|
+
self.message = message
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private struct EventNameCollisionMessage: DiagnosticMessage {
|
|
78
176
|
let message: String
|
|
79
|
-
let
|
|
177
|
+
let diagnosticID = MessageID(domain: "ExpoModulesMacros", id: "event-name-collision")
|
|
178
|
+
let severity: DiagnosticSeverity = .warning
|
|
80
179
|
|
|
81
180
|
init(_ message: String) {
|
|
82
181
|
self.message = message
|
|
83
182
|
}
|
|
84
183
|
}
|
|
184
|
+
|
|
185
|
+
private struct JSNameCollisionNote: NoteMessage {
|
|
186
|
+
let message: String
|
|
187
|
+
let noteID: MessageID
|
|
188
|
+
|
|
189
|
+
init(_ message: String, id: String = "js-name-collision-first") {
|
|
190
|
+
self.message = message
|
|
191
|
+
self.noteID = MessageID(domain: "ExpoModulesMacros", id: id)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -349,6 +349,19 @@ internal func memberHasJSAttribute(_ decl: DeclSyntaxProtocol) -> Bool {
|
|
|
349
349
|
return false
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
/// The generic parameter clause of a `@JS` function or initializer, or `nil` when it has none. The
|
|
353
|
+
/// binding decodes each argument by its static type, so a generic member can't be bound: `@JS` reports
|
|
354
|
+
/// it, and `@ExpoModule` and `@SharedObject` leave it out.
|
|
355
|
+
internal func jsGenericParameterClause(of decl: DeclSyntaxProtocol) -> GenericParameterClauseSyntax? {
|
|
356
|
+
if let funcDecl = decl.as(FunctionDeclSyntax.self) {
|
|
357
|
+
return funcDecl.genericParameterClause
|
|
358
|
+
}
|
|
359
|
+
if let initDecl = decl.as(InitializerDeclSyntax.self) {
|
|
360
|
+
return initDecl.genericParameterClause
|
|
361
|
+
}
|
|
362
|
+
return nil
|
|
363
|
+
}
|
|
364
|
+
|
|
352
365
|
/// Decides whether the macro should stamp `@JavaScriptActor` on a `@JS`-marked member.
|
|
353
366
|
/// The macro defers to the user when they've already chosen an isolation:
|
|
354
367
|
/// - the member opted out with `@JS(.concurrent)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-macros",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.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.
|
|
30
|
-
"@expo/modules-macros-linux-x64": "0.
|
|
31
|
-
"@expo/modules-macros-win32-arm64": "0.
|
|
32
|
-
"@expo/modules-macros-win32-x64": "0.
|
|
29
|
+
"@expo/modules-macros-linux-arm64": "0.32.0",
|
|
30
|
+
"@expo/modules-macros-linux-x64": "0.32.0",
|
|
31
|
+
"@expo/modules-macros-win32-arm64": "0.32.0",
|
|
32
|
+
"@expo/modules-macros-win32-x64": "0.32.0"
|
|
33
33
|
}
|
|
34
34
|
}
|