Gulp Env and Preprocess - node.js

In Grunt I used to use a plugin called env. That would allow me to define an environment in specific build. I had 3 builds. One was DEV which would use all the files split up individually. PROD would concat everything and RELEASE would concat and uglify. I'm looking to do the same in Gulp. I do see a preprocessor for Gulp but nothing to define environment.
The question is. What can I do? Obviously I don't want to define all JS files all the time, and I don't want 3 different HTML pages with different script tags.
In my HTML I would have something like this:
<!-- #if NODE_ENV == 'DEVELOPMENT' -->
<script src="js/example1.js" type="text/javascript"></script>
<script src="js/example2.js" type="text/javascript"></script>
<script src="js/example3.js" type="text/javascript"></script>
<!-- #endif -->
<!-- #if NODE_ENV == 'PRODUCTION' -->
<script src="js/project.js" type="text/javascript"></script>
<!-- #endif -->
<!-- #if NODE_ENV == 'RELEASE' -->
<script src="js/project.min.js" type="text/javascript"></script>
<!-- #endif -->
And my grunt plugins would look like this:
env: {
dev: {
NODE_ENV: 'DEVELOPMENT'
},
prod: {
NODE_ENV: 'PRODUCTION'
},
release: {
NODE_ENV: 'RELEASE'
}
},
preprocess: {
options: {
context: {
name: '<%= pkg.outputName %>',
version: '<%= pkg.version %>',
port: '<%= pkg.port %>'
}
},
dev: {
src: 'index.html',
dest: '<%= pkg.outputFolder %>/index.html'
},
prod: {
src: 'index.html',
dest: '<%= pkg.outputFolder %>/index.html'
},
release: {
src: 'index.html',
dest: '<%= pkg.outputFolder %>/index.html'
}
},

You should probably use gulp-preprocess and do stuff like this in gulp
var preprocess = require('gulp-preprocess');
.pipe(preprocess({context: { NODE_ENV: 'PRODUCTION', RELEASE_TAG: '2.6.4', DEBUG: false}}))
with stuff like this in your html
<!-- #if NODE_ENV='DEVELOPMENT' -->
<a href="test?v<!-- #echo RELEASE_TAG -->" />
<!-- #endif -->

Here is how I accomplished what I think you want.
I have set up a folder that contains html pages to be preprocessed.
Within that folder I have folders corresponding to each page where I store html fragments and a json file.
Each JSON file has variables defining page assets for a specific page.
For example, say my page is index.html. It looks something like this:
<html>
<head>
... Meta stuff title etc ...
<!-- #ifdef pagecss1 -->
<link href="<!-- #echo pagecss1 -->" rel="stylesheet">
<!-- #endif -->
<!-- #ifdef pagecss2 -->
<link href="<!-- #echo pagecss2 -->" rel="stylesheet">
<!-- #endif -->
</head>
/// so on - same stuff with scripts at bottom
In my JSON file for that page I either have pagecss1 define or not.
Then I use gulp.watch.
I don't want to write out the whole thing, but the upshot is every time any of the files in the sub folders change a function intercepts the already existing global context variable, and reads the JSON file for that page. Then I use node.util._extend to overwrite the variables with page specific variables. I then pass the changed object to the preprocessor task as the context. It's all lightening quick and returns a callback that livereload know which page to reload.
I wrote this on mobile, so I may come back to edit for clarity, but solving this riddle saved me an incredible amount of time and effort.

Related

Using Mocked Service Worker (msw) with #web/test-runner

I am trying to setup msw with #web/test-runner (and playwright). The problem is that I don't know how the mockServiceWorker.js can be picked up by the test runner (which uses browser, not nodejs like jest). There is an example with karma:
https://github.com/mswjs/examples/tree/master/examples/with-karma, probably I have to do something similar but I have no idea where to start. Any hints are welcome.
I am not sure if it is important, but let me share my web.test-runner.config.js
import vite from 'vite-web-test-runner-plugin'
import { playwrightLauncher } from '#web/test-runner-playwright';
export default {
plugins: [ vite() ],
coverageConfig: {
include: [ 'src/**/*.{svelte,js,jsx,ts,tsx}' ]
},
browsers: [
playwrightLauncher({ product: 'chromium' })
],
testRunnerHtml: testFramework => `
<!DOCTYPE html>
<html>
<head>
<script type="module">
window.global = window;
window.process = { env: {} };
</script>
<script type="module" src="${testFramework}"></script>
</head>
</html>
};
and my test command
"test": "web-test-runner \"test/**/*.test.ts\"",

Requirejs optimized file is not executed

I am seriously missing some vital element on how to use the r.js optimiser. This is probably a trivial answer for many, but I can for the life of me not figure out the problem.
I've set up my environment using a grunt task to build an optimised file. The optimised file is built and dependencies seems to resolve correctly, but the code in my mainfile is never executed.
I've created a minimal environment to help you help me where there is pretty much only the Gruntfile, the mainfile and some dependencies (jquery, almond).
My project structure is:
require_this/
│
├──Gruntfile.coffee
│
└──src/
│
├──index.html
│
├──bower_components/(jquery,almond,requirejs)
│
└──app/
│
└──main.js
Gruntfile:
module.exports = (grunt) ->
'use strict'
grunt.initConfig
pkg: grunt.file.readJSON 'package.json'
settings:
distDirectory: 'dist'
srcDirectory: 'src'
tempDirectory: '.temp'
allFile: 'main.js'
clean:
working: ['<%= settings.tempDirectory %>', '<%= settings.distDirectory %>']
finished: ['<%= settings.tempDirectory %>']
copy:
app:
files: [
cwd: '<%= settings.srcDirectory %>'
src: '**'
dest: '<%= settings.tempDirectory %>'
expand: true
]
requirejs:
scripts:
options:
baseUrl: '<%= settings.tempDirectory %>'
mainConfigFile: '<%= settings.tempDirectory %>/app/main.js'
optimize: 'none'
logLevel: 0
findNestedDependencies: true
name: 'main'
include: ['requireLib']
paths:
'main': 'app/main'
'requireLib': 'bower_components/almond/almond'
out: '<%= settings.distDirectory %>/<%= settings.allFile %>'
processhtml:
your_target:
files:
'dist/index.html': '.temp/index.html'
grunt.loadNpmTasks 'grunt-processhtml'
grunt.loadNpmTasks 'grunt-contrib-clean'
grunt.loadNpmTasks 'grunt-contrib-copy'
grunt.loadNpmTasks 'grunt-contrib-requirejs'
grunt.registerTask 'build', [
'clean:working'
'copy:app'
'requirejs'
'processhtml'
]
index.html:
<!DOCTYPE html>
<head>
<!-- build:js main.js -->
<!-- The line below will be changed to <script src="main.js"></script> after processhtml -->
<script src="bower_components/requirejs/require.js" data-main="app/main.js"></script>
<!-- /build -->
</head>
<body>
</body>
main.js
require.config({
baseUrl: './',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min'
}
});
define([
'jquery',
], function ($) {
console.log('changing html');
$('body').append('<div>Hello World</div>');
});
After completing the build task, my dist directory will contain index.html and main.js file.
The dist/main.js file will look like:
/*almond stuff*/
/*jquery stuff*/
require.config({
baseUrl: './',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min'
}
});
define('main',[
'jquery',
], function ($) {
console.log('changing html');
$('body').append('<div>Hello World</div>');
});
Using a static fileserver against the uncompressed files works as expected. When using the build files, nothing is logged and nothing is added to the html even though the optimised file is loaded.
I suspect the answer is similar to Why is my RequireJS ignoring the code in my optimized main.js?, (some module name mismatch?) but I did not understand this well enough to fix my own problem.
Help is greatly appreciated!
The optimized version simply defines module main but it is never executed because it is never required. I'm not sure why is main executed in the non-optimized version.
The main.js could have rather looked like:
require.config({
baseUrl: './',
paths: {
'jquery': 'bower_components/jquery/dist/jquery.min'
}
});
// use require instead of define here
require([
'jquery',
], function ($) {
console.log('changing html');
$('body').append('<div>Hello World</div>');
});
the difference is that instead of defining a module this code requires jquery and passes it to the anonymous function, which is executed right away. It doesn't look like your app actually relied on that code being available as a module (the function doesn't return anything), so this change should be sufficient.

requirejs optimizer doesn't generate '.js' version of the text resource

In my project I use yeoman (1.0.6). In a fresh webapp copy installed requirejs-text plugin to include template.html.
main.js
require.config({
paths: {
jquery: '../bower_components/jquery/jquery',
text: '../bower_components/requirejs-text/text'
}
});
require(['jquery', 'text!../templates.html'], function ($, templates) {
....
After building and optimizing a whole project, I expect to have generated templates.js file instead of templates.html ( added
"optimizeAllPluginResources: true" as described here )
Gruntfile.js ( won't paste all code, just optimization settings )
....
requirejs: {
dist: {
options: {
baseUrl: '<%= yeoman.app %>/scripts',
optimize: 'none',
optimizeAllPluginResources: true,
preserveLicenseComments: false,
useStrict: true,
wrap: true
}
}
},
....
After grunt 'build' task is completed I see that template.html content is in main.js and there is no generated templates.js file
After adding (also have to set in copy task to copy requirejs-text plugin form app to dir folder ):
stubModules: ['text'],
exclude: ['text!../templates.html'],
files are excluded as expected, but there is still no templates.js file. ( get an error as expected: XMLHttpRequest cannot load file:///...../dist/templates.html. Cross origin requests are only supported for HTTP. it works fine with local HTTP )
My question is: What settings am I missing to generate templates.js file with a requirejs optimizer?
p.s. googled, spent all day, tried more than wrote here.
Thank You in Advance

Ember.js - Handlebars not recognized if required via Grunt (Node.js)

is anybody else using Grunt as build tool for the Ember web application and is experiencing the same behaviour as I do? At this time, I'm using the framework in version RC3 and I can use my build tool without hassle and import all necessary libraries, uglify and compress them and everything works like a charm.
Anyhow, at least since Ember RC5 I'm not able to use Grunt for building my application anymore as Ember would not recognize Handlebars anymore!. It's always complaining that Ember Handlebars requires Handlebars version 1.0.0-rc.4. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember. and right afterwards it says Cannot read property 'COMPILER_REVISION' of undefined which leads me to the assumption that Ember is not recognizing the included Handlebars library.
I haven't changed anything in my app.js (the order of the libraries/frameworks is untouched) except the references to the js files (using Ember RC5/6 instead of RC3 and Handlebars RC4 instead of RC3). But it seems that something breaks the initialization of Ember.Handlebars since then...
Do I get something wrong here? Is there a solution out there so that I can continue using Grunt as build tool?
EDIT
Here's my Gruntfile.js:
/*jshint camelcase: false */
/*global module:false */
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
meta: {
dev: {
buildPath: '.'
},
prod: {
buildPath: '.'
}
},
/*
Task for uglifyng the application javascript file in production environment
*/
uglify: {
options: {
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */'
},
prod: {
files: [
{
src: '<%= meta.prod.buildPath %>/js/application.js',
dest: '<%= meta.prod.buildPath %>/js/application.min.js'
}
]
}
},
/*
Task for creating css files out of the scss files
*/
compass: {
prod: {
options: {
environment: 'production',
noLineComments: true,
outputStyle: 'expanded',
cssDir: '<%= meta.prod.buildPath %>/css',
fontsDir: '<%= meta.prod.buildPath %>/fonts',
imagesDir: '<%= meta.prod.buildPath %>/images',
javascriptsDir: '<%= meta.prod.buildPath %>/js'
}
},
dev: {
options: {
environment: 'development',
noLineComments: false,
outputStyle: 'expanded',
cssDir: '<%= meta.dev.buildPath %>/css',
fontsDir: '<%= meta.dev.buildPath %>/fonts',
imagesDir: '<%= meta.dev.buildPath %>/images',
javascriptsDir: '<%= meta.dev.buildPath %>/js'
}
}
},
/*
Task to minify all css files in production mode.
All css files will end with '.min.css' instead of
just '.css'.
*/
cssmin: {
minify: {
expand: true,
cwd: '<%= meta.prod.buildPath %>/css/',
src: ['*.css', '!*.min.css'],
dest: '<%= meta.prod.buildPath %>/css/',
ext: '.min.css'
}
},
/*
Clean up the production build path
*/
clean: {
cssd: ['<%= meta.prod.buildPath %>/css/**/*']
},
/*
A simple ordered concatenation strategy.
This will start at app/app.js and begin
adding dependencies in the correct order
writing their string contents into 'application.js'
Additionally it will wrap them in evals
with # sourceURL statements so errors, log
statements and debugging will reference
the source files by line number.
This option is set to false for production.
*/
neuter: {
prod: {
options: {
includeSourceURL: false
},
files: [
{
src: 'app/app.js',
dest: '<%= meta.prod.buildPath %>/js/application.js'
}
]
},
dev: {
options: {
includeSourceURL: true
},
files: [
{
src: 'app/app.js',
dest: '<%= meta.dev.buildPath %>/js/application.js'
}
]
}
},
/*
Watch files for changes.
Changes in dependencies/ember.js or application javascript
will trigger the neuter task.
Changes to any templates will trigger the ember_templates
task (which writes a new compiled file into dependencies/)
and then neuter all the files again.
*/
watch: {
application_code: {
files: ['js/dependencies/ember.js', 'app/**/*.js'],
tasks: ['neuter:dev']
},
compass: {
files: [
'styles/**/*.scss'
],
tasks: ['compass:dev']
}
},
/*
Runs all .html files found in the test/ directory through PhantomJS.
Prints the report in your terminal.
*/
qunit: {
all: ['test/**/*.html']
},
/*
Reads the projects .jshintrc file and applies coding
standards. Doesn't lint the dependencies or test
support files.
*/
jshint: {
all: ['Gruntfile.js', 'app/**/*.js', 'test/**/*.js', '!js/dependencies/*.*', '!test/support/*.*'],
options: {
jshintrc: '.jshintrc'
}
},
/*
Generate the YUI Doc documentation.
*/
yuidoc: {
name: '<%= pkg.name %>',
description: '<%= pkg.description %>',
version: '<%= pkg.version %>',
options: {
paths: '<%= meta.dev.buildPath %>/app/',
outdir: '<%= meta.dev.buildPath %>/yuidocs/'
}
},
/*
Find all the <whatever>_test.js files in the test folder.
These will get loaded via script tags when the task is run.
This gets run as part of the larger 'test' task registered
below.
*/
build_test_runner_file: {
all: ['test/**/*_test.js']
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-neuter');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-yuidoc');
/*
A task to build the test runner html file that get place in
/test so it will be picked up by the qunit task. Will
place a single <script> tag into the body for every file passed to
its coniguration above in the grunt.initConfig above.
*/
grunt.registerMultiTask('build_test_runner_file', 'Creates a test runner file.', function () {
var tmpl = grunt.file.read('test/support/runner.html.tmpl');
var renderingContext = {
data: {
files: this.filesSrc.map(function (fileSrc) {
return fileSrc.replace('test/', '');
})
}
};
grunt.file.write('test/runner.html', grunt.template.process(tmpl, renderingContext));
});
/*
A task to run the application's unit tests via the command line.
It will
- convert all the handlebars templates into compile functions
- combine these files + application files in order
- lint the result
- build an html file with a script tag for each test file
- headlessy load this page and print the test runner results
*/
grunt.registerTask('test', ['neuter', 'jshint', 'build_test_runner_file', 'qunit']);
/*
Configures all tasks which will be executed with production setup
*/
grunt.registerTask('prod_tasks', ['clean', 'compass:prod', 'cssmin', 'neuter:prod', 'uglify:prod']);
/*
Setup for the production build. Sets the production build path.
*/
grunt.registerTask('prod', 'Production Build', function () {
grunt.task.run('prod_tasks');
});
/*
Configures all tasks which will be executed with development setup
*/
grunt.registerTask('dev_tasks', ['compass:dev', 'neuter:dev', 'watch']);
/*
Setup for the development build. Sets the development build path.
*/
grunt.registerTask('dev', 'Development Build', function () {
grunt.task.run('dev_tasks');
});
// Default task
grunt.registerTask('default', 'dev');
/*
Configures all tasks which will be executed with doc setup
*/
grunt.registerTask('doc_tasks', ['yuidoc']);
/*
Setup for the YUI doc generation.
*/
grunt.registerTask('doc', 'Generate YuiDoc Documentation for the App', function () {
grunt.task.run('doc_tasks');
});
};
EDIT 2
I took the ember-1.0.0-rc.6.js and handlebars-1.0.0-rc.4.js files from the starter kit at the Ember.js website and tried to run the Grunt tasks on it. Here's what Chrome is telling me:
EDIT 3
Just in case if anybody cares, here's the link to the raised issue over at the Ember.js Github page: https://github.com/emberjs/ember.js/issues/2894
EDIT 4
Finally, the issue was identified to be a Handlebars inconsistency when dealing with global exports, like #Tao reported in his answer. Here's the link to the Issue on GitHub if you want to follow: https://github.com/wycats/handlebars.js/issues/539
It looks like this issue is being addressed in the next version of Handlebars: https://github.com/wycats/handlebars.js/issues/539 Stay tuned.
You have a mismatch in the version used to compile the Handlebars templates and the version included via the script tag.
If you are using grunt-contrib-handlebars, it uses the npm module handlebars to compile the the templates. The handlebars module/project is independent of Ember and has its own revisions that may or not be compatible with Ember.
To maintain compatibility with Handlebars Ember needs specific versions of handlebars which it is warning you about.
The tricky part here is you need to ensure that grunt-contrib-handlebars is forced to use that specific version of handlebars.
Solution 1: Use shrinkwrap to change grunt-contrib-handlebars's handlebars dependency version.
Solution 2: This is what I am currently using. I have switched to Emblem. The emblem grunt task asks for your handlebars file explicitly so you don't have to drop down to node sub dependency management. And your build includes the same file into your script tags, thus avoiding duplication/mismatch for future revisions.
Edit: After Gruntfile edit
Looking at the Gruntfile I don't see anything amiss. Looks like a standard build process, js -> neuter -> (if prod) -> uglify etc.
I think you need to try refreshing both emberjs and handlebars js files. Try using the files from the starter kit itself, those definitely work together.
And verify this for your index.html by looking at the unminified source in Chrome/Inspector. Handlebars has the revision numbers below the banner something like Handlebars.VERSION and Handlebars.COMPILER_REVISION. Match those with what you see in the ember.js file, somewhere below #submodule ember-handlebars-compiler in the Ember.assert.

nodejs / grunt usemin plugin

I'm using yeoman and grunt to build my project and grunt-css plugin for using 'cssmin' instead of 'css' built-in with grunt.js
index.html
<!-- build:css styles/styles.css -->
<link rel="stylesheet" href="styles/main.css"/>
<!-- endbuild -->
<!-- build:js scripts/scripts.js -->
<script src="scripts/vendor/jquery.min.js"></script>
<script src="scripts/vendor/handlebars-1.0.0.beta.6.js"></script>
<script src="scripts/vendor/ember-1.0.pre.min.js"></script>
<script src="scripts/main.js"></script>
<script src="scripts/routes/app-router.js"></script>
<script src="scripts/store.js"></script>
<script src="scripts/controllers/application-controller.js"></script>
<script src="scripts/models/application-model.js"></script>
<script src="scripts/views/application-view.js"></script>
<!-- endbuild -->
Gruntfile.js
rev: {
js: 'dist/scripts/**/*.js', // scripts/**/*.js
css: 'dist/styles/**/*.css', // styles/**/*.css
img: 'dist/images/**' // images/**
},
'usemin-handler': {
html: 'index.html'
},
usemin: {
html: ['dist/**/*.html'], // **/*.html
css: ['dist/**/*.css'] // **/*.css
},
rjs: {
// no minification, is done by the min task
optimize: 'none',
baseUrl: './scripts',
wrap: true
},
cssmin: {
dist: {
src: [
'app/styles/**/*.css'
],
dest: 'dist/styles/styles.css'
}
},
concat: {
dist: {
src: [
'app/scripts/**/*.js'
],
dest: 'dist/scripts/scripts.js',
separator: '/**********/\n'
}
},
min: {
dist: {
src: [
'dist/scripts/scripts.js'
],
dest: 'dist/scripts/scripts.js',
separator: '/**********/\n'
}
}
Then the build project structure is:
dist/
|__scripts/
|____04216377.scripts.js
|__styles/
|____d41d8cd9.styles.css
|__index.html
Then output index.html file
<link rel="stylesheet" href="styles/styles.css"/?>
<script src="scripts/04216377.scripts.js"></script>
As you see all went OK except renaming the revisioned styles in index.html that should be 'styles/d41d8cd9.styles.css
Anyone knows why?
And is the questionmark '?' in the line normal???
Note: for more information this is outputted in my console (no errors)
Running "rev:js" (rev) task
dist/scripts/scripts.js --- 04216377.scripts.js
Running "rev:css" (rev) task
dist/styles/styles.css --- d41d8cd9.styles.css
Running "rev:img" (rev) task
Running "usemin:html" (usemin) task
usemin:html - dist/index.html
scripts/scripts.js
was <script src="scripts/scripts.js"></script>
now <script src="scripts/04216377.scripts.js"></script>
Running "usemin:css" (usemin) task
usemin:css - dist/styles/d41d8cd9.styles.css
And no renaming has been done!
Thanks a lot guys!
I've found the problem.
I've got Yeoman 0.94 version and needs a fix on usemin task.
The ?character at <link>is a regex mistake.
You should rewrite this expression because css renaming is failing.
Found the correct workaround at https://github.com/yeoman/yeoman/issues/586
replace
content.replace(block, indent + '<link rel="stylesheet" href="' + target + '"\/?>');
with
content.replace(block, indent + '<link rel="stylesheet" href="' + target + '"/>');
If apply changes this issue is solved.
Note: apply the patch on usemin.js at /usr/local/lib/node_modules/yeoman/tasks (on OSX)

Resources