How to load front end NPM/Yarn packages with RequireJS - javascript

I have a front end SPA written in Knockout. Due to its size, I want to split it into multiple files, making my folder structure look something like this:
node_modules
|- knockout
\- requirejs
components
|- MyFutureViewModel.js
\- etc.
index.html
app.js
Index.html looks something like this:
<!DOCTYPE html>
<html>
<head>
<title>NCounter</title>
<meta charset="utf-8">
<script type="text/javascript" data-main="app.js" src="./node_modules/requirejs/require.js"></script>
</head>
<body>
<p>The name is <input data-bind="value: name" /></p>
<p>You entered <span data-bind="text: name"></span>.</p>
</body>
</html>
My app.js file looks something like this:
requirejs.config({
//Pass the top-level main.js/index.js require
//function to requirejs so that node modules
//are loaded relative to the top-level JS file.
nodeRequire: require
});
require(['knockout'], function(ko) {
var viewModel = function() {
name: ko.observable('name')
};
ko.applyBindings(viewModel);
});
However, requirejs does not appear to see knockout. Is there a configuration or step I'm missing?

You have to modify your RequireJS config
requirejs.config({
baseUrl: "node_modules/",
paths: {
// assuming that knockout lives in the node_modules/knockout/knockout.js file
// if not, you have to specify relative to `node_modules` path
// without .js extension
'knockout': 'knockout/knockout',
}
});

Related

Vue CLI - combine build output to a single html file

I have a vue project created with vue-cli. The normal output when running yarn build is a dist folder with an index.html and a js and css sub-directory with the corresponding .js and .css files.
I want the build output to be a single html file that contains the js and css.
I added a vue.config.js file in the root of my project and set it to output a single js file and that is working ok. But I want to only have a single html file with the js and any css already on the html file.
module.exports = {
css: {
extract: false,
},
configureWebpack: {
optimization: {
splitChunks: false
}
}
}
Basically I want my html file to be something like this:
<html lang=en>
<head>
... meta tags
<title>my title</title>
</head>
<body>
<div id=app></div>
<script>
// contents of the output js file here
</script>
</body>
</html>
Is this possible?
Using Vue 3.9.3
Someone answered with a suggestion to look into html-webpack-inline-source-plugin but removed their answer. But that was exactly what I needed to get this done.
The plugin is not Vue or Vue-CLI specific but it works if you do the following:
1) Add a vue.config.js file in the root of the app.
2) The linked plugin above is actually an extension of another package. You need both.
npm install --save-dev html-webpack-plugin
npm install --save-dev html-webpack-inline-source-plugin
3)
// vue.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
module.exports = {
css: {
extract: false,
},
configureWebpack: {
optimization: {
splitChunks: false // makes there only be 1 js file - leftover from earlier attempts but doesn't hurt
},
plugins: [
new HtmlWebpackPlugin({
filename: 'output.html', // the output file name that will be created
template: 'src/output-template.html', // this is important - a template file to use for insertion
inlineSource: '.(js|css)$' // embed all javascript and css inline
}),
new HtmlWebpackInlineSourcePlugin()
]
}
}
4) Add a template. This is necessary for working in the Vue context because without this the output html file by default won't have the necessary <div id="app"></div> and Vue won't mount to anything. I basically took the normal output html file and modified it a little.
<!-- output-template.html -->
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset=utf-8>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=viewport content="width=device-width,initial-scale=1">
<title>example title</title>
</head>
<body><noscript><strong>We're sorry but my example doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
<div id=app>
</div>
<!-- plugin will insert js here by default -->
</body>
</html>
Then build like normal and the output.html file will be in the /dist folder

How to automatically include js/css file?

In normal web project, we have to inlcude js/css file like this:
<link type="text/css" rel="stylesheet" href="/css/main.css" />
<script type="text/javascript" src="/scripts/js/main.js"></script>
whenever we add a new css file or js file we have to add one more line.
In Meteorjs, its automatically taken care by Meteor itself, but for non-meteor project, can we do that?
for example, I just put any js file into js folder and I don't need to care about it anymore.
use the "browserify"
in the "package.json"
..."scripts": {
"build": "browserify public/javascripts/main.js > public/javascripts/main-build.js",....
in the "main.js"
var ObjectName = require('./another_file');
html
<script src="javascripts/main-build.js"></script>
another_file.js
ObjectName = function(){ ..... }
module.exports = ObjectName;

RequireJS, jquery still undefined

I know this has already been discussed, but after searching for a while I can't figure out why my small setup does not load jquery correctly with requireJS.
I'm running a small sample html page from 'file://' and try to load 2 modules with a call to require:
jquery
a custom module I wrote
If my custom module loads correctly (i can use it with its alias in the require call, jquery is always undefined)
I tried to setup paths in require.config, as well as shim (exporting '$' or 'jQuery') but it does not work.
The only way i can get jquery to correctly load, is removing all paths definition and naming the jquery file on my filesystem 'jquery.js'
Here is my config:
main.js:
require.config({
baseUrl: 'scripts',
paths: {
jquery: 'jquery-2.1.3.min' //does not work
//jquery: 'http://code.jquery.com/jquery-2.1.3.min.js' //does not work either
}
});
console.log( "main.js is loaded" ); //this is correctly ouputed to the console
test.html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<!--
... page content ...
-->
<!-- main.js is loaded from data-main -->
<script data-main="scripts/main" src="scripts/require.js"></script>
<script type='text/javascript'>
require(
['jquery','custom'],
function($, Custom){
console.info('$:');
console.info($); //outputs undefined
console.info('Custom:');
console.info(Custom); //outputs my custom object
});
</script>
</body>
</html>
Once again, it works if I remove all paths definition for jquery, and simply name my jquery js file 'jquery.js' but this is sloppy.
Can somebody points me on the right way to go ?
The problem is that you load your RequireJS configuration, which is in main, through data-main. The data-main attribute merely tells RequireJS to load the module listed there but the loading is still asynchronous, so main may load after RequireJS tries to load jQuery. When this happens, RequireJS fails to find jQuery and $ is undefined. (Incidentally, I'd suggest setting enforceDefine to true in your RequireJS configuration so that you get an error message telling you when a module has not loaded. That's a better clue than having an undefined symbol.)
One solution would be to move your configuration outside of main. So you remove it from main and add it to a script element in front of the one loading RequireJS:
<script>
// RequireJS will use the value of `require` as its configuration.
require = {
baseUrl: 'scripts',
paths: {
jquery: 'jquery-2.1.3.min'
},
enforceDefine: true
};
</script>
<script src="scripts/require.js"></script>
Or you can nest require calls to force main to load before any other module:
require(['main'], function () {
require(['jquery','custom'], ...
});

why doesn't this simple require.js setup work?

I'm trying to learn how to use require.js to load my scripts, but something is wrong with my setup/understanding. I can't see what is wrong. It is something simple with the setup that I'm missing.
When I load this index.html page in chrome, none of the script require.js action is working.
***index.html
<html>
<head>
<title>My Sample Project</title>
</head>
<body>
<h1 class="h1">sample project header</h1>
<script data-main="main" src="require.js"></script>
</body>
</html>
***main.js
(function() {
requirejs.config({
//By default load any module IDs from baseUrl
baseUrl: '',
// paths config is relative to the baseUrl, and
// never includes a ".js" extension
paths: {
'small-blue-mod': './a-script'
}
});
// Start the main app logic.
requirejs(['small-blue-mod'], function (sbm) {
alert(sbm.color);
});
})();
***small-blue-mod.js
define({
color: "blue",
size: "small"
});
*File System looks like...
index.html
main.js
require.js
FOLDER called "a-script"
a-script folder contains small-blue-mod.js
The path small-blue-mod refers to the a-script folder, either require it like small-blue-mod/small-blue-mod or recommended change your path to this:
paths: {
'small-blue-mod': './a-script/small-blue-mod'
}

Why is my javascript file not loading?

I'm new to both JavaScript & AngularJS. I'm building an app which talks to a building management system: it has a module called 'JaceMod' containing a service to contact the BMS and retrieve data from it. I'm adding functionality to graph data using Dygraphs, and I've created a directive to instantiate a graph and populate it with data.
I would like to put my Dygraphs directive into a module called 'dygraphs', in a separate file 'angular-dygraphs.js', so I can re-use it later, and require this module as a dependency in 'JaceMod'. Like this:
'use strict';
console.log('Loading angular-dygraphs.js');
angular.module('dygraphs', []);
angular.module('dygraphs').directive('mrhDygraph', function ($parse) {
return {
...etc ...
};
});
And then the main file 'app.js':
'use strict';
console.log('Loading app.js');
angular.module('JaceMod', ['dygraphs']);
angular.module('JaceMod').factory('JaceData', function ($http, $timeout) {
var JaceDataService = {};
...etc ...
return JaceDataService;
});
angular.module('JaceMod').controller('myCtrl', function myCtrl($scope, JaceData) {
JaceData.initialise($scope);
JaceData.subscribe($scope);
});
The html looks like this:
<html lang="en" ng-app="JaceMod">
<head>
<meta charset="utf-8">
<title>AngularJACE</title>
<style type="text/css">
body {font-family: Trebuchet MS,Helvetica,Arial,sans-serif; }
</style>
</head>
<body ng-controller="myCtrl">
<div mrh-dygraph="graphData"
mrh-dygraph-options="{xlabel: 'Local Time', legend: 'always',
showRangeSelector: true, labels: ['Time', 'Outside'],
ylabel: 'Temperature (ÂșC)', height: 420}"
style="width: 100%; height 420px;"/>
<script src="dygraph-combined.js"></script>
<script src="angular.js"></script>
<script src="angular-dygraphs.js"/>
<script src="app.js"></script>
</body>
But when I load this html, I see my console message 'Loading angular-dygraphs.js', followed by an eror 'Error: No module: JaceMod'. I never see the log message in app.js. If I switch the load order of the last 2 javascript files round, I see that app.js loads and the error message is 'Error: No module: dygraphs'. The second listed file seems never to be loaded.
If I copy the code from angular-dygraphs.js into app.js, leaving the directive in its own module, it works.
Why is this happening? Is it not possible to put modules in their own files, or something? The documents haven't been much help...
EDITED TO ADD
The html & js files are all in the same directory, including the dygraphs & angular libraries. I'm loading the html from file, no server involved.
UPDATE
I copied everything from angular-dygraphs.js into app.js, and commented out the entirety of angular-dygraphs.js: still getting the problem, app.js does not load.
Your angular-dygraphs.js script tag isn't closed so it never reads app.js. Script tags cannot be self-closing:
<script src="angular-dygraphs.js"/>
should be
<script src="angular-dygraphs.js"></script>

Categories

Resources