windclutter 0.0.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7228d312e25ff4308cb7e6f753a71d25452c9c1cfde852bbab75f35853f3ff98
4
+ data.tar.gz: 580a419ed1d91abc97831fc0836e7547c5b1544c6563f5ee4a454181778d6bc6
5
+ SHA512:
6
+ metadata.gz: 5a54ffa345fbfb0b234126b9e1f304ebdea473e84087ed387c2c555b87549abcdebcc510b83040bccb45f9de28c1f65325bb2984345cbd9b4d1cfe50fa583a3a
7
+ data.tar.gz: 2b6ea373ca2273fed179ded3e24ee3553b3fd0cb465d7dff8d3b0968b9b5ab73551180c613c70b04b717f3f03c332486115286f6dc072e022a100ff05a9082d9
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ ![Test Badge](https://github.com/Xavier-iV/windclutter/actions/workflows/main.yml/badge.svg)
2
+
3
+ ## WindClutter
4
+
5
+ You created awesome project, it's completed, your users are happy. But now you are left with this question:
6
+
7
+ > What am I going to do with this TailwindCSS class clutter?
8
+
9
+ You know what I'm talking about.
10
+
11
+ This tool aim to:
12
+
13
+ - Provide analysis of your project
14
+ - Identify common uses of Tailwind class
15
+ - Cleanup for large ejected project
16
+
17
+ Once your project grows and ready to for your clients, chances are you are left with
18
+ humongous task of Tailwind CSS cleanup. I call this "ejected" states.
19
+
20
+ ## In Action
21
+
22
+ ```html
23
+ <div class="my-class border rounded-md px-4 py-2 bg-primary-100">
24
+ <!-- -->
25
+ </div>
26
+ ```
27
+
28
+ What if we can help you, if not a bit?
29
+
30
+ ```html
31
+ <div class="my-class adkuioqa">
32
+ <!-- -->
33
+ </div>
34
+ ```
35
+
36
+ We'll cleanup your html files and organize the CSSes. You can then choose what to name them.
37
+
38
+ `adkuioqa = .btn`
39
+ ```
40
+ <div class="my-class btn">
41
+ <!-- -->
42
+ </div>
43
+ ```
44
+
45
+ ## Concept
46
+
47
+ This is currently in ideation, but me myself are eagerly waiting to try this myself.
48
+
49
+ I have a lot of codebases that needs cleanup! 🤯
50
+
51
+ ## Contributing
52
+
53
+ Take a look into:
54
+ https://github.com/Xavier-IV/windclutter/wiki/Developer
55
+
56
+
57
+ ## Great alternative
58
+
59
+ There are some limited alternative that I'm aware of and are still searching:
60
+
61
+ - [Windi CSS Analyzer](https://windicss.org/features/analyzer.html) (Sunsetting)
62
+ - [Tailwind CSS Analysis](https://github.com/apvarun/tailwindcss-analysis)
data/bin/windclutter ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'dry/cli'
5
+
6
+ require 'windclutter/cli/core'
7
+
8
+ Dry::CLI.new(WindClutter::CLI::Commands).call
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WindClutter
4
+ # Analyser for windclutter
5
+ class Analyser
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require 'windclutter/util/file_handler'
5
+ require 'windclutter/util/config'
6
+ require 'windclutter/util/generator'
7
+ require 'windclutter/processor'
8
+
9
+ module WindClutter
10
+ module CLI
11
+ module Commands
12
+ module Generate
13
+ # Initiate setup for specified project
14
+ class OnFly < Dry::CLI::Command
15
+ include WindClutter::Util
16
+
17
+ desc 'Perform inline generation of class'
18
+
19
+ def call(**opt)
20
+ puts
21
+ name = Generator.random_class
22
+
23
+ classes = WindClutter::Processor.sort(opt[:args])
24
+ puts WindClutter::Processor.build_single(name, classes)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require 'windclutter/util/file_handler'
5
+ require 'windclutter/util/config'
6
+
7
+ module WindClutter
8
+ module CLI
9
+ module Commands
10
+ module Project
11
+ # Initiate setup for specified project
12
+ class Init < Dry::CLI::Command
13
+ include WindClutter::Util
14
+
15
+ desc 'Setup windclutter for your project'
16
+
17
+ argument :name, required: true, desc: 'Name of your project.'
18
+
19
+ def call(name:, **)
20
+ FileHandler.create_project(name)
21
+ puts 'Successfully created!'.green
22
+ end
23
+ end
24
+
25
+ # List all of created project for windclutter
26
+ class List < Dry::CLI::Command
27
+ include WindClutter::Util
28
+
29
+ desc 'List all of created windclutter project.'
30
+
31
+ def call(**)
32
+ puts FileHandler.list_projects
33
+ end
34
+ end
35
+
36
+ # Use specified project
37
+ class Use < Dry::CLI::Command
38
+ include WindClutter::Util
39
+
40
+ desc 'Use the project, automatically create one if not exists yet.'
41
+
42
+ argument :name, required: true, desc: 'Name of your project.'
43
+
44
+ def call(name:, **)
45
+ return Config.read('active_project') if name.nil?
46
+
47
+ unless FileHandler.list_projects.include? name
48
+ FileHandler.create_project(name)
49
+ puts 'No project, we created one instead'.green
50
+ end
51
+
52
+ Config.update('active_project', name)
53
+ puts "Using project \"#{name}\"".green
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require 'windclutter/util/file_handler'
5
+ require 'windclutter/cli/commands/project'
6
+ require 'windclutter/cli/commands/generate'
7
+
8
+ module WindClutter
9
+ module CLI
10
+ # Command module to be use by CLI
11
+ module Commands
12
+ extend Dry::CLI::Registry
13
+
14
+ # Print out the version of windclutter
15
+ class Version < Dry::CLI::Command
16
+ desc 'Print version'
17
+
18
+ def call(*)
19
+ puts '0.0.1'
20
+ end
21
+ end
22
+
23
+ # Perform initial setup for windclutter
24
+ class Install < Dry::CLI::Command
25
+ include WindClutter::Util
26
+
27
+ desc 'Initiate first setup for tail_draft'
28
+
29
+ def call(*)
30
+ FileHandler.init_config
31
+ end
32
+ end
33
+
34
+ # Uninstallation handler
35
+ class Uninstall < Dry::CLI::Command
36
+ include WindClutter::Util
37
+
38
+ desc 'Remove all windclutter setup and configurations'
39
+
40
+ def call(*)
41
+ FileHandler.uninstall
42
+ end
43
+ end
44
+
45
+ register 'version', Version, aliases: %w[v -v --version]
46
+ register 'install', Install, aliases: %w[i -i --install]
47
+ register 'uninstall', Uninstall, aliases: %w[u -u --uninstall]
48
+
49
+ register 'project', aliases: ['p'] do |prefix|
50
+ prefix.register 'init', Commands::Project::Init
51
+ prefix.register 'list', Commands::Project::List
52
+ prefix.register 'use', Commands::Project::Use
53
+ end
54
+
55
+ register 'generate', aliases: ['g'] do |prefix|
56
+ prefix.register 'on-fly', Commands::Generate::OnFly
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'windclutter/util/generator'
4
+
5
+ module WindClutter
6
+ # Processor for windclutter
7
+ class Processor
8
+ include WindClutter::Util
9
+
10
+ def initialize
11
+ @key_tag = :windclutter
12
+ end
13
+
14
+ def self.build_single(name, classes)
15
+ <<~OUTPUT
16
+ .#{name} {
17
+ @apply #{classes.join(' ')};
18
+ }
19
+ OUTPUT
20
+ end
21
+
22
+ def self.auto_process(file_content, collections)
23
+ return if file_content.nil?
24
+
25
+ regex = /class="(?:(?!#{@key_tag}:)[^"])*"/
26
+ class_occurrences = file_content.scan(regex)
27
+
28
+ class_occurrences.each do |occurrence|
29
+ gen_class = Generator.random_class
30
+ file_content = file_content.gsub(/#{occurrence}/, "class=\"#{gen_class}\"")
31
+
32
+ collections.push({
33
+ generated_name: gen_class,
34
+ provided_name: nil,
35
+ class: occurrence.to_s.match(/class="([^"]*)"/)[1],
36
+ named: false
37
+ })
38
+ end
39
+ file_content
40
+ end
41
+
42
+ def self.sort(classes)
43
+ classes.sort
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'colorize'
5
+ require 'yaml'
6
+
7
+ module WindClutter
8
+ module Util
9
+ # Config handler
10
+ class Config
11
+ def self.update(key, value)
12
+ return unless config_exists?
13
+
14
+ config = YAML.load_file('/tmp/windclutter/config.yml')
15
+ config[key] = value
16
+
17
+ File.open('/tmp/windclutter/config.yml', 'w') { |f| YAML.dump(config, f) }
18
+ end
19
+
20
+ def self.read(key)
21
+ return unless config_exists?
22
+
23
+ config = YAML.load_file('/tmp/windclutter/config.yml')
24
+ config[key]
25
+ end
26
+
27
+ def self.config_exists?
28
+ unless File.file?('/tmp/windclutter/config.yml')
29
+ puts 'You have not install windclutter yet'.yellow
30
+ puts 'To install, run:'
31
+ puts "\twindclutter install".green
32
+ return false
33
+ end
34
+ true
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'colorize'
5
+ require 'yaml'
6
+
7
+ module WindClutter
8
+ module Util
9
+ # Generator helper
10
+ class FileHandler
11
+ def self.init_config
12
+ FileUtils.mkdir_p('/tmp/windclutter')
13
+
14
+ return puts 'Setup already performed!'.yellow if File.file?('/tmp/windclutter/config.yml')
15
+
16
+ template = File.expand_path('../../template', File.dirname(__FILE__))
17
+
18
+ puts 'Created the following files:'
19
+ Dir["#{template}/*.yml"].each do |t|
20
+ FileUtils.cp(t, '/tmp/windclutter')
21
+ puts "\t#{t}"
22
+ end
23
+
24
+ puts "\nSetup completed!".green
25
+ end
26
+
27
+ def self.uninstall
28
+ Dir['/tmp/windclutter'].each do |t|
29
+ FileUtils.rm_rf(t)
30
+ end
31
+
32
+ puts 'Uninstall completed!'.green
33
+ end
34
+
35
+ def self.create_project(value)
36
+ FileUtils.mkdir_p("/tmp/windclutter/projects/#{value}")
37
+ end
38
+
39
+ def self.list_projects
40
+ Dir['/tmp/windclutter/projects/*'].map do |t|
41
+ t.split('/tmp/windclutter/projects/')[1]
42
+ end
43
+ end
44
+
45
+ def self.scanners(extension)
46
+ Dir["#{File.dirname(__FILE__)}/**/*#{extension}"]
47
+ end
48
+
49
+ def self.overwrite(file, content)
50
+ File.open(file, 'w') { |t| t.puts content }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WindClutter
4
+ module Util
5
+ # Generator helper
6
+ class Generator
7
+ def self.random_class(prefixed: true)
8
+ random = [*'A'..'Z'].sample(10).join.downcase
9
+ "windclutter:#{random}" if prefixed
10
+ random
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'windclutter/processor'
4
+ require 'windclutter/cli'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+
5
+ Dir['test/windclutter/**/*.rb'].sort.each { |f| require f.split('/')[1..].join('/') }
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'windclutter/processor'
5
+
6
+ class WindClutterProcessorTest < Minitest::Test
7
+ def test_able_to_generate_block
8
+ result = WindClutter::Processor.build_single('class', %w[container mx-auto])
9
+
10
+ assert_equal ".class {\n @apply container mx-auto;\n}\n",
11
+ result
12
+ end
13
+
14
+ def test_able_to_auto_process
15
+ WindClutter::Util::Generator.stub(:random_class, 'windclutter:mocked_value') do
16
+ collections = []
17
+ result = WindClutter::Processor.auto_process(dummy_content, collections)
18
+
19
+ assert_equal result, expected_output
20
+ assert_equal collections[0][:generated_name], 'windclutter:mocked_value'
21
+ assert_equal collections[0][:class], 'flex flex-col items-center gap-6 px-10 py-40 overflow-y-scroll'
22
+ assert_nil collections[0][:provided_name]
23
+ assert_equal collections[0][:named], false
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def dummy_content
30
+ <<~CONTENT
31
+ <div class="flex flex-col items-center gap-6 px-10 py-40 overflow-y-scroll">
32
+ </div>
33
+ CONTENT
34
+ end
35
+
36
+ def expected_output
37
+ <<~CONTENT
38
+ <div class=\"windclutter:mocked_value\">
39
+ </div>
40
+ CONTENT
41
+ end
42
+
43
+ def expected_collections
44
+ { generated_name: 'windclutter:mocked_value',
45
+ class: 'flex flex-col items-center gap-6 px-10 py-40 overflow-y-scroll' }
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minitest/autorun'
4
+ require 'windclutter/util/generator'
5
+
6
+ class WindClutterUtilGeneratorTest < Minitest::Test
7
+ def test_able_to_randomize_class_name
8
+ assert_match(/.[a-z]+/,
9
+ WindClutter::Util::Generator.random_class)
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: windclutter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Zafranudin Zafrin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-09-06 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Your buddy to skyrocket your development with TailwindCSS.
14
+ email: coffee@zafranudin.dev
15
+ executables:
16
+ - windclutter
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - bin/windclutter
22
+ - lib/windclutter.rb
23
+ - lib/windclutter/analyser.rb
24
+ - lib/windclutter/cli/commands/generate.rb
25
+ - lib/windclutter/cli/commands/project.rb
26
+ - lib/windclutter/cli/core.rb
27
+ - lib/windclutter/processor.rb
28
+ - lib/windclutter/util/config.rb
29
+ - lib/windclutter/util/file_handler.rb
30
+ - lib/windclutter/util/generator.rb
31
+ - test/test_windclutter.rb
32
+ - test/windclutter/test_processor.rb
33
+ - test/windclutter/util/test_generator.rb
34
+ homepage: https://rubygems.org/gems/tailwind_buddy
35
+ licenses:
36
+ - MIT
37
+ metadata: {}
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.7.0
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.4.18
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Quickly develop with TailwindCSS without worries.
57
+ test_files: []