mirror of
https://github.com/10h30/wordpress-export-to-markdown.git
synced 2026-06-05 15:09:59 +09:00
@@ -1,25 +1,32 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import * as commander from 'commander';
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
import * as parser from './src/parser.js';
|
||||
import * as wizard from './src/wizard.js';
|
||||
import * as settings from './src/settings.js';
|
||||
import * as intake from './src/intake.js';
|
||||
import * as writer from './src/writer.js';
|
||||
|
||||
(async () => {
|
||||
// parse any command line arguments and run wizard
|
||||
const config = await wizard.getConfig(process.argv);
|
||||
// configure command line help output
|
||||
commander.program
|
||||
.name('node index.js')
|
||||
.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')
|
||||
|
||||
// gather config options from command line and wizard
|
||||
const config = await intake.getConfig();
|
||||
|
||||
// parse data from XML and do Markdown translations
|
||||
const posts = await parser.parseFilePromise(config)
|
||||
|
||||
// write files, downloading images as needed
|
||||
// write files and download images
|
||||
await writer.writeFilesPromise(posts, config);
|
||||
|
||||
// happy goodbye
|
||||
console.log('\nAll done!');
|
||||
console.log('Look for your output files in: ' + path.resolve(config.output));
|
||||
})().catch(ex => {
|
||||
console.log('Look for your output files in: ' + path.resolve(settings.output_directory));
|
||||
})().catch((ex) => {
|
||||
// sad goodbye
|
||||
console.log('\nSomething went wrong, execution halted early.');
|
||||
console.error(ex);
|
||||
|
||||
Generated
+137
-168
@@ -9,11 +9,11 @@
|
||||
"version": "2.4.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@inquirer/prompts": "^7.2.3",
|
||||
"axios": "^1.7.9",
|
||||
"camelcase": "^8.0.0",
|
||||
"chalk": "^5.4.1",
|
||||
"commander": "^13.0.0",
|
||||
"inquirer": "^10.2.2",
|
||||
"luxon": "^3.5.0",
|
||||
"turndown": "^7.2.0",
|
||||
"turndown-plugin-gfm": "^1.0.2",
|
||||
@@ -124,45 +124,48 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@inquirer/checkbox": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz",
|
||||
"integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.6.tgz",
|
||||
"integrity": "sha512-PgP35JfmGjHU0LSXOyRew0zHuA9N6OJwOlos1fZ20b7j8ISeAdib3L+n0jIxBtX958UeEpte6xhG/gxJ5iUqMw==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/figures": "^1.0.5",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/figures": "^1.0.9",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/confirm": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz",
|
||||
"integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==",
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.3.tgz",
|
||||
"integrity": "sha512-fuF9laMmHoOgWapF9h9hv6opA5WvmGFHsTYGCmuFxcghIhEhb3dN0CdQR4BUMqa2H506NCj8cGX4jwMsE4t6dA==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3"
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core": {
|
||||
"version": "9.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz",
|
||||
"integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==",
|
||||
"version": "10.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.4.tgz",
|
||||
"integrity": "sha512-5y4/PUJVnRb4bwWY67KLdebWOhOc7xj5IP2J80oWXa64mVag24rwQ1VAdnj7/eDY/odhguW0zQ1Mp1pj6fO/2w==",
|
||||
"dependencies": {
|
||||
"@inquirer/figures": "^1.0.6",
|
||||
"@inquirer/type": "^2.0.0",
|
||||
"@types/mute-stream": "^0.0.4",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/wrap-ansi": "^3.0.0",
|
||||
"@inquirer/figures": "^1.0.9",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
"cli-width": "^4.1.0",
|
||||
"mute-stream": "^1.0.0",
|
||||
"mute-stream": "^2.0.0",
|
||||
"signal-exit": "^4.1.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^6.2.0",
|
||||
@@ -172,41 +175,36 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/core/node_modules/@inquirer/type": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz",
|
||||
"integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==",
|
||||
"dependencies": {
|
||||
"mute-stream": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/editor": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz",
|
||||
"integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.3.tgz",
|
||||
"integrity": "sha512-S9KnIOJuTZpb9upeRSBBhoDZv7aSV3pG9TECrBj0f+ZsFwccz886hzKBrChGrXMJwd4NKY+pOA9Vy72uqnd6Eg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"external-editor": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/expand": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz",
|
||||
"integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.6.tgz",
|
||||
"integrity": "sha512-TRTfi1mv1GeIZGyi9PQmvAaH65ZlG4/FACq6wSzs7Vvf1z5dnNWsAAXBjWMHt76l+1hUY8teIqJFrWBk5N6gsg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/figures": {
|
||||
@@ -218,113 +216,134 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/input": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz",
|
||||
"integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.3.tgz",
|
||||
"integrity": "sha512-zeo++6f7hxaEe7OjtMzdGZPHiawsfmCZxWB9X1NpmYgbeoyerIbWemvlBxxl+sQIlHC0WuSAG19ibMq3gbhaqQ==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3"
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/number": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz",
|
||||
"integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.6.tgz",
|
||||
"integrity": "sha512-xO07lftUHk1rs1gR0KbqB+LJPhkUNkyzV/KhH+937hdkMazmAYHLm1OIrNKpPelppeV1FgWrgFDjdUD8mM+XUg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3"
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/password": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz",
|
||||
"integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.6.tgz",
|
||||
"integrity": "sha512-QLF0HmMpHZPPMp10WGXh6F+ZPvzWE7LX6rNoccdktv/Rov0B+0f+eyXkAcgqy5cH9V+WSpbLxu2lo3ysEVK91w==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"ansi-escapes": "^4.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/prompts": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.5.0.tgz",
|
||||
"integrity": "sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==",
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.2.3.tgz",
|
||||
"integrity": "sha512-hzfnm3uOoDySDXfDNOm9usOuYIaQvTgKp/13l1uJoe6UNY+Zpcn2RYt0jXz3yA+yemGHvDOxVzqWl3S5sQq53Q==",
|
||||
"dependencies": {
|
||||
"@inquirer/checkbox": "^2.5.0",
|
||||
"@inquirer/confirm": "^3.2.0",
|
||||
"@inquirer/editor": "^2.2.0",
|
||||
"@inquirer/expand": "^2.3.0",
|
||||
"@inquirer/input": "^2.3.0",
|
||||
"@inquirer/number": "^1.1.0",
|
||||
"@inquirer/password": "^2.2.0",
|
||||
"@inquirer/rawlist": "^2.3.0",
|
||||
"@inquirer/search": "^1.1.0",
|
||||
"@inquirer/select": "^2.5.0"
|
||||
"@inquirer/checkbox": "^4.0.6",
|
||||
"@inquirer/confirm": "^5.1.3",
|
||||
"@inquirer/editor": "^4.2.3",
|
||||
"@inquirer/expand": "^4.0.6",
|
||||
"@inquirer/input": "^4.1.3",
|
||||
"@inquirer/number": "^3.0.6",
|
||||
"@inquirer/password": "^4.0.6",
|
||||
"@inquirer/rawlist": "^4.0.6",
|
||||
"@inquirer/search": "^3.0.6",
|
||||
"@inquirer/select": "^4.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/rawlist": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz",
|
||||
"integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.6.tgz",
|
||||
"integrity": "sha512-QoE4s1SsIPx27FO4L1b1mUjVcoHm1pWE/oCmm4z/Hl+V1Aw5IXl8FYYzGmfXaBT0l/sWr49XmNSiq7kg3Kd/Lg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/search": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz",
|
||||
"integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==",
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.6.tgz",
|
||||
"integrity": "sha512-eFZ2hiAq0bZcFPuFFBmZEtXU1EarHLigE+ENCtpO+37NHCl4+Yokq1P/d09kUblObaikwfo97w+0FtG/EXl5Ng==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/figures": "^1.0.5",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/figures": "^1.0.9",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/select": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz",
|
||||
"integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.6.tgz",
|
||||
"integrity": "sha512-yANzIiNZ8fhMm4NORm+a74+KFYHmf7BZphSOBovIzYPVLquseTGEkU5l2UTnBOf5k0VLmTgPighNDLE9QtbViQ==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/figures": "^1.0.5",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@inquirer/core": "^10.1.4",
|
||||
"@inquirer/figures": "^1.0.9",
|
||||
"@inquirer/type": "^3.0.2",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
"yoctocolors-cjs": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@inquirer/type": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz",
|
||||
"integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==",
|
||||
"dependencies": {
|
||||
"mute-stream": "^1.0.0"
|
||||
},
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz",
|
||||
"integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@mixmark-io/domino": {
|
||||
@@ -367,27 +386,15 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mute-stream": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz",
|
||||
"integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz",
|
||||
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/wrap-ansi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz",
|
||||
"integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz",
|
||||
@@ -445,17 +452,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-escapes/node_modules/type-fest": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
@@ -587,9 +583,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz",
|
||||
"integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==",
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
|
||||
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1007,6 +1003,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/globals/node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/graphemer": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||
@@ -1084,24 +1092,6 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/inquirer": {
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-10.2.2.tgz",
|
||||
"integrity": "sha512-tyao/4Vo36XnUItZ7DnUXX4f1jVao2mSrleV/5IPtW/XAEA26hRVsbc68nuTEKWcr5vMP/1mVoT2O7u8H4v1Vg==",
|
||||
"dependencies": {
|
||||
"@inquirer/core": "^9.1.0",
|
||||
"@inquirer/prompts": "^5.5.0",
|
||||
"@inquirer/type": "^1.5.3",
|
||||
"@types/mute-stream": "^0.0.4",
|
||||
"ansi-escapes": "^4.3.2",
|
||||
"mute-stream": "^1.0.0",
|
||||
"run-async": "^3.0.0",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -1265,11 +1255,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/mute-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/natural-compare": {
|
||||
@@ -1459,14 +1449,6 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/run-async": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
|
||||
"integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -1490,14 +1472,6 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
@@ -1605,11 +1579,6 @@
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||
},
|
||||
"node_modules/turndown": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz",
|
||||
@@ -1636,10 +1605,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"dev": true,
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
|
||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -1650,7 +1618,8 @@
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
|
||||
+1
-1
@@ -21,11 +21,11 @@
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@inquirer/prompts": "^7.2.3",
|
||||
"axios": "^1.7.9",
|
||||
"camelcase": "^8.0.0",
|
||||
"chalk": "^5.4.1",
|
||||
"commander": "^13.0.0",
|
||||
"inquirer": "^10.2.2",
|
||||
"luxon": "^3.5.0",
|
||||
"turndown": "^7.2.0",
|
||||
"turndown-plugin-gfm": "^1.0.2",
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import camelcase from 'camelcase';
|
||||
import chalk from 'chalk';
|
||||
import * as commander from 'commander';
|
||||
import * as luxon from 'luxon';
|
||||
import path from 'path';
|
||||
import * as normalizers from './normalizers.js';
|
||||
import * as questions from './questions.js';
|
||||
import * as shared from './shared.js';
|
||||
|
||||
// visual formatting for wizard
|
||||
const promptTheme = {
|
||||
prefix: {
|
||||
idle: chalk.gray('\n?'),
|
||||
done: chalk.green('✓')
|
||||
},
|
||||
style: {
|
||||
description: (text) => chalk.gray('example: ' + text)
|
||||
}
|
||||
};
|
||||
|
||||
export async function getConfig() {
|
||||
// check command line for any config options
|
||||
const commandLineQuestions = questions.all;
|
||||
const commandLineAnswers = getCommandLineAnswers(commandLineQuestions);
|
||||
|
||||
let wizardAnswers;
|
||||
if (commandLineAnswers.wizard) {
|
||||
console.log('\nStarting wizard...');
|
||||
|
||||
// run wizard for remaining config options
|
||||
const wizardQuestions = questions.all.filter((question) => !(camelcase(question.name) in commandLineAnswers));
|
||||
wizardAnswers = await getWizardAnswers(wizardQuestions, commandLineAnswers);
|
||||
} else {
|
||||
console.log('\nSkipping wizard...');
|
||||
}
|
||||
|
||||
return { ...commandLineAnswers, ...wizardAnswers };
|
||||
}
|
||||
|
||||
function getCommandLineAnswers(questions) {
|
||||
// show errors in red
|
||||
commander.program.configureOutput({
|
||||
outputError: (str, write) => write(chalk.red(str))
|
||||
});
|
||||
|
||||
questions.forEach((question) => {
|
||||
const option = new commander.Option('--' + question.name + ' <' + question.type + '>', question.description);
|
||||
option.default(question.default);
|
||||
|
||||
if (question.choices && question.type !== 'boolean') {
|
||||
// let commander handle non-boolean multiple choice validation
|
||||
option.choices(question.choices.map((choice) => choice.value));
|
||||
} else {
|
||||
option.argParser((value) => normalize(value, question.type, (errorMessage) => {
|
||||
throw new commander.InvalidArgumentError(errorMessage);
|
||||
}));
|
||||
}
|
||||
|
||||
commander.program.addOption(option);
|
||||
});
|
||||
|
||||
const answers = commander.program.parse().opts();
|
||||
|
||||
// do some post-processing on the answers
|
||||
for (const [key, value] of Object.entries(answers)) {
|
||||
// the "wizard" answer and any user-provided (not defaulted) answers are left alone
|
||||
if (key === 'wizard' || commander.program.getOptionValueSource(key) !== 'default') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (answers.wizard) {
|
||||
// remove this default answer so the wizard will ask about it later
|
||||
delete answers[key];
|
||||
} else {
|
||||
// normalize and validate default answer
|
||||
const question = questions.find((question) => camelcase(question.name) === key);
|
||||
answers[key] = normalize(value, question.type, (errorMessage) => {
|
||||
// this is formatted to match how commander displays other errors
|
||||
commander.program.error(`error: option '--${question.name} <${question.type}>' argument '${value}' is invalid. ${errorMessage}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
export async function getWizardAnswers(questions, commandLineAnswers) {
|
||||
const answers = {};
|
||||
for (const question of questions) {
|
||||
let answerKey = camelcase(question.name);
|
||||
let normalizedAnswer; // holds normalized answer value potentially returned during validation
|
||||
|
||||
const promptConfig = {
|
||||
theme: promptTheme,
|
||||
message: question.description + '?',
|
||||
default: question.default,
|
||||
};
|
||||
|
||||
if (question.choices) {
|
||||
promptConfig.choices = question.choices;
|
||||
promptConfig.loop = false;
|
||||
|
||||
if (question.isPathQuestion) {
|
||||
// create a snapshot config of command line answers and wizard answers so far
|
||||
const config = { ...commandLineAnswers, ...answers };
|
||||
|
||||
promptConfig.choices.forEach((choice) => {
|
||||
// show example path if this choice is selected
|
||||
config[answerKey] = choice.value;
|
||||
choice.description = buildSamplePostPath(config);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
promptConfig.validate = (value) => {
|
||||
let validationErrorMessage;
|
||||
normalizedAnswer = normalize(value, question.type, (errorMessage) => {
|
||||
validationErrorMessage = errorMessage;
|
||||
});
|
||||
return validationErrorMessage ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
const answer = await question.prompt(promptConfig).catch((ex) => {
|
||||
// exit gracefully if user hits ctrl + c during wizard
|
||||
if (ex instanceof Error && ex.name === 'ExitPromptError') {
|
||||
console.log('\nUser quit wizard early.');
|
||||
process.exit(0);
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
});
|
||||
|
||||
answers[answerKey] = normalizedAnswer ?? answer;
|
||||
}
|
||||
|
||||
return answers;
|
||||
}
|
||||
|
||||
function normalize(value, type, onError) {
|
||||
const normalizer = normalizers[camelcase(type)];
|
||||
if (!normalizer) {
|
||||
return value;
|
||||
}
|
||||
|
||||
try {
|
||||
return normalizer(value);
|
||||
} catch (ex) {
|
||||
onError(ex.message);
|
||||
}
|
||||
}
|
||||
|
||||
export function buildSamplePostPath(config) {
|
||||
const outputDir = path.sep;
|
||||
const type = '';
|
||||
const date = luxon.DateTime.now().toFormat('yyyy-LL-dd');
|
||||
const slug = 'my-post';
|
||||
|
||||
return shared.buildPostPath(outputDir, type, date, slug, config);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export function boolean(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
} else if (value === 'true') {
|
||||
return true;
|
||||
} else if (value === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error('Must be true or false.');
|
||||
}
|
||||
|
||||
export function filePath(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 new Error('File not found at ' + absolute + '.');
|
||||
}
|
||||
}
|
||||
+9
-15
@@ -14,14 +14,14 @@ export async function parseFilePromise(config) {
|
||||
});
|
||||
const channelData = allData.rss.channel[0].item;
|
||||
|
||||
const postTypes = getPostTypes(channelData, config);
|
||||
const postTypes = getPostTypes(channelData);
|
||||
const posts = collectPosts(channelData, postTypes, config);
|
||||
|
||||
const images = [];
|
||||
if (config.saveAttachedImages) {
|
||||
if (config.saveImages === 'attached' || config.saveImages === 'all') {
|
||||
images.push(...collectAttachedImages(channelData));
|
||||
}
|
||||
if (config.saveScrapedImages) {
|
||||
if (config.saveImages === 'scraped' || config.saveImages === 'all') {
|
||||
images.push(...collectScrapedImages(channelData, postTypes));
|
||||
}
|
||||
|
||||
@@ -31,18 +31,12 @@ export async function parseFilePromise(config) {
|
||||
return posts;
|
||||
}
|
||||
|
||||
function getPostTypes(channelData, config) {
|
||||
if (config.includeOtherTypes) {
|
||||
// search export file for all post types minus some default types we don't want
|
||||
// effectively this will be 'post', 'page', and custom post types
|
||||
const types = channelData
|
||||
.map(item => item.post_type[0])
|
||||
.filter(type => !['attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset'].includes(type));
|
||||
return [...new Set(types)]; // remove duplicates
|
||||
} else {
|
||||
// just plain old vanilla "post" posts
|
||||
return ['post'];
|
||||
}
|
||||
function getPostTypes(channelData) {
|
||||
// search export file for all post types minus some specific types we don't want
|
||||
const types = channelData
|
||||
.map(item => item.post_type[0])
|
||||
.filter(type => !settings.filter_post_types.includes(type));
|
||||
return [...new Set(types)]; // remove duplicates
|
||||
}
|
||||
|
||||
function getItemsOfType(channelData, type) {
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import * as inquirer from '@inquirer/prompts';
|
||||
|
||||
export const all = [
|
||||
{
|
||||
name: 'wizard',
|
||||
type: 'boolean',
|
||||
description: 'Use wizard',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'input',
|
||||
type: 'file-path',
|
||||
description: 'Path to WordPress export file',
|
||||
default: 'export.xml',
|
||||
prompt: inquirer.input
|
||||
},
|
||||
{
|
||||
name: 'post-folders',
|
||||
type: 'boolean',
|
||||
description: 'Put each post into its own folder',
|
||||
default: true,
|
||||
choices: [
|
||||
{
|
||||
name: 'Yes',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
value: false
|
||||
}
|
||||
],
|
||||
isPathQuestion: true,
|
||||
prompt: inquirer.select
|
||||
},
|
||||
{
|
||||
name: 'prefix-date',
|
||||
type: 'boolean',
|
||||
description: 'Prefix with date',
|
||||
default: false,
|
||||
choices: [
|
||||
{
|
||||
name: 'Yes',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
value: false
|
||||
}
|
||||
],
|
||||
isPathQuestion: true,
|
||||
prompt: inquirer.select
|
||||
},
|
||||
{
|
||||
name: 'date-folders',
|
||||
type: 'choice',
|
||||
description: 'Organize into folders based on date',
|
||||
default: 'none',
|
||||
choices: [
|
||||
{
|
||||
name: 'Year folders',
|
||||
value: 'year'
|
||||
},
|
||||
{
|
||||
name: 'Year and month folders',
|
||||
value: 'year-month'
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
value: 'none'
|
||||
}
|
||||
],
|
||||
isPathQuestion: true,
|
||||
prompt: inquirer.select
|
||||
},
|
||||
{
|
||||
name: 'save-images',
|
||||
type: 'choice',
|
||||
description: 'Save images',
|
||||
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
|
||||
}
|
||||
];
|
||||
@@ -38,3 +38,17 @@ export const filter_categories = ['uncategorized'];
|
||||
// Strict SSL is enabled as the safe default when downloading images, but will not work with
|
||||
// self-signed servers. You can disable it if you're getting a "self-signed certificate" error.
|
||||
export const strict_ssl = true;
|
||||
|
||||
// Post types to exclude from output.
|
||||
export const filter_post_types = [
|
||||
'attachment',
|
||||
'revision',
|
||||
'nav_menu_item',
|
||||
'custom_css',
|
||||
'customize_changeset',
|
||||
'wp_global_styles',
|
||||
'wp_navigation'
|
||||
];
|
||||
|
||||
// Output directory.
|
||||
export const output_directory = 'output';
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
import * as luxon from 'luxon';
|
||||
import path from 'path';
|
||||
import * as settings from './settings.js';
|
||||
|
||||
export function buildPostPath(outputDir, type, date, slug, config) {
|
||||
let dt;
|
||||
if (settings.custom_date_formatting) {
|
||||
dt = luxon.DateTime.fromFormat(date, settings.custom_date_formatting);
|
||||
} else {
|
||||
dt = luxon.DateTime.fromISO(date);
|
||||
}
|
||||
|
||||
// start with base output dir and post type
|
||||
const pathSegments = [outputDir, type];
|
||||
|
||||
if (config.dateFolders === 'year' || config.dateFolders === 'year-month') {
|
||||
pathSegments.push(dt.toFormat('yyyy'));
|
||||
}
|
||||
|
||||
if (config.dateFolders === 'year-month') {
|
||||
pathSegments.push(dt.toFormat('LL'));
|
||||
}
|
||||
|
||||
// create slug fragment, possibly date prefixed
|
||||
let slugFragment = slug;
|
||||
if (config.prefixDate) {
|
||||
slugFragment = dt.toFormat('yyyy-LL-dd') + '-' + slugFragment;
|
||||
}
|
||||
|
||||
// use slug fragment as folder or filename as specified
|
||||
if (config.postFolders) {
|
||||
pathSegments.push(slugFragment, 'index.md');
|
||||
} else {
|
||||
pathSegments.push(slugFragment + '.md');
|
||||
}
|
||||
|
||||
return path.join(...pathSegments);
|
||||
}
|
||||
|
||||
export function getFilenameFromUrl(url) {
|
||||
let filename = url.split('/').slice(-1)[0];
|
||||
try {
|
||||
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
import camelcase from 'camelcase';
|
||||
import * as commander from 'commander';
|
||||
import fs from 'fs';
|
||||
import inquirer from 'inquirer';
|
||||
import path from 'path';
|
||||
|
||||
// all user options for command line and wizard are declared here
|
||||
const options = [
|
||||
// wizard must always be first
|
||||
{
|
||||
name: 'wizard',
|
||||
type: 'boolean',
|
||||
description: 'Use wizard',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'input',
|
||||
type: 'file',
|
||||
description: 'Path to WordPress export file',
|
||||
default: 'export.xml'
|
||||
},
|
||||
{
|
||||
name: 'output',
|
||||
type: 'folder',
|
||||
description: 'Path to output folder',
|
||||
default: 'output'
|
||||
},
|
||||
{
|
||||
name: 'year-folders',
|
||||
aliases: ['yearfolders', 'yearmonthfolders'],
|
||||
type: 'boolean',
|
||||
description: 'Create year folders',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
name: 'month-folders',
|
||||
aliases: ['yearmonthfolders'],
|
||||
type: 'boolean',
|
||||
description: 'Create month folders',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
name: 'post-folders',
|
||||
aliases: ['postfolders'],
|
||||
type: 'boolean',
|
||||
description: 'Create a folder for each post',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'prefix-date',
|
||||
aliases: ['prefixdate'],
|
||||
type: 'boolean',
|
||||
description: 'Prefix post folders/files with date',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
name: 'save-attached-images',
|
||||
aliases: ['saveimages'],
|
||||
type: 'boolean',
|
||||
description: 'Save images attached to posts',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'save-scraped-images',
|
||||
aliases: ['addcontentimages'],
|
||||
type: 'boolean',
|
||||
description: 'Save images scraped from post body content',
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'include-other-types',
|
||||
type: 'boolean',
|
||||
description: 'Include custom post types and pages',
|
||||
default: false
|
||||
}
|
||||
];
|
||||
|
||||
export async function getConfig(argv) {
|
||||
extendOptionsData();
|
||||
const unaliasedArgv = replaceAliases(argv);
|
||||
const opts = parseCommandLine(unaliasedArgv);
|
||||
|
||||
let answers;
|
||||
if (opts.wizard) {
|
||||
console.log('\nStarting wizard...');
|
||||
const questions = options.map(option => ({
|
||||
when: option.name !== 'wizard' && !option.isProvided,
|
||||
name: camelcase(option.name),
|
||||
type: option.prompt,
|
||||
message: option.description + '?',
|
||||
default: option.default,
|
||||
|
||||
// these are not used for all option types and that's fine
|
||||
filter: option.coerce,
|
||||
validate: option.validate
|
||||
}));
|
||||
answers = await inquirer.prompt(questions);
|
||||
} else {
|
||||
console.log('\nSkipping wizard...');
|
||||
answers = {};
|
||||
}
|
||||
|
||||
const config = { ...opts, ...answers };
|
||||
return config;
|
||||
}
|
||||
|
||||
function extendOptionsData() {
|
||||
// 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 replaceAliases(argv) {
|
||||
let paths = argv.slice(0, 2);
|
||||
let replaced = [];
|
||||
let unmodified = [];
|
||||
|
||||
argv.slice(2).forEach(arg => {
|
||||
let aliasFound = false;
|
||||
|
||||
// this loop does not short circuit because an alias can map to multiple options
|
||||
options.forEach(option => {
|
||||
const aliases = option.aliases || [];
|
||||
aliases.forEach(alias => {
|
||||
if (arg.includes('--' + alias)) {
|
||||
replaced.push(arg.replace('--' + alias, '--' + option.name));
|
||||
aliasFound = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!aliasFound) {
|
||||
unmodified.push(arg);
|
||||
}
|
||||
});
|
||||
|
||||
return [...paths, ...replaced, ...unmodified];
|
||||
}
|
||||
|
||||
function parseCommandLine(argv) {
|
||||
// setup for help output
|
||||
commander.program
|
||||
.name('node index.js')
|
||||
.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');
|
||||
|
||||
options.forEach(input => {
|
||||
const flag = '--' + input.name + ' <' + input.type + '>';
|
||||
const coerce = (value) => {
|
||||
// commander only calls coerce when an input is provided on the command line, which
|
||||
// makes for an easy way to flag (for later) if it should be excluded from the wizard
|
||||
input.isProvided = true;
|
||||
return input.coerce(value);
|
||||
};
|
||||
commander.program.option(flag, input.description, coerce, input.default);
|
||||
});
|
||||
|
||||
commander.program.parse(argv);
|
||||
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);
|
||||
}
|
||||
+11
-43
@@ -3,7 +3,6 @@ import chalk from 'chalk';
|
||||
import fs from 'fs';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import * as luxon from 'luxon';
|
||||
import path from 'path';
|
||||
import * as settings from './settings.js';
|
||||
import * as shared from './shared.js';
|
||||
@@ -22,7 +21,7 @@ async function processPayloadsPromise(payloads, loadFunc) {
|
||||
console.log(chalk.green('[OK]') + ' ' + payload.name);
|
||||
resolve();
|
||||
} catch (ex) {
|
||||
console.log(chalk.red('[FAILED]') + ' ' + payload.name + ' ' + chalk.red('(' + ex.toString() + ')'));
|
||||
console.log(chalk.red('[FAILED]') + ' ' + payload.name + ' ' + chalk.red('(' + ex.message + ')'));
|
||||
reject();
|
||||
}
|
||||
}, payload.delay);
|
||||
@@ -47,7 +46,7 @@ async function writeMarkdownFilesPromise(posts, config) {
|
||||
let skipCount = 0;
|
||||
let delay = 0;
|
||||
const payloads = posts.flatMap(post => {
|
||||
const destinationPath = getPostPath(post, config);
|
||||
const destinationPath = buildPostPath(post, config);
|
||||
if (checkFile(destinationPath)) {
|
||||
// already exists, don't need to save again
|
||||
skipCount++;
|
||||
@@ -55,7 +54,7 @@ async function writeMarkdownFilesPromise(posts, config) {
|
||||
} else {
|
||||
const payload = {
|
||||
item: post,
|
||||
name: (config.includeOtherTypes ? post.meta.type + ' - ' : '') + post.meta.slug,
|
||||
name: post.meta.type + ' - ' + post.meta.slug,
|
||||
destinationPath,
|
||||
delay
|
||||
};
|
||||
@@ -105,7 +104,7 @@ async function writeImageFilesPromise(posts, config) {
|
||||
let skipCount = 0;
|
||||
let delay = 0;
|
||||
const payloads = posts.flatMap(post => {
|
||||
const postPath = getPostPath(post, config);
|
||||
const postPath = buildPostPath(post, config);
|
||||
const imagesDir = path.join(path.dirname(postPath), 'images');
|
||||
return post.meta.imageUrls.flatMap(imageUrl => {
|
||||
const filename = shared.getFilenameFromUrl(imageUrl);
|
||||
@@ -162,7 +161,7 @@ async function loadImageFilePromise(imageUrl) {
|
||||
} catch (ex) {
|
||||
if (ex.response) {
|
||||
// request was made, but server responded with an error status code
|
||||
throw 'StatusCodeError: ' + ex.response.status;
|
||||
throw new Error('StatusCodeError: ' + ex.response.status);
|
||||
} else {
|
||||
// something else went wrong, rethrow
|
||||
throw ex;
|
||||
@@ -171,44 +170,13 @@ async function loadImageFilePromise(imageUrl) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function getPostPath(post, config) {
|
||||
let dt;
|
||||
if (settings.custom_date_formatting) {
|
||||
dt = luxon.DateTime.fromFormat(post.frontmatter.date, settings.custom_date_formatting);
|
||||
} else {
|
||||
dt = luxon.DateTime.fromISO(post.frontmatter.date);
|
||||
}
|
||||
function buildPostPath(post, config) {
|
||||
const outputDir = settings.output_directory;
|
||||
const type = post.meta.type;
|
||||
const date = post.frontmatter.date;
|
||||
const slug = post.meta.slug;
|
||||
|
||||
// start with base output dir
|
||||
const pathSegments = [config.output];
|
||||
|
||||
// create segment for post type if we're dealing with more than just "post"
|
||||
if (config.includeOtherTypes) {
|
||||
pathSegments.push(post.meta.type);
|
||||
}
|
||||
|
||||
if (config.yearFolders) {
|
||||
pathSegments.push(dt.toFormat('yyyy'));
|
||||
}
|
||||
|
||||
if (config.monthFolders) {
|
||||
pathSegments.push(dt.toFormat('LL'));
|
||||
}
|
||||
|
||||
// create slug fragment, possibly date prefixed
|
||||
let slugFragment = post.meta.slug;
|
||||
if (config.prefixDate) {
|
||||
slugFragment = dt.toFormat('yyyy-LL-dd') + '-' + slugFragment;
|
||||
}
|
||||
|
||||
// use slug fragment as folder or filename as specified
|
||||
if (config.postFolders) {
|
||||
pathSegments.push(slugFragment, 'index.md');
|
||||
} else {
|
||||
pathSegments.push(slugFragment + '.md');
|
||||
}
|
||||
|
||||
return path.join(...pathSegments);
|
||||
return shared.buildPostPath(outputDir, type, date, slug, config);
|
||||
}
|
||||
|
||||
function checkFile(path) {
|
||||
|
||||
Reference in New Issue
Block a user