I wanted to know if there is any way I can customize how r.js processes the source code of javascript (or other) files during the merging/optimization of a project that uses require.js.
I need to apply some custom transformations, as for example handling some java-like annotations.
I'm looking for something similar to what is possible with browserify using the b.use().
Found the solution. Is it actually possible using the "onBuildWrite" configuration parameter , and specifying a callback that is executed before each module is serialized.
Something like:
var config = {
baseUrl: 'src',
onBuildWrite: function( name, path, contents ) {
//Additional processing
return contents;
},
modules: [{
name: 'main'
}],
};
requirejs.optimize( config, function(results) {});
Related
I was able to output an assets library and many other libraries that work as remote federated modules and as deep import libraries in case I am not connecting to a remote or I am not using webpack in the consumer end.
The issue is now that all my assets exports a module, that either have the raw data as uri or the string that points to the right asset. Eg: the bird.svg is outputed to dust with it's hash plus the modules that resolves to the file bird-[hash].svg.
The above is great from javascript but not so much for css. Now I can't rewrite the url() to point to the right remote path which would be sg like:
//since I don't know when the assets will change names I can't refer to it directly. So I would need to first read the string from the bird.js module. Append the publicPath and then rewrite the url.
.someClass {
background-image: url('/assets/bird.js')
}
//the above won't work for obvious reasons.
Só, the question is how can I solve this? Or is there any loader for this? I checked the resolve url loader but it does not seem to be what need.
Ok I resolved this issue, by passing additional data as variable to sass-loader. That way I can evaluate the actual name of the files, and put it as a sass map before and handle it from sass.
//I am using glob to return an object with all the assets.
//This can probably be automated better. That would be an easier way.
//But this way works for me in all 3 scenarios, node, browser and federated module.
//Also has caching ootb for the assets.
const assetsPaths = {
...glob.sync('../assets/dist/img/**.node.js').reduce(function (obj, el) {
obj['img/' + path.parse(el).name] = '../../assets/dist/' + require(el).default;
return obj
}, {}), ...glob.sync('../assets/dist/fonts/**.node.js').reduce(function (obj, el) {
obj['fonts/' + path.parse(el).name] = '../../assets/dist/' + require(el).default;
return obj
}, {})
};
//...
{
loader: 'sass-loader',
options: {
additionalData: "$assets: '" + assetsMap + "';",
sourceMap: true,
sassOptions: {
outputStyle: "compressed",
},
}
},
//...
you also need to disable url rewriting
{
loader: 'css-loader',
options: {
url: false,
}
},
then you can use assets map in your sass files:
#font-face {
font-family: 'Some Font';
src: local('Some Font');
src: url("#{map-get($assets, SomeFont)}");
}
You will need probably have your project setup sort like a mono repo and you also need to build those assets library with two bundles.
One for node so you can use the string path to your actual assets when bundling you sass/whatever.
And another for normally loading it from the browser.
update:
Instead of doing all this I just used the manifest generated from 'webpack-manifest-plugin' to build the $assets map to be used in sass.
const assetsManifest = JSON.parse(fs.readFileSync('../assets/dist/manifest.json'));
const assetsMapFn = asset => `'${asset[0]}':'${asset[1]}'`;
const assetsMap = `(
${Object.entries(assetsManifest).map(assetsMapFn).join(',')}
); `;
If anyone knows a better way to do this please reply or comment.
So I'm trying to set up Typescript and Chutzpah for testing purposes. Typescript is set up to output in this format:
define(['require', 'exports', './someModule'], function(require, exports, someModule) {
//examplecode
});
Which works fine, the problem occurs when someModule is actually a directory with an index.js.
/app
app.js
/someModule
index.js
require.js is unable to resolve someModule in this way and the test fails.
Is there any way to tell require.js that this is a module?
RequireJS won't automatically check for the presence of index.js and load that as your module. You need to tell RequireJS that when you want to load someModule, it should load someModule/index. I'd set a map in my call to require.config:
require.config({
[ ... ]
map: {
'*': {
someModule: 'someModule/index',
}
},
});
You have to adjust the name you give there so that it is a path relative to your baseUrl. It's not clear from the information you give in your question what it should be.
(For the record, there's also a packages setting that you could probably tweak to do what you want but putting something packages says "this is a package", which is not what you appear to have here. So I would not use it for what you are trying to do.)
I didn't like the configuration in map either. The most simple way I accomplished this was writing a plugin for require.
Let's name the plugin mod, where it is to be used as mod!module/someModule, you can also call it index as in index!module/someModule, whatever suits you best.
define(function(require, exports, module) {
// loading module/someModule/index.js with `mod!`
var someModule = require('mod!module/someModule');
// whatever this is about ..
module.exports = { .. };
});
So lets assume you have paths set in require's configuration with some sort of project structure:
- app
- modules
- someModule/index.js // the index we want to load
- someModule/..
- someModule/..
- etc
- plugins
- mod.js // plugin to load a module with index.js
Requires config:
require.config({
paths: {
'module': 'app/modules',
// the plugin we're going to use so
// require knows what mod! stands for
'mod': 'app/plugins/mod.js'
}
});
To read all the aspects of how to write a plugin, read the docs at requirejs.org. The simplest version would be to just rewrite the name of the requested "module" you are attempting to access and pass it back to load.
app/plugins/mod.js
(function() {
define(function () {
function parse(name, req) {
return req.toUrl(name + '/index.js');
}
return {
normalize: function(name, normalize) {
return normalize(name);
},
load:function (name, req, load) {
req([parse(name, req)], function(o) {
load(o);
});
}
};
});
})();
This is not production code, it's just a simple way to demonstrate that requires config wasn't meant to solve problems like this.
I'm using the requirejs-babel plugin which requires prepending 'es6!' to all module ids that need babel transpilation.
define(['es6!some-es6-module'], function(module) {
// ...
});
Is there an API in RequireJS that would allow me to inspect a module id and prepend the plugin id as-needed? For example, if I wanted to apply 'es6!' to all module ids in a specific directory?
Ultimately I need to be able to write defines like this define(['some-es6-module'], ...) and automatically add the es6! prefix depending on what the module id is.
Not looking for information on SystemJS or gulp tasks that do the transpilation ahead of time, etc.
The exact module ids are not known at configuration time- I just know in certain locations/directories, modules will need es6!.
Needs to work in the browser, at runtime
I am not 100% sure on your overall objective (do you want the es6 addition to module ID saved permanently or always auto-added?), but you may be able to use RequireJS mapping to substitute module ID's for defined modules. For example: -
requirejs.config({
map: {
// * - for all modules that require these, do this
'*': {
'some-es6-module': 'es6!some-es6-module'
}
}
});
However, considering your use-case you may need something more complicated than this, as mapping assumes you have actual different versions of files and is generally used for this purpose.
A more complicated solution I assume you are looking to avoid could be to dynamically loop your files before optimising them in r.js and loading/editing them via Node. It would get a little messy!
var config = requirejs.s.contexts._.config;
var needBabel = ['some-es6-module', 'another-module-name', 'another'];
for (var property in config.paths) {
if (config.paths.hasOwnProperty(property) && needBabel.indexOf(property) > -1) {
// load the module in node
// fs.readFileSync(__dirname + config.paths[property] + '.js');
// dynamically modify this file with text replacement
// save this file via Node again
}
}
// run Require JS optimiser
// undo everything you've just done when optimisation is complete
I ended up overriding the load method. The override uses the standard load for modules with mapped paths, otherwise it uses the es6 (requirejs-babel) plugin to load the module.
require.standardLoad = require.load;
require.load = function(context, moduleName, url) {
var config = requirejs.s.contexts._.config;
if (moduleName in config.paths) {
return require.standardLoad(context, moduleName, url);
}
require(['es6'], function(es6) {
es6.load(
moduleName,
require,
{
fromText: function(text) {
require.exec(text);
context.completeLoad(moduleName);
}
},
{});
});
};
Here it is in action: https://gist.run/?id=7542e061bc940cde506b
When I minified my css, I was left with an incorrect path to the fonts from various libraries. So, I created a task to move the fonts from my bower_components/ folder to dist/public/fonts:
gulp.task('doit', function() {
gulp.src(["public/bower_components/bootstrap/dist/fonts/*", "public/bower_components/font-awesome/fonts/*"])
.pipe(gulp.dest("dist/public/fonts"));
});
Basically that should throw any fonts I need into a generic fonts folder, which my minified css should now be able to access.
But after I run it, dist/public/fonts doesn't exist. Why not?
I don't fully understand the paths you're src-ing (public/bower_components?), but I believe you'll want to use the base option for gulp.src.
Because these two globs will have different bases, I'd suggest breaking it into two separate tasks, and building a third to aggregate them into a single. Otherwise you'll need to get into merging streams or the addSrc plugin.
gulp.task('copy:fonts:bootstrap', function () {
return gulp.src(
[
'public/bower_components/bootstrap/dist/fonts/**/*'
],
{
base: 'public/bower_components/bootstrap/dist/fonts'
}
)
.pipe(gulp.dest('dist/public/fonts'));
});
gulp.task('copy:fonts:fontawesome', function () {
return gulp.src(
[
'public/bower_components/font-awesome/fonts/**/*'
],
{
base: 'public/bower_components/font-awesome/fonts'
}
)
.pipe(gulp.dest('dist/public/fonts'));
});
gulp.task('copy:fonts', ['copy:fonts:bootstrap', 'copy:fonts:fontawesome']);
According to this article, specify your src like this:
gulp.src(['src/js/**/*.js'], { base: 'src' })
.pipe(foo())
.pipe(gulp.dest("./public/"));
and it will auto create the destination directories for you. In this case, the 'js' folder will be created in public if it doesnt exist already.
I notice in the documentation there is a way to pass custom configuration into a module:
requirejs.config({
baseUrl: './js',
paths: {
jquery: 'libs/jquery-1.9.1',
jqueryui: 'libs/jquery-ui-1.9.2'
},
config: {
'baz': {
color: 'blue'
}
}
});
Which you can then access from the module:
define(['module'], function (module) {
var color = module.config().color; // 'blue'
});
But is there also a way to access the top-level paths configuration, something like this?
define(['module', 'require'], function (module, require) {
console.log( module.paths() ); // no method paths()
console.log( require.paths() ); // no method paths()
});
FYI, this is not for a production site. I'm trying to wire together some odd debug/config code inside a QUnit test page. I want to enumerate which module names have a custom path defined. This question touched on the issue but only lets me query known modules, not enumerate them.
It is available, but it's an implementation detail that shouldn't be depended on in production code ( which you've already said it's not for, but fair warning to others! )
The config for the main context is available at require.s.contexts._.config. Other configurations will also hang off of that contexts property with whatever name you associated with it.
I don't believe require exposes that anywhere, at least I can't find it looking through the immense codebase. There are two ways you could achieve this though. The first and most obvious is to define the config as a global variable. The second, and closer to what you want, is to create a require plugin that overrides the load function to attach the config to the module:
define({
load: function (name, req, onload, config) {
req([name], function (value) {
value.requireConfig = config;
onload(value);
});
}
});