Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: epubcheck MED-004 PKG-021 #2

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
dist
lib/templates
*.epub
.idea
.npmrc

# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node
Expand Down
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ On the server (with Node.js), image paths can also start with `file://`, in whic
Install the lib and add it as a dependency (recommended), run in your project directory:

```shell
npm install epub-gen-memory --save
npm install @mnhlt/epub-gen-memory --save
```

Then put this in your code:

```js
import epub from 'epub-gen-memory';
import epub from '@mnhlt/epub-gen-memory';

epub(options).then(
content => console.log("Ebook Generated Successfully!"),
Expand All @@ -34,30 +34,50 @@ epub(options).then(

See [JSZip documentation](https://github.com/Stuk/jszip/blob/master/documentation/howto/write_zip.md) on how to get the zip to the user. For a nodejs example please see the tests.

In environments where `SharedArrayBuffer` is not available, you might want to instead import from `epub-gen-memory/sabstub`, which includes a non-functional stub:
In environments where `SharedArrayBuffer` is not available, you might want to instead import from `@mnhlt/epub-gen-memory/sabstub`, which includes a non-functional stub:

```js
import epub from 'epub-gen-memory/sabstub';
import epub from '@mnhlt/epub-gen-memory/sabstub';
```

The package also includes a [browserify](https://www.npmjs.com/package/browserify)d bundle (UMD) as `epub-gen-memory/bundle`. It is possible to use the bundle if you want to build for the browser. The bundle is also available from a CDN: [UNPKG](https://unpkg.com/epub-gen-memory) ([latest](https://unpkg.com/epub-gen-memory), [latest 1.x](https://unpkg.com/epub-gen-memory@^1.0.0)). The bundle also includes the proper return type for the browser (`Promise<Blob>` instead of `Promise<Buffer>`).
The package also includes a [browserify](https://www.npmjs.com/package/browserify)d bundle (UMD) as @mnhlt/epub-gen-memory/bundle`. It is possible to use the bundle if you want to build for the browser. The bundle is also available from a CDN: [UNPKG](https://unpkg.com/epub-gen-memory) ([latest](https://unpkg.com/epub-gen-memory), [latest 1.x](https://unpkg.com/epub-gen-memory@^1.0.0)). The bundle also includes the proper return type for the browser (`Promise<Blob>` instead of `Promise<Buffer>`).

```js
import epub from 'epub-gen-memory/bundle';
import epub from '@mnhlt/epub-gen-memory/bundle';
```

**Note**: This library was written in TypeScript and thus uses ESM exports, but it was compiled to CommonJS, so you can also use the following:

```js
const epub = require('epub-gen-memory').default;
const epub = require('@mnhlt/epub-gen-memory').default;
```

### Write epub with file streaming mode.
**Note**: This mode is only available in Node.js.

In case you want to write the large epub to a file, you can use the streaming mode to avoid memory issues.

```js
import epub from '@mnhlt/epub-gen-memory';

epub(options, content, { stream: true }).then(
content => console.log("Ebook Generated Successfully!"),
err => console.error("Failed to generate Ebook because of ", err)
);

or
import {EpubStream} from '@mnhlt/epub-gen-memory';

const epubStream = new EpubStream(options, content);
await epubStream.genEpub();
```


## API

```ts
import epub, { EPub, optionsDefaults, chapterDefaults } from 'epub-gen-memory';
import type { Options, Content, Chapter, Font } from 'epub-gen-memory';
import epub, { EPub, optionsDefaults, chapterDefaults } from '@mnhlt/epub-gen-memory';
import type { Options, Content, Chapter, Font } from '@mnhlt/epub-gen-memory';
```


Expand Down Expand Up @@ -137,6 +157,10 @@ protected:
Instead of throwing, emit a warning and write an empty file if a font or image fails to download
- `verbose`: `boolean | ((type, ...args) => void)` (optional, default `false`)
Whether to log progress messages; If a function is provided, the first argument will either be `'log'` or `'warn'`
- `tempDir`: `string` (optional, default `uuid()`)
Directory to store temporary files in
- `output`: `string` (optional)
file path to write the epub to


### Chapters
Expand Down
83 changes: 83 additions & 0 deletions lib/AEpub.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { render as renderTemplate } from 'ejs';
import mime from 'mime/lite';
import { Chapter, Content, Font, Image, isString, NormChapter, NormOptions, Options, type, uuid, validateAndNormalizeChapters, validateAndNormalizeOptions } from './util';

export abstract class AEpub {
protected options: NormOptions;
protected content: NormChapter[];
protected uuid: string;
protected images: Image[] = [];
protected cover?: { extension: string, mediaType: string; };

protected log: typeof console.log;
protected warn: typeof console.warn;

constructor(options: Options, content: Content) {
this.options = validateAndNormalizeOptions(options);
switch (this.options.verbose) {
case true:
this.log = console.log.bind(console);
this.warn = console.warn.bind(console);
break;
case false:
this.log = this.warn = () => { };
break;
default:
this.log = this.options.verbose.bind(null, 'log');
this.warn = this.options.verbose.bind(null, 'warn');
break;
}
this.uuid = uuid();
this.content = validateAndNormalizeChapters.call(this, content);

if (this.options.cover) {
const fname = isString(this.options.cover) ? this.options.cover : this.options.cover.name;
const mediaType = mime.getType(fname);
const extension = mime.getExtension(mediaType || '');
if (mediaType && extension)
this.cover = { mediaType, extension };
else this.warn('Could not detect cover image type from file', fname);
}
}

async render() {
this.log('Generating Template Files...');
await this.generateTemplateFiles();
this.log('Downloading fonts...');
await this.downloadAllFonts();
this.log('Downloading images...');
await this.downloadAllImages();
this.log('Making cover...');
await this.makeCover();
this.log('Finishing up...');
return this;
}

async genEpub() {
try {
await this.render();
const content = await this.generateFinal();
this.log('Done');
return content;
} finally {
await this.cleanup();
}
}

protected abstract generateTemplateFiles(): Promise<void>;
protected abstract downloadAllFonts(): Promise<void>;
protected abstract downloadAllImages(): Promise<void>;
protected abstract makeCover(): Promise<void>;
protected abstract generateFinal(): Promise<any>;
protected abstract cleanup(): Promise<void>;

protected getTemplateOptions() {
return {
...this.options,
id: this.uuid,
images: this.images,
cover: this.cover,
content: this.content,
};
}
}
33 changes: 33 additions & 0 deletions lib/EpubStream-browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { AEpub } from './AEpub';
import { Options, Content } from './util';

export class EpubStream extends AEpub {
constructor(options: Options, content: Content) {
super(options, content);
throw new Error('EpubStream is not supported in browser environments');
}

protected generateTemplateFiles(): Promise<void> {
throw new Error('Method not implemented.');
}

protected downloadAllFonts(): Promise<void> {
throw new Error('Method not implemented.');
}

protected downloadAllImages(): Promise<void> {
throw new Error('Method not implemented.');
}

protected makeCover(): Promise<void> {
throw new Error('Method not implemented.');
}

protected generateFinal(): Promise<any> {
throw new Error('Method not implemented.');
}

protected cleanup(): Promise<void> {
throw new Error('Method not implemented.');
}
}
Loading