@yahoo/uds 0.1.13 → 0.1.15

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.
Files changed (57) hide show
  1. package/cli/README.md +57 -5
  2. package/cli/commands/expo/_setup.ts +100 -73
  3. package/cli/commands/expo/build.ts +84 -3
  4. package/cli/commands/expo/dev.ts +51 -41
  5. package/cli/commands/expo/install/cocoapods.rb +35 -0
  6. package/cli/commands/purge.ts +15 -0
  7. package/cli/commands/{config/sync.ts → sync.ts} +2 -2
  8. package/cli/utils/configWorker.ts +21 -1
  9. package/cli/utils/purgeCSS.ts +139 -0
  10. package/cli/utils/setupConfigWorker.ts +12 -13
  11. package/dist/{chunk-P7GR6E3K.js → chunk-AHFH5E5L.js} +1 -1
  12. package/dist/{chunk-MBOOJIH7.js → chunk-FLBMVDKG.js} +1 -1
  13. package/dist/{chunk-AWTLI4D3.js → chunk-U3UPAQ7V.js} +1 -1
  14. package/dist/fixtures/index.cjs +1 -1
  15. package/dist/fixtures/index.d.cts +2 -2
  16. package/dist/fixtures/index.d.ts +2 -2
  17. package/dist/fixtures/index.js +1 -1
  18. package/dist/index.cjs +1 -1
  19. package/dist/index.d.cts +40 -31
  20. package/dist/index.d.ts +40 -31
  21. package/dist/index.js +1 -1
  22. package/dist/{index.native-9kYJrUPa.d.ts → index.native-TvtXtTXg.d.ts} +2 -2
  23. package/dist/{index.native-3ww4C4UV.d.cts → index.native-dgGFONLf.d.cts} +2 -2
  24. package/dist/index.native.cjs +1 -1
  25. package/dist/index.native.d.cts +10 -24
  26. package/dist/index.native.d.ts +10 -24
  27. package/dist/index.native.js +1 -1
  28. package/dist/tailwindPlugin.cjs +1 -1
  29. package/dist/tailwindPlugin.d.cts +1 -1
  30. package/dist/tailwindPlugin.d.ts +1 -1
  31. package/dist/tailwindPlugin.js +1 -1
  32. package/dist/tailwindPurge.cjs +4 -0
  33. package/dist/tailwindPurge.d.cts +16 -0
  34. package/dist/tailwindPurge.d.ts +16 -0
  35. package/dist/tailwindPurge.js +4 -0
  36. package/dist/tokens/index.cjs +1 -1
  37. package/dist/tokens/index.d.cts +3 -3
  38. package/dist/tokens/index.d.ts +3 -3
  39. package/dist/tokens/index.js +1 -1
  40. package/dist/tokens/index.native.cjs +1 -1
  41. package/dist/tokens/index.native.d.cts +2 -2
  42. package/dist/tokens/index.native.d.ts +2 -2
  43. package/dist/tokens/index.native.js +1 -1
  44. package/dist/tokens/parseTokens.cjs +1 -1
  45. package/dist/tokens/parseTokens.d.cts +12 -12
  46. package/dist/tokens/parseTokens.d.ts +12 -12
  47. package/dist/tokens/parseTokens.js +1 -1
  48. package/dist/tokens/parseTokens.native.d.cts +2 -2
  49. package/dist/tokens/parseTokens.native.d.ts +2 -2
  50. package/dist/{types-J4DLS6Xj.d.cts → types-3GXulqnG.d.cts} +1 -1
  51. package/dist/{types-J4DLS6Xj.d.ts → types-3GXulqnG.d.ts} +1 -1
  52. package/dist/{types-hirL9Qk5.d.cts → types-8OHfDki5.d.cts} +47 -54
  53. package/dist/{types-hirL9Qk5.d.ts → types-8OHfDki5.d.ts} +47 -54
  54. package/package.json +21 -18
  55. package/cli/commands/config/config.ts +0 -10
  56. package/cli/commands/nextjs/dev.ts +0 -17
  57. package/cli/commands/nextjs/nextjs.ts +0 -10
package/cli/README.md CHANGED
@@ -1,16 +1,68 @@
1
- # UDS ClI
1
+ # UDS Cli
2
2
 
3
3
  We leverage Bluebun, which is a CLI framework inspired by [Gluegun](https://github.com/infinitered/gluegun), but specifically designed to be used with [Bun](https://bun.sh), the new JS runtime.
4
4
 
5
5
  Bluebun relies on Bun APIs and is designed to be extremely fast, with no-dependencies.
6
6
 
7
- # Standalone executable
8
-
9
- We use Bun to build our standalone executable. See [Bun build docs](https://bun.sh/docs/bundler/executables) for more details. The uds package.json's `build:cli` script handles building the CLI. The bin is output to the `dist/uds` binary file and this path is defined in the package.json's bin field.
7
+ > Trying to add a new command? Please see the "Adding a Command" section below
10
8
 
11
9
  # Commands
12
10
 
13
- ## Command structure
11
+ In any consumer of `@yahoo/uds` the following commands are available:
12
+
13
+ > Please note: If you are _not_ running the CLI from a package.json script you will need to add `bun` before the binary in order to run it directly. i.e. `bun uds purge`
14
+
15
+ ## Config
16
+
17
+ ### Using args
18
+
19
+ | Arg | Required | Default |
20
+ | ------- | -------- | --------------- |
21
+ | id | true | |
22
+ | outFile | false | ./uds.config.ts |
23
+
24
+ ```shell
25
+ uds sync --id [id] --outFile [path]
26
+ ```
27
+
28
+ ```shell
29
+ uds sync --id [id]
30
+ ```
31
+
32
+ ### Using ENV vars
33
+
34
+ | Env Var | Required | Default |
35
+ | ------------ | -------- | --------------- |
36
+ | UDS_ID | true | |
37
+ | UDS_OUT_FILE | false | ./uds.config.ts |
38
+
39
+ ```shell
40
+ UDS_ID=[id] uds sync --outFile [path]
41
+ ```
42
+
43
+ ```shell
44
+ UDS_ID=[id] UDS_OUT_FILE=[path] uds sync
45
+ ```
46
+
47
+ ## Expo
48
+
49
+ ```shell
50
+ uds expo build --profile [profile] --platform [ios|android]
51
+ uds expo dev --profile [profile] --platform [ios|android]
52
+ uds expo launch --profile [profile] --platform [ios|android]
53
+ uds expo update --profile [profile] --platform [ios|android]
54
+ uds expo --help
55
+ ```
56
+
57
+ ## Purge CSS
58
+
59
+ ```shell
60
+ uds purge
61
+ ```
62
+
63
+ ## Adding a command
64
+
65
+ > Please note: Adding nested commands, i.e. uds expo dev, appears to not work correctly when UDS is consumed from npm. As a workaround, please see code for expo/expo.ts for re-routing sub-commands from the root command file. To verify your CLI command works correctly you should run `npm pack` within the packages/uds directory. Once you have your generated tarball you should copy that tarball to a test application such as https://github.com/yahoo-design/uds-nextjs-demo, then add `"@yahoo/uds": "file:./tarball-generated-from-npm-pack.tgz` to it's dependencies and run an install. Now you should be able to run `bun uds [your command name]` to test your functionality.
14
66
 
15
67
  Commands are organized in a tree structure. The root command is the name of the CLI (uds), and then we can have subcommands under that, and subcommands under those, and so on.
16
68
 
@@ -1,22 +1,23 @@
1
1
  import { EasJsonAccessor, EasJsonUtils, Platform } from '@expo/eas-json';
2
2
  import { type Props, print } from 'bluebun';
3
- import { $ } from 'bun';
3
+ import { $, semver, which } from 'bun';
4
+ import fs from 'node:fs';
5
+ import os from 'node:os';
4
6
 
5
7
  export interface MobileProps extends Props {
6
8
  options: {
7
9
  profile: string;
8
- platform: Platform.IOS | Platform.ANDROID;
10
+ platform: Platform;
9
11
  jsEngine?: 'hermes' | 'jsc';
10
12
  debug?: boolean;
11
13
  };
12
14
  }
13
15
 
14
- async function needsBinary(lib: string) {
15
- const whichLib = await $`which ${lib}`.text();
16
- return whichLib.includes('not found');
16
+ export async function needsBinary(lib: string) {
17
+ return which(lib) === null;
17
18
  }
18
19
 
19
- async function needsBrewFormula(formula: string) {
20
+ export async function needsBrewFormula(formula: string) {
20
21
  return (await $`brew list --formula | grep ${formula} | wc -l`.text()).trim() === '0';
21
22
  }
22
23
 
@@ -28,87 +29,108 @@ export async function setup({
28
29
  env?: Partial<typeof Bun.env>;
29
30
  }) {
30
31
  const { profile, platform, jsEngine = 'hermes', debug = false } = props.options;
32
+ const isIOS = platform === Platform.IOS;
33
+ const isAndroid = platform === Platform.ANDROID;
34
+ const pathAsString = await $`echo $PATH`.text();
35
+ const needsBunInPath = !pathAsString.includes('.bun');
31
36
 
32
37
  /* -------------------------------------------------------------------------- */
33
- /* HOMEBREW SETUP */
38
+ /* VERIFY BUN IS IN PATH */
34
39
  /* -------------------------------------------------------------------------- */
35
- const needsBrew = await needsBinary('brew');
36
- if (needsBrew) {
37
- console.log('Installing brew...');
38
- await $`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`;
39
- console.log('Installing brew...Complete');
40
-
40
+ if (needsBunInPath) {
41
41
  /**
42
- * This portion of logic was ported from the homebrew installer script
43
- * https://github.com/Homebrew/install/blob/master/install.sh#L153C5-L153C63.
44
- * However, their script just alerts users to the fact that they need to add
45
- * brew to their path, but doesn't do it for them. This logic attempts to do that
46
- * so we can automatically proceed to the next steps of auto-installing
47
- * the necessary dependencies for local development.
42
+ * If BUN_INSTALL is not in the path, we need to add it.
43
+ * https://bun.sh/docs/installation#checking-installation:~:text=shell%27s%20configuration%20file.
48
44
  */
49
- const machineArch = await $`uname -m`.text();
50
- const isLinux = (await $`uname`.text()) === 'Linux';
51
- const shellType = await $`echo $SHELL`.text();
52
- const homebrewPrefix = machineArch.includes('arm64') ? '/opt/homebrew' : '/usr/local';
53
- let shellRcFile;
54
-
55
- if (isLinux) {
56
- shellRcFile = shellType.includes('zsh') ? '.zshrc' : '.bashrc';
57
- } else {
58
- shellRcFile = shellType.includes('zsh') ? '.zprofile' : '.bash_profile';
59
- }
60
-
61
- const shellRcPath = `${Bun.env.HOME}/${shellRcFile}`;
62
- let shellRcContents = '';
63
- try {
64
- shellRcContents = await Bun.file(shellRcPath).text();
65
- } catch (err) {
66
- // Ignore if it doesn't exist. We create it below.
67
- }
68
-
69
- if (!shellRcContents.includes('homebrew')) {
70
- console.log('Adding brew to $PATH...');
71
- const newShellRcContents = `${shellRcContents}\neval "$(${homebrewPrefix}/bin/brew shellenv)"`;
72
- await Bun.write(shellRcPath, newShellRcContents);
73
- }
45
+ console.write(
46
+ 'BUN is not installed globally or is not available in your $PATH. Adding to your $PATH...',
47
+ );
48
+ const whichShell = await $`echo $SHELL`.text();
49
+ const homeDirectory = os.homedir();
50
+ const shellRcFile = whichShell.includes('zsh') ? '.zshrc' : '.bashrc';
51
+ fs.appendFileSync(
52
+ `${homeDirectory}/${shellRcFile}`,
53
+ '\n# bun\nexport BUN_INSTALL="$HOME/.bun"\nexport PATH="$BUN_INSTALL/bin:$PATH"',
54
+ );
55
+ await $`source $HOME/${shellRcFile}`;
74
56
  }
75
57
 
76
58
  /* -------------------------------------------------------------------------- */
77
- /* FASTLANE SETUP */
59
+ /* XCODE SETUP */
78
60
  /* -------------------------------------------------------------------------- */
79
- const needsFastlane = await needsBrewFormula('fastlane');
80
- if (needsFastlane) {
81
- console.log('Installing fastlane...');
82
- await $`brew install fastlane`;
83
- }
61
+ if (isIOS) {
62
+ const xcodePath = await $`xcode-select -p`.text();
63
+
64
+ if (xcodePath) {
65
+ const isInvalidXcodePath = !xcodePath.startsWith('/Applications');
66
+
67
+ if (isInvalidXcodePath) {
68
+ console.write(`
69
+ /* -------------------------------------------------------------------------- */
70
+ /* XCODDE PATH IS INVALID */
71
+ /* -------------------------------------------------------------------------- */
72
+
73
+ Run the following command to set the correct path to XCode:
74
+
75
+ sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
76
+ `);
77
+ }
78
+ } else {
79
+ console.write(`
80
+ /* -------------------------------------------------------------------------- */
81
+ /* XCODE NOT INSTALLED */
82
+ /* -------------------------------------------------------------------------- */
83
+
84
+ You must have XCode installed before continuing...
85
+
86
+ Visit https://apps.apple.com/us/app/xcode/id497799835?mt=12 to download.
87
+ `);
88
+ throw new Error('XCode not installed');
89
+ }
84
90
 
85
- /* -------------------------------------------------------------------------- */
86
- /* COCOAPODS SETUP */
87
- /* -------------------------------------------------------------------------- */
88
- const needsCocoapods = await needsBrewFormula('cocoapods');
89
- if (needsCocoapods) {
90
- console.log('Installing cocoapods...');
91
- await $`brew install cocoapods`;
91
+ /**
92
+ * Installing the XCode command line tools & simulators normally
93
+ * requires opening up XCode to install.
94
+ * This conditional is to avoid having to do that.
95
+ *
96
+ * If xcode command line tools and simulators have already been installed, that's
97
+ * fine. This command is really fast and will continue running if already available.
98
+ *
99
+ * https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes#Install-and-manage-Simulator-runtimes-from-the-command-line
100
+ */
101
+ await $`xcodebuild -runFirstLaunch`;
102
+ await $`xcodebuild -downloadPlatform iOS`;
92
103
  }
93
104
 
94
105
  /* -------------------------------------------------------------------------- */
95
106
  /* JAVA DEVELOPMENT KIT */
96
107
  /* -------------------------------------------------------------------------- */
97
- if (platform === 'android') {
108
+ if (isAndroid) {
98
109
  const jdkPath = await $`brew info --cask zulu17`.text();
99
110
  const needsJdk = jdkPath.includes('unavailable');
100
111
  if (needsJdk) {
101
- console.log('Installing Java Development Kit for Android...');
112
+ console.write('Installing Java Development Kit for Android...');
102
113
  await $`brew install --cask zulu17`;
103
114
  }
104
115
  }
105
116
 
106
117
  /* -------------------------------------------------------------------------- */
107
- /* EXPO SETUP */
118
+ /* EAS CONFIG SETUP */
108
119
  /* -------------------------------------------------------------------------- */
120
+ /**
121
+ * eas.json is used to define build profiles for different environments.
122
+ * For example, you may have a profile for development, staging, and production.
123
+ *
124
+ * The EAS CLI is used to build and run an app locally based on the eas.json,
125
+ * but it isn't opinionated about how you define your profiles.
126
+ *
127
+ * We however want to enforce some conventions to make it easier to get started.
128
+ * We also want to store our builds outside of EAS ecosystem since they auto
129
+ * delete after 30 days & our builds infrequently.
130
+ *
131
+ */
109
132
  const appDirectory = Bun.env.PWD;
110
133
  const easJsonAccessor = EasJsonAccessor.fromProjectPath(appDirectory);
111
-
112
134
  const easProfiles = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
113
135
 
114
136
  if (!easProfiles.includes(profile)) {
@@ -126,20 +148,22 @@ export async function setup({
126
148
  }
127
149
 
128
150
  const easJsonConfig = await EasJsonUtils.getBuildProfileAsync(easJsonAccessor, platform, profile);
129
-
130
151
  const easCliConfig = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
131
152
  const easCliVersion = easCliConfig?.version ?? 'latest';
132
153
 
133
154
  /* -------------------------------------------------------------------------- */
134
155
  /* EAS CLI SETUP */
135
156
  /* -------------------------------------------------------------------------- */
157
+ /**
158
+ * EAS CLI is used to build and run an app locally.
159
+ */
136
160
  const needsEasCli = await needsBinary('eas');
137
161
  if (needsEasCli) {
138
- await $`bun install eas-cli@${easCliVersion} -g`;
162
+ await $`bun install eas-cli@${easCliVersion} -g`.quiet();
139
163
  } else {
140
164
  const currentEasCliVersion = await $`eas --version`.text();
141
- if (currentEasCliVersion !== easCliVersion) {
142
- await $`bun install eas-cli@${easCliVersion} -g`;
165
+ if (!semver.satisfies(currentEasCliVersion, easCliVersion)) {
166
+ await $`bun install eas-cli@${easCliVersion} -g`.quiet();
143
167
  }
144
168
  }
145
169
 
@@ -150,7 +174,7 @@ export async function setup({
150
174
  } = easJsonConfig.env ?? {};
151
175
 
152
176
  // TODO: Add additional checks for ensuring this value adheres to Android formatting specs
153
- if (platform === Platform.ANDROID && !androidId) {
177
+ if (isAndroid && !androidId) {
154
178
  throw new Error(`
155
179
  APP_ANDROID_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
156
180
  See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
@@ -174,7 +198,7 @@ export async function setup({
174
198
  }
175
199
 
176
200
  // TODO: Add additional checks for ensuring this value adheres to Uniform Type Identifier
177
- if (platform === Platform.IOS && !appleId) {
201
+ if (isIOS && !appleId) {
178
202
  throw new Error(`
179
203
  APP_APPLE_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
180
204
  See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
@@ -197,11 +221,13 @@ export async function setup({
197
221
  }
198
222
 
199
223
  const outputName = `${platform}-${profile}-${jsEngine}`;
200
- // TODO: make this configurable
201
- const prebuildsDir = `${appDirectory}/prebuilds`;
224
+ const prebuildsDir = `${appDirectory}/prebuilds`; // TODO: make this configurable
202
225
  const outputDir = `${prebuildsDir}/${outputName}`;
203
226
  const outputFileBase = `${outputDir}/${outputName}`;
204
227
 
228
+ /* -------------------------------------------------------------------------- */
229
+ /* ENVIRONMENT VARIABLES */
230
+ /* -------------------------------------------------------------------------- */
205
231
  let envVars = Bun.env;
206
232
 
207
233
  envVars = {
@@ -226,6 +252,7 @@ export async function setup({
226
252
  envVars = { ...envVars, ...envOpts };
227
253
  }
228
254
 
255
+ /** Change the default environment variables for shells created by this instance. */
229
256
  $.env(envVars);
230
257
 
231
258
  const output = {
@@ -233,10 +260,10 @@ export async function setup({
233
260
  dir: outputDir,
234
261
  prebuildsDir: prebuildsDir,
235
262
  fileBase: outputFileBase,
236
- artifact: platform === 'ios' ? `${outputFileBase}.tar.gz` : `${outputFileBase}.zip`,
237
- app: platform === 'ios' ? `${outputFileBase}.app` : 'todo fix android',
263
+ artifact: isIOS ? `${outputFileBase}.tar.gz` : `${outputFileBase}.zip`,
264
+ app: isIOS ? `${outputFileBase}.app` : 'todo fix android',
238
265
  get launchFile() {
239
- return platform === 'ios' ? this.artifact : this.apk.signed;
266
+ return isIOS ? this.artifact : this.apk.signed;
240
267
  },
241
268
  apk: {
242
269
  contents: `${outputFileBase}/build`,
@@ -248,7 +275,7 @@ export async function setup({
248
275
  };
249
276
 
250
277
  return {
251
- scheme: platform === 'ios' ? appleId : androidId,
278
+ scheme: isIOS ? appleId : androidId,
252
279
  channel: easJsonConfig.channel,
253
280
  debug,
254
281
  profile,
@@ -1,6 +1,5 @@
1
- import { $ } from 'bun';
2
- import type { MobileProps } from './_setup';
3
- import { setup } from './_setup';
1
+ import { $, semver } from 'bun';
2
+ import { setup, needsBinary, needsBrewFormula, type MobileProps } from './_setup';
4
3
 
5
4
  export default {
6
5
  name: 'build',
@@ -10,6 +9,88 @@ export default {
10
9
  props,
11
10
  });
12
11
 
12
+ /* ----- Homebrew, Fastlane, and Cocoapods are only required for builds. ---- */
13
+ /* -------------------------------------------------------------------------- */
14
+ /* HOMEBREW SETUP */
15
+ /* -------------------------------------------------------------------------- */
16
+ const needsBrew = await needsBinary('brew');
17
+ if (needsBrew) {
18
+ console.write('Installing brew...');
19
+ await $`/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`;
20
+ console.write('Installing brew...Complete');
21
+
22
+ /**
23
+ * This logic was ported from the homebrew installer script
24
+ * https://github.com/Homebrew/install/blob/master/install.sh#L153C5-L153C63.
25
+ * However, their script just alerts users to the fact that they need to add
26
+ * brew to their path, but doesn't do it for them. This logic attempts to do that
27
+ * so we can automatically proceed to the next steps of auto-installing
28
+ * the necessary dependencies for local development.
29
+ */
30
+ const machineArch = await $`uname -m`.text();
31
+ const isLinux = (await $`uname`.text()) === 'Linux';
32
+ const shellType = await $`echo $SHELL`.text();
33
+ const homebrewPrefix = machineArch.includes('arm64') ? '/opt/homebrew' : '/usr/local';
34
+ let shellRcFile;
35
+
36
+ if (isLinux) {
37
+ shellRcFile = shellType.includes('zsh') ? '.zshrc' : '.bashrc';
38
+ } else {
39
+ shellRcFile = shellType.includes('zsh') ? '.zprofile' : '.bash_profile';
40
+ }
41
+
42
+ const shellRcPath = `${Bun.env.HOME}/${shellRcFile}`;
43
+ let shellRcContents = '';
44
+ try {
45
+ shellRcContents = await Bun.file(shellRcPath).text();
46
+ } catch (err) {
47
+ // Ignore if it doesn't exist. We create it below.
48
+ }
49
+
50
+ if (!shellRcContents.includes('homebrew')) {
51
+ console.write('Adding brew to $PATH...');
52
+ const newShellRcContents = `${shellRcContents}\neval "$(${homebrewPrefix}/bin/brew shellenv)"`;
53
+ await Bun.write(shellRcPath, newShellRcContents);
54
+ }
55
+ }
56
+
57
+ /* -------------------------------------------------------------------------- */
58
+ /* FASTLANE SETUP */
59
+ /* -------------------------------------------------------------------------- */
60
+ const needsFastlane = await needsBrewFormula('fastlane');
61
+ if (needsFastlane) {
62
+ console.write('Installing fastlane...');
63
+ await $`brew install fastlane`;
64
+ }
65
+
66
+ /* -------------------------------------------------------------------------- */
67
+ /* COCOAPODS SETUP */
68
+ /* -------------------------------------------------------------------------- */
69
+ const needsCocoapods = await needsBrewFormula('cocoapods');
70
+ /** https://github.com/facebook/react-native/issues/42698#issuecomment-1915670708 */
71
+ const validCocoapodsVersion = '1.14.3';
72
+
73
+ const installCocoapods = async () =>
74
+ await $`brew install ${import.meta.dirname}/install/cocoapods.rb`;
75
+
76
+ if (needsCocoapods) {
77
+ console.write('Installing cocoapods...');
78
+ await installCocoapods();
79
+ } else {
80
+ const cocoapodsVersion = await $`pod --version`.text();
81
+ const needsDowngrade = semver.satisfies(cocoapodsVersion, `>${validCocoapodsVersion}`);
82
+ if (needsDowngrade) {
83
+ await $`brew unlink cocoapods`;
84
+ await $`brew uninstall cocoapods`;
85
+ console.write(
86
+ `Downgrading cocoapods from ${cocoapodsVersion} to ${validCocoapodsVersion}...`,
87
+ );
88
+ await installCocoapods();
89
+ await $`brew link cocoapods`;
90
+ }
91
+ }
92
+
13
93
  await $`eas build --local --non-interactive --json --clear-cache --platform ${platform} --profile ${profile} --output ${output.artifact}`;
94
+ console.write('You can now run your dev command to start the app!');
14
95
  },
15
96
  };
@@ -1,5 +1,5 @@
1
1
  import { Props } from 'bluebun';
2
- import { $ } from 'bun';
2
+ import { $, sleep } from 'bun';
3
3
  import { setup, type MobileProps } from './_setup';
4
4
 
5
5
  interface MobileStartProps extends Props {
@@ -12,7 +12,7 @@ interface MobileStartProps extends Props {
12
12
  type SimDevice = { udid: string; name: string; state: 'Booted' | 'Shutdown' };
13
13
  type SimList = {
14
14
  devices: {
15
- ['com.apple.CoreSimulator.SimRuntime.iOS-17-2']: SimDevice[];
15
+ [key: string]: SimDevice[];
16
16
  };
17
17
  };
18
18
 
@@ -20,7 +20,6 @@ export default {
20
20
  name: 'dev',
21
21
  description: '🚧 Dev',
22
22
  run: async (props: MobileStartProps) => {
23
- console.log('running dev');
24
23
  const { platform, scheme, output } = await setup({
25
24
  props,
26
25
  env: {
@@ -28,52 +27,63 @@ export default {
28
27
  },
29
28
  });
30
29
 
31
- if (platform === 'ios') {
32
- const xcodePath = await $`xcode-select -p`.text();
33
- const xcodeExists = xcodePath.startsWith('/Applications');
30
+ const extraArgs = [];
34
31
 
35
- if (xcodeExists) {
36
- await $`open -a simulator`;
37
- const deviceList = (await $`xcrun simctl list devices available -j -e`.json()) as SimList;
32
+ if (props.options.clear) {
33
+ extraArgs.push('--clear');
34
+ }
35
+ const extrArgsString = extraArgs.join(' ');
38
36
 
37
+ try {
38
+ /**
39
+ * Get list of devices.
40
+ * If there is a booted device, open the simulator on that device.
41
+ * If no booted device is found, open the simulator on iPhone 15 Pro Max.
42
+ * * https://github.com/expo/orbit/blob/main/packages/eas-shared/src/run/ios/simulator.ts#L98
43
+ *
44
+ * TODO:
45
+ * Should we open app on all booted devices if there are multiple open?
46
+ * Should we prompt user to pick from one of the booted devices?
47
+ * Should we prompt user to pick a device if there are no booted devices?
48
+ */
49
+ if (platform === 'ios') {
50
+ const deviceList =
51
+ (await $`xcrun simctl list devices available --json -e`.json()) as SimList;
39
52
  const devices = Object.values(deviceList?.devices).flatMap((item) => item);
53
+ const bootedDevice = devices.find((device: SimDevice) => device.state === 'Booted');
54
+ if (!bootedDevice) {
55
+ const iphone15ProMax = devices.find((device) => device.name === 'iPhone 15 Pro Max');
56
+ const iphone15Udid = iphone15ProMax?.udid;
57
+ await $`open -a Simulator --args -CurrentDeviceUDID ${iphone15Udid}`;
58
+ }
59
+ const appContainerPath =
60
+ await $`xcrun simctl get_app_container booted ${scheme} data`.text();
40
61
 
41
- if (devices.length === 0) {
42
- throw new Error('No devices found');
43
- } else {
44
- const isBooted = devices.find((device: SimDevice) => device.state === 'Booted');
45
- if (!isBooted) {
46
- const iphone15Max = devices.find(
47
- (device: SimDevice) => device.name === 'iPhone 15 Pro Max',
48
- );
49
- if (!iphone15Max) {
50
- throw new Error('iPhone 15 Pro Max not found');
51
- } else {
52
- const udid = iphone15Max.udid;
53
- await $`xcrun simctl boot ${udid}`;
54
- }
55
- }
62
+ if (!appContainerPath) {
63
+ console.write('App not installed on booted device. Installing...');
64
+ await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
56
65
  }
57
- } else {
58
- console.log(
59
- 'You must have XCode installed before continuing... Visit https://apps.apple.com/us/app/xcode/id497799835?mt=12 to download.',
60
- );
61
- throw new Error('XCode not installed');
66
+ /**
67
+ * In dev mode we use a debug build, which requires a metro server to serve
68
+ * the Javascript bundle.
69
+ *
70
+ * To avoid the, "Could not find metro server" error screen on launch,
71
+ * we delay the launch of the app until after the metro server has started.
72
+ */
62
73
  }
63
- }
64
74
 
65
- await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
75
+ sleep(3000).then(async () => {
76
+ /**
77
+ * Launch the app on the booted device
78
+ * https://github.com/expo/eas-cli?tab=readme-ov-file#eas-buildrun
79
+ */
80
+ await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
81
+ });
66
82
 
67
- const extraArgs = [];
68
-
69
- if (props.options.clear) {
70
- extraArgs.push('--clear');
83
+ console.write('Starting Metro server...');
84
+ await $`expo start --${platform} --dev-client --localhost --scheme ${scheme} ${extrArgsString}`;
85
+ } catch (err) {
86
+ throw err;
71
87
  }
72
-
73
- const extrArgsString = extraArgs.join(' ');
74
-
75
- console.log(`Starting metro server...`);
76
-
77
- await $`expo start --${platform} --dev-client --localhost --scheme ${scheme} ${extrArgsString}`;
78
88
  },
79
89
  };
@@ -0,0 +1,35 @@
1
+ class Cocoapods < Formula
2
+ desc "Dependency manager for Cocoa projects"
3
+ homepage "https://cocoapods.org/"
4
+ url "https://github.com/CocoaPods/CocoaPods/archive/refs/tags/1.14.3.tar.gz"
5
+ sha256 "de05766e5771e0cef7af89f73b0e42a1f1c52a76ce1288592cd9511bcd688a9e"
6
+ license "MIT"
7
+ revision 1
8
+
9
+ bottle do
10
+ sha256 cellar: :any, arm64_sonoma: "0fb8e638fb4901b6c578c44ae1af0098a0b3530e7a339bf43f2fb67f2819d412"
11
+ sha256 cellar: :any, arm64_ventura: "e3d0c8624df429cb30c5cf818f3a358d4f678b374410e9fbc8fde090889f9b61"
12
+ sha256 cellar: :any, arm64_monterey: "a6df519bae3f51b1609cfcd017b4d47cb688200780ffb9b27d57a5dc05ea93de"
13
+ sha256 cellar: :any, sonoma: "5c2ee41824fcb154b46f9fa967f203fbf9009d2051f8c898375d69d333052988"
14
+ sha256 cellar: :any, ventura: "91459cb108161201a81fdd0e96a126e9843be8213112c208051e8e72ce9736f9"
15
+ sha256 cellar: :any, monterey: "316b0954e21f76c013d8c581c589e4f884231687eb2253a9f9a38a77a87728a6"
16
+ sha256 cellar: :any_skip_relocation, x86_64_linux: "2d5be1290e8161d9a49b3fd191fc8423aac29f61fbc84148c8ba08cdc03d8d84"
17
+ end
18
+
19
+ depends_on "pkg-config" => :build
20
+ depends_on "ruby"
21
+ uses_from_macos "libffi", since: :catalina
22
+
23
+ def install
24
+ ENV["GEM_HOME"] = libexec
25
+ system "gem", "build", "cocoapods.gemspec"
26
+ system "gem", "install", "cocoapods-#{version}.gem"
27
+ # Other executables don't work currently.
28
+ bin.install libexec/"bin/pod", libexec/"bin/xcodeproj"
29
+ bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV["GEM_HOME"])
30
+ end
31
+
32
+ test do
33
+ system "#{bin}/pod", "list"
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ import { Props, spinStart, spinStop } from 'bluebun';
2
+
3
+ import { purge } from '../utils/purgeCSS';
4
+
5
+ export default {
6
+ name: 'purge',
7
+ description: `Purge unused CSS`,
8
+ run: async (props: Props) => {
9
+ spinStart('Purging css...');
10
+
11
+ await purge();
12
+
13
+ spinStop('✅ Purging css done!');
14
+ },
15
+ };
@@ -1,6 +1,6 @@
1
1
  import { Props } from 'bluebun';
2
- import { setupConfigWorker } from '../../utils/setupConfigWorker';
3
- import { SyncOptions } from '../../utils/types';
2
+ import { setupConfigWorker } from '../utils/setupConfigWorker';
3
+ import { SyncOptions } from '../utils/types';
4
4
 
5
5
  interface SyncProps extends Props {
6
6
  options: SyncOptions;