DEV Community

Brian Winkers
Brian Winkers

Posted on • Edited on

Developing and packaging Vue components using NPM

This article provides some tips from my recent real-world experience with developing and packaging the Multicorder as an NPM.

The Goal

My goal was to publish a fully styled UI component. I accomplished that but learned a lot along the way. My goals changed somewhat as the weekend progressed.

Why publish NPM's?

I'm a firm believer in separating logic out into packages, no matter what the language. They don't necessarily need to be published on public repositories.

The Problems

Best Practices

I came to understand that including relatively heavy dependencies like Vuetify as the sole artifact in an NPM wasn't a best practice.

I ended up with all the logic in a Vue 2 file with no dependencies. Furthermore, I also publish a Vuetify component that uses the base module and wraps it in some straight forward interface logic.

This gives me the best of all worlds. I have a fully functional UI component that will fit right in with any Vue2/Vuetify app I create. If I run into a situation where that UI component won't work I can easily call the same functionality from other UI components, I could even build a UI in MDB Bootstrap.

Testing the Code Locally

I'm very adept at testing software and have written many NPM using npm link to test the package locally. So I figured this would be the easy part, I was wrong. I guess the vue-loader has issues with npm link.

I was able to find a solution using npm pack. It's a bit cumbersome, but I can add automation later. It provides completely accurate testing of how the npm will behave and that is the key.

That solution doesn't support hot reloading so yet another paradigm was needed for iterative development. For that we load the Vue component directly from within a Vuetify app. The vuetify_ui code is used for that.

The Solution

  • Use vue-sfc-rollup
  • Load Vue components directly for iterative development
  • Run npm pack as part of predeployment testing
  • Include Vue source files in NPM

Using vue-sfc-rollup

This saves a lot of time, and it is always good to let someone else do the undifferentiated heavy lifting. I could come up with something better but it would use up effort that would be better applied elsewhere. No matter how much "better" I made it the build process would never be something that moved the dial on the business end of things.

In short, you just install the vue-sfc-rollup NPM and run the command sfc-init command.

npm install vue-sfc-rollup
sfc-init
Enter fullscreen mode Exit fullscreen mode

It will lead you though choosing to export a single module or a library as well as whether to use Vue 2 or 3. I'm sticking with Vue 2 until Vuetify support for Vue 3 is fully baked.

Loading Vue components from source

In this instance I needed to include the src/lib-components/* directly.

This:

import { Multicorder } from "multicorder";
Enter fullscreen mode Exit fullscreen mode

changes to:

import { Multicorder } from '../../../src/lib-components/index.js'
Enter fullscreen mode Exit fullscreen mode

If I need to deal with that scenario for a while I'll add something to the build step to make sure that link is always correct when the NPM is published.

The vue-sfc-rollup package provides a dev server, but it's not useful for creating UI's that depend on libraries like Vuetify.

Test NPM with locally

Use npm pack to create a .tgz file that can be installed as an npm. The file name depends on the version number in the package.json.

That can be installed with npm install <path to *.tgz> syntax.

In the Multicorder Vuetify UI that means running something liek this for the vuetify_ui directory:

npm install ../multicorder-1.10.0.tgz
Enter fullscreen mode Exit fullscreen mode

Including Vue source files in NPM

I export the Vue source files for the main component as well as the Vuetify UI. This provides context within the NPM as well as enabling direct access of those files.

  "files": [
    "dist/*",
    "src/**/*.vue",
    "vuetify_ui/**/*.js",
    "vuetify_ui/**/*.vue",
    "vuetify_ui/**/*.json"
  ],
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
vovchisko profile image
Vladimir Ishenko • Edited

I was dealing with npm link problem for a few days, and ended up with the same pack approach. I'd save a lot of time reading your article first :)

Here github.com/vovchisko/fu-kit

I made a script to update docs.
I take version from the package.json, and run a few commands on postbuild:

const { version } = require('../package.json')
const path = require('path')
const { exec } = require('child_process')

const PKG_FILE = `fu-kit-${ version }.tgz`
const DOCS_SRC = `src-docs`

function run (cmd, ...args) {
  return new Promise((resolve, reject) => {
    const child = exec(cmd, ...args)
    child.stdout.pipe(process.stdout)
    child.stderr.pipe(process.stderr)
    child.stdin.pipe(process.stdin)
    child.on('close', (code) => {
      code !== 0 ? reject() : resolve(code)
    })
  })
}

async function main () {

  console.log(`RE-LINK: PACK AS FOR NPM`)
  await run(`npm pack`)

  console.log(`RE-LINK: LINK PACKAGE - [ npm i ../${ PKG_FILE } ]`)
  await run(`npm i ../${ PKG_FILE }`, { cwd: path.resolve(DOCS_SRC) })

  console.log(`RE-LINK: INSTALL EVERYTHING ELSE`)
  await run(`npm i`, { cwd: path.resolve(DOCS_SRC) })

  console.log(`RE-LINK: BUILDING DOCS`)
  await run(`npm run build`, { cwd: path.resolve(DOCS_SRC) })
}


main()
Enter fullscreen mode Exit fullscreen mode