@swiftbrowser/swift 0.0.0-stage → 0.1.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/Package.swift +23 -0
- package/README.md +12 -2
- package/Sources/Charts/Charts.swift +65 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +77 -0
- package/Sources/FoundationBridge/Locale.swift +86 -0
- package/Sources/FoundationBridge/Timer.swift +48 -0
- package/Sources/FoundationBridge/UITextChecker.swift +30 -0
- package/Sources/FoundationLite/Common.swift +3 -0
- package/Sources/FoundationLite/FoundationLite.swift +171 -0
- package/Sources/FoundationLite/Interaction.swift +146 -0
- package/Sources/FoundationLite/Timer.swift +48 -0
- package/Sources/SwiftUI/API/Accessibility.swift +132 -0
- package/Sources/SwiftUI/API/Alert.swift +221 -0
- package/Sources/SwiftUI/API/Animation.swift +253 -0
- package/Sources/SwiftUI/API/App.swift +57 -0
- package/Sources/SwiftUI/API/Color.swift +161 -0
- package/Sources/SwiftUI/API/Controls.swift +268 -0
- package/Sources/SwiftUI/API/CustomLayout.swift +403 -0
- package/Sources/SwiftUI/API/Decorations.swift +92 -0
- package/Sources/SwiftUI/API/EditMode.swift +269 -0
- package/Sources/SwiftUI/API/Effects.swift +290 -0
- package/Sources/SwiftUI/API/Environment.swift +355 -0
- package/Sources/SwiftUI/API/Focus.swift +129 -0
- package/Sources/SwiftUI/API/Font.swift +131 -0
- package/Sources/SwiftUI/API/Gauge.swift +289 -0
- package/Sources/SwiftUI/API/Geometry.swift +89 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +124 -0
- package/Sources/SwiftUI/API/Gestures.swift +342 -0
- package/Sources/SwiftUI/API/Grid.swift +188 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +117 -0
- package/Sources/SwiftUI/API/Layout.swift +165 -0
- package/Sources/SwiftUI/API/Lists.swift +276 -0
- package/Sources/SwiftUI/API/Menu.swift +51 -0
- package/Sources/SwiftUI/API/Modifiers.swift +243 -0
- package/Sources/SwiftUI/API/Navigation.swift +371 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +279 -0
- package/Sources/SwiftUI/API/OnChange.swift +48 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +394 -0
- package/Sources/SwiftUI/API/Picker.swift +176 -0
- package/Sources/SwiftUI/API/Presentation.swift +109 -0
- package/Sources/SwiftUI/API/Primitives.swift +390 -0
- package/Sources/SwiftUI/API/Publishers.swift +107 -0
- package/Sources/SwiftUI/API/RowActions.swift +83 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +104 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +311 -0
- package/Sources/SwiftUI/API/State.swift +127 -0
- package/Sources/SwiftUI/API/StrokeStyle.swift +30 -0
- package/Sources/SwiftUI/API/Tabs.swift +119 -0
- package/Sources/SwiftUI/API/Tasks.swift +35 -0
- package/Sources/SwiftUI/API/Timeline.swift +212 -0
- package/Sources/SwiftUI/API/Toolbar.swift +215 -0
- package/Sources/SwiftUI/API/View.swift +207 -0
- package/Sources/SwiftUI/API/ViewModifier.swift +40 -0
- package/Sources/SwiftUI/API/VisualEffect.swift +82 -0
- package/Sources/SwiftUI/Charts/Axis.swift +319 -0
- package/Sources/SwiftUI/Charts/Chart.swift +646 -0
- package/Sources/SwiftUI/Charts/ChartContent.swift +376 -0
- package/Sources/SwiftUI/Charts/Marks.swift +204 -0
- package/Sources/SwiftUI/Charts/Plottable.swift +99 -0
- package/Sources/SwiftUI/Charts/Scales.swift +326 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +83 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +65 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +71 -0
- package/Sources/SwiftUI/Runtime/JSONParser.swift +443 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +106 -0
- package/Sources/SwiftUI/Runtime/Reconciler.swift +210 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +1367 -0
- package/Sources/_FoundationCommon/Bundle.swift +82 -0
- package/Sources/_FoundationCommon/UserDefaults.swift +170 -0
- package/Sources/_FoundationCommon/_FoundationCommon.swift +10 -0
- package/index.d.ts +2 -0
- package/index.mjs +16 -0
- package/package.json +28 -4
- package/sync-sources.mjs +19 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// `Bundle` for the browser: the app's resource files (what the Xcode target's
|
|
2
|
+
// Copy Bundle Resources phase lists, or `resources` in swiftbrowser.json) are
|
|
3
|
+
// served next to the module and mounted by the host at `/bundle` in the WASI
|
|
4
|
+
// filesystem before `main` runs, so `Data(contentsOf:)` and friends read them
|
|
5
|
+
// like any file. corelibs Foundation's `Bundle.main` traps on WASI; this one
|
|
6
|
+
// answers the resource lookups apps make. Headless runs mount a directory
|
|
7
|
+
// through `SB_BUNDLE_DIR` (scripts/run-wasi.mjs).
|
|
8
|
+
|
|
9
|
+
import FoundationEssentials
|
|
10
|
+
import SwiftUI
|
|
11
|
+
|
|
12
|
+
public final class Bundle: @unchecked Sendable {
|
|
13
|
+
/// Where the host mounts the resources (`SB_BUNDLE_PATH`, default `/bundle`).
|
|
14
|
+
nonisolated(unsafe) public static let main = Bundle(path: _environment("SB_BUNDLE_PATH") ?? "/bundle")!
|
|
15
|
+
|
|
16
|
+
public let bundlePath: String
|
|
17
|
+
|
|
18
|
+
public init?(path: String) {
|
|
19
|
+
bundlePath = path.hasSuffix("/") && path.count > 1 ? String(path.dropLast()) : path
|
|
20
|
+
}
|
|
21
|
+
public convenience init?(url: URL) { self.init(path: url.path) }
|
|
22
|
+
/// Every class lives in the one bundle.
|
|
23
|
+
public convenience init(for aClass: AnyClass) { self.init(path: Bundle.main.bundlePath)! }
|
|
24
|
+
public convenience init?(identifier: String) { self.init(path: Bundle.main.bundlePath) }
|
|
25
|
+
|
|
26
|
+
public var bundleURL: URL { URL(fileURLWithPath: bundlePath, isDirectory: true) }
|
|
27
|
+
public var resourcePath: String? { bundlePath }
|
|
28
|
+
public var resourceURL: URL? { bundleURL }
|
|
29
|
+
public var executablePath: String? { nil }
|
|
30
|
+
public var bundleIdentifier: String? { _environment("SB_BUNDLE_ID") }
|
|
31
|
+
public var infoDictionary: [String: Any]? {
|
|
32
|
+
var info: [String: Any] = [:]
|
|
33
|
+
if let id = bundleIdentifier { info["CFBundleIdentifier"] = id }
|
|
34
|
+
if let name = _environment("SB_APP_NAME") { info["CFBundleName"] = name; info["CFBundleDisplayName"] = name }
|
|
35
|
+
return info
|
|
36
|
+
}
|
|
37
|
+
public func object(forInfoDictionaryKey key: String) -> Any? { infoDictionary?[key] }
|
|
38
|
+
|
|
39
|
+
// MARK: Resources
|
|
40
|
+
|
|
41
|
+
public func path(forResource name: String?, ofType ext: String?) -> String? {
|
|
42
|
+
path(forResource: name, ofType: ext, inDirectory: nil)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public func path(forResource name: String?, ofType ext: String?, inDirectory subpath: String?) -> String? {
|
|
46
|
+
guard let name, !name.isEmpty else { return nil }
|
|
47
|
+
var file = name
|
|
48
|
+
if let ext, !ext.isEmpty, !name.hasSuffix(".\(ext)") { file += ".\(ext)" }
|
|
49
|
+
var components = [bundlePath]
|
|
50
|
+
if let subpath, !subpath.isEmpty { components.append(subpath) }
|
|
51
|
+
components.append(file)
|
|
52
|
+
let path = components.joined(separator: "/")
|
|
53
|
+
return FileManager.default.fileExists(atPath: path) ? path : nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public func url(forResource name: String?, withExtension ext: String?) -> URL? {
|
|
57
|
+
path(forResource: name, ofType: ext).map { URL(fileURLWithPath: $0) }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public func url(forResource name: String?, withExtension ext: String?, subdirectory subpath: String?) -> URL? {
|
|
61
|
+
path(forResource: name, ofType: ext, inDirectory: subpath).map { URL(fileURLWithPath: $0) }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/// Every resource with the extension (`nil`: all files) in the bundle or a subdirectory.
|
|
65
|
+
public func urls(forResourcesWithExtension ext: String?, subdirectory subpath: String?) -> [URL]? {
|
|
66
|
+
let dir = subpath.map { "\(bundlePath)/\($0)" } ?? bundlePath
|
|
67
|
+
guard let names = try? FileManager.default.contentsOfDirectory(atPath: dir) else { return nil }
|
|
68
|
+
return names.sorted()
|
|
69
|
+
.filter { ext == nil || ext!.isEmpty || $0.hasSuffix(".\(ext!)") }
|
|
70
|
+
.map { URL(fileURLWithPath: "\(dir)/\($0)") }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public func paths(forResourcesOfType ext: String?, inDirectory subpath: String?) -> [String] {
|
|
74
|
+
urls(forResourcesWithExtension: ext, subdirectory: subpath)?.map(\.path) ?? []
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// No string tables are loaded: the key (or `value` when given) comes back.
|
|
78
|
+
public func localizedString(forKey key: String, value: String?, table: String?) -> String {
|
|
79
|
+
if let value, !value.isEmpty { return value }
|
|
80
|
+
return key
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// `UserDefaults` that survives reloads. Values live in memory and are mirrored
|
|
2
|
+
// to the host: every change is sent as a `defaults` op (the whole dictionary
|
|
3
|
+
// as JSON), which the browser stores per app in localStorage and hands back at
|
|
4
|
+
// the next launch through the `SB_DEFAULTS` environment variable. Headless
|
|
5
|
+
// runs print the op like any other, so a snapshot shows what an app saves.
|
|
6
|
+
//
|
|
7
|
+
// JSON cannot tell an Int from a Double or carry Data and Date; integers
|
|
8
|
+
// travel as whole numbers (and come back as Int), `Data` as `{"$data": base64}`,
|
|
9
|
+
// `Date` as `{"$date": seconds}`, `URL` as `{"$url": string}`.
|
|
10
|
+
|
|
11
|
+
import FoundationEssentials
|
|
12
|
+
import SwiftUI
|
|
13
|
+
|
|
14
|
+
public final class UserDefaults: @unchecked Sendable {
|
|
15
|
+
nonisolated(unsafe) public static let standard = UserDefaults(persistent: true)
|
|
16
|
+
|
|
17
|
+
private var storage: [String: Any] = [:]
|
|
18
|
+
private var registered: [String: Any] = [:]
|
|
19
|
+
private let persistent: Bool
|
|
20
|
+
|
|
21
|
+
init(persistent: Bool) {
|
|
22
|
+
self.persistent = persistent
|
|
23
|
+
if persistent, let text = _environment("SB_DEFAULTS"), let json = try? _JSON.parse(text), case .object(let values) = json {
|
|
24
|
+
for (key, value) in values { storage[key] = _defaultsValue(fromJSON: value) }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public convenience init() { self.init(persistent: false) }
|
|
29
|
+
/// Suites are not separated: every suite is the standard defaults.
|
|
30
|
+
public convenience init?(suiteName: String?) { self.init(persistent: true) }
|
|
31
|
+
|
|
32
|
+
// MARK: Reading
|
|
33
|
+
|
|
34
|
+
public func object(forKey key: String) -> Any? { storage[key] ?? registered[key] }
|
|
35
|
+
public func string(forKey key: String) -> String? {
|
|
36
|
+
switch object(forKey: key) {
|
|
37
|
+
case let s as String: s
|
|
38
|
+
case let n as Int: String(n)
|
|
39
|
+
case let n as Double: String(n)
|
|
40
|
+
default: nil
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
public func array(forKey key: String) -> [Any]? { object(forKey: key) as? [Any] }
|
|
44
|
+
public func dictionary(forKey key: String) -> [String: Any]? { object(forKey: key) as? [String: Any] }
|
|
45
|
+
public func data(forKey key: String) -> Data? { object(forKey: key) as? Data }
|
|
46
|
+
public func stringArray(forKey key: String) -> [String]? {
|
|
47
|
+
guard let array = object(forKey: key) as? [Any] else { return nil }
|
|
48
|
+
let strings = array.compactMap { $0 as? String }
|
|
49
|
+
return strings.count == array.count ? strings : nil
|
|
50
|
+
}
|
|
51
|
+
public func url(forKey key: String) -> URL? {
|
|
52
|
+
switch object(forKey: key) {
|
|
53
|
+
case let url as URL: url
|
|
54
|
+
case let s as String: URL(string: s)
|
|
55
|
+
default: nil
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
public func integer(forKey key: String) -> Int {
|
|
59
|
+
switch object(forKey: key) {
|
|
60
|
+
case let n as Int: n
|
|
61
|
+
case let n as Double: Int(n)
|
|
62
|
+
case let s as String: Int(s) ?? 0
|
|
63
|
+
case let b as Bool: b ? 1 : 0
|
|
64
|
+
default: 0
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
public func float(forKey key: String) -> Float { Float(double(forKey: key)) }
|
|
68
|
+
public func double(forKey key: String) -> Double {
|
|
69
|
+
switch object(forKey: key) {
|
|
70
|
+
case let n as Double: n
|
|
71
|
+
case let n as Int: Double(n)
|
|
72
|
+
case let s as String: Double(s) ?? 0
|
|
73
|
+
case let b as Bool: b ? 1 : 0
|
|
74
|
+
default: 0
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
public func bool(forKey key: String) -> Bool {
|
|
78
|
+
switch object(forKey: key) {
|
|
79
|
+
case let b as Bool: b
|
|
80
|
+
case let n as Int: n != 0
|
|
81
|
+
case let n as Double: n != 0
|
|
82
|
+
case let s as String: s == "true" || s == "YES" || s == "1"
|
|
83
|
+
default: false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// MARK: Writing
|
|
88
|
+
|
|
89
|
+
public func set(_ value: Any?, forKey key: String) {
|
|
90
|
+
if let value { storage[key] = value } else { storage[key] = nil }
|
|
91
|
+
persist()
|
|
92
|
+
}
|
|
93
|
+
public func set(_ value: Int, forKey key: String) { set(value as Any, forKey: key) }
|
|
94
|
+
public func set(_ value: Double, forKey key: String) { set(value as Any, forKey: key) }
|
|
95
|
+
public func set(_ value: Float, forKey key: String) { set(Double(value) as Any, forKey: key) }
|
|
96
|
+
public func set(_ value: Bool, forKey key: String) { set(value as Any, forKey: key) }
|
|
97
|
+
public func set(_ url: URL?, forKey key: String) { set(url as Any?, forKey: key) }
|
|
98
|
+
public func removeObject(forKey key: String) { set(nil, forKey: key) }
|
|
99
|
+
|
|
100
|
+
/// Fallback values, read when nothing was set; never persisted.
|
|
101
|
+
public func register(defaults: [String: Any]) {
|
|
102
|
+
for (key, value) in defaults { registered[key] = value }
|
|
103
|
+
}
|
|
104
|
+
public func dictionaryRepresentation() -> [String: Any] {
|
|
105
|
+
var all = registered
|
|
106
|
+
for (key, value) in storage { all[key] = value }
|
|
107
|
+
return all
|
|
108
|
+
}
|
|
109
|
+
public func removePersistentDomain(forName domainName: String) {
|
|
110
|
+
storage.removeAll()
|
|
111
|
+
persist()
|
|
112
|
+
}
|
|
113
|
+
@discardableResult public func synchronize() -> Bool { true }
|
|
114
|
+
|
|
115
|
+
/// The saved values as the host stores them.
|
|
116
|
+
public var _json: _JSON {
|
|
117
|
+
var fields: [String: _JSON] = [:]
|
|
118
|
+
for (key, value) in storage {
|
|
119
|
+
if let json = _defaultsJSON(from: value) { fields[key] = json }
|
|
120
|
+
}
|
|
121
|
+
return .object(fields)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private func persist() {
|
|
125
|
+
guard persistent else { return }
|
|
126
|
+
let json = _json
|
|
127
|
+
MainActor.assumeIsolated { _Runtime.shared.storeDefaults(json) }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// A defaults value as JSON; nil for a type the store cannot carry.
|
|
132
|
+
func _defaultsJSON(from value: Any) -> _JSON? {
|
|
133
|
+
switch value {
|
|
134
|
+
case let v as String: return .string(v)
|
|
135
|
+
case let v as Bool: return .bool(v)
|
|
136
|
+
case let v as Int: return .number(Double(v))
|
|
137
|
+
case let v as Double: return .number(v)
|
|
138
|
+
case let v as Float: return .number(Double(v))
|
|
139
|
+
case let v as Data: return .object(["$data": .string(v.base64EncodedString())])
|
|
140
|
+
case let v as Date: return .object(["$date": .number(v.timeIntervalSince1970)])
|
|
141
|
+
case let v as URL: return .object(["$url": .string(v.absoluteString)])
|
|
142
|
+
case let v as [Any]: return .array(v.compactMap(_defaultsJSON(from:)))
|
|
143
|
+
case let v as [String: Any]:
|
|
144
|
+
var fields: [String: _JSON] = [:]
|
|
145
|
+
for (key, item) in v { if let json = _defaultsJSON(from: item) { fields[key] = json } }
|
|
146
|
+
return .object(fields)
|
|
147
|
+
default:
|
|
148
|
+
print("[UserDefaults] cannot save a value of type \(type(of: value)); use a property-list type")
|
|
149
|
+
return nil
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func _defaultsValue(fromJSON json: _JSON) -> Any? {
|
|
154
|
+
switch json {
|
|
155
|
+
case .null: return nil
|
|
156
|
+
case .bool(let b): return b
|
|
157
|
+
case .string(let s): return s
|
|
158
|
+
case .number(let n): return n == n.rounded() && abs(n) < 9_007_199_254_740_992 ? Int(n) as Any : n
|
|
159
|
+
case .array(let items): return items.compactMap(_defaultsValue(fromJSON:))
|
|
160
|
+
case .object(let fields):
|
|
161
|
+
if fields.count == 1 {
|
|
162
|
+
if case .string(let b64)? = fields["$data"], let data = Data(base64Encoded: b64) { return data }
|
|
163
|
+
if case .number(let seconds)? = fields["$date"] { return Date(timeIntervalSince1970: seconds) }
|
|
164
|
+
if case .string(let text)? = fields["$url"], let url = URL(string: text) { return url }
|
|
165
|
+
}
|
|
166
|
+
var out: [String: Any] = [:]
|
|
167
|
+
for (key, item) in fields { if let value = _defaultsValue(fromJSON: item) { out[key] = value } }
|
|
168
|
+
return out
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/// What FoundationLite (lean builds) and FoundationBridge (full builds) share:
|
|
2
|
+
/// the browser's `UserDefaults` and `Bundle`. Both are written against
|
|
3
|
+
/// FoundationEssentials, whose `Data`, `Date`, `URL` and `FileManager` are the
|
|
4
|
+
/// very types the full Foundation re-exports, so one implementation serves
|
|
5
|
+
/// both modes. FoundationLite re-exports this module; FoundationBridge aliases
|
|
6
|
+
/// the two classes, and the CLI's generated file aliases them again in the app
|
|
7
|
+
/// so they shadow corelibs Foundation's (whose `UserDefaults` cannot persist
|
|
8
|
+
/// and whose `Bundle.main` traps on WASI). See docs/ops-protocol.md, "Phase 11".
|
|
9
|
+
import FoundationEssentials
|
|
10
|
+
import SwiftUI
|
package/index.d.ts
ADDED
package/index.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Where the Swift package lives: next to this file once `Sources/` was synced
|
|
2
|
+
// (a published tarball), else the repository root (a checkout, whose
|
|
3
|
+
// Package.swift carries the same library products plus the examples).
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
|
|
10
|
+
/** Absolute path of a directory holding a Package.swift with the SwiftUI, Charts, FoundationLite and FoundationBridge products. */
|
|
11
|
+
export function swiftPackagePath() {
|
|
12
|
+
if (existsSync(path.join(here, 'Sources', 'SwiftUI'))) return here;
|
|
13
|
+
const repoRoot = path.resolve(here, '../..');
|
|
14
|
+
if (existsSync(path.join(repoRoot, 'Package.swift')) && existsSync(path.join(repoRoot, 'Sources', 'SwiftUI'))) return repoRoot;
|
|
15
|
+
throw new Error(`@swiftbrowser/swift: no Swift package found at ${here} or ${repoRoot}`);
|
|
16
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swiftbrowser/swift",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The SwiftBrowser Swift package (the SwiftUI shim, Charts, FoundationLite and FoundationBridge) as an npm package, so @swiftbrowser/cli builds apps against the exact version it ships with.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/dabbott/SwiftBrowser.git",
|
|
8
|
+
"directory": "packages/swift"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"Package.swift",
|
|
12
|
+
"Sources",
|
|
13
|
+
"index.mjs",
|
|
14
|
+
"index.d.ts",
|
|
15
|
+
"sync-sources.mjs",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"prepack": "node sync-sources.mjs"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./index.mjs",
|
|
23
|
+
"types": "./index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./index.d.ts",
|
|
27
|
+
"default": "./index.mjs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/sync-sources.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// `npm pack` / `npm publish` hook: copy the repository's Sources/ (the SwiftUI
|
|
2
|
+
// shim, Charts, FoundationLite, FoundationBridge) into this package so the published tarball is a
|
|
3
|
+
// complete Swift package. The copy is gitignored.
|
|
4
|
+
import { cpSync, existsSync, rmSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const repoSources = path.resolve(here, '../../Sources');
|
|
10
|
+
const dest = path.join(here, 'Sources');
|
|
11
|
+
if (!existsSync(repoSources)) {
|
|
12
|
+
console.error(`sync-sources: ${repoSources} not found; run from a repository checkout`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
rmSync(dest, { recursive: true, force: true });
|
|
16
|
+
for (const target of ['SwiftUI', 'Charts', '_FoundationCommon', 'FoundationLite', 'FoundationBridge']) {
|
|
17
|
+
cpSync(path.join(repoSources, target), path.join(dest, target), { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
console.log(`sync-sources: copied Sources/{SwiftUI,Charts,_FoundationCommon,FoundationLite,FoundationBridge} into ${dest}`);
|