vuejs / component-compiler-utils

Lower level utilities for compiling Vue single file components
321 stars 75 forks source link

Fail to locate @import file "~@pkg/style.styl" in node_modules #49

Open KarlBao opened 6 years ago

KarlBao commented 6 years ago

Expected behavior

Expect to locate the .styl file from node_modules with ~ (which works well when bundled with Webpack)

<style lang="stylus" scoped>
@import '~@my-pkg/style.styl'
</style>

Actual behavior

Get the error:

failed to locate @import file ~@my-pkg/style.styl
    at Evaluator.visitImport (C:\Users\dev\node_modules\stylus\lib\visitor\evaluator.js:915:21)
    ...
    ...
    ...

However, when I import the .styl file with relative path:

<style lang="stylus">
@import '../node_modules/@my-pkg/style.styl'
</style>

It works fine.

Steps to reproduce the behavior

My rollup config:

import vue from 'rollup-plugin-vue'
import typescript from 'rollup-plugin-typescript2'
import nodeResolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import json from 'rollup-plugin-json'

export default {
  input: 'src/index.vue',
  output: {
    format: 'esm',
    file: 'dist/bundle.esm.js'
  },
  plugins: [
    nodeResolve({
      jsnext: true,
      main: true
    }),
    commonjs(),
    json(),
    typescript({
      rollupCommonJSResolveHack: true,
      useTsconfigDeclarationDir: true
    }),
    vue()
  ]
}
r4fx commented 6 years ago

this is the most problematic in rollup-plugin-vue for me, have you find a way for this issue ?

y-nk commented 5 years ago

it looks like rollup-plugin-vue does not support alias resolution in style blocks... how come this is not solved yet ?

y-nk commented 5 years ago

@KarlBao @r4fx after few hours of debugging, i found a fix which works.

In your rollup config, add :

  1. Import the stylus node evaluator with import { Evaluator } from 'stylus'

  2. Pass it along the vue options with

vue({
  style: {
    preprocessOptions: {
      stylus: { Evaluator }
    }
  }
}),
  1. Now, we can patch Evaluator to support alias resolution :
const visitImport = Evaluator.prototype.visitImport
Evaluator.prototype.visitImport = function(imported) {
  const path = imported.path.first

  if (path.string.startsWith('~'))
    path.string = path.string.replace('~', '').replace('@', '/whatever-absolute-path-we-want/src')

  return visitImport.call(this, imported)
}

This dirty solution works as a workaround and should definitely be addressed in other way. Although, it works cleanly enough not to relay on any dirty patch located in node_modules.

All in one place explanation :

import alias from 'rollup-plugin-alias'
import vue from 'rollup-plugin-vue'

import { Evaluator } from 'stylus'

const aliases = {
  '@' : `${__dirname}/src`
}

const visitImport = Evaluator.prototype.visitImport
Evaluator.prototype.visitImport = function(imported) {
  const path = imported.path.first

  if (path.string.startsWith('~')) {
    const alias = Object.keys(aliases).find(entry => path.string.startsWith(`~${entry}`))

    if (alias)
      path.string = path.string.substr(1).replace(alias, aliases[alias])
  }

  return visitImport.call(this, imported)
}

const plugins = [
  alias(aliases),
  vue({
    style: {
      preprocessOptions: {
        stylus: { Evaluator }
      }
    }
  }),
]

🚀

KarlBao commented 5 years ago

@y-nk You rock, man! It works well!