windclutter 0.0.2 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7228d312e25ff4308cb7e6f753a71d25452c9c1cfde852bbab75f35853f3ff98
4
- data.tar.gz: 580a419ed1d91abc97831fc0836e7547c5b1544c6563f5ee4a454181778d6bc6
3
+ metadata.gz: 572c1ec51a38ae7c4229153f3c951c0e6f484f65c147e06f71a769be93a5b624
4
+ data.tar.gz: 214ddc81d57c501cf2b98d2fd8d02fc78e20bb95df4a6286015c39ca2a934357
5
5
  SHA512:
6
- metadata.gz: 5a54ffa345fbfb0b234126b9e1f304ebdea473e84087ed387c2c555b87549abcdebcc510b83040bccb45f9de28c1f65325bb2984345cbd9b4d1cfe50fa583a3a
7
- data.tar.gz: 2b6ea373ca2273fed179ded3e24ee3553b3fd0cb465d7dff8d3b0968b9b5ab73551180c613c70b04b717f3f03c332486115286f6dc072e022a100ff05a9082d9
6
+ metadata.gz: 166c6061d6e7dd566286f6a9c912b6b893377758c86f0c3eaae4f6e215feef3ad238a58318c07233d4d6ddc40f212492d69cc4dfdbaab16d1dbe91c8c3c1d1e0
7
+ data.tar.gz: 531261c0bb3da1178e200eaa2ca4cf9d61a3bf6366c717b84f88723e4d104b0eacf19c0f3cc02374450a1739eb86d1ec1089777517e38ef1cb4f65157890aede
data/README.md CHANGED
@@ -17,6 +17,13 @@ This tool aim to:
17
17
  Once your project grows and ready to for your clients, chances are you are left with
18
18
  humongous task of Tailwind CSS cleanup. I call this "ejected" states.
19
19
 
20
+ ## Installation
21
+
22
+ ```bash
23
+ # requires ruby 2.7 and above
24
+ $ gem install windclutter
25
+ ```
26
+
20
27
  ## In Action
21
28
 
22
29
  ```html
@@ -0,0 +1,2 @@
1
+ name: windclutter
2
+ projects: {}
@@ -1,7 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'windclutter/util/config'
4
+
3
5
  module WindClutter
4
6
  # Analyser for windclutter
5
7
  class Analyser
8
+ include WindClutter::Util
9
+
10
+ def self.init(content)
11
+ active_project = Config.read('active_project')
12
+
13
+ class_key = Config.read_project(active_project, 'class_key')
14
+ class_start = Config.read_project(active_project, 'class_start')
15
+ class_end = Config.read_project(active_project, 'class_end')
16
+
17
+ regex = /#{class_key}=#{class_start}(?:(?!windclutter:)[^#{class_end}])*#{class_end}/
18
+ occurrence_regex = /#{class_key}=#{class_start}([^#{class_end}]*)#{class_end}/
19
+
20
+ collections = {}
21
+ content.scan(regex).each do |occurrence|
22
+ cls = occurrence.to_s.match(occurrence_regex)[1].split(' ')
23
+
24
+ cls.each do |c|
25
+ total = collections[c].nil? ? 0 : collections[c]
26
+ collections[c] = total + 1
27
+ end
28
+ end
29
+ collections.sort_by { |_, v| -v }.to_h
30
+ end
6
31
  end
7
32
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/cli'
4
+ require 'windclutter/analyser'
5
+ require 'windclutter/util/file_handler'
6
+ require 'windclutter/cli/commands/project'
7
+ require 'windclutter/cli/commands/generate'
8
+
9
+ module WindClutter
10
+ module CLI
11
+ module Commands
12
+ module Analysis
13
+ # Initiate setup for specified project
14
+ class FilePath < Dry::CLI::Command
15
+ include WindClutter::Util
16
+
17
+ desc 'Perform CSS analysis of the path'
18
+ argument :path, aliases: ['-p'], required: true, desc: 'Path of your CSS file to be dump.'
19
+
20
+ def call(path:, **)
21
+ puts "Analysing #{path}...".green
22
+
23
+ file = File.open(FileHandler.scan_one(path))
24
+ puts 'Done!'.green
25
+ puts WindClutter::Analyser.init(file.read)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ 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 DeClutter
13
+ # Initiate setup for specified project
14
+ class FilePath < Dry::CLI::Command
15
+ include WindClutter::Util
16
+
17
+ desc 'Perform decluttering on the specified path'
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
@@ -50,9 +50,61 @@ module WindClutter
50
50
  end
51
51
 
52
52
  Config.update('active_project', name)
53
+ Config.setup_project(name)
53
54
  puts "Using project \"#{name}\"".green
54
55
  end
55
56
  end
57
+
58
+ # Show current active project
59
+ class Current < Dry::CLI::Command
60
+ include WindClutter::Util
61
+
62
+ desc 'Show which current project is actively used.'
63
+
64
+ def call(**)
65
+ project_name = Config.read('active_project')
66
+
67
+ print 'Currently using: '.green
68
+ puts project_name
69
+ end
70
+ end
71
+
72
+ # Dump the generated CSS to the specified file
73
+ class DumpPath < Dry::CLI::Command
74
+ include WindClutter::Util
75
+
76
+ desc 'Dump path for the generated CSS'
77
+
78
+ argument :path, required: true, desc: 'Path of your CSS file to be dump.'
79
+
80
+ def call(path:, **)
81
+ active_project = Config.read('active_project')
82
+ return puts 'No project specified. Select a project with `use` command.'.yellow if active_project.nil?
83
+
84
+ dump_path = File.expand_path(path, Dir.pwd)
85
+
86
+ Config.update_project(active_project, 'dump_path', dump_path)
87
+ end
88
+ end
89
+
90
+ # Update config
91
+ class ConfigUpdate < Dry::CLI::Command
92
+ include WindClutter::Util
93
+
94
+ desc 'Update the config for your project'
95
+
96
+ argument :key, required: true, desc: 'Key of your config.'
97
+ argument :value, required: true, desc: 'Value of your config.'
98
+
99
+ def call(key:, value:, **)
100
+ active_project = Config.read('active_project')
101
+ return puts 'No project specified. Select a project with `use` command.'.yellow if active_project.nil?
102
+
103
+ puts "Updating #{key}:#{value}".green
104
+
105
+ Config.update_project(active_project, key, value)
106
+ end
107
+ end
56
108
  end
57
109
  end
58
110
  end
@@ -4,6 +4,7 @@ require 'dry/cli'
4
4
  require 'windclutter/util/file_handler'
5
5
  require 'windclutter/cli/commands/project'
6
6
  require 'windclutter/cli/commands/generate'
7
+ require 'windclutter/cli/commands/analysis'
7
8
 
8
9
  module WindClutter
9
10
  module CLI
@@ -24,7 +25,7 @@ module WindClutter
24
25
  class Install < Dry::CLI::Command
25
26
  include WindClutter::Util
26
27
 
27
- desc 'Initiate first setup for tail_draft'
28
+ desc 'Initiate first setup for windclutter'
28
29
 
29
30
  def call(*)
30
31
  FileHandler.init_config
@@ -42,14 +43,33 @@ module WindClutter
42
43
  end
43
44
  end
44
45
 
46
+ # A helper for you to dissect the configuration quickly
47
+ class Debug < Dry::CLI::Command
48
+ include WindClutter::Util
49
+
50
+ desc 'Debug the configuration of windclutter'
51
+
52
+ def call(*)
53
+ puts Config.wtf?
54
+ end
55
+ end
56
+
45
57
  register 'version', Version, aliases: %w[v -v --version]
46
58
  register 'install', Install, aliases: %w[i -i --install]
47
59
  register 'uninstall', Uninstall, aliases: %w[u -u --uninstall]
60
+ register 'debug', Debug, aliases: %w[d -d --debug]
48
61
 
49
62
  register 'project', aliases: ['p'] do |prefix|
50
63
  prefix.register 'init', Commands::Project::Init
51
64
  prefix.register 'list', Commands::Project::List
52
65
  prefix.register 'use', Commands::Project::Use
66
+ prefix.register 'current', Commands::Project::Current
67
+ prefix.register 'dump-path', Commands::Project::DumpPath, aliases: ['-d']
68
+ prefix.register 'config', Commands::Project::ConfigUpdate, aliases: ['-c']
69
+ end
70
+
71
+ register 'analysis', aliases: %w[a -a] do |prefix|
72
+ prefix.register 'file', Commands::Analysis::FilePath, aliases: ['-p']
53
73
  end
54
74
 
55
75
  register 'generate', aliases: ['g'] do |prefix|
@@ -9,7 +9,7 @@ module WindClutter
9
9
  # Config handler
10
10
  class Config
11
11
  def self.update(key, value)
12
- return unless config_exists?
12
+ return unless exists?
13
13
 
14
14
  config = YAML.load_file('/tmp/windclutter/config.yml')
15
15
  config[key] = value
@@ -17,14 +17,58 @@ module WindClutter
17
17
  File.open('/tmp/windclutter/config.yml', 'w') { |f| YAML.dump(config, f) }
18
18
  end
19
19
 
20
+ def self.setup_project(name)
21
+ return unless exists?
22
+
23
+ config = YAML.load_file('/tmp/windclutter/config.yml')
24
+
25
+ config['projects'][name] = {
26
+ 'project_path' => nil,
27
+ 'dump_path' => nil,
28
+ 'class_key' => 'class',
29
+ 'class_start' => '"',
30
+ 'class_end' => '"'
31
+ }
32
+
33
+ File.open('/tmp/windclutter/config.yml', 'w') { |f| YAML.dump(config, f) }
34
+ end
35
+
36
+ def self.update_project(name, key, value)
37
+ return unless exists?
38
+
39
+ config = YAML.load_file('/tmp/windclutter/config.yml')
40
+ if config['projects'][name].nil?
41
+ config['projects'][name] = {
42
+ 'project_path' => nil,
43
+ 'dump_path' => nil
44
+ }
45
+ end
46
+ config['projects'][name][key] = value
47
+
48
+ File.open('/tmp/windclutter/config.yml', 'w') { |f| YAML.dump(config, f) }
49
+ end
50
+
51
+ def self.read_project(name, key)
52
+ return unless exists?
53
+
54
+ config = YAML.load_file('/tmp/windclutter/config.yml')
55
+ config['projects'][name][key]
56
+ end
57
+
20
58
  def self.read(key)
21
- return unless config_exists?
59
+ return unless exists?
22
60
 
23
61
  config = YAML.load_file('/tmp/windclutter/config.yml')
24
62
  config[key]
25
63
  end
26
64
 
27
- def self.config_exists?
65
+ def self.wtf?
66
+ return puts 'No config found.'.red unless exists?
67
+
68
+ YAML.load_file('/tmp/windclutter/config.yml')
69
+ end
70
+
71
+ def self.exists?
28
72
  unless File.file?('/tmp/windclutter/config.yml')
29
73
  puts 'You have not install windclutter yet'.yellow
30
74
  puts 'To install, run:'
@@ -16,9 +16,12 @@ module WindClutter
16
16
  template = File.expand_path('../../template', File.dirname(__FILE__))
17
17
 
18
18
  puts 'Created the following files:'
19
- Dir["#{template}/*.yml"].each do |t|
20
- FileUtils.cp(t, '/tmp/windclutter')
21
- puts "\t#{t}"
19
+ Dir["#{template}/*.yml"].each do |file|
20
+ FileUtils.cp(file, '/tmp/windclutter')
21
+ end
22
+
23
+ Dir['/tmp/windclutter/*'].each do |file|
24
+ puts "\t#{file}"
22
25
  end
23
26
 
24
27
  puts "\nSetup completed!".green
@@ -46,6 +49,10 @@ module WindClutter
46
49
  Dir["#{File.dirname(__FILE__)}/**/*#{extension}"]
47
50
  end
48
51
 
52
+ def self.scan_one(path)
53
+ File.expand_path(path, Dir.pwd)
54
+ end
55
+
49
56
  def self.overwrite(file, content)
50
57
  File.open(file, 'w') { |t| t.puts content }
51
58
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ def config_fixture
4
+ { 'projects' => { 'test_project' => { 'class_key' => 'class', 'class_start' => '"', 'class_end' => '"' } } }
5
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minitest/autorun'
4
+ require 'minitest/mock'
5
+ require 'mocha/minitest'
4
6
 
7
+ Dir['test/fixtures/**/*.rb'].sort.each { |f| require f.split('/')[1..].join('/') }
5
8
  Dir['test/windclutter/**/*.rb'].sort.each { |f| require f.split('/')[1..].join('/') }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'windclutter/analyser'
4
+ require 'windclutter/util/config'
5
+
6
+ class WindClutterAnalyserTest < Minitest::Test
7
+ include WindClutter
8
+ include WindClutter::Util
9
+ def test_able_to_analyse_content
10
+ Config.expects(:read).returns('test_project')
11
+ File.stubs(:file?).with('/tmp/windclutter/config.yml').returns(true)
12
+ Config.any_instance.stubs(:exists?).returns(true)
13
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config_fixture)
14
+
15
+ # Config.expects(:read_project).with('test_project', 'class_key').returns('class')
16
+ assert_equal Analyser.init(dummy_content), expected_output
17
+ end
18
+
19
+ private
20
+
21
+ def dummy_content
22
+ <<~CONTENT
23
+ <div class="flex flex-col items-center gap-6 px-10 py-40 overflow-y-scroll">
24
+ </div>
25
+ CONTENT
26
+ end
27
+
28
+ def expected_output
29
+ { 'flex' => 1, 'flex-col' => 1, 'items-center' => 1, 'gap-6' => 1, 'px-10' => 1, 'py-40' => 1,
30
+ 'overflow-y-scroll' => 1 }
31
+ end
32
+ end
@@ -1,27 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest/autorun'
4
3
  require 'windclutter/processor'
5
4
 
6
5
  class WindClutterProcessorTest < Minitest::Test
6
+ include WindClutter::Util
7
+ include WindClutter
8
+
7
9
  def test_able_to_generate_block
8
- result = WindClutter::Processor.build_single('class', %w[container mx-auto])
10
+ result = Processor.build_single('class', %w[container mx-auto])
9
11
 
10
12
  assert_equal ".class {\n @apply container mx-auto;\n}\n",
11
13
  result
12
14
  end
13
15
 
14
16
  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
17
+ Generator.stubs(:random_class).returns('windclutter:mocked_value')
18
+ collections = []
19
+ result = Processor.auto_process(dummy_content, collections)
20
+
21
+ assert_equal result, expected_output
22
+ assert_equal collections[0][:generated_name], 'windclutter:mocked_value'
23
+ assert_equal collections[0][:class], 'flex flex-col items-center gap-6 px-10 py-40 overflow-y-scroll'
24
+ assert_nil collections[0][:provided_name]
25
+ assert_equal collections[0][:named], false
25
26
  end
26
27
 
27
28
  private
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'windclutter/analyser'
4
+ require 'windclutter/util/config'
5
+
6
+ class WindClutterUtilConfigTest < Minitest::Test
7
+ include WindClutter::Util
8
+
9
+ def test_it_can_update
10
+ config = {}
11
+ Config.expects(:exists?).returns(true)
12
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
13
+ File.expects(:open).with('/tmp/windclutter/config.yml', 'w').once
14
+
15
+ Config.update('key', 'value')
16
+ assert_equal config['key'], 'value'
17
+ end
18
+
19
+ def test_it_can_setup_project
20
+ config = { 'projects' => {} }
21
+ Config.expects(:exists?).returns(true)
22
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
23
+ File.expects(:open).with('/tmp/windclutter/config.yml', 'w').once
24
+
25
+ Config.setup_project('project_name')
26
+ assert_equal config['projects']['project_name'],
27
+ { 'project_path' => nil, 'dump_path' => nil, 'class_key' => 'class', 'class_start' => '"',
28
+ 'class_end' => '"' }
29
+ end
30
+
31
+ def test_it_can_update_project
32
+ config = { 'projects' => {} }
33
+ Config.expects(:exists?).returns(true)
34
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
35
+ File.expects(:open).with('/tmp/windclutter/config.yml', 'w').once
36
+
37
+ Config.update_project('project_name', 'key', 'value')
38
+ assert_equal config['projects']['project_name']['key'],
39
+ 'value'
40
+ end
41
+
42
+ def test_it_can_read_project
43
+ config = { 'projects' => { 'project_name' => { 'key' => 'read_value' } } }
44
+ Config.expects(:exists?).returns(true)
45
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
46
+
47
+ assert_equal Config.read_project('project_name', 'key'),
48
+ 'read_value'
49
+ end
50
+
51
+ def test_it_can_read
52
+ config = { 'key' => 'value' }
53
+ Config.expects(:exists?).returns(true)
54
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
55
+
56
+ assert_equal Config.read('key'), 'value'
57
+ end
58
+
59
+ def test_it_can_wtf
60
+ config = { 'key' => 'value' }
61
+ Config.expects(:exists?).returns(true)
62
+ YAML.stubs(:load_file).with('/tmp/windclutter/config.yml').returns(config)
63
+
64
+ assert_equal Config.wtf?, config
65
+ end
66
+
67
+ def test_it_verifies_config_exists
68
+ File.expects(:file?).with('/tmp/windclutter/config.yml').returns(true)
69
+
70
+ assert_equal Config.exists?, true
71
+ end
72
+
73
+ def test_it_verifies_config_not_exists
74
+ File.expects(:file?).with('/tmp/windclutter/config.yml').returns(false)
75
+
76
+ assert_equal Config.exists?, false
77
+ end
78
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest/autorun'
4
3
  require 'windclutter/util/generator'
5
4
 
6
5
  class WindClutterUtilGeneratorTest < Minitest::Test
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: windclutter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zafranudin Zafrin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-06 00:00:00.000000000 Z
11
+ date: 2023-09-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Your buddy to skyrocket your development with TailwindCSS.
14
14
  email: coffee@zafranudin.dev
@@ -19,8 +19,11 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - README.md
21
21
  - bin/windclutter
22
+ - lib/template/config.yml
22
23
  - lib/windclutter.rb
23
24
  - lib/windclutter/analyser.rb
25
+ - lib/windclutter/cli/commands/analysis.rb
26
+ - lib/windclutter/cli/commands/de_clutter.rb
24
27
  - lib/windclutter/cli/commands/generate.rb
25
28
  - lib/windclutter/cli/commands/project.rb
26
29
  - lib/windclutter/cli/core.rb
@@ -28,8 +31,11 @@ files:
28
31
  - lib/windclutter/util/config.rb
29
32
  - lib/windclutter/util/file_handler.rb
30
33
  - lib/windclutter/util/generator.rb
34
+ - test/fixtures/config_fixture.rb
31
35
  - test/test_windclutter.rb
36
+ - test/windclutter/test_analyser.rb
32
37
  - test/windclutter/test_processor.rb
38
+ - test/windclutter/util/test_config.rb
33
39
  - test/windclutter/util/test_generator.rb
34
40
  homepage: https://rubygems.org/gems/tailwind_buddy
35
41
  licenses: