As it seems right now graphql-js 17 will ship without a commonjs solution.
Since ESM modules can only be loaded with a dynamic import within a commonjs context that will make a lot of libraries that support multiple graphql-js versions (commonjs and ESM in GraphQL.js 16 and ESM only in GraphQL.js 17) AND both CommonJS and ESM a pain to use and maintain.
Before making the decision to only ship ESM, there should be a clear path for those libraries. Otherwise, the ecosystem around graphql-js will result in kind of a broken state.
There was no clear description provided of why graphql-js 17 will be ESM only in the PR: #3552
It would be great to hear the benefit and intention of dropping CommonJS support.
To me, it seems like the Node.js ESM ecosystem is not major enough to justify dropping CommonJS.
A common reason for going ESM only is the "dual package hazard" issue, where a module is loaded as both ESM and CJS in the same application resulting in subtle bugs e.g. when relying on instanceof checks.
Instead could be another solution to add something like this to the "entry" modules to ensure only one version of GraphQL.js is loaded? Are there use-cases in which you would want to run two graphql-js versions alongside each other?
if (globalThis[`_______graphql${version}`]) {
throw new Error("Please fix your resolutions. <explanation and link for setting yarn and npm resolution>")
}
globalThis[`_______graphql${version}`] = true
However, this would still not solve the issue of an esm-only dependency requiring esm graphql and another cjs-only dependency requiring cjs graphql...
A possible workaround could be to instruct people to do ad-hoc esm graphql to cjs graphql conversions... I already tested this method...
scripts/cjsify-graphql.js
"use strict";
const path = require("path");
const fs = require("fs");
const babel = require("@babel/core");
const cjsTransform = require("@babel/plugin-transform-modules-commonjs");
const glob = require("glob");
const graphqlPath = path.dirname(require.resolve("graphql"));
const pkgJSONPath = path.join(graphqlPath, "package.json");
const oldPkgJSONPath = path.join(graphqlPath, "package.json.backup");
const fileExists = (path) => {
try {
fs.statSync(path);
} catch (_) {
return false;
}
return true;
};
console.log("Need some graphql commonjs?");
if (fileExists(oldPkgJSONPath)) {
console.log("already applied everything. all good.");
process.exit(0);
}
const pkgJSON = JSON.parse(fs.readFileSync(pkgJSONPath, "utf-8"));
fs.copyFileSync(pkgJSONPath, oldPkgJSONPath);
for (const [exportName, exportPathName] of Object.entries(pkgJSON.exports)) {
pkgJSON.exports[exportName] = {
import: exportPathName,
require: exportPathName.replace(".js", ".cjs"),
};
}
fs.writeFileSync(pkgJSONPath, JSON.stringify(pkgJSON, null, 2));
const files = glob.sync(path.join(graphqlPath, "**", "*.js"));
for (const file of files) {
console.log("process", file);
const cjsRegex = /from '(.*).js';$/gm;
fs.writeFileSync(
file.replace(".js", ".cjs"),
babel.transform(
fs
.readFileSync(file, "utf-8")
.replace(cjsRegex, (str, match1) => `from '${match1}.cjs'`),
{
plugins: [cjsTransform],
babelrc: false,
filename: file,
}
).code
);
}
Related PR links:
As it seems right now graphql-js 17 will ship without a commonjs solution.
Since ESM modules can only be loaded with a dynamic import within a commonjs context that will make a lot of libraries that support multiple graphql-js versions (commonjs and ESM in GraphQL.js 16 and ESM only in GraphQL.js 17) AND both CommonJS and ESM a pain to use and maintain.
Before making the decision to only ship ESM, there should be a clear path for those libraries. Otherwise, the ecosystem around graphql-js will result in kind of a broken state.
There was no clear description provided of why graphql-js 17 will be ESM only in the PR: #3552
It would be great to hear the benefit and intention of dropping CommonJS support.
To me, it seems like the Node.js ESM ecosystem is not major enough to justify dropping CommonJS.
A common reason for going ESM only is the "dual package hazard" issue, where a module is loaded as both ESM and CJS in the same application resulting in subtle bugs e.g. when relying on
instanceofchecks.Instead could be another solution to add something like this to the "entry" modules to ensure only one version of GraphQL.js is loaded? Are there use-cases in which you would want to run two
graphql-jsversions alongside each other?However, this would still not solve the issue of an esm-only dependency requiring esm graphql and another cjs-only dependency requiring cjs graphql...
A possible workaround could be to instruct people to do ad-hoc esm graphql to cjs graphql conversions... I already tested this method...
scripts/cjsify-graphql.js
Related PR links:
instanceofissue impacts graphql-js-wg#63