Upgrade to Node22 (#12488)

BlocknoteJS requires an ESM module where our server is CJS, this forced
us to pin the server-util version, which led us to force the resolution
of several packages, leading to bugs downstream.

From Node 22.12 Node supports requiring ESM modules (available from Node
22.0 with a flag). So I upgrade the module.
I picked Node 22 and not Node 23 or Node 24 because 22 is the LTS and we
don't plan to change node versions frequently.

If you remain on Node 18, things should still mostly work, except if you
edit a Rich Text field.

I also starting changing the default runtime for Serverless Functions
which isn't directly related. This means new serverless functions will
be created on Node 22, but we will still need another PR to migrate
existing serverless functions before September (end of support by AWS).

(In this PR I also remove the upgrade commands from 0.43 since they rely
on Blocknote and I didn't want to have to deal with this)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Félix Malfait
2025-06-06 18:35:30 +02:00
committed by GitHub
parent 0188b66280
commit 322c8a1852
39 changed files with 1882 additions and 3146 deletions

View File

@ -0,0 +1,76 @@
import { MutableRefObject } from 'react';
import { combineRefs } from '../combineRefs';
describe('combineRefs', () => {
it('should handle function refs', () => {
const ref1 = jest.fn();
const ref2 = jest.fn();
const node = document.createElement('div');
const combinedRef = combineRefs(ref1, ref2);
combinedRef(node);
expect(ref1).toHaveBeenCalledWith(node);
expect(ref2).toHaveBeenCalledWith(node);
});
it('should handle object refs', () => {
const ref1: MutableRefObject<HTMLDivElement | null> = { current: null };
const ref2: MutableRefObject<HTMLDivElement | null> = { current: null };
const node = document.createElement('div');
const combinedRef = combineRefs(ref1, ref2);
combinedRef(node);
expect(ref1.current).toBe(node);
expect(ref2.current).toBe(node);
});
it('should handle mixed function and object refs', () => {
const funcRef = jest.fn();
const objRef: MutableRefObject<HTMLDivElement | null> = { current: null };
const node = document.createElement('div');
const combinedRef = combineRefs(funcRef, objRef);
combinedRef(node);
expect(funcRef).toHaveBeenCalledWith(node);
expect(objRef.current).toBe(node);
});
it('should handle undefined refs', () => {
const ref1 = jest.fn();
const node = document.createElement('div');
const combinedRef = combineRefs(ref1, undefined);
combinedRef(node);
expect(ref1).toHaveBeenCalledWith(node);
});
it('should handle all undefined refs', () => {
const node = document.createElement('div');
const combinedRef = combineRefs(undefined, undefined);
expect(() => combinedRef(node)).not.toThrow();
});
it('should handle empty refs array', () => {
const node = document.createElement('div');
const combinedRef = combineRefs();
expect(() => combinedRef(node)).not.toThrow();
});
it('should handle null refs', () => {
const ref1 = jest.fn();
const node = document.createElement('div');
const combinedRef = combineRefs(ref1, null);
combinedRef(node);
expect(ref1).toHaveBeenCalledWith(node);
});
});

View File

@ -1,4 +1,4 @@
import { getDirtyFields } from './getDirtyFields';
import { getDirtyFields } from '~/utils/getDirtyFields';
describe('getDirtyFields', () => {
it('should return all defined fields from draft when persisted is null', () => {

View File

@ -0,0 +1,84 @@
import { isEmptyObject } from '../isEmptyObject';
describe('isEmptyObject', () => {
it('should return true for empty object', () => {
expect(isEmptyObject({})).toBe(true);
});
it('should return false for object with properties', () => {
expect(isEmptyObject({ key: 'value' })).toBe(false);
});
it('should return false for object with multiple properties', () => {
expect(isEmptyObject({ key1: 'value1', key2: 'value2' })).toBe(false);
});
it('should return false for null', () => {
expect(isEmptyObject(null)).toBe(false);
});
it('should return false for undefined', () => {
expect(isEmptyObject(undefined)).toBe(false);
});
it('should return false for string', () => {
expect(isEmptyObject('test')).toBe(false);
});
it('should return false for number', () => {
expect(isEmptyObject(42)).toBe(false);
});
it('should return false for boolean', () => {
expect(isEmptyObject(true)).toBe(false);
expect(isEmptyObject(false)).toBe(false);
});
it('should return true for empty array (as it has no enumerable keys)', () => {
expect(isEmptyObject([])).toBe(true);
});
it('should return false for non-empty array with enumerable properties', () => {
const arr = [1, 2, 3];
expect(isEmptyObject(arr)).toBe(false);
});
it('should return false for function', () => {
expect(isEmptyObject(() => {})).toBe(false);
});
it('should return true for Date object (as it has no enumerable keys)', () => {
expect(isEmptyObject(new Date())).toBe(true);
});
it('should return true for object created with Object.create(null)', () => {
expect(isEmptyObject(Object.create(null))).toBe(true);
});
it('should return true for object with inherited properties only', () => {
const parent = { parentProp: 'value' };
const child = Object.create(parent);
expect(isEmptyObject(child)).toBe(true); // Only checks own enumerable properties
});
it('should return true for object with non-enumerable properties only', () => {
const obj = {};
Object.defineProperty(obj, 'nonEnumerableProp', {
value: 'test',
enumerable: false,
});
expect(isEmptyObject(obj)).toBe(true); // Object.keys only returns enumerable properties
});
it('should return false for array with custom properties', () => {
const arr: any = [];
arr.customProp = 'value';
expect(isEmptyObject(arr)).toBe(false);
});
it('should return false for Date object with custom properties', () => {
const date: any = new Date();
date.customProp = 'value';
expect(isEmptyObject(date)).toBe(false);
});
});

View File

@ -0,0 +1,12 @@
import { isInFrame } from '../isInIframe';
describe('isInFrame', () => {
it('should return a boolean value', () => {
const result = isInFrame();
expect(typeof result).toBe('boolean');
});
it('should not throw an error when called', () => {
expect(() => isInFrame()).not.toThrow();
});
});

View File

@ -0,0 +1,51 @@
import { logDebug } from '../logDebug';
describe('logDebug', () => {
let consoleDebugSpy: jest.SpyInstance;
beforeEach(() => {
consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {});
});
afterEach(() => {
consoleDebugSpy.mockRestore();
});
it('should call console.debug with the provided message', () => {
const debugMessage = 'Test debug message';
logDebug(debugMessage);
expect(consoleDebugSpy).toHaveBeenCalledWith(debugMessage, []);
expect(consoleDebugSpy).toHaveBeenCalledTimes(1);
});
it('should handle optional parameters', () => {
const debugMessage = 'Test debug message';
const param1 = 'param1';
const param2 = { key: 'value' };
logDebug(debugMessage, param1, param2);
expect(consoleDebugSpy).toHaveBeenCalledWith(debugMessage, [
param1,
param2,
]);
});
it('should handle no optional parameters', () => {
const debugMessage = 'Test debug message';
logDebug(debugMessage);
expect(consoleDebugSpy).toHaveBeenCalledWith(debugMessage, []);
});
it('should handle null and undefined messages', () => {
logDebug(null);
expect(consoleDebugSpy).toHaveBeenCalledWith(null, []);
logDebug(undefined);
expect(consoleDebugSpy).toHaveBeenCalledWith(undefined, []);
});
});

View File

@ -0,0 +1,46 @@
import { logError } from '../logError';
describe('logError', () => {
let consoleErrorSpy: jest.SpyInstance;
beforeEach(() => {
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
consoleErrorSpy.mockRestore();
});
it('should call console.error with the provided message', () => {
const errorMessage = 'Test error message';
logError(errorMessage);
expect(consoleErrorSpy).toHaveBeenCalledWith(errorMessage);
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
});
it('should handle object messages', () => {
const errorObject = { error: 'Test error', code: 500 };
logError(errorObject);
expect(consoleErrorSpy).toHaveBeenCalledWith(errorObject);
});
it('should handle null and undefined messages', () => {
logError(null);
expect(consoleErrorSpy).toHaveBeenCalledWith(null);
logError(undefined);
expect(consoleErrorSpy).toHaveBeenCalledWith(undefined);
});
it('should handle Error objects', () => {
const error = new Error('Test error');
logError(error);
expect(consoleErrorSpy).toHaveBeenCalledWith(error);
});
});