wp-epub-gen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +24 -0
- package/.mocharc.json +3 -0
- package/.prettierrc.json +19 -0
- package/README.md +87 -0
- package/dist/downloadImage.d.ts +7 -0
- package/dist/downloadImage.js +82 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +14 -0
- package/dist/generateTempFile.d.ts +7 -0
- package/dist/generateTempFile.js +121 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +128 -0
- package/dist/makeCover.d.ts +7 -0
- package/dist/makeCover.js +63 -0
- package/dist/parseContent.d.ts +7 -0
- package/dist/parseContent.js +330 -0
- package/dist/render.d.ts +7 -0
- package/dist/render.js +90 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +48 -0
- package/package.json +66 -0
- package/src/downloadImage.ts +75 -0
- package/src/errors.ts +11 -0
- package/src/generateTempFile.ts +144 -0
- package/src/index.ts +152 -0
- package/src/makeCover.ts +52 -0
- package/src/parseContent.ts +350 -0
- package/src/render.ts +84 -0
- package/src/types.d.ts +79 -0
- package/src/utils.ts +39 -0
- package/templates/epub2/content.opf.ejs +57 -0
- package/templates/epub2/toc.xhtml.ejs +26 -0
- package/templates/epub3/content.opf.ejs +86 -0
- package/templates/epub3/toc.xhtml.ejs +38 -0
- package/templates/template.css +46 -0
- package/templates/toc.ncx.ejs +45 -0
- package/test/basic.test.js +28 -0
- package/test/child-page-image.test.js +21 -0
- package/test/data/1.json +21 -0
- package/test/tt.js +16 -0
- package/tsconfig.json +31 -0
- package/webpack.config.js +75 -0
package/src/render.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render
|
|
3
|
+
* @author: oldj
|
|
4
|
+
* @homepage: https://oldj.net
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as archiver from 'archiver'
|
|
8
|
+
import * as fs from 'fs-extra'
|
|
9
|
+
import * as path from 'path'
|
|
10
|
+
import { downloadAllImages } from './downloadImage'
|
|
11
|
+
import { generateTempFile } from './generateTempFile'
|
|
12
|
+
import makeCover from './makeCover'
|
|
13
|
+
import { IEpubData } from './types'
|
|
14
|
+
import { fileIsStable } from './utils'
|
|
15
|
+
|
|
16
|
+
export async function render(data: IEpubData): Promise<void> {
|
|
17
|
+
let { log } = data
|
|
18
|
+
|
|
19
|
+
log('Generating Template Files...')
|
|
20
|
+
await generateTempFile(data)
|
|
21
|
+
log('Downloading Images...')
|
|
22
|
+
await downloadAllImages(data)
|
|
23
|
+
log('Making Cover...')
|
|
24
|
+
await makeCover(data)
|
|
25
|
+
log('Generating Epub Files...')
|
|
26
|
+
await genEpub(data)
|
|
27
|
+
if (fs.existsSync(data.output)) {
|
|
28
|
+
log('Output: ' + data.output)
|
|
29
|
+
log('Done.')
|
|
30
|
+
} else {
|
|
31
|
+
log('Output fail!')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function genEpub(epubData: IEpubData): Promise<void> {
|
|
36
|
+
let { log, dir, output } = epubData
|
|
37
|
+
|
|
38
|
+
let archive = archiver('zip', { zlib: { level: 9 } })
|
|
39
|
+
let outputStream = fs.createWriteStream(epubData.output)
|
|
40
|
+
log('Zipping temp dir to ' + output)
|
|
41
|
+
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
archive.on('end', async () => {
|
|
44
|
+
log('Done zipping, clearing temp dir...')
|
|
45
|
+
|
|
46
|
+
// log(fs.statSync(epubData.output).size)
|
|
47
|
+
// setTimeout(() => log(fs.statSync(epubData.output).size), 1)
|
|
48
|
+
// setTimeout(() => log(fs.statSync(epubData.output).size), 100)
|
|
49
|
+
// setTimeout(() => log(fs.statSync(epubData.output).size), 1000)
|
|
50
|
+
let stable = await fileIsStable(epubData.output)
|
|
51
|
+
if (!stable) {
|
|
52
|
+
log('Output epub file is not stable!')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
fs.removeSync(dir)
|
|
57
|
+
resolve()
|
|
58
|
+
} catch (e) {
|
|
59
|
+
log('[Error] ' + e.message)
|
|
60
|
+
reject(e)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
archive.on('close', () => {
|
|
65
|
+
log('Zip close..')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
archive.on('finish', () => {
|
|
69
|
+
log('Zip finish..')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
archive.on('error', (err) => {
|
|
73
|
+
log('[Archive Error] ' + err.message)
|
|
74
|
+
reject(err)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
archive.pipe(outputStream)
|
|
78
|
+
archive.append('application/epub+zip', { store: true, name: 'mimetype' })
|
|
79
|
+
archive.directory(path.join(dir, 'META-INF'), 'META-INF')
|
|
80
|
+
archive.directory(path.join(dir, 'OEBPS'), 'OEBPS')
|
|
81
|
+
|
|
82
|
+
archive.finalize()
|
|
83
|
+
})
|
|
84
|
+
}
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* types.d.ts
|
|
3
|
+
* @author: oldj
|
|
4
|
+
* @homepage: https://oldj.net
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface IEpubImage {
|
|
8
|
+
id: string
|
|
9
|
+
url: string
|
|
10
|
+
dir: string
|
|
11
|
+
mediaType: string
|
|
12
|
+
extension: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IEpubGenOptions {
|
|
16
|
+
title: string
|
|
17
|
+
author?: string | string[]
|
|
18
|
+
publisher?: string
|
|
19
|
+
cover: string
|
|
20
|
+
output: string
|
|
21
|
+
version?: 2 | 3
|
|
22
|
+
css?: string
|
|
23
|
+
fonts?: string[]
|
|
24
|
+
lang?: 'en'
|
|
25
|
+
tocTitle?: string
|
|
26
|
+
appendChapterTitles?: boolean // For advanced customizations: absolute path to an OPF template.
|
|
27
|
+
customOpfTemplatePath?: string // For advanced customizations: absolute path to a NCX toc template.
|
|
28
|
+
customHtmlTocTemplatePath?: string // For advanced customizations: absolute path to a HTML toc template.
|
|
29
|
+
customNcxTocTemplatePath?: string
|
|
30
|
+
content: IChapter[] // Book Chapters content. It's should be an array of objects. eg. [{title: "Chapter 1",data: "<div>..."}, {data: ""},...]
|
|
31
|
+
verbose?: boolean // specify whether or not to console.log progress messages, default: false
|
|
32
|
+
description?: string
|
|
33
|
+
date?: string
|
|
34
|
+
tmpDir?: string
|
|
35
|
+
timeoutSeconds?: number
|
|
36
|
+
tocAutoNumber?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface IEpubData extends IEpubGenOptions {
|
|
40
|
+
id: string
|
|
41
|
+
docHeader: string
|
|
42
|
+
dir: string
|
|
43
|
+
images: IEpubImage[]
|
|
44
|
+
tmpDir: string
|
|
45
|
+
baseDir: string
|
|
46
|
+
_coverMediaType?: string
|
|
47
|
+
_coverExtension?: string
|
|
48
|
+
log: (msg) => void
|
|
49
|
+
content: IChapterData[]
|
|
50
|
+
timeoutSeconds: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IChapter {
|
|
54
|
+
id?: string
|
|
55
|
+
title?: string
|
|
56
|
+
author?: string | string[]
|
|
57
|
+
data: string // HTML String of the chapter content. image paths should be absolute path (should start with "http" or "https"), so that they could be downloaded. With the upgrade is possible to use local images (for this the path must start with file: //)
|
|
58
|
+
excludeFromToc?: boolean // default: false
|
|
59
|
+
beforeToc?: boolean // if is shown before Table of content, such like copyright pages. default: false
|
|
60
|
+
filename?: string // specify filename for each chapter
|
|
61
|
+
url?: string
|
|
62
|
+
children?: IChapter[]
|
|
63
|
+
appendChapterTitle?: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface IChapterData extends IChapter {
|
|
67
|
+
id: string
|
|
68
|
+
href?: string
|
|
69
|
+
dir?: string
|
|
70
|
+
children: IChapterData[]
|
|
71
|
+
filePath: string
|
|
72
|
+
author: string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface IOut {
|
|
76
|
+
success?: boolean
|
|
77
|
+
message?: string
|
|
78
|
+
options?: IEpubGenOptions
|
|
79
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils
|
|
3
|
+
* @author: oldj
|
|
4
|
+
* @homepage: https://oldj.net
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs'
|
|
8
|
+
import { promisify } from 'util'
|
|
9
|
+
|
|
10
|
+
export const readFile = promisify(fs.readFile)
|
|
11
|
+
export const writeFile = promisify(fs.writeFile)
|
|
12
|
+
|
|
13
|
+
export const USER_AGENT =
|
|
14
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36'
|
|
15
|
+
|
|
16
|
+
export const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
17
|
+
|
|
18
|
+
export async function fileIsStable(filename: string, max_wait: number = 30000): Promise<boolean> {
|
|
19
|
+
let start_time = new Date().getTime()
|
|
20
|
+
let last_size = fs.statSync(filename).size
|
|
21
|
+
|
|
22
|
+
while (new Date().getTime() - start_time <= max_wait) {
|
|
23
|
+
await wait(1000)
|
|
24
|
+
let size = fs.statSync(filename).size
|
|
25
|
+
if (size === last_size) return true
|
|
26
|
+
last_size = size
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function simpleMinifier(xhtml: string) {
|
|
33
|
+
xhtml = xhtml
|
|
34
|
+
.replace(/\s+<\/a>/gi, '</a>')
|
|
35
|
+
.replace(/\n\s+</g, '\n<')
|
|
36
|
+
.replace(/\n+/g, '\n')
|
|
37
|
+
|
|
38
|
+
return xhtml
|
|
39
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<package xmlns="http://www.idpf.org/2007/opf"
|
|
3
|
+
version="2.0"
|
|
4
|
+
unique-identifier="BookId">
|
|
5
|
+
|
|
6
|
+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
7
|
+
xmlns:opf="http://www.idpf.org/2007/opf">
|
|
8
|
+
|
|
9
|
+
<dc:identifier id="BookId" opf:scheme="URN"><%= id %></dc:identifier>
|
|
10
|
+
<dc:title><%= title %></dc:title>
|
|
11
|
+
<dc:description><%= description %></dc:description>
|
|
12
|
+
<dc:publisher><%= publisher || "anonymous" %></dc:publisher>
|
|
13
|
+
<dc:creator opf:role="aut" opf:file-as="<%= author.length ? author.join(",") : author %>"><%= author.length ? author.join(",") : author %></dc:creator>
|
|
14
|
+
<dc:date opf:event="modification"><%= date %></dc:date>
|
|
15
|
+
<dc:language><%= lang || "en" %></dc:language>
|
|
16
|
+
<meta name="cover" content="image_cover"/>
|
|
17
|
+
<meta name="generator" content="epub-gen"/>
|
|
18
|
+
|
|
19
|
+
</metadata>
|
|
20
|
+
|
|
21
|
+
<manifest>
|
|
22
|
+
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
|
23
|
+
<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml"/>
|
|
24
|
+
<item id="css" href="style.css" media-type="text/css"/>
|
|
25
|
+
|
|
26
|
+
<% if(locals.cover) { %>
|
|
27
|
+
<item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>"/>
|
|
28
|
+
<% } %>
|
|
29
|
+
|
|
30
|
+
<% images.forEach(function(image, index){ %>
|
|
31
|
+
<item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>"/>
|
|
32
|
+
<% }) %>
|
|
33
|
+
|
|
34
|
+
<% content.forEach(function(content, index){ %>
|
|
35
|
+
<item id="content_<%= index %>_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml"/>
|
|
36
|
+
<% }) %>
|
|
37
|
+
|
|
38
|
+
<% fonts.forEach(function(font, index) { %>
|
|
39
|
+
<item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf"/>
|
|
40
|
+
<% }) %>
|
|
41
|
+
</manifest>
|
|
42
|
+
|
|
43
|
+
<spine toc="ncx">
|
|
44
|
+
<% content.forEach(function(content, index){ %>
|
|
45
|
+
<% if(content.beforeToc && !content.excludeFromToc){ %>
|
|
46
|
+
<itemref idref="content_<%= index %>_<%= content.id %>"/>
|
|
47
|
+
<% } %>
|
|
48
|
+
<% }) %>
|
|
49
|
+
<itemref idref="toc"/>
|
|
50
|
+
<% content.forEach(function(content, index){ %>
|
|
51
|
+
<% if(!content.beforeToc && !content.excludeFromToc){ %>
|
|
52
|
+
<itemref idref="content_<%= index %>_<%= content.id %>"/>
|
|
53
|
+
<% } %>
|
|
54
|
+
<% }) %>
|
|
55
|
+
</spine>
|
|
56
|
+
<guide/>
|
|
57
|
+
</package>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
3
|
+
<html xml:lang="<%- lang %>" xmlns="http://www.w3.org/1999/xhtml">
|
|
4
|
+
<head>
|
|
5
|
+
<title><%= title %></title>
|
|
6
|
+
<meta charset="UTF-8"/>
|
|
7
|
+
<link rel="stylesheet" type="text/css" href="style.css"/>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1 class="h1"><%= tocTitle %></h1>
|
|
11
|
+
<% content.forEach(function(content, index){ %>
|
|
12
|
+
<% if(!content.excludeFromToc){ %>
|
|
13
|
+
<p class="table-of-content">
|
|
14
|
+
<a href="<%= content.href %>"><%= (1 + index) + ". " + (content.title || "Chapter " + (1 + index)) %>
|
|
15
|
+
<% if(content.author.length){ %>
|
|
16
|
+
- <small class="toc-author"><%= content.author.join(",") %></small>
|
|
17
|
+
<% } %>
|
|
18
|
+
<% if(content.url){ %><span class="toc-link"><%= content.url %></span>
|
|
19
|
+
<% }else{ %><span class="toc-link"></span>
|
|
20
|
+
<% } %>
|
|
21
|
+
</a>
|
|
22
|
+
</p>
|
|
23
|
+
<% } %>
|
|
24
|
+
<% }) %>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<package xmlns="http://www.idpf.org/2007/opf"
|
|
3
|
+
version="3.0"
|
|
4
|
+
unique-identifier="BookId"
|
|
5
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
6
|
+
xmlns:dcterms="http://purl.org/dc/terms/"
|
|
7
|
+
xml:lang="en"
|
|
8
|
+
xmlns:media="http://www.idpf.org/epub/vocab/overlays/#"
|
|
9
|
+
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
|
|
10
|
+
|
|
11
|
+
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
12
|
+
xmlns:opf="http://www.idpf.org/2007/opf">
|
|
13
|
+
|
|
14
|
+
<dc:identifier id="BookId"><%= id %></dc:identifier>
|
|
15
|
+
<meta refines="#BookId" property="identifier-type" scheme="onix:codelist5">
|
|
16
|
+
22</meta>
|
|
17
|
+
<meta property="dcterms:identifier" id="meta-identifier">
|
|
18
|
+
BookId</meta>
|
|
19
|
+
<dc:title><%= title %></dc:title>
|
|
20
|
+
<meta property="dcterms:title" id="meta-title"><%= title %></meta>
|
|
21
|
+
<dc:language><%= lang || "en" %></dc:language>
|
|
22
|
+
<meta property="dcterms:language" id="meta-language"><%= lang || "en" %></meta>
|
|
23
|
+
<meta property="dcterms:modified"><%= (new Date()).toISOString().split(".")[0] + "Z" %></meta>
|
|
24
|
+
<dc:creator id="creator"><%= author.length ? author.join(",") : author %></dc:creator>
|
|
25
|
+
<meta refines="#creator" property="file-as"><%= author.length ? author.join(",") : author %></meta>
|
|
26
|
+
<meta property="dcterms:publisher"><%= publisher || "anonymous" %></meta>
|
|
27
|
+
<dc:publisher><%= publisher || "anonymous" %></dc:publisher>
|
|
28
|
+
<% var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var stringDate = "" + year + "-" + month + "-" + day; %>
|
|
29
|
+
<meta property="dcterms:date"><%= stringDate %></meta>
|
|
30
|
+
<dc:date><%= stringDate %></dc:date>
|
|
31
|
+
<meta property="dcterms:rights">
|
|
32
|
+
All rights reserved</meta>
|
|
33
|
+
<dc:rights>Copyright © <%= (new Date()).getFullYear() %> by <%= publisher || "anonymous" %></dc:rights>
|
|
34
|
+
<meta name="cover" content="image_cover"/>
|
|
35
|
+
<meta name="generator" content="epub-gen"/>
|
|
36
|
+
<meta property="ibooks:specified-fonts">
|
|
37
|
+
true</meta>
|
|
38
|
+
</metadata>
|
|
39
|
+
|
|
40
|
+
<manifest>
|
|
41
|
+
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
|
|
42
|
+
<item id="toc" href="toc.xhtml" media-type="application/xhtml+xml" properties="nav"/>
|
|
43
|
+
<item id="css" href="style.css" media-type="text/css"/>
|
|
44
|
+
|
|
45
|
+
<% if(locals.cover) { %>
|
|
46
|
+
<item id="image_cover" href="cover.<%= _coverExtension %>" media-type="<%= _coverMediaType %>"/>
|
|
47
|
+
<% } %>
|
|
48
|
+
|
|
49
|
+
<% images.forEach(function(image, index){ %>
|
|
50
|
+
<item id="image_<%= index %>" href="images/<%= image.id %>.<%= image.extension %>" media-type="<%= image.mediaType %>"/>
|
|
51
|
+
<% }) %>
|
|
52
|
+
|
|
53
|
+
<% function renderContentItem(content) { %>
|
|
54
|
+
<% content.forEach(function(content){ %>
|
|
55
|
+
<item id="content_<%= content.id %>" href="<%= content.href %>" media-type="application/xhtml+xml"/>
|
|
56
|
+
<% if (Array.isArray(content.children)) { %>
|
|
57
|
+
<% renderContentItem(content.children) %>
|
|
58
|
+
<% } %>
|
|
59
|
+
<% }) %>
|
|
60
|
+
<% } %>
|
|
61
|
+
<% renderContentItem(content) %>
|
|
62
|
+
|
|
63
|
+
<% fonts.forEach(function(font, index){ %>
|
|
64
|
+
<item id="font_<%= index %>" href="fonts/<%= font %>" media-type="application/x-font-ttf"/>
|
|
65
|
+
<% }) %>
|
|
66
|
+
</manifest>
|
|
67
|
+
|
|
68
|
+
<spine toc="ncx">
|
|
69
|
+
<% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
|
|
70
|
+
<% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
|
|
71
|
+
<% function renderToc(nodes) { %>
|
|
72
|
+
<% nodes.forEach(function(content){ %>
|
|
73
|
+
<itemref idref="content_<%= content.id %>"/>
|
|
74
|
+
<% if (Array.isArray(content.children)) { %>
|
|
75
|
+
<% renderToc(content.children) %>
|
|
76
|
+
<% } %>
|
|
77
|
+
<% }) %>
|
|
78
|
+
<% } %>
|
|
79
|
+
<% renderToc(nodes_1) %>
|
|
80
|
+
<itemref idref="toc"/>
|
|
81
|
+
<% renderToc(nodes_2) %>
|
|
82
|
+
</spine>
|
|
83
|
+
<guide>
|
|
84
|
+
<reference type="text" title="Table of Content" href="toc.xhtml"/>
|
|
85
|
+
</guide>
|
|
86
|
+
</package>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="<%- lang %>"
|
|
4
|
+
lang="<%- lang %>">
|
|
5
|
+
<head>
|
|
6
|
+
<title><%= title %></title>
|
|
7
|
+
<meta charset="UTF-8" />
|
|
8
|
+
<link rel="stylesheet" type="text/css" href="style.css" />
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<h1 class="h1"><%= tocTitle %></h1>
|
|
12
|
+
<nav id="toc" class="TOC" epub:type="toc">
|
|
13
|
+
<% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>
|
|
14
|
+
<% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>
|
|
15
|
+
<% function renderToc(nodes, indent = 0) { %>
|
|
16
|
+
<ol>
|
|
17
|
+
<% nodes.forEach(function(content, index){ %>
|
|
18
|
+
<li class="table-of-content">
|
|
19
|
+
<a href="<%= content.href %>"><%= (content.title || "Chapter " + (1 + index)) %>
|
|
20
|
+
<% if(content.author.length){ %> - <small
|
|
21
|
+
class="toc-author"><%= content.author.join(",") %></small>
|
|
22
|
+
<% } %>
|
|
23
|
+
<% if(content.url){ %><span class="toc-link"><%= content.url %></span>
|
|
24
|
+
<% } %>
|
|
25
|
+
</a>
|
|
26
|
+
<% if (Array.isArray(content.children) && content.children.length > 0) { %>
|
|
27
|
+
<% renderToc(content.children, indent + 1) %>
|
|
28
|
+
<% } %>
|
|
29
|
+
</li>
|
|
30
|
+
<% }) %>
|
|
31
|
+
</ol>
|
|
32
|
+
<% } %>
|
|
33
|
+
<% renderToc(nodes_1) %>
|
|
34
|
+
<% renderToc(nodes_2) %>
|
|
35
|
+
</nav>
|
|
36
|
+
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
.epub-author {
|
|
2
|
+
color: #555;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.epub-link {
|
|
6
|
+
margin-bottom: 30px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.epub-link a {
|
|
10
|
+
color: #666;
|
|
11
|
+
font-size: 90%;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.toc-author {
|
|
15
|
+
font-size: 90%;
|
|
16
|
+
color: #555;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.toc-link {
|
|
20
|
+
color: #999;
|
|
21
|
+
font-size: 85%;
|
|
22
|
+
display: block;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
hr {
|
|
26
|
+
border: 0;
|
|
27
|
+
border-bottom: 1px solid #dedede;
|
|
28
|
+
margin: 60px 10%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.TOC > ol {
|
|
32
|
+
margin: 0;
|
|
33
|
+
padding: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.TOC > ol ol {
|
|
37
|
+
padding: 0;
|
|
38
|
+
margin-left: 2em;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.TOC li {
|
|
42
|
+
font-size: 16px;
|
|
43
|
+
list-style: none;
|
|
44
|
+
margin: 0 auto;
|
|
45
|
+
padding: 0;
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
|
|
3
|
+
<head>
|
|
4
|
+
<meta name="dtb:uid" content="<%= id %>"/>
|
|
5
|
+
<meta name="dtb:generator" content="epub-gen"/>
|
|
6
|
+
<meta name="dtb:depth" content="<%= (toc_depth || 1)%>"/>
|
|
7
|
+
<meta name="dtb:totalPageCount" content="0"/>
|
|
8
|
+
<meta name="dtb:maxPageNumber" content="0"/>
|
|
9
|
+
</head>
|
|
10
|
+
<docTitle>
|
|
11
|
+
<text><%= title %></text>
|
|
12
|
+
</docTitle>
|
|
13
|
+
<docAuthor>
|
|
14
|
+
<text><%= author %></text>
|
|
15
|
+
</docAuthor>
|
|
16
|
+
<navMap>
|
|
17
|
+
<% var _index = 1; %>
|
|
18
|
+
<% var nodes_1 = content.filter(c => !c.excludeFromToc && c.beforeToc) %>
|
|
19
|
+
<% var nodes_2 = content.filter(c => !c.excludeFromToc && !c.beforeToc) %>
|
|
20
|
+
<% function renderToc(nodes) { %>
|
|
21
|
+
<% nodes.forEach(function(content, index){ %>
|
|
22
|
+
<navPoint id="content_<%= content.id %>" playOrder="<%= _index++ %>" class="chapter">
|
|
23
|
+
<navLabel>
|
|
24
|
+
<text><%= (tocAutoNumber ? ((1 + index) + ". ") : "") + (content.title || "Chapter " + (1 + index)) %></text>
|
|
25
|
+
</navLabel>
|
|
26
|
+
<content src="<%= content.href %>"/>
|
|
27
|
+
<% if (Array.isArray(content.children)) { %>
|
|
28
|
+
<% renderToc(content.children) %>
|
|
29
|
+
<% } %>
|
|
30
|
+
</navPoint>
|
|
31
|
+
<% }) %>
|
|
32
|
+
<% } %>
|
|
33
|
+
|
|
34
|
+
<% renderToc(nodes_1) %>
|
|
35
|
+
|
|
36
|
+
<navPoint id="toc" playOrder="<%= _index++ %>" class="chapter">
|
|
37
|
+
<navLabel>
|
|
38
|
+
<text><%= tocTitle %></text>
|
|
39
|
+
</navLabel>
|
|
40
|
+
<content src="toc.xhtml"/>
|
|
41
|
+
</navPoint>
|
|
42
|
+
|
|
43
|
+
<% renderToc(nodes_2) %>
|
|
44
|
+
</navMap>
|
|
45
|
+
</ncx>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author: oldj
|
|
3
|
+
* @homepage: https://oldj.net
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from 'chai'
|
|
7
|
+
import { epubGen } from '../dist/index'
|
|
8
|
+
import { errors } from '../dist/errors'
|
|
9
|
+
import * as data from './data/1.json'
|
|
10
|
+
|
|
11
|
+
describe('Basic', () => {
|
|
12
|
+
it('no output path error', async () => {
|
|
13
|
+
let out = await epubGen({ ...data, verbose: false })
|
|
14
|
+
assert.equal(out.success, false)
|
|
15
|
+
assert.equal(out.message, errors.no_output_path)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('basic test', async () => {
|
|
19
|
+
let out = { options: {} }
|
|
20
|
+
try {
|
|
21
|
+
out = await epubGen({ ...data }, 'test.epub')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.error('Error 24: ' + e.message)
|
|
24
|
+
}
|
|
25
|
+
assert.equal(out.success, true)
|
|
26
|
+
assert.equal(typeof out.options.tmpDir, 'string')
|
|
27
|
+
}).timeout(60 * 1000)
|
|
28
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author: oldj
|
|
3
|
+
* @homepage: https://oldj.net
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from 'chai'
|
|
7
|
+
import { epubGen } from '../dist/index'
|
|
8
|
+
import * as data from './data/1.json'
|
|
9
|
+
|
|
10
|
+
describe('Child page image', () => {
|
|
11
|
+
it.only('should has image', async () => {
|
|
12
|
+
let out = { options: {} }
|
|
13
|
+
try {
|
|
14
|
+
out = await epubGen({ ...data }, 'cpi.epub')
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error('Error 24: ' + e.message)
|
|
17
|
+
}
|
|
18
|
+
assert.equal(out.success, true)
|
|
19
|
+
assert.equal(typeof out.options.tmpDir, 'string')
|
|
20
|
+
}).timeout(60 * 1000)
|
|
21
|
+
})
|
package/test/data/1.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "多层目录",
|
|
3
|
+
"author": "Andy",
|
|
4
|
+
"lang": "zh-cn",
|
|
5
|
+
"content": [
|
|
6
|
+
{
|
|
7
|
+
"title": "aaa",
|
|
8
|
+
"data": "<div>aa</div>"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"title": "bbb",
|
|
12
|
+
"data": "bb",
|
|
13
|
+
"children": [
|
|
14
|
+
{
|
|
15
|
+
"title": "cc1",
|
|
16
|
+
"data": "<div><img src=\"https://media.zine.la/booklet/81/a3/23/8b05c1faa4fb7ffe84727d99a2_1593030700_James-Turrell-2.jpg?imageMogr2/thumbnail/!400x520r/gravity/Center/crop/400x520\"/></div>"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
package/test/tt.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* t.js
|
|
3
|
+
* @author: oldj
|
|
4
|
+
* @homepage: https://oldj.net
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const {epubGen} = require('../dist/index')
|
|
8
|
+
const data = require('./data/1.json')
|
|
9
|
+
|
|
10
|
+
;(async () => {
|
|
11
|
+
try {
|
|
12
|
+
await epubGen({...data, tocAutoNumber: true}, 'test.epub')
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error('Error 24: ' + e.message)
|
|
15
|
+
}
|
|
16
|
+
})()
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es6",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"sourceMap": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"resolveJsonModule": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"useUnknownInCatchVariables": false,
|
|
14
|
+
"noImplicitThis": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"moduleResolution": "node",
|
|
17
|
+
"experimentalDecorators": true,
|
|
18
|
+
"plugins": [
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"include": [
|
|
22
|
+
"src"
|
|
23
|
+
],
|
|
24
|
+
"exclude": [
|
|
25
|
+
"node_modules",
|
|
26
|
+
"test",
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"externals": {
|
|
30
|
+
}
|
|
31
|
+
}
|