Gatsby with CDN reference - reactjs

I'm trying to get bootstrap.css and Google fonts CDNs working in my Gatsby project.
There is no HTML file; just JavaScript files.
For bootstrap, I can npm install bootstrap and then import the min.css from that.
Trying to figure out how to get Amatic SC font from Google fonts; I have npm installed google-fonts-webpack-plugin.
I am using gatsby-node.js by adding:
const GoogleFontsPlugin = require("google-fonts-webpack-plugin")
exports.modifyWebpackConfig = ({ config, stage }) => {
config.plugin("google-fonts-webpack-plugin",new GoogleFontsPlugin(
{
fonts: [
{ family: "Amatic SC" }
]
}
),null)
};
However, I get the error below;
Invalid 'constructor' parameter. You must provide either a function or null
What am I doing wrong and how can I fix it?
Is there a way of referencing a CDN directly so rather than npm installing bootstrap, I could just reference its latest version?

You can include the font using typeface-amatic-sm from NPM, and in your JS do:
import 'typeface-amatic-sc'
Otherwise, can include scripts it in your </head> using helmet like:
<Helmet>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
</Helmet>

Related

How to import ReactJS Material UI using a CDN through Webpack's externals?

The Problem:
I'm trying to create a website (web app) with React and Material UI, it's working just fine using npm. But when I try to make them as externals and import them through a CDN instead, I get an error with Material UI (React works fine).
My Code:
I linked CDNs in index.html like this:
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/#material-ui/core/umd/material-ui.production.min.js"></script>
<script src="app.min.js"></script>
And in app.min.js, I imported them like this:
import { Component } from 'react';
import ReactDOM from 'react-dom';
import { Button } from '#material-ui/core';
My Attempt:
In the webpack.config.js, I tried the following (again, only Material UI causes an error):
Using a string:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'#material-ui/core': 'Button'
}
gives:
Uncaught ReferenceError: Button is not defined
Using an object:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'#material-ui/core': {
Button: '#material-ui/core'
}
}
gives:
TypeError: Cannot read property 'Button' of undefined
Doing it manually, so Material UI isn't in externals:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
Then removing minified Material UI code from app.min.js, this leaves the code incomplete and it doesn't run.
Searched through GitHub issue and SO questions without any luck, some links:
How should material-ui be externalized when bundling with webpack
Externals defined in webpack.config still getting error module not found
React CDN: Webpack externals library not resolved in code
Any idea how can I solve this?
Solution::
in webpack.config.js:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'material-ui': 'window["material-ui"]'
},
then in app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Button } from 'material-ui';
Explanation:
If you check the cdn version of material ui js, you will find it exports its content in to material-ui namespace.
if you config webpack like:
'material-ui': 'material-ui'
webpack will compile it to:
which leads to the code looking for material and ui in global environment which does not exist. So we have to specify window["material-ui"] explicitly
Might be a little late to the party but I will add an answer which worked for me.
step 1:
add the script tag from unpkg. The difference between this and cdnjs is that unkpg have an option for umd. May or may not be an issue in your particular situation. It is for me.
url:
https://unpkg.com/#material-ui/core#4.11.0/umd/material-ui.production.min.js
script tag:
<script src="https://unpkg.com/#material-ui/core#4.11.0/umd/material-ui.production.min.js"></script>
step 1b:
add the font and font icon external resources as described in the material-ui docs:
material-ui getting started - installation guide
roboto font:
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
font icons:
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
step 2:
destructure the elements you want to use from window.MaterialUI or use the square bracket notation (but unnecessary here since this package ditched the '-' char.
const { Button } = window['MaterialUI'];
step 3:
use the element as you 'normally' would
<Button variant="contained" color="primary">
Primary
</Button>
I have just solved this issue after spending way too much time on this issue while trying to build an app using a micro-frontend architecture.
TL;DR;
The solution is to put the following in the webpack.config.js:
module.exports = {
// rest of the config clipped for brevity
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'#material-ui/core': 'MaterialUI'
}
};
Further details:
I was building a macro-frontend composed of multiple micro-frontends. Each of the micro-frontends was developed in React, exported as a web-component, and would be independently deployed such that each micro-frontend would be available via a URL like:
http://foo.example.com/main.js
http://bar.example.com/main.js
http://baz.example.com/main.js
These would be imported into the macro-app using a <script> tag.
The macro-app was hosted on a separate domain, e.g., http://example.com.
The issue I was facing was that Material UI (and possibly React as well) was being initialized multiple times in each of the micro-apps.
To avoid that, I had to externalize all these libraries using the webpack config block above.
I had to make 2 concessions.
I did not use create-react-app and react-scripts to scaffold the macro-app because that setup would hide the webpack config. In order to expose the webpack config, I could either eject the CRA project, or use some other modules, such as react-app-rewired, etc. That felt like too much work. The downside of this was that I could not use BrowserRouter and had to accept using HashRouter for client-side routing.
I could not use SvgIcon-based icons from #material-ui/icons, because I could not find a good way of externalizing Material UI Icons. Instead, I put in a link to Material UI Icons stylesheet, and opted to use Icon from #material-ui/core/Icon to render icons. using SvgIcon-based icons was causing Material UI to be initialized in the micro-apps too, which is what I was trying to avoid. One upside of the workaround is that Icon works with Font Awesome as well, so at least all icons would be written consistently in code.
Overall, I am happy with the end results.

how to include webpack in in-browser Babel?

I am newbie to React.js. For learning purpose just I created the login page which you can find here.
In my local project also, I used the CDN for babel and react, like below.
<script src="https://unpkg.com/react#latest/dist/react.js"></script>
<script src="https://unpkg.com/react-dom#latest/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone#6.15.0/babel.min.js"></script>
I want to use one of the form validation plugin into this example. But when I tried to include this (as per the document)
import ValidateableForm from 'react-form-validate';
I am getting the following error.
Uncaught ReferenceError: require is not defined
I went through few posts and they said that I have to use webpack or Rollup or Browsify .I am not sure how to include this in to my current local project setup. Since I am not using npm (in learing I dont want to use npm)
I dont know how to include that plugin into my project
If it is already coming with external site , I cant able to figure
it out what is the issue.
Please help me to resolve the issue.
This is an old question, but at least currently, this is completely possible with conventional script tags. This article is very helpful for understanding development setup alternatives for React
In that article it details bringing in React, React-Dom, and Babel via script tags for development, like this (I'm linking to npm downloaded packages, but that isn't necessary):
<script src="/node_modules/react/umd/react.development.js"></script>
<script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="/node_modules/#babel/standalone/babel.min.js"></script>
In my case, I needed to bring in the react-notification-system plugin:
<script src="/node_modules/react-notification-system/dist/react-notification-system.min.js"></script>
(Note the use of the compiled 'dist' version)
Once that was included I was able use it like this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.notificationSystem = new ReactNotificationSystem();
}
addNotification = event => {
event.preventDefault();
const notification = this.notificationSystem.current;
notification.addNotification({
message: 'Notification message',
level: 'success'
});
};
render() {
return (
<div>
<button onClick={this.addNotification}>Add notification</button>
<ReactNotificationSystem ref={this.notificationSystem} />
</div>
);
}
}
I had to look in the plugin's code to know that the name: ReactNotificationSystem would be available, much of the plugin documentation that you find is not written with this type of dev setup in mind, but it does work.

Angular2 - Angular-CLI installing lodash - Cannot find module

Mac OSX El capitan | angular-cli: 0.1.0 | node: 5.4.0 | os: darwin x64
I try to install a 3rd party npm module according to the angular-cli wiki: https://github.com/angular/angular-cli/wiki/3rd-party-libs but fail. I've been struggling with this for days now and would greatly appreciate any help.
Steps to get the error:
ng new lodashtest3
cd lodashtest3
npm install lodash --save
typings install lodash --ambient --save
angular-cli-build.json:
module.exports = function(defaults) {
return new Angular2App(defaults, {
vendorNpmFiles: [
...
'lodash/**/*.js'
]
});
};
ng build
(lodash gets correctly added in dist/vendor)
system-config.ts:
/** Map relative paths to URLs. */
const map: any = {
'lodash': 'vendor/lodash/lodash.js'
};
/** User packages configuration. */
const packages: any = {
'lodash': {
format: 'cjs'
}
};
(all according to spec in
https://github.com/angular/angular-cli/wiki/3rd-party-libs)
Note - I've tried all config settings I can think of here, all giving the same result.
lodashtest3.component.ts:
import * as _ from 'lodash';
ng build
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
Build failed.
The Broccoli Plugin: [BroccoliTypeScriptCompiler] failed with:
Error: Typescript found the following errors:
/Users/danielmattsson/git/lodashtest3/tmp/broccoli_type_script_compiler-input_base_path-g2lDIaq6.tmp/0/src/app/lodashtest3.component.ts (2, 20): Cannot find module 'lodash'.
at BroccoliTypeScriptCompiler._doIncrementalBuild (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:115:19)
at BroccoliTypeScriptCompiler.build (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:43:10)
at /Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/index.js:152:21
at lib$rsvp$$internal$$tryCatch (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/node_modules/rsvp/dist/rsvp.js:1036:16)
at lib$rsvp$$internal$$invokeCallback (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/node_modules/rsvp/dist/rsvp.js:1048:17)
at lib$rsvp$$internal$$publish (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/node_modules/rsvp/dist/rsvp.js:1019:11)
at lib$rsvp$asap$$flush (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/node_modules/rsvp/dist/rsvp.js:1198:9)
at nextTickCallbackWith0Args (node.js:456:9)
at process._tickCallback (node.js:385:13)
The broccoli plugin was instantiated at:
at BroccoliTypeScriptCompiler.Plugin (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/node_modules/broccoli-plugin/index.js:10:31)
at BroccoliTypeScriptCompiler.CachingWriter [as constructor] (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/broccoli-caching-writer/index.js:21:10)
at BroccoliTypeScriptCompiler (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/broccoli-typescript.js:26:49)
at Angular2App._getTsTree (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/angular2-app.js:280:18)
at Angular2App._buildTree (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/angular2-app.js:101:23)
at new Angular2App (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/lib/broccoli/angular2-app.js:42:23)
at module.exports (/Users/danielmattsson/git/lodashtest3/angular-cli-build.js:6:10)
at Class.module.exports.Task.extend.setupBroccoliBuilder (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/lib/models/builder.js:55:19)
at Class.module.exports.Task.extend.init (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/lib/models/builder.js:89:10)
at new Class (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/node_modules/core-object/core-object.js:18:12)
at Class.module.exports.Task.extend.run (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/lib/tasks/build.js:15:19)
at /Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/lib/commands/build.js:32:24
at lib$rsvp$$internal$$tryCatch (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/node_modules/rsvp/dist/rsvp.js:1036:16)
at lib$rsvp$$internal$$invokeCallback (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/node_modules/rsvp/dist/rsvp.js:1048:17)
at /Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/node_modules/rsvp/dist/rsvp.js:331:11
at lib$rsvp$asap$$flush (/Users/danielmattsson/git/lodashtest3/node_modules/angular-cli/node_modules/angular-cli/node_modules/rsvp/dist/rsvp.js:1198:9)
EDIT: more information according to comments below
dist/index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Lodashtest3</title>
<base href="/">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Service worker support is disabled by default.
Install the worker script and uncomment to enable.
Only enable service workers in production.
<script type="text/javascript">
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/worker.js').catch(function(err) {
console.log('Error installing service worker: ', err);
});
}
</script>
-->
</head>
<body>
<lodashtest3-app>Loading...</lodashtest3-app>
<script src="vendor/es6-shim/es6-shim.js"></script>
<script src="vendor/reflect-metadata/Reflect.js"></script>
<script src="vendor/systemjs/dist/system.src.js"></script>
<script src="vendor/zone.js/dist/zone.js"></script>
<script>
System.import('system-config.js').then(function () {
System.import('main');
}).catch(console.error.bind(console));
</script>
</body>
</html>
With the stable release and current angular-cli (1.0.0-beta.15), it's simply adding the npm package plus type definitions
npm install lodash --save
npm install #types/lodash --save-dev
For early versions such as 1.0.0-beta.15, the next is necessary. It should not be required for current versions:
Add the library to the angular-cli.json to list of global scripts (add "../node_modules/lodash/lodash.js" to the list apps[0].scripts).
See https://github.com/angular/angular-cli#global-library-installation
FWIW, as of today (1.0.0-beta.26), adding the scripts entry is not necessary anymore.
Simply add the proper entries to package.json using:
npm i --save lodash
npm i --save-dev #types/lodash
Then, in your TypeScript code, use:
import * as _ from 'lodash';
The code will run just fine.
In my case, actually adding the scripts entry was causing issues.
[updated answer] After the new version of angular-cli (1.0.0-beta.15):
just add
npm install lodash --save
npm install #types/lodash --save-dev
then add the library to the angular-cli.json to list of global scripts(add "../node_modules/lodash/lodash.js" to the list apps[0].scripts).
and in your component where you want to use , try this way
declare var _:any;
#Component({
})
export class YourComponent {
ngOnInit() {
console.log(_.chunk(['a', 'b', 'c', 'd'], 2));
}
}
before : angular-cli (1.0.0-beta.15):
add this line in src/index.html
<script src="/vendor/lodash/lodash.js" type="text/javascript"></script>
and in your component where you want to use , try this way
declare var _:any;
#Component({
})
export class YourComponent {
ngOnInit() {
console.log(_.chunk(['a', 'b', 'c', 'd'], 2));
}
}
I tried straight away , it worked for me
In order to support 3rd party libraries in Angular CLI and System.js, you have to specify them manually on system.config.ts and angular-cli-build.js.
I've answered it here with example for lodash and also other dependencies.
As of 4/15/2017 with #angular/cli 1.0.0:
You need to be very specific about which versions you install otherwise the TypeScript bindings will give all sorts of failures. What I did is update my devDependencies as follows in package.json:
"#types/lodash": "ts2.0"

Using gulp-babel gives Uncaught ReferenceError: require is not defined

I am using the following gulp.js file
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task('bundle', bundle);
function bundle () {
gulp.src('./src/*.jsx')
.pipe(babel())
.pipe(gulp.dest('./dist'));
}
gulp.task('build', ['bundle']);
Before transpile "main.jsx" content
import React from 'react';
After Transpile, "js" files generated in the dist folder, has require('')
var _react = require('react');
while requesting for the page index.html
<body>
<div id="app" ></div>
<script src="main.js"></script>
</body>
Uncaught ReferenceError: require is not defined is shown in the console.
I know there is something wrong in the build task, but i am unable to figure out.
In the browser, you can not use require API, so you need to somehow bundle your code, you have few options:
Browserify
Webpack
Rollup
These module bundlers allow you to specify an 'entry' point and then bundle all the required modules together in a single file.
Similar questions that answer this problem:
Gulp + babelify + browserify issue

referencing an amd module(arcgis) in webpack app

I'm building a react app with webpack and i need to incorporate arcgis maps into a react component. I have know idea how to bring this into my project. I've tried creating an arcgis directory with an index.js of the built javascript and trying to reference that:
import {Map} from 'arcgis/index'
That doesn't work. I then just tried to include the css/js script tags directly into my index.html but when I try to require them, like in the example, webpack obviously can't find them. Is there some way to tell webpack to ignore require calls in my src file so it gets handled by the browser? I'm trying and failing at doing the following:
import React from 'react'
export default class EsriMap extends React.Component {
componentDidMount() {
const _this = this
require(["esri/map", "dojo/domReady!"], function(Map) {
var map = new Map(_this.refs.map, {
center: [-118, 34.5],
zoom: 8,
basemap: "topo"
});
});
}
render() {
return (
<div ref="map"></div>
)
}
}
You may want to try this https://github.com/tomwayson/esri-webpack-babel .
This method is nice because it doesn't bog down the build. You pull in the ESRI Api from the CDN, and tell webpack that it's an external.
//Add this...
externals: [
// Excludes any esri or dojo modules from the bundle.
// These are included in the ArcGIS API for JavaScript,
// and its Dojo loader will pull them from its own build output
function (context, request, callback) {
if (/^dojo/.test(request) ||
/^dojox/.test(request) ||
/^dijit/.test(request) ||
/^esri/.test(request)
) {
return callback(null, "amd " + request);
}
callback();
}
],
//And this to you output config
output: {
libraryTarget: "amd"
},
When your app loads you bootstrap you webpack modules using Dojo in a script tag.
<!-- 1. Configure and load ESRI libraries -->
<script>
window.dojoConfig = {
async: true
};
</script>
<script src="https://js.arcgis.com/4.1/"></script>
<!-- Load webpack bundles-->
<script>
require(["Angular/dist/polyfills.bundle.js", "Angular/dist/vendor.bundle.js", "Angular/dist/app.bundle.js"], function (polyfills, vendor, main) { });
</script>
I've got it working with an Angular 2 App I'm working on. The only downside is I haven't yet got the unit tests to run right using Karma. I've only been working on that a few hours now.. Hope to have a solution to the testing issue soon.
#getfuzzy's answer will work well as long as you don't need to lazy load the ArcGIS API (say for example only on a /map route).
For that you will want to take the approach I describe in this answer
This blog post explains why you need to use one of these two approaches and explains how they work as well as the pros/cons of each.
I think you can try using bower version of esrijsapi. Doc link

Resources