expo-modules-macros 0.20.0 → 0.22.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/linux.yml +132 -0
- package/.github/workflows/publish.yml +160 -11
- package/.github/workflows/swift-format.yml +76 -0
- package/.github/workflows/windows.yml +3 -28
- package/AGENTS.md +19 -9
- package/README.md +27 -8
- package/apple/ExpoModulesMacros +0 -0
- package/apple/Package.resolved +3 -3
- package/apple/Package.swift +1 -2
- package/apple/Sources/ExpoModulesMacros/ClosureArguments.swift +39 -14
- package/apple/Sources/ExpoModulesMacros/DecorateModuleBuilder.swift +117 -25
- package/apple/Sources/ExpoModulesMacros/EventMacro.swift +29 -15
- package/apple/Sources/ExpoModulesMacros/ExpoModuleMacro.swift +55 -45
- package/apple/Sources/ExpoModulesMacros/ExpoModulesOptimizedMacro.swift +2 -1
- package/apple/Sources/ExpoModulesMacros/FreeFormTypes.swift +2 -61
- package/apple/Sources/ExpoModulesMacros/JSConstructor.swift +6 -3
- package/apple/Sources/ExpoModulesMacros/JSMacro.swift +180 -44
- package/apple/Sources/ExpoModulesMacros/JSNameCollisions.swift +84 -0
- package/apple/Sources/ExpoModulesMacros/MacroHelpers.swift +165 -43
- package/apple/Sources/ExpoModulesMacros/RecordMacro.swift +358 -234
- package/apple/Sources/ExpoModulesMacros/SharedObjectMacro.swift +48 -32
- package/apple/Sources/ExpoModulesMacros/TypeConformanceAssertion.swift +57 -27
- package/apple/Sources/ExpoModulesMacros/UnionMacro.swift +23 -9
- package/apple/Sources/ExpoModulesMacros/ViewPropsMacro.swift +10 -5
- package/apple/Sources/ExpoModulesScanner/CLI.swift +3 -1
- package/apple/Sources/ExpoModulesScanner/Core/DetectionVisitor.swift +11 -7
- package/apple/Sources/ExpoModulesScanner/Core/ScanBuildConfiguration.swift +2 -1
- package/apple/Sources/ExpoModulesScanner/Core/SourceScan.swift +10 -7
- package/apple/Sources/ExpoModulesScanner/Exports/Availability.swift +6 -3
- package/apple/Sources/ExpoModulesScanner/Exports/ExportedSurface.swift +3 -2
- package/apple/Sources/ExpoModulesScanner/Exports/ResolveRefs.swift +15 -5
- package/apple/Sources/ExpoModulesScanner/Exports/ScanExports.swift +8 -3
- package/apple/Sources/ExpoModulesScanner/Exports/SurfaceVisitor.swift +127 -45
- package/apple/Sources/ExpoModulesScanner/Exports/TypeNode.swift +2 -1
- package/apple/Sources/ExpoModulesScanner/Modules/ScanModules.swift +8 -2
- package/build/index.d.ts +4 -3
- package/build/index.js +33 -18
- package/build/types.d.ts +7 -1
- package/package.json +7 -3
|
@@ -100,7 +100,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
100
100
|
accessLevel: spelledAccessLevel(of: node.modifiers),
|
|
101
101
|
platforms: platforms,
|
|
102
102
|
file: file
|
|
103
|
-
)
|
|
103
|
+
)
|
|
104
|
+
)
|
|
104
105
|
} else if inherits(from: enumerableConformanceName, in: node.inheritanceClause) {
|
|
105
106
|
let rawType = rawValueType(of: node.inheritanceClause)
|
|
106
107
|
enums.append(
|
|
@@ -111,7 +112,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
111
112
|
accessLevel: spelledAccessLevel(of: node.modifiers),
|
|
112
113
|
platforms: platforms,
|
|
113
114
|
file: file
|
|
114
|
-
)
|
|
115
|
+
)
|
|
116
|
+
)
|
|
115
117
|
}
|
|
116
118
|
// An enum is often a namespace for other types, so its body is searched too.
|
|
117
119
|
return .visitChildren
|
|
@@ -156,7 +158,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
156
158
|
accessLevel: accessLevel,
|
|
157
159
|
platforms: platforms,
|
|
158
160
|
file: file
|
|
159
|
-
)
|
|
161
|
+
)
|
|
162
|
+
)
|
|
160
163
|
return
|
|
161
164
|
}
|
|
162
165
|
|
|
@@ -173,7 +176,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
173
176
|
accessLevel: accessLevel,
|
|
174
177
|
platforms: platforms,
|
|
175
178
|
file: file
|
|
176
|
-
)
|
|
179
|
+
)
|
|
180
|
+
)
|
|
177
181
|
return
|
|
178
182
|
}
|
|
179
183
|
|
|
@@ -185,7 +189,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
185
189
|
accessLevel: accessLevel,
|
|
186
190
|
platforms: platforms,
|
|
187
191
|
file: file
|
|
188
|
-
)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
|
|
@@ -214,7 +219,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
214
219
|
|
|
215
220
|
if let varDecl = decl.as(VariableDeclSyntax.self),
|
|
216
221
|
varDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue) == nil,
|
|
217
|
-
let attribute = varDecl.attributes.firstAttribute(named: DetectedMacro.event.rawValue)
|
|
222
|
+
let attribute = varDecl.attributes.firstAttribute(named: DetectedMacro.event.rawValue)
|
|
223
|
+
{
|
|
218
224
|
events.append(contentsOf: makeEvents(varDecl: varDecl, attribute: attribute, platforms: platforms))
|
|
219
225
|
continue
|
|
220
226
|
}
|
|
@@ -224,7 +230,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
if let initDecl = decl.as(InitializerDeclSyntax.self),
|
|
227
|
-
initDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue) != nil
|
|
233
|
+
initDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue) != nil
|
|
234
|
+
{
|
|
228
235
|
// At most one `@JS init`; keep the first if a malformed source has more (the macro errors).
|
|
229
236
|
if initializer == nil {
|
|
230
237
|
initializer = ExportedInitializer(
|
|
@@ -237,13 +244,15 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
if let funcDecl = decl.as(FunctionDeclSyntax.self),
|
|
240
|
-
let attribute = funcDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue)
|
|
241
|
-
|
|
247
|
+
let attribute = funcDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue)
|
|
248
|
+
{
|
|
249
|
+
functions.append(contentsOf: makeFunctions(funcDecl: funcDecl, attribute: attribute, platforms: platforms))
|
|
242
250
|
continue
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
if let varDecl = decl.as(VariableDeclSyntax.self),
|
|
246
|
-
let attribute = varDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue)
|
|
254
|
+
let attribute = varDecl.attributes.firstAttribute(named: DetectedMacro.js.rawValue)
|
|
255
|
+
{
|
|
247
256
|
properties.append(contentsOf: makeProperties(varDecl: varDecl, attribute: attribute, platforms: platforms))
|
|
248
257
|
}
|
|
249
258
|
}
|
|
@@ -256,10 +265,13 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
256
265
|
/// reporting one would describe a surface that does not exist. `@JS` on the same property is
|
|
257
266
|
/// caught by the caller, which reaches the `@JS` branch first.
|
|
258
267
|
private func makeEvents(
|
|
259
|
-
varDecl: VariableDeclSyntax,
|
|
268
|
+
varDecl: VariableDeclSyntax,
|
|
269
|
+
attribute: AttributeSyntax,
|
|
270
|
+
platforms: [ExportedPlatform]
|
|
260
271
|
) -> [ExportedEvent] {
|
|
261
272
|
guard varDecl.bindingSpecifier.tokenKind != .keyword(.let),
|
|
262
|
-
!isTypeLevel(varDecl.modifiers)
|
|
273
|
+
!isTypeLevel(varDecl.modifiers)
|
|
274
|
+
else {
|
|
263
275
|
return []
|
|
264
276
|
}
|
|
265
277
|
let override = stringArgument(of: attribute)
|
|
@@ -274,7 +286,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
274
286
|
binding.accessorBlock == nil,
|
|
275
287
|
let functionType = underlyingFunctionType(of: binding.typeAnnotation?.type),
|
|
276
288
|
isVoidEventReturn(functionType.returnClause.type),
|
|
277
|
-
functionType.parameters.count <= 1
|
|
289
|
+
functionType.parameters.count <= 1
|
|
290
|
+
else {
|
|
278
291
|
continue
|
|
279
292
|
}
|
|
280
293
|
let name = ident.identifier.identifierName
|
|
@@ -286,36 +299,53 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
286
299
|
isSync: isSync,
|
|
287
300
|
accessLevel: spelledAccessLevel(of: varDecl.modifiers),
|
|
288
301
|
platforms: platforms
|
|
289
|
-
)
|
|
302
|
+
)
|
|
303
|
+
)
|
|
290
304
|
}
|
|
291
305
|
return result
|
|
292
306
|
}
|
|
293
307
|
|
|
294
|
-
/// Builds
|
|
295
|
-
/// `nil`, and the effect/static flags.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
308
|
+
/// Builds the `ExportedFunction` entries for a `@JS func`: JS-name fallback, parameters, a `Void`
|
|
309
|
+
/// return as `nil`, and the effect/static flags. A synchronous function with `.withAsync("name")`
|
|
310
|
+
/// gets a second, `async` entry under that name, for the promise-returning variant the macro binds.
|
|
311
|
+
private func makeFunctions(
|
|
312
|
+
funcDecl: FunctionDeclSyntax,
|
|
313
|
+
attribute: AttributeSyntax,
|
|
314
|
+
platforms: [ExportedPlatform]
|
|
315
|
+
) -> [ExportedFunction] {
|
|
299
316
|
let effects = funcDecl.signature.effectSpecifiers
|
|
300
317
|
let returnType = funcDecl.signature.returnClause?.type
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
318
|
+
let isAsync = effects?.asyncSpecifier != nil
|
|
319
|
+
let exportedParameters = parameters(of: funcDecl.signature.parameterClause)
|
|
320
|
+
let returns = isVoidType(returnType) ? nil : returnType.map { typeNode(from: $0) }
|
|
321
|
+
let makeFunction = { (jsName: String, isAsync: Bool) in
|
|
322
|
+
ExportedFunction(
|
|
323
|
+
name: funcDecl.name.identifierName,
|
|
324
|
+
jsName: jsName,
|
|
325
|
+
parameters: exportedParameters,
|
|
326
|
+
returns: returns,
|
|
327
|
+
isAsync: isAsync,
|
|
328
|
+
isThrowing: effects?.throwsClause?.throwsSpecifier != nil,
|
|
329
|
+
isStatic: isTypeLevel(funcDecl.modifiers),
|
|
330
|
+
accessLevel: spelledAccessLevel(of: funcDecl.modifiers),
|
|
331
|
+
platforms: platforms
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
let function = makeFunction(stringArgument(of: attribute) ?? funcDecl.name.identifierName, isAsync)
|
|
335
|
+
// The macro binds no variant for an `async` function: it reports the option there instead.
|
|
336
|
+
guard !isAsync, let variantName = asyncVariantName(of: attribute) else {
|
|
337
|
+
return [function]
|
|
338
|
+
}
|
|
339
|
+
return [function, makeFunction(variantName, true)]
|
|
312
340
|
}
|
|
313
341
|
|
|
314
342
|
/// Builds the `ExportedProperty` entries for a `@JS var`/`let`. One declaration can introduce several
|
|
315
343
|
/// bindings (`var a, b: Int`), so this returns an array. The value type is the annotation, else the
|
|
316
344
|
/// literal default's inferred type, else `nil`.
|
|
317
345
|
private func makeProperties(
|
|
318
|
-
varDecl: VariableDeclSyntax,
|
|
346
|
+
varDecl: VariableDeclSyntax,
|
|
347
|
+
attribute: AttributeSyntax,
|
|
348
|
+
platforms: [ExportedPlatform]
|
|
319
349
|
) -> [ExportedProperty] {
|
|
320
350
|
let isLet = varDecl.bindingSpecifier.tokenKind == .keyword(.let)
|
|
321
351
|
let isStatic = isTypeLevel(varDecl.modifiers)
|
|
@@ -339,7 +369,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
339
369
|
defaultValue: binding.initializer?.value.trimmedDescription,
|
|
340
370
|
accessLevel: spelledAccessLevel(of: varDecl.modifiers),
|
|
341
371
|
platforms: platforms
|
|
342
|
-
)
|
|
372
|
+
)
|
|
373
|
+
)
|
|
343
374
|
}
|
|
344
375
|
return result
|
|
345
376
|
}
|
|
@@ -380,7 +411,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
380
411
|
// `@Record` reads only the properties written directly in the body, so `#if` is not looked into.
|
|
381
412
|
for member in members {
|
|
382
413
|
guard let varDecl = member.decl.as(VariableDeclSyntax.self),
|
|
383
|
-
!isExcludedRecordModifier(varDecl.modifiers)
|
|
414
|
+
!isExcludedRecordModifier(varDecl.modifiers)
|
|
415
|
+
else {
|
|
384
416
|
continue
|
|
385
417
|
}
|
|
386
418
|
let platforms = platformResolver.platforms(of: varDecl)
|
|
@@ -406,7 +438,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
406
438
|
defaultValue: binding.initializer?.value.trimmedDescription,
|
|
407
439
|
accessLevel: spelledAccessLevel(of: varDecl.modifiers),
|
|
408
440
|
platforms: platforms
|
|
409
|
-
)
|
|
441
|
+
)
|
|
442
|
+
)
|
|
410
443
|
}
|
|
411
444
|
}
|
|
412
445
|
return properties
|
|
@@ -440,7 +473,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
440
473
|
name: name,
|
|
441
474
|
rawValue: writtenRawValue(of: element) ?? derivedStringRawValue(for: name, rawType: rawType),
|
|
442
475
|
platforms: platforms
|
|
443
|
-
)
|
|
476
|
+
)
|
|
477
|
+
)
|
|
444
478
|
}
|
|
445
479
|
}
|
|
446
480
|
return cases
|
|
@@ -467,12 +501,17 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
467
501
|
for element in caseDecl.elements {
|
|
468
502
|
guard let parameters = element.parameterClause?.parameters,
|
|
469
503
|
parameters.count == 1, let parameter = parameters.first,
|
|
470
|
-
parameter.defaultValue == nil
|
|
504
|
+
parameter.defaultValue == nil
|
|
505
|
+
else {
|
|
471
506
|
continue
|
|
472
507
|
}
|
|
473
508
|
result.append(
|
|
474
509
|
ExportedUnionMember(
|
|
475
|
-
name: element.name.identifierName,
|
|
510
|
+
name: element.name.identifierName,
|
|
511
|
+
type: typeNode(from: parameter.type),
|
|
512
|
+
platforms: platforms
|
|
513
|
+
)
|
|
514
|
+
)
|
|
476
515
|
}
|
|
477
516
|
}
|
|
478
517
|
return result
|
|
@@ -508,7 +547,8 @@ final class SurfaceVisitor: SyntaxVisitor {
|
|
|
508
547
|
}
|
|
509
548
|
guard let item = node.parent?.as(MemberBlockItemSyntax.self),
|
|
510
549
|
let parent = declaration(owning: item),
|
|
511
|
-
let scope = enclosingTypes(of: parent)
|
|
550
|
+
let scope = enclosingTypes(of: parent)
|
|
551
|
+
else {
|
|
512
552
|
return nil
|
|
513
553
|
}
|
|
514
554
|
if let decl = parent.as(ExtensionDeclSyntax.self) {
|
|
@@ -633,7 +673,8 @@ private func underlyingFunctionType(of type: TypeSyntax?) -> FunctionTypeSyntax?
|
|
|
633
673
|
return underlyingFunctionType(of: attributed.baseType)
|
|
634
674
|
}
|
|
635
675
|
if let tuple = type.as(TupleTypeSyntax.self),
|
|
636
|
-
tuple.elements.count == 1, let element = tuple.elements.first, element.firstName == nil
|
|
676
|
+
tuple.elements.count == 1, let element = tuple.elements.first, element.firstName == nil
|
|
677
|
+
{
|
|
637
678
|
return underlyingFunctionType(of: element.type)
|
|
638
679
|
}
|
|
639
680
|
return type.as(FunctionTypeSyntax.self)
|
|
@@ -643,7 +684,8 @@ private func underlyingFunctionType(of type: TypeSyntax?) -> FunctionTypeSyntax?
|
|
|
643
684
|
/// accepts neither `Swift.Void` nor a parenthesized `(Void)`, so it would drop valid events.
|
|
644
685
|
private func isVoidEventReturn(_ type: TypeSyntax) -> Bool {
|
|
645
686
|
if let tuple = type.as(TupleTypeSyntax.self), tuple.elements.count == 1,
|
|
646
|
-
let element = tuple.elements.first, element.firstName == nil
|
|
687
|
+
let element = tuple.elements.first, element.firstName == nil
|
|
688
|
+
{
|
|
647
689
|
return isVoidEventReturn(element.type)
|
|
648
690
|
}
|
|
649
691
|
let text = type.trimmedDescription
|
|
@@ -664,6 +706,41 @@ private func boolArgument(of attribute: AttributeSyntax, label: String) -> Bool?
|
|
|
664
706
|
return nil
|
|
665
707
|
}
|
|
666
708
|
|
|
709
|
+
/// The name in a `@JS` attribute's `.withAsync("name")` option, in the arguments or inside an array
|
|
710
|
+
/// literal (`@JS("load", [.withAsync("loadAsync")])`), or `nil` when the option is absent or its
|
|
711
|
+
/// argument isn't a plain string literal. Mirrors `asyncVariantOption` and `asyncVariantName` in the
|
|
712
|
+
/// macros.
|
|
713
|
+
private func asyncVariantName(of attribute: AttributeSyntax) -> String? {
|
|
714
|
+
guard let arguments = attribute.arguments?.as(LabeledExprListSyntax.self) else {
|
|
715
|
+
return nil
|
|
716
|
+
}
|
|
717
|
+
let expressions = arguments.flatMap { argument -> [ExprSyntax] in
|
|
718
|
+
if let array = argument.expression.as(ArrayExprSyntax.self) {
|
|
719
|
+
return array.elements.map(\.expression)
|
|
720
|
+
}
|
|
721
|
+
return [argument.expression]
|
|
722
|
+
}
|
|
723
|
+
for expression in expressions {
|
|
724
|
+
guard let call = expression.as(FunctionCallExprSyntax.self),
|
|
725
|
+
let member = call.calledExpression.as(MemberAccessExprSyntax.self),
|
|
726
|
+
member.declName.baseName.text == "withAsync"
|
|
727
|
+
else {
|
|
728
|
+
continue
|
|
729
|
+
}
|
|
730
|
+
guard call.arguments.count == 1,
|
|
731
|
+
let argument = call.arguments.first,
|
|
732
|
+
argument.label == nil,
|
|
733
|
+
let literal = argument.expression.as(StringLiteralExprSyntax.self),
|
|
734
|
+
literal.segments.count == 1,
|
|
735
|
+
let segment = literal.segments.first?.as(StringSegmentSyntax.self)
|
|
736
|
+
else {
|
|
737
|
+
return nil
|
|
738
|
+
}
|
|
739
|
+
return segment.content.text
|
|
740
|
+
}
|
|
741
|
+
return nil
|
|
742
|
+
}
|
|
743
|
+
|
|
667
744
|
private func isTypeLevel(_ modifiers: DeclModifierListSyntax) -> Bool {
|
|
668
745
|
modifiers.contains {
|
|
669
746
|
$0.name.tokenKind == .keyword(.static) || $0.name.tokenKind == .keyword(.class)
|
|
@@ -721,7 +798,8 @@ private func writtenRawValue(of element: EnumCaseElementSyntax) -> String? {
|
|
|
721
798
|
return value.trimmedDescription
|
|
722
799
|
}
|
|
723
800
|
guard literal.segments.count == 1,
|
|
724
|
-
let segment = literal.segments.first?.as(StringSegmentSyntax.self)
|
|
801
|
+
let segment = literal.segments.first?.as(StringSegmentSyntax.self)
|
|
802
|
+
else {
|
|
725
803
|
return nil
|
|
726
804
|
}
|
|
727
805
|
// The segment's text is the literal's content with its delimiters already stripped, so a plain
|
|
@@ -777,7 +855,8 @@ private let knownNonRawValueProtocols: Set<String> = [
|
|
|
777
855
|
func rawValueType(of clause: InheritanceClauseSyntax?) -> TypeNode? {
|
|
778
856
|
guard let first = clause?.inheritedTypes.first?.type,
|
|
779
857
|
let trailing = first.trimmedDescription.split(separator: ".").last.map(String.init),
|
|
780
|
-
!knownNonRawValueProtocols.contains(trailing)
|
|
858
|
+
!knownNonRawValueProtocols.contains(trailing)
|
|
859
|
+
else {
|
|
781
860
|
return nil
|
|
782
861
|
}
|
|
783
862
|
return typeNode(from: first)
|
|
@@ -801,7 +880,8 @@ extension AttributeListSyntax {
|
|
|
801
880
|
fileprivate func firstAttribute(named name: String) -> AttributeSyntax? {
|
|
802
881
|
for element in self {
|
|
803
882
|
if let attribute = element.as(AttributeSyntax.self),
|
|
804
|
-
attribute.attributeName.trimmedDescription == name
|
|
883
|
+
attribute.attributeName.trimmedDescription == name
|
|
884
|
+
{
|
|
805
885
|
return attribute
|
|
806
886
|
}
|
|
807
887
|
}
|
|
@@ -814,14 +894,16 @@ extension AttributeListSyntax {
|
|
|
814
894
|
/// an array literal. A local copy of the macros' helper (the macro target can't be imported here).
|
|
815
895
|
private func classListArgument(of attribute: AttributeSyntax, label: String) -> [String] {
|
|
816
896
|
guard let args = attribute.arguments?.as(LabeledExprListSyntax.self),
|
|
817
|
-
let array = args.first(where: { $0.label?.text == label })?.expression.as(ArrayExprSyntax.self)
|
|
897
|
+
let array = args.first(where: { $0.label?.text == label })?.expression.as(ArrayExprSyntax.self)
|
|
898
|
+
else {
|
|
818
899
|
return []
|
|
819
900
|
}
|
|
820
901
|
return array.elements.compactMap { element in
|
|
821
902
|
guard let memberAccess = element.expression.as(MemberAccessExprSyntax.self),
|
|
822
903
|
memberAccess.declName.baseName.text == "self",
|
|
823
904
|
let base = memberAccess.base,
|
|
824
|
-
base.is(DeclReferenceExprSyntax.self) || base.is(MemberAccessExprSyntax.self)
|
|
905
|
+
base.is(DeclReferenceExprSyntax.self) || base.is(MemberAccessExprSyntax.self)
|
|
906
|
+
else {
|
|
825
907
|
return nil
|
|
826
908
|
}
|
|
827
909
|
return base.trimmedDescription
|
|
@@ -201,7 +201,8 @@ func typeNode(from type: TypeSyntax) -> TypeNode {
|
|
|
201
201
|
}
|
|
202
202
|
// `(T)`: one unlabeled element in parentheses is the same type as `T`, as in `((Int) -> Void)?`.
|
|
203
203
|
if let tuple = type.as(TupleTypeSyntax.self), tuple.elements.count == 1,
|
|
204
|
-
let element = tuple.elements.first, element.firstName == nil
|
|
204
|
+
let element = tuple.elements.first, element.firstName == nil
|
|
205
|
+
{
|
|
205
206
|
return typeNode(from: element.type)
|
|
206
207
|
}
|
|
207
208
|
// A nominal type, possibly generic: `Int`, `Point`, `Optional<T>`, `Array<T>`, `Promise<T>`.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import SwiftParser
|
|
2
|
+
|
|
1
3
|
#if canImport(FoundationEssentials)
|
|
2
4
|
import FoundationEssentials
|
|
3
5
|
#else
|
|
4
6
|
import Foundation
|
|
5
7
|
#endif
|
|
6
|
-
import SwiftParser
|
|
7
8
|
|
|
8
9
|
/// The scanner's public entry point. Argument parsing, subcommand dispatch, and usage text live in
|
|
9
10
|
/// the CLI target; this just runs a command and writes its JSON report to stdout.
|
|
@@ -122,7 +123,12 @@ func scanModules(paths: [String], defines: Set<String> = []) -> ScanModulesResul
|
|
|
122
123
|
var platformsByDetection: [String: [String]] = [:]
|
|
123
124
|
for platform in platformUniverse {
|
|
124
125
|
let configuration = ScanBuildConfiguration(platform: platform.condition, defines: defines)
|
|
125
|
-
let visitor = DetectionVisitor(
|
|
126
|
+
let visitor = DetectionVisitor(
|
|
127
|
+
file: file,
|
|
128
|
+
tree: tree,
|
|
129
|
+
detectedMacros: [.expoModule],
|
|
130
|
+
configuration: configuration
|
|
131
|
+
)
|
|
126
132
|
visitor.walk(tree)
|
|
127
133
|
for detection in visitor.detections {
|
|
128
134
|
platformsByDetection[detectionKey(detection), default: []].append(platform.reported)
|
package/build/index.d.ts
CHANGED
|
@@ -41,9 +41,10 @@ export declare class ScannerSchemaVersionError extends Error {
|
|
|
41
41
|
*
|
|
42
42
|
* - macOS: the universal binary shipped in this package, next to the Swift package rather than in a
|
|
43
43
|
* `bin` directory.
|
|
44
|
-
* - Windows: `ExpoModulesMacros.exe` from the package for the
|
|
45
|
-
* `
|
|
46
|
-
* it falls back to the executable that `npm run build`
|
|
44
|
+
* - Windows and Linux: `ExpoModulesMacros.exe` or `ExpoModulesMacros` from the package for the
|
|
45
|
+
* current platform and architecture (see `platformBinaryPackageName`). When that package isn't
|
|
46
|
+
* installed, for example in this repository, it falls back to the executable that `npm run build`
|
|
47
|
+
* writes to `platforms/<platform>-<arch>`.
|
|
47
48
|
*
|
|
48
49
|
* The path isn't checked here; running a missing binary fails with a `ScannerError` that says how to
|
|
49
50
|
* get it.
|
package/build/index.js
CHANGED
|
@@ -51,12 +51,21 @@ class ScannerSchemaVersionError extends Error {
|
|
|
51
51
|
}
|
|
52
52
|
exports.ScannerSchemaVersionError = ScannerSchemaVersionError;
|
|
53
53
|
/**
|
|
54
|
-
* The
|
|
55
|
-
*
|
|
56
|
-
* `cpu` fields make package managers install it only on a matching Windows machine.
|
|
54
|
+
* The platforms whose binaries ship in packages of their own, one per architecture, with the
|
|
55
|
+
* machines that those packages install on.
|
|
57
56
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const platformPackageMachines = {
|
|
58
|
+
win32: 'Windows x64 and arm64',
|
|
59
|
+
linux: 'Linux x64 and arm64 with glibc',
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The package with the binary for the current platform and architecture, for example
|
|
63
|
+
* `@expo/modules-macros-win32-x64` or `@expo/modules-macros-linux-arm64`. It is an optional
|
|
64
|
+
* dependency of this package, and its `os`, `cpu` and (on Linux) `libc` fields make package managers
|
|
65
|
+
* install it only on a matching machine.
|
|
66
|
+
*/
|
|
67
|
+
function platformBinaryPackageName() {
|
|
68
|
+
return `@expo/modules-macros-${process.platform}-${process.arch}`;
|
|
60
69
|
}
|
|
61
70
|
/**
|
|
62
71
|
* Absolute path to the scanner binary for the current platform. It doubles as the macro plugin
|
|
@@ -64,34 +73,40 @@ function windowsBinaryPackageName() {
|
|
|
64
73
|
*
|
|
65
74
|
* - macOS: the universal binary shipped in this package, next to the Swift package rather than in a
|
|
66
75
|
* `bin` directory.
|
|
67
|
-
* - Windows: `ExpoModulesMacros.exe` from the package for the
|
|
68
|
-
* `
|
|
69
|
-
* it falls back to the executable that `npm run build`
|
|
76
|
+
* - Windows and Linux: `ExpoModulesMacros.exe` or `ExpoModulesMacros` from the package for the
|
|
77
|
+
* current platform and architecture (see `platformBinaryPackageName`). When that package isn't
|
|
78
|
+
* installed, for example in this repository, it falls back to the executable that `npm run build`
|
|
79
|
+
* writes to `platforms/<platform>-<arch>`.
|
|
70
80
|
*
|
|
71
81
|
* The path isn't checked here; running a missing binary fails with a `ScannerError` that says how to
|
|
72
82
|
* get it.
|
|
73
83
|
*/
|
|
74
84
|
function getScannerBinaryPath() {
|
|
75
|
-
if (process.platform
|
|
85
|
+
if (process.platform === 'darwin') {
|
|
76
86
|
return node_path_1.default.join(__dirname, '..', 'apple', 'ExpoModulesMacros');
|
|
77
87
|
}
|
|
88
|
+
const fileName = process.platform === 'win32' ? 'ExpoModulesMacros.exe' : 'ExpoModulesMacros';
|
|
78
89
|
try {
|
|
79
|
-
const packageJson = require.resolve(`${
|
|
80
|
-
return node_path_1.default.join(node_path_1.default.dirname(packageJson),
|
|
90
|
+
const packageJson = require.resolve(`${platformBinaryPackageName()}/package.json`);
|
|
91
|
+
return node_path_1.default.join(node_path_1.default.dirname(packageJson), fileName);
|
|
81
92
|
}
|
|
82
93
|
catch {
|
|
83
|
-
return node_path_1.default.join(__dirname, '..', 'platforms',
|
|
94
|
+
return node_path_1.default.join(__dirname, '..', 'platforms', `${process.platform}-${process.arch}`, fileName);
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
/** The message for a scanner binary that doesn't exist at `binaryPath`. */
|
|
87
98
|
function missingBinaryMessage(binaryPath) {
|
|
88
|
-
if (process.platform === '
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
if (process.platform === 'darwin') {
|
|
100
|
+
return `The scanner binary is missing at ${binaryPath}. Run \`npm run build\` in this package to build it.`;
|
|
101
|
+
}
|
|
102
|
+
const machines = platformPackageMachines[process.platform];
|
|
103
|
+
if (machines === undefined) {
|
|
104
|
+
return `The scanner binary is missing at ${binaryPath}. No binary is built for ${process.platform}.`;
|
|
93
105
|
}
|
|
94
|
-
return `The scanner binary is missing at ${binaryPath}.
|
|
106
|
+
return (`The scanner binary is missing at ${binaryPath}. It comes from the optional package ` +
|
|
107
|
+
`${platformBinaryPackageName()}, which your package manager installs only on ${machines}. ` +
|
|
108
|
+
'Reinstall dependencies without skipping optional ones (for example without `--omit=optional`), ' +
|
|
109
|
+
'or run `npm run build` in this package to build it locally.');
|
|
95
110
|
}
|
|
96
111
|
/**
|
|
97
112
|
* Runs a scanner subcommand and parses its JSON report.
|
package/build/types.d.ts
CHANGED
|
@@ -139,8 +139,14 @@ export interface ExportedParameter {
|
|
|
139
139
|
}
|
|
140
140
|
/** One `@JS func` on a module or shared object. */
|
|
141
141
|
export interface ExportedFunction extends ExportedAccess, ExportedAvailability {
|
|
142
|
+
/**
|
|
143
|
+
* The Swift declaration name. A function with `.withAsync("x")` has two entries with this name.
|
|
144
|
+
*/
|
|
142
145
|
name: string;
|
|
143
|
-
/**
|
|
146
|
+
/**
|
|
147
|
+
* The JS name it binds under: the `@JS("x")` override, else `name`. The second entry of a function
|
|
148
|
+
* with `.withAsync("x")` binds under `x`.
|
|
149
|
+
*/
|
|
144
150
|
jsName: string;
|
|
145
151
|
parameters: ExportedParameter[];
|
|
146
152
|
/** Absent for a `Void` return. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-macros",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Swift macro plugin for Expo modules",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"build:ts": "tsc -p tsconfig.json",
|
|
17
17
|
"clean": "rm -rf build",
|
|
18
18
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
19
|
+
"swift:format": "node scripts/swift-format.js",
|
|
20
|
+
"swift:lint": "node scripts/swift-format.js --lint",
|
|
19
21
|
"prepublishOnly": "npm run build:ts"
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {
|
|
@@ -23,7 +25,9 @@
|
|
|
23
25
|
"typescript": "^5.4.0"
|
|
24
26
|
},
|
|
25
27
|
"optionalDependencies": {
|
|
26
|
-
"@expo/modules-macros-
|
|
27
|
-
"@expo/modules-macros-
|
|
28
|
+
"@expo/modules-macros-linux-arm64": "0.22.0",
|
|
29
|
+
"@expo/modules-macros-linux-x64": "0.22.0",
|
|
30
|
+
"@expo/modules-macros-win32-arm64": "0.22.0",
|
|
31
|
+
"@expo/modules-macros-win32-x64": "0.22.0"
|
|
28
32
|
}
|
|
29
33
|
}
|