Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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