Skip to content

Commit

Permalink
feat: xhtml jsx supports
Browse files Browse the repository at this point in the history
  • Loading branch information
yjl9903 committed Feb 28, 2023
1 parent 5a2ced8 commit 9f34b2c
Show file tree
Hide file tree
Showing 12 changed files with 122 additions and 33 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@
"packageManager": "pnpm@7.28.0",
"engines": {
"node": ">=v16.19.0"
},
"pnpm": {
"patchedDependencies": {
"unbuild@1.1.2": "patches/unbuild@1.1.2.patch"
}
}
}
18 changes: 12 additions & 6 deletions packages/core/build.config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { defineBuildConfig } from 'unbuild'
import { defineBuildConfig } from 'unbuild';

export default defineBuildConfig({
entries: [
'src/index',
],
entries: ['src/index'],
declaration: true,
clean: true,
rollup: {
emitCJS: true,
},
});
esbuild: {
jsxFactory: 'h',
jsxFragment: 'fragment',
loaders: {
'.ts': 'ts',
'.tsx': 'tsx'
}
}
}
});
2 changes: 0 additions & 2 deletions packages/core/src/bundle/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export * from './xhtml';

export * from './bundle';
18 changes: 8 additions & 10 deletions packages/core/src/epub/nav.ts → packages/core/src/epub/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { XHTMLBuilder, XHTMLNode, h } from '../bundle/xhtml';
import { XHTMLBuilder, XHTMLNode, h } from '../render';

interface NavRoot {
heading?: 1 | 2 | 3 | 4 | 5 | 6;
Expand Down Expand Up @@ -32,18 +32,16 @@ export function buildTocNav(nav: NavRoot) {
if (nav.title && nav.heading) {
root.children.push(h('h' + nav.heading, {}, nav.title));
}
root.children.push(h('ol', {}, list(nav.list)));
root.children.push(<ol>{list(nav.list)}</ol>);

return builder.title(nav.title ?? 'Toc').body(root);

function list(items: Array<SubNavItem | NavItem>): XHTMLNode[] {
return items.map((i) => ({
tag: 'li',
attrs: {},
children: [
i.href ? h('a', { href: i.href }, i.text) : h('span', {}, i.text),
'list' in i && i.list && h('ol', {}, list(i.list))
].filter(Boolean) as XHTMLNode[]
}));
return items.map((i) => (
<li>
{i.href ? <a href={i.href}>{i.text}</a> : <span>{i.text}</span>}
{'list' in i && i.list && <ol>{list(i.list)}</ol>}
</li>
));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { XMLBuilder } from 'fast-xml-parser';

import { TextCSS } from '../constant';

export * from './types';

const builder = new XMLBuilder({
format: true,
ignoreAttributes: false,
Expand All @@ -12,7 +14,7 @@ const builder = new XMLBuilder({
export interface XHTMLNode {
tag: string;
attrs?: Record<string, string>;
children?: string | Array<XHTMLNode>;
children?: Array<XHTMLNode | string>;
}

export class XHTMLBuilder {
Expand Down Expand Up @@ -45,7 +47,7 @@ export class XHTMLBuilder {
rel: 'stylesheet',
type: TextCSS
},
children: ''
children: ['']
});
return this;
}
Expand All @@ -64,10 +66,14 @@ export class XHTMLBuilder {
const obj: any = {
...attrs
};
if (typeof node.children === 'string') {
obj['#text'] = node.children;
} else if (Array.isArray(node.children)) {
Object.assign(obj, list(node.children));

if (Array.isArray(node.children)) {
const text = node.children.filter((c): c is string => typeof c === 'string');
const nodes = node.children.filter((c): c is XHTMLNode => typeof c !== 'string');
if (text.length > 0) {
obj['#text'] = text[0];
}
Object.assign(obj, list(nodes));
}

return obj;
Expand Down Expand Up @@ -101,10 +107,23 @@ export class XHTMLBuilder {
}
}

export function h(tag: string, attrs: XHTMLNode['attrs'], children: XHTMLNode['children']) {
return {
export function h(
tag: string,
attrs: Record<string, string> = {},
...children: Array<string | XHTMLNode | Array<string | XHTMLNode>>
) {
const sub = children.filter((c: any) => c !== undefined && c !== null && c !== false).flat();

const o = {
tag,
attrs,
children
attrs: attrs ?? {},
children: sub
} satisfies XHTMLNode;

return o;
}

export function fragment(...args: any[]) {
console.log(args);
return {};
}
12 changes: 12 additions & 0 deletions packages/core/src/render/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
declare global {
namespace JSX {
interface IntrinsicElements extends EpubIntrinsicElements {}
}
}

export interface EpubIntrinsicElements {
li: {};
ol: {};
span: {};
a: { href: string };
}
2 changes: 1 addition & 1 deletion packages/core/test/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest';

import { Epub } from '../src';
import { Html } from '../src/epub/item';
import { XHTMLBuilder } from '../src/render';
import { buildTocNav } from '../src/epub/nav';
import { XHTMLBuilder } from '../src/bundle/xhtml';
import { makeContainer, makePackageDocument } from '../src/bundle';

describe('Bundle Epub', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it } from 'vitest';

describe('hello', () => {
it('should work', () => {
expect('Hello').toEqual('Hello');
});
});
});
27 changes: 27 additions & 0 deletions packages/core/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { defineConfig } from 'vitest/config';

import { transform } from 'esbuild';

export default defineConfig({
plugins: [
{
name: 'epubook:transform-jsx',
enforce: 'pre',
async transform(code, id) {
if (id.endsWith('.tsx')) {
return await transform(code, {
loader: 'tsx',
sourcefile: id,
jsxFactory: 'h',
jsxFragment: 'fragment'
});
}
}
}
],
test: {
transformMode: {
web: [/\.[jt]sx$/]
}
}
});
15 changes: 15 additions & 0 deletions patches/unbuild@1.1.2.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
diff --git a/dist/shared/unbuild.e1e6feef.mjs b/dist/shared/unbuild.e1e6feef.mjs
index 5efb149df405e81132bb6ed0f906765036a6514e..dc5493f0b2ea75df3bdb235c7d3a43567d4f1d5e 100644
--- a/dist/shared/unbuild.e1e6feef.mjs
+++ b/dist/shared/unbuild.e1e6feef.mjs
@@ -363,7 +363,9 @@ function esbuild(options) {
target: options.target,
define: options.define,
sourcemap: options.sourceMap,
- sourcefile: id
+ sourcefile: id,
+ jsxFactory: options.jsxFactory,
+ jsxFragment: options.jsxFragment
});
printWarnings(id, result, this);
return result.code && {
10 changes: 8 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "fragment",
"strictNullChecks": true,
"resolveJsonModule": true,
"skipLibCheck": true,
Expand Down

0 comments on commit 9f34b2c

Please sign in to comment.