meteordependenciesmeteor-packages

Meteor package import css files from npm package that is only marked as dependency using tmeasday:check-npm-versions


Since quite some time there is this package tmeasday:check-npm-versions that allows to define an implicit npm package dependency to a Meteor package.

Use this package if you are writing an Atmosphere package that depends on a given npm package is installed at the app level of projects you are installed in.

Now I am writing a Meteor package, say me:my-package and have a dependency to an npm package, say some-package. This npm package requires me to import these styles manually.

The folder for that is located at some-package/style/main.less.

I tried to import the file within the package but it can't be imported into my package's less file:

@import "some-package/style/main";
@import "{}/some-package/style/main";
@import "node_modules/some-package/style/main";
@import "{}/node_modules/some-package/style/main";

all throw the same error:

While processing files with less (for target web.browser):
packages/me:my-package/style.less:1: Unknown import: <one of the path's above>

The obvious reason here: a Meteor package usually requires me to add files via api.addFiles but the package has no 'real dependency' to the npm package to import this file.

I could move the 'responsibility' of importing the styles to the application that will use 'me:my-package' because it will have to install the npm package, too.

Putting the following line into the css of the application that will use me:my-package actually works:

@import "/node_modules/some-package/style/main.less";

But this would also force the app to install less or sass if the package uses it (as less in my case).

Somebody managed to solve this in a package-user-friendly way?


Solution

  • After some time I came back to this issue and found an answer just by accident. It can be achieved with the dynamic-import package.

    You need to add all the styles to import in an exported function that returns an array of dynamic imports.

    So let's consider the hypothetical package me:mypackage:

    Package.describe({
      name: 'me:mypackage',
      // ...
    });
    
    // this example uses it only on the client 
    // because the focus is importing styles
    // but the pattern could work for other assets, too
    Package.onUse(function(api) {
      api.versionsFrom('1.6');
      api.use('ecmascript', 'client');
      api.use('underscore', 'client');
      api.use('tmeasday:check-npm-versions', 'client')
      api.mainModule('mypackage.js', 'client');
    });
    

    Note, that I use api.mainModule here, so we will export something. If you want to add the package to the global namespace you will have to attach the function with the dynamic imports somewhere to make it accessible.

    In your main module you declare your npm dependencies and your dynamic styles import:

    import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions';
    checkNpmVersions({
      'some-package': '4.x.x'
    }, 'me:mypackage');
    
    const somepackage = require('some-package)
    // do whatever with some-package....
    
    // export a function that let's your project
    // to be able to just import the right style deps
    export const importStyles = function() {
      return [
        import('some-package/style/main.css'),
        // ... and more if required
      ]
    }
    

    In you project you need to add dynamic-imports and me:mypackage:

    $ meteor add dynamic-imports me:mypackage
    

    and import it on top-level in your client/main.js:

    import { importStyles } from 'meteor/me:mypackage'
    importStyles()
    

    Advantage:

    Disadvantage: