Best practices for incorporating and leveraging node packages with Laravel Mix

As I embark on my Laravel (v 8.x) Mix project, I am encountering challenges when it comes to incorporating JavaScript from node modules.

To kick things off, here is a snippet from my webpack.mix.js:

mix.js('node_modules/mxgraph/javascript/mxClient.min.js', 'public/js');
mix.js('resources/js/*.js', 'public/js').postCss('resources/css/app.css', 'public/css', [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
]).version();

Then, within my app.js, I have the following setup:

import Canvas from './canvas';

require('mxgraph');

const canvas = new Canvas();

This script also imports canvas.js:

export default class Canvas {
    constructor() {
        this.container = document.getElementById('graphContainer');
        if (!mxClient.isBrowserSupported())
        {
            // Displays an error message if the browser is not supported.
            alert('Browser is not supported!');
        }
.
.
.
    }
}

Furthermore, in the Scripts section of my Blade layout:

<script src="{{ mix('js/mxClient.min.js') }}" defer></script>
<script src="{{ mix('js/app.js') }}" defer></script>

Upon loading the page, the console throws the following error:

Uncaught ReferenceError: mxClient is not defined
    at new Canvas (app.js:3866)
    at Module../resources/js/app.js (app.js:3813)
    at __webpack_require__ (app.js:114081)
    at app.js:114143
    at app.js:114149

The variable mxClient is indeed present in mxClient.min.js, and the reference in Canvas comes after its loading.

I've experimented with different approaches without success. Any insights or suggestions would be highly appreciated.

Answer №1

After much exploration and experimentation, I finally stumbled upon a solution that does the trick. It was inspired by my findings here:

Although my approach may seem a bit cumbersome, it gets the job done, allowing me to proceed with my mxGraph development project.

As a result, I have removed the explicit inclusion of mxgraph in webpack.mix.js, reverting back to the Laravel default setup:

mix.js('resources/js/*.js', 'public/js').postCss('resources/css/app.css', 'public/css', [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
]).version();

I have also eliminated require('mxgraph'); from app.js, resulting in the following streamlined code:

import Canvas from './canvas';

const canvas = new Canvas();

My revised canvas.js now looks like this:

import mx from 'mxgraph';

const mxgraph = mx({
    mxImageBasePath: './src/images',
    mxBasePath: './src'
});

window.mxGraph = mxgraph.mxGraph;
window.mxGraphModel = mxgraph.mxGraphModel;
window.mxEvent = mxgraph.mxEvent;
window.mxEditor = mxgraph.mxEditor;
window.mxGeometry = mxgraph.mxGeometry;
window.mxRubberband = mxgraph.mxRubberband;
window.mxDefaultKeyHandler = mxgraph.mxDefaultKeyHandler;
window.mxDefaultPopupMenu = mxgraph.mxDefaultPopupMenu;
window.mxStylesheet = mxgraph.mxStylesheet;
window.mxDefaultToolbar = mxgraph.mxDefaultToolbar;

const {mxGraph, mxClient, mxEvent, mxCodec, mxUtils, mxConstants, mxPerimeter, mxRubberband} = mxgraph;

export default class Canvas {
    constructor() {
        let container = document.getElementById('graphContainer');
        if (typeof(mxClient) !== 'undefined') {
            this.draw(container);
        }
    }

    draw (container) {
        if (! mxClient.isBrowserSupported())
        {
            // Display error message for unsupported browser
            mxUtils.error('Browser is not supported!', 200, false);
        }
        else
        {
            // Disable context menu
            mxEvent.disableContextMenu(container);

            // Create graph inside container
            var graph = new mxGraph(container);

            // Enable rubberband selection
            new mxRubberband(graph);

            // Get default parent for inserting cells
            var parent = graph.getDefaultParent();

            // Add cells to model in a single step
            graph.getModel().beginUpdate();
            try
            {
                var v1 = graph.insertVertex(parent, null, 'Hello,', 20, 20, 80, 30);
                var v2 = graph.insertVertex(parent, null, 'World!', 200, 150, 80, 30);
                var e1 = graph.insertEdge(parent, null, '', v1, v2);
              
            }
            finally
            {
                // Update display
                graph.getModel().endUpdate();
            }
        }
    }
}

The majority of the code within the draw() method is adapted from an mxGraph "Hello World" demo ().

A big thank you to @codedge for your invaluable assistance!

Answer №2

After some experimenting, I discovered a solution.

Combine files into one

Merge mxClient.min.js and app.js into a single file using the following code:

mix.js(
    [
        "resources/js/app.js",
        "node_modules/mxgraph/javascript/mxClient.min.js",
    ],
    "public/js"
).postCss('resources/css/app.css', 'public/css', [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
]).version();

Import mxGraph and Canvas

Add this to your app.js:

import "./canvas";

require("mxgraph");

const canvas = new Canvas();

Now run npm run dev without any problems.

Update

I came across an alternative option which might be simpler. Remove mxClient from your webpack.mix.js, only include your app.js.

// app.js

window.mxClient = new require("mxgraph")().mxClient;

let isBrowserSupported = mxClient.isBrowserSupported();

console.log(isBrowserSupported);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Prevent JSON.parse() function from stripping away any trailing zeros in a JSON string dataset

After creating a JSON string, I encountered an issue where the values were not being parsed correctly. Here is the code snippet: <script> var string = JSON.parse('{"items":[{"data":[5.1]}, {"values":[5.10]}, {"offer":[3.100]}, {"grandtotal":[12 ...

How to execute a system command or external command in Node.js

I am encountering an issue with Node.js. When using Python, I would typically perform an external command execution like this: import subprocess subprocess.call("bower init", shell=True) Although I have explored child_process.exec and spawn in Node.js, I ...

Executing numerous tests on a single response using Node.js along with Chai, Mocha, and Should

I have a setup similar to the one below that allows me to perform a series of API tests using Mocha. While this method works well, it involves making an individual API call for each test. My goal is to streamline the process by utilizing the same API cal ...

Unable to generate package.json file following Node installation

I'm experiencing difficulties creating the package.json file following the installation of Node on my system and configuring the path. I keep receiving error -4058, which seems to be connected to npm's inability to locate the file. ...

Make sure that a specific script is loaded prior to another script

I'm struggling to understand how scripts are loaded on a page. I have jQuery plugins that rely on other scripts. Specifically, I am using the timeago jQuery plugin. I have loaded these scripts in the head in the following order: <script src="< ...

Having trouble displaying a background image on a React application

Public>images>img-2.jpg src>components>pages>Services.js> import React from 'react'; import '../../App.css'; export default function Services() { return <h1 className='services ...

What is the best way to assign the result of a promise to a variable?

My code includes an async function that retrieves a value async fetchUserName(environment: string, itemName: string, authToken: string): Promise<any> { let result = await this.obtainDeviceName(environment, itemName, authToken); return ...

Keeping an HTML field constantly refreshed with dynamic content from Django

Imagine having two input fields along with an HTML paragraph displaying a Django value. Field A: <input ...> Field B: <input ...> <p>{{ sum }}</p> The goal is to have the sum update in real time, meaning that once both numbers ...

Apologies, but the development JavaScript bundle could not be generated due to an unexpected token error with code #981

After cloning my repository and installing all packages via npm, I encountered an error when trying to start my program with gatsby develop. The error appears in all files within the templates directory. https://i.stack.imgur.com/MsAUm.png I've atte ...

Selection pseudo selectors for tooltips are a great way to provide additional

Can someone explain how tooltips change on Medium.com when you double click a word or sentence to reveal options like share and edit? https://i.stack.imgur.com/0EVmH.png ...

The specified property 'slug' is not found within the designated type 'ParsedUrlQuery | undefined'

I am faced with an issue in my code where I am attempting to retrieve the path of my page within the getServerSideProps function. However, I have encountered a problem as the type of params is currently an object. How can I convert this object into a stri ...

The HTTP request is malfunctioning in a different location

I'm facing an issue where my code works in the w3schools code editor, but not on localhost or cpanel host. When I try to run it on another host, it gives me a bad request and doesn't return the answer. Here is the code snippet that I am working ...

How can express.js be properly installed using typescript?

Currently, I am in the process of setting up a new project that involves using express.js with typescript integration. Would it suffice to just install @types/express by running the following command: npm install @types/express Alternatively, do I also ...

Adjusting the size of an object as the page dimensions change

I am looking to dynamically resize an element whenever the document resizes. For example, if a draggable div is moved and causes the document to scroll, I want to adjust the size of another element using $("#page").css('height','120%'); ...

The dynamic duo: Formik meets Material-UI

Trying to implement Formik with Material-UI text field in the following code: import TextField from '@material-ui/core/TextField'; import { Field, FieldProps, Form, Formik, FormikErrors, FormikProps } from 'formik'; import ...

Connecting Ember controllers with views/elements

As a developer with an Angular background, I am relatively new to Ember. Let's consider a scenario where there are multiple elements, each containing different sets of data. #elem1 10 #elem2 20 #elem3 30 My objective is to link each of these indiv ...

The command 'webpack-dev-server' is not valid and cannot be executed as an internal or external command, operable program, or batch file

E:\MyProjects\MyMLM\Layer1.UI>npm start <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="84e9fde9e8e9c4b5aab4aab4">[email protected]</a> start E:\MyProjects\MyMLM\Layer1.UI webpack ...

The error message thrown is: "TypeError - attempting to call an undefined function

I'm new to working with node and express, and I encountered an error when running my App. Does anyone have any solutions? I suspect the error may be in this line "app.use(express.static(user));" but I'm not certain. var express = require('e ...

The content of package.json should adhere to the JSON format strictly, rather than being in

After utilizing http://jsonlint.com/ to verify the syntax of this package.json file, here's what I found: { "name": "hello-world", "description": "hello world test app", "version": "0.0.1", "private": true, "dependencies": { "express": ...

Comparing JSON and JavaScript object arrays: Exploring the differences in outcomes and strategies for achieving desired results

Although it's not valid JSON, I've found that by declaring this as a variable directly in my code, I can treat it like an object. <script> // this will result in object var mydata = { users: [{ person: { ...