ES6 - How to modify a variable in other modules - javascript

in module Global.js:
export let transLog = [];
in main:
import * as G from "./Global";
G.transLog = [];
I got a error:
app.js?c99e:19 Uncaught TypeError: Cannot set property q of #<Object> which has only a getter
at eval (app.js?c99e:19)
at Object.<anonymous> (bootstrap e92860b74eb6dd40b159:62)
at __webpack_require__ (bootstrap e92860b74eb6dd40b159:19)
at bootstrap e92860b74eb6dd40b159:62
at bootstrap e92860b74eb6dd40b159:62
webpack config:
const webpack = require('webpack');
module.exports = {
entry: './js/app.js',
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: "[file].map"
})
],
output: {
filename: './dist/app.js'
},
devtool: 'source-map'
};
So, how to modify a variable in other modules?

You cannot assign new values to exported variables, only the module itself can do that (and when it does, it can be pretty confusing, so I'd recommend to avoid this).
You can mutate exported objects though, e.g. G.translog.push(…) or G.translog.length = 0.

Related

Webpack multi files with dependencies

I use Webpack to create a library to use in all of my projects (both client and server).
This is my webpack.config.js:
const path = require('path');
const libraryName = 'shared';
const config = {
mode: 'development',
entry: {
utils: `${__dirname}/src/shared/utils.js`,
enums: `${__dirname}/src/shared/enums.js`
},
devtool: 'inline-source-map',
output: {
path: `${__dirname}/dist`,
filename: '[name].js',
library: libraryName,
libraryTarget: 'umd',
umdNamedDefine: true,
globalObject: 'typeof self !== "undefined" ? self : this'
},
externals: {
moment: {
commonjs: 'moment',
commonjs2: 'moment',
amd: 'moment',
root: 'moment'
}
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
loader: 'babel-loader',
exclude: /(node_modules|bower_components)/
}]
},
resolve: {
modules: [path.resolve('./node_modules'), path.resolve('./src')],
extensions: ['.json', '.js']
}
};
module.exports = config;
This is my enums.js:
import { EnvironmentMode} from './enums/config.enum';
export { EnvironmentMode };
This is my config.enum.js
const { enumUtils } = require('../utils/enum.utils');
// This enum defines the possible environment mode of the application.
const EnvironmentMode = enumUtils.createEnum(new Map([
['DEVELOPMENT', 'development'],
['TEST_LOCAL', 'test_local'],
['TEST_LOCAL_DEMO', 'test_local_demo'],
['TEST_DEMO', 'test_demo'],
['TEST_LINUX', 'test_linux'],
['PRODUCTION', 'production']
]));
module.exports = {
EnvironmentMode: EnvironmentMode
};
This is my utils.js:
import enumUtils from './utils/enum.utils';
export {
enumUtils
};
This is my enum.utils.js
class EnumUtils {
constructor() {}
// This method takes a map of elements and converts them to freeze objects (an enum-like object).
createEnum(mapItems) {
// Check the existence of the mapItems parameter. If not exists, return null instance.
if (!mapItems || mapItems.length <= 0) {
return null;
}
// This new object will hold the freeze array object.
const symbolMap = {};
// Assign each object.
mapItems.forEach((value, key) => {
symbolMap[key] = value;
});
// Freeze the object and return it.
return Object.freeze(symbolMap);
}
}
const enumUtils = new EnumUtils();
module.exports = enumUtils;
For some reason, when I build the project, and inject the compiled code to the projects, run any project, I get the following error:
C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:212
var EnvironmentMode = enumUtils.createEnum(new Map([['DEVELOPMENT', 'development'], ['TEST_LOCAL', 'test_local'], ['TEST_LOCAL_DEMO', 'test_local_demo'], ['TEST_DEMO', 'test_demo'], ['TEST_LINUX', 'test_linux'], ['PRODUCTION', 'production']])); // This enum define the possible error code types that can appear when the server is listening and running.
^
TypeError: Cannot read property 'createEnum' of undefined
at Object../src/shared/enums/config.enum.js (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:212:33)
at __webpack_require__ (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:30:30)
at Object../src/shared/enums.js (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:191:15)
at __webpack_require__ (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:30:30)
at ./src/shared/enums.js.Object.defineProperty.value (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:94:18)
at C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:97:10
at webpackUniversalModuleDefinition (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:3:20)
at Object.<anonymous> (C:\Or\Web\OSRStreamer\Streamer\streamer\src\shared\enums.js:10:3)
at Module._compile (internal/modules/cjs/loader.js:701:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
What am I doing wrong?
My bad.
I did
const { enumUtils } = require('../utils/enum.utils');
Instead of
const enumUtils = require('../utils/enum.utils');
Fixed it. It works.

Variable masking

I have a class defined in an index.js file like this
const BLOG = BLOG || {};
BLOG.ComponentFactory = class {
}
window.BLOG = BLOG;
Then, in a file init.js in a bundle, I try to access that var, the file is like this
const BLOG = BLOG || {};
BLOG.init = function() {
var c = new BLOG.ComponentFactory()
}
I get BLOG.ComponentFactory is not a constructor and I cannot understand why. Is the BLOG definition inside the file init.js masking the global var?
There is something strange: using Chrome debugger in the init function, I see Blog.ComponentFactory defined inside "Global", but if I add to the properties to watch, I see Blog.ComponentFactory = undefined
I'd like to understand what's happening.
I need to defin BLOG as a global var as I'm using ES6 together with old javascritpt code.
EDIT: if I use the following code all works (init.js)
const BLOG = BLOG || {};
BLOG.init = function() {
var c = new window.BLOG.ComponentFactory()
}
So, it seems the local const BLOG is masking the global BLOG var, but I need to define BLOG because otherwise I get BLOG is undefined. So, how do I solve the problem?
EDIT2: my webpack config (the problem is in the bundling, the vars are defined inside functions which are generated while bundling)
const webpack = require('webpack');
const path = require('path');
var config = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
]
},
// workaround for
// ERROR in ./node_modules/wpcom/build/lib/site.media.js
// Module not found: Error: Can't resolve 'fs' in '/node_modules/wpcom/build/lib'
node: {
fs: 'empty'
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
var aConfig = Object.assign({}, config, {
name: "a",
entry: "./WebContent/js/blog/index.js",
output: {
path: __dirname + '/WebContent/js/blogW',
filename: "bundle.js",
publicPath: 'http://localhost:8080/js/blogW'
},
devServer: {
historyApiFallback: true,
contentBase: './WebContent',
publicPath: "http://localhost:8080/js/blogW",
hot: true
}
});
module.exports = [
aConfig
];

Why is webpack not exposing jQuery as a global?

My project requires a few npm modules, which I'm using yarn and webpack to load in. Namely, they are jquery, bootstrap3, moment, jquery-tablesort, jquery-ujs, bootstrap-select, and livestamp.
Some of those plugins need jQuery to be exposed as the global $ object. I've tried to make webpack do that, but so far I've failed.
My webpack config currently looks like this:
module.exports = {
entry: packPaths.reduce(
(map, entry) => {
const localMap = map;
const namespace = relative(join(entryPath), dirname(entry));
const key = join(namespace, basename(entry, extname(entry)));
localMap[key] = [resolve(entry)];
if (entry.includes('vendor')) {
localMap[key].unshift('babel-polyfill');
}
return localMap;
}, {}
),
output: {
filename: '[name].js',
path: output.path,
publicPath: output.publicPath
},
module: {
rules: [{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: '$'
}]
}, ...sync(join(loadersDir, '*.js')).map(loader => require(loader))]
},
plugins: [
new webpack.EnvironmentPlugin(JSON.parse(JSON.stringify(env))),
new ExtractTextPlugin(env.NODE_ENV === 'production' ? '[name]-[hash].css' : '[name].css'),
new ManifestPlugin({
publicPath: output.publicPath,
writeToFileEmit: true
})
],
resolve: {
extensions: settings.extensions,
modules: [
resolve(settings.source_path),
'node_modules'
]
},
resolveLoader: {
modules: ['node_modules']
}
};
On the advice in this question, I added the module section for expose-loader and removed a previous ProvidePlugin section that I had tried; neither of these options have worked.
My scripts are included like so (in application.html.erb):
<%= javascript_pack_tag 'vendor.js', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag "//code.highcharts.com/highcharts.js", "chartkick" %>
There are no other scripts included before or after these three. In vendor.js:
import * as $ from 'jquery';
window.$ = window.jQuery = $;
import 'bootstrap3'
import 'moment'
import 'jquery-tablesort';
import 'jquery-ujs';
import 'bootstrap-select';
import 'livestamp';
The manual assignment to window.$ also doesn't work. When Bootstrap loads, it gives me this error:
transition.js:59 Uncaught ReferenceError: jQuery is not defined
at Object.<anonymous> (transition.js:59)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.module.exports (npm.js:2)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.<anonymous> (vendor.js:1)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at Object.<anonymous> (runtime.js:736)
at __webpack_require__ (bootstrap bde90632505934e68af9:19)
at bootstrap bde90632505934e68af9:65
at vendor.js:69
(Note the vendor.js in the last line of the trace there is the compiled vendor.js, i.e. not the one included above but the Webpack result.)
What can I do to fix this?
I think in this situation you should use require instead import
Try this in your vendor.js:
window.$ = window.jQuery = require('jquery');
require('bootstrap');
require('moment');
// others...

Javascript es6 + Webpack referencing imports

I am trying to work out why i can reference an import from one file, but not the other
(I think it is to do with the way javascript works and that I cannot reference a variable before it is defined... If that is the case then i'm hoping one of you can give me some reference to a workaround for that.)
I am building an electron app with webpack and babel.
my webpack.config.js looks like this:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
app: ['./app/src/app.js', './app/src/test.js']
},
output: {
path: __dirname,
filename: './app/lib/bundle.js'
},
module: {
loaders: [
{
test: /.js?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['env']
}
}
]
},
};
And my app.js
import { test, foo } from './app';
class app {
constructor() {
console.log("hello from app.js");
}
}
// let a = new test(); // gives error (shown below)
// foo(); // same exact error but referencing function instead of constructor
export { app };
And test.js
import { app } from './app';
class test {
constructor() {
console.log("hello from test.js");
}
}
let foo = () => console.log("foo");
let b = new test(); // works as expected
export { test, foo };
the error is as follows:
Uncaught TypeError: _app.test is not a constructor
at Object.<anonymous> (bundle.js:88)
at __webpack_require__ (bundle.js:20)
at Object.defineProperty.value (bundle.js:97)
at __webpack_require__ (bundle.js:20)
at Object.defineProperty.value (bundle.js:63)
at bundle.js:66
So I can call classes from app inside test, but cannot do the same the other way around? why?
Thanks for responses in advance
This is a circular dependency issue. Your app imports test, which in turn imports app.
Thank you commenters Luke and Li357.
I was breaking my brain over this and confused myself with folder names vs file names.
The problem was i was importing files from the same places and causing circular dependency error.
Solved.

Node Webpack eval() sourcemaps

I am struggling setting up a Node project with TypeScript.
My workflow is: I run a Node script using nodemon. The node script creates a webpack compiler instance and sets the filesystem to MemoryFS. The webpack config includes loaders for TypeScript and Babel.
After webpack has finished compiling, if there are errors, I throw then, if not I fetch the result with MemoryFS and eval() it to run the code.
All of that works fine. But I tried to add sourcemaps support. I added a banner plugin in webpack which adds 'source-map-support'. In webpack, I set the devtool to 'source-map'. In tsconfig.json, I have the sourcemaps option enabled. In the src/index.ts I just throw an error, and at runtime Node doesn't tell me where did the error occur. (the sourcemap)
But if I run webpack normally, and then I run it using node dist/bundle.js. It is working.
I think there is a problem because of me using eval() to run the compiled output.
src/index.ts:
console.log('Hello world');
throw new Error('not');
build.js:
const path = require('path');
const webpack = require('webpack');
const MemoryFS = require('memory-fs');
const fs = new MemoryFS();
const config = require('./webpack.config');
const compiler = webpack(config);
compiler.outputFileSystem = fs;
compiler.run((err, stats) => {
console.clear();
const jsonStats = stats.toJson();
if(jsonStats.errors.length > 0 || jsonStats.warnings.length > 0)
return console.log(jsonStats.warning, jsonStats.errors);
const result = fs.readFileSync(path.resolve(__dirname, 'dist', 'bundle.js')).toString();
eval(result);
});
webpack.config.js:
const path = require('path');
const webpack = require('webpack');
const SRC_DIR = path.resolve(__dirname, 'src');
const DIST_DIR = path.resolve(__dirname, 'dist');
module.exports = {
entry: SRC_DIR + '/index.ts',
output: {
path: DIST_DIR,
filename: 'bundle.js'
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
{
test: /\.ts$/,
use: [
{ loader: 'babel-loader' },
{ loader: 'ts-loader' },
{ loader: 'tslint-loader' }
]
}
]
},
resolve: {
extensions: ['.ts', '.js', '.json']
},
target: 'node',
plugins: [
new webpack.BannerPlugin({
raw: true,
entryOnly: false,
banner: 'require("source-map-support").install();'
}),
new webpack.NoEmitOnErrorsPlugin()
]
};
After running webpack normally and executing the code (webpack && node dist\bundle.js, as expected):
C:\Users\shachar\Desktop\graph\dist\webpack:\src\index.ts:3
throw new Error('not');
^
Error: not
at Object.<anonymous> (C:\Users\shachar\Desktop\graph\dist\webpack:\src\index.ts:3:7)
at __webpack_require__ (C:\Users\shachar\Desktop\graph\dist\webpack:\webpack\bootstrap a6ba0885ca7e8b14ee63:19:1)
at C:\Users\shachar\Desktop\graph\dist\webpack:\webpack\bootstrap a6ba0885ca7e8b14ee63:62:1
at Object.<anonymous> (C:\Users\shachar\Desktop\graph\dist\bundle.js:67:10)
at Module._compile (module.js:573:30)
at Object.Module._extensions..js (module.js:584:10)
at Module.load (module.js:507:32)
at tryModuleLoad (module.js:470:12)
at Function.Module._load (module.js:462:3)
at Function.Module.runMain (module.js:609:10)
Running it using build.js:
Hello world
Error: not
at Object.eval (eval at compiler.run (C:\Users\shachar\Desktop\graph\build.js:23:5), <anonymous>:75:7)
at __webpack_require__ (eval at compiler.run (C:\Users\shachar\Desktop\graph\build.js:23:5), <anonymous>:21:30)
at eval (eval at compiler.run (C:\Users\shachar\Desktop\graph\build.js:23:5), <anonymous>:64:18)
at eval (eval at compiler.run (C:\Users\shachar\Desktop\graph\build.js:23:5), <anonymous>:67:10)
at compiler.run (C:\Users\shachar\Desktop\graph\build.js:23:5)
at emitRecords.err (C:\Users\shachar\Desktop\graph\node_modules\webpack\lib\Compiler.js:269:13)
at Compiler.emitRecords (C:\Users\shachar\Desktop\graph\node_modules\webpack\lib\Compiler.js:375:38)
at emitAssets.err (C:\Users\shachar\Desktop\graph\node_modules\webpack\lib\Compiler.js:262:10)
at applyPluginsAsyncSeries1.err (C:\Users\shachar\Desktop\graph\node_modules\webpack\lib\Compiler.js:368:12)
at next (C:\Users\shachar\Desktop\graph\node_modules\tapable\lib\Tapable.js:218:11)
Thanks for any help!
When you use eval to run the code, Node will have no idea about the source map. Perhaps you can try to run a child node process instead of using eval to run the result, or is there any reason you're using eval? See https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback

Categories