Make atmosphere package lazy

I git cloned an atmosphere Meteor package to make it lazy loading for dynamic import. Following what I believe are the instructions, I added api.mainModule('client/main.js', 'client', {lazy:true}); to the package.json of the module.

However, any attempt to import 'meteor/module-name' results in

Uncaught (in promise) Error: Cannot find module 'meteor/module-name'

Has anybody run into this or knows the solution (using METEOR@1.6.1.3)

Hmmm, this should work. Let me try in a fresh repo

Could it be that you haven’t added the package to .meteor/packages?

I tried it on a fresh repo and it worked both with static and dynamic imports:

Setup
meteor create meteor-forum-44390 --release=1.6.1.3
cd meteor-forum-44390
meteor create --package test
meteor add test
meteor
Steps taken
  • Edited packages/test/package.js
  • Checked that module had not loaded in client
  • Edited client/main.js to match “static import”
  • Checked that module had loaded on start, and behaved correctly
  • Edited client/main.js to match “dynamic import”
  • Checked that module had not loaded on start
  • Checked that module correctly loaded after four button clicks and behaved correctly after five
static import
import { Template } from "meteor/templating";
import { ReactiveVar } from "meteor/reactive-var";

import "./main.html";

Template.hello.onCreated(function helloOnCreated() {
  // counter starts at 0
  this.counter = new ReactiveVar(0);
});

Template.hello.helpers({
  counter() {
    return Template.instance().counter.get();
  },
  gt(one, two) {
    return one > two;
  },
  importTest() {
    import { name } from "meteor/test";
    return name;
  },
});

Template.hello.events({
  "click button"(event, instance) {
    // increment the counter when button is clicked
    instance.counter.set(instance.counter.get() + 1);
  },
});
dynamic import
import { Template } from "meteor/templating";
import { ReactiveVar } from "meteor/reactive-var";

import "./main.html";

Template.hello.onCreated(function helloOnCreated() {
  // counter starts at 0
  this.counter = new ReactiveVar(0);

  this.autorun(async () => {
    if (this.counter.get() > 3) {
      const { name } = await import("meteor/test");
      this.name = name;
    }
  });
});

Template.hello.helpers({
  counter() {
    return Template.instance().counter.get();
  },
  gt(one, two) {
    return one > two;
  },
  importTest() {
    const inst = Template.instance();
    return inst.name;
  },
});

Template.hello.events({
  "click button"(event, instance) {
    // increment the counter when button is clicked
    instance.counter.set(instance.counter.get() + 1);
  },
});
package.js
Package.describe({
  name: "test",
  version: "0.0.1",
  // Brief, one-line summary of the package.
  summary: "",
  // URL to the Git repository containing the source code for this package.
  git: "",
  // By default, Meteor will default to using README.md for documentation.
  // To avoid submitting documentation, set this field to null.
  documentation: "README.md",
});

Package.onUse(function(api) {
  api.versionsFrom("1.6.1.3");
  api.use("ecmascript");
  api.mainModule("test.js", "client", { lazy: true });

});

Package.onTest(function(api) {
  api.use("ecmascript");
  api.use("tinytest");
  api.use("test");
  api.mainModule("test-tests.js");
});

EDIT: Added package.js code
EDIT: Added setup and steps taken

1 Like

Definitely, package was already added to .meteor/packages. I just changed the package.json to not export anything and define api.mainModule(‘client/main.js’, ‘client’, {lazy:true});

can you post the package.js?

I tried to make accounts-admin-ui-bootstrap-3 load lazy

Package.describe({
	name: "accounts-admin-ui-bootstrap-3",
	summary: "A roles based account management system using bootstrap 3",
	version: "0.2.9",
	git: "https://github.com/hharnisc/meteor-accounts-admin-ui-bootstrap-3.git"
});
//
// api.mainModule("main.js", "client", { lazy: true })
// https://docs.meteor.com/packages/modules.html#Lazy-loading-modules-from-a-package
// Note: Packages with lazy main modules cannot use api.export to export global symbols
//
Package.on_use(function (api, where) {
	api.versionsFrom("METEOR@0.9.0");
	api.use('reactive-var', ['client', 'server']);
	api.use('standard-app-packages', ['client', 'server']);
	api.use("alanning:roles@1.2.8", ['client', 'server']);

	api.add_files('libs/user_query.js', ['client', 'server']);

	// api.add_files('client/startup.js', 'client');
	//api.add_files('client/accounts_admin.html', 'client');
	//api.add_files('client/accounts_admin.js', 'client');
	//api.add_files('client/delete_account_modal.html', 'client');
	//api.add_files('client/delete_account_modal.js', 'client');
	//api.add_files('client/info_account_modal.html', 'client');
	//api.add_files('client/info_account_modal.js', 'client');
	//api.add_files('client/update_account_modal.html', 'client');
	//api.add_files('client/update_account_modal.js', 'client');
	//api.add_files('client/update_roles_modal.html', 'client');
	//api.add_files('client/update_roles_modal.js', 'client');
	//api.add_files('client/filter_roles_modal.html', 'client');
	//api.add_files('client/filter_roles_modal.js', 'client');
	api.mainModule('client/main.js', 'client', {lazy:true});
	// api.export('AccountsAdmin');

	api.add_files('style/style.css', 'client');

	api.add_files('server/startup.js', 'server');
	api.add_files('server/publish.js', 'server');
	api.add_files('server/methods.js', 'server');
});

Just created a new empty package and was able to use it with lazy, so it must have to do something with the original package.js file

First thing, I notice is that the docs say:

For packages, you can enable modules by adding api.use('modules') to the Package.onUse or Package.onTest sections of your package.js file.

And your package.js doesn’t have that.

So I tried it on my repro and that didn’t work, but adding api.use("ecmascript") (which implies modules) did work!

I had to make a guess about the contents of client/main.js so I just made one that imports all the other client files.

package client/main.js
import './startup.js';
import './accounts_admin.html';
import './accounts_admin.js';
import './delete_account_modal.html';
import './delete_account_modal.js';
import './info_account_modal.html';
import './info_account_modal.js';
import './update_account_modal.html';
import './update_account_modal.js';
import './update_roles_modal.html';
import './update_roles_modal.js';

And this worked with both static and dynamic imports. I changed my client/main.js to log one of the blaze templates from that package before and after the dynamic import to test it:

Template.hello.onCreated(function helloOnCreated() {
  // counter starts at 0
  this.counter = new ReactiveVar(0);

  this.autorun(async () => {
    if (this.counter.get() === 4) {
      console.log("Before:", Template.accountsAdmin);
      await import("meteor/accounts-admin-ui-bootstrap-3");
      console.log("After:", Template.accountsAdmin);
      this.name = name;
    }
  });
});

Which logs:

main.js:12 Before: undefined
main.js:14 After: Blaze.Template {viewName: "Template.accountsAdmin", renderFunction: ƒ, __helpers: HelperMap, __eventMaps: Array(1), _callbacks: {…}, …}

So try adding api.use("ecmascript") and see if it works for you too

1 Like

Thank you, that was definitely it.

Now I am having a problem with the import paths in the module. I created a main.js file in the root directory of the module that imports everything for the client side, like the first line is

import './client/accounts_admin.html';

but once I import the module I’m hit by

Error: Cannot find module './client/accounts_admin.html'

Any hints?