Packaging Vue Libraries
Hanoi, August 2019

Hien Dao
Hien Dao Vinh
Senior developer @
- PHP/Nodejs/Golang
- octicons-vue, vue-teible & vue-tabs
var vm = new Vue({}) since v1


Table of Contents
I. Source control patterns
II. Bundling
III. Testing
IV. Example
V. Alternatives
Source Control Patterns
Monorepo / Multi-repo

Image courtesy of toptal.com
The organizations of your repositories
Source Control Patterns
[ ] It takes so much effort for you to maintain dependencies & versioning
[ ] Your packages are sharing the same workflow or environment
[ ] You are having problems when refactoring global features
Monorepo or Multi-repo?
The checklist
Source Control Patterns
[x] It takes so much effort for you to maintain dependencies & versioning
[ ] Your packages are sharing the same workflow or environment
[ ] You are having problems when refactoring global features
Monorepo or Multi-repo?
The checklist
Source Control Patterns
Monorepo
Maintaining dependencies & versioning
Vue Library
A single package
Source Control Patterns
Monorepo
Maintaining dependencies & versioning
Vue
Core
Utilities
3 separated repositories

Source Control Patterns
Monorepo
Maintaining dependencies & versioning
Vue
Core
Utilities
React
Foo
Bar
v0.1.0
v0.1.0
v0.1.0
v0.1.0
v0.1.0
v0.1.0
vx.y.z
vx.y.z
vx.y.z
vx.y.z
vx.y.z
vx.y.z
6 separated packages

Source Control Patterns
[x] It takes so much effort for you to maintain dependencies & versioning
[x] Your packages are sharing the same workflow & environment
[ ] You are having problems when refactoring global features
Monorepo or Multi-repo?
The checklist
Source Control Patterns
Monorepo
Sharing workflow & environment
Processes
- Make changes
- Lint & test
- Build & test again
- Release
Environment
- .editorconfig
- .eslintrc
- .babelrc
- etc
Source Control Patterns
Monorepo
Sharing workflow & environment
Source Control Patterns
[x] It takes so much effort for you to maintain dependencies & versioning
[x] Your packages are sharing the same workflow or environment
[x] You are having problems when refactoring global features
Monorepo or Multi-repo?
The checklist
Source Control Patterns
Monorepo
Coordinate cross-package changes with atomic commits
Vue
Core
Utilities
Source Control Patterns
Monorepo
Coordinate cross-package changes with atomic commits
Introducing changes across multiple packages could be a pain in the @ss
- we want to group distinct changes into a single commit or a PR
- we want to publish the packages in a correct order
- we want to test the packages together
- npm linking is not helping much :(
Source Control Patterns
[x] It takes so much effort for you to maintain dependencies & versioning
[x] Your packages are sharing the same workflow or environment
[x] You are having problems when refactoring global features
Monorepo or Multi-repo?
The checklist

Source Control Patterns

Source Control Patterns
Leaf devDependencies are supposed to be in the root
Lerna
/packages
foo bar
foo qux
bar qux
foo bar qux
{
"devDependencies": {
"@babel/core": "^7.5.5",
"@commitlint/cli": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"@commitlint/config-lerna-scopes": "^8.1.0",
"@hiendv/bem-sass": "^0.1.0",
"@vue/cli-plugin-babel": "^3.10.0",
"@vue/cli-plugin-eslint": "^3.10.0",
"@vue/cli-service": "^3.10.0",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.8.0",
"core-js": "^2.6.9",
"eslint": "^6.1.0",
"eslint-plugin-vue": "^5.2.3",
"flatted": "^2.0.1",
"husky": "^3.0.3",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.8.0",
"jest-serializer-vue": "^2.0.2",
"lerna": "^3.16.4",
"octicons-vue": "^0.18.5",
"rimraf": "^2.6.3",
"rollup": "^1.19.4",
"rollup-plugin-commonjs": "^10.0.2",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-postcss": "^2.0.3",
"rollup-plugin-vue": "^5.0.1",
"sass-loader": "^7.2.0",
"vue": "^2.6.10",
"vue-jest": "^3.0.4",
"vue-template-compiler": "^2.6.10"
}
}
Source Control Patterns
Hoist your dependencies to save time & space
Lerna
- Common dependencies
- Common binaries
- Dependencies with different versions
- Symlinked binaries
./node_modules
./packages/*/node_modules
Source Control Patterns
Private git-hosted packages could be a problem
Lerna
Lerna does support git hosted dependencies with committish versions
(e.g., #v1.0.0 or #semver:^1.0.0)
// packages/pkg-1/package.json
{
name: "pkg-1",
version: "1.0.0",
dependencies: {
"pkg-2": "github:example-user/pkg-2#v1.0.0"
}
}
// packages/pkg-2/package.json
{
name: "pkg-2",
version: "1.0.0"
}
Source Control Patterns
Private git-hosted packages could be a problem
Lerna
By using splitsh/lite, you could splitting each packages to their own [READONLY] repository. Read more about the implementation here.
#!/usr/bin/env bash
mirror () {
echo "Mirroring $1"
if ! git config remote.$1.url > /dev/null; then
echo "Adding origin $1"
git remote add ${1} git@github.com:hiendv/${1}.git
fi
sha=$(./splitsh-lite --prefix packages/$1 --quiet)
echo "Pushing: $1 $sha:refs/heads/master"
git push -u $1 $sha:refs/heads/master
}
for d in packages/*; do
if [ -d ${d} ]; then
mirror ${d##*/}
fi
done
Source Control Patterns
Private git-hosted packages could be a problem
Lerna
But Lerna does not support self-hosted (non-public) instances of git because of npm/hosted-git-info limitation
E.g. git@github.yourcompany.com:acme/foobar.git
Just run your own registry, forget about git-hosted packages and work happily ever after.
Recommendation: Verdaccio
Bundling
Choose your bundler



@vue/cli-service
(not official logo)
Bundling
@vue/cli-service
@vue/cli-service is "react-scripts" for Vue, it's a wrapper for webpack.
It offers all kind of development magics with powerful hooks so you can just forget the boring work with webpack.
It's great for applications.
Instant prototyping & easy development
Bundling
@vue/cli-service
The ESM problem
Webpack does not support ESM output, which means your awesome library cannot be distributed in ESM. See @chrisvfritz/1060282363031289865.
Does it matter? Yes, because of tree-shaking
- Smaller bundle
- Less bloat, messy & dead code
- Less unused dependencies

Bundling
Rule of thumb
- If you are building a library, go with rollup.
- If you are building an application, go with @vue/cli-service.
- If you are building a library & example without Storybook, use both.
Bundling
Wait, Parcel?
- Aiming to be a webpack opponent.
- It's too much magic with the zero-configuration motto but in order to do what we really want, we have to get our hands dirty anyway.
- Not supported widely.
- No tree-shaking.
Bundling
Use rollup-plugin-vue. Modify preprocessOptions for convenience when playing with pre-processors.
Rollup
import importer from 'node-sass-tilde-importer'
vue({
style: {
preprocessOptions: {
scss: {
importer,
includePaths: [reslv('src')],
data: `@import "assets/variables.scss";`
}
}
}
})
You might not need babel. buble is a lite version of babel: faster transpilation, smaller output & less configuration but it's limited to ES6 only.
If you are using typescript, you may have problems because tools don't play nice
- Use rollup-plugin-typescript2
- TSLint must exclude .vue files (palantir/tslint#2099)
- ESLint should include .vue files with the help from @typescript-eslint/parser & @vue/typescript rules
Testing
Jest & ESLint
Jest is a no-brainer for Vue
- vue-jest: Transformer
- jest-serializer-vue: Snapshot serializer
ESLint is also a de facto linter
- eslint-plugin-vue: Official plugin
Testing
Jest
Check out Testing Vue Apps - Edd Yerburgh - VueConf US 2018 if you are new with testing Vue components.
You can test the output when needed (e.g. tree-shaking).
Storybook is a helpful toy with states & visual explanation & testing.
You can test components naturally with template string and createLocalVue
ModuleNameMapper may come in handy when working with stylesheets or symlinked packages exporting module only.
Testing
Jest
You can easily do this
<!-- template -->
<Parent>
<slot/>
</Parent>
<!-- expectation -->
<Parent>
<Children/>
</Parent>
const wrapper = mount(Parent, {
slots: {
default: [Children] // it works
}
})
Slot mounting with template
Testing
Jest
But there are scenarios where you want more from the slot component
<!-- template -->
<Parent>
<slot/>
</Parent>
<!-- expectation -->
<Parent>
<Child foo="bar"><div>Lorem ipsum</div></Child>
<Child foo="qux"><div>Dolor sit amet</div></Child>
</Parent>
const wrapper = mount(Parent, {
slots: {
// ehhh, where to pass attrs
default: [Children]
}
})
//
const wrapper = mount(Parent, {
slots: {
default: {
// this wont work, too
render (h) {
return h(Child, {
props: { foo: 'bar' }
})
}
}
}
})
Slot mounting with template
Testing
Jest
Solution ?
<!-- template -->
<Parent>
<slot/>
</Parent>
<!-- expectation -->
<Parent>
<Child foo="bar"><div>Lorem ipsum</div></Child>
<Child foo="qux"><div>Dolor sit amet</div></Child>
</Parent>
const localVue = createLocalVue()
localVue.component('child', Child)
const wrapper = mount(Parent, {
slots: {
default: `
<child foo="bar"><div>Lorem ipsum</div></child>
<child foo="qux"><div>Dolor sit amet</div></child>
`
},
localVue
})
Slot mounting with template
Testing
Jest
ModuleNameMapper
{
"jest": {
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "<rootDir>/__mocks__/styleMock.js"
}
}
}
Mocking static assets imports & CSS modules
{
"jest": {
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
}
}
// CSS Modules are now a map[key] => value where value === key
Example
vue-tabs
tabs
react-tabs
vue-tabs-example
react-tabs-example
Tabs
Example
Tabs
Example
Tabs
- Monorepo with Lerna
- Core component for utilities & themes
- Vue & React components with Rollup
- Examples with @vue/cli-serivice & react-scripts
- Linting with ESLint
- Testing & coverage with Jest
- Demonstration with GitHub Pages
- Travis CI
Alternatives
Web Component using @vue/web-component-wrapper (vue-cli-service)
- Leverages Custom Elements, Shadow DOM, ES Modules and HTML Templates
- Requires ES2015 classes. IE11 and below not supported.
Bit using bit.envs/bundlers/vue
- Manages & publish components
- Compiler for Vue is basically webpack under the hood
Sharing & reusing your components
Q & A
Any questions, guys?
You can reach me anytime at <hien.dv.neo@gmail.com>
If you have any questions regarding the talk, please create an issue at https://github.com/hiendv/vue-meetup-vietnam-2019-talk
Thank You!
Let's start building your new Vue.js library
Packaging Vue Libraries
By hiendv
Packaging Vue Libraries
Gotchas & traps when packaging vue libraries
- 1,418