TypeError: Handlebars.templates is undefined - undefined

I'm working with yeoman, gruntjs and handlebars.js, but my template don't load anymore with the following error in firebug:
TypeError: Handlebars.templates is undefined
var compiledTemplate = Handlebars.templates['cheatsheet.hbs'];
Handlebars.JS
In my package.json, I got:
"grunt-contrib-handlebars": "~0.5.9" // previously used ~0.5.8
Gruntjs tasks
Task: handlebars
I'm compiling .hbs to .hbs.js files:
handlebars: {
compile: {
options: {
namespace: 'JST'
},
files: {
'<%= yeoman.app %>/scripts/cheatsheet.hbs.js':
[ '<%= yeoman.app %>/templates/{,*/}*.hbs'] ,
}
}
},
Task: watch
I added the following in the watch section:
watch: {
// recompile handlebars' templates when they change
// #see: https://github.com/yeoman/yeoman/wiki/Handlebars-integration
handlebarsCompile: {
files: ['<%= yeoman.app %>/templates/{,*/}*.hbs'],
tasks: ['handlebars:compile']
},
handlebarsReload: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.hbs.js'],
tasks: ['livereload']
},
Tasks: grunt server and grunt build
I added the following entry to both task:
'handlebars:compile',
HTML file
I'm importing handlebars, the template and the script to inflate it:
<script src="components/handlebars.js/dist/handlebars.runtime.js"></script>
<script src="scripts/cheatsheet.hbs.js"></script>
<script src="scripts/main.js"></script>
Compiled template: cheatsheet.hbs.js
In the top lines, I got this:
this["JST"]["app/templates/cheatsheet.hbs"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
Template inflater: main.js
To inflate my compiled template I'm using this:
var compiledTemplate = Handlebars.templates['cheatsheet.hbs'];
Question
So what's the matter here Handlebars.templates array? Why is not created? How to create it?
More info
I created a gist to hold the full Gruntfile.js and cheatsheet.hbs.js.

After reading the section on precompiler usage:
If using the precompiler's normal mode, the resulting templates will
be stored to the Handlebars.templates object using the relative
template name sans the extension. These templates may be executed in
the same manner as templates.
I went on to debug the compiled template.
Debugging
Manual compilation
As I installed handlebars global, I can run compile templates manually. This wasn't enough, and I had to update the live file:
handlebars ./app/templates/cheatsheet.hbs -f ./app/scripts/cheatsheet.hbs.js # compile
cp ./app/scripts/cheatsheet.hbs.js ./.tmp/scripts/cheatsheet.hbs.js # update .tmp's template
Comparing with what grunt outputs
I saw that compiled template where different, the template reference doesn't occur in the same variable.
Manually compiled vs. Grunt compiled
- (function() {
- var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
- templates['cheatsheet.hbs'] = template(function (Handlebars,depth0,helpers,partials,data) {
+ this["JST"] = this["JST"] || {};
+
+ this["JST"]["cheatsheet.hbs"] = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
So I went to my task and saw
namespace: 'CHSH.Templates'
So I read the doc about namespace, and I wasn't using the right namespace in main.js
Solution
Step #1: Updating package
First globally:
sudo npm update handlebars -g
Then locally
bower update
I got some message about handlebars, but doesn't block:
Please note that
requires handlebars.js ~1.0.11
Resolved to handlebars.js v1.0.0, which matches the requirement
defined in the project's component.json. Conflicts may occur.
Step #2: Update Gruntfile.js
I set the namespace to CHSH.Templates (cf. doc about namespace) ;
I updated the files option to compile the *.hbs template from the app/templates directory to the .tmp/scripts/ and
app/scripts directories;
handlebars: {
compile: {
options: {
namespace: 'CHSH.Templates'
},
files: [{
expand: true,
cwd: '<%= yeoman.app %>/templates',
src: '*.hbs',
dest: '<%= yeoman.app %>/scripts/',
ext: '.hbs.js'
},
{
expand: true,
cwd: '<%= yeoman.app %>/templates',
src: '*.hbs',
dest: '.tmp/scripts/',
ext: '.hbs.js'
}
]
}
}
I also edited to watch task to look after scripts/{,*/}*.js.
Step #3: Update main.js
Then I updated the namespace to match what I declared in my Gruntfile.js
- var compiledTemplate = Handlebars.templates['cheatsheet.hbs'];
+ var compiledTemplate = CHSH.Templates['app/templates/cheatsheet.hbs'];

Related

Grunt bake file include cannot read file

I have the following index.html in my root directory
When I run grunt I get the following error:
Verifying property bake.my_target exists in config...OK
Files: index.html -> dist/index.html
Options: content="content.json", section=null, semanticIf=false, basePath="", transforms={}, parsePattern={}, variableParsePattern={}, removeUndefined
Reading content.json...OK
Parsing content.json...OK
Reading index.html...OK
Reading /includes/test.html...ERROR
Warning: Unable to read "/includes/test.html" file (Error code: ENOENT). Use --force to continue.
This is my gruntfile.js:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
},
bake: {
my_target: {
options: {
content: "content.json"
},
files: {
"dist/index.html": "index.html"
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks( "grunt-bake" );
// Default task(s).
grunt.registerTask('default', ['uglify', 'bake']);
};
What am I doing wrong. I just followed the docs from this link: https://www.npmjs.com/package/grunt-bake
It looks like grunt-bake has a bug with include path parsing if baking file is placed at root of folder. Try to put your index.html to some folder, like 'src/index.html' - this worked for me (after few hours wasted).
Source code of grunt-bake contains this line so it always put '/' to "include" file path - I guess this is why it does not work for files at root folder:
directory( filePath ) + "/" + includePath

Polymer 3.0.5 - "DOMException: Failed to execute 'define' on 'CustomElementRegistry'"

I don't think this is a duplicate issue. I only have #polymer/polymer installed as a dependency and imported into my vendor bundle (no #polymer/paper-input). I'm using v3.0.5 and I don't even see iron-meta in the dependency tree (via npm list) and my stack trace looks different - it points to polymer/lib/elements/dom-module.js
dom-module.js:178 Uncaught DOMException: Failed to execute 'define' on
'CustomElementRegistry': this name has already been used with this
registry
The trace points to this line customElements.define('dom-module', DomModule);
at #polymer/polymer/lib/elements/dom-module.js?:178:16
I'm attempting to setup a basic Polymer 3 project. I'm using Webpack with babel-loader to compile to es5. Because I'm compiling to es5, I'm including the custom-elements-es5-adapter.js along with webcomponents-bundle.js per instructions on the webcomponentsjs repo. Those scripts are simply copied from node_modules to the output directory and the script tags are included in the html head.
As for my component code, I'm creating separate js chunks for each polymer component as well as a separate chunk for shared imports which currently only includes Polymer. The compilation and code splitting works without error and the resulting chunks are added to the html before the closing body tag.
The Webpack SplitChunks plugin pulls the #polymer/polymer imports into the separate chunk so that they are only included once.
The goal is to have all required vendor code pulled into a common script and each component in a tiny chunk of it's own that can be selectively included.
my-common.js (shared/common chunk)
my-button.js (component chunk)
my-tabs.js (component chunk)
...more component chunks
With my current setup, the chunks appear to be created correctly.
In theory and based on what I've read so far, this should work but I'm completely stuck on this error.
If I bundle my component files together, everything works fine.
Here's an example of one of my very simple component files:
import { html, PolymerElement } from '#polymer/polymer';
export default class MyButton extends PolymerElement {
constructor() {
super();
}
static get template() {
return html`
<slot></slot>
`;
}
static get properties() {
return { }
}
}
customElements.define('my-button', MyButton);
Here is the webpack config I've created for this proof of concept:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const SRC_PATH = path.resolve(__dirname, './src');
const DIST_PATH = path.resolve(__dirname, './dist');
module.exports = {
entry: {
'my-button': `${SRC_PATH}/js/components/my-button.js`,
'my-tabs': `${SRC_PATH}/js/components/my-tabs.js`
},
output: {
filename: 'js/[name].js',
path: DIST_PATH
},
resolve: {
extensions: ['.js']
},
module: {
rules: [{
test: /\.js$/,
loader: 'babel-loader',
query: {
presets: [[
'env',
{
targets: {
browsers: [
'last 2 versions',
'ie > 10'
]
},
debug: true
}
]]
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: `${SRC_PATH}/index.html`,
filename: 'index.html',
inject: 'head'
}),
new CopyWebpackPlugin([{
from: './node_modules/#webcomponents/webcomponentsjs/custom-elements-es5-adapter.js',
to: 'js/vendor',
toType: 'dir'
}, {
from: './node_modules/#webcomponents/webcomponentsjs/webcomponents-bundle.js',
to: 'js/vendor',
toType: 'dir'
}, {
from: './node_modules/#webcomponents/webcomponentsjs/webcomponents-loader.js',
to: 'js/vendor',
toType: 'dir'
}]),
new BundleAnalyzerPlugin()
],
optimization: {
splitChunks: {
cacheGroups: {
default: false,
commons: {
name: 'my-common',
chunks: 'all',
minChunks: 2
}
}
},
minimizer: [
new UglifyJSPlugin({
uglifyOptions: {
ie8: false,
safari10: false,
compress: {
warnings: false,
drop_console: true
},
output: {
ascii_only: true,
beautify: false
}
}
})
]
},
devServer: {
contentBase: DIST_PATH,
compress: false,
overlay: {
errors: true
},
port: 8080,
host: '127.0.0.1'
}
};
And here's the html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>polymer-3-sandbox</title>
<meta name="description" content="A polymer 3 sandbox">
<link rel="manifest" href="/manifest.json">
<script src="/js/vendor/webcomponents-bundle.js"></script>
<script src="/js/vendor/custom-elements-es5-adapter.js"></script>
<script type="text/javascript" src="js/my-common.js"></script>
<script type="text/javascript" src="js/my-button.js"></script>
<script type="text/javascript" src="js/my-tabs.js"></script>
</head>
<body>
<p>
<my-button>Learn More</my-button>
</p>
</body>
</html>
We have solved this problem with a nested polymer removal script, check the original github issue here.
The trick is to get npm to run a preinstall.sh script by adding the following to your package.json file :
"scripts": {
"preinstall": "../preinstall.sh"
}
Then run the following script which installs npm scriptlessly twice to get around install bugs :
#!/bin/bash
# Author: Flatmax developers
# Date : 2018 10 17
# License : free
npm i --ignore-scripts || true
if [ `ls node_modules/ | wc -l` -eq "0" ]; then
zenity --error --text="ERROR : cb() never called\nrm node_modules and pacakge-lock.json and try again"
fi
npm i --ignore-scripts || true
if [ `ls node_modules/ | wc -l` -eq "0" ]; then
zenity --error --text="ERROR : cb() never called\nrm node_modules and pacakge-lock.json and try again"
fi
. ../fixNestings.sh
Finally, the actual nestings removal script is like so :
#!/bin/bash
# Author: Flatmax developers
# Date : 2018 10 17
# License : free
# The following function will remove nested directories, where $1 exists like so
# node_modules/.*/node_modules/$1
# #param $1 the module name to remove nestings of
function rmNestedMod(){
name=$1
paths=`find -L node_modules -name $1 | sed "s|^node_modules/||;s|/\$name$||" | grep node_modules`
for p in $paths; do
echo rm -rf node_modules/$p/$name
rm -rf node_modules/$p/$name
done
}
# remove all nested polymer namespaces
namespaces=`ls node_modules/#polymer/`
for n in $namespaces; do
rmNestedMod "$n"
done

composer in a gulp build and deployment

How do you get composer to install dependencies correctly whenusing a gulp build?
My build process set up that outputs to a set location, either ../sites/www/public_html or ../sites/dev/public_html dependent on if an environment argument is passed to a gulp task. These locations essentially mirror my remote host.
I'm wanting to automate composer installs, updates and optimisation to output the correct vendor files in either ../sites/www/vendor or ../sites/dev/vendor whenever the build is initially run or to just optimise based on any watched php files being changed.
My build folder has the following structure:
source/
bower.json
composer.json
composer.lock
gulpfile.json
package.json
My example composer.json has the following:
{
"name": "mycomposer/mycomposer",
"version": "1.0.0",
"autoload": {
"psr-4" : {
"mycomposer\\": "public_html/app/mycomposer"
}
},
"require": {
"rollbar/rollbar": "^1.3",
"vlucas/phpdotenv": "^2.4.0"
}
}
I have tried a gulp-compose and task to install the composer libraries and to run dumpautoload for local first party libraries
gulp.task('composer', function() {
var dest = argv.live ? 'www' : 'devsite',
env = '../sites/' + dest + '/public_html';
$.composer('config vendor-dir ' + env.replace('public_html', 'vendor') );
$.composer({
"no-ansi": true,
"no-dev": true,
"no-interaction": true,
"no-progress": true,
"no-scripts": true,
"optimize-autoloader": true
});
$.composer('dumpautoload ', {
optimize: true
});
});
When the task is complete I'm finding is that the $baseDir variable references the build directory
Expected
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
Output
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname(dirname(dirname($vendorDir))).'/mybuild';
Is this something I can achieve, or should I really be running composer separately from my build process?
Thanks
I had a similar problem.
I use gulp-composer package.
gulpfile.js
const composer = require('gulp-composer');
gulp.task('composer-deployed', async function() {
let opts = {
"working-dir": 'my-path-to-composer.json',
"self-install": true, // false for my case
optimize: true,
"classmap-authoritative": true
};
composer("dumpautoload", opts);
});
In my case, I use composer installed globaly but you can choose the bin path with bin: 'path-to-composer.phar' in opts{}.

babel v6 + react-intl v2 + webpack extraction of messages to file

I would like to extract the messages from the source code to a JSON file, using Babel v6, react-intl v2, and webpack. It's all compiling fine, but not extracting anything. How do I make v2 of react-intl properly extract the messages?
There's an option messagesDir:
I've tried:
new file .babel.rc from here: https://github.com/yahoo/babel-plugin-react-intl/issues/23 – probably misspelt name, should be .babelrc.
new file .babelrc:
{
"extra": {
"react-intl": {
"messagesDir": "./i18n",
"enforceDescriptions": true
}
}
}
With webpack config
{ test: /\.js$/,
loader: 'babel-loader',
query: {
// react needed for JSX syntax
// es2015 needed for modules
// stage0 needed to do splats (...variable)
presets: ['react', 'es2015', 'stage-0'],
// react-intl extracts i18n messages to a shared file
// add-module-exports removes the need to do `require('a').default`
plugins: ['react-intl', 'add-module-exports'],
cacheDirectory: true
},
exclude: /node_modules|bower_components/
},
gives: "index.js: Unknown option: /.babelrc.extra" as the error message.
I've tried adding the above extra property to the webpack configuration above, too. It gives a very similar error message.
Versions:
✗ npm ls | grep "intl"
├─┬ babel-plugin-react-intl#2.0.0
│ └── intl-messageformat-parser#1.2.0
├── intl#1.0.1
├── intl-locales-supported#1.0.0
├── intl-messageformat#1.2.0
├─┬ react-intl#2.0.0-pr-3
│ ├── intl-format-cache#2.0.4
The answer is to use a very custom webpack syntax:
Syntax is thus:
5 plugins: [
- ['react-intl', {
6 'messagesDir': './i18n'
6 }], 'add-module-exports']

No tests executed with grunt-qunit-junit

I am following this guide to generate junit output from my js tests:
https://github.com/sbrandwoo/grunt-qunit-junit
I have installed grunt-qunit-junit into my local test project:
npm install grunt-contrib-qunit --save-dev
And this is my Gruntfile.js:
module.exports = function(grunt) {
"use:strict";
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
qunit_junit: {
options: {
},
all: ["all_tests.html"]
},
})
grunt.loadNpmTasks('grunt-qunit-junit');
};
where all_tests.html is located in the same dir and lists all my *test.js files. But when I run:
user#ubuntu:~/Test$ grunt qunit_junit
Running "qunit_junit" task
>> XML reports will be written to _build/test-reports
Done, without errors.
Why are the tests not executed (the folder _build/test-reports is not created)?
The README states that you should execute both the qunit_junit and qunit tasks: http://github.com/sbrandwoo/grunt-qunit-junit#usage-examples
For example: grunt.registerTask('test', ['qunit_junit', 'qunit']);