Skip to content

Commit

Permalink
fix: CodeNodeEditor walk cannot read properties of null (#11129)
Browse files Browse the repository at this point in the history
Co-authored-by: Elias Meire <[email protected]>
  • Loading branch information
michael-radency and elsmr authored Nov 21, 2024
1 parent 40dd02f commit d99e0a7
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 13 deletions.
44 changes: 44 additions & 0 deletions packages/editor-ui/src/components/CodeNodeEditor/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as esprima from 'esprima-next';
import { walk } from './utils';

describe('CodeNodeEditor utils', () => {
describe('walk', () => {
it('should find the correct syntax nodes', () => {
const code = `const x = 'a'
function f(arg) {
arg['b'] = 1
return arg
}
const y = f({ a: 'c' })
`;
const program = esprima.parse(code);
const stringLiterals = walk(
program,
(node) => node.type === esprima.Syntax.Literal && typeof node.value === 'string',
);
expect(stringLiterals).toEqual([
new esprima.Literal('a', "'a'"),
new esprima.Literal('b', "'b'"),
new esprima.Literal('c', "'c'"),
]);
});

it('should handle null syntax nodes', () => {
// ,, in [1,,2] results in a `null` ArrayExpressionElement
const code = 'const fn = () => [1,,2]';
const program = esprima.parse(code);
const arrayExpressions = walk(
program,
(node) => node.type === esprima.Syntax.ArrayExpression,
);
expect(arrayExpressions).toEqual([
new esprima.ArrayExpression([
new esprima.Literal(1, '1'),
null,
new esprima.Literal(2, '2'),
]),
]);
});
});
});
30 changes: 17 additions & 13 deletions packages/editor-ui/src/components/CodeNodeEditor/utils.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import type * as esprima from 'esprima-next';
import * as esprima from 'esprima-next';
import type { Completion } from '@codemirror/autocomplete';
import type { Node } from 'estree';
import type { RangeNode } from './types';
import { sanitizeHtml } from '@/utils/htmlUtils';
import type { Node } from 'estree';

export function walk<T extends RangeNode>(
node: Node | esprima.Program,
test: (node: Node) => boolean,
found: Node[] = [],
) {
// @ts-ignore
if (test(node)) found.push(node);
const isProgram = node.type === esprima.Syntax.Program;
if (!isProgram && test(node)) found.push(node);

for (const key in node) {
if (!(key in node)) continue;
if (isProgram) {
node.body.forEach((n) => walk(n as Node, test, found));
} else {
for (const key in node) {
if (!(key in node)) continue;

// @ts-ignore
const child = node[key];
// @ts-expect-error Node is not string indexable, but it has many possible properties
const child = node[key];

if (child === null || typeof child !== 'object') continue;
if (child === null || typeof child !== 'object') continue;

if (Array.isArray(child)) {
child.forEach((node) => walk(node, test, found));
} else {
walk(child, test, found);
if (Array.isArray(child)) {
child.filter(Boolean).forEach((n) => walk(n, test, found));
} else {
walk(child, test, found);
}
}
}

Expand Down

0 comments on commit d99e0a7

Please sign in to comment.