@yahoo/uds 0.1.12 → 0.1.14

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 (93) hide show
  1. package/cli/README.md +135 -0
  2. package/cli/commands/config/config.ts +10 -0
  3. package/cli/commands/config/sync.ts +22 -0
  4. package/cli/commands/expo/_setup.ts +244 -0
  5. package/cli/commands/expo/build.ts +96 -0
  6. package/cli/commands/expo/dev.ts +82 -0
  7. package/cli/commands/expo/expo.ts +23 -0
  8. package/cli/commands/expo/install/cocoapods.rb +35 -0
  9. package/cli/commands/expo/launch.ts +15 -0
  10. package/cli/commands/expo/update.ts +16 -0
  11. package/cli/commands/nextjs/dev.ts +17 -0
  12. package/cli/commands/nextjs/nextjs.ts +10 -0
  13. package/cli/commands/uds.ts +10 -0
  14. package/cli/commands/version.ts +11 -0
  15. package/cli/env.d.ts +15 -0
  16. package/cli/eslint.config.mjs +8 -0
  17. package/cli/tsconfig.json +10 -0
  18. package/cli/uds-cli +7 -0
  19. package/cli/utils/configWorker.ts +9 -0
  20. package/cli/utils/getCommandHelp.ts +65 -0
  21. package/cli/utils/setupConfigWorker.ts +81 -0
  22. package/cli/utils/sortKeys.ts +27 -0
  23. package/cli/utils/types.ts +13 -0
  24. package/dist/{chunk-JKZI2WLD.js → chunk-AWTLI4D3.js} +1 -1
  25. package/dist/chunk-D4K3CXV6.js +0 -0
  26. package/dist/{chunk-Z34QGHWU.js → chunk-MBOOJIH7.js} +1 -1
  27. package/dist/chunk-MFA2Y7DA.js +1 -0
  28. package/dist/chunk-P7GR6E3K.js +1 -0
  29. package/dist/chunk-PQBOZFJV.js +1 -0
  30. package/dist/index.cjs +1 -1
  31. package/dist/index.d.cts +7 -40
  32. package/dist/index.d.ts +7 -40
  33. package/dist/index.js +1 -1
  34. package/dist/{index.native-VVqy3X9H.d.cts → index.native-3ww4C4UV.d.cts} +1 -1
  35. package/dist/{index.native-DzfcCYUh.d.ts → index.native-9kYJrUPa.d.ts} +1 -1
  36. package/dist/index.native.cjs +1 -0
  37. package/dist/index.native.d.cts +1493 -0
  38. package/dist/index.native.d.ts +1493 -0
  39. package/dist/index.native.js +1 -0
  40. package/dist/tailwindPlugin.cjs +1 -1
  41. package/dist/tailwindPlugin.d.cts +1 -1
  42. package/dist/tailwindPlugin.d.ts +1 -1
  43. package/dist/tailwindPlugin.js +1 -1
  44. package/dist/tokens/index.cjs +1 -1
  45. package/dist/tokens/index.d.cts +3 -3
  46. package/dist/tokens/index.d.ts +3 -3
  47. package/dist/tokens/index.js +1 -1
  48. package/dist/tokens/index.native.cjs +1 -1
  49. package/dist/tokens/index.native.d.cts +2 -2
  50. package/dist/tokens/index.native.d.ts +2 -2
  51. package/dist/tokens/index.native.js +1 -1
  52. package/dist/tokens/parseTokens.cjs +1 -1
  53. package/dist/tokens/parseTokens.d.cts +1 -1
  54. package/dist/tokens/parseTokens.d.ts +1 -1
  55. package/dist/tokens/parseTokens.js +1 -1
  56. package/dist/tokens/parseTokens.native.d.cts +1 -1
  57. package/dist/tokens/parseTokens.native.d.ts +1 -1
  58. package/dist/tokens/parseTokens.native.js +1 -1
  59. package/dist/types-J4DLS6Xj.d.cts +38 -0
  60. package/dist/types-J4DLS6Xj.d.ts +38 -0
  61. package/dist/{types-VgTlNoi_.d.cts → types-hirL9Qk5.d.cts} +1 -0
  62. package/dist/{types-VgTlNoi_.d.ts → types-hirL9Qk5.d.ts} +1 -0
  63. package/fonts/mobile.cjs +29 -0
  64. package/fonts/mobile.d.ts +3 -0
  65. package/fonts/yahoo-icons.ttf +0 -0
  66. package/fonts/yahoo-sans-beta-bold.otf +0 -0
  67. package/fonts/yahoo-sans-beta-medium.otf +0 -0
  68. package/fonts/yahoo-sans-beta-regular.otf +0 -0
  69. package/fonts/yahoo-sans-black.otf +0 -0
  70. package/fonts/yahoo-sans-bold.otf +0 -0
  71. package/fonts/yahoo-sans-condensed-black.otf +0 -0
  72. package/fonts/yahoo-sans-condensed-bold.otf +0 -0
  73. package/fonts/yahoo-sans-condensed-light.otf +0 -0
  74. package/fonts/yahoo-sans-condensed-medium.otf +0 -0
  75. package/fonts/yahoo-sans-condensed-regular.otf +0 -0
  76. package/fonts/yahoo-sans-extrabold.otf +0 -0
  77. package/fonts/yahoo-sans-extralight.otf +0 -0
  78. package/fonts/yahoo-sans-italic.otf +0 -0
  79. package/fonts/yahoo-sans-light.otf +0 -0
  80. package/fonts/yahoo-sans-medium.otf +0 -0
  81. package/fonts/yahoo-sans-regular.otf +0 -0
  82. package/fonts/yahoo-sans-semibold.otf +0 -0
  83. package/fonts/yahoo-serif-display-black.otf +0 -0
  84. package/fonts/yahoo-serif-display-bold.otf +0 -0
  85. package/fonts/yahoo-serif-display-extrabold.otf +0 -0
  86. package/fonts/yahoo-serif-display-light.otf +0 -0
  87. package/fonts/yahoo-serif-display-regular.otf +0 -0
  88. package/fonts/yahoo-serif-text-bold.otf +0 -0
  89. package/fonts/yahoo-serif-text-italic.otf +0 -0
  90. package/fonts/yahoo-serif-text-regular.otf +0 -0
  91. package/package.json +114 -49
  92. package/bin/uds +0 -0
  93. package/dist/chunk-7FQGDIJ2.js +0 -1
package/cli/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # UDS ClI
2
+
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
+
5
+ Bluebun relies on Bun APIs and is designed to be extremely fast, with no-dependencies.
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.
10
+
11
+ # Commands
12
+
13
+ ## Command structure
14
+
15
+ 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
+
17
+ For example, a command structure might look like this:
18
+
19
+ ```
20
+ cli/
21
+ commands/
22
+ pizza.ts # pizza
23
+ help.ts # pizza help
24
+ bake/
25
+ bake.ts # pizza bake
26
+ cheese.ts # pizza bake cheese
27
+ pepperoni.ts # pizza bake pepperoni
28
+ ```
29
+
30
+ ## Command files
31
+
32
+ Commands are exported as defaults from each command file. They look like this:
33
+
34
+ ```typescript
35
+ import { type Props } from 'bluebun';
36
+
37
+ export default {
38
+ name: 'bake',
39
+ description: 'Bake a pizza',
40
+ alias: ['b'],
41
+ run: async (props: Props) => {
42
+ // bake it!
43
+ },
44
+ };
45
+ ```
46
+
47
+ ## Command properties
48
+
49
+ Commands have the following properties:
50
+
51
+ - `name` - the name of the command
52
+ - `description` - a description of the command for the automatic help system
53
+ - `alias` - an array of aliases for the command (can also be a single string)
54
+ - `run` - the function that is run when the command is run, usually `async`
55
+
56
+ ## Props
57
+
58
+ The `run` function is passed a `Props` object. This object contains the command path, the arguments, and the options, as well as a few other useful things.
59
+
60
+ Here are the properties available on the `props` object if we were to run `uds bake cheese convection --sliced --temp=400 --time 30`:
61
+
62
+ - `name` - the name of the CLI (uds)
63
+ - `cliPath` - the path to the CLI (./uds-cli)
64
+ - `argv` - the raw arguments passed to the CLI (e.g. `["/bin/bun", "/bin/uds", "bake", "cheese", "convection", "--sliced", "--temp=400", "--time", "30"]`)
65
+ - `commandPath` - the path to the command that was run (e.g. `["bake", "cheese"]`)
66
+ - `arguments` - the positional arguments passed to the command (e.g. `["convection"]`)
67
+ - `options` - the options passed to the command (e.g. `{ sliced: true, temp: 400, time: 30 }`)
68
+ - `first` - the first argument passed to the command (e.g. `"convection"`)
69
+ - `second` - the second argument passed to the command (e.g. `undefined`)
70
+ - `third` - the third argument passed to the command (e.g. `undefined`)
71
+
72
+ Given these props, we might have a command that looks like this:
73
+
74
+ ```typescript
75
+ import { type Props, spinStart, spinStop } from 'bluebun';
76
+ import { createPizza, slice } from './_pizza';
77
+ import { convectionBake, toasterBake, regularBake } from './_bakePizza';
78
+
79
+ export default {
80
+ name: 'bake',
81
+ description: 'Bake a pizza',
82
+ alias: ['b'],
83
+ run: async (props: Props) => {
84
+ const { first, second, third, options } = props;
85
+
86
+ const pizza = createPizza();
87
+
88
+ spinStart('Baking pizza...');
89
+ if (first === 'convection') {
90
+ await convectionBake(pizza, options.temp, options.time);
91
+ } else if (first === 'toaster') {
92
+ await toasterBake(pizza, options.temp, options.time);
93
+ } else {
94
+ await regularBake(pizza, options.temp, options.time);
95
+ }
96
+ spinStop('✅ Pizza baked!');
97
+
98
+ spinStart('Slicing pizza...');
99
+ if (options.sliced) {
100
+ await slice(pizza);
101
+ }
102
+ spinStop('✅ Pizza sliced!');
103
+ },
104
+ };
105
+ ```
106
+
107
+ # Reference
108
+
109
+ Bluebun comes with a number of built-in utilities that are useful. They're all exported from `bluebun` directly, so you can import them like this:
110
+
111
+ ```typescript
112
+ import { inputKey } from 'bluebun';
113
+ ```
114
+
115
+ Each of the following docs has usage examples and a Testing section that gives examples on how to write tests for them.
116
+
117
+ ## CLI
118
+
119
+ - [run](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/run.md) - run the CLI and command
120
+ - [cli](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/cli.md) - start the CLI without running the command
121
+ - [commandHelp](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/commandHelp.md) - get a list of all commands and their descriptions
122
+
123
+ ## User Interaction
124
+
125
+ - [ask](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/ask.md) - ask the user a question via a prompt
126
+ - [cursor](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/cursor.md) - manipulate the cursor
127
+ - [inputKey](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/inputKey.md) - wait for a single keypress
128
+ - [inputKeys](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/inputKeys.md) - wait for and handle multiple keypresses
129
+
130
+ ## Output
131
+
132
+ - [print](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/print.md) - print a string to the terminal
133
+ - [spinner](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/spinner.md) - start and stop a spinner
134
+ - [progress](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/progress.md) - start, update, and stop a progress bar
135
+ - [styles and colors](https://github.com/jamonholmgren/bluebun/blob/main/docs/reference/styles.md) - style and colorize text
@@ -0,0 +1,10 @@
1
+ import { type Props } from 'bluebun';
2
+ import { getCommandHelp } from '../../utils/getCommandHelp';
3
+
4
+ export default {
5
+ name: 'config',
6
+ description: '',
7
+ run: async (props: Props) => {
8
+ await getCommandHelp(props);
9
+ },
10
+ };
@@ -0,0 +1,22 @@
1
+ import { Props } from 'bluebun';
2
+ import { setupConfigWorker } from '../../utils/setupConfigWorker';
3
+ import { SyncOptions } from '../../utils/types';
4
+
5
+ interface SyncProps extends Props {
6
+ options: SyncOptions;
7
+ }
8
+
9
+ export default {
10
+ name: 'sync',
11
+ description: '🌈 Sync',
12
+ run: async ({ options }: SyncProps) => {
13
+ await setupConfigWorker({
14
+ ...options,
15
+ onUpdate: ({ worker }) => {
16
+ if (!options.watch) {
17
+ worker.terminate();
18
+ }
19
+ },
20
+ });
21
+ },
22
+ };
@@ -0,0 +1,244 @@
1
+ import { EasJsonAccessor, EasJsonUtils, Platform } from '@expo/eas-json';
2
+ import { type Props, print } from 'bluebun';
3
+ import { $, semver, which } from 'bun';
4
+
5
+ export interface MobileProps extends Props {
6
+ options: {
7
+ profile: string;
8
+ platform: Platform;
9
+ jsEngine?: 'hermes' | 'jsc';
10
+ debug?: boolean;
11
+ };
12
+ }
13
+
14
+ export async function needsBinary(lib: string) {
15
+ return which(lib) === null;
16
+ }
17
+
18
+ export async function needsBrewFormula(formula: string) {
19
+ return (await $`brew list --formula | grep ${formula} | wc -l`.text()).trim() === '0';
20
+ }
21
+
22
+ export async function setup({
23
+ props,
24
+ env: envOpts,
25
+ }: {
26
+ props: MobileProps;
27
+ env?: Partial<typeof Bun.env>;
28
+ }) {
29
+ const { profile, platform, jsEngine = 'hermes', debug = false } = props.options;
30
+ const isIOS = platform === Platform.IOS;
31
+ const isAndroid = platform === Platform.ANDROID;
32
+
33
+ /* -------------------------------------------------------------------------- */
34
+ /* XCODE SETUP */
35
+ /* -------------------------------------------------------------------------- */
36
+ if (isIOS) {
37
+ const xcodePath = await $`xcode-select -p`.text();
38
+ const needsXcode = !xcodePath.startsWith('/Applications');
39
+ const needsXcrun = await needsBinary('xcrun');
40
+
41
+ if (needsXcode) {
42
+ console.write(
43
+ 'You must have XCode installed before continuing... Visit https://apps.apple.com/us/app/xcode/id497799835?mt=12 to download.',
44
+ );
45
+ throw new Error('XCode not installed');
46
+ }
47
+
48
+ /**
49
+ * Installing the XCode command line tools & simulators normally
50
+ * requires opening up XCode to install.
51
+ * This conditional is to avoid having to do that.
52
+ */
53
+ if (needsXcrun) {
54
+ /**
55
+ * https://developer.apple.com/documentation/xcode/installing-additional-simulator-runtimes#Install-and-manage-Simulator-runtimes-from-the-command-line
56
+ */
57
+ await $`xcodebuild -runFirstLaunch`;
58
+ await $`xcodebuild -downloadPlatform iOS`;
59
+ }
60
+ }
61
+
62
+ /* -------------------------------------------------------------------------- */
63
+ /* JAVA DEVELOPMENT KIT */
64
+ /* -------------------------------------------------------------------------- */
65
+ if (isAndroid) {
66
+ const jdkPath = await $`brew info --cask zulu17`.text();
67
+ const needsJdk = jdkPath.includes('unavailable');
68
+ if (needsJdk) {
69
+ console.write('Installing Java Development Kit for Android...');
70
+ await $`brew install --cask zulu17`;
71
+ }
72
+ }
73
+
74
+ /* -------------------------------------------------------------------------- */
75
+ /* EAS CONFIG SETUP */
76
+ /* -------------------------------------------------------------------------- */
77
+ /**
78
+ * eas.json is used to define build profiles for different environments.
79
+ * For example, you may have a profile for development, staging, and production.
80
+ *
81
+ * The EAS CLI is used to build and run an app locally based on the eas.json,
82
+ * but it isn't opinionated about how you define your profiles.
83
+ *
84
+ * We however want to enforce some conventions to make it easier to get started.
85
+ * We also want to store our builds outside of EAS ecosystem since they auto
86
+ * delete after 30 days & our builds infrequently.
87
+ *
88
+ */
89
+ const appDirectory = Bun.env.PWD;
90
+ const easJsonAccessor = EasJsonAccessor.fromProjectPath(appDirectory);
91
+ const easProfiles = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
92
+
93
+ if (!easProfiles.includes(profile)) {
94
+ const profileNames = easProfiles.join(', ');
95
+ const suggestion = `Please add one or use ${profileNames}.`;
96
+
97
+ if (!profile) {
98
+ print(`No profile name was provided. ${suggestion}`);
99
+ } else {
100
+ print(
101
+ `A profile with name ${profile} does not exist as a key in eas.json > build object.\n${suggestion}`,
102
+ );
103
+ }
104
+ process.exit(0);
105
+ }
106
+
107
+ const easJsonConfig = await EasJsonUtils.getBuildProfileAsync(easJsonAccessor, platform, profile);
108
+ const easCliConfig = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
109
+ const easCliVersion = easCliConfig?.version ?? 'latest';
110
+
111
+ /* -------------------------------------------------------------------------- */
112
+ /* EAS CLI SETUP */
113
+ /* -------------------------------------------------------------------------- */
114
+ /**
115
+ * EAS CLI is used to build and run an app locally.
116
+ */
117
+ const needsEasCli = await needsBinary('eas');
118
+ if (needsEasCli) {
119
+ await $`bun install eas-cli@${easCliVersion} -g`.quiet();
120
+ } else {
121
+ const currentEasCliVersion = await $`eas --version`.text();
122
+ if (!semver.satisfies(currentEasCliVersion, easCliVersion)) {
123
+ await $`bun install eas-cli@${easCliVersion} -g`.quiet();
124
+ }
125
+ }
126
+
127
+ const {
128
+ APP_BUNDLE_IDENTIFIER,
129
+ APP_ANDROID_BUNDLE_IDENTIFIER: androidId = APP_BUNDLE_IDENTIFIER as string,
130
+ APP_APPLE_BUNDLE_IDENTIFIER: appleId = APP_BUNDLE_IDENTIFIER as string,
131
+ } = easJsonConfig.env ?? {};
132
+
133
+ // TODO: Add additional checks for ensuring this value adheres to Android formatting specs
134
+ if (isAndroid && !androidId) {
135
+ throw new Error(`
136
+ APP_ANDROID_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
137
+ See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
138
+
139
+ This env variable is used to populate the package key in your app.config.(js|ts) > android
140
+ The format of this env variable must use DNS notation unique name for your app, which is a valid Android Application ID.
141
+
142
+ For example you could use, com.company.app, where com.company is our domain and app is our app.
143
+
144
+ We recommend having the release build use the simplest identifier such as com.company.app and your debug variants
145
+ adding additional context such as com.company.app_debug. When publishing, the release identifier must be unique on the Play Store.
146
+
147
+ The name may only contain lowercase and uppercase letters (a-z, A-Z), numbers (0-9) and underscores (_),
148
+ separated by periods (.). Each component of the name should start with a lowercase letter.
149
+
150
+ These formatting rules only applies to Android. iOS has different requirements.
151
+
152
+ See https://docs.expo.dev/versions/latest/config/app/#package for more details on formatting.
153
+ See https://docs.expo.dev/build-reference/variants/ for information about build variants.
154
+ `);
155
+ }
156
+
157
+ // TODO: Add additional checks for ensuring this value adheres to Uniform Type Identifier
158
+ if (isIOS && !appleId) {
159
+ throw new Error(`
160
+ APP_APPLE_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
161
+ See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
162
+
163
+ This env variable is used to populate the package key in your app.config.(js|ts) > ios
164
+ The format of this env variable must use DNS notation unique name for your app, which is a valid Android Application ID.
165
+
166
+ For example you could use, com.company.app, where com.company is our domain and app is our app.
167
+
168
+ We recommend having the release build use the simplest identifier such as com.company.app and your debug variants
169
+ adding additional context such as com.company.app-debug. When publishing, the release identifier must be unique to the App Store.
170
+
171
+ The string format should be Uniform Type Identifier(UTI), which is alphanumeric characters (A-Z,a-z,0-9), hyphen (-), and period (.)
172
+
173
+ These formatting rules only applies to iOS. Android has different requirements.
174
+
175
+ See https://docs.expo.dev/versions/latest/config/app/#bundleidentifier for more details on formatting.
176
+ See https://docs.expo.dev/build-reference/variants/ for information about build variants.
177
+ `);
178
+ }
179
+
180
+ const outputName = `${platform}-${profile}-${jsEngine}`;
181
+ const prebuildsDir = `${appDirectory}/prebuilds`; // TODO: make this configurable
182
+ const outputDir = `${prebuildsDir}/${outputName}`;
183
+ const outputFileBase = `${outputDir}/${outputName}`;
184
+
185
+ /* -------------------------------------------------------------------------- */
186
+ /* ENVIRONMENT VARIABLES */
187
+ /* -------------------------------------------------------------------------- */
188
+ let envVars = Bun.env;
189
+
190
+ envVars = {
191
+ ...envVars,
192
+ APP_ANDROID_BUNDLE_IDENTIFIER: androidId,
193
+ APP_APPLE_BUNDLE_IDENTIFIER: appleId,
194
+ RCT_NO_LAUNCH_PACKAGER: '1',
195
+ EXPO_NO_TELEMETRY: '1',
196
+ EXPO_NO_WEB_SETUP: '1',
197
+ };
198
+
199
+ if (debug) {
200
+ envVars = {
201
+ ...envVars,
202
+ DEBUG: '*',
203
+ EAS_LOCAL_BUILD_SKIP_CLEANUP: '1',
204
+ EXPO_PROFILE: '1',
205
+ };
206
+ }
207
+
208
+ if (envOpts) {
209
+ envVars = { ...envVars, ...envOpts };
210
+ }
211
+
212
+ /** Change the default environment variables for shells created by this instance. */
213
+ $.env(envVars);
214
+
215
+ const output = {
216
+ name: outputName,
217
+ dir: outputDir,
218
+ prebuildsDir: prebuildsDir,
219
+ fileBase: outputFileBase,
220
+ artifact: isIOS ? `${outputFileBase}.tar.gz` : `${outputFileBase}.zip`,
221
+ app: isIOS ? `${outputFileBase}.app` : 'todo fix android',
222
+ get launchFile() {
223
+ return isIOS ? this.artifact : this.apk.signed;
224
+ },
225
+ apk: {
226
+ contents: `${outputFileBase}/build`,
227
+ rebuilt: `${outputFileBase}/binary-rebuilt.apk`,
228
+ rebuiltAligned: `${outputFileBase}/binary-rebuilt-aligned.apk`,
229
+ signed: `${outputFileBase}/binary.apk`,
230
+ test: `${outputFileBase}/testBinary.apk`,
231
+ },
232
+ };
233
+
234
+ return {
235
+ scheme: isIOS ? appleId : androidId,
236
+ channel: easJsonConfig.channel,
237
+ debug,
238
+ profile,
239
+ platform,
240
+ jsEngine,
241
+ appDirectory,
242
+ output,
243
+ };
244
+ }
@@ -0,0 +1,96 @@
1
+ import { $, semver } from 'bun';
2
+ import { setup, needsBinary, needsBrewFormula, type MobileProps } from './_setup';
3
+
4
+ export default {
5
+ name: 'build',
6
+ description: '🚀 Build',
7
+ run: async (props: MobileProps) => {
8
+ const { platform, profile, output } = await setup({
9
+ props,
10
+ });
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
+
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!');
95
+ },
96
+ };
@@ -0,0 +1,82 @@
1
+ import { Props } from 'bluebun';
2
+ import { $, sleep } from 'bun';
3
+ import { setup, type MobileProps } from './_setup';
4
+
5
+ interface MobileStartProps extends Props {
6
+ options: MobileProps['options'] & {
7
+ // Clear the cache when starting dev server
8
+ clear?: boolean;
9
+ };
10
+ }
11
+
12
+ type SimDevice = { udid: string; name: string; state: 'Booted' | 'Shutdown' };
13
+ type SimList = {
14
+ devices: {
15
+ [key: string]: SimDevice[];
16
+ };
17
+ };
18
+
19
+ export default {
20
+ name: 'dev',
21
+ description: '🚧 Dev',
22
+ run: async (props: MobileStartProps) => {
23
+ const { platform, scheme, output } = await setup({
24
+ props,
25
+ env: {
26
+ EXPO_USE_METRO_WORKSPACE_ROOT: '1',
27
+ },
28
+ });
29
+
30
+ const extraArgs = [];
31
+
32
+ if (props.options.clear) {
33
+ extraArgs.push('--clear');
34
+ }
35
+ const extrArgsString = extraArgs.join(' ');
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;
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
+ }
60
+
61
+ /**
62
+ * In dev mode we use a debug build, which requires a metro server to serve
63
+ * the Javascript bundle.
64
+ *
65
+ * To avoid the, "Could not find metro server" error screen on launch,
66
+ * we delay the launch of the app until after the metro server has started.
67
+ */
68
+ sleep(3000).then(async () => {
69
+ /**
70
+ * Launch the app on the booted device
71
+ * https://github.com/expo/eas-cli?tab=readme-ov-file#eas-buildrun
72
+ */
73
+ await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
74
+ });
75
+
76
+ console.write('Starting Metro server...');
77
+ await $`expo start --${platform} --dev-client --localhost --scheme ${scheme} ${extrArgsString}`;
78
+ } catch (err) {
79
+ throw err;
80
+ }
81
+ },
82
+ };
@@ -0,0 +1,23 @@
1
+ import { type Props } from 'bluebun';
2
+ import { getCommandHelp } from '../../utils/getCommandHelp';
3
+
4
+ export default {
5
+ name: 'expo',
6
+ description: '',
7
+ run: async (props: Props) => {
8
+ switch (props?.first) {
9
+ case 'build':
10
+ return require('./build').default.run(props);
11
+ case 'dev':
12
+ return require('./dev').default.run(props);
13
+ case 'launch':
14
+ return require('./launch').default.run(props);
15
+ case 'update':
16
+ return require('./update').default.run(props);
17
+ default: {
18
+ await getCommandHelp(props);
19
+ break;
20
+ }
21
+ }
22
+ },
23
+ };
@@ -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 { $ } from 'bun';
2
+ import type { MobileProps } from './_setup';
3
+ import { setup } from './_setup';
4
+
5
+ export default {
6
+ name: 'launch',
7
+ description: '🚀 Launch',
8
+ run: async (props: MobileProps) => {
9
+ const { platform, output } = await setup({
10
+ props,
11
+ });
12
+
13
+ await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
14
+ },
15
+ };