Ruby on Rails 6 Webpack with Javascript libraries - javascript

I am building a new project in Rails 6. I have a front-end library I want to use (#tarekraafat/autocomplete.js) that is installed by yarn and exists in my node_modules directory, but is not being made available to other JS code in the browser. Here is what I have set up currently:
/package.json:
"dependencies": {
"#tarekraafat/autocomplete.js": "^8.2.1"
}
/app/javascript/packs/application.js:
import "#tarekraafat/autocomplete.js/dist/js/autoComplete.min.js"
/app/views/.../example.html.erb
<script type="text/javascript">
window.onload = () => {
new autoComplete({
[...]
});
};
</script>
I am getting an error in the browser pointing to the new autoComplete():
Uncaught ReferenceError: autoComplete is not defined
Some reading seems to indicate that I need to modify the /config/webpack/environment.js file, in which I have tried various versions of the following with no luck (including restarting the dev server):
/config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
autoComplete: 'autocomplete.js'
})
);
module.exports = environment
First, what do I need to do to add this library so it can be used correctly? Second, as someone who has not directly used webpack previously, what is the function of adding this definition to the environments.js file, and why don't I need to do it for some libraries (bootstrap, popper) but I do for others (jquery, and maybe autocomplete.js)?

In Webpacker, the usage of this library would be as follows:
// app/javascript/src/any_file.js
import autoComplete from "#tarekraafat/autocomplete"
new autoComplete(...)
// app/javascript/packs/application.js
import "../src/any_file"
This alone does not import the autoComplete variable into the global scope. To do that, the simplest thing is assign the variable to window from within your webpack dependency graph.
// app/javascript/src/any_file.js
import autoComplete from "#tarekraafat/autocomplete"
window.autoComplete = autoComplete
As an aside, you don't need to use the ProvidePlugin configuration for this library. The ProvidePlugin says: “add this import to all files in my dependency graph.” This might be helpful for something like jQuery to make legacy jQuery plugins work in webpack. It is not necessary to make your autocomplete lib work

Related

How can I implement a spell check with a suggestions fixes on my js project that have wyswyg editor? [duplicate]

I am writing an application with the Node.js, Express.js, and Jade combination.
I have file client.js, which is loaded on the client. In that file I have code that calls functions from other JavaScript files. My attempt was to use
var m = require('./messages');
in order to load the contents of messages.js (just like I do on the server side) and later on call functions from that file. However, require is not defined on the client side, and it throws an error of the form Uncaught ReferenceError: require is not defined.
These other JavaScript files are also loaded at runtime at the client, because I place the links at the header of the webpage. So the client knows all the functions that are exported from these other files.
How do I call these functions from these other JavaScript files (such as messages.js) in the main client.js file that opens the socket to the server?
This is because require() does not exist in the browser/client-side JavaScript.
Now you're going to have to make some choices about your client-side JavaScript script management.
You have three options:
Use the <script> tag.
Use a CommonJS implementation. It has synchronous dependencies like Node.js
Use an asynchronous module definition (AMD) implementation.
CommonJS client side-implementations include (most of them require a build step before you deploy):
Browserify - You can use most Node.js modules in the browser. This is my personal favorite.
Webpack - Does everything (bundles JavaScript code, CSS, etc.). It was made popular by the surge of React, but it is notorious for its difficult learning curve.
Rollup - a new contender. It leverages ES6 modules and includes tree-shaking abilities (removes unused code).
You can read more about my comparison of Browserify vs (deprecated) Component.
AMD implementations include:
RequireJS - Very popular amongst client-side JavaScript developers. It is not my taste because of its asynchronous nature.
Note, in your search for choosing which one to go with, you'll read about Bower. Bower is only for package dependencies and is unopinionated on module definitions like CommonJS and AMD.
I am coming from an Electron environment, where I need IPC communication between a renderer process and the main process. The renderer process sits in an HTML file between script tags and generates the same error.
The line
const {ipcRenderer} = require('electron')
throws the Uncaught ReferenceError: require is not defined
I was able to work around that by specifying Node.js integration as true when the browser window (where this HTML file is embedded) was originally created in the main process.
function createAddItemWindow() {
// Create a new window
addItemWindown = new BrowserWindow({
width: 300,
height: 200,
title: 'Add Item',
// The lines below solved the issue
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
})}
That solved the issue for me. The solution was proposed here.
ES6: In HTML, include the main JavaScript file using attribute type="module" (browser support):
<script type="module" src="script.js"></script>
And in the script.js file, include another file like this:
import { hello } from './module.js';
...
// alert(hello());
Inside the included file (module.js), you must export the function/class that you will import:
export function hello() {
return "Hello World";
}
A working example is here. More information is here.
Replace all require statements with import statements. Example:
// Before:
const Web3 = require('web3');
// After:
import Web3 from 'web3';
It worked for me.
In my case I used another solution.
As the project doesn't require CommonJS and it must have ES3 compatibility (modules not supported) all you need is just remove all export and import statements from your code, because your tsconfig doesn't contain
"module": "commonjs"
But use import and export statements in your referenced files
import { Utils } from "./utils"
export interface Actions {}
Final generated code will always have(at least for TypeScript 3.0) such lines
"use strict";
exports.__esModule = true;
var utils_1 = require("./utils");
....
utils_1.Utils.doSomething();
This worked for me
Get the latest release from the RequireJS download page
It is the file for RequestJS which is what we will use.
Load it into your HTML content like this:
<script data-main="your-script.js" src="require.js"></script>
Notes!
Use require(['moudle-name']) in your-script.js,
not require('moudle-name')
Use const {ipcRenderer} = require(['electron']),
not const {ipcRenderer} = require('electron')
Even using this won't work. I think the best solution is Browserify:
module.exports = {
func1: function () {
console.log("I am function 1");
},
func2: function () {
console.log("I am function 2");
}
};
-getFunc1.js-
var common = require('./common');
common.func1();
window = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
I confirm. We must add:
webPreferences: {
nodeIntegration: true
}
For example:
mainWindow = new BrowserWindow({webPreferences: {
nodeIntegration: true
}});
For me, the problem has been resolved with that.
People are asking what is the script tag method. Here it is:
<script src='./local.js'></script>.
Or from network:
<script src='https://mycdn.com/myscript.js'></script>
You need plugin the right url for your script.
I was trying to build metronic using webpack. In my package.json I had to remove the "type": "module" section.

Jquery plugins giving errors $(...).ionRangeSlider not a function

I am trying to use jquery plugins in my react application. Every time I try to use some jquery third party plugin, I get an error saying $(...).somePlugin not a function. Currently I am trying to use ionRangeSlider. It is giving me error
Uncaught TypeError: (0 , _jquery2.default)(...).ionRangeSlider is not a function
js file
import $ from 'jquery'
import 'bootstrap-tagsinput'
class AddTagSection extends Component {
componentDidMount=()=> {
this.slider = $(this.inputSlider).ionRangeSlider();
}
<div className="irs-wrapper">
<input type="text" ref={node=>this.inputSlider=node} className='input-slider' id="ionSlider-newTag" name="ionSlider"/>
</div>
Below is the function which is getting called in ionRangeSlider.js (plugin)
$.fn.ionRangeSlider = function (options) {
return this.each(function() {
if (!$.data(this, "ionRangeSlider")) {
$.data(this, "ionRangeSlider", new IonRangeSlider(this, options, plugin_count++));
}
});
};
As far as I have read on web, this is a multiple jquery clashes issue.
About my Application: My project has jquery installed via npm. So there is one jquery present in package.json. I have also included jquery in scripts in my index.jade file. So there is another one there.
This is not an issue of jquery placed after plugin's js file. I placed jquery at the top in the scripts of index.jade file.
block head_scripts
script(src='https://code.jquery.com/jquery-3.2.1.min.js')
script(src='/public/ion.rangeSlider.js')
link(href='/public/normalize.css' rel="stylesheet")
link(href='/public/ion.rangeSlider.css' rel="stylesheet")
link(href='/public/ion.rangeSlider.skinFlat.css' rel='stylesheet')
I also tried noConfilct. But that too didn't work.
var $ = jQuery.noConflict();
$( ".slider" ).ionRangeSlider();
I tried including jquery in my webpack config.js file
new webpack.ProvidePlugin({
React: 'react',
$: 'jquery',
jQuery: 'jquery'
}),
Nothing from the above worked. Everytime I got the same error.
How can I solve this?
If your project contains a different version of jQuery than the one specified in ionRangeSlider package.json dependencies and you're using yarn to install your project's dependencies and webpack to bundle the project, then this might be the source of the problem. Yarn will install node_modules even in ion-range-slider folder, which will cause that the webpack will build the project with your jQuery, as well as with the one in ion-range-slider/node_modules. Hence the jQuery versions clash. ionRangeSlider initiates with the first jQuery and your project's jQuery won't contain it.
It's kind of a fault of ionRangeSlider developer because he should not specify the jQuery in dependencies, but instead in peerDependencies.
try changing internal url to external url for rangeslider
<script src="http://ionden.com/a/plugins/ion.rangeSlider/static/js/ion-rangeSlider/ion.rangeSlider.js"></script>
<link rel="stylesheet" href="http://ionden.com/a/plugins/ion.rangeSlider/static/css/ion.rangeSlider.css" />
then do import Slider from 'react-rangeslider' if not using the es6 transpiler then do var Slider = require('react-rangeslider')
I also had this error, looks like a Jquery version conflict please try downgrading ion-rangeslider in package.json "ion-rangeslider": "2.2.0".

Integrating React components with Pux - where does require() come from?

The Pux documentation tells me to use require() in the browser. Where does that function come from and how do I use it?
Background:
I'm trying to integrate the Quill editor with my web application that uses purescript-pux.
Following the Pux documentation I created a file MyEditor.js like this:
// module MyEditor
var React = require("react");
var Pux = require("purescript-pux");
var MyEditor = React.createClass({
displayName: "MyEditor",
onTextChange: function onTextChange(value) {
this.setState({ text: value });
},
render: function render() {
return React.createElement(ReactQuill, { value: this.state.text,
onChange: this.onTextChange });
}
});
exports.fromReact = Pux.fromReact(MyEditor);
and a file MyEditor.purs as follows:
module MyEditor where
import Pux.Html (Html, Attribute)
foreign import fromReact :: forall a. Array (Attribute a) -> Array (Html a) -> Html a
I then use MyEditor.fromReact [value p.description] in my Html Action and the code compiles, but the browser complains about ReferenceError: require is not defined.
I'm not very familiar with the javascript ecosystem. I'm aware that several libraries providing a require() function exist, but which one do I use with Pux and how?
require is the NodeJS way of importing modules, it's not supported in the browser so you'll need to run your project through a bundler like browserify or webpack to produce a bundle that the browser can understand.
If you are using the pulp build tool it's as simple as running
pulp browserify --to app.js
and then loading app.js in your html through a script tag.
pulp browserify documentation: https://github.com/bodil/pulp#commonjs-aware-builds
In addition to Christophs answer (but can't comment since comments don't allow code blocks):
Using Thermite 4.1.1 this worked for me:
add a package.json file with:
{
"dependencies": {
"react": "^0.14",
"react-dom": "^0.14"
}
}
run npm install
from then on pulp browserify --optimise gets the whole shebang packaged.
This is really badly documented and I opened an issue about that on purescript-react.

How to properly load local AMD modules with jspm/system.js

I am having a really hard time using local AMD modules in an Aurelia project that uses es6, JSPM, and system.js. I am hoping someone out there can help me configure my project to enable me to import/load my AMD modules and use them in my project. The local AMD style modules are in a format similar to the following:
define
(['require',
'lib/alerts/STARTSTOP',
'lib/alerts/STOPPED',
...
],
function( require, STARTSTOP, STOPPED, ... ) {
return {
alert: function( data ) {
var type = data.type;
var ev = data.event;
var cls = require( 'lib/alerts/' + ev );
return new cls( data );
}
};
});
When I try to import/load this module into an es6 module I am running into the following error: Uncaught TypeError: Unexpected anonymous AMD define.. I get this error when trying to load the module in either of the following ways:
System.import('lib/alerts/AlertFactory').then( (m) => {
console.log( m );
});
or
import AlertFactory from 'lib/alerts/AlertFactory.js';
I have also made sure to add the following script to my index.html:
<script>
window.define = System.amdDefine;
window.require = window.requirejs = System.amdRequire;
</script>
In addition to the above I have also added a meta format property to my config.js file, but that hasn't seemed to help either.
meta: {
...
"lib/alerts/*.js": {
"format": "amd"
}
}
Does anyone have any ideas on why I am running into the error I am seeing and how to properly load my modules? I appreciate any help/insight you can offer.
UPDATE
I finally realized that the main issue here is that I'm trying to use existing AMD modules in and Aurelia project, and the default Aurelia gulp build assumes that all code is written in ES6 and not mixed with AMD. That's why I'm having issues. Vanilla jspm/system.js handle a mix of module formats, but Aurelia does not out of the box.
Just put your AMD modules out of src so babel will not be able to transpile it. Here is working solution I use to import jquery modules:
First, I have local_packages folder in project root and I have jquery module local_packages/somelib/js/mymodule.js
Then in config.js
paths: {
...
"local/*": "local_packages/*",
}
map: {
...
"somelib": "local/somelib",
"somelib1": "/local_packages/somelib1",
}
And finally my import looks like: import 'somelib/js/mymodule';

Using webpack with an existing requirejs application

I am working with an existing application (canvas-lms) that uses RequireJS in its build system. I'm working on a pseudo-standalone application that plugs into Canvas (a "client_app" in Canvas parlance). This is a fontend-only app that makes API calls back to the host Canvas app. The details aren't terribly important for my question - all a client_app needs to do is have a build script that spits out a JS file in a defined place within the Canvas app tree.
I'm trying to use Webpack to build my app instead of RequireJS. Everything works great if I keep all my dependencies self-contained (e.g. npm-install everything I need); however, Canvas already provides many of these dependencies (e.g. React, jQuery), and in jQuery's case, it provides a patched version that I'd like to use instead. This is where I start to have problems.
Getting React to work was easy; Canvas installs it with bower, so I was able to add an alias in my webpack config to point at it:
alias: {
'react': __dirname + '/vendor/canvas/public/javascripts/bower/react/react-with-addons',
}
(__dirname + /vendor/canvas is a symlink in my application tree to the host Canvas application's tree)
Where I'm having trouble is trying to load the provided copy of jQuery.
Canvas has the following jQuery structure:
/public/javascripts/jquery.js:
define(['jquery.instructure_jquery_patches'], function($) {
return $;
});
/public/javascripts/jquery.instructure_jquery_patches.js:
define(['vendor/jquery-1.7.2', 'vendor/jquery.cookie'], function($) {
// does a few things to patch jquery ...
// ...
return $;
});
/public/javascripts/vendor/jquery.cookie.js -- looks like the standard jquery.cookie plugin, wrapped in an AMD define:
define(['vendor/jquery-1.7.2'], function(jQuery) {
jQuery.cookie = function(name, value, options) {
//......
};
});
and finally, /public/javascripts/vendor/jquery-1.7.2.js. Not going to paste it in, since it's bog-standard jQuery1.7.2, except that the AMD define has been made anonymous -- reverting it to the stock behaviour doesn't make a difference.
I want to be able to do something like var $ = require('jquery') or import $ from 'jquery' and have webpack do whatever magic, poorly-documented voodoo it needs to do to use jquery.instructure-jquery-patches.
I've tried adding the path to resolve.root in my webpack.config.js file:
resolve: {
extensions: ['', '.js', '.jsx'],
root: [
__dirname + '/src/js',
__dirname + '/vendor/canvas/public/javascripts'
],
alias: {
'react': 'react/addons',
'react/addons/lib': 'react/../lib'
}
},
This should mean that when I do a require('jquery'), it first finds /public/javascripts/jquery.js, which defines a module with instructure_jquery_patches as a dependency. That falls into instructure_jquery_patches, which defines a module with two dependencies ('vendor/jquery-1.7.2', 'vendor/jquery.cookie').
In my main entry point (index.js), I am importing jQuery (also tried a commonjs require, no difference), and trying to use it:
import React from 'react';
import $ from 'jquery';
$('h1').addClass('foo');
if (__DEV__) {
require('../scss/main.scss');
window.React = window.React || React;
console.log('React: ', React.version);
console.log('jQuery:', $.fn.jquery);
}
Building the bundle with webpack seems to work; there are no errors. When I try to load the page in the browser, though, I'm getting an error from within jquery.instructure-jquery-patches.js:
It would seem that jQuery is not availble within jquery.instructure-jquery-patches.
It is, however, available in the global scope after the page loads, so jQuery is being evaluated and executed.
My guess is that I'm running into some sort of requirejs/amd asynchronicity problem, but that's a shot in the dark. I don't know enough about requirejs or amd to know for sure.
TobiasK's comment pointed me at needing to add amd: { jQuery: true } to my webpack config. Everything is working now.

Categories

Resources