I'm having issues with the typescript compiler and external js libraries that use corresponding .d.ts definition files.
The issue is that these have to be referenced in the html code. With many nested libraries this is becoming an issue that I'm not sure how to solve this.
E.g.
I have my external modules like so:
Module 1 (js) - no dependencies
Module 2 (ts) - depends on Module 1
Module 3 (ts) - depends on Module 2
Application (ts) - depends on Module 3
In my html, because Module 1 is JavaScript, even though it has a definition file, I have to add:
<script src="Module1.min.js"></script>
Is there anyway round this as this is easily shooting up to 50+ script tags that need to be swapped out on a regular basis as dependencies change within the modules.
Have a look at System.js, it will load any required modules for you
Is there anyway round this as this is easily shooting up to 50+ script tags that need to be swapped out on a regular basis as dependencies change within the modules.
Use an external module system. Managing them manually is a nightmare : https://github.com/TypeStrong/atom-typescript/blob/master/docs/out.md
Recommend you just use --module commonjs with http://webpack.github.io/
Related
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.
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.
Currently my process for building is:
Write lots of typescript files with ES6 module syntax
Generate an index.ts which re-exports all modules from one point
Compile to CommonJS + System
Output Descriptor/Typing files
This results in an index.js file which re-exports all the internal files without the developer consuming it needing to know about it, as well as a lot of d.ts files which mirror the file structure.
Now this works, but if I were to take this approach to the browser I would need to webpack up all the js or it would be a http request nightmare pulling in all the individual files. Currently this library would be consumed as a dependency for other libraries, so it is not an end point for logic or anything it is a module/library.
Now the main question is with webpack I know I can load TS in and get a commonJS module out, however I cannot find any way to generate d.ts files with webpack. So is there a way for me to use webpack as the compiler/packager in this scenario and have an output my-lib.js and my-lib.d.ts rather than the current approach which yields lots of individual files.
== Extra Clarification on Use Case ==
Just to try and make sure everyone is on the same page here when I say it is a library that would be re-used what I mean is that this is something that would be loaded via npm or jspm or something as a module dependency for other modules.
So for example let us pretend jquery doesn't exist and I am going to create it but write it in typescript for other developers to consume in both JS and TS. Now typescript outputs both js files and d.ts files, the js files are to be used as you would expect, but the d.ts files explain to other typescript files what the types contained within the library are.
So assuming I have developed jquery in TS as listed above, I would then want to publish this output (be it created by webpack or tsc) on npm/jspm/bower etc. So then others can re-use this library in their own projects.
So webpack typically is used to package an "application" if you will, which contains logic and business concerns and is consumed directly as an entry point to a larger set of concerns. In this example it would be used as a compilation and packaging step for a library and would be consumed via var myLib = require("my-lib"); or similar.
Generating the .d.ts files is not related to webpack. With webpack you can use either ts-loader or awesome-typescript-loader. Both of them make use of tsconfig.json. What you need to do is to add declaration: true in your tsconfig.json.
I'd also suggest you to take a look at typescript-library-starter. You'll find how's set up there, including UMD bundle and type definitions :).
I am working on a javascript module/library that should work in 3 environments:
in node.js
in requirejs
when simply included using tags into the webpage. In this case the whole module should be hooked up under window.myModule
Do you have any suggestions as to how to write the structure of the library so that it works in all these environments?
EDIT: basically I mean some sort of wrapper code around the library so that I can call the file form any of those three methods and I'm fine...
This requirement and its solution is known as Universal Module Definition (UMD). It is currently a draft proposal. Background and current status is described in Addy Osmani - Writing Modular JavaScript With AMD, CommonJS & ES Harmony article. Look for "UMD" link pointing to various templates you can use.
Quite many other templates can be found on the web - UMD is the search keyword.
(did not find the final link myself yet :)
We're working on the same thing, I think.
And we have some success. We have library (we call it 'slib'), compiled to AMD js files. It does not depend on npm modules or browser, so it can be called from node and from browser.
1) To call it from node, we use requirejs:
file require.conf.js
module.exports = function(nodeRequire){
global.requirejs = require('requirejs');
requirejs.config({
baseUrl: __dirname+"/../web/slib/",
paths: {
slib: "."
},
nodeRequire: nodeRequire
});
}
In any other serverside (nodejs) file we add this line at the beginning
require("./require.conf")(require);
then we call slib's code by:
var Computation = requirejs("slib/Computation");
2) To call slib from browser, we just use requirejs. It handles everything fine.
3) We do not need to call slib from < script > directly.
For production, we use r.js to make a bundle js file with most of dependencies and use it on the page with one < script >. And this script downloads all other deps, if they are not included, using standard requirejs and it does not need requirejs (as far as I remember), it just works alone. This is very flexible for large projects: use requirejs while development, use r.js to bundle core files in production to speed up page load, use whole bundle if you need only one < script > without any other requests. r.js bundles all dependencies correctly, including old js libraries, which were commonly loading using only < script > and accessible by window.myOldLibrary using shim param on config.
It seems you can use browserfy to make some npm modules accessible from slib's code, but we did not tried yet.
Also, using requirejs on node's side, I think, can be simpler (why we need second 'requirejs' function together with node's one?) We just have not investigated it well, but this works.
In any slib module you can write
if (window)
window.module1 = this // or whatever
and it will be exported as old js lib upon load
What is the best way to do Dependency Management amongst CoffeeScript files if the resulting javascript files eventually need to be concatenated together for use on the client side?
For server side CoffeeScript I can just use the 'require' keyword to require other pieces of javascript. When this is compiled for client side apps to have the dependency tree, based on the requires, calculated and a concatenated stand alone javascript file produced. Is there anyway to do this in a generic way?
Another option is to use CoffeeToaster, which uses another approach than implementing AMD / CJS module patterns.
Take a look:
http://github.com/serpentem/coffee-toaster
Usually, for client-side packaging of JavaScript (and CSS), you want some sort of asset-packaging plugin. We use Jammit, but there are many other options: Sprockets, Django-Compress ... and more.
Villain (a CoffeeScript game engine for browsers) defines functions that do just that (dependency management and js concatenation).
The code for that is in these two files:
cake.coffee, see , determineDependencies(), wrapModule(), and bundleSources()
brequire.coffee, a require replacement for the browser, to use with wrapModule()
I use it here (see the 'bundle' Cake task).
Note: I just declare the 'main' module's build directory, and Villain scans my compiled JS files to build the dependency tree (starting with index.js), then creates a JavaScript bundle file containing Villain's require replacement and all my relevant code sorted and correctly wrapped.
Villain's author uses it in orona, a CoffeeScript game made with Villain.
For dependency management on client side, I use requirejs for javascript and coffeescript source. It'possible to use a requirejs plugin to load natively coffee files, but I prefer to "compile" into js.
requirejs also provide / work with r.js optimizer. It can be used to aggregate a set of js file into one and minified it. you don't have to specify the file to aggregate it is the dependency definition of each module require by your "main.js". (feature that match your request)
Something I like a lot with requirejs, it "promots" creating module and declare explicit dependencies.
# A.coffee
define(() ->
class A
constructor: (#c1, #c2) ->
#c2 ?= 1
m1 : () ->
"hello"
toString : () -> "#{#c1}#{#c2}"
)
# B.coffee
define(['A'], (A) ->
a = new A(33)
console.log(a, a.m1())
)
I've used (and I guess am still using) requirejs but I've started to find it to be fairly clumsy. A lot of my files end up having ~10-12 imports at the top which just take up a lot of space and don't look great.
For a new project I tried browserify. It's great! And if you use grunt (you should), you can make a watch task to browserify your code on change. grunt-browserify also provides the ability to do a coffeescript transform.
https://github.com/jmreidy/grunt-browserify
So your watch task in your Gruntfile.coffee would look something like:
watch:
files: [
"app/**/*.coffee"
]
tasks: "browserify"
browserify:
'build/app.js': ['app/**/*.coffee']
options:
transform: ['coffeeify']