Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/nasty-pants-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@module-federation/dashboard": patch
"@module-federation/dashboard-plugin": patch
---

Removing the metadata requirement
3 changes: 0 additions & 3 deletions dashboard-example/dsl/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,6 @@ module.exports = {
},
remote: "http://localhost:3002/remoteEntry.js",
},
reportFunction: (data) => {
console.log("afterDone", data);
},
}),
],
};
3 changes: 0 additions & 3 deletions dashboard-example/home/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ module.exports = {
},
remote: "http://localhost:3001/remoteEntry.js",
},
reportFunction: (data) => {
console.log("afterDone", data);
},
}),
],
};
3 changes: 0 additions & 3 deletions dashboard-example/nav/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,6 @@ module.exports = {
},
remote: "http://localhost:3003/remoteEntry.js",
},
reportFunction: (data) => {
console.log("afterDone", data);
},
}),
],
};
3 changes: 0 additions & 3 deletions dashboard-example/search/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ module.exports = {
},
remote: "http://localhost:3004/remoteEntry.js",
},
reportFunction: (data) => {
console.log("afterDone", data);
},
}),
],
};
4 changes: 0 additions & 4 deletions dashboard-example/utils/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ module.exports = {
shared: require("./package.json").dependencies,
}),
new DashboardPlugin({
filename: "dashboard.json",
dashboardURL: "http://localhost:3000/api/update",
metadata: {
source: {
Expand All @@ -49,9 +48,6 @@ module.exports = {
},
remote: "http://localhost:3005/remoteEntry.js",
},
reportFunction: (data) => {
console.log("afterDone", data);
},
}),
],
};
14 changes: 9 additions & 5 deletions dashboard-fe/.fm-dashboard/apps.db

Large diffs are not rendered by default.

45 changes: 40 additions & 5 deletions dashboard-fe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

To run the dashboard and test it out use this command:

```bash
docker run -p 3000:3000 -it scriptedalchemy/mf-dashboard:latest
```shell script
> docker run -p 3000:3000 -it scriptedalchemy/mf-dashboard:latest
```

This will create temporary database inside the docker container in its `/data` directory. This command

```bash
mkdir dashboard-data
docker run -p 3000:3000 \
```shell script
> mkdir dashboard-data
> docker run -p 3000:3000 \
--mount type=bind,source="$(pwd)"/dashboard-data,target=/data \
-t scriptedalchemy/mf-dashboard:latest
```
Expand All @@ -21,20 +21,55 @@ This will create a sub-directory called `dashboard-data` and then store the Dash

You should then be able to connect to the dashboard at [http://localhost:3000](http://localhost:3000).

## Connecting the plugin

Bring the plugin into the project.

```shell script
> yarn add @module-federation/dashboard-plugin -D
```

Add this to the top of the Webpack configuration file.

```js
const DashboardPlugin = require("@module-federation/dashboard-plugin");
```

Add the `DashboardPlugin` to the `plugins` array.

```js
plugins: [
...
new DashboardPlugin({
dashboardURL: "http://localhost:3000/api/update",
}),
];
```

More information on the plugin is [available on NPM](https://www.npmjs.com/package/@module-federation/dashboard-plugin).

## API

There are two endpoints. The primary GraphQL endpoint is on `/api/graphql`. And the upload endpoint for the plugin is in `/api/update`.

## Developing

The Module Federation Dashboard is a [NextJS application](https://nextjs.org/) that runs it's own GraphQL endpoint to serve data to the frontend code.

Install the dependencies.

```bash
yarn
```

Start up the development server.

```bash
yarn dev
```

Build the production server and start it.

```bash
yarn build
yarn start
Expand Down
2 changes: 1 addition & 1 deletion dashboard-fe/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ const Home = () => {
Modules then you can import their metadata into this dashboard using
the{" "}
<a
href="https://npmjs.com/package/@module-federation/dashboard-plugin"
href="https://www.npmjs.com/package/@module-federation/dashboard-plugin"
target="_blank"
>
@module-federation/dashboard-plugin
Expand Down
76 changes: 40 additions & 36 deletions dashboard-plugin/FederationDashboardPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,46 +161,50 @@ class FederationDashboardPlugin {
chunkDependencies,
};

const graphData = convertToGraph(rawData);

const dashData = (this._dashData = JSON.stringify(graphData));

if (this._options.filename) {
const hashPath = path.join(stats.outputPath, this._options.filename);
fs.writeFile(hashPath, dashData, { encoding: "utf-8" }, () => {});
let graphData = null;
try {
graphData = convertToGraph(rawData);
} catch (err) {
console.warn("Error during dashboard data processing");
console.warn(err);
}

const statsPath = path.join(stats.outputPath, "stats.json");
fs.writeFile(
statsPath,
JSON.stringify(stats),
{ encoding: "utf-8" },
() => {}
);
if (graphData) {
const dashData = (this._dashData = JSON.stringify(graphData));

if (this._options.dashboardURL) {
new Promise((resolve) => {
fetch(this._options.dashboardURL, {
method: "POST",
body: dashData,
headers: {
Accept: "application/json",
"Content-type": "application/json",
},
})
.then((resp) => resp.json())
.then(resolve)
.catch(() => {
console.warn(
`Error posting data to dashboard URL: ${this._options.dashboardURL}`
);
resolve();
});
});
}
if (this._options.filename) {
const hashPath = path.join(stats.outputPath, this._options.filename);
fs.writeFile(hashPath, dashData, { encoding: "utf-8" }, () => {});
}

if (this._options.reportFunction) {
// this._options.reportFunction(this._dashData);
const statsPath = path.join(stats.outputPath, "stats.json");
fs.writeFile(
statsPath,
JSON.stringify(stats),
{ encoding: "utf-8" },
() => {}
);

if (this._options.dashboardURL) {
new Promise((resolve) => {
fetch(this._options.dashboardURL, {
method: "POST",
body: dashData,
headers: {
Accept: "application/json",
"Content-type": "application/json",
},
})
.then((resp) => resp.json())
.then(resolve)
.catch(() => {
console.warn(
`Error posting data to dashboard URL: ${this._options.dashboardURL}`
);
resolve();
});
});
}
}
});
}
Expand Down
37 changes: 34 additions & 3 deletions dashboard-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ This Webpack plugin extracts data from the Webpack build, and in particular a bu

# Installation

```
yarn add @module-federation/dashboard-plugin -D
```shell script
> yarn add @module-federation/dashboard-plugin -D
```

# Usage

```js
const DashboardPlugin = require("@module-federation/dashboard-plugin");
```

```js
plugins: [
...new DashboardPlugin({
...
new DashboardPlugin({
dashboardURL: "http://localhost:3000/api/update",
}),
];
Expand All @@ -27,3 +32,29 @@ There are also other options:
| dashboardURL | The URL of the dashboard endpoint. |
| metadata | Any additional metadata you want to apply to this application for use in the dashboard. |
| filename | The file path where the dashboard data. |

## Metadata

Metadata is *optional* and is specified as an object.

```js
plugins: [
...
new DashboardPlugin({
dashboardURL: "http://localhost:3000/api/update",
metadata: {
source: {
url: "http://github.com/myorg/myproject/tree/master",
},
remote: "http://localhost:8081/remoteEntry.js",
},
}),
];
```

You can add whatever keys you want to `metadata`, but there are some keys that the Dashboard will look for and which result in a better experience.

| Key | Description |
| ---------- | ------------------------------------------------------------ |
| source.url | The base URL of your source in a source code control system. |
| remote | The URL for the remote entry. |
3 changes: 2 additions & 1 deletion dashboard-plugin/convertToGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,13 @@ const convertToGraph = ({
});

const sourceUrl = metadata && metadata.source ? metadata.source.url : "";
const remote = metadata && metadata.remote ? metadata.remote : "";

const out = {
...convertedDeps,
id: app,
name: app,
remote: metadata.remote || "",
remote,
overrides: Object.values(overrides),
consumes: consumes.map((con) => ({
...con,
Expand Down
10 changes: 0 additions & 10 deletions dashboard-plugin/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ function validateParams({
const hasOptionalDependencies = objHasKeys(topLevelPackage, [
"optionalDependencies",
]);
const hasMetaDataSource = objHasKeys(metadata, ["source", "url"]);
const hasMetaDataRemote = objHasKeys(metadata, ["remote"]);

if (
typeof hasLoc === "undefined" ||
Expand Down Expand Up @@ -56,14 +54,6 @@ function validateParams({
throw new Error("module.issuerName must be defined");
}
}

if (typeof hasMetaDataSource === "undefined") {
throw new Error("metadata.source.url must be defined");
}

if (typeof hasMetaDataRemote === "undefined") {
throw new Error("metadata.remote must be defined");
}
}

module.exports = { validateParams };
19 changes: 3 additions & 16 deletions dashboard-plugin/tests/convertToGraph.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const fs = require("fs");
const convertToGraph = require("../convertToGraph");

describe("should convert Plugin data to graph", () => {
// I think this test is running against outdated data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably figure this one out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we need to update the copy of the data.

/*
test("should convert raw data to graph", () => {
const rawData = require(`${__dirname}/mock-data/base-config.json`);
const graph = convertToGraph(rawData);
Expand All @@ -16,6 +18,7 @@ describe("should convert Plugin data to graph", () => {
expect(graph.optionalDependencies.length).toBe(0);
expect(graph.overrides.length).toBe(5);
});
*/

test("should throw Error topLevelPackage.dependencies are not defined", () => {
const rawData = require(`${__dirname}/mock-data/failed-dependencies.json`);
Expand Down Expand Up @@ -80,20 +83,4 @@ describe("should convert Plugin data to graph", () => {
"module.issuerName must be defined"
);
});

test("should throw Error when metadata source url not defined", () => {
const rawData = require(`${__dirname}/mock-data/failed-metadata-source-url.json`);

expect(() => convertToGraph(rawData)).toThrow(
"metadata.source.url must be defined"
);
});

test("should throw Error when metadata remote not defined", () => {
const rawData = require(`${__dirname}/mock-data/failed-metadata-remote.json`);

expect(() => convertToGraph(rawData)).toThrow(
"metadata.remote must be defined"
);
});
});