My Meteor project is structured in packages only (there are several articles on the internet recommending and explaining on how to do this). I ran into a basic problem which I can not seem to solve. Here is the issue stripped down to a minimum test case:
I have two packages, 'one' and 'two' (created with 'meteor create --package' and added to the project subsequently) Package two holds a variable and exports this variable. Package one logs the contents of this variable to the console:
two.js:
two_var = 'two'
package.js (two):
Package.describe({
name: 'two'
});
Package.onUse(function (api) {
api.addFiles('two.js', 'client');
api.export('two_var', 'client')
});
one.js:
Meteor.startup(function() {
console.log(two_var) //two_var found, unexpected
})
setTimeout(function () {
console.log(two_var) //two_var found, unexpected
}, 3000)
console.log(two_var) //two_var not found, expected
package.js (one):
Package.describe({
name: 'one'
});
Package.onUse(function(api) {
//api.use('two', 'client'); //not using two, but two_var found in certain situations
api.addFiles('one.js', 'client');
});
As you can see the variable from package two can be accessed in situations I would not expect so. Am I overlooking something or do I misunderstand the concept of the package?
Related
I've been using Webpack for my ES6 JS project and has been going well until I started to play with dynamic imports.
What I had that worked (router.js):
import { navigo } from "Navigo"; // router
import { clients } from "Controllers/clients.js";
const navigo = new Navigo();
navigo_router.on({
'/clients': () => {
clients.init();
}
});
But the more pages/routes I add, the more imports get stacked up in the head of the module. This is a relatively large app and I have a lot of pages/routes to add and therefore I need to load them dynamically to reduce the size of the initial page load.
So, following Webpack's documentation for dynamic imports, I tried the following which loads the controller module only when the relative route is called:
import { navigo } from "Navigo"; // router
const navigo = new Navigo();
navigo_router.on({
'/clients': () => {
import("Controllers/clients.js").then((clients) => {
clients.init();
});
}
});
But saving this in my editor resulted in a Babel transpiling error; SyntaxError: 'import' and 'export' may only appear at the top level, and clients.init() is not being called when tested in browser.
After a bit of reading, I discovered I needed a Babel plugin to transpile dynamic import() to require.ensure. So, I installed the plugin using the following command:
npm install babel-plugin-dynamic-import-webpack --save-dev
And declared the plugin in my babel.rc file
{ "plugins": ["dynamic-import-webpack"] }
After installing the plugin, the transpiling error disappeared and checking my transpiled code I found that the dynamic import()s has in fact been changed to require.ensure as expected. But now I get the following browser errors when testing:
Error: Loading chunk 0 failed.
Stack trace:
u#https://<mydomain.com>/js/app.bundle.js:1:871
SyntaxError: expected expression, got '<' 0.app.bundle.js:1
Error: Loading chunk 0 failed.
I didn't understand why it was referencing 0.app.bundle.js with the 0. prefix, so I checked my output/dist folder and I now have a new file in there called 0.app.bundle.js:
0.app.bundle.js 1,962bytes
app.bundle.js 110,656bytes
I imagine this new bundled file is the dynamically imported module, clients.js.
I only added dynamic importing to that one route and have left all the other routes as they were. So, during testing, I can view all routes except that one /clients route that now throws the above errors.
I'm totally lost at this point and hoped somebody could help push me over the finish line. What is this new file 0.app.bundle.js and how am I supposed to be using it/including it in my application?
I hope I've explained myself clearly enough and look forward to any responses.
I managed to fix my own problem in the end, so I will share what I discovered in an answer.
The reason the chunk file wasn't loading was because Webpack was looking in the wrong directory for it. I noticed in the Network tab of my developer console that the the chunk file/module was being called from my root directory / and not in /js directory where it belongs.
As per Webpack's documentation, I added the following to my Webpack config file:
output: {
path: path.resolve(__dirname, 'dist/js'),
publicPath: "/js/", //<---------------- added this
filename: 'app.bundle.js'
},
From what I understand, path is for Webpack's static modules and publicPath is for dynamic modules.
This made the chunk load correctly but I also had further issues to deal with, as client.init() wasn't being called and yielded the following error:
TypeError: e.init is not a function
To fix this, I also had to change:
import("Controllers/clients.js").then((clients) => {
clients.init();
});
To:
import("Controllers/clients.js").then(({clients}) => {
clients.init();
});
Note the curly braces in the arrow function parameter.
I hope this helps somebody else.
For debugging, you need to do
import("Controllers/clients.js").then((clients) => {
console.log(clients);
});
maybe working
import("Controllers/clients.js").then((clients) => {
clients.default.init();
});
I'm developing a custom package. Its package.js is :
Package.describe({
name: 'adigiovanni:one-way-accounts',
version: '0.0.1',
summary: 'One Way Accounts',
git: '',
documentation: 'README.md',
});
Package.onUse(function (api) {
api.versionsFrom('1.2.0.2');
api.use('ecmascript');
api.use('mongo');
// api.imply('mongo');
api.addFiles([
'lib/collections/Accounts.js',
'lib/methods.js',
'lib/OneWayAccounts.js',
]);
api.export('OneWayAccounts');
});
Package.onTest(function (api) {
api.use([
'ecmascript',
'sanjo:jasmine#0.20.2',
'velocity:html-reporter',
]);
api.use('adigiovanni:one-way-accounts');
api.addFiles('tests/client/OneWayAccounts.js', 'client');
api.addFiles('tests/server/OneWayAccounts.js', 'server');
});
As you can see, package makes use of 'mongo'.
Tests fail with :
Reference error: Mongo is not defined
But if I uncomment the line api.imply('mongo') then tests succeed.
Same odd behavior applies to ecmascript dependency, if I don't api.use('ecmascript') in Package.onTest, tests fail.
Meteor version is 1.2.0.2.
Test runner is velocity.
Test framework is jasmine.
I am using Mongo and ES6 syntax and features in my tests.
What is happening and how can I fix it?
Using a package with api.use('other-package') in Package.onUse does not make 'other-package' available in your test codes in the same way it doesn't make it available for other packages that use('my-package') or in applications that meteor add my-package. To solve this issue there is two solutions depending on the need for other-package :
Allowing users of the package (including your tests) to access 'other-package' with api.imply
Package.onUse(function (api) {
//...
api.imply('other-package')
//...
})
This makes sense if and only if the package you imply is necessary to use your own package. Do not imply everything willy-nilly for scope convenience. See more in this question.
If it does not fall into that category,
Simply use the package in your tests
Package.onTest(function (api) {
//...
api.use('my-package')
api.use('other-package')
//...
})
This will allow you to use other-package in your tests too, without polluting scopes.
I'm pretty new to Meteor and am getting this error:
=> Errors prevented startup:
While building the application:
lib/packages/iron-router/examples/hooks/hooks.js:30:58: Unexpected token ;
=> Your application has errors. Waiting for file change.
It cropped up with the addition of Iron-Router to my app. The error refers to this file, specifically the line that begins ready: promiseToReady:
this.route('adminPage', {
path: '/admin',
// 10. 3rd party API calls that are similar to waitOns:
waitOn: function() {
return {
// this is made up but I do have code conceptually similar to this
ready: promiseToReady(GoogleApi.call('/foo/bar'));
}
}
});
In Javascript you terminate object params with ,, not with ;.
Incorrect:
return {
ready: promiseToReady(GoogleApi.call('/foo/bar'));
};
Correct:
return {
ready: promiseToReady(GoogleApi.call('/foo/bar')),
};
I also ran into the same problem. Just remove iron-router using mrt/meteor remove iron-router and remove all the directories related to iron-router from packages folder.
Check the directory path from where you are running the mrt/meteor add command...Make sure that you run the command from your project root directory. Running it from project root directory fixed my issue..Earlier I ran the command from one of the sub folder of the project.
It seems the package had installed incorrectly. I made sure to fully delete it and reinstall:
> mrt remove iron-router
> meteor remove iron-router
> mrt add iron-router
> meteor add iron-router
This is currently possible:
ember build --environment=production
... and I would like to do something like this instead:
ember build --environment=production --baseurl=foo
but config/environment.js only gets passed in the value of environment.
Is it possible to get the value of the other options passed in at the command line too?
You could set environment variables the old fashioned way (export WHATEVER=wee) from terminal or as part of a build script, then reference them in your Brocfile.js via node with process.env.WHATEVER. After that, it would be a matter of having broccoli do whatever it is you needed to do with them. You could pre-process files and replace strings, for example.
... just a suggestion. Not sure if that's what you're looking for or not.
It appears that this is not allowed:
Looking in node_modules/ember-cli/lib/commands/build.js, we see:
availableOptions: [
{ name: 'environment', type: String, default: 'development' },
{ name: 'output-path', type: path, default: 'dist/' }
],
... and in node_modules/ember-cli/lib/models/command.js
this.availableOptions.forEach(function(option) {
knownOpts[option.name] = option.type;
});
... which together mean that any options that are not defined, for each subcommand of ember, get discarded.
You can do foo=bar ember build (however doing ember build foo=bar doesn't work)
And the argument is available via process.env.foo.
To extend upon #ben's answer.
The raw command line arguments are available inside ember-cli-build.js and other files from the
process.argv.[]
So a command like this
ember build staging
you can access via:
process.argv.includes('staging')
see node's documentation for whats available.
https://nodejs.org/docs/latest/api/process.html
I cannot seem to access a global variable in Ext.application after I do a production or test build with Cmd 4. This happens during the first application launch. I have read other similar threads but there is nothing new in there that can solve my problem for whatever reason.
Before I started using Cmd, I would run my application from a server against the application directory, and things ran just fine. I had no problems with my other files picking up the global variables.
Now that I have moved to Cmd 4 / ST2.3.1, the test and production builds get built into one big app.js file. So it seems that when code that is earlier in the js file calls a global variable, it cannot find it, with the console exception:
Uncaught TypeError: Cannot read property 'targetServer' of undefined
This happens during the first application launch, and the app just hangs. The loading indicators are not even removed. I noticed that the Ext.application code is at the end of the app.js. Could it be some code is launching before the application is fully loaded?
In my app.js, I have the following. This is last in my app.js at line 76623. The global variable not being read is "targetServer":
Ext.application({
name: 'qxtapp',
targetServer: 'http://192.168.1.70:8080'
...
});
One of my stores looks like this. This is where I get the exception. The below code is earlier in my app.js, at line 70742:
Ext.define('qxtapp.store.AccountsListStore', {
extend : Ext.data.Store ,
xtype : 'accountsListStore',
config: {
model: 'qxtapp.model.AccountsList',
data: [
{ accountName: qxtapp.app.targetServer+'/account_one' },
// ^ Causes exception- cannot read property "targetServer"
// of undefined
{ accountName: qxtapp.app.targetServer+'/account_two' },
...
]
}
})
Any idea what I'm missing here? Any help is greatly appreciated.
Thanks!
This is an order-of-operations error.
In development, your Ext.application() code (in app.js) is run first because any other classes (e.g. qxtapp.store.AccountsListStore) are loaded dynamically after the browser physically reads app.js.
But when you use Cmd to bundle your classes together, the resulting single JS file is read entirely at once by the browser. What happens is that the Ext.define() methods all run BEFORE Ext.application()... so qxtapp.app isn't yet assigned.
The easiest way to circumvent this problem is to use a true global variable, not just a property assigned to the global "app" object (in app.js):
var TARGET_SERVER = 'http://192.168.1.70:8080';
Ext.application({
//...
})
And in your other classes...
Ext.define('qxtapp.store.AccountsListStore', {
extend : Ext.data.Store ,
xtype : 'accountsListStore',
config: {
model: 'qxtapp.model.AccountsList',
data: [
{ accountName: TARGET_SERVER + '/account_one' }
//...
]
}
});