Skip to content
Open
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
1 change: 1 addition & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ prod,react-dom,MIT,Copyright (c) Facebook, Inc. and its affiliates.
dev,typedoc,Apache-2.0,TypeStrong
dev,@eslint/js,MIT,Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
dev,@jsdevtools/coverage-istanbul-loader,MIT,Copyright (c) 2015 James Messinger
dev,@module-federation/enhanced,MIT, Copyright (c) 2020 ScriptedAlchemy LLC (Zack Jackson) Zhou Shaw (zhouxiao)
dev,@playwright/test,Apache-2.0,Copyright Microsoft Corporation
dev,@swc/core,Apache-2.0,Copyright (c) SWC Contributors
dev,@types/chrome,MIT,Copyright Microsoft Corporation
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default tseslint.config(
'test/**/dist',
'test/apps/react-heavy-spa',
'test/apps/react-shopist-like',
'test/apps/microfrontend',
'sandbox',
'coverage',
'rum-events-format',
Expand Down
1 change: 1 addition & 0 deletions scripts/build/build-test-apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ runMain(async () => {
buildApp('test/apps/react-router-v6-app')
buildApp('test/apps/react-heavy-spa')
buildApp('test/apps/react-shopist-like')
buildApp('test/apps/microfrontend')
await buildReactRouterv7App()
await buildExtensions()

Expand Down
3 changes: 3 additions & 0 deletions test/apps/microfrontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
node_modules
.yarn/*
3 changes: 3 additions & 0 deletions test/apps/microfrontend/app1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createApp } from './common'

createApp('app1', 'App 1 (mfe-app1-service v1.0.0)', 'blue')
3 changes: 3 additions & 0 deletions test/apps/microfrontend/app2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createApp } from './common'

createApp('app2', 'App 2 (mfe-app2-service v0.2.0)', 'green')
2 changes: 2 additions & 0 deletions test/apps/microfrontend/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @ts-ignore The dynamic import keeps shell.ts out of the entry chunk,
void import('./shell')
67 changes: 67 additions & 0 deletions test/apps/microfrontend/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
function createContainer(id: string, title: string, borderColor: string) {
const container = document.createElement('div')
container.id = id
container.style.flex = '1'
container.style.border = `2px solid ${borderColor}`
container.style.padding = '10px'

const appTitle = document.createElement('h2')
appTitle.textContent = title
container.appendChild(appTitle)
document.body.appendChild(container)
return container
}

function createButton(container: HTMLElement, eventType: string, clickHandler: () => void) {
const button = document.createElement('button')
button.id = `${container.id}-${eventType}`
button.textContent = `${container.id}-${eventType}`
button.onclick = clickHandler
container.appendChild(button)
}

export function createApp(id: string, title: string, borderColor: string) {
const container = createContainer(id, title, borderColor)

createButton(container, 'fetch', () => {
void fetch('/ok')
})

createButton(container, 'xhr', () => {
const xhr = new XMLHttpRequest()
xhr.open('GET', '/ok')
xhr.send()
})

createButton(container, 'error', () => {
window.DD_RUM.addError(new Error(`${id}-error`))
})

createButton(container, 'console-error', () => {
console.error(`${id}-console-error`)
})

createButton(container, 'runtime-error', () => {
throw new Error(`${id}-runtime-error`)
})

createButton(container, 'loaf', () => {
const end = performance.now() + 55
while (performance.now() < end) {
// block the handler for ~55ms to trigger a long task
}
})

createButton(container, 'custom-action', () => {
window.DD_RUM.addAction(`${id}-action`)
})

createButton(container, 'vital', () => {
const ref = window.DD_RUM.startDurationVital(`${id}-vital`)
window.DD_RUM.stopDurationVital(ref)
})

createButton(container, 'view', () => {
window.DD_RUM.startView({ name: `${id}-view` })
})
}
15 changes: 15 additions & 0 deletions test/apps/microfrontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "microfrontend-test-app",
"private": true,
"scripts": {
"build": "webpack --config webpack.app1.js && webpack --config webpack.app2.js && webpack --config webpack.shell.js"
},
"devDependencies": {
"@datadog/webpack-plugin": "^3.1.0",
"@module-federation/enhanced": "^2.0.1",
"ts-loader": "9.5.4",
"typescript": "5.9.3",
"webpack": "5.105.2",
"webpack-cli": "^6.0.1"
}
}
6 changes: 6 additions & 0 deletions test/apps/microfrontend/shell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
async function initShell() {
// @ts-ignore Module Federation remote entries
await Promise.all([import('app1/app1'), import('app2/app2')])
}

void initShell()
10 changes: 10 additions & 0 deletions test/apps/microfrontend/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "esnext",
"lib": ["ES2018", "DOM"],
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true
}
}
12 changes: 12 additions & 0 deletions test/apps/microfrontend/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// declare module 'app1/app1'
// declare module 'app2/app2'

interface Window {
DD_RUM: {
addError: (error: Error) => void
addAction: (name: string, context?: any) => void
startDurationVital: (name: string) => any
stopDurationVital: (ref: any) => void
startView: (options: { name: string }) => void
}
}
38 changes: 38 additions & 0 deletions test/apps/microfrontend/webpack.app1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const path = require('node:path')
const { datadogWebpackPlugin } = require('@datadog/webpack-plugin')
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack')

module.exports = {
mode: 'development',
devtool: 'source-map',
module: {
rules: [{ test: /\.ts$/, use: 'ts-loader' }],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app1.js',
chunkFilename: 'chunks/[name]-[contenthash]-app1.js',
publicPath: 'auto',
},
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'app1Entry.js',
exposes: {
'./app1': './app1.ts',
},
}),
datadogWebpackPlugin({
rum: {
enable: true,
sourceCodeContext: {
service: 'mfe-app1-service',
version: '1.0.0',
},
},
}),
],
}
38 changes: 38 additions & 0 deletions test/apps/microfrontend/webpack.app2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const path = require('node:path')
const { datadogWebpackPlugin } = require('@datadog/webpack-plugin')
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack')

module.exports = {
mode: 'development',
devtool: 'source-map',
module: {
rules: [{ test: /\.ts$/, use: 'ts-loader' }],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app2.js',
chunkFilename: 'chunks/[name]-[contenthash]-app2.js',
publicPath: 'auto',
},
plugins: [
new ModuleFederationPlugin({
name: 'app2',
filename: 'app2Entry.js',
exposes: {
'./app2': './app2.ts',
},
}),
datadogWebpackPlugin({
rum: {
enable: true,
sourceCodeContext: {
service: 'mfe-app2-service',
version: '0.2.0',
},
},
}),
],
}
39 changes: 39 additions & 0 deletions test/apps/microfrontend/webpack.shell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const path = require('node:path')
const { datadogWebpackPlugin } = require('@datadog/webpack-plugin')
const { ModuleFederationPlugin } = require('@module-federation/enhanced/webpack')

module.exports = {
mode: 'development',
entry: './bootstrap.ts',
devtool: 'source-map',
module: {
rules: [{ test: /\.ts$/, use: 'ts-loader' }],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'shell.js',
chunkFilename: 'chunks/[name]-[contenthash]-shell.js',
publicPath: 'auto',
},
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
app1: 'app1@/microfrontend/app1Entry.js',
app2: 'app2@/microfrontend/app2Entry.js',
},
}),
datadogWebpackPlugin({
rum: {
enable: true,
sourceCodeContext: {
service: 'mf-shell-service',
version: '2.0.0',
},
},
}),
],
}
Loading