Skip to content

api.injectImports won't add a side-effects-only import if there is already one present in the file #6283

Open
@jessevanassen

Description

@jessevanassen

Version

4.5.11

Environment info

Environment Info:

  System:
    OS: Linux 5.8 Ubuntu 20.04.2 LTS (Focal Fossa)
    CPU: (8) x64 Intel(R) Core(TM) i5-8365U CPU @ 1.60GHz
  Binaries:
    Node: 15.6.0 - ~/.nvm/versions/node/v15.6.0/bin/node
    Yarn: Not Found
    npm: 6.14.11 - ~/.nvm/versions/node/v15.6.0/bin/npm
  Browsers:
    Chrome: 88.0.4324.150
    Firefox: 85.0.1
  npmGlobalPackages:
    @vue/cli: 4.5.11

Steps to reproduce

Create a plugin with the following generator, and invoke it multiple times.

module.exports = function (api) {
	api.injectImports(api.entryFile, `import './${Date.now()}.js'`);
}

What is expected?

Every time the plugin is invoked, it will add a new import, as the filename of the import is unique.

What is actually happening?

The first time the plugin is invoked, the import is added. Subsequent runs of the plugin won't add new imports.


The culprit seems to be the toImportHash function in @vue/cli/lib/util/codemods/injectImports.js.

jscodeshift outputs nodes according to the ESTree spec. According to this spec, for an ImportDeclaration the source is of type Literal. Literal doesn't have a raw property.

This is where different parsers output different results, as can be seen in this AST Explorer example. Parsers like Acorn and Flow do have the raw propery. However, jscodeshift uses @babel/parser by default, which doesn't have this property.

That is why toImportHash fails for this case. node.source.raw will never be defined, so it won't be included in the result of JSON.stringify. Additionally, node.specifiers will be always be an empty array.
That means that toImportAST for something like import './side-effects-only.js' will always return '{"specifiers":[]}', additional side-effects-only imports will be marked as duplicates, and won't be added.

For a regular import like import Vue from 'vue', toImportAST will return '{"specifiers":["Vue"]}' which is unique enough for basically every case, and why the problem doesn't occur for those.

Suggested solution
Instead of using node.source.raw, node.source.value is probably the safer option, as that is defined in the spec so won't be dependent on the parser. That means that the type of quotes used in the string will get lost, but I expect that to be acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions