Why does browserify in 2 separate bundles not load the dependencies? - browserify

I am trying to separate my browserify vendor libs from my own code. I ran the following (simplified):
browserify -r angular -o app/dist/vendor.js
browserify -x angular app/js/main.js -o app/dist/app.js
When I load my app, I get Error: Cannot find module 'angular'.
If I run it all bundled as browserify app/js/main.js -o app/dist/app.js everything works fine. My main.js is rather simple, just looks like:
var angular = require('angular'), app = require('./app');
angular.bootstrap(document,[app.name]);
It is the first line, require('angular') that it stumbles upon.
Yes, I did set up a simple shim for angular along with a browser entry in package.json mapping it to my angular shim path, so that it works correctly (or it would not have worked in the all-in-one case).
I also tried manually editing the vendor.js and app.js with some logs to see when how they run. It looks like the wrapper function (yes, IIFE) for the app.js runs first, followed by the wrapper for vendor.js. And, I verified multiple times that the script tag for vendor.js is first, followed by the tag for app.js.
Could i have something to do with the fact that vendor.js is so much bigger (1.5 orders of magnitude) than app.js, and so app.js finishes loading first? I doubt it, or every ordered script tag for jquery or angular would break, but I don't know.

Never mind, I figured it out. Turns out the 2 tags were auto-generated. This causes them to be async and the browser is free to do what it wants with them in terms of completion order.

Related

Creating a single Vue component inside a larger project

I have a PHP project that uses Kirby CMS. I also use Gulp for building my assets. Now, I need to add a calculator on the homepage that is complex enough to justify the usage of Vue. How would I incorporate Vue in my project without introducing a ton of new tooling? All I want is a simple Single File Component basically. I have:
<div id="calculator"></div>
and I want the component to be rendered there. Nothing more.
After some consideration, I came up with the following options but found issues with each of them:
Use the Vue CLI for instant prototyping. That's the closest solution for my use case, but I can't easily develop the component. If I use vue serve, I get to see the component isolated in a new page. The issue lies in the fact the component isn't a part of my project's page. It's not affected by its stylesheets, layout, and other scripts. I can't know if it'll work properly once I build it and view it in my project. Running vue build on each change would be pretty painful and time consuming. Sadly, vue watch isn't a thing, which leads me to:
Creating a project and using Vue CLI Service. If I create a project, I'd be able to run vue-cli-service build --watch and have my component automatically refresh on each change of its source file. While developing the component, I simply make a change, wait for it to compile, and refresh my project in the browser to see the modified component in action. While that would work, it introduces a bunch of node_modules inside my project, along with a package.json. I feel that's too much for just a single component. It would pollute the project more than I'd like:
assets/
js/
build/
calculator/
dist/
node_modules/ # modules here
public/ # I don't need that
package.json # package here
package-lock.json
App.vue
scripts/
main.js
content/
site/
node_modules/ # modules here as well
panel/
package.json # package here as well
package-lock.json
index.php
I would basically have a project within a project.
Use vueify to compile the component with Browserify and Gulp (which I already use). While this appears OK, vueify is deprecated and not supported. Besides, I'd have to add a bunch of stuff to my gulpfile.js in order to use Babel + ESLint for the component.
How do I set up Vue in such a way that I'm able to develop a very simple component as a part of a larger project with as little friction as possible?
If anyone has dealt with a similar problem, how did they solve it?
I ended up using the second approach I mentioned in my question with one small twist - I initialized the Vue project in my main project. I merged them.
I opened the parent folder of my project in a terminal.
I ran vue create my-project where my-project was the actual folder name of my project. The CLI asked if it should overwrite the project or merge it. I chose merge.
After the project was created, my old package.json was overwritten and only had the Vue dependencies listed in it.
I reverted my old package.json and installed these packages: #vue/cli-plugin-babel, #vue/cli-service, vue-template-compiler, and vue.
I added the following npm script in my package.json:
"scripts": {
"calculator": "vue-cli-service build assets/js/calculator/main.js --watch --dest assets/js/calculator/build"
}
Result
My project's folder structure remained the same, except for a few new packages in node_modules. I put my component files in assets/js/calculator/. There, I have main.js which is the main component script, and build which is a folder containing the processed component.
I have:
<div id="calculator"></div>
in my page, and:
<script src="/assets/js/calculator/build/app.js"></script>
in the footer. When I open the page, the component is rendered correctly.
To modify the component, I simply run npm run calculator in a terminal, which spins up the CLI service. It monitors the main.js file and builds the component on each change. Once the build is complete (which happens in under a second), I refresh the page and the updated component is there.
Conclusion
I believe that's the smoothest way to handle this use case. It didn't bloat the project, all dependencies were listed, and the development experience is great. The part where my package.json got overwritten was a bit concerning, but other than that - it worked perfectly. If there's a better way to do this, please leave an answer!
This is probably not the answer you're looking for but if I were you I'd look into inline templates and x-templates as they seem well suited to your use case.
Also have a look at this blog post. It offers a nice write up about the different template authoring methods in Vue and their pros/cons.

SailsJS Include node_module in view

I'm using sails(http://sailsjs.com) to develop a little platform. Everything goes smoothly following the documentation. But being new to this javascript frameworks world and npm etc etc, i've been having a trouble including other node_modules and use them in the .ejs views...
I understand not all modules are to be included in the views but how can I manage to include some?
Trying to use https://www.npmjs.com/package/vue-slider-component
Thank you in advance and sorry if this error is just plain out stupid.
Your confusion is understandable. The issue is that, until relatively recently, things installed in node_modules were solely for use in the back end code; that is, your Sails.js controller actions, models, etc. After all, the node_modules folder has the word "Node" right in it, and it was created for use with NPM (the Node Package Manager) to help organize Node (i.e. server-side JavaScript) files!
While many front-end plugins were (and still are) published on Bower, newer frameworks like Angular 2 and Vue often publish their plugins to NPM because it reduces the number of moving parts for your app. The problem is, if you try to require('vue-slider-component') in your server-rendered .ejs view, the server (i.e. Sails.js) will try and load and run that code before it renders the view, where what you really want is for that plugin to run in the browser.
The long-term solution is to use something like Browserify or Webpack to compile all of your front-end JavaScript files into a "bundle". So for example if you have a file like assets/js/my-vue-app.js that includes the line:
import vueSlider from 'vue-slider-component/src/vue2-slider.vue'
then Browserify will see that line, load up that vue2-slider.vue file, add it to the top of the my-vue-app.js file, perform some other magic, combine it with your other front-end .js files and output a file like browserified.js which you would then include via <script src="/path/to/browserified.js"> in your HTML.
Since new Sails apps use Grunt to organize and inject those <script> tags into your views for you, it can be kinda confusing as to how you would get something like Browserify or Webpack to work with Sails. For Sails 1.0, there's a seed project for using Webpack instead of Grunt. For Sails v0.12.x, you'll have to Google around to find some examples of using Broswerify or Webpack with Sails.
A short-term solution, and probably not as maintainable in the long run, is to save the contents of the minified vue-js-slider component into your assets folder (e.g. as assets/js/vue-slider-component.js), add it to your HTML with <script src="/js/vue-slider-component.js"> and access it in your code as window['vue-slider-component'].

Extracting vendor libraries from factor-bundle

I am creating a semi-automatic build pipeline for my application using npm, Gulp, and browserify. This is what I'm trying to accomplish:
Have page specific files which I can include via script tags (i.e. page1.js, page2.js, ...)
Factor out my custom code that is shared inbetween the page specific scripts as common.js.
Extract vendor libraries (i.e. bootstrap, jquery) installed via npm into vendor.js.
I'm having trouble combining browserify and browserify-shim for my application. In my webpage, I include vendor.js, common.js, and page1.js. When I try to load the page and my script is trying to require('bootstrap'), an exception is thrown here:
// Part of bootstrap.js
if (typeof jQuery === 'undefined') {
throw new Error('Bootstrap\'s JavaScript requires jQuery')
}
I expect to have to tell browserify-shim that bootstrap depends on jquery, that it expects jquery's export to be bound to the jQuery variable. I attempted to do this in my package.json.
I'm having trouble pinpointing exactly what is going wrong. I've tried various permutations of values to go into bootstrap's shim (jquery:jQuery, jquery:$, jquery) to no avail. I've also tried including and omitting various values in the browser path. I also tried building the vendor bundle via the command line using: ./node_modules/.bin/browserify -r jquery -r bootstrap > vendor.js, but the same error comes up on the browser. A lot of questions have been asked about browserify-shim and factor-bundle, but none of them address anyone trying to combine the two.
I have created a repository that demonstrates the issue here. Steps to install and reproduce are in the README. https://github.com/linkleonard/browserify-shim-factor-bundle

Use select2 version 4.0 with NPM and browserify

I am trying to use select2 version 4.0 in an ampersand-js app - as such that means I am using npm and browserify.
Unfortunately I cannot get select2 to load up.
the js file is being loaded up without error, since I can add a few console.log statements in relevant places and see them output,
but when I try to use select2 I'm getting told it's not defined.
Uncaught TypeError: $(...).select2 is not a function
Here's what I'm trying to do.
var $ = require('jquery');
require('Select2');
$('select').select2();
I have a feeling the issue comes from this line in the select2.js https://github.com/select2/select2/blob/4.0.0/dist/js/select2.js#L14
Specifically that it calls factory(require('jquery')); hence I believe that select2 is loading into a copy of jQuery that is then thrown away?
I found this issue which sounds like it's about the same thing, except I cannot get it to work either: npm browserify version of jquery-select2 version 4.x
So my train of thought was almost correct - it was loading select2 onto the wrong copy of jQuery.
There was two versions of jQuery being loaded.
In my package.json I had listed jQuery as a dependancy, however I wa also loading in the bower version of jQuery via the browser: {"jquery: "./bower_components/.../jquery.js"} key.
It seems that anything outside of the node_modules directory likely uses the "browser" defined module, whereas anything inside the node_modules directory will use the npm loaded module.
Basically, if something similar happens double check that you're not loading in two copies of libraryX.

Browserify run a single file

I'm trying to build up an embeddable script that runs onload with browserify.
So it compiles all the modules together, wraps them in a closure as expected, but what then? I can't figure out how to tell browserify to actually run one file. ie that 1 particular file is the entry point. For instance, if I had a file like so:
// runner.js
var app = require('./app'),
$ = require('jquery');
$(function(){
app.run();
});
How do I tell browserify during build step that I want this file to actually run. For instance can I wrap it in a self invoking function?
I've read that you can expose globals with browserify, but I don't want to expose every file in my app as a global that the app can see. Ideally I don't want to expose anything, I just want the script to run.
Any help?
Scratch that, looks like browserify does indeed run the files.
I forgot I was playing with the --standalone option and that doesn't actually seem to run anything, but I think let's you just build a module that someone else can call.