Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit bb2d665

Browse files
committed
Baseline for the frame
1 parent f364f7d commit bb2d665

13 files changed

+14994
-1
lines changed

.eslintrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": ["important-stuff", "plugin:prettier/recommended"],
3+
"parser": "babel-eslint"
4+
}

.prettierignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.gitignore
2+
.prettierignore
3+
yarn.lock
4+
yarn-error.log
5+
package-lock.json
6+
LICENSE
7+
*.ejs
8+
dist

README.md

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,230 @@
1-
# micro-frame
1+
# Topcoder Frame Single-Spa Application (micro-frontends-frame)
2+
3+
This is the micro-frontends-frame [single-spa](https://single-spa.js.org/) application which loads all other Topcoder micro applications.
4+
It always loads **Topcoder Navbar Microapp** which show the top navigation and handles authorization and loads other microapps depend on the current URL.
5+
6+
## Overview
7+
8+
Topcoder Single Spa consist of 3 main components:
9+
10+
- This frame application which is `micro-frontends-frame` [single-spa](https://single-spa.js.org/) application. The only function of this application is to register other micro applications to load.
11+
- **Topcoder Navbar Microapp** - micro application which is always loaded by the frame application and shows top navigation bar and handles user authorization.
12+
- Any other micro application can be loaded as main content of the overall application.
13+
14+
## Requirements
15+
16+
- node - v10.22.1
17+
- npm - v6.14.6
18+
19+
## Config
20+
21+
This `micro-frontends-frame` app configs 2 things:
22+
23+
1. URLs to all microapps it can load inside
24+
25+
- edit `src/public/importmap-production.json` to configure microapp names and URL to be used when deployed to production
26+
- edit `src/public/importmap-local.json` to configure microapp names and URL to be used when deployed to locally
27+
28+
2. Mapping between URL path and microapp to load by that path in `src/index.ejs`.
29+
30+
To set `<MICRO_APP_NAME>` to be loaded on `<RELATIVE_URL_PATH>` URL path, add:
31+
32+
```html
33+
<route path="<RELATIVE_URL_PATH>">
34+
<application name="<MICRO_APP_NAME>"></application>
35+
</route>
36+
```
37+
38+
## NPM Commands
39+
40+
| Command | Description |
41+
| ---------------- | ----------------------------------------------------------------- |
42+
| `npm start` | Run server which serves production ready build from `dist` folder |
43+
| `npm run dev` | Run app in the development mode |
44+
| `npm run build` | Build app for production and puts files to the `dist` folder |
45+
| `npm run lint` | Check code for lint errors |
46+
| `npm run format` | Format code using prettier |
47+
| `npm run test` | Run unit tests |
48+
49+
## Local Deployment
50+
51+
To deploy `micro-frontends-frame` app locally run inside the project root:
52+
53+
- `npm i` to install dependencies
54+
- `npm run dev` to start the app on port `3000`
55+
56+
Note, that to make authorization work locally, you have to use domain `local.topcoder-dev.com` with port `3000`. So you should add into your `/etc/hosts` the line `127.0.0.1 local.topcoder-dev.com` and open app by URL http://local.topcoder-dev.com:3000.
57+
58+
## Deployment to Production
59+
60+
- `npm i` - install dependencies
61+
- `npm run build` - build code to `dist/` folder
62+
- Now you can host `dist/` folder using any static server with fallback to `index.html` file for any not found route. For example, you may run a simple `Express` server by running `npm start`.
63+
64+
### Deploying to Heroku
65+
66+
Make sure you have [Heroky CLI](https://devcenter.heroku.com/articles/heroku-cli) installed and you have a Heroku account. And then inside the project folder run the next commands:
67+
68+
- If there is not Git repository inited yet, create a repo and commit all the files:
69+
- `git init`
70+
- `git add .`
71+
- `git commit -m'inital commit'`
72+
- `heroku apps:create` - create Heroku app
73+
- `git push heroku master` - push changes to Heroku and trigger deploying
74+
- NOTE: Authorization would not work because only predefined list of domain allowed by `accounts-app`.
75+
76+
## Add/Remove child app
77+
78+
For adding a child app to the root app make the following steps:
79+
80+
1. Add child app path to importmap. User file `micro-frontends-frame/src/public/importmap-local.json` for local deployment and `micro-frontends-frame/src/public/importmap-production.json` for production:
81+
82+
React example:
83+
84+
```js
85+
"@topcoder/micro-frontends-react-app": "//localhost:8500/topcoder-micro-frontends-react-app.js"
86+
```
87+
88+
Angular example:
89+
90+
```js
91+
"@topcoder/micro-frontends-angular-app": "//localhost:4200/topcoder-micro-frontends-angular-app.js"
92+
```
93+
94+
2. Add a route which should show the app:
95+
96+
```html
97+
<route path="<RELATIVE_URL_PATH>">
98+
<application name="<MICRO_APP_NAME>"></application>
99+
</route>
100+
```
101+
102+
## Add-hoc child app replacement (import override)
103+
104+
To run a child app locally we always need to have frame (`micro-frontends-frame`) which would load a child app. But the cool thing is that we don't have to deploy the frame locally and we can use already deployed frame app. We can use a dev tool to override a child app URL so it would be loaded from the local machine by following the next steps:
105+
106+
- Load already deployed frame app in the browser.
107+
- Open browser console and set `devtools` flag in the local storage by executing the next command:
108+
```js
109+
localStorage.setItem("devtools", true);
110+
```
111+
- Reload the page, you would see the `{...}` icon in the bottom right corner.
112+
- Clicking it would open **Import Map Overrides** tool where you can change the URL for any loaded microapp to the local URL.
113+
Note, that if the frame is deployed using `HTTPS` protocol, then you have to run microapp locally using HTTPS protocol too. For example this could be done by `npm run dev-https` command in the **Topcoder Navbar Microapp**. Also, you would have to open the local app using direct HTTPS link first to make sure that browser allows loading it.
114+
- Now reload the page and you would see the microapp loaded into the frame from the local machine. You can update it locally and see changes in the frame which is deployed to the remote server.
115+
116+
See video [Javascript tutorial: local development with microfrontends, single-spa, and import maps](https://www.youtube.com/watch?v=vjjcuIxqIzY&list=PLLUD8RtHvsAOhtHnyGx57EYXoaNsxGrTU) as a part of the official documentation of Single Spa.
117+
118+
## Creating child apps (microapps)
119+
120+
⚠️ NOTE that once we configure React/Angular application be run as child application in Single SPA we cannot run it as standalone application anymore. So while this app can be deployed and run independently, we would need some frame [single-spa](https://single-spa.js.org/) which would load it. While technically we can achieve running this app as standalone app it's strongly not recommended by the author of the `single-spa` approch, see this [GitHub Issue](https://github.com/single-spa/single-spa/issues/640) for details.
121+
122+
### Create new or use existent Angular child app
123+
124+
Any existent Angular app could be configured to be used as child app, but exact way would depend on the Angular version, see [documentation](https://single-spa.js.org/docs/ecosystem-angular) for details. For the Angular >= 7 the next approach should work.
125+
126+
- If you don't have existent Angular app, create a new app using [Angular CLI](https://cli.angular.io/):
127+
128+
```sh
129+
ng new micro-frontends-angular-app --routing --prefix tc-ex
130+
```
131+
132+
⚠️ Note that the `--prefix` is important so that when you have multiple angular applications their component selectors won't have the same names.
133+
134+
- Now as we have Angular app, in the root of the application run the following command which would adapt Angular app so it could be run as a child app:
135+
136+
```sh
137+
ng add single-spa-angular
138+
```
139+
140+
Answer the following questions:
141+
142+
- Does your application use Angular routing?: `Y` _(we've created the app using `--routing` flag, so yes)_
143+
- Does your application use BrowserAnimationsModule? `N` _(using browser animations might break re-rendering of the app and might require additional fixes)_
144+
145+
- Run `npm i` to install dependencies added by `ng add single-spa-angular`
146+
147+
- Now you must [configure routes](https://single-spa.js.org/docs/ecosystem-angular/#configure-routes). Update `src/app/app-routing.module.ts`:
148+
149+
- Add 2 imports:
150+
151+
```js
152+
import { EmptyRouteComponent } from "./empty-route/empty-route.component";
153+
import { APP_BASE_HREF } from "@angular/common";
154+
```
155+
156+
- Add route to the `routes` array:
157+
158+
```js
159+
const routes: Routes = [
160+
// ... other routes here
161+
{ path: "**", component: EmptyRouteComponent }, // add this line
162+
];
163+
```
164+
165+
- Add provider to the `providers` array:
166+
167+
```js
168+
@NgModule({
169+
imports: [RouterModule.forRoot(routes)], // imports stay as it is
170+
exports: [RouterModule], // exports stay as it is
171+
providers: [ // add providers array if it's not here
172+
// ... other providers here
173+
{ provide: APP_BASE_HREF, useValue: '/' }, // add this line
174+
],
175+
})
176+
```
177+
178+
- Add a declaration for `EmptyRouteComponent` in `app.module.ts`:
179+
180+
```js
181+
// add import for `EmptyRouteComponent`
182+
import { EmptyRouteComponent } from './empty-route/empty-route.component';
183+
184+
...
185+
186+
declarations: [
187+
// ... other declarations here
188+
EmptyRouteComponent, // add this line
189+
],
190+
```
191+
192+
Now the new Angular app is ready to be run as child app. You can run it by `npm run serve:single-spa:micro-frontends-angular-app`. To see it in browser you have to config the root app to show it first, see "[Add/Remove child app](#addremove-child-app)" section.
193+
194+
The URL for the Angular app started this way would look like:
195+
196+
```
197+
//localhost:4200/micro-frontends-angular-app.js
198+
```
199+
200+
⚠️ Note, that you cannot see this Angular app a standalone app anymore, `npm start` would not work.
201+
202+
### Create new React child app
203+
204+
The easiest way to create a new React child app is using [create-single-spa](https://single-spa.js.org/docs/create-single-spa/) CLI tool.
205+
206+
Run `npx create-single-spa` and answer questions:
207+
208+
- Directory for new project: `micro-frontends-react-app`
209+
- Select type to generate: `single-spa application / parcel`
210+
- Which framework do you want to use?: `react`
211+
- Which package manager do you want to use?: `npm`
212+
- Will this project use Typescript?: `N`
213+
- Organization name: `topcoder`
214+
- Project name: `micro-frontends-react-app`
215+
216+
Now the new React app is created in the folder `micro-frontends-react-app`. You can run it by `npm start -- --port 8500`. To see it in browser you have to config the root app to show it first, see "[Add/Remove child app](#addremove-child-app)" section.
217+
218+
The URL for the React app started this way would look like:
219+
220+
```
221+
//localhost:8500/topcoder-micro-frontends-react-app.js
222+
```
223+
224+
⚠️ Note, that you cannot see this React app a standalone app.
225+
226+
### Use existent React child app
227+
228+
There is no universal approach to run any React app as child app in Single SPA. This is because unlike Angular application which always use Angular CLI, each React application has it's own Webpack config. And to be able to run React app as a child microapp we need the Webpack to be configured in a certain way.
229+
230+
- Here is Official Video form the creator of Single Spa on [How To Convert a create-react-app (CRA) project to single-spa](https://www.youtube.com/watch?v=W8oaySHuj3Y&list=PLLUD8RtHvsAOhtHnyGx57EYXoaNsxGrTU)

babel.config.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"presets": ["@babel/preset-env"],
3+
"plugins": [
4+
[
5+
"@babel/plugin-transform-runtime",
6+
{
7+
"useESModules": true,
8+
"regenerator": false
9+
}
10+
]
11+
],
12+
"env": {
13+
"test": {
14+
"presets": [
15+
[
16+
"@babel/preset-env",
17+
{
18+
"targets": "current node"
19+
}
20+
]
21+
]
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)