npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

demf-share-form

v3.1.5

Published

The Share Form parcel displays and manages a share form functionalities. Provides the management to share resources with other users in the Digital Mashup Editor (DME).

Readme

Digital Enabler Share Form parcel

The Share Form parcel displays and manages a share form functionalities. Provides the management to share resources with other users in the Digital Mashup Editor (DME).

Built with Vue 3, Vuetify 3, and Vite. Designed for Single-SPA integration.

demf-share-form

This repository documents how to create and use a Single-SPA Parcel within the same microfrontend architecture used in the DME workspace.

The concrete example is demf-share-form: the share modal content is exposed as a parcel and mounted on demand by another microfrontend, instead of being registered as an <application> in the root-config layout.

Table of Contents

Quick Start

Minimal summary for anyone who only needs to use demf-share-form from an existing microfrontend (full details in sections 3 and 4).

1. Import map (locally, in the root-config public/environment/env.js):

localMicrofrontends: {
  "demf-share-form": "http://localhost:9901/mf-app.js"
}

2. Props received from the root-config in the parent caller MF:

props: ['mfConfig', 'realm', 'palette']

3. Dialog host + mount point in the parent caller MF:

<v-dialog v-model="dialogShare" max-width="500px" scrollable>
  <v-card>
    <v-card-text>
      <div ref="parcelMountPoint"></div>
    </v-card-text>
  </v-card>
</v-dialog>

4. Parcel mount / unmount in the parent caller MF:

async mountShareParcel() {
  if (this.parcel) return
  const el = this.$refs.parcelMountPoint
  const [parcelConfig, { mountRootParcel }] = await Promise.all([
    window.importShim('demf-share-form'),
    window.importShim('single-spa'),
  ])
  this.parcel = mountRootParcel(parcelConfig, {
    domElement: el,
    realm: this.realm,
    palette: this.palette,
    root: 'projects', // API segment for the shared resource
    resourceType: 'mashup',
    resourceData: this.mutableItem,
    'demf-share-form-config': this.shareFormConfig,
  })
  await this.parcel.mountPromise
},
unmountShareParcel() {
  if (this.parcel) {
    this.parcel.unmount().catch(console.error)
    this.parcel = null
  }
},

Useful analogy: for a parcel, the object passed to mountRootParcel(parcelConfig, { ...props }) plays the same role that props="..." plays for an <application> in the root-config. The mount mechanism changes, but you are still propagating props to the child microfrontend.

Call unmountShareParcel() both when the dialog closes and in the host component beforeUnmount().

When to Use a Parcel

Use a parcel when:

  • you want to mount reusable UI inside another MF's DOM
  • you want to open an embedded modal, drawer, or widget without adding a dedicated route
  • you want to avoid an always-on MF listening to global events
  • you want the calling microfrontend to directly control opening, closing, and the UI container

Do not use a parcel when:

  • the MF must live as a standalone page or route
  • the MF must always be present in the global layout
  • you only need event-to-event communication without embedded UI

Final Architecture

Caller MF (e.g. demf-mashups-list)
  -> opens a local v-dialog
  -> calls importShim('demf-share-form')
  -> mountRootParcel(parcelConfig, { domElement, ...props })

demf-share-form
  -> bootstrap / mount / unmount single-spa
  -> receives realm, palette, mfConfig, resourceData
  -> renders only the modal content

root-config
  -> exposes import map and remote configurations
  -> does NOT register the parcel in microfrontend-layout.html
  -> must support props.domElement in the custom single-spa-vue shim

Required Prerequisites

For a parcel to work in this codebase, all of these conditions must be met:

  1. the parcel MF must export the bootstrap, mount, and unmount lifecycles
  2. the root-config must resolve the parcel bundle through the import map
  3. the custom single-spa-vue in the root-config must use props.domElement when present
  4. the parcel router must not touch the browser URL
  5. the caller MF must pass realm, palette, and the parcel configuration

1. How to Create the Parcel Microfrontend

Start from the Vue 3 / Vite template already used by your microfrontends.

1.1 package.json

The package name must match the name resolved by the import map:

{
  "name": "demf-share-form"
}

1.2 main.js

The parcel must use single-spa-vue and forward props to the root component.

Minimal example:

import { createApp, h } from 'vue'
import App from './App.vue'
import singleSpaVue from 'single-spa-vue'
import router from './router'
import createVuetifyPlugin from './plugins/vuetify.js'
import i18n from './locales/i18n.js'
import pinia from './store'

let singleSpaProps = {}

const vueLifecycles = singleSpaVue({
  createApp,
  appOptions: {
    render() {
      return h(App, {
        mfConfig: singleSpaProps['demf-share-form-config'] || {},
        palette: singleSpaProps.palette,
        realm: singleSpaProps.realm,
        resourceType: singleSpaProps.resourceType,
        resourceData: singleSpaProps.resourceData,
        root: singleSpaProps.root,
      })
    },
  },
  handleInstance: (app, props) => {
    singleSpaProps = props
    app.use(createVuetifyPlugin(props.palette || {}, i18n))
    app.use(router)
    app.use(i18n)
    app.use(pinia)
  },
})

export const bootstrap = vueLifecycles.bootstrap
export const mount = vueLifecycles.mount
export const unmount = vueLifecycles.unmount

1.3 App.vue

The parcel root component must be embedded content, not a full page.

So:

  • do not use v-app as a local root
  • do not assume its own routes
  • do not read context from window.location
  • receive everything through props

Recommended props:

props: {
  resourceType: String,
  resourceData: Object,
  root: String,
  realm: String,
  mfConfig: Object,
}

1.4 Parcel Router

If the parcel uses Vue Router, it must use memory history.

Correct:

import { createRouter, createMemoryHistory } from 'vue-router'

export default createRouter({
  history: createMemoryHistory(),
  routes: [],
})

Avoid:

createWebHistory(import.meta.env.BASE_URL)

With createWebHistory, the parcel modifies the browser URL and breaks root-config routing.

2. How to Prepare the Root Config

The parcel must not be added to microfrontend-layout.html.

It must instead be resolvable through the import map, and the custom single-spa-vue shim must support mounting on domElement.

2.1 Import map

Add this to the repository that hosts the import map:

{
  "imports": {
    "demf-share-form": "https://cdn.jsdelivr.net/npm/demf-share-form@latest/mf-app.js"
  }
}

For local development, in the root-config public/environment/env.js:

localMicrofrontends: {
  "demf-share-form": "http://localhost:9901/mf-app.js"
}

2.2 Remote config

Add the parcel config file to the tenant config store, for example:

{
  "mf": "demf-share-form",
  "api": "https://your-api/api"
}

Then reference it from the tool main config.json.

2.3 Support for props.domElement in the custom single-spa-vue

Your root-config contains a custom single-spa-vue, so to support parcels the container must be resolved like this:

const container =
  props.domElement ||
  document.getElementById(props.name) ||
  document.querySelector(`#single-spa-application\\:${props.name}`) ||
  document.querySelector(`[data-spa-app="${props.name}"]`)

This change must be applied in both:

  • app/public/single-spa-vue-shim.mjs
  • app/public/systemjs-register.js

If this step is missing, you will get errors like:

Container not found for parcel-1

3. How to Use the Parcel from Another Microfrontend

The calling MF remains the owner of the v-dialog and the mount point.

3.1 Caller MF props

The caller must receive at least these props from the root-config:

  • mfConfig
  • realm
  • palette

Example:

props: ['mfConfig', 'realm', 'palette']

3.2 Dialog host

Create a modal in the caller to host the parcel:

<v-dialog v-model="dialogShare" max-width="500px" scrollable>
  <v-card>
    <v-toolbar flat color="secondary">
      <v-toolbar-title class="text-white">Sharing</v-toolbar-title>
    </v-toolbar>
    <v-card-text>
      <div ref="parcelMountPoint"></div>
    </v-card-text>
  </v-card>
</v-dialog>

3.3 Parcel mount

Full example:

async mountShareParcel() {
  if (this.parcel) return

  const el = this.$refs.parcelMountPoint
  const [parcelConfig, { mountRootParcel }] = await Promise.all([
    window.importShim('demf-share-form'),
    window.importShim('single-spa'),
  ])

  this.parcel = mountRootParcel(parcelConfig, {
    domElement: el,
    realm: this.realm,
    palette: this.palette,
    root: 'projects',
    resourceType: 'mashup',
    resourceData: this.mutableItem,
    'demf-share-form-config': this.shareFormConfig,
  })

  await this.parcel.mountPromise
}

3.4 Parcel unmount

unmountShareParcel() {
  if (this.parcel) {
    this.parcel.unmount().catch(console.error)
    this.parcel = null
  }
}

Call unmount:

  • when you close the dialog
  • in the host component beforeUnmount()

4. Correct Prop Propagation

To behave consistently with the rest of the suite, you must forward these props:

| Prop | Passed by | Purpose | | --- | --- | --- | | realm | caller MF | current tenant | | palette | caller MF | consistent Vuetify theme | | demf-share-form-config | caller MF | parcel remote config | | resourceType | caller MF | logical resource type | | resourceData | caller MF | owner, sharedWith, and already available metadata | | root | caller MF | API segment, e.g. projects |

If you do not pass palette, the parcel uses Vuetify default colors and the modal appears with a different theme.

5. Full Step-by-Step Flow

  1. create the parcel MF from the template
  2. expose bootstrap, mount, and unmount
  3. make the root component generic and props-driven
  4. use createMemoryHistory() in the router
  5. publish or serve mf-app.js
  6. add the parcel to the import map
  7. add the parcel remote config
  8. enable props.domElement in the root-config custom single-spa-vue
  9. add palette to the props received by the caller
  10. open a v-dialog in the caller with <div ref="parcelMountPoint">
  11. execute mountRootParcel(parcelConfig, { domElement, ...props }) in the caller
  12. execute parcel.unmount() on close

6. Local Development

Inside the parcel app/ folder:

npm install
npm run dev

The bundle will be served at:

http://localhost:9901/mf-app.js

Then make sure the root-config env.js points to that URL in localMicrofrontends.

7. Troubleshooting

Error: Container not found for parcel-X

Possible causes:

  • the root-config does not use props.domElement in the custom single-spa-vue
  • the caller invokes mountRootParcel without domElement
  • the mount point ref does not exist, or the dialog is not rendered yet

Checks:

  1. verify props.domElement || ... in the root-config shim
  2. verify that the caller passes domElement: this.$refs.parcelMountPoint
  3. verify that the dialog is open before mounting

Error: the browser URL changes to something like http://localhost:9000/http://localhost:9901/...

Cause:

  • the parcel router uses createWebHistory()

Fix:

  • use createMemoryHistory()

Error: the modal colors do not match the rest of the app

Cause:

  • the caller does not pass palette to the parcel

Fix:

  1. add palette to the caller MF props
  2. pass :palette="palette" to the host component
  3. pass palette: this.palette in mountRootParcel

Error: the parcel opens but does not receive the correct context

Cause:

  • the parcel still reads window.location or assumes hardcoded data such as mashup

Fix:

  • use only resourceType, resourceData, root, realm, and mfConfig

Error: a parcel menu/dropdown/dialog opens but appears behind the host dialog

Cause:

  • each MF (host and parcel) bundles its own copy of Vuetify, so it has an independent z-index stack (globalStack at module level in vuetify/composables/stack.js). If the parcel is mounted inside a host overlay, such as a dialog opened from a menu, the parcel first overlay starts again from the default z-index and can end up below the host dialog.

Fix, depending on the Vuetify component used inside the parcel:

| Component | Direct prop works? | How to set it | | --- | --- | --- | | v-dialog, v-menu, v-tooltip, v-overlay | yes | :z-index="N" | | v-autocomplete, v-select, v-combobox | no (it falls through as an ignored HTML attribute) | :menu-props="{ zIndex: N }" |

Runtime check: inspect document.querySelectorAll('.v-overlay--active') and compare the parcel overlay style attribute (z-index: N) with the host dialog overlay.

Note: if a second overlay from the same bundle opens while the first one is already active, Vuetify still ignores the explicit prop and uses lastZIndex + 10 (useStack) instead. The explicit value only matters for the first overlay opened in the parcel stack.

8. What NOT to Do

  • do not register the parcel in microfrontend-layout.html
  • do not use createWebHistory() in the parcel
  • do not put v-app inside the parcel root if it is mounted inside another MF
  • do not assume the parcel has its own route
  • do not read owner or id from window.location
  • do not forget palette if you want to keep the theme consistent

9. Useful References

  • Root config: DME/dme-gui-root
  • Caller example: DME/demf-mashups-list
  • Parcel example: COMMON/demf-share-form

10. Final Checklist

  • import map updated
  • parcel remote config present
  • custom single-spa-vue compatible with props.domElement
  • parcel router using createMemoryHistory()
  • caller with props: ['mfConfig', 'realm', 'palette']
  • mountRootParcel(..., { domElement, realm, palette, resourceData, root, 'demf-share-form-config': ... })
  • parcel.unmount() on close

If all these points are satisfied, the parcel works in the same MF architecture used in the DME workspace.

2. Prepare the dist folder

cp ../README.md dist/README.md cp package.json dist/package.json rm dist/index.html # Linux/macOS

Remove-Item dist/index.html # Windows PowerShell

3. Publish

npm publish --access=public dist


> **NOTE:** Remember to update the `"version"` field in `package.json` before publishing a new release.

---

#### Code Quality

```bash
npm run lint        # Lint and fix files with ESLint
npm run format      # Format code with Prettier

NOTE: Alternatively to the commands indicated above you can use the Vue UI browser interface.


🌐 Internationalization

The demf-share-form microfrontend supports multiple languages through vue-i18n with automatic Vuetify locale integration.

How It Works

  • Locale files are stored in src/locales/*.json (e.g., en.json, it.json)
  • The script scripts/generate-vuetify-locales.mjs automatically scans these files
  • Matching Vuetify translations are imported and merged
  • The active language is read from localStorage.getItem('lang') (defaults to en)

Supported Languages

The demf-share-form includes translations for the languages defined in src/locales/:

  • English (en)
  • Italian (it)
  • [Add other languages as needed]

Adding a New Language

  1. Create a new file: src/locales/<code>.json (e.g., es.json)
  2. Add your translations following the existing structure
  3. Run npm run dev or npm run build
  4. The generator will automatically include Vuetify translations for that language

🎨 Features

  • Dynamic theming: Automatically adapts to the palette passed from root-config
  • Multi-language support: Full internationalization with vue-i18n
  • Responsive design: Works seamlessly on mobile, tablet, and desktop
  • Material Design: Built with Vuetify 3 components and Material Design Icons
  • Consistent branding: Shows uniform demf-share-form across all Digital Enabler services
  • Platform information: Displays version, copyright, and relevant links

📁 Project Structure

demf-share-form/
├── app/
│   ├── src/
│   │   ├── App.vue                    # Main component
│   │   ├── main.js                    # Entry point
│   │   ├── components/                # demf-share-form components
│   │   ├── mixins/
│   │   │   └── events.js             # Shared setError helper
│   │   ├── locales/
│   │   │   ├── i18n.js               # i18n configuration
│   │   │   ├── en.json               # English translations
│   │   │   ├── it.json               # Italian translations
│   │   │   └── vuetify-generated.js  # Auto-generated Vuetify locales
│   │   ├── plugins/
│   │   │   └── vuetify.js            # Vuetify configuration
│   │   ├── router/
│   │   └── store/
│   ├── scripts/
│   │   └── generate-vuetify-locales.mjs
│   ├── public/
│   ├── dist/                          # Build output
│   ├── package.json
│   ├── vite.config.js
│   └── eslint.config.js
├── docker/
└── README.md

🔍 Troubleshooting

demf-share-form not visible

  • Verify the import map includes demf-share-form
  • Check the layout HTML has the <application> tag with correct name
  • Ensure the bundle is accessible at the configured URL
  • Look for console errors in the browser developer tools

Configuration not working

  • Check that demf-share-form-config prop is passed in the layout
  • Verify the configuration JSON structure matches the expected format
  • Ensure the root-config is loading the remote configuration correctly
  • Check console for warnings about missing configuration

Styling issues

  • Ensure the palette prop is being passed from root-config
  • Verify Material Design Icons fonts are loaded
  • Check that Vuetify theme configuration is correct
  • Clear browser cache and reload

API connection errors

  • Verify the api field in demf-share-form-config.json is correct
  • Check network tab for failed API requests
  • Ensure CORS is properly configured on the backend
  • Verify the API endpoint is accessible from the browser

Development server not starting

  • Check that port 9001 is not already in use
  • Verify Node.js version is compatible (v22+ or v24.8+)
  • Try removing node_modules and running npm install again
  • Ensure all dependencies are correctly installed

📚 Tech Stack

Runtime Dependencies

  • Vue 3 (^3.5.22) - Progressive JavaScript framework
  • Vuetify 3 (^3.10.7) - Material Design component library
  • Single-SPA Vue (^3.0.1) - Single-SPA integration for Vue
  • Vue Router (^4.6.3) - Official router for Vue.js
  • Vue i18n (^9.14.5) - Internationalization plugin
  • Vuex (^4.1.0) - State management
  • Axios (^1.13.0) - HTTP client
  • Material Design Icons (^7.4.47) - Icon library

Development Dependencies

  • Vite (^7.1.12) - Next generation frontend tooling
  • ESLint (^9.38.0) - Code linting
  • Prettier (^3.6.2) - Code formatting
  • Vite Plugin Vue DevTools (^8.0.3) - Vue DevTools integration
  • Concurrently (^9.2.1) - Run multiple commands

For complete dependencies, see package.json.


📖 Related Documentation


📄 License

This project is part of Digital Enabler Ecosystem.

© 2025 Engineering Ingegneria Informatica S.p.A.


🆘 Support

For support, questions, or issues: