@yahoo/uds 0.1.13 → 0.1.15
Sign up to get free protection for your applications and to get access to all the features.
- package/cli/README.md +57 -5
- package/cli/commands/expo/_setup.ts +100 -73
- package/cli/commands/expo/build.ts +84 -3
- package/cli/commands/expo/dev.ts +51 -41
- package/cli/commands/expo/install/cocoapods.rb +35 -0
- package/cli/commands/purge.ts +15 -0
- package/cli/commands/{config/sync.ts → sync.ts} +2 -2
- package/cli/utils/configWorker.ts +21 -1
- package/cli/utils/purgeCSS.ts +139 -0
- package/cli/utils/setupConfigWorker.ts +12 -13
- package/dist/{chunk-P7GR6E3K.js → chunk-AHFH5E5L.js} +1 -1
- package/dist/{chunk-MBOOJIH7.js → chunk-FLBMVDKG.js} +1 -1
- package/dist/{chunk-AWTLI4D3.js → chunk-U3UPAQ7V.js} +1 -1
- package/dist/fixtures/index.cjs +1 -1
- package/dist/fixtures/index.d.cts +2 -2
- package/dist/fixtures/index.d.ts +2 -2
- package/dist/fixtures/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +40 -31
- package/dist/index.d.ts +40 -31
- package/dist/index.js +1 -1
- package/dist/{index.native-9kYJrUPa.d.ts → index.native-TvtXtTXg.d.ts} +2 -2
- package/dist/{index.native-3ww4C4UV.d.cts → index.native-dgGFONLf.d.cts} +2 -2
- package/dist/index.native.cjs +1 -1
- package/dist/index.native.d.cts +10 -24
- package/dist/index.native.d.ts +10 -24
- package/dist/index.native.js +1 -1
- package/dist/tailwindPlugin.cjs +1 -1
- package/dist/tailwindPlugin.d.cts +1 -1
- package/dist/tailwindPlugin.d.ts +1 -1
- package/dist/tailwindPlugin.js +1 -1
- package/dist/tailwindPurge.cjs +4 -0
- package/dist/tailwindPurge.d.cts +16 -0
- package/dist/tailwindPurge.d.ts +16 -0
- package/dist/tailwindPurge.js +4 -0
- package/dist/tokens/index.cjs +1 -1
- package/dist/tokens/index.d.cts +3 -3
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/index.native.cjs +1 -1
- package/dist/tokens/index.native.d.cts +2 -2
- package/dist/tokens/index.native.d.ts +2 -2
- package/dist/tokens/index.native.js +1 -1
- package/dist/tokens/parseTokens.cjs +1 -1
- package/dist/tokens/parseTokens.d.cts +12 -12
- package/dist/tokens/parseTokens.d.ts +12 -12
- package/dist/tokens/parseTokens.js +1 -1
- package/dist/tokens/parseTokens.native.d.cts +2 -2
- package/dist/tokens/parseTokens.native.d.ts +2 -2
- package/dist/{types-J4DLS6Xj.d.cts → types-3GXulqnG.d.cts} +1 -1
- package/dist/{types-J4DLS6Xj.d.ts → types-3GXulqnG.d.ts} +1 -1
- package/dist/{types-hirL9Qk5.d.cts → types-8OHfDki5.d.cts} +47 -54
- package/dist/{types-hirL9Qk5.d.ts → types-8OHfDki5.d.ts} +47 -54
- package/package.json +21 -18
- package/cli/commands/config/config.ts +0 -10
- package/cli/commands/nextjs/dev.ts +0 -17
- package/cli/commands/nextjs/nextjs.ts +0 -10
package/cli/README.md
CHANGED
@@ -1,16 +1,68 @@
|
|
1
|
-
# UDS
|
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
|
-
|
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
|
-
|
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 {
|
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
|
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
|
-
|
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
|
-
/*
|
38
|
+
/* VERIFY BUN IS IN PATH */
|
34
39
|
/* -------------------------------------------------------------------------- */
|
35
|
-
|
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
|
-
*
|
43
|
-
* https://
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
const
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
shellRcFile
|
57
|
-
|
58
|
-
|
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
|
-
/*
|
59
|
+
/* XCODE SETUP */
|
78
60
|
/* -------------------------------------------------------------------------- */
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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 (
|
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.
|
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
|
-
/*
|
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
|
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 (
|
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 (
|
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:
|
237
|
-
app:
|
263
|
+
artifact: isIOS ? `${outputFileBase}.tar.gz` : `${outputFileBase}.zip`,
|
264
|
+
app: isIOS ? `${outputFileBase}.app` : 'todo fix android',
|
238
265
|
get launchFile() {
|
239
|
-
return
|
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:
|
278
|
+
scheme: isIOS ? appleId : androidId,
|
252
279
|
channel: easJsonConfig.channel,
|
253
280
|
debug,
|
254
281
|
profile,
|
@@ -1,6 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
import type
|
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
|
};
|
package/cli/commands/expo/dev.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Props } from 'bluebun';
|
2
|
-
import {
|
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
|
-
[
|
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
|
-
|
32
|
-
const xcodePath = await $`xcode-select -p`.text();
|
33
|
-
const xcodeExists = xcodePath.startsWith('/Applications');
|
30
|
+
const extraArgs = [];
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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 (
|
42
|
-
|
43
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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 '
|
3
|
-
import { SyncOptions } from '
|
2
|
+
import { setupConfigWorker } from '../utils/setupConfigWorker';
|
3
|
+
import { SyncOptions } from '../utils/types';
|
4
4
|
|
5
5
|
interface SyncProps extends Props {
|
6
6
|
options: SyncOptions;
|