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)
});
Related
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.
I'm new to frontend world, I would like to write some test using protractor-image-comparison. I followed installation instructions from https://github.com/wswebcreation/protractor-image-comparison. Also I make configuration according to this page.
When I try to use functions form this lib I get following error: "TypeError: Cannot read property 'checkFullPageScreen' of undefined". I'm getting a warrning in protractor.conf.js in
const protractorImageComparison = require('protractor-image-comparison');
"Could not find a declaration file for module
'protractor-image-comparison'.
'/home/rafa/repos/example/src/example/node_modules/protractor-image-comparison/index.js'
implicitly has an 'any' type. Try npm install
#types/protractor-image-comparison if it exists or add a new
declaration (.d.ts) file containing declare module
'protractor-image-comparison';"
So I did, I made simple *.d.ts file with `declare module protractor-image-comparison' in it, but it didn't solve the problem just the warning disappear. It's propably the config issue, but I can't handle it or maybe I made wrong declaration. This is my config file :
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const reporter = require("cucumber-html-reporter");
const path = require("path");
const jsonReports = path.join(process.cwd(), "/reports/json");
const htmlReports = path.join(process.cwd(), "/reports/html");
const targetJson = jsonReports + "/cucumber_report.json";
const cucumberReporterOptions = {
jsonFile: targetJson,
output: htmlReports + "/cucumber_reporter.html",
reportSuiteAsScenarios: true,
theme: "bootstrap",
};
exports.config = {
allScriptsTimeout: 110000,
restartBrowserBetweenTests: true,
//SELENIUM_PROMISE_MANAGER: false,
specs: [
'./e2e/**/login.feature'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'custom',
frameworkPath: require.resolve('protractor-cucumber-framework'),
cucumberOpts: {
format: "json:" + targetJson,
require: ['./e2e/steps/*.ts', "./e2e/timeout.ts"],
},
useAllAngular2AppRoots: true,
onPrepare: () => {
browser.ignoreSynchronization = true;
const protractorImageComparison = require('protractor-image-comparison');
browser.protractorImageComparison = new protractorImageComparison(
{
baselineFolder: "report/screens/baseline",
screenshotPath: "report/screens/actual"
}
);
},
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
});
},
onComplete: () => {
reporter.generate(cucumberReporterOptions);
}
};
Ok, I solved it. The reason why I was getting this TypeError is that I lunched few test scenarios and onPrepare was lunched only in the begining. I move config of protractor-image-comparison to cucumber befor hook and everything works fine now.
I'm probably doing something wrong, so feel free to question all things. I'm using an npm package xrm-mock for a MS CRM mocking framework. I've setup my config as such
steal.config({
meta: {
"dependencyModule": {
deps: [
/***********************************
* List of Spec Files goes here! *
***********************************/
"spec/po_/commonSpec"
,"spec/xrmMockGeneratorSpec"
]
},
"jasmine": {
"exports": "jasmineRequire"
},
"jasmine-html": {
deps: ["jasmine"]
},
"jasmine-boot": {
deps: ["jasmine", "jasmine-html"]
},
"xrm-mock-generator": {
deps: ["xrm-mock"]
}
},
bundlesPath: "../WebResources",
loadBundles: true,
paths: {
"jasmine": "../node_modules/jasmine-core/lib/jasmine-core/jasmine.js",
"jasmine-html": "../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js",
"jasmine-boot": "../node_modules/jasmine-core/lib/jasmine-core/boot.js",
"sourcemapped-stacktrace": "../node_modules/sourcemapped-stacktrace/dist/sourcemapped-stacktrace.js",
"xrm-mock": "../node_modules/xrm-mock/index.js",
"xrm-mock-generator": "../node_modules/xrm-mock-generator/dist/xrm-mock-generator.js"
},
map: {},
main: "./testRunner"
});
but xrm-mock/index.js looks like this:
"use strict";
exports.__esModule = true;
var formselector_mock_1 = require("./dist/page/formselector/formselector.mock");
exports.FormSelectorMock = formselector_mock_1.FormSelectorMock;
var formitem_mock_1 = require("./dist/page/formitem/formitem.mock");
exports.FormItemMock = formitem_mock_1.FormItemMock;
... 80 more lines...
and I get 404s for each require: "http://localhost:62576/test/dist/page/formselector/formselector.mock.js" which should be "http://localhost:62576/node_modules/xrm-mock/dist/page/formselector/formselector.mock.js"
I'm guessing I could add each and every module file as a module with a path, but that's 40 some modules I'd have to define. Is there an easier way?
Most devs use the npm plugin nowadays, do you know about it? Removes the need for this manual (and difficult) configuration
To answer your question though, I think what you want to do is remove the xrm-mock path and instead have something like
"xrm-mock/*": "../node_modules/xrm-mock/*.js"
and then a map for the main module:
"map": {
"xrm-mock": "xrm-mock/index"
}
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>
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