feat: add xml linter to code mirror (#306)
This commit is contained in:
29
package-lock.json
generated
29
package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.0.0",
|
||||
"@codemirror/lang-xml": "^6.0.0",
|
||||
"@codemirror/lint": "^6.2.1",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@reduxjs/toolkit": "^1.8.1",
|
||||
@@ -31,7 +32,8 @@
|
||||
"reselect": "^4.1.5",
|
||||
"tinymce": "^5.10.4",
|
||||
"video-react": "^0.15.0",
|
||||
"video.js": "^7.18.1"
|
||||
"video.js": "^7.18.1",
|
||||
"xmlchecker": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "^11.0.2",
|
||||
@@ -2824,9 +2826,9 @@
|
||||
"integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw=="
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.0.tgz",
|
||||
"integrity": "sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz",
|
||||
"integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
@@ -34451,6 +34453,14 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlchecker": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlchecker/-/xmlchecker-0.1.0.tgz",
|
||||
"integrity": "sha512-ElfXP7Fse9G37go0mjD5a7zY9fnE19BHk1qbH02uXX/O6Gi5Be2TPCg3zHG7ZIzV2yoGr/Gybfw/Z2Cs62tDxw==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -36532,9 +36542,9 @@
|
||||
}
|
||||
},
|
||||
"@codemirror/lint": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.0.tgz",
|
||||
"integrity": "sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==",
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz",
|
||||
"integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==",
|
||||
"requires": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
@@ -60979,6 +60989,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"xmlchecker": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlchecker/-/xmlchecker-0.1.0.tgz",
|
||||
"integrity": "sha512-ElfXP7Fse9G37go0mjD5a7zY9fnE19BHk1qbH02uXX/O6Gi5Be2TPCg3zHG7ZIzV2yoGr/Gybfw/Z2Cs62tDxw=="
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.0.0",
|
||||
"@codemirror/lang-xml": "^6.0.0",
|
||||
"@codemirror/lint": "^6.2.1",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@reduxjs/toolkit": "^1.8.1",
|
||||
@@ -79,7 +80,8 @@
|
||||
"reselect": "^4.1.5",
|
||||
"tinymce": "^5.10.4",
|
||||
"video-react": "^0.15.0",
|
||||
"video.js": "^7.18.1"
|
||||
"video.js": "^7.18.1",
|
||||
"xmlchecker": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": ">1.15.0",
|
||||
|
||||
@@ -115,7 +115,7 @@ exports[`EditorFooter render snapshot: save failed. Show error message 1`] = `
|
||||
show={true}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Error: Content save failed. Try again later."
|
||||
defaultMessage="Error: Content save failed. Please check recent changes and try again later."
|
||||
description="Error message displayed when content fails to save."
|
||||
id="authoring.editorfooter.save.error"
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ const messages = defineMessages({
|
||||
|
||||
contentSaveFailed: {
|
||||
id: 'authoring.editorfooter.save.error',
|
||||
defaultMessage: 'Error: Content save failed. Try again later.',
|
||||
defaultMessage: 'Error: Content save failed. Please check recent changes and try again later.',
|
||||
description: 'Error message displayed when content fails to save.',
|
||||
},
|
||||
cancelButtonAriaLabel: {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import xmlChecker from 'xmlchecker';
|
||||
|
||||
import { basicSetup } from 'codemirror';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import { html } from '@codemirror/lang-html';
|
||||
import { xml } from '@codemirror/lang-xml';
|
||||
|
||||
import { linter } from '@codemirror/lint';
|
||||
import alphanumericMap from './constants';
|
||||
import './index.scss';
|
||||
|
||||
@@ -27,6 +28,32 @@ export const cleanHTML = ({ initialText }) => {
|
||||
return initialText.replace(translateRegex, translator);
|
||||
};
|
||||
|
||||
export const syntaxChecker = ({ textArr, lang }) => {
|
||||
const diagnostics = [];
|
||||
if (lang === 'xml') {
|
||||
const docString = textArr.join('\n');
|
||||
const xmlDoc = `<?xml version="1.0" encoding="UTF-8"?> ${docString}`;
|
||||
|
||||
try {
|
||||
xmlChecker.check(xmlDoc);
|
||||
} catch (error) {
|
||||
let errorStart = 0;
|
||||
for (let i = 0; i < error.line - 1; i++) {
|
||||
errorStart += textArr[i].length;
|
||||
}
|
||||
const errorLine = error.line;
|
||||
const errorEnd = errorStart + textArr[errorLine - 1].length;
|
||||
diagnostics.push({
|
||||
from: errorStart,
|
||||
to: errorEnd,
|
||||
severity: 'error',
|
||||
message: `${error.name}: ${error.message}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
return diagnostics;
|
||||
};
|
||||
|
||||
export const createCodeMirrorDomNode = ({
|
||||
ref,
|
||||
initialText,
|
||||
@@ -38,7 +65,15 @@ export const createCodeMirrorDomNode = ({
|
||||
const cleanText = cleanHTML({ initialText });
|
||||
const newState = EditorState.create({
|
||||
doc: cleanText,
|
||||
extensions: [basicSetup, languageExtension, EditorView.lineWrapping],
|
||||
extensions: [
|
||||
basicSetup,
|
||||
languageExtension,
|
||||
EditorView.lineWrapping,
|
||||
linter((view) => {
|
||||
const textArr = view.docView.view.viewState.state.doc.text;
|
||||
return syntaxChecker({ textArr, lang });
|
||||
}),
|
||||
],
|
||||
});
|
||||
const view = new EditorView({ state: newState, parent: ref.current });
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
||||
@@ -114,6 +114,35 @@ describe('CodeEditor', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('xmlSyntaxChecker', () => {
|
||||
describe('lang equals html', () => {
|
||||
it('returns empty array', () => {
|
||||
const textArr = ['<problem>', '<p>', 'this is some text', '</p>', '</problem>'];
|
||||
const diagnostics = hooks.syntaxChecker({ textArr, lang: 'html' });
|
||||
expect(diagnostics).toEqual([]);
|
||||
});
|
||||
});
|
||||
describe('lang equals xml', () => {
|
||||
it('returns empty array', () => {
|
||||
const textArr = ['<problem>', '<p>', 'this is some text', '</p>', '</problem>'];
|
||||
const diagnostics = hooks.syntaxChecker({ textArr, lang: 'xml' });
|
||||
expect(diagnostics).toEqual([]);
|
||||
});
|
||||
it('returns an array with error object', () => {
|
||||
const textArr = ['<problem>', '<p>', '<p>', 'this is some text', '</p>', '</problem>'];
|
||||
const expectedDiagnostics = hooks.syntaxChecker({ textArr, lang: 'xml' });
|
||||
const diagnostics = [{
|
||||
from: 9,
|
||||
to: 12,
|
||||
severity: 'error',
|
||||
message: 'SyntaxError: Expected that start and end tag must be identical but "<" found.',
|
||||
}];
|
||||
expect(expectedDiagnostics).toEqual(diagnostics);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component', () => {
|
||||
describe('Snapshots', () => {
|
||||
const mockHideBtn = jest.fn().mockName('mockHidebtn');
|
||||
|
||||
Reference in New Issue
Block a user