Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merging requirejs and plain js file together

I'm developping a small website, and the main HTML page basically looks like this:

<html lang="en">
  <head>
    <script src="ace.js" type="text/javascript"><script>
    <script src="ext-language_tools.js" type="textjavascript"></script>
    <script src="mode-mongo.js" type="text/javascript"></script>
    <script src="playground.js" type="text/javascript"></script>
    <script type="text/javascript">
    
      window.onload = function() {
    
        ace.config.setModuleUrl("ace/mode/mongo", "mode-mongo.js")
    
        configEditor = ace.edit(document.getElementById("editor"), {
            "mode": "ace/mode/mongo",
            "useWorker": false
        })
      }
    </script>
  </head>
  <body>
    <div class="editor">
  </body>
</html>

( real one is visible here ).

Here are the unminified files:

  • ace.js
  • ext-language_tools.js
  • mode-mongo.js
  • playground.js

The first 3 js files use requirejs, the 4th is just plain js

Is there a way to merge those 4 files into a single file in order to have something like this in my HTML instead ?

<html lang="en">
  <head>
    <script src="bundle.js" type="text/javascript"><script>
    <script type="text/javascript">
    
      window.onload = function() {

        configEditor = ace.edit(document.getElementById("editor"), {
            "mode": "ace/mode/mongo",
            "useWorker": false
        })
      }
    </script>
  </head>
  <body>
    <div class="editor">
  </body>
</html>

Edit

My goal is to load all this js code at once in a single HTTP request

like image 493
felix Avatar asked Oct 15 '21 05:10

felix


People also ask

Is RequireJS still relevant?

RequireJS has been a hugely influential and important tool in the JavaScript world. It's still used in many solid, well-written projects today.

How do I include an external JS file in another JavaScript file?

To include an external JavaScript file, we can use the script tag with the attribute src . You've already used the src attribute when using images. The value for the src attribute should be the path to your JavaScript file.

Can you have two JavaScript files?

You can write your JS in separate files, but when it comes to deploying, it's more efficient to minify them all into a single file.


Video Answer


6 Answers

@Booboo's answer is mostly correct. But I'd like to add my 5 cents:

  1. ace.js in your repo, and in your question is already bundled! It doesn't actually use the requirejs. Everything is already inside. You can see it by yourself in the DevTools: there will be only one network request. And as such it won't work with the setup suggested by the requirejss docs and the @Booboo's answer.
  2. To be able to use the requirejs's bundle mechanism you'll need to add the sources of the ace.
  3. Also you can, but you don't have to use the r.js. It's not clear what is your goal. But if for some reason you just want a single script entry point in your html (not sure why would you need this) - <script src="require.js" data-main="main" /> will be enough.
  4. And again, as your goal is unclear, let me suggest - don't do this. There are only 2 additional files from the ace project. Others are yours, so bundle only them. If you'll bundle your code will the ace.js, then every time your code changes your users will have to download all the KB's of ace.js again and again, because browser won't be able to use a cached version of it. And the main idea of the bundles (and also one of the reasons behind CDN) is exactly about minimizing the number of requests in the long run. And if your worry that new users won't come again because your site took a little bit longer to initialize on their first visit... well, maybe the speed is not your main concern. Also, there can be 6 parallel connections from the browser, so one bundle can actually slow your site down.
like image 164
x00 Avatar answered Nov 15 '22 13:11

x00


I am a bit confused. You say you are using requirejs for the first 3 files, but this is not my understanding of how requirejs is used. You would normally have a main JavaScript file (let's call it main.js) and your HTML would load a single JavaScript file, i.e. the require.js file, which would specify the main.js file to be loaded:

<html lang="en">
  <head>
    <script src="require.js" data-main="main"><script>
    <!-- The following does not currently use require.js: -->
    <script src="playground.js"><script>
    <script>
    
      window.onload = function() {

        configEditor = ace.edit(document.getElementById("editor"), {
            "mode": "ace/mode/mongo",
            "useWorker": false
        })
      }
    </script>
  </head>
  <body>
    <div class="editor">
  </body>
</html>

And then inside of main.js, you would use require() (or requirejs()) to load any other scripts you need to run. For example:

require(["ace"], function(ace) {
    //This function is called when scripts/ace.js is loaded.
    //If ace.js calls define(), then this function is not fired until
    //ace's dependencies have loaded, and the ace argument will hold
    //the module value for "ace".
});

And the window.onload event handler, which requires the ace module, would be moved to inside main.js file.

Now if you want to bundle the 4 files into a single file, then assuming you have Node.js installed the easiest way to proceed is to first install requirejs using npm:

npm install -g requirejs

Next install uglify:

npm install uglify-js -g

Then create a new JavaScript file main.js in the directory that has your scripts:

require(['ace', 'ext-language-tools', 'mode-mongo', 'playground'], function(ace) {
    window.onload = function() {
        configEditor = ace.edit(document.getElementById("editor"), {
            "mode": "ace/mode/mongo",
            "useWorker": false
        });
    };
});    

Then execute from the scripts directory:

r.js -o name=main out=bundle.js

If you are running under Windows, you will need to replace r.js above with r.js.cmd. This should give you a single, minified file bundle.js. If you don't want the file minified then:

r.js -o name=main out=bundle.js optimize=none

Then modify your HTML to look something like:

<html lang="en">
  <head>
    <script src="require.js" data-main="bundle"><script>
  </head>
  <body>
    <div class="editor">
  </body>
</html>

Of course, you do not need the window.onload event in your main.js:

require(['ace', 'ext-language-tools', 'mode-mongo', 'playground'], function() {});

And then your HTML becomes:

<html lang="en">
  <head>
    <script src="require.js" data-main="bundle"><script>
    <script>
        require(['ace'], function(ace) {
            window.onload = function() {
                configEditor = ace.edit(document.getElementById("editor"), {
                    "mode": "ace/mode/mongo",
                    "useWorker": false
                });
            };
        });
    </script>
  </head>
  <body>
    <div class="editor">
  </body>
</html>
like image 37
Booboo Avatar answered Nov 15 '22 13:11

Booboo


This is an interesting question, since there are a bunch of potential solutions that span a few eras and philosophies in JavaScript and Web development. I'll talk about about the easiest and oldest, file concatenation, and briefly touch upon RequireJS, and more modern approaches of using dedicated web bundlers. There's also the unstated, underlying assumption of why you feel you need to bundle it-- there might be some assumptions of file loading which might not be true, particularly with HTTP/2.

The Quick Way

If you want something quick, easy, and old school, you can just combine (concatenate) all of your JavaScript files together. That's essentially what's happening in your initial web page: the browser downloads all of the JavaScript files and runs them in order.

To concatenate using Unix/Linux/OS X:

cat path/to/ace.js  <(echo) \
  path/to/ext-language_tools.js  <(echo) \
  path/to/mode-mongo.js  <(echo) \
  path/to/playground.js \
  > path/to/bundle.js

(You can combine them all on one line if you remove the \s. You can also omit the <(echo) if you know the file ends with a new line)

Alternatively, you can manually copy and paste the files into one big file.

The RequireJS Way

It bears mentioning the RequireJS way of doing things, that uses require statements, since that's the philosophy that ace.js is developed under. Using this philosophy, files are intended to stay separated as modules and are loaded as needed. Other answers explain this approach in more detail, though I'll add that bundling doesn't seem to be the idiomatic way to use RequireJS-- the library originally intended (though doesn't require) modules to be split into different files.

The Web Bundler Way

In recent years, people have adopted web bundlers-- like webpack, parcel, rollup, and more -- to manage multiple files and dependencies. These tools are intended to output a single web bundle and have a lot of nice, customizable features for that. They might need a bit of work to get up and running, and need to use a CommonJS plugin to get require working. For instance, see here to get ace working with webpack.

Do you need to bundle?

Depending on your concerns and situation, bundling might not need be an optimization you need. Historically, bundling was used as a way to minimize network requests, since the number of network connections was limited, and sometimes files requested other files, leading to loading in serial. Many of these concerns were addressed with HTTP/2. Just make sure that your web server supports HTTP/2 and that you're serving all the files on that server. For more information about this, see this question. You probably care most of how it operates in practice, so you'd probably want to benchmark it either way, but you might not be gaining much.

like image 33
Steve Avatar answered Nov 15 '22 13:11

Steve


We can simply use webpack to get what you're looking for

webpack with vanilla

module.exports = {
  entry: ["./ace.js", "./playground.js" ....],
  output: {
    filename: "bundle.js"
  }
}
like image 27
lost_in_magento Avatar answered Nov 15 '22 13:11

lost_in_magento


You can require them in one js file and reference that in your template.

Something like this:

bundle.js:

require(../ace.js);
// other files
require(../playground.js);
like image 33
MahdiJoon Avatar answered Nov 15 '22 12:11

MahdiJoon


You can use require and source script together,

At the first, config your webpack.config.js Webpack Docs

const path = require('path');
const toml = require('toml');
const yaml = require('yamljs');
const json5 = require('json5');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    stats: 'errors-only',
    mode: 'development',
    entry: './src/index.js',

    devServer: {
        static: {
            directory: path.join(__dirname, 'public'),
        },
        compress: true,
        port: 3000,
    },

    plugins: [
        new HtmlWebpackPlugin({
            title: 'Artificial Intelligence',
        }),
    ],

    output: {
        filename: '[contenthash].bundle.js',
        path: path.resolve(__dirname, 'public'),
    },

    module: {
        rules: [
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader'],
            },

            {
                test: /\.toml$/i,
                type: 'json',
                parser: {
                    parse: toml.parse,
                },
            },

            {
                test: /\.yaml$/i,
                type: 'json',
                parser: {
                    parse: yaml.parse,
                },
            },

            {
                test: /\.json5$/i,
                type: 'json',
                parser: {
                    parse: json5.parse,
                },
            },
        ],
    },
};

In the second step, require the files you need

// Es5
const x = require('x');
const y = require('y');

// Es6
import * as x from 'x';
import y from 'y';
import { z } from 'z';

Use the script tag instead of importing

<html lang="en">
  <head>
    ...
  </head>
  <body>
    <script src="x.js"></script>
    <script src="y.js"></script>
    <script>
        const x = new X();
    </script>
  </body>
</html>

Notice: Scripts must be executed after all

You can also use the scripts you imported in the script tag in your files

App.js

const x = new X();
cosnt y = new Y();

List the files you do not require as follows

like image 38
Ali Yaghoubi Avatar answered Nov 15 '22 13:11

Ali Yaghoubi