Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ability to init medmark #20

Merged
merged 5 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .changeset/smooth-pandas-fetch.md
Original file line number Diff line number Diff line change
@@ -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
```
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enable-pre-post-scripts=true
1 change: 1 addition & 0 deletions lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions lib/rollup.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const pkg = require('./package.json');
module.exports = [
{
cache: false,
external: ['fs', 'path'],
input: 'src/index.ts',
output: [
{
Expand All @@ -59,9 +60,8 @@ module.exports = [
typescript({
tsconfig: './tsconfig.lib.json',
}),
// terser(),
terser(),
],
// external: ['fs', 'path'],
},
{
cache: false,
Expand Down
8 changes: 8 additions & 0 deletions lib/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
7 changes: 4 additions & 3 deletions lib/src/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -194,7 +195,7 @@ async function gatherPostData(
postsToSkip: string[],
): Promise<MedmarkTemplateRenderOptions> {
output.note({
bodyLines: [filePath],
bodyLines: [`${chalk.gray('[path]')} ${filePath}`],
title: 'Gathering post data started',
});

Expand All @@ -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',
});
}
Expand Down Expand Up @@ -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',
});

Expand Down
9 changes: 8 additions & 1 deletion lib/src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(),
),
),
},
};

Expand Down
51 changes: 41 additions & 10 deletions lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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',
},
Expand All @@ -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',
},
Expand All @@ -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:',
});
Expand All @@ -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';
138 changes: 138 additions & 0 deletions lib/src/init.ts
Original file line number Diff line number Diff line change
@@ -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;
Loading
Loading