-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update createApp to no longer use individual commands #33
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just added some comments and suggestions.
Regarding the overall strategy I do like the idea of having a boilerplate as it makes it safer for us to generate applications with less chance of errors out of the box. However, I'm still keen to a hybrid where we have the option to pass a --latest
flag and generate the app from create-expo, this would be particularly helpful if Belt falls behind on Expo version (which I don't expect to happen 😄). Regardless I believe we can maintain it with the boilerplate and update when necessary.
One thing I had in mind is, how can we tackle the difference in Expo versions when we are generating vs running commands on the repo? For instance, if I generate the project from the boilerplate on Expo 50, but then some time after I run the add-notifications
command with dependencies that expect a newer 51 version? Should we lock dependency versions on independent commands based on the currently supported version of Expo in Belt?
bin/diff-expo.js
Outdated
await exec(`git clean -fxd`); | ||
await exec(`cp -r ../expo-${newVersion}/* .`); | ||
execSync(`git diff`, { stdio: 'inherit' }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of removing the generated project files at the end of the script? This would make sure the script leaves the project folder clean. Additionally, we could also add a --keep-files
flag to not delete them if we want to inspect them closer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to keep iterating on this script in a separate PR. I don't think it's perfect as is, and figuring out how we will streamline Expo upgrades merits more thought.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update, I found that this script is not actually necessary. I removed this script and added documentation in contributing.md
for how we can upgrade Expo in the project with: yarn add expo@latest
or yarn upgrade-interactive --latest
followed by npx expo install --fix
to ensure all installed package versions are compatible with the installed Expo version.
src/util/copyTemplateDirectory.ts
Outdated
srcDir: string, | ||
destinationDir: string, | ||
substitutions: Record<string, string>, | ||
variables: object, | ||
) { | ||
const filenames = await getFiles(srcDir); | ||
// eslint-disable-next-line no-restricted-syntax | ||
for await (const filename of filenames) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of doing await Promise.all(filenames.map(async (filename) => {
in order to stick to array iterators rule and remove that eslint-disable
line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call! Updated in e6043cd.
extends: [ | ||
'@thoughtbot/eslint-config/native', | ||
'@thoughtbot/eslint-config/typescript', | ||
], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
], | |
], | |
plugins: ['import'], | |
rules: { | |
'import/order': [ | |
'error', | |
{ | |
'newlines-between': 'always', | |
groups: [ | |
'builtin', | |
'external', | |
'internal', | |
'parent', | |
'sibling', | |
'index', | |
], | |
alphabetize: { | |
order: 'asc', | |
caseInsensitive: true, | |
}, | |
}, | |
], | |
}, |
The notifications command does introduce some rules for the import/order that weren't present in our @thoughtbot/eslint-config
that will be helpful to when we add new imports to files dynamically and need to rearrange them to keep things organized. What do you think of making it part of the boilerplate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we didn't have these rules before, how would you feel about capturing this in a ticket so we can implement it in a separate PR?
templates/boilerplate/.gitignore
Outdated
# typescript | ||
*.tsbuildinfo | ||
|
||
/.cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include the android
and ios
folders in the .gitignore
file by default or let that be added by specific commands when needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call! Updated in af1970a.
76c81fd
to
45899c0
Compare
Previously, the flow was: 1. Copy the template directory to the destination directory 2. Iterate through each of the files copied 1. Perform string substitutions 2. Render ETA templates (and remove .eta) When copying the "boilerplate" directory, we ideally ignore any files that are "gitignored" by this repo. This can be especially helpful with the "boilerplate" directory in the case where a developer has run the app and has a node_modules directory there. The new approach is: 1. Iterate through each of the files in the template directory 2. Filter out files that are gitignored (based on gitignore contents passed in) 3. For each file, read contents, perform substitutions, copy to destination
45899c0
to
1ae864a
Compare
1ae864a
to
76bc418
Compare
Remove diff-expo as npx expo install --fix already handles this
ec28f16
to
e6043cd
Compare
@codeofdiego thanks so much for your review! I think I've addressed your feedback, so this is ready for re-review when you get a chance. |
7dd3571
to
edcbf28
Compare
edcbf28
to
7fcfab2
Compare
Good questions! I don't know the answer yet, but at a high level, we should probably be using |
This has been open for a couple weeks, so I'm going to go ahead and merge it. |
This PR addresses tickets: Architecture of "create" vs individual commands and How to prevent flakiness when versions change?.
I recommend reading the context from those tickets for background information. The issues this PR seeks to address are:
Create architecture
Issues
The
create
command currently runs a collection of isolated individual commands. These commands sometimes modify similar files, likepackage.json
,App.tsx
, test files, etc. Because these commands touch the same files, it becomes difficult to "layer" these changes on top of each other. Each command is unable to make assumptions about the state of the file and must instead have a way to mutate the existing file. A secondary issue is that it becomes difficult to reason about what the final output of thecreate
command is without actually creating a new app (no easy way to visualize how the individual commands layer together).Solution
The
create
command has been refactored to no longer run any other commands. Instead, we have aboilerplate
directory that we copy directly to the new app directory. After that, we perform a few string substitutions to insert the app name in the appropriate places (package.json
,app.json
) and then install dependencies with the specified package manager. That's it!The
templates/boilerplate
directory is the entire app! You can literallycd templates/boilerplate
then runnpm install
andnpm run ios
ornpm run test:all
to run the app straight from there while updating it! This is a huge improvement in the dev experience and greatly simplifies understanding of what we're generating. This PR does not introduce any changes in the app that gets generated -- it's identical to what it was before. I generated theboilerplate
directory in this PR by creating an app using our old method and then copying the result toboilerplate
.Preventing flakiness
Issues
Previously, Belt has been using the latest version of Expo, and the latest version of any other dependencies it installs. Always being on the latest and greatest is certainly desired, but it comes at the cost of stability. If any dependency updates to a new major or minor version, there is a chance that Belt might quit working. This actually happened when Expo 50 was released (see #31).
Solution
We need to balance being up to date with being stable. While we want both, stability should be a non-negotiable, so let's prioritize stability and then find ways to simplify the dependency upgrade process so we can stay as up-to-date as possible. If we can streamline updating Belt to support newer versions of Expo or other dependencies to take just a few minutes, then we are more likely to be able to keep up-to-date.