Is there anyway to have javascript code defined in 2 or more separate files to run in the same IIFE? I'm open to using build tools like gulp to accomplish this.
It just seems like such a mundane problem. I want my code to be organized and separated into their own files (distinct knockout view models, by the way). But I want them all to run in the same function and not pollute global.
The modern way to do this is to use modules rather than try to put everything into an IIFE. Right now, using modules means using a module bundler like RequireJS, SystemJS, Webpack, Browserify, etc. In the medium-term future, you'll be able to use ES2015+ modules directly in the browser if you like, or again use bundlers to bundle them into a single file. (You can use ES2015+ module syntax today with transpilers like Babel, but you still need a bundler.)
You've mentioned you're using RequireJS at the moment but not using its define functionality. Just for the purposes of illustration, here's roughly how you'd define a module (I'm not a fan of Require's syntax, but you can use Babel to transpile ES2015 syntax to it):
Say I have a module that defines a KO component:
define("my-component", ["ko"], function(ko) {
// ...define the component...
// Return it
return MyComponent;
});
That:
Says the module is called my-component (module names are optional, so for instance the top-level module of an app needn't have a name)
Says it depends on the module ko (which provides Knockout); note how that dependency is then provided as an argument to the callback that you use to define your module
Returns MyComponent as the top-level thing defined by the module
Then in my app:
define(["my-component", "another-component"], function(MyComponent, AnotherComponent) {
// use MyComponent and AnotherComponent here
});
You can also have modules that group together other modules that are commonly used in groups, to simplify things.
In ES2015+ syntax instead:
my-component.js:
import ko from "./ko";
// ...define MyComponent...
export default MyComponent;
app.js:
import MyComponent from "./my-component";
import AnotherComponent from "./another-component";
// ...use them...
Obviously, both examples are very simplified and there's a lot more you can do.
This is two separate issues.
Can an IIFE span multiple files - NO!
Can classes/functions/etc in multiple files NOT polute global scope - YES!
file1
var myNS = {};
file2
myNS.MyViewModel = function(){ ... }
file3
myNS.OtherViewModel = function() { ... }
As a very simplistic example, of which there are 101 ways to achieve the same.
Related
The question
I have an ES6 module that uses another bunch of ES6 modules.
E.g
import { a,b,c } from 'whatever'
import { x,y,x } from 'whatever2'
export default function(){
// do the awesome thing
}
Ie.
// wrapped.js
let a = function(){ /* the imported thing */ }
let b = "something else imported"
// ... etc
export default function(){
// do the awesome thing
}
Note that the imported modules also have further ES6 dependencies.
Is there any module bundler that can wrap this up into a single file with just the default export. I.e, everything is still ES6.
why?
I have a monorepo with lots of shared dependencies. However I need to use some of the code in a legacy system so want to avoid manually copying every single file that I need into a new repository.
I can't use lerna because I can't publish all the dependencies.
I can't get webpack to produce the right format for importing the module - see this SO question
I had a quick look at rollup but that does not seem to allow ES6 as a target for bundling similar to webpack.
Here comes silly and simple question:
I am new to this whole webpack tools.
I have been trying to build a simple web-app using typescript and webpack. In the old days, I created typescript and compile them without bundling them.
With webpack, I already installed necessary loaders, typescript, and jQuery.
The problem is, I have 3 typescript files:
main.ts -> imports all assets (images, css) and other typescripts
functions.ts -> consist all of my custom functions/modules
ui-controller.ts
in functions.ts I always created namespaces such as:
module Functions{
export module StringTools{
export function IsEmpty(): boolean{
//some code
}
}
}
in the browser, I knew that the snippet code above will be called, but it is not recognized in the main.ts (in the run time) even thou I already import it.
This is how I import it in main.ts:
import '.src/functions'
Any suggestion how I can resolve this?
Typescript module keyword is confusing, and in version 1.5 it was indeed changed to namespace to better reflect it's meaning. Look here.
Namespaces are also called internal modules. They are meant to be used when your files are evaluated at the global scope. You can use typescript playground to see how namespaces are transpiled. The point is - namespaces are not modules.
Webpack however, does not evaluate files in the global scope, it evaluates them inside a function in order to provide real module behavior.
So what does make your typescript file into a module? the keywords export and import (but not inside a namespace like in your example).
Typescript will see those keywords, and will transpile them into commonjs\AMD\es6 require or define statements, according to your configuration in tsconfig.json. Now you have "real" modules. Then it's Webpack's job to do something (lots of info about that online) with those modules so that they will work in the browser where you don't have modules, but that part is not related to typescript.
TL;DR -
So how would you do this in Webpack?
/* functions.ts */
export function IsEmpty(): boolean{
//some code
}
and
/* main.ts */
import {isEmpty} from './functions';
if you want better code organisation as suggested by your use of module StringTools, just split into different files. You can read more about es6 import syntax for more info.
The first part of this answer is that your main module (which has been replaced with the namespace keyword) is not exported... so there are no exported members for your functions file.
export namespace Functions { //...
Part two of the answer is that if you are using modules, you don't need to use namespaces as the module is enclosed within its own scope.
Working version of the file as you designed it, I'd say drop the wrapping namespace:
functions.ts
export namespace StringTools{
export function IsEmpty(): boolean{
//some code
}
}
Is there is a performance or behavioural difference between importing from an index file or importing individual modules?
For example, with an index file (#/modules/user) of...
import routes from './routes'
import controller from './controller'
const user = {
routes,
controller
}
export default user
If I import just the routes from that file...
import user from '#/modules/user'
const routes = Router()
routes.use('/user', user.routes)
Is this any different to just importing the routes individually from their own file (#/modules/user/routes)? Does the controller get imported as it's in the user object?
import userRoutes from '#/modules/user/routes'
const routes = Router()
routes.use('/user', userRoutes)
There are currently no native ES modules in Node.js. The actual difference depends on the toolchain. When the application is built with Webpack/Rollup (a minifier is potentially needed too) and configured to use ES modules internally, tree-shaking can be applied. This is best case scenario.
This would be a case for tree-shaking if there were a reexporting module:
import routes from './routes'
import controller from './controller'
export {
routes,
controller
}
And it was imported like
import { routes } from '#/modules/user'
However, the cases in the original post are different. In one case, once user constant is imported, it's impossible to remove unused controllers property from it with tree-shaking. In another case, #/modules/user/controller module remains unused and doesn't need tree-shaking. It will be ignored even if the application is configured to use CommonJS modules.
So yes, it's possible for ES modules to import only modules that are in use, and this heavily depends on actual code and project configuration.
Client-side applications primarily benefit from tree-shaking because it affects bundle size. This concern shouldn't be taken into account in server-side Node.js application - unless unused module imports massive amount of third-party modules that aren't used anywhere else.
Currently you can only use the import syntax using transpilers(converters from one syntax to another) as it is not yet natively supported by engines. Lets take babel for example.
Babel converts the import to CommonJS style code that can work within Node.js. While the syntax is conformant to ES6, the implementation is not.
Babel converts the syntax into CommonJS, evaluating the imported code before determining what is being imported. With ES6, the imports and exports are determined before the code is evaluated.
With future support of es6 imports inside node, importing a specific function inside the code will only return that exported function, without evaluating the whole script in the targeted file.
Currently they work the same since transpilers convert them to traditional node require syntax.
However, you can use webpack Treeshaking to remove unused code from the output file, this way they are almost identical in behavior.
The # syntax
The # syntax depends on the module loader or module bundler. The module loader is not part of the ECMAScript spec. You most likely have something like babel-plugin-root-import in your webpack/babel config.
Basically it means from the root of the project.. it avoids having to write things like import Component from '../../../../components/component'. in this case, it has nothing to do with partial imports.
Let's say I have an old school 3rd party javascript library in the following format, which effectively creates the global SomeModule:
SomeModule.js
(function() {
this.SomeModule = ...
})();
I'd rather avoid the global and use ES6 syntax to import SomeModule into just the files where I need it. I figure I can wrap the 3rd party library using something like this:
SomeModuleES6.js
var dummy;
(function(){
require("SomeModule.js");
}.bind(dummy))();
export default dummy.SomeModule;
Files where SomeModule is needed
import SomeModule from "SomeModuleES6";
There's gotta be a better way though. Is there a standard practice for this situation? How would this work through WebPack and package.json? Should I just fork the 3rd party library and use my own updated version instead?
Say I have two React Components, A and B, where B depends upon (makes use of) A. Say that A is in a.js and B is in b.js .
Is there a way in React to safely resolve a dependency from B to A? Thereby guaranteeing that regardless of what order I actually include a.js and b.js, things will still resolve correctly?
I believe that the Google Closure compiler effectively fakes a dependency system for you in both development and production mode. This makes the order that different code is included in the source irrelevant; is there something like this for React?
Note: the answer below is very outdated.
I stopped using RequireJS a long time ago due to the fact that it gets rather slow in development because doesn’t bundle JS in a single file in development.
These days I’m mostly using Webpack.
You may also consider other bundlers such as Browserify and Rollup.
I use ES modules as the module system, transpiled by Babel into CommonJS. Eventually I plan to swich to ES modules completely as more bundlers with its first-class support, such as Rollup and Webpack 2, become mainstream.
Old Answer
To my knowledge, React itself doesn't provide any dependency management system; if you need one, you must use some existing tool that fits your workflow.
Personally, I'm happy using RequireJS with AMD sugar syntax so my modules look like this:
/** #jsx React.DOM */
/* jshint trailing:false, quotmark:false, newcap:false */
define(function (require) {
'use strict';
var React = require('react'),
_ = require('underscore'),
JoinWidget = require('common/views/join_widget');
var LoginWidget = React.createClass({
// ...
});
});
Each React view, as any other class, gets its own file.
This is fine in development environment but I don't want to load scripts on the fly in production, so I use grunt-requirejs task with almond to concatenate files in proper order so they are always loaded synchronously in a single file.