initial commit
This commit is contained in:
commit
ed9650997e
48 changed files with 31481 additions and 0 deletions
120
.gitignore
vendored
Normal file
120
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
# astro build output
|
||||
dist/
|
||||
.output/
|
||||
.astro/
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sergio A. Arevalo Soria
|
||||
|
||||
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.
|
||||
27
README.md
Normal file
27
README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# astro-microfrontends
|
||||
|
||||
This demo takes advantage of EcmaScript Modules (ESM) and url-imports to do client-side composition of microfrontends. Express is used for serving assets, but ideally one would have a dedicated CDN hosting them.
|
||||
|
||||
## Features
|
||||
|
||||
- ✨ Client-side composition of microfrontends
|
||||
- 📦 Multiframeworks with Astro Islands
|
||||
- 🚀 SSG/SSR supported by Astro
|
||||
|
||||
## Usage
|
||||
|
||||
### Micro frontends
|
||||
|
||||
- Build each micro frontend with `npm run build`
|
||||
- Start each micro frontend with `npm run preview`
|
||||
|
||||
For production you would start the node server in the `server` folder, after building.
|
||||
|
||||
### Shell
|
||||
|
||||
- Build the shell with `npm run build`
|
||||
- Start the shell with `npm run preview`
|
||||
|
||||
## Shared dependencies
|
||||
|
||||
Dependencies such as react and react-dom are shared across applications. They are fetched from [esm.sh](https://esm.sh/) and gets cached in the browser, reducing the bundle size. Each app can share other dependencies as well through url imports.
|
||||
12
app-cart/index.html
Normal file
12
app-cart/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="no">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Mikrofrontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
2343
app-cart/package-lock.json
generated
Normal file
2343
app-cart/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
app-cart/package.json
Normal file
26
app-cart/package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "app-a",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build:preview": "vite build && vite preview",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"utils": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "1.0.7",
|
||||
"vite": "3.2.3",
|
||||
"vite-plugin-css-injected-by-js": "1.4.0"
|
||||
},
|
||||
"main": "vite.config.js",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": ""
|
||||
}
|
||||
1053
app-cart/server/package-lock.json
generated
Normal file
1053
app-cart/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
15
app-cart/server/package.json
Normal file
15
app-cart/server/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "navikt",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
"express": "4.18.2"
|
||||
}
|
||||
}
|
||||
11
app-cart/server/server.js
Normal file
11
app-cart/server/server.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const path = require("path");
|
||||
const buildPath = path.resolve(__dirname, "../dist");
|
||||
const server = express();
|
||||
|
||||
server.use(cors({ origin: "http://localhost:3000" }));
|
||||
|
||||
server.use("/", express.static(buildPath));
|
||||
|
||||
server.listen(7100, () => console.log("Server listening on port 7100"));
|
||||
3
app-cart/src/App.css
Normal file
3
app-cart/src/App.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.app-a {
|
||||
color: #7c3aed;
|
||||
}
|
||||
11
app-cart/src/App.jsx
Normal file
11
app-cart/src/App.jsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
import Cart from "./Cart";
|
||||
import "./App.css";
|
||||
|
||||
const App = () => (
|
||||
<section className="app-a">
|
||||
<Cart />
|
||||
</section>
|
||||
);
|
||||
|
||||
export default App;
|
||||
71
app-cart/src/Cart.jsx
Normal file
71
app-cart/src/Cart.jsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// @ts-check
|
||||
// import { broadcast } from 'utils/orchestrator'
|
||||
import React, { useState } from "react";
|
||||
const Cart = () => {
|
||||
const [products, setProducts] = useState([
|
||||
{
|
||||
name: "Shoe A",
|
||||
description: "It is a good shoe",
|
||||
price: 100,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
name: "Shoe B",
|
||||
description: "It is a comfortable shoe",
|
||||
price: 120,
|
||||
count: 7,
|
||||
},
|
||||
]);
|
||||
|
||||
// listen to add-to-cart messages
|
||||
// if item is already in cart, increment count
|
||||
|
||||
const getTotal = () => {
|
||||
return products.reduce((acc, product) => {
|
||||
return acc + product.price * product.count;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const decreaseCount = (index) => {
|
||||
// if in cart, decrease count
|
||||
const updatedProducts = [...products];
|
||||
if (updatedProducts[index].count > 1) {
|
||||
updatedProducts[index].count--;
|
||||
} else {
|
||||
updatedProducts.splice(index, 1);
|
||||
}
|
||||
setProducts(updatedProducts);
|
||||
};
|
||||
|
||||
const increaseCount = (index) => {
|
||||
// if in cart, increase count
|
||||
const updatedProducts = [...products];
|
||||
updatedProducts[index].count++;
|
||||
setProducts(updatedProducts);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>🛒 Cart</h2>
|
||||
<ul>
|
||||
{products.map((product, index) => (
|
||||
<li key={index}>
|
||||
<span>({product.count}x) </span>
|
||||
<span>{product.name}</span>
|
||||
<span> - </span>
|
||||
<strong>${product.price * product.count}</strong>
|
||||
<span>
|
||||
<button onClick={() => decreaseCount(index)}> - </button>
|
||||
<button onClick={() => increaseCount(index)}> + </button>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<span>Total: </span>
|
||||
<strong>${getTotal()}</strong>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cart;
|
||||
22
app-cart/vite.config.js
Normal file
22
app-cart/vite.config.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import cssInject from "vite-plugin-css-injected-by-js";
|
||||
import { resolve } from "path";
|
||||
|
||||
export default ({ command }) => ({
|
||||
plugins: [react(), cssInject()],
|
||||
preview: {
|
||||
port: 7100,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: resolve(__dirname, "src/App.jsx"),
|
||||
preserveEntrySignatures: "exports-only",
|
||||
external: ["react", "react-dom"],
|
||||
output: {
|
||||
entryFileNames: "bundle.js",
|
||||
format: "esm",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
12
app-heading/index.html
Normal file
12
app-heading/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="no">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Mikrofrontend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
2343
app-heading/package-lock.json
generated
Normal file
2343
app-heading/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
40
app-heading/package.json
Normal file
40
app-heading/package.json
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "app-b",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build:preview": "vite build && vite preview",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"utils": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "1.0.7",
|
||||
"vite": "3.2.3",
|
||||
"vite-plugin-css-injected-by-js": "1.4.0"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx}": [
|
||||
"eslint -c .eslintrc.pre-commit.cjs --fix",
|
||||
"prettier --cache --write",
|
||||
"git add"
|
||||
],
|
||||
"*.{css,md,html,json}": [
|
||||
"prettier --cache --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"main": "vite.config.js",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": ""
|
||||
}
|
||||
1053
app-heading/server/package-lock.json
generated
Normal file
1053
app-heading/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
15
app-heading/server/package.json
Normal file
15
app-heading/server/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "navikt",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
"express": "4.18.2"
|
||||
}
|
||||
}
|
||||
11
app-heading/server/server.js
Normal file
11
app-heading/server/server.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const path = require("path");
|
||||
const buildPath = path.resolve(__dirname, "../dist");
|
||||
const server = express();
|
||||
|
||||
server.use(cors({ origin: "http://localhost:3000" }));
|
||||
|
||||
server.use("/", express.static(buildPath));
|
||||
|
||||
server.listen(7200, () => console.log("Server listening on port 7200"));
|
||||
3
app-heading/src/App.css
Normal file
3
app-heading/src/App.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.app-b {
|
||||
color: #7c3aed;
|
||||
}
|
||||
19
app-heading/src/App.jsx
Normal file
19
app-heading/src/App.jsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React from "react";
|
||||
import "./App.css";
|
||||
|
||||
const App = (props) => {
|
||||
const { title } = props;
|
||||
const [count, setCount] = React.useState(0);
|
||||
|
||||
// listen to add-to-cart messages
|
||||
|
||||
return (
|
||||
<section className="app-b">
|
||||
<h1>
|
||||
{title} <span style={{ background: "red", color: "white", padding: "0 0.25rem" }}>{count}</span>
|
||||
</h1>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
22
app-heading/vite.config.js
Normal file
22
app-heading/vite.config.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import cssInject from "vite-plugin-css-injected-by-js";
|
||||
import { resolve } from "path";
|
||||
|
||||
export default ({ command }) => ({
|
||||
plugins: [react(), cssInject()],
|
||||
preview: {
|
||||
port: 7200,
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: resolve(__dirname, "src/App.jsx"),
|
||||
preserveEntrySignatures: "exports-only",
|
||||
external: ["react", "react-dom"],
|
||||
output: {
|
||||
entryFileNames: "bundle.js",
|
||||
format: "esm",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
2
app-products/.gitignore
vendored
Normal file
2
app-products/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
dist
|
||||
34
app-products/README.md
Normal file
34
app-products/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
## Usage
|
||||
|
||||
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
|
||||
|
||||
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
|
||||
|
||||
```bash
|
||||
$ npm install # or pnpm install or yarn install
|
||||
```
|
||||
|
||||
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm dev` or `npm start`
|
||||
|
||||
Runs the app in the development mode.<br>
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br>
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `dist` folder.<br>
|
||||
It correctly bundles Solid in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br>
|
||||
Your app is ready to be deployed!
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
|
||||
15
app-products/index.html
Normal file
15
app-products/index.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>Solid App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<script src="/src/index.jsx" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
15
app-products/jsconfig.json
Normal file
15
app-products/jsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"types": ["vite/client"],
|
||||
"noEmit": true,
|
||||
"isolatedModules": true
|
||||
}
|
||||
}
|
||||
2582
app-products/package-lock.json
generated
Normal file
2582
app-products/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
app-products/package.json
Normal file
24
app-products/package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "vite-template-solid",
|
||||
"version": "0.0.0",
|
||||
"description": "Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build:preview": "vite build && vite preview"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"vite": "^4.1.1",
|
||||
"vite-plugin-css-injected-by-js": "3.0.1",
|
||||
"vite-plugin-solid": "^2.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"solid-js": "^1.6.10",
|
||||
"utils": "*"
|
||||
},
|
||||
"main": "vite.config.js",
|
||||
"author": ""
|
||||
}
|
||||
1053
app-products/server/package-lock.json
generated
Normal file
1053
app-products/server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
15
app-products/server/package.json
Normal file
15
app-products/server/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "navikt",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "2.8.5",
|
||||
"express": "4.18.2"
|
||||
}
|
||||
}
|
||||
11
app-products/server/server.js
Normal file
11
app-products/server/server.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const path = require("path");
|
||||
const buildPath = path.resolve(__dirname, "../dist");
|
||||
const server = express();
|
||||
|
||||
server.use(cors({ origin: "http://localhost:3000" }));
|
||||
|
||||
server.use("/", express.static(buildPath));
|
||||
|
||||
server.listen(7300, () => console.log("Server listening on port 7300"));
|
||||
63
app-products/src/App.jsx
Normal file
63
app-products/src/App.jsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import styles from "./App.module.css";
|
||||
|
||||
function App() {
|
||||
const products = [
|
||||
{
|
||||
name: "Shoe A",
|
||||
description: "It is a good shoe",
|
||||
price: 100,
|
||||
},
|
||||
{
|
||||
name: "Shoe B",
|
||||
description: "It is a comfortable shoe",
|
||||
price: 120,
|
||||
},
|
||||
{
|
||||
name: "Shoe C",
|
||||
description: "It is a stylish shoe",
|
||||
price: 150,
|
||||
},
|
||||
{
|
||||
name: "Shoe D",
|
||||
description: "It is a durable shoe",
|
||||
price: 90,
|
||||
},
|
||||
];
|
||||
|
||||
const addToCart = (product) => {
|
||||
console.log(">>> add to cart", product);
|
||||
// broadcast add product to cart
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles["app-c"]}>
|
||||
<h2>🥾 Products</h2>
|
||||
<div
|
||||
style="
|
||||
display:flex;
|
||||
flex-wrap:wrap;
|
||||
"
|
||||
>
|
||||
{products.map((product, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style="
|
||||
border:1px solid #ccc;
|
||||
padding:30px;
|
||||
border-radius:5px;
|
||||
margin: 0 15px 15px 0;
|
||||
width: calc(50% - 85px)
|
||||
"
|
||||
>
|
||||
<strong>{product.name}</strong>
|
||||
<p>{product.description}</p>
|
||||
<p>Price: ${product.price}</p>
|
||||
<button onClick={() => addToCart(product)}>Add to Cart</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
3
app-products/src/App.module.css
Normal file
3
app-products/src/App.module.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.app-c {
|
||||
color: #7c3aed;
|
||||
}
|
||||
13
app-products/src/index.css
Normal file
13
app-products/src/index.css
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
26
app-products/vite.config.js
Normal file
26
app-products/vite.config.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { defineConfig } from "vite";
|
||||
import solidPlugin from "vite-plugin-solid";
|
||||
import { resolve } from "path";
|
||||
import cssInject from "vite-plugin-css-injected-by-js";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solidPlugin(), cssInject()],
|
||||
server: {
|
||||
port: 3000,
|
||||
},
|
||||
preview: {
|
||||
port: 7300,
|
||||
},
|
||||
build: {
|
||||
target: "esnext",
|
||||
rollupOptions: {
|
||||
input: resolve(__dirname, "src/App.jsx"),
|
||||
preserveEntrySignatures: "exports-only",
|
||||
external: ["solid-js"],
|
||||
output: {
|
||||
entryFileNames: "bundle.js",
|
||||
format: "esm",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
20
app-shell/astro.config.mjs
Normal file
20
app-shell/astro.config.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import react from "@astrojs/react";
|
||||
import solidJs from "@astrojs/solid-js";
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
react(),
|
||||
solidJs(),
|
||||
{
|
||||
name: 'importmap-externals',
|
||||
hooks: {
|
||||
'astro:build:setup': ({ vite, target }) => {
|
||||
if(target === 'client') {
|
||||
vite.build.rollupOptions["external"] = ["react", "react-dom", "solid-js"];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
12149
app-shell/package-lock.json
generated
Normal file
12149
app-shell/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
33
app-shell/package.json
Normal file
33
app-shell/package.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "@example/basics",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"build:preview": "astro build && astro preview",
|
||||
"astro": "astro",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/react": "3.0.7",
|
||||
"@astrojs/solid-js": "3.0.2",
|
||||
"@types/react": "18.0.27",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"astro": "4.0.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"solid-js": "1.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.8.4",
|
||||
"prettier-plugin-astro": "0.8.0"
|
||||
},
|
||||
"main": "index.js",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": ""
|
||||
}
|
||||
17
app-shell/src/components/ReactComponent.jsx
Normal file
17
app-shell/src/components/ReactComponent.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import MicroFrontendA from "http://localhost:7100/bundle.js";
|
||||
import MicroFrontendB from "http://localhost:7200/bundle.js";
|
||||
|
||||
const ReactComponent = (props) => {
|
||||
const { title } = props;
|
||||
|
||||
console.log(">>> title", title);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MicroFrontendB title={title} />
|
||||
<MicroFrontendA />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReactComponent;
|
||||
11
app-shell/src/components/SolidComponent.jsx
Normal file
11
app-shell/src/components/SolidComponent.jsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import MicroFrontendC from "http://localhost:7300/bundle.js";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<>
|
||||
<MicroFrontendC />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
1
app-shell/src/env.d.ts
vendored
Normal file
1
app-shell/src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="astro/client" />
|
||||
56
app-shell/src/pages/index.astro
Normal file
56
app-shell/src/pages/index.astro
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
import ReactComponent from "../components/ReactComponent.jsx";
|
||||
import SolidComponent from "../components/SolidComponent.jsx";
|
||||
const appName = 'Scalable Shoe Shop'
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{appName}</title>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"react": "https://esm.sh/react",
|
||||
"react-dom": "https://esm.sh/react-dom",
|
||||
"solid-js": "https://esm.sh/solid-js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<ReactComponent title={appName} client:only="react" />
|
||||
<SolidComponent client:only="solid" />
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1.5rem;
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 800;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.source === 'app-a') {
|
||||
console.log('>>> message', event)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
7
app-shell/tsconfig.json
Normal file
7
app-shell/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js"
|
||||
}
|
||||
}
|
||||
8042
package-lock.json
generated
Normal file
8042
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
21
package.json
Normal file
21
package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "astro-microfrontends",
|
||||
"version": "1.0.0",
|
||||
"description": "This demo takes advantage of EcmaScript Modules (ESM) and url-imports to do client-side composition of microfrontends. Express is used for serving assets, but ideally one would have a dedicated CDN hosting them.",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "npm run build --workspaces --if-present",
|
||||
"preview": "npm run preview -workspaces --if-present"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"workspaces": [
|
||||
"app-cart",
|
||||
"app-heading",
|
||||
"app-products",
|
||||
"app-shell",
|
||||
"utils"
|
||||
]
|
||||
}
|
||||
0
utils/index.js
Normal file
0
utils/index.js
Normal file
3
utils/orchestrator.mjs
Normal file
3
utils/orchestrator.mjs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// implement messaging system between MF's
|
||||
// - broadcast message
|
||||
// - listen to message & attach callback
|
||||
12
utils/package.json
Normal file
12
utils/package.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "utils",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
Loading…
Reference in a new issue