@appsent-co/react-native-watchos 0.1.0 → 0.1.2

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.
@@ -8,7 +8,7 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>hermes.framework/hermes</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>watchos-arm64-simulator</string>
11
+ <string>watchos-arm64</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>hermes.framework</string>
14
14
  <key>SupportedArchitectures</key>
@@ -17,14 +17,12 @@
17
17
  </array>
18
18
  <key>SupportedPlatform</key>
19
19
  <string>watchos</string>
20
- <key>SupportedPlatformVariant</key>
21
- <string>simulator</string>
22
20
  </dict>
23
21
  <dict>
24
22
  <key>BinaryPath</key>
25
23
  <string>hermes.framework/hermes</string>
26
24
  <key>LibraryIdentifier</key>
27
- <string>watchos-arm64</string>
25
+ <string>watchos-arm64-simulator</string>
28
26
  <key>LibraryPath</key>
29
27
  <string>hermes.framework</string>
30
28
  <key>SupportedArchitectures</key>
@@ -33,6 +31,8 @@
33
31
  </array>
34
32
  <key>SupportedPlatform</key>
35
33
  <string>watchos</string>
34
+ <key>SupportedPlatformVariant</key>
35
+ <string>simulator</string>
36
36
  </dict>
37
37
  </array>
38
38
  <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,6 +89,7 @@ function isWatchTargetConfig(dir) {
80
89
  return false;
81
90
  }
82
91
 
92
+ /** @param {string} targetDir */
83
93
  function writeContentView(targetDir) {
84
94
  const dest = path.join(targetDir, 'ContentView.swift');
85
95
  const src = path.join(TEMPLATES, 'ContentView.swift');
@@ -87,6 +97,7 @@ function writeContentView(targetDir) {
87
97
  console.log(`› Wrote ${path.relative(process.cwd(), dest)}`);
88
98
  }
89
99
 
100
+ /** @param {string} cwd */
90
101
  function writeEntryFile(cwd) {
91
102
  for (const name of ENTRY_NAMES) {
92
103
  if (fs.existsSync(path.join(cwd, name))) {
@@ -99,6 +110,47 @@ function writeEntryFile(cwd) {
99
110
  console.log(`› Wrote ${path.relative(cwd, dest)}`);
100
111
  }
101
112
 
113
+ /**
114
+ * @param {unknown} entry
115
+ * @param {string} name
116
+ */
117
+ const isEntry = (entry, name) =>
118
+ Array.isArray(entry) ? entry[0] === name : entry === name;
119
+
120
+ /**
121
+ * @param {string} cwd
122
+ */
123
+ function stripOurPluginFromAppJson(cwd) {
124
+ const p = path.join(cwd, 'app.json');
125
+ if (!fs.existsSync(p)) return;
126
+
127
+ /** @type {{ expo?: { plugins?: Array<unknown> } }} */
128
+ const json = JSON.parse(fs.readFileSync(p, 'utf8'));
129
+ const plugins =
130
+ json && json.expo && Array.isArray(json.expo.plugins)
131
+ ? json.expo.plugins
132
+ : null;
133
+ if (!plugins) return;
134
+
135
+ let removed = 0;
136
+ for (let i = plugins.length - 1; i >= 0; i--) {
137
+ if (isEntry(plugins[i], '@appsent-co/react-native-watchos')) {
138
+ plugins.splice(i, 1);
139
+ removed++;
140
+ }
141
+ }
142
+ if (removed) {
143
+ fs.writeFileSync(p, JSON.stringify(json, null, 2) + '\n');
144
+ console.log(
145
+ `› Removed pre-existing @appsent-co/react-native-watchos entry from ${path.relative(cwd, p)} (will reinsert in correct position).`
146
+ );
147
+ }
148
+ }
149
+
150
+ /**
151
+ * @param {string} cwd
152
+ * @param {string} targetName
153
+ */
102
154
  function patchAppJson(cwd, targetName) {
103
155
  const p = path.join(cwd, 'app.json');
104
156
  if (!fs.existsSync(p)) {
@@ -112,31 +164,44 @@ function patchAppJson(cwd, targetName) {
112
164
  }
113
165
 
114
166
  const raw = fs.readFileSync(p, 'utf8');
167
+ /** @type {{ expo?: { plugins?: Array<unknown> } }} */
115
168
  const json = JSON.parse(raw);
116
169
  const expo = (json.expo = json.expo || {});
117
170
  const plugins = (expo.plugins = expo.plugins || []);
118
171
 
119
- const isEntry = (entry, name) =>
120
- Array.isArray(entry) ? entry[0] === name : entry === name;
121
-
122
- if (plugins.some((e) => isEntry(e, '@appsent-co/react-native-watchos'))) {
123
- console.log(
124
- '@appsent-co/react-native-watchos plugin already present in app.json.'
125
- );
126
- return;
172
+ // `npx expo install` auto-adds the plugin as a bare string in the wrong
173
+ // position (before @bacons/apple-targets) and with no targetName. Strip
174
+ // any existing entry so we can reinsert it in the correct position.
175
+ const removed = [];
176
+ for (let i = plugins.length - 1; i >= 0; i--) {
177
+ if (isEntry(plugins[i], '@appsent-co/react-native-watchos')) {
178
+ removed.push(plugins[i]);
179
+ plugins.splice(i, 1);
180
+ }
127
181
  }
128
182
 
129
183
  const entry = ['@appsent-co/react-native-watchos', { targetName }];
130
- const baconIdx = plugins.findIndex((e) =>
131
- isEntry(e, '@bacons/apple-targets')
184
+ const baconIdx = plugins.findIndex(
185
+ /** @param {unknown} e */ (e) => isEntry(e, '@bacons/apple-targets')
132
186
  );
133
187
  if (baconIdx >= 0) plugins.splice(baconIdx + 1, 0, entry);
134
188
  else plugins.push(entry);
135
189
 
136
190
  fs.writeFileSync(p, JSON.stringify(json, null, 2) + '\n');
137
- console.log(`› Patched ${path.relative(cwd, p)}`);
191
+ if (removed.length) {
192
+ console.log(
193
+ `› Replaced existing @appsent-co/react-native-watchos entry in ${path.relative(cwd, p)}`
194
+ );
195
+ } else {
196
+ console.log(`› Patched ${path.relative(cwd, p)}`);
197
+ }
138
198
  }
139
199
 
200
+ /**
201
+ * @param {string} cmd
202
+ * @param {string[]} args
203
+ * @returns {Promise<void>}
204
+ */
140
205
  function spawnInherit(cmd, args) {
141
206
  return new Promise((resolve, reject) => {
142
207
  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.0",
3
+ "version": "0.1.2",
4
4
  "description": "Build WatchOS apps in react-native",
5
5
  "main": "./plugin/src/index.js",
6
6
  "bin": {
@@ -37,6 +37,15 @@
37
37
  "build/xcframework",
38
38
  "!**/.*"
39
39
  ],
40
+ "scripts": {
41
+ "build:xcframework": "./scripts/build-xcframework.sh",
42
+ "lint": "eslint .",
43
+ "clean": "del-cli build/xcframework example/ios",
44
+ "docs": "pnpm --filter react-native-watchos-docs start",
45
+ "docs:build": "pnpm --filter react-native-watchos-docs build",
46
+ "release": "release-it --only-version",
47
+ "prepublishOnly": "pnpm build:xcframework"
48
+ },
40
49
  "keywords": [
41
50
  "react-native",
42
51
  "watchos",
@@ -74,21 +83,21 @@
74
83
  "globals": "^15.14.0",
75
84
  "prettier": "^3.6.2",
76
85
  "react": "19.1.0",
77
- "react-reconciler": "0.32.0",
78
86
  "release-it": "^19.0.4",
79
87
  "typescript": "^5.9.2"
80
88
  },
81
89
  "dependencies": {
82
90
  "@expo/config-plugins": "^54.0.0",
91
+ "react-reconciler": "0.32.0",
83
92
  "whatwg-fetch": "^3.6.20"
84
93
  },
85
94
  "peerDependencies": {
86
95
  "@bacons/apple-targets": ">=4.0.0",
87
96
  "expo": ">=54.0.0",
88
97
  "react": "*",
89
- "react-native": "*",
90
- "react-reconciler": "*"
98
+ "react-native": "*"
91
99
  },
100
+ "packageManager": "pnpm@10.10.0",
92
101
  "commitlint": {
93
102
  "extends": [
94
103
  "@commitlint/config-conventional"
@@ -107,6 +116,7 @@
107
116
  },
108
117
  "plugins": {
109
118
  "@release-it/conventional-changelog": {
119
+ "ignoreRecommendedBump": true,
110
120
  "preset": {
111
121
  "name": "angular"
112
122
  }
@@ -119,13 +129,5 @@
119
129
  "tabWidth": 2,
120
130
  "trailingComma": "es5",
121
131
  "useTabs": false
122
- },
123
- "scripts": {
124
- "build:xcframework": "./scripts/build-xcframework.sh",
125
- "lint": "eslint .",
126
- "clean": "del-cli build/xcframework example/ios",
127
- "docs": "pnpm --filter react-native-watchos-docs start",
128
- "docs:build": "pnpm --filter react-native-watchos-docs build",
129
- "release": "release-it --only-version"
130
132
  }
131
- }
133
+ }
@@ -49,6 +49,7 @@ const withWatchTurboModuleCodegen = require('./withWatchTurboModuleCodegen');
49
49
  * @type {import('@expo/config-plugins').ConfigPlugin<ReactNativeWatchOSPluginOpts | void>}
50
50
  */
51
51
  const withReactNativeWatchOS = (config, opts) => {
52
+ assertPluginOrder(config);
52
53
  const targetName = (opts && opts.targetName) || 'watch';
53
54
  const bundleName = (opts && opts.bundleName) || 'main';
54
55
  const entryFile = opts && opts.entryFile;
@@ -65,5 +66,42 @@ const withReactNativeWatchOS = (config, opts) => {
65
66
  ]);
66
67
  };
67
68
 
69
+ /**
70
+ * @param {{ plugins?: Array<string | [string, unknown] | unknown> }} config
71
+ */
72
+ function assertPluginOrder(config) {
73
+ const plugins = Array.isArray(config && config.plugins)
74
+ ? config.plugins
75
+ : null;
76
+ if (!plugins) return;
77
+
78
+ /** @param {unknown} e */
79
+ const nameOf = (e) =>
80
+ typeof e === 'string' ? e : Array.isArray(e) ? e[0] : null;
81
+ const ourIdx = plugins.findIndex(
82
+ (e) => nameOf(e) === '@appsent-co/react-native-watchos'
83
+ );
84
+ const baconsIdx = plugins.findIndex(
85
+ (e) => nameOf(e) === '@bacons/apple-targets'
86
+ );
87
+ if (ourIdx < 0) return;
88
+
89
+ if (baconsIdx < 0) {
90
+ throw new Error(
91
+ '[@appsent-co/react-native-watchos] requires "@bacons/apple-targets" ' +
92
+ 'to be present in your Expo plugins array (before this plugin). ' +
93
+ 'Run `npx react-native-watchos init` to wire it up.'
94
+ );
95
+ }
96
+ if (baconsIdx > ourIdx) {
97
+ throw new Error(
98
+ '[@appsent-co/react-native-watchos] must come AFTER ' +
99
+ '"@bacons/apple-targets" in your Expo plugins array, so the watch ' +
100
+ 'target exists when this plugin patches the pbxproj. ' +
101
+ 'Run `npx react-native-watchos init` to fix the order.'
102
+ );
103
+ }
104
+ }
105
+
68
106
  module.exports = withReactNativeWatchOS;
69
107
  module.exports.default = withReactNativeWatchOS;