Skip to content

Commit

Permalink
create cli, remove manuscript
Browse files Browse the repository at this point in the history
  • Loading branch information
revdavethompson committed Apr 7, 2023
1 parent ba28204 commit 4832ba6
Show file tree
Hide file tree
Showing 44 changed files with 5,406 additions and 6,254 deletions.
Binary file renamed build/.DS_Store → .DS_Store
Binary file not shown.
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,10 @@
"indent": 4
},
"MD024":false
}
},
"editor.hover.delay": 1500,
"workbench.colorTheme": "Default Dark+",
"editor.fontFamily": "Inter, Monaco, 'Courier New', monospace",
"editor.fontSize": 14,
"editor.fontWeight": "500"
}
13 changes: 0 additions & 13 deletions NOTES.md

This file was deleted.

7 changes: 0 additions & 7 deletions book.yaml

This file was deleted.

7 changes: 0 additions & 7 deletions bs-config.js

This file was deleted.

Binary file removed build/html/theme/media/canvas.jpg
Binary file not shown.
Binary file removed build/html/theme/media/html-18.png
Binary file not shown.
143 changes: 143 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env node

// cli.js
import fs from 'fs';
import { fileURLToPath } from 'url';
import { Command } from 'commander';
import convert from './lib/convert.js';
import path from 'path';
import nodemon from 'nodemon';
import { readFileSync, existsSync } from 'fs';
import { spawn } from 'child_process';

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

const manuscriptDir = path.join(process.cwd(), 'manuscript');

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

async function buildHtml(manuscriptDir, outputDir, outputType) {
await convert(manuscriptDir, outputDir, outputType);
}

async function buildPdf(manuscriptDir, outputDir, outputType) {
await buildHtml(manuscriptDir, outputDir, outputType);

// Run the prince command-line tool
const prince = spawn('prince', ['build/pdf/index.html'], { stdio: 'inherit' });

prince.on('error', (error) => {
console.error(`Error: ${error.message}`);
});

return prince;
}

program
.command('build')
.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(`Building ${options.type.toUpperCase()}...`);

if (options.type === 'html') {
await buildHtml(manuscriptDir, path.join(process.cwd(), 'build', 'html'), options.type);
} else if (options.type === 'pdf') {
await buildPdf(manuscriptDir, path.join(process.cwd(), 'build', 'pdf'), options.type);
} else {
console.error('Invalid output type specified. Use either "html" or "pdf".');
}
});

program
.command('dev')
.option('-t, --type <type>', 'Specify the output type (html or pdf)', 'html')
.description('Run the development server with live-reloading')
.action(async (options) => {
console.log(`Running Nodemon and Webpack Server for ${options.type.toUpperCase()}...`);

if (options.type === 'html') {
await buildHtml(manuscriptDir, path.join(process.cwd(), 'build', 'html'), options.type);
await runWebpackDevServerAsync('html');
await runNodemonAsync('html');
} else if (options.type === 'pdf') {
await buildPdf(manuscriptDir, path.join(process.cwd(), 'build', 'pdf'), options.type);
await runWebpackDevServerAsync('pdf');
await runNodemonAsync('pdf');
} else {
console.error('Invalid output type specified. Use either "html" or "pdf".');
}
});

// Setup nodemon function to return as a Promise
function runNodemonAsync(outputType) {
return new Promise((resolve, reject) => {
runNodemon(outputType).on('quit', resolve).on('error', reject);
});
}

// Run the webpack server using the user's webpack.config.js
function runWebpackDevServerAsync(outputType) {
const server = spawn(
'npx',
['webpack', 'serve', '--env', `outputType=${outputType}`],
{ stdio: 'inherit' }
);

server.on('error', (error) => {
console.error(`Error: ${error.message}`);
});

return server;
}

// Use Nodemon to watch for changes and rebuild/serve/refresh

// Helper function to validate the user's nodemon.json file
function validateUserNodemonConfig(config) {
if (!config || !config.execMap || !config.execMap.html) {
return false;
}
return true;
}

function runNodemon(outputType) {
const userNodemonConfigPath = path.join(process.cwd(), 'nodemon.json');
let nodemonConfig = {};

// Check if the user's nodemon.json file exists
if (existsSync(userNodemonConfigPath)) {
const userNodemonConfig = JSON.parse(readFileSync(userNodemonConfigPath, 'utf-8'));

// Validate the user's nodemon.json configuration
if (validateUserNodemonConfig(userNodemonConfig)) {
nodemonConfig = { configFile: userNodemonConfigPath };
}
}

// If the user's nodemon.json file is not found or is not valid, use default settings
if (!nodemonConfig.configFile) {
console.log(`Using default Nodemon settings with outputType: ${outputType}.`);
nodemonConfig = {
script: __filename,
ext: outputType === 'pdf' ? 'md,mdx,js,ejs,json,html,css,yaml' : 'md,mdx,js,ejs,json,html,css,yaml',
exec: `bookshop build --type ${outputType}`,
watch: 'manuscript',
};
}

return nodemon(nodemonConfig).on('restart', async () => {
console.log(`Rebuilding ${outputType.toUpperCase()}...`);
if (outputType === 'html') {
await buildHtml(manuscriptDir, path.join(process.cwd(), 'build', 'html'), outputType);
} else if (outputType === 'pdf') {
await buildPdf(manuscriptDir, path.join(process.cwd(), 'build', 'pdf'), outputType);
}
});
}


program.parseAsync(process.argv);
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import convert from './lib/convert.js';

convert();
3 changes: 0 additions & 3 deletions index.mjs

This file was deleted.

75 changes: 0 additions & 75 deletions lib/buildMarkdown.js

This file was deleted.

120 changes: 120 additions & 0 deletions lib/convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import fs from 'fs';
import ejs from 'ejs';
import fse from 'fs-extra';
import path from 'path';
import { unified } from 'unified';
import remarkGfm from 'remark-gfm';
import remarkParse from 'remark-parse';
import remarkDirective from 'remark-directive';
import remarkHtml from 'remark-html';
import yaml from 'js-yaml';
import sass from 'sass';

// Small hack to import commonJS Smartypants into our ES Module
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const SmartyPants = require('smartypants');

// Convert markdown and source files in /manuscript into
// one .html file in build/html
export default async function convert(manuscriptDir, outputDir, outputType) {
try {

// Define outputFile within the convert function
const outputFile = path.join(outputDir, 'index.html');

// Set initial run variable to false (will be assigned true if already run)
let hasRun = false;

// Make sure this only gets called once per build
if (hasRun) {
return;
}
hasRun = true;
// Create the output directory if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}

// Load and log the book.yml file
console.log("Loading book.config.yml...\n");
const book = yaml.load(fs.readFileSync(path.join(manuscriptDir, '../book.config.yml'), 'utf8'));

// Load the files listed
const entryFile = book.settings.entryfile;

// Read the contents of each file and concatenate them into one markdown document
console.log(`Loading manuscript source files...\n`);
const templatePath = path.join(manuscriptDir, entryFile);
const templateContent = fs.readFileSync(templatePath, 'utf8');

// Render the EJS template with the book data and processed HTML content
const processedEJS = ejs.render(templateContent, {
meta: book.meta,
outputType: outputType,
manuscriptDir: manuscriptDir,
path: path,
}, { views: [manuscriptDir] });

// Convert the markdown document to HTML using Unified Remark
console.log("Converting manuscript Markdown files into HTML...\n");
const html = unified()
// convert markdown to a MD-AST syntax tree
.use(remarkParse)
// convert md-ast to gfm-ast (Github Flavored Markdown) and pass options
.use(remarkGfm, book['gfm-options'])
// convert md-ast into directive-ast
// allows plugin creation for custom directives
.use(remarkDirective)
// convert gfm-ast to html-ast
// but don't git rid of none-markdown syntax
.use(remarkHtml, { sanitize: false })
.processSync(processedEJS)
// convert to a string
.toString();

// Use Smartypants to process symbols and quotes
console.log("Converting symbols to smart symbols with \"smartypants\"...\n");
const smartHtml = SmartyPants.default(html)

// Write final output to output file (build/html/index.html)
fs.writeFileSync(outputFile, smartHtml);

// Copy the contents of the "./manuscript/theme" directory into the "./build/html" directory
const themeSrcDir = path.join(manuscriptDir, 'theme');
const themeDestDir = path.join(outputDir, 'theme');

console.log("Copying theme into build folder, but ignoring styles..css...\n");
// Copy the theme/css directory, but not its contents
await fse.copy(themeSrcDir, themeDestDir, {
filter: (src) => {
// If there is a theme/css directory, include it
if (fs.lstatSync(src).isDirectory()) {
return true;
}

// But ignore files in the theme/css/ directory
const relativePath = path.relative(themeSrcDir, src);
return !relativePath.startsWith('css');
},
});

// Compile SCSS to CSS
console.log(`Loading the input scss file path with ${outputType}...\n`);
const scssInput = path.join(manuscriptDir, 'theme', 'css', `styles.${outputType}.scss`);

console.log(`Loading the output css file path with ${outputType}...\n`);
const cssOutput = path.join(outputDir, 'theme', 'css', `styles.${outputType}.css`);

try {
const result = sass.renderSync({ file: scssInput, outputStyle: 'compressed' });
fs.writeFileSync(cssOutput, result.css);
} catch (error) {
console.error('\x1b[31m%s\x1b[0m', `Error compiling SCSS: ${error.message}`);
}

console.log("All Finished!\n")
} catch (error) {
console.error('\x1b[31m%s\x1b[0m', `Error Converting Source Files: ${error.message}`);
}
}
Loading

0 comments on commit 4832ba6

Please sign in to comment.