javascript better import hook without navigating through relative folders - javascript

something which is bothering me when writing tests in my folder structure is the following thing:
//App
meteor/imports/api/tasks.js
//test
meteor/test/imports/api/tasks.test.js
So now when i import something from tasks.js i go like import { task } from '../../../imports/api/tasks.js' and my folder structure gets much bigger than this.
is there a better solution?
I was thinking of an import hook, maybe in the root tests directory, so i can import all the things from there, and when i am on the test, i can import from the import hook and don't have to do all the ../../../../ navigation.

If you are using babel, you can add babel-plugin-module-resolver to your babel configuration.
A Babel plugin to add a new resolver for your modules when compiling
your code using Babel. This plugin allows you to add new "root"
directories that contain your modules. It also allows you to setup a
custom alias for directories, specific files, or even other npm
modules.
The module resolver may collide with webpack2 module handling, so you'll want to limit it just to tests:
.babelrc example:
"env": {
"test": {
"plugins": [
["module-resolver", {
"root": ["./meteor/imports"]
}]
]
}
}

Use
/imports/api/tasks.js
instead of
../../../imports/api/tasks.js
The / to start with marks root.

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.

Relative vs. non-relative module import for custom modules

Update[09/12/2017 16:58 EST]
Added reason why I hesitate to use the natively-supported non-relative import with my own modules to the bottom of this question.
Update[09/12/2017 12:58 EST]:
Per request, I made the file structure below reflect my actual use case. Which is a nested view module requesting a utility module somewhere up the directory tree.
Per request
In both TypeScript and ES6, one can import custom module by
// Relative
import { numberUtil } from './number_util';
// OR
// Non-relative
import { numberUtil } from 'number_util';
And according to TypeScript docs (link), one should:
... use
relative imports for your own modules that are guaranteed to maintain
their relative location at runtime.
... Use non-relative paths when importing any
of your external dependencies.
My problem here is that I have a project structure like this:
project/
|--utils
| |--number_util.ts
|--views
| |--article_page
| |-- editor_view
| |--editor_text_area.ts
And when I include utils/number_util inside my editor_text_area module, the import statement looks like:
import { numberUtil } from './../../../utils/number_util';
Which is long and not readable and, worst of all, difficult to maintain: whenever I need to move editor_text_area, I will have to update each these relative paths, when in the meantime I can just use the non-relative way of
import { numberUtil } from 'utils/number_util';
Does anyone have any suggestions on how best to do module imports to achieve the highest readability and maintainability?
But using the non-relative way poses a problem (other than that it is not recommended by official docs): what if I installed an npm module that has the same name with the module I'm importing? On that note, it is not as safe as the uglier alternative mentioned above.
Depending on your project tooling and structure, you have some options.
A) You could publish part of your dependencies as stand-alone modules, perhaps in a private registry. You can then install them with npm and require them like any other external dependency.
B) Many module systems support some sort of path mapping. The vue-js webpack template uses webpack's alias feature to set # to the source code root. TypeScript supports path mapping too. After you introduce that mapping you can use
import { numberUtil } from '#/utils/number_util';
This approach basically introduces a private namespace for your modules.
It is safe, in that you could only ever shadow an npm module with the name # which is an invalid name and therefore cannot exist.
For your example, you would have to have these entries in your compilerOptions:
"baseUrl": ".",
"paths": {
"#/*": ["*"]
}
Or if you only want to import modules from utils, you could change the mapping to "#/*": ["utils/*"] and import using '#/number_util'.
C) Another thing to consider is to improve your project structure. Depending on your actual project, it might make sense to apply the facade pattern at one point or another. Maybe inject the dependency into editor_text_area instead of letting it import it itself.
you can add "baseUrl": "./src", to you tsconfig.json,
then you can import * as utils from 'utils' to import ./src/utils/index.ts

How to publish a library of Vue.js components?

I am working on a project containing a Vuex module and an abstract components that users can extend from.
I would love to publish this on NPM to clean up my codebase and pull this away from my project as a solid well tested module. I have specified the main file in package.json to load an index which imports everything I want to expose:
https://github.com/stephan-v/vue-search-filters/
The index contains this at the moment:
import AbstractFilter from './src/components/filters/abstract/AbstractFilter.vue';
import Search from './src/store/modules/search';
module.exports = {
AbstractFilter,
Search
};
For this to work I need to transpile this since a babel compiler normally won't transpile files imported from node_modules(Correct me if I am wrong here). Besides that I would probably be a good idea to do this so it can be used by different systems.
How do I transpile only the files that I need though with Webpack? Do I have to create a separate config for this?
What does a config like that look like? I know the vue-cli has a build command for one single file component but this is a bit different.
Any tips or suggestions on how to transpile something like this are welcome.
Edit
This seems like a good start as well:
https://github.com/Akryum/vue-share-components
The most import thing for Webpack users to notice is that you need to transpile your files in UMD which can be set by:
libraryTarget: 'umd'
This will make sure your are transpiling for Universal Module Definition, meaning your code will work in different environments like AMD,CommonJS, as a simple script tag, etc.
Besides that it is import to provide the externals property in webpack:
externals: {}
Here you can define which libraries your project users but should not be built into your dist file. For example you don't want the Vue library to be compiled / transpiled into the source of your NPM package.
I will research a bit more, so far the best options looks like to create a custom project myself If I want flexibility and unit testing.
Webpack docs
This is also a useful page which goes in depth about how to publish something with Webpack:
https://webpack.js.org/guides/author-libraries/#add-librarytarget
The best way probably will be to build the module and set main in your package.json to my_dist/my_index.js. Otherwise every project that will use your module will have to add it to include which is tedious.
You will also want your webpack build to follow UMD (Universal Module Definition). For that you must set libraryTarget to umd:
...
output: {
filename: 'index.js',
library:'my_lib_name',
libraryTarget: 'umd'
},
...
Also a good thing will be to add Vue to externals so that you didn't pack extra 200kb of vue library.
externals: {
vue: 'vue'
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
And add it to peerDependencies in package.json:
...
"peerDependencies": {
"vue": "^2.0.0"
},
"devDependencies": {
"vue": "^2.0.0"
}
...
If you need an existing example of how to pack a vue.js component, you can take a look in one of the modules I maintain:
https://github.com/euvl/vue-js-popover
Particularly webpack.config.js and package.json will be interesting for you.
I was searching for a similar solution and found rollup https://github.com/thgh/rollup-plugin-vue2 (but was not able to make it work) and this component https://github.com/leftstick/vue-expand-ball where all the component code gets compiled to one reusable js-file.
I know that's not a proper solution but maybe it's sufficient for your needs.

Is it possible to do custom imports with Webpack like it is with Browserify?

So in my projects I tend to have a lot of custom scripts, as well as components etc.
I really don't like this pattern of importing things:
import Navigation from '../../Navigation/Navigation.js
/* or */
import Navigation from 'src/universal/components/Navigation/Navigation.js'
Is it possible to declare an entry point via Webpack like it is with Browserify? I am making the switch now and will sorely miss this functionality if it's not possible
With Browserify, declaring custom imports is as easy as including this in your package.json:
"browser": {
"navigation": "src/universal/components/Navigation/Navigation"
}
Then, to import anywhere, it's as simple as:
import Navigation from 'navigation'
How does one do this via Webpack?
You can try use resolve.alias
resolve.alias - Replace modules by other modules or paths.
webpack.config.js
resolve: {
alias: {
navigation: 'src/universal/components/Navigation/Navigation'
}
}

Categories