Is it possible to integrate React (using Webpack) with Grails? - grails

I've been thinking about using React as the frontend for a Grails application, but I'm having a bit of trouble getting started.
So far, I've become accustomed to write a React app using Node/NPM with the help of Webpack, and that's been pretty easy because there is plenty of documentation for that setup.
However, I'm struggling to find anything concrete integrating React seamlessly with Grails.
Ideally, I would just do grails run-app and it should take care of everything. I do not want other team members to worry about starting up two different servers or something along those lines.
Please let me know if anyone has done this before.

Webpack can be configured to work quite well with Grails. The key is to have webpack generate its bundle whenever the app is started up, and to output the bundle in a directory where it can be served from the GSP. You do not want your source JavaScript (I.e, React/ES6 code) in the asset pipeline if your using Webpack, instead you want to keep those source files in another directory (such as src/webapp), and configure Webpack to bundle these files and output the result to the asset pipeline (assuming you're using AP at all).
Here's an example configuration for Webpack:
var path = require('path');
module.exports = {
entry: {
index: './src/webapp/index.js'
},
output: {
path: './grails-app/assets/javascripts',
publicPath: '/assets/',
filename: 'bundle.js'
},
Finally, to achieve the integrated webpack/Grails startup, you can use the Gradle node plugin and attach the webpack run script to the application startup in a custom task in your build.gradle (this is assuming that you have a npm script named "webpack" defined to run webpack)
assetCompile.dependsOn(['npmInstall', 'npm_run_webpack'])
Please note that if you want to run webpack in "watch" mode, you'll need to do that seperately from starting up the Grails app, so that that script can run continuously (there actually is support for this in the Gradle mode plugin but it's currently broken).
See this link for a more in-depth explanation of this approach, with a sample application: http://grailsblog.objectcomputing.com/posts/2016/05/28/using-react-with-grails.html
Also checkout the React profile for Grails 3: https://github.com/grails-profiles/react
It has not been released yet but should be in the next few days. It makes use of the same approach outlined here and in the linked post.

You could use npm's scripts feature to combine all steps necessary to start up the development environment into a single command, e.g.:
// package.json
{
...
"scripts": {
"start": "npm start-grails & npm start-react",
"start-grails": "grails run-app",
"start-react": "node server.js"
},
...
}
Now all it takes is a simple npm start to launch all relevant applications.

Related

How to service a Gatsby website with multiple domains and its contents?

I built a website using Gatsby, Contentful, and deployed on Netlify.
I am going to run this website with multiple domain aliases.
ex:
alias1.example.com
alias2.example.com
In that case, the aliases work well and the website have to show contents that belong to the own alias in Contentful.
For example, let's say the current alias is alias1, then the website have to fetch data only have alias1 entry from Contentful.
What I was trying is to add the codes to identify alias in gatsby-config.js using windows.location.href, and set siteUrl as dynamic, but it didn't work.
I am not sure it could be possible and how to implement it.
Thank you.
The best (and almost the only) approach to achieving this is to use an environment variables for each site/alias and configure the deploy command to trigger and use the variables for each site. In that way, each deploy will fetch the data from each Contentful environment.
In your gatsby-config.js (above the module exportation) add:
require("dotenv").config({
path: `.env.${process.env.NODE_ENV}`,
})
The next step is to create one environment file for each alias. In your project root:
.env.alias1
.env.alias2
Each file should contain your environment variables from Contentful:
CONTENTFUL_ACCESS_TOKEN:12345
CONTENTFUL_SPACE_ID:12345
Then, in your gatsby-config.js just replace your hardcoded variables for the ones in your environment files:
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
},
},
The last step is to configure the deploy scripts to trigger each desired alias. In your package.json:
"scripts": {
"clean": "gatsby clean",
"test": "jest",
"format": "prettier --write \"**/*.{js,jsx,json,md}\""
"develop-alias1": "gatsby develop GATSBY_ACTIVE_ENV=alias1"
"build-alias1": "gatsby build GATSBY_ACTIVE_ENV=alias1"
"develop-alias2": "gatsby develop GATSBY_ACTIVE_ENV=alias2"
"build-alias2": "gatsby build GATSBY_ACTIVE_ENV=alias2"
},
Note that you will replace the default gatsby develop and gatsby build for your aliased commands.
By adding this bunch of configuration, for each develop or build/deploy you are telling your Gatsby project to which environment file should look at (it will take your .env.alias* instead). Each file will contain the keys for each environment in Contentful with different content in it, allowing you to deploy aliased sites with different content using a unique CMS.
This might be the most critical problem of Gatsby, and almost people has hard time with it.
The core problem is the "browser environment" is not available when you "build" Gatsby project. And gatsby-config.js is used for NodeJS environment. In other words, everything sticked with window variable is not accessible.
You should read the offical docs about gatsby build process here:
https://www.gatsbyjs.com/docs/overview-of-the-gatsby-build-process/#build-time-vs-runtime.
Solution: you can define different "scripts" in package.json for each alias which you can provide environment variables for NodeJS environment. Then in gatsby-config.js, use dotenv package to read passed variables.
You can read more here about using environment variables: https://www.gatsbyjs.com/docs/environment-variables/#reach-skip-nav

Assets folder resolution: Running an application within a project not picking up assets

I created an application within an Angular workspace. When running
ng serve [application-name]
Picks up images and files in asset folder fine. Now I want to run the workspace with just
ng serve
I would expect through having the application lazy loaded the path would resolve but instead I get
zone.js:3243 GET http://localhost:4200/assets/terms.txt 404 (Not Found)
What is the proper setup to access assets of an application within a workspace?
EDIT After talking offline I think we've found a solution. I'll post it here and leave the below for reference even though the original answer I posted doesn't really apply to the problem.
You're trying to run multiple Angular apps and route between them via something like Firebase where you can direct different routes to different apps.
To get this to work locally for development you will need to run each Angular app separately on it's own port. I suggest you control the destination of the route in the environment files. This way when you are running locally you can point to the port the app is running on and then point to the endpoint Firebase uses in production.
Example environment.ts file in your root app
{
...,
OTHER_APP_URL: 'localhost:4201'
}
prod.environment.ts
{
...,
OTHER_APP_URL: '/otherApp'
}
Then in your component
....
import {environment} from '<path to environments file>';
#Component({
selector: 'my-component',
template: '<a [href]="otherAppUrl"></a>'
})
export class MyComponent implements OnInit {
otherAppUrl: string;
ngOnInit() {
this.otherAppUrl = environment.OTHER_APP_URL;
}
}
You will probably need to do something similar in the other apps so you can route from them to the root app or other child apps. You will probably also need to build the other apps with the --baseHref flag when you build for production so their assets are available. See here for more info from the docs: https://angular.io/cli/build
Old answer - doesn't really apply to the question
Looking at your repo I don't see the terms.txt in your root project's assets folder. I checked to see if it was in one of the other libraries in the repo but wasn't able to find it there either.
If this is an asset that is included or referenced by a component or service in one of your libraries you will need to copy that over to the library's output folder as part of your build process since that functionality isn't currently supported by the Angular CLI.
An example of a build script that might do this for you is:
ng build my-lib-with-assets --prod && cp -r projects/my-lib-with-assets/src/assets dist/my-lib-with-assets && ng build --prod
Don't forget that you need to build your libraries before you build your main project.

How to work with angular 2 profile for grails

it's great news that Grails 3.2.1 now comes with an Angular2 profile, but I don't know how to use it.
The profile description tells me that there should be the standard command like create-domain-class, but when I create an app through
grails create-app test-ng --profile angular2
I get a working angular2 project, but it even seems that this project is not recognized as grails app. When I enter the grails cli, I only get the commands like create-app which are available outside of projects.
What am I doing wrong?
your grails create-app test-ng --profile angular2
command should have created three folders in your test-ng-project-folder:
client
gradle
server
change to server and start grails command
now you should have the wellknown grails project.
but i am still on the first steps of examining the new grails-profile. so i hope i could help you.
Nowadays this layout is called "multi-project". Separate 4 the client and server applications. To make things easier, the tasks test, integrationTest, and bootRun have been created in the client application to make executing those tasks easier across the whole application.
Since Gradle executes tasks synchronously, and the bootRun task will never finish, it is important to execute it in parallel. At the root of the project:
./gradlew bootRun --parallel
Opening things also separately by 2 instances of your IDE or preferred text processor.
see the docs
grails list-profiles
show list of available profiles, I suggest you use this because for example now angular2 profile is angular and angular1 is angularJS.

ERROR: need to include some adapter that implements __karma__.start method [duplicate]

I'm trying to add some unit tests to one of my projects.
So far I've installed and configured karma, and have installed jasmine. I've one test file in my test/ folder.
The karma server has started, the browser page is ready, but karma run fails as follows:
$ karma run karma-conf.js
[2014-06-14 15:19:11.046] [DEBUG] config - Loading config /foo/test/karma-conf.js
Waiting for previous execution...
Chrome 35.0.1916 (Linux) ERROR
You need to include some adapter that implements __karma__.start method!
This error message doesn't Google well.
Is this something obvious, or do I need to provide more information?
It does seem like this is a very general error, however in my case the problem was either that I didn't run karma start from the correct folder, or that I didn't restart it after changing the configuration.
I'll leave this question open and hopefully it can become a resource for others who experience this error message.
If you name your karma config file karma.conf.js, you can simply type karma start.
Otherwise specify the filename karma start karmafile.js
(I was in the right directory, but was not specifying a file name.)
You need to run your karma run or whatever function in the folder where karma-conf.js is in.
In my case I had to rename the file to karma.conf.js then do karma start
I had a problem with karma.conf.js file code format:
files: [
// Modules
,"client/bower_components/angular/angular.min.js"
,"client/bower_components/angular-mocks/angular-mocks.js"
// App
,"client/app/app.module.js"
// Test
// ,"test/**/*.spec.js"
],
The extra comma in files array have cause this error:
You need to include some adapter that implements __karma__.start method!
I was not running karma start command from the path where my karma.conf.js was. I switched to that folder and ran the same command. Thats all
I was facing this issue while running specs on the Angular.js codebase. I had to run npm install karma-jasmine -g to get this working.
If you typing "karma start", you must have a karma.conf.js file in the current folder. or just "karma start /path/karma.conf.js"
Try to create a new karmar.conf.js by "karma init /path/karma.conf.js"
In my case, I was applying by mistake the commonjs preprocessor to the karma-* modules and the adapter.js from karma-chrome and karma-firefox was broken this way.
Unfortunately, this is a very generic error.
I was getting the same error because the project was using Babel 6 and I had forgotten to add the babelrc file. Just sharing in case this could help.
In my case, the message was in fact very descriptive: I forgot to add an adapter, in my case Jasmine, into the config file. Thus:
module.exports = function(config) {
config.set({
browsers: ['Chrome'],
singleRun: true,
frameworks: ['jasmine'],
files: [
// ... files ...
],
// other configs
});
};
You need to configure your "my.conf.js" file, because it contains all the info to use to test your code.
All what you need to write is this:
The configuration file can be generated using karma init:
$ karma init my.conf.js
Which testing framework do you want to use?
Press tab to list possible options. Enter to move to the next question.
jasmine
Do you want to use Require.js?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
no
Do you want to capture a browser automatically?
Press tab to list possible options. Enter empty string to move to the next question.
Chrome
What is the location of your source and test files?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Press Enter to move to the next question.
*.js
test/**/*.js
Should any of the files included by the previous patterns be excluded?
You can use glob patterns, eg. "**/*.swp".
Press Enter to move to the next question.
Do you want Karma to watch all the files and run the tests on change?
Press tab to list possible options.
yes
Config file generated at "/Users/vojta/Code/karma/my.conf.js".
You must specify the karma configuration file to karma.
karma start karma.config.js
It works for me
I must add that in my case karma was giving me the error because I had [square] brackets in the name of one of the parent folders.
Tt's complaining about the adapter which is probably jasmine or mocha.
Either the adapter is missing, either is not setup correctly, either it's an outdated or buggy version of the adapter.
In my case I had an old version of mocha 2.5.3 which was not compatible with karma 1+.
I updated the mocha dependency to the latest version available 3.2.0 and the problem solved.
While working on numerous vaguely described errors, I tried setting basePath: '../', After correcting other errors (like missing commas that were described as object content errors) the last change back to basePath: '', got karma to work. karma's error statements need a lot of work. It is not simple to get it going.
I had the same issue. My browser would open but on my terminal it threw the following error
30 08 2017 11:19:28.272:INFO [Chrome 58.0.3029 (Linux 0.0.0)]: Connected `enter code here`on socket 5sSs6E5KmpUVRp6LAAAB with id 93886631
Chrome 58.0.3029 (Linux 0.0.0) ERRORSome of your tests did a full page reload!
I checked my karma.conf.js file. In the framework array , i had included jasmine and requirejs. But i had only installed karma-jasmine.Removing requireJS and rerunning the karma start worked!
I've been struggling with this too. What I came up to is that this error occurs because one of the following reasons
No test adapter installed or defined (frameworks property in karma.conf.js
No karma configuration found (ie, no karma.conf.js in current dir or no config file specified in command)
My problem was that my config file was named karma.config.js instead of karma.conf.js.
If you've installed the test adapter but it still doesn't work, try running karma init and go through the guided config setup.
You will have to specify the directory of karma.config.js when you run start karma.
Try
karma start karma.config.js
I had a bad 'files' configuration in my karma.conf.js
files: ['**/*.js'],
this caught up all the files in node_modules/ including those of the karma-jasmine plugin, as it was seen as sourde files it wasn't loaded on startup. Changing to
files: [
'src/*.js',
'spec/*.js'
],
solved the problem in my case
FWIW - sourcing the file path of karma.conf.js worked for me locally, but not on my jenkins builder. I have zero clue why this is the case, but on jenkins it was throwing this error unless the karma.conf.js file was in the root directory where the karma command was given. I'm using rails and running the js specs through a rake task. Code is below which might be helpful for some.
https://gist.github.com/daino3/a39486ff8bfc1668e923
The issue in my case was that karma didn't pick up any files. Fixing it in karma.conf.js solved the issue.
Same issue happened to me, and it was due to an outdated module.
Running npm update solved it.
In my case, it was not karma-related at all! I run karma from gulp, with ES6/babel, and there was actually a code syntax error, flagged by a babel error above my karma:
ERROR [preprocessor.babel]: xxx.js: Unexpected token (19:83)
I fixed that in my src and karma was happy again.
In my case, the configuration file name was different. So, running the command specifying the conf file solved my issue.
>> karma start unit-tests.conf.js
No captured browser, open http://localhost:9876/
...
And for 'PhantomJS', opening the link specified(localhost:9876) in the browser executes the test cases.
Hope it helps. :)
EDIT 1: My Karma Config File
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'./node_modules/angular/angular.js',
'./node_modules/angular-ui-router/release/angular-ui-router.js',
'./node_modules/angular-mocks/angular-mocks.js',
'./app/services/users/users.js',
'./app/app.js',
'./app/services/users/users.spec.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['spec'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
I received this error because I required the same dependency twice!
Removing the duplicate file dependency removed the error for me. Circled in blue below.
Given this is the first result on google for this error message, I figured I'd mention my resolution here.
I was getting this error after converting an AngularJS 1.8 project to Typescript and trying to change over my Karma tests to use the typescript preprocessor.
I finally realized that adding import angular from 'angular' resolved the issue on Karma's side, but then angular turned up as undefined in webpack's build.
Eventually thanks to this answer on a question about that error message, I updated my import statements to import * as angular from 'angular', and finally it worked! (Ultimately I had to resolve every compilation error on every typescript file for complete resolution, which I had been somewhat ignoring since the webpack build worked.)
Hopefully this can help someone with this problem!
I had the same problem but above mentioned solutions didn't work for me. What I eventually found out is that my karmaWebpackConfig was using "babel-loader" as loader. I just removed that line and it worked for me.

Automatically switching to minified CSS & JS files in different environments

I am using ASP.Net MVC 5 and also taking advantage of WebEssentials LESS and bundle features.
WebEssentials is setup to create minified css and js files on save. This is a feature I want because I can monitor the output and it lowers the application startup time (not using Asp.Net MVC bundles).
How can I automatically get my app to use the minified version of the files when it is deployed without having to manually change the file names?
So, for example, from:
<link href="somestyles.css" .. >
to:
<link href="somestyles-min.css" .. >
I did read that I could combine WebEssentials and Asp.Net MVC Bundles, providing I disabled the minification option in Asp.Net MVC Bundles. This would then substitute the MIN files in production (web.config [debug="false"]).
Is this my only option or is there a better way to achieve this?
This is definitely not the only way. Another way would be to completely disconnect all Microsoft-based tools (ie bundling, web essentials, etc) and use a Javascript Task Runner. Then the compiling of supersets and pre-processers, minification and whatever other front-end heavy lifting can be in one place. It can also be based on the environment.
So let's address some of your specific concerns.
Task running in the flavor of nodejs and gulp
Download nodejs
After downloading, open up a command prompt and navigate to your project source. For example:
cd "C:\Users\beloud\Documents\Visual Studio 2013\Projects\YourProject"
Initialize a node project by running npm init. This will ask you a bunch of questions about your project. After completion, it will create a file, package.json, which will track your node dependencies and project details.
Now we need to install a few packages. In the command prompt, enter the following commands:
npm install gulp -g
npm install gulp --save-dev
npm install gulp-less --save-dev
npm install gulp-minify-css --save-dev
npm install gulp-if --save-dev
We install gulp globally, so we can use it anywhere (it will add a path for you). Then we install a handful of packages locally to our project that will be doing that actual work (minifying, processing, etc).
Create a file in the same directory as your package.json named gulpfile.js.
Now we need to create our actual tasks. In gulpfile.js, add the following:
//these are the modules that we'll be using in our task
var gulp = require('gulp'),
less = require('gulp-less'),
gulpif = require('gulp-if'),
minifycss = require('gulp-minify-css');
var isDebug = true; // I usually have a config.js file that has this and some handy static paths that I'll be referencing regularly. Then I just require it above.
gulp.task('default', function() {
return gulp.src('Content/less/**/*.less')
.pipe(less())
.pipe(gulpif(isDebug === false, minifycss())) //the first argument is the condition, the second is the method to call if the condition is true
.pipe(gulp.dest('Content/css'));
});
Run gulp in command prompt. That will run the default task. Basically, it will look for any less files in all directories under Content/less, compile them to css, minify them if isDebug is false and output it to Content/css.
Let's make it a little bit more awesome by adding a watch. Add the following to gulpfile.js:
gulp.task('watch', function() {
// Watch .less files
gulp.watch('Content/less/**/*.less', ['default']);
});
Upon running gulp, the task will stay alive until manually terminated. It will watch for changes made to any less file in Content/less and will re-run the task upon saved changes.
Now, you just need to include the name of the css file as it will remain the same, regardless of the environment.
This is a very basic example of using a task runner to achieve what you're trying to do. You can do a whole lot more with nodejs, gulp and everything else I've referenced. I would personally suggest this because, it is way more powerful than the one-off tools you're currently using and Visual Studio 2015 is already heavily relying on this new methodology so you'll most likely have to learn this anyways.
You can learn more by following this really amazing tutorial, Getting started with gulp, by Mark Goodyear.
Grunt (and gulp) support is added in the next Visual studio. This is the javascript developers tools for doing the same thing - bundling for production.
Grunt can create a build version that is not minified for testing but minified for production. I might take some more time and effort but it is the future instead of the MS try they did with bundling. You can already use grunt if you have Node.js installed and be ready for the future.
There is plenty of getting started resource out there. See also Introducing Gulp, Grunt, Bower, and npm support for Visual Studio
Is this my only option or is there a better way to achieve this?
It's not your only option, but since you are working in the realm of MVC it's one of the better options. Since it is designed to be leveraged at different levels, such as individual pages as well as layouts, it will take care of generating the appropriate link.
In general, I would recommend you use a server side bundling framework oriented to MVC so that it can handle the link generation and gives you an intuitive API.
SquishIt is an open-source framework that integrates well with MVC, and is also capable of being switched based on criteria such as a debug flag to generate the original source versus minified versions.
Both SquishIt and the new builtin MVC bundles are fairly similar in terms of what they are meant to accomplish.

Resources