how webpack replace a string in .ts file? - javascript

I have a angular 2 project originally used to bundle works with systemjs.
now i want use webpack to bundle this project.
But the original code has a lot of such a string. such as
I want to replace all the strings with the absolute directory where the files are located.The original time, prod mode or dev mode, this string will be replaced with a path string.
Now I would like to use a webpack to replace this string before a .ts file is compiled. What kind of plugin should I use?
for example:
login.module.routing.ts
{ path: 'login', /app/src/login#LoginModule },
I want replace before use webpack compile.
such as : { path: 'login', /root/myproject/app/src/login#LoginModule },
thanks very much !

For this requirement you can use string-replace-webpack-plugin.
Usage Example:
var StringReplacePlugin = require("string-replace-webpack-plugin");
module.exports = {
module: {
loaders: [
// configure replacements for file patterns
{
test: /index.html$/,
loader: StringReplacePlugin.replace({
replacements: [
{
pattern: /<!-- #secret (\w*?) -->/ig,
replacement: function (match, p1, offset, string) {
return secrets.web[p1];
}
}
]})
}
]
},
plugins: [
// an instance of the plugin must be present
new StringReplacePlugin()
]
}
I hope this helps to solve your problem.

Related

How to write regEx for node modules path?

I have the following library inside my node_modules folder
C:\myProject\node_modules\#ng-select\ng-option-highlight
I need to write regex which will point to this file path ( webpack needs regex )
So what i did until now is this
/node_modules\/#ng-select/,
this is code that i found from somewhere else I don't know even if it is correct
now if i try to add ng-option-highlight I get error from webpack
An unhandled exception occurred: Invalid regular expression flags
/node_modules/#ng-select/ng-option-highlight
How can i edit this regex file path here ?
This is my webpack config file
const webpack = require('webpack');
const TerserPlugin = require("terser-webpack-plugin");
const linkerPlugin = require("#angular/compiler-cli/linker/babel");
const path = require('path');
module.exports = {
/**
* Remove all unused MomentJS locales
* The problem is described in this article
* https://medium.jonasbandi.net/angular-cli-and-moment-js-a-recipe-for-disaster-and-how-to-fix-it-163a79180173
*/
plugins: [
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /de|en|fr|it/),
],
module: {
rules: [
{
include: /node_modules/,
test: /\.mjs$/,
type: 'javascript/auto'
}
]
},
plugins: [
new webpack.DefinePlugin({
ngDevMode: true,
}),
new webpack.LoaderOptionsPlugin({
options: {
rules: [
{
test: /.*\.d.ts$/,
include: /node_modules\/#ng-select/,
use: {
loader: 'babel-loader',
options: {
configFile: false,
plugins: [linkerPlugin],
}
}
}
]
}
})
],
}
To indicate that something is a regex, a regex is enclosed with slashes, like so: /<regex>/. In this specific case, that's rather confusing, because paths often start and end with slashes as well.
To write a path in a regex, we need to enclose it with slashes and escape the inner slashes using backslashes to not confuse the parser. The value for your new path will thus be /node_modules\/#ng-select\/ng-option-highlight/.

How to use gulp webpack-stream to generate a proper named file?

Currently we're using Webpack for our Module loader, and Gulp for everything else (sass -> css, and the dev/production build process)
I want to wrap the webpack stuff into gulp, so all I have to do is type gulp and it starts, watches and runs webpack and the rest of what our gulp is setup to do.
So I found webpack-stream and implemented it.
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack({
watch: true,
module: {
loaders: [
{ test: /\.css$/, loader: 'style!css' },
],
},
}))
.pipe(gulp.dest('dist/bundle.js'));
});
The problem is that it generates a random character name for the .js file, how are we suppose to use that in our app?
From the github repo:
The above will compile src/entry.js into assets with webpack into dist/ with the output filename of [hash].js (webpack generated hash of the build).
How do you rename these files? Also the new gulp task generates a new file everytime I save an edit:
I can't use c2212af8f732662acc64.js I need it to be named bundle.js or something else normal.
Our Webpack config:
var webpack = require('webpack');
var PROD = JSON.parse(process.env.PROD_DEV || '0');
// http://stackoverflow.com/questions/25956937/how-to-build-minified-and-uncompressed-bundle-with-webpack
module.exports = {
entry: "./entry.js",
devtool: "source-map",
output: {
devtoolLineToLine: true,
sourceMapFilename: "app/assets/js/bundle.js.map",
pathinfo: true,
path: __dirname,
filename: PROD ? "app/assets/js/bundle.min.js" : "app/assets/js/bundle.js"
},
module: {
loaders: [
{ test: /\.css$/, loader: "style!css" }
]
},
plugins: PROD ? [
new webpack.optimize.UglifyJsPlugin({minimize: true})
] : []
};
There was a comment to Leon Gaban's answer as to what his webpack.config.js looked like. Rather than answer that within a comment, I'm providing it here so it formats better.
Per the docs for webpack-stream, "You can pass webpack options in with the first argument"...
So, I did the following to force webpack to use the same output name each time (for me, I used bundle.js):
gulp.task('webpack', ['babelify'],
() => {
return gulp.src('Scripts/index-app.js')
.pipe(webpack({output: {filename: 'bundle.js'} }))
.pipe(debug({ title: 'webpack:' }))
.pipe(gulp.dest('Scripts/'));
});
The key being the options inside webpack(), which are:
{output: {filename: 'bundle.js'} }
As recommended in docs you should use the vinyl-named package on the pipe before webpack-stream. This way you can use a more cleaner Webpack configuration. The following is the task definition i use myself:
'use strict';
const gulp = require('gulp'),
named = require('vinyl-named'),
webpack = require('webpack-stream');
gulp.task('webpack', function () {
gulp.src(['./src/vendor.js', './src/bootstrap.js', './src/**/*.spec.js'])
.pipe(named())
.pipe(webpack({
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['es2015', 'angular2']
}
}
]
}
}))
.pipe(gulp.dest('./build'))
});
The only problem i'm facing with this task definition is that the subfolder are loosed. For example ./src/components/application.spec.js will produce ./build/application.spec.js instead of ./build/components/application.spec.js.
Ah I read on a bit further and figured it out:
gulp.task('webpack', function() {
return gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
.pipe(gulp.dest('app/assets/js'));
});
^ here I can just pass in my actual webpack.config and it will use the paths I have already set in there. In my case I just removed app/assets/js since I have that path in now gulp instead.
Still no earthly idea though, why with the first task I created, it generates random hash filenames?
Rather than giving your javascript a fixed filename, a better solution would be to use gulp-inject and insert the generated hash filename into a script tag. This means you don't have to worry about cache expiry on the compiled javascript (which is why the hash filename is being used in the first place).
const inject = require('gulp-inject');
gulp.task('webpack', function() {
const index = './src/index.html';
const scripts = gulp.src('entry.js')
.pipe(webpack( require('./webpack.config.js') ))
.pipe(gulp.dest('dist/js'));
return target
.pipe(inject(scripts))
.pipe(gulp.dest('dist/'));
});
and of course you need the inject section in your src/index.html:
<!DOCTYPE html>
<html>
<head>
<title>index page</title>
</head>
<body>
<!-- inject:js -->
<!-- endinject -->
</body>
</html>

Make webpack's library output compatible with babel6

Babel's 6th version changes the functioning of export default and in particular its relation with commonjs require.
To summarise, while until babel5, require('module') where giving the default export of the module, it now always returns the module object containing all of the exports of the module.
If one only wants the default, he/she must use require('module').default.
As explained here, there is very good reasons behind this and the aim of this question is not to break or hack this behaviour.
However, if one is building a library, he/she usually does not want to distribute a module but the export value of his library (e.g. a function, whatever module system is used internally).
This is well dealt with by webpack and the output.library configuration when using commonjs or AMD. Because prior babel's versions allowed the default export to be required with commonjs, babel was also compatible with this mechanism. However it is not the case anymore: the library now always provides an es6 module object.
Here is an example.
src/main.js
export default "my lib content";
webpack.config.js
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.join(__dirname, "dist"),
filename: "mylib-build.js",
library: 'myLib'
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.join(__dirname, "src"),
query: { presets: ['es2015'] }
}
]
}
};
test.html
<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>
This is a very simple example but I obviously want the export of mylib to be the string "my lib content" instead of { default: "my lib content" }.
One solution could be to create an export source file in commonjs to perform the transformation:
module.exports = require('./main').default;
However I find this solution quite poor. One should be able to solve it at the compilation level, without changing the source code.
Any idea?
Was just going at this my self. Whether one like to call it a workaround or solution, there seem to be a Babel plugin that "solve it".
Using the plugin babel-plugin-add-module-exports as referenced in https://stackoverflow.com/a/34778391/1592572
Example config
var webpackOptions = {
entry: {
Lib1: './src/Lib1.js',
Lib2: './src/Lib2.js'
},
output: {
filename: "Master.[name].js",
library: ["Master","[name]"],
libraryTarget: "var"
},
module: {
loaders: [
{
loader: 'babel',
query: {
presets: ['es2015'],
plugins: ["add-module-exports"]
}
}
]
}
};
This yields Master.Lib1 to be lib1 instead of Master.Lib1.default.
Webpack 2 now supports es6 modules which partially solves this issue. Migrating from webpack 1 to webpack 2 is relatively painless. One just needs to remember to disable babel's es6 module to commonjs conversion to make this work:
.babelrc
{
"presets": [
["es2015", {"modules": false}]
]
}
However, unfortunately, it does not work properly with export default (but an issue is opened, hopefully a solution will be released eventually).
EDIT
Good news! Webpack 3 supports the output.libraryExport option that can be used to directly expose the default export:
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "mylib-build.js",
library: "myLib",
// Expose the default export.
libraryExport: "default"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.resolve(__dirname, "src")
}
]
}
};
You can use this solution (this is more like workaround, but it allow you to keep your sources from change):
There is a loader called callback-loader. It allow you to change your sources in a build time by calling a callback and put a result instead of it. In other words you can turn all you require('module') into a require('module').default automatically in a build time.
Here is your config for it:
var webpackConfig = {
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
...
]
},
...
callbackLoader: {
require: function() {
return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
}
}
};

How to use Webpack with Angular + templateCache?

I'm learning Webpack. I made an App with Angular and I use templateCache to generate all my html views in one js file than require in App. It works cool. But then the Webpack job:
entry: {
app: ["bootstrap-webpack!./bootstrap.config.js", './app/app.js'],
vendor: ['angular', 'bootstrap', 'angular-ui-router', 'oclazyload']
},
output: {
path: path.join(__dirname, "dist"),
filename: '/bundle.js'
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(
/* chunkName= */ "vendor", /* filename= */ "/vendor.bundle.js"),
That was the part of my webpack config. As the result I get directory "dist" with "bundle.js" && "vendor.bundle.js" and index.html. After that I start server and my App says that it can't GET views. Why? :( As I understand all my views have to be bundled and should be available in the "dist" directory.
I do not use the templateCache at all. Since Angular directives also accept a template as string, I just require() the template with the html-loader.
function MyDirective() {
return {
restrict: "E",
replace: true,
template: require("./MyDirective.html")
};
}
// in your webpack.config.js
module.exports = {
module: {
loaders: [
{ test: /\.html$/, loaders: ["html"] }
]
}
}
Its late but might as well share this. If you really want to use html fragments maybe for
<div ng-include="'file.tplc.html'"></div>
here is how I made it work
var appMod = angular.module('app');
appMod.run(function($templateCache) {
function requireAll(requireContext) {
return requireContext.keys().map(function(val){
return {
// tpl will hold the value of your html string because thanks to wepbpack "raw-loader" **important**
tpl:requireContext(val),
//name is just the filename
name : val.split('/').pop()
}
});
}
// here "../" is my app folder root
// tplc is the naming convention of the templates
let modules = requireAll(require.context("../", true, /\.tplc\.html$/));
modules.map(function (val) {
$templateCache.put(val.name, val.tpl);
})
});

In webpack, how can I have different output directories for multiple entry points?

I have the following webpack configuration with multiple entry points...
module.exports = {
entry: {
somePage: "./scripts/someDir/somePage.js",
anotherPage: "./scripts/someDir/someSubDir/anotherPage.js"
},
output: {
path: path.resolve(__dirname, 'out'),
filename: '[name].js'
},
...
Is it possible to set a different output path for each entry?
Instead of getting an output of...
/out/somePage.js
/out/anotherPage.js
I want...
/out/someDir/somePage.js
/out/someDir/someSubDir/anotherPage.js
The ideal solution for me would be for output.path to accept a function. For example...
...
output: {
path: function (name, hash) {
return path.resolve(__dirname, myMapOfFilenamesToDirs[name]);
},
filename: '[name].js'
},
Does anyone know if this is possible or if there is an existing plugin that can accomplish this?
EDIT I don't want to use multiple config entries (multi-compiler) because I won't be able to create a shared file among the entry points with CommonsChunkPlugin anymore
A bit hacky, but this should do the trick.
module.exports = {
entry: {
"somePage": "./scripts/someDir/somePage.js",
"someSubDir/anotherPage": "./scripts/someDir/someSubDir/anotherPage.js"
},
output: {
path: path.resolve(__dirname, 'out/someDir'),
filename: '[name].js'
},
// Etc.
}
You cannot set the path to a function, webpack won't call it for you.
You can return an array of configurations for webpack to execute. I think that will give you enough control over the output path to achieve what you need.
I had the same issue today, adding on to the answer from #quentin-roy, https://stackoverflow.com/a/33086806/6552940
You can also create the output path mappings for input files using glob and the following callback. Adjust your glob pattern according to your needs. The following pattern and callback if applied on the directory structure
- src
- file01.ts
lib
- file02.ts
will result in
- dist
- file01.js
lib
- file02.js
config = {
entry: () => {
const entries = {};
glob.sync("./src/**/*.ts").forEach(filePath => {
entries[
path
.relative("./src", filePath)
.replace(path.extname(filePath), "")
] = filePath;
});
console.debug(
`Entries created:\n${JSON.stringify(entries, undefined, 4)}`,
);
return entries;
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
}
After some evaluations I'm using the Webpack-watched-glob-entries-plugin for some time, because it is small, does what we need and works also in watch mode.
If you need even more ideas take a look here Wildcards in entry points.

Categories

Resources