webpack expose object as global - javascript

I have a coffeeScript file 'app.coffee'
root = exports ? this
class DefipsyApplication extends Marionette.Application
user_is_superuser: () ->
app_config.is_superuser
app = new DefipsyApplication
app.addRegions {
modalRegion: '#modal-region'
}
unless root.App
root.App = app
I want to expose the App function in the browser
so here is my webpack config
module.exports = {
entry: {
app: './coffee/app.coffee',
},
output: {
path: './build/',
filename: '[name].bundle.js',
libraryTarget: "var",
library: ["MyProject", "[name]"],
},
module: {
loaders: [
{ test: './coffee/app.coffee', loaders: ['expose?App', 'coffee'] },
]
},
};
SO whene I test in browser I found that webpack was exposed my object to the browser, but this object is englobed by an other Object
so to call my method I should do
App.App
I Want to expose my function without this global var
I need to access directely with
App
here is a scree to show the object structur

In general, to expose Global Variables, Methods, and Modules in JavaScript, you can use several methods.
You could add it manually to the browser's window object by
window.app = app
or build a new object by
window.app = { func1, func2,...}
The expose-loader is an addon to Webpack, which adds modules to the global object. If you are using yarn for dependency management, you can install the expose-loader package via
yarn add expose-loader --dev
Alternatively, you can use the npm installer (Node Package Manager) via
npm i expose-loader --save
You can expose any function with
require("expose-loader?package!functionName");
If you need more information, have a look at my post here

Related

Unable to bundle a Web Worker to be imported like an NPM package

My goal is to be able to publish a Web Worker NPM package which can be imported normally (import MyPkg from 'my-pkg') without requiring the user to import it with worker-loader (inline or otherwise)
To accomplish this, I've tried using a Babel build script as well as Webpack with worker-loader.
In the following examples there are two projects: the Web Worker package ("Package") which is npm linked to a test application ("App").
The Package is split into two files: entry.webpack.js and index.worker.js. The entry, when built and moved to /dist is designated as the main file in the package.json, and it currently looks like this:
entry.webpack.js
var MyPkg = require('worker-loader!./index.worker.js')
module.exports = MyPkg
index.worker.js
// This is just example code. It doesn't really matter
// what this code does so long as it ends up being run
// as a Web Worker.
var selfRef = self;
function ExampleWorker () {
console.log('Running Worker...');
setTimeout(function () {
// wait 10 seconds then post a message
selfRef.postMessage({foo: "bar"});
}, 10000)
}
module.exports = ExampleWorker
I then bundle the Package with Webpack:
package.json
"build": "rm -rf dist/*.* && webpack --progress"
webpack.config.js
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: __dirname + '/src/entry.webpack.js',
output: {
filename: 'bundle.js',
path: __dirname + '/dist'
},
optimization: {
minimize: false
}
}
This generates two files: bundle.js and a Web Worker file as a hash: [hash].worker.js with the code we want evaluated in it. They key part in this, though, is that because we used worker-loader inline to import, the webpack compiled output looks something like:
module.exports = function() {
return new Worker(__webpack_require__.p + "53dc9610ebc22e0dddef.worker.js");
};
Finally, the App should be able to import it and use it like this:
App.js
import MyPkg from 'my-pkg'
// logging MyPkg here produces `{}`
const worker = new MyPkg()
// That throws an Error:
// Uncaught TypeError: _my_pkg__WEBPACK_IMPORTED_MODULE_4___default.a is not a constructor
worker.onmessage = event => {
// this is where we'd receive our message from the web worker
}
However, you can get it to work if, in the App itself you import the worker build like this:
import MyPkg from 'my-pkg/dist/53dc9610ebc22e0dddef.worker.js'
But, it's a requirement of the package to:
A) NOT require applications using the package to have to explicitly install worker-loader and
B) not have to reference the my-pkg/dist/[hash].worker.js explicitly.
I've tried also designating the built [hash].worker.js' as themain` in package.json but that doesn't work either.
Edit 1: I forgot to mention that I'm basing all of this off of how react-pdf does it. If you take a look in /src/entry.webpack.js and follow how it works throughout the package you'll see a few similarities.
you could try worker-loader with option:
{
test: /\.worker\.js$/,
use: {
loader: 'worker-loader',
options: {
name: '[name].[hash:8].js',
// notice here
inline: true,
fallback: false
}
}
},

Webpack authoring libraries import errors with TypeError: xxx is not a constructor

I am having trouble importing a javascript package I wrote (and published to npm) into a new project.
I created an ES6 package and bundled it using webpack. Within the package I can import the library using the bundled file using a script tag,
<script src='../dist/awesome-table-dist.js'></script>
then new up the class like so:
let awesomeTable = new AwesomeTable.AwesomeTable('record');
Works like a charm! awesome-table
I pushed the package up to npm, now I want to bring it into a new project like so:
import AwesomeTable from '#iannazzi/awesome-table'
then
let awesomeTable = new AwesomeTable('record');
which is erroring:
TypeError: _iannazzi_awesome_table__WEBPACK_IMPORTED_MODULE_0___default.a is not a constructor
Now that I am importing it to a new project I have tried a variety of ways new up the class, but seem completely stuck.
I can use the package by including the script again, but obviously I want to import so I can re-package:
<script src='node_modules/#iannazzi/awesome-table/dist/awesome-table-dist.js'></script>
Here is the webpack configuration for the package:
var path = require('path');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const SRC_DIR = path.resolve(__dirname,'../src');
const DIST_DIR = path.resolve(__dirname, '../dist');
module.exports = {
entry: {
app: SRC_DIR + '/table/AwesomeTable.js'
},
output: {
path: DIST_DIR,
filename: 'awesome-table-dist.js',
library: 'AwesomeTable',
// libraryTarget: 'window'
},
module: {
rules: [
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [
new ExtractTextPlugin('style.css')
//if you want to pass in options, you can do so:
//new ExtractTextPlugin({
// filename: 'style.css'
//})
]
};
First of all, you need to expose your lib, you can do that via defining library & libraryTarget: 'umd' properties.
Second, it is not recommended to publish lib as a bundle. Think about that scenario, You lib is composed from several parts, but not all of them are mandatory. When you ship your lib a a bundle, you are forcing your users to download redundant extra code.
The best practice today is to transpile your code via Babel to be compatible es5 and commonjs as a module system.
Recently, there is a trend to ship es6 modules in a separate folder, so that whenever your users will use bundles that supports tree-shaking, they will able to use it.

Vue & Webpack: Global development and production variables

I'm using vue-cli to build my web app. My app uses an api in a number of places like so:
axios.post(API + '/sign-up', data).then(res => {
// do stuff
});
The API variable is a constant containing the beginning of the address, e.g., https://example.com.
How would I detect whether this is a dev or prod build and set that variable accordingly? For now, I have a script tag in my index.html document and I am manually changing it for dev and prod:
<script>var API = 'https://example.com'</script>
Is there a better way to handle this?
If you're using the vue-cli webpack template, within the config folder you'll see two files: dev.env.js and prod.env.js.
Both files contain an object that is globally available throughout your Vue app:
dev.env.js
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
prod.env.js
module.exports = {
NODE_ENV: '"production"'
}
Note that string values require the nested single and double quotes. You can add your own variables like so:
dev.env.js
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API: '"http://localhost:8000"'
})
prod.env.js
module.exports = {
NODE_ENV: '"production"',
API: '"https://api.example.com"'
}
Now the variable can be accessed within any Vue method from the process.env object:
process.env.API

Using expose-loader with webpack 2 to load a module

I have jwplayer in my lib/ folder because no node_module exists. I tried to use expose-loader in order to be able to import it. In my webpack, I have the following basic setup in order to get this to work:
const path = require('path');
module.exports = {
// ...
module: {
rules: [{
test: /\.jwplayer\.js$/,
use: {
loader: 'expose-loader',
options: 'jwplayer', // have also tried `options: { name: 'jwplayer' }`
},
}]
},
resolve: {
alias: {
jwplayer: path.join(__dirname, './lib/jwplayer-7.7.4/jwplayer.js'),
}
},
externals: {
window: 'Window',
}
};
The strange thing is, jwplayer is exposed on the window object, but it is not available as a module.
import jwplayer from 'jwplayer';
console.log(jwplayer); // Object {} (not jwplayer)
console.log(window.jwplayer); // function jwplayer() {}
Am I loading it incorrectly? How should I load in jwplayer with webpack 2?
That's not how you use the expose loader. The expose loader tells to webpack to expose something to the global context when the bundle is loaded. My understanding is that you want to use jwplayer inside the bundle itself.
You can use the script-loader, that's how I usually import scripts (analytics, for instance)
Actually you can use
externals: ['jwplayer'],
Because externals is for passing global variables inside the bundle to be able to use them as a dependency and then you can import your library as any other
import jwplayer from 'jwplayer';
webpack documentation

Conditionally importing npm modules?

My project structure is as follows:
- workspace
- customPackage
- customIndex.js
- myProject
- index.js
- myProject2
- index.js
In dev, I want to import the package from my local workspace like following:
//index.js
import something from '../customePackage/customIndex.js'
where as in production I need the import to work from npm modules like the following:
//index.js
import something from 'customPackage';
The purpose being to be able to use the local changes in the package(without going through the commit cycle). Finally after testing the package can be pushed and used normally via npm package.
How to do this in an efficient way without having to make code changes every time?
You can use Resolve#alias with Webpack:
resolve: {
alias: {
"customPackage": process.env.NODE_ENV === "production" ?
"customPackage" :
path.resolve(__dirname, "../customePackage/customIndex.js")
}
}
Then in your source, you only need to do:
import something from 'customPackage';
And it will point to the correct package. Obviously you need to set the NODE_ENV environment variable, or change that depending on your build environment.
if you are already using webpack you can make two different entry points:
entry: {
bundle: './Scripts/index.tsx',
bundle2: './Scripts/index2.tsx'
},
output: {
publicPath: "/js/",
path: path.join(__dirname, '/wwwroot/js/'),
filename: '[name].js'
},
then in index you import main module and in index2 import your test module. So you will have different bundle files bundle.js and bundle2.js.

Categories

Resources