dojo: multipleDefine error with qwest.js - javascript

I'm trying to load qwest.js using the dojo (ArcGIS) AMD loader but am getting a multipleDefine error.
require([
// `../vendor/react/react.js`, // this works fine
`../vendor/qwest/qwest.min.js`, // this causes error
], (
// React,
qwest,
) => { ... })
At first I thought it was because I added it as a package in the dojo config object, but doing this throws the exact same error.
Config:
require({
async: true
, parseOnLoad: true
, packages: [{
name: `app`
, location: `${location.pathname}js`
, main: `main`
}]
}, [`app`])

I don't really know why you get that error but you can workaround it by letting qwest thinking commonjs should be used instead of amd:
//for testing purpose
require({
packages: [{ name: 'pyrsmk', location: 'https://rawgit.com/pyrsmk'}]
});
//the trick is to let qwest think you use commonjs instead of amd
window.module = {};
require(['pyrsmk/qwest/master/build/qwest.min'], function(qwest) {
qwest = module.exports;
delete window.module;
console.log(qwest);
});
<script src="https://rawgit.com/dojo/dojo/1.10/dojo.js"></script>

Related

How can I create a bundled package with dependencies bundled individually

I have a requirement to create a JavaScript framework for a third party plugin where the plugin can use any dependencies and any versions of those dependencies that it wants to without conflicting with other third party plugins that are used on the same page.
I've also got to think about sharing the dependencies between plugins so that we don't end up with x number of plugins all using the same dependency and version and bloating the page with x number of the same dependency.
My initial idea was to use Webpack to bundle the third party plugins, but I'd need to have separate bundles for each individual dependency to reduce page bloat. So I added this config to the config for webpack:
module.exports = {
entry: {
pluginX: './src/index.js',
},
devtool: 'source-map',
target: 'web',
output: {
path: path.resolve(__dirname, './dist'),
library: 'pluginX',
},
optimization: {
moduleIds: 'named',
chunkIds: 'named',
splitChunks: {
chunks: 'all',
name(module, chunk, cacheGroupKey) {
const context = module.context;
const splitContext = context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/);
const packageJson = getPackageJson(context);
const version = packageJson ? packageJson.version : '0.0.0';
const name = splitContext[1].replace('#', '');
return `${cacheGroupKey}-${name}-${version}`;
},
}
},
plugins: [
new CleanPlugin('./dist', {
verbose: false,
}),
]
}
For reference the getPackageJson function looks like this:
function getPackageJson(context) {
do {
const packageJson = `${context}\\package.json`;
if (fs.existsSync(packageJson)) { return require(packageJson); }
const splits = context.split(/\\/);
splits.pop();
context = splits.length > 0 ? splits.join('\\') : undefined;
} while (context);
return undefined;
}
I've set up three test plugins, 1 using d3 v5.9.1, and the other 2 both using d3 v3.5.17. Using this configuration, I can correctly bundle up the plugins and their dependencies separately, I've created a test server to serve the bundled entry bundles and the dependency bundles. Plugin 1 using d3 v5.9.1 seems to work fine consistently, but plugin 2 and plugin 3 never work together; one of them will always be undefined when I try to access the plugin name (specified in the output.library property).
For reference, here is the index.html page:
<!DOCTYPE html>
<html>
<head>
<script src="./vendors-d3-3.5.17.js" type="text/javascript"></script>
<script src="./vendors-d3-5.9.1.js" type="text/javascript"></script>
<script src="./plugin1.js" type="text/javascript"></script>
<script src="./plugin2.js" type="text/javascript"></script>
<script src="./plugin3.js" type="text/javascript"></script>
</head>
<body></body>
</html>
The index.js gets added automatically by the HtmlWebpackPlugin I'm using (I'm using webpack-dev-server to test this).
The index.js page that is the test page for this is this:
setTimeout(function () {
plugin1.run();
plugin2.run();
plugin3.run();
}, 1000);
plugin 1 index.js file looks like this:
import * as d3 from 'd3';
export function run() {
console.log('plugin1', !!d3.scaleLinear, !!(d3.scale || {}).linear);
}
plugin 2 and 3 has this as their index.js file:
import * as d3 from 'd3';
export function run() {
console.log('pluginX', !!d3.scale.linear, !!d3.scaleLinear);
}
The console output should read:
plugin1 true false
plugin2 true false
plugin3 true false
But I actually get either this:
plugin1 true false
plugin2 true false
Uncaught TypeError: Cannot read property 'run' of undefined
or this:
plugin1 true false
Uncaught TypeError: Cannot read property 'run' of undefined
Because either plugin2 or plugin3 (always only one of them) is always undefined.
I suspect it has something to do with the bootstrapping code that webpack inserts, but I can't be 100% sure about that.

Angular2 ReferenceError: packages is not defined

I have made small app using Angular2, I am getting following error in Chrome browser:
zone.js:323 Error: ReferenceError: packages is not defined
at materialComponents.forEach.name (http://localhost:4200/system-config.js:17:5)
at Array.forEach (native)
at eval (http://localhost:4200/system-config.js:16:20)
Evaluating http://localhost:4200/system-config.js
Error loading http://localhost:4200/system-config.js
Basically, this error happening on the following code.
system-config.ts
/** Map relative paths to URLs. */
const map: any = {
'#angular2-material': 'vendor/#angular2-material', // created in vendor directory
'ng2-material': 'vendor/ng2-material',
'moment': 'vendor/moment/moment.js'
};
/** User packages configuration. */
const packages: any = {};
// Material Components
const materialComponents = [
'core',
'checkbox',
'input',
'progress-bar',
'progress-circle',
'radio',
'sidenav',
'slide-toggle',
'toolbar'
];
materialComponents.forEach(name => { //Here it says package not defined
packages[`#angular2-material/${name}`] = {
format: 'cjs',
defaultExtension: 'js',
main: name + '.js'
};
});
Thanks

Unexpected "Uncaught TypeError: XXX is not a constructor" errors with Babel and ES6

I am giving a try to Webpack, and am giving a try to the instructions in this tutorial, give or take a few custom things.
This is simple code, really, but I'm quite puzzled about this error, and feel this is something silly that I missed.
I defined two ES6 classes, each corresponding to a Handlebars template, and my app's entrypoint is supposed to replace the placeholder HTML in the index file by their contents:
Entrypoint:
import './bloj.less'
// If we have a link, render the Button component on it
if (document.querySelectorAll('a').length) {
require.ensure([], () => {
const Button = require('./Components/Button.js');
const button = new Button('9gag.com');
button.render('a');
}, 'button');
}
// If we have a title, render the Header component on it
if (document.querySelectorAll('h1').length) {
require.ensure([], () => {
const Header = require('./Components/Header.js');
new Header().render('h1');
}, 'header');
}
Index:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>My title</h1>
<a>Click me</a>
<script src="build/bloj.js"></script>
</body>
</html>
Button:
import $ from 'jquery';
import './Button.less';
export default class Button {
constructor(link) {
this.link = link;
}
onClick(event) {
event.preventDefault();
alert(this.link);
}
render(node) {
const text = $(node).text();
var compiled = require('./Button.hbs');
// Render our button
$(node).html(
compiled({"text": text, "link": this.link})
);
// Attach our listeners
$('.button').click(this.onClick.bind(this));
}
}
Header:
import $ from 'jquery';
import './Header.less';
export default class Header {
render(node) {
const text = $(node).text();
var compiled = require('./Header.hbs');
// Render the header
$(node).html(
compiled({"text": text})
);
}
}
Sadly, it does not work, and I get both these errors when displaying the page:
Uncaught TypeError: Header is not a constructor
Uncaught TypeError: Button is not a constructor
What could I be missing?
Here is my webpack configuration:
var path = require('path');
var webpack = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
var ExtractPlugin = require('extract-text-webpack-plugin');
var production = process.env.NODE_ENV === 'production';
var appName = 'bloj';
var entryPoint = './src/bloj.js';
var outputDir = './build/';
var publicDir = './build/';
// ************************************************************************** //
var plugins = [
//new ExtractPlugin(appName + '.css', {allChunks: true}),
new CleanPlugin(outputDir),
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
children: true,
minChunks: 2
})
];
if (production) {
plugins = plugins.concat([
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.MinChunkSizePlugin({
minChunkSize: 51200 // 50ko
}),
new webpack.optimize.UglifyJsPlugin({
mangle: true,
compress: {
warnings: false // Suppress uglification warnings
}
}),
new webpack.DefinePlugin({
__SERVER__: false,
__DEVELOPMENT__: false,
__DEVTOOLS__: false,
'process.env': {
BABEL_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]);
}
module.exports = {
entry: entryPoint,
output: {
path: outputDir,
filename: appName + '.js',
chunkFilename: '[name].js',
publicPath: publicDir
},
debug: !production,
devtool: production ? false : 'eval',
module: {
loaders: [
{
test: /\.js/,
loader: "babel",
include: path.resolve(__dirname, 'src'),
query: {
presets: ['es2015']
}
},
{
test: /\.less/,
//loader: ExtractPlugin.extract('style', 'css!less')
loader: "style!css!less"
},
{
test: /\.html/,
loader: 'html'
},
{
test: /\.hbs/,
loader: "handlebars-template-loader"
}
]
},
plugins: plugins,
node: {
fs: "empty" // Avoids Handlebars error messages
}
};
What could I be missing?
Babel assigns default exports to the default property. So if you use require to import ES6 modules, you need to access the default property:
const Button = require('./Components/Button.js').default;
I realize that you already have an answer. However I had a similar issue to which I found an answer. Starting my own question and answering it seems weird.
So I'm just going to leave this here.
I had the same error as you got. However, I managed to solve it by changing my
export default {Class}
to
export default Class
I don't know why I wrapped the Class in an object but I remember having seen it somewhere so I just started using it.
So instead of the default returning a Class it returned an object like this {Class: Class}.
This is completely valid yet it will break webpack+babel.
EDIT: I've since come to know why this probably breaks babel+webpack. The export default is meant to only have 1 export. A javascript-object can contain many properties. Which means it can have more than 1 export. (See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).
For multiple exports use: export {definition1, definition2}.
Use-case: I've used this in a situation where I've created a library which exported different types of an editor (while the underlying code was the same, the appearance of the editor changes depending on which export you use).
You can just put export var __useDefault = true; just after exporting your Class.
export default class Header {
...
}
export var __useDefault = true;
I was able to fix this by adding babel-plugin-add-module-exports to the .babelrc file
npm install babel-plugin-add-module-exports --save-dev
{
"presets": ["#babel/env"],
"plugins": ["add-module-exports"]
}
this adds
module.exports = exports.default;
to the last line when compiling the class with babel.
Although this is not the cause of your particular issue, I ran into a very similar problem when trying to rip babel out of an existing node app that was using ES6's import and export syntax, so this post is to help out anyone else struggling with this in the future.
Babel will resolve any circular dependencies between one module and another, so you can use ES6's import and export with reckless abandon. However, if you need to get rid of babel and use native node, you will need to replace any import and exports with require. This can reintroduce a latent circular reference issues that babel was taking care of in the background. If you find yourself in this situation, look for an area in your code that looks like this:
File A:
const B = require('B');
class A {
constructor() {
this.b = new B();
}
}
module.exports = A;
File B:
const A = require('A'); // this line causes the error
class B {
constructor() {
this.a = new A();
}
}
module.exports = B;
There are several different ways to resolve this issue depending on how you structured your code. The easiest way is probably to pass B a reference to A instead of creating a new instance of class A. You could also dynamically resolve the reference when loading A. There are a myriad of other alternatives, but this is a good place to get started.
It's not the problem in this particular question, but for some reasons, babel does not hoist classes in the same file.
So if you declare your class Token at the top of the file, and write later new Token(), it will run.
If you declare your class after the constructor call, you will have the xxx is not a constructor error
I had the same error message and discovered that the cause was circular import statements. That is: I had two files that imported each other, wherein one file contained an export default class that contained a method that was dependent upon an export function from the other file.
My solution was to move one of the dependencies (functions) out of the class and into a utils.js file, which was a more appropriate place for it anyway!
This is the way I am using / importing my classes:
Utils.class.js
export default class Utils {
somefunction(val) {
return val
}
}
Using Utils into my controllers:
import {default as U} from '../helpers/Utils.class';
const Utils = new U();
console.log(Utils.somefunction(123));

Error building two different files from same tree with broccoli-requirejs

I'm trying to compile two different JS files using broccoli-requirejs. A logical way to go about this seemed to be to run the requireJs filter on my scripts tree twice, with two different configurations. Doing so produces some really strange errors that, to me, resembles a race condition or something.
See below for Broccoli config and error output.
var compileCoffee = require("broccoli-coffee"),
compileStatic = require("broccoli-static-compiler"),
mergeTrees = require("broccoli-merge-trees"),
requireJs = require("broccoli-requirejs"),
_ = require("lodash");
var scripts = compileStatic("app/coffee", {
srcDir: "/",
destDir: "scripts"
});
scripts = compileCoffee(scripts, {bare: true});
var rjsOptions = {
baseUrl: "scripts",
inlineText: true,
optimize: "uglify",
stubModules: ["text"],
paths: {
knockout: "empty:"
}
};
var fooScript = requireJs(scripts, {
requirejs: _.extend(rjsOptions, {
include: ["foo"],
insertRequire: ["main"],
mainConfigFile: "scripts/main.js",
name: "main",
out: "scripts/main.js"
})
});
var barScript = requireJs(scripts, {
requirejs: _.extend(rjsOptions, {
insertRequire: ["bar"],
mainConfigFile: "scripts/main.js",
name: "bar",
out: "scripts/bar.js"
})
});
module.exports = mergeTrees([
fooScript,
barScript
]);
I get the following error when building this:
$ broccoli build build
Error: Merge error: file scripts/bar.js exists in /home/fredrik/app/tmp/require_js_filter-tmp_dest_dir-yMHQNi3F.tmp and /home/fredrik/app/tmp/require_js_filter-tmp_dest_dir-C8Wv970J.tmp
Pass option { overwrite: true } to mergeTrees in order to have the latter file win.
at mergeRelativePath (/home/fredrik/app/node_modules/broccoli-merge-trees/index.js:98:21)
at mergeRelativePath (/home/fredrik/app/node_modules/broccoli-merge-trees/index.js:122:17)
at /home/fredrik/app/node_modules/broccoli-merge-trees/index.js:23:5
at $$$internal$$tryCatch (/home/fredrik/app/node_modules/broccoli-merge-trees/node_modules/promise-map-series/node_modules/rsvp/dist/rsvp.js:490:16)
at $$$internal$$invokeCallback (/home/fredrik/app/node_modules/broccoli-merge-trees/node_modules/promise-map-series/node_modules/rsvp/dist/rsvp.js:502:17)
at $$$internal$$publish (/home/fredrik/app/node_modules/broccoli-merge-trees/node_modules/promise-map-series/node_modules/rsvp/dist/rsvp.js:473:11)
at Object.$$rsvp$asap$$flush [as _onImmediate] (/home/fredrik/app/node_modules/broccoli-merge-trees/node_modules/promise-map-series/node_modules/rsvp/dist/rsvp.js:1581:9)
at processImmediate [as _immediateCallback] (timers.js:345:15)
Build failed
And if I do pass {overwrite: true} to the mergeTrees call, I get the output of the first requireJs call (ie. the scripts/main.js), but with the filename bar.js.
It seems my problem was totally unrelated to both Broccoli and broccoli-requirejs. It's my call to _.extend that overwrites rjsConfig object. And since the actual r.js optimization with its of the config object doesn't happen before the trees are merged, the result of the second _.extend call is passed in twice.
Simply changing the order of the arguments passed to _.extend made it work as expected:
var fooScript = requireJs(scripts, {
requirejs: _.extend({
include: ["foo"],
insertRequire: ["main"],
mainConfigFile: "scripts/main.js",
name: "main",
out: "scripts/main.js"
}, rjsOptions)
});

Configuring modules with RequireJS when config depends on RequireJS

Apologies if I have missed this in the docs. Basically I want to use the RequireJS module configuration feature. I would like to centrally manage the config values given to modules in a package.
This is an example from the docs:
requirejs.config({
config: {
'bar': {
size: 'large'
},
'baz': {
color: 'blue'
}
}
});
//bar.js, which uses simplified CJS wrapping:
define(function (require, exports, module) {
//Will be the value 'large'
var size = module.config().size;
});
//baz.js which uses a dependency array,
define(['module'], function (module) {
//Will be the value 'blue'
var color = module.config().color;
});
My problem is that my configuration info will be a little more complex, and will itself have dependencies. I would like to do:
requirejs.config({
config: {
'bar': {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
},
}
});
Where variables in my config need to use requireJS to evaluate.
To me it would make sense for there to be a logical separation between the RequireJS configuration - the config necessary to load modules - and the user's module configuration. But I am currently struggling to find this :(
For this sort of solution, I would have the module depend on a "config" module that you can swap for a different one using paths config. So if "bar" needed some config, "bar.js" would look like:
define(['barConfig'], function (config) {
});
Then barConfig.js could have your other dependencies:
define(['crypto'], function (crypto) {
return {
key: crypto.randomBytes(64)
}
});
Then, if you needed different configs for say, production vs. dev, use paths config to map barConfig to other values:
requirejs.config({
paths: {
barConfig: 'barConfig-prod'
}
});
I think the proper way to do this is to make a config module...
// config.js
define(['module', 'path', 'crypto'], function(module, path, crypto) {
return {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
};
});
Then use it in other modules...
// bar.js
define(['config'], function (config) {
var key = config.key;
});
You can then make it as complicated as you like!
EDIT: You could pollute the global namespace for this special class...
define(['module', 'path', 'crypto'], function(module, path, crypto) {
window.config = {
path: path.dirname(module.uri)
key: crypto.randomBytes(64)
};
});
Add it to the top level require call:
require(['config', 'main']);
Then you can use it without always adding it to your define:
// bar.js
define([], function() {
var key = config.key;
});
Having thought about this a little more I have come up with a workaround. It is not particularly pretty but it does seem to work.
I simply do requireJS(...) twice, first to create the config, and second to load the application modules with the config..
requireJSConfig =
baseUrl: __dirname
nodeRequire: require
# Create the require function with basic config
requireJS = require('requirejs').config(requireJSConfig)
requireJS ['module', 'node.extend', 'crypto', 'path'], (module, extend, crypto, path) ->
# Application configuration
appConfig =
'bar':
path: path.dirname(module.uri)
key: crypto.randomBytes(64) # for doing cookie encryption
# get a new requireJS function with CONFIG data
requireJS = require('requirejs').config(extend(requireJSConfig, config: appConfig))
requireJS ['bar'], (app) ->
###
Load and start the server
###
appServer = new app()
# And start the app on that interface (and port).
appServer.start()
And in bar.coffee
# bar.coffee
define ['module'], (module) ->
# config is now available in this module
console.log(module.config().key)
Riffing on what #jrburke is saying, I found the following pattern to be quite useful: define a config module and it's dependencies in the main.js just before the invocation of require.config().
main.js
define('config', ['crypto'], function (crypto) {
return {
'bar': {
key: crypto.randomBytes(64)
},
};
});
requirejs.config({
deps: ['app'],
});
app.js
require(['config'], function (config){
// outputs value of: crypto.bar.key
console.log(config.bar.key);
});
Plnkr Demo: http://plnkr.co/edit/I35bEgaazEAMD0u4cNuj

Categories

Resources