I know I have to remove all deep imports before upgrading to Angular 4, but I have no idea what is a deep import. Literally nobody mentions it. What is it? How does it look like?
Taken from How to deal with losing deep imports in Angular 4, an example of a deep import:
import { VALID } from '#angular/forms/src/model'
meaning 3 levels deep, whereas now you can only go 1 level:
import { VALID } from '#angular/forms'
which is invalid if "VALID" is in model, 3 levels deep. that's all there is to it. If you need something that's in "deep", it should be exported to in the first level now, or you need to open a ticket to angular to export it.
Not trying to advertise my own gist article, but I explain them here
Description
A "deep import" is simply an ESM import that goes deeper than the package root:
import thingA from 'my-package-name/src/components/thingA'
import thingB from '#my-namespace/my-package-name/src/components/thingA'
A namespaced package does not necessitate a deep import if the package name (package.json:name key) contains a slash:
import thingA from '#my-namespace/my-package-name'
Relative imports (./path/to/module) are also not considered deep imports, in fact it might be considered bad practice to "shallow import" an exported module from one's own package by it's package.json:main key location (i.e. src/index.js) as this can often cause circular dependencies.
Why deep imports can be a problem
Package bundling
Package bundlers are often used to transpile and condense a project to a distributive, single-file version (i.e. dist/bundle.js). Other packages that try to deep import from such packages experience errors, as the directory structure from src may often not be maintained or even included in the published package.
Unintended imports and directory changes
A developer might deep import a module from a package when it was not intended to be used outside of the scope of that package. Your changing of the name of an internally-used export or your package's directory structure should not require a major version (breaking change) update for a module that was only intended to be consumed internally.
Related
I'm developing a module that doesn't have a build that the user imports. Instead, he imports individual components and then bundles them along with his code. However, those components share utilities and I want to import them without going through relative path hell.
I know that's a pretty common question and I did some research. Suppose I have module/components/foo/bar/baz/index.js that wants to import module/utils/helper.js
Option 1
Just use relative paths and do:
import helper from '../../../../utils/helper'
Option 2
Use the module-alias package and have:
import helper from '#utils/helper'
This would work in Node.js because modules are resolved at runtime. However, let's say the module user has Webpack and imports the module:
import component from 'module/components/foo/bar/baz'
Webpack wouldn't be able to resolve #utils unless the user specifies that alias in his own Webpack configuration. That would be pretty annoying.
Option 3
Use Webpack aliases in webpack.config.js:
module.exports = {
resolve: {
alias: {
'#utils': path.join(__dirname, 'utils')
}
}
}
This would work fine if the module was pre-bundled. But as I've previously mentioned, I want the library to be usable with ES6 imports so that users can bundle only the parts they need.
Option 4
I could use the module name in the module's own source code:
import helper from 'module/utils/helper'
This appears to solve the problem, but I think it's a pretty bad solution. For development, you'd have to create a symlink node_modules/module -> module. I'm sure this hides many potential issues and collaborators would have to manually do it as well.
Is there a way to avoid relative paths while allowing the library to be used with ES6 imports?
I have a few npm modules published, all modules for existing libraries, like three.js or react.
The packages seem to be downloaded but i've received no feedback on whether it's done right or not.
Dependencies
What is the high level goal when defining dependencies?
three.js:
This is confusing because every "extension" just assumes that there is a THREE object available in some context.
My three.js module thus only mentions:
"devDependencies": {
"three": "^0.88.0"
}
And it's being used like this:
require( 'three-instanced-mesh' )(THREE)
Which both makes sense and doesn't.
The module can't work without three.js and a proper context passed in (THREE), but since i pass it in at runtime(?) it doesn't seem like it is an actual dependency. When I checkout the repo and want to develop in it, i do need to install three.js if i want the code to run.
React
I've published a React component which i intended to be used as such:
npm install my-module
import MyModule from 'my-module
<MyModule/>
For some reason I listed react as a peerDependencies dep.
<MyModule/> in JSX would imply that I've done something to have react already available in this context (similar to how THREE is passed in first example?).
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
What is the desired goal here and how to describe it? I only know that i don't want npm install my-module to install a different version of react, or to cause more react somehow to be bundled in the final bundle (but i'm not even sure about that).
What type of dependency (if any) should react be to my-react-component and how would i actually link it to my module?
For example using the externals thing with webpack vs having an actual import React from 'react'?
Build
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
What is the high level goal when defining dependencies?
You have to define which dependencies you are using only when developing (devDependencies) and the ones that are needed when someone installs your package and are going to be installed automatic (dependencies), and dependencies you need to be available, but you want the user to install (which, honestly, does not makes sense) peerDependencies.
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
It would require React to be available where the file is being imported, i.e: A imports myModule, but A has to have react imported. Putting as peer dependencies is the best way here indeed.
For example using the externals thing with webpack vs having an actual import React from 'react'?
Using externals in webpack just tells webpack to not bundle react and says that react will have been imported before the import of this component.
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
Usually the index.js file that contains the library minified/bundled. Publish that with npm, you'll need to setup main field on package.json
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
foo points to the name of the package that you created, i.e: the name this package was publish under. When you go to npmjs.org and search for foo, that is going to be the package. foo is in your node_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
I've used multiple 3rd party libraries in my ReactJS project like lodash, d3 etc. I just found out that I've not written explicit imports
like
import d3 from 'd3'
or import _ from 'lodash'
in all my components (I've imported them in some though). Yet it all works fine and I can also get the d3 object and _ in the browser console. How is it supposed to be?
Considering this is okay behavior can I just import the node_modules dependencies for react only once in my App(Root) Component and don't import them at all in all the other child components.
P.S I'm using webpack 1 and I've verified the behavior.
Even when it works, it is a bad practice, so my advice would be to play nice and always explicitly import modules you are using.
The reason why it is working is probably because some of those modules declare globals when they are imported, so your components that do not import them still reach global.
I want to be able to use lodash ( _ ) in a convenient way, but am looking for the best 'Aurealia' way to do so. I can see a few options:
Use a downloaded copy or CDN and simply include a <script> tag
reference in index.html
Install via npm or jspm (which??) and
then either:
import it in every module, which seems tedious
use one of Aurelia's feature or plugin or globalResources or someother??? features to load it and make universally available.
The answer to this question seems to indicate in a generic way that using import is best, but I'm stumped as to how.
These are my opinions- take with a grain of salt:
Aurelia Purist
The Aurelia Purist doesn't use lodash, instead opting to write modern javascript using the new array methods that ship with ES6. Sometimes the Aurelia purist consults you might not need underscore when he or she is in doubt. Other times the Aurelia purist consults you might not need jquery.
Aurelia Pragmatist
The Aurelia Pragmatist recognizes that Aurelia is just one tool in his or her toolbelt. The Aurelia framework, much like lodash and jQuery, help the Aurelia Pragmatist ship quality software that delights users. The Aurelia Pragmatist recognizes that there is more than one way to bake a cake and chooses to use the tools he or she is most effective with.
Answering the question...
You won't find anything in Aurelia that makes _ universally available with no strings attached. You can certainly make it a classic global OR you could install it with jspm and import it into each module as-needed. There's no middle ground that saves you from importing it AND saves you from feeling bad about using a global.
IMHO it's not the end of the world if your stuff has deps on the lodash global. It's a preference thing, your project certainly won't fail if you pick one or the other.
Great answer from Jeremy (as always). I'll just add up to that...
Try to use ES6 array methods wherever you can (Aurelia Purist standpoint). If you need some thing that cannot be replicated with native ECMAScript, or you really prefer to use lodash, install it using jspm:
jspm install lodash
lodash is an alias for npm:lodash defined in jspm registry. jspm will install lodash from npm's registry but will manage it itself - inside jspm_packages and with system.js module loader.
To use _ from your module, do an import:
import _ from 'lodash';
After that, you can use _ in your module code, like you would expected:
let result = _.map(...);
Edit: Thanks #VolkerRose for suggestion.
lodash modules with full installation
It's also possible to import only the functionality you need from lodash. If you need map function only, this is what you would use within your modules:
import map from 'lodash/map';
// ...
let result = map(...);
When lodash/map is required, jspm will find a lodash module folder (jspm_packages/npm/lodash#4.3.0 if version 4.3.0 is installed) and use the rest of the from value to search within that folder. In this case, all lodash modules/files are in the root folder - so map.js module is used. If there were any subfolders involved, you would need to use something like import map from 'lodash/some/sub/folder/map').
lodash modules with standalone installation
As #VolkerRose said in comment, lodash is modularized and you can install just the modules you need.
jspm install npm:lodash.map
This will install lodash's map module. Note that we need npm: prefix this time, since jspm doesn't have aliases for standalone lodash modules.
The lodash.map module can now be used similar as above:
import map from 'lodash.map';
// ...
let result = map(...);
you can install the lodash with jspm
jspm install lodash
and then to consume it globally in you app using a global import as follow:
import 'lodash';
example:
in your main file, it will load the lib globally in the window context.
// importing lodash as global resource
import 'lodash';
import 'bootstrap';
import {Aurelia, LogManager} from 'aurelia-framework';
import {ConsoleAppender} from 'aurelia-logging-console';
import {authConfig} from './config/auth-config';
LogManager.addAppender(new ConsoleAppender());
LogManager.setLevel(LogManager.logLevel.debug);
export function configure(aurelia: Aurelia) {
aurelia.use
.defaultBindingLanguage()
.defaultResources()
.history()
.router()
.eventAggregator();
aurelia.start().then(a => a.setRoot());
}