@expo/expo-modules-macros-plugin 0.3.0 → 0.5.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/apple/ExpoModulesMacros-tool +0 -0
- package/apple/Package.swift +27 -2
- package/apple/Sources/ExpoModulesMacros/DecorateModuleBuilder.swift +148 -98
- package/apple/Sources/ExpoModulesMacros/ExpoModuleMacro.swift +0 -52
- package/apple/Sources/ExpoModulesMacros/JSConstructor.swift +60 -0
- package/apple/Sources/ExpoModulesMacros/MacroHelpers.swift +84 -0
- package/apple/Sources/ExpoModulesMacros/Receiver.swift +58 -0
- package/apple/Sources/ExpoModulesMacros/RecordMacro.swift +5 -3
- package/apple/Sources/ExpoModulesMacros/SharedObjectMacro.swift +29 -90
- package/apple/Sources/ExpoModulesScanner/Core/Detection.swift +61 -0
- package/apple/Sources/ExpoModulesScanner/Core/DetectionVisitor.swift +109 -0
- package/apple/Sources/ExpoModulesScanner/Core/SourceScan.swift +137 -0
- package/apple/Sources/ExpoModulesScanner/Modules/ScanModules.swift +68 -0
- package/apple/Sources/ExpoModulesScannerCLI/main.swift +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// The scanner's public entry point. Argument parsing, subcommand dispatch, and usage text live in
|
|
4
|
+
/// the CLI target; this just runs a command and writes its JSON report to stdout.
|
|
5
|
+
///
|
|
6
|
+
/// The detection model (`Detection`, `DetectionVisitor`, …) stays `internal`: tests reach it via
|
|
7
|
+
/// `@testable import`, and the CLI only needs these entries, so nothing else is exposed.
|
|
8
|
+
public enum Scanner {
|
|
9
|
+
/// Runs the `scan-modules` command over `paths`, prints the JSON report to stdout, and returns a
|
|
10
|
+
/// process exit code: `0` on success, `1` if encoding fails. (`scan-exports` will get its own
|
|
11
|
+
/// `run`-style entry returning its own result type when implemented.)
|
|
12
|
+
public static func runModules(paths: [String]) -> Int32 {
|
|
13
|
+
let result = scanModules(paths: paths)
|
|
14
|
+
|
|
15
|
+
do {
|
|
16
|
+
let encoder = JSONEncoder()
|
|
17
|
+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
|
|
18
|
+
let data = try encoder.encode(result)
|
|
19
|
+
FileHandle.standardOutput.write(data)
|
|
20
|
+
FileHandle.standardOutput.write(Data("\n".utf8))
|
|
21
|
+
return 0
|
|
22
|
+
} catch {
|
|
23
|
+
FileHandle.standardError.write(Data("error: failed to encode results: \(error)\n".utf8))
|
|
24
|
+
return 1
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// One module in the `scan-modules` output. Trimmed to what `expo-modules-autolinking` needs to
|
|
30
|
+
/// register a module: the Swift class name, the JS name it registers under, and the file it's in.
|
|
31
|
+
/// The richer fields the visitor captures (declaration kind, raw macro arguments, line/column) are
|
|
32
|
+
/// dropped here — they're redundant for this command (the macro is always `@ExpoModule` on a class)
|
|
33
|
+
/// and belong to the deep `scan-exports` surface instead.
|
|
34
|
+
struct ScannedModule: Codable, Equatable {
|
|
35
|
+
/// The Swift class name the module is declared as.
|
|
36
|
+
let name: String
|
|
37
|
+
|
|
38
|
+
/// The fully-resolved JS module name: the `@ExpoModule("Foo")` override when present, otherwise the
|
|
39
|
+
/// class name. Resolved here (rather than left `nil`) so it matches how the macro derives the name
|
|
40
|
+
/// and the consumer never has to apply the fallback itself.
|
|
41
|
+
let jsName: String
|
|
42
|
+
|
|
43
|
+
/// Source file the module was found in, relative to the path the scanner was invoked with.
|
|
44
|
+
let file: String
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// The `scan-modules` result: the detected modules plus the stats describing the run. Encoded as the
|
|
48
|
+
/// command's JSON output. (`scan-exports` will return its own shape when implemented; the two
|
|
49
|
+
/// commands serve different consumers and aren't expected to share an envelope.)
|
|
50
|
+
struct ScanModulesResult: Codable, Equatable {
|
|
51
|
+
let modules: [ScannedModule]
|
|
52
|
+
let stats: ScanStats
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Scans the given paths for top-level `@ExpoModule` types and returns the modules (in file then
|
|
56
|
+
/// source order) plus the stats for the run — the `scan-modules` command. Kept separate from the
|
|
57
|
+
/// public entry (and `internal`) so tests can drive it without going through argv/stdout.
|
|
58
|
+
func scanModules(paths: [String]) -> ScanModulesResult {
|
|
59
|
+
let scan = collectDetections(paths: paths, macros: [.expoModule])
|
|
60
|
+
|
|
61
|
+
let modules = scan.detections.map {
|
|
62
|
+
// Resolve the JS name the way the macro does: explicit `@ExpoModule("Foo")` override, else the
|
|
63
|
+
// class name.
|
|
64
|
+
ScannedModule(name: $0.name, jsName: $0.jsName ?? $0.name, file: $0.file)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return ScanModulesResult(modules: modules, stats: scan.stats)
|
|
68
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import ExpoModulesScanner
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// Command-line front end for the scanner. Parses the subcommand and paths, then delegates to the
|
|
5
|
+
/// matching library entry (which runs the scan and writes its JSON output). Each path may be a
|
|
6
|
+
/// `.swift` file or a directory (scanned recursively for `.swift` files).
|
|
7
|
+
///
|
|
8
|
+
/// Subcommands:
|
|
9
|
+
/// scan-modules <path>... fast: top-level `@ExpoModule` types, for autolinking
|
|
10
|
+
/// scan-exports <path>... deep: full JS-exported surface, for TS type generation
|
|
11
|
+
|
|
12
|
+
let toolName = "ExpoModulesScanner"
|
|
13
|
+
|
|
14
|
+
let usageText = """
|
|
15
|
+
usage: \(toolName) <subcommand> <path> [<path> ...]
|
|
16
|
+
|
|
17
|
+
subcommands:
|
|
18
|
+
scan-modules fast scan for top-level @ExpoModule types (autolinking)
|
|
19
|
+
scan-exports deep scan of the full JS-exported surface (type generation)
|
|
20
|
+
|
|
21
|
+
options:
|
|
22
|
+
-h, --help print this help and exit
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
/// Prints the usage text to the given handle. Goes to stdout when help was explicitly requested
|
|
27
|
+
/// (a successful action), stderr when it accompanies a usage error.
|
|
28
|
+
func printUsage(to handle: FileHandle = .standardError) {
|
|
29
|
+
handle.write(Data(usageText.utf8))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func fail(_ message: String, usage: Bool = false, code: Int32 = 2) -> Never {
|
|
33
|
+
FileHandle.standardError.write(Data("error: \(message)\n".utf8))
|
|
34
|
+
if usage {
|
|
35
|
+
printUsage()
|
|
36
|
+
}
|
|
37
|
+
exit(code)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var arguments = Array(CommandLine.arguments.dropFirst())
|
|
41
|
+
|
|
42
|
+
// `-h`/`--help` anywhere is treated as a help request: print usage to stdout and exit 0.
|
|
43
|
+
if arguments.contains(where: { $0 == "-h" || $0 == "--help" }) {
|
|
44
|
+
printUsage(to: .standardOutput)
|
|
45
|
+
exit(0)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
guard !arguments.isEmpty else {
|
|
49
|
+
printUsage()
|
|
50
|
+
exit(2)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let subcommand = arguments.removeFirst()
|
|
54
|
+
let paths = arguments
|
|
55
|
+
|
|
56
|
+
switch subcommand {
|
|
57
|
+
case "scan-modules":
|
|
58
|
+
guard !paths.isEmpty else {
|
|
59
|
+
fail("scan-modules requires at least one path", usage: true)
|
|
60
|
+
}
|
|
61
|
+
exit(Scanner.runModules(paths: paths))
|
|
62
|
+
|
|
63
|
+
case "scan-exports":
|
|
64
|
+
// Deep extraction (members, record fields, the JS surface of each type) lands in a separate PR.
|
|
65
|
+
// The subcommand is recognized so the CLI surface is stable, but it isn't implemented yet.
|
|
66
|
+
fail("scan-exports is not yet implemented", code: 1)
|
|
67
|
+
|
|
68
|
+
default:
|
|
69
|
+
fail("unknown subcommand '\(subcommand)'", usage: true)
|
|
70
|
+
}
|