Skip to content

Commit

Permalink
Migrate the repo to a PNPM@7-based monorepo with apps and libraries (#3)
Browse files Browse the repository at this point in the history
For now, there are two skeleton React apps and no libraries.
  • Loading branch information
stefcameron committed Feb 16, 2024
1 parent 758a2f3 commit ccf6880
Show file tree
Hide file tree
Showing 30 changed files with 8,472 additions and 15,030 deletions.
15 changes: 11 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,17 @@ const jestSettings = {
},
};

// where packages are located in the repo; does NOT end with a slash
const pkgGlobs = ['apps/*', 'libraries/*'];

module.exports = {
root: true,
overrides: [
// project JavaScript files (tooling, etc.)
{
// traditional CJS/require scripts
files: ['**/*.js'],
excludedFiles: ['src/**/*.*'],
excludedFiles: pkgGlobs.map((glob) => `${glob}/src/**/*.*`),
extends: jsExtends,
parserOptions: {
...parserOptions,
Expand All @@ -232,8 +236,9 @@ module.exports = {
},
},
{
// modern ESM/import scripts
files: ['**/*.mjs'],
excludedFiles: ['src/**/*.*'],
excludedFiles: pkgGlobs.map((glob) => `${glob}/src/**/*.*`),
extends: jsExtends,
parserOptions,
env,
Expand All @@ -245,7 +250,7 @@ module.exports = {

// source files
{
files: ['src/**/*.{js,jsx}'],
files: pkgGlobs.map((glob) => `${glob}/src/**/*.{js,jsx}`),

// @see https://www.npmjs.com/package/@babel/eslint-plugin
// currently, none of the rules overridden in the plugin are enforced here
Expand All @@ -271,7 +276,9 @@ module.exports = {
// extension; and just test.<ext> or spec.<ext>; as long as the file is inside
// a __test__ directory at any depth within the base path
files: [
'src/**/__tests__/**/?(*.)+(spec|test).{js,jsx}',
...pkgGlobs.map(
(glob) => `${glob}/src/**/__tests__/**/?(*.)+(spec|test).{js,jsx}`
),
'tools/tests/**/*.js',
],

Expand Down
18 changes: 0 additions & 18 deletions .github/dependabot.yml

This file was deleted.

43 changes: 13 additions & 30 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ name: CI
on:
push:
branches:
- 'master'
- master
pull_request:

jobs:
test:
name: Lint/Test/Build
name: Master CI
runs-on: ubuntu-latest
env:
CI: true
Expand All @@ -19,43 +19,26 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup environment
id: setup
run: |-
echo "npm-cache-dir=$(npm config get cache)" >> ${GITHUB_OUTPUT}
- name: Setup PNPM
uses: pnpm/action-setup@v3
with:
version: 7

- name: Setup Node ${{ matrix.node }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
check-latest: true

# TODO: this does work (it'll find the cache) but it appears it's not putting the cache
# back into ./node_modules because skipping the install step results in subsequent
# steps failing with messages like `prettier not found`, which implies there's no
# node_modules directory there with Prettier installed in it...
#
# - name: NPM cache check
# uses: actions/cache@v3
# id: npm-cache
# with:
# path: ${{ steps.setup.outputs.npm-cache-dir }}
# key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
# restore-keys: |
# ${{ runner.os }}-node-
cache: pnpm

- name: Install packages
# if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
run: pnpm install --frozen-lockfile

- name: Lint
run: |-
npm run fmt:check;
npm run lint;
run: pnpm run ci:lint

- name: Test
run: |-
npm run test:unit;
- name: Build
run: pnpm run ci:build

- name: Build # Tests to see if a build can succeed
run: npm run build
- name: Test
run: pnpm run ci:test
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
# Try Micro Frontends

An exploration of micro-frontend application architecture in a PNPM-based monorepo context.
An exploration of micro-frontend application architecture in a PNPM-based monorepo context, starting with an existing, not necessarily ideal, monorepo layout.

The focus is on:

- how PNPM manages the workspace;
- how to tweak the monorepo layout to make it work when transforming `//apps/app1` into a set of sub-packages; and
- how the various packages can be statically or dynamically imported to be composed into "applications".

> ❗️ The focus is __not on__ proper monorepo layout, tooling, deduplication of build configs, etc.
## Workspace

- Packages MUST have `build` and `ci:build` scripts. They MUST yield the prod build, one under local conditions and the other under CI.
- Packages MAY have a `ci:test` script. It SHOULD perform any additional test-related tasks that need to run under CI.
- For the purposes of this POC, it's not possible to format/lint code in an individual package; only from the root.

---

> Based on [React App Template](https://github.com/stefcameron/react-app-template).
## Packages

Based on [React App Template](https://github.com/stefcameron/react-app-template).

A "create react app"-style repo with a stack that I find works well, is easy to
understand, and doesn't need to be ejected in order to get into its guts
Expand All @@ -26,28 +42,28 @@ and figure out why it isn't working if something comes up.
- Formatting: [Prettier](https://prettier.io/)
- Bundling: [Webpack](https://webpack.js.org/)

## Running
### Running

Using the latest stable version of Node (v20) and NPM (v9.6)...

```bash
$ npm install
$ pnpm install
# installs all dependencies
$ npm start
$ pnpm start
# opens a browser to localhost:3000
# set PORT=XXXX env to run on a different port
# set PORT=XXXX in env to run on a different port

$ npm fmt
$ pnpm fmt
# formats the code using Prettier
$ npm build
$ pnpm build
# builds the production bundle
$ npm build:dev
$ pnpm build:dev
# builds the development bundle
```

> 💬 If your browser doesn't open, please open it manually to `localhost:3000`
## Testing
### Testing

```bash
$ npm test
Expand All @@ -60,9 +76,9 @@ $ npm run fmt:check
# runs Prettier in verification mode only
```

## Styles
### Styles

Pure CSS, just `import './MyComponent.styles.css'` in your component's module
the styles will get loaded whenever/if ever the module is loaded at runtime.

See `./src/components/App/App.js` for an example.
See `./apps/app1/src/components/App/App.js` for an example.
21 changes: 21 additions & 0 deletions apps/app1/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015-2016 David Clark

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
34 changes: 34 additions & 0 deletions apps/app1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"private": true,
"name": "@try-micro-frontends/app1",
"version": "1.0.0",
"description": "Application 1",
"scripts": {
"build": "webpack",
"build:dev": "NODE_ENV=development webpack",
"ci:build": "pnpm build",
"fmt": "cd ../..; pnpm fmt",
"fmt:check": "cd ../..; pnpm fmt:check",
"lint": "cd ../..; pnpm lint",
"start": "NODE_ENV=development webpack-dev-server",
"test:coverage": "jest --config=\"../../jest.config.mjs\" --coverage",
"test:unit": "jest --config=\"../../jest.config.mjs\"",
"test": "npm run build && npm run test:coverage"
},
"author": "Stefan Cameron",
"license": "MIT",
"peerDependencies": {
"classnames": "^2.5.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0"
},
"devDependencies": {
"classnames": "^2.5.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import './App.styles.css';
export const App = function () {
return (
<div className="app">
<h1>React App</h1>
<h2>Template</h2>
<h1>React App 1</h1>
</div>
);
};
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { App } from '../App';
describe('/components/App', () => {
it('renders the app title', () => {
render(<App />);
expect(screen.getByText('React App')).toBeInTheDocument();
expect(screen.getByText('React App 1')).toBeInTheDocument();
});

it('is accessible', async () => {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions webpack.config.mjs → apps/app1/webpack.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// WEBPACK build configuration
// APP1 WEBPACK build configuration
//
// ENV:
//
Expand Down Expand Up @@ -57,7 +57,7 @@ const getOpenConfig = function () {
};

const loadBabelConfig = function () {
const filepath = path.resolve(__dirname, './babel.config.js');
const filepath = path.resolve(__dirname, '../../babel.config.js');

// NOTE: we must use inline `require()` statements since the Babel config
// files are JS files; we need to not only read them, but evaluate them
Expand Down
21 changes: 21 additions & 0 deletions apps/app2/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2015-2016 David Clark

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
34 changes: 34 additions & 0 deletions apps/app2/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"private": true,
"name": "@try-micro-frontends/app2",
"version": "1.0.0",
"description": "Application 2",
"scripts": {
"build": "webpack",
"build:dev": "NODE_ENV=development webpack",
"ci:build": "pnpm build",
"fmt": "cd ../..; pnpm fmt",
"fmt:check": "cd ../..; pnpm fmt:check",
"lint": "cd ../..; pnpm lint",
"start": "NODE_ENV=development webpack-dev-server",
"test:coverage": "jest --config=\"../../jest.config.mjs\" --coverage",
"test:unit": "jest --config=\"../../jest.config.mjs\"",
"test": "npm run build && npm run test:coverage"
},
"author": "Stefan Cameron",
"license": "MIT",
"peerDependencies": {
"classnames": "^2.5.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0"
},
"devDependencies": {
"classnames": "^2.5.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0"
}
}
9 changes: 9 additions & 0 deletions apps/app2/src/components/App/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import './App.styles.css';

export const App = function () {
return (
<div className="app">
<h1>React App 2</h1>
</div>
);
};
17 changes: 17 additions & 0 deletions apps/app2/src/components/App/App.styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.app {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100vw;
height: 25vh;
}

.app > h1 {
color: lightslategray;
}

.app > h2 {
color: lightblue;
font-family: var(--app-font-family-monospace);
}
Loading

0 comments on commit ccf6880

Please sign in to comment.