How to use node_modules on a "traditional website"? - javascript

I have used node to manage dependencies on React apps and the like, in those you use package.json to keep track of libs and use them in your scripts using ES6 import module syntax.
But now I'm working on a legacy code base that uses a bunch of jQuery plugins (downloaded manually and placed in a "libs" folder) and links them directly in the markup using script tags.
I want to use npm to manage these dependencies. Is my only option:
run npm init
install all plugins through npm and have them in package.json
link to the scripts in the node_modules folder directly from the markup:
<script src="./node_modules/lodash/lodash.js"></script>
or is there a better way?

Check out this tutorial for going from using script tags to bundling with Webpack. You will want to do the following: (Do steps 1 and 2 as you mentioned in your question then your step 3 will change to the following 3 steps)
Download webpack with npm: npm install webpack --save-dev
Create a webpack.config.js file specifying your entry file and output file. Your entry file will contain any custom JS components your app is using. You will also need to specify to include your node_modules within your generated Javascript bundle. Your output file will be the resulting Javascript bundle that Webpack will create for you and it will contain all the necessary Javascript your app needs to run. A simple example webpack.config.js would be the following:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
resolve: {
alias: {
'node_modules': path.join(__dirname, 'node_modules'),
}
}
};
Lastly, add a <script> tag within your main HTML page pointing to your newly generated Javascript bundle:
<script src="dist/my-first-webpack.bundle.js"></script>
Now your web application should work the same as before your refactoring journey.
Cheers

I recommend Parcel js.
Then you only need:
Run npm init
Install dependency, for example npm install jquery
Import with ES6 syntax: import $ from "jquery";
And run with parcel

Related

How does webpack pick a relative path inside node_modules ? does it reference package.json at all?

When i do npm install react-slick, i get the following in my node_modules folder:
Now in my react application in a src/index.js file, when i do the following:
import Slider from "react-slick";
How does webpack know where to pick slider from ? will it at all look for some clue or definition inside node_modules/react-slick/package.json at all ?
Edit :- so here is the package.json file for webpack, when i import Slider from 'react-slick' , does it resolve to dist or lib ? and which file does it pick then and why ?
Well, the simple walkthrough of it will be as below:
Simple Walkthrough
If you carefully look at the node_modules/react-slick/package.json there is a property named main. Something like this:
{
"name": "react-slick",
"main": "index.js"
}
It will tell the Webpack which file is the entry file of the whole package (It's usually referred to index.js). All the necessary exports for the package lies in this file, so Webpack will only look for those exports and will import what you looking for. In this particular case, there should be a default export for the Slider that you using right now. So the index.js is probably something like this:
// index.js
var slider = require('./lib/slider'); // Usually all the main modules are lies under lib folder.
// other imports ...
module.exports = slider;
Difference between lib and dist
Usually, the dist folder is for shipping a UMD that a user can use if they aren't using package management. The lib folder is what package.json, main property points to, and users that install your package using npm will consume that directly. The only use of the lib as opposed to src is to transform your source using babel and Webpack to be more generally compatible since most build processes don't run babel transforms on packages in node_modules.
Webpack uses aliases to target node_modules using a shorthand.
Example #1:
import 'xyz'
/abc/node_modules/xyz/index.js
Example #2:
import 'xyz/file.js'
/abc/node_modules/xyz/file.js
Once it targets the correct folder in node_modules, it follows the rules written in the package itself (manifest, package.json)
You can also define your own aliases as such:
webpack.config.js
const path = require('path');
module.exports = {
//...
resolve: {
alias: {
xyz$: path.resolve(__dirname, 'path/to/file.js')
}
}
};
And then can be used as import xyz from $xyz

How to split a library and its plugins among npm packages?

Im working on a JavaScript library and I want future users to be able to pick and choose the plugins they want to add to their project among with the main library. I'm having few issues with modules and webpack. I'm writing pseudo code to give an idea of how the code is organized.
My index.js for the main library looks like this:
import ClassA from "./classA";
import ClassB from "./classB";
export default class MyLib {
.....
}
export { ClassA, ClassB }
I can easily output the library with webpack:
output: {
path: ...
filename: 'mylib.min.js',
library: "MyLib",
libraryTarget: "umd"
}
To be able to choose which plugins to add, I'm creating different npm packages (one for each plugin), adding MyLib as an external dependency and then doing:
import {ClassA, ClassB} from "MyLib";
class PluginA extends ClassB {
constructor() {
this.test = new ClassA();
}
}
This works perfectly but, when "compiling" PluginA, webpack would include MyLib in the final js file for PluginA. If I want to include multiple plugins the code would end up with multiple copies of the main lib.
My final goal is to organize the code in such a way that can be easily installed with the following npm commands without having duplicated code everywhere:
npm install MyLib
npm install MyLib-PluginA
npm install MyLib-PluginB
Of course, one obvious solution would be to not use webpack for the plugins but I'd like to keep this option as the last resource in case nothing else works.
Thanks!
I wouldn't recommend using webpack to build your plugins/library. Rather, I'd let the consumer of the library decide on their own bundler. Your best step for the library should just be transpilation (if needed) of any intermediate code like babel-featured JS or TypeScript into something that can be safely require'd by node.
In addition, each plugin ought to have MyLib as a peerDependency instead of a regular dependency. That will make sure that MyLib doesn't get nested inside of the plugin's node_modules and will thus avoid duplicates being bundled. The plugins could in addition has MyLib as a devDependency for the sake of unit tests, but the important bit is that it's never a regular dependency.
Digging into webpack documentation, I've found a solution that uses webpack's externals.
From webpack documentation:
The externals configuration option provides a way of excluding dependencies from the output bundles.
I've just added the following lines to the webpack's configuration for the plugin:
module.exports = {
...,
externals: {
mylib: {
commonjs: 'MyLib',
commonjs2: 'MyLib',
amd: 'MyLib',
root: 'MyLib'
}
}
};
Webpack documentation: https://webpack.js.org/configuration/externals/
Hope this will help others.

Vue JS Module not found (datepicker)

I am pretty new to vue.js - I only started using it today and naturally I have run into an error I cannot seem to resolve.
I am using the v-md-date-range-picker module:
(https://ly525.github.io/material-vue-daterange-picker/#quick-start.
The instructions tell me to do the following:
1
npm install --save v-md-date-range-picker
2
<template>
<v-md-date-range-picker></v-md-date-range-picker>
</template>
3
<script>
import Vue from 'vue';
import VMdDateRangePicker from "v-md-date-range-picker";
import "v-md-date-range-picker/dist/v-md-date-range-picker.css";
Vue.use(VMdDateRangePicker);
</script>
So, I ran the command in terminal in my project folder, added the 2 bit of code to my HelloWorld.vue page and then added the code from step 3 into the main.js.
When I have a look in my package.json file, I see:
"dependencies": {
"core-js": "^2.6.5",
"v-md-date-range-picker": "^2.6.0",
"vue": "^2.6.10"
},
However, I get the error:
Module not found: Error: Can't resolve 'v-md-date-range-picker/dist/v-md-date-range-picker.css' in '/Users/James/Documents/projects/vue-test/src'
am I missing something blatantly obvious here?
Edit:
I tried the response in the comments below which did not work.
On the main page of the module, I followed the instructions. However, going through the pages I found the same instructions with some extra text:
I assume that you have a working bundler setup e.g. generated by the vue-cli thats capable of loading SASS stylesheets and Vue.js SFC (Single File Components).
I am going to go out on a limb here and say I do not have a working bundler. I went into the node_modules folder, found that module and looked inside. There was no dist folder. Just .scss files etc..
So, I assume that I somehow need to build this project first.
How do I do that?
I thought running it in the browser would have done this on the fly but it clearly has not.
Edit 2:
After some googling around I found the command:
$ npm run build.
Which gives me this error:
This dependency is not found, To install it, you can run: npm install --save v-md-date-range-picker/dist/v-md-date-range-picker.css
So, I run that command and then I get the error:
Could not install from "v-md-date-range-picker/dist/v-md-date-range-picker.css" as it does not contain a package.json file.
Check if you can find this in the webpack.base.conf.js inside the build folder. If not add it.
module: {
rules: [
{
test: /\.css$/,
loader: ['style-loader', 'css-loader'], // Note that the order is very important
},
Run npm install style-loader css-loader --save before adding it to the file if it isn't there.
To Address your question
Run the command: npm install sass-loader --save
Then add an import for every SCSS file in the module.
This is not the most optimal solution, but that package looks broken to me and this is merely a workaround.
I will take time to try out the library myself and try to provide a fix for it.
Create v-md-date-range-picker.css in v-md-date-range-picker/dist/ and copy css from
md-date-range-picker.min.css
and refresh your page. For some reason css file is not being created when we install md-date-range-picker.min

bundling multiple js files

in react using webpack every js files is bundle into a single bundle.js , for my normal html , css, js application for example , i am having 6 libraries. for an example consider
i am using jquery and bootstrap min versions. so if i reference two files the request will be two. so how can i make it into a single file. So there will be a single request.
like when i checked the file size is about in kb's and the request is processed within less that 1 or 2 seconds , like the chrome dev tools shows the time for to load also it parrallely loads the two files.
But how can i bundle the two librarys using webpack and get a single file that i can refer in my application.
i am a beginner to webpack
You need to import them in your entry point file and Webpack will handle the bundling. As you have worked with React, I assume you have basic command line skills.
You can read the Getting Started guide which bundles Lodash like how you are trying to bundle jQuery and Bootstrap.
First of install, ensure that you are installing jQuery, Bootstrap, and any other libraries using npm (or yarn, if you prefer):
# Install Webpack as a dev dependency
npm install webpack webpack-cli --save-dev
# Install dependencies (I've added Popper.js as Bootstrap requires it)
npm install jquery bootstrap popper.js
Create a folder called src and a file inside there called index.js. This is your entry point and Webpack will look for this file unless configured differently. Import the libraries like this:
import $ from 'jquery'
import 'bootstrap'
// Do something with jQuery
$(document).ready(() => console.log('Hello world!'))
Then run Webpack using npx:
npx webpack
A file named main.js should be created in a folder called dist that contains the bundled code. This is your output file. You can use a <script> tag in your HTML file to load this JavaScript:
<!-- assuming your index.html is in the dist folder -->
<script src='main.js'></script>
Once you get here, you can explore more advanced things like importing Bootstrap components individually, minifying code, multiple bundles, transpiling TypeScript, etc.
You will likely need to add a Webpack configuration file very soon as there is only so much that can be done using zero-config mode.
Good practice is to keep two sepearate bundles for the application logic and external libraries and in webpack this can be achieved by the following code,
app.js - appliation index file,
vendors.js - import all external libraries in this file
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
}
To get a single file, import vendors.js file inside app.js file and give entry key in webpack as
entry: './src/app.js'
Let us assume that you have the files in src directory. You can merge multiple files by specifying them in webpack.config.js to have a single named file as an output. I hope this is what you are looking for.
const path = require('path');
module.exports = {
entry: {
'bundle.js': [
path.resolve(__dirname, 'src/file1.js'),
path.resolve(__dirname, 'src/file2.js')
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [{
exclude: /node_modules/
}]
}
};
As above, the two files "file1.js" and "file2.js" will be combined into a single file "bundle.js" and stored in "dist" directory.
You can also exclude node_modules by specifying a rule in module object of webpack configuration.

Applying loaders to files imported via resolve.modules in webpack

I have two javascript projects in separate directories within a parent directory and I want both of them to be able to import files from a common directory. The structure looks a bit like this:
- parentDir
- project1
- package.json
- webpack.config.js
- src
- index.js
- project2
- package.json
- webpack.config.js
- src
- index.js
- common
- components
- CommonComponent.vue
- application
- app.js
I'd like both project1's index.js and project2's index.js to be able to import CommonComponent.vue and app.js.
Currently this works if I do:
import CommonComponent from ../../common/components/CommonComponent.vue
However those import paths starts to get very messy and hard to maintain the deeper into each tree we go, with huge numbers of ../s, so I'm trying to find a way of making the imports neater and easier to manage and I came across resolve options in webpack. So I've tried adding this to my webpack.config.js:
resolve: {
modules: [
path.resolve("../common/"),
path.resolve("./node_modules")
]
},
so then the import would look like:
import CommonComponent from "components/CommonComponent.vue"
import app from "application/app"
Importing the plain js file works, but when trying to import the .vue file, webpack throws an error:
ERROR in C:/parentDir/common/components/CommonComponent.vue
Module not found: Error: Can't resolve 'vue-style-loader' in 'C:/parentDir/common/components'
So how can I apply webpack loaders to files imported via resolve.modules?
Note: importing .vue files from within a single project works fine, so my module.rules config is correct.
It turns out the common package needed its own node_modules. That doesn't seem to be the case when importing a file from there directly via its path, but it is when using either resolve.modules or resolve.alias in webpack.
So the answer was to npm init in common and then to npm install all the dependencies and devDependencies needed there. e.g (of course these will depend on the project):
npm install --save vue
npm install --save-dev babel-core babel-loader css-loader less-loader vue-loader vue-template-compiler webpack
Once that's done, both of these webpack configs seem to have the same result as far as I can tell:
resolve: {
modules: [
path.resolve("../../common"),
path.resolve("./node_modules")
]
},
and
resolve: {
alias: {
components: path.resolve("../../common/components")
}
}
Both allow a file in project1 or project2 to do an import like:
import CommonComponent from "components/CommonComponent.vue"

Categories