requirejs and pre-built bundles - javascript

I can't get bundles to work in the optimized build, I'm trying to load some external pre-built bundle (not included in the require build process output).
In requirejs.config:
paths: {
'mymodules': '../lib/test-bundle/test-bundle'
},
bundles: {
'mymodules': ['mymodule1', 'mymodule2']
}
test-bundle content is:
console.log("defining modules...");
define('mymodule1', ['jquery'], function($) {
console.log('within mymodule1', $.fn.jquery);
return {
test: 'module1'
};
});
define('mymodule2', ['jquery'], function($) {
console.log('within mymodule2', $.fn.jquery);
return {
test: 'module2'
};
});
In the build config paths for mymodules, mymodule1 and mymodule2 are set to empty: (or the build process fail), I'm not using the modules option in the build config to generate bundles.
If I use the sources as they are everything is working fine, as expected.
In the built version (but not optimized) test-bundle is loaded and "defining modules" printed, then timeout loading mymodule2:
Error: Failed to load root module (viewmodels/shell). Details: Load timeout for modules: mymodule2(…)
Uncaught Error: Failed to load root module (viewmodels/shell). Details: Load timeout for modules: mymodule2
http://requirejs.org/docs/errors.html#timeout
In the built and optimized version there's one more error:
Uncaught ReferenceError: define is not defined
like if test-bundle is loaded before requirejs implement define().
What I'm missing or doing wrong?
edit
I've created a branch with the test above to install and build (nodejs npm and probably grunt-cli are required on the system)
git clone https://github.com/xenogenesi/HTMLStarterKitPro
cd HTMLStarterKitPro
git checkout test-bundle
# nodejs npm required on the system (maybe grunt-cli)
npm install # required only once to install node modules
grunt build-only # create a build/ directory and the content
php -S localhost:8888 # to publish the sources as they are
# browse to http://localhost:8888
php -S localhost:7777 -t build # to publish the built versions
# browse to http://localhost:7777 for built but not optimized
# browse to http://localhost:7777/index2.html for built optimized
(see this commit for all files modified to add the test-bundle)

You can only use one define per file. If you want to use anyway, there is a bundle configuration option that you can say that a module is inside an already bundled file as yours.
As it states:
bundle config is for pointing multiple module IDs to a bundle's module ID.

So, apparently even being require.js itself, the loader once included in the optimized version is or at least behave in a different way.
One simple way to get it working is to not include any loader and keep using require to kickstart:
Gruntfile.js:
- name: 'requireLib',
+ name: false,
index2.html:
- <script src="app/main-built.js"></script>
+ <script src="lib/require/require.js" data-main="app/main-built"></script>
Still, if somebody know how to include the loader and get pre-built external bundles (multiple modules in one file) working, I'd like to know and will accept as answer.

Related

How to make Webpack use project's "node_modules" in js scripts located outside the project folder?

I have Node.js project and the following structure of folders:
lib
awesome-formatter.js
FrontEndApp
prettify.js
node_modules
awesome-parser
BackEndApp
...
I use awesome-parser module and awesome-formatter.js library in prettify.js script like this:
require('awesome-parser')
require('../lib/awesome-formatter.js')
awesome-formatter.js, in turns, should use awesome-parser too:
require('awesome-parser')
My FrontEndApp has been configured to use Webpack, and I'm trying to run it in dev mode using npm run dev command. However, I got the following error:
ERROR Failed to compile with 1 errors
These dependencies were not found:
* awesome-parser in /home/user/dev/lib/awesome-formatter.js
I don't want to move awesome-formatter.js inside the FrontEndApp because I also use it in BackEndApp project (and probably in some other projects) and I don't want to create separate "node_modules" in "lib" for it just not to duplicate installed modules on disk.
So, my question is, how to make Webpack use project's "node_modules" in js scripts located outside the project folder?
P.S. Of course there are workarounds like symlinks or making a full-featured module (with package.json etc.) from lib/awesome-fromatter and installing it into FrontEndApp/node_modules, but is there a direct way to solve this problem?
I've found a solution: resolve.modules sould be added to Webpack configuration file.
module.exports = {
...
resolve: {
...
modules: [
'node_modules',
resolve('node_modules')
]
},
...
}
This means that Webpack is searching modules in 'node_modules' as a relative subfolder (and it's the usual behavior), and at the absolute path to the project's 'node_modules' as well: resolve('node_modules'), so that scripts in folders outside the project (like lib in my structure) can find and use it.

Bootstrap v4 runtime/load error in Aurelia

I have the following in my aurelia.json file, among the rest of what you'd usually find. I copied it directly from the reference implementation, and as you'd therefore expect, it works fine.
{
'build': {
'bundles': [
'name': 'vendor-bundle.js'
'dependencies': [
"jquery",
{
"name": "bootstrap",
"path": "../node_modules/bootstrap/dist",
"main": "js/bootstrap.min",
"deps": ["jquery"],
"exports": "$",
"resources": [
"css/bootstrap.css"
]
}
]
]
}
}
However, I'm trying to migrate to Bootstrap 4, and it just doesn't seem to be working. In order to update the package, I've tried changing build.bundles.dependencies[].path to ../jspm_packages/github/twbs/bootstrap#4.0.0-beta as well as to ../node_modules/bootstrap-v4-dev/dist, but it doesn't change the error code or make the error manifest any less. I've also tried copying the v4 files into the dist folder for v3, which also causes the same problem.
Build is always clean; the error occurs at run-time:
DEBUG [templating] importing resources for app.html
Uncaught TypeError: plugin.load is not a function
Unhandled rejection Error: Failed loading required CSS file: bootstrap/css/bootstrap.css
EDIT:
Thanks to Ashley Grant's answer, I have updated Bootstrap through NPM, obviating any changes to aurelia.json. The error remains unchanged, which would seem to indicate a bug were it not for the fact that other people have successfully performed this migration without errors using the same toolchain.
EDIT2:
I've created steps to reproduce the bug:
$ au new
name # can be any valid value
2 # Selects TypeScript as the language
1 # Create project structure
1 # Install dependencies
cd into the project directory.
Add the two entries listed above to build.bundles[1].dependencies in aurelia_project/aurelia.json
$ npm install jquery --save
$ npm install bootstrap#^4.0.0-beta --save
Change src/app.html to the following:
<template>
<require from="bootstrap/css/bootstrap.css"></require>
</template>
Finally, execute either of the following and browse to the provided URL.
$ au run
OR
$ au build
$ serve
This yields the errors described in both Google Chrome Version 55.0.2883.87 (64-bit) and Mozilla Firefox 55.0.3 on my Arch Linux systems. I've not yet had the opportunity to test it on other systems.
Edit3:
Thanks to #vidriduch, everything appears to be working. However, if you look at the console, you find the following:
Uncaught SyntaxError: Unexpected token export
vendor-bundle.js:3927Uncaught Error: Mismatched anonymous define() module: [entirety of vendor-bundle.js printed here]
These are the two very first messages when the page loads in debug mode, but no other errors arise.
You are missing popper.js dependency for Bootstrap 4.0.0-beta.
In order for Aurelia to accept this add
"node_modules/popper.js/dist/umd/popper.js"
on the top (as per comment from #hxtk) of prepend part of aurelia.json (assuming that you are using RequireJS, otherwise have a look at webpack dependency linking for Bootstrap https://getbootstrap.com/docs/4.0/getting-started/webpack/)
Just to mention, the version of popper.js you need to install is 1.11.0 (https://github.com/twbs/bootstrap/issues/23381), so
npm install popper.js#1.11.0
or
yarn add popper.js#1.11.0
and it should work :)
Your aurelia.json configuration is correct. I'm going to guess you never ran npm install bootstrap#^4.0.0-beta --save as you are mentioning copying files in to a versioned node_modules folder, and NPM doesn't use versioned folders like JSPM does.
So run npm install bootstrap#^4.0.0-beta --save and things should start working. I have your exact configuration working in an application for one of my clients.

Loading NPM module when using Bower Grunt Angularjs Build

I'm currently trying to integrate an npm module into an application that is built on Angularjs 1.4, Grunt, and Bower.
Require and Import do not work in the angualrjs framework which is the only way I can think of accessing the node_modules folder.
Does anyone have any idea how to use both npm and bower modules in the same application?
Here is a very trimmed down version of my app.js folder:
(function(angular) {
'use strict';
angular
.module('AppApp', [dependencies])
.constant('appconfig',{})
.config(function(...){
$statprovider.state{...}
.run(function($state){
$state.go('login);
})
})(angular);
I currently get all my dependencies through bower and access via index.html file. This does not seem to work if I write a script tag linking to the node_modules folder there.
USING MODULE INJECTION IN ANGULARJS
Accessing AngularJS modules from node_modules and bower_components is straight forward:
Let's take an example of angular-bootstrap from node_modules (Similar can be done from bower_components):
In HTML file, specify the reference of js file.
<script type="text/javascript" src="../node_modules/angular-bootstrap/ui-bootstrap-tpls.js"></script>
In your angular module, register the dependency as (You can check the module name on npm website or github while downloading or you can check it in the js files in node_modules/bower_components after downloading):
angular.module('AppApp',
[
'*ui.bootstrap*',
]);
However, you can also make Require and Import work in the AngularJS framework. This can be done using browserify or webpack that bundles your all the javascript files containing require/import into one to resolve the dependencies so that browser can understand require/import syntax, which is otherwise not understood by browsers.
USING BROWSERIFY
For using browserify when using grunt (app.js is the file containing require, you can specify other files in the array),
browserify: {
options: {
browserifyOptions: {
debug: true,
insertGlobalVars: []
},
transform: ['brfs' ]
},
app: {
files: {
['app.js']
}
}
}
node_modules dependencies required to use browserify are: brfs, grunt-browserify

Browserify External Requires with Grunt-Browserify

The Browserify docs section on external requires shows how to make a module within a bundle available to the global environment:
browserify -r through -r duplexer -r ./my-file.js:my-module > bundle.js
But I'm having trouble configuring this to work with Grunt-Browserify.
The -r flag seems to correspond to the require option in Grunt-Browserify, but the docs description for this option doesn't make any mention of external requires or exporting a require() function.
In my Gruntfile, I've tried setting the require option to the module I need to expose (which is already in the bundle, by the way):
options: {
require: ['./dev/js/foomod.js'],
}
And then in my page script, I try to require() the module as shown in the docs:
<script>
var Foomod = require('./foomod.js');
Foomod.init({foo: 'bar'});
</script>
But that logs the error require is not defined.
My goal is to call the module's init() method as shown so that I can pass in runtime data without putting it in a window global.
Using browserify version 5.11.1 and grunt-browserify version 3.0.1, I have managed to get similar setup to work:
options:{
preBundleCB: function (b) {
b.require("./dev/js/foomod.js",{expose: 'foomod'});
}
}

Using pre-compiled templates with Handlebars.js (jQuery Mobile environment)

I am struggling somewhat with pre-compilation of templates in Handlebars. My jQuery Mobile project is getting pretty big template-wise and I wish to pre-compile the templates I use.
However I can't seem to find a good explanation (like a step by step tutorial) of how to do this with Handlebars.
I still have my templates all inline using the script tags. I have handlebars installed using NPM. But now I am kinda lost in how to proceed.
I am guessing doing something like
handlebars -s event.handlebars > event.compiled
and somehow including the event.compiled contents? But then, how to call it.
I am calling my templates like so
var source = $('#tmpl_profile').html(),
template = Handlebars.compile(source),
context = user.profile()),
html = template(context);
Hope someone can shed some light on this for me.
So after much trial and error (which is the best way to learn it) here's the way that works for me.
First- externalize all your templates, say you have a template inside script tags like
<script id="tmpl_ownevents" type="text/templates">
{{#unless event}}
<div class="notfoundevents"><p>No events for you</p></div>
{{/unless}}
</script>
Create a new file called events.tmpl and copy/paste the template into that.
Be sure to remove the script elements themselves, this gotcha bit me in the a..
Install the Handlebars commandline script like so
npm install -g handlebars
go to the folder you have saved events.tmpl into and run
handlebars events.tmpl -f events.tmpl.js
Include the compiled javascript into your HTML after you included Handlebars.js
<script src="events.tmpl.js"></script>
Now all that is left is change your normal template code into something resembling the following
var template = Handlebars.templates['events.tmpl'], // your template minus the .js
context = events.all(), // your data
html = template(context);
And there you have it, super fast pre-compiled Handlebars templates.
Another great option for this is to use GruntJS. Once installed:
npm install grunt-contrib-handlebars --save-dev
Then inside your gruntfile.js
grunt.initConfig({
dirs: {
handlebars: 'app/handlebars'
},
watch: {
handlebars: {
files: ['<%= handlebars.compile.src %>'],
tasks: ['handlebars:compile']
}
},
handlebars: {
compile: {
src: '<%= dirs.handlebars %>/*.handlebars',
dest: '<%= dirs.handlebars %>/handlebars-templates.js'
}
}
});
grunt.loadNpmTasks('grunt-contrib-handlebars');
Then you simply type grunt watch from your console, and grunt will automatically compile all *.handlebars files into your handlebars-templates.js file.
Really rad way to work with handlebars.
Here is the way I do it:
Preparation
We will just assume all your template variables are in a variable called context:
var context = {
name: "Test Person",
...
};
Where to put your templates
Create a directory containing all your templates (we'll call it templates/)
Add your templates here, called filename.handlebars.
Your directory structure:
.
└── templates
├── another_template.handlebars
└── my_template.handelbars
Compiling your templates
First go to your root directory, then compile your templates with the npm CLI program:
handlebars templates/*.handlebars -f compiled.js
New directory structure:
.
├── compiled.js
└── templates
├── another_template.handlebars
└── my_template.handlebars
Usage
Include the compiled.js in your HTML page after you include the runtime:
<script src="handlebars.runtime.js"></script>
<script src="compiled.js"></script>
Render your templates using the global Handlebars object:
// If you used JavaScript object property conform file names
// Original filename: "my_template.handlebars"
Handlebars.templates.my_template(context)
// if you used special characters which are not allowed in JavaScript properties
// Original filename: "my-template.handlebars"
Handlebars.templates["my-template"](context)
Remarks
Note the file extension .handlebars. It is automatically stripped when compiling the templates.
Extra: if you use one of the Jetbrains IDEs together with the Handlebars/Mustache plugin you even get quite a good editor support.
Precompiling Handlebars Templates with Grunt
Assuming you have Node.js installed. If you don't, go do that.
1) Setting up Node dependencies:
In your applications root directory add a file named package.json. Paste the following into that file:
{
"devDependencies": {
"grunt-contrib-handlebars": "~0.6.0",
"grunt-contrib-watch": "~0.5.3",
"handlebars": "~1.3.0"
},
}
This JSON file tells Node what packages it needs to install. This helps to get other developers get up-and-running very quickly, as you'll see in the next step.
2) Installing Node Dependencies:
In your terminal/command prompt/powershell, cd into your projects root directory and run the following commands:
npm install grunt -g
npm install grunt-cli -g
And to install the dependencies listed in your package.json:
npm install
Now you've installed all of the node dependencies that you need. In your projects root directory, you'll see a node_modules folder.
3) Configuring Grunt:
In your projects root directory, create a file named Gruntfile.js. Paste the following into that file:
module.exports = function(grunt) {
var TEMPLATES_LOCATION = "./src/templates/", // don't forget the trailing /
TEMPLATES_EXTENSION = ".hbs",
TEMPLATES_OUTPUT_LOCATION = TEMPLATES_LOCATION, // don't forget the trailing /
TEMPLATES_OUTPUT_FILENAME = "compiled_templates.js"; // don't forget the .js
grunt.initConfig({
watch: {
handlebars: {
files: [TEMPLATES_LOCATION + '**/*' + TEMPLATES_EXTENSION],
tasks: ['handlebars:compile']
}
},
handlebars: {
compile: {
src: TEMPLATES_LOCATION + '**/*' + TEMPLATES_EXTENSION,
dest: TEMPLATES_OUTPUT_LOCATION + TEMPLATES_OUTPUT_FILENAME,
options: {
amd: true,
namespace: "templates"
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-handlebars');
grunt.loadNpmTasks('grunt-contrib-watch');
}
4) Configuring to Your Liking:
If you are not using Require.js, you'll want to change handlebars.compile.options.amd to false. You may also want to tweak the namespace option to your liking. If you're using require/amd modules, the namespace property is unimportant (it's default is "JST", if you remove it).
Because all project structures are a little bit different, you'll need to configure the Gruntfile just a little bit. Modify the constants TEMPLATES_LOCATION, TEMPLATES_EXTENSION, TEMPLATES_OUTPUT_LOCATION, TEMPLATES_OUTPUT_FILENAME to fit your project.
If your templates are scattered throughout your application, you'll want to change TEMPLATES_LOCATION to the lowest directory possible. Make sure your templates are isolated from your node_modules, bower_components or any other 3rd party directories, because you don't want Grunt to compile 3rd Party templates into your applications compiled templates. If you include all of your own code in a ./src, ./js, ./app directory, you'll be okay.
5) Compiling templates with Grunt:
To run grunt in the background, open your terminal and cd into your projects root directory and run the command: grunt watch:handlebars (just grunt watch works as well). With grunt running in the background, all changes to your template files will be automatically compiled and the output file specified handlebars.compile.dest will be rewritten as needed. The output will look something like this:
Running "watch" task
Waiting...
To run the compile task alone, open your terminal and cd into your projects root directory and run the command: grunt handlebars:compile (just grunt:handlebars works as well). The output will look something like:
Running "handlebars:compile" <handlebars> task
File "./src/templates/compiled_templates.js" created.
Done, without errors.
For Handlebars 2.0.0 alpha Update:
#Macro has explained quite clearly how it works with 1 piece of template, please see this answer for how to make handlebars.js works
If you see "TypeError: 'undefined' is not a function" when using precompiled templates:
This error happened at "handlebar.runtime.js" line 436 when evaluting templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data),
because the compiler npm installed is not matching the one used by the browser. The latest stable version downloaded is v1.3.0 while if you use npm install handlebars, it will install 2.0.0-alpha4 for you.
Please check it out using
handlebars any_your_template_before_compile.handlebars | grep "compiler"
which will give you like, if you indeed installed handlebar 2.0.0-alpha4:
this.compiler = [5, '>=2.0.0']
With the first number stands for the version for your handlebar compiler. Type in the following code in browser console, see if the result match the version.
Handlebars.COMPILER_REVISION
If you have compiler 5 with browser revison 4, then you will have the above problem.
To fix it, install handlebar 1.3.0 with the following command
npm install handlebars#1.3.0 -g
Then try to compile it again, you will see this time, it gives you matching version info and you are good to go with the precompiled templates.
this.compilerInfo = [4, '>=1.0.0']
Just explain if you have tons of templates:
Firstly externalize each piece of your braced templates: event.handlebars, item.handlebars, etc... You can put them all in one folder, say "/templates"
Compile all the files and concatenate it into 1 file with the following command:
handlebars *.handlebars -f templates.js
And include handlebars.runtime, this file in your HTML
<script src="/lib/handlebars.runtime.js"></script>
<script src="templates.js"></script>
The templates will be injected into Handlebars.templates by their name. For example, event.handlebars can be accessed by Handlebars.tempaltes['event'].

Categories

Resources