diff --git a/packages/ui/src/components/MarkdownRenderer.svelte b/packages/ui/src/components/MarkdownRenderer.svelte
index 79a30f0..0dca3d1 100644
--- a/packages/ui/src/components/MarkdownRenderer.svelte
+++ b/packages/ui/src/components/MarkdownRenderer.svelte
@@ -1,19 +1,6 @@
unsafe link',
});
- await tick();
+ await waitForMarkdown(container);
expect(container.querySelector('script')).toBeNull();
expect(container.querySelector('[onerror]')).toBeNull();
@@ -30,7 +39,9 @@ describe('MarkdownRenderer security and enhancement', () => {
const { container } = render(MarkdownRenderer, {
content: '```js\nconst answer = 42;\n```',
});
- await tick();
+ await waitFor(() => {
+ expect(container.querySelector('.code-block-wrapper')).not.toBeNull();
+ });
const wrapper = container.querySelector('.code-block-wrapper');
const code = wrapper?.querySelector('code');
@@ -55,7 +66,7 @@ describe('MarkdownRenderer security and enhancement', () => {
await rerender({
content: '```">
\nunsafe\n```',
});
- await tick();
+ await waitForMarkdown(container);
const header = container.querySelector('.code-block-wrapper > div');
expect(header).not.toBeNull();
@@ -80,10 +91,11 @@ describe('MarkdownRenderer security and enhancement', () => {
originalAddEventListener.call(this, type, listener, options);
});
- const { rerender } = render(MarkdownRenderer, { content: '```text\none\n```' });
+ const { container, rerender } = render(MarkdownRenderer, { content: '```text\none\n```' });
+ await waitForMarkdown(container);
await rerender({ content: '```text\ntwo\n```' });
await rerender({ content: '```text\nthree\n```' });
- await tick();
+ await waitForMarkdown(container);
expect(delegatedClickBindings).toBe(1);
});
diff --git a/scripts/check-package-packs.ts b/scripts/check-package-packs.ts
index 1cfb7db..d475150 100644
--- a/scripts/check-package-packs.ts
+++ b/scripts/check-package-packs.ts
@@ -31,6 +31,7 @@ interface PackageManifest {
bin?: string | Record;
exports?: unknown;
dependencies?: Record;
+ devDependencies?: Record;
peerDependencies?: Record;
}
@@ -43,6 +44,12 @@ const repositoryRoot = resolve(import.meta.dir, '..');
const packagesRoot = join(repositoryRoot, 'packages');
const tscPath = join(repositoryRoot, 'node_modules', 'typescript', 'bin', 'tsc');
const pnpmVersion = '11.11.0';
+const optionalMarkdownPeers = [
+ 'highlight.js',
+ 'isomorphic-dompurify',
+ 'marked',
+ 'marked-highlight',
+] as const;
const expectations: PackageExpectation[] = [
{
@@ -291,9 +298,16 @@ async function verifyUiPnpmPeerTree(
await readFile(join(repositoryRoot, 'package.json'), 'utf8'),
) as { overrides?: Record };
const svelteVersion = rootManifest.overrides?.svelte;
+ const viteVersion = rootManifest.overrides?.vite;
const queryVersion = uiManifest.peerDependencies?.['@tanstack/svelte-query'];
+ const sveltePluginVersion = uiManifest.devDependencies?.['@sveltejs/vite-plugin-svelte'];
assert(svelteVersion, 'root package.json: overrides.svelte is required for pnpm verification');
+ assert(viteVersion, 'root package.json: overrides.vite is required for pnpm verification');
assert(queryVersion, '@svadmin/ui: @tanstack/svelte-query peer range is required for pnpm verification');
+ assert(
+ sveltePluginVersion,
+ '@svadmin/ui: @sveltejs/vite-plugin-svelte dev dependency is required for pnpm verification',
+ );
const consumerDirectory = join(packDirectory, 'pnpm-peer-consumer');
await mkdir(consumerDirectory, { recursive: true });
@@ -309,6 +323,10 @@ async function verifyUiPnpmPeerTree(
'@tanstack/svelte-query': queryVersion,
svelte: svelteVersion,
},
+ devDependencies: {
+ '@sveltejs/vite-plugin-svelte': sveltePluginVersion,
+ vite: viteVersion,
+ },
}, null, 2)}\n`,
);
@@ -352,6 +370,12 @@ async function verifyUiPnpmPeerTree(
);
const virtualStoreEntries = await readdir(join(consumerDirectory, 'node_modules', '.pnpm'));
+ for (const optionalPeer of optionalMarkdownPeers) {
+ assert(
+ !virtualStoreEntries.some((entry) => entry.startsWith(`${optionalPeer}@`)),
+ `@svadmin/ui: pnpm strict consumer unexpectedly installed optional peer ${optionalPeer}`,
+ );
+ }
const svelteVersions = new Set(
virtualStoreEntries
.map((entry) => /^svelte@([^_]+)(?:_|$)/.exec(entry)?.[1])
@@ -372,7 +396,25 @@ async function verifyUiPnpmPeerTree(
['--yes', `pnpm@${pnpmVersion}`, 'list', 'svelte', '--depth', 'Infinity'],
consumerDirectory,
);
- return `pnpm@${pnpmVersion} strict packed consumer passed\nforbidden dependencies absent: cmdk-sv, sonner-svelte, @melt-ui/svelte\n${dependencyTree.trim()}\nresolved Svelte versions: ${resolvedSvelteVersion}`;
+
+ const consumerEntry = join(consumerDirectory, 'markdown-import.ts');
+ await writeFile(
+ consumerEntry,
+ `import { MarkdownRenderer } from '@svadmin/ui';\nconsole.info(typeof MarkdownRenderer);\n`,
+ );
+ const viteConfig = join(consumerDirectory, 'vite.config.mjs');
+ await writeFile(
+ viteConfig,
+ `import { svelte } from '@sveltejs/vite-plugin-svelte';\nexport default { root: ${JSON.stringify(consumerDirectory)}, plugins: [svelte()], build: { lib: { entry: ${JSON.stringify(consumerEntry)}, formats: ['es'] } } };\n`,
+ );
+ const vitePath = join(repositoryRoot, 'node_modules', 'vite', 'bin', 'vite.js');
+ const optionalPeerBuild = run(
+ 'node',
+ [vitePath, 'build', '--config', viteConfig],
+ consumerDirectory,
+ );
+
+ return `pnpm@${pnpmVersion} strict packed consumer passed\nforbidden dependencies absent: cmdk-sv, sonner-svelte, @melt-ui/svelte\noptional markdown peers absent: ${optionalMarkdownPeers.join(', ')}\n${optionalPeerBuild.trim()}\n${dependencyTree.trim()}\nresolved Svelte versions: ${resolvedSvelteVersion}`;
}
async function createConsumer(packDirectory: string, results: Map): Promise {