This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a webpack bundle, every import/require you do, is managed by webpack using their internal require function. That means you cannot use anymore the original NodeJS global require if you are inside a webpack bundle.
See also: Exporting from outside webpack bundle
There's a workaround provided by webpack actually:
A variable called __non_webpack_require__ which is the original require so, in your code, you can do this:
const internalModule = require('internal/module');
// or import internalModule from 'internal/module'; in the ES6 way
const externalModule = __non_webpack_require__('external/module');
If you are using TypeScript, you can add this line in your global.d.ts file to avoid syntax errors:
declare const __non_webpack_require__: NodeRequireFunction;
Fact 1: Actually after the build, you can see how your commonly used require (webpack's) has been renamed to __webpack_require__, and that __non_webpack_require__ has been preserved as the original require.
Fact 2: Webpack also use the original require to import modules from the system (not bundled with the code), such as net, events, fs, etc.
Related
My question is how do I achieve importing a standalone module obtained with browserify, inside another javascript file. Here are some specific details:
I created a standalone module with browserify like this:
browserify module.js --s module_name > output.js
The file module.js contains at the end the line
module.exports = module_name;
Inside a file "use_module.js", I can now use the module module_name, with this code in some html file:
<sctipt src="output.js">
<sctipt src="use_module.js">
But what I would like is to specify just the script "use_module.js" in my html file, and somehow import "output.js" inside "use_module.js" directly in the javascript code. I tried two different approaches :
I tried javascript import module_name from './output.js' inside use_module.js, but then it cannot find the module module_name. Somehow I'm not sure module.exports is the same as export, neither with what browserify does with my module.exports anyway. (I'm very confused about all that).
I tried with require(./output.js) and then re-using browserify, but I then get a lot of weird errors with browserify, about tons of missing modules. Maybe I should specify some module informations in a file next to "output.js", that browserify can process, but I don't know really.
I understood what was failing in both approaches :
As I thought, import works only with export and not modules.exports. Also I read that browserify cannot work with the import/export syntax which is still too new.
More interestingly : a browserified file contains plenty of require(...) calls, which refers to a function created by browserify, and not to the CommonJS require keyword. Of course browserify does not know the difference, and thus look for tons a module to import a second time. All that needs to be done is to rename the word "require" into something else, and browserify can be applied a second time...
I however do not find my answer completely satisfactory : there must be a proper way to do this...
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 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!
This is a problem I faced more than one. Here is an example file structure:
app.js
folder
-- index.js
-- module1.js
-- module2.js
From app.js, the entry point of my application, I require folder/index.js. This file itself is just a loader who requires all the other files in the directory. module1.js and module2.js both define some methods I want to use eventually. They are never directly required by app.js since index.js takes care of that and adds common helper utilities and applies some transformations to these modules.
All works well if I then use some methods defined in those files from app.js after I required them. But the problem comes when a method defined in module2.js wants to use a method defined in method1.js (or vice-versa). Sometimes it will work, sometimes not (in folders with multiple files I didn't get to find out when precisely it works and when it doesn't yet).
If, from module2.js I require ./index.js and then use methods in it defined by module1.js, I sometimes get Cannot read property 'myMethod' of undefined. I assume it has to do with the order the modules are required by index.js, but I can't seem to find a solution to this common problem (other than duplicating code or not using methods of these other modules).
Is there a common solution to this problem? I tried doing something like this :
var modules = require(./index.js);
exports.myMethod = function() {
if(!modules.module1 || !modules.module1.myOtherMethod) {
modules = require('./index.js');
}
modules.module1.myOtherMethod();
};
But it doesn't to do anything, modules.module1 is still undefined.
It just sounds like module should require module2. That's the solution to module1 needing to call methods in module2.
If you're worried about many calls to require, don't. That's standard practice in every programming language I've used (in particular look at the import statements in any Java program. Java is verbose, I know, but this is correct practice.)
It's definitely bad practice to look at code in one module and have no idea where anything comes from, because the require statements are missing.
You have a circular dependency problem. Try moving some of the common functions to a third file and have module1 and module2 require it, or make sure that one of them requires the other in one way only.
Never ever require a file that requires the current file back.
If I have a node.js application that has hundreds of files that reference a module (say underscore) and I want to replace that module with another (say lodash) then the obvious way to do this substitution would be a global name replace and switch out the modules in the package.json file.
Is there anyway to just change the module that a name refers to so that when node.js sees require('moduleA') it actually loads 'moduleB' instead? Now I know that this would cause naming hell because anyone working on the project would see require('moduleA') and wouldn't know that the real module being loaded was 'moduleB' so ultimately you'd probably want to go with the first solution. The use case that I'm thinking of is if you want to try a few alternatives for API compatible modules to measure your application's performance (for example) with each module.
If this is an on-going thing and you want to maintain the ability to programmatically switch between the options often, such as in tests:
Instead of using require("underscore"); throughout your codebase, require a local file instead like require("./lib/underscore");, and have that file conditionally re-export underscore or a different library:
if (global.USE_LODASH) {
module.exports = require("lodash");
} else {
module.exports = require("underscore");
}
If this is a one-off thing to try out an alternative library before making the decision to switch, and you want to do this test quickly first without find-and-replacing in all of your files:
Go inside your node_modules folder, delete or rename the underscore folder, and make a symlink named underscore to the replacement module's folder. I don't recommend this as a long-term solution: running npm install again will likely undo this hack, and most projects choose to avoid checking the node_modules folder into their source repository.
Try to use mock-require module.