Gulp with React not compiling a functional component correctly - reactjs

I have the following gulpfile.js:
var gulp = require('gulp'),
babel = require('gulp-babel'),
concat = require('gulp-concat'),
react = require('gulp-react'),
sass = require('gulp-sass'),
jsxToJs = function() {
//gulp.src('src/**/*.js')
gulp.src('./src/sections/header/header.js')
.pipe(react())
.pipe(babel({
presets: ['es2015']
}))
.pipe(concat('javascript.js'))
.pipe(gulp.dest('./'));
};
gulp.task('jsxToJs', jsxToJs);
gulp.task('build', ['jsxToJs', 'styles']);
gulp.task('watch', function () {
gulp.watch([
'./src/**/*.js',
'./src/**/*.scss'
], [
'jsxToJs',
'styles'
]);
});
gulp.task('default', ['build', 'watch']);
And I'm trying to compile the following functional React component:
let Header = (props) => {
return(
<div />
);
};
However, when I run the javascript.js file created by gulp I get the following error:
Uncaught TypeError: e.render is not a function
If I convert the component back to the old way of doing things like this (which is how I found it as I'm revisiting an old problem):
var Header = React.createClass({
render: function() {
}
});
Then it works.
Looking at the compiled JS shows me this - where I can see that render is being compiled out correctly with the old syntax, but for the new syntax, while it's being ESfivified it's not being reactified:
// not working
"use strict";
var Header = function Header(props) {
return React.createElement("div", );
};
// working
"use strict";
var Header = React.createClass({ displayName: "Header",
render: function render() {
return React.createElement("div", );
}
});
I've checked that I've installed my gulp requires correctly and I'm using Node 6.10.2. My gulp file has some extra things for scss in that I've removed for this question.
A couple of other points:
I'm not using a bundling tool like browserify as I think it's overkill for this project - so no imports or exports.
I'm just loading HTML pages that do JSONP to an endpoint and load a script on page that includes the JSON - this is done in a getInitialState in the page level HOCs.
Can anyone explain what I'm doing wrong?

The solution turned out to be pretty simple.
Babel requires presets to be provided in order to transpile.
I had the es2015 preset, but not the react one. Therefore react specific tranpilations were not occurring. This addition fixed the problem:
.pipe(react())
.pipe(babel({
presets: ['es2015', 'react']
}))
The mistake I was making, that sent me down the wrong rabbit hole in Google, was assuming that failing to reactify was something to do with the gulp-react function - silly me.

Related

Requirejs callback undefined on code splitting

I'm a newbie to RequireJS I have a ReactJS app with index.jsx as an entry point
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
export function callBackForRequirejs() {
return "testing";
}
When I load my build via RequireJS I get these callbacks
require(["/path/to/bundle"], function(callback) {
console.log(callback) // I get "callBackForRequirejs"
}, function(err){
console.log(err)
});
But when I do code splitting I'm getting undefined in the callback, for code splitting I'm using these configs
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "initial",
}
}
}
}
UPDATE:
Actually, my react app is a plugin for some external app, the external app loads my plugin via RequireJS. The code inside an external app is something like this
case 1:
require(['/pathof/my/react/plugin/bundle.js'],
function(callbackwhenpluginloads){
callbackwhenpluginloads()
})
Since the size of my bundle.js is very large so I decided to split it into two parts one which comes from node_modules and one from my code
Now the external plugin loads my react plugin something like this
case 2:
require(['/pathof/my/react/plugin/bundle.js',
'/pathof/my/react/plugin/vendor.js' ], function(callbackwhenpluginloads){
callbackwhenpluginloads() // callbackwhenpluginloads is undefined
})
I'm getting undefined callback when the external app loads my plugin in
Actually, based on RequireJS docs for starting you did the following way and it works well:
require(['/path/to/bundle.js'], function(callback) {
console.log(callback) // you get callbackForRequireJS
}, function(error) {
console.log(error)
});
And now you did a code-splitting on your project, so you should consider this the vendor.js is like a dependency to split bundle.js file. so you should follow this example to load the dependencies at the first and then run the other split code. so your code is something like below:
requirejs.config({
paths: {
reactApp: 'path/to/bundle.js'
},
deps: ['path/to/vendor.js'],
});
require(['reactApp'], function(callback) {
console.log(callback) // it should works now
}, function(error) {
console.log(error)
});
Or there is another way that I don't recommend it:
require(['path/to/vendor.js'], function() {
require(['path/to/bundle.js'], function(callback) {
console.log(callback) // it should works now
}, function(bundleError) {
console.log('bundleError', bundleError)
});
}, function(vendorError) {
console.log('vendorError', vendorError)
});
It seems, for code splitting you are using the webpack. webpack and require js don't really get along.
you should try vanilla JS instead.
<script onload="handleOnLoad()" />
Or go for a npm package.
react-load-script - npm

Building Typescript for Angular using Gulp

I'm trying to setup an angularjs project according to Johnpapa's Angular Style Guide whilst using TypeScript and Gulp as a build tool. I believe Gulp is currently recommended over Grunt but I'm not very experienced with Gulp.
What I have:
My project currently looks like this:
src/
+- ts/ # contains .ts source files
+- typings/ # contains .d.ts typing definitions
+- html/ # contains .html files
dist/
+- bundle.js # single .js file containing compiled typescript and sourcemaps
Following the angular style guide I have created a separate .ts file for each angular element.
my-app.module.ts
----------------
angular.module('myApp', []);
for initialization of the module and another for a simple implementation of a controller:
my-controller.controller.ts
----------------------------
export class MyController {
testString = 'test';
}
angular
.module('myApp')
.controller('MyController', MyController);
typescript is configured using a simple tsconfig.json. (Note that filesGlob is not active yet - it will become available from TypeScript 2.0)
tsconfig.json
-------------
{
"exclude" : [
"node_modules"
],
"filesGlob" : [
"./src/typings/index.d.ts",
"./src/ts/**/*.ts",
"!./node_modules/**/*.ts"
],
"compilerOptions": {
"noImplicitAny": true,
"target": "es5",
"sourceMap" : true,
"outFile" : "./dist/bundle.js",
"removeComments": false
}
}
What I want:
I would ideally like to
Have Gulp monitor new or updated .ts files in ./src/ts/**/*.ts
Concatenate all the files from ./src/ts/**/*.ts. This is required for angular to work properly. Other methods I've tried using requirejs or browserify can't find the other .ts files without having to manually input references to these files.
Compile using the definitions from tsconfig.json. This would take into consideration the typings in ./src/typings/index.d.ts (for external modules including 'angular'). Also sourcemaps.
Possibly an uglify or babelify step to finish it.
What I tried:
I've tried following the manual from the typescriptlang handbook but this uses browserify and won't work with angular.
Gulp-typescript also has a note on concatenating files but the out option doesn't work like this:
var gulp = require('gulp');
var ts = require('gulp-typescript');
var tsProject = ts.createProject('tsconfig.json');
gulp.task('default', function () {
var tsResult = tsProject.src().pipe(ts(tsProject));
return tsResult.js.pipe(gulp.dest('dist'));
});
This configuration will output an empty file with only comments.
Another method mentioned in this question:
gulp.task('ts', function () {
gulp.src('./src/ts/**/*.ts')
.pipe(ts({
noImplicitAny: true,
out: 'output.js'
}))
.pipe(gulp.dest('./tmp/ts'));
});
gulp.task('default', ['ts'], function() {
gulp.src(['./tmp/ts/output.js'])
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(sourcemaps.write('/'))
.pipe(gulp.dest('./dist/'));
});
But this gave two issues: 1. Even though I only pointed at the .ts files in ./src/ts the typescript compiler started spewing errors from .ts in ./node_modules. 2. It still didn't manage to concatenate everything.
I'm at quite a loss here. Can anyone help me set up this build script? I'm surprised I couldn't find a similar working demo anywhere.
Solution:
I've configured the gulp environment based on the solution in this answer and removed the 'export' statement for classes / objects that are not inside a typescript module.
If that helps, here is a Angular Typescript Gulp Tutorial that has a basic TypeScript, Angular, Gulp, etc. setup that concatenate the app and the vendor/nodes files. There is the demo code on github.
/* File: gulpfile.js */
// grab our gulp packages
var gulp = require('gulp');
// Include plugins
var plugins = require("gulp-load-plugins")({
pattern: ['gulp-*', 'gulp.*', 'main-bower-files', 'del'],
replaceString: /\bgulp[\-.]/
});
var browserSync = require('browser-sync').create();
var reload = browserSync.reload;
// create a default task to build the app
gulp.task('default', ['jade', 'typescript', 'bowerjs', 'bowercss', 'appcss'], function() {
return plugins.util.log('App is built!')
});
In my example, we use Jade to HTML:
// Jade to HTML
gulp.task('jade', function() {
return gulp.src('src/**/*.jade')
.pipe(plugins.jade()) // pip to jade plugin
.pipe(gulp.dest('dist')) // tell gulp our output folder
.pipe(reload({stream: true}))
;
});
For TypeScript, we compiled into one single app.js file:
// TYPESCRIPT to JavaScript
gulp.task('typescript', function () {
return gulp.src('src/**/*.ts')
.pipe(plugins.typescript({
noImplicitAny: true,
out: 'app.js'
}))
.pipe(gulp.dest('dist/js/'))
.pipe(reload({stream: true}))
;
});
For bower, we merge all the js files in vendor.js and CSS in vendor.css:
// BOWER
gulp.task('bowerjs', function() {
gulp.src(plugins.mainBowerFiles())
.pipe(plugins.filter('**/*.js'))
.pipe(plugins.debug())
.pipe(plugins.concat('vendor.js'))
.pipe(plugins.uglify())
.pipe(gulp.dest('dist/js'));
});
gulp.task('bowercss', function() {
gulp.src(plugins.mainBowerFiles())
.pipe(plugins.filter('**/*.css'))
.pipe(plugins.debug())
.pipe(plugins.concat('vendor.css'))
.pipe(gulp.dest('dist/css'));
});
Custom CSS:
// APP css
gulp.task('appcss', function () {
return gulp.src('src/css/**/*.css')
.pipe(gulp.dest('dist/css/'))
.pipe(reload({
stream: true
}));
});
// CLEAN
gulp.task('clean', function(done) {
var delconfig = [].concat(
'dist',
'.tmp/js'
);
// force: clean files outside current directory
plugins.del(delconfig, {
force: true
}, done);
});
This is what reloads the browser when changes occur:
// Watch scss AND html files, doing different things with each.
gulp.task('serve', ['default'], function () {
// Serve files from the root of this project
browserSync.init({
server: {
baseDir: "./dist/"
}
});
gulp.watch("src/**/*.jade", ['jade']).on("change", reload);
gulp.watch("src/**/*.ts", ['typescript']).on("change", reload);
gulp.watch("src/**/*.css", ['appcss']).on("change", reload);
});
My tsconfig.json looks like this... I put the JS files that are automatically compiled from the text editor (Atom) into .tmp/js/atom ... some people put the .js in the same directory as the .ts but I find it confusing... less files is better for me:
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"outDir": ".tmp/js/atom"
},
"exclude": [
"node_modules",
"typings"
]
}

No Directive annotation found on TodoApp Server Rendering

I'm trying to do server rendering with angular2-universal. I copy paste the example todo app of the official repo https://github.com/angular/universal/tree/master/examples/src/universal/todo into my own Trails/Express server.
I manage to start my server but when I call http://localhost:3000 I have the following error :
Error: No Directive annotation found on TodoApp
at new BaseException (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/facade/exceptions.js:17:23)
at DirectiveResolver.resolve (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/directive_resolver.js:31:15)
at CompileMetadataResolver.getDirectiveMetadata (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/metadata_resolver.js:55:51)
at RuntimeCompiler.resolveComponent (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/compiler/src/runtime_compiler.js:34:47)
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:99:37
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:292:26
at ZoneDelegate.invoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:281:29)
at Object.NgZoneImpl.inner.inner.fork.onInvoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone_impl.js:45:41)
at ZoneDelegate.invoke (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:280:35)
at Zone.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/zone.js/dist/zone-node.js:174:44)
at NgZoneImpl.runInner (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone_impl.js:76:71)
at NgZone.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/zone/ng_zone.js:223:66)
at ApplicationRef_.run (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:290:14)
at Object.coreLoadAndBootstrap (/Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/#angular/core/src/application_ref.js:96:19)
at /Users/jaumard/IdeaProjects/trails-angular2-isomorphic/node_modules/angular2-universal/dist/node/bootloader.js:186:34
at Array.map (native)
The example of the universal repo is working so I don't understand why it's not working for me. I don't change anything on the angular2 sources.
All my code is here https://github.com/jaumard/trails-angular2-isomorphic with the configuration here https://github.com/jaumard/trails-angular2-isomorphic/blob/master/api/controllers/ViewController.js#L58 for the route and here for the template engine https://github.com/jaumard/trails-angular2-isomorphic/blob/master/config/web.js#L76
Your problem is that you're working with different packages angular2 at the same time.
You start server with modules from ./node_modules/ folder but your component TodoApp will be decorated by instance ComponentMetadata (extends DirectiveMetadata) from ./dist/src/node_modules/#angular/core/ folder.
I think that first you need to transfer packages to dist folder and then run server. And you have to use the same path to angular2 modules (./dist/src/node_modules)
For example you can try something like this:
server.js
'use strict'
const gulp = require('gulp');
const rimraf = require('gulp-rimraf');
const path = require('path');
const dest = './dist';
gulp.task('clean', (cb) => {
return gulp.src(dest).pipe(rimraf())
});
gulp.task('prerun', ['clean'], () => {
return gulp.src([
'node_modules/rxjs/**/*',
'node_modules/zone.js/**/*',
'node_modules/core-js/**/*',
'node_modules/reflect-metadata/**/*',
'node_modules/systemjs/**/*',
'node_modules/#angular/**/*',
'node_modules/angular2-universal/**/*',
'node_modules/angular2-universal-polyfills/**/*',
'node_modules/angular2-express-engine/**/*',
'node_modules/angular2-hapi-engine/**/*'
], { base: './' })
.pipe(gulp.dest(path.join(dest, 'src')))
});
gulp.start('prerun', run)
function run() {
require('./dist//src/node_modules/angular2-universal/polyfills')
const app = require('./')
const TrailsApp = require('trails')
const server = new TrailsApp(app)
server.start().catch(err => server.stop(err))
}
I added reflect-metadata in package.json. Also you need to change a bit componentUrl like this:
ViewController.js
module.exports = class ViewController extends Controller {
helloWorld(req, res) {
const todoApp = require('../../dist/src/todo/app')
let queryParams = ng2U.queryParamsToBoolean(req.query);
let options = Object.assign(queryParams , {
// client url for systemjs
buildClientScripts: true,
systemjs: {
componentUrl: 'todo/browser', <== remove src/
map: {
'angular2-universal': 'node_modules/angular2-universal',
'#angular': 'node_modules/#angular'
},
packages: PACKAGES
},
...
Then you can see the following error:
So you need to add rxjs in your configuration:
ViewController.js
const PACKAGES = {
'angular2-universal/polyfills': {
format: 'cjs',
main: 'dist/polyfills',
defaultExtension: 'js'
},
...
rxjs: { <== this property
defaultExtension: 'js'
}
};
...
systemjs: {
componentUrl: 'todo/browser',
map: {
'angular2-universal': 'node_modules/angular2-universal',
'#angular': 'node_modules/#angular',
'rxjs': 'node_modules/rxjs' <== this line
},
packages: PACKAGES
},
See also the full list of changes here https://github.com/alexzuza/trails-angular2-isomorphic/commit/45f2e59529821757f6a6c03c5872e08fdce3f3e7
Hope it helps you!

Using gulp, browserify and react with addons

I'm trying to learn about gulp, browserify and react and have been knocking up a little test project. This was fine until I decided to implement some animations in there. Specifically this:
var React = require("react");
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
I'm getting an error because "React.addons" is null.
I also have the issue that my build is taking an age - between 20 secs and a minute. I think the reason is partly because react itself is being included in my bundle, whereas I would ideally like to retrieve it from a CDN (or at least keep it separate).
This is my gulpfile:
var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
gulp.task('js', function () {
return browserify('./public/js/app.js', {
debug: false, bundleExternal: true
})
.transform(babelify, {"presets": ["es2015", "react"]})
.bundle()
.pipe(source('app.js'))
.pipe(gulp.dest('./build/js/'));
});
If I set "bundleExternal" to false then it does stop react being included in my js - but then nothing works because "react" is not found. I found something about browserify-shims but couldn't get it to work from gulp. And wasn't sure if it was the right way to go?
Apologies for the newbie question!
To include ReactCSSTransitionGroup you need to install it first:
npm install react-addons-css-transition-group
Then just require it:
var ReactCSSTransitionGroup = require('react-addons-css-transition-group');

Browserify recipe for AngularJS + source maps + minification

I am looking for a working recipe that can minify my AngularJS code and still provide a source map. Currently I have this gulp task but minification won't work:
gulp.task('browserify', function(cb) {
var bundler = browserify({
entries: [paths.browserEntry],
globals: false,
debug: !settings.PRODUCTION
})
bundler
.bundle()
.on('error', cb)
.on('log', util.log)
.pipe(gulpif(!settings.PRODUCTION, mold.transformSourcesRelativeTo(paths.js)))
.pipe(source(paths.js))
.pipe(buffer()) // because the next steps do not support streams
.pipe(concat('bundle.js'))
.pipe(gulpif(settings.server.minify.js, rename({suffix: '.min'})))
.pipe(gulpif(settings.server.minify.js, uglify()))
.pipe(gulp.dest(paths.js))
.on('end', function() {
cb()
})
})
Any clues?
You may see an example here. The example will output a minified bundle.min.js plus a bundle.map. The crucial points which makes the example works:
Installed debowerify
Installed minifyify
package.json - added transform property
"browserify": {
"transform": [
"debowerify"
]
}
Gruntfile.js - using preBundleCB to make minifyify work
preBundleCB: function (b) {
b.plugin( minifyify,
{ output: './dist/bundle.map',
map:'bundle.map'
});
}
Hope the example is useful to you.

Resources