Skip to content

Commit

Permalink
Merge pull request #18 from revdavethompson/5-test-library
Browse files Browse the repository at this point in the history
v0.3.1 create-bp-project package, check for local
  • Loading branch information
revdavethompson authored May 17, 2023
2 parents 1e533dc + 0196383 commit 9c89457
Show file tree
Hide file tree
Showing 90 changed files with 2,545 additions and 145 deletions.
Binary file modified .DS_Store
Binary file not shown.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.3.1] - 2023-5-16

### Added

* BookPub will check for a local installation in node-modules and use that, otherwise it will look for a global install
* This will permit users to keep the correct version of BookPub that corresponds to their manuscript (should there be changes in the future)
* Created separate NPM Package **create-bookpub-project**
* Stored in same repository, under `packages/create-bookpub-project/`
* Users will now run `npx create-bookpub-project`

## [0.3.0] - 2023-5-14

### Added
Expand Down
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# BookPub - book publishing, web tools
# BookPub - book publishing with web-driven tools

<!--- Logo picture element for user's light/dark modes --->
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./assets/bookpub-logo_white.png">
<img style="text-align: center" alt="BookPub logo header" src="./assets/bookpub-logo.svg">
</picture>
<p align="center">
<img src="assets/bookpub-logo.svg" width="70%" height="70%" alt="BookPub logo header"/>
</p>

**BookPub** is an advanced book publishing framework for a web-driven world.
**BookPub** is an advanced book publishing framework based on Markdown, HTML, CSS and Javascript.

BookPub manages a manuscript-to-market toolchain, allowing publishing firms, authors and other stakeholders to manage one markdown-based manuscript source, which can be professionally designed and typeset (even for print) using HTML, CSS and Javascript Web Standards. Bookpub can then build your source into any format (PDF, EPUB, MOBI, HTML).
BookPub manages a manuscript-to-market toolchain, allowing publishing firms, authors and other stakeholders to manage one markdown-based manuscript source, which can be professionally designed and typeset (even for print) using HTML, CSS and Javascript Web Standards. Bookpub will build your manuscript into any format (PDF, EPUB, MOBI, HTML).

## Features

Expand Down Expand Up @@ -48,13 +46,13 @@ BookPub manages a manuscript-to-market toolchain, allowing publishing firms, aut
* [NodeJS](https://nodejs.org/) - You will need to have a working install of Node.js (which will include NPM) in order to use BookPub. There are two options:

* [NodeJS Installer](https://nodejs.org/en/download)

If you don't plan to use NodeJS outside of using BookPub, we recommend using the NodeJS Installer [by visiting their download page](https://nodejs.org/en/download) and selecting the installer for your operating system.

* [NVM (Node Version Manager)](https://github.com/nvm-sh/nvm)

If you plan on using NodeJS in other contexts, we recommend using NVM (NodeJS Version Manager). It is far more robust and flexible. You can visit [the NVM Github page](https://github.com/nvm-sh/nvm) for detailed instructions. But generally it can be installed using the following command:

`curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash`

### Installing **BookPub**
Expand All @@ -79,7 +77,7 @@ BookPub manages a manuscript-to-market toolchain, allowing publishing firms, aut

`bookpub new my-book` (where `my-book` is the name of your project)

This will walk you through the creation of your new book project. You will be asked a series of questions and will generate our default book example, using your answers.
This will walk you through the creation of your new book project. You will be asked a series of questions and will generate our default book example, using your answers.

The project will have the following structure:

Expand Down Expand Up @@ -132,13 +130,13 @@ settings:
In your `index.md.ejs` you can access the book title using EJS syntax:

```ejs
The title of my book is "<%= meta.title %>".
The title of my book is <%= meta.title %>.
```

Your build file will then be renderred as:

```
The title of my book is "My Book Title".
The title of my book is My Book Title.
```

### 3. Working With Your Manuscript
Expand Down
File renamed without changes.
Binary file modified assets/bookpub-logo.pdf
Binary file not shown.
Binary file added assets/bookpub-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions assets/bookpub-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/bookpub-logo_white.gif
Binary file not shown.
Binary file removed assets/bookpub-logo_white.pdf
Binary file not shown.
Binary file removed assets/bookpub-logo_white.png
Binary file not shown.
55 changes: 39 additions & 16 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,23 @@ import createNewBookProject from './lib/create-new-book.js'
import yaml from 'js-yaml';
import lintEjs from './lib/lint-ejs.js'

// Grab all the arguments after "node bookpub.js"
const args = process.argv.slice(2).join(' ');


// Define a variable to store the path of bookpub to use
let bookpubPath;

// Check for local bookpub before running other cli.js functions
checkLocalBookpub();

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = process.env.TEST_PACKAGE_JSON || JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8'));

const manuscriptDir = process.env.TEST_MANUSCRIPT || path.join(process.cwd(), 'manuscript');
const manRel = path.relative(process.cwd(), manuscriptDir);

// Load the book.config.yml into the book object
const book = await loadBookConfig();

const program = new Command();
program.version(packageJson.version);

Expand All @@ -32,19 +39,21 @@ program
.option('-t, --type <type>', 'Specify the output type (html or pdf)', 'html')
.description('Build the output from the manuscript markdown files')
.action(async (options) => {
console.log(`\nBuilding ${options.type.toUpperCase()} Format...\n`);
// Load the book.config.yml into the book object
const book = await loadBookConfig();
console.log(chalk.cyanBright(`\nBuilding ${options.type.toUpperCase()} Format...\n`));
if (options.type === 'html') {
await buildHtml(book, manuscriptDir, path.join(process.cwd(), 'build', 'html'), options.type);
} else if (options.type === 'pdf') {
await buildPdf(book, manuscriptDir, path.join(process.cwd(), 'build', 'pdf'), options.type);
} else {
console.error('Invalid output type specified. Use either "html" or "pdf".');
}
console.log(chalk.greenBright(`
---------------------------
Yay! Your ${chalk.greenBright(options.type.toUpperCase())} Book Was Built!!
---------------------------
`));
console.log(chalk.white(`
---------------------------------------
${chalk.greenBright(`Yay! Your ${chalk.yellowBright(options.type.toUpperCase())} Book Was Built!!`)}
---------------------------------------
\n`));
});

program
Expand Down Expand Up @@ -97,6 +106,22 @@ program
}
});

// If local bookpub available, use it. Otherwise, use global
function checkLocalBookpub() {
// Check if there's a local version of bookpub
var localBookpubPath = path.join(process.cwd(), 'node_modules', 'bookpub', 'cli.js');
if (fs.existsSync(localBookpubPath)) {
// If there's a local version, use it
console.log('(Using local BookPub)');
bookpubPath = `node ${localBookpubPath}`;
} else {
// Otherwise, use the global version
console.log('Using global bookpub');
bookpubPath = `bookpub`;
}
}


async function loadBookConfig() {
try {
const book = yaml.load(fs.readFileSync(path.join(process.cwd(), 'book.config.yml'), 'utf-8'));
Expand All @@ -106,11 +131,9 @@ async function loadBookConfig() {
}
}



async function buildHtml(book, manuscriptDir, outputDir, outputType) {
async function buildHtml(book, manuscriptDir, outputType, outputDir) {
const outRel = path.relative(process.cwd(), outputDir);
console.log(` Manuscript Location: ${chalk.yellowBright(`/${manRel}/`)}\n Build Output Location: ${chalk.yellowBright(`/${outRel}/`)}\n\n`)
console.log(` Manuscript Location: ${chalk.yellowBright(`/${manRel}/`)}\n Build Output Location: ${chalk.yellowBright(`/${outRel}/`)}\n\n`)
try {
await convert(book, manuscriptDir, outputDir, outputType);
} catch (error) {
Expand All @@ -132,7 +155,7 @@ async function buildPdf(book, manuscriptDir, outputDir, outputType) {
try {
const pdfOutDirRel = path.relative(process.cwd(), outputDir);
// Run the prince command-line tool
console.log(` 10. Building your Print PDF to ${chalk.yellowBright(`/${pdfOutDirRel}/index.pdf`)}\n `);
console.log(` * Building your Print PDF to ${chalk.yellowBright(`/${pdfOutDirRel}/index.pdf`)}\n `);
const prince = spawn('prince', ['build/pdf/index.html'], { stdio: 'inherit' });
prince.on('error', (error) => {
console.error(`Error: ${error.message}`);
Expand Down Expand Up @@ -178,7 +201,7 @@ async function runWebpackDevServerAsync(outputType) {
}

async function runNodemon(outputType) {
const env = { outputType };
const env = { outputType, bookpubPath };
const userNodemonConfig = await getNodemonConfig(env);
let nodemonConfig = {};

Expand All @@ -189,7 +212,7 @@ async function runNodemon(outputType) {
nodemonConfig = {
script: __filename,
ext: outputType === 'pdf' ? 'md,mdx,js,ejs,json,html,css,scss,yaml' : 'md,mdx,js,ejs,json,html,css,scss,yaml',
exec: `bookpub build --type ${outputType}`,
exec: `${bookpubPath} build --type ${outputType}`,
watch: 'manuscript',
};
}
Expand Down
Binary file not shown.
47 changes: 16 additions & 31 deletions lib/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
// Conversion Constants
// --------------------


// Book Manuscript Entry File/Path
const entryFile = book.settings.entryfile;
const entryPath = path.join(manuscriptDir, entryFile);
Expand All @@ -28,14 +29,16 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
fs.mkdirSync(outputDir, { recursive: true });
}

console.log(' 2. Loading your manuscript...\n');

console.log(' * Loading your manuscript\n');
const manuscript = fs.readFileSync(entryPath, 'utf8');


// Render EJS Content
// --------------------
let processedEJS;
try {
console.log(' 4. Processing your EJS files...\n')
console.log(' * Processing your EJS files\n')
processedEJS = await renderEjs(manuscript, book, outputType, manuscriptDir);
if (!processedEJS) {
console.warn(chalk.yellow(' Warning: Your \'processedEJS\' variable is empty or undefined.\n This means something isn\'t working in the EJS conversion process.\n'));
Expand All @@ -44,16 +47,15 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
console.error(chalk.redBright('Dang. We couldn\'t render your EJS:'), chalk.redBright(error));
};


// Convert Markdown to HTML
// --------------------
let parsedMarkdown;
try {
console.log(" 5. Converting your manuscript Markdown into HTML...\n");
console.log(" * Converting your manuscript Markdown into HTML\n");
parsedMarkdown = unified()
// convert markdown to a MD-AST syntax tree
.use(remarkParse)
// remove indentation before processing
.use(removeIndentation)
// convert md-ast to gfm-ast (Github Flavored Markdown) and pass options
.use(remarkGfm, book['gfm-options'])
// convert gfm-ast to html-ast
Expand All @@ -69,11 +71,12 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
console.error(chalk.redBright('Markdown Processing Error:'), chalk.redBright(error));
}


// Use Smartypants to process quotes and symbols
// --------------------
let parsedSmarty;
try {
console.log(" 6. Converting quotes and symbols...\n");
console.log(" * Converting quotes and symbols\n");
parsedSmarty = await parseSmarty(parsedMarkdown);
if (!parsedSmarty) {
console.warn(chalk.yellow(' Warning: Your \'parsedSmarty\' variable is empty or undefined.\n This means something isn\'t working in the conversion of your quotes and symbols.\n'));
Expand All @@ -83,18 +86,20 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
}



// Write Final Build Output
// --------------------
const relOutFile = path.relative(process.cwd(), outputFile);
console.log(` 7. Writing final build to ${chalk.yellowBright(`/${relOutFile}`)}\n`)
console.log(` * Writing final build to ${chalk.yellowBright(`/${relOutFile}`)}\n`)
fs.writeFileSync(outputFile, parsedSmarty);


// Copy Manuscript Theme to Build Location
// --------------------
const themeSrcDir = path.join(manuscriptDir, 'theme');
const themeDestDir = path.join(outputDir, 'theme');
const relTheme = path.relative(process.cwd(), themeDestDir);
console.log(` 8. Copying Manuscript Theme to ${chalk.yellowBright(`/${relTheme}/`)}\n`)
console.log(` * Copying Manuscript Theme to ${chalk.yellowBright(`/${relTheme}/`)}\n`)

await fse.copy(themeSrcDir, themeDestDir, {
filter: (src) => {
Expand All @@ -108,39 +113,19 @@ export default async function convert(book, manuscriptDir, outputType, outputDir
return !relativePath.startsWith('css');
},
});

// Compile SCSS to CSS
// --------------------
const scssInputFilename = isTest ? `styles.${outputType}.mock.scss` : `styles.${outputType}.css`;
const scssInputFilename = isTest ? `styles.${outputType}.mock.scss` : `styles.${outputType}.scss`;
const scssInput = path.join(manuscriptDir, 'theme', 'css', scssInputFilename);
const cssOutputFilename = isTest ? `styles.${outputType}.mock.css` : `styles.${outputType}.css`;
const cssOutput = path.join(outputDir, 'theme', 'css', cssOutputFilename);

const relCssOut = path.relative(process.cwd(), cssOutput);
console.log(` 9. Compiling SASS and Copying to ${chalk.yellowBright(`/${relCssOut}`)}\n`)
console.log(` * Compiling SASS file: ${chalk.yellowBright(scssInputFilename)} \n |\n |=> Creating CSS at ${chalk.yellowBright(`/${relCssOut}`)}\n`)

await compileSASS(scssInput, cssOutput);

} catch (error) {
console.error(chalk.redBright('\nError Converting Manuscript:'), chalk.redBright(error));
}
}

function removeIndentation() {
try {
return (tree) => {
tree.children = tree.children.map((node) => {
if (node.type === 'paragraph') {
node.children = node.children.map((textNode) => {
if (textNode.type === 'text') {
textNode.value = textNode.value.replace(/(^\s+)/gm, '');
}
return textNode;
});
}
return node;
});
};
} catch (error) {
console.error(chalk.redBright('\nThere was a problem when we tried to remove indentations in your manuscript:'), chalk.redBright(error));
}
}
Loading

0 comments on commit 9c89457

Please sign in to comment.