Using a text loader in Webpack for RequireJS text plugin - angularjs

So my AngularJS code looks like this:
define(['angular', 'text!./template.html'], function (angular, template) {
'use strict';
return angular.module('app.widget', [])
.directive('MyWidget', function(){
//.... use the template here
})
I'm using the text plugin for RequireJS to get the html template and use in in the AngularJS directive.
I want to use webpack and it's reading the AMD style code ok but it can't work with the text plugin.
Does anyone know how to get the text-loader for webpack to work with requirejs?
There are some solutions out there and a discussion thread but I can't get them to work.
In my webpack.config.js I've got
loaders: [
{ test: /^text\!/, loader: "text" }
]
Thanks

Actually you need dont need to specify node modules path. It works if you specify just the name of the actual loader like below
resolveLoader: {
alias: { "text": "text-loader" }
},

You need to install raw-loader, which is the webpack equivalent for loading raw files
npm i raw-loader
Then you need to alias the requireJS style with the raw-loader (resolveLoader is to be put in the root of the webpack config object)
resolveLoader: {
alias: {
'text': {
test: /\.html$/,
loader: "raw-loader"
},
}
}
Check this SO question: Webpack loader aliases?

A similar solution which worked for me with Webpack 2.2:
resolveLoader: {
alias: {
// Support for require('text!file.json').
'text': 'full/path/to/node_modules/text-loader'
}
}
And install text-loader:
npm install text-loader

Related

Correct way to load AngularJS templates for Webpack 4?

So far I've been able to bundle up all our controllers/directives/services into 1 script and I also bundled all the vendor scripts into another script file. Now I'm having trouble figuring out what the right way to load the AngularJS template files are. We are currently using Grunt and just copying the exact folder structure over to the dist folder but clearly this won't work in Webpack. Here is an example of our project structure(from the John Papa style guide)
My project inside the dist folder is currently rendered as follows:
Anybody have any input?
AngularJS templates are html files, so you need to add some loader for handling them.
You have multiple options, bundle those html files into the js bundle, using html-loader, pros of this approach is that your app won't make ajax call for the template, cons, your js bundle size will become large.
This will allow you to "require" your html template inside your controllers.
copy those raw files using copy-webpack-plugin, this way it will work the same way it works with Grunt (providing templateUrl path to the file that was copied).
In-order to be specific regarding lazy-loaded files you can attach .lazy.html suffix.
Then, enable file-loader on the .lazy.html files & assign it to templateUrl, and the regular once use with template: require('template.html').
As of best practice, I would "require" critical templates so they will be in the js bundle, and lazy load (via templateUrl) non-critical ones.
This is an example of webpack.config.js file:
module.exports = {
module: {
rules: [
{
test: /\.lazy\.html$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
{
test: /\.html$/,
exclude: /\.lazy\.html$/
use: [
{
loader: 'html-loader',
options: {
minimize: true,
},
},
],
},
],
},
};
// critical.component.js
angular.
module('myApp').
component('greetUser', {
template: require('critical.html'),
controller: function GreetUserController() {
this.user = 'world';
}
});
// none-critical.component.js
angular.
module('myApp').
component('greetUser', {
templateUrl: require('non-critical.lazy.html'),
controller: function GreetUserController() {
this.user = 'world';
}
});

Cant use css modules in React with Bootstrap4

I want to use React CSS modules with Bootstrap.
First, I created a project with the create-react-app command. Then, I installed Bootstrap 4 with NPM and included it in my app.js like:
import 'bootstrap/dist/css/bootstrap.css';
I also did npm eject to have access to my Webpack config file.
Meanwhile I wanted to use CSS modules in my layout.js file, so I went to webpack.config.dev.js and changed modules to true:
test: /\.css$/,
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
modules: true
}
When I change modules to true Bootstrap CSS is not showing correctly. If I change modules to false then Bootstrap is working. Any idea?
Modules: true
Modules: false
Here's a good discussion how to use CSS-modules with other global CSS (e.g. Bootstrap)
https://github.com/css-modules/css-modules/pull/65
Hope it helps.
I had same issue and just loaded bootstrap directly in the index.html from public folder. My project not so big to include bootstrap in the same bundle
P.S.
Actually, I also tried this solution and it works good for me.
https://github.com/css-modules/css-modules/issues/113
I just renamed all of my global css files as .global..css
{
test: /^((?!\.global).)*\.css$/, // anything with .global will not
go through css modules loader
loaders: [
'style-loader',
'css-loader?modules&sourceMap&importLoaders=1&localIdentName=
[name]__[local]___[hash:base64:5]',
'postcss-loader'
]
}

Using Webpack on an existing angular 1.x application

I have an existing, very large, angular 1.x application which runs today ES5 code.
Almost all of the application runs on the same module. My main module is defined in the file "dashboardApp.js".
I want to start using ES6 with modules per component as the app is component structured. For it to run in develpment, I want to start using Webpack.
I tried adding Webpack so I added all the needed npm dependencies and added the following webpack.config.js
var webpack = require('webpack');
module.exports = {
entry: '../app/dashboardApp.js',
output:{
path: __dirname + '/../dst/dist',
filename: 'my.bundle.js'
},
module:{
rules: [{
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules|bower_components)/
}]
}
};
Also, I added to package.json the following property:
"scripts": {
"build": "webpack --config webpack.config.js"
},
and was able to successfully run build and create my.bundle.js. However, when trying to load the app using just the my.bundle.js script, I got an exception:
Uncaught Error: [$injector:modulerr] Failed to instantiate module dashboardApp due to:
Error: [$injector:unpr] Unknown provider: myConsts
myConsts is an angular constant which was included before using Webpack by loading the script and hence my question:
Whats needed in order to transform an existing angular 1.x app that used to load all scripts explicitly to be one Webpack generated script app. What changes I need to do in all my files, that are all defined on the same module, in order to be included in the generated file. I understand that webpack is a module bundler, but I lack the understanding on what I need to do in order to make the old app work with Webpack. Do I need to transform all the files to ES6 module import/export syntax? How does Webpack knows what files to load when the old angular syntax (1 controller/service/constant... per file when all on the same module)? What does it do given the entry point.
Thanks
If your app is using requirejs, then you could achieve it using webpack2. Just configure it properly using rules and aliases. My app too uses requirejs and I successfully managed to replace Grunt with webpack2 after a lot of struggle.
Below is the webpack.config.js file:
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
let basePath = path.join(__dirname, '/');
let config = {
// Entry, file to be bundled
entry: {
'main': basePath + '/src/main.js',
},
devtool: 'source-map',
output: {
// Output directory
path: basePath + '/dist/',
library: '[name]',
// [hash:6] with add a SHA based on file changes if the env is build
filename: env === EnvEnum.BUILD ? '[name]-[hash:6].min.js' : '[name].min.js',
libraryTarget: 'amd',
umdNamedDefine: true
},
module: {
rules: [{
test: /(\.js)$/,
exclude: /(node_modules|bower_components)/,
use: {
// babel-loader to convert ES6 code to ES5 + amdCleaning requirejs code into simple JS code, taking care of modules to load as desired
loader: 'babel-loader',
options: {
presets: ['es2015'],
plugins: []
}
}
}, { test: /jQuery/, loader: 'expose-loader?$' },
{ test: /application/, loader: 'expose-loader?application' },
{ test: /base64/, loader: 'exports-loader?Base64' }
]
},
resolve: {
alias: {
'jQuery': 'bower_components/jquery/dist/jquery.min',
'application': 'main',
'base64': 'vendor/base64'
},
modules: [
// Files path which will be referenced while bundling
'src/**/*.js',
'src/bower_components',
path.resolve('./src')
],
extensions: ['.js'] // File types
},
plugins: [
]
};
module.exports = config;
Let me know if you have any more queries. I still remember how hard I had to try to make things work. WIll be happy to help you!
Putting this here in case anyone else runs into this problem. Essentially what webpack is trying to do is build a dependency graph. Meaning there is an entry point, and then webpack will look at that file and see what it depends on by seeing if there are any imports or require statements in it. It will then travel to the dependency file and bundle that while also looking for more dependencies and so on. In this way, it knows what things need to be loaded before others.
It sounds like you didn't alter your source code to import or require any of the module's dependencies, so Webpack simply built that one file you pointed it to instead of all of the files of your app.
Lets say ModuleA depends on ModuleB and ModuleC.
in ModuleA.js, you'll import (or require) moduleB as well as ModuleC.
In both ModuleB and ModuleC, you'll need to export them and make sure your exporting the .name property from the module since AngularJS wants strings for its dependencies.
The tricky thing about using AngularJS with Webpack, is that Angular has its own Module system which is different from the commonJS pattern or ESModules, so its a bit of an odd combination.
Softvar's solution above works because he told webpack what to bundle when defining his modules under the resolve property. If all of your sub modules are exported, another solution to bundling all of your angular files into one parent module to export, is like this, where the file is index.js and webpack looks here as its entry point:
const modules = [];
function importAll(webpackContext) {
// the webpackContext parameter is a function returned after invoking require.context() that has
// access to all of the resolved paths defined in the require.context call.
// The keys will be an array of all of the resolved module paths returned from the initial
// require.context invocation within the importAll invocation a number of lines below this declaration.
webpackContext.keys()
// this will fetch each module itself and give us access to all of the exports from that module.
// Since we are exporting the angular modules as the default export from all of our index files,
// we are just pushing the default property into the modules array. In this case the default property
// is the string name of the angular module.
.forEach(modulePath => modules.push( webpackContext(modulePath).default) );
}
// recurse through all sub directories in ./src and find the path for each index.js file.
importAll(require.context("./src/", true, /index\.js$/));
// take all of the module's name strings and spread them out as module dependencies.
// export the single module all glued together.
export default angular.module("YOUR_MODULE_NAME", [...modules]).name;

How angular-translate-loader for webpack works?

I'm trying to integrate the webpack loader: angular-translate-loader to my project.
The documentation lacks a full example and I can't figure out how to make everything works together.
What I want:
Have a "languages" folder at the same level of my root component that will contain the locales for other languages like:
locale-fr.json
locale-sp.json
What I tried:
I added this in my webpack.config.js (as per documentation)
module.exports = {
module: {
preLoaders: [{
test: /\.json$/,
loader: 'json'
}],
loaders: [{
test: /\.json$/,
loader: 'angular-translate?module=translations'
}]
},
angularTranslate: {
namespaces: ['app'],
sep: '.',
defaultLocale: 'en'
}
};
And in the root component of my application I got this:
$translateProvider.translations('en', {
TITLE: "Translation is working",
ANOTHER_TEXT: "But is it really working"
})
.translations('fr', localFr)
.registerAvailableLanguageKeys(['en', 'cn', 'fr', 'sp'], {
'gb': 'en',
'es': 'sp'
})
.preferredLanguage('en')
//See http://angular-translate.github.io/docs/#/guide/19_security for more details about Sanitize
.useSanitizeValueStrategy('escape')
//Remember the choice of Language in the local storage
.useLocalStorage();
The default language obviously works (en) but not the others.
I'm missing something but I can't figure out why.
Does someone know of a sample project using angular-translate-loader and webpack ?
I was stuck on the same thing the whole day, but after a lot of trial and error I've finally found a working solution. I have a similiar set-up as you: I have a folder assets/languages at the root of my project, containing languates in JSON files with the format locale-nl.json.
What worked for me was to import angular-translate directly (together with some extra dependencies) instead of using angular-translate-loader:
npm install --save angular-translate angular-sanitize angular-cookies
I then added this to my app.module.js file (which is what I use instead of index.js):
// No "real" module support yet for angular-translate, wo we have to load these manually.
// Reference: https://github.com/angular-translate/angular-translate/issues/1517
import "angular-sanitize";
import "angular-cookies";
import "angular-translate";
import "angular-translate/dist/angular-translate-loader-static-files/angular-translate-loader-static-files.js";
import "angular-translate/dist/angular-translate-storage-cookie/angular-translate-storage-cookie.js";
Then, I define my module and configure the $translate service as follows:
angular.module(MODULE_NAME, [ "pascalprecht.translate", "ngSanitize", "ngCookies" ])
.config(['$translateProvider', function($translateProvider) {
$translateProvider
.useStaticFilesLoader({
prefix: "../assets/languages/locale-",
suffix: ".json"
})
.preferredLanguage('en')
.useCookieStorage()
.useSanitizeValueStrategy('sanitize');
}])
My translation files, e.g. locale-nl.json all contain a single object in this format:
{
"PASSWORD": "Wachtwoord",
"FORGOTPASSWORD": "Wachtwoord vergeten",
"SETTINGS": "Instellingen",
"LOGOUT": "Uitloggen",
"LASTNAME": "Achternaam",
"FIRSTNAME": "Voornaam",
"BIRTHYEAR": "Geboortejaar"
}
Finally, in my HTML code, I call the translations through the $translate directive:
<span translate="SETTINGS">Settings</span>
I don't have time now to create a sample project, but since there were no responses to your question yet I wanted to at least let you what worked for me. I'll see if I have the time to create a sample project this weekend.

How to use webpack with js that isn't exported

I'm trying to figure out how to include this javascript with webpack but I'm struggling. I'm installing an angular module called angular-colorthief via npm. I've installed it and included the module in my webpack config file:
if(TEST) {
config.entry = {};
} else {
config.entry = {
app: './client/app/app.js',
polyfills: './client/polyfills.js',
vendor: [
'angular',
'angular-colorthief',
'angular-animate',
'angular-aria',
'angular-cookies',
'angular-resource',
'angular-route',
'angular-sanitize',
'angular-socket-io',
'angular-material',
'lodash'
]
};
}
The problem is that inside of the angular-colorthief directory is another js which the angular module depends on which is called color-thief.js. This file does not export anything. The angular-colorthief module requires it this way:
use strict';
require("./color-thief");
angular.module('ngColorThief', [])
.provider('$colorThief', [function () {
When I run my app I keep getting an error that ColorThief is undefined because although this script is being included in my vendor bundle, it's not available when the angular module runs. Can anyone help me to fix this? I've tried installing the exports-loader module but I'm unsure how to use it to solve this problem. This is the loader I tried to add but this didn't work.
{
include: require.resolve("./color-thief"),
loader: "exports?ColorThief!node_modules/angular-colorthief/color-thief.js"
}
So I'm not certain if this is the correct solution but it solved my problem. If someone could tell me a better way I would definitely appreciate that. For anyone else, here is what I ended up adding to my loaders.
{
include: require.resolve(path.resolve(__dirname, 'node_modules/angular-colorthief/color-thief.js')),
loader: "exports?ColorThief"
}, {
include: require.resolve(path.resolve(__dirname, 'node_modules/angular-colorthief/angular-colorthief.js')),
loader: "imports?ColorThief=./color-thief.js"
}

Resources