Browserify standalone option doesn't directly wraps the code with specified name? - javascript

I tried to use gulp, browserify, watchify, and babelify to compile a standalone js module.
The options is like this:
const browserifyOpts = {
debug: true,
entries: path.join('src', 'index.js'),
standalone: 'Goo',
transform: [
'babelify',
],
};
My JS module is as follows:
export default class Goo {
constructor() {
this.a = 'hello';
}
}
It compiles fine. But when I include it in a script tag in a html file, the Goo module is wrapped inside window.Goo.default, that is, I cannot directly use:
new Goo();
but have to use:
new Goo.default();
Any idea where went wrong?
Thanks!

The easiest option would be to wrap this yourself by having your index.js file contain something like
module.exports = require('./main').default;
and move your current index.js to main.js. Then you'll be set.
As the other answer says, you can potentially use babel-plugin-add-module-exports, but that is not something I'd generally recommend because while it fixes this problem, it also introduces the possibility that you could accidentally write import/export pairs that work but are not spec-compliant.

By default, Babel 6 handles default module exports differently. If you want to use the legacy version, you can use the add-module-exports plugin.
Either add that into your .babelrc, or add it as an option in Babelify.
More information from this Github Issue:
has to do with the new way Babel handles default exports. if you dont
want to have to reference the default key, you need to use this
plugin: https://github.com/59naga/babel-plugin-add-module-exports

Related

Is there a way to assign the content of a file to a specific variable using webpack?

Is there a way to assign the file contents directly to a variable/method without webpack generating an additional variable?
Currently webpack does something like this:
index.js
import scss from './styles.scss';
console.log(scss);
output.js (actual)
const styles = '...';
console.log(styles);
I want webpack to return the content of the styles.scss file directly to the console.log() method in the output without adding styles variable.
output.js (expected)
console.log('...');
I'm currently using my custom loader for reading .scss files:
css-loader.js
const sass = require('sass');
module.exports = function(source) {
const result = sass.compile(this.resourcePath);
return 'export default `\n' + result.css + '\n`';
};
I know there is a way to do this using the require() function but it generates a lot of extra code coming from webpack. Is there any other better way to do it?
So it's really a scoping issue. So there's no real requirement to not wanting webpacks default behaviour.
Webpack has a few ways how to do that. See this other answer for how to define a global variable, which is Webpacks way of doing what you want to do.

How to package object prototypes in separate file

I have created a number of String.prototype functions which for maintainability I'd like to have in its own file. That is, I'd like to include the file in a javascript project and thus have all the String functions defined.
I could create a module that exports each function, but then I'd have to assign each function as its own String prototype, yes? Something like
var myStringFunctions = require("myStringFunctions");
String.prototype.func1 = myStringFunctions.func1;
Is there a way to include such a file so that the prototypes are defined as part of the inclusion?
Try it, you will see your code and using require("./myStringFunctions"); works just fine.
./myStringFunctions.js
String.prototype.func1 = function() {
return this.toUpperCase(this);
};
./index.js
require("./myStringFunctions");
console.log("foo".func1()); // FOO
If your JS is going to run in the browser, you can use JS modules with the import and export syntax if you use a module bundling build tool like Webpack: https://webpack.js.org/ .
If your JS is running in a Node.js environment, modules are supported: https://www.w3schools.com/nodejs/nodejs_modules.asp

0x800a1391 - JavaScript runtime error: 'module' is not defined

I am from a C++ background and have a day's experience in TypeScript. I am trying to implement a design (interface/class) that is split between multiple files. It is flashing runtime error : 0x800a1391 - JavaScript runtime error: 'module' is not defined
Info.ts
export = Test; <----------- 0x800a1391 - JavaScript runtime error: 'module' is not defined
namespace Test {
export class Info {
}
}
TestWrapper.ts
import { Info } from "./Info";
namespace Test {
class TestWrapper {
public GetInfo(): Info {
return this.m_info;
}
}
}
Am I using something in the wrong way?
A guess:
export = Something
Compiles to something like
module.exports = Something
module.exports is a construct of the so-called "commonjs module system", something that is not available in browsers, but in node.js. So if you run the generated code in a browser through a direct <script> import, it will bring an error like that. TS transpiles imports and exports to module systems that can be specified in the tsconfig.json, but TS itself is not responsible for implementing the module system.
What to do?
If this code is really supposed to run in the browser, you could opt on one of the 2 following options:
Run the code through a module bundler (webpack), which will generate a browser compatible "bundle" (a big JS) concatenating all your files. Webpack has ways of including typescript as a plugin, so that you dont need to make your "build" a 2-step process; Module bundlers are usually fairly complicated.
Make your files module-less (which means: no top-levem imports or exports) and import them through vanilla <script> tags.
Am I using something in the wrong way?
While namespaces are commonly used in other languages, they do not exist in JavaScript, so the typescript version is just some rarely used way to add proper typing to some things (I never used it yet). In your case you actually don't need a namespace. Just:
export default class Info { /*...*/ }
Then you can
import Info from ".Info";
export default class TestWrapper { /*...*/ }
PS: That said, I actually don't know how to make this work with namespaces to resolve the error
It looks like you are trying to mix modules and namespaces. I would suggest reading Namespaces and Modules.
You don't need to to export at the top level using export = Test, you also shouldn't import { Info } from "./Info"; because Info is part of the Test namespace which TestWrapper is a member of.
If you want to go the namespace route, consider:
Info.ts
namespace Test {
export class Info {}
}
TestWrapper.ts
namespace Test {
export class TestWrapper {
m_info: Info;
public GetInfo(): Info {
return this.m_info;
}
}
}
Consumer.ts
console.log(Test.TestWrapper);
Compile this using:
tsc --outFile foo.js Info.ts TestWrapper.ts Consumer.ts
Then run using:
node foo.js
Which prints:
[Function: TestWrapper]

How should I define a global TypeScript variable in a definition file so that it can be imported?

I have an external JS library with a global parameter:
function Thing() { ... }
...
var thing = new Thing();
There is a TypeScript definition file, so in thing.d.ts:
declare var thing: ThingStatic;
export default thing;
export interface ThingStatic {
functionOnThing(): ThingFoo;
}
export interface ThingFoo {
... and so on
Then I import this into my own TS files with:
import thing from 'thing';
import {ThingFoo} from 'thing';
...
const x:ThingFoo = thing.functionOnThing();
The problem is that transpiles to:
const thing_1 = require("thing");
...
thing_1.default.functionOnThing();
Which throws an error. I've asked about that in another question, and the suggestion is to use:
import * as thing from 'thing';
That doesn't fix it - it gives me thing.default in TS but then that's undefined once transpiled to JS.
I think there's something wrong with thing.d.ts - there must be a way to define a typed global parameter that can be imported.
How should I write thing.d.ts so that it represents the JS correctly and doesn't transpile to include default or other properties not actually present?
If the only way to use that library is by accessing its globals (as opposed to importing it as node module or amd or umd module), then the easiest way to go is have a declaration file without any exports at top level. Just declaring a variable is enough. To use it, you have to include that declaration file when compiling your typescript code, either by adding it to files or include in tsconfig.json, or directly on command line. You also have to include the library with a <script> tag at runtime.
Example: thing.d.ts
declare var thing: ThingStatic;
declare interface ThingStatic {
functionOnThing(): ThingFoo;
}
declare interface ThingFoo {
}
test-thing.ts
const x:ThingFoo = thing.functionOnThing();
can be compiled together
./node_modules/.bin/tsc test-thing.ts thing.d.ts
the result in test-thing.js:
var x = thing.functionOnThing();
See also this question about ambient declarations.
Note: there are module loaders out there that allow using global libraries as if they were modules, so it's possible to use import statement instead of <script> tag, but how to configure these module loaders to do that is another, more complicated question.

Can I use an ES6/2015 module import to set a reference in 'global' scope?

I have this situation where I am trying to import an existing library, which I'll call troublesome (using Webpack/Babel FWIW) and it has a global reference to jQuery in it which i am trying to resolve using module syntax.
I have successfully imported jquery into the 'local' scope of a module, via:
import jQuery from 'jquery'
so I tried:
import jQuery from 'jquery'
import 'troublesome'
but perhaps not surprisingly, I get something like jQuery is not a function kicked back from troublesome.js
I have tried this as well:
System.import('jquery')
.then(jQuery => {
window.jQuery = jQuery
})
import 'troublesome'
but, it turns out that System.import is part of the, so-called, 'module-loader' spec, which was pulled from the es6/2015 spec, so it isn't provided by Babel. There is a poly-fill, but Webpack wouldn't be able to manage dynamic imports accomplished via calls to System.import anyway.
but... if I call out the script files in index.html like so:
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/troublesome/troublesome.js"></script>
<script src="the-rest-of-my-js.js"></script>
the reference to jQuery is resolved in troublesome.js and things are good,
but I would prefer to avoid the script tag route as webpack doesn't manage those.
Can anyone recommend a decent strategy for dealing with scenarios like this?
update
with some guidance from #TN1ck, I was eventually able to identify one Webpack-centric solution, using the imports-loader
The configuration for this solution looks something like this:
//...
module: {
loaders: [
//...
{
test: require.resolve('troublesome'),
loader: "imports?jQuery=jquery,$=jquery"
}
]
}
Shimming modules is the way to go: http://webpack.github.io/docs/shimming-modules.html
I quote from the page:
plugin ProvidePlugin
This plugin makes a module available as variable in every module. The module is required only if you use the variable.
Example: Make $ and jQuery available in every module without writing require("jquery").
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
To use this with your webpack-config just add this object to an array called plugins in the config:
// the plugins we want to use
var plugins = [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
];
// this is your webpack-config
module.exports = {
entry: ...,
output: ...,
module: ...,
plugins: plugins
}
For es6/2015 I done the following.
import {jsdom} from 'jsdom';
import jQuery from 'jquery';
var window = jsdom(undefined, {}).defaultView;
var $ = jQuery(window);
//window.jQuery = $; //probably not needed but it's there if something wrong
//window.$ = $;//probably not needed but it's there if something wrong
Then you can use it as normal
var text = $('<div />').text('hello world!!!').text();
console.log(text);
Hope this helps.
Importing jQuery into your module does not make it available for 'troublesome'. Instead, you could create a thin wrapper module for 'troublesome' that provides jQuery and any other required "globals".
troublesome-module.js:
// Bring jQuery into scope for troublesome.
import jQuery from 'jquery';
// Import any other 'troublesome'-assumed globals.
// Paste or have build tool interpolate existing troublesome.js code here.
Then in your code you should be able to
import 'troublesome-module';
I've had a similar issue using jspm and dygraphs. The way i solved it was to use dynamic loading like you attempted using System.import but the important part was to chain-load each consecutive "part" using System.import again inside the promise onfulfillment handler (then) after setting the global namespace variable. In my scenario I actually had to have several import steps separated between then handlers.
The reason it didn't work with jspm, and probably why it didn't work for you as well is that the import ... from ... syntax is evaluated before any code, and definitely before System.import which of async.
In your case it could be as simple as:
import jQuery from 'jquery';
window.jQuery = jQuery;
System.import('troublesome').then(troublesome => {
// Do something with it...
});
Also note that the System module loader recommendation has been left out of the final ES6 specification, and a new loader spec is being drafted.
run npm install import-loader.
replace import 'troublesome' with import 'imports?jQuery=jquery,$=jquery!troublesome.
In my opinion, this is the simplest solution to your question. It is similar to the answer you wrote in your question #TN1ck, but without altering your webpack config. For more reading, see: https://github.com/webpack/imports-loader
Shimming is fine and there are various ways of resolving this, but as per my answer here, the simplest is actually just to revert to using require for the loading of the library that requires the global dependency - then just make sure your window. assignment is before that require statement, and they are both after your other imports, and your ordering should remain as intended. The issue is caused by Babel hoisting imports such that they all get executed before any other code.

Categories