Developing and packaging Vue components using NPM
Published: 2021-04-27 07:17:09

This article provides some tips from my recent real-world experience with developing and packaging the [Multicorder](https://github.com/bwinkers/multicorder) as an [NPM](https://www.npmjs.com/package/multicorder).

## 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. 

```bash
npm install vue-sfc-rollup
sfc-init
```

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:
```javascript
import { Multicorder } from "multicorder";
```

changes to:

```javascript
import { Multicorder } from '../../../src/lib-components/index.js'
```

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:

```bash
npm install ../multicorder-1.10.0.tgz
```

### 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.

```json
  "files": [
    "dist/*",
    "src/**/*.vue",
    "vuetify_ui/**/*.js",
    "vuetify_ui/**/*.vue",
    "vuetify_ui/**/*.json"
  ],
```