@yahoo/uds 0.1.12 → 0.1.13

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 (92) 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 +260 -0
  5. package/cli/commands/expo/build.ts +15 -0
  6. package/cli/commands/expo/dev.ts +79 -0
  7. package/cli/commands/expo/expo.ts +23 -0
  8. package/cli/commands/expo/launch.ts +15 -0
  9. package/cli/commands/expo/update.ts +16 -0
  10. package/cli/commands/nextjs/dev.ts +17 -0
  11. package/cli/commands/nextjs/nextjs.ts +10 -0
  12. package/cli/commands/uds.ts +10 -0
  13. package/cli/commands/version.ts +11 -0
  14. package/cli/env.d.ts +15 -0
  15. package/cli/eslint.config.mjs +8 -0
  16. package/cli/tsconfig.json +10 -0
  17. package/cli/uds-cli +7 -0
  18. package/cli/utils/configWorker.ts +9 -0
  19. package/cli/utils/getCommandHelp.ts +65 -0
  20. package/cli/utils/setupConfigWorker.ts +81 -0
  21. package/cli/utils/sortKeys.ts +27 -0
  22. package/cli/utils/types.ts +13 -0
  23. package/dist/{chunk-JKZI2WLD.js → chunk-AWTLI4D3.js} +1 -1
  24. package/dist/chunk-D4K3CXV6.js +0 -0
  25. package/dist/{chunk-Z34QGHWU.js → chunk-MBOOJIH7.js} +1 -1
  26. package/dist/chunk-MFA2Y7DA.js +1 -0
  27. package/dist/chunk-P7GR6E3K.js +1 -0
  28. package/dist/chunk-PQBOZFJV.js +1 -0
  29. package/dist/index.cjs +1 -1
  30. package/dist/index.d.cts +7 -40
  31. package/dist/index.d.ts +7 -40
  32. package/dist/index.js +1 -1
  33. package/dist/{index.native-VVqy3X9H.d.cts → index.native-3ww4C4UV.d.cts} +1 -1
  34. package/dist/{index.native-DzfcCYUh.d.ts → index.native-9kYJrUPa.d.ts} +1 -1
  35. package/dist/index.native.cjs +1 -0
  36. package/dist/index.native.d.cts +1493 -0
  37. package/dist/index.native.d.ts +1493 -0
  38. package/dist/index.native.js +1 -0
  39. package/dist/tailwindPlugin.cjs +1 -1
  40. package/dist/tailwindPlugin.d.cts +1 -1
  41. package/dist/tailwindPlugin.d.ts +1 -1
  42. package/dist/tailwindPlugin.js +1 -1
  43. package/dist/tokens/index.cjs +1 -1
  44. package/dist/tokens/index.d.cts +3 -3
  45. package/dist/tokens/index.d.ts +3 -3
  46. package/dist/tokens/index.js +1 -1
  47. package/dist/tokens/index.native.cjs +1 -1
  48. package/dist/tokens/index.native.d.cts +2 -2
  49. package/dist/tokens/index.native.d.ts +2 -2
  50. package/dist/tokens/index.native.js +1 -1
  51. package/dist/tokens/parseTokens.cjs +1 -1
  52. package/dist/tokens/parseTokens.d.cts +1 -1
  53. package/dist/tokens/parseTokens.d.ts +1 -1
  54. package/dist/tokens/parseTokens.js +1 -1
  55. package/dist/tokens/parseTokens.native.d.cts +1 -1
  56. package/dist/tokens/parseTokens.native.d.ts +1 -1
  57. package/dist/tokens/parseTokens.native.js +1 -1
  58. package/dist/types-J4DLS6Xj.d.cts +38 -0
  59. package/dist/types-J4DLS6Xj.d.ts +38 -0
  60. package/dist/{types-VgTlNoi_.d.cts → types-hirL9Qk5.d.cts} +1 -0
  61. package/dist/{types-VgTlNoi_.d.ts → types-hirL9Qk5.d.ts} +1 -0
  62. package/fonts/mobile.cjs +29 -0
  63. package/fonts/mobile.d.ts +3 -0
  64. package/fonts/yahoo-icons.ttf +0 -0
  65. package/fonts/yahoo-sans-beta-bold.otf +0 -0
  66. package/fonts/yahoo-sans-beta-medium.otf +0 -0
  67. package/fonts/yahoo-sans-beta-regular.otf +0 -0
  68. package/fonts/yahoo-sans-black.otf +0 -0
  69. package/fonts/yahoo-sans-bold.otf +0 -0
  70. package/fonts/yahoo-sans-condensed-black.otf +0 -0
  71. package/fonts/yahoo-sans-condensed-bold.otf +0 -0
  72. package/fonts/yahoo-sans-condensed-light.otf +0 -0
  73. package/fonts/yahoo-sans-condensed-medium.otf +0 -0
  74. package/fonts/yahoo-sans-condensed-regular.otf +0 -0
  75. package/fonts/yahoo-sans-extrabold.otf +0 -0
  76. package/fonts/yahoo-sans-extralight.otf +0 -0
  77. package/fonts/yahoo-sans-italic.otf +0 -0
  78. package/fonts/yahoo-sans-light.otf +0 -0
  79. package/fonts/yahoo-sans-medium.otf +0 -0
  80. package/fonts/yahoo-sans-regular.otf +0 -0
  81. package/fonts/yahoo-sans-semibold.otf +0 -0
  82. package/fonts/yahoo-serif-display-black.otf +0 -0
  83. package/fonts/yahoo-serif-display-bold.otf +0 -0
  84. package/fonts/yahoo-serif-display-extrabold.otf +0 -0
  85. package/fonts/yahoo-serif-display-light.otf +0 -0
  86. package/fonts/yahoo-serif-display-regular.otf +0 -0
  87. package/fonts/yahoo-serif-text-bold.otf +0 -0
  88. package/fonts/yahoo-serif-text-italic.otf +0 -0
  89. package/fonts/yahoo-serif-text-regular.otf +0 -0
  90. package/package.json +122 -45
  91. package/bin/uds +0 -0
  92. 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,260 @@
1
+ import { EasJsonAccessor, EasJsonUtils, Platform } from '@expo/eas-json';
2
+ import { type Props, print } from 'bluebun';
3
+ import { $ } from 'bun';
4
+
5
+ export interface MobileProps extends Props {
6
+ options: {
7
+ profile: string;
8
+ platform: Platform.IOS | Platform.ANDROID;
9
+ jsEngine?: 'hermes' | 'jsc';
10
+ debug?: boolean;
11
+ };
12
+ }
13
+
14
+ async function needsBinary(lib: string) {
15
+ const whichLib = await $`which ${lib}`.text();
16
+ return whichLib.includes('not found');
17
+ }
18
+
19
+ async function needsBrewFormula(formula: string) {
20
+ return (await $`brew list --formula | grep ${formula} | wc -l`.text()).trim() === '0';
21
+ }
22
+
23
+ export async function setup({
24
+ props,
25
+ env: envOpts,
26
+ }: {
27
+ props: MobileProps;
28
+ env?: Partial<typeof Bun.env>;
29
+ }) {
30
+ const { profile, platform, jsEngine = 'hermes', debug = false } = props.options;
31
+
32
+ /* -------------------------------------------------------------------------- */
33
+ /* HOMEBREW SETUP */
34
+ /* -------------------------------------------------------------------------- */
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
+
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.
48
+ */
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
+ }
74
+ }
75
+
76
+ /* -------------------------------------------------------------------------- */
77
+ /* FASTLANE SETUP */
78
+ /* -------------------------------------------------------------------------- */
79
+ const needsFastlane = await needsBrewFormula('fastlane');
80
+ if (needsFastlane) {
81
+ console.log('Installing fastlane...');
82
+ await $`brew install fastlane`;
83
+ }
84
+
85
+ /* -------------------------------------------------------------------------- */
86
+ /* COCOAPODS SETUP */
87
+ /* -------------------------------------------------------------------------- */
88
+ const needsCocoapods = await needsBrewFormula('cocoapods');
89
+ if (needsCocoapods) {
90
+ console.log('Installing cocoapods...');
91
+ await $`brew install cocoapods`;
92
+ }
93
+
94
+ /* -------------------------------------------------------------------------- */
95
+ /* JAVA DEVELOPMENT KIT */
96
+ /* -------------------------------------------------------------------------- */
97
+ if (platform === 'android') {
98
+ const jdkPath = await $`brew info --cask zulu17`.text();
99
+ const needsJdk = jdkPath.includes('unavailable');
100
+ if (needsJdk) {
101
+ console.log('Installing Java Development Kit for Android...');
102
+ await $`brew install --cask zulu17`;
103
+ }
104
+ }
105
+
106
+ /* -------------------------------------------------------------------------- */
107
+ /* EXPO SETUP */
108
+ /* -------------------------------------------------------------------------- */
109
+ const appDirectory = Bun.env.PWD;
110
+ const easJsonAccessor = EasJsonAccessor.fromProjectPath(appDirectory);
111
+
112
+ const easProfiles = await EasJsonUtils.getBuildProfileNamesAsync(easJsonAccessor);
113
+
114
+ if (!easProfiles.includes(profile)) {
115
+ const profileNames = easProfiles.join(', ');
116
+ const suggestion = `Please add one or use ${profileNames}.`;
117
+
118
+ if (!profile) {
119
+ print(`No profile name was provided. ${suggestion}`);
120
+ } else {
121
+ print(
122
+ `A profile with name ${profile} does not exist as a key in eas.json > build object.\n${suggestion}`,
123
+ );
124
+ }
125
+ process.exit(0);
126
+ }
127
+
128
+ const easJsonConfig = await EasJsonUtils.getBuildProfileAsync(easJsonAccessor, platform, profile);
129
+
130
+ const easCliConfig = await EasJsonUtils.getCliConfigAsync(easJsonAccessor);
131
+ const easCliVersion = easCliConfig?.version ?? 'latest';
132
+
133
+ /* -------------------------------------------------------------------------- */
134
+ /* EAS CLI SETUP */
135
+ /* -------------------------------------------------------------------------- */
136
+ const needsEasCli = await needsBinary('eas');
137
+ if (needsEasCli) {
138
+ await $`bun install eas-cli@${easCliVersion} -g`;
139
+ } else {
140
+ const currentEasCliVersion = await $`eas --version`.text();
141
+ if (currentEasCliVersion !== easCliVersion) {
142
+ await $`bun install eas-cli@${easCliVersion} -g`;
143
+ }
144
+ }
145
+
146
+ const {
147
+ APP_BUNDLE_IDENTIFIER,
148
+ APP_ANDROID_BUNDLE_IDENTIFIER: androidId = APP_BUNDLE_IDENTIFIER as string,
149
+ APP_APPLE_BUNDLE_IDENTIFIER: appleId = APP_BUNDLE_IDENTIFIER as string,
150
+ } = easJsonConfig.env ?? {};
151
+
152
+ // TODO: Add additional checks for ensuring this value adheres to Android formatting specs
153
+ if (platform === Platform.ANDROID && !androidId) {
154
+ throw new Error(`
155
+ APP_ANDROID_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
156
+ See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
157
+
158
+ This env variable is used to populate the package key in your app.config.(js|ts) > android
159
+ The format of this env variable must use DNS notation unique name for your app, which is a valid Android Application ID.
160
+
161
+ For example you could use, com.company.app, where com.company is our domain and app is our app.
162
+
163
+ We recommend having the release build use the simplest identifier such as com.company.app and your debug variants
164
+ adding additional context such as com.company.app_debug. When publishing, the release identifier must be unique on the Play Store.
165
+
166
+ The name may only contain lowercase and uppercase letters (a-z, A-Z), numbers (0-9) and underscores (_),
167
+ separated by periods (.). Each component of the name should start with a lowercase letter.
168
+
169
+ These formatting rules only applies to Android. iOS has different requirements.
170
+
171
+ See https://docs.expo.dev/versions/latest/config/app/#package for more details on formatting.
172
+ See https://docs.expo.dev/build-reference/variants/ for information about build variants.
173
+ `);
174
+ }
175
+
176
+ // TODO: Add additional checks for ensuring this value adheres to Uniform Type Identifier
177
+ if (platform === Platform.IOS && !appleId) {
178
+ throw new Error(`
179
+ APP_APPLE_BUNDLE_IDENTIFIER must be defined in eas.json within the ${profile} > env config.
180
+ See https://docs.expo.dev/build-reference/variables/#setting-plaintext-environment-variables-in-easjson for information about env variables in eas.json
181
+
182
+ This env variable is used to populate the package key in your app.config.(js|ts) > ios
183
+ The format of this env variable must use DNS notation unique name for your app, which is a valid Android Application ID.
184
+
185
+ For example you could use, com.company.app, where com.company is our domain and app is our app.
186
+
187
+ We recommend having the release build use the simplest identifier such as com.company.app and your debug variants
188
+ adding additional context such as com.company.app-debug. When publishing, the release identifier must be unique to the App Store.
189
+
190
+ The string format should be Uniform Type Identifier(UTI), which is alphanumeric characters (A-Z,a-z,0-9), hyphen (-), and period (.)
191
+
192
+ These formatting rules only applies to iOS. Android has different requirements.
193
+
194
+ See https://docs.expo.dev/versions/latest/config/app/#bundleidentifier for more details on formatting.
195
+ See https://docs.expo.dev/build-reference/variants/ for information about build variants.
196
+ `);
197
+ }
198
+
199
+ const outputName = `${platform}-${profile}-${jsEngine}`;
200
+ // TODO: make this configurable
201
+ const prebuildsDir = `${appDirectory}/prebuilds`;
202
+ const outputDir = `${prebuildsDir}/${outputName}`;
203
+ const outputFileBase = `${outputDir}/${outputName}`;
204
+
205
+ let envVars = Bun.env;
206
+
207
+ envVars = {
208
+ ...envVars,
209
+ APP_ANDROID_BUNDLE_IDENTIFIER: androidId,
210
+ APP_APPLE_BUNDLE_IDENTIFIER: appleId,
211
+ RCT_NO_LAUNCH_PACKAGER: '1',
212
+ EXPO_NO_TELEMETRY: '1',
213
+ EXPO_NO_WEB_SETUP: '1',
214
+ };
215
+
216
+ if (debug) {
217
+ envVars = {
218
+ ...envVars,
219
+ DEBUG: '*',
220
+ EAS_LOCAL_BUILD_SKIP_CLEANUP: '1',
221
+ EXPO_PROFILE: '1',
222
+ };
223
+ }
224
+
225
+ if (envOpts) {
226
+ envVars = { ...envVars, ...envOpts };
227
+ }
228
+
229
+ $.env(envVars);
230
+
231
+ const output = {
232
+ name: outputName,
233
+ dir: outputDir,
234
+ prebuildsDir: prebuildsDir,
235
+ fileBase: outputFileBase,
236
+ artifact: platform === 'ios' ? `${outputFileBase}.tar.gz` : `${outputFileBase}.zip`,
237
+ app: platform === 'ios' ? `${outputFileBase}.app` : 'todo fix android',
238
+ get launchFile() {
239
+ return platform === 'ios' ? this.artifact : this.apk.signed;
240
+ },
241
+ apk: {
242
+ contents: `${outputFileBase}/build`,
243
+ rebuilt: `${outputFileBase}/binary-rebuilt.apk`,
244
+ rebuiltAligned: `${outputFileBase}/binary-rebuilt-aligned.apk`,
245
+ signed: `${outputFileBase}/binary.apk`,
246
+ test: `${outputFileBase}/testBinary.apk`,
247
+ },
248
+ };
249
+
250
+ return {
251
+ scheme: platform === 'ios' ? appleId : androidId,
252
+ channel: easJsonConfig.channel,
253
+ debug,
254
+ profile,
255
+ platform,
256
+ jsEngine,
257
+ appDirectory,
258
+ output,
259
+ };
260
+ }
@@ -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: 'build',
7
+ description: '🚀 Build',
8
+ run: async (props: MobileProps) => {
9
+ const { platform, profile, output } = await setup({
10
+ props,
11
+ });
12
+
13
+ await $`eas build --local --non-interactive --json --clear-cache --platform ${platform} --profile ${profile} --output ${output.artifact}`;
14
+ },
15
+ };
@@ -0,0 +1,79 @@
1
+ import { Props } from 'bluebun';
2
+ import { $ } 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
+ ['com.apple.CoreSimulator.SimRuntime.iOS-17-2']: SimDevice[];
16
+ };
17
+ };
18
+
19
+ export default {
20
+ name: 'dev',
21
+ description: '🚧 Dev',
22
+ run: async (props: MobileStartProps) => {
23
+ console.log('running dev');
24
+ const { platform, scheme, output } = await setup({
25
+ props,
26
+ env: {
27
+ EXPO_USE_METRO_WORKSPACE_ROOT: '1',
28
+ },
29
+ });
30
+
31
+ if (platform === 'ios') {
32
+ const xcodePath = await $`xcode-select -p`.text();
33
+ const xcodeExists = xcodePath.startsWith('/Applications');
34
+
35
+ if (xcodeExists) {
36
+ await $`open -a simulator`;
37
+ const deviceList = (await $`xcrun simctl list devices available -j -e`.json()) as SimList;
38
+
39
+ const devices = Object.values(deviceList?.devices).flatMap((item) => item);
40
+
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
+ }
56
+ }
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');
62
+ }
63
+ }
64
+
65
+ await $`eas build:run --platform ${platform} --path ${output.launchFile}`;
66
+
67
+ const extraArgs = [];
68
+
69
+ if (props.options.clear) {
70
+ extraArgs.push('--clear');
71
+ }
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
+ },
79
+ };
@@ -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,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
+ };
@@ -0,0 +1,16 @@
1
+ import { $ } from 'bun';
2
+ import type { MobileProps } from './_setup';
3
+ import { setup } from './_setup';
4
+
5
+ export default {
6
+ name: 'update',
7
+ description: '🚀 Update',
8
+ run: async (props: MobileProps) => {
9
+ const { channel } = await setup({
10
+ props,
11
+ });
12
+
13
+ await $`eas update --auto --channel ${channel}`;
14
+ await $`rm -rf dist`;
15
+ },
16
+ };
@@ -0,0 +1,17 @@
1
+ import { setupConfigWorker } from '../../utils/setupConfigWorker';
2
+ import { Props } from 'bluebun';
3
+ import { SyncOptions } from '../../utils/types';
4
+ import { $ } from 'bun';
5
+
6
+ interface DevProps extends Props {
7
+ options: SyncOptions;
8
+ }
9
+
10
+ export default {
11
+ name: 'dev',
12
+ description: '🚧 Dev',
13
+ run: async ({ options }: DevProps) => {
14
+ await setupConfigWorker(options);
15
+ await $`next dev`;
16
+ },
17
+ };
@@ -0,0 +1,10 @@
1
+ import { type Props } from 'bluebun';
2
+ import { getCommandHelp } from '../../utils/getCommandHelp';
3
+
4
+ export default {
5
+ name: 'nextjs',
6
+ description: '',
7
+ run: async (props: Props) => {
8
+ await getCommandHelp(props);
9
+ },
10
+ };
@@ -0,0 +1,10 @@
1
+ import { type Props } from 'bluebun';
2
+ import { getCommandHelp } from '../utils/getCommandHelp';
3
+
4
+ export default {
5
+ name: 'uds',
6
+ description: '',
7
+ run: async (props: Props) => {
8
+ await getCommandHelp(props);
9
+ },
10
+ };
@@ -0,0 +1,11 @@
1
+ import { print } from 'bluebun';
2
+
3
+ import packageJson from '../../package.json';
4
+
5
+ export default {
6
+ name: 'version',
7
+ description: `💾 ${packageJson.version}`,
8
+ run: async () => {
9
+ print(packageJson.version);
10
+ },
11
+ };
package/cli/env.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ declare module 'bun' {
2
+ interface Env {
3
+ PWD: string;
4
+ APP_APPLE_BUNDLE_IDENTIFIER: string;
5
+ APP_ANDROID_BUNDLE_IDENTIFIER: string;
6
+ RCT_NO_LAUNCH_PACKAGER?: string;
7
+ EXPO_NO_TELEMETRY?: string;
8
+ EAS_LOCAL_BUILD_ARTIFACTS_DIR?: string;
9
+ EAS_LOCAL_BUILD_SKIP_CLEANUP?: string;
10
+ EXPO_PROFILE?: string;
11
+ EXPO_USE_METRO_WORKSPACE_ROOT?: string;
12
+ EXPO_BUNDLE_APP?: string;
13
+ ESLINT_USE_FLAT_CONFIG?: string;
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ import { defineFlatConfig, nodeConfig } from 'eslint-config-custom';
2
+
3
+ export default defineFlatConfig(
4
+ nodeConfig.map((config) => ({
5
+ ...config,
6
+ files: ['**/*.{ts,tsx}'],
7
+ })),
8
+ );
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "tsconfig/bun.json",
3
+ "include": ["."],
4
+ "compilerOptions": {
5
+ "paths": {
6
+ "@yahoo/uds/*": ["../src/*"],
7
+ "root/*": ["../../*"],
8
+ },
9
+ },
10
+ }