RequireJS + Babel + JSX - requirejs

I'm trying to invoke in-browser JSX transformations using Babel.
I'm loading an AMD JS module in the browser using the following:
require(["nbextensions/ht"] function(ext){});
Which eventually imports this "ui" module, which has calls a function to render JSX.
However, this doesn't seem to be triggering Babel's in-browser JSX transformer.
Is it even possible to call Babel's JSX Transformer from within a RequireJS context?
var BOWER = '/nbextensions/ht/bower_components'
var COMPONENTS = '/nbextensions/ht/components'
var NODE_MODULES = '/nbextensions/ht/node_modules'
requirejs.config({
paths: {
es6: NODE_MODULES + "/requirejs-babel/es6",
babel: NODE_MODULES + "/requirejs-babel/babel-5.8.22.min"
}
})
define([
BOWER + '/react/react.min.js',
"es6!" + COMPONENTS + "/App.jsx"
],function(React, App){
console.log("Loaded React v" + React.version)
var ui = {}
ui.render = function() {
React.render(<App/>, document.getElementById("ht_main"))
}
return ui
})

Ah, figured it out. The method used here https://github.com/podio/requirejs-react-jsx does work, but the RequireJS JSX transformation only works on the imported module (main.js cannot have JSX mixed in).
Therefore, the Component module should simply be wrapped in a function prototype that exposes a render() method to React.render.
Refer to the example on the github page.

Related

Gulp + Browserify + TypeScript To Browser

My problem is the following:
I use gulp+browserify to compile my TypeScript to JavaScript that you can use on normal HTML pages, the problem is that my class is never available on the browser:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
This is my TypeScript File:
class Test {
public test(): void {
console.log("aa");
}
}
This is my gulpfile
var gulp = require("gulp");
var browserify = require("browserify");
var source = require('vinyl-source-stream');
var tsify = require("tsify");
gulp.task("default", function () {
return browserify({
//basedir: '.',
debug: true,
entries: ['app/Resources/typescript/Test.ts'],
cache: {},
packageCache: {}
})
.plugin(tsify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest("web/bundles/framework/js"));
});
The file compiles without problem, and is included in my index.html (the compiled js file).
But when i try:
var t = new Test();
I get the following error:
VM633:1 Uncaught ReferenceError: Test is not defined
at <anonymous>:1:13
I can't resolve it, I have read a lot and I haven't found anything clear, I tried all and nothing worked.
There are a few things missing here:
If you want your class to be accessible outside of your module, you have to export it:
export class Test {
// ...
}
Browserify creates functions on top of the classes you define. So it won't be accessible globally (which is a good thing). Normally you would import it in another file and use it:
// in any TS file that wants to use `Test` class. Make sure this is included in the gulp entries as well
import {Test} from "test";
var t = new Test();
console.log(t);
Or if really want it to be accessible globally, you can attach it to window object:
// In Test.ts file:
(window as any).Test = Test; // This can be useful when debuging. But don't do this in production code.

babelify pipe results in: The code generator has deoptimised the styling of "/.../jquery.js" as it exceeds the max of "100KB"

I'm trying to create a single js file containing jquery, bootstrap and reactjs components using a gulp task:
app.jsx:
var React = require('react');
var ReactDOM = require('react-dom');
var HelloWorld = require('./Application.jsx');
var $, jQuery = require('../../libraries/jquery/dist/jquery');
var bootstrap = require('../../libraries/bootstrap-sass/assets/javascripts/bootstrap');
ReactDOM.render(
<Application />,
document.getElementById('example')
);
gulp task:
gulp.task('js', function () {
browserify('./public/javascripts/src/app.jsx')
.transform(babelify, {presets: ["es2015", "react"]})
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('public/javascripts/build/'));
});
When running gulp, I get the following message:
[BABEL] Note: The code generator has deoptimised the styling of "/Users/.../jquery/dist/jquery.js" as it exceeds the max of "100KB".
How I design the gulp task, so that jquery and bootstrap does not pass the babelify pipe?
One possible solution is to add an ignore key to your babelify configuration so that it looks like something along the lines of:
.transform(babelify, {ignore: ['./libraries/**/*'], presets:["es2015", "react"]})
This should keep bableify from messing with your lib files that are already es5/minified/production ready.
(You may need to adjust the path a little bit... not 100% of your project structure)

Not able to load AMD modules through Jest

I'm trying to use Jest for unit testing my React code but I'm also using requirejs and so all my React code is in AMD modules. It obviously works well in browser but when I run Jest tests, it can't load my AMD modules.
Initially it was giving me error for define saying define is not defined, so I used amdefine by Requirejs to fix it. Now it can understand define but still can't load my module.
I had the same problem today and here is what I've done :
Module.js
(()=> {
const dependencies = ['./dep.js'];
const libEnv = function (dep) {
// lib content
function theAnswer (){
return dep.answer;
}
return {theAnswer}; // exports
};
//AMD & CommonJS compatibility stuff
// CommonJS
if (typeof module !== 'undefined' && typeof require !== 'undefined'){
module.exports = libEnv.apply(this, dependencies.map(require));
module.exports.mockable = libEnv; // module loader with mockable dependencies
}
// AMD
if (typeof define !== 'undefined') define(dependencies, libEnv);
})();
You will found all needed files on my github repository to test :
in browser for requirejs
with node for jest
https://github.com/1twitif/testRequireJSAmdModulesWithJest
I ran into same problem, so I went ahead and mocked my dependency at the top of test file:
jest.mock('../../components/my-button', () => {
// mock implementation
})
import MyButton from '../../components/my-button';
This way, when MyButton is loaded, its dependency is already mocked, so it won't try to load the RequireJS module.
There's no official Facebook support for requirejs in Jest yet. But's planned. Look this thread:
https://github.com/facebook/jest/issues/17
Also in this thread Sterpe posted a plugin he wrote to do it (but I didn't try it):
https://github.com/sterpe/jest-requirejs

How to ignore libraries in browserify programmatic api

Assume the below code is found in bundler.js and tracing entry.js leads to var B = require('backbone'); (Backbone is a dependency installed as declared in package.json).
var browserify = require('browserify');
var bundle = new browserify();
bundle.add('entry.js');
bundle.bundle({
noParse: ['backbone']
});
Executing this bundler yields a stream that contains the original backbone source. Based on browserify's command line options I expected it to skip backbone alltogether. Reading through the source, I expected perhaps the following would work:
var browserify = require('browserify');
var bundle = new browserify({
noParse: ['backbone']
});
bundle.add('entry.js');
bundle.bundle();
Though backbone source still appears in the stream output.
Is it possible to use --noparse=FILE as a configuration option in this application of the api?
As you can see from here the --noparse option provided on the command line is passed to the browserify({ }) call.
So in order to tell browserify to not parse jquery and three.js you have to pass the full path to your jquery and three.js files.
Example:
browserify({
noParse: [
require.resolve('./vendor/jquery'),
require.resolve('./vendor/three')
]
})
.require(require.resolve('./entry.js'), { entry: true })
.bundle();
var browserify = require("browserify")
browserify({entries: ['./src/client/app.js']})
.ignore('jquery')
That would make browserify ignore jquery, and then jquery can be added on index.html directly.

Multi-context requirejs and inheriting dependencies between contexts

I am using RequireJs to load AMD-modules on my company's main web site. We are also using requireJS to load modules hosted on separate domains. requireJS supports this using the "context"-field; ie. create a separate sandboxed environment with a separate baseUrl.
The problem at hand is when we want the two separate contexts to share a common object; eg. jquery (to avoid loading it twice) or a pubsub-implementation that trigger events in between widgets.
I've figured a way to inject modules in between contexts, using a define function together with a global requireJS
main.js - hosted on http://example.com/src/main.js
require(['jquery'], functin($){
// create sandbox/contexted instance of requireJs
var sandbox = requireJS.config({
context: "myContext",
baseUrl : 'http://otherdomain.com/src'
});
// load module (with internal dependencies) from other domain
sandbox.require(['modules/bootstrap.js'], function(bootstrap){
bootstrap.run();
});
});
bootstrap.js - eg. hosted on http://widgets.com/src/modules/bootstrap.js
define(function(){
// define jquery into sandboxed environemnt
requireJs('jquery', function($) {
define('jquery', function(){
return window.jQuery;
});
});
// obtain sandboxed instance from parent
var sandbox = requireJs.config({
context: "myContext"
});
sandbox(['jquery'], function($){
console.log($);
});
});
The problem is that if Im defining jquery (or any other module that returns a function), tje "requreJS"-way (not using globals) it will always throw an error
// define jquery into sandboxed environemnt
requireJs('jquery', function($) {
define('jquery', $);
});
Is this a bug or a feature ?
Ive actually solved this myself. The trick is to wrap the returned function within a new anonymous function.
// inject global module into contexted require
function injectContext(moduleId, module){
var m = module;
if(typeof module === 'function') {
m = function() {
return module;
};
}
define(moduleId, m);
}

Resources