');
expect(full).to.contain('');
expect(full).to.not.contain('__img');
+ expect(full).to.not.contain('__jsxImg');
});
// Fumadocs' own renderer fails the whole page over an image in a description; `ApiMarkdown` stands in for it.
diff --git a/test/portal-template/remark-jsx-images.test.ts b/test/portal-template/remark-jsx-images.test.ts
new file mode 100644
index 000000000..25ee4818d
--- /dev/null
+++ b/test/portal-template/remark-jsx-images.test.ts
@@ -0,0 +1,152 @@
+import path from 'node:path';
+import { expect } from 'chai';
+import { remark } from 'remark';
+import remarkMdx from 'remark-mdx';
+import { FileName } from '../../src/types/file/fileName';
+import { parsePage } from '../../src/types/portal/page';
+import { remarkJsxImages } from '../../portal-template/remark-jsx-images';
+
+describe('the
\n\n
\n')).to.equal(
+ 'import __jsxImg0 from "./flow.png"\n\nimport __jsxImg1 from "../../static/images/logo.png"\n\n' +
+ '
here.\n\n
\n
\n')).to.equal(
+ 'import __jsxImg0 from "./my flow.png"\n\n
',
+ '
',
+ '
\n\n
\n\n
\n', false)).to.equal(
+ 'import __jsxImg0 from "./flow.png"\n\n
\n\n
\n');
+ remarkJsxImages({ publicDir: staticDirectory })(tree, { dirname: pageDirectory });
+
+ expect(markdown.stringify(tree)).to.equal('
\n');
+ });
+
+ it('leaves a page it has no path for as written', () => {
+ const tree = processor.parse('
\n');
+ remarkJsxImages({ publicDir: staticDirectory })(tree, {});
+
+ expect(processor.stringify(tree)).to.equal('
\n');
+ });
+
+ // MDX compiles these programs, not the text the tests above read.
+ it('gives MDX the program of each import and of the address', () => {
+ const tree = processor.parse('
\n');
+ remarkJsxImages({ publicDir: staticDirectory })(tree, { dirname: pageDirectory });
+ const [esm, image] = tree.children as unknown as [
+ { data: { estree: unknown } },
+ { attributes: [{ value: { data: { estree: unknown } } }] }
+ ];
+
+ expect(esm.data.estree).to.deep.equal({
+ type: 'Program',
+ sourceType: 'module',
+ body: [
+ {
+ type: 'ImportDeclaration',
+ source: { type: 'Literal', value: './flow.png' },
+ specifiers: [{ type: 'ImportDefaultSpecifier', local: { type: 'Identifier', name: '__jsxImg0' } }]
+ }
+ ]
+ });
+ expect(image.attributes[0].value.data.estree).to.deep.equal({
+ type: 'Program',
+ sourceType: 'module',
+ body: [{ type: 'ExpressionStatement', expression: { type: 'Identifier', name: '__jsxImg0' } }]
+ });
+ });
+
+ // The CLI reports a missing file before a build that would otherwise fail over its import, naming no page.
+ it('imports exactly the files the CLI checks', async () => {
+ const mdx =
+ [
+ '---\ntitle: Images\n---',
+ '
',
+ 'Inline
here.',
+ '
\n
',
+ '
',
+ ...namingNoFile
+ ].join('\n\n') + '\n';
+ const checked = (await parsePage(mdx, new FileName('page.mdx'), 'content/guides/page.mdx')).images;
+ const imported = (publicDir: string | false) =>
+ [...transformed(mdx, publicDir).matchAll(/^import __jsxImg\d+ from "(.+)"$/gm)].map(([, source]) =>
+ path.join(pageDirectory, source)
+ );
+
+ expect(imported(staticDirectory)).to.deep.equal(
+ checked.map(({ from, path: file }) => path.join(from === 'static' ? staticDirectory : pageDirectory, file))
+ );
+ // Without a static directory the CLI refuses a `/…` image as missing, so the two need to agree only on the rest.
+ expect(imported(false)).to.deep.equal(
+ checked.flatMap(({ from, path: file }) => (from === 'page' ? [path.join(pageDirectory, file)] : []))
+ );
+ });
+
+ it('imports a static file on another drive by its own path, which static-images.ts resolves', function () {
+ if (process.platform !== 'win32') this.skip();
+
+ const tree = processor.parse('
\n');
+ remarkJsxImages({ publicDir: 'D:\\static' })(tree, { dirname: 'C:\\portal\\content' });
+
+ expect(processor.stringify(tree)).to.equal(
+ 'import __jsxImg0 from "./D:/static/images/logo.png"\n\nSpaced out by a class only this page uses.
+ +
+
+The steps, from beside this page:
diff --git a/test/resources/portal-inputs/subpath/src/content/flow.png b/test/resources/portal-inputs/subpath/src/content/flow.png
new file mode 100644
index 000000000..716070fb6
Binary files /dev/null and b/test/resources/portal-inputs/subpath/src/content/flow.png differ
diff --git a/test/types/portal-source-context.test.ts b/test/types/portal-source-context.test.ts
index 7b55d3e59..81f2319af 100644
--- a/test/types/portal-source-context.test.ts
+++ b/test/types/portal-source-context.test.ts
@@ -1932,7 +1932,7 @@ describe('PortalSourceContext', () => {
});
});
- // The build imports each Markdown image, and fails as a whole over one it cannot find.
+ // The build imports each image, and fails as a whole over one it cannot find.
describe('the images the pages show', () => {
beforeEach(() => {
writeConfig({ site: { name: 'Calc' } });
@@ -2023,6 +2023,18 @@ describe('PortalSourceContext', () => {
]);
});
+ it('names an
\n\n
\n\n
\n'
+ );
+
+ expect(await missingImages()).to.deep.equal([
+ ['content/guides/index.mdx:8', './missing.png', 'content/guides/missing.png', null]
+ ]);
+ });
+
it('reports the images with the other problems in the content', async () => {
write('content/notes.md', '# Notes');
write('content/index.md', page('Home') + '\n');
diff --git a/test/types/portal/page.test.ts b/test/types/portal/page.test.ts
index a07ef80d7..d4fbf7a99 100644
--- a/test/types/portal/page.test.ts
+++ b/test/types/portal/page.test.ts
@@ -148,5 +148,38 @@ describe('parsePage', () => {
expect(found.map(({ path, line }) => [path, line])).to.deep.equal([['a.png', 3]]);
});
+
+ it('reads the src of an
\n\nInline
here.\n\n' +
+ '
\n
\n\n\n', 'page.mdx')).to.deep.equal([
+ { url: '/images/a.png', line: 1, from: 'static', path: 'images/a.png' },
+ { url: ' ./b.png ', line: 3, from: 'page', path: ' ./b.png ' }
+ ]);
+ });
+
+ it('leaves out an
\n\n
\n\n\n';
+
+ expect(await images(mdx, 'page.mdx')).to.deep.equal([]);
+ });
+
+ // The build leaves out a `.md` page's HTML, an `
\n\nInline
here.\n')).to.deep.equal([]);
+ });
});
});