Using rollup in combination with babel and commonjs plugins doesn't resolve all modules - babeljs

I'm using rollup with the Babel and CommonJS plugins, like this:
const inputOptions = {
input: "...",
plugins: [
resolve(),
babel({
exclude: "node_modules/**",
externalHelpers: true,
include: "**/components/**/*.js",
}),
commonjs(),
],
};
But what happens is that modules referenced from components don't seem to be recognized by the CommonJS plugin, they end up as plain require(...) statements in the output (just like the source input) where of course they cannot be resolved. Modules imported (also through require() statements) by modules outside the components directory get picked up properly and included in the bundle.
I have tried moving the babel plugin up (before the resolve plugin), but this had no effect. I also tried moving it down, but then Rollup chokes on the JSX in the components.
I also tried removing the include option, so that all files go through Babel and then the result is that no modules get picked up besides the entry point, so it really seems like the Babel and CommonJS plugins aren't playing along nicely, though I can hardly imagine I'm the only one with a setup like this. Am I missing something?
Update: One other thing that I notice is that the files for which the requires() aren't recognized, aren't properly exported either. Instead, for each component that fails, I see this in the output bundle:
module.exports = ComponentName;
var componentName = /*#__PURE__*/Object.freeze({
});
The module.exports line comes from the source, but that Object.freeze() statement is something rollup adds, maybe because it doesn't see any default export?
To add a bit of extra confusion: There's actually one component that gets transpiled by Babel and for which the module resolution works and the requires() get replaced like you'd expect, but all the components included from that component in turn have the defective behavior described above.
Update 2: I have been able to reproduce the problem in a minimal example as well, and it allowed me to pinpoint why things worked for the one component, but not by the components it includes in turn. Apparently, functional React components work properly, but class components trigger the issue. So now my hypothesis is that the Babel transform for ES6 classes somehow confuses the CommonJS plugin.
Update 3: As I believe this is a bug, I have also created issues with the relevant projects: https://github.com/rollup/rollup-plugin-babel/issues/297 and https://github.com/rollup/rollup-plugin-commonjs/issues/369

Related

dynamic import with interpolated string

I'm trying out parcel in a hobby project, having worked with create-react-app (i.e. webpack) before. I have had a great experience with dynamic imports of the following sort:
const Page = React.lazy(() => import(`./${page}`));
This is in a wrapper component that takes care of the suspense etc. and gets page as a prop (always a literal string, no variable/expression. not sure if that makes a difference).
With webpack this works wonderfully, even though I'm not sure how. Each such page I hit in the app gets loaded the first time, then its available instantly. I understand this is quite hard for the bundler to figure out, but yeah, it works.
When I try the same with parcel, it still builds but fails at runtime. If I dynamically import e.g. './SomePage', that is exactly what is requested from the server (GET /SomePage), which of course serves index.html. This happens both on the dev server and with a build. The build also only produces one .js file, so it doesn't split at all.
Is this even possible with parcel to import like this? Am I missing some configuration (don't have any at the moment)?
I think there is an unfix bug in webpack that dynamic import with interpolation will be having issue due to they are not able to pinpoint the exact file path.
Source
Webpack will collects all the dependencies, then check the import statement, parse the params to a reg statement, like this:
import('./app'+path+'/util') => /^\.\/app.*\/util$/
Webpack look for modules that meet the criteria based on reg. So if the param is a variable, Webpack will use the wrong reg statement to look for modules, in other words, will look for modules globally.
Try adding an empty string and append the interpolation might help you on this.
const Page = React.lazy(() => import("" + `./${page}`));

Best way to make #types packages visible in an nx workspace

Background
I'm trying to remove resize-observer-polyfill from an nx workspace that I'm working on because it's natively supported in the browsers that we are targeting. Once I removed the polyfill, I needed to add #types/resize-observer-browser because the workspace currently uses typescript#4.0.5 and my understanding is that TypeScript does not have a "native" type for ResizeObserver until v4.2 which I'd love to update to, but can't atm.
Problem
In order to make TypeScript happy, it seems like I have to go in and manually add "resize-observer-browser" to individual tsconfig compilerOptions.types entries. This didn't seem that bad to me at first. I just updated the tsconfig.lib.json file of the libraries that happened to utilize ResizeObserver. However, I soon realized I needed to also add it to the tsconfig.spec.json of the libraries so that the unit tests could run, and then I also needed to add it to the tsconfig.app.json of any applications that happened to import those libraries.
Question
Is there an easier way in an nx workspace to handle this sort of problem?
I think that I could remove the default types overrides in each of the tsconfig files, since that would let TypeScript just utilize everything that exists under node_modules/#types when compiling. I didn't want to take that path since I assume there is a good reason for the default nx library/app generators to add the types override (I assume it's to force you to be explicit and not accidentally get away with accidental imports of test code from business logic).
The docs seem to recommend against this for #types packages, but /// <reference types="..." /> (e.g. /// <reference types="resize-observer-browser" />) can be also be used to include types, and might be easier to manage if the type is only used in a few places.
Docs: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-

Flutter imports: relative path or package?

In Flutter, for importing libraries within our own package's lib directory, should we use relative imports
import 'foo.dart'
or package import?
import 'package:my_app/lib/src/foo.dart'
Dart guidelines advocate to use relative imports :
PREFER relative paths when importing libraries within your own package’s lib directory.
whereas Provider package says to always use packages imports :
Always use package imports. Ex: import 'package:my_app/my_code.dart';
Is there a difference other than conciseness? Why would packages imports would reduce errors over relative imports?
From the same Dart guidelines, further down they give this reason for the relative imports:
There is no profound reason to prefer the former—it’s just shorter, and we want to be consistent.
Personally, I prefer the absolute method, despite it being more verbose, as it means when I'm importing from different dart files (in other folders), I don't have to work out where the file to be imported is, relative to the current file. Made-up example:
I have two dart files, at different folder levels, that need to import themes/style.dart:
One is widgets/animation/box_anim.dart, where the relative path import would be:
import '../../themes/style.dart';
The other is screens/home_screen.dart with the relative import:
import '../themes/style.dart';
This can get confusing, so I find it better to just use the absolute in both files, keeping it consistent:
import 'package:myapp/themes/style.dart';
And just stick that rule throughout. So, basically, whatever method you use - Consistency is key!
The Linter for Dart package, also has something to say about this, but is more about the Don'ts of mixing in the '/lib' folder:
DO avoid relative imports for files in lib/.
When mixing relative and absolute imports it's possible to create
confusion where the same member gets imported in two different ways.
An easy way to avoid that is to ensure you have no relative imports
that include lib/ in their paths.
TLDR; Choose the one you prefer, note that prefer_relative_imports is recommended in official Effective Dart guide
First of all, as mentioned in this answer, Provider do not recommands package imports anymore.
Dart linter provides a list of rules, including some predefined rulesets :
pedantic for rules enforced internally at Google
lints or even flutter_lints (previously effective_dart) for rules corresponding to the Effective Dart style guide
flutter for rules used in flutter analyze
Imports rules
There is actually more than two opposites rules concerning imports :
avoid_relative_lib_imports, enabled in pedantic and lints rulesets, basically recommend to avoid imports that have 'lib' in their paths.
The two following are the one you mention :
prefer_relative_imports, enabled in no predefined rulesets, but recommended in Effective Dart guide in opposition to :
always_use_package_imports, enabled in no predefined rulesets. Which means that it is up to you and to your preferences to enable it (be careful, it is incompatible with the previous rule)
Which one should I choose?
Choose the rule you want ! It will not cause any performance issue, and no rule would reduce errors over the other. Just pick one and make your imports consistent across all your project, thanks to Dart linter.
I personnaly prefer using prefer_relative_imports, as it is recommended by Dart team, with this VSCode extension which automatically fix and sort my imports.
Provider do not need packages imports anymore.
This was a workaround to an old Dart bug: Flutter: Retrieving top-level state from child returns null
TL;DR, by mixing relative and absolute imports, sometimes Dart created a duplicate of the class definition.
This led to the absurd line that is:
import 'package:myApp/test.dart' as absolute;
import './test.dart' as relative;
void main() {
print(relative.Test().runtimeType == absolute.Test().runtimeType); // false
}
Since provider relies on runtimeType to resolve objects, then this bug made provider unable to obtain an object in some situations.
My 5 cents on the topic are that absolute (package:my_app/etc/etc2...) imports cause much less trouble than relative ones (../../etc/etc2...) when you decide to reorganize/cleanup your project`s structure because whenever you move a file from one directory to another you change the "starting point" of every relative import that this file uses thus breaking all the relative imports inside the moved file...
I'd personally always prefer absolute to relative paths for this reason
This question already has good answers, but I wanted to mention an insanely annoying and hard-to-find problem I experienced with unit testing that was caused by a relative import.
The expect fail indicator for an exception-catching expect block
expect(
() => myFunction,
throwsA(isA<InvalidUserDataException>())
);
was showing the actual result as exactly the same as the expected result, and zero indication of why it's failing.
After massive trial-and-error, the issue was because the expected InvalidUserDataException (a custom-made class) was being imported to the test file in RELATIVE format vs PACKAGE format.
To find this, I had to compare side-by-side, line-by-line, call-by-call between this test file and another test file that uses the exact same exception expecters (It's lucky, we had this), and just by chance, I happened to scroll to the top of this file's imports and see the blue underline saying prefer relative imports to /lib directory.
No, they're not preferred; they're necessary, because the moment I changed that to a PACKAGE (absolute) import, everything suddenly started working.
What I learned from this is: Use absolute imports for test files (files outside the lib directory)
e.g. inside of src/test/main_test.dart
DON'T: use import '../lib/main.dart'
DO: use package:my_flutter_app/main.dart
Maybe other people knew this already, but I didn't, and I couldn't find anything online with searches about this issue, so I thought I would share my experience that might help others who got stuck around this.
Does anyone know why this happens?
Edit: For context, this happened while using Flutter 2.1.4 (stable) with Sound Null Safety
Do you use Integration Tests?
If the answer is yes, then in most cases you need to use package imports. When you attempt to run your integration tests on a physical device, any relative imports will not be able to find what they're looking for.
Example: https://github.com/fluttercommunity/get_it/issues/76
You can enforce package imports in your project by using these two linting rules:
always_use_package_imports
avoid_relative_lib_imports
I also prefer package imports because they stick even when rearranging your files and folders. Relative imports frequently break and it's a pain to have to remove them and reimport the offending dependency.
One very simple reason to not use package imports: rename your package without editing every dart file
Renaming can happen a few times while the product does not have a definitive name, and you or your product owner decides to change it.
It is much more painful to rename with package imports as your package name is in every import.
Of course you can change it with a find/replace query, but it's a useless edit on every dart file you can avoid with relative imports.
Plus, vscode allows to automatically update relative imports on file move/rename and I have never had any issue with this feature.

babel-polyfill vs babel-plugins

I am a bit lost in the Babel options / config. I want to use recent js features and compile (with webpack) to browser code.
What is the difference between babel-polyfill and babel plugins with babel-preset-env?
Are they intended to work together?
Answer from this article:
The distinction between a babel transform plugin versus
babel-polyfill / babel-runtime is whether or not you can
reimplement the feature today, in ES5. For example, Array.from can
be rewritten in ES5 but there is nothing I can write in ES5 to add
arrow function syntax to JavaScript. Therefore, there is a transform
for arrow functions but none for Array.from. It will have to be
provided by a separate polyfill like babel-polyfill, or
babel-runtime.
As a side note, here is my current understanding of the babel eco-system.
Babel is a javascript compiler: it parses, transforms and outputs transformed code.
babel-core
This is the parse and output parts.
It does not do any transformation.
It can be used from the command line or from a bundler (webpack, rollup and co.)
babel-polyfill / babel-runtime
Acts on the transform part by prepending es5 javascript to your code to emulate es2015+ functions (like Object.assign).
Relies on Regenerator (to polyfill generators) and core-js (to polyfill all the rest).
Difference between babel-polyfill and babel-runtime: the former defines global methods (and pollutes the global scope) whereas the latter transforms your code to make the same functionnality available as explained in this answer.
babel plugins
Transform the code you wrote.
babel syntax / transform plugins: parse and transform es2015+ syntax (like arrow functions) to convert it to es5.
babel-plugins-stage-x (from stage-0 to stage-4): transform future javascript syntax which is not in the JS specs yet, starting at stage-0 (just an idea) down to stage-4 (will land in the babel-plugins soon).
babel-preset-env
babel-preset-env determines the Babel plugins and polyfills needed for a specific environment.
With no configuration, it will load all the plugins (including es2015, es2016 and es2017) required to transpile es2015+ to es5.
With a target option, it loads only the plugins required to run on a specific target.
With the builtIn option, it uses only the babel-polyfill which are not built-in the target.
Does not work with babel-transform-runtime yet (as of nov. 2017). (see this issue)
babel-preset-env is a Babel preset meant to automatically set up babel plugins and include the necessary babel polyfills based on a set of target environments checked against a feature compatibility table.
In order to make a fully working ES2015+ environment run on a non-ES2015+ client, simple code transpilation is sometimes not enough:
ES generators are enabled using regenerator library (provided by babel-polyfill)
Missing ES2015+ methods (like Promise, Map, Object.assign...) are polyfilled with core-js (provided by babel-polyfill, too)
Any other transpilable feature is generated by standard babel plugins, often used trough pre-configured babel-presets
So, back to your question, it's babel-preset-env that makes use of babel-polyfill and babel plugins.

Fay: include another Fay file?

I have one Fay file which is the heart of my program, however I need some helpers for my logic, for instance a method to replace substrings. From what I understand, if I need such methods which are offered by many Haskell libraries from Hackage directly, I can't use those Haskell libraries, but I must copy-paste the code in my project. So it's what I did, I copy-pasted a "replace" function together with other helpers from the MissingH library in a new file in my project: Utils.hs.
That Utils.hs compiles without problems with Fay. Also I import it in my main Fay file and I get a JS file for the main project file without problems. However at runtime I get the following error:
ReferenceError: Utils$$36$ is not defined
I don't think that Fay will include the code from the helper file in my main JS file, so I'm including both JS files in the loading HTML. And to make even more sure that when I load the main file, that the utils file is loaded, I load it like that:
$.getScript("Utils.js", function(){
$.getScript("FayConfig.js");
});
But despite this I still get the error. I tried compiling the Utils.hs with "--library" but it didn't help.
So my question is, which setup do I need to achieve that the generated JS will find the helper functions that I put in another HS file, knowing that at compile-time, Fay (apparently) finds them without problems? Is there an example of such a setup online? Most of the Fay uses that I found have all the code in a single HS file, though they often use external Fay code from cabal, as with fay-jquery. In my case, setting up a cabal project just for these simple helpers would be overkill.
Which version of Fay are you using (fay --version)? It seems like you are using a version older than
0.16 where forgetting import Prelude wouldn't give any warnings, see this closed ticket. So upgrade fay and/or add import Prelude.
We're also considering renaming operators in the produced output to make error messages like these easier to understand.
You do not need to invoke fay several times, fay outputs all dependencies into the same js file. So there's no difference from using a cabal package in that regard.
Hope this helps, otherwise please give me a way to reproduce this.