Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recent update seems to have broken WebStorms ability to find components #51

Closed
leeovery opened this issue Jun 14, 2020 · 10 comments
Closed

Comments

@leeovery
Copy link

Prior to the recent update webstorm was able to index and resolve the components when being auto imported.

Now we seem to be getting the squiggly line error saying webstorm cannot find the component.

Before it could but cmd clicking took you to the index where all the components are registered. But this was better than no cmd click functionality at all.

Thanks
Lee

@pi0
Copy link
Member

pi0 commented Jun 16, 2020

Hi @leeovery. Please ensure components: true (or with array value) is set inside nuxt.config. Not related to IDE.

@pi0 pi0 closed this as completed Jun 16, 2020
@leeovery
Copy link
Author

leeovery commented Jun 16, 2020 via email

@leeovery
Copy link
Author

leeovery commented Jun 16, 2020

@pi0 To be clear with what the issue is here....

Prior to the v0.3.4 update, the component imports were picked up by the IDE and therefore the use of the components didn't generate an IDE error.

However, now the importing layout seems to have changed in nuxt/components/index.js and as a result, the IDE is now not picking up the imports/definitions. This means that the component use in the templates is showing an error as the ide doesn't know where they live, or even whether they exist.

I'll add, in both cases I am correctly using the plugin. It's registered in buildModules and I have the component:true flag at the root level of the nuxt config. Importantly, the module is working, in that the components are being rendered in the browser.

The issue is the IDE isn't able to pick up the registration of the imports since the latest update.

And whilst this isn't a bug in the code as such - an issue like this will result in problems, because nobody likes seeing their code scattered with ide errors.

Edit: Another important issue is lack of autocompletion as a result of the IDE not being able to register the new import structure.

@pi0
Copy link
Member

pi0 commented Jun 17, 2020

Hi @leeovery, Sorry but with all description you provided, I still can't figure out what's wrong with " not picking up the imports/definitions". If you wanted would be happy having a chat on discord (probably sharing screen) to see what's not working.

@leeovery
Copy link
Author

@pi0 That would be great, thank you. Im about whenever you are. username on discord is leeovery#1110

@leeovery
Copy link
Author

https://www.dropbox.com/s/rxjad3fsr2oqocg/Screenshot%202020-06-17%2016.13.49.png?dl=0

This error shows the issue. WebStorm doesnt know where the components are and whether they exist. Prior to the recent update, this was not the case. There was no error.

@grunghi
Copy link

grunghi commented Nov 8, 2020

Hi, I've came up with a temporary workaround until this is fixed on the IDE level - simple nuxt module that creates .components.gen.js file with component imports. This file sits outside of the .nuxt folder where it is picked up by Intellij IDE's for autocompletion etc.
The IDE ignores imports when the file is placed inside .nuxt folder for some reason.

  1. create a module in ~/modules/idea-imports-fix.js
import glob from 'glob'
import { resolve, join } from 'path'

export default function ideaImportsFix () {
  if (process.env.NODE_ENV === 'production') {
    return
  }

  const components = glob.sync(join(__dirname, '../components/**/*.vue')).map(file => {
    const name = file.match(/(\w*)\.vue$/)[1]
    return { name, file }
  })

  const getComponents = () => components

  this.addTemplate({
    src: resolve(__dirname, './templates', 'components.js'),
    fileName: '../.components.gen.js',
    options: { getComponents },
  })
}
  1. create a template in ~/modules/templates/components.js:
<%= options.getComponents().map(({ name, file }) => {
  return `import ${name} from '${file}'`
}).join('\n') %>

<%= options.getComponents().map(({ name, file }) => {
  return `Vue.component('${name}', ${name})`
}).join('\n') %>
  1. register module in nuxt buildModules property
export default {
  buildModules: [
  '~/modules/idea-imports-fix.js'
  ]
}
  1. add .components.gen.js to .gitignore

@reinoldus
Copy link

@grunghi Thank you very much for these snippets! I extended it a bit (I do not know much about nodeJs, just wanted to get it done):

I copied all the functions from this module (de-typescripted them):

// ~/modules/idea-imports-fix.js
import { resolve } from 'path'
import { basename, extname, join, dirname, relative } from 'upath'
import globby from 'globby'
import { pascalCase, splitByCase } from 'scule'
// const { basename, extname, join, dirname, relative } = require('upath')
//
// const globby = require('globby')
// const { pascalCase, splitByCase } = require('scule')

function sortDirsByPathLength({ path: pathA }, { path: pathB }) {
  return (
    pathB.split(/[\\/]/).filter(Boolean).length -
    pathA.split(/[\\/]/).filter(Boolean).length
  )
}

// vue@2 src/shared/util.js
function hyphenate(str) {
  return str.replace(/\B([A-Z])/g, '-$1').toLowerCase()
}

async function scanComponents(dirs, srcDir) {
  const components = []
  const filePaths = new Set()
  const scannedPaths = []

  for (const {
    path,
    pattern,
    ignore = [],
    prefix,
    extendComponent,
    pathPrefix,
    level,
  } of dirs.sort(sortDirsByPathLength)) {
    const resolvedNames = new Map()
    console.log(pattern)

    for (const _file of await globby(pattern, { cwd: path, ignore })) {
      const filePath = join(path, _file)

      if (scannedPaths.find((d) => filePath.startsWith(d))) {
        continue
      }

      if (filePaths.has(filePath)) {
        continue
      }
      filePaths.add(filePath)

      // Resolve componentName
      const prefixParts = [].concat(
        prefix ? splitByCase(prefix) : [],
        pathPrefix !== false
          ? splitByCase(relative(path, dirname(filePath)))
          : []
      )
      const fileName = basename(filePath, extname(filePath))
      const fileNameParts =
        fileName.toLowerCase() === 'index' ? [] : splitByCase(fileName)

      const componentNameParts = []

      while (
        prefixParts.length &&
        (prefixParts[0] || '').toLowerCase() !==
          (fileNameParts[0] || '').toLowerCase()
      ) {
        componentNameParts.push(prefixParts.shift())
      }

      const componentName =
        pascalCase(componentNameParts) + pascalCase(fileNameParts)

      if (resolvedNames.has(componentName)) {
        // eslint-disable-next-line no-console
        console.warn(
          `Two component files resolving to the same name \`${componentName}\`:\n` +
            `\n - ${filePath}` +
            `\n - ${resolvedNames.get(componentName)}`
        )
        continue
      }
      resolvedNames.set(componentName, filePath)

      const pascalName = pascalCase(componentName)
      const kebabName = hyphenate(componentName)
      const shortPath = relative(srcDir, filePath)
      const chunkName = 'components/' + kebabName

      let component = {
        filePath,
        pascalName,
        kebabName,
        chunkName,
        shortPath,
        import: '',
        asyncImport: '',
        export: 'default',
        global: Boolean(global),
        level: Number(level),
      }

      if (typeof extendComponent === 'function') {
        component = (await extendComponent(component)) || component
      }

      component.import =
        component.import ||
        `require('${component.filePath}').${component.export}`
      component.asyncImport =
        component.asyncImport ||
        `function () { return import('${component.filePath}' /* webpackChunkName: "${component.chunkName}" */).then(function(m) { return m['${component.export}'] || m }) }`

      // Check if component is already defined, used to overwite if level is inferiour
      const definedComponent = components.find(
        (c) => c.pascalName === component.pascalName
      )
      if (definedComponent && component.level < definedComponent.level) {
        Object.assign(definedComponent, component)
      } else if (!definedComponent) {
        components.push(component)
      }
    }

    scannedPaths.push(path)
  }

  return components
}

function matcher(tags, components) {
  return tags.reduce((matches, tag) => {
    const match = components.find(({ pascalName, kebabName }) =>
      [pascalName, kebabName].includes(tag)
    )
    match && matches.push(match)
    return matches
  }, [])
}

export default async function ideaImportsFix() {
  if (process.env.NODE_ENV === 'production') {
    return
  }

  const sh = require('shelljs')
  const c = sh.pwd()
  console.log(c)
  const data = await scanComponents(
    [
      {
        path: './components',
        pattern: '**',
      },
    ],
    '/home/ubuntu/git/wimj-website/'
  )
  let components = data.map(({ filePath: file, kebabName, pascalName }) => {
    if (file.match(/(\w*)\.vue$/)) {
      console.log(pascalName)
      return { name: pascalName, file, kebabName, pascalName }
    }
  })
  components = components.filter((v) => v !== undefined)
  const getComponents = () => components

  this.addTemplate({
    src: resolve(__dirname, './templates', 'components.js'),
    fileName: '../.components.gen.js',
    options: { getComponents },
  })
}

And altered the component.js template file to:

<%= options.getComponents().map(({ name, file,kebabName }) => {
return `import ${name} from '${file}'`
}).join('\n') %>

<%= options.getComponents().map(({ name, file, kebabName }) => {
  return `Vue.component('${name}', ${name})
Vue.component('${kebabName}', ${name})`
}).join('\n') %>

Finally webstorm is usable again. Once the file is imported you have to right click on it and select "inspect code" otherwise wouldn't work for me

@acidjazz
Copy link

acidjazz commented Apr 8, 2021

@grunghi this is excellent work and would make a nice nuxt module until JetBrains does something!

@JasonLandbridge
Copy link
Sponsor

For those looking for a solution: Nuxt-storm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants