diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 8c541d5..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "env": { - "commonjs": true, - "es6": true, - "node": true - }, - "extends": "eslint:recommended", - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2018 - }, - "rules": { - } -} \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..c2dfa4c --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# How to Contribute + +Contributions are welcome! Thank you! + +## General Guidelines + +Some quick notes when making a pull request. + +- Match the style and formatting of the code you are editing. +- Each pull request should be focused on a single thing (a single bug fix, a single feature, etc.). This makes reviewing easier and minimizes merge conflicts. +- Include a description of the problem being solved and what your code does. Steps to reproduce the problem or example input/output are very helpful. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index dbfa64b..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# How to Contribute - -Contributions are welcome! Thank you! - -## General Guidelines - -Some quick notes when making a pull request. - -- Match the style and formatting of the code you are editing. -- Each pull request should be focused on a single thing (a single bug fix, a single feature, etc.). This makes reviewing easier and minimizes merge conflicts. -- Include a description of the problem being solved and what your code does. Steps to reproduce the problem or example input/output are very helpful. - -## Adding Options - -Keeping the wizard as short as possible is a priority. Pull requests that add options to the wizard will probably not be accepted. Instead, you can add an advanced setting to [settings.js](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/src/settings.js). - -## Adding Frontmatter Fields - -Similarly, default frontmatter output is limited to just a few widely used fields to avoid bloat. However, you may add new optional frontmatter fields. - -To do so, follow the instructions in [/src/frontmatter/example.js](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/src/frontmatter/example.js). - -Users will be able to include your new frontmatter field by editing `frontmatter_fields` in [settings.js](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/src/settings.js). diff --git a/README.md b/README.md index b4f99ad..72fa063 100644 --- a/README.md +++ b/README.md @@ -1,150 +1,247 @@ # wordpress-export-to-markdown -Converts a WordPress export file into Markdown files that are compatible with static site generators ([Eleventy](https://www.11ty.dev/), [Gatsby](https://www.gatsbyjs.com/), [Hugo](https://gohugo.io/), etc.). +Converts a WordPress export XML file into Markdown files. This makes it easy to migrate from WordPress to a static site generator ([Eleventy](https://www.11ty.dev/), [Gatsby](https://www.gatsbyjs.com/), [Hugo](https://gohugo.io/), etc.). -Each post is saved as a separate Markdown file with frontmatter. Images are downloaded and saved. +![wordpress-export-to-markdown running in a terminal](https://github.com/user-attachments/assets/7ac1aa07-b6ee-46f4-ab49-291c1c45f350) -![wordpress-export-to-markdown running in a terminal](https://user-images.githubusercontent.com/1245573/72686026-3aa04280-3abe-11ea-92c1-d756a24657dd.gif) +## Features + +- Saves each post as a separate Markdown file with frontmatter. +- Also saves drafts, pages, and custom post types, if you have any. +- Downloads images and updates references to them. +- User-friendly wizard guides you through the process. +- Lots of command line options for configuration, if needed. ## Quick Start You'll need: -- [Node.js](https://nodejs.org/) installed -- Your [WordPress export file](https://wordpress.org/support/article/tools-export-screen/) (be sure to export "All content"). -To make things easier, you can rename your WordPress export file to `export.xml` and drop it into the same directory that you run this script from. +- [Node.js](https://nodejs.org/) installed. +- Your [WordPress export file](https://wordpress.org/support/article/tools-export-screen/). Be sure to export "All Content". -You can run this script immediately in your terminal with `npx`: +Then run this in your terminal: ``` npx wordpress-export-to-markdown ``` -Or you can clone this repo, then from within the repo's directory, install and run: +## Options -``` -npm install && node index.js -``` +The script will start with a wizard to ask you a few questions. -Either way, the script will start a wizard to configure your options. Answer the questions and off you go! - -## Command Line - -Options can also be configured via the command line. The wizard will skip asking about any such options. For example, the following will give you [Jekyll](https://jekyllrb.com/)-style output in terms of folder structure and filenames. - -Using `npx`: +Optionally, you can provide answers to any of these questions via command line arguments, in which case the wizard will skip asking those questions. Here's an example: ``` npx wordpress-export-to-markdown --post-folders=false --prefix-date=true ``` -Using a locally cloned repo: - -``` -node index.js --post-folders=false --prefix-date=true -``` - -The wizard will still ask you about any options not specified on the command line. To skip the wizard entirely and use default values for unspecified options, add `--wizard=false`. - -## Options - -These are the questions asked by the wizard. Command line arguments, along with their default values, are also being provided here if you want to use them. +The questions are given below, including a snippet for each one showing its command line argument set to its default value. ### Path to WordPress export file? -**Command line:** `--input=export.xml` +``` +--input=export.xml +``` -The path to your WordPress export file. To make things easier, you can rename your WordPress export file to `export.xml` and drop it into the same directory that you run this script from. +The path to your [WordPress export file](https://wordpress.org/documentation/article/tools-export-screen/). To make things easier, you can rename it to `export.xml` and drop it into the same directory that you run the script from. + +Allowed values: + +- Any path to a file that exists. + +### Put each post into its own folder? + +``` +--post-folders=true +``` + +Whether or not to create a separate folder for each post's Markdown file (and images). + +Allowed values: + +- `true` - A folder is created for each post, with an `index.md` file and `/images` folder within. The post slug is used to name the folder. +- `false` - The post slug is used to name each post's Markdown file. These files are all saved in the same folder. All images are saved in a shared `/images` folder. + +### Add date prefix to posts? + +``` +--prefix-date=false +``` + +Whether or not to prepend the post date when naming a post's folder or file. + +Allowed values: + +- `true` - Prepend the date, in the format `--`. Nothing will be prepended if there is no date (for example, an undated draft post). +- `false` - Don't prepend the date. + +### Organize posts into date folders? + +``` +--date-folders=none +``` + +If and how output is organized into folders based on date. + +Allowed values: + +- `year` - Output is organized into folders by year. This won't happen for posts with no date (for example, an undated draft post). +- `year‑month` - Output is organized into folders by year, then into nested folders by month. Again, for posts with no date, this won't happen. +- `none` - No date folders are created. + +### Save images? + +``` +--save-images=all +``` + +Which images you want to download and save. + +Allowed values: + +- `attached` - Save images attached to posts. Generally speaking, these are images that were uploaded by using **Add Media** or **Set Featured Image** in WordPress. +- `scraped` - Save images scraped from `` tags in post body content. The `` tags are updated to point to where the images are saved. +- `all` - Save all images, essentially the results of `attached` and `scraped` combined. +- `none` - Don't save any images. + +## Advanced Options + +These are not included in the wizard, so you'll need to set them on the command line. + +### Use wizard? + +``` +--wizard=true +``` + +Whether or not to use the wizard. + +Allowed values: + +- `true` - The script will start with a wizard to ask five questions (the ones from the [Options](#options) section) minus any that were answered on the command line. +- `false` - Skip wizard. Options set via command line are taken, while the rest have their default values used. ### Path to output folder? -**Command line:** `--output=output` +``` +--output=output +``` -The path to the output directory where Markdown and image files will be saved. If it does not exist, it will be created. +The path to the output folder where files will be saved. It'll be created if it doesn't exist. Existing files there won't be overwritten and won't be downloaded again. This lets you resume progress by restarting the script, if it was previously terminated early. To start clean, delete the output folder. -### Create year folders? +Allowed values: -**Command line:** `--year-folders=false` +- Any valid folder path. -Whether or not to organize output files into folders by year. +### Frontmatter fields? -### Create month folders? +``` +--frontmatter-fields=title,date,categories,tags,coverImage,draft +``` -**Command line:** `--month-folders=false` +Comma separated list of the frontmatter fields to include in Markdown files. Order is preserved. If a post doesn't have a value for a field, it is left off. -Whether or not to organize output files into folders by month. You'll probably want to combine this with `--year-folders` to organize files by year then month. +Allowed values: -### Create a folder for each post? +- A comma separated list with any of the following: `author`, `categories`, `coverImage`, `date`, `draft`, `excerpt`, `id`, `slug`, `tags`, `title`, `type`. You can rename a field by appending `:` and the alias to use. For example, `date:created` will rename `date` to `created`. -**Command line:** `--post-folders=true` +### Delay between image file requests? -Whether or not to save files and images into post folders. +``` +--request-delay=500 +``` -If `true`, the post slug is used for the folder name and the post's Markdown file is named `index.md`. Each post folder will have its own `/images` folder. +Time (in milliseconds) to wait between requesting image files. Increasing this might help if you see timeouts or server errors. - /first-post - /images - potato.png - index.md - /second-post - /images - carrot.jpg - celery.jpg - index.md +Allowed values: -If `false`, the post slug is used to name the post's Markdown file. These files will be side-by-side and images will go into a shared `/images` folder. +- Any positive integer. - /images - carrot.jpg - celery.jpg - potato.png - first-post.md - second-post.md +### Delay between writing markdown files? -Either way, this can be combined with with `--year-folders` and `--month-folders`, in which case the above output will be organized under the appropriate year and month folders. +``` +--write-delay=10 +``` -### Prefix post folders/files with date? +Time (in milliseconds) to wait between saving Markdown files. Increasing this might help if your file system becomes overloaded. -**Command line:** `--prefix-date=false` +Allowed values: -Whether or not to prepend the post date to the post slug when naming a post's folder or file. +- Any positive integer. -If `--post-folders` is `true`, this affects the folder. +### Timezone to apply to date? - /2019-10-14-first-post - index.md - /2019-10-23-second-post - index.md +``` +--timezone=utc +``` -If `--post-folders` is `false`, this affects the file. +The timezone applied to post dates. - 2019-10-14-first-post.md - 2019-10-23-second-post.md +Allowed values: -### Save images attached to posts? +- Any valid timezone as [specified here](https://moment.github.io/luxon/#/zones?id=specifying-a-zone). -**Command line:** `--save-attached-images=true` +### Include time with frontmatter date? -Whether or not to download and save images attached to posts. Generally speaking, these are images that were uploaded by using **Add Media** or **Set Featured Image** in WordPress. Images are saved into `/images`. +``` +--include-time=false +``` -### Save images scraped from post body content? +Whether or not time should be included with the date in frontmatter. -**Command line:** `--save-scraped-images=true` +Allowed values: -Whether or not to download and save images scraped from `` tags in post body content. Images are saved into `/images`. The `` tags are updated to point to where the images are saved. +- `true` - Time is included using an ISO 8601-compliant format. For example, `2020-12-25T11:20:35.000Z`. +- `false` - Time is not included. For example, `2020-12-25`. -### Include custom post types and pages? +### Frontmatter date format string? -**Command line:** `--include-other-types=false` +``` +--date-format="" +``` -Some WordPress sites make use of a `"page"` post type and/or custom post types. Set this to `true` to include these post types in the output. Posts will be organized into post type folders. +A custom formatting string to apply to frontmatter dates. If set, takes precedence over `--include-time`. An empty string (the default) is ignored, resulting in the basic `--` format. -## Customizing Frontmatter and Other Advanced Settings +Allowed values: -You can edit [settings.js](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/src/settings.js) to configure advanced settings beyond the options above. This includes things like customizing frontmatter, date formatting, throttling image downloads, and more. +- Any valid custom formatting string. See [this table of tokens](https://moment.github.io/luxon/#/parsing?id=table-of-tokens). -You'll need to run the script locally (not using `npx`) to edit these advanced settings. +### Wrap frontmatter date in quotes? + +``` +--quote-date=false +``` + +Whether or not to put double quotes around the date when writing it to frontmatter. + +Allowed values: + +- `true` - Adds double quotes. This technically turns the date into a string value. +- `false` - Doesn't add double quotes. + +### Use strict SSL? + +``` +--strict-ssl=true +``` + +Whether or not to use strict SSL when downloading images. + +Allowed values: + +- `true` - Use strict SSL. This is the safer option. +- `false` - Don't use strict SSL. This will let you avoid the "self-signed certificate" error when working with a self-signed server. Just make sure you know what you're doing. + +## Local Development + +You can install and run this script locally if you want to tinker with it: + +1. `git clone` this repo. +2. `cd` into the repo directory. +3. Run `npm install`. + +Now instead of running `npx wordpress-export-to-markdown` you can run `node app`. They both take all the same command line arguments in the same way. ## Contributing -Please read the [contribution guidelines](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/CONTRIBUTING.md). +Please read the [contribution guidelines](https://github.com/lonekorean/wordpress-export-to-markdown/blob/master/.github/CONTRIBUTING.md). diff --git a/app.js b/app.js new file mode 100644 index 0000000..806bc53 --- /dev/null +++ b/app.js @@ -0,0 +1,38 @@ +#!/usr/bin/env node + +import chalk from 'chalk'; +import * as commander from 'commander'; +import path from 'path'; +import * as intake from './src/intake.js'; +import * as parser from './src/parser.js'; +import * as shared from './src/shared.js'; +import * as writer from './src/writer.js'; + +(async () => { + // configure command line help output + commander.program + .name('npx wordpress-export-to-markdown') + .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') + .configureHelp({ + styleOptionTerm: (str) => str.replace(/(<.*>)$/, chalk.gray('$1')), + styleOptionDescription: (str) => str.replace(/(\(.*\))$/, chalk.gray('$1')) + }); + + // gather config options from command line and wizard + await intake.getConfig(); + + // parse data from XML and do Markdown translations + const posts = await parser.parseFilePromise() + + // write files and download images + await writer.writeFilesPromise(posts); + + // happy goodbye + console.log('\nAll done!'); + console.log('Look for your output files in: ' + path.resolve(shared.config.output)); +})().catch((ex) => { + // sad goodbye + console.log('\nSomething went wrong, execution halted early.'); + console.error(ex); +}); diff --git a/index.js b/index.js deleted file mode 100644 index ff993cc..0000000 --- a/index.js +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node - -const path = require('path'); -const process = require('process'); - -const wizard = require('./src/wizard'); -const parser = require('./src/parser'); -const writer = require('./src/writer'); - -(async () => { - // parse any command line arguments and run wizard - const config = await wizard.getConfig(process.argv); - - // parse data from XML and do Markdown translations - const posts = await parser.parseFilePromise(config) - - // write files, downloading images as needed - 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 => { - // sad goodbye - console.log('\nSomething went wrong, execution halted early.'); - console.error(ex); -}); diff --git a/package-lock.json b/package-lock.json index cb2be38..62ed219 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,204 +9,325 @@ "version": "2.4.2", "license": "MIT", "dependencies": { - "axios": "^1.7.9", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "commander": "^13.0.0", - "inquirer": "^8.2.6", + "@guyplusplus/turndown-plugin-gfm": "^1.0.7", + "@inquirer/prompts": "^7.4.0", + "axios": "^1.8.2", + "chalk": "^5.4.1", + "commander": "^13.1.0", "luxon": "^3.5.0", - "require-directory": "^2.1.1", "turndown": "^7.2.0", - "turndown-plugin-gfm": "^1.0.2", "xml2js": "^0.6.2" }, "bin": { - "wordpress-export-to-markdown": "index.js" - }, - "devDependencies": { - "eslint": "^8.57.1" + "wordpress-export-to-markdown": "app.js" }, "engines": { - "node": ">= 18.0.0" + "node": ">= 20.5.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", - "dev": true, + "node_modules/@guyplusplus/turndown-plugin-gfm": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@guyplusplus/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.7.tgz", + "integrity": "sha512-k2KATk491JIeq1KNsjOwaD88cdknQeTpKVXh9OuZfHdjFuSEtEVVDCfCbxzmRvJxeKDPbZQl8DGP5o7SaIvWBw==", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "turndown": "^7.1.1" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.4.tgz", + "integrity": "sha512-d30576EZdApjAMceijXA5jDzRQHT/MygbC+J8I7EqA6f/FRpYxlRtRJbHF8gHeWYeSdOuTEJqonn7QLB1ELezA==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, + "node_modules/@inquirer/confirm": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.8.tgz", + "integrity": "sha512-dNLWCYZvXDjO3rnQfk2iuJNL4Ivwz/T2+C3+WnNfJKsNGSuOs3wAo2F6e0p946gtSAk31nZMfW+MRmYaplPKsg==", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=18" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, + "node_modules/@inquirer/core": { + "version": "10.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.9.tgz", + "integrity": "sha512-sXhVB8n20NYkUBfDYgizGHlpRVaCRjtuzNZA6xpALIUbkgfd2Hjz+DfEN6+h1BRnuxw0/P4jCIMjMsEOAMwAJw==", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" + "node": ">=18" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true + "node_modules/@inquirer/editor": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.9.tgz", + "integrity": "sha512-8HjOppAxO7O4wV1ETUlJFg6NDjp/W2NP5FB9ZPAcinAlNT4ZIWOLe2pUVwmmPRSV0NMdI5r/+lflN55AwZOKSw==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.11.tgz", + "integrity": "sha512-OZSUW4hFMW2TYvX/Sv+NnOZgO8CHT2TU1roUCUIF2T+wfw60XFRRp9MRUPCT06cRnKL+aemt2YmTWwt7rOrNEA==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", + "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.8.tgz", + "integrity": "sha512-WXJI16oOZ3/LiENCAxe8joniNp8MQxF6Wi5V+EBbVA0ZIOpFcL4I9e7f7cXse0HJeIPCWO8Lcgnk98juItCi7Q==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.11.tgz", + "integrity": "sha512-pQK68CsKOgwvU2eA53AG/4npRTH2pvs/pZ2bFvzpBhrznh8Mcwt19c+nMO7LHRr3Vreu1KPhNBF3vQAKrjIulw==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.11.tgz", + "integrity": "sha512-dH6zLdv+HEv1nBs96Case6eppkRggMe8LoOTl30+Gq5Wf27AO/vHFgStTVz4aoevLdNXqwE23++IXGw4eiOXTg==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.0.tgz", + "integrity": "sha512-EZiJidQOT4O5PYtqnu1JbF0clv36oW2CviR66c7ma4LsupmmQlUwmdReGKRp456OWPWMz3PdrPiYg3aCk3op2w==", + "dependencies": { + "@inquirer/checkbox": "^4.1.4", + "@inquirer/confirm": "^5.1.8", + "@inquirer/editor": "^4.2.9", + "@inquirer/expand": "^4.0.11", + "@inquirer/input": "^4.1.8", + "@inquirer/number": "^3.0.11", + "@inquirer/password": "^4.0.11", + "@inquirer/rawlist": "^4.0.11", + "@inquirer/search": "^3.0.11", + "@inquirer/select": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.11.tgz", + "integrity": "sha512-uAYtTx0IF/PqUAvsRrF3xvnxJV516wmR6YVONOmCWJbbt87HcDHLfL9wmBQFbNJRv5kCjdYKrZcavDkH3sVJPg==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/type": "^3.0.5", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.11.tgz", + "integrity": "sha512-9CWQT0ikYcg6Ls3TOa7jljsD7PgjcsYEM0bYE+Gkz+uoW9u8eaJCRHJKkucpRE5+xKtaaDbrND+nPDoxzjYyew==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.0.tgz", + "integrity": "sha512-z0a2fmgTSRN+YBuiK1ROfJ2Nvrpij5lVN3gPDkQGhavdvIVGHGW29LwYZfM/j42Ai2hUghTI/uoBuTbrJk42bA==", + "dependencies": { + "@inquirer/core": "^10.1.9", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", + "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } }, "node_modules/@mixmark-io/domino": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==" }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -221,17 +342,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", @@ -254,125 +364,39 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", + "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, - "node_modules/balanced-match": { + "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "engines": { - "node": ">=10" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -383,42 +407,12 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "engines": { - "node": ">= 10" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" + "node": ">= 12" } }, "node_modules/color-convert": { @@ -449,67 +443,13 @@ } }, "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" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -518,16 +458,17 @@ "node": ">=0.4.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dependencies": { - "esutils": "^2.0.2" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=6.0.0" + "node": ">= 0.4" } }, "node_modules/emoji-regex": { @@ -535,159 +476,45 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "es-errors": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 0.4" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, "node_modules/external-editor": { @@ -703,103 +530,6 @@ "node": ">=4" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true - }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -820,84 +550,107 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dependencies": { - "is-glob": "^4.0.3" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/iconv-lite": { @@ -911,109 +664,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1022,145 +672,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/luxon": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", @@ -1169,6 +680,14 @@ "node": ">=12" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1188,103 +707,12 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/os-tmpdir": { @@ -1295,244 +723,11 @@ "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "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", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "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/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1543,38 +738,15 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/string-width": { @@ -1601,40 +773,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -1646,11 +784,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", @@ -1659,28 +792,10 @@ "@mixmark-io/domino": "^2.2.0" } }, - "node_modules/turndown-plugin-gfm": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz", - "integrity": "sha512-vwz9tfvF7XN/jE0dGoBei3FXWuvll78ohzCZQuOb+ZjWrs3a0XhQVomJEb2Qh4VHTPNRO4GPZh0V7VRbiWwkRg==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "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" }, @@ -1688,52 +803,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -1747,12 +816,6 @@ "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -1773,13 +836,12 @@ "node": ">=4.0" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 10512ed..41b80cf 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "wordpress-export-to-markdown", "version": "2.4.2", "description": "Converts a WordPress export XML file into Markdown files.", - "main": "index.js", + "main": "app.js", "repository": "https://github.com/lonekorean/wordpress-export-to-markdown.git", "keywords": [ "blog", @@ -11,30 +11,23 @@ "markdown", "wordpress" ], - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, "author": "Will Boyd (https://codersblock.com)", "license": "MIT", "engines": { - "node": ">= 18.0.0" + "node": ">= 20.5.0" }, + "type": "module", "dependencies": { - "axios": "^1.7.9", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "commander": "^13.0.0", - "inquirer": "^8.2.6", + "@guyplusplus/turndown-plugin-gfm": "^1.0.7", + "@inquirer/prompts": "^7.4.0", + "axios": "^1.8.2", + "chalk": "^5.4.1", + "commander": "^13.1.0", "luxon": "^3.5.0", - "require-directory": "^2.1.1", "turndown": "^7.2.0", - "turndown-plugin-gfm": "^1.0.2", "xml2js": "^0.6.2" }, - "devDependencies": { - "eslint": "^8.57.1" - }, "bin": { - "wordpress-export-to-markdown": "./index.js" + "wordpress-export-to-markdown": "./app.js" } } diff --git a/src/data.js b/src/data.js new file mode 100644 index 0000000..55db05a --- /dev/null +++ b/src/data.js @@ -0,0 +1,108 @@ +import xml2js from 'xml2js'; + +class Data { + #obj; + #expression; + + constructor(obj, expression) { + // xml2js returns leaf nodes as strings, turn those into consistent objects + // I found this to be safer and more efficient than using the explicitCharkey option + this.#obj = typeof obj === 'string' ? { _: obj } : obj; + + // this identifies how the object was referenced, helps a ton with debugging + this.#expression = expression; + } + + #buildExpression(propName, index = undefined) { + let expression = `${this.#expression}.${propName}`; + if (index !== undefined) { + expression += `[${index}]`; + } + + return expression; + } + + // used by "optional" functions to return undefined instead of throwing an error + #optional(func) { + try { + return func(); + } catch (ex) { + return undefined; + } + } + + // will not throw an error if property doesn't exist, defaults to empty array + children(propName) { + const nodes = this.#obj[propName] ?? []; + return nodes.map((value, index) => new Data(value, this.#buildExpression(propName, index))); + } + + // throws an error if property (or index on property) doesn't exist + child(propName, index = 0) { + const nodes = this.#obj[propName]; + if (nodes === undefined) { + throw new Error(`Could not find ${this.#buildExpression(propName)}.`); + } + + const node = nodes[index]; + if (node === undefined) { + throw new Error(`Could not find ${this.#buildExpression(propName, index)}.`); + } + + return new Data(node, this.#buildExpression(propName, index)); + } + + // convenience function, since it's very common to want the value of a child + childValue(propName, index = 0) { + return this.child(propName, index).value(); + } + + // throws an error if this object doesn't have a value string + value() { + const value = this.#obj._; + if (value === undefined) { + throw new Error(`Could not get value from ${this.#expression}.`); + } + + return value; + } + + // throws an error if attribute does not exist + attribute(attrName) { + const attribute = this.#obj.$?.[attrName]; + if (attribute === undefined) { + throw new Error(`Could not get attribute ${attrName} from ${this.#expression}.`); + } + + return attribute; + } + + optionalChild(propName, index = 0) { + return this.#optional(() => this.child(propName, index)); + } + + optionalChildValue(propName, index = 0) { + return this.#optional(() => this.childValue(propName, index)); + } + + optionalValue() { + return this.#optional(() => this.value()); + } +} + +export async function load(content) { + const rootData = await xml2js.parseStringPromise(content, { + tagNameProcessors: [xml2js.processors.stripPrefix], + trim: true + }).catch((ex) => { + ex.message = 'Could not parse XML. This likely means your import file is malformed.\n\n' + ex.message; + throw ex; + }); + + const rssData = rootData.rss; + if (rssData === undefined) { + throw new Error('Could not find root node. This likely means your import file is malformed.') + } + + return new Data(rssData, 'rss'); +} diff --git a/src/frontmatter.js b/src/frontmatter.js new file mode 100644 index 0000000..22f37c9 --- /dev/null +++ b/src/frontmatter.js @@ -0,0 +1,63 @@ +export function author(post) { + // not decoded (WordPress doesn't allow funky characters in usernames anyway) + // surprisingly, does not always exist (squarespace exports, for example) + return post.data.optionalChildValue('creator'); +} + +export function categories(post) { + // array of decoded category names, excluding 'uncategorized' + const categories = post.data.children('category'); + return categories + .filter((category) => category.attribute('domain') === 'category' && category.attribute('nicename') !== 'uncategorized') + .map((category) => decodeURIComponent(category.attribute('nicename'))); +} + +export function coverImage(post) { + // cover image filename, previously parsed and decoded + return post.coverImage; +} + +export function date(post) { + // a luxon datetime object, previously parsed + return post.date; +} + +export function draft(post) { + // boolean representing the previously parsed draft status, only included when true + return post.isDraft ? true : undefined; +} + +export function excerpt(post) { + // not decoded, newlines collapsed + // does not always exist (squarespace exports, for example) + const encoded = post.data.optionalChildValue('encoded', 1); + return encoded ? encoded.replace(/[\r\n]+/gm, ' ') : undefined; +} + +export function id(post) { + // previously parsed as a string, converted to integer here + return parseInt(post.id); +} + +export function slug(post) { + // previously parsed and decoded + return post.slug; +} + +export function tags(post) { + // array of decoded tag names (yes, they come from nodes, not a typo) + const categories = post.data.children('category'); + return categories + .filter((category) => category.attribute('domain') === 'post_tag') + .map((category) => decodeURIComponent(category.attribute('nicename'))); +} + +export function title(post) { + // not decoded + return post.data.childValue('title'); +} + +export function type(post) { + // previously parsed but not decoded, can be "post", "page", or other custom types + return post.type; +} diff --git a/src/frontmatter/author.js b/src/frontmatter/author.js deleted file mode 100644 index e6a9c4f..0000000 --- a/src/frontmatter/author.js +++ /dev/null @@ -1,5 +0,0 @@ -// get author, without decoding -// WordPress doesn't allow funky characters in usernames anyway -module.exports = (post) => { - return post.data.creator[0]; -} diff --git a/src/frontmatter/categories.js b/src/frontmatter/categories.js deleted file mode 100644 index 0328e0f..0000000 --- a/src/frontmatter/categories.js +++ /dev/null @@ -1,14 +0,0 @@ -const settings = require('../settings'); - -// get array of decoded category names, filtered as specified in settings -module.exports = (post) => { - if (!post.data.category) { - return []; - } - - const categories = post.data.category - .filter(category => category.$.domain === 'category') - .map(({ $: attributes }) => decodeURIComponent(attributes.nicename)); - - return categories.filter(category => !settings.filter_categories.includes(category)); -}; diff --git a/src/frontmatter/coverImage.js b/src/frontmatter/coverImage.js deleted file mode 100644 index ea63c6b..0000000 --- a/src/frontmatter/coverImage.js +++ /dev/null @@ -1,5 +0,0 @@ -// get cover image filename, previously decoded and set on post.meta -// this one is unique as it relies on special logic executed by the parser -module.exports = (post) => { - return post.meta.coverImage; -}; diff --git a/src/frontmatter/date.js b/src/frontmatter/date.js deleted file mode 100644 index 0cbe1bb..0000000 --- a/src/frontmatter/date.js +++ /dev/null @@ -1,17 +0,0 @@ -const luxon = require('luxon'); - -const settings = require('../settings'); - -// get post date, optionally formatted as specified in settings -// this value is also used for year/month folders, date prefixes, etc. as needed -module.exports = (post) => { - const dateTime = luxon.DateTime.fromRFC2822(post.data.pubDate[0], { zone: settings.custom_date_timezone }); - - if (settings.custom_date_formatting) { - return dateTime.toFormat(settings.custom_date_formatting); - } else if (settings.include_time_with_date) { - return dateTime.toISO(); - } else { - return dateTime.toISODate(); - } -}; diff --git a/src/frontmatter/example.js b/src/frontmatter/example.js deleted file mode 100644 index 2159d87..0000000 --- a/src/frontmatter/example.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - 1. Copy this file, rename to the frontmatter field name you want, camelcased - 2. Edit frontmatter_fields in settings.js to include your new field name - 3. Run the script to see post data dumps, to see what you can work with - 4. Write your code to get and return what you want - 5. Update "get whatever" comment to describe what you're getting - 6. Remove your field name from frontmatter_fields in settings.js - 7. Remove this comment block and the debug console code - 8. Make that pull request! -*/ - -// get whatever -module.exports = (post) => { - console.log('\nBEGIN POST DATA DUMP ===========================================================\n'); - console.dir(post, { depth: null }); - console.log('\nEND POST DATA DUMP =============================================================\n'); - - return 'EXAMPLE: ' + post.data.title[0]; -}; diff --git a/src/frontmatter/excerpt.js b/src/frontmatter/excerpt.js deleted file mode 100644 index bc6310d..0000000 --- a/src/frontmatter/excerpt.js +++ /dev/null @@ -1,4 +0,0 @@ -// get excerpt, not decoded, newlines collapsed -module.exports = (post) => { - return post.data.encoded[1].replace(/[\r\n]+/gm, ' '); -}; diff --git a/src/frontmatter/id.js b/src/frontmatter/id.js deleted file mode 100644 index 5403fc0..0000000 --- a/src/frontmatter/id.js +++ /dev/null @@ -1,4 +0,0 @@ -// get ID -module.exports = (post) => { - return post.data.post_id[0]; -} diff --git a/src/frontmatter/slug.js b/src/frontmatter/slug.js deleted file mode 100644 index 7664b22..0000000 --- a/src/frontmatter/slug.js +++ /dev/null @@ -1,4 +0,0 @@ -// get slug, previously decoded and set on post.meta -module.exports = (post) => { - return post.meta.slug; -}; diff --git a/src/frontmatter/tags.js b/src/frontmatter/tags.js deleted file mode 100644 index 0b53fd8..0000000 --- a/src/frontmatter/tags.js +++ /dev/null @@ -1,12 +0,0 @@ -// get array of decoded tag names -module.exports = (post) => { - if (!post.data.category) { - return []; - } - - const categories = post.data.category - .filter(category => category.$.domain === 'post_tag') - .map(({ $: attributes }) => decodeURIComponent(attributes.nicename)); - - return categories; -}; diff --git a/src/frontmatter/title.js b/src/frontmatter/title.js deleted file mode 100644 index b10bd29..0000000 --- a/src/frontmatter/title.js +++ /dev/null @@ -1,4 +0,0 @@ -// get simple post title, but not decoded like other frontmatter string fields -module.exports = (post) => { - return post.data.title[0]; -}; diff --git a/src/frontmatter/type.js b/src/frontmatter/type.js deleted file mode 100644 index 25bfab4..0000000 --- a/src/frontmatter/type.js +++ /dev/null @@ -1,5 +0,0 @@ -// get type, often this will always be "post" -// but can also be "page" or other custom types -module.exports = (post) => { - return post.data.post_type[0]; -} diff --git a/src/intake.js b/src/intake.js new file mode 100644 index 0000000..beed047 --- /dev/null +++ b/src/intake.js @@ -0,0 +1,165 @@ +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.load(); + const commandLineAnswers = getCommandLineAnswers(commandLineQuestions); + + let wizardAnswers; + if (commandLineAnswers.wizard) { + shared.logHeading('Starting wizard'); + + // run wizard for questions with prompts that were not answered via the command line + const wizardQuestions = questions.load().filter((question) => { + return question.prompt && !(shared.camelCase(question.name) in commandLineAnswers); + }); + wizardAnswers = await getWizardAnswers(wizardQuestions, commandLineAnswers); + } else { + shared.logHeading('Skipping wizard'); + } + + Object.assign(shared.config, 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.description) { + option.hideHelp(); + } + + 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; + } + + const question = questions.find((question) => shared.camelCase(question.name) === key); + if (answers.wizard && question.prompt) { + // remove this default answer, allowing the wizard to ask about it later + delete answers[key]; + } else { + // normalize and validate default answer + 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 = shared.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) { + promptConfig.choices.forEach((choice) => { + // show example path if this choice is selected + choice.description = buildSamplePostPath({ + ...commandLineAnswers, // with command line answers + ...answers, // and wizard answers so far + output: path.sep, // and a simplified output folder + [answerKey]: choice.value // and this choice selected + }); + }); + } + } 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[shared.camelCase(type)]; + if (!normalizer) { + return value; + } + + try { + return normalizer(value); + } catch (ex) { + onError(ex.message); + } +} + +export function buildSamplePostPath(overrideConfig) { + const samplePost = { + date: luxon.DateTime.now(), + slug: 'my-post' + }; + + return shared.buildPostPath(samplePost, overrideConfig); +} diff --git a/src/normalizers.js b/src/normalizers.js new file mode 100644 index 0000000..b6674e0 --- /dev/null +++ b/src/normalizers.js @@ -0,0 +1,49 @@ +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; + } + + throw new Error('File not found at ' + absolute + '.'); +} + +export function list(value) { + if (Array.isArray(value)) { + return value; + } else { + return value.trim().split(/\s*,\s*/); + } +} + +export function integer(value) { + const int = parseInt(value); + if (!Number.isNaN(int) && int >= 0) { + return int; + } + + throw new Error('Must be an integer >= 0.'); +} diff --git a/src/parser.js b/src/parser.js index 5fe5415..d538e3a 100644 --- a/src/parser.js +++ b/src/parser.js @@ -1,32 +1,26 @@ -const fs = require('fs'); -const requireDirectory = require('require-directory'); -const xml2js = require('xml2js'); +import chalk from 'chalk'; +import fs from 'fs'; +import * as luxon from 'luxon'; +import * as data from './data.js'; +import * as frontmatter from './frontmatter.js'; +import * as shared from './shared.js'; +import * as translator from './translator.js'; -const shared = require('./shared'); -const settings = require('./settings'); -const translator = require('./translator'); +export async function parseFilePromise() { + shared.logHeading('Parsing'); + const content = await fs.promises.readFile(shared.config.input, 'utf8'); + const rssData = await data.load(content); + const allPostData = rssData.child('channel').children('item'); -// dynamically requires all frontmatter getters -const frontmatterGetters = requireDirectory(module, './frontmatter', { recurse: false }); - -async function parseFilePromise(config) { - console.log('\nParsing...'); - const content = await fs.promises.readFile(config.input, 'utf8'); - const allData = await xml2js.parseStringPromise(content, { - trim: true, - tagNameProcessors: [xml2js.processors.stripPrefix] - }); - const channelData = allData.rss.channel[0].item; - - const postTypes = getPostTypes(channelData, config); - const posts = collectPosts(channelData, postTypes, config); + const postTypes = getPostTypes(allPostData); + const posts = collectPosts(allPostData, postTypes); const images = []; - if (config.saveAttachedImages) { - images.push(...collectAttachedImages(channelData)); + if (shared.config.saveImages === 'attached' || shared.config.saveImages === 'all') { + images.push(...collectAttachedImages(allPostData)); } - if (config.saveScrapedImages) { - images.push(...collectScrapedImages(channelData, postTypes)); + if (shared.config.saveImages === 'scraped' || shared.config.saveImages === 'all') { + images.push(...collectScrapedImages(allPostData, postTypes)); } mergeImagesIntoPosts(images, posts); @@ -35,110 +29,135 @@ 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(allPostData) { + // search export file for all post types minus some specific types we don't want + const postTypes = [...new Set(allPostData // new Set() is used to dedupe array + .map((postData) => postData.childValue('post_type')) + .filter((postType) => ![ + 'attachment', + 'revision', + 'nav_menu_item', + 'custom_css', + 'customize_changeset', + 'oembed_cache', + 'user_request', + 'wp_block', + 'wp_global_styles', + 'wp_navigation', + 'wp_template', + 'wp_template_part' + ].includes(postType)) + )]; + + // change order to "post", "page", then all custom post types (alphabetically) + prioritizePostType(postTypes, 'page'); + prioritizePostType(postTypes, 'post'); + + return postTypes; } -function getItemsOfType(channelData, type) { - return channelData.filter(item => item.post_type[0] === type); +function getItemsOfType(allPostData, type) { + return allPostData.filter((item) => item.childValue('post_type') === type); } -function collectPosts(channelData, postTypes, config) { - // this is passed into getPostContent() for the markdown conversion - const turndownService = translator.initTurndownService(); - +function collectPosts(allPostData, postTypes) { let allPosts = []; - postTypes.forEach(postType => { - const postsForType = getItemsOfType(channelData, postType) - .filter(postData => postData.status[0] !== 'trash' && postData.status[0] !== 'draft') - .map(postData => ({ - // raw post data, used by frontmatter getters - data: postData, + postTypes.forEach((postType) => { + const postsForType = getItemsOfType(allPostData, postType) + .filter((postData) => postData.childValue('status') !== 'trash') + .filter((postData) => !(postType === 'page' && postData.childValue('post_name') === 'sample-page')) + .map((postData) => buildPost(postData)); - // meta data isn't written to file, but is used to help with other things - meta: { - id: getPostId(postData), - slug: getPostSlug(postData), - coverImageId: getPostCoverImageId(postData), - coverImage: undefined, // possibly set later in mergeImagesIntoPosts() - type: postType, - imageUrls: [] // possibly set later in mergeImagesIntoPosts() - }, - - // contents of the post in markdown - content: translator.getPostContent(postData, turndownService, config) - })); - - if (postTypes.length > 1) { - console.log(`${postsForType.length} "${postType}" posts found.`); + if (postsForType.length > 0) { + if (postType === 'post') { + console.log(`${postsForType.length} normal posts found.`); + } else if (postType === 'page') { + console.log(`${postsForType.length} pages found.`); + } else { + console.log(`${postsForType.length} custom "${postType}" posts found.`); + } } allPosts.push(...postsForType); }); - if (postTypes.length === 1) { - console.log(allPosts.length + ' posts found.'); - } return allPosts; } -function getPostId(postData) { - return postData.post_id[0]; +function buildPost(data) { + return { + // full raw post data + data, + + // body content converted to markdown + content: translator.getPostContent(data.childValue('encoded')), + + // particularly useful values for all sorts of things + type: data.childValue('post_type'), + id: data.childValue('post_id'), + isDraft: data.childValue('status') === 'draft', + slug: decodeURIComponent(data.childValue('post_name')), + date: getPostDate(data), + coverImageId: getPostMetaValue(data, '_thumbnail_id'), + + // these are possibly set later in mergeImagesIntoPosts() + coverImage: undefined, + imageUrls: [] + }; } -function getPostSlug(postData) { - return decodeURIComponent(postData.post_name[0]); +function getPostDate(data) { + const date = luxon.DateTime.fromRFC2822(data.childValue('pubDate'), { zone: shared.config.timezone }); + return date.isValid ? date : undefined; } -function getPostCoverImageId(postData) { - if (postData.postmeta === undefined) { - return undefined; - } - - const postmeta = postData.postmeta.find(postmeta => postmeta.meta_key[0] === '_thumbnail_id'); - const id = postmeta ? postmeta.meta_value[0] : undefined; - return id; +function getPostMetaValue(data, key) { + const metas = data.children('postmeta'); + const meta = metas.find((meta) => meta.childValue('meta_key') === key); + return meta ? meta.childValue('meta_value') : undefined; } -function collectAttachedImages(channelData) { - const images = getItemsOfType(channelData, 'attachment') +function collectAttachedImages(allPostData) { + const images = getItemsOfType(allPostData, 'attachment') // filter to certain image file types - .filter(attachment => attachment.attachment_url && (/\.(gif|jpe?g|png|webp)$/i).test(attachment.attachment_url[0])) - .map(attachment => ({ - id: attachment.post_id[0], - postId: attachment.post_parent[0], - url: attachment.attachment_url[0] + .filter((attachment) => { + const url = attachment.childValue('attachment_url'); + return url && (/\.(gif|jpe?g|png|webp)$/i).test(url); + }) + .map((attachment) => ({ + id: attachment.childValue('post_id'), + postId: attachment.optionalChildValue('post_parent') ?? 'nope', // may not exist (cover image in a squarespace export, for example) + url: attachment.childValue('attachment_url') })); console.log(images.length + ' attached images found.'); return images; } -function collectScrapedImages(channelData, postTypes) { +function collectScrapedImages(allPostData, postTypes) { const images = []; - postTypes.forEach(postType => { - getItemsOfType(channelData, postType).forEach(postData => { - const postId = postData.post_id[0]; - const postContent = postData.encoded[0]; - const postLink = postData.link[0]; + postTypes.forEach((postType) => { + getItemsOfType(allPostData, postType).forEach((postData) => { + const postId = postData.childValue('post_id'); + + const postContent = postData.childValue('encoded'); + const scrapedUrls = [...postContent.matchAll(/]+?(?<=\s)src="(.+?)"[^>]*>/gi)].map((match) => match[1]); + scrapedUrls.forEach((scrapedUrl) => { + let url; + if (isAbsoluteUrl(scrapedUrl)) { + url = scrapedUrl; + } else { + const postLink = postData.childValue('link'); + if (isAbsoluteUrl(postLink)) { + url = new URL(scrapedUrl, postLink).href; + } else { + throw new Error(`Unable to determine absolute URL from scraped image URL '${scrapedUrl}' and post link URL '${postLink}'.`); + } + } - const matches = [...postContent.matchAll(/]*src="(.+?\.(?:gif|jpe?g|png|webp))"[^>]*>/gi)]; - matches.forEach(match => { - // base the matched image URL relative to the post URL - const url = new URL(match[1], postLink).href; images.push({ - id: -1, - postId: postId, + id: 'nope', // scraped images don't have an id + postId, url }); }); @@ -150,43 +169,52 @@ function collectScrapedImages(channelData, postTypes) { } function mergeImagesIntoPosts(images, posts) { - images.forEach(image => { - posts.forEach(post => { + images.forEach((image) => { + posts.forEach((post) => { let shouldAttach = false; // this image was uploaded as an attachment to this post - if (image.postId === post.meta.id) { + if (image.postId === post.id) { shouldAttach = true; } // this image was set as the featured image for this post - if (image.id === post.meta.coverImageId) { + if (image.id === post.coverImageId) { shouldAttach = true; - post.meta.coverImage = shared.getFilenameFromUrl(image.url); + post.coverImage = shared.getFilenameFromUrl(image.url); } - if (shouldAttach && !post.meta.imageUrls.includes(image.url)) { - post.meta.imageUrls.push(image.url); + if (shouldAttach && !post.imageUrls.includes(image.url)) { + post.imageUrls.push(image.url); } }); }); } function populateFrontmatter(posts) { - posts.forEach(post => { - const frontmatter = {}; - settings.frontmatter_fields.forEach(field => { + posts.forEach((post) => { + post.frontmatter = {}; + shared.config.frontmatterFields.forEach((field) => { const [key, alias] = field.split(':'); - let frontmatterGetter = frontmatterGetters[key]; + let frontmatterGetter = frontmatter[key]; if (!frontmatterGetter) { throw `Could not find a frontmatter getter named "${key}".`; } - frontmatter[alias || key] = frontmatterGetter(post); + post.frontmatter[alias ?? key] = frontmatterGetter(post); }); - post.frontmatter = frontmatter; }); } -exports.parseFilePromise = parseFilePromise; +function prioritizePostType(postTypes, postType) { + const index = postTypes.indexOf(postType); + if (index !== -1) { + postTypes.splice(index, 1); + postTypes.unshift(postType); + } +} + +function isAbsoluteUrl(url) { + return (/^https?:\/\//i).test(url); +} diff --git a/src/questions.js b/src/questions.js new file mode 100644 index 0000000..e9bec84 --- /dev/null +++ b/src/questions.js @@ -0,0 +1,158 @@ +import * as inquirer from '@inquirer/prompts'; + +export function load() { + // questions with a description are displayed in command line help + // questions with a prompt are included in the wizard (if not set on the command line) + return [ + { + 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: 'Add date prefix to posts', + default: false, + choices: [ + { + name: 'Yes', + value: true + }, + { + name: 'No', + value: false + } + ], + isPathQuestion: true, + prompt: inquirer.select + }, + { + name: 'date-folders', + type: 'choice', + description: 'Organize posts into date folders', + 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: 'All Images', + value: 'all' + }, + { + name: 'No', + value: 'none' + } + ], + prompt: inquirer.select + }, + { + name: 'wizard', + type: 'boolean', + description: 'Use wizard', + default: true + }, + { + name: 'output', + type: 'folder-path', + description: 'Path to output folder', + default: 'output' + }, + { + name: 'frontmatter-fields', + type: 'list', + description: 'Frontmatter fields', + default: 'title,date,categories,tags,coverImage,draft' + }, + { + name: 'request-delay', + type: 'integer', + description: 'Delay between image file requests', + default: 500 + }, + { + name: 'write-delay', + type: 'integer', + description: 'Delay between writing markdown files', + default: 25 + }, + { + name: 'timezone', + type: 'string', + description: 'Timezone to apply to date', + default: 'utc' + }, + { + name: 'include-time', + type: 'boolean', + description: 'Include time with frontmatter date', + default: false + }, + { + name: 'date-format', + type: 'string', + description: 'Frontmatter date format string', + default: '' + }, + { + name: 'quote-date', + type: 'boolean', + description: 'Wrap frontmatter date in quotes', + default: false + }, + { + name: 'strict-ssl', + type: 'boolean', + description: 'Use strict SSL', + default: true + } + ]; +} diff --git a/src/settings.js b/src/settings.js deleted file mode 100644 index 39e6334..0000000 --- a/src/settings.js +++ /dev/null @@ -1,40 +0,0 @@ -// Which fields to include in frontmatter. Look in /src/frontmatter to see available fields. -// Order is preserved. If a field has an empty value, it will not be included. You can rename a -// field by providing an alias after a ':'. For example, 'date:created' will include 'date' in -// frontmatter, but renamed to 'created'. -exports.frontmatter_fields = [ - 'title', - 'date', - 'categories', - 'tags', - 'coverImage' -]; - -// Time in ms to wait between requesting image files. Increase this if you see timeouts or -// server errors. -exports.image_file_request_delay = 500; - -// Time in ms to wait between saving Markdown files. Increase this if your file system becomes -// overloaded. -exports.markdown_file_write_delay = 25; - -// Enable this to include time with post dates. For example, "2020-12-25" would become -// "2020-12-25T11:20:35.000Z". -exports.include_time_with_date = false; - -// Override post date formatting with a custom formatting string (for example: 'yyyy LLL dd'). -// Tokens are documented here: https://moment.github.io/luxon/#/parsing?id=table-of-tokens. If -// set, this takes precedence over include_time_with_date. -exports.custom_date_formatting = ''; - -// Specify the timezone used for post dates. See available zone values and examples here: -// https://moment.github.io/luxon/#/zones?id=specifying-a-zone. -exports.custom_date_timezone = 'utc'; - -// Categories to be excluded from post frontmatter. This does not filter out posts themselves, -// just the categories listed in their frontmatter. -exports.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. -exports.strict_ssl = true; diff --git a/src/shared.js b/src/shared.js index 786f3ac..c1a13c4 100644 --- a/src/shared.js +++ b/src/shared.js @@ -1,4 +1,77 @@ -function getFilenameFromUrl(url) { +import chalk from 'chalk'; +import path from 'path'; + +// simple data store, populated via intake, used everywhere +export const config = {}; + +export function camelCase(str) { + return str.replace(/-(.)/g, (match) => match[1].toUpperCase()); +} + +export function getSlugWithFallback(post) { + return post.slug ? post.slug : 'id-' + post.id; +} + +export function logHeading(text) { + console.log(`\n${chalk.cyan(text + '...')}`); +} + +export function buildPostPath(post, overrideConfig) { + const pathConfig = overrideConfig ?? config; + + // start with output folder + const pathSegments = [pathConfig.output]; + + // add folder for post type if exists + if (post.type) { + switch (post.type) { + case 'post': + pathSegments.push('posts'); + break; + case 'page': + pathSegments.push('pages'); + break; + default: + pathSegments.push('custom'); + pathSegments.push(post.type); + } + } + + // add drafts folder if this is a draft post + if (post.isDraft) { + pathSegments.push('_drafts'); + } + + // add folders for date year/month as appropriate + if (post.date) { + if (pathConfig.dateFolders === 'year' || pathConfig.dateFolders === 'year-month') { + pathSegments.push(post.date.toFormat('yyyy')); + } + + if (pathConfig.dateFolders === 'year-month') { + pathSegments.push(post.date.toFormat('LL')); + } + } + + // get slug with fallback + let slug = getSlugWithFallback(post); + + // prepend date to slug as appropriate + if (pathConfig.prefixDate && post.date) { + slug = post.date.toFormat('yyyy-LL-dd') + '-' + slug; + } + + // use slug as folder or filename as specified + if (pathConfig.postFolders) { + pathSegments.push(slug, 'index.md'); + } else { + pathSegments.push(slug + '.md'); + } + + return path.join(...pathSegments); +} + +export function getFilenameFromUrl(url) { let filename = url.split('/').slice(-1)[0]; try { filename = decodeURIComponent(filename) @@ -8,5 +81,3 @@ function getFilenameFromUrl(url) { } return filename; } - -exports.getFilenameFromUrl = getFilenameFromUrl; diff --git a/src/translator.js b/src/translator.js index c974599..a775a28 100644 --- a/src/translator.js +++ b/src/translator.js @@ -1,5 +1,9 @@ -const turndown = require('turndown'); -const turndownPluginGfm = require('turndown-plugin-gfm'); +import turndownPluginGfm from '@guyplusplus/turndown-plugin-gfm'; +import turndown from 'turndown'; +import * as shared from './shared.js'; + +// init single reusable turndown service object upon import +const turndownService = initTurndownService(); function initTurndownService() { const turndownService = new turndown({ @@ -10,15 +14,17 @@ function initTurndownService() { turndownService.use(turndownPluginGfm.tables); + turndownService.remove(['style']); //