尝试使用 rollup 和 vueJs 3 构建 vue 组件库

Posted

技术标签:

【中文标题】尝试使用 rollup 和 vueJs 3 构建 vue 组件库【英文标题】:Trying to build vue component library using rollup and vueJs 3 【发布时间】:2021-02-02 21:42:51 【问题描述】:

我正在尝试使用 rollup 和 vuejs 创建一个 vue 组件库。它与 vue2 一起工作,但无法用 vue3 解析 css。我已经升级了 package.json 中的依赖

package.json


  "name": "vue2tslibrary",
  "version": "0.1.59",
  "scripts": 
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "build:js": "rimraf dist && rollup -c && rollup -c --environment MINIFY"
  ,
  "sideEffects": [
    "*.css",
    "*.scss"
  ],
  "files": [
    "dist",
    "src"
  ],
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "unpkg": "dist/vueslib.min.js",
  "dependencies": 
    "@mathieustan/vue-datepicker": "^0.2.8",
    "core-js": "^3.6.5",
    "date-fns": "^2.16.1",
    "vue": "^3.0.1",
    "vue-router": "^4.0.0-beta.13",
    "vue-slider-component": "^4.0.0-beta.2",
    "vue-template-compiler": "^2.6.12",
    "vuex": "^4.0.0-beta.4"
  ,
  "devDependencies": 
    "@babel/plugin-proposal-optional-chaining": "^7.12.1",
    "@rollup/plugin-alias": "2.2.0",
    "@rollup/plugin-babel": "^5.2.1",
    "@rollup/plugin-commonjs": "^15.1.0",
    "@rollup/plugin-image": "^2.0.5",
    "@rollup/plugin-node-resolve": "^9.0.0",
    "@rollup/plugin-url": "^5.0.1",
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.1",
    "@vue/eslint-config-standard": "^5.1.2",
    "@vue/eslint-config-typescript": "^5.0.2",
    "autoprefixer": "^9.8.6",
    "cssnano": "^4.1.10",
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "node-sass": "^4.14.1",
    "postcss": "^8.1.1",
    "postcss-calc": "^7.0.3",
    "postcss-color-function": "^4.1.0",
    "postcss-cssnext": "^3.1.0",
    "postcss-discard-comments": "^4.0.2",
    "postcss-discard-empty": "^4.0.1",
    "postcss-discard-unused": "^4.0.1",
    "postcss-each": "^0.10.0",
    "postcss-extend-rule": "^3.0.0",
    "postcss-import": "^12.0.1",
    "postcss-mixins": "^6.2.3",
    "postcss-nested": "^4.2.1",
    "postcss-rem": "^1.1.5",
    "postcss-simple-vars": "^5.0.2",
    "postcss-sort-media-queries": "^1.7.26",
    "postcss-url": "^8.0.0",
    "rollup": "1.17.0",
    "rollup-plugin-analyzer": "^3.3.0",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-css-only": "^2.1.0",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-postcss": "^3.1.8",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript": "^1.0.1",
    "rollup-plugin-typescript2": "^0.28.0",
    "rollup-plugin-uglify": "^6.0.4",
    "rollup-plugin-vue": "^6.0.0-beta.8",
    "sass-loader": "^10.0.3",
    "style-resources-loader": "1.3.3",
    "typescript": "~3.9.3"
  ,
  "eslintConfig": 
    "root": true,
    "env": 
      "node": true
    ,
    "extends": [
      "plugin:vue/essential",
      "@vue/standard",
      "@vue/typescript/recommended"
    ],
    "parserOptions": 
      "ecmaVersion": 2020
    ,
    "rules": 
  ,
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]

rollup.config.js

import vue from 'rollup-plugin-vue'
import node from '@rollup/plugin-node-resolve'
import cjs from '@rollup/plugin-commonjs'
import babel from '@rollup/plugin-babel'
import postcss from 'rollup-plugin-postcss'
import  terser  from 'rollup-plugin-terser'
import css from 'rollup-plugin-css-only'
import postcssImport from 'postcss-import'
import autoprefixer from 'autoprefixer'
import simplevars from 'postcss-simple-vars'
import nested from 'postcss-nested'
import postcssEach from 'postcss-each'
import postcssMixin from 'postcss-mixins'
import postcssColor from 'postcss-color-function'
import postcssCalc from 'postcss-calc'
import postcssextend from 'postcss-extend-rule'
import postcssDiscardComment from 'postcss-discard-comments'
import postcssDiscardEmpty from 'postcss-discard-empty'
import postcssUrl from 'postcss-url'
import postcssRem from 'postcss-rem'
import sortMedia from 'postcss-sort-media-queries'
import cssnano from 'cssnano'
import url from '@rollup/plugin-url'
import typescript from 'rollup-plugin-typescript2'
import analyze from 'rollup-plugin-analyzer'

import fs from 'fs'
import path from 'path'

const babelConfig = 
  exclude: 'node_modules/**',
  babelHelpers: true,
  babelrc: false,
  presets: [['@babel/preset-env',  modules: false ]]


const baseFolder = './src/'
const componentsFolder = 'components/'

const components = fs
  .readdirSync(baseFolder + componentsFolder)
  .filter((f) =>
    fs.statSync(path.join(baseFolder + componentsFolder, f)).isDirectory()
  )

const entries = 
  index: './src/index.ts',
  ...components.reduce((obj, name) => 
    obj[name] = (baseFolder + componentsFolder + name)
    return obj
  , )


const capitalize = (s) => 
  if (typeof s !== 'string') return ''
  return s.charAt(0).toUpperCase() + s.slice(1)


const vuePluginConfig = 
  defaultLang: 
    style: 'postcss',
    script: 'ts'
  ,
  transformAssetUrls: 
    includeAbsolute: true
  ,
  preProcessStyles: true,
  compileTemplate: false,
  template: 
    isProduction: true,
    compilerOptions: 
      whitespace: 'condense'
    
  ,
  style: 
    postcssPlugins: [
      autoprefixer,
      postcssImport(
        resolve (id, basedir) 
          // resolve alias @css, @import '@css/style.css'
          // because @css/ has 5 chars
          if (id.startsWith('@css')) 
            // basedir will resolve to /src/components
            return path.resolve('src/assets/styles/css', id.slice(5))
          

          // resolve node_modules, @import '~normalize.css/normalize.css'
          // similar to how css-loader's handling of node_modules
          // if (id.startsWith('~')) 
          //   return path.resolve(basedir, '../node_modules', id);
          // 

          // resolve relative path, @import './components/style.css'
          return path.resolve(basedir, id)
        
      ),
      postcssEach,
      postcssMixin,
      simplevars,
      postcssColor,
      postcssCalc,
      nested,
      postcssextend,
      postcssDiscardComment,
      postcssDiscardEmpty,
      postcssUrl( url: 'inline' ),
      postcssRem(
        baseline: 16, // Default to 16
        // convert: 'px', // Default to rem
        fallback: true, // Default to false
        precision: 6 // Default to 5
      ),
      sortMedia(
        sort: 'mobile-first'
      ),
      autoprefixer(
        overrideBrowserslist: '> 1%, IE 6, Explorer >= 10, Safari >= 7'
      ),
      cssnano(
        zindex: false
      )

    ]
  


export default () => 
  const mapComponent = (name) => 
    return [
      
        input: baseFolder + componentsFolder + `$name/index.ts`,
        external: ['vue'],
        output: 
          format: 'umd',
          name: capitalize(name),
          file: `dist/components/$name/index.js`,
          exports: 'named',
          globals: 
            vue: 'Vue'
          
        ,
        plugins: [
          typescript(),
          url(
            include: [
              '**/*.svg',
              '**/*.png',
              '**/*.gif',
              '**/*.jpg',
              '**/*.jpeg'
            ]
          ),
          node(
            extensions: ['.vue', '.js', '.ts']
          ),
          cjs(),
          vue(vuePluginConfig),
          css(),
          babel(babelConfig)
        ]
      
    ]
  

  let config = [
    
      input: entries,
      external: ['vue'],
      output: 
        format: 'esm',
        dir: 'dist/esm'
      ,
      plugins: [
        typescript(),
        cjs(),
        url(
          include: [
            '**/*.svg',
            '**/*.png',
            '**/*.gif',
            '**/*.jpg',
            '**/*.jpeg'
          ]
        ),
        node(
          extensions: ['.vue', '.js', '.ts']
        ),
        vue(vuePluginConfig),
        css(),
        babel(babelConfig),
        analyze(),
        terser(
          output: 
            comments: '/^!/'
          ,
          compress: 
            defaults: true
          
        )
      ]
    ,
    
      input: entries,
      external: ['vue'],
      output: 
        format: 'cjs',
        dir: 'dist/cjs',
        exports: 'named'
      ,
      plugins: [
        typescript(),
        url(
          include: [
            '**/*.svg',
            '**/*.png',
            '**/*.gif',
            '**/*.jpg',
            '**/*.jpeg'
          ]
        ),
        node(
          extensions: ['.vue', '.js', '.ts']
        ),
        vue(vuePluginConfig),
        css(),
        babel(babelConfig),
        cjs()
      ]
    ,
    // 
    //   input: 'src/index.ts',
    //   external: ['vue'],
    //   output: 
    //     format: 'umd',
    //     name: capitalize('vu'),
    //     file: 'dist/vueslib.js',
    //     exports: 'named',
    //     globals: 
    //       vue: 'Vue'
    //     
    //   ,
    //   plugins: [
    //     typescript(),
    //     url(
    //       include: [
    //         '**/*.svg',
    //         '**/*.png',
    //         '**/*.gif',
    //         '**/*.jpg',
    //         '**/*.jpeg'
    //       ]
    //     ),
    //     node(
    //       extensions: ['.vue', '.js', '.ts']
    //     ),
    //     vue(vuePluginConfig),
    //     babel(babelConfig),
    //     cjs()
    //   ]
    // ,
    
      input: 'src/index.ts',
      external: ['vue'],
      output: 
        format: 'esm',
        file: 'dist/vueslib.esm.js'
      ,
      plugins: [
        typescript(),
        url(
          include: [
            '**/*.svg',
            '**/*.png',
            '**/*.gif',
            '**/*.jpg',
            '**/*.jpeg'
          ]
        ),
        node(
          extensions: ['.vue', '.js', '.ts', '.css']
        ),
        vue(vuePluginConfig),
        css(),
        babel(babelConfig),
        cjs()
      ]
    ,
    // individual components
    ...components.map((f) => mapComponent(f)).reduce((r, a) => r.concat(a), [])

  ]

  if (process.env.MINIFY === 'true') 
    config = config.filter((c) => !!c.output.file)
    config.forEach((c) => 
      c.output.file = c.output.file.replace(/\.js/g, '.min.js')
      c.plugins.push(terser(
        output: 
          comments: '/^!/'
        ,
        compress: 
          defaults: true
        
      ))
    )
  
  return config

错误截图

组件

<template>
<!-- @css/_app-partials.css-->
<div>
    <p class="yellow" @click="test()"> I am Dummy Component
      <span class="reda">asdasdas</span>
    </p>
    <span class="red">asdasdas</span>
    <img :src="require('../../assets/img/icons/download.jpeg')" />
    <div class="imgtest">
    </div>
</div>
</template>

<script lang="ts">
import  defineComponent, PropType  from 'vue'

export default defineComponent(
  name: 'DummyComponent',
  data () 
    return 
      text1: 'I am Text Component'
    
  ,
  props: 
    isValid: 
      type: Boolean,
      default: false
    ,
    msg: 
      type: Object as PropType<foo: string; bar: string>
    
  ,
  methods: 
    test (): void 
      console.log('test1', this.$props.msg)
    
  
)

</script>

<style lang="postcss" scoped>
@import '../../assets/styles/css/_app-partials.css';

.yellow 
    color: red;

    .reda 
        color: $black;
    


.red 
    color: green;


.imgtest 
    background-image: url('../../assets/img/icons/star-half.svg');
    background-size: 100%;
    width: 100px;
    height: 100px;

</style>

github repository link https://github.com/shubhadip/vue-typescript-component-library

【问题讨论】:

这能回答你的问题吗? Rollup, Vue and Buble, unexpected token in scss file 【参考方案1】:

看起来(在撰写本文时)汇总 vue plgin 不完全支持 vue3 和 scss。

github issue

急救

@P-Seebauer 的评论(上图)建议了解决此问题的方法。我添加了rollup-plugin-scss ,这似乎取悦了汇总。构建继续进行。它同时处理 .scss 和 .css 文件。

但是,由于预计 Vue.js 会处理这些问题,因此要求应用程序开发人员添加这样的插件似乎没有必要。

您还可以在以下位置查看@akauppi 的回答: Rollup, Vue and Buble, unexpected token in scss file(虽然这似乎是 vue2 特有的)

试试这个:

$ npm install --save-dev rollup-plugin-scss

在 rollup.config.js 中:

import scss from 'rollup-plugin-scss';      // handles '.css' and '.scss'
plugins:  ..., scss() 

我真的不知道发生了什么,这里。以上对我有用(Vue.js 3(beta)和 rollup-plugin-vue 6.0.0-beta.6。

【讨论】:

实际上我通过将 lang="postcss" 更改为 lang="css" 来实现这一点。但现在范围不适用于嵌套类。它正在生成这样的类.tsetsfc3essfc3-sample[data-v-48e10e12]__test【参考方案2】:

我通过将 lang="postcss" 更改为 lang="css" 然后调整 rollup-plugin-vue 选项来实现这个工作,这些选项在组件上内联 css 并在单独的文件中生成 css 必须使用 rollup-plugin-css-only。工作代码在 Github

【讨论】:

以上是关于尝试使用 rollup 和 vueJs 3 构建 vue 组件库的主要内容,如果未能解决你的问题,请参考以下文章

jest + vuejs + vuetify 的意外标识符

使用TypeScript和Rollup构建迭代器

外部依赖项错误地捆绑在 rollup.js 中?

VUEJS 3 组合计算和 vuex 没有反应

VueJS 进程显示未定义

使用roolup构建你的lib