I'm working on a browser app and am using the below method to import modules as needed. Unfortunately any modules imported at the 'second level' or deeper treat the asterisk '*' as an unexpected token. It's been a few years since my last JavaScript work and am unsure what I'm doing wrong as much has changed and is new to me.
Below is a minimum example that encounters the issue. The structure of each module is to emulate C# classes with public, private, and static objects and almost entirely remove the need to type 'this.' over and over and over again as is the .js way of doing things.
Content.js
//Directly attached to the page using <script type="module" src="Content.js"></script>
import * as XRVideo from "../Viewer/Scripts/XRVideo.js"; //This works perfectly
var urlA = "Testing/VideoA.mp4";
var urlB = "Testing/VideoB.mp4";
var arrXRVideos = [];
window.addEventListener("load", (e) =>{
arrXRVideos.Push(XRVideo.New(urlA));
arrXRVideos.Push(XRVideo.New(urlB));
});
XRVideo.js
import * as XRScene from "./Core/XRScene.js"; //This throws an Unexpected Token '*' Error
import * as XRSkybox from "./Core/XRSkybox.js";
/* Static */
export const XRStatus = {
Stopped : 0,
};
/* Instanced */
export function New(videoURL){
var _isInitialized = false;
var _url = "";
//#region Pseudo Constructor
{
_url = videoUrl;
_isInitialized = true;
}
//#endregion
/* Getters & Setters */
function GetURL(){
return _url;
}
/* Exposed As Public */
//Properties can't be directly exposed otherwise they become static...
return{
GetURL : GetURL
}
}
It turned out the issue was due to a broken line comment throwing off the system. Always look at the previous line for 'Unexpected' errors...
I didn't notice this due to how VS Code's Default theme, visually styles code comments and hyperlinks the same. Specifically both lines look like the same green text despite one being a comment and the other not.
//This is a comment, VS Code shows it as a dirty green.
//I am a code comment
//This is a URL that accidentally didn't get the '//' to turn it into a comment,
//VS Code shows it as a dirty green making it look like a comment.
https://github.com/blah-blah-blah
import * as Bah from "./Foo.js"; //Unexpected token '*'
Related
I am working on supporting a REST API that literally has thousands of functions/objects/stats/etc., and placing all those calls into one file does not strike me as very maintainable. What I want to do is have a 'base' file that has the main constructor function, a few utility and very common functions, and then files for each section of API calls.
The Problem: How do you attach functions from other files to the 'base' Object so that referencing the main object allows for access from the subsections you have added to your program??
Let me try and illustrate what I am looking to do:
1) 'base' file has the main constructor:
var IPAddr = "";
var Token = "";
exports.Main = function(opts) {
IPAddr = opts.IPAddr;
Token = opts.Token;
}
2) 'file1' has some subfunctions that I want to define:
Main.prototype.Function1 = function(callback) {
// stuff done here
callback(error, data);
}
Main.prototype.Function2 = function(callback) {
// stuff done here
callback(error,data);
}
3) Program file brings it all together:
var Main = require('main.js');
var Main?!? = require('file1.js');
Main.Function1(function(err,out) {
if(err) {
// error stuff here
}
// main stuff here
}
Is there a way to combine an Object from multiple code files?? A 120,000 line Node.JS file just doesn't seem to be the way to go to me....not to mention it takes too long to load! Thanks :)
SOLUTION: For those who may stumble upon this in the future... I took the source code for Object.assign and back ported it to my v0.12 version of Node and got it working.
I used the code from here: https://github.com/sindresorhus/object-assign/blob/master/index.js and put it in a separate file that I just require('./object-assign.js') without assigning it to a var. Then my code looks something like this:
require('./object-assign.js');
var Main = require('./Main.js');
Object.assign(Main.prototype, require('./file1.js'));
Object.assign(Main.prototype, require('./file2.js'));
And all my functions from the two files show up under the Main() Object...too cool :)
At first each file works in its own scope, so all local variables are not shared.
However, as hacky approach, you may just add Main into global scope available everywhere by writing global.Main = Main right after you define it, please make sure that you require main file first in list of requires.
The better(who said?) approach is to extend prototype of Main later, but in this case you may need to update a lot of code. Just mix-in additional functionality into base class
file1.js
module.exports = {
x: function() {/*****/}
}
index.js
var Main = require('main.js');
Object.assign(Main.prototype, require('file1.js'));
Shure.
constructor.js
module.exports = function(){
//whatever
};
prototype.js
module.exports = {
someMethod(){ return "test";}
};
main.js
const Main = require("./constructor.js");
Object.assign( Main.prototype, require("./prototype.js"));
I am pretty new to Javascript and I've across an issue I can't seem to understand. The console is complaining saying it is not recognising the function.
I have the following code (GraphActions.js):
var VentureDispatcher = require('../dispatcher/VentureDispatcher');
var GraphStoreConstants = require('../constants/GraphStoreConstants');
var StockService = require('../utils/StockService');
var GraphActions = {};
GraphActions.getGraphData = function(request){
//have the API call here.
VentureDispatcher.handleViewAction({
actionType: GraphStoreConstants.GET_GRAPH_DATA,
data: request
});
StockService.getHistoricStockData(request);
};
GraphActions.recieveGraphData = function(response){
VentureDispatcher.handleServerAction({
actionType: GraphStoreConstants.GRAPH_DATA_RESPONSE,
response: response
});
};
GraphActions.test = function(){
console.debug("found this.");
};
module.exports = GraphActions;
And the following Javascript file which is calling the function within the code above:
var request = require('superagent');
var GraphActions = require('../actions/GraphActions');
var StockService = {
getHistoricStockData: function(symbol){
request.post('http://localhost:8080/historicalData')
.send({
"symbol":"GOOG",
"from":"2016-06-01",
"to":"2016-07-01"
})
.set('Content-Type','application/json')
.end(function(err,res){
if(err || !res.ok){
console.error('Error making request!');
}else {
console.debug(JSON.stringify(res.body));
GraphActions.recieveGraphData(res.body);
}
});
}
};
module.exports = StockService;
The console is throwing the following error and not too sure why:
venture-stock.js:44673 Uncaught TypeError: GraphActions.recieveGraphData is not a function.
Does anyone understand why this is happening? The same method has been called in another place with no problem.
When debugging the code and evaluating GraphAction object in the above code I get the following where the functions defined above are not available:
Where as in another location, the functions are available:
Any help would be appreciated!
It happens because of a circular reference among your modules. The module GraphActions requires the module StockService, which also requires GraphActions. It doesn't matter if what's actually needed in StockService it's the recieveGraphData method, while the method that requires StockService is getGraphData: the module loader doesn't have this level of code analysis. require would always return the whole module, so it's a circular reference.
So, what happens in these cases?
The module loader, when loading GraphActions, meets require('../utils/StockService'), then stops the execution of GraphActions in order to load StockService. At this point, the exported properties of GraphActions are... none. So that's why in StockService you get an empty object.
Solution A
Merge the two modules in one, e.g.
var GraphService = {
getGraphData: function(request) {
...
GraphService.getHistoricStockData(request);
},
recieveGraphData: function(response) { ... },
getHistoricStockData: function(symbol) {
...
GraphService.recieveGraphData(res.body);
}
};
module.exports = GraphService;
Solution B
It's the opposite, i.e. decouple getGraphData and recieveGraphData in two different modules. I don't really like this one, because it can lead to excessive module fragmentation.
Solution C (recommended)
As long as CommonJS is used, you can take advantage of using require wherever you want, so in StockService.js you'd do:
getHistoricStockData: function(symbol) {
request.post('http://localhost:8080/historicalData')
...
.end(function(err, res) {
...
var GraphActions = require('../actions/GraphActions');
GraphActions.recieveGraphData(res.body);
});
}
Now, since the execution will not immediately require GraphActions, the module will be loaded later when all its dependencies are fully loaded, so you'll get a fully working module.
Differences with ES2015 modules
In ES6, you cannot use import outside the root level, i.e. you can't use import inside a function. So you couldn't use solution C with ES6 syntax?
Actually that wouldn't be necessary to begin with, because CommonJS module resolution is different than ES6's, the latter being more advanced. So, you'd have written:
import * as StockService from '../utils/StockService';
...
export function getGraphData(request) {
...
StockService.getHistoricStockData(request);
};
export function recieveGraphData(response) { ... };
export function test() { ... };
and in StockService.js:
import * as GraphActions from '../actions/GraphActions';
...
export function getHistoricStockData(symbol) {
request.post('http://localhost:8080/historicalData')
.send( ... )
.set('Content-Type','application/json')
.end(function(err, res) {
...
GraphActions.recieveGraphData(res.body);
});
};
So what happens here? GraphActions.js is loaded, the import statement is reached, the execution of the module stops and then StockService is loaded. So far, it's the same as CommonJS.
In StockService, the loader would meet the import statement, then the execution of GraphActions resumes and the whole module is correctly exported. So you'd be golden from the start with ES6.
This will lead us to
Solution D (most recommended... if you can)
Since you're already using Babel, use the plugin "transform-es2015-modules-commonjs" or "preset-es2015" and use ES6 modules instead.
Hope this cleared your doubts!
(Btw, it's "receive", not "recieve" ;)
I am trying to create a typescript definition file for an existing javaScript library, but keep getting run time errors.
The library contains the following code:
/**
* Pdok namespace, will hold Api namespace
* #namespace
*/
Pdok = {};
Pdok.Api = function(config) {
/* implementation details are here... */
}
The javascript example code that was supplied works and contains the following line:
var api = new Pdok.Api(config);
I created a typescript definition file, and am able to compile without any problems. The definition looks like:
declare namespace Pdok
{
interface configOptions {
/* .. details .. */
}
class Api {
constructor(config: configOptions);
}
}
At run time, i get the following error:
Uncaught TypeError: Pdok.Api is not a constructor
What should be in my .d.ts-file to get this working?
Based on this answer https://stackoverflow.com/a/15008808/4516689 it could be that the source isn't loaded correctly.
Set a debugger; directive before your code and check to what the variable Pdok.Api is set to.
debugger;
var api = new Pdok.Api(config);
or use this
alert(Pdok.Api);
var api = new Pdok.Api(config);
it should return "function (config) { ..."
This typescript:
export enum UID {
FACTORY,
ROBOT
}
compiles to this javascript:
(function (UID) {
UID._map = [];
UID._map[0] = "FACTORY";
UID.FACTORY = 0;
UID._map[1] = "ROBOT";
UID.ROBOT = 1;
})(exports.UID || (exports.UID = {}));
var UID = exports.UID;
I have to admit that the code seems rather obscure to me but I trusted the tsc compiler to know what it's doing. Unfortunately the javascript can't be executed. nodejs complains that:
(function (UID) {
^ TypeError: object is not a function
at ...
What have I done wrong ?
UPDATE:
Matt B. has solved the problem. This is a known bug in the typescript compiler. tsc fails to insert semicolons after require statements, this can lead to strange errors. Manually adding the semicolons to my code solved the problem. Here's the link to the codeplex issue:
http://typescript.codeplex.com/workitem/364
UPDATE 2:
for those of you that experience the same error. You can insert the missing semicolons manually but that is not a very comfortable solution since you have to do this after every compilation. I noted that the problem only occurs with the enum. There are lots of other modules in the project and none of them have caused this error. Apparently a class definition is not "harmed" by the missing semicolons in front of it. Just move the definition of the enum behind one of your class definitions and the error should disappear. It's not sufficient to move the enum behind an interface since interfaces have no direct equivalent and are just deleted by the compiler
I think this is the same issue as described here -- which turned out to be due to a missing semicolon after a require() statement:
Typescript generating javascript that doesn't work
Is there a line like this in another compiled file?
var UID = require('UID')
If so, try adding a semicolon at the end:
var UID = require('UID');
This appears to be a TypeScript bug; here's the bug report (vote it up!): http://typescript.codeplex.com/workitem/364
In your Javascript you should have something like
var exports = exports || {};
Before
(function (UID) {
UID._map = [];
UID._map[0] = "FACTORY";
UID.FACTORY = 0;
UID._map[1] = "ROBOT";
UID.ROBOT = 1;
})(exports.UID || (exports.UID = {}));
var UID = exports.UID;
I tried and I have the same problem - I assume that the missing exports variables should be generated by the JavaScript code used to load the module when you do
import XXXX = module("XXXX");
that generates this JavaScript:
var XXXX = require("./XXXX")
and I think Matt B. is right and the problem there is the missing semi-colon after require(), that messes things up afterwards.
A fix is to place the enumeration declaration in a module:
module Test {
export enum UID {
FACTORY,
ROBOT
}
}
that generates:
var test;
(function (test) {
(function (UID) {
UID._map = [];
UID._map[0] = "FACTORY";
UID.FACTORY = 0;
UID._map[1] = "ROBOT";
UID.ROBOT = 1;
})(test.UID || (test.UID = {}));
var UID = test.UID;
})(test || (test = {}));
in this way the exports variable is no longer needed.
I've been banging my head a against this particular brick wall now for more than two days. I am attempting to create an XPCOM service for use in a Firefox extension but am unable to initialise the component with the following error displayed in the error console in Firefox.
Timestamp: 07/06/2012 09:23:28 Error: uncaught exception: [Exception...
"Component returned failure code: 0x80570016 (NS_ERROR_XPC_GS_RETURNED_FAILURE)
[nsIJSCID.getService]" nsresult: "0x80570016 (NS_ERROR_XPC_GS_RETURNED_FAILURE)"
location: "JS frame :: chrome://logger/content/logger.js :: <TOP_LEVEL> :: line 21"
data: no]
I have reduced the component to the bare minimum using the excellent boilerplate generator at ted.mielczarek.org. The component code is as follows...
const nsISupports = Components.interfaces.nsISupports;
const CLASS_ID = Components.ID("808e1607-caea-418c-b563-d9fe1df6ee08");
const CLASS_NAME = "Test component";
const CONTRACT_ID = "#test/loggerservice;1";
function LoggerService() {
this.wrappedJSObject = this;
}
LoggerService.prototype = {
QueryInterface: function(aIID)
{
if (!aIID.equals(nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
}
The remainder of the boilerplate that creates the module and factory interfaces is unchanged.
The chrome.manifest file looks like this...
content logger chrome/content/
skin logger classic/1.0 chrome/skin/
locale logger en-US chrome/locale/en-US/
component {808e1607-caea-418c-b563-d9fe1df6ee08} components/loggerservice.js
contract #test/loggerservice;1 {808e1607-caea-418c-b563-d9fe1df6ee08}
overlay chrome://browser/content/browser.xul chrome://logger/content/logger-overlay.xul
style chrome://global/content/customizeToolbar.xul chrome://logger/skin/overlay.css
Finally, the logger-overlay.xul file includes a script file - logger.js - which attempts to get a reference to the LoggerService component using the following code...
this.loggerService = Components.classes["#test/logger;1"].getService().wrappedJSObject;
and it is this line that is reporting in the firefox error console.
I can't see how much simpler I can make it - any insight would be very much appreciated.
This is a nice boilerplate generator but unfortunately an outdated one. For one, you should be using XPCOMUtils, this will get rid of most of the boilerplate. More importantly, this boilerplace generator hasn't been updated to XPCOM changes in Gecko 2.0 and defines NSGetModule function instead of NSGetFactory. Module code like this should work however:
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function LoggerService() {
this.wrappedJSObject = this;
}
LoggerService.prototype = {
classID: Components.ID("808e1607-caea-418c-b563-d9fe1df6ee08"),
classDescription: "Test component",
contractID: "#test/loggerservice;1",
QueryInterface: XPCOMUtils.generateQI([])
}
if ("generateNSGetFactory" in XPCOMUtils)
var NSGetFactory = XPCOMUtils.generateNSGetFactory([LoggerService]); // 2.0+
else
var NSGetModule = XPCOMUtils.generateNSGetModule([LoggerService]); // 1.9.x
You can remove the NSGetModule code if your extension doesn't need to be compatible with Firefox 3.6. You can also remove the classDescription and contractID properties then, these are specified in chrome.manifest already.
Note: If you only need an object that will stay around for the entire browsing session and can be accessed from anywhere then a JavaScript code module would be a better choice - no XPCOM boilerplate and no wrappedJSObject hacks.