Skip to content
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

fix(form-data): fix ssr error due to window object access #373

Merged
merged 2 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions src/httpsnippet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { map as eventStreamMap } from 'event-stream';
import FormData from 'form-data';
import FormData from 'form-data/lib/form_data';
import { Param, PostDataCommon, Request as NpmHarRequest } from 'har-format';
import { validateRequest } from 'har-validator-compiled';
import { stringify as queryStringify } from 'querystring';
Expand All @@ -13,6 +13,13 @@ import { ClientId, TargetId, targets } from './targets/targets';
export { availableTargets, extname } from './helpers/utils';
export { addTarget, addTargetClient } from './targets/targets';

// We're implementing the logic for which FormData object to use, ourselves.
// This allows us to use the native FormData object in the browser and the `form-data` module in Node,
// instead of relying on the package entrypoint to handle that.
const resolveFormData =
// @ts-expect-error — we're only using window.FormData if it exists
typeof window !== 'undefined' && window.FormData ? window.FormData : FormData;

const DEBUG_MODE = false;

const debug = {
Expand Down Expand Up @@ -174,7 +181,7 @@ export class HTTPSnippet {
request.postData.mimeType = 'multipart/form-data';

if (request.postData?.params) {
const form = new FormData();
const form = new resolveFormData();

// The `form-data` module returns one of two things: a native FormData object, or its own polyfill
// Since the polyfill does not support the full API of the native FormData object, when this library is running in a browser environment it'll fail on two things:
Expand All @@ -186,15 +193,13 @@ export class HTTPSnippet {
// Since the native FormData object is iterable, we easily detect what version of `form-data` we're working with here to allow `multipart/form-data` requests to be compiled under both browser and Node environments.
//
// This hack is pretty awful but it's the only way we can use this library in the browser as if we code this against just the native FormData object, we can't polyfill that back into Node because Blob and File objects, which something like `formdata-polyfill` requires, don't exist there.
// @ts-expect-error TODO
const isNativeFormData = typeof form[Symbol.iterator] === 'function';

// TODO: THIS ABSOLUTELY MUST BE REMOVED.
// IT BREAKS SOME USE-CASES FOR MULTIPART FORMS THAT DEPEND ON BEING ABLE TO SET THE BOUNDARY.
// easter egg
const boundary = '---011000010111000001101001'; // this is binary for "api". yep.
if (!isNativeFormData) {
// @ts-expect-error THIS IS WRONG. VERY WRONG.
form._boundary = boundary;
}

Expand All @@ -205,16 +210,13 @@ export class HTTPSnippet {

if (isNativeFormData) {
if (isBlob(value)) {
// @ts-expect-error TODO
form.append(name, value, filename);
} else {
form.append(name, value);
}
} else {
form.append(name, value, {
// @ts-expect-error TODO
filename,
// @ts-expect-error TODO
contentType: param.contentType || null,
});
}
Expand Down
4 changes: 4 additions & 0 deletions src/types/form-data.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module 'form-data/lib/form_data' {
import FormData from 'form-data';
export default FormData;
}
Loading