Rollup Handlebars Helpers - javascript

I am trying to use rollup on my project in which I also use handlebars. In particular, I am using a simple helper for one of the templates. I found 3 different rollup plugins for handlebars and have tried all of them. The closest I've gotten to having it work is using rollup-plugin-handlebars-plus.
Here is the rollup.config.js:
import resolve from 'rollup-plugin-node-resolve';
import handlebars from 'rollup-plugin-handlebars-plus';
// import handlebars from 'rollup-plugin-handlebars';
///import handlebars from 'rollup-plugin-hbs';
import commonjs from 'rollup-plugin-commonjs';
export default {
// tell rollup our main entry point
input:'assets/js/exp.js',
output: {
name: 'rootmont',
file: 'build/js/main.min.js',
format: 'iife',
globals: {
jquery: '$'
}
// format: 'umd'
},
plugins: [
resolve({
// pass custom options to the resolve plugin
customResolveOptions: {
moduleDirectory: [ 'node_modules']
}
}),
commonjs({
include: 'node_modules/**',
}),
handlebars({
helpers:['assets/js/handlebarsHelpers.js'],
// templateExtension: '.html'
})
],
};
Here is handlebarsHelpers.js:
export default function(Handlebars) {
Handlebars.registerHelper( 'percent', function( number ) {
let num = number * 100;
num = Math.round( num * 100 ) / 100;
return num;
});
}
And here is pct.hbs:
<div>
{{#each data}}
<tr>
<td>
{{#key}}
</td>
<td>
{{percent this}}%
</td>
</tr>
{{/each}}
</div>
After I do rollup -c in the command line, here is the output.
assets/js/exp.js → build/js/main.min.js... (!) Unresolved dependencies
https://rollupjs.org/guide/en#warning-treating-module-as-external-dependency
assets/js/handlebarsHelpers.js (imported by assets/hbs/pct.hbs) (!)
Missing global variable name Use output.globals to specify browser
global variable names corresponding to external modules
assets/js/handlebarsHelpers.js (guessing 'Helpers0')
It says handlebarsHelpers.js is imported by pct.hbs, which it isn't but maybe that's a result of preprocessing. It seems like I need to explicitly import handlebarsHelpers.js somewhere to get rollup to put it in the bundle. But, doing so presents it's own problems, like having to import the handlebars and fs library, which seems like the wrong way.
Anyone know what I'm missing here?

Try to do something like this in your rollup.config.js:
{
external: [
'assets/js/handlebarsHelpers.js'
],
output: {
globals: {
'assets/js/handlebarsHelpers.js': 'Helpers0',
},
...
},
...
}
It would be nice to have a repo for reproducing though...

Related

How to dynamically bundle module/object in RollupJs output?

How can I dynamically bundle a module/object into my RollupJs output file? I have tried a ton off different options but can not get the expected output I am looking for.
I put together a short sample project below to help illustrate what I am looking for. The expected output should print "Hello John Doe" from the overrideApp object that is dynamically injected as a dependency.
src/app.js
export default {
sayHello: function() {
console.log('Hello Mr.Roboto')
},
sayGoodBye: function() {
console.log('Goodbye Mr.Roboto')
}
}
index.js
import app from './src/app.js'
import overrideApp from 'overrideApp'
export default { ...app, ...overrideApp }.sayHello()
.rollup.config.js
let overrideApp = {
sayHello: function() {
console.log('Hello John Doe')
}
}
export default [
{
input: 'index.js',
external: ['overrideApp'], // This is not working, expecting to pass overrideApp to index.js
output: {
file: './dist/app.js',
format: 'umd',
name: 'bundle',
}
}
]
This is totally correct your mixing here a lot of stuff together that does not work together.
You are looking for a virtual module
Install
npm install #rollup/plugin-virtual --save-dev
Usage
Note. Use this plugin before any others such as node-resolve or commonjs, so they do not alter the output.
Suppose an entry file containing the snippet below exists at src/entry.js, and attempts to load batman and src/robin.js from memory:
// src/entry.js
import batman from 'batman';
import robin from './robin.js';
console.log(batman, robin);
Create a rollup.config.js configuration file and import the plugin:
import virtual from '#rollup/plugin-virtual';
export default {
entry: 'src/entry.js',
// ...
plugins: [
virtual({
batman: `export default 'na na na na na'`,
'src/robin.js': `export default 'batmannnnn'`
})
]
};
https://github.com/rollup/plugins/edit/master/packages/virtual

Rollup config for React component library not working with SSR

I'm trying to set up a React component library using Rollup, which my React app then installs and uses. The consuming React application is being rendered on the server. I have managed to get this set up working with Webpack, wherein I'm bundling my React component library into one single file and sending it down. Problem is this is a huge file and negates the point of SSR (part of it at least) as it takes a long time to download.
So I decided to use Rollup to split up the components into individual ES modules which are then pulled in and used as required by the app. The problem is that I can't get this to work with SSR at all. This is my first time using Rollup so I may have missed something obvious. Here is my rollup.config.js
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import css from 'rollup-plugin-css-only';
import alias from '#rollup/plugin-alias';
import image from '#rollup/plugin-image';
import json from '#rollup/plugin-json';
import replace from '#rollup/plugin-replace';
import namedDeps from './named-dependencies';
import stylus from './rollup-plugin-stylus-v2';
import postcssUrl from './rollup-plugin-postcss-url';
import autoExternal from 'rollup-plugin-auto-external';
import url from '#rollup/plugin-url';
import fs from 'fs';
const namedDepsObject = namedDeps
.map(depPath => ({
keys: Object.keys(require(depPath)).filter(v => v !== 'default'),
name: depPath,
}))
.reduce((acc, val) => {
acc[val.name] = val.keys;
return acc;
}, {});
let ComponentsList = require('./components-file'); // This has an array of component src and name
const rollConfigObject = fileName => {
const configOpts = {
input: `${fileName.src}`,
output: [
{
file: `dist/ssr/esm/${fileName.name}.js`,
format: 'esm',
}
],
plugins: [
autoExternal(),
url({
include: ['**/*.svg', '**/*.png', '**/*.jpg', '**/*.gif', '**/*.woff', '**/*.woff2', '**/*.eot', '**/*.ttf', ],
emitFiles: false
}),
babel({
exclude: 'node_modules/**',
presets: ['#babel/env', '#babel/react'],
plugins: ['#babel/plugin-syntax-dynamic-import'],
runtimeHelpers: true,
}),
resolve(),
commonjs({
sourceMap: false,
namedExports: {
...namedDepsObject,
},
}),
image(),
json(),
stylus(),
postcssUrl(),
css({
output: function(styles, styleNodes){
fs.appendFileSync(__dirname + '/dist/ssr/styles.css', styles);
}
}),
],
};
return { ...configOpts };
};
export default ComponentsList.map(
moduleName => {
return rollConfigObject(moduleName);
}
)
Now this creates the separate component bundles like I want, but the problem is that it seems to be including code within the createCommonjsModule methods from node_modules which is then injecting browser objects like document, which when I attempt to render on the server throws an error.
I am trying to not include node_modules at all, so when I try to run this with only the babel plugin, it throws an Error: Could not resolve entry module and refuses to build. An example is Braintree, it's adding a lot of code with document usage.
I'm not sure how to get it to not inject node_modules code within the component. In the app, I'm using Webpack to run these modules through babel, but objects like document will pass through and then refuse to work on the server.
Any suggestions would be great here, been struggling with this for a number of days now. Thanks!
I got the config to work after I updated my babel setting to
babel({
exclude: [
'../../node_modules/**',
'../node_modules/**',
'node_modules/**',
],
presets: [
[
'#babel/env',
{
modules: false,
targets: {
esmodules: true,
},
},
],
'#babel/react',
],
plugins: [
'#babel/plugin-proposal-object-rest-spread',
'#babel/plugin-transform-runtime',
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
],
runtimeHelpers: true,
}),
commonjs({ sourceMap: false }),

Use glob or regex pattern for rollup.js externals

Is it possible to use a glob or regex pattern for the externals in my rollup config? With this config:
export default {
...
external: [
'prop-types',
'react',
'prettier/standalone',
'prettier/parser-babylon',
'react-syntax-highlighter/prism-light',
'react-syntax-highlighter/languages/prism/jsx',
'react-syntax-highlighter/styles/prism/tomorrow',
'react-element-to-string'
],
...
};
I would like to do something like:
export default {
...
external: [
'prop-types',
'react',
'prettier/**',
'react-syntax-highlighter/**',
'react-element-to-string'
],
...
};
This is not possible at the moment. You can however use a function to achieve something similar:
export default {
...
external(id) {
return [
'prop-types',
'react',
'prettier',
'react-syntax-highlighter',
'react-element-to-string'
].includes(id.split('/')[0]);
},
...
};
You should avoid costly computations in this function as it will be called a lot (once for every import in every file to be precise).
Another option is to add the rollup-pluginutils package as a dependency which contains a createFilter function for glob support:
import { createFilter } from 'rollup-pluginutils';
const external = createFilter([
'prop-types',
'react',
'prettier/**',
'react-syntax-highlighter/**',
'react-element-to-string'
], null, {resolve: false});
// {resolve: false} will make sure these filters are not passed to
// path.resolve first and resolved against the current working directory
export default {
...
external,
...
};
Regex in external is also supported by rollup now, like this:
export default {
external: ['three', /three\/.*/]
}
This will mark three and three/* as externals, I have used in my projects, it works as expected.
Please refer https://rollupjs.org/guide/en/#external for more options.

(!) Unused external imports. 'reduce' imported from external module 'lodash' but never used

I am using rollup to build my library and I have a dependency on lodash.
but when I run rollup to bundle my code, I get this warning.
(!) Unused external imports
reduce imported from external module 'lodash' but never used
A sample of my code is as follow:
import { reduce } from "lodash"
export function someutilityfunction(args) {
return reduce(args,() => {
// do somthing
}, {}) // A generic use case of reduce function
}
the bundled library works fine.
I have even tried using
import * as _ from "lodash"
and lodash-es instead of lodash
but no success.
Here is my rollup.config.js
import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'
import filesize from 'rollup-plugin-filesize'
import typescript from 'rollup-plugin-typescript2'
import commonjs from 'rollup-plugin-commonjs'
import uglify from 'rollup-plugin-uglify'
let production = (process.env.NODE_ENV == "production")
export default {
input: 'src/index.ts',
output: {
file: 'lib/index.js',
format: 'cjs',
name: 'my-library',
sourcemap: true
},
external: [
'rxjs',
'axios',
'lodash'
],
plugins: [
resolve(),
typescript({
tsconfigOverride: {
compilerOptions: {
declaration: true,
moduleResolution: "node",
allowSyntheticDefaultImports: true
}
},
// verbosity: 3,
clean: true,
rollupCommonJSResolveHack: true,
abortOnError: false,
typescript: require('typescript'),
}),
commonjs(),
babel({
exclude: 'node_modules/**'
}),
production && uglify(),
filesize()
],
watch: {
include: 'src/**'
}
};
I have used this rollup config before and it has worked fine, until now.
Am I missing something?
And I know the title of the question can be more generic. feel free to improve the post.
Looks like there is a known issue with the tree-shaking within Rollup and Lodash (also D3 has a similar problem):
https://github.com/rollup/rollup/issues/691

typescript: resolving typeahead.js with typescript and webpack 2

I am getting the following error from webpack.
ERROR in ./wwwroot/js/admin/infrastructure/typeaheadComponent.ts
Module not found: Error: Can't resolve 'typeahead' in ...
I have the following installed
npm install typeahead.js
npm install #types/typeahead
My typescript is as follows, using node module resolution.
import { module } from "angular";
import "typeahead";
// necessary to import typeahead into JQuery, as otherwise
// typeahead below is not defined.
class TypeAheadController {
foo(e) {
$(e).typeahead(...)
}
}
this generates javascript as follows:
"use strict";
var angular_1 = require("angular");
require("typeahead");
var TypeAheadController = (function () { ...
My webpack.config.js is as follows:
module.exports = {
context: __dirname,
entry: [
"./app.ts",
"./tab.ts",
"./client/clientService.ts",
"./client/clientSearchComponent.ts",
"./infrastructure/messageComponent.ts",
"./infrastructure/typeaheadComponent.ts",
"./url.ts"],
output: {
filename: "./wwwroot/js/admin/admin.js"
},
devtool: "source-map",
module: {
rules: [
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
imported into a gulp task.
How do I specify that typeahead is located in node_modules/typeahead.js/dist/typeahead.bundle.js
The module is called typeadhead.js so you also need to import typeahead.js, not typeahead.
import "typeahead.js";
The import is always the same as the name you use to install it with npm. And it's not even special, it simple looks into node_modules and finds the directory with the given name. Then it looks into package.json and imports the file specified in the main field. See also Node.js - Folders as Modules.
You could use resolve.alias to change the name of the import, but there is not really a good reason for doing that in this case.
I resolved this by making the following changes.
You need to import Bloodhound and Typeahead seperately. To do this edit your webpack.config.js
resolve: {
extensions: ['.js', '.ts'],
alias: {
typeahead: 'corejs-typeahead/dist/typeahead.jquery.min.js',
bloodhound: 'corejs-typeahead/dist/bloodhound.min.js'
}
},
And then in your .ts file:
import "typeahead";
import * as Bloodhound from "bloodhound";
You could solve this using aliasing. Minimal example of what to change in your webpack.config.js:
module.exports = {
/* ... everything you currently have */
resolve: {
alias: {
typeahead: 'typeahead.js'
}
}
}

Categories