Properly configure scripts with Angular-CLI - javascript

Let's say I have a project which uses these JS libraries:
main.js which has to be loaded in all pages
joe.js which is a npm package to be loaded in all pages
bob.js which is an old-style 3rd party JS library with no module defined to be loaded in all pages
max.js which is a CommonJS library to be loaded on-demand in some components
So far, I succeded in:
including main.js in the scripts property of angular-cli.json
the same as above for joe.js using relative paths (../node_modules/joe/dist/joe.js)
so they end up in the generated bundle that is loaded on every page.
I had instead a lot of problems with the other two. So far I've managed to include bob.js in the bundle by wrapping it in a self-executing function:
(function() {
// old code of bob.js
})();
but why?
And I'm totally clueless on how to include/bundle max.js...

For main, joe and bob.js you should do this:
To have the scripts available everywhere you can use the src/assets folder in your angular-cli folder structure.
And then include your scripts with:
<script src="assets/my-script.js"></script>
As an option you can get those dependencies from node_modules to src/assets with a webpack.
For most cases using CDN is better option than all this.
As for max.js you i would create a service to conveniently inject it:
import { Injectable } from '#angular/core';
#Injectable()
export class MaxService {
lib: any;
constructor(){
this.lib = require('max.js');
}
}

Related

Symfony Webpack Encore with company common bundle

There maybe some documentation out there on how to deal with this situation but i don't even know how to look for it.
Here's the deal, we have a Symfony "module" (ex Bundle) company-made that we share across multiple projects. Atm it is not listed on packagist and we require it with local composer repository paths if that matters.
Inside the shared module we have some css and some js that needs to be included. Since one of those shared-module (or bundle, w/e you want to call it) has bootstrap (the css frontend toolkit) the module itself requires it together with his css.
Inside the shared module we have a JS file "CoreLibrary.js" and we import the required js like this:
import $ from 'jquery';
import 'bootstrap';
export class CoreLibrary {
... more code
}
Then, inside the main application we include the common js file from the app.js file like this:
import { CoreLibrary } from '../public/bundles/thebundlename/js/CoreLibrary';
That doesn't seem to be ideal, and beside that, with encore we have to repeat
import $ from 'jquery';
import 'bootstrap';
import { CoreLibrary } from '../public/bundles/thebundlename/js/CoreLibrary';
In every .js file we need. That's so much of a burden that I can't belive there are no better ways do to that.
Sidenote: not so long ago i had to even follow this one:
Yarn and Webpack Encore configuration in Symfony 4.1 project because i was getting error during "yarn watch".
What is the correct way of doing it with company-shared module that requires 3rd party library like bootstrap?
For global jquery i have this in main js file
const $ = require('jquery');
global.$ = global.jQuery = $;
Also uncommented line in webpack config about jquery.

Natively import ES module dependencies from npm without bundling/transpiling first-party source

Background
I'm trying to create a "buildless" JavaScript app, one where I don't need a watch task running to transpile JSX, re-bundle code, etc every time I save any source file.
It works fine with just first-party code, but I'm stuck when I try to import dependencies from npm.
Goal
I want to achieve this kind of workflow:
npm install foo (assume it's an ES module, not CommonJS)
Edit source/index.js and add import { bar } from 'foo'
npm run build. Something (webpack, rollup, a custom script, whatever) runs, and bundles foo and its dependencies into ./build/vendor.js (without anything from source/).
Edit index.html to add <script src="build/vendor.js" type="module"...
I can reload source/index.js in my browser, and bar will be available. I won't have to run npm run build until the next time I add/remove a dependency.
I've gotten webpack to split dependencies into a separate file, but to import from that file in a buildless context, I'd have to import { bar } from './build/vendor.js. At that point webpack will no longer bundle bar, since it's not a relative import.
I've also tried Snowpack, which is closer to what I want conceptually, but I still couldn't configure it to achieve the above workflow.
I could just write a simple script to copy files from node_modules to build/, but I'd like to use a bundled in order to get tree shaking, etc. It's hard to find something that supports this workflow, though.
I figured out how to do this, using Import Maps and Snowpack.
High-Level Explanation
I used Import Maps to translate bare module specifiers like import { v4 } from 'uuid' into a URL. They're currently just a drafted standard, but are supported in Chrome behind an experimental flag, and have a shim.
With that, you can use bare import statements in your code, so that a bundler understands them and can work correctly, do tree-shaking, etc. When the browser parses the import, though, it'll see it as import { v4 } from 'http://example.org/vendor/uuid.js', and download it like a normal ES module.
Once those are setup, you can use any bundler to install the packages, but it needs to be configured to build individual bundles, instead of combining all packages into one. Snowpack does a really good job at this, because it's designed for an unbundled development workflow. It uses esbuild under the hood, which is 10x faster than Webpack, because it avoids unnecessarily re-building packages that haven't changed. It still does tree-shaking, etc.
Implementation - Minimal Example
index.html
<!doctype html>
<!-- either use "defer" or load this polyfill after the scripts below-->
<script defer src="es-module-shims.js"></script>
<script type="importmap-shim">
{
"imports": {
"uuid": "https://example.org/build/uuid.js"
}
}
</script>
<script type="module-shim">
import { v4 } from "uuid";
console.log(v4);
</script>
snowpack.config.js
module.exports = {
packageOptions: {
source: 'remote',
},
};
packageOptions.source = remote tells Snowpack to handle dependencies itself, rather than expecting npm to do it.
Run npx snowpack add {module slug - e.g., 'uuid'} to register a dependency in the snowpack.deps.json file, and install it in the build folder.
package.json
"scripts": {
"build": "snowpack build"
}
Call this script whenever you add/remove/update dependencies. There's no need for a watch script.
Implementation - Full Example
Check out iandunn/no-build-tools-no-problems/f1bb3052. Here's direct links to the the relevant lines:
snowpack.config.js
snowpack.deps.json
package.json
core.php outputs the shim
plugin.php - outputs the import map
passphrase-generator.js - imports the modules. (They're commented out in this example, for reasons outside the scope of this answer, just uncomment them, run the bundle script, and they'll work).
If you are willing to use an online service, the Skypack CDN seems to work nicely for this. For instance I wanted to use the sample-player NPM module and I've chosen to use a bundle-less workflow for my project using only ES6 modules as I'm targeting embedded Chromium latest version so don't need to worry about legacy browser support, so all I needed to do was:
import SamplePlayer from "https://cdn.skypack.dev/sample-player#^0.5.5";
// init() once the page has finished loading.
window.onload = init;
function init() {
console.log('hello sampler', SamplePlayer)
}
and in my html:
<script src="./src/sampler/sampler.js" type="module"></script>
And of course you could just look inside the JS file the CDN generates at the above url and download the generated all-in-one js file it points to, in order to use it offline as well if needed.

How do I globally declare jQuery inside a Vue-CLI 3 project?

I have seen a lot of questions about this on SO and elsewhere, but so far I had no luck.
For a bit of context, I built a SPA website on a Vue project I created with an "old" command. I don't remember which one but it looked like the following:
vue init webpack <my project>
I recently realized that Vue-CLI 3 would be way easier for me to maintain, keep updated and improve for various contextual reasons, so I installed #vue/cli, created a new project and started to copy/paste files from my old project to the new one.
The old project had a build directory with various webpack config files in it, and I needed jQuery set globally for a package I wanted to use, so I added the following to the "base" config of Webpack.
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
}),
However, with the new project, all I have now as config file is babel.config.js and vue.config.js at the project's root, there are no build nor config directory.
I tried to set jQuery globally with the following lines inside my main.js file:
import jQuery from 'jquery'
window.$ = window.jQuery = jQuery
global.$ = global.jQuery = jQuery
But everytime I reload my page, I get the following message:
jQuery is not defined
So, so far, I use the CDN version of jQuery but I don't feel at ease with this solution.
How should I proceed with a Vue-CLI 3 project?
Thank you in advance
You can use Vue plugins. This allows you to add new property to every single component.
In main.js
import Vue from 'vue';
import jQuery from 'jquery';
Vue.use({
install (V) {
V.$jQuery = V.prototype.$jQuery = jQuery
}
})
Then you can use jQuery everywhere in component.
In MyComponent.vue
import Vue from 'vue'
export default {
mounted () {
this.$jQuery('h1').text('Hello World')
Vue.$jQuery('h1').text('Hello World')
}
}
Even another js file.
In util.js
import Vue from 'vue'
Vue.$jQuery('h1').text('Hello World')
You could go on public/index.html and add the script tag to the body with the route to your jquery file or cdn.
If you put it in a jquery folder in public it would look like:
<body>
<div id="app"></div>
<script type="text/javascript" src="/jquery/jquery-3.4.1.min.js"></script>
</body>
I am not sure if that's exactly what you want but it would make it available globally across your project.
If you are using Jquery plugins, you should do this in your main.js:
/*
* If using Jquery plugins they will expect to be available in the global namespace,
* which isn’t the case when you import/require it. So manually assign jquery to
* window. Use require instead of import because the imports are hoisted to the
* top of a file by the compiler, which would break VueJS code.
*/
const $ = require('jquery')
window.jQuery = window.$ = $;

Making Webpack register modules in RequireJS cache

Currently I'm using RequireJS for all my modules.
I'm considering using Webpack for my main project but need to load modules not known during build time. Like plugins.
One approach would be to use Webpack at build time and then use RequireJS at runtime. The only problem is that files loaded from Webpack bundle won't be found in the RequireJS cache.
If I manually register them it works:
import jQuery from 'jquery';
define('jquery', [], function() { return jQuery; });
But is there some easier way? Like Webpack generating code that does this?
I ended up writing a custom loader that added the code at the end of each file. Using window.define instead of define makes Webpack leave it intact for RequireJS.

React: Good Practices For CDNs, Node_Modules and Both

Been building a React application on top of create-react-app which uses Webpack through react-scripts and has React/Redux/React-dom, etc. as dependencies in our package.json.
To use those JS dependencies, we use import statements, however, for other JS dependencies (i.e. the JS used in Bootstrap and jQuery), we import those through CDN. (We have the Bootstrap CSS files in our CSS directory, and use import statements).
Is there a generally accepted practice to use CDNs vs node_modules or both on the front-end side?
My initial thought was to use CDNs to get static JS files if you're not going to use them as imports in your JS files, but wondered if that was the right way to think about it.
Obviously this isn't a React specific question, just using React to give more context.
Here's what I generally do:
(1) Setup webpack.config.js to assume global React and ReactDOM variables are available, to prevent them from being transpiled, minified, and bundled on every build. You can still use import statements in your files, as before, but the libraries will not be in your final bundle:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
(2) Use a CDN like http://www.jsdelivr.com/, which allows you to get multiple scripts as a single concatenated string, in a single request. This will minimize loading time.
(3) (Optional) Install React and ReactDOM as deps, and have the build process copy the minified js files from node_modules/react(-dom)/dist to your project's public files directory, and then create simple checks for the variables at the foot of the page, to inject includes pointing to your server, if the CDN calls happen to fail. For example:
if (!window.React) {
$('head').append('<script src="/js/react.min.js">')
}

Categories

Resources