The documentation I've read is rather hand-wavy about what exactly import does in Javascript, particularly in the Angular framework. I get that it imports modules from another file that has one or more exports. But there are many permutations of its syntax, and not all are discussed with much detail. I'm currently having a very hard time with the #asymmetik/ngx-leaflet-markercluster module. When I try to compile my Angular app, I get a message reading "Can't resolve 'leaflet.markercluster' in 'C:\sca_root\city8\node_modules#asymmetrik\ngx-leaflet-markercluster\dist\leaflet-markercluster" -- this is in reference to a line that reads simply
import 'leaflet.markercluster';
This seems to me (and I know, perhaps I am making too many assumptions here) that there should be a file in that same directory named leaflet.markercluster.js or perhaps leaflet.markercluster.ts (it's Javascript, not TypeScript, so it will be the former). But there is no file named leaflet.markercluster.js in that directory. These are the files in that directory:
leaflet-markercluster.directive.js.map
leaflet-markercluster.directive.metadata.json
leaflet-markercluster.module.d.ts
leaflet-markercluster.module.js
leaflet-markercluster.module.js.map
leaflet-markercluster.module.metadata.json
leaflet-markercluster.directive.d.ts
eaflet-markercluster.directive.js
Which one would that import statement import? If not any of them, where outside this directory would it import that file from? What other information (perhaps in tsconfig.json or angular.json) might affect where this import statement imports from?
the problem in this case was that leaflet.markercluster ALSO needs #types installed. i had to issue a
npm install #types/leaflet #types/leaflet.markercluster
i don't know why the error had nothing to do with #types, but this is in keeping with the soul-crushing nature of Angular development.
Related
The title may seems a bit confusing, let me explain.
I'm using an opensource library pico which has a not exported object "pico", so I put ?raw keyword to import it in my frontend project with webpack 5.
import "./lib/pico.js?raw";
And it works properly on my frontend page.
--
Then when I want to unit test it by jest, I found jest-raw-loader by this post, but the pico seems won't be imported by using this library.
The ?raw on webpack seems more similar as script-loader library.
And jest-raw-loader seems more similar as raw-loader on older versions.
Although webpack official doc suggest to replace raw-loader with ?raw, but they seem different in my case.
I know I can simply edit this pico.js file to export this object. But what if next time I have a far larger library need to be managed? Is there any other way to import this kind of "not exported" object?
I am a newbie to webpack and currently trying to understand basic concepts.
Looking at the official docs, on the page Concepts it uses module term and gives link to read more about modules on page Modules.
So on this page we have question "What is a module" but no explicit answer to that is given. Rather, it describes modules by how they "express their dependencies":
What is a webpack Module
In contrast to Node.js modules, webpack modules can express their
dependencies in a variety of ways. A few examples are:
An ES2015 import statement
A CommonJS require() statement
An AMD define and require statement
An #import statement inside of a css/sass/less file.
An image url in a stylesheet url(...) or HTML file.
So it doesn't explicitly defines what exactly is the module and I am confused now.
Is module just a javascript file?
Or is it any type of file like .css or images?
Or is module some logical concept not related to physical files at all?
The simple answer to your question is that a Webpack Module is a Javascript file that is processed by Webpack.
As for why that's a thing, well, consider the following Javascript:
if (window.matchMedia('(max-width: 600px)')) {
const img = document.querySelector('#header-responsive-image');
img.src = 'different/image/url/file.jpg';
img.classList.add('some-class');
}
Note that this code has dependencies on specific markup, image files, and CSS rules. But crucially you have to read the code to find out what they are. There's no (easy) way to statically analyze the dependencies (or even do it by hand!).
This may not seem like a big deal in small apps, but when you're working with a large Javascript codebase or authoring a component library, the ability to statically analyze the real dependency graph and have your tools warn you immediately when your JS, CSS, markup, and files on disk get out of sync is a lifesaver.
With Webpack at the top of the file you're going to see something more like this:
import header600Width from '../img/header-600-width.jpg';
import '../styles/has-some-class.css';
// etc.
You can load images, csv, xml, toml, json, css, and the list goes on. This is something that other modules systems by-and-large can't or won't do. So a Webpack module is, in a sense, a superset of a Javascript module.
In an Ember app, when using certain dependencies like moment installed via bower, we have to also import the same in the ember-cli-build.js file:
app.import('bower_components/moment/moment.js');
My question is why is that needed, since I would assume everything inside node_modules as well as bower_components should be available for use inside the app.
Also if that is not the case, how do we identify which dependencies would require such explicit import to be able to use them ?
You don't have to, actually.
There is a package now that lets you 'just import' things: https://github.com/ef4/ember-auto-import
Some reading on the topic of importing: https://discuss.emberjs.com/t/readers-questions-how-far-are-we-from-being-able-to-just-use-any-npm-package-via-the-import-statement/14462?u=nullvoxpopuli
In in-depth answer to your question and the reasons behind why things are the way they are is posted here:
https://discuss.emberjs.com/t/readers-questions-why-does-ember-use-broccoli-and-how-is-it-different-from-webpack-rollup-parcel/15384?u=nullvoxpopuli
(A bit too long for stack overflow, also on mobile, and I wouldn't want to lose all the links and references in a copy-paste)
Hope this helps
Edit:
To answer:
I just wanted to understand "in what cases" do we need to use the import statement in our ember-cli-build (meaning we do not do import for all the dependencies we have in our package/bower.json)...But only for specific ones...I wanted to know what is the criteria or use case for doing import.
Generally, for every package, hence the appeal of the auto-import and / or packagers (where webpack may be used instead of rollup in the future).
Though, it's common for ember-addons to define their own app.import so that you don't need to configure it, like any of these shims, specifically, here is how the c3 charting library is shimmed: https://github.com/mike-north/ember-c3-shim/blob/master/index.js#L7
Importing everything 'manually' like this is a bit of a nuisance, but it is, in part, due to the fact that js packages do not have a consistent distribution format. There is umd, amd, cjs, es6, etc.
with the rollup and broccoli combo, we need to manually specify which format a file is. There are some big advantages to the rollup + broccoli approach, which can be demonstrated here
and here
Sometimes, depending on the transform, you'll need a "vendor-shim".
These are handy when a module has decided it wants to be available on the window / global object instead of available as a module export.
Link: https://simplabs.com/blog/2017/02/13/npm-libs-in-ember-cli.html
(self represents window/global)
however, webpack has already done the work of figuring out how to detect what format a js file is in, and abstracts all of that away from you. webpack is what ember-auto-import uses, and is what allows you to simply
import { stuff} from 'package-name';. The downside to webpack is that you can't pipeline your transforms (which most people may not need, but it can be handy if you're doing Typescript -> Babel -> es5).
Actually: (almost) everything!
Ember does, by default, not add anything to your app except ember addons. There are however some addons that dynamically add stuff to your app like ember-browserify or ember-auto-import.
Also just because you do app.import this does not mean you can use the code with import ... from 'my-package'. The one thing app.import does is it adds the specified file to your vendor.js file. Noting else.
How you use this dependency depends completely on the provided JS file! Ember uses loader.js, an AMD module loader. So if the JS file you app.imported uses AMD (or UMD) this will work and you can import Foo from 'my-package'. (Because this is actually transpiled to AMD imports)
If the provided JS file provides a global you can just use the global.
However there is also the concept of vendor-shims.. Thats basically just a tiny AMD module you can write to export the global as AMD module.
However there are a lot of ember addons that add stuff to your app. For example things like ember-cli-moment-shim just exist to automagically add a dependency to your project. However how it's done completely depends on the addon.
So the rule is:
If its an ember addon read the addon docs but usually you shouldn't app.import
In every other case you manually need to use the library either by app.import or manual broccoli transforms.
The only exception is if you use an addon that tries to generically add dependencies to your project like ember-browserify or ember-auto-import
I was going through Angular2 quickstart tutorial with Javascript and Typescript as well, In javascript version I observed that components and modules are first assigned to a variable (window.app which I understood as some global variable that can be accessed across js files or script blocks) and that is fine. Coming to type script version just export and import were used, I tried to analyze the generated javascript code but understood nothing. Can some one explain me how this export and import works in Tyepescript.
Import and export in typescript are explained well by the documentation here https://www.typescriptlang.org/docs/handbook/modules.html.
Like toskv said in his comment, how those statements in your TypeScript files get transpiled into statements in your JavaScript files depends largely on the module system you set up in your tsconfig.json file.
For example, setting "module": "commonjs" will cause the TypeScript compiler (tsc) to transform your import/export statements into essentially node.js-style require() statements. This documentation has a few simple, but helpful, examples of how node.js modules work: https://nodejs.org/api/modules.html.
Using a setting of "systemjs" instead of "commonjs" will make TypeScript translate your import/export statements into a format that SystemJS understands, of which I am no expert.
This process is further complicated by the fact that Angular 2 projects also require build steps that take the transpiled JavaScript files and turn them into packaged "bundles." These bundled files are (depending on your configuration settings) concatenated, minified, and perhaps even uglified. So looking at the final javascript code that is run is really not helpful, as it was not written by humans.
For example, the Webpack build system (google webpack.js) takes require() statements it finds in JavaScript code and does some magic to wrap each module in its own __webpack_require__ function, which allows the build system to take your whole project file structure and bundle it in to one or several JavaScript files which still maintain their dependencies on each other.
In other words, by the time you look at the production JavaScript code, it's not meant to be intelligible by human readers. The flow can be simply represented by TS Source Code > TS Transpilation into JS Code > Module/Dependency Build Steps into Production JS Code.
TL;DR TypeScript doesn't actually handle the module importing/exporting. During transpilation, it converts those statements into statements other module systems (node.js or SystemJS) can understand, which are in turn converted into production code for serving an Angular 2 application.
I have quite an annoying, but probably simple, problem that I just cannot figure out.
In a TypeScript file I have defined the following line:
import test1 = require('domReady');
This "domReady" module is defined in a main.js file that is loaded as the entry point for RequireJS. The definition is as followed:
require.config({
paths: {
'domReady': '../domReady',
}
However... in my TypeScript file I simply get a "cannot find module 'domReady'" and it is driving me insane, as I have double checked the pathing to the file and it is indeed in the correct location with the correct name. Additionally, I am fairly certain that the domReady.js file IS AMD compatible, so it should define an external module just fine! (domReady GitHub Link).
I seriously can't understand why the module can't be found in the import statement. Does anyone have any ideas to what the problem may be?
EDIT 1
The directory structure is as follows:
.
+--App
| +--main.js
| +--dashboard.js
+--domReady.js
The import statement takes place in the "dashboard.js" file, and the config for require.js happens in "main.js".
In order for TypeScript to find a module, you must actually provide module information to TypeScript.
TypeScript doesn’t yet support AMD-style paths configuration, it doesn’t ever use calls within your JavaScript code (like require.config()) to configure itself, and it won’t treat JavaScript files on disk as modules when compiling. So right now, you aren’t doing anything to actually give the compiler the information it needs to successfully process the import statement.
For your code to compile without error, you have to explicitly declare an ambient declaration for the module you’re importing within the compiler, in a separate d.ts file:
// in domReady.d.ts
declare module 'domReady' {
function domReady(callback: () => any): void;
export = domReady;
}
Then, include this d.ts in the list of files you pass to the compiler:
tsc domReady.d.ts App/main.ts App/dashboard.ts
Any other third party JavaScript code that you import also needs ambient declarations to compile successfully; DefinitelyTyped provides d.ts files for many of these.
I've had problems before when the path key and the directory name or file name are the same (in your case, domReady). Might not work, but worth giving it a quick try, i.e.
either
'domReadyModule': '../domReady',
require('domReadyModule')
or rename domReady.js to e.g. domReady-1.0.js and use
'domReady': '../domReady-1.0',
require('domReady')
If that doesn't work, I'd check the relative directories between main.js and the file that is doing the require, or else try importing another library in the same fashion, or finally compare with other libraries that you are importing successfully.
Good luck, hope you resolve the problem!