-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Problem bundling simple jsdom example - request.resolve #1311
Comments
At the moment the use case for
The bundler doesn't yet do this automatically for you though. |
Thanks @evanw that seems like a reasonable workaround for this unusual scenario. I actually removed the need for jsdom to get around it in my project but satisfied myself your solution would work with a bit of jigging around! |
For people having an issues with const fs = require('fs');
const jsdomPatch = {
name: 'jsdom-patch',
setup(build) {
build.onLoad({ filter: /jsdom\/living\/xhr\/XMLHttpRequest-impl\.js$/ }, async args => {
let contents = await fs.promises.readFile(args.path, 'utf8');
contents = contents.replace(
'const syncWorkerFile = require.resolve ? require.resolve("./xhr-sync-worker.js") : null;',
`const syncWorkerFile = "${require.resolve('jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js')}";`,
);
return { contents, loader: 'js' };
});
},
}; |
Thanks for that @alexgorbatchev! Looks like some of the paths have changed since your post, this works for jsdom 18: const fs = require('fs');
const jsdomPatch = {
name: 'jsdom-patch',
setup(build) {
- build.onLoad({ filter: /jsdom\/living\/xhr\/XMLHttpRequest-impl\.js$/ }, async args => {
+ build.onLoad({ filter: /jsdom\/living\/xmlhttprequest\.js$/ }, async (args) => {
let contents = await fs.promises.readFile(args.path, 'utf8');
contents = contents.replace(
'const syncWorkerFile = require.resolve ? require.resolve("./xhr-sync-worker.js") : null;',
- `const syncWorkerFile = "${require.resolve('jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js')}";`,
+ `const syncWorkerFile = "${require.resolve('jsdom/lib/jsdom/living/xhr-sync-worker.js')}";`,
);
return { contents, loader: 'js' };
});
},
}; |
And changed again. This works for jsdom 19:
|
Update for latest version and support windows file path:
|
Here is a version of the fix that works from // esbuild.mjs
import fs from "fs"
import module from "module"
import path from "path"
import esbuild from "esbuild"
import { sassPlugin } from "esbuild-sass-plugin"
import svg from "esbuild-plugin-svg"
import postcss from "postcss"
import copyAssets from "postcss-copy-assets"
function parse(data) {
data = data.toString("utf-8")
//
// Remove a possible UTF-8 BOM (byte order marker) as this can lead to parse
// values when passed in to the JSON.parse.
//
if (data.charCodeAt(0) === 0xfeff) data = data.slice(1)
try {
return JSON.parse(data)
} catch (e) {
return false
}
}
var iteratorSymbol =
typeof Symbol === "function" && typeof Symbol.iterator === "symbol"
? Symbol.iterator
: null
function addSymbolIterator(result) {
if (!iteratorSymbol) {
return result
}
result[iteratorSymbol] = function () {
return this
}
return result
}
function findPackageJson(root) {
root = root || process.cwd()
if (typeof root !== "string") {
if (typeof root === "object" && typeof root.filename === "string") {
root = root.filename
} else {
throw new Error(
"Must pass a filename string or a module object to finder"
)
}
}
return addSymbolIterator({
/**
* Return the parsed package.json that we find in a parent folder.
*
* @returns {Object} Value, filename and indication if the iteration is done.
* @api public
*/
next: function next() {
if (root.match(/^(\w:\\|\/)$/))
return addSymbolIterator({
value: undefined,
filename: undefined,
done: true,
})
var file = path.join(root, "package.json"),
data
root = path.resolve(root, "..")
if (fs.existsSync(file) && (data = parse(fs.readFileSync(file)))) {
data.__path = file
return addSymbolIterator({
value: data,
filename: file,
done: false,
})
}
return next()
},
})
}
const EXTENSIONS = {
".cjs": "dynamic",
".mjs": "module",
".es": "module",
".es6": "module",
".node": "addon",
".json": "json",
".wasm": "wasm",
}
async function requireResolve(specifier, parent, system) {
try {
// Let the default resolve algorithm try first
let { url, format } = system(specifier, parent)
// Resolve symlinks
if (url.startsWith("file://")) {
const realpath = await fs.promises.realpath(url.replace("file://", ""))
url = `file://${realpath}`
}
return { url, format }
} catch (error) {
const base = parent
? path.dirname(parent.replace("file://", ""))
: process.cwd()
const require = module.createRequire(path.join(base, specifier))
let modulePath
try {
modulePath = require.resolve(specifier)
} catch (e) {
// .cjs is apparently not part of the default resolution algorithm,
// so check if .cjs file exists before bailing completely
modulePath = require.resolve(`${specifier}.cjs`)
}
const ext = path.extname(modulePath)
let format = EXTENSIONS[ext] || "module"
// Mimic default behavior of treating .js[x]? as ESM iff
// relevant package.json contains { "type": "module" }
if (!ext || [".js", ".jsx"].includes(ext)) {
const dir = path.dirname(modulePath)
const pkgdef = findPackageJson(dir).next()
const type = pkgdef && pkgdef.value && pkgdef.value.type
format = type === "module" ? "module" : "dynamic"
}
modulePath = await fs.promises.realpath(modulePath)
return { url: `file://${path}`, format }
}
}
const jsdomPatch = {
name: "jsdom-patch",
setup(build) {
build.onLoad({ filter: /XMLHttpRequest-impl\.js$/ }, async (args) => {
let contents = await fs.promises.readFile(args.path, "utf8")
contents = contents.replace(
'const syncWorkerFile = require.resolve ? require.resolve("./xhr-sync-worker.js") : null;',
`const syncWorkerFile = "${await requireResolve(
"jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js"
)}";`.replaceAll("\\", process.platform === "win32" ? "\\\\" : "\\")
)
return { contents, loader: "js" }
})
},
}
esbuild
.build({
entryPoints: ["src/cli.ts"],
platform: "node",
bundle: true,
sourcemap: true,
outfile: "dist/cli.cjs",
plugins: [
sassPlugin({
type: "style",
async transform(source, _resolveDir, filePath) {
const { css } = await postcss()
.use(copyAssets({ base: `public` }))
.process(source, { from: filePath, to: `public/index.css` })
return css
},
}),
svg(),
jsdomPatch,
],
loader: { ".js": "jsx", ".tsx": "tsx", ".ts": "tsx" },
jsxFactory: "h",
jsxFragment: "Fragment",
jsx: "automatic",
external: ["canvas"],
inject: ["./src/document_shim.ts"],
})
.catch(() => process.exit(1)) // src/document_shim.js
import { JSDOM } from "jsdom"
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`)
global.document = dom.window.document |
If the above solutions did not work for you, this is what I ended up using as a plugin configuration that worked. Nodejs env that uses esbuild (trigger.dev to be specific) {
name: "jsdom-patch",
setup(build) {
build.onLoad({ filter: /XMLHttpRequest-impl\.js$/ }, async (args) => {
let contents = await fs.promises.readFile(args.path, "utf8");
contents = contents.replace(
'const syncWorkerFile = require.resolve ? require.resolve("./xhr-sync-worker.js") : null;',
`const syncWorkerFile = "${require.resolve("jsdom/lib/jsdom/living/xhr/xhr-sync-worker.js")}";`
);
return {
contents,
loader: "js",
resolveDir: path.dirname(args.path),
};
});
},
} |
if somebody came and still have this issue, and nothing worked. (maybe like in our case, we have big monorepo with many micro services) and the paths is kinda not working I suggest to simply disable for our production was created as follow.
|
I have a simple
jsdom
example program:I am bundling with the following command:
This gives the following warning:
When running
node out.js
(not surprisingly) it gives an error:Am not sure how to get past this. I tried using the API and custom resolver / loader plugins but it seems this is not the right approach because in both cases the
require.resolve
is still emitted.Many thanks
The text was updated successfully, but these errors were encountered: