Marionette Modules missing after minifying the files through grunt - requirejs

I'm using Backbonejs with RequirejS to create an application and used
Grunt as a build tool. Minified files were generated using grunt task "grunt-requirejs".
After that I implemented MarionetteJs for using the marionette modules and after implementing marionette modules in the application, the problem started occurring in Grunt build process. When i'm running the application with minified file, i'm getting error
"uncaught Error: undefined missing modules/main/mainApp"
mainApp.js is my main module which would govern the other modules.
Code snippet
app.js -
define([ 'marionette' ], function(Marionette) {
var MainApplication = new Marionette.Application();
MainApplication.on("start", function(){
if(Backbone.history){
require(['modules/main/mainApp'], function (MainApp) {
Backbone.history.start();
MainApp.start();
});
}
});
return MainApplication;
}
I googled a lot about this question but couldn't find a concrete solution.
Do i have to include any other task for minifying the marionette files?
Also i came up with this blogpost , which confuses whether to used Requirejs with Marionette modules.
Any suggestions/approach for checking the marionette files are minified and are working correctly would be really helpful.
Edit:
Added code snippet.

I got the solution. It was just simply adding one more option to the optimization task.
In my grunt task
requirejs: {
compile: {
options: {
findNestedDependencies: true,
// Edited for brevity
}
}
},
I added findeNestedDependencies : true and it actually picked up the nested dependencies.
I got the reference from David Sulc's book for RequireJS, BackboneJS and Marionette. In this book he was optimizing the application with RequireJS optimizer and then i found that attribute in the build.js file.

Related

Webpack'd oboe module will not run

I've compiled a project using webpack, which among its dependencies has oboe. After including the compiled project as a dependency in a node project, and starting it up with npm run dev, I get this error:
/usr/src/app/node_modules/#myproject/blockchain/lib/#myproject/blockchain.js:37762
return self;
^
ReferenceError: self is not defined
Looking at that bit of code in the compiled project, I find
// Access to the window object throws an exception in HTML5 web workers so
// point it to "self" if it runs in a web worker
try {
return window;
} catch (e) {
return self;
}
Seems like this is some kind of Webpack feature, but I'm not sure how to cope with it. I found this thread, but looking at my current configuration for globalObject:
globalObject: 'typeof self !== \'undefined\' ? self : this'
that does not seem to be the right answer. I looked through the output configuration of Webpack, but not sure what I'm looking for.
Any idea how I can include oboe as a dependency such that it will run in a webpack compiled project?

How to import a node module inside an angular web worker?

I try to import a node module inside an Angular 8 web worker, but get an compile error 'Cannot find module'. Anyone know how to solve this?
I created a new worker inside my electron project with ng generate web-worker app, like described in the above mentioned ng documentation.
All works fine until i add some import like path or fs-extra e.g.:
/// <reference lib="webworker" />
import * as path from 'path';
addEventListener('message', ({ data }) => {
console.log(path.resolve('/'))
const response = `worker response to ${data}`;
postMessage(response);
});
This import works fine in any other ts component but inside the web worker i get a compile error with this message e.g.
Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.
How can i fix this? Maybe i need some additional parameter in the generated tsconfig.worker.json?
To reproduce the error, run:
$ git clone https://github.com/hoefling/stackoverflow-57774039
$ cd stackoverflow-57774039
$ yarn build
Or check out the project's build log on Travis.
Note:
1) I only found this as a similar problem, but the answer handles only custom modules.
2) I tested the same import with a minimal electron seed which uses web workers and it worked, but this example uses plain java script without angular.
1. TypeScript error
As you've noticed the first error is a TypeScript error. Looking at the tsconfig.worker.json I've found that it sets types to an empty array:
{
"compilerOptions": {
"types": [],
// ...
}
// ...
}
Specifying types turns off the automatic inclusion of #types packages. Which is a problem in this case because path has its type definitions in #types/node.
So let's fix that by explicitly adding node to the types array:
{
"compilerOptions": {
"types": [
"node"
],
// ...
}
// ...
}
This fixes the TypeScript error, however trying to build again we're greeted with a very similar error. This time from Webpack directly.
2. Webpack error
ERROR in ./src/app/app.worker.ts (./node_modules/worker-plugin/dist/loader.js!./src/app/app.worker.ts)
Module build failed (from ./node_modules/worker-plugin/dist/loader.js):
ModuleNotFoundError: Module not found: Error: Can't resolve 'path' in './src/app'
To figure this one out we need to dig quite a lot deeper...
Why it works everywhere else
First it's important to understand why importing path works in all the other modules. Webpack has the concept of targets (web, node, etc). Webpack uses this target to decide which default options and plugins to use.
Ordinarily the target of a Angular application using #angular-devkit/build-angular:browser would be web. However in your case, the postinstall:electron script actually patches node_modules to change that:
postinstall.js (parts omitted for brevity)
const f_angular = 'node_modules/#angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
fs.readFile(f_angular, 'utf8', function (err, data) {
var result = data.replace(/target: "electron-renderer",/g, '');
var result = result.replace(/target: "web",/g, '');
var result = result.replace(/return \{/g, 'return {target: "electron-renderer",');
fs.writeFile(f_angular, result, 'utf8');
});
The target electron-renderer is treated by Webpack similarily to node. Especially interesting for us: It adds the NodeTargetPlugin by default.
What does that plugin do, you wonder? It adds all known built in Node.js modules as externals. When building the application, Webpack will not attempt to bundle externals. Instead they are resolved using require at runtime. This is what makes importing path work, even though it's not installed as a module known to Webpack.
Why it doesn't work for the worker
The worker is compiled separately using the WorkerPlugin. In their documentation they state:
By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code - this avoids running things like html-webpack-plugin twice. For cases where it's necessary to apply a plugin to Worker code, use the plugins option.
Looking at the usage of WorkerPlugin deep within #angular-devkit we see the following:
#angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js (simplified)
new WorkerPlugin({
globalObject: false,
plugins: [
getTypescriptWorkerPlugin(wco, workerTsConfigPath)
],
})
As we can see it uses the plugins option, but only for a single plugin which is responsible for the TypeScript compilation. This way the default plugins, configured by Webpack, including NodeTargetPlugin get lost and are not used for the worker.
Solution
To fix this we have to modify the Webpack config. And to do that we'll use #angular-builders/custom-webpack. Go ahead and install that package.
Next, open angular.json and update projects > angular-electron > architect > build:
"build": {
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
}
// existing options
}
}
Repeat the same for serve.
Now, create extra-webpack.config.js in the same directory as angular.json:
const WorkerPlugin = require('worker-plugin');
const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
module.exports = (config, options) => {
let workerPlugin = config.plugins.find(p => p instanceof WorkerPlugin);
if (workerPlugin) {
workerPlugin.options.plugins.push(new NodeTargetPlugin());
}
return config;
};
The file exports a function which will be called by #angular-builders/custom-webpack with the existing Webpack config object. We can then search all plugins for an instance of the WorkerPlugin and patch its options adding the NodeTargetPlugin.

Can I require jsx files in node?

I have a script that does some analysis on my source files and a part of that analysis is to require the file. Some of the files are in JSX format however and node does not understand this by default.
Is it possible to make it so that a file that looks like this:
function MyModule () {
return <div>hello</div>
}
module.exports = MyModule
is possible to require through require('./my-module')?
Use JSX as a template engine in Node
NPM Package : https://www.npmjs.com/package/jsx-node
To be able to simply require .jsx files, you need to tell Node what to do with them. Running the following code makes you able to require('./SomeFile.jsx'):
require('jsx-node').install({
replace: {
preact: 'jsx-node',
}
});
Warning:
This module is still in a very early phase. Any production use should be approached with caution.
For more Detail visit Link.

Yeoman + Grunt Disable Uglify

Background:
I'm working on a chrome extension. I used the yeoman generator. It worked like a charm. After I deployed the extension, I needed to debug a few issues.
Problem:
The code is uglified. I can't set break points. I can hardly read it. It is also optimized. This makes it hard to read as well. I would like to tell Grunt to skip uglifying the code.
Attempted Solutions:
I tried to comment out the uglify task in the Grunt file. If I do this, not only is uglify not executed, but most of the scripts fail to copy into the "dist" directory.
I can deploy the application from the "app" directory. If I do this, my human written code is loaded rather than the "dist" values. While this works, I wish to learn more about the inner workings of Grunt. It seems likely that there is some mechanism by which uglifying may be disabled while preserving copying.
It's the usemin task that supplies targets to the uglify task. When you comment out the uglify task usemin can't complete its flow (by default concat and uglify) and the scripts never get copied.
So you must configure the flow in useminPrepare options. Like this:
[...]
useminPrepare: {
options: {
stripBanners: true,
dest: '<%= config.dist %>',
flow: {
steps: {
js: ['concat'], css: ['concat', 'cssmin']
},
post: {}
}
},
[...]
This way you can remove the uglify task from the build sequence (you must, as it will complaint that have no targets and fail).
Documentation here: https://github.com/yeoman/grunt-usemin#flow

How to use Gulp to create a separate vendor bundle with Browserify from Bower components

I'm using Gulp and Browserify to package my Javascript into 2 separate bundles: application.js and vendor.js.
How do I bundle the vendor package if my vendor libraries are installed with Bower?
In my gulpfile, I'm using the following modules:
var gulp = require("gulp");
var browserify = require("browserify");
var debowerify = require("debowerify");
var source = require("vinyl-source-stream");
Assuming that I have only the Phaser framework installed with bower (for this example), my Gulp task to create the application package looks like this:
gulp.task("scripts-app", function () {
browserify("./app/javascripts/index.js")
.external("phaser")
.pipe(source("application.js"))
.pipe(gulp.dest("./tmp/assets"));
});
Meanwhile, the vendor task looks like this:
gulp.task("scripts-vendor", function () {
browserify()
.transform(debowerify)
.require("phaser")
.pipe(source("vendor.js"))
.pipe(gulp.dest("./tmp/assets"));
});
When I run this Gulp task, I get an error that states Error: Cannot find module 'phaser' from and then all the directories it search through (none of which are the bower_components directory).
Any ideas about how to package these up successfully are greatly appreciated. Thanks!
Answered my own question:
When using require in the Gulp task, you need to supply a path to a file, not just a name.
gulp.task("scripts-vendor", function () {
browserify()
.transform(debowerify)
.require("./bower_components/phaser/phaser.js")
.pipe(source("vendor.js"))
.pipe(gulp.dest("./tmp/assets"));
});
Notice that require("phaser") became require("./bower_components/phaser/phaser.js").
Doing this works, although the bundle takes forever to build (around 20 seconds). You're probably better of just loading giant libraries/frameworks directly into your app through a <script> tag and then using Browserify Shim.
This let's you require() (in the NodeJS/Browserify sense) global variables (documentation).
Seems like you figured out how to require the bower file. Hopefully you'll only have to bundle it once initially, and not every build. Including the library via a script tag isn't a bad idea. Another technique I'm using is to use scriptjs (a polyfill would work too), to async load whatever vender libraries I need, but make sure to include any/all require's after the script loads. For example, your index.js could be like:
$script.('/assets/vendor', function() {
var phaser = require('phaser');
//rest of code
});
It's especially nice for loading cdn files or having the ability to defer loading certain libraries that aren't necessarily used in the core app by every user, or loading libraries after client-side routing.

Resources