Bellow, you will find some notes I took along the way.
create-react-app related
- React specific
- Miscellaneous
Due to the following error:
> react-scripts test --env=jsdom
Determining test suites to run...2016-10-07 11:38 node[873] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
2016-10-07 11:38 node[873] (FSEvents.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-22)
events.js:160
throw er; // Unhandled 'error' event
^
Error: Error watching file for changes: EMFILE
at exports._errnoException (util.js:953:11)
at FSEvent.FSWatcher._handle.onchange (fs.js:1400:11)
npm ERR! Test failed. See above for more details.
I installed watchman, which solved the problem (known problem on MacOS).
Note: create-react-app
doesn't rely on the latest version jest
(currently [email protected]
). This should be resolved for more recent versions.
The registry.npmjs.org
doesn't return CORS headers, so we need some mechanism like CORS anywhere development proxy. Thankfully, create-react-app
ships with that kind of thing: read here.
If you take a look at the "proxy"
field of the package.json
, you'll see the following:
"proxy": {
"/api/npm-registry": {
"target": "https://registry.npmjs.org",
"changeOrigin": true,
"pathRewrite": {
"^/api/npm-registry": ""
}
},
"/api/npm-api": {
"target": "https://api.npmjs.org",
"changeOrigin": true,
"pathRewrite": {
"^/api/npm-api": ""
}
}
}
This means that when you npm start
, /api/npm-registry
and /api/npm-api
urls will proxy to there APIs, from the root of your development server.
That way, in development mode, you don't have to worry about CORS and your api server will definitly be accessible from a mobile device (when you test on wifi - not from localhost
but with a real IP, you'll have the same experience).
You will notice that the .env file sets the following urls for the servers (which are the ones that will be used in development):
REACT_APP_NPM_REGISTRY_API_BASE_URL=/api/npm-registry
REACT_APP_NPM_API_BASE_URL=/api/npm-api
In production, you'll need to provide production servers that support CORS, this is why you have .env.production.
In .env.production, a specific CORS proxy is setup that will be used at build time.
Here are the commits where I setup prettier and eslint, you should check the infos in the commit messages:
-
3372bad - chore(prettier): setup prettier with eslint
Based on Using Prettier with VS Code and Create React App
npm install --save-dev --save-exact prettier npm install --save-dev eslint-plugin-prettier
-
a4a67b9 - style(*): prettify code
-
247d4ef - chore(eslint): setup airbnb rules
eslint-config-airbnb has peer dependencies, take the latest v15 to ensure compatibility with installed versions of the dependencies in create-react-app
npm install --save-dev [email protected]
Turns off all rules that are unnecessary or might conflict with Prettier: eslint-config-prettier.
npm install --save-dev eslint-config-prettier
-
4746848 - chore(prettier): add precommit hook
I had the following error lying in the cypress console:
Error: Blocked a frame with origin "http://localhost:5000" from accessing a cross-origin frame.
Solution: add following in cypress.json:
"chromeWebSecurity": false
Sources: cypress-io/cypress#262
Also, when the app is ran inside cypress, the calls to outside are proxied by cypress, the Origin
header is not added on CORS requests.
You can't manually set this header, you'll have the following error: Refused to set unsafe header "Origin"
Since the CORS proxies used in production ask for this origin header and it's missing when ran in cypress, we don't run through the CORS proxy when running tests in cypress with a production build - see src/services/apis/index.js.
This project applies multiple CI (Continuous Integration) good practices such as:
- Linting / Formatting of the source code
- Unit tests
- End to end tests
We can rely on them when going through Travis CI to automate the deployment on multiple environments, such as:
- staging: https://staging-npm-registry-browser.surge.sh
- production: https://topheman.github.io/npm-registry-browser/
- mocked version: https://mock-npm-registry-browser.surge.sh/
- upload build artefacts to github releases: https://github.com/topheman/npm-registry-browser/releases
I am using github-pages and surge.sh for hosting, you will see that Travis CI supports a lot of providers out of the box.
Here are some example of use of providers. If you want multiple ones at a time (Mutiple Provider), you can take a look at my .travis.yml
file.
Add the 2 following env vars to the travis settings of your repo:
SURGE_LOGIN
: Set it to the email address you use with SurgeSURGE_TOKEN
: Set it to your login token (get it by runningsurge token
)
Update your .travis.yml
with:
deploy:
provider: surge
project: ./build/
domain: example.surge.sh
skip_cleanup: true
📂 Checkout the PR where I implemented it
You might want to archive the builds you deployed for multiple reasons, for example:
- expose them internally to your team to ease re-deployment
- keep them to download the exact same release when someone files an issue on a specific version
To do that, we compress the build
folder and upload it to github via its API, using the release
provider from Travis CI.
Follow the steps:
- Install travis-cli
- Log into travis on the terminal inside your project folder travis login
- Run
travis setup releases
sources
This will ask for a few credentials and add the necessary configuration in your .travis.yml
file. You will end up with a config like that:
# add this step to compress the build folder
before_deploy:
- tar czvf build.tar.gz -C build .
deploy:
provider: releases
api_key: "GITHUB OAUTH TOKEN"
file: "build.tar.gz"
skip_cleanup: true
on:
tags: true
📂 Checkout the PR where I implemented it
If you use github-pages as hosting, you can automate the deployment. You'll need a github token (you can generate it from here with the public_repo
scope).
Then add the following to your .travis.yml
:
deploy:
provider: pages
skip-cleanup: true
github-token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable
keep-history: true
on:
branch: master
📂 Checkout the PR where I implemented it
A pull request sent from a fork of the upstream repository could be manipulated to expose environment variables. Travis CI makes encrypted variables and data available only to pull requests coming from the same repository. These are considered trustworthy, as only members with write access to the repository can send them. Pull requests sent from forked repositories do not have access to encrypted variables or data.
Since cypress needs a record key passed as an environment value, on PRs from forks, you would come against the following error:
You passed the --record flag but did not provide us your Record Key.
You can pass us your Record Key like this:
cypress run --record --key <record_key>
You can also set the key as an environment variable with the name CYPRESS_RECORD_KEY.
https://on.cypress.io/how-do-i-record-runs
To avoid that, I disable the record on PRs.
The app relies on three APIs:
- https://registry.npmjs.org
- the API used by the npm cli
- provides package details and search results
- https://api.npmjs.org
- provides stats
- https://api.npms.io
- provides search and suggestions
api.npms.io
provides sharper search results (even the npmjs.com website uses it under the hood). It also has an interresting api /search/suggestions
(more oriented for autocomplete).
From the beginning I made wrappers for the API calls of both registry.npmjs.org
and api.npms.io
that took the same kind of input/output, so they could be switchable.
Some day, api.npms.io
went down and all the search features were broken. All I had to do was switch the api client (temporary solution) - see bellow for the final fix 👇.
I went back enventually, but made sure that, if api.npms.io
is down, the app automatically switches back to registry.npmjs.org
. That way, the user can enjoy a great search experience and if the api.npms.io
goes down, it will seamlessly switch, without any downtime, without him/her noticing.
PR where I implemented fallback.
Deprecated: please use API proxy for development.
The npm registry doesn't return any CORS headers nor can it be called with jsonp. So any in-browser XHR request will be blocked.
I made a little script that will proxy any request, adding those CORS headers to the response. You can launch it with npm run proxy-apis
.
Note: ./bin/cors-anywhere.js
is for development purpose only, it may not be suited for production (at least update the originWhiteList
).
Use it for any api you will use in that project, they will be accessible (without anymore configuration) at http://localhost:8000/ROOT_API_URL
. Examples:
http://localhost:8000/https://registry.npmjs.org
http://localhost:8000/https://api.npmjs.org