Skip to content

[lexical][lexical-yjs][lexical-playground] Chore: Respect browserslist#8512

Merged
etrepum merged 10 commits into
facebook:mainfrom
levensta:add-eslint-compat
May 17, 2026
Merged

[lexical][lexical-yjs][lexical-playground] Chore: Respect browserslist#8512
etrepum merged 10 commits into
facebook:mainfrom
levensta:add-eslint-compat

Conversation

@levensta
Copy link
Copy Markdown
Contributor

Description

The project has added an eslint plugin that checks features against the current browserslist https://lexical.dev/docs/getting-started/supported-browsers. This will help avoid potential compatibility issues

Closes #6573

Test plan

Before

There is no rule that would automatically check the compatibility of a feature with a specific list of browsers.

After

  • Added eslint-plugin-compat
  • The lookbehind of prohibited characters in the playground has been replaced with a set of allowed characters
  • In lexical-yjs, Object.hasOwn has been replaced with the old safe equivalent Object.prototype.hasOwnProperty

@vercel
Copy link
Copy Markdown

vercel Bot commented May 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment May 17, 2026 11:07pm
lexical-playground Ready Ready Preview, Comment May 17, 2026 11:07pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label May 14, 2026
Comment thread packages/lexical-react/src/shared/useCharacterLimit.ts
Comment thread eslint.config.mjs Outdated
// Compatibility with browserslist
{
...compat.configs['flat/recommended'],
ignores: ['packages/lexical-playground/**'],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think compatibility should be enforced in playgrounds? Theoretically, it's possible to set up checks there and disable some code. Or add a fallback for simple cases like structuredClone

> @lexical/monorepo@0.44.0 lint
> eslint ./


./packages/lexical-playground/src/plugins/AutocompletePlugin/index.tsx
  93:25  error  navigator.userAgentData() is not supported in Safari 15, Firefox 115, Edge 86, Chrome 86  compat/compat

./packages/lexical-playground/src/plugins/ContextMenuPlugin/index.tsx
  70:38  error  navigator.permissions() is not supported in Safari 15  compat/compat
  99:38  error  navigator.permissions() is not supported in Safari 15  compat/compat

./packages/lexical-playground/src/plugins/PagesReactExtension/PageSetupDropdown.tsx
  34:14  error  structuredClone is not supported in Safari 15, Edge 86, Chrome 86  compat/compat

./packages/lexical-playground/src/utils/docSerialization.ts
  40:14  error  CompressionStream is not supported in Safari 15    compat/compat
  61:14  error  DecompressionStream is not supported in Safari 15  compat/compat

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably best practice to keep the compat on in the playground and simply enable/disable features accordingly. It doesn't look like we have a real use case for structuredClone, a shallow {...clone} will work just as well in those two call sites. The other situations could be behind typeof guards maybe with warnOnlyOnce usage to put something in the console on very old environments

@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label May 14, 2026
Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the @ is missing from the negative lookbehind transformation


const URL_REGEX =
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)(?<![-.+():%])/;
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*[a-zA-Z0-9_~#?&//=])?/;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*[a-zA-Z0-9_~#?&//=])?/;
/((https?:\/\/(www\.)?)|(www\.))[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*[a-zA-Z0-9@_~#?&//=])?/;

Comment on lines +52 to +57
it.each([true, false])(
'preserves indentation (CompressionStream: %s)',
async hasCompressionStream => {
if (!hasCompressionStream) {
vi.stubGlobal('CompressionStream', undefined);
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the point of this? There's no CompressionStream related code anywhere in here?

Comment on lines +60 to +61
encoded = encodeURIComponent(json);
CompressionAPIWarning();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we provide a fallback encoding it should decode correctly in implementations that do have the DecompressionStream API. Probably better to just provide a warning and do nothing.

Comment on lines +98 to +99
CompressionAPIWarning();
return JSON.parse(decodeURIComponent(b64));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is primarily going to fail so probably not worth trying

Comment on lines +45 to +61
if (typeof CompressionStream !== 'undefined') {
const cs = new CompressionStream('gzip');
const writer = cs.writable.getWriter();
const [, output] = await Promise.all([
writer
.write(new TextEncoder().encode(JSON.stringify(doc)))
.then(() => writer.close()),
readBytestoString(cs.readable.getReader()),
]);
return `#doc=${btoa(output)
.replace(/\//g, '_')
.replace(/\+/g, '-')
.replace(/=+$/, '')}`;
}

CompressionAPIWarning();
return '';
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would generally reverse the order of this, bail early if something is missing and leave the happy path with less indentation

Comment on lines +71 to +90
if (typeof CompressionStream !== 'undefined') {
const ds = new DecompressionStream('gzip');
const writer = ds.writable.getWriter();
const b64 = atob(m[1].replace(/_/g, '/').replace(/-/g, '+'));
const array = new Uint8Array(b64.length);
for (let i = 0; i < b64.length; i++) {
array[i] = b64.charCodeAt(i);
}
const closed = writer.write(array).then(() => writer.close());
const output = [];
for await (const chunk of generateReader(
ds.readable.pipeThrough(new TextDecoderStream()).getReader(),
)) {
output.push(chunk);
}
await closed;
return JSON.parse(output.join(''));
}
await closed;
return JSON.parse(output.join(''));
CompressionAPIWarning();
return null;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should also re-order this

Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what I had meant, there's no need for an else here

Comment thread packages/lexical-playground/src/utils/docSerialization.ts Outdated
Comment thread packages/lexical-playground/src/utils/docSerialization.ts Outdated
Co-authored-by: Bob Ippolito <bob@redivi.com>
Comment thread packages/lexical-playground/src/utils/docSerialization.ts Outdated
Comment on lines +65 to +69
function getCompressionStream() {
if (typeof CompressionStream !== 'undefined') {
return new CompressionStream('gzip');
}
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skillfully!

@etrepum etrepum added this pull request to the merge queue May 17, 2026
Merged via the queue into facebook:main with commit e65fcd7 May 17, 2026
42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: AutoLinkPlugin's negative lookbehind breaks in older browsers

2 participants