diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json
index 95fe5f8d5aab..bfae15c3f5a6 100644
--- a/packages/integrations/mdx/package.json
+++ b/packages/integrations/mdx/package.json
@@ -31,7 +31,8 @@
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
"build:ci": "astro-scripts build \"src/**/*.ts\"",
"dev": "astro-scripts dev \"src/**/*.ts\"",
- "test": "astro-scripts test --timeout 70000 \"test/**/*.test.js\""
+ "test": "astro-scripts test --timeout 70000 \"test/**/*.test.ts\"",
+ "typecheck:tests": "tsc --build tsconfig.test.json"
},
"dependencies": {
"@astrojs/markdown-remark": "workspace:*",
diff --git a/packages/integrations/mdx/test/css-head-mdx.test.js b/packages/integrations/mdx/test/css-head-mdx.test.ts
similarity index 97%
rename from packages/integrations/mdx/test/css-head-mdx.test.js
rename to packages/integrations/mdx/test/css-head-mdx.test.ts
index 81f1ee49170e..94ae75527b85 100644
--- a/packages/integrations/mdx/test/css-head-mdx.test.js
+++ b/packages/integrations/mdx/test/css-head-mdx.test.ts
@@ -3,10 +3,10 @@ import { before, describe, it } from 'node:test';
import mdx from '@astrojs/mdx';
import * as cheerio from 'cheerio';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
describe('Head injection w/ MDX', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
diff --git a/packages/integrations/mdx/test/invalid-mdx-component.test.js b/packages/integrations/mdx/test/invalid-mdx-component.test.ts
similarity index 76%
rename from packages/integrations/mdx/test/invalid-mdx-component.test.js
rename to packages/integrations/mdx/test/invalid-mdx-component.test.ts
index b8152e89c718..e6bfb248576c 100644
--- a/packages/integrations/mdx/test/invalid-mdx-component.test.js
+++ b/packages/integrations/mdx/test/invalid-mdx-component.test.ts
@@ -1,12 +1,12 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
import mdx from '../dist/index.js';
const FIXTURE_ROOT = new URL('./fixtures/invalid-mdx-component/', import.meta.url);
describe('MDX component with runtime error', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -16,22 +16,21 @@ describe('MDX component with runtime error', () => {
});
describe('build', () => {
- /** @type {Error | null} */
- let error;
+ let error: Error | null = null;
before(async () => {
error = null;
try {
await fixture.build();
} catch (e) {
- error = e;
+ error = e as Error;
}
});
it('Throws the right error', async () => {
assert.ok(error);
assert.match(
- error?.hint,
+ (error as Error & { hint?: string })?.hint ?? '',
/This issue often occurs when your MDX component encounters runtime errors/,
);
});
diff --git a/packages/integrations/mdx/test/mdx-astro-container-escape.test.js b/packages/integrations/mdx/test/mdx-astro-container-escape.test.ts
similarity index 86%
rename from packages/integrations/mdx/test/mdx-astro-container-escape.test.js
rename to packages/integrations/mdx/test/mdx-astro-container-escape.test.ts
index e3a7df509f13..106a10cdac79 100644
--- a/packages/integrations/mdx/test/mdx-astro-container-escape.test.js
+++ b/packages/integrations/mdx/test/mdx-astro-container-escape.test.ts
@@ -1,10 +1,10 @@
import assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
describe('MDX Component & Astro Container escape issue', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
diff --git a/packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.js b/packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.ts
similarity index 79%
rename from packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.js
rename to packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.ts
index f8cb3c98ff9a..b63c049c5252 100644
--- a/packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.js
+++ b/packages/integrations/mdx/test/mdx-astro-markdown-remarkRehype.test.ts
@@ -21,9 +21,9 @@ describe('MDX with Astro Markdown remark-rehype config', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- assert.equal(document.querySelector('#footnote-label').textContent, 'Catatan kaki');
+ assert.equal(document.querySelector('#footnote-label')!.textContent, 'Catatan kaki');
assert.equal(
- document.querySelector('.data-footnote-backref').getAttribute('aria-label'),
+ document.querySelector('.data-footnote-backref')!.getAttribute('aria-label'),
'Kembali ke konten',
);
});
@@ -50,9 +50,9 @@ describe('MDX with Astro Markdown remark-rehype config', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- assert.equal(document.querySelector('#footnote-label').textContent, 'Catatan kaki');
+ assert.equal(document.querySelector('#footnote-label')!.textContent, 'Catatan kaki');
assert.equal(
- document.querySelector('.data-footnote-backref').getAttribute('aria-label'),
+ document.querySelector('.data-footnote-backref')!.getAttribute('aria-label'),
'Kembali ke konten',
);
});
@@ -62,7 +62,6 @@ describe('MDX with Astro Markdown remark-rehype config', () => {
root: new URL('./fixtures/mdx-astro-markdown-remarkRehype/', import.meta.url),
integrations: [
mdx({
- extendPlugins: 'astroDefaults',
remarkRehype: {
footnoteLabel: 'Catatan kaki',
},
@@ -79,9 +78,9 @@ describe('MDX with Astro Markdown remark-rehype config', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- assert.equal(document.querySelector('#footnote-label').textContent, 'Catatan kaki');
+ assert.equal(document.querySelector('#footnote-label')!.textContent, 'Catatan kaki');
assert.equal(
- document.querySelector('.data-footnote-backref').getAttribute('aria-label'),
+ document.querySelector('.data-footnote-backref')!.getAttribute('aria-label'),
'Back to reference 1',
);
});
diff --git a/packages/integrations/mdx/test/mdx-basics.test.js b/packages/integrations/mdx/test/mdx-basics.test.ts
similarity index 92%
rename from packages/integrations/mdx/test/mdx-basics.test.js
rename to packages/integrations/mdx/test/mdx-basics.test.ts
index b2e4de4818fa..064c2bb0062a 100644
--- a/packages/integrations/mdx/test/mdx-basics.test.js
+++ b/packages/integrations/mdx/test/mdx-basics.test.ts
@@ -3,7 +3,7 @@ import { after, before, describe, it } from 'node:test';
import mdx from '@astrojs/mdx';
import * as cheerio from 'cheerio';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
// Merged fixture: combines mdx-component, mdx-slots, mdx-frontmatter,
// mdx-url-export, mdx-get-static-paths, and mdx-script-style-raw.
@@ -11,7 +11,7 @@ import { loadFixture } from '../../../astro/test/test-utils.js';
const FIXTURE_ROOT = new URL('./fixtures/mdx-basics/', import.meta.url);
describe('MDX basics (merged fixture)', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -32,8 +32,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/component/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const foo = document.querySelector('#foo');
+ const h1 = document.querySelector('h1')!;
+ const foo = document.querySelector('#foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -43,8 +43,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/component/glob/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-default-export] h1');
- const foo = document.querySelector('[data-default-export] #foo');
+ const h1 = document.querySelector('[data-default-export] h1')!;
+ const foo = document.querySelector('[data-default-export] #foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -54,8 +54,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/component/glob/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-content-export] h1');
- const foo = document.querySelector('[data-content-export] #foo');
+ const h1 = document.querySelector('[data-content-export] h1')!;
+ const foo = document.querySelector('[data-content-export] #foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -66,8 +66,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/component/w-fragment/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const p = document.querySelector('p');
+ const h1 = document.querySelector('h1')!;
+ const p = document.querySelector('p')!;
assert.equal(h1.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -79,10 +79,10 @@ describe('MDX basics (merged fixture)', () => {
const h = document.querySelector(
'[data-default-export] [data-file="WithFragment.mdx"] h1',
- );
+ )!;
const p = document.querySelector(
'[data-default-export] [data-file="WithFragment.mdx"] p',
- );
+ )!;
assert.equal(h.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -94,10 +94,10 @@ describe('MDX basics (merged fixture)', () => {
const h = document.querySelector(
'[data-content-export] [data-file="WithFragment.mdx"] h1',
- );
+ )!;
const p = document.querySelector(
'[data-content-export] [data-file="WithFragment.mdx"] p',
- );
+ )!;
assert.equal(h.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -112,9 +112,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/slots/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const defaultSlot = document.querySelector('[data-default-slot]');
- const namedSlot = document.querySelector('[data-named-slot]');
+ const h1 = document.querySelector('h1')!;
+ const defaultSlot = document.querySelector('[data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -125,9 +125,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/slots/glob/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-default-export] h1');
- const defaultSlot = document.querySelector('[data-default-export] [data-default-slot]');
- const namedSlot = document.querySelector('[data-default-export] [data-named-slot]');
+ const h1 = document.querySelector('[data-default-export] h1')!;
+ const defaultSlot = document.querySelector('[data-default-export] [data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-default-export] [data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -138,9 +138,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/slots/glob/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-content-export] h1');
- const defaultSlot = document.querySelector('[data-content-export] [data-default-slot]');
- const namedSlot = document.querySelector('[data-content-export] [data-named-slot]');
+ const h1 = document.querySelector('[data-content-export] h1')!;
+ const defaultSlot = document.querySelector('[data-content-export] [data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-content-export] [data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -173,8 +173,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/frontmatter/index.html');
const { document } = parseHTML(html);
- const contentTitle = document.querySelector('[data-content-title]');
- const frontmatterTitle = document.querySelector('[data-frontmatter-title]');
+ const contentTitle = document.querySelector('[data-content-title]')!;
+ const frontmatterTitle = document.querySelector('[data-frontmatter-title]')!;
assert.equal(contentTitle.textContent, 'Using YAML frontmatter');
assert.equal(frontmatterTitle.textContent, 'Using YAML frontmatter');
@@ -253,14 +253,14 @@ describe('MDX basics (merged fixture)', () => {
const html = await fixture.readFile('/script-style-raw/index.html');
const { document } = parseHTML(html);
- const scriptContent = document.getElementById('test-script').innerHTML;
+ const scriptContent = document.getElementById('test-script')!.innerHTML;
assert.equal(
scriptContent.includes("console.log('raw script')"),
true,
'script should not be html-escaped',
);
- const styleContent = document.getElementById('test-style').innerHTML;
+ const styleContent = document.getElementById('test-style')!.innerHTML;
assert.equal(
styleContent.includes('h1[id="script-style-raw"]'),
true,
@@ -271,7 +271,7 @@ describe('MDX basics (merged fixture)', () => {
});
describe('dev', () => {
- let devServer;
+ let devServer: DevServer;
before(async () => {
devServer = await fixture.startDevServer();
@@ -291,8 +291,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const foo = document.querySelector('#foo');
+ const h1 = document.querySelector('h1')!;
+ const foo = document.querySelector('#foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -305,8 +305,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-default-export] h1');
- const foo = document.querySelector('[data-default-export] #foo');
+ const h1 = document.querySelector('[data-default-export] h1')!;
+ const foo = document.querySelector('[data-default-export] #foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -319,8 +319,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-content-export] h1');
- const foo = document.querySelector('[data-content-export] #foo');
+ const h1 = document.querySelector('[data-content-export] h1')!;
+ const foo = document.querySelector('[data-content-export] #foo')!;
assert.equal(h1.textContent, 'Hello component!');
assert.equal(foo.textContent, 'bar');
@@ -334,8 +334,8 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const p = document.querySelector('p');
+ const h1 = document.querySelector('h1')!;
+ const p = document.querySelector('p')!;
assert.equal(h1.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -350,10 +350,10 @@ describe('MDX basics (merged fixture)', () => {
const h = document.querySelector(
'[data-default-export] [data-file="WithFragment.mdx"] h1',
- );
+ )!;
const p = document.querySelector(
'[data-default-export] [data-file="WithFragment.mdx"] p',
- );
+ )!;
assert.equal(h.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -368,10 +368,10 @@ describe('MDX basics (merged fixture)', () => {
const h = document.querySelector(
'[data-content-export] [data-file="WithFragment.mdx"] h1',
- );
+ )!;
const p = document.querySelector(
'[data-content-export] [data-file="WithFragment.mdx"] p',
- );
+ )!;
assert.equal(h.textContent, 'MDX containing ');
assert.equal(p.textContent, 'bar');
@@ -389,9 +389,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
- const defaultSlot = document.querySelector('[data-default-slot]');
- const namedSlot = document.querySelector('[data-named-slot]');
+ const h1 = document.querySelector('h1')!;
+ const defaultSlot = document.querySelector('[data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -405,9 +405,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-default-export] h1');
- const defaultSlot = document.querySelector('[data-default-export] [data-default-slot]');
- const namedSlot = document.querySelector('[data-default-export] [data-named-slot]');
+ const h1 = document.querySelector('[data-default-export] h1')!;
+ const defaultSlot = document.querySelector('[data-default-export] [data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-default-export] [data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -421,9 +421,9 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('[data-content-export] h1');
- const defaultSlot = document.querySelector('[data-content-export] [data-default-slot]');
- const namedSlot = document.querySelector('[data-content-export] [data-named-slot]');
+ const h1 = document.querySelector('[data-content-export] h1')!;
+ const defaultSlot = document.querySelector('[data-content-export] [data-default-slot]')!;
+ const namedSlot = document.querySelector('[data-content-export] [data-named-slot]')!;
assert.equal(h1.textContent, 'Hello slotted component!');
assert.equal(defaultSlot.textContent, 'Default content.');
@@ -441,14 +441,14 @@ describe('MDX basics (merged fixture)', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const scriptContent = document.getElementById('test-script').innerHTML;
+ const scriptContent = document.getElementById('test-script')!.innerHTML;
assert.equal(
scriptContent.includes("console.log('raw script')"),
true,
'script should not be html-escaped',
);
- const styleContent = document.getElementById('test-style').innerHTML;
+ const styleContent = document.getElementById('test-style')!.innerHTML;
assert.equal(
styleContent.includes('h1[id="script-style-raw"]'),
true,
diff --git a/packages/integrations/mdx/test/mdx-content-layer.test.js b/packages/integrations/mdx/test/mdx-content-layer.test.ts
similarity index 74%
rename from packages/integrations/mdx/test/mdx-content-layer.test.js
rename to packages/integrations/mdx/test/mdx-content-layer.test.ts
index c6998b26f725..887699750839 100644
--- a/packages/integrations/mdx/test/mdx-content-layer.test.js
+++ b/packages/integrations/mdx/test/mdx-content-layer.test.ts
@@ -1,12 +1,11 @@
import assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
describe('Content Layer MDX rendering dev', () => {
- /** @type {import("../../../astro/test/test-utils.js").Fixture} */
- let fixture;
+ let fixture: Fixture;
- let devServer;
+ let devServer: DevServer;
before(async () => {
fixture = await loadFixture({
root: new URL('./fixtures/content-layer/', import.meta.url),
@@ -19,7 +18,7 @@ describe('Content Layer MDX rendering dev', () => {
});
it('Render an MDX file', async () => {
- const html = await fixture.fetch('/reptiles/iguana').then((r) => r.text());
+ const html = await fixture.fetch('/reptiles/iguana').then((r: Response) => r.text());
assert.match(html, /Iguana/);
assert.match(html, /This is a rendered entry/);
@@ -27,8 +26,7 @@ describe('Content Layer MDX rendering dev', () => {
});
describe('Content Layer MDX rendering build', () => {
- /** @type {import("../../../astro/test/test-utils.js").Fixture} */
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
root: new URL('./fixtures/content-layer/', import.meta.url),
diff --git a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.ts
similarity index 59%
rename from packages/integrations/mdx/test/mdx-frontmatter-injection.test.js
rename to packages/integrations/mdx/test/mdx-frontmatter-injection.test.ts
index 5c8b69d07337..d82e7e4f3c4f 100644
--- a/packages/integrations/mdx/test/mdx-frontmatter-injection.test.js
+++ b/packages/integrations/mdx/test/mdx-frontmatter-injection.test.ts
@@ -1,12 +1,19 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
const FIXTURE_ROOT = new URL('./fixtures/mdx-frontmatter-injection/', import.meta.url);
+type FrontmatterEntry = {
+ layout: string;
+ title: string;
+ description: string;
+ injectedReadingTime: { text: string; minutes: number; time: number; words: number } | null;
+};
+
describe('MDX frontmatter injection', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -15,28 +22,30 @@ describe('MDX frontmatter injection', () => {
await fixture.build();
});
+ const readFrontmatterByPage = async (): Promise => {
+ return JSON.parse(await fixture.readFile('/glob.json')) as FrontmatterEntry[];
+ };
+
it('remark supports custom vfile data - get title', async () => {
- const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
- const titles = frontmatterByPage.map((frontmatter = {}) => frontmatter.title);
+ const frontmatterByPage = await readFrontmatterByPage();
+ const titles = frontmatterByPage.map((frontmatter) => frontmatter.title);
assert.equal(titles.includes('Page 1'), true);
assert.equal(titles.includes('Page 2'), true);
});
it('rehype supports custom vfile data - reading time', async () => {
- const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
- const readingTimes = frontmatterByPage.map(
- (frontmatter = {}) => frontmatter.injectedReadingTime,
- );
+ const frontmatterByPage = await readFrontmatterByPage();
+ const readingTimes = frontmatterByPage.map((frontmatter) => frontmatter.injectedReadingTime);
assert.equal(readingTimes.length > 0, true);
- for (let readingTime of readingTimes) {
+ for (const readingTime of readingTimes) {
assert.notEqual(readingTime, null);
- assert.match(readingTime.text, /^\d+ min read/);
+ assert.match(readingTime!.text, /^\d+ min read/);
}
});
it('allow user frontmatter mutation', async () => {
- const frontmatterByPage = JSON.parse(await fixture.readFile('/glob.json'));
- const descriptions = frontmatterByPage.map((frontmatter = {}) => frontmatter.description);
+ const frontmatterByPage = await readFrontmatterByPage();
+ const descriptions = frontmatterByPage.map((frontmatter) => frontmatter.description);
assert.equal(
descriptions.includes('Processed by remarkDescription plugin: Page 1 description'),
true,
@@ -51,8 +60,8 @@ describe('MDX frontmatter injection', () => {
const html1 = await fixture.readFile('/page-1/index.html');
const html2 = await fixture.readFile('/page-2/index.html');
- const title1 = parseHTML(html1).document.querySelector('title');
- const title2 = parseHTML(html2).document.querySelector('title');
+ const title1 = parseHTML(html1).document.querySelector('title')!;
+ const title2 = parseHTML(html2).document.querySelector('title')!;
assert.equal(title1.innerHTML, 'Page 1');
assert.equal(title2.innerHTML, 'Page 2');
diff --git a/packages/integrations/mdx/test/mdx-get-headings.test.js b/packages/integrations/mdx/test/mdx-get-headings.test.ts
similarity index 86%
rename from packages/integrations/mdx/test/mdx-get-headings.test.js
rename to packages/integrations/mdx/test/mdx-get-headings.test.ts
index e4e07a281fc8..f889c5140153 100644
--- a/packages/integrations/mdx/test/mdx-get-headings.test.js
+++ b/packages/integrations/mdx/test/mdx-get-headings.test.ts
@@ -4,10 +4,10 @@ import { rehypeHeadingIds } from '@astrojs/markdown-remark';
import mdx from '@astrojs/mdx';
import { parseHTML } from 'linkedom';
import { visit } from 'unist-util-visit';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
describe('MDX getHeadings', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -22,9 +22,9 @@ describe('MDX getHeadings', () => {
const html = await fixture.readFile('/test/index.html');
const { document } = parseHTML(html);
- const h2Ids = document.querySelectorAll('h2').map((el) => el?.id);
- const h3Ids = document.querySelectorAll('h3').map((el) => el?.id);
- assert.equal(document.querySelector('h1').id, 'heading-test');
+ const h2Ids = Array.from(document.querySelectorAll('h2')).map((el) => el?.id);
+ const h3Ids = Array.from(document.querySelectorAll('h3')).map((el) => el?.id);
+ assert.equal(document.querySelector('h1')!.id, 'heading-test');
assert.equal(h2Ids.includes('section-1'), true);
assert.equal(h2Ids.includes('section-2'), true);
assert.equal(h3Ids.includes('subsection-1'), true);
@@ -70,15 +70,15 @@ describe('MDX getHeadings', () => {
const html = await fixture.readFile('/test-with-frontmatter/index.html');
const { document } = parseHTML(html);
- const h3Ids = document.querySelectorAll('h3').map((el) => el?.id);
+ const h3Ids = Array.from(document.querySelectorAll('h3')).map((el) => el?.id);
- assert.equal(document.querySelector('h1').id, 'the-frontmatter-title');
- assert.equal(document.querySelector('h2').id, 'frontmattertitle');
+ assert.equal(document.querySelector('h1')!.id, 'the-frontmatter-title');
+ assert.equal(document.querySelector('h2')!.id, 'frontmattertitle');
assert.equal(h3Ids.includes('keyword-2'), true);
assert.equal(h3Ids.includes('tag-1'), true);
- assert.equal(document.querySelector('h4').id, 'item-2');
- assert.equal(document.querySelector('h5').id, 'nested-item-3');
- assert.equal(document.querySelector('h6').id, 'frontmatterunknown');
+ assert.equal(document.querySelector('h4')!.id, 'item-2');
+ assert.equal(document.querySelector('h5')!.id, 'nested-item-3');
+ assert.equal(document.querySelector('h6')!.id, 'frontmatterunknown');
});
it('generates correct getHeadings() export', async () => {
@@ -100,7 +100,7 @@ describe('MDX getHeadings', () => {
});
describe('MDX heading IDs can be customized by user plugins', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -132,7 +132,7 @@ describe('MDX heading IDs can be customized by user plugins', () => {
assert.equal(h1?.textContent, 'Heading test');
assert.equal(h1?.getAttribute('id'), '0');
- const headingIDs = document.querySelectorAll('h1,h2,h3').map((el) => el.id);
+ const headingIDs = Array.from(document.querySelectorAll('h1,h2,h3')).map((el) => el.id);
assert.equal(
JSON.stringify(headingIDs),
JSON.stringify(Array.from({ length: headingIDs.length }, (_, idx) => String(idx))),
@@ -157,7 +157,7 @@ describe('MDX heading IDs can be customized by user plugins', () => {
});
describe('MDX heading IDs can be injected before user plugins', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
diff --git a/packages/integrations/mdx/test/mdx-images.test.js b/packages/integrations/mdx/test/mdx-images.test.ts
similarity index 77%
rename from packages/integrations/mdx/test/mdx-images.test.js
rename to packages/integrations/mdx/test/mdx-images.test.ts
index 543b9021ebe2..304dcc163fa6 100644
--- a/packages/integrations/mdx/test/mdx-images.test.js
+++ b/packages/integrations/mdx/test/mdx-images.test.ts
@@ -1,13 +1,13 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
const imageTestRoutes = ['with-components', 'esm-import', 'content-collection'];
describe('MDX Page', () => {
- let devServer;
- let fixture;
+ let devServer: DevServer;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -31,17 +31,17 @@ describe('MDX Page', () => {
const imgs = document.getElementsByTagName('img');
assert.equal(imgs.length, 6);
// Image using a relative path
- assert.equal(imgs.item(0).src.startsWith('/_image'), true);
+ assert.equal(imgs.item(0)!.src.startsWith('/_image'), true);
// Image using an aliased path
- assert.equal(imgs.item(1).src.startsWith('/_image'), true);
+ assert.equal(imgs.item(1)!.src.startsWith('/_image'), true);
// Image with title
- assert.equal(imgs.item(2).title, 'Houston title');
+ assert.equal(imgs.item(2)!.title, 'Houston title');
// Image with spaces in the path
- assert.equal(imgs.item(3).src.startsWith('/_image'), true);
+ assert.equal(imgs.item(3)!.src.startsWith('/_image'), true);
// Image using a relative path with no slashes
- assert.equal(imgs.item(4).src.startsWith('/_image'), true);
+ assert.equal(imgs.item(4)!.src.startsWith('/_image'), true);
// Image using a relative path with nested directory
- assert.equal(imgs.item(5).src.startsWith('/_image'), true);
+ assert.equal(imgs.item(5)!.src.startsWith('/_image'), true);
});
for (const route of imageTestRoutes) {
@@ -55,11 +55,11 @@ describe('MDX Page', () => {
const imgs = document.getElementsByTagName('img');
assert.equal(imgs.length, 2);
- const assetsImg = imgs.item(0);
+ const assetsImg = imgs.item(0)!;
assert.equal(assetsImg.src.startsWith('/_image'), true);
assert.equal(assetsImg.hasAttribute('data-my-image'), true);
- const publicImg = imgs.item(1);
+ const publicImg = imgs.item(1)!;
assert.equal(publicImg.src, '/favicon.svg');
assert.equal(publicImg.hasAttribute('data-my-image'), true);
});
diff --git a/packages/integrations/mdx/test/mdx-infinite-loop.test.js b/packages/integrations/mdx/test/mdx-infinite-loop.test.ts
similarity index 82%
rename from packages/integrations/mdx/test/mdx-infinite-loop.test.js
rename to packages/integrations/mdx/test/mdx-infinite-loop.test.ts
index a4c78fcfff1e..383c89d9a52c 100644
--- a/packages/integrations/mdx/test/mdx-infinite-loop.test.js
+++ b/packages/integrations/mdx/test/mdx-infinite-loop.test.ts
@@ -1,10 +1,10 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import mdx from '@astrojs/mdx';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
describe('MDX Infinite Loop', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -14,7 +14,7 @@ describe('MDX Infinite Loop', () => {
});
describe('build', () => {
- let err;
+ let err: unknown;
before(async () => {
try {
await fixture.build();
diff --git a/packages/integrations/mdx/test/mdx-math.test.js b/packages/integrations/mdx/test/mdx-math.test.ts
similarity index 100%
rename from packages/integrations/mdx/test/mdx-math.test.js
rename to packages/integrations/mdx/test/mdx-math.test.ts
diff --git a/packages/integrations/mdx/test/mdx-namespace.test.js b/packages/integrations/mdx/test/mdx-namespace.test.ts
similarity index 82%
rename from packages/integrations/mdx/test/mdx-namespace.test.js
rename to packages/integrations/mdx/test/mdx-namespace.test.ts
index 13137e40e98c..3f7cd47bb375 100644
--- a/packages/integrations/mdx/test/mdx-namespace.test.js
+++ b/packages/integrations/mdx/test/mdx-namespace.test.ts
@@ -1,10 +1,10 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
describe('MDX Namespace', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -22,7 +22,7 @@ describe('MDX Namespace', () => {
const { document } = parseHTML(html);
const island = document.querySelector('astro-island');
- const component = document.querySelector('#component');
+ const component = document.querySelector('#component')!;
assert.notEqual(island, undefined);
assert.equal(component.textContent, 'Hello world');
@@ -33,7 +33,7 @@ describe('MDX Namespace', () => {
const { document } = parseHTML(html);
const island = document.querySelector('astro-island');
- const component = document.querySelector('#component');
+ const component = document.querySelector('#component')!;
assert.notEqual(island, undefined);
assert.equal(component.textContent, 'Hello world');
@@ -41,7 +41,7 @@ describe('MDX Namespace', () => {
});
describe('dev', () => {
- let devServer;
+ let devServer: DevServer;
before(async () => {
devServer = await fixture.startDevServer();
@@ -60,7 +60,7 @@ describe('MDX Namespace', () => {
const { document } = parseHTML(html);
const island = document.querySelector('astro-island');
- const component = document.querySelector('#component');
+ const component = document.querySelector('#component')!;
assert.notEqual(island, undefined);
assert.equal(component.textContent, 'Hello world');
@@ -75,7 +75,7 @@ describe('MDX Namespace', () => {
const { document } = parseHTML(html);
const island = document.querySelector('astro-island');
- const component = document.querySelector('#component');
+ const component = document.querySelector('#component')!;
assert.notEqual(island, undefined);
assert.equal(component.textContent, 'Hello world');
diff --git a/packages/integrations/mdx/test/mdx-optimize.test.js b/packages/integrations/mdx/test/mdx-optimize.test.ts
similarity index 82%
rename from packages/integrations/mdx/test/mdx-optimize.test.js
rename to packages/integrations/mdx/test/mdx-optimize.test.ts
index bc66a5732c91..3f79cf3742ee 100644
--- a/packages/integrations/mdx/test/mdx-optimize.test.js
+++ b/packages/integrations/mdx/test/mdx-optimize.test.ts
@@ -1,13 +1,12 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
const FIXTURE_ROOT = new URL('./fixtures/mdx-optimize/', import.meta.url);
describe('MDX optimize', () => {
- /** @type {import('../../../astro/test/test-utils').Fixture} */
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
root: FIXTURE_ROOT,
@@ -19,17 +18,17 @@ describe('MDX optimize', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- assert.equal(document.querySelector('h1').textContent.includes('MDX page'), true);
+ assert.equal(document.querySelector('h1')!.textContent.includes('MDX page'), true);
assert.equal(
- document.querySelector('p').textContent.includes('I once heard a very inspirational quote:'),
+ document.querySelector('p')!.textContent.includes('I once heard a very inspirational quote:'),
true,
);
- const blockquote = document.querySelector('blockquote.custom-blockquote');
+ const blockquote = document.querySelector('blockquote.custom-blockquote')!;
assert.notEqual(blockquote, null);
assert.equal(blockquote.textContent.includes('I like pancakes'), true);
- const code = document.querySelector('pre.astro-code');
+ const code = document.querySelector('pre.astro-code')!;
assert.notEqual(code, null);
assert.equal(code.textContent.includes(`const pancakes = 'yummy'`), true);
});
@@ -38,13 +37,13 @@ describe('MDX optimize', () => {
const html = await fixture.readFile('/import/index.html');
const { document } = parseHTML(html);
- assert.equal(document.querySelector('h1').textContent.includes('Astro page'), true);
+ assert.equal(document.querySelector('h1')!.textContent.includes('Astro page'), true);
assert.equal(
- document.querySelector('p').textContent.includes('I once heard a very inspirational quote:'),
+ document.querySelector('p')!.textContent.includes('I once heard a very inspirational quote:'),
true,
);
- const blockquote = document.querySelector('blockquote.custom-blockquote');
+ const blockquote = document.querySelector('blockquote.custom-blockquote')!;
assert.notEqual(blockquote, null);
assert.equal(blockquote.textContent.includes('I like pancakes'), true);
});
@@ -90,7 +89,7 @@ describe('MDX optimize', () => {
assert.doesNotMatch(html, /set:html=/);
assert.equal(
- document.getElementById('injected-root-hast').textContent,
+ document.getElementById('injected-root-hast')!.textContent,
'Injected root hast from rehype plugin',
);
});
diff --git a/packages/integrations/mdx/test/mdx-page.test.js b/packages/integrations/mdx/test/mdx-page.test.ts
similarity index 85%
rename from packages/integrations/mdx/test/mdx-page.test.js
rename to packages/integrations/mdx/test/mdx-page.test.ts
index 0327bcaf0a82..6237f2185701 100644
--- a/packages/integrations/mdx/test/mdx-page.test.js
+++ b/packages/integrations/mdx/test/mdx-page.test.ts
@@ -2,10 +2,10 @@ import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
describe('MDX Page', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
@@ -24,7 +24,7 @@ describe('MDX Page', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
+ const h1 = document.querySelector('h1')!;
assert.equal(h1.textContent, 'Hello page!');
});
@@ -59,13 +59,13 @@ describe('MDX Page', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- const keyTest = document.querySelector('#key-test');
+ const keyTest = document.querySelector('#key-test')!;
assert.equal(keyTest.textContent, 'oranges');
});
});
describe('dev', () => {
- let devServer;
+ let devServer: DevServer;
before(async () => {
devServer = await fixture.startDevServer();
@@ -83,7 +83,7 @@ describe('MDX Page', () => {
const html = await res.text();
const { document } = parseHTML(html);
- const h1 = document.querySelector('h1');
+ const h1 = document.querySelector('h1')!;
assert.equal(h1.textContent, 'Hello page!');
});
@@ -94,7 +94,7 @@ describe('MDX Page', () => {
const html = await res.text();
const $ = cheerio.load(html);
assert.equal($('h1').text(), '我的第一篇博客文章');
- assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.doesNotMatch(res.headers.get('content-type') ?? '', /charset=utf-8/);
assert.match(html, / {
const res = await fixture.fetch('/chinese-encoding-layout-frontmatter/');
assert.equal(res.status, 200);
const html = await res.text();
- assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.doesNotMatch(res.headers.get('content-type') ?? '', /charset=utf-8/);
assert.doesNotMatch(html, / {
const res = await fixture.fetch('/chinese-encoding-layout-manual/');
assert.equal(res.status, 200);
const html = await res.text();
- assert.doesNotMatch(res.headers.get('content-type'), /charset=utf-8/);
+ assert.doesNotMatch(res.headers.get('content-type') ?? '', /charset=utf-8/);
assert.doesNotMatch(html, / {
for (const extendMarkdownConfig of [true, false]) {
describe(`extendMarkdownConfig = ${extendMarkdownConfig}`, () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await buildFixture({
// Use unique outDir to avoid cache pollution between builds with different configs
@@ -75,8 +80,8 @@ describe('MDX plugins - Astro config integration', () => {
const html = await fixture.readFile(FILE);
const { document } = parseHTML(html);
- assert.notEqual(selectRemarkExample(document, 'MDX remark plugins not applied.'), null);
- assert.notEqual(selectRehypeExample(document, 'MDX rehype plugins not applied.'), null);
+ assert.notEqual(selectRemarkExample(document), null, 'MDX remark plugins not applied.');
+ assert.notEqual(selectRehypeExample(document), null, 'MDX rehype plugins not applied.');
});
it('Handles Markdown plugins', async () => {
@@ -84,11 +89,9 @@ describe('MDX plugins - Astro config integration', () => {
const { document } = parseHTML(html);
assert.equal(
- selectTocLink(
- document,
- '`remarkToc` plugin applied unexpectedly. Should override Markdown config.',
- ),
+ selectTocLink(document),
null,
+ '`remarkToc` plugin applied unexpectedly. Should override Markdown config.',
);
});
@@ -107,7 +110,7 @@ describe('MDX plugins - Astro config integration', () => {
const html = await fixture.readFile(FILE);
const { document } = parseHTML(html);
- const quote = selectSmartypantsQuote(document);
+ const quote = selectSmartypantsQuote(document)!;
if (extendMarkdownConfig === true) {
// smartypants: false inherited from markdown config — straight quotes and dashes preserved
@@ -129,7 +132,7 @@ describe('MDX plugins - Astro config integration', () => {
}
});
-async function buildFixture(config) {
+async function buildFixture(config: AstroInlineConfig = {}): Promise {
const fixture = await loadFixture({
root: FIXTURE_ROOT,
...config,
@@ -138,41 +141,42 @@ async function buildFixture(config) {
return fixture;
}
-function remarkExamplePlugin() {
+const remarkExamplePlugin: RemarkPlugin = () => {
return (tree) => {
tree.children.push({
type: 'html',
value: '',
});
};
-}
+};
-function rehypeExamplePlugin() {
+const rehypeExamplePlugin: RehypePlugin = () => {
return (tree) => {
tree.children.push({
type: 'element',
tagName: 'div',
properties: { 'data-rehype-plugin-works': 'true' },
+ children: [],
});
};
-}
+};
-function selectTocLink(document) {
+function selectTocLink(document: Document) {
return document.querySelector('ul a[href="#section-1"]');
}
-function selectGfmLink(document) {
+function selectGfmLink(document: Document) {
return document.querySelector('a[href="https://handle-me-gfm.com"]');
}
-function selectSmartypantsQuote(document) {
+function selectSmartypantsQuote(document: Document) {
return document.querySelector('blockquote');
}
-function selectRemarkExample(document) {
+function selectRemarkExample(document: Document) {
return document.querySelector('div[data-remark-plugin-works]');
}
-function selectRehypeExample(document) {
+function selectRehypeExample(document: Document) {
return document.querySelector('div[data-rehype-plugin-works]');
}
diff --git a/packages/integrations/mdx/test/mdx-plus-react-errors.test.js b/packages/integrations/mdx/test/mdx-plus-react-errors.test.ts
similarity index 67%
rename from packages/integrations/mdx/test/mdx-plus-react-errors.test.js
rename to packages/integrations/mdx/test/mdx-plus-react-errors.test.ts
index 9d87fa8a0045..67d909f6df1f 100644
--- a/packages/integrations/mdx/test/mdx-plus-react-errors.test.js
+++ b/packages/integrations/mdx/test/mdx-plus-react-errors.test.ts
@@ -4,30 +4,29 @@ import { loadFixture } from '../../../astro/test/test-utils.js';
function hookError() {
const error = console.error;
- const errors = [];
- console.error = function (...args) {
+ const errors: unknown[][] = [];
+ console.error = function (...args: unknown[]) {
errors.push(args);
};
- return () => {
+ return (): unknown[][] => {
console.error = error;
return errors;
};
}
describe('MDX and React with build errors', () => {
- let fixture;
- let unhook;
+ let unhook: (() => unknown[][]) | undefined;
it('shows correct error messages on build error', async () => {
try {
- fixture = await loadFixture({
+ const fixture = await loadFixture({
root: new URL('./fixtures/mdx-plus-react-errors/', import.meta.url),
});
unhook = hookError();
await fixture.build();
} catch (err) {
- assert.equal(err.message, 'a is not defined');
+ assert.equal((err as Error).message, 'a is not defined');
}
- unhook();
+ unhook?.();
});
});
diff --git a/packages/integrations/mdx/test/mdx-plus-react.test.js b/packages/integrations/mdx/test/mdx-plus-react.test.ts
similarity index 82%
rename from packages/integrations/mdx/test/mdx-plus-react.test.js
rename to packages/integrations/mdx/test/mdx-plus-react.test.ts
index 87f420fc06ef..7d821eb2302c 100644
--- a/packages/integrations/mdx/test/mdx-plus-react.test.js
+++ b/packages/integrations/mdx/test/mdx-plus-react.test.ts
@@ -1,23 +1,23 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
function hookError() {
const error = console.error;
- const errors = [];
- console.error = function (...args) {
+ const errors: unknown[][] = [];
+ console.error = function (...args: unknown[]) {
errors.push(args);
};
- return () => {
+ return (): unknown[][] => {
console.error = error;
return errors;
};
}
describe('MDX and React', () => {
- let fixture;
- let unhook;
+ let fixture: Fixture;
+ let unhook: () => unknown[][];
before(async () => {
fixture = await loadFixture({
@@ -31,7 +31,7 @@ describe('MDX and React', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- const p = document.querySelector('p');
+ const p = document.querySelector('p')!;
assert.equal(p.textContent, 'Hello world');
});
@@ -39,7 +39,7 @@ describe('MDX and React', () => {
it('mdx renders fine', async () => {
const html = await fixture.readFile('/post/index.html');
const { document } = parseHTML(html);
- const h = document.querySelector('#testing');
+ const h = document.querySelector('#testing')!;
assert.equal(h.textContent, 'Testing');
});
diff --git a/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js b/packages/integrations/mdx/test/mdx-syntax-highlighting.test.ts
similarity index 92%
rename from packages/integrations/mdx/test/mdx-syntax-highlighting.test.js
rename to packages/integrations/mdx/test/mdx-syntax-highlighting.test.ts
index 2f72d4eb2866..f490ff15babc 100644
--- a/packages/integrations/mdx/test/mdx-syntax-highlighting.test.js
+++ b/packages/integrations/mdx/test/mdx-syntax-highlighting.test.ts
@@ -24,9 +24,12 @@ describe('MDX syntax highlighting', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- const shikiCodeBlock = document.querySelector('pre.astro-code');
+ const shikiCodeBlock = document.querySelector('pre.astro-code')!;
assert.notEqual(shikiCodeBlock, null);
- assert.equal(shikiCodeBlock.getAttribute('style').includes('background-color:#24292e'), true);
+ assert.equal(
+ shikiCodeBlock.getAttribute('style')!.includes('background-color:#24292e'),
+ true,
+ );
});
it('respects markdown.shikiConfig.theme', async () => {
@@ -45,9 +48,12 @@ describe('MDX syntax highlighting', () => {
const html = await fixture.readFile('/index.html');
const { document } = parseHTML(html);
- const shikiCodeBlock = document.querySelector('pre.astro-code');
+ const shikiCodeBlock = document.querySelector('pre.astro-code')!;
assert.notEqual(shikiCodeBlock, null);
- assert.equal(shikiCodeBlock.getAttribute('style').includes('background-color:#282A36'), true);
+ assert.equal(
+ shikiCodeBlock.getAttribute('style')!.includes('background-color:#282A36'),
+ true,
+ );
});
});
@@ -140,7 +146,7 @@ describe('MDX syntax highlighting', () => {
[
rehypePrettyCode,
{
- onVisitHighlightedLine(node) {
+ onVisitHighlightedLine(node: { properties: Record }) {
node.properties.style = 'background-color:#000000';
},
},
diff --git a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js b/packages/integrations/mdx/test/mdx-vite-env-vars.test.ts
similarity index 95%
rename from packages/integrations/mdx/test/mdx-vite-env-vars.test.js
rename to packages/integrations/mdx/test/mdx-vite-env-vars.test.ts
index 213386ceb712..393c2b639719 100644
--- a/packages/integrations/mdx/test/mdx-vite-env-vars.test.js
+++ b/packages/integrations/mdx/test/mdx-vite-env-vars.test.ts
@@ -1,10 +1,10 @@
import * as assert from 'node:assert/strict';
import { before, describe, it } from 'node:test';
import { parseHTML } from 'linkedom';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture } from '../../../astro/test/test-utils.js';
describe('MDX - Vite env vars', () => {
- let fixture;
+ let fixture: Fixture;
before(async () => {
fixture = await loadFixture({
root: new URL('./fixtures/mdx-vite-env-vars/', import.meta.url),
@@ -54,7 +54,7 @@ describe('MDX - Vite env vars', () => {
const html = await fixture.readFile('/vite-env-vars/index.html');
const { document } = parseHTML(html);
- const dataAttrDump = document.querySelector('[data-env-dump]');
+ const dataAttrDump = document.querySelector('[data-env-dump]')!;
assert.notEqual(dataAttrDump, null);
assert.equal(dataAttrDump.getAttribute('data-env-prod'), 'true');
diff --git a/packages/integrations/mdx/test/remark-imgattr.test.js b/packages/integrations/mdx/test/remark-imgattr.test.ts
similarity index 75%
rename from packages/integrations/mdx/test/remark-imgattr.test.js
rename to packages/integrations/mdx/test/remark-imgattr.test.ts
index 067d18e236c6..50528561ed08 100644
--- a/packages/integrations/mdx/test/remark-imgattr.test.js
+++ b/packages/integrations/mdx/test/remark-imgattr.test.ts
@@ -1,17 +1,15 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
-import { loadFixture } from '../../../astro/test/test-utils.js';
+import { loadFixture, type Fixture, type DevServer } from '../../../astro/test/test-utils.js';
const FIXTURE_ROOT = new URL('./fixtures/image-remark-imgattr/', import.meta.url);
describe('Testing remark plugins for image processing', () => {
- /** @type {import('../../../astro/test/test-utils.js').Fixture} */
- let fixture;
+ let fixture: Fixture;
describe('start dev server', () => {
- /** @type {import('../../../astro/test/test-utils.js').DevServer} */
- let devServer;
+ let devServer: DevServer;
before(async () => {
fixture = await loadFixture({
@@ -26,7 +24,7 @@ describe('Testing remark plugins for image processing', () => {
});
describe('Test image attributes can be added by remark plugins', () => {
- let $;
+ let $: ReturnType;
before(async () => {
let res = await fixture.fetch('/');
let html = await res.text();
@@ -42,7 +40,10 @@ describe('Testing remark plugins for image processing', () => {
it('
was processed properly', async () => {
let $img = $('img');
- assert.equal(new URL($img.attr('src'), 'http://example.com').searchParams.get('w'), '300');
+ assert.equal(
+ new URL($img.attr('src') ?? '', 'http://example.com').searchParams.get('w'),
+ '300',
+ );
});
});
});
diff --git a/packages/integrations/mdx/test/test-utils.ts b/packages/integrations/mdx/test/test-utils.ts
new file mode 100644
index 000000000000..65da8a290573
--- /dev/null
+++ b/packages/integrations/mdx/test/test-utils.ts
@@ -0,0 +1,19 @@
+import type * as estree from 'estree';
+import type * as hast from 'hast';
+import type * as mdast from 'mdast';
+import type * as unified from 'unified';
+
+export type RemarkPlugin = unified.Plugin<
+ PluginParameters,
+ mdast.Root
+>;
+
+export type RehypePlugin = unified.Plugin<
+ PluginParameters,
+ hast.Root
+>;
+
+export type RecmaPlugin = unified.Plugin<
+ PluginParameters,
+ estree.Program
+>;
diff --git a/packages/integrations/mdx/test/units/mdx-compilation.test.js b/packages/integrations/mdx/test/units/mdx-compilation.test.ts
similarity index 88%
rename from packages/integrations/mdx/test/units/mdx-compilation.test.js
rename to packages/integrations/mdx/test/units/mdx-compilation.test.ts
index 945131ff5d6e..c45e9c65dcbe 100644
--- a/packages/integrations/mdx/test/units/mdx-compilation.test.js
+++ b/packages/integrations/mdx/test/units/mdx-compilation.test.ts
@@ -1,17 +1,20 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
-import { compile as _compile } from '@mdx-js/mdx';
import { rehypeHeadingIds } from '@astrojs/markdown-remark';
+import { compile as _compile, type CompileOptions, nodeTypes } from '@mdx-js/mdx';
+import type { AstroIntegrationLogger } from 'astro';
+import { visit as estreeVisit } from 'estree-util-visit';
+import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import remarkSmartypants from 'remark-smartypants';
import { visit } from 'unist-util-visit';
+import { ignoreStringPlugins } from '../../dist/utils.js';
+import type { RecmaPlugin, RehypePlugin, RemarkPlugin } from '../test-utils.js';
/**
* Compile MDX to JSX string output for inspection.
- * @param {string} mdxCode
- * @param {Readonly} options
*/
-async function compile(mdxCode, options = {}) {
+async function compile(mdxCode: string, options: Readonly = {}) {
const result = await _compile(mdxCode, {
jsx: true,
...options,
@@ -22,9 +25,10 @@ async function compile(mdxCode, options = {}) {
/**
* Compile MDX with rehype-raw (like Astro does) and return the JSX output.
*/
-async function compileWithRaw(mdxCode, options = {}) {
- const { nodeTypes } = await import('@mdx-js/mdx');
- const rehypeRaw = (await import('rehype-raw')).default;
+async function compileWithRaw(
+ mdxCode: string,
+ options: Readonly = {},
+): Promise {
return compile(mdxCode, {
rehypePlugins: [[rehypeRaw, { passThrough: nodeTypes }], ...(options.rehypePlugins || [])],
remarkPlugins: options.remarkPlugins || [],
@@ -96,14 +100,14 @@ describe('MDX SmartyPants plugin', () => {
describe('MDX remark plugins', () => {
it('supports custom remark plugins that modify the tree', async () => {
/** Remark plugin that appends a div */
- function remarkAddDiv() {
+ const remarkAddDiv: RemarkPlugin = () => {
return (tree) => {
tree.children.push({
type: 'html',
value: '',
});
};
- }
+ };
const code = await compileWithRaw('# Hello', {
remarkPlugins: [remarkAddDiv],
@@ -118,7 +122,7 @@ describe('MDX remark plugins', () => {
describe('MDX rehype plugins', () => {
it('supports custom rehype plugins that modify the tree', async () => {
/** Rehype plugin that appends a div */
- function rehypeAddDiv() {
+ const rehypeAddDiv: RehypePlugin = () => {
return (tree) => {
tree.children.push({
type: 'element',
@@ -127,7 +131,7 @@ describe('MDX rehype plugins', () => {
children: [],
});
};
- }
+ };
const code = await compileWithRaw('# Hello', {
rehypePlugins: [rehypeAddDiv],
@@ -139,7 +143,7 @@ describe('MDX rehype plugins', () => {
});
it('supports rehype plugins with namespaced SVG attributes', async () => {
- function rehypeSvg() {
+ const rehypeSvg: RehypePlugin = () => {
return (tree) => {
tree.children.push({
type: 'element',
@@ -155,7 +159,7 @@ describe('MDX rehype plugins', () => {
],
});
};
- }
+ };
const code = await compileWithRaw('# Hello', {
rehypePlugins: [rehypeSvg],
@@ -166,13 +170,12 @@ describe('MDX rehype plugins', () => {
describe('MDX recma plugins', () => {
it('supports custom recma plugins that transform the estree', async () => {
- const { visit: estreeVisit } = await import('estree-util-visit');
-
- function recmaExample() {
+ const recmaExample: RecmaPlugin = () => {
return (tree) => {
estreeVisit(tree, (node) => {
if (
node.type === 'VariableDeclarator' &&
+ node.id.type === 'Identifier' &&
node.id.name === 'recmaPluginWorking' &&
node.init?.type === 'Literal'
) {
@@ -184,7 +187,7 @@ describe('MDX recma plugins', () => {
}
});
};
- }
+ };
const mdxCode = `export const recmaPluginWorking = false;
@@ -226,7 +229,7 @@ describe('MDX heading IDs', () => {
});
it('allows user plugins to override heading IDs', async () => {
- function customIdPlugin() {
+ const customIdPlugin: RehypePlugin = () => {
return (tree) => {
let count = 0;
visit(tree, 'element', (node) => {
@@ -236,7 +239,7 @@ describe('MDX heading IDs', () => {
}
});
};
- }
+ };
const mdxCode = `# Hello
@@ -256,10 +259,9 @@ describe('MDX string-based plugin filtering', () => {
// When a string-based plugin is provided, the ignoreStringPlugins
// function filters it out. We test the filter function directly in utils.test.js.
// Here we verify that only function plugins affect output.
- const { ignoreStringPlugins } = await import('../../dist/utils.js');
- const logger = { warn() {} };
+ const logger = { warn() {} } as unknown as AstroIntegrationLogger;
- const plugins = ['remark-toc', () => (tree) => tree];
+ const plugins = ['remark-toc', () => (tree: unknown) => tree];
const filtered = ignoreStringPlugins(plugins, logger);
assert.equal(filtered.length, 1, 'Should filter out string plugin');
diff --git a/packages/integrations/mdx/test/units/rehype-optimize-static.test.js b/packages/integrations/mdx/test/units/rehype-optimize-static.test.ts
similarity index 81%
rename from packages/integrations/mdx/test/units/rehype-optimize-static.test.js
rename to packages/integrations/mdx/test/units/rehype-optimize-static.test.ts
index 6121975a405a..f93b8d3f3338 100644
--- a/packages/integrations/mdx/test/units/rehype-optimize-static.test.js
+++ b/packages/integrations/mdx/test/units/rehype-optimize-static.test.ts
@@ -1,13 +1,9 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
-import { compile as _compile } from '@mdx-js/mdx';
+import { compile as _compile, type CompileOptions } from '@mdx-js/mdx';
import { rehypeOptimizeStatic } from '../../dist/rehype-optimize-static.js';
-/**
- * @param {string} mdxCode
- * @param {Readonly} options
- */
-async function compile(mdxCode, options) {
+async function compile(mdxCode: string, options?: Readonly) {
const result = await _compile(mdxCode, {
jsx: true,
rehypePlugins: [rehypeOptimizeStatic],
@@ -20,12 +16,14 @@ async function compile(mdxCode, options) {
return dedent(jsx);
}
-function dedent(str) {
+function dedent(str: string) {
const lines = str.split('\n');
if (lines.length <= 1) return str;
// Get last line indent, and dedent this amount for the other lines
- const lastLineIndent = lines[lines.length - 1].match(/^\s*/)[0].length;
- return lines.map((line, i) => (i === 0 ? line : line.slice(lastLineIndent))).join('\n');
+ const lastLineIndent = /^\s*/.exec(lines[lines.length - 1])![0].length;
+ return lines
+ .map((line: string, i: number) => (i === 0 ? line : line.slice(lastLineIndent)))
+ .join('\n');
}
describe('rehype-optimize-static', () => {
diff --git a/packages/integrations/mdx/test/units/rehype-plugins.test.js b/packages/integrations/mdx/test/units/rehype-plugins.test.ts
similarity index 63%
rename from packages/integrations/mdx/test/units/rehype-plugins.test.js
rename to packages/integrations/mdx/test/units/rehype-plugins.test.ts
index bbcc96241e1f..89e65acaa53b 100644
--- a/packages/integrations/mdx/test/units/rehype-plugins.test.js
+++ b/packages/integrations/mdx/test/units/rehype-plugins.test.ts
@@ -1,20 +1,23 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
-import rehypeMetaString from '../../dist/rehype-meta-string.js';
+import type * as hast from 'hast';
+import { VFile } from 'vfile';
import { rehypeInjectHeadingsExport } from '../../dist/rehype-collect-headings.js';
+import rehypeMetaString from '../../dist/rehype-meta-string.js';
describe('rehypeMetaString', () => {
- function createCodeNode(meta) {
+ function createCodeNode(meta: string | undefined): hast.Element {
return {
type: 'element',
tagName: 'code',
- properties: {},
data: meta != null ? { meta } : undefined,
children: [{ type: 'text', value: 'const x = 1;' }],
+ position: undefined,
+ properties: {},
};
}
- function createTree(children) {
+ function createTree(children: hast.RootContent[]): hast.Root {
return { type: 'root', children };
}
@@ -32,7 +35,7 @@ describe('rehypeMetaString', () => {
const transform = rehypeMetaString();
transform(tree);
- assert.equal(codeNode.properties.metastring, '{1:3}');
+ assert.equal(codeNode.properties!.metastring, '{1:3}');
});
it('does not set metastring when no data.meta', () => {
@@ -44,26 +47,28 @@ describe('rehypeMetaString', () => {
const transform = rehypeMetaString();
transform(tree);
- assert.equal(codeNode.properties.metastring, undefined);
+ assert.equal(codeNode.properties!.metastring, undefined);
});
it('handles code elements without properties', () => {
- const codeNode = {
+ const codeNode: hast.Element = {
type: 'element',
tagName: 'code',
data: { meta: 'title="test"' },
children: [],
+ position: undefined,
+ properties: {},
};
const tree = createTree([codeNode]);
const transform = rehypeMetaString();
transform(tree);
- assert.equal(codeNode.properties.metastring, 'title="test"');
+ assert.equal(codeNode.properties!.metastring, 'title="test"');
});
it('ignores non-code elements', () => {
- const divNode = {
+ const divNode: hast.Element = {
type: 'element',
tagName: 'div',
properties: {},
@@ -75,42 +80,52 @@ describe('rehypeMetaString', () => {
const transform = rehypeMetaString();
transform(tree);
- assert.equal(divNode.properties.metastring, undefined);
+ assert.equal(divNode.properties!.metastring, undefined);
});
});
describe('rehypeInjectHeadingsExport', () => {
it('injects getHeadings export from vfile headings data', () => {
const headings = [
- { depth: 1, slug: 'hello', text: 'Hello' },
- { depth: 2, slug: 'world', text: 'World' },
+ { depth: 1, slug: 'hello1', text: 'Hello2' },
+ { depth: 2, slug: 'world3', text: 'World4' },
];
- const tree = { type: 'root', children: [] };
- const vfile = {
+ const tree: hast.Root = { type: 'root', children: [] };
+ const vfile = new VFile({
data: {
- astro: { headings },
+ astro: {
+ headings,
+ },
},
- };
+ });
const transform = rehypeInjectHeadingsExport();
transform(tree, vfile);
assert.equal(tree.children.length, 1);
- const injectedNode = tree.children[0];
+ const injectedNode = tree.children[0] as hast.Element & {
+ data: { estree: { type: string; body: unknown } };
+ };
assert.equal(injectedNode.type, 'mdxjsEsm');
// The node should contain a getHeadings function with our headings data
assert.ok(injectedNode.data.estree);
assert.equal(injectedNode.data.estree.type, 'Program');
+ // The function should contain the injected heading data
+ const functionBody = JSON.stringify(injectedNode.data.estree.body);
+ assert.match(functionBody, /hello1/);
+ assert.match(functionBody, /Hello2/);
+ assert.match(functionBody, /world3/);
+ assert.match(functionBody, /World4/);
});
it('injects empty array when no headings', () => {
- const tree = { type: 'root', children: [] };
- const vfile = {
+ const tree: hast.Root = { type: 'root', children: [] };
+ const vfile = new VFile({
data: {
astro: {},
},
- };
+ });
const transform = rehypeInjectHeadingsExport();
transform(tree, vfile);
@@ -121,13 +136,19 @@ describe('rehypeInjectHeadingsExport', () => {
});
it('prepends to existing children', () => {
- const existingChild = { type: 'element', tagName: 'p', children: [] };
- const tree = { type: 'root', children: [existingChild] };
- const vfile = {
+ const existingChild: hast.Element = {
+ type: 'element',
+ tagName: 'p',
+ children: [],
+ position: undefined,
+ properties: {},
+ };
+ const tree: hast.Root = { type: 'root', children: [existingChild] };
+ const vfile = new VFile({
data: {
astro: { headings: [] },
},
- };
+ });
const transform = rehypeInjectHeadingsExport();
transform(tree, vfile);
diff --git a/packages/integrations/mdx/test/units/server.test.js b/packages/integrations/mdx/test/units/server.test.ts
similarity index 100%
rename from packages/integrations/mdx/test/units/server.test.js
rename to packages/integrations/mdx/test/units/server.test.ts
diff --git a/packages/integrations/mdx/test/units/utils.test.js b/packages/integrations/mdx/test/units/utils.test.ts
similarity index 89%
rename from packages/integrations/mdx/test/units/utils.test.js
rename to packages/integrations/mdx/test/units/utils.test.ts
index 3c8cc4ea8c9b..0973d46699c4 100644
--- a/packages/integrations/mdx/test/units/utils.test.js
+++ b/packages/integrations/mdx/test/units/utils.test.ts
@@ -1,5 +1,6 @@
import assert from 'node:assert/strict';
import { describe, it } from 'node:test';
+import type { AstroConfig, AstroIntegrationLogger } from 'astro';
import {
appendForwardSlash,
getFileInfo,
@@ -27,15 +28,14 @@ describe('utils', () => {
});
describe('getFileInfo', () => {
- /** @param {Partial} overrides */
- function mockConfig(overrides = {}) {
+ function mockConfig(overrides: Partial = {}): AstroConfig {
return {
root: new URL('file:///project/'),
base: '/',
site: undefined,
trailingSlash: 'ignore',
...overrides,
- };
+ } as AstroConfig;
}
it('computes fileUrl for pages', () => {
@@ -96,22 +96,23 @@ describe('utils', () => {
describe('jsToTreeNode', () => {
it('parses a simple export statement', () => {
const node = jsToTreeNode('export const x = 1;');
+ const estree = node.data!.estree!;
assert.equal(node.type, 'mdxjsEsm');
- assert.equal(node.data.estree.type, 'Program');
- assert.equal(node.data.estree.sourceType, 'module');
- assert.ok(node.data.estree.body.length > 0);
+ assert.equal(estree.type, 'Program');
+ assert.equal(estree.sourceType, 'module');
+ assert.ok(estree.body.length > 0);
});
it('parses an import statement', () => {
const node = jsToTreeNode("import foo from 'bar';");
assert.equal(node.type, 'mdxjsEsm');
- assert.equal(node.data.estree.body[0].type, 'ImportDeclaration');
+ assert.equal(node.data!.estree!.body[0].type, 'ImportDeclaration');
});
it('parses a function export', () => {
const node = jsToTreeNode('export function getHeadings() { return []; }');
assert.equal(node.type, 'mdxjsEsm');
- const decl = node.data.estree.body[0];
+ const decl = node.data!.estree!.body[0];
assert.equal(decl.type, 'ExportNamedDeclaration');
});
@@ -123,14 +124,14 @@ describe('utils', () => {
});
describe('ignoreStringPlugins', () => {
- function mockLogger() {
- const warnings = [];
+ function mockLogger(): AstroIntegrationLogger & { warnings: string[] } {
+ const warnings: string[] = [];
return {
- warn(msg) {
+ warn: (msg: string) => {
warnings.push(msg);
},
warnings,
- };
+ } as AstroIntegrationLogger & { warnings: string[] };
}
it('returns function plugins unchanged', () => {
diff --git a/packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.js b/packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.ts
similarity index 99%
rename from packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.js
rename to packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.ts
index 6e63c83cc9c8..8cbddd643e5e 100644
--- a/packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.js
+++ b/packages/integrations/mdx/test/units/vite-plugin-mdx-postprocess.test.ts
@@ -14,7 +14,7 @@ await init;
/**
* Helper: parse code with es-module-lexer and return [imports, exports]
*/
-function parseCode(code) {
+function parseCode(code: string) {
return parse(code);
}
diff --git a/packages/integrations/mdx/tsconfig.test.json b/packages/integrations/mdx/tsconfig.test.json
new file mode 100644
index 000000000000..853326403557
--- /dev/null
+++ b/packages/integrations/mdx/tsconfig.test.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "include": ["test/**/*.ts"],
+ "exclude": ["test/fixtures/**"],
+ "compilerOptions": {
+ "allowJs": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "rewriteRelativeImportExtensions": true,
+ "rootDir": "."
+ },
+ "references": [{ "path": "../../astro/tsconfig.test.json" }]
+}