Make binary a webpack target, add static/standalone build target

This commit is contained in:
Daniel Jönsson 2018-03-14 15:26:36 +01:00
parent 12c935d698
commit 12177f2ec2
8 changed files with 265 additions and 113 deletions

1
.gitignore vendored
View file

@ -8,4 +8,5 @@ extension.zip
fimfic2epub.safariextension/
dist/
build/
bin/
*.epub

View file

@ -1,94 +1,3 @@
#!/usr/bin/env node
const args = require('commander')
.command('fimfic2epub <story> [filename]')
.description(require('../package.json').description)
.version(require('../package.json').version)
.option('-d, --dir <path>', 'Directory to store ebook in. Is prepended to filename')
.option('-t, --title <value>', 'Set the title of the story')
.option('-a, --author <value>', 'Set the author of the story')
.option('-c, --no-comments-link', 'Don\'t add link to online comments')
.option('-H, --no-headings', 'Don\'t add headings to chapters')
.option('-r, --no-reading-ease', 'Don\'t calculate Flesch reading ease')
.option('-e, --no-external', 'Don\'t embed external resources, such as images (breaks EPUB spec)')
.option('-n, --no-notes', 'Don\'t include author notes')
.option('-i, --notes-index', 'Create an index with all author notes at the end of the ebook')
.option('-p, --paragraphs <style>', 'Select a paragraph style <spaced|indented|indentedall|both>', 'spaced')
.option('-j, --join-subjects', 'Join dc:subjects to a single value')
.option('-C, --cover <url>', 'Set cover image url')
.parse(process.argv)
if (args.args.length < 1) {
console.error('Error: No story id/url provided')
process.exit(1)
}
const outputStdout = args.args[1] === '-' || args.args[1] === '/dev/stdout'
if (outputStdout) {
console.log = console.error
console.log('Outputting to stdout')
}
// use a mock DOM so we can run mithril on the server
require('mithril/test-utils/browserMock')(global)
const FimFic2Epub = require('../dist/fimfic2epub')
const fs = require('fs')
const path = require('path')
const STORY_ID = args.args[0]
const ffc = new FimFic2Epub(STORY_ID, {
addCommentsLink: !!args.commentsLink,
includeAuthorNotes: !!args.notes,
useAuthorNotesIndex: !!args.notesIndex,
addChapterHeadings: !!args.headings,
includeExternal: !!args.external,
paragraphStyle: args.paragraphs,
joinSubjects: !!args.joinSubjects,
calculateReadingEase: !!args.readingEase,
readingEaseWakeupInterval: 800
})
ffc.coverUrl = args.cover
ffc.fetchMetadata()
.then(() => {
if (args.title) {
ffc.setTitle(args.title)
}
if (args.author) {
ffc.setAuthorName(args.author)
}
})
.then(ffc.fetchAll.bind(ffc))
.then(ffc.build.bind(ffc))
.then(() => {
let filename = (args.args[1] || '').replace('%id%', ffc.storyInfo.id) || ffc.filename
let stream
if (args.dir) {
filename = path.join(args.dir, filename)
}
if (outputStdout) {
stream = process.stdout
} else {
stream = fs.createWriteStream(filename)
}
ffc.streamFile(null)
.pipe(stream)
.on('finish', () => {
if (!outputStdout) {
console.log('Saved story as ' + filename)
}
})
})
.catch((err) => {
if (err && err.stack) {
console.error(err.stack)
} else {
console.error('Error: ' + (err || 'Unknown error'))
}
process.exit(1)
})
// fimfic2epub 1.7.15
!function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},o.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o.w={},o(o.s=6)}([function(e,t){e.exports=require("../package.json")},function(e,t){e.exports=require("path")},function(e,t){e.exports=require("fs")},function(e,t){e.exports=require("../dist/fimfic2epub")},function(e,t){e.exports=require("mithril/test-utils/browserMock")},function(e,t){e.exports=require("commander")},function(e,t,o){"use strict";const n=o(5).command("fimfic2epub <story> [filename]").description(o(0).description).version(o(0).version).option("-d, --dir <path>","Directory to store ebook in. Is prepended to filename").option("-t, --title <value>","Set the title of the story").option("-a, --author <value>","Set the author of the story").option("-c, --no-comments-link","Don't add link to online comments").option("-H, --no-headings","Don't add headings to chapters").option("-r, --no-reading-ease","Don't calculate Flesch reading ease").option("-e, --no-external","Don't embed external resources, such as images (breaks EPUB spec)").option("-n, --no-notes","Don't include author notes").option("-i, --notes-index","Create an index with all author notes at the end of the ebook").option("-p, --paragraphs <style>","Select a paragraph style <spaced|indented|indentedall|both>","spaced").option("-j, --join-subjects","Join dc:subjects to a single value").option("-C, --cover <url>","Set cover image url").parse(process.argv);n.args.length<1&&(console.error("Error: No story id/url provided"),process.exit(1));const r="-"===n.args[1]||"/dev/stdout"===n.args[1];r&&(console.log=console.error,console.log("Outputting to stdout")),o(4)(global);const s=o(3),i=o(2),a=o(1),c=new s(n.args[0],{addCommentsLink:!!n.commentsLink,includeAuthorNotes:!!n.notes,useAuthorNotesIndex:!!n.notesIndex,addChapterHeadings:!!n.headings,includeExternal:!!n.external,paragraphStyle:n.paragraphs,joinSubjects:!!n.joinSubjects,calculateReadingEase:!!n.readingEase,readingEaseWakeupInterval:800});c.coverUrl=n.cover,c.fetchMetadata().then(()=>{n.title&&c.setTitle(n.title),n.author&&c.setAuthorName(n.author)}).then(c.fetchAll.bind(c)).then(c.build.bind(c)).then(()=>{let e,t=(n.args[1]||"").replace("%id%",c.storyInfo.id)||c.filename;n.dir&&(t=a.join(n.dir,t)),e=r?process.stdout:i.createWriteStream(t),c.streamFile(null).pipe(e).on("finish",()=>{r||console.log("Saved story as "+t)})}).catch(e=>{e&&e.stack?console.error(e.stack):console.error("Error: "+(e||"Unknown error")),process.exit(1)})}]);

View file

@ -10,6 +10,8 @@ import filter from 'gulp-filter'
import merge from 'merge-stream'
import change from 'gulp-change'
import rename from 'gulp-rename'
import banner from 'gulp-banner'
import chmod from 'gulp-chmod'
import jsonedit from 'gulp-json-editor'
import zip from 'gulp-zip'
@ -57,7 +59,11 @@ function webpackTask (callback) {
hash: false,
version: false,
chunks: false,
chunkModules: false
timings: false,
modules: false,
chunkModules: false,
cached: false,
maxModules: 0
}))
sequence('pack', callback)
})
@ -80,6 +86,7 @@ let lintPipe = lazypipe()
// Cleanup task
gulp.task('clean', () => del([
'bin/',
'build/',
'extension/build/',
'dist/',
@ -97,6 +104,13 @@ gulp.task('version', (done) => {
// Main tasks
gulp.task('webpack', ['version', 'fontawesome'], webpackTask)
gulp.task('binaries', ['version'], () => {
return gulp.src(['build/fimfic2epub.js', 'build/fimfic2epub-static.js'])
.pipe(rename({ extname: '' }))
.pipe(banner('#!/usr/bin/env node\n// fimfic2epub ' + packageVersion + '\n'))
.pipe(chmod(0o777))
.pipe(gulp.dest('bin/'))
})
gulp.task('watch:webpack', () => {
return watch(['src/**/*.js', 'src/**/*.styl', './package.json'], watchOpts, () => {
return sequence('webpack')
@ -104,10 +118,10 @@ gulp.task('watch:webpack', () => {
})
gulp.task('lint', () => {
return gulp.src(['gulpfile.babel.js', 'webpack.config.babel.js', 'src/**/*.js', 'bin/fimfic2epub']).pipe(lintPipe())
return gulp.src(['gulpfile.babel.js', 'webpack.config.babel.js', 'src/**/*.js']).pipe(lintPipe())
})
gulp.task('watch:lint', () => {
return watch(['src/**/*.js', 'gulpfile.babel.js', 'webpack.config.babel.js', 'bin/fimfic2epub'], watchOpts, (file) => {
return watch(['src/**/*.js', 'gulpfile.babel.js', 'webpack.config.babel.js'], watchOpts, (file) => {
return gulp.src(file.path).pipe(lintPipe())
})
})
@ -135,7 +149,7 @@ gulp.task('fontawesome', () => {
.pipe(gulp.dest('build/'))
return merge(copy, codes)
})
gulp.task('pack', (done) => {
gulp.task('pack', ['binaries'], (done) => {
sequence(['pack:firefox', 'pack:chrome'], done)
})
gulp.task('watch:pack', () => {

View file

@ -1,6 +1,6 @@
{
"name": "fimfic2epub",
"version": "1.7.14",
"version": "1.7.15",
"description": "Tool to generate improved EPUB ebooks from Fimfiction stories",
"author": "djazz",
"license": "MIT",
@ -18,7 +18,7 @@
"main": "dist/fimfic2epub.js",
"files": [
"dist/",
"bin/",
"bin/fimfic2epub",
"LICENSE"
],
"dependencies": {
@ -52,12 +52,15 @@
"babel-preset-es2015": "^6.14.0",
"babel-preset-node6": "^11.0.0",
"babel-register": "^6.26.0",
"binary-loader": "0.0.1",
"del": "^3.0.0",
"es6-event-emitter": "^1.10.2",
"exports-loader": "^0.7.0",
"file-saver": "^1.3.2",
"gulp": "^3.9.1",
"gulp-banner": "^0.1.3",
"gulp-change": "^1.0.0",
"gulp-chmod": "^2.0.0",
"gulp-filter": "^5.0.1",
"gulp-json-editor": "^2.2.1",
"gulp-rename": "^1.2.2",

View file

@ -1,6 +1,5 @@
/* global chrome */
import path from 'path'
import JSZip from 'jszip'
import escapeStringRegexp from 'escape-string-regexp'
import zeroFill from 'zero-fill'
@ -613,12 +612,7 @@ class FimFic2Epub extends Emitter {
if (!isNode) {
fontPath = chrome.extension.getURL('build/fonts/fontawesome-webfont.ttf')
} else {
// TODO: Fix better font detection
const fs = require('fs')
fontPath = path.join(__dirname, '../node_modules/', 'font-awesome/fonts/fontawesome-webfont.ttf')
if (!fs.existsSync(fontPath)) {
fontPath = path.join(__dirname, '../../', 'font-awesome/fonts/fontawesome-webfont.ttf')
}
fontPath = require('font-awesome/fonts/fontawesome-webfont.ttf') // resolve the path, see webpack config
}
this.iconsFont = await subsetFont(fontPath, glyphs, {local: isNode})
}

93
src/cli.js Executable file
View file

@ -0,0 +1,93 @@
const args = require('commander')
.command('fimfic2epub <story> [filename]')
.description(require('../package.json').description)
.version(require('../package.json').version)
.option('-d, --dir <path>', 'Directory to store ebook in. Is prepended to filename')
.option('-t, --title <value>', 'Set the title of the story')
.option('-a, --author <value>', 'Set the author of the story')
.option('-c, --no-comments-link', 'Don\'t add link to online comments')
.option('-H, --no-headings', 'Don\'t add headings to chapters')
.option('-r, --no-reading-ease', 'Don\'t calculate Flesch reading ease')
.option('-e, --no-external', 'Don\'t embed external resources, such as images (breaks EPUB spec)')
.option('-n, --no-notes', 'Don\'t include author notes')
.option('-i, --notes-index', 'Create an index with all author notes at the end of the ebook')
.option('-p, --paragraphs <style>', 'Select a paragraph style <spaced|indented|indentedall|both>', 'spaced')
.option('-j, --join-subjects', 'Join dc:subjects to a single value')
.option('-C, --cover <url>', 'Set cover image url')
.parse(process.argv)
if (args.args.length < 1) {
console.error('Error: No story id/url provided')
process.exit(1)
}
const outputStdout = args.args[1] === '-' || args.args[1] === '/dev/stdout'
if (outputStdout) {
console.log = console.error
console.log('Outputting to stdout')
}
// use a mock DOM so we can run mithril on the server
require('mithril/test-utils/browserMock')(global)
const FimFic2Epub = require('./FimFic2Epub')
const fs = require('fs')
const path = require('path')
const STORY_ID = args.args[0]
const ffc = new FimFic2Epub(STORY_ID, {
addCommentsLink: !!args.commentsLink,
includeAuthorNotes: !!args.notes,
useAuthorNotesIndex: !!args.notesIndex,
addChapterHeadings: !!args.headings,
includeExternal: !!args.external,
paragraphStyle: args.paragraphs,
joinSubjects: !!args.joinSubjects,
calculateReadingEase: !!args.readingEase,
readingEaseWakeupInterval: 800
})
ffc.coverUrl = args.cover
ffc.fetchMetadata()
.then(() => {
if (args.title) {
ffc.setTitle(args.title)
}
if (args.author) {
ffc.setAuthorName(args.author)
}
})
.then(ffc.fetchAll.bind(ffc))
.then(ffc.build.bind(ffc))
.then(() => {
let filename = (args.args[1] || '').replace('%id%', ffc.storyInfo.id) || ffc.filename
let stream
if (args.dir) {
filename = path.join(args.dir, filename)
}
if (outputStdout) {
stream = process.stdout
} else {
stream = fs.createWriteStream(filename)
}
ffc.streamFile(null)
.pipe(stream)
.on('finish', () => {
if (!outputStdout) {
console.log('Saved story as ' + filename)
}
})
})
.catch((err) => {
if (err && err.stack) {
console.error(err.stack)
} else {
console.error('Error: ' + (err || 'Unknown error'))
}
process.exit(1)
})

View file

@ -3,18 +3,25 @@ import isNode from 'detect-node'
import { Font } from 'fonteditor-core'
import fs from 'fs'
import fetch from './fetch'
import fileType from 'file-type'
async function subsetFont (fontPath, glyphs, options = {}) {
let data
if (!isNode || !options.local) {
data = await fetch(fontPath, 'arraybuffer')
} else {
data = await new Promise((resolve, reject) => {
fs.readFile(fontPath, (err, data) => {
if (err) reject(err)
else resolve(data)
let fontdata = Buffer.from(fontPath, 'binary')
let type = fileType(fontdata)
if (type && type.mime === 'font/ttf') {
data = fontdata
} else {
data = await new Promise((resolve, reject) => {
fs.readFile(fontPath, (err, data) => {
if (err) reject(err)
else resolve(data)
})
})
})
}
}
return Font.create(data, {
type: 'ttf',

View file

@ -1,4 +1,5 @@
import webpack from 'webpack'
import path from 'path'
import nodeExternals from 'webpack-node-externals'
@ -49,6 +50,7 @@ const bundleExtensionConfig = {
plugins: [
// new (require('webpack-bundle-analyzer').BundleAnalyzerPlugin)()
new webpack.NormalModuleReplacementPlugin(/font-awesome\/fonts\/fontawesome-webfont\.ttf/, './false')
],
performance: {
hints: false
@ -106,7 +108,9 @@ const bundleNpmModuleConfig = {
__dirname: false
},
externals: [nodeExternals({whitelist: ['es6-event-emitter', /^babel-runtime/]})],
externals: [nodeExternals({whitelist: ['es6-event-emitter', /^babel-runtime/]}), {
'font-awesome/fonts/fontawesome-webfont.ttf': 'require.resolve(\'font-awesome/fonts/fontawesome-webfont.ttf\')'
}],
plugins: [],
performance: {
@ -120,4 +124,131 @@ const bundleNpmModuleConfig = {
mode: inProduction ? 'production' : 'development'
}
export default [bundleExtensionConfig, bundleNpmModuleConfig]
const bundleNpmBinaryConfig = {
entry: './src/cli',
output: {
path: path.join(__dirname, '/'),
filename: './build/fimfic2epub.js'
},
target: 'node',
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
sourceMaps: !inProduction,
presets: [['env', {
targets: {
node: '8.0.0'
}
}]]
}
},
{
test: /\.styl$/,
use: ['raw-loader', 'stylus-loader']
}
]
},
resolve: {
extensions: ['.js', '.json', '.styl'],
modules: [
path.resolve('./src'),
'node_modules'
]
},
node: {
__dirname: false
},
externals: [nodeExternals(), {
'./FimFic2Epub': 'require(\'../dist/fimfic2epub\')',
'../package.json': 'require(\'../package.json\')',
'font-awesome/fonts/fontawesome-webfont.ttf': 'require.resolve(\'font-awesome/fonts/fontawesome-webfont.ttf\')'
}],
plugins: [],
performance: {
hints: false
},
optimization: {
concatenateModules: inProduction,
minimize: inProduction
},
devtool: false,
mode: inProduction ? 'production' : 'development'
}
const bundleStaticNpmModuleConfig = {
entry: './src/cli',
output: {
path: path.join(__dirname, '/'),
filename: './build/fimfic2epub-static.js'
},
target: 'node',
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
sourceMaps: !inProduction,
presets: [['env', {
targets: {
node: '9.0.0'
}
}]]
}
},
{
test: /\.styl$/,
use: ['raw-loader', 'stylus-loader']
},
{
test: /\.ttf$/,
use: 'binary-loader'
}
]
},
resolve: {
extensions: ['.js', '.json', '.styl'],
modules: [
path.resolve('./bin'),
'node_modules'
]
},
node: {
__dirname: false
},
plugins: [],
performance: {
hints: false
},
optimization: {
concatenateModules: inProduction,
minimize: inProduction
},
devtool: false,
mode: inProduction ? 'production' : 'development'
}
export default [
bundleExtensionConfig,
bundleNpmModuleConfig,
bundleNpmBinaryConfig,
bundleStaticNpmModuleConfig
]