Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,21 @@ $ yarn e2e:headless

### Code style

_tl;dr_: Use oxfmt and eslint!
_tl;dr_: Use oxfmt and oxlint!

Format code with oxfmt to get uniform codestyle:

```
$ yarn format
```

Lint code with [eslint](http://eslint.org/), including [eslint react plugin](https://github.com/yannickcr/eslint-plugin-react), [eslint-plugin-import](https://github.com/benmosher/eslint-plugin-import), [eslint-plugin-jsx-a11y](https://github.com/evcohen/eslint-plugin-jsx-a11y#readme).
Beside linting with globally installed eslint, eslint can be invoked with `yarn`:
Lint code with [oxlint](https://oxc.rs/). Beside linting with globally installed oxlint, oxlint can be invoked with `yarn`:

```
$ yarn lint
```

Rules are configured in `./.eslintrc.js` and extends [eslint-config-react-app](https://github.com/facebook/create-react-app/tree/master/packages/eslint-config-react-app). If feeling brave, try `eslint --fix`.
Rules are configured in `./oxlint.config.ts` and extends our [base config]("https://github.com/ndlano/frontend-packages/tree/master/packages/oxlint-config"). If feeling brave, try `oxlint --fix`.

## Other scripts

Expand Down
4 changes: 3 additions & 1 deletion custom-typings/index-javascript.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/*
/**
* Copyright (c) 2019-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

declare module "*";
6 changes: 4 additions & 2 deletions custom-typings/ndla-embed.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
/*
/**
* Copyright (c) 2022-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import React from "react";

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
// oxlint-disable-next-line typescript/no-namespace
namespace JSX {
// NOTE: We use `ndlaembed` for embedding content, this isn't a regular HTML-tag.
// Let's declare it so typescript doesn't complain when we're building it in JSX.
Expand Down
15 changes: 7 additions & 8 deletions e2e/apiMock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
*/

import { readFile, writeFile } from "fs/promises";
import type { TestInfo } from "@playwright/test";
import { test as Ptest } from "@playwright/test";
import { test as Ptest, type TestInfo } from "@playwright/test";
import { brightcoveTokenMock, copyrightMock, getNoteUsersMock, responsiblesMock, userDataMock } from "./mockResponses";

const mockDir = "e2e/apiMocks/";
Expand All @@ -35,7 +34,7 @@ const mockFile = ({ titlePath, title: test_name }: TestInfo) => {
*/
export const test = Ptest.extend<ExtendParams>({
harCheckpoint: [
async ({ context, page }, use) => {
async ({ context, page }, call) => {
let checkpointIndex = 0;

// Appending the checkpoint index to the request headers
Expand All @@ -59,7 +58,7 @@ export const test = Ptest.extend<ExtendParams>({
}

// Appending the new checkpoint index to the request headers
await use(async () => {
await call(async () => {
checkpointIndex += 1;
if (process.env.RECORD_FIXTURES !== "true") {
await page.setExtraHTTPHeaders({
Expand All @@ -70,7 +69,7 @@ export const test = Ptest.extend<ExtendParams>({
},
{ auto: true, scope: "test" },
],
page: async ({ page }, use, testInfo) => {
page: async ({ page }, call, testInfo) => {
// Creating the API mocking for the wanted API's
await page.routeFromHAR(mockFile(testInfo), {
update: process.env.RECORD_FIXTURES === "true",
Expand All @@ -79,12 +78,12 @@ export const test = Ptest.extend<ExtendParams>({
updateContent: "embed",
});

await use(page);
await call(page);

await page.close();
},
context: async ({ context }, use, testInfo) => {
await use(context);
context: async ({ context }, call, testInfo) => {
await call(context);
await context.close();

// Removing sensitive data from the HAR file after saving. Har files are saved on close.
Expand Down
16 changes: 0 additions & 16 deletions eslint.config.mjs

This file was deleted.

32 changes: 32 additions & 0 deletions oxlint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { baseConfig } from "@ndla/oxlint-config";
import { defineConfig } from "oxlint";

export default defineConfig({
extends: [baseConfig],
options: {
typeAware: true,
},
ignorePatterns: ["**/h5pResizer.ts"],
overrides: [
{
files: ["**/__tests__/**/*"],
rules: {
"import-js/no-extraneous-dependencies": "off",
},
},
{
files: ["**/*"],
rules: {
"typescript/unbound-method": "off",
},
},
],
});
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"clean": "node scripts/clean.mjs",
"check-all": "yarn lint && yarn tsc --noEmit && yarn test",
"lint": "yarn format-check && yarn lint-es",
"lint-es": "eslint --cache --cache-location '.eslintcache/' --max-warnings=0 src",
"lint-es": "oxlint --deny-warnings",
"format-check": "oxfmt --check",
"format": "oxfmt",
"start:tsc": "tsc -w",
Expand All @@ -32,6 +32,7 @@
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@ndla/oxlint-config": "^0.1.0",
"@ndla/preset-panda": "^0.0.74",
"@ndla/types-backend": "^1.0.106",
"@ndla/types-embed": "^5.0.21-alpha.0",
Expand All @@ -48,24 +49,23 @@
"@types/is-hotkey": "^0.1.10",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.10.12",
"@types/node-fetch": "^2.6.13",
"@types/prismjs": "^1.26.5",
"@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^5.1.3",
"concurrently": "^9.2.1",
"cross-env": "^10.1.0",
"eslint": "^9.39.2",
"eslint-config-ndla": "^6.0.14-alpha.0",
"filter-console": "^1.0.0",
"jsdom": "^28.0.0",
"nock": "^14.0.10",
"oxlint": "^1.57.0",
"oxlint-tsgolint": "^0.18.0",
"postcss": "^8.5.6",
"postcss-preset-env": "^11.1.3",
"rolldown": "^1.0.0-rc.6",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.54.0",
"vite": "^7.3.1",
"vitest": "^4.0.18"
},
Expand Down
4 changes: 3 additions & 1 deletion postcss.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import panda from "@pandacss/dev/postcss";
import postcssPresetEnv from "postcss-preset-env";

export default {
const config = {
plugins: [panda(), postcssPresetEnv()],
};

export default config;
8 changes: 8 additions & 0 deletions scripts/clean.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/**
* Copyright (c) 2026-present, NDLA.
*
* This source code is licensed under the GPLv3 license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import { rm } from "node:fs/promises";

// This will do nothing if the path does not exist.
Expand Down
6 changes: 3 additions & 3 deletions src/components/DisplayEmbed/helpers/h5pResizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
actionHandlers.hello = (iframe, data, respond) => {
// Make iframe responsive
iframe.style.width = "100%"; // eslint-disable-line no-param-reassign
iframe.style.width = "100%";

// Tell iframe that it needs to resize when our window resizes
const resize = () => {
Expand All @@ -41,7 +41,7 @@
// Do not resize unless page and scrolling differs
if (iframe.clientHeight !== data.scrollHeight || data.scrollHeight !== data.clientHeight) {
// Reset iframe height, in case content has shrinked.
iframe.style.height = `${data.clientHeight}px`; // eslint-disable-line no-param-reassign
iframe.style.height = `${data.clientHeight}px`;
respond("resizePrepared");
}
};
Expand All @@ -50,7 +50,7 @@
* Resize parent and iframe to desired height.
*/
actionHandlers.resize = (iframe, data) => {
iframe.style.height = `${data.scrollHeight}px`; // eslint-disable-line no-param-reassign
iframe.style.height = `${data.scrollHeight}px`;
};

// Listen for messages from iframes
Expand Down
2 changes: 1 addition & 1 deletion src/components/Form/SearchSaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const SearchSaveButton = ({ filters, searchContentType, userData }: Props) => {

useEffect(() => {
setError("");
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, [window.location.search]);

const handleSuccess = () => {
Expand Down
5 changes: 3 additions & 2 deletions src/components/H5PElement/H5PElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ const H5PElement = ({ h5pUrl, onSelect, onClose, locale, canReturnResources }: P
window.addEventListener("message", handleH5PClose);
try {
fetchAndSetH5PUrl();
} catch (e) {
} catch (_) {
setFetchFailed(true);
}

return () => {
window.removeEventListener("message", handleH5PChange);
window.removeEventListener("message", handleH5PClose);
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, []);

const fetchAndSetH5PUrl = async () => {
const data = h5pUrl ? await editH5PiframeUrl(h5pUrl, locale) : await fetchH5PiframeUrl(locale, canReturnResources);
Expand Down
3 changes: 2 additions & 1 deletion src/components/MonacoEditor/MonacoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import "monaco-editor/esm/vs/editor/contrib/fontZoom/browser/fontZoom";
import "monaco-editor/esm/vs/editor/contrib/linesOperations/browser/linesOperations";
import "monaco-editor/esm/vs/editor/contrib/multicursor/browser/multicursor";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
// oxlint-disable-next-line import/default
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
// Uncomment the following line to test all monaco-editor features
// import * as monaco from "monaco-editor";
Expand Down Expand Up @@ -96,7 +97,7 @@ export const MonacoEditor = ({ value, onChange, onSave, size }: Props) => {
});
});
return () => editor?.dispose();
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, []);

useEffect(() => {
Expand Down
6 changes: 2 additions & 4 deletions src/components/PreviewDraft/useTransformedArticle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,16 @@ export const useTransformedArticle = <T extends FormArticle | undefined>({
articleLanguage: getUpdatedLanguage(draft.language),
});

const disclaimer = transform(disclaimerContent?.data ?? "", {});

return {
title: draft.title ? parse(draft.title) : "",
introduction: draft.introduction ? parse(draft.introduction) : "",
content,
copyright: draft.copyright,
published: draft.published ? formatDate(draft.published) : "",
footNotes: [],
disclaimer,
disclaimer: transform(disclaimerContent?.data ?? "", {}),
};
}, [transformedContent.data, draft, previewAlt]);
}, [transformedContent.data, draft, previewAlt, disclaimerContent]);

return { article, draft };
};
2 changes: 1 addition & 1 deletion src/components/SlateEditor/PlainTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const PlainTextEditor = ({
status: undefined,
}));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, [status]);

return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/SlateEditor/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const RichTextEditor = ({
}
}
prevSubmitted.current = submitted;
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, [status, submitted]);

const renderElement = useCallback((renderProps: RenderElementProps) => {
Expand All @@ -120,7 +120,7 @@ const RichTextEditor = ({
}
}
return renderInvalidElement?.({ ...renderProps, editor }) ?? <p {...attributes}>{children}</p>;
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, []);

const renderLeaf = useCallback((renderProps: RenderLeafProps) => {
Expand All @@ -132,7 +132,7 @@ const RichTextEditor = ({
}
}
return <span {...attributes}>{children}</span>;
// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, []);

const onDragStart = useCallback((e: DragEvent<HTMLDivElement>) => nativeOnDragStart(editor, e), [editor]);
Expand Down
2 changes: 1 addition & 1 deletion src/components/SlateEditor/VisualElementEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const VisualElementEditor = ({ value, plugins, onChange, types, language }: Prop
editor.reinitialize({ value });
}

// eslint-disable-next-line react-hooks/exhaustive-deps
// oxlint-disable-next-line eslint-plugin-react-hooks/exhaustive-deps
}, [value]);

return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/SlateEditor/ndlaEmbed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { HTMLAttributes, Attributes } from "react";

declare module "react/jsx-runtime" {
// eslint-disable-next-line @typescript-eslint/no-namespace
// oxlint-disable-next-line typescript/no-namespace
namespace JSX {
interface IntrinsicElements {
ndlaembed: HTMLAttributes<HTMLElement> & Attributes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const SlateCommentInline = ({ attributes, editor, element, children }: Props) =>
>
<PopoverTrigger asChild type={undefined}>
<InlineComment
// oxlint-disable-next-line jsx_a11y/prefer-tag-over-role
role="button"
tabIndex={0}
onMouseDown={(e) => preventAutoFocusInEditor(e.nativeEvent)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ const InnerForm = () => {
if (rule) {
try {
url = await rule.transform(url);
} catch (e) {
} catch (_) {
setFieldError("url", t("form.content.link.unSupported"));
return;
}
Expand All @@ -245,7 +245,7 @@ const InnerForm = () => {
}),
true,
);
} catch (e) {
} catch (_) {
const provider = getWhitelistedProvider(url);
setValues(
(values) => ({ ...values, height: provider?.height ?? "486px", resource: "iframe", validUrl: url, url }),
Expand Down
Loading
Loading