Merging requirejs and plain js file together
Asked Answered
T

6

11

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:

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

Tweeter answered 15/10, 2021 at 5:15 Comment(4)
You've updated your question with a "goal"... but here comes the XY-problem. Why'd you need this?Alpestrine
@Alpestrine I don't really need it, but I was curious. Actually, I was asking myself several question: Is it possible to bundle those files together ? Does bundling files together leads to better minification / compression ? What' the overhead from serving files from 2 different domain ? So I implemented the 'quick way' solution provided by Steve and deployed it on my prod server, and according to page speed insight lab data there is a speedup. Now I'll wait a few weeks to see if real world data show the same trendTweeter
I can give it shot with webpack or rollup if that's what you're looking for?Zwiebel
Copy and paste the code from all 3 files into one main js file and call that? Unless I'm not understanding your question fullyNorri
A
2

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.

Aromatize answered 19/10, 2021 at 20:38 Comment(0)
F
4

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>
Fleur answered 17/10, 2021 at 16:33 Comment(0)
A
4

@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.
Alpestrine answered 17/10, 2021 at 21:16 Comment(0)
A
2

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.

Aromatize answered 19/10, 2021 at 20:38 Comment(0)
Z
2

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"
  }
}
Zwiebel answered 22/10, 2021 at 16:18 Comment(0)
S
1

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);
Spermatic answered 17/10, 2021 at 8:31 Comment(0)
S
1

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

Sty answered 24/10, 2021 at 5:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.