Babel compiles constant to undefined - javascript

With this code
const noCurrencies: Map<CurrencyCode, Currency> = new Map();
/**
* Reducer for the finished currency request.
*/
export function currencies(state: Map<CurrencyCode, Currency> = noCurrencies, action: Action): Map<CurrencyCode, Currency> {
switch (action.type) {
case SET_CURRENCIES:
return action.currencies;
default:
console.log('currencies(reducer): noCurrencies', noCurrencies)
return state;
}
}
I get this console output:
currencies(reducer): noCurrencies undefined
Is this a known problem with Babel? How do I debug it? I have a feeling that it's due to this particular file having been called twice during initialisation and thus having a circular dependency with another file.
(I'm not 'recreating a repro from scratch', so don't suggest that, and the types are https://github.com/flowtype/flow-typed which get removed in a pre-processor step, and I've tried without types as well, with the same result)

The reason was a large circular dependency between modules. Even if using ECMAScript module syntax, the compiler (WebPack 3 + babel) is not smart enough to only load what is needed when it's needed; but instead evaluate all exports and imports whenever a file is used/touched.
That means that if you have index.js files that liberally export things from around them, to create a single entry-point for callers outside its folder, you'll likely run into issues like this.
I had to go through the code-base and make the imports 'deep' by directing imports straight to the, of the index file, surrounding files, instead.
E.g.
folder A
epics.js
commands.js
index.js
types.js
actions.js
reducers.js
messages.js
...
folder B
import { create } from '../A' becomes import { create } from '../A/types'
and so forth for all things; in the end I'm only exporting React views and types from index.js

Related

process.env.FOO transformed into {}.FOO and throwing error "expected expression, got "."

I wrote a TypeScript module that interacts with a specific RESTful API. The module never refers to process.env anywhere (target is browser, possibly Node down the line as well).
I use Rollup to transpile to a single JS file. From there, going into Node and require('./build/index') is successful, and I can even run the functions and get expected results. So Rollup itself appears to work.
But the file contains many references to things like process.env.DEBUG. (I suspect Rollup is doing something to create loggers that can work in both Node and browser.)
Now I import this into a Gatsby UI project that will need to connect to the API using this module:
import { api } from 'my-api'
api.someApiCall()
Problem is that when Gatsby compiles all this (using Webpack?) into commons.js (some big JS file with a lot of combined code from different libraries, including my API module), it appears to transform the module's process.env.DEBUG (for example) into {}.DEBUG. Then the browser complains that "expected expression, got '.'". Which makes sense. You cannot access {}.DEBUG. It would have to be ({}).DEBUG or const o = {}; o.DEBUG.
Now I have been off in the world of other languages for a while. Rollup is fairly new to me. Gatsby is very new to me. What is the way forward? Do I tell Rollup via a config to replace process.env with ({}) so that way ? But then that precludes the library from ever being used in Node.js and taking advantage of process.env.
Do I need to change something about Gatsby to have it replace process.env with ({})?
Edit For example, here is some output showing up in my browserin commons.js:
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete {}.DEBUG;
} else {
{}.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* #return {String} returns the previously persisted debug modes
* #api private
*/
function load() {
return {}.DEBUG;
}
In my module, those are process.env.DEBUG.
Edit 2 I've also tried putting a gatsby-node.js containing this:
exports.onCreateWebpackConfig = ({
plugins,
actions,
}) => {
const { setWebpackConfig } = actions;
setWebpackConfig({
plugins: [
plugins.define({
'process.env': "({})",
'{}.DEBUG': '({}).DEBUG',
})
]
})
}
No effect.

Is there a way to create a module that can read the directory that it is used in? (TypeScript/JavaScript)

What I'm trying do is make a handler that will read a folder that it is in. However, this handler is a module that will be imported into multiple folders.
For example, if given the current directory:
->Main
->folder a
->languages
a.js
->folder b
->languages
b.js
->folder c
->languages
c.js
d.js
d.js - handler used in a.js, b.js and c.js
var Translate = ((lang) => {
// this line below is what I want to know how to do.
import { lang } from "./languages/"+lang;
// do stuff with lang
}
export { Translate }
With the d.js handler, it should be able to be used in a.js/b.js/c.js. However I want to know how to read the directory (in this case the language folder) it is being imported in rather than from the handler. I'm doing this to handle multiple languages for an app, and I figure this way its a lot easier to manage.
If you are executing the code in NodeJS environment, you can use this to get the current directory name:
import path from "path";
const dirName = path.basename(path.dirname(filename));
The dirName variable will contain the folder name as a string.
Then you can dynamically import using ES6 template literal.
import { lang } from `./languages/${dirname}`

When does the code outside React Component class in a JS file run?

Say I have a JS file contains 2 React Component class definition and a bunch of other things:
// a file called OuterComponent.react.js
import xxx from xxx;
import yyy from yyy;
// When does these run?
let a = 0;
a = 1;
export default class OuterComponent extends React.PureComponent<> {
render() {
return (
<View>
<InnerComponent />
</View>
);
}
}
class InnerComponent extends React.PureComponent<> {
//... something
}
Questions
When will the declaration and value setting code for 'a' run? Does it run when this file is imported/required by other files?
Can I have some flag in this file and changes from time to time, and then read the value of this flag from many other files? (like a singleton manager?) If I can, how do I export it and import it?
What does creating multiple files actually mean? (except that it breaks huge chunk of code into small chunks for better readability) Does it make any other difference?
Question 1: When will the declaration and value setting code for 'a' run? Does it run when this file is imported/required by other files?
Runs the first time the file is imported. It is not executed on subsequential imports.
Question 2: Can I have some flag in this file and changes from time to time, and then read the value of this flag from many other files? (like a singleton manager?) If I can, how do I export it and import it?
You can.
Exporting:
export let a = 0;
Importing:
import { a } from './filename.js';
Question 3: What does creating multiple files actually mean? (except that it breaks huge chunk of code into small chunks for better readability) Does it make any other difference?
Breaks code into small chunks;
Allows reuse; and
Enables encapsulation of non-exported variables/functions.
--
Check a demo of modules usage: http://plnkr.co/edit/0ImgQj2KzLj9O1D63Gq9?p=preview
Notice how a.js is loaded only once, even though it is imported both by b.js and c.js. Also see how it is exported and imported, and how when it changes, the change is picked up in other modules.
okay here it is
Answer 1: Yes, it runs when you import this file.
Answer 2: You can define some varible and export it to use in some other file, but that we do for constant values that does not change over time, like your action types etc, the thing you are referring to here is not what it is for, you want to use react Context or Redux store for that.
Answer 3: Creating multiple files is a modular approach to code, React emphasis on Composition that is the whole point of composing components in one another and build an App
Yes. This code will run immediately when the file is imported. It has nothing to do with react but with how js works.
You can share a variable in js using export keyword like this:
export let a = 0
Changes to this variable won't rerender your components because it's not part of any state.
Readability is huge impact by itself. It also allows reuse of variables names.
When working in collaboration it makes the flow much easier and reduces conflicts to only the places where they really are.

Avoid bringing code from imports of imports

I'm going through the exercise of making my webpack bundle smaller, and using webpack's bundle analyzer I saw that a really heavy package is being included in two different async chunks, even though it's only used once. After some digging in my code, I've come to realize that it is probably because of the following scenario:
file1.js
import { foo } from 'ReallyHeavyPackage'
export function a(): string {
console.log("called a()");
}
export function b(): string {
return foo();
}
file2.js
import { a } from './file1.js'
a();
file3.js
import { b } from './file1.js'
b();
I'm assuming that since file1 imports the heavy package globally, and file2 imports a function from file1, it gets the heavy package as a dependency, even though it doesn't import the function that is actually using the package. I'd expect (or rather, wish) that only the chunk for file3 has the heavy dependency included, since it's the only place where it is being used.
Is there a specific way I should be handling an import like this? Perhaps a magic configuration I can do in webpack to help with this, a better way of structuring the modules/functions or just better way to import functions/modules/packages?
I'm using webpack 4 and I'm on ES2017
Maybe try dynamic imports?
export function a(): string {
console.log("called a()");
}
export async function b(): string {
const { foo } = await import('ReallyHeavyPackage');
return foo();
}
https://webpack.js.org/guides/code-splitting/#dynamic-imports
I think what you're looking for is the Webpack CommonChunksPlugin: https://webpack.js.org/plugins/commons-chunk-plugin/. This plugin takes common chunks (modules/libraries) from different bundles and puts them into their own bundle.

How to make Webpack recognize dynamic exports

I'm seeing the following warning when building with Webpack v4 (using babel-loader for the JS files):
Warning in ./src/components/Foo
"export 'ADDENDUM' was not found in '../../types'
...
The import in ./src/components/Foo is:
import { ADDENDUM } from '../../types';
../../types:
import { each } from 'lodash';
export const typesDict = {
ADDENDUM: 'addendum',
};
each(typesDict, (type, typeConstant) => {
exports[typeConstant] = type;
});
This isn't causing a build error, just a warning. The warning is wrong though, since I am exporting ADDENDUM (though dynamically), and everything works as it should.
Is there a way for Webpack to handle these dynamic imports, or to at least turn off the warning? I'm upgrading from Webpack v1 right now, and v1 does not have this problem (or if it does, it's being hidden somehow).
Also please note: I do NOT want to silence all Webpack warnings, such as via the devServer config. I just want to silence this one type of warning.
Based on your ../../types file i assume your approach was to skip writing again the components in the exports object.
Instead of silencing the warning, try something simpler to fix the issue. Since you don't want to write twice the same names, take a look at my example.
No lodash is required, no loops are used and exported constants are written once.
../../types:
export const ADDENDUM = 'addendum';
export const ADDENDUM2 = 'addendum2';
export const ADDENDUM3 = 'addendum3';
That is all, no more dynamic imports, no more warnings.
UPDATE:
Your code is indeed valid, but when you use dynamic exports/imports, compilers/bundlers loose trace of your exports(in your case) since they don't check the contents of your exports object, thus the warning you receive, because the compiler(babel) didn't find exports.ADDENDUM in your code, only you know that it's there, therefore the compiler thinks you're using an unexisting component.
As of imports, it's the same story, the same type of warning was emitted by webpack when something like require('/path/to/' + someVar + '/some.file.js'), because webpack wanted to make a chunk out of it, but that wasn't a full path for webpack and it couldn't find the file because it was a concatenated string (dynamic import). (i don't know if this changed over the years, but i'm sure you understand/knew this perfectly well too)

Categories