@appsent-co/react-native-watchos 0.1.1 → 0.1.3

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.
@@ -152,8 +152,16 @@ public final class ReactNativeWatchOSHost: ObservableObject {
152
152
  Bundle.main.url(forResource: name, withExtension: "jsbundle")
153
153
  }
154
154
 
155
+ /// - Parameter entry: Metro entry path (no extension). Defaults to
156
+ /// `"index.watchos"` so the request becomes `/index.watchos.bundle`,
157
+ /// which Metro resolves to the literal `index.watchos.{tsx,ts,jsx,js}`
158
+ /// on disk. A bare `"index"` would resolve to `index.ts` (the
159
+ /// iOS/Android entry from package.json `main`) — Metro applies the
160
+ /// `.watchos.*` extension to in-graph `require`s, not to the entry
161
+ /// path itself. Monorepo callers can override with e.g.
162
+ /// `entry: "my-app/index.watchos"`.
155
163
  public static func defaultBundleURL(
156
- entry: String = "index",
164
+ entry: String = "index.watchos",
157
165
  host: String = "127.0.0.1",
158
166
  port: Int = 8081,
159
167
  name: String = "main"
@@ -10,7 +10,7 @@
10
10
  <key>HeadersPath</key>
11
11
  <string>Headers</string>
12
12
  <key>LibraryIdentifier</key>
13
- <string>watchos-arm64</string>
13
+ <string>watchos-arm64-simulator</string>
14
14
  <key>LibraryPath</key>
15
15
  <string>libReactNativeWatchOSCxx.a</string>
16
16
  <key>SupportedArchitectures</key>
@@ -19,6 +19,8 @@
19
19
  </array>
20
20
  <key>SupportedPlatform</key>
21
21
  <string>watchos</string>
22
+ <key>SupportedPlatformVariant</key>
23
+ <string>simulator</string>
22
24
  </dict>
23
25
  <dict>
24
26
  <key>BinaryPath</key>
@@ -26,7 +28,7 @@
26
28
  <key>HeadersPath</key>
27
29
  <string>Headers</string>
28
30
  <key>LibraryIdentifier</key>
29
- <string>watchos-arm64-simulator</string>
31
+ <string>watchos-arm64</string>
30
32
  <key>LibraryPath</key>
31
33
  <string>libReactNativeWatchOSCxx.a</string>
32
34
  <key>SupportedArchitectures</key>
@@ -35,8 +37,6 @@
35
37
  </array>
36
38
  <key>SupportedPlatform</key>
37
39
  <string>watchos</string>
38
- <key>SupportedPlatformVariant</key>
39
- <string>simulator</string>
40
40
  </dict>
41
41
  </array>
42
42
  <key>CFBundlePackageType</key>
package/cli/init.js CHANGED
@@ -34,6 +34,13 @@ async function main() {
34
34
  const targetsRoot = path.join(cwd, 'targets');
35
35
  const existing = listWatchTargets(targetsRoot);
36
36
 
37
+ // `create-target watch` loads the Expo config internally, which runs our
38
+ // plugin's order assertion. If `npx expo install` auto-added us before
39
+ // `@bacons/apple-targets`, that assertion throws and create-target exits 1
40
+ // before we ever get to patchAppJson. Strip our entry first; patchAppJson
41
+ // reinserts it in the correct position after create-target succeeds.
42
+ stripOurPluginFromAppJson(cwd);
43
+
37
44
  console.log('› Running `npx create-target watch`…');
38
45
  await spawnInherit('npx', ['--yes', 'create-target', 'watch']);
39
46
 
@@ -59,6 +66,7 @@ async function main() {
59
66
  console.log(' • Next: npx expo prebuild -p ios --clean');
60
67
  }
61
68
 
69
+ /** @param {string} targetsRoot */
62
70
  function listWatchTargets(targetsRoot) {
63
71
  if (!fs.existsSync(targetsRoot)) return [];
64
72
  const out = [];
@@ -70,6 +78,7 @@ function listWatchTargets(targetsRoot) {
70
78
  return out;
71
79
  }
72
80
 
81
+ /** @param {string} dir */
73
82
  function isWatchTargetConfig(dir) {
74
83
  for (const f of ['expo-target.config.json', 'expo-target.config.js']) {
75
84
  const p = path.join(dir, f);
@@ -80,13 +89,31 @@ function isWatchTargetConfig(dir) {
80
89
  return false;
81
90
  }
82
91
 
92
+ /** @param {string} targetDir */
83
93
  function writeContentView(targetDir) {
94
+ // `create-target watch` drops its own `content.swift` placeholder that
95
+ // declares `struct ContentView: View`. Ours does too, so leaving both in
96
+ // the target compiles to "Invalid redeclaration of 'ContentView'". Wipe
97
+ // the placeholder (root + preview/) before writing ours.
98
+ for (const dir of [targetDir, path.join(targetDir, 'preview')]) {
99
+ if (!fs.existsSync(dir)) continue;
100
+ for (const name of fs.readdirSync(dir)) {
101
+ if (name.toLowerCase() !== 'content.swift') continue;
102
+ const p = path.join(dir, name);
103
+ fs.rmSync(p);
104
+ console.log(
105
+ `› Removed ${path.relative(process.cwd(), p)} (create-target placeholder; conflicts with ContentView.swift).`
106
+ );
107
+ }
108
+ }
109
+
84
110
  const dest = path.join(targetDir, 'ContentView.swift');
85
111
  const src = path.join(TEMPLATES, 'ContentView.swift');
86
112
  fs.copyFileSync(src, dest);
87
113
  console.log(`› Wrote ${path.relative(process.cwd(), dest)}`);
88
114
  }
89
115
 
116
+ /** @param {string} cwd */
90
117
  function writeEntryFile(cwd) {
91
118
  for (const name of ENTRY_NAMES) {
92
119
  if (fs.existsSync(path.join(cwd, name))) {
@@ -99,6 +126,47 @@ function writeEntryFile(cwd) {
99
126
  console.log(`› Wrote ${path.relative(cwd, dest)}`);
100
127
  }
101
128
 
129
+ /**
130
+ * @param {unknown} entry
131
+ * @param {string} name
132
+ */
133
+ const isEntry = (entry, name) =>
134
+ Array.isArray(entry) ? entry[0] === name : entry === name;
135
+
136
+ /**
137
+ * @param {string} cwd
138
+ */
139
+ function stripOurPluginFromAppJson(cwd) {
140
+ const p = path.join(cwd, 'app.json');
141
+ if (!fs.existsSync(p)) return;
142
+
143
+ /** @type {{ expo?: { plugins?: Array<unknown> } }} */
144
+ const json = JSON.parse(fs.readFileSync(p, 'utf8'));
145
+ const plugins =
146
+ json && json.expo && Array.isArray(json.expo.plugins)
147
+ ? json.expo.plugins
148
+ : null;
149
+ if (!plugins) return;
150
+
151
+ let removed = 0;
152
+ for (let i = plugins.length - 1; i >= 0; i--) {
153
+ if (isEntry(plugins[i], '@appsent-co/react-native-watchos')) {
154
+ plugins.splice(i, 1);
155
+ removed++;
156
+ }
157
+ }
158
+ if (removed) {
159
+ fs.writeFileSync(p, JSON.stringify(json, null, 2) + '\n');
160
+ console.log(
161
+ `› Removed pre-existing @appsent-co/react-native-watchos entry from ${path.relative(cwd, p)} (will reinsert in correct position).`
162
+ );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * @param {string} cwd
168
+ * @param {string} targetName
169
+ */
102
170
  function patchAppJson(cwd, targetName) {
103
171
  const p = path.join(cwd, 'app.json');
104
172
  if (!fs.existsSync(p)) {
@@ -112,13 +180,11 @@ function patchAppJson(cwd, targetName) {
112
180
  }
113
181
 
114
182
  const raw = fs.readFileSync(p, 'utf8');
183
+ /** @type {{ expo?: { plugins?: Array<unknown> } }} */
115
184
  const json = JSON.parse(raw);
116
185
  const expo = (json.expo = json.expo || {});
117
186
  const plugins = (expo.plugins = expo.plugins || []);
118
187
 
119
- const isEntry = (entry, name) =>
120
- Array.isArray(entry) ? entry[0] === name : entry === name;
121
-
122
188
  // `npx expo install` auto-adds the plugin as a bare string in the wrong
123
189
  // position (before @bacons/apple-targets) and with no targetName. Strip
124
190
  // any existing entry so we can reinsert it in the correct position.
@@ -131,8 +197,8 @@ function patchAppJson(cwd, targetName) {
131
197
  }
132
198
 
133
199
  const entry = ['@appsent-co/react-native-watchos', { targetName }];
134
- const baconIdx = plugins.findIndex((e) =>
135
- isEntry(e, '@bacons/apple-targets')
200
+ const baconIdx = plugins.findIndex(
201
+ /** @param {unknown} e */ (e) => isEntry(e, '@bacons/apple-targets')
136
202
  );
137
203
  if (baconIdx >= 0) plugins.splice(baconIdx + 1, 0, entry);
138
204
  else plugins.push(entry);
@@ -147,6 +213,11 @@ function patchAppJson(cwd, targetName) {
147
213
  }
148
214
  }
149
215
 
216
+ /**
217
+ * @param {string} cmd
218
+ * @param {string[]} args
219
+ * @returns {Promise<void>}
220
+ */
150
221
  function spawnInherit(cmd, args) {
151
222
  return new Promise((resolve, reject) => {
152
223
  const child = spawn(cmd, args, { stdio: 'inherit' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appsent-co/react-native-watchos",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Build WatchOS apps in react-native",
5
5
  "main": "./plugin/src/index.js",
6
6
  "bin": {
@@ -20,7 +20,7 @@ struct ContentView: View {
20
20
  // For real-device testing, pass the Mac's LAN IP:
21
21
  // ReactNativeWatchOSHost.defaultBundleURL(host: "192.168.1.42")
22
22
  // pnpm/monorepo setups serve bundles under `/<package>/index.bundle`:
23
- // ReactNativeWatchOSHost.defaultBundleURL(entry: "my-app/index")
23
+ // ReactNativeWatchOSHost.defaultBundleURL(entry: "my-app/index.watchos")
24
24
  private let bundleURL = ReactNativeWatchOSHost.defaultBundleURL()
25
25
 
26
26
  var body: some View {