Move AI provider usage to the backend (#18562)

GitOrigin-RevId: 4f66c6576571c4fbb7381d8d0e34f2e468d6f34f
This commit is contained in:
Alf Eaton 2024-06-17 11:34:09 +01:00 committed by Copybot
parent e36de5a62d
commit b9a8a7f7ec
18 changed files with 147 additions and 141 deletions

132
package-lock.json generated
View file

@ -14708,17 +14708,6 @@
"node": ">= 6.0.0"
}
},
"node_modules/agentkeepalive": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
"dependencies": {
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@ -21858,6 +21847,14 @@
"resolved": "https://registry.npmjs.org/events-listener/-/events-listener-1.1.0.tgz",
"integrity": "sha512-Kd3EgYfODHueq6GzVfs/VUolh2EgJsS8hkO3KpnDrxVjU3eq63eXM2ujXkhPP+OkeUOhL8CxdfZbQXzryb5C4g=="
},
"node_modules/eventsource-parser": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
"integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==",
"engines": {
"node": ">=14.18"
}
},
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -25275,14 +25272,6 @@
"node": ">=8.12.0"
}
},
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"dependencies": {
"ms": "^2.0.0"
}
},
"node_modules/hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
@ -28840,23 +28829,6 @@
"micromark-util-types": "^2.0.0"
}
},
"node_modules/micromark-extension-gfm-table": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz",
"integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==",
"dev": true,
"dependencies": {
"devlop": "^1.0.0",
"micromark-factory-space": "^2.0.0",
"micromark-util-character": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-factory-destination": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz",
@ -30897,32 +30869,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openai": {
"version": "4.36.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.36.0.tgz",
"integrity": "sha512-AtYrhhWY64LhB9P6f3H0nV8nTSaQJ89mWPnfNU5CnYg81zlYaV8nkyO+aTNfprdqP/9xv10woNNUgefXINT4Dg==",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"bin": {
"openai": "bin/cli"
}
},
"node_modules/openai/node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"engines": {
"node": ">= 8"
}
},
"node_modules/openapi3-ts": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-3.1.2.tgz",
@ -44630,6 +44576,7 @@
"east": "^2.0.2",
"ejs": "^3.1.10",
"email-addresses": "^5.0.0",
"eventsource-parser": "^1.1.2",
"express": "^4.19.2",
"express-bearer-token": "^2.4.0",
"express-http-proxy": "^1.6.0",
@ -44661,7 +44608,6 @@
"nodemailer": "^6.7.0",
"nodemailer-ses-transport": "^1.5.1",
"on-headers": "^1.0.2",
"openai": "^4.36.0",
"otplib": "^12.0.1",
"p-limit": "^2.3.0",
"p-props": "4.0.0",
@ -44833,7 +44779,6 @@
"mathjax": "^3.2.2",
"mensch": "^0.3.4",
"micromark": "^4.0.0",
"micromark-extension-gfm-table": "^2.0.0",
"mini-css-extract-plugin": "^2.7.6",
"mocha": "^10.2.0",
"mocha-each": "^2.0.1",
@ -53295,6 +53240,7 @@
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"events": "^3.3.0",
"eventsource-parser": "^1.1.2",
"expose-loader": "^4.1.0",
"express": "^4.19.2",
"express-bearer-token": "^2.4.0",
@ -53331,7 +53277,6 @@
"mensch": "^0.3.4",
"method-override": "^2.3.3",
"micromark": "^4.0.0",
"micromark-extension-gfm-table": "^2.0.0",
"mini-css-extract-plugin": "^2.7.6",
"minimatch": "^7.4.2",
"minimist": "^1.2.7",
@ -53349,7 +53294,6 @@
"nodemailer-ses-transport": "^1.5.1",
"nvd3": "^1.8.6",
"on-headers": "^1.0.2",
"openai": "^4.36.0",
"otplib": "^12.0.1",
"p-limit": "^2.3.0",
"p-props": "4.0.0",
@ -59198,14 +59142,6 @@
"debug": "4"
}
},
"agentkeepalive": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
"requires": {
"humanize-ms": "^1.2.1"
}
},
"aggregate-error": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
@ -64617,6 +64553,11 @@
"resolved": "https://registry.npmjs.org/events-listener/-/events-listener-1.1.0.tgz",
"integrity": "sha512-Kd3EgYfODHueq6GzVfs/VUolh2EgJsS8hkO3KpnDrxVjU3eq63eXM2ujXkhPP+OkeUOhL8CxdfZbQXzryb5C4g=="
},
"eventsource-parser": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
"integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA=="
},
"execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@ -67268,14 +67209,6 @@
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
"dev": true
},
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"requires": {
"ms": "^2.0.0"
}
},
"hyphenate-style-name": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
@ -70942,19 +70875,6 @@
"micromark-util-types": "^2.0.0"
}
},
"micromark-extension-gfm-table": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz",
"integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==",
"dev": true,
"requires": {
"devlop": "^1.0.0",
"micromark-factory-space": "^2.0.0",
"micromark-util-character": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
}
},
"micromark-factory-destination": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz",
@ -72380,28 +72300,6 @@
"is-wsl": "^2.2.0"
}
},
"openai": {
"version": "4.36.0",
"resolved": "https://registry.npmjs.org/openai/-/openai-4.36.0.tgz",
"integrity": "sha512-AtYrhhWY64LhB9P6f3H0nV8nTSaQJ89mWPnfNU5CnYg81zlYaV8nkyO+aTNfprdqP/9xv10woNNUgefXINT4Dg==",
"requires": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7",
"web-streams-polyfill": "^3.2.1"
},
"dependencies": {
"web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="
}
}
},
"openapi3-ts": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-3.1.2.tgz",

View file

@ -670,6 +670,7 @@ const _ProjectController = {
debugPdfDetach,
showSymbolPalette,
symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
showAiErrorAssistant: user.alphaProgram, // TODO: labs experiment
detachRole,
metadata: { viewport: false },
showUpgradePrompt,

View file

@ -8,6 +8,7 @@ const VALID_KEYS = [
'writefull-integration',
'writefull-oauth-promotion',
'bib-file-tpr-prompt',
'ai-error-assistant-consent',
]
async function completeTutorial(req, res, next) {

View file

@ -21,6 +21,7 @@ meta(name="ol-pdfjsVariant" content=pdfjsVariant)
meta(name="ol-debugPdfDetach" data-type="boolean" content=debugPdfDetach)
meta(name="ol-showSymbolPalette" data-type="boolean" content=showSymbolPalette)
meta(name="ol-symbolPaletteAvailable" data-type="boolean" content=symbolPaletteAvailable)
meta(name="ol-showAiErrorAssistant" data-type="boolean" content=showAiErrorAssistant)
meta(name="ol-detachRole" data-type="string" content=detachRole)
meta(name="ol-allowedImageNames" data-type="json" content=allowedImageNames)
meta(name="ol-languages" data-type="json" content=languages)

View file

@ -876,6 +876,7 @@ module.exports = {
sourceEditorExtensions: [],
sourceEditorComponents: [],
pdfLogEntryComponents: [],
diagnosticActions: [],
sourceEditorCompletionSources: [],
sourceEditorSymbolPalette: [],
sourceEditorToolbarComponents: [],

View file

@ -5,6 +5,7 @@ import PreviewLogEntryHeader from '../../preview/components/preview-log-entry-he
import PdfLogEntryContent from './pdf-log-entry-content'
import HumanReadableLogsHints from '../../../ide/human-readable-logs/HumanReadableLogsHints'
import { sendMB } from '@/infrastructure/event-tracking'
import getMeta from '@/utils/meta'
function PdfLogEntry({
ruleId,
@ -25,7 +26,10 @@ function PdfLogEntry({
onClose,
index,
logEntry,
id,
}) {
const showAiErrorAssistant = getMeta('ol-showAiErrorAssistant')
if (ruleId && HumanReadableLogsHints[ruleId]) {
const hint = HumanReadableLogsHints[ruleId]
formattedContent = hint.formattedContent(contentDetails)
@ -44,11 +48,29 @@ function PdfLogEntry({
[level, onSourceLocationClick, ruleId, sourceLocation]
)
const logEntryRef = useCallback(
element => {
if (element) {
window.addEventListener('editor:view-compile-log-entry', event => {
if (event.detail.id === id) {
element.scrollIntoView({ block: 'start', inline: 'nearest' })
}
if (event.detail.suggestFix) {
element.querySelector('button[data-action="suggest-fix"]')?.click()
}
})
}
},
[id]
)
return (
<div
className={classNames('log-entry', customClass)}
aria-label={entryAriaLabel}
data-ruleid={ruleId}
ref={logEntryRef}
>
<PreviewLogEntryHeader
level={level}
@ -62,7 +84,7 @@ function PdfLogEntry({
onClose={onClose}
/>
{(rawContent || formattedContent || window.user.alphaProgram) && (
{(rawContent || formattedContent || showAiErrorAssistant) && (
<PdfLogEntryContent
rawContent={rawContent}
formattedContent={formattedContent}
@ -101,6 +123,7 @@ PdfLogEntry.propTypes = {
onClose: PropTypes.func,
index: PropTypes.number,
logEntry: PropTypes.any,
id: PropTypes.string,
}
export default memo(PdfLogEntry)

View file

@ -26,6 +26,7 @@ function PdfLogsEntries({ entries, hasErrors }) {
<PdfLogEntry
key={logEntry.key}
index={index}
id={logEntry.key}
logEntry={logEntry}
ruleId={logEntry.ruleId}
headerTitle={logEntry.messageComponent ?? logEntry.message}

View file

@ -41,7 +41,7 @@ export function handleOutputFiles(outputFiles, projectId, data) {
let nextEntryId = 1
function generateEntryKey() {
return '' + nextEntryId++
return 'compile-log-entry-' + nextEntryId++
}
export const handleLogFiles = async (outputFiles, data, signal) => {
@ -140,6 +140,7 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
const rootDocDirname = dirname(fileTreeData, rootDocId)
const logEntryAnnotations = {}
const seenLine = {}
for (const entry of entries) {
if (entry.file) {
@ -153,12 +154,17 @@ export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
}
logEntryAnnotations[entity._id].push({
id: entry.key,
entryIndex: logEntryAnnotations[entity._id].length, // used for maintaining the order of items on the same line
row: entry.line - 1,
type: entry.level === 'error' ? 'error' : 'warning',
text: entry.message,
source: 'compile', // NOTE: this is used in Ace for filtering the annotations
ruleId: entry.ruleId,
firstOnLine: !seenLine[entry.line],
})
seenLine[entry.line] = true
}
}
}

View file

@ -12,6 +12,34 @@ import {
import { Annotation } from '../../../../../types/annotation'
import { debugConsole } from '@/utils/debugging'
import { sendMB } from '@/infrastructure/event-tracking'
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
interface CompileLogDiagnostic extends Diagnostic {
compile?: true
ruleId?: string
id?: string
entryIndex: number
firstOnLine?: boolean
}
type RenderedDiagnostic = Pick<
CompileLogDiagnostic,
| 'message'
| 'severity'
| 'ruleId'
| 'compile'
| 'source'
| 'id'
| 'firstOnLine'
>
export type DiagnosticAction = (
diagnostic: RenderedDiagnostic
) => HTMLButtonElement | null
const diagnosticActions = importOverleafModules('diagnosticActions') as {
import: { default: DiagnosticAction }
}[]
const compileLintSourceConf = new Compartment()
@ -56,7 +84,8 @@ export const lintSourceConfig = {
*/
const compileLogLintSource = (): Extension =>
linter(view => {
const items: Diagnostic[] = []
const items: CompileLogDiagnostic[] = []
// NOTE: iter() changes the order of diagnostics on the same line
const cursor = view.state.field(compileDiagnosticsState).iter()
while (cursor.value !== null) {
const { diagnostic } = cursor.value
@ -68,14 +97,11 @@ const compileLogLintSource = (): Extension =>
})
cursor.next()
}
// restore the original order of items
items.sort((a, b) => a.from - b.from || a.entryIndex - b.entryIndex)
return items
}, lintSourceConfig)
interface CompileLogDiagnostic extends Diagnostic {
compile?: true
ruleId?: string
}
class CompileLogDiagnosticRangeValue extends RangeValue {
constructor(public diagnostic: CompileLogDiagnostic) {
super()
@ -117,7 +143,7 @@ export const compileDiagnosticsState = StateField.define<
})
export const setAnnotations = (doc: Text, annotations: Annotation[]) => {
const diagnostics: Diagnostic[] = []
const diagnostics: CompileLogDiagnostic[] = []
for (const annotation of annotations) {
// ignore "whole document" (row: -1) annotations
@ -162,19 +188,31 @@ const convertAnnotationToDiagnostic = (
message: annotation.text,
ruleId: annotation.ruleId,
compile: true,
id: annotation.id,
entryIndex: annotation.entryIndex,
source: annotation.source,
firstOnLine: annotation.firstOnLine,
}
}
export const renderMessage = (
diagnostic: Pick<
CompileLogDiagnostic,
'message' | 'severity' | 'ruleId' | 'compile'
>
) => {
export const renderMessage = (diagnostic: RenderedDiagnostic) => {
const { message, severity, ruleId, compile = false } = diagnostic
const div = document.createElement('div')
div.textContent = message
div.classList.add('ol-cm-diagnostic-message')
div.append(message)
const activeDiagnosticActions = diagnosticActions
.map(m => m.import.default(diagnostic))
.filter(Boolean) as HTMLButtonElement[]
if (activeDiagnosticActions.length) {
const actions = document.createElement('div')
actions.classList.add('ol-cm-diagnostic-actions')
actions.append(...activeDiagnosticActions)
div.append(actions)
}
window.setTimeout(() => {
if (div.isConnected) {

View file

@ -155,6 +155,15 @@ const baseTheme = EditorView.baseTheme({
boxShadow: '0 1px 1px rgba(255, 255, 255, 0.7)',
backgroundColor: 'rgba(255, 255, 255, 0.2)',
},
'.cm-diagnosticSource': {
display: 'none',
},
'.ol-cm-diagnostic-actions': {
marginTop: '4px',
},
'.cm-diagnostic:last-of-type .ol-cm-diagnostic-actions': {
marginBottom: '4px',
},
})
/**

View file

@ -211,7 +211,7 @@ export default class LatexParser {
// Check if we're entering or leaving a new file in this line
parseParensForFilenames() {
const pos = this.currentLine.search(/\(|\)/)
const pos = this.currentLine.search(/[()]/)
if (pos !== -1) {
const token = this.currentLine[pos]
this.currentLine = this.currentLine.slice(pos + 1)
@ -251,12 +251,14 @@ export default class LatexParser {
consumeFilePath() {
// Our heuristic for detecting file names are rather crude
// A file may not contain a ')' in it
// To be a file path it must have at least one /
if (!this.currentLine.match(/^\/?([^ )]+\/)+/)) {
// To contain a file path this line must have at least one / before any '(', ')' or '\'
if (!this.currentLine.match(/^\/?([^ ()\\]+\/)+/)) {
return false
}
let endOfFilePath = this.currentLine.search(/ |\)/)
// A file may not contain a '(', ')' or '\'
let endOfFilePath = this.currentLine.search(/[ ()\\]/)
// handle the case where there is a space in a filename
while (endOfFilePath !== -1 && this.currentLine[endOfFilePath] === ' ') {

View file

@ -596,6 +596,18 @@ export const LocalCompileProvider: FC = ({ children }) => {
// used for that compile.
const lastCompileOptions = useMemo(() => data?.options || {}, [data])
useEffect(() => {
const listener = (event: Event) => {
setShowLogs((event as CustomEvent<boolean>).detail as boolean)
}
window.addEventListener('editor:show-logs', listener)
return () => {
window.removeEventListener('editor:show-logs', listener)
}
}, [])
const value = useMemo(
() => ({
animateCompileDropdownArrow,

View file

@ -10,6 +10,7 @@ const ProjectContext = createContext<
_id: string
name: string
rootDocId?: string
compiler: string
members: { _id: UserId; email: string; privileges: string }[]
invites: { _id: UserId }[]
features: {
@ -66,6 +67,7 @@ export const ProjectProvider: FC = ({ children }) => {
const {
_id,
compiler,
name,
rootDoc_id: rootDocId,
members,
@ -87,6 +89,7 @@ export const ProjectProvider: FC = ({ children }) => {
const value = useMemo(() => {
return {
_id,
compiler,
name,
rootDocId,
members,
@ -99,6 +102,7 @@ export const ProjectProvider: FC = ({ children }) => {
}
}, [
_id,
compiler,
name,
rootDocId,
members,

View file

@ -85,6 +85,10 @@
);
background: var(--premium-gradient);
transition: none;
&:hover {
background: var(--blue-70);
}
}
}

View file

@ -127,6 +127,9 @@
.btn-premium {
.premium-background;
color: @white;
&:hover {
background: @blue-70;
}
}
.reset-btns {

View file

@ -109,6 +109,7 @@
"east": "^2.0.2",
"ejs": "^3.1.10",
"email-addresses": "^5.0.0",
"eventsource-parser": "^1.1.2",
"express": "^4.19.2",
"express-bearer-token": "^2.4.0",
"express-http-proxy": "^1.6.0",
@ -140,7 +141,6 @@
"nodemailer": "^6.7.0",
"nodemailer-ses-transport": "^1.5.1",
"on-headers": "^1.0.2",
"openai": "^4.36.0",
"otplib": "^12.0.1",
"p-limit": "^2.3.0",
"p-props": "4.0.0",
@ -312,7 +312,6 @@
"mathjax": "^3.2.2",
"mensch": "^0.3.4",
"micromark": "^4.0.0",
"micromark-extension-gfm-table": "^2.0.0",
"mini-css-extract-plugin": "^2.7.6",
"mocha": "^10.2.0",
"mocha-each": "^2.0.1",

View file

@ -1,6 +1,6 @@
// Run babel on tests to allow support for import/export statements in Node
require('@babel/register')({
extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs'],
extensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.svg'],
plugins: [['module-resolver', { alias: { '^@/(.+)': './frontend/js/\\1' } }]],
})

View file

@ -4,4 +4,7 @@ export type Annotation = {
text: string
source?: string
ruleId?: string
id: string
entryIndex: number
firstOnLine: boolean
}