So at work we bought a theme that I have to customize and use it for an internal project. They use pug to generate the final files from some templates. In the .pug templates I can insert, of course, some external javascript files.
An empty js example for a file from them looks like this:
(function(window, document, $) {
'use strict';
$(window).on('load', function () {
})
})(window, document, jQuery);
(jQuery and bootstrap are automatically included in the templates).
I sucessfully created my javascript files and used them to work on the project using only pure js and some jQuery (however I kind of missed angularjs, but I wasn't able to use .pug and angular in the same time).
I came to the point where I had to make some multiple GET request to a server and I need them to be asynchronous. In my previous projects I successfully used the async module and I wanted to use that here too. Surprise, surprise, I can not import the module.
I tried with a simple line like import * as async from '../libs/async/async.js'; but if I want to use a simple async.series, it says: Uncaught TypeError: async.series is not a function. I tried in every way possible. The errors were varying from the one above to "Unrecognized token *". And yes, I declared the .js file as type="module".
I tried to simulate the problem in by myself and I created the following files structure (it resembles the one in the theme).
.
├── assets
│ └── js
│ ├── dashboards
│ │ └── dash.js
│ └── libs
│ └── async.js
└── src
└── index.html
In the .html file, I only include jQuery and the dash.js file. The dash.js file contains the following code:
'use strict';
import * as async from '../libs/async.js';
$(window).on('load', function () {
function init() {
console.log("Hello there!");
async.series([
function(callback) { console.log(5); callback(); },
function(callback) { console.log(6); callback(); }
]);
}
init();
});
But...errors again.
Uncaught ReferenceError: async is not defined
at init (dash.js:8)
How on earth can I import the async module (or any other module) and use it correctly?
UPDATE:
In the .html file, If I include the line
<script src="https://cdnjs.cloudflare.com/ajax/libs/async/2.6.1/async.js"></script>
it seems it works, but I really want to know if this can be done without it.
In the .pug file, changing the order of the .js files helped. I put async.js
script(src=assets_path + '/js/libs/async.min.js', type='text/javascript')
as the first line for the custom js files and it worked. No other imports were needed.
Related
Recently, I've supporting a Laravel project that uses Laravel Mix as front-end asset bundler (CSS and JS). I've noticed that, in main JS file, the developer was used some require() rules at start, and those require() rules was referring to other JS files located in same directory, and those JS files are "normal" browser-friendly JS script (like jQuery, Bootstrap, GSAP etc). After those require() rules, the developer used normal import rules from ES6, referring to NodeJS dependencies and other modules within the project.
To better explain what I'm trying to show...
File tree:
.
└── src
├── bootstrap.js
├── components
│ ├── AnotherFeature.js
│ ├── Carousel.js
│ └── Something.js
├── jquery.js
└── main.js
The main.js file...
require("jquery");
require("bootstrap");
import {AnotherFeature} from "./components/AnotherFeature";
import {Carousel} from "./components/Carousel";
import {Something} from "./components/Something";
class Main {
constructor() {
new AnotherFeature();
new Carousel();
new Something();
}
}
new Main();
And, even using Laravel Mix (that uses Webpack), the jQuery, $ and bootstrap are available to run through browser`s console.
My question is: how can I do something like this outside Laravel Mix? I'm working on a project that uses JS, and some third-party resources aren`t ES6 modules, and I can't find a beautiful solution to use them in my project. The way Laravel Mix "import" extra JS files is a good way to fix my issues, keeping simple and all thing in one bundled JS file.
You can find my project at my GitHub. It consists in a Wordpress starter template that people want to use at my job, and this method of "requiring" and "importing" things will be very helpful.
Thanks!
A brief introduction
First of all, I know, the question is often asked and I have read many discussion before asking.
Tipical approches to handle the cache problem that are suggested in other question are:
Versioning js files in this way: <script src="/myJavascript.js?version=4"></script>. See Keparo's answer here.
Create a rewrite rule on the web server in this way: RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]. See Kip's answer here.
Context description
Currently I'm refactoring my old php code running on apache2, introducing an heavy layer of js/jquery to handle the frontend and decoupling FE and BE building some api.
The js project is organized in this way:
my-project/
├── main.js
├── main.css
├── main.php
├── js/
│ ├── JsClass1.js
│ └── JsClass2.js
└── src/
└── php_code/
├── class1.php
└── class2.php
My problem
I don't know how to avoid that js classes are cached from web browser because the previous solution describer in the description are not feasible.
Just to make a brief example of my problem this is a portion of code that gives me problems:
main.js
//Import my js classes
import JsClass1 from './js/JsClass1.js';
import JsClass2 from './js/JsClass2.js';
//Instantiate js objects
const j1 = new JsClass1();
const j2 = new JsClass2();
//Use js object
j2.do_stuff();
j2.do_stuff();
JsClass1.js
import JsClassN from "./JsClassN.js";
export default class JsClass1 {
constructor(var1) {
//Create stuff
}
do_stuff() {
//Doing real stuff
}
}
main.php
<!DOCTYPE html>
<html lang='it-IT'>
<head>
<script type='module' src='/main.js?ver=<? echo getVersion(); ?>' type='text/javascript'></script>
</head>
<body>
...
</body>
Wrap up
How can I handle those js imports and avoid that browser caches old classes when I release in production new versions?
Do I need to reorganize my code structure to achieve this? And if so, what's the best approach?
How to configure eslintrc for a project like in this example?
user
└── projectA
├── index.html
└── lib
├── .eslintrc
├── main.js
└── main_lib.js
html file includes both of the js files. How .eslintrc is needed to configure to overcome function is not defined and defined but not used errors properly?
The rules concerned by your question are:
for defined but not used it's the no-unused-vars rule
for function is not defined, if you're trying to use a global method declared from an external script, then you can declare it as part of the global config in .eslintrc, like "globals": { "yourFunctionName": "readonly" }, to make eslint aware that this function was not declared by you but still it's available from somewhere.
I'm writing an Electron application using ES6 and ChartJS. I have babel fully set up and works fine. So far the only way I have gotten ChartJS to work is by sticking the (CDN) javascript into my head tag. When I try to import it the ES6 way (not sure how to do so properly), or simply using var Chart = require('chartjs') I get the error that Chart is not defined.
I installed ChartJS with
npm install chartjs --save
I have tried various ways of loading the javascript but they all end up blank. Only when I uncomment the CDN it works.
How do I import and use ChartJS in an electron app?
My structure
root_folder/
├── lib/
│ ├── app.js
│ ├── plot.js
│ ├── channel.js
├── src/
│ ├── app.js
│ ├── plot.js
│ ├── channel.js
├── index.html
└── main.js
At the bottom of my index.html I simply call app.js which is the entry point for the renderer.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js"></script>
</head>
<body>
...
</body>
<script>
require('./lib/app.js')
</script>
</html>
Edit 1:
I've run some tests and even the most simple var Chart =
require('chartjs'); in a clean file automatically instantiates the chart as a BarChart. From what I've been reading is that it has to be imported globally somehow which is what happens when it's inside a <script> tag in the head.
chart.js package is loaded from CDN in head script.
chartjs package is loaded in require.
They are different packages with different names.
Generally this should be done with
var Chart = require('chart.js');
in the place where a module should be used.
If the application is transpiled with Babel, ES module imports are translated to require in transpiled code, and require can be replaced with equivalent import statement:
import * as Chart from 'chart.js';
var chart = require(chartjs) I get the error that Chart is not
defined.
There's a few things here that need fixing. First you mentioned you were using ECMAScript 6 so change var to const. Also change require to ES6's import, and import the default package from chart js (looks like it may be just chart.
The other thing was already mentioned, you should be putting chartjs in quotes: "chartjs".
In summation, it should look something like,
import chart from 'chartjs`
After you run this line, try logging chart and you should see the chart API
I am trying to write some JS modules which should be usable in both a browser and node.js environment.
To facilitate this I'm writing these modules as AMD modules, and using requirejs in the Node.js environment to provide the define() function as well as the ability to load these modules.
However, I've encountered some behavior I do not understand.
I made a SSCCE that illustrates the problem:
├── bar.js
├── node_modules
│ └── foo
│ ├── foo.js
│ ├── index.js
│ ├── node_modules
│ │ └── requirejs
│ │ ├── bin
│ │ │ └── r.js
│ │ ├── package.json
│ │ ├── README.md
│ │ └── require.js
│ └── package.json
└── test.js
foo is a node module which wraps the AMD module foo.js
node_modules/foo/foo.js
define([], function() {
return function() {
console.log("This is foo!");
};
});
node_modules/foo/index.js
var requirejs = require('requirejs');
requirejs.config({
baseUrl: __dirname,
nodeRequire: require
});
module.exports = requirejs('foo');
bar.js is another AMD module:
define([], function() {
return function() {
console.log("This is bar!");
};
});
test.js is a script that wants to use both foo and bar:
var requirejs=require('requirejs');
requirejs.config(
{
baseUrl: __dirname,
nodeRequire: require
}
);
var foo=require("foo");
foo();
var bar=requirejs("bar");
console.log("bar is:",bar);
If I run test.js, I get this:
This is foo!
bar is: undefined
/home/harmic/tmp/test_rjs/node_modules/foo/node_modules/requirejs/bin/r.js:393
throw err;
^
Error: Mismatched anonymous define() module: function () {
return function() {
console.log("This is bar!");
};
}
http://requirejs.org/docs/errors.html#mismatch
at makeError (/home/harmic/tmp/test_rjs/node_modules/foo/node_modules/requirejs/bin/r.js:418:17)
at intakeDefines (/home/harmic/tmp/test_rjs/node_modules/foo/node_modules/requirejs/bin/r.js:1501:36)
at null._onTimeout (/home/harmic/tmp/test_rjs/node_modules/foo/node_modules/requirejs/bin/r.js:1699:25)
at Timer.listOnTimeout (timers.js:119:15)
There are two things that don't make sense to me here.
In test.js, the call to requirejs("bar") returns undef. In fact it appears
that the loading of the module is deferred, as if there were some circular
dependancy going on, because it is only after it has returned that the
module definition for bar is being executed.
Why does it think this is an anonymous define() ? My use case does not appear
to meet the criteria in the given URL.
To supress the second issue I tried naming the define in bar.js, like this:
define('bar', [], function() {
...
That works in the sense that the exception is gone, but requirejs("bar") still
returns undef.
The example is a simplified version of what I am trying to do - basically I will
have a number of modules, which will contain some common components that can be
used in browser and in node, and some node specific components that will only
be used in node. There will be dependancies between the modules.
If there is some better way of doing this then I'm open to that also.
I've recreated locally the code and hierarchy you show in the question but I'm not able to get the exact error message you report getting. I'll say that looking at what you show in the question, I do not get how your code could result in that error message. When I run your code, the error I do get is:
This is foo!
/tmp/t1/node_modules/requirejs/bin/r.js:2604
throw err;
^
Error: Tried loading "bar" at /tmp/t1/node_modules/foo/bar.js then tried node's require("bar") and it failed with error: Error: Cannot find module 'bar'
at /tmp/t1/node_modules/requirejs/bin/r.js:2597:27
Which is exactly what I would expect: RequireJS running in node will try to use Node's own require if it cannot find a module through its own machinery.
Now, the problem you ran into is not a problem with running RequireJS in Node but a problem with how you use RequireJS. You could run into the exact same issue in a browser. The problem is that you are running requirejs.config twice, without using contexts so one config overwrites the other. (RequireJS is able to merge configs but the configs you are using are such that they'll overwrite one another.) I can get the code to run by changing test.js so that:
The configuration of RequireJS uses a context value.
I save the return value of requirejs.config and use this to require modules.
The end result is:
var requirejs=require('requirejs');
var r = requirejs.config(
{
context: "me",
baseUrl: __dirname,
nodeRequire: require
}
);
var foo=require("foo");
foo();
var bar= r("bar");
console.log("bar is:",bar);
Ideally, the code in index.js should also use a context.
This being said, I've written code that runs in Node and the browser as AMD modules for years. It is rare that I need to load AMD modules with RequireJS in Node. One rare case where I'd want to do it is if I'm testing how a module gets its configuration through module.config. I've never ever found the need to use amdefine. What I do use when I want to load AMD modules in Node is amd-loader (aka node-amd-loader). It just hooks into Node's module loading machinery to make it able to load AMD modules. I guess the downside of amd-loader is that projects that want to use your code then have to depend on having a loader installed like amd-loader.
In the end I did not get any satisfactory answer to this problem.
I have worked around it by using amdefine when using these modules in the node.js environment, instead of trying to use requirejs.
The main drawback to this approach is that you have to add some boilerplate to the front of all the AMD modules so that they will load amdefine if needed, but other than that, amdefine is working for my use case at least.
I also found the UMD project, which provides some other alternatives.