Requirejs optimizer. Exclude all dependencies - ruby-on-rails

I'm using requireJS in Rails application with requirejs-rails gem. What I'm trying to achieve is to create several bundles which contain common code. For example libs_a, libs_b, libs_c, ... Then for some pages of the application create own bundles which contain modules related only to those pages. Problem is that all common modules should be listed for each page to exclude them from optimized files, like:
# config/requirejs.yml
modules:
# in 'libs' all dependencies should be included
- name: 'libs_a'
...
- name: 'libs_b'
...
- name: 'libs_c'
...
# in 'pages' all dependencies should be excluded since all of them are already in 'libs'
- name: 'page_a'
include: ['a1', 'a2', 'a3', ...]
exclude: ['libs_a', 'libs_b', 'libs_c', ...]
- name: 'page_b'
include: ['b1', 'b2', 'b3', ...]
exclude: ['libs_a', 'libs_b', 'libs_c', ...]
bundles:
libs_a: [...]
libs_b: [...]
libs_c: [...]
...
With large number of modules it becomes a bit cumbersome. As I understood there is no option to include in optimized file only those modules which are listed in build config and skip all dependencies. So that repeating exclude: ['libs_a',...] could be avoided. How do you solve such problem?

Just an idea - It should be possible to define build config as a .js file. If you do that, you will be able to include simple logic to amend config with excluded libraries.

Related

Q: How to ensure vendor chunk hash doesn't change with webpacker?

I have a Rails 6 project with webpacker 4.2.2 configured to split vendor chunks into individual files:
# config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.config.merge({
plugins: [
new webpack.HashedModuleIdsPlugin(),
],
optimization: {
minimize: true,
runtimeChunk: 'single',
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
// #see https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName.replace('#', '')}`;
},
priority: 10,
},
}
}
}
})
module.exports = environment
When we precompile our assets, this produces fingerprinted files for each NPM dependency, which we upload for long-term caching and CDN-based distribution.
However, we're noticing that when we add a new library to the pack, this unexpectedly causes a rehash of many chunk files for dependencies that have not changed at all.
For example, this change in my app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import 'msr'
import copy from 'clipboard-copy'
+import axios from 'axios'
will produce the following change in my output chunks (produced from running bin/rails webpacker:compile):
--- a 2020-07-06 18:39:52.202440803 +0000
+++ b 2020-07-06 18:39:52.210440748 +0000
## -1,6 +1,8 ##
-application-1e8721172ae65f57286b.chunk.js
-npm.clipboard-copy-10b42ffbc97b4e927071.chunk.js
-npm.msr-01ea266e2c932167f10b.chunk.js
-npm.rails-a4564cfc542024efeb95.chunk.js
-npm.turbolinks-eeef46ff44962af9ac87.chunk.js
-npm.webpack-7226f5cf46a8c4e61c26.chunk.js
+application-bad0ed20808541f88894.chunk.js
+npm.axios-40b4b54ebace2b9e3907.chunk.js
+npm.clipboard-copy-79d2051f48603e0267e0.chunk.js
+npm.msr-f5a4252b7a7e0a94157f.chunk.js
+npm.process-cfe824ecbab5abe0eecc.chunk.js
+npm.rails-aa1c430d6ceee3ca6bd6.chunk.js
+npm.turbolinks-e28554dbfd4b75aa12e5.chunk.js
+npm.webpack-35f718d9a20b8bca2927.chunk.js
This is a double whammy because of unnecessary cache invalidation and additional CDN transfer costs.
My question is, is there a way to ensure the vendor chunk doesn't get rehashed because of dependency changes?
I don't know if this is a limitation with the way that webpack's SplitChunksPlugin works, but any advice is appreciated.
By the way, I've prepared a minimal Rails project that reproduces the situation I've described above: https://github.com/alextsui05/webpacker-vendor-chunks
A detailed summary is included in the README on the repository, and I invite any answerers to discuss based on that code.
Try setting the option moduleIds: 'hashed'
https://v4.webpack.js.org/configuration/optimization/#optimizationmoduleids

Dart Testing: can I configure platforms to skip certain tags?

With dart_test.yaml it is easy to configure tests with a particular tag to be skipped, with perhaps an override. For example, configure the chromeUnstableOnly tag to cause skipping on all platforms, with a "-P force" preset to make them run:
tags:
chromeUnstableOnly:
skip: "Not implemented on stable yet"
presets: {force: {skip: false}}
In the tests themselves I can configure a skip for a particular platform:
test('Foo Test', () {
// ...
}, onPlatform: {
'chrome_stable': new Skip('Not yet supported on stable.')
});
Is it possible to configure platform-specific skipping based on tags in dart_test.yaml so that unit tests with a 'chromeUnstableOnly' tag is skipped on other platforms? I want to define my tests as follows:
test('Foo Test', () {
// ...
}, tags: ['chromeUnstableOnly']);
I can define presets, and have presets select platforms:
# This works, but can I set a default preset for "pub run"?
presets:
stable:
exclude_tags: chromeUnstable
platforms:
- chrome_stable
I can't find a way to specify a default preset though. It also doesn't seem to work to use exclude_tags or add_presets under platforms:
define_platforms:
chrome_stable_with_exclude_tags
name: Chrome Stable
extends: chrome
exclude_tags: [chromeUnstableOnly] # does not seem to work
chrome_stable_with_preset
name: Chrome Stable
extends: chrome
add_presets: [stable] # does not seem to work
Yes, it's possible to skip tests with certain tags on certain platforms by adding on_platform fields to tags:
tags:
chromeUnstableOnly:
on_platform:
chrome_stable:
skip: "Not implemented on stable yet. Use -P force to force tests."
presets: {force: {skip: false}}

How to add multiple source_path in Rails Webpacker

We are using Webpacker for loading JavaScripts and CSS files into the webpage.
Currently, in webpacker.yml we have set the source_path to app/javascript. Which is working fine to load the JavaScript files form this directory.
But in our application, we have an engines directory, and all the JavaScript files are located inside different engines in engines directory, to load these JavaScript files we created a link in app/javascript/packs for each pack in engines directory.
Is there a better way to do this, without providing links OR by providing multiple source_path in the webpacker.yml file.
For reference:
This is the folder structure currently we have:
-root
|
|-app
|-javascript
|-packs
|-[link to engine1.js pack files]
|-[link to engine2.js pack files]
|-engines
|- engine1
|-app
|-javascript
|-packs
|-engine1.js
|- engine2
|-app
|-javascript
|-packs
|-engine1.js
And this is how the webpacker.yml configuration
default: &default
source_path: app/javascript
source_entry_path: packs
public_output_path: packs
cache_path: tmp/cache/webpacker
# Additional paths webpack should lookup modules
# ['app/assets', 'engine/foo/app/assets']
resolved_paths: ['app/assets']
I think you'll want to do something like this:
additional_paths: ['engines']
Source: https://github.com/rails/webpacker#resolved
If you are adding Webpacker to an existing app that has most of the assets inside app/assets or inside an engine, and you want to share that with webpack modules, you can use the additional_paths option available in config/webpacker.yml. This lets you add additional paths that webpack should lookup when resolving modules:
Perhaps a better way to do what you mention is to take advantage of the webpacker folder structure.
Instead of creating links to files inside the "app/javascript/packs" folder, maybe you could reference them through the "index.js" files in those folders.
According to the documentation, webpacker will look for the "index.js" file inside each imported folder.
So you could modify the file structure to something like this:
-root
|
|-app
|-javascript
|-packs
|-engine1.js
|-engine2.js
|-engines
|- engine1
|-index.js
|-app
|-javascript
|-packs
|-engine1.js
|- engine2
|-index.js
|-app
|-javascript
|-packs
|-engine1.js
Create the files and import the respective folder
// app/javascript/packs/engine1.js
import 'engines/engine1'
// app/javascript/packs/engine2.js
import 'engines/engine2'
This will import the "index.js" file from the mentioned folder, and within that file you can import everything else you need from the "engineX" folder.
Then you can reference them as follows
<%# app/views/layouts/application.html.erb %>
<%= javascript_pack_tag 'engine1' %>
<%= javascript_pack_tag 'engine2' %>
All this can be done, without the need to modify the default Webpacker configuration

Grails: excluding a JS file from minification

I read tons of documentation on asset-pipeline Grails plugin, but found no definite answers and I can not exclude a single stupid JS file from being minified.
Grails is in ver. 3.3.6 and assetPipelineVersion 2.14.1.
Goal:
Stop minifying grails-app/assets/javascripts/json-tree/json-tree.js.
What I tried:
I changed the assets block in build.gradle:
assets {
minifyJs = true
minifyCss = true
includes = []
excludes = [ '**/json-tree.js' ] // tried also with 'json-tree/json-tree.js'
}
I also added a section to my application.yml:
grails:
assets:
excludes: ['json-tree/*.js'] # also tried with **
Problem: the file still gets minified.
How can I solve the problem?
Also, how the assets pipeline should be configured: per build.gradle or application.yml or both?
Please try with following :
grails.assets.minifyOptions.excludes = ["json-tree/json-tree.js"]
To exclude single file you can try above otherwise you can exclude whole folder from minify
grails.assets.minifyOptions.excludes = ["json-tree/*.js"]
For more information you check documentation
Hope this will help you

how to deploy your dart app (using Web ui) without using Pub Deploy

What is the best strategy to deploy a Dart Web-ui app manually ?
pub deploy doesn't work for me and I have raised bug report. So am thinking what is the best way to manually deploy.
This is how I started:
1) From project root I compile the webui components (dwc.dart)
2) change directory to web/out then run dart2js
3) copy all .js files into that scripts/js public folder on server
4) copy appname.html to server changing css and script paths to option 3
5) Make sure dart.js is also in the same directory as item 3
this is as far as I got. So what else do I need to do ?
A few questions:
1) Do I manually change the file paths in the generated .js files to point to public folders on server for the files they are referencing and make sure those files are on server also ?
2) Do I need to copy all packages to server also ?
3) Any preferred file structure on server?
Any tips on this really appreciated.
Thanks.
I wrote a Grunt script for it (since I had no time to look up how to properly write code for Grunt, I did not share the code since it's a mess) but I basically do this:
compiling a list of files with dwc to a given out dir
compile it to javascript
clean up all non-deployable files
change some paths inside the HTML to match the server paths (for some reasons, this gets changed by the compilation process)
remove all packages except the ones I really need (JS interopt and browser)
Since I'm only using the JS version, I remove all dart packages. Since the paths inside the HTML files are up to you, you can already use a structure that suits you/your server.
I can provide you with a Grunt script to understand the order of tasks. Practically the order I use is this one:
Create the build directory. I usually use /build/web. I usually create these files (index.html, main.dart, /css and so on into the /web dir). I create the rest of components into /lib directory.
Compile the .dart file that contains the main() function ("main.dart" in my case for simpler projects) file to Javascript and put it into /build/web directory
Copy the other needed files and folders to the /build/web directory. Also, during this process you'll be copying the packages that your project needs. You'll see in the example provided below.
Remove all empty folders from the project
You can create a Grunt task to open the /index.html file in the browser once the building process has ended (I will not provide this example)
The structure of the dart test project:
testApp
- gruntfile.js
- package.js
/lib
/packages
/angular
/web
- index.html
- main.dart
/css
/img
So, the Grunt example script to cover steps from 1 - 4 looks like this (copy it to gruntfile.js):
module.exports = function (grunt) {
grunt.initConfig({
// 1.
// create build web directory
mkdir: {
build: {
options: {
create: ['build/web']
}
}
},
// 2.
// compile dart files
dart2js: {
options: {
// use this to fix a problem into dart2js node module. The module calls dart2js not dart2js.bat.
// this is needed for Windows. So use the path to your dart2js.bat file
"dart2js_bin": "C:/dart/dart-sdk/bin/dart2js.bat"
},
compile: {
files: {'build/web/main.dart.js': 'web/main.dart'}
}
},
// 3.
// copy all needed files, including all needed packages
// except the .dart files.
copy: {
build: {
files: [
{
expand: true,
src: [
'web/!(*.dart)',
'web/css/*.css',
'web/res/*.svg',
'web/packages/angular/**/!(*.dart)',
'web/packages/browser/**/!(*.dart)'
],
dest: 'build'
}
]
}
},
// 4.
// remove empty directories copied using the previous task
cleanempty: {
build: {
options: {
files: false
},
src: ['build/web/packages/**/*']
}
},
});
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.registerTask('default', [
'mkdir:build',
'dart2js',
'copy:build',
'cleanempty:build'
]);
};
So this is the Grunt script example.
Create a /gruntfile.js file into your project's root directory and copy/paste the script to it.
Create a /package.json file into your project's root directory and copy/paste the following script:
{
"name": "testApp",
"version": "0.0.1",
"description": "SomeDescriptionForTheTestApp",
"main": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "YourName",
"peerDependencies": {
"grunt-cli": "^0.1.13"
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-cleanempty": "^1.0.3",
"grunt-contrib-copy": "^0.7.0",
"grunt-dart2js": "0.0.5",
"grunt-mkdir": "^0.1.2",
"matchdep": "^0.3.0"
}
}
Open Command Prompt in Windows, Terminal in Linux, navigate to your project's root directory and use this command:
npm install
Wait untill all Grunt modules needed will be downloaded to your local project. Once this is finished, issue this command in Command Prompt or Terminal:
node -e "require('grunt').cli()"
You can use this to initiate Grunt default task without having Grunt installed globally on your system.
Now, to know the exact build structure for your project (including the packages that the project needs), make a build using Pub Build. Then you will be able to instruct Grunt to create the same dir structure.
You can add other tasks (like minification) if you want.
Hope this will help you all to understand the process and get you started with a test app first. Add your comments to make this even better and simplify it even more.

Resources