I am using requirejs to build frontend on play 2.2 framework. play provides great development/stage code difference for this case. In devel mode I am working with browser based requirejs and in stage I am using precompiled with r.js version of the project. But one feature fails - is it possible to distinguish on javascript side is it development mode or not and remove part of code during compilation or something like:
#ifdef DEVELOPMENT
code in Development only
#endif
By default r.js uses UglifyJS to optimize your modules. In r.js' configuration you can use the uglify option to send configuration options to UglifyJS. For instance,
uglify: {
defines: {
DEV: ['name', 'false']
}
},
This would tell uglifyjs replace every instance of the symbol DEV with the name false. Then, parts like this:
if (DEV) {
// ....
}
would be automatically removed by uglifyjs as being unreachable.
See uglifyjs' documentation for the details of how that works.
You might also want to look at UglifyJS2, because it perhaps does more than UglifyJS. You can tell r.js to use it by setting the optimize option to uglify2, and use the uglify2 option to control what it does.
UglifyJS2 from sbt-rjs 1.0.7 already has some other usefull option:
uglify: {
drop_console: true
}
uglify2: {
compress: { drop_console: true }
}
it removes all console.log messages from minified script.
Related
I'm trying to include a VLC video playing in my Electron app, which is possible through WebChimera.js. This package is distributed a bit weirdly (to me at least), to use it you need to require wcjs-prebuilt, specify some settings in package.json and configure Webpack to allow importing .node files as explained in this Wiki page for WebChimera.js.
However I believe this Wiki page is outdated, as loaders isn't a valid key anymore in a Webpack config. I'm not very experienced using Webpack so most of this is new to me. Also note that this Wiki explanation used a fork of node-loader, although this fork seems to be merged to the actual node-loader now (?).
I now use this Webpack config:
target: 'node',
node: {
__dirname: false,
},
module: {
rules: [
{
test: /\.node$/,
loader: 'node-loader',
},
],
},
externals: [
'wcjs-prebuilt',
],
Because that's how the Webpack page for node-loader seems to do it. However this doesn't work for me, as now I get the error: Uncaught ReferenceError: exports is not defined in the chunk-vendors.js:1 file. Which probably means it's trying to use require syntax somewhere it shouldn't, but I have no idea how to proceed here. This error still occurs in an otherwise empty vue-electron project (template here), when I comment out all WebChimera related code. WebChimera code I use for testing in this project (Right now I'm just trying to get it to work):
const wcjs = require("wcjs-prebuilt");
console.log(wcjs)
When I remove the webpack config I showed above, the error about exports is not defined goes away, which is why I believe it's something in my webpack config rather than my code causing that error.
Long story short, I want to know how configure webpack to allow me to import or a require a .node file.
I'm able to get vue electron building with wcjs-prebuilt using a vue.config.js like this. You will also need to set the VLC_PLUGIN_PATH correctly or video won't play.
module.exports = {
configureWebpack: {
externals: {
'wcjs-prebuilt': 'commonjs wcjs-prebuilt'
},
},
chainWebpack: (config) => {
config.module
.rule('node')
.test(/.node$/i)
.use('node-loader')
.loader('node-loader')
.end()
},
pluginOptions: {
electronBuilder: {
externals: ['wcjs-prebuilt']
}
}
}
Since posting the question I've switched to mpv.js for video playback so this isn't an issue for me anymore. However after posting this question I experimented a lot, after I finally got it working in Webpack somehow (see first link below), it worked for me but with distorted video. The node file added some properties to an array which Webpack somehow stripped away, causing some missing values in the video renderer. I fixed that by forking WebChimera and editing the C++ code so that the values weren't added as properties but as separate values.
I ended up forking WebChimera.js, wcjs-prebuilt, wcjs-renderer, and libvlc_wrapper to get VLC to finally work with Webpack+Electron, all that probably wasn't necessary but oh well..
Links for whoever might be interested:
https://github.com/RuurdBijlsma/vlc-video-demo (working demo project featuring VLC in an Electron+Webpack+Vue project.)
https://github.com/RuurdBijlsma/libvlc_wrapper
https://github.com/RuurdBijlsma/wcjs-renderer
https://github.com/RuurdBijlsma/WebChimera.js
https://github.com/RuurdBijlsma/wcjs-prebuilt
I'm searching a Flask-Assets filter that allows me to uglify javascript and support ES6 syntax. I tried to use uglifyjs-es binary instead of uglifyjs but I can't figure out how to configure my filter to use the uglifyjs-es binary.
I've this:
my_app_js = Bundle(
'js/MyApp.js',
filters='uglifyjs',
output='my_app_js.js'
)
From the Webassets documentation :
UglifyJS is an external tool written for NodeJS; this filter assumes
that the uglifyjs executable is in the path. Otherwise, you may define
a UGLIFYJS_BIN setting
Maybe the solution is there but I can't figure out where and how to change that UGLIFYJS_BIN setting, any idea ?
Also, I read here that uglifyjs-es project isn't maintenained anymore. terser seems to be the alternative, but could it be used as a filter too ?
Edit
If you know a good alternative to uglifyjs-es with a code example, you win a bounty ;)
If you have installed uglifyjs using npm, it should be in the node_modules folder in your project.
You could configure flask as follows:
app = Flask(__name__)
app.config['UGLIFYJS_BIN'] = 'path/to/node_modules/uglify-js/bin/uglifyjs'
Regarding uglifyjs-es, you know it's unmaintained. But if your code gets minified using it, it's still a good option.
As mentioned here:
uglify-js only supports ES5 code as input.
uglify-es also supports ES6, but is buggy and has been abandoned.
Terser's command line is almost as baroque as with ls. I found that easiest for me is to use Rollup with Terser plugin, and there's ready made Rollup filter for webassets. This way all Terser configuration is done in Rollup's config that you specify in filter extra args. Minimal Terser's configuration for ES6 modules:
{
compress: {ecma: 2015, module: true},
mangle: {module: true},
output: {ecma: 2015},
parse: {ecma: 2015},
rename: {},
}
With configured Terser plugin you may now use it as if it was both bundle and minify filters applied:
from flask_assets import Bundle
from webassets.filter import register_filter
from webassets_rollup import Rollup
register_filter(Rollup)
all_css = Bundle(
'css/app.scss', filters='node-scss,cleancss', output='dist/all.%(version)s.min.css',
)
all_js = Bundle(
'js/main.js', filters='rollup', output='dist/all.%(version)s.min.js',
)
Looks like you need to set an environment variable named UGLIFYJS_BIN. See here for Linux instructions, or over here for Windows instructions.
In Grunt or Gulp, I used to define all the requirements myself, like: stuff should be minified only for production, livereload should be enabled only in dev server.
Webpack handles this on its own, via its -d and -p options, that toggle the loaders' minimize mode (most loaders ship with their relevant minifiers), the devtool and similar stuff (I don't know what exactly). Most of that "just works".
But on the other hand, there are some libraries that have a development and production mode. For example, React looks at process.NODE_ENV, and if it's production, it disables the propTypes checking (which will be later stripped off by minifier as dead code, thus reducing bundle size). In Webpack, a common way to do this is to use the DefinePlugin.
The problem is, that this plugin should be enabled only in the production build. Some people go as far as having 2 separate webpack configs because of that. This seems overkill, since most of the job is already done by webpack. I'd like to avoid this.
To find some better solution, I'd like to understand what exactly changes when I use the -d or -p option, and how does it affect all the loaders and plugins. I didn't find it documented anywhere. The existing documentation just mentions "debug mode" or "watch mode" without explaining what does it actually mean.
Note that I'm not asking for a do-this-and-that answer. A good, detailed explanation would be appreciated.
Shortcuts
Like you said, -d is a shortcut for --debug --devtool source-map --output-pathinfo where:
--debug activates debug mode for loaders and its behaviour depends on each loader. You can activate it via param { debug: true } in your webpack.config.js
--devtool is basically allow to select type of sourcemaps for your files via param { devtool: "sourcemap" } or if you use webpack#2: { devtool: 'eval-cheap-module-source-map' }(See all options)
--output-pathinfo will write original filepath into webpack's requires like this: __webpack_require__(/* ./src/containers/App/App.js */15). You can activate it via: { output: { pathinfo: true } }
Second shortcut, -p stands for --optimize-minimize --optimize-occurence-order where:
--optimize-minimize will add webpack.optimize.UglifyJsPlugin into your module plugins which will minify your code (remove spaces, mangle variable names and other optimizations)
--optimize-occurence-order will add webpack.optimize.OccurenceOrderPlugin to plugin list which will assign your most used dependencies lower ids, so code will be smaller.
For example: you have imported react in every file and webpack will try to require it like that __webpack_require__(1); instead of __webpack_require__(1266);
Putting all together
So, in your case if you have webpack.config.js, you can change it like this:
var webpack = require('webpack');
var webpackConfig = {
// here goes your base config
};
// -d shortcut analogue
if (process.env.NODE_ENV === 'development') {
webpackConfig.debug = true;
webpackConfig.devtool = 'sourcemap';
webpackConfig.output.pathinfo = true;
}
// -p shortcut analogue
if (process.env.NODE_ENV === 'production') {
webpackConfig.plugins.push(new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}));
webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin());
webpackConfig.plugins.push(new webpack.optimize.OccurenceOrderPlugin());
}
module.exports = webpackConfig;
If you want to look at actual parsing of these params, look at https://github.com/webpack/webpack/blob/master/bin/convert-argv.js#L19
When I write tests for my in-browser TS code, I hit the following problem. My "test" code files are located in a separate folder from the "application" code files (an arrangement that I am not willing to give up). Therefore, in order to import my "app" modules, I have to do this:
// tests/TS/SubComponent/Module.Test.ts
import m = module("../../Web/Scripts/SubComponent/Module");
This compiles just fine. But when loaded in browser, it will obviously not work, because from the standpoint of RequireJS running in the browser, the module is located at "app/SubComponent/Module" (after being remapped through web server and RequireJS config).
With TS 0.8.3 I was able to pull off this clever trick, but in 0.9.0 it no longer works, because now the compiler doesn't let me treat a module as an interface.
So the question is: how do you test your client-side code?
Clearly, I can't be the only person to be doing it, can I? :-)
I can't tell if you are using Visual Studio - this next bit is Visual Studio specific...
This is how I do it:
In my test project, I created a folder named "ReferencedScripts" and
referenced the scripts from the project being tested (add existing
item > add as link). Set the file to copy to the output folder.
Source: Include JavaScript and TypeScript tests in Visual Studio.
Using add-as-link makes the scripts available in your test project.
Not using Visual Studio? I recommend creating a task / job / batch file to copy the files into the test folder. You could even use tsc to do this task for you.
I am in the middle of a project where I have to migrate parts of a large javascript project to typescript and this is how I managed to keep the tests running:
Use grunt-typescript task to watch and compile all my .ts files from the source to a tmp folder (with their source-maps). If you only have to deal with typescript files, then you can use the tsc in watch mode to do it as well. The latter would be faster, but the former allowed me to simultaneous edit javascript and typescript files with livereload.
Include the .ts files in karma.conf but don't watch them or include them:
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
// ...
// We want the *.js to appear in in the window.__karma__.files list
{ pattern: 'app/**/*.ts', included: false, watched: false, served: true },
{ pattern: 'app/**/*.js', included: false },
// We do watch the folder where the typescript files are compiled
{ pattern: 'tmp/**/*.js', included: false },
// ...
// Finally, the test-main file.
'tests/test-main.js'
];
Finally, in the test-main.js file, I mangle the names of typescript files and declare them as require modules with the correct paths (to the corresponding .js file) in test-main.js:
var dynPaths = {
'jquery' : 'lib/jquery.min',
'text' : 'lib/text'
};
var baseUrl = 'base/app/',
compilePathUrl = '../tmp/';
Object.keys(window.__karma__.files)
.forEach(function (file) {
if ((/\.ts$/).test(file)) {
// For a typescript file, include compiled file's path
var fileName = file.match(/(.*)\.ts$/)[1].substr(1),
moduleName = fileName.substr(baseUrl.length);
dynPaths[moduleName] = compilePathUrl +
fileName.substr(baseUrl.length);
}
});
require({
// Karma serves files from '/base'
baseUrl: '/' + baseUrl,
paths: dynPaths,
shim: { /* ... */ },
deps: [ /* tests */ ],
// start test run, once requirejs is done
callback: function () {
window.__karma__.start();
}
});
Then as I edit the typescript files, they are compiled and put in the tmp folder as javascript files. These trigger karma's auto watch and it reruns the tests. In the tests, the require calls resolve correctly since we have explicitly overwritten the paths to the typescript files.
I realise that this is a bit hacky, but I had to jump through similar hoops while trying to include all my tests with REQUIRE_ADAPTER. So I assumed that there is no cleaner way of doing it.
Hopefully, if typescript becomes more prevalent, we will see better support for testing.
So here's ultimately what I've done: it turns out that Karma can handle/watch/serve files that are not within the base directory, and it makes them look to the browser in the form of "/absolute/C:/dir/folder/blah/file.js". This happens whenever files -> pattern starts with "../".
This feature can be used to make RequireJS see the whole directory structure exactly as it exists on the file system, thus allowing the tests to import app modules in the form of "../../Web/App/Module.ts".
files = [
// App files:
{ pattern: '../../Web/App/**/*', watched: true, served: true, included: false },
// Test files:
{ pattern: '../js/test/**/*.js', watched: true, served: true, included: false }
];
Reference (version 0.8): http://karma-runner.github.io/0.8/config/files.html
Since the typescript code is compiled to Javascript you can use all Javascript test frameworks.
I am using Jasmine: https://github.com/pivotal/jasmine/wiki
You can write your tests in Typescript with the .d.ts file here: https://github.com/borisyankov/DefinitelyTyped/blob/master/jasmine/jasmine.d.ts
But my client code is rather small and compiled to one output file, so I don't have the module issues that you describe.
Might be that I misunderstood your question - can't comment yet...
The runtime of the browser does not need any typescript information. So your test script should import the compiled ts files the same way as any other javascript files they need. Might be that you have to copy them to a subfolder of your test-project before you run your script.
I assume the bigger problem is that you have no interface information. Why do you want to import these informations instead of referencing them? Especially since importing them will also occur in the browser.
The Reference will only take place in the IDE , so it does not matter in which folders the interface-files are located.
/// <reference path="../../Web/Scripts/SubComponent/Module/References.ts" />
I am looking for a way to prevent r.js (RequireJS' optimization script) from ugylyfying our JS-modules to maintain readability for debugging purposes.
I expect the script (running on Node.js by the way) to have some command line option to be passed.
Unfortunately, the documentation if this tool is rather poor.
Pass optimize=none on the command line to r.js, or include optimize: "none" in your build script.
eg:
({
baseUrl: ".",
paths: {
jquery: "some/other/jquery"
},
name: "main",
out: "main-built.js",
optimize: "none"
})
See http://requirejs.org/docs/optimization.html for more information.
If you check the source, you will see that the default is set to "uglify". Here are the options which are accepted:
uglify: (default) uses UglifyJS to minify the code.
uglify2: in version 2.1.2+. Uses UglifyJS2.
closure: uses Google's Closure Compiler in simple optimization mode to minify the code. Only available if running the optimizer using Java.
closure.keepLines: Same as closure option, but keeps line returns in the minified files.
none: no minification will be performed.