Skip to content

Comments

✨ Add Node.js parsing-only entry point#5

Merged
williamchong merged 5 commits intolikecoin:masterfrom
williamchong:master
Feb 18, 2026
Merged

✨ Add Node.js parsing-only entry point#5
williamchong merged 5 commits intolikecoin:masterfrom
williamchong:master

Conversation

@williamchong
Copy link
Member

No description provided.

Add `@likecoin/epub-ts/node` subpath export that shims DOMParser,
XMLSerializer, and document via linkedom, enabling EPUB parsing
(metadata, spine, navigation, section rendering) in Node.js without
a browser.

- Add linkedom as optional peerDependency
- Create src/node.ts with linkedom shims and parsing-safe re-exports
- Add vite.config.node.ts for ESM + CJS node bundles
- Guard window references in archive.ts, url.ts, replacements.ts
- Replace window.decodeURIComponent with global decodeURIComponent
- Add try/catch for CSS namespace selectors in querySelectorByType
- Add getElementsByTagName fallback for parsers without NS support
- Add 10 Node.js integration tests (191 total)
- Fix typo: decodededUrl → decodedUrl in archive.ts (6 occurrences)
- Remove `any` escape: use typed assertion for globalThis.document in node.ts
- Fix README example: slice Buffer to exact ArrayBuffer to avoid corruption
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Node.js-specific, parsing-only entry point to epub.ts so consumers can parse EPUBs (metadata/spine/navigation/section HTML) in Node without a browser DOM, while keeping the existing browser-focused API intact.

Changes:

  • Added @likecoin/epub-ts/node entry point with linkedom-based DOM shims (DOMParser, XMLSerializer, document).
  • Added a dedicated Node build (vite.config.node.ts) and package subpath export (./node) producing epub.node.js / epub.node.cjs + node.d.ts.
  • Improved Node/SSR compatibility by guarding window usage and adding parser fallbacks for selector / namespace limitations; added Vitest Node coverage + docs updates.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
vite.config.node.ts Adds a separate Vite library build to emit Node ESM/CJS bundles and Node entry d.ts.
src/node.ts Implements Node entry point with linkedom DOM shims and re-exports parsing-related classes.
package.json Adds ./node export map entry, runs Node build in npm run build, and updates sideEffects + deps/peers for linkedom.
package-lock.json Locks linkedom and its transitive dependencies.
test/node.test.ts Adds Node-environment Vitest coverage for opening/parsing an EPUB via the Node entry.
src/utils/url.ts Guards window access for Node/SSR import safety.
src/utils/replacements.ts Guards window access for Node/SSR import safety.
src/utils/core.ts Makes querySelectorByType resilient to DOM engines lacking namespaced selectors.
src/packaging.ts Adds fallback lookup for DC metadata when XML namespace APIs aren’t supported.
src/archive.ts Removes direct window.* usage (decodeURIComponent / URL selection) for Node compatibility.
README.md Documents Node parsing-only usage and new import path.
PROJECT_STATUS.md Updates status/outputs to reflect Node entry point completion and new test totals.
CHANGELOG.md Documents new Node entry point and related compatibility fixes.
AGENTS.md Documents new src/node.ts entry point file.

src/archive.ts Outdated
const _URL = window.URL || window.webkitURL || window.mozURL;
const _URL = typeof window !== "undefined" ? (window.URL || window.webkitURL || window.mozURL) : URL;
for (const fromCache in this.urlCache) {
_URL.revokeObjectURL(fromCache);
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In destroy(), the loop revokes object URLs using the cache key (fromCache) rather than the cached object URL value. Since urlCache maps original archive paths to generated object URLs, this likely leaves the created object URLs unreleased. Consider revoking this.urlCache[fromCache] (and checking it’s defined) instead of the key string.

Suggested change
_URL.revokeObjectURL(fromCache);
const cachedUrl = this.urlCache[fromCache];
if (cachedUrl) {
_URL.revokeObjectURL(cachedUrl);
}

Copilot uses AI. Check for mistakes.
The for...in loop iterated urlCache keys (archive paths) and passed
them to revokeObjectURL, leaving the actual blob URLs unreleased.
Now revokes the cached URL values instead.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

console.log(book.navigation.toc.map(item => item.label));

const section = book.spine.first();
const html = await section.render(book.archive.request.bind(book.archive));
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Node.js usage snippet won’t typecheck as written: book.archive is typed as possibly undefined, so book.archive.request.bind(book.archive) will error in TypeScript. Consider using a non-null assertion (matching the test) or add a small guard before calling section.render(...) so the snippet compiles cleanly.

Suggested change
const html = await section.render(book.archive.request.bind(book.archive));
const html = await section.render(book.archive!.request.bind(book.archive!));

Copilot uses AI. Check for mistakes.
@williamchong williamchong merged commit f4bf928 into likecoin:master Feb 18, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant