mirror of
https://github.com/10h30/wordpress-export-to-markdown.git
synced 2026-07-11 18:56:14 +09:00
Just a whole lot of stuff in progress
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import process from 'process';
|
|
||||||
import * as parser from './src/parser.js';
|
import * as parser from './src/parser.js';
|
||||||
import * as settings from './src/settings.js';
|
import * as settings from './src/settings.js';
|
||||||
import * as wizard from './src/wizard.js';
|
import * as wizard from './src/wizard.js';
|
||||||
@@ -9,7 +8,7 @@ import * as writer from './src/writer.js';
|
|||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// parse any command line arguments and run wizard
|
// parse any command line arguments and run wizard
|
||||||
const config = await wizard.getConfig(process.argv);
|
const config = await wizard.getConfig();
|
||||||
|
|
||||||
// parse data from XML and do Markdown translations
|
// parse data from XML and do Markdown translations
|
||||||
const posts = await parser.parseFilePromise(config)
|
const posts = await parser.parseFilePromise(config)
|
||||||
@@ -20,7 +19,7 @@ import * as writer from './src/writer.js';
|
|||||||
// happy goodbye
|
// happy goodbye
|
||||||
console.log('\nAll done!');
|
console.log('\nAll done!');
|
||||||
console.log('Look for your output files in: ' + path.resolve(settings.output_directory));
|
console.log('Look for your output files in: ' + path.resolve(settings.output_directory));
|
||||||
})().catch(ex => {
|
})().catch((ex) => {
|
||||||
// sad goodbye
|
// sad goodbye
|
||||||
console.log('\nSomething went wrong, execution halted early.');
|
console.log('\nSomething went wrong, execution halted early.');
|
||||||
console.error(ex);
|
console.error(ex);
|
||||||
|
|||||||
+164
-83
@@ -7,7 +7,6 @@ import path from 'path';
|
|||||||
|
|
||||||
// all user options for command line and wizard are declared here
|
// all user options for command line and wizard are declared here
|
||||||
const options = [
|
const options = [
|
||||||
// wizard must always be first
|
|
||||||
{
|
{
|
||||||
name: 'wizard',
|
name: 'wizard',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
@@ -16,50 +15,132 @@ const options = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'input',
|
name: 'input',
|
||||||
type: 'file',
|
type: 'file-path',
|
||||||
description: 'Path to WordPress export file',
|
description: 'Path to WordPress export file',
|
||||||
default: 'export.xml'
|
default: 'export.xml',
|
||||||
},
|
prompt: inquirer.input
|
||||||
{
|
|
||||||
name: 'year-folders',
|
|
||||||
type: 'boolean',
|
|
||||||
description: 'Create year folders',
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'month-folders',
|
|
||||||
type: 'boolean',
|
|
||||||
description: 'Create month folders',
|
|
||||||
default: false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'post-folders',
|
name: 'post-folders',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Create a folder for each post',
|
description: 'Put each post into its own folder',
|
||||||
default: true
|
default: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Yes',
|
||||||
|
value: true,
|
||||||
|
description: '/my-post/index.md'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'No',
|
||||||
|
value: false,
|
||||||
|
description: '/my-post.md'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
prompt: inquirer.select
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'prefix-date',
|
name: 'prefix-date',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Prefix post folders/files with date',
|
description: 'Prefix with date',
|
||||||
default: false
|
default: false,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Yes',
|
||||||
|
value: true,
|
||||||
|
description: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'No',
|
||||||
|
value: false,
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
prompt: inquirer.select
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'save-attached-images',
|
name: 'date-folders',
|
||||||
type: 'boolean',
|
type: 'choice',
|
||||||
description: 'Save images attached to posts',
|
description: 'Organize into folders based on date',
|
||||||
default: true
|
default: 'none',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Year folders',
|
||||||
|
value: 'year',
|
||||||
|
description: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Year and month folders',
|
||||||
|
value: 'year-month',
|
||||||
|
description: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'No',
|
||||||
|
value: 'none',
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
prompt: inquirer.select
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'save-scraped-images',
|
name: 'save-images',
|
||||||
type: 'boolean',
|
type: 'choice',
|
||||||
description: 'Save images scraped from post body content',
|
description: 'Save images',
|
||||||
default: true
|
default: 'all',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Images attached to posts',
|
||||||
|
value: 'attached'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Images scraped from post body content',
|
||||||
|
value: 'scraped'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Both',
|
||||||
|
value: 'all'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'No',
|
||||||
|
value: 'none'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
prompt: inquirer.select
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const validators = {
|
||||||
|
'boolean': (value) => {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return value;
|
||||||
|
} else if (value === 'true') {
|
||||||
|
return true;
|
||||||
|
} else if (value === 'false') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'Must be true or false.';
|
||||||
|
},
|
||||||
|
'file-path': (value) => {
|
||||||
|
const unwrapped = value.replace(/"(.*?)"/, '$1');
|
||||||
|
const absolute = path.resolve(unwrapped);
|
||||||
|
|
||||||
|
let fileExists;
|
||||||
|
try {
|
||||||
|
fileExists = fs.existsSync(absolute) && fs.statSync(absolute).isFile();
|
||||||
|
} catch (ex) {
|
||||||
|
fileExists = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileExists) {
|
||||||
|
return absolute;
|
||||||
|
} else {
|
||||||
|
throw 'File not found at ' + absolute + '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export async function getConfig(argv) {
|
export async function getConfig(argv) {
|
||||||
extendOptionsData();
|
|
||||||
const opts = parseCommandLine(argv);
|
const opts = parseCommandLine(argv);
|
||||||
|
|
||||||
const answers = {};
|
const answers = {};
|
||||||
@@ -67,17 +148,36 @@ export async function getConfig(argv) {
|
|||||||
console.log('\nStarting wizard...');
|
console.log('\nStarting wizard...');
|
||||||
const questions = options.filter(option => (option.name !== 'wizard' && !option.isProvided));
|
const questions = options.filter(option => (option.name !== 'wizard' && !option.isProvided));
|
||||||
for (const question of questions) {
|
for (const question of questions) {
|
||||||
answers[camelcase(question.name)] = await inquirer[question.prompt]({
|
let answer = await question.prompt({
|
||||||
message: question.description + '?',
|
message: question.description + '?',
|
||||||
|
choices: question.choices,
|
||||||
|
loop: false,
|
||||||
default: question.default,
|
default: question.default,
|
||||||
validate: question.validate, // not all questions have this, which is fine
|
validate: question.validate, // not all questions have this, which is fine
|
||||||
|
|
||||||
theme: {
|
theme: {
|
||||||
prefix: {
|
prefix: {
|
||||||
idle: chalk.cyan('?'),
|
idle: chalk.cyan('\n?'),
|
||||||
done: chalk.green('✓')
|
done: chalk.green('✓')
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
description: (text) => chalk.gray('example: ' + text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}).catch((ex) => {
|
||||||
|
if (ex instanceof Error && ex.name === 'ExitPromptError') {
|
||||||
|
console.log('\nUser quit wizard early.');
|
||||||
|
process.exit(0);
|
||||||
|
} else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (question.normalize) {
|
||||||
|
answer = question.normalize(answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
answers[camelcase(question.name)] = answer;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('\nSkipping wizard...');
|
console.log('\nSkipping wizard...');
|
||||||
@@ -87,66 +187,47 @@ export async function getConfig(argv) {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extendOptionsData() {
|
function parseCommandLine() {
|
||||||
// add more data to each option based on its type
|
|
||||||
const map = {
|
|
||||||
boolean: {
|
|
||||||
prompt: 'confirm',
|
|
||||||
coerce: coerceBoolean,
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
prompt: 'input',
|
|
||||||
coerce: coercePath,
|
|
||||||
validate: validateFile
|
|
||||||
},
|
|
||||||
folder: {
|
|
||||||
prompt: 'input',
|
|
||||||
coerce: coercePath
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
options.forEach(option => {
|
|
||||||
Object.assign(option, map[option.type]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseCommandLine(argv) {
|
|
||||||
// setup for help output
|
|
||||||
commander.program
|
commander.program
|
||||||
.name('node index.js')
|
.name('node index.js')
|
||||||
.helpOption('-h, --help', 'See the thing you\'re looking at right now')
|
.helpOption('-h, --help', 'See the thing you\'re looking at right now')
|
||||||
.addHelpText('after', '\nMore documentation is at https://github.com/lonekorean/wordpress-export-to-markdown');
|
.addHelpText('after', '\nMore documentation is at https://github.com/lonekorean/wordpress-export-to-markdown')
|
||||||
|
.configureOutput({
|
||||||
|
outputError: (str, write) => write(chalk.red(str))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
options.forEach(input => {
|
options.forEach(input => {
|
||||||
const flag = '--' + input.name + ' <' + input.type + '>';
|
const flag = '--' + input.name + ' <' + input.type + '>';
|
||||||
const coerce = (value) => {
|
const option = new commander.Option(flag, input.description);
|
||||||
// commander only calls coerce when an input is provided on the command line, which
|
option.default(input.default);
|
||||||
// makes for an easy way to flag (for later) if it should be excluded from the wizard
|
|
||||||
input.isProvided = true;
|
if (input.choices && input.type !== 'boolean') {
|
||||||
return input.coerce(value);
|
option.choices(input.choices.map((choice) => choice.value));
|
||||||
};
|
} else {
|
||||||
commander.program.option(flag, input.description, coerce, input.default);
|
option.argParser((value) => {
|
||||||
|
const validator = validators[input.type];
|
||||||
|
if (!validator) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return validator(value);
|
||||||
|
} catch (ex) {
|
||||||
|
commander.program.error(`error: option '${flag}' argument '${value}' is invalid. ${ex.toString()}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
commander.program.addOption(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
commander.program.parse();
|
||||||
|
|
||||||
|
options.forEach((option) => {
|
||||||
|
const opt = camelcase(option.name);
|
||||||
|
option.isProvided = commander.program.getOptionValueSource(opt) === 'cli';
|
||||||
});
|
});
|
||||||
|
|
||||||
commander.program.parse(argv);
|
|
||||||
return commander.program.opts();
|
return commander.program.opts();
|
||||||
}
|
}
|
||||||
|
|
||||||
function coerceBoolean(value) {
|
|
||||||
return !['false', 'no', '0'].includes(value.toString().toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
function coercePath(value) {
|
|
||||||
return path.normalize(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateFile(value) {
|
|
||||||
let isValid;
|
|
||||||
try {
|
|
||||||
isValid = fs.existsSync(value) && fs.statSync(value).isFile();
|
|
||||||
} catch (ex) {
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid ? true : 'Unable to find file: ' + path.resolve(value);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user