using r.js optimization to optimize modules - node.js

If I use Modules options, I see the whole files are copied and I am not sure how to use a single optimized file.
I can build a file with references to JS files and use that file to generate a single file.
But using Modules option, I see only the source files are copied directly and I see no optimization....any idea on how to use modules option and optimize it?
Here is the snippet...
({
baseUrl: ".",
dir: "app/main-built-125",
modules: [{
name: "scripts/Address/AddressList"
}, {
name: "scripts/Office/OfficeDetails"
}],
paths: {
jquery: 'empty'
},
fileExclusionRegExp: /^\./,
})

You'll need to use the include option to specify which dependencies to include with the module.
Note that by default dependencies of the defined modules (the one set as name) should be fetched and included in the builded file (Of course, as long as they're defined in the define() deps array).

Related

Configure Rollup to add file extensions into import/require statements

I have a hybrid cjs/esm Node package written in Typescript.
It consists of, let's say, two files - core.ts and extra.ts and I want to keep them separate. extra.ts imports core.ts (literally import { ... } from './core';) and they are separate entry points of the package. Clients import either only core.ts or both.
Rollup makes it easy to have build steps for multiple entry points, for multiple output formats, and all seemed fine.
But now I ran into an issue:
Let's say I have example.mjs or example.cjs where I import or require my-package and my-package/extra entry points.
That doesn't work. Error messages are slightly different but meaning is the same - ./core module can not be found when reading extra.mjs/cjs.
extra.cjs built by Rollup contains the line var core = require('./core');
extra.mjs built by Rollup contains the line import { ... } from './core';
By default, Node 12 does not guess file extensions (I'm not questioning this).
I have to call it as node --experimental-modules --es-module-specifier-resolution=node ./example.mjs to make it work. This is unsatisfactory solution. I need example.mjs to be runnable without additional flags.
It appears to me that file extensions can and should be added to import/require statements in compiled cjs/mjs files to make it work according to the spec.
Although, since I have different build steps in Rollup config for both files and external: ['./core'] in the options for extra.ts, for Rollup they are totally unrelated. And Rollup will just bundle them into a single file otherwise, which is not what I need either.
So the question:
Is there a plugin or a configuration option to make Rollup produce files with correct local imports (file extensions added to local imports according to the format)?
What would be the best way to add an extra step to the building process to patch imports if there is no existing solution?
Maybe there a different bundler that might work for the same task?
Got a satisfactory solution.
output.preserveModules option keeps all files separate while building them in one pass (can be limiting in some aspects though).
output.entryFileNames option allows to specify file extensions. (It won't work with modules considered external, can't use it without preserveModules.)
I can build only extra.ts as it imports every other file. But I have to be mindful of tree shaking when building like this - need to retain all exports of core.ts.
export default [
{
external: [],
input: 'src/extra.ts',
treeshake: false,
plugins: [
typescript(),
cleanup({ extensions: ['ts'] })
],
output: [
{
dir: 'lib',
format: 'es',
preserveModules: true,
entryFileNames: '[name].mjs',
},
{
dir: 'lib',
format: 'cjs',
preserveModules: true,
entryFileNames: '[name].cjs',
},
],
},
];
One step back is that I get another output file for another ts file imported by core.ts and extra.ts. I think I can live with that.
Ideal solution would require to monkey-patch output files with sed-like tool after the build in my initial configuration.

karma config moving a file to base directory

Is there a way to move a file loaded in the Karma config to new directory, such as /base/? I have an external project which contains the source files. I am able to load the file via ../../directory. However, it inserts the file into the /absolute/ folder. If I load the test(s) on another computer, the directory of /absolute/ is different. Is there a preprocessor or some configuration I am missing? I searched through the karma documentation, but couldn't find what I am looking for.
// list of files / patterns to load in the browser
files: [
{ pattern: 'node_modules/jquery/dist/jquery.min.js', included: false },
{ pattern: 'node_modules/knockout/build/output/knockout-latest.js', included: false },
{ pattern: 'node_modules/knockout-mapping/dist/knockout.mapping.min.js', included: false },
{ pattern: 'node_modules/knockout-postbox/build/knockout-postbox.min.js', included: false },
{ pattern: '../FantasyFootball/wwwroot/js/site.js', included: false, served: true, watched: true },
{ pattern: 'tests/*.js', included: false },
{ pattern: 'test-main.js', included: true }
],
Karma generates paths that start with /absolute/... for files you ask it to
load files that are outside Karma's basePath. Karma does this because there's no
simple way to provide a path to these files through the usual /base/ path that
is both unique and inferable. If you are to provide a relative path to a
file above basePath, Karma cannot just combine it with /base/. For instance,
if you had ../my/file.js and ../../my/file.js in your files array, then
combined with /base/, they would both turn into the path /my/file.js. For
cases like this, Karma generates a unique and inferable path by using
/absolute/ and appending the absolute path of the file on disk.
Ok, so where does that leave you? You want Karma to load files outside
basePath and you want to be able to use their paths in your loader's setup.
The problem can be overcome. You can modify karma.conf.js to provide
information useful to compute the paths you need:
client: {
absoluteTopDir: path.join("/absolute/", __dirname),
},
Then in test-main.js, you need to use the absoluteTopDir parameter:
var absoluteTopDir = window.__karma__.config.absoluteTopDir;
require.config({
baseUrl: "...",
paths: {
foo: absoluteTopDir + "../../my/file",
},
});
I've run Karma for years without any trouble but I recently ran into the problem
you ran into and used the method I described above in one of my test suites with
great success.
I've used this solution both with RequireJS and SystemJS.

requirejs HTML structure

I know we could use requirejs combine files into one js file.
such like the following config.
module.exports = {
baseUrl: 'js/',
mainConfigFile: 'src/js/common.js',
dir: 'scripts/',
optimize: 'uglify2',
modules: [
{
name: 'common',
include: [
'jquery',
]
}
]
};
my result into one file is
common.js
----------------
jquery.js
modernizr.js
common.js
my question is, do we still need to put a require.js file in scripts folder and to use the following format
<script data-main="scripts/common" src="scripts/require.js"></script>
or we could just use
<script src="scripts/common.js"></script>
as files are compressed into one file?
You still need to load require.js the usual way to actually make use of the module loading benefits that it provides, and especially if you use the asynchronous functionality a lot. However, you can have a look at almond providing your code uses AMD and (from the README):
optimize all the modules into one file -- no dynamic code loading.
all modules have IDs and dependency arrays in their define() calls -- the RequireJS optimizer will take care of this for you.
only have one requirejs.config() or require.config() call.
do not use RequireJS multiversion support/contexts.
do not use require.toUrl() or require.nameToUrl().
do not use packages/packagePaths config. If you need to
use packages that have a main property,
volo can create an adapter module so
that it can work without this config. Use the amdify add command to
add the dependency to your project.
Almond is great because it doesn't need require.js at all; it wraps your own code with itself, which is a very minimal AMD loader skeleton and nowhere near as powerful as the main library. You then get a single optimised file that can be linked directly in your HTML:
<script src="scripts/common.js"></script>
The Gruntfile config for almond could look something like this:
compile: {
options: {
name: 'path/to/almond',
baseUrl: 'js',
include: ['main'],
insertRequire: ['main'],
mainConfigFile: 'scripts/config.js',
out: 'scripts/main.js',
optimizeAllPluginResources: true,
wrap: true
}
}
The above is all standard r.js boilerplate, you can find many more examples at the almond homepage.

Use RequireJS config file as the build file?

I've got some paths configured in require-config.js as follows:
var require = {
baseUrl: '/javascript',
paths: {
'jquery': 'jquery/jquery-1.8.1.min'
// etc. -- several paths to vendor files here
},
}
I am trying to get the optimization working for deployment. The docs say I should have a build.js that looks something like this:
({
baseUrl: 'javascript',
paths: {
'jquery': 'jquery/jquery-1.8.1.min'
},
name: 'main',
out: 'main-build.js'
})
Is there a way to have the optimizer read my config file instead of (or in addition to) build.js? I don't want to have to manually keep the paths configured the same in both files if they change.
I tried to just run node r.js -o path/to/require-config.js, but it threw an error, "malformed: SyntaxError: Unexpected token var"
Edit: for clarification, my require-config.js file is the config only, not my main module. I did this so I could use the same configuration but load a different main module when unit testing.
You'll need to adjust the way your config options are defined. Taken from the RequireJS documentation:
In version 1.0.5+ of the optimizer, the mainConfigFile option can be used to specify the location of the runtime config. If specified with the path to your main JS file, the first requirejs({}), requirejs.config({}), require({}), or require.config({}) found in that file will be parsed out and used as part of the configuration options passed to the optimizer:
So basically you can point your r.js build file to your config options that will also be shared with the browser.
You will need to make use of the mainConfigFile option
For other's reference:
https://github.com/jrburke/r.js/blob/master/build/example.build.js
The build settings (no need to repeat your config.js lib inclusions here):
baseUrl: 'app',
name: 'assets/js/lib/almond', // or require
// Read config and then also build it into the app
mainConfigFile: 'app/config.js',
include: ['config'],
// Needed for almond (and does no harm for require)
wrap: true,

requireJs build script

I have code like the following
define("ModuleA", ["InitialDependency"], function (initDep){
return {};
});
define("ModuleB", ["ModuleA", "OtherDependency"], function (moduleA, otherDep){
return {};
});
Each of these modules is defined in separate files "ModuleA.js", "Moduleb.js", "InitialDependency.js" and "OtherDependency.js".
These modules are loaded sequentially in my application. ModuleB is always loaded after ModuleA. this means that in the optimization stage I do not want ModuleA's script combined in the built script for ModuleB. I want the following
ModuleA.built.js includes
InitialDependency
ModuleA
ModuleB.built.js includes
OtherDependency
ModuleB
I don't want them all in the same file however as ModuleB may never be loaded.
I can do a build script for both modules but this will be time consuming as I have quite a few modules in my project and would like a build script that will build the lot of them at once.
What do I need to know to create a build script for building both of these modules (and more that follow the same dependency pattern)?
To achieve this, you'd have to play with the modules configuration option.
It could look like this:
{
modules: [
{
name: "ModuleA",
include: [],
exclude: []
},
{
name: "ModuleB",
exclude: [
"moduleA"
]
}
]
}
There's a similar example setup by James here: https://github.com/requirejs/example-multipage
Of course, by building these modules separately, you may end up needing to update paths. If so, the best way then would be to create a file containing a require.config call with special setting for your builded app and including this configuration instead of your usual one. But if you set dependencies in a good separated way, then you'll probably be fine. By "good separated" way, I mean that if moduleA is the base script, then it shouldn't have dependencies packed with moduleB - but I guess this is common sense!
Note about shimmed modules: As shimmed config only work whe files are loaded and by r.js to order plugins, be sure you don't include a shim module without it's dependency if you're not 100% sure these will be loaded before. More info here: https://github.com/requirejs/example-multipage-shim
Hope this help!

Resources