@osmn-byhn/css-formatter-cli 1.0.0

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/package.json +46 -0
  4. package/src/cli.js +96 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Osman Beyhan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # CSS Formatter CLI
2
+
3
+ > **A powerful CLI for bidirectional CSS-HTML conversion, powered by [@osmn-byhn/css-formatter](https://www.npmjs.com/package/@osmn-byhn/css-formatter).**
4
+
5
+ This tool allows you to convert between external/internal CSS and inline styles effortlessly directly from your terminal. It's perfect for email templates, production optimization, and modernizing legacy HTML.
6
+
7
+ ## 🚀 Key Features
8
+
9
+ - **Bidirectional Conversion**: Move styles from `<style>` tags to inline attributes and back.
10
+ - **Smart Selector Generation**: Automatically generates clean, semantic CSS selectors based on existing classes and structure.
11
+ - **Preserves Critical CSS**: Keeps `@media` queries, `:hover` states, and `@font-face` rules in a `<style>` tag while inlining the rest.
12
+ - **Style Deduplication**: Groups identical inline styles under single CSS rules to keep your output clean.
13
+
14
+ ## 📦 Installation
15
+
16
+ You can install it globally via npm:
17
+
18
+ ```bash
19
+ npm install -g @osmn-byhn/css-formatter-cli
20
+ ```
21
+
22
+ Or run it instantly using npx:
23
+
24
+ ```bash
25
+ npx css-formatter <command> [options]
26
+ ```
27
+
28
+ ## 🎯 Usage & Commands
29
+
30
+ ### 1. Inlining CSS (`inline`)
31
+ Converts all CSS from `<style>` tags and linked stylesheets into inline `style` attributes.
32
+
33
+ ```bash
34
+ css-formatter inline input.html -o output.html
35
+ ```
36
+ - **Use Case**: Preparing HTML for email clients that only support inline styles.
37
+
38
+ ---
39
+
40
+ ### 2. Extracting to Internal CSS (`internal`)
41
+ Extracts all inline `style` attributes and moves them into a single `<style>` tag in the `<head>`.
42
+
43
+ ```bash
44
+ css-formatter internal input.html -o output.html
45
+ ```
46
+ - **Use Case**: Refactoring messy inline styles into a readable, centralized format.
47
+
48
+ ---
49
+
50
+ ### 3. Extracting to External CSS (`extract`)
51
+ Extracts inline styles into a separate `.css` file and adds a `<link>` tag to the HTML.
52
+
53
+ ```bash
54
+ css-formatter extract input.html styles.css -o output.html
55
+ ```
56
+ - **Use Case**: Optimizing production websites for browser caching by separating CSS from HTML.
57
+
58
+ ## 🔧 Options
59
+
60
+ | Argument | Description |
61
+ | :--- | :--- |
62
+ | `<input>` | Path to your input HTML file. |
63
+ | `[cssOutput]` | (Extract only) Name of the generated CSS file. |
64
+ | `-o, --output` | Custom path for the output HTML (defaults to overwriting input). |
65
+ | `-v, --version` | Display version information. |
66
+ | `-h, --help` | Display help for commands. |
67
+
68
+ ## 📄 License
69
+
70
+ MIT © Osman Beyhan
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@osmn-byhn/css-formatter-cli",
3
+ "version": "1.0.0",
4
+ "description": "Command line interface for @osmn-byhn/css-formatter to inline or extract CSS from HTML",
5
+ "type": "module",
6
+ "bin": {
7
+ "css-formatter": "src/cli.js"
8
+ },
9
+ "files": [
10
+ "src",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "keywords": [
15
+ "css",
16
+ "formatter",
17
+ "cli",
18
+ "inline",
19
+ "extract",
20
+ "html",
21
+ "email-templates",
22
+ "web-performance"
23
+ ],
24
+ "author": "Osman Beyhan",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/osmn-byhn/css-formatter-cli.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/osmn-byhn/css-formatter-cli/issues"
32
+ },
33
+ "homepage": "https://css-formatter-cli.osmanbeyhan.com",
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "dependencies": {
38
+ "@osmn-byhn/css-formatter": "^1.0.1",
39
+ "chalk": "^5.6.2",
40
+ "commander": "^14.0.3"
41
+ },
42
+ "scripts": {
43
+ "test": "echo \"Error: no test specified\" && exit 1",
44
+ "release": "pnpm publish --access public --no-git-checks"
45
+ }
46
+ }
package/src/cli.js ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs/promises';
6
+ import path from 'path';
7
+ import { inlineCSS, reverseCSSInternal, reverseCSSExternal } from '@osmn-byhn/css-formatter';
8
+
9
+ const program = new Command();
10
+
11
+ program
12
+ .name('css-formatter')
13
+ .description('CLI tool for inlining and extracting CSS using @osmn-byhn/css-formatter')
14
+ .version('1.0.0');
15
+
16
+ program
17
+ .command('inline')
18
+ .description('Inlines CSS into the HTML file')
19
+ .argument('<input>', 'Input HTML file path')
20
+ .option('-o, --output <path>', 'Output file path (defaults to overwriting input)')
21
+ .action(async (input, options) => {
22
+ try {
23
+ const inputPath = path.resolve(input);
24
+ const html = await fs.readFile(inputPath, 'utf-8');
25
+
26
+ console.log(chalk.blue(`🚀 Inlining CSS in ${input}...`));
27
+ const result = await inlineCSS(html);
28
+
29
+ const outputPath = options.output ? path.resolve(options.output) : inputPath;
30
+ await fs.writeFile(outputPath, result);
31
+
32
+ console.log(chalk.green(`✅ Successfully inlined CSS. Saved to: ${outputPath}`));
33
+ } catch (error) {
34
+ console.error(chalk.red(`❌ Error: ${error.message}`));
35
+ process.exit(1);
36
+ }
37
+ });
38
+
39
+ program
40
+ .command('internal')
41
+ .description('Extracts inline styles into a <style> tag')
42
+ .argument('<input>', 'Input HTML file path')
43
+ .option('-o, --output <path>', 'Output file path (defaults to overwriting input)')
44
+ .action(async (input, options) => {
45
+ try {
46
+ const inputPath = path.resolve(input);
47
+ const html = await fs.readFile(inputPath, 'utf-8');
48
+
49
+ console.log(chalk.blue(`🚀 Extracting inline styles to internal <style> tag in ${input}...`));
50
+ const result = await reverseCSSInternal(html);
51
+
52
+ const outputPath = options.output ? path.resolve(options.output) : inputPath;
53
+ await fs.writeFile(outputPath, result);
54
+
55
+ console.log(chalk.green(`✅ Successfully extracted styles to internal <style>. Saved to: ${outputPath}`));
56
+ } catch (error) {
57
+ console.error(chalk.red(`❌ Error: ${error.message}`));
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+ program
63
+ .command('extract')
64
+ .description('Extracts inline styles into an external CSS file')
65
+ .argument('<input>', 'Input HTML file path')
66
+ .argument('[cssOutput]', 'Output CSS file path (defaults to styles.css)')
67
+ .option('-o, --output <path>', 'Output HTML file path (defaults to overwriting input)')
68
+ .action(async (input, cssOutput, options) => {
69
+ try {
70
+ const inputPath = path.resolve(input);
71
+ const html = await fs.readFile(inputPath, 'utf-8');
72
+
73
+ const cssFileName = cssOutput || 'styles.css';
74
+ const cssPath = path.resolve(path.dirname(inputPath), cssFileName);
75
+
76
+ console.log(chalk.blue(`🚀 Extracting inline styles from ${input} to ${cssFileName}...`));
77
+ const { html: htmlOutput, css: cssResult } = await reverseCSSExternal(html);
78
+
79
+ // The library's reverseCSSExternal might be putting a default link tag.
80
+ // We should check if we need to adjust the link tag href if the user provided a specific path.
81
+
82
+ const outputPath = options.output ? path.resolve(options.output) : inputPath;
83
+
84
+ await fs.writeFile(outputPath, htmlOutput);
85
+ await fs.writeFile(cssPath, cssResult);
86
+
87
+ console.log(chalk.green(`✅ Successfully extracted styles.`));
88
+ console.log(chalk.green(` HTML saved to: ${outputPath}`));
89
+ console.log(chalk.green(` CSS saved to: ${cssPath}`));
90
+ } catch (error) {
91
+ console.error(chalk.red(`❌ Error: ${error.message}`));
92
+ process.exit(1);
93
+ }
94
+ });
95
+
96
+ program.parse();