I need a way to babel transpile some dependencies in node_modules
, and there doesn't seem to be a way to do so with parcel.
I've read these relevant issues: #948 #13 , and I'm aware of #1101 which solves the case for local repo symlink.
I'm using file:
dependencies to reference local repos in my project and I need to run babel for those, but no existing solution covers this case.
What would the problems be if we simply specify the target node_modules
that need to be transpiled with babel? I'm curious why this wasn't considered as an alternative solution to #1101
Having the repos flag themselves as "source" only solves part of the problem, and being able to manually specify packages from the root project seems a lot more intuitive and flexible. I agree that the community needs to collectively come up with a cleaner, more thought-out solution for managing code compatibility for released packages/modules, but until then I believe it is up to the package consumers to make sure that every code they import and release into production is transpiled and made compatible with their desired browser targets. Parcel is a tool for package consumers, therefore I feel it should support such use case.
| Software | Version(s) |
| ---------------- | ---------- |
| Parcel |1.9.3
| Node |8.9.1
| npm/Yarn |5.5.1/1.3.2
| Operating System | MacOS HighSierra 10.13.3
This seems like a relevant place to mention that the local modules (src/node_modules
) pattern does not work in Parcel, due to the above restriction; Babel does not seem to transpile dependencies in any node_modules
, including src/node_modules
.
It should also be noted that in order to _effectively_ use the local modules pattern, the --no-autoinstall
option must be used, to avoid installing npm modules instead of local modules.
Right now a project I'm currently working on is using along the lines of:
// .browserslistrc.packages
node 10.11
// package.json
{
"scripts": {
"postinstall": "npm-run-all -p \"postinstall:*\"",
"postinstall:p-retry": "cpy --rename=.browserslistrc .browserslistrc.packages node_modules/p-retry",
"postinstall:query-string": "cpy --rename=.browserslistrc .browserslistrc.packages node_modules/query-string"
}
}
It's super hacky, but it more or less works.
Babel 7 allows custom transpilations of dependencies in node_modules with project-wide babel.config.js
and overrides
property (Link).
module.exports = {
...
overrides: [
{
test: ["./node_modules/myPackage/dist"],
plugins: ["@babel/plugin-transform-modules-commonjs"]
}
]
}
Can we somehow emulate this on Parcel 1.x ? Will be such scenarios supported in Parcel 2, where a package consumer can decide how he transpiles a package?
E.g. a use case would be a common module with es-next language syntax which can be consumed by browser (consider transpile to browser target, use babel plugins like @babel/plugin-proposal-class-properties
) or in an Electron environment without any conversion, as a very decent chromium browser is used.
I know, there is source
field - in this case though it would not be viable.
Will be such scenarios supported in Parcel 2, where a package consumer can decide how he transpiles a package?
Parcel 2 will use the provided babel config without modification (and will most likely also support babel.config.js
).
For future reference, from the other issue:
For example, I'd like to use the only or include fields of my .babelrc to specify that it should be used to transform specific files.
Right now a project I'm currently working on is using along the lines of:
// .browserslistrc.packages node 10.11 // package.json { "scripts": { "postinstall": "npm-run-all -p \"postinstall:*\"", "postinstall:p-retry": "cpy --rename=.browserslistrc .browserslistrc.packages node_modules/p-retry", "postinstall:query-string": "cpy --rename=.browserslistrc .browserslistrc.packages node_modules/query-string" } }
It's super hacky, but it more or less works.
Are there other requirements for that to work? Every IE support issue in this tracker is being closed and marked as duplicate of this one. But I cannot get IE 11 to work.
Even adding a .browserslistrc
to every node_module
defined in my package.json
, does not solve the trouble. I still end up with arrow functions in my bundle.
What am I missing?
const fs = require('fs');
const package = JSON.parse(
fs.readFileSync('package.json', { encoding: 'utf8' })
);
const modules = Object.keys(package.dependencies);
const browserslistrc = '.browserslistrc';
modules.forEach(name => {
fs.copyFileSync('.browserslistrc', `node_modules/${name}/${browserslistrc}`);
console.log(`copied ${browserslistrc} to node_modules/${name}`);
});
If I first run that script, and after it rm -rf dist\ .cache\ && npx parcel index.html
, I still end up with the arrow functions in the bundle. But also, the build still completes in 115s. Giving the impression that not every module is being transpiled.
node 10.11
will still emit arrow functions IIUC, you probably need a more robust browserslist config than that to get it to transpile to something that IE 11 will understand
@floyd-may , I've been defining IE 11
. This is my .browserslistrc
. Is the trick here to use node
? This should work also, right?
last 2 chrome versions
last 2 firefox versions
last 2 edge versions
last 2 opera versions
last 2 safari versions
IE 11
Does the fact that I'm using both .ts
as well as .js
files have anything to do with this?
@smeijer Your .browserslistrc
appears correct (I think you can check exactly which browsers it's compiling for with npx browserslist
).
Part of the issue is that Parcel seems to strictly require you to use npm packages that follow the general convention that bundled/compiled versions of a package are published as ES5 code for maximal compatibility. The downside is that when (for whatever reason) you need to use a package that does not follow this convention, there doesn't seem to be an easy way to make Parcel transpile the module.
The hack I figured out when I used to use Parcel was to copy whatever ES6 file(s) were causing grief and manually transpile them. Then I just used the transpiled versions and ignored the original module. The disadvantage of this approach is that it makes receiving updates hard, but chances are the modules which don't bother transpiling down to ES5 probably aren't updated frequently anyways.
The long-term solution I found was to switch to Webpack 4 for new projects. It incrementally rebuilds much faster (at least for me) and it provides much greater flexibility over how Babel and other processes run.
I have a feeling people in this thread are talking of two different things as if they were the one and same...
.browserslistrc
is meant to specify what browsers the transpiled code is supposed to be compatible with. Alternatively you could put that in the package.json.
But the problem discussed in this thread is that Parcel (v 1.*) doesn't transpile external node modules even if you put a browsers list in your project. The root cause of this is that Parcel assume NPM packages are ES5 by default and thus no need to transpile them.
One then needs to find some way to "convince" Parcel somehow that the NPM package is actually in a very modern form of JavaScript and needs to be transpiled to be browser-compatible. One hacky way of doing this is putting a .browserslistrc
in the module folder (not the root folder) to make Parcel "understand" that the module actually has a very modern form of JS. That is the purpose of the .browserslistrc.package
discussed above by @icopp.
So, while the root .browserslistrc
should correctly be conservative in what compatibility level they prescribe in order to make sure the transpiled code is compatible in common browsers - the .browserslistrc
that is copied into the NPM modules should actually prescribe a very 'modern' form of JavaScript (e.g. node 10.11
), because they're not used to describe the _output_, but the _input_ for transpilation.
@arntj thanks for the clarification!
I have a feeling people in this thread are talking of two different things as if they were the one and same...
.browserslistrc
is meant to specify what browsers the transpiled code is supposed to be compatible with. Alternatively you could put that in the package.json.But the problem discussed in this thread is that Parcel (v 1.*) doesn't transpile external node modules even if you put a browsers list in your project. The root cause of this is that Parcel assume NPM packages are ES5 by default and thus no need to transpile them.
One then needs to find some way to "convince" Parcel somehow that the NPM package is actually in a very modern form of JavaScript and needs to be transpiled to be browser-compatible. One hacky way of doing this is putting a
.browserslistrc
in the module folder (not the root folder) to make Parcel "understand" that the module actually has a very modern form of JS. That is the purpose of the.browserslistrc.package
discussed above by @icopp.So, while the root
.browserslistrc
should correctly be conservative in what compatibility level they prescribe in order to make sure the transpiled code is compatible in common browsers - the.browserslistrc
that is copied into the NPM modules should actually prescribe a very 'modern' form of JavaScript (e.g.node 10.11
), because they're not used to describe the _output_, but the _input_ for transpilation.
Exactly! I don't want to assume that all node_modules are already transpiled. Not every developer follows the same browser support so assuming node_modules are safe for every project feels irresponsible.
So if I'm understanding correctly; I have been doing this wrong? https://github.com/parcel-bundler/parcel/issues/1655#issuecomment-549442120
I've been copying .browserslistrc
into the modules with IE 11
defined, because I need to support IE11. But what I was doing instead, was telling parcel that this module already was in IE 11
compatible syntax?
So, .browserslistrc
in node_modules
tell's parcel which browsers are already supported. Not into which syntax it should be compiled. And .browserlistsrc
in your own project tell's parcel which browsers should be supported and be compiled for.
So
./node_modules/x/.browserslistrc
defines input syntax
./.browserslistrc
defines output syntax.
correct?
I used parcel and svelte for several months,
during which I developed a more accurate postinstall script to solve this issue.
// postinstall.js
const fs = require('fs')
const nodeVersion = 'node 10.11'
// Patch to node_modules/*
const patch = (staticPath) => {
let folderNames = fs.readdirSync(staticPath)
for(let folderName of folderNames){
let stats = fs.statSync(staticPath + '/' + folderName)
if(! stats.isDirectory()) continue
try{
let packageFilePath = `${staticPath}/${folderName}/package.json`
let browserListFilePath = `${staticPath}/${folderName}/.browserslistrc`
let packageFileData = JSON.parse(fs.readFileSync(packageFilePath))
delete packageFileData['browserslist']
fs.writeFileSync(browserListFilePath, nodeVersion)
fs.writeFileSync(packageFilePath, JSON.stringify(packageFileData, null, 2))
// console.log(`Fixed browserlist in ${packageFilePath}`)
// Patch to node_modules/*/node_modules/*
let nestedModulePath = `${staticPath}/${folderName}/node_modules`
if(fs.existsSync(nestedModulePath)) patch(nestedModulePath)
}catch(e) {}
}
}
patch('./node_modules')
console.log(`All browserlist has been updated.`)
Enter the above code into the project main folder to 'postinstall.js'
and add the following script to package.json.
{
"scripts": {
"postinstall": "rm -rf ./.cache && node ./postinstall.ts"
}
}
Now, if you do npm install
, the browser list is automatically patched at the end.
Postinstall does not appear to run automatically on manual module installation commands such as npm install <module name>
, The npm run postinstall
command must be manually executed after you install the manual module install.
@hmmhmmhm
CAUTION
Postinstall does not appear to run automatically on manual module installation commands such as
npm install <module name>
, Thenpm run postinstall
command must be manually executed after you install the manual module install.
as npm hooks mentioned, a not so elegant way to run Postinstall automatically is add node_modules/.hooks/postinstall :
npm run postinstall
remember to run chmod +x node_modules/.hooks/postinstall
, otherwise it maybe permission denied
Does anyone know if the latest version of Parcel still has the same limitation?
I'm asking because @arntj specifically mentioned Parcel v1.* in his comment, so I'm wondering if the new version has a solution to this problem.
But the problem discussed in this thread is that Parcel (v 1.*) doesn't transpile external node modules even if you put a browsers list in your project. The root cause of this is that Parcel assume NPM packages are ES5 by default and thus no need to transpile them.
It's been more than a year and I still think it's not practical to have the tool rely entirely on the community upholding a certain standard.
๐
When you place a .browserslistrc
in a node_modules
external package directory with the JS dialect it's written in (eg node 10
), then parcel is transpiling the external package, which is pretty nice (and still hacky, but easily done with some scripting, see examples above).
However, when a "browserslist"
section already exists in the module package.json
this will get presedence so the .browserslistrc
will be ignored. It took me hours to figure this out so maybe I'll prevent some frustration with others by posting this.
(The script above by hmmhmmhm is actually removing the existing browserslist
key from a package.json
file)
Most helpful comment
I have a feeling people in this thread are talking of two different things as if they were the one and same...
.browserslistrc
is meant to specify what browsers the transpiled code is supposed to be compatible with. Alternatively you could put that in the package.json.But the problem discussed in this thread is that Parcel (v 1.*) doesn't transpile external node modules even if you put a browsers list in your project. The root cause of this is that Parcel assume NPM packages are ES5 by default and thus no need to transpile them.
One then needs to find some way to "convince" Parcel somehow that the NPM package is actually in a very modern form of JavaScript and needs to be transpiled to be browser-compatible. One hacky way of doing this is putting a
.browserslistrc
in the module folder (not the root folder) to make Parcel "understand" that the module actually has a very modern form of JS. That is the purpose of the.browserslistrc.package
discussed above by @icopp.So, while the root
.browserslistrc
should correctly be conservative in what compatibility level they prescribe in order to make sure the transpiled code is compatible in common browsers - the.browserslistrc
that is copied into the NPM modules should actually prescribe a very 'modern' form of JavaScript (e.g.node 10.11
), because they're not used to describe the _output_, but the _input_ for transpilation.