How to export symbols from modules compiled by Closure Compiler? - javascript

I have many js es6 modules compiled by Closure Compiler (Advanced level) in a single library file.
I failed to export some symbols using /** #export */ or using goog.exportSymbol('whatever', whatever) to uncompiled javascript in HTML pages .
How to do it?
Remarque: If I replace the es6 import/export by goog.require/goog.provide in all modules, it works and I can successfully use these symbols in HTML pages.

After further investigations, I found the solution.
Although loaded in the browser without any error in the console (except undefined whatever of course), my library was not executed. I simply moved the closure library ahead of the file stack to be compiled and my library was then properly executed by the browser with my symbols properly exported.
The 3 ways to export symbols are working in compiled es6 modules: /** #export */ whatever, goog.exportSymbol('whatever', whatever), window['whatever'] = whatever. The first 2 being a handy way for the third one.
Fort more details see No exported symbols with es6 modules library compiled by Closure Compiler

You can't use #export in a module, but goog.exportSymbol should work. Your other option is to manually export them:
window['whatever'] = whatever;

Related

Multiple TypeScripts Files Configuration

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
}
}

How to compile a TypeScript project to a single JS file so it is usable in browser

I have to write a Javascript SDK for a little project I am working on. To do that, I had thought of creating a TypeScript project and compiling it to a single Javascript file, so the users of my SDK could just inject that file in their web pages.
However, I just came to know that if I use import, and try to compile to a single file, then it only supports SystemJS.
So, how to compile a TypeScript project to a single JS file so it is usable in browser?
By usable in browser, I mean that if I create a class App in TypeScript, then I could do this in dev console:
var x = new App();
I have been at this for more than a hour now, and everything I have found seems to suggest that this is not possible.
Edit: This doesn't really answer my question. Like I said in the example, I need the functionality that if there is a class called App in my TypeScript project, it should be visible to the browser with the same name, so I could do var x = new App() in my dev console. (Or a user can do this in his JS file that he injects after injecting my SDK file). That answer is just telling how to create an outfile in SystemJS.
For this you can use webpack, it is a Node.JS utility that attempts to bundle Node.JS-like modules. Webpack doesn't automatically export modules to the global object, but generates (or attempts to generate) a function that replace the Node.JS's default require, which is used to execute the entry module and others, thus you can modify this function for exporting each module (or properties of each module) in the global object.
(In TypeScript, use the CommonJS module. Second, install and use the ts-loader plugin among with webpack, so you'll directly compile TypeScript from webpack.)
Maybe that applies to Webpack 2. For example, you modify the __webpack_require__ function. It is not inside the global object and thus you must interfere in the webpack's generated source code, at function __webpack_require__:
function __webpack_require__(moduleId) {
// [...] (After the `if (installedModules...) ...`)
/*
* You don't have access to the module name, so export each
* property to the browser's global object.
*/
var exports = module.exports;
for (var key in exports)
window[key] = exports[key];
}

Do I still need a module loader if I'm using ES6 modules?

Unfortunately my knowledge of JavaScript module loaders is still growing and I'm trying to understand their relationship to the new ES6 Modules. As far as I can tell using a module loader like CommonJS or RequireJS using ES5 compliant JavaScript really needed the use of an asynchronous module loader to increase performance and load only as needed using the respective module loader's syntax.
However looking at the ES6 module documentation and reading other information, it appears to me that module loading is natively supported via the import and export keywords. If this is the case am I correct that ES6 JS modules natively support asynchronous module loading and therefore I do not need to use an additional tool like CommonJS or RequireJS?
it appears to me that module loading is natively supported via the import and export keywords.
Not exactly. The import and export declarations only define the dependencies and the interface of each module. They allow to statically extract strings that name the required modules, nothing else.
If this is the case, do I not need to use an additional tool like CommonJS or RequireJS?
No. You still need to use a loader for ES6 modules, which resolves the names or paths or whatever from the imports to actual module files and loads them with an implementation-dependent method.
There are many tools or toolchains available, examples for the different solutions are
webpack: bundles everything into one large script
System.js: loads single modules dynamically and asynchronously (similar to what require.js does)
native: node.js and web browsers are still figuring out how to support module loading without additional libraries
babel transpilation: you can convert ES6 modules to AMD or CommonJS format and use the known tools like require.js for these
As far as my understanding goes, ES6 supports the syntax for defining and importing modules. The actual act of importing the modules that are required are a job of the infrastructure.
In modern browsers (as of 2016 that is) do not have built in functionality to support module loading and as such you will still need something like SystemJS to do the actual loading.
ES6 JavaScript Files are inherently treated as a module. if you define anything in a .js file, it will only be visible within that file (local scope ). what export does is, it exposes the classes / variables defined as export, visible to outside. then you can import it to a another module. There are other ways to define modules such as using Commonjs or AMD etc.. . Module loaders are required if you want to dynamically lazy load modules. ex. Systemjs is a such a Dynamic Module loader. it will fetch the physical module file from server dynamically when it is requested, and will prevent having multiple loads the same file. in SPA application in past had to load everything at the beginning to it to work. with dynamic module loaders now we can have only the files we need to do the intended job. hope this will help you.
https://github.com/systemjs/systemjs

ES2015 modules vs ES5 globals determined by 'export' keyword?

I'm reading about ES2015 modules and trying to make sure I understand this new feature.
Since there's nothing like "use strict", how does the browser determine that a .js file is an ES2015 module v.s. an ES5 file with a bunch of globals? Is it just by the presence of at least one "export" statement?
// This file is interpreted as ES5 with globals
function fun1() {...}
function fun1() {...}
// This file is interpreted as ES2015 module
function fun1() {...}
function fun1() {...}
export default function(){...}
Since there's nothing like "use strict", how does the browser determine that a .js file is an ES2015 module v.s. an ES5 file with a bunch of globals? Is it just by the presence of at least one "export" statement?
When you asked this question, it hadn't been decided, but it was a couple of years later: The type attribute is used:
<script type="module" src="./mod.js"></script>
You also need to include a path (not just src="mod.js"), unless you use a import map (which is relatively new as of this writing in July 2019, and I don't think any browser supports them natively yet).
If you use import or export in something that isn't a module, you'll get a syntax error.
In Node.js, which has its own CommonJS-like (CJS) module system, ECMAScript Modules (ESM) are signified in one of two ways:
By having "type": "module" in the nearest package.json, or
By giving the script the extension .mjs instead of .js.
(If the nearest package.json has "type": "module", you can still have a script that's a CJS module by giving it the extension .cjs.) Details here.
According to this page, modules can't go inside script tags, they must go inside module tags. So code loaded by script tags can't be treated as modules, and code loaded by module tags must be a module.

Typescript: how to switch off ambient declarations when using AMD

I'm just starting to migrate from internal modules to external modules in Typescript. Until now in my .d.ts files I have used the "declare var ..." part, because everything was used from the global namespace. But now with AMD I want my IDE to highlight errors if I use something without a corresponding import statement, so I do not want any ambiet declarations. How do I achieve that without modifying all my definition files manually (which would not work well with my tsd retrieval tool if I needed to do any updates)
Thanks!
Sorry : Can't be done without modifying the declaration files manually.

Categories

Resources