Importing XML as text
Recently, I wanted to have a way to import XML files as a string in my project.
So, for example, if I had a file rss.xml
:
<rss version="2.0"> <channel> <title>Hacker News</title> <link>https://news.ycombinator.com/</link> <description>Links for the intellectually curious, ranked by readers.</description> <item> <title>Extreme Pi Boot Optimization</title> <link>https://kittenlabs.de/blog/2024/09/01/extreme-pi-boot-optimization/</link> <pubDate>Sun, 1 Sep 2024 21:36:55 +0000</pubDate> <comments>https://news.ycombinator.com/item?id=41420597</comments> <description> <![CDATA[<a href="https://news.ycombinator.com/item?id=41420597">Comments</a>]]> </description> </item> <item> <title>Programming Zero Knowledge Proofs: From Zero to Hero</title> <link>https://zkintro.com/articles/programming-zkps-from-zero-to-hero</link> <pubDate>Fri, 30 Aug 2024 06:08:41 +0000</pubDate> <comments>https://news.ycombinator.com/item?id=41398092</comments> <description> <![CDATA[<a href="https://news.ycombinator.com/item?id=41398092">Comments</a>]]> </description> </item> </channel></rss>
I want to be able to import the full content of the document as a string in my project:
import rss from './rss.xml';
console.log(rss);
<rss version="2.0"> <channel> <title>Hacker News</title> <link>https://news.ycombinator.com/</link> <description>Links for the intellectually curious, ranked by readers.</description> <item> <title>Extreme Pi Boot Optimization</title> <link>https://kittenlabs.de/blog/2024/09/01/extreme-pi-boot-optimization/</link> <pubDate>Sun, 1 Sep 2024 21:36:55 +0000</pubDate> <comments>https://news.ycombinator.com/item?id=41420597</comments> <description> <![CDATA[<a href="https://news.ycombinator.com/item?id=41420597">Comments</a>]]> </description> </item> <item> <title>Programming Zero Knowledge Proofs: From Zero to Hero</title> <link>https://zkintro.com/articles/programming-zkps-from-zero-to-hero</link> <pubDate>Fri, 30 Aug 2024 06:08:41 +0000</pubDate> <comments>https://news.ycombinator.com/item?id=41398092</comments> <description> <![CDATA[<a href="https://news.ycombinator.com/item?id=41398092">Comments</a>]]> </description> </item> </channel></rss>
TypeScript#
We need to let TypeScript know how to handle XML files to resolve the following error:
Cannot find module './resources/rss.xml' or its corresponding type declarations.ts(2307)
We can do this by adding a declaration file xml.d.ts
:
declare module '*.xml' { const text: string; export default text;}
Directorysrc
Directoryresources
- rss.xml
- xml.d.ts
- index.ts
- tsconfig.json
Bundling with ESBuild#
But types don’t exist at runtime. So, how do we actually import the XML file? Basically, we want to read the content of the file and then export it as a string.
import { readFile } from 'node:fs/promises';
const rss = await readFile('./resources/rss.xml', 'utf-8');
export default rss;
We can tell esbuild
to do this by using the text loader:
import { build } from "esbuild";import esbuildPluginPino from "esbuild-plugin-pino";
build({ entryPoints: ["./src/startServer.ts"], bundle: true, platform: "node", outdir: "./dist", minify: true, loader: { ".xml": "text", }, plugins: [esbuildPluginPino({ transports: ["pino-pretty"] })],}).catch((error) => console.error(error));
If this doesn’t fit your needs, you can create your own plugin like esbuild-plugin-inline-import.
Vite / Vitest#
vitest uses vite under the hood for running your tests. With vite
, you can
import XML files directly using ?raw:
import rss from './resources/rss.xml?raw';
Unfortunately, since I was using esbuild
for bundling, I couldn’t modify the import path to include ?raw
. So, instead
I created a plugin to tell vite
how to handle the XML files:
import { readFile } from 'node:fs/promises';import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { globals: true, include: ['**/src/**/*.{test,spec}.?(c|m)[jt]s?(x)'], }, plugins: [ { name: 'handle-xml', async transform(_, id) { const xmlRegex = /\.xml$/; if (!xmlRegex.test(id)) { return; } const xml = (await readFile(id)).toString(); return { code: `export default \`${xml}\``, }; }, }, ],});
Essentially, we are filtering for XML files and then reading the content of the file and exporting it as a string. This took inspiration from vite-plugin-xml-loader.