diff --git a/.changeset/smooth-pandas-fetch.md b/.changeset/smooth-pandas-fetch.md new file mode 100644 index 0000000..c1b7298 --- /dev/null +++ b/.changeset/smooth-pandas-fetch.md @@ -0,0 +1,21 @@ +--- +'medmark-playground': minor +'medmark': minor +--- + +Add ability to init Medmark by executing `medmark init`. + +```bash +medmark init +``` +This will do the following. + +- Create a `.medmark` folder at the root of the execution with the following folder structure. + +```bash +├── .medmark +│ ├── medium-export # Should extract the medium archive here. +│ │ ├── .gitkeep +│ ├── templates # Should contain the templates for Medmark. +│ │ ├── sample-medmark-template.js +``` diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..6c59086 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +enable-pre-post-scripts=true diff --git a/lib/package.json b/lib/package.json index e7ede71..04ee5d5 100644 --- a/lib/package.json +++ b/lib/package.json @@ -36,6 +36,7 @@ "scripts": { "build": "pnpm clean:dist && rollup -c rollup.config.cjs", "clean:dist": "rimraf dist", + "dev": "pnpm clean:dist && rollup -c rollup.config.cjs --watch --watch.buildDelay 500", "format": "prettier --write \"**/*.{js,jsx,ts,tsx,css,json,md,mdx}\"", "test": "echo \"Error: no test specified\" && exit 0", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", diff --git a/lib/rollup.config.cjs b/lib/rollup.config.cjs index 4c734ae..eb05e1d 100644 --- a/lib/rollup.config.cjs +++ b/lib/rollup.config.cjs @@ -34,6 +34,7 @@ const pkg = require('./package.json'); module.exports = [ { cache: false, + external: ['fs', 'path'], input: 'src/index.ts', output: [ { @@ -59,9 +60,8 @@ module.exports = [ typescript({ tsconfig: './tsconfig.lib.json', }), - // terser(), + terser(), ], - // external: ['fs', 'path'], }, { cache: false, diff --git a/lib/src/constants.ts b/lib/src/constants.ts index 1ede026..593719e 100644 --- a/lib/src/constants.ts +++ b/lib/src/constants.ts @@ -26,3 +26,11 @@ * The delimiter used to split the HTML of embedded tweets */ export const EMBEDDED_TWEET_HTML_SPIT_DELIMITER: string = 'EMBEDDED_TWEET_HTML_SPIT_DELIMITER'; + +export const DEFAULT_MEDMARK_FOLDER_NAME: string = '.medmark'; +export const DEFAULT_MEDMARK_LOGS_FOLDER_NAME: string = 'logs'; +export const DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME: string = 'medium-export'; +export const DEFAULT_MEDIUM_OUTPUT_FOLDER_NAME: string = 'output'; +export const DEFAULT_TEMPLATES_FOLDER_NAME: string = 'templates'; +export const DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME: string = 'sample-medmark-template.js'; +export const MEDIUM_EXPORT_POSTS_FOLDER_NAME: string = 'posts'; diff --git a/lib/src/converter.ts b/lib/src/converter.ts index fc237ee..a65cc6f 100755 --- a/lib/src/converter.ts +++ b/lib/src/converter.ts @@ -28,6 +28,7 @@ import fs, {WriteStream} from 'fs'; import cheerio, {CheerioAPI, Element, Cheerio} from 'cheerio'; import mkdirp from 'mkdirp'; import {join, resolve, basename, extname} from 'path'; +import chalk from 'chalk'; import output from './output'; import {transformHtmlToMarkdown} from './markdown'; import Reporter from './reporter'; @@ -194,7 +195,7 @@ async function gatherPostData( postsToSkip: string[], ): Promise { output.note({ - bodyLines: [filePath], + bodyLines: [`${chalk.gray('[path]')} ${filePath}`], title: 'Gathering post data started', }); @@ -204,7 +205,7 @@ async function gatherPostData( await inlineGists($cheerio, reporter); } catch (e) { output.error({ - bodyLines: [`File Path: ${filePath}`, `Stack Trace: ${e}`], + bodyLines: [`${chalk.gray('[path]')} ${chalk.red(filePath)}`, `${chalk.gray('[stack]')} ${chalk.red(e)}`], title: 'An error occurred while inlining Gists', }); } @@ -373,7 +374,7 @@ async function convertMediumFile( const filename: string = basename(PATHS.file, '.html'); output.note({ - bodyLines: [`PATH: ${PATHS.file}`, `FILE: ${filename}`], + bodyLines: [`${chalk.gray('[path]')} ${PATHS.file}`, `${chalk.gray('[output]')} ${filename}`], title: 'Converting', }); diff --git a/lib/src/debug.ts b/lib/src/debug.ts index 3a7e922..563d9bd 100644 --- a/lib/src/debug.ts +++ b/lib/src/debug.ts @@ -25,6 +25,7 @@ import fs from 'fs-extra'; import {dirname, join, resolve} from 'path'; import ConfigurationService from './configuration-service'; +import {DEFAULT_MEDMARK_FOLDER_NAME, DEFAULT_MEDMARK_LOGS_FOLDER_NAME} from './constants'; /** * Defines the structure of the paths for the logs. @@ -83,7 +84,13 @@ const PATHS: { return resolve(join(PATHS.logs.root, articleId, 'meta.json')); }, }, - root: resolve(join('logs', ConfigurationService.getInstance().getRunnerTimestamp())), + root: resolve( + join( + DEFAULT_MEDMARK_FOLDER_NAME, + DEFAULT_MEDMARK_LOGS_FOLDER_NAME, + ConfigurationService.getInstance().getRunnerTimestamp(), + ), + ), }, }; diff --git a/lib/src/index.ts b/lib/src/index.ts index 44addf3..127d967 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -23,12 +23,24 @@ * SOFTWARE. */ -import {program} from 'commander'; +import {Command} from 'commander'; import inquirer from 'inquirer'; +import path from 'path'; import convert from './converter'; import ConfigurationService from './configuration-service'; import output from './output'; import debug from './debug'; +import init from './init'; +import { + DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME, + DEFAULT_MEDIUM_OUTPUT_FOLDER_NAME, + DEFAULT_MEDMARK_FOLDER_NAME, + DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME, + DEFAULT_TEMPLATES_FOLDER_NAME, + MEDIUM_EXPORT_POSTS_FOLDER_NAME, +} from './constants'; + +const program: Command = new Command(); program .version('0.1.0') @@ -49,18 +61,28 @@ program debug: debugMode, } = await inquirer.prompt([ { - message: 'Enter the path to the `posts` folder of the medium exported archive', + default: path.join( + DEFAULT_MEDMARK_FOLDER_NAME, + DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME, + MEDIUM_EXPORT_POSTS_FOLDER_NAME, + ), + message: 'Enter the path to the `posts` folder of the medium exported archive.', name: 'input', type: 'input', }, { - default: 'output', - message: 'Enter destination folder for output files (default is "./")', + default: path.join(DEFAULT_MEDMARK_FOLDER_NAME, DEFAULT_MEDIUM_OUTPUT_FOLDER_NAME), + message: 'Enter destination folder for output files.', name: 'output', type: 'input', }, { - message: 'Enter the path to the template file', + default: path.join( + DEFAULT_MEDMARK_FOLDER_NAME, + DEFAULT_TEMPLATES_FOLDER_NAME, + DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME, + ), + message: 'Enter the path to the template file.', name: 'template', type: 'input', }, @@ -71,7 +93,7 @@ program type: 'confirm', }, { - message: 'Enter a comma-separated list of files to skip', + message: 'Enter a comma-separated list of files to skip.', name: 'skip', type: 'input', }, @@ -87,11 +109,12 @@ program output.log({ bodyLines: [ - `→ ⬇️ INPUT: ${inputPath}`, - `→ ⬆️ OUTPUT (-o): ${outputPath}`, + `→ ⬇️ INPUT: ${inputPath}`, + `→ ⬆️ OUTPUT (-o): ${outputPath}`, `→ 💅 TEMPLATE (-t): ${templatePath || 'none'}`, `→ ❌ TO SKIP (-s): ${toSkip || 'none'}`, `→ 🚧 SHOULD EXPORT DRAFTS? (-d): ${drafts}`, + `→ 🐞 DEBUG MODE? (-D): ${debugMode}`, ], title: '💡 Following context has been initialized:', }); @@ -104,7 +127,15 @@ program } convert(inputPath, outputPath, templatePath, drafts, toSkip); - }) - .parse(process.argv); + }); + +program + .command('init') + .description('Initialize Medmark') + .action(async () => { + init(); + }); + +program.parse(process.argv); export * from './public-api'; diff --git a/lib/src/init.ts b/lib/src/init.ts new file mode 100644 index 0000000..0072c1c --- /dev/null +++ b/lib/src/init.ts @@ -0,0 +1,138 @@ +/** + * MIT License + * + * Copyright (c) 2023, Brion Mario + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import fs from 'fs'; +import path from 'path'; +import { + DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME, + DEFAULT_MEDMARK_FOLDER_NAME, + DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME, + DEFAULT_TEMPLATES_FOLDER_NAME, +} from './constants'; +import output from './output'; + +function init(): void { + try { + output.addNewline(); + output.log({ + title: '🚀 Initializing Medmark...', + }); + + const medmarkDir: string = path.join(process.cwd(), DEFAULT_MEDMARK_FOLDER_NAME); + const mediumExportDir: string = path.join(medmarkDir, DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME); + const templatesDir: string = path.join(medmarkDir, DEFAULT_TEMPLATES_FOLDER_NAME); + const sampleTemplateFile: string = path.join(templatesDir, DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME); + + // Create the .medmark folder + if (!fs.existsSync(medmarkDir)) { + fs.mkdirSync(medmarkDir); + output.log({bodyLines: [output.check({text: `${DEFAULT_MEDMARK_FOLDER_NAME} folder created.`})]}); + } else { + output.log({bodyLines: [output.skip({text: `${DEFAULT_MEDMARK_FOLDER_NAME} folder already exists.`})]}); + } + + // Create the medium-export folder and .gitkeep file + if (!fs.existsSync(mediumExportDir)) { + fs.mkdirSync(mediumExportDir); + fs.writeFileSync(path.join(mediumExportDir, '.gitkeep'), ''); + output.log({bodyLines: [output.check({text: `${DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME} folder created.`})]}); + } else { + output.log({bodyLines: [output.skip({text: `${DEFAULT_MEDIUM_EXPORTS_FOLDER_NAME} folder already exists.`})]}); + } + + // Create the templates folder + if (!fs.existsSync(templatesDir)) { + fs.mkdirSync(templatesDir); + output.log({bodyLines: [output.check({text: `${DEFAULT_TEMPLATES_FOLDER_NAME} folder created.`})]}); + } else { + output.log({bodyLines: [output.skip({text: `${DEFAULT_TEMPLATES_FOLDER_NAME} folder already exists.`})]}); + } + + // Create the sample-template.js file with content + const sampleTemplateContent: string = `\ +const { frontMatterToYaml } = require('medmark'); + +module.exports = { + getOptions() { + return { + defaultCodeBlockLanguage: 'js', + folderForEachSlug: true, + imagePath: '/resources', + imageStorageStrategy: 'REMOTE', + }; + }, + + render(data) { + const date = new Date(data.published); + const prettyDate = \`\${date.getFullYear()}-\${(date.getMonth() + 1).toString().padStart(2, 0)}-\${date.getDate().toString().padStart(2, 0)}\`; + + const frontMatterAsJSON = { + slug: \`/posts/\${data.titleForSlug}/\`, + date: prettyDate, + title: data.title, + description: data.description, + authors: data.authors, + readingTime: data.readingTime, + draft: data.draft, + categories: data.categories, + tags: data.tags, + bannerImage: data.images.map(image => image.mediumUrl)[0], + ogImage: data.images.map(image => image.mediumUrl)[0], + images: data.images.map(image => image.mediumUrl), + }; + + const frontMatter = \`\\ +--- +\${frontMatterToYaml(frontMatterAsJSON)} +--- + +\${data.body} +\`; + + return frontMatter; + }, +}; +`; + + if (!fs.existsSync(sampleTemplateFile)) { + fs.writeFileSync(sampleTemplateFile, sampleTemplateContent); + output.log({bodyLines: [output.check({text: `${DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME} file created.`})]}); + } else { + output.log({ + bodyLines: [output.skip({text: `${DEFAULT_MEDMARK_TEMPLATE_SAMPLE_FILENAME} file already exists.`})], + }); + } + + output.success({ + title: '🎉 Initialization completed successfully.', + }); + } catch (error) { + output.error({ + bodyLines: [error.message.toString()], + title: '❌ Initialization failed.', + }); + } +} + +export default init; diff --git a/lib/src/output.ts b/lib/src/output.ts index d5a5e21..2700efb 100644 --- a/lib/src/output.ts +++ b/lib/src/output.ts @@ -77,6 +77,17 @@ export interface CLISuccessMessageConfig { title: string; } +export interface CLILogMessageConfig { + /** + * An array of strings representing the body of the log message. + */ + bodyLines?: string[] | void[]; + /** + * The title of the log message. + */ + title?: string; +} + class CLIOutput { readonly X_PADDING: string = ' '; @@ -148,12 +159,12 @@ class CLIOutput { * @param body - The body to write to the response. If `null` or `undefined`, no body will be written. * @returns A Promise that resolves once the body has been written to the response. */ - private writeOptionalOutputBody(bodyLines?: string[]): void { + private writeOptionalOutputBody(bodyLines?: string[] | void[]): void { if (!bodyLines) { return; } - this.addNewline(); - bodyLines.forEach((bodyLine: string) => this.writeToStdOut(` ${bodyLine}${EOL}`)); + + bodyLines.forEach((bodyLine: string | void) => this.writeToStdOut(` ${bodyLine}${EOL}`)); } /** @@ -318,18 +329,32 @@ class CLIOutput { * @param config.bodyLines - The body lines of the log message. Optional. * @param config.color - The color of the log message. Optional. */ - log({title, bodyLines, color}: CLIWarnMessageConfig & {color?: string}): void { - this.addNewline(); + log({title, bodyLines, color}: CLILogMessageConfig & {color?: string}): void { + if (title) { + this.addNewline(); - this.writeOutputTitle({ - color: 'cyan', - title: color ? (chalk as any)[color](title) : title, - }); + this.writeOutputTitle({ + color: 'cyan', + title: color ? (chalk as any)[color](title) : title, + }); + + this.addNewline(); + } this.writeOptionalOutputBody(bodyLines); this.addNewline(); } + + // eslint-disable-next-line class-methods-use-this + check({text, color}: {color?: string; text: string}): string { + return `${chalk.green('✔')} ${color ? (chalk as any)[color](text) : text}`; + } + + // eslint-disable-next-line class-methods-use-this + skip({text, color}: {color?: string; text: string}): string { + return `${chalk.yellow('🚸')} ${color ? (chalk as any)[color](text) : text}`; + } } export default new CLIOutput(); diff --git a/lib/src/public-api.ts b/lib/src/public-api.ts index 20e3e5b..4b5bb9f 100644 --- a/lib/src/public-api.ts +++ b/lib/src/public-api.ts @@ -23,3 +23,6 @@ */ export {frontMatterToYaml} from './utils'; +export {default as chalk} from 'chalk'; +export * from './constants'; +export {default as logger} from './output'; diff --git a/package.json b/package.json index 498b12b..d3e4aef 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "author": "Brion Mario", "repository": { "type": "git", - "url": "https://github.com/brionmario/markdown" + "url": "https://github.com/brionmario/medmark" }, "scripts": { "build": "turbo run build", diff --git a/playground/.eslintrc.cjs b/playground/.eslintrc.cjs index b93f9b6..df02b17 100644 --- a/playground/.eslintrc.cjs +++ b/playground/.eslintrc.cjs @@ -46,6 +46,6 @@ module.exports = { project: [path.resolve(__dirname, 'tsconfig.eslint.json')], }, rules: { - 'import/prefer-default-export': 'off', + 'import/no-extraneous-dependencies': ['error', {devDependencies: ['**/*.config.*cjs', '**/scripts/*.js']}], }, }; diff --git a/playground/.gitignore b/playground/.gitignore index 4f4dd6e..407943b 100755 --- a/playground/.gitignore +++ b/playground/.gitignore @@ -42,5 +42,6 @@ yarn-error.log* next-env.d.ts # Medmark -!.medmark/templates +# TODO: Modify these as necessary if you are planning to use the playground for an actual project. .medmark +pages/blogs diff --git a/playground/.medmark/templates/playground.js b/playground/.medmark/templates/playground.js deleted file mode 100755 index 00905a2..0000000 --- a/playground/.medmark/templates/playground.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2022, Brion Mario. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -const {frontMatterToYaml} = require('medmark'); - -module.exports = { - /** - * Returns an object with default options for rendering markdown. - * @returns {Object} Object containing default options. - */ - getOptions() { - return { - defaultCodeBlockLanguage: 'js', // Set default language for code blocks. - folderForEachSlug: true, // Create a separate folder for each blog post. - imagePath: '/resources', // Path for images referenced in markdown files. - imageStorageStrategy: 'REMOTE', // Where to store images. - }; - }, - - /** - * Takes a data object and returns a string of front matter and markdown body. - * @param {Object} data Data object containing blog post information. - * @returns {string} String containing front matter and markdown. - */ - render(data) { - // Convert published date to YYYY-MM-DD format. - const date = new Date(data.published); - const prettyDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, 0)}-${date - .getDate() - .toString() - .padStart(2, 0)}`; - - /* eslint-disable sort-keys */ - const frontMatterAsJSON = { - slug: `/posts/${data.titleForSlug}/`, - date: prettyDate, - title: data.title, - description: data.description, - authors: data.authors, - readingTime: data.readingTime, - draft: data.draft, - categories: data.categories, - tags: data.tags, - bannerImage: data.images.map(image => image.mediumUrl)[0], - ogImage: data.images.map(image => image.mediumUrl)[0], - images: data.images.map(image => image.mediumUrl), - }; - /* eslint-enable sort-keys */ - - const frontMatter = `\ ---- -${frontMatterToYaml(frontMatterAsJSON)} ---- - -${data.body} -`; - - return frontMatter; - }, -}; diff --git a/playground/next.config.mjs b/playground/next.config.mjs index e1c9cbb..4a72398 100644 --- a/playground/next.config.mjs +++ b/playground/next.config.mjs @@ -1,13 +1,37 @@ -import nextra from 'nextra' +/** + * MIT License + * + * Copyright (c) 2023, Brion Mario + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import nextra from 'nextra'; const withNextra = nextra({ theme: 'nextra-theme-blog', themeConfig: './theme.config.jsx', staticImage: true, defaultShowCopyCode: true, - readingTime: true -}) + readingTime: true, +}); export default withNextra({ - reactStrictMode: true -}) + reactStrictMode: true, +}); diff --git a/playground/package.json b/playground/package.json index 4aaaa74..3e3c30d 100644 --- a/playground/package.json +++ b/playground/package.json @@ -19,12 +19,14 @@ "medmark-sample" ], "scripts": { - "dev": "next", "build": "next build", - "lint": "next lint", - "start": "next start", + "clean": "rimraf .next .turbo", "debug": "NODE_OPTIONS='--inspect' next dev", - "clean": "rimraf .next .turbo" + "predev": "node scripts/pre-dev.js", + "dev": "next", + "lint": "next lint", + "medmark": "medmark", + "start": "next start" }, "dependencies": { "next": "^13.4.3", @@ -38,6 +40,7 @@ "@brionmario/prettier-config": "^0.1.0", "eslint": "^8.30.0", "eslint-plugin-mdx": "^2.0.5", + "fs-extra": "^11.1.0", "medmark": "workspace:*", "prettier": "^2.8.1" } diff --git a/playground/pages/blog/.gitkeep b/playground/pages/blog/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/playground/scripts/pre-dev.js b/playground/scripts/pre-dev.js new file mode 100644 index 0000000..6274f35 --- /dev/null +++ b/playground/scripts/pre-dev.js @@ -0,0 +1,64 @@ +#!/usr/bin/env node +/** + * MIT License + * + * Copyright (c) 2023, Brion Mario + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const path = require('path'); +const fs = require('fs-extra'); + +const logger = { + // eslint-disable-next-line no-console + log: console.log, + // eslint-disable-next-line no-console + error: console.error, +}; + +/* ====================================================================================== */ +/* Execution starts from here */ +/* ====================================================================================== */ + +logger.log('⚠️ Checking for Medmark Posts'); + +if (!fs.existsSync(path.resolve(__dirname, path.join('..', '.medmark', 'output')))) { + logger.log('No Medmark posts directory found (.medmark/output)'); + logger.log( + 'Run `pnpm medmark init` to initialize Medmark and then copy the posts from the medium archive to .medmark/medium-output', + ); + process.exit(1); +} + +logger.log('> Started copying Medmark Posts'); + +try { + fs.copySync( + path.resolve(__dirname, path.join('..', '.medmark', 'output')), + path.resolve(__dirname, path.join('..', 'pages', 'blogs')), + {overwrite: true}, + ); + + logger.log('✅ Successfully copied Medmark Posts'); +} catch (e) { + logger.error('❌ Failed to copy Medmark Posts'); + logger.error(e); + process.exit(1); +} diff --git a/playground/site.config.js b/playground/site.config.js index ff8b4c5..8ea2a4d 100644 --- a/playground/site.config.js +++ b/playground/site.config.js @@ -1 +1,25 @@ +/** + * MIT License + * + * Copyright (c) 2023, Brion Mario + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + export default {}; diff --git a/playground/theme.config.jsx b/playground/theme.config.jsx index a7980c2..6a946f8 100644 --- a/playground/theme.config.jsx +++ b/playground/theme.config.jsx @@ -1,6 +1,29 @@ +/** + * MIT License + * + * Copyright (c) 2023, Brion Mario + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + import Footer from './components/Footer'; -/* eslint sort-keys: error */ export default { components: { h1: ({children}) => ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5afacc..54f6c61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -163,6 +163,9 @@ importers: eslint-plugin-mdx: specifier: ^2.0.5 version: 2.1.0(eslint@8.30.0) + fs-extra: + specifier: ^11.1.0 + version: 11.1.0 medmark: specifier: workspace:* version: link:../lib @@ -3294,7 +3297,6 @@ packages: graceful-fs: 4.2.10 jsonfile: 6.1.0 universalify: 2.0.0 - dev: false /fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} @@ -4222,7 +4224,6 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.10 - dev: false /jsprim@1.4.2: resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} @@ -7125,7 +7126,6 @@ packages: /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - dev: false /untildify@4.0.0: resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}