vuejs / vue-cli

🛠️ webpack-based tooling for Vue.js Development
https://cli.vuejs.org/
MIT License
29.75k stars 6.33k forks source link

Preset API: Using api.injectImports in generator while using @vue/cli-plugin-eslint fails because "no-unused-vars" error #3894

Open graup opened 5 years ago

graup commented 5 years ago

Version

3.6.3

Environment info

Environment Info:

  System:
    OS: macOS High Sierra 10.13.6
    CPU: (4) x64 Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
  Binaries:
    Node: 11.14.0 - /usr/local/bin/node
    Yarn: 1.15.2 - /usr/local/bin/yarn
    npm: 6.9.0 - /usr/local/bin/npm
  Browsers:
    Chrome: 73.0.3683.103
    Firefox: 64.0
    Safari: 12.1
  npmGlobalPackages:
    @vue/cli: 3.6.3

Steps to reproduce

Following the preset guide, I'm trying to inject a dependency to the entry file using api.injectImports and then add a Vue.use() line for it during api.onCreateComplete. I also added @vue/cli-plugin-eslint through the preset. The problem is that the eslint plugin's onCreateComplete hook runs before my own, causing a "no-unused-vars" error and failing the create process before I got a chance to add the Vue.use line.

preset.json

{
  "useConfigFiles": true,
  "router": true,
  "cssPreprocessor": "sass",
  "plugins": {
    "@vue/cli-plugin-babel": {},
    "@vue/cli-plugin-eslint": {
      "config": "airbnb",
      "lintOn": ["save", "commit"]
    }
  }
}

generator.json

const { EOL } = require('os');

module.exports = api => {
    api.extendPackage({
        dependencies: {
            // my-plugin dependency
        }
    });

    api.injectImports(api.entryFile, `import myPlugin from 'my-plugin';`);

    api.onCreateComplete(() => {
        const entryFile = api.resolve(api.entryFile);
        const fs = require('fs');
        const contentMain = fs.readFileSync(entryFile, { encoding: 'utf-8' });
        const lines = contentMain.split(/\r?\n/g);
        const renderIndex = lines.findIndex(line => line.match(/new Vue/))
        lines[renderIndex] = `Vue.use(myPlugin, {});${EOL}${EOL}`   lines[renderIndex];
        fs.writeFileSync(entryFile, lines.join(EOL), { encoding: 'utf-8' });
    });
}

What is expected?

The import is injected, the line of code is added, and everything lints properly.

What is actually happening?

The lint runs before adding the line of code causing a "no-unused-vars" error.


  1. Is there a workaround for this scenario?
  2. Would it be worth pointing this problem out in the guide? It took me a moment to understand that injectImports adds lines during the initial generation, but onCreateComplete runs after everything is done, possibly after other plugins.
graup commented 5 years ago

Addition: it works if I leave out the "config": "airbnb" option from the @vue/cli-plugin-eslint config, because in that case it doesn't run the lint automatically on completion.

Maybe my suggestion would be to remove the auto-lint here or provide an option to turn if off.

LinusBorg commented 5 years ago

The autolint is needed, we can't drop it. Without it, the whole project would be one big lint error as the files that were generated don't match the rules enforced by the respective standard/airbnb/prettier rules.

However I agree that it would be better to postpone this until all plugin's onComplete hooks have run.

LinusBorg commented 5 years ago

As an immediate solution to your problem, you can manipulate files before that hook by using api.generator.files. That property references an object containing all of the project's files as strings, before they are written to disk.

The format is

{
'path-relative-to-project-root': 'file content as string'
}
graup commented 5 years ago

Yes, that makes sense. Thanks.

I found another workaround. Using this code to push my hook to the front of the callbacks. Not very pretty since it's using private API, but works as well.

api.onCreateCompleteFirst = function(cb) {
    this.generator.completeCbs.unshift(cb);
};

api.onCreateCompleteFirst(() => {
    ....
});
Akryum commented 5 years ago

I would strongly advise against using private APIs if possible.