Announcing: VS Code Extension - Intellisense for your Meteor Packages

Hello friends,

At work we’ve moved to a Meteor package based approach. By default, VS Code doesn’t know that when you import ... from 'meteor/package-name' that it is possibly a reference to a file in /packages. It also doesn’t know how to look for api.mainModule entries in a Package.js file. However, VS Code does let you map this kind of thing in jsconfig.json like this:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "meteor/package-name": [
        "packages/package-name/lib/client/main.js",
        "packages/package-name/lib/server/main.js"
      ]
    }
  }
}

Problem is that this is a bit tedious for something that feels like it should come out of the box.

So, I created an extension, with a single command that does exactly this.

https://marketplace.visualstudio.com/items?itemName=mattblack.meteor-package-intellisense

https://github.com/mattblackdev/meteor-package-intellisense

Just run the command:

Meteor Package IntelliSense

and it will go through your /packages folders, find the Package.js for each and wire up the mainModule entries to your jsconfig.json compilerOptions.paths value.

Next steps for me and anyone who wants to contribute, will be to add a setting for METEOR_PACKAGE_DIRS. For projects like VulcanJS this will be really handy. Then I’d also like to find out where meteor downloads atmosphere packages and do the same thing.

Hope this helps some folks! Enjoy!

15 Likes

Thank you!!

I’ve been having success with other tools, but this looks great. I’ll definitely try it out now

1 Like

Pretty interesting, thanks for creating this! Would love to see METEOR_PACKAGE_DIRS support, since a lot of my packages are included this way.

Will this also support auto-completion for meteor package names in import directives? This currently works for Meteor’s standard packages, but not for custom packages with the : syntax.

1 Like

I’ve been using @types/meteor, which works with JavaScript as well as TypeScript, but I’ll definitely be giving this a try!

1 Like

One question: Is there a reason why your auto-generated jsconfig.json does not exclude the .meteor folder? I am new to this and wondering if I should include this to prevent parsing of the built files.

1 Like

Not sure if this is what you mean by auto-completion, but once you get to the from 'meteor/' part VS Code will show you a list that includes the packages added by the command. I think the more you use an import the smarter that list gets.

Definitely want to get METEOR_PACKAGE_DIRS as a setting implemented. I’ll post here when I do it. I’ll also accept PRs if someone can get to it before me.

And I will add the excludes field to the default jsconfig.json. My original intent was to only focus on the package intellisense feature but I think you make a point, I don’t see any reason to not include the basic stuff that every meteor project should have. Here’s my usual exclude field:

"exclude": ["node_modules", "**/node_modules/*", ".meteor"]

Note, the extension does a deep merge but does not merge arrays. So if one already has an exclude field the extension won’t touch it.

1 Like

Hey @robfallows, correct me if I’m wrong but isn’t @types/meteor just types for Meteor’s packages? I’m not sure how it’s related to this?

1 Like

Yeah - I realised later I’d misunderstood what you’d put together :roll_eyes:

Yes, that’s what I meant. In standard VS Code, if you type meteor/, you would get a list that includes standard packages like meteor/jquery, but not community packages like meteor/kadira:flow-router.

Ah, so for atmosphere packages, we need to figure out where their source gets downloaded when you do a meteor add. I believe it’s somewhere in the ~/.meteor dir. But once that is known, should be able to apply the same mapping strategy in jsconfig.

Btw, I believe the out-of-the-box support for standard meteor packages is coming from VS Code’s ability to download typescript types behind the scenes. Which is like what rob mentioned above.

1 Like

Honestly hoping one of the Meteor devs (@benjamn :slight_smile:) will point us in the right direction for the storage location of atmosphere packages. I’ve searched for it in the past and never had any luck.

Not on my computer right now, but look at the fastosphere repo.

Edit: Also check this: http://richsilv.github.io/meteor/accessing-meteor-package-data/

1 Like

Great, thanks! Looking forward to trying this out.

Keep an eye on this issue, once it’s cleared Intellisense should support leading slashes. It’s had some movement lately so hopefully it’ll be done soon.

2 Likes

the imports-js package has pretty good Meteor package resolution.
You can find the code for that here:

It scans the packages file and then checks local and global installs for the source, before parsing them to find exports (either api.export or api.mainModule). I use it for import fixing, but it doesn’t give intellisense

@coagmano WOW! Those comments are a gold mine!! Check this out. Looks like I was right about the ~/.meteor dir, but apparently I never looked inside it :see_no_evil:. The packages are all in there plain as day… But in this case getting the mainModule is not as simple.

The first step will be to deduce the package version to get the file path to the package code. I think that can be obtained from the project’s .meteor/versions file. Once it has that it will need to get the mapping between the package name and the paths for VS Code.

There are package.js files, but they only have this:

// This file is included for compatibility with the Meteor 0.6.4 package downloader.

Another delicious comment:

  // There are two ways that meteor packages expose their exported interface,
  // pre-ES6 and post-ES6.

It looks like getting the paths for pre-ES6 packages is going to be difficult. AFAIK, there’s no mapping:

  // The pre-ES6 modules method is to call api.exports within the package.js
  // file for each variable exported. These calls all result in declaredExports
  // entries in the individual platform's json files. Within
  // aldeed:simple-schema's web.browser.json for example, we find
  //
  //  "declaredExports": [
  //      {
  //        "name": "SimpleSchema",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "MongoObject",
  //        "testOnly": false
  //      },
  //      {
  //        "name": "humanize",
  //        "testOnly": true
  //      }
  //    ],
  //
  // From this, we can determine that the namedExports entry should be
  //
  //   'aldeed:simple-schema': ['SimpleSchema', 'MongoObject']

My understanding is for import-js that’s all they need, but for our case, we need to know what files to point VS Code to.
So, I’m stuck there. Likely that we’ll need a completely different approach.

For post-ES6 packages, I see a way.

The web.browser.json (and probably the other architectures’ similar files) has this bit:

"resources": [
    {
      "type": "source",
      "extension": "js",
      "file": "web.browser/lib/client/main.js",
      "length": 37,
      "offset": 0,
      "path": "lib/client/main.js",
      "hash": "30ed8a6f16aab5c81623a0fe37ac627b1206c29d",
      "fileOptions": {
        "mainModule": true,
        "lazy": false
      }
    },
   ...

The extension can collect the mappings from this resources field where objects have fileOptions.mainModule = true.

I’m not gonna lie, tackling this seems like a lot of work and probably not the best use of time considering we’ve all made it this far without IntelliSense…but it also seems possible, at least for newer packages using ES6 modules. So, I think I’ll leave it up to the community to decide if it’s worth it, or propose a better way.

For now, I’m going to focus on local packages and supporting the METEOR_PACKAGE_DIRS option since this is lower hanging fruit and will directly benefit VulcanJS and my needs at work.

1 Like

I know right!

I absolutely love how detailed and comprehensive they are. I added mainModule support to that library a while ago, and the only reason I had any chance of figuring out what was going on was because of how good the comments were!

Would it work to use the list of resources from web.browser.json?
I don’t really understand the paths setting in VSCode

Holy shit… I think you’re right. I think I was over thinking it. I should stop thinking. Just need to point VS Code to the source. The compilerOptions.paths is like aliases.

For example, aldeed:simple-schema@1.3.3 has this in web.browser.json:

  "resources": [
    {
      "type": "source",
      "extension": "js",
      "file": "web.browser/packages/aldeed_simple-schema.js",
      "length": 486182,
      "offset": 0,
      "usesDefaultSourceProcessor": true,
      "path": "/packages/aldeed_simple-schema.js",
      "hash": "ef7b8848de610918aa5bbcd8ab1e94e81263410b"
    }
  ]
}

If we constrain it to the web.browser architecture for simplicity’s sake, the extension should produce something like this in jsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "meteor/aldeed:simple-schema": ["../../.meteor/packages/aldeed_simple-schema/1.3.3/web.browser/packages/aldeed_simple-schema.js"]
    }
  }
}

The file paths are supposed to be relative to baseUrl and with "." as that value it means relative to the workspace/project root. This example assumes the project folder is two directories down from the user’s home folder and meteor was installed in the home folder. I’m not sure of a good strategy for determining meteor’s install location from the context of a VS Code extension. Anyone have thoughts on that? Maybe it’s in those import-js comments.

Note that a little known feature of Meteor is that it supports different package servers (e.g. I’ve written one myself: https://github.com/sebakerckhof/stratosphere ). Then it will save packages to .meteor/packages-from-server/[server]/. But of course that’s only something that affects < 1% of users. Also note the .meteor dir on windows is in %APPDATA%\..\Local\.meteor