nconv-cli 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,123 @@
1
+ import { input, confirm } from '@inquirer/prompts';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import * as logger from '../utils/logger.js';
6
+
7
+ export interface TokenConfig {
8
+ TOKEN_V2: string;
9
+ FILE_TOKEN: string;
10
+ }
11
+
12
+ function getConfigPath(): string {
13
+ return path.join(os.homedir(), '.nconv', '.env');
14
+ }
15
+
16
+ function getConfigDir(): string {
17
+ return path.join(os.homedir(), '.nconv');
18
+ }
19
+
20
+ /**
21
+ * Load existing config from file
22
+ */
23
+ export function loadConfig(): TokenConfig | null {
24
+ const configPath = getConfigPath();
25
+ if (!fs.existsSync(configPath)) {
26
+ return null;
27
+ }
28
+
29
+ const content = fs.readFileSync(configPath, 'utf-8');
30
+ const config: TokenConfig = { TOKEN_V2: '', FILE_TOKEN: '' };
31
+
32
+ content.split('\n').forEach((line) => {
33
+ const trimmed = line.trim();
34
+ if (trimmed.startsWith('#') || !trimmed) return;
35
+
36
+ const [key, ...valueParts] = trimmed.split('=');
37
+ const value = valueParts.join('=').trim();
38
+
39
+ if (key === 'TOKEN_V2') config.TOKEN_V2 = value;
40
+ if (key === 'FILE_TOKEN') config.FILE_TOKEN = value;
41
+ });
42
+
43
+ return config;
44
+ }
45
+
46
+ /**
47
+ * Save config to file
48
+ */
49
+ export function saveConfig(config: TokenConfig): void {
50
+ const configDir = getConfigDir();
51
+ const configPath = getConfigPath();
52
+
53
+ const envContent = `# Notion Access Tokens
54
+ # These tokens are required to fetch content from Notion.
55
+ # You can find them in your browser's cookies when logged into Notion.
56
+
57
+ TOKEN_V2=${config.TOKEN_V2}
58
+ FILE_TOKEN=${config.FILE_TOKEN}
59
+ `;
60
+
61
+ try {
62
+ if (!fs.existsSync(configDir)) {
63
+ fs.mkdirSync(configDir, { recursive: true });
64
+ }
65
+ fs.writeFileSync(configPath, envContent);
66
+ logger.info('✅ Configuration saved successfully.');
67
+ } catch (error) {
68
+ logger.error('Failed to save configuration.');
69
+ if (error instanceof Error) {
70
+ logger.error(error.message);
71
+ }
72
+ throw error;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Prompt user to input tokens (for /init)
78
+ */
79
+ export async function promptInitConfig(): Promise<TokenConfig> {
80
+ logger.info('Please enter your Notion access tokens.');
81
+ logger.info('You can find these in your browser cookies when logged into Notion.\n');
82
+
83
+ const TOKEN_V2 = await input({
84
+ message: 'TOKEN_V2:',
85
+ required: true,
86
+ validate: (value) => {
87
+ if (!value.trim()) return 'TOKEN_V2 is required';
88
+ return true;
89
+ },
90
+ });
91
+
92
+ const FILE_TOKEN = await input({
93
+ message: 'FILE_TOKEN:',
94
+ required: true,
95
+ validate: (value) => {
96
+ if (!value.trim()) return 'FILE_TOKEN is required';
97
+ return true;
98
+ },
99
+ });
100
+
101
+ return { TOKEN_V2, FILE_TOKEN };
102
+ }
103
+
104
+ /**
105
+ * Prompt user to edit existing config (for /config)
106
+ */
107
+ export async function promptEditConfig(existing: TokenConfig): Promise<TokenConfig> {
108
+ logger.info('Current configuration:\n');
109
+
110
+ const TOKEN_V2 = await input({
111
+ message: 'TOKEN_V2:',
112
+ default: existing.TOKEN_V2,
113
+ required: true,
114
+ });
115
+
116
+ const FILE_TOKEN = await input({
117
+ message: 'FILE_TOKEN:',
118
+ default: existing.FILE_TOKEN,
119
+ required: true,
120
+ });
121
+
122
+ return { TOKEN_V2, FILE_TOKEN };
123
+ }
@@ -2,35 +2,35 @@ import chalk from 'chalk';
2
2
  import ora, { Ora } from 'ora';
3
3
 
4
4
  /**
5
- * 성공 메시지 출력
5
+ * print success log
6
6
  */
7
7
  export function success(message: string): void {
8
8
  console.log(chalk.green('✓'), message);
9
9
  }
10
10
 
11
11
  /**
12
- * 에러 메시지 출력
12
+ * print error log
13
13
  */
14
14
  export function error(message: string): void {
15
15
  console.error(chalk.red('✗'), message);
16
16
  }
17
17
 
18
18
  /**
19
- * 정보 메시지 출력
19
+ * print info log
20
20
  */
21
21
  export function info(message: string): void {
22
22
  console.log(chalk.blue('ℹ'), message);
23
23
  }
24
24
 
25
25
  /**
26
- * 경고 메시지 출력
26
+ * print warn log
27
27
  */
28
28
  export function warn(message: string): void {
29
29
  console.log(chalk.yellow('⚠'), message);
30
30
  }
31
31
 
32
32
  /**
33
- * 스피너 생성
33
+ * print spinner
34
34
  */
35
35
  export function spinner(text: string): Ora {
36
36
  return ora(text).start();
@@ -0,0 +1,54 @@
1
+ import { NotionExporter } from 'notion-exporter';
2
+ import { getNotionConfig, validateConfig } from '../src/config.js';
3
+
4
+ async function testExportTypes() {
5
+ try {
6
+ const validation = validateConfig();
7
+ if (!validation.valid) {
8
+ console.error('❌ Configuration error:', validation.message);
9
+ process.exit(1);
10
+ }
11
+
12
+ const config = getNotionConfig();
13
+ const notionUrl = process.argv[2];
14
+
15
+ if (!notionUrl) {
16
+ console.error('Usage: tsx test-export-types.ts <notion-url>');
17
+ process.exit(1);
18
+ }
19
+
20
+ const exportTypes = ['markdown', 'html', 'pdf', 'PDF'];
21
+
22
+ console.log('🔍 Testing different export types...\n');
23
+
24
+ for (const exportType of exportTypes) {
25
+ try {
26
+ console.log(`Testing exportType: "${exportType}"`);
27
+ const exporter = new NotionExporter(config.tokenV2, config.fileToken, {
28
+ exportType: exportType as any,
29
+ });
30
+
31
+ const taskId = await exporter.getTaskId(notionUrl);
32
+ console.log(` ✅ Task created: ${taskId}`);
33
+
34
+ // Wait a bit for the task to process
35
+ await new Promise(resolve => setTimeout(resolve, 2000));
36
+
37
+ // Try to get the result
38
+ const zip = await exporter.getZip(notionUrl);
39
+ const entries = zip.getEntries().map(e => e.entryName);
40
+ console.log(` 📦 Files in ZIP:`, entries);
41
+ console.log('');
42
+ } catch (error: any) {
43
+ console.log(` ❌ Failed: ${error.message}`);
44
+ console.log('');
45
+ }
46
+ }
47
+
48
+ } catch (error) {
49
+ console.error('❌ Error:', error);
50
+ process.exit(1);
51
+ }
52
+ }
53
+
54
+ testExportTypes();
@@ -0,0 +1,45 @@
1
+ import { NotionMarkdownExporter } from '../src/core/exporter.js';
2
+ import { getNotionConfig, validateConfig } from '../src/config.js';
3
+ import path from 'path';
4
+ import { tmpdir } from 'os';
5
+
6
+ async function testMarkdownExport() {
7
+ try {
8
+ // Validate config first
9
+ const validation = validateConfig();
10
+ if (!validation.valid) {
11
+ console.error('❌ Configuration error:', validation.message);
12
+ process.exit(1);
13
+ }
14
+
15
+ const config = getNotionConfig();
16
+
17
+ // Markdown exporter 생성
18
+ const mdExporter = new NotionMarkdownExporter(config, 'markdown');
19
+
20
+ // 테스트할 Notion URL
21
+ const notionUrl = process.argv[2];
22
+
23
+ if (!notionUrl) {
24
+ console.error('Usage: tsx test-markdown.ts <notion-url>');
25
+ process.exit(1);
26
+ }
27
+
28
+ console.log('🚀 Starting Markdown export...');
29
+ console.log('📄 Notion URL:', notionUrl);
30
+
31
+ const tempDir = path.join(tmpdir(), 'nconv-md-test');
32
+ const result = await mdExporter.exportWithImages(notionUrl, tempDir);
33
+
34
+ console.log('✅ Markdown export successful!');
35
+ console.log('📝 Markdown length:', result.markdown.length, 'characters');
36
+ console.log('🖼️ Images found:', result.imageFiles.length);
37
+ console.log('📁 Temp directory:', tempDir);
38
+
39
+ } catch (error) {
40
+ console.error('❌ Error:', error);
41
+ process.exit(1);
42
+ }
43
+ }
44
+
45
+ testMarkdownExport();
@@ -0,0 +1,44 @@
1
+ import { NotionMarkdownExporter } from '../src/core/exporter.js';
2
+ import { getNotionConfig, validateConfig } from '../src/config.js';
3
+ import path from 'path';
4
+ import { tmpdir } from 'os';
5
+
6
+ async function testPDFExport() {
7
+ try {
8
+ // Validate config first
9
+ const validation = validateConfig();
10
+ if (!validation.valid) {
11
+ console.error('❌ Configuration error:', validation.message);
12
+ process.exit(1);
13
+ }
14
+
15
+ const config = getNotionConfig();
16
+
17
+ // PDF exporter 생성
18
+ const pdfExporter = new NotionMarkdownExporter(config, 'pdf');
19
+
20
+ // 테스트할 Notion URL (여기에 실제 URL을 넣으세요)
21
+ const notionUrl = process.argv[2];
22
+
23
+ if (!notionUrl) {
24
+ console.error('Usage: tsx test-pdf.ts <notion-url>');
25
+ process.exit(1);
26
+ }
27
+
28
+ console.log('🚀 Starting PDF export...');
29
+ console.log('📄 Notion URL:', notionUrl);
30
+
31
+ const tempDir = path.join(tmpdir(), 'nconv-pdf-test');
32
+ const result = await pdfExporter.exportPDF(notionUrl, tempDir);
33
+
34
+ console.log('✅ PDF export successful!');
35
+ console.log('📁 PDF saved to:', result.pdfPath);
36
+ console.log('📝 Filename:', result.filename);
37
+
38
+ } catch (error) {
39
+ console.error('❌ Error:', error);
40
+ process.exit(1);
41
+ }
42
+ }
43
+
44
+ testPDFExport();
package/README.en.md DELETED
@@ -1,200 +0,0 @@
1
- <p align="right">
2
- <a href="./README.md">한국어</a>
3
- </p>
4
-
5
- # nconv-cli (Notion Convertor CLI)
6
-
7
- > A CLI tool that converts Notion pages into blog-ready Markdown
8
- > (with automatic image extraction and path normalization)
9
-
10
- nconv-cli converts public Notion pages into Markdown
11
- and extracts images into local files, organizing everything
12
- in a blog-friendly directory structure.
13
-
14
- ## Features
15
-
16
- - 🚀 Convert public Notion pages into blog-ready Markdown
17
- - 🖼️ Automatically download images and rewrite them as relative paths
18
- - 📁 Output organized by post-level directory structure
19
- - 🎨 Simple and intuitive CLI interface
20
-
21
- ## Problems with Existing Notion → Markdown Workflows
22
-
23
- When moving content written in Notion to a blog,
24
- simple copy & paste or the built-in Markdown export often causes issues.
25
-
26
- - **Copy & Paste**
27
- - Text is converted into Markdown-like syntax
28
- - Images remain linked to Notion CDN URLs
29
- - Image access frequently breaks with `Access Denied` errors
30
-
31
- - **Notion Markdown Export**
32
- - Requires manual download and extraction of exported files
33
- - Difficult to organize content by individual posts
34
-
35
- As a result, publishing to a blog usually involves
36
- manually downloading images, renaming files, fixing paths,
37
- and reorganizing directory structures.
38
-
39
- | Method | Image Handling | Blog Usability |
40
- |------|---------------|----------------|
41
- | Copy & Paste | ❌ Dependent on Notion CDN | ❌ Broken images |
42
- | Notion Export | ⚠️ Manual organization required | ⚠️ Inconvenient |
43
- | **nconv-cli** | ✅ Local image extraction | ✅ Ready to publish |
44
-
45
- ## Recommended For
46
-
47
- - Developers who write in Notion and publish Markdown posts to
48
- Velog, Tistory, GitHub Pages, Hugo, or similar platforms
49
- - Anyone who has experienced broken images due to Notion CDN issues
50
- - Users looking to migrate Notion content into tools like Obsidian
51
-
52
- ## Environment
53
-
54
- - **Node.js**: v20 or higher (npm v10 or higher)
55
- - **TypeScript**: v5 or higher
56
-
57
- ## Installation
58
-
59
- ```bash
60
- # Install dependencies
61
- npm install
62
-
63
- # Build
64
- npm run build
65
-
66
- # Install CLI locally
67
- npm link
68
- ````
69
-
70
- ## Configuration
71
-
72
- Set your Notion authentication tokens in the `.env` file.
73
-
74
- ```bash
75
- # Create .env file
76
- cp .env.example .env
77
- ```
78
-
79
- Edit `.env` and set the following values:
80
-
81
- ```
82
- TOKEN_V2=your_token_v2_here
83
- FILE_TOKEN=your_file_token_here
84
- ```
85
-
86
- ### How to Find Notion Tokens
87
-
88
- 1. Log in to [notion.so](https://notion.so)
89
- 2. Open browser developer tools (F12)
90
- 3. Application > Cookies > notion.so
91
- 4. Copy `token_v2` → paste into `.env` as `TOKEN_V2`
92
- 5. Copy `file_token` → paste into `.env` as `FILE_TOKEN`
93
-
94
- ## Usage
95
-
96
- ### Basic Usage
97
-
98
- ```bash
99
- nconv md <notion-url>
100
- ```
101
-
102
- ### Examples
103
-
104
- ```bash
105
- # Default output (saved to ./output)
106
- nconv md "https://notion.so/My-Page-abc123"
107
-
108
- # Specify output directory
109
- nconv md "https://notion.so/My-Page-abc123" -o ./blog-posts
110
-
111
- # Custom filename
112
- nconv md "https://notion.so/My-Page-abc123" -f "my-article"
113
-
114
- # Verbose logging
115
- nconv md "https://notion.so/My-Page-abc123" -v
116
-
117
- # All options
118
- nconv md "https://notion.so/My-Page-abc123" -o ./blog -i assets -f "article-1" -v
119
- ```
120
-
121
- ## Options
122
-
123
- | Option | Short | Description | Default |
124
- | ------------------- | ----- | ------------------------------------------- | ------------------ |
125
- | `--output <dir>` | `-o` | Output directory | `./output` |
126
- | `--image-dir <dir>` | `-i` | Image directory (relative to output) | `images` |
127
- | `--filename <name>` | `-f` | Output filename (with or without extension) | Extracted from URL |
128
- | `--verbose` | `-v` | Enable verbose logging | `false` |
129
-
130
- ## Output Structure
131
-
132
- ```text
133
- output/
134
- ├── my-article-folder/
135
- │ ├── my-article.md
136
- │ └── images/
137
- │ ├── abc12345.png
138
- │ ├── def67890.jpg
139
- │ └── ...
140
- ```
141
-
142
- Image paths inside the Markdown file are converted to relative paths:
143
-
144
- ```md
145
- ![image](./images/abc12345.png)
146
- ```
147
-
148
- ## Libraries
149
-
150
- ### Main Libraries
151
-
152
- * **notion-exporter**: Export Notion pages to Markdown
153
- * **commander**: CLI command definition and parsing
154
- * **axios**: HTTP client (image downloads)
155
- * **dotenv**: Environment variable management
156
- * **chalk**: Terminal output styling
157
- * **ora**: Terminal spinner for progress display
158
- * **slugify**: Convert strings to URL-friendly slugs
159
- * **uuid**: Generate unique IDs
160
-
161
- ### Open Source Licenses
162
-
163
- * **nconv-cli**: [ISC License](LICENSE)
164
- * This project uses multiple open source libraries,
165
- each distributed under its respective license.
166
-
167
- ## Development
168
-
169
- ```bash
170
- # Development mode (watch files)
171
- npm run dev
172
-
173
- # Build
174
- npm run build
175
-
176
- # Local testing
177
- npm link
178
- nconv md "https://notion.so/test-page"
179
- ```
180
-
181
- ## Project Structure
182
-
183
- ```text
184
- nconv/
185
- ├── src/
186
- │ ├── index.ts
187
- │ ├── config.ts
188
- │ ├── commands/
189
- │ │ └── md.ts
190
- │ ├── core/
191
- │ │ ├── exporter.ts
192
- │ │ └── image-processor.ts
193
- │ └── utils/
194
- │ ├── file.ts
195
- │ └── logger.ts
196
- ├── bin/
197
- │ └── nconv.js
198
- ├── package.json
199
- ├── tsconfig.json
200
- └── tsup.config.ts