postnhost 0.1.0 → 0.1.1
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +11 -9
- data/Rakefile +28 -0
- data/app/assets/builds/postnhost/application.css +1 -1
- data/app/assets/stylesheets/application.tailwind.css +5 -1
- data/app/helpers/postnhost/application_helper.rb +0 -11
- data/app/views/layouts/postnhost/application.html.erb +2 -2
- data/app/views/layouts/postnhost/articles.html.erb +3 -3
- data/app/views/layouts/postnhost/product.html.erb +3 -3
- data/app/views/layouts/postnhost/public/templates/default.html.erb +2 -2
- data/app/views/layouts/postnhost/public/templates/swiss-editorial.html.erb +2 -2
- data/app/views/layouts/postnhost/public/templates/workspace-journal.html.erb +2 -2
- data/app/views/postnhost/onboarding/_post_install_checklist.html.erb +8 -8
- data/app/views/postnhost/pages/index.html.erb +1 -1
- data/app/views/postnhost/templates/edit.html.erb +1 -1
- data/lib/generators/postnhost/install/install_generator.rb +11 -12
- data/lib/generators/postnhost/locale/locale_generator.rb +1 -1
- data/lib/generators/postnhost/static_pages/static_pages_generator.rb +1 -1
- data/lib/generators/postnhost/tailwindcss/install_generator.rb +28 -25
- data/lib/generators/postnhost/tailwindcss/templates/host.tailwind.css +0 -3
- data/lib/generators/postnhost/user/user_generator.rb +1 -1
- data/lib/generators/postnhost/views/views_generator.rb +5 -7
- data/lib/postnhost/css_build.rb +68 -0
- data/lib/postnhost/css_scope.rb +77 -0
- data/lib/postnhost/sample_data.rb +1 -1
- data/lib/postnhost/tailwind_compiler.rb +75 -0
- data/lib/postnhost/tailwind_input.rb +32 -0
- data/lib/postnhost/version.rb +1 -1
- data/lib/tasks/postnhost_tasks.rake +40 -65
- metadata +19 -2
- data/lib/generators/postnhost/tailwindcss/templates/tailwind.config.js +0 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="<%= document_lang %>"
|
|
2
|
+
<html lang="<%= document_lang %>" data-postnhost>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<%= csrf_meta_tags %>
|
|
11
11
|
<%= csp_meta_tag %>
|
|
12
12
|
|
|
13
|
-
<%= stylesheet_link_tag
|
|
13
|
+
<%= stylesheet_link_tag "postnhost/application", "data-turbo-track": "reload" %>
|
|
14
14
|
<%= javascript_include_tag "postnhost/application", "data-turbo-track": "reload", type: "module" %>
|
|
15
15
|
|
|
16
16
|
<%= render 'layouts/postnhost/favicon' %>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="<%= document_lang %>"
|
|
2
|
+
<html lang="<%= document_lang %>" data-postnhost>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<%= csrf_meta_tags %>
|
|
11
11
|
<%= csp_meta_tag %>
|
|
12
12
|
|
|
13
|
-
<%= stylesheet_link_tag
|
|
13
|
+
<%= stylesheet_link_tag "postnhost/application", "data-turbo-track": "reload" %>
|
|
14
14
|
<%= javascript_include_tag "postnhost/application", "data-turbo-track": "reload", type: "module" %>
|
|
15
15
|
|
|
16
16
|
<%= render "layouts/postnhost/favicon" %>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="<%= document_lang %>"
|
|
2
|
+
<html lang="<%= document_lang %>" data-postnhost>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<%= csrf_meta_tags %>
|
|
11
11
|
<%= csp_meta_tag %>
|
|
12
12
|
|
|
13
|
-
<%= stylesheet_link_tag
|
|
13
|
+
<%= stylesheet_link_tag "postnhost/application", "data-turbo-track": "reload" %>
|
|
14
14
|
<%= javascript_include_tag "postnhost/application", "data-turbo-track": "reload", type: "module" %>
|
|
15
15
|
|
|
16
16
|
<%= render "layouts/postnhost/favicon" %>
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
<div class="space-y-5">
|
|
2
2
|
<p class="text-sm text-gray-600">
|
|
3
|
-
|
|
3
|
+
Review these host-app settings before deployment. Details are available in the Production checklist section of the README.
|
|
4
4
|
</p>
|
|
5
5
|
|
|
6
6
|
<div class="rounded-xl border border-gray-200 bg-gray-50 p-4">
|
|
7
7
|
<ul class="space-y-3 text-sm text-gray-700">
|
|
8
8
|
<li>
|
|
9
9
|
<span class="font-medium text-gray-900">1. Favicon</span><br>
|
|
10
|
-
|
|
10
|
+
Replace the favicon and related icon files in the host app if needed.
|
|
11
11
|
</li>
|
|
12
12
|
<li>
|
|
13
|
-
<span class="font-medium text-gray-900">2. 404
|
|
14
|
-
|
|
13
|
+
<span class="font-medium text-gray-900">2. 404 and 500 pages</span><br>
|
|
14
|
+
Review the host app’s static error pages at <code>public/404.html</code> and <code>public/500.html</code>.
|
|
15
15
|
</li>
|
|
16
16
|
<li>
|
|
17
17
|
<span class="font-medium text-gray-900">3. robots.txt</span><br>
|
|
18
|
-
|
|
18
|
+
Update the host app’s <code>public/robots.txt</code> and confirm that the sitemap URL matches your mount path.
|
|
19
19
|
</li>
|
|
20
20
|
<li>
|
|
21
21
|
<span class="font-medium text-gray-900">4. Public template styling</span><br>
|
|
22
|
-
|
|
22
|
+
To customize the public site, run <code>bin/rails g postnhost:views --views-scope=minimal</code> or use <code>--views-scope=full</code>, then edit the copied templates.
|
|
23
23
|
</li>
|
|
24
24
|
<li>
|
|
25
|
-
<span class="font-medium text-gray-900">5.
|
|
26
|
-
|
|
25
|
+
<span class="font-medium text-gray-900">5. Production uploads</span><br>
|
|
26
|
+
The generated CarrierWave initializer uses S3-compatible storage in production. Configure it through Rails credentials or <code>config/initializers/postnhost.rb</code>, or replace that storage configuration.
|
|
27
27
|
</li>
|
|
28
28
|
</ul>
|
|
29
29
|
</div>
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
</div>
|
|
105
105
|
<% else %>
|
|
106
106
|
<div class="rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-600">
|
|
107
|
-
No code-defined static pages found. Add templates in <code>app/views/postnhost/static_pages</code> (for example, <code>about.html.erb</code> maps to <code>/about</code>), or run <code>rails g postnhost:static_pages about</code> to scaffold a starter template.
|
|
107
|
+
No code-defined static pages found. Add templates in <code>app/views/postnhost/static_pages</code> (for example, <code>about.html.erb</code> maps to <code>/about</code>), or run <code>bin/rails g postnhost:static_pages about</code> to scaffold a starter template.
|
|
108
108
|
</div>
|
|
109
109
|
<% end %>
|
|
110
110
|
</div>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<p class="text-sm leading-6 text-gray-600">If your changes add new Tailwind classes, install and run the optional host Tailwind pipeline:</p>
|
|
56
56
|
<div class="space-y-1">
|
|
57
57
|
<code class="block overflow-x-auto rounded-lg bg-gray-900 px-3 py-2 text-xs leading-5 text-white">bin/rails g postnhost:tailwindcss:install</code>
|
|
58
|
-
<code class="block overflow-x-auto rounded-lg bg-gray-900 px-3 py-2 text-xs leading-5 text-white">
|
|
58
|
+
<code class="block overflow-x-auto rounded-lg bg-gray-900 px-3 py-2 text-xs leading-5 text-white">bin/rails postnhost:tailwindcss:watch</code>
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
@@ -5,7 +5,7 @@ module Postnhost
|
|
|
5
5
|
|
|
6
6
|
source_root File.expand_path("templates", __dir__)
|
|
7
7
|
|
|
8
|
-
desc "Creates
|
|
8
|
+
desc "Creates the PostnHost initializer and mounts the engine"
|
|
9
9
|
|
|
10
10
|
def create_initializer
|
|
11
11
|
template "postnhost.rb", "config/initializers/postnhost.rb"
|
|
@@ -24,7 +24,7 @@ module Postnhost
|
|
|
24
24
|
def show_post_install
|
|
25
25
|
say ""
|
|
26
26
|
say "=" * 60
|
|
27
|
-
say "
|
|
27
|
+
say "PostnHost has been installed!", :green
|
|
28
28
|
say "=" * 60
|
|
29
29
|
say ""
|
|
30
30
|
say "Next steps:"
|
|
@@ -32,23 +32,22 @@ module Postnhost
|
|
|
32
32
|
say " bin/rails postnhost:install:migrations"
|
|
33
33
|
say " bin/rails db:migrate"
|
|
34
34
|
say ""
|
|
35
|
-
say " 2. Open first-time setup in
|
|
36
|
-
say "
|
|
35
|
+
say " 2. Open first-time setup in your app:"
|
|
36
|
+
say " #{DEFAULT_MOUNT_PATH}/onboarding"
|
|
37
37
|
say " Or create a CMS user interactively: bin/rails g postnhost:user"
|
|
38
38
|
say ""
|
|
39
|
-
say " 3.
|
|
39
|
+
say " 3. Review config/initializers/postnhost.rb for optional defaults, production uploads, and AI translations."
|
|
40
40
|
say ""
|
|
41
41
|
say " 4. (Optional) Customize public views:"
|
|
42
|
-
say " rails g postnhost:views --views-scope=minimal"
|
|
43
|
-
say "
|
|
44
|
-
say " rails g postnhost:views --views-scope=full"
|
|
42
|
+
say " bin/rails g postnhost:views --views-scope=minimal"
|
|
43
|
+
say " or"
|
|
44
|
+
say " bin/rails g postnhost:views --views-scope=full"
|
|
45
45
|
say ""
|
|
46
|
-
say " 5. (Optional) Enable host Tailwind
|
|
47
|
-
say " rails g postnhost:tailwindcss:install"
|
|
48
|
-
say " bundle exec rails postnhost:tailwindcss:watch"
|
|
46
|
+
say " 5. (Optional) Enable host Tailwind support for custom PostnHost classes:"
|
|
47
|
+
say " bin/rails g postnhost:tailwindcss:install"
|
|
49
48
|
say ""
|
|
50
49
|
say " 6. (Optional) Add a new public locale (copy English strings into your app):"
|
|
51
|
-
say " rails g postnhost:locale sv"
|
|
50
|
+
say " bin/rails g postnhost:locale sv"
|
|
52
51
|
say ""
|
|
53
52
|
end
|
|
54
53
|
end
|
|
@@ -5,7 +5,7 @@ module Postnhost
|
|
|
5
5
|
class LocaleGenerator < Rails::Generators::Base
|
|
6
6
|
argument :locale, type: :string, desc: "Locale code to match Language#html_lang (e.g. sv, pt-BR)"
|
|
7
7
|
|
|
8
|
-
desc "Copies
|
|
8
|
+
desc "Copies the PostnHost English locale (en.yml) to config/locales/<locale>.yml in your app for translation."
|
|
9
9
|
|
|
10
10
|
def copy_postnhost_locale
|
|
11
11
|
code = locale.to_s.strip
|
|
@@ -7,7 +7,7 @@ module Postnhost
|
|
|
7
7
|
|
|
8
8
|
def create_static_pages
|
|
9
9
|
if slugs.empty?
|
|
10
|
-
say("Please provide at least one page slug, e.g. rails g postnhost:static_pages terms privacy", :red)
|
|
10
|
+
say("Please provide at least one page slug, e.g. bin/rails g postnhost:static_pages terms privacy", :red)
|
|
11
11
|
return
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -7,19 +7,12 @@ module Postnhost
|
|
|
7
7
|
source_root File.expand_path("templates", __dir__)
|
|
8
8
|
|
|
9
9
|
namespace "postnhost:tailwindcss:install"
|
|
10
|
-
desc "Install host Tailwind
|
|
10
|
+
desc "Install host Tailwind support for PostnHost view overrides."
|
|
11
11
|
|
|
12
12
|
def create_stylesheet_directory
|
|
13
13
|
empty_directory "app/assets/stylesheets/postnhost"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def create_tailwind_config
|
|
17
|
-
path = Rails.root.join("postnhost.tailwind.config.js")
|
|
18
|
-
return if path.exist?
|
|
19
|
-
|
|
20
|
-
copy_file "tailwind.config.js", path
|
|
21
|
-
end
|
|
22
|
-
|
|
23
16
|
def create_host_stylesheet
|
|
24
17
|
path = Rails.root.join("app/assets/stylesheets/postnhost/host.tailwind.css")
|
|
25
18
|
return if path.exist?
|
|
@@ -31,12 +24,14 @@ module Postnhost
|
|
|
31
24
|
path = Rails.root.join("package.json")
|
|
32
25
|
return say_package_json_instructions unless path.exist?
|
|
33
26
|
|
|
27
|
+
@package_json_present = true
|
|
28
|
+
|
|
34
29
|
json_data = JSON.parse(File.read(path))
|
|
35
30
|
json_data["scripts"] ||= {}
|
|
36
31
|
json_data["devDependencies"] ||= {}
|
|
37
32
|
|
|
38
|
-
json_data["scripts"]["postnhost:tailwindcss"] =
|
|
39
|
-
json_data["scripts"]["postnhost:tailwindcss:watch"] = "
|
|
33
|
+
json_data["scripts"]["postnhost:tailwindcss"] = "bundle exec rails postnhost:tailwindcss:build"
|
|
34
|
+
json_data["scripts"]["postnhost:tailwindcss:watch"] = "bundle exec rails postnhost:tailwindcss:watch"
|
|
40
35
|
|
|
41
36
|
ensure_dependency(json_data, "tailwindcss", "^4.1.12")
|
|
42
37
|
ensure_dependency(json_data, "@tailwindcss/cli", "^4.1.12")
|
|
@@ -44,8 +39,7 @@ module Postnhost
|
|
|
44
39
|
|
|
45
40
|
File.write(path, "#{JSON.pretty_generate(json_data)}\n")
|
|
46
41
|
|
|
47
|
-
say "Updated package.json
|
|
48
|
-
say " yarn postnhost:tailwindcss:watch"
|
|
42
|
+
say "Updated package.json with PostnHost Tailwind scripts and dependencies.", :green
|
|
49
43
|
end
|
|
50
44
|
|
|
51
45
|
def wire_procfile_dev
|
|
@@ -54,32 +48,41 @@ module Postnhost
|
|
|
54
48
|
|
|
55
49
|
if path.exist?
|
|
56
50
|
if File.read(path).include?("postnhost:tailwindcss:watch")
|
|
57
|
-
say "Procfile.dev already includes
|
|
51
|
+
say "Procfile.dev already includes the PostnHost watcher.", :green
|
|
58
52
|
else
|
|
59
53
|
append_to_file path, "\n#{entry}\n"
|
|
60
|
-
say "Added
|
|
54
|
+
say "Added the PostnHost watcher to Procfile.dev.", :green
|
|
61
55
|
end
|
|
62
56
|
else
|
|
63
57
|
create_file "Procfile.dev", "#{entry}\n"
|
|
64
|
-
say "Created Procfile.dev with
|
|
58
|
+
say "Created Procfile.dev with the PostnHost watcher.", :green
|
|
65
59
|
end
|
|
66
60
|
end
|
|
67
61
|
|
|
68
62
|
def print_next_steps
|
|
69
63
|
say ""
|
|
70
|
-
say "
|
|
64
|
+
say "PostnHost host Tailwind support installed.", :green
|
|
71
65
|
say "Next steps:"
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
step = 1
|
|
67
|
+
|
|
68
|
+
if @package_json_present
|
|
69
|
+
say " #{step}. Install dependencies with the JavaScript package manager used by this app."
|
|
70
|
+
step += 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if Rails.root.join("bin/dev").exist?
|
|
74
|
+
say " #{step}. Run bin/dev; the PostnHost watcher is wired in Procfile.dev."
|
|
75
|
+
else
|
|
76
|
+
say " #{step}. Run bin/rails postnhost:tailwindcss:watch alongside your Rails server."
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
say " #{step + 1}. Use normal Tailwind classes in copied PostnHost views."
|
|
80
|
+
say " #{step + 2}. Keep the default layout tags; the combined build overrides postnhost/application automatically."
|
|
74
81
|
say ""
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
private
|
|
78
85
|
|
|
79
|
-
def tailwind_build_command
|
|
80
|
-
"./node_modules/.bin/tailwindcss -i ./app/assets/stylesheets/postnhost/host.tailwind.css -o ./app/assets/builds/postnhost/host.css -c ./postnhost.tailwind.config.js --minify"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
86
|
def ensure_dependency(json_data, package_name, version)
|
|
84
87
|
return if json_data["dependencies"]&.key?(package_name) || json_data["devDependencies"].key?(package_name)
|
|
85
88
|
|
|
@@ -87,9 +90,9 @@ module Postnhost
|
|
|
87
90
|
end
|
|
88
91
|
|
|
89
92
|
def say_package_json_instructions
|
|
90
|
-
say "package.json not found. Add
|
|
91
|
-
say
|
|
92
|
-
say "
|
|
93
|
+
say "package.json not found. Add a Tailwind CSS 4 compiler to the host Gemfile:", :yellow
|
|
94
|
+
say ' gem "tailwindcss-ruby", "~> 4.0"'
|
|
95
|
+
say "Then run bundle install."
|
|
93
96
|
end
|
|
94
97
|
end
|
|
95
98
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Postnhost
|
|
2
2
|
module Generators
|
|
3
3
|
class ViewsGenerator < Rails::Generators::Base
|
|
4
|
-
desc "Copies public
|
|
4
|
+
desc "Copies public PostnHost views to your application for customization"
|
|
5
5
|
class_option :views_scope,
|
|
6
6
|
type: :string,
|
|
7
7
|
required: true,
|
|
@@ -28,7 +28,7 @@ module Postnhost
|
|
|
28
28
|
copy_file "layouts/postnhost/public_header/_dashboard_sign_in.html.erb", "app/views/layouts/postnhost/public_header/_dashboard_sign_in.html.erb"
|
|
29
29
|
say ""
|
|
30
30
|
warn_if_host_tailwind_not_installed
|
|
31
|
-
say "
|
|
31
|
+
say views_scope == "full" ? "All public templates and layouts were copied." : "The minimal public view set was copied.", :green
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def engine_views_path
|
|
@@ -123,15 +123,13 @@ module Postnhost
|
|
|
123
123
|
def warn_if_host_tailwind_not_installed
|
|
124
124
|
return if host_tailwind_enabled?
|
|
125
125
|
|
|
126
|
-
say "Tip: if you add new Tailwind classes in copied views, install host
|
|
127
|
-
say " rails g postnhost:tailwindcss:install"
|
|
128
|
-
say " bundle exec rails postnhost:tailwindcss:watch"
|
|
126
|
+
say "Tip: if you add new Tailwind classes in copied views, install host Tailwind support:", :yellow
|
|
127
|
+
say " bin/rails g postnhost:tailwindcss:install"
|
|
129
128
|
say ""
|
|
130
129
|
end
|
|
131
130
|
|
|
132
131
|
def host_tailwind_enabled?
|
|
133
|
-
Rails.root.join("postnhost.tailwind.
|
|
134
|
-
Rails.root.join("app/assets/stylesheets/postnhost/host.tailwind.css").exist?
|
|
132
|
+
Rails.root.join("app/assets/stylesheets/postnhost/host.tailwind.css").exist?
|
|
135
133
|
end
|
|
136
134
|
|
|
137
135
|
def source_paths
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "English"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
require_relative "css_scope"
|
|
5
|
+
|
|
6
|
+
module Postnhost
|
|
7
|
+
class CssBuild
|
|
8
|
+
POLL_INTERVAL = 0.1
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def run(command:, unscoped_path:, output_path:, watch: false)
|
|
12
|
+
FileUtils.rm_f(unscoped_path)
|
|
13
|
+
|
|
14
|
+
if watch
|
|
15
|
+
watch(command:, unscoped_path:, output_path:)
|
|
16
|
+
else
|
|
17
|
+
system(*command, exception: true)
|
|
18
|
+
CssScope.scope_file(unscoped_path, output_path)
|
|
19
|
+
end
|
|
20
|
+
ensure
|
|
21
|
+
FileUtils.rm_f(unscoped_path)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def watch(command:, unscoped_path:, output_path:)
|
|
27
|
+
process_id = Process.spawn(*command)
|
|
28
|
+
applied_signature = nil
|
|
29
|
+
pending_signature = nil
|
|
30
|
+
|
|
31
|
+
loop do
|
|
32
|
+
process_status = Process.waitpid(process_id, Process::WNOHANG)
|
|
33
|
+
signature = file_signature(unscoped_path)
|
|
34
|
+
|
|
35
|
+
if signature && signature == pending_signature && signature != applied_signature
|
|
36
|
+
CssScope.scope_file(unscoped_path, output_path)
|
|
37
|
+
applied_signature = signature
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
pending_signature = signature
|
|
41
|
+
break if process_status
|
|
42
|
+
|
|
43
|
+
sleep POLL_INTERVAL
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
raise "Tailwind CSS watcher exited unsuccessfully" unless $CHILD_STATUS.success?
|
|
47
|
+
ensure
|
|
48
|
+
terminate(process_id)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def file_signature(path)
|
|
52
|
+
return unless File.exist?(path)
|
|
53
|
+
|
|
54
|
+
stat = File.stat(path)
|
|
55
|
+
[stat.mtime.to_f, stat.size]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def terminate(process_id)
|
|
59
|
+
return unless process_id
|
|
60
|
+
|
|
61
|
+
Process.kill("TERM", process_id)
|
|
62
|
+
Process.wait(process_id)
|
|
63
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require "crass"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
module Postnhost
|
|
5
|
+
class CssScope
|
|
6
|
+
ROOT_SELECTOR = "html[data-postnhost]"
|
|
7
|
+
ROOT_OR_DESCENDANT_SELECTOR = ":is(#{ROOT_SELECTOR}, #{ROOT_SELECTOR} *)"
|
|
8
|
+
GROUP_AT_RULES = %w[container layer media starting-style supports].freeze
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def call(css)
|
|
12
|
+
rules = Crass::Parser.parse_stylesheet(css, preserve_comments: true)
|
|
13
|
+
scope_rules(rules)
|
|
14
|
+
Crass::Parser.stringify(rules)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def scope_file(input_path, output_path = input_path)
|
|
18
|
+
output_path = File.expand_path(output_path)
|
|
19
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
20
|
+
temporary_path = "#{output_path}.tmp"
|
|
21
|
+
File.write(temporary_path, call(File.read(input_path)))
|
|
22
|
+
FileUtils.mv(temporary_path, output_path)
|
|
23
|
+
ensure
|
|
24
|
+
FileUtils.rm_f(temporary_path) if temporary_path
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def scope_rules(rules)
|
|
30
|
+
rules.each do |rule|
|
|
31
|
+
case rule[:node]
|
|
32
|
+
when :style_rule
|
|
33
|
+
scope_style_rule(rule)
|
|
34
|
+
when :at_rule
|
|
35
|
+
scope_at_rule(rule)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def scope_at_rule(rule)
|
|
41
|
+
return unless rule[:block] && GROUP_AT_RULES.include?(rule[:name])
|
|
42
|
+
|
|
43
|
+
nested_rules = Crass::Parser.parse_rules(rule[:block], preserve_comments: true)
|
|
44
|
+
scope_rules(nested_rules)
|
|
45
|
+
rule[:block] = nested_rules
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def scope_style_rule(rule)
|
|
49
|
+
selectors = split_selectors(rule.dig(:selector, :tokens)).map { |selector| scope_selector(selector) }.uniq
|
|
50
|
+
scoped_rule = Crass::Parser.parse_rules("#{selectors.join(',')}{}").first
|
|
51
|
+
rule[:selector] = scoped_rule[:selector]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def split_selectors(tokens)
|
|
55
|
+
selectors = tokens.each_with_object([[]]) do |token, groups|
|
|
56
|
+
if token[:node] == :comma
|
|
57
|
+
groups << []
|
|
58
|
+
else
|
|
59
|
+
groups.last << token
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
selectors.map { |selector_tokens| Crass::Parser.stringify(selector_tokens).strip }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def scope_selector(selector)
|
|
67
|
+
return ROOT_SELECTOR if selector.match?(/\A(?::root|:host)\z/)
|
|
68
|
+
return selector.sub(/\A:root/, ROOT_SELECTOR) if selector.match?(/\A:root(?=$|[\s.#\[:>+~])/)
|
|
69
|
+
return selector.sub(/\Ahtml/, ROOT_SELECTOR) if selector.match?(/\Ahtml(?=$|[\s.#\[:>+~])/)
|
|
70
|
+
return selector.sub(/\A\*/, ROOT_OR_DESCENDANT_SELECTOR) if selector.start_with?("*")
|
|
71
|
+
return "#{ROOT_OR_DESCENDANT_SELECTOR}#{selector}" if selector.match?(/\A[.#\[:]/)
|
|
72
|
+
|
|
73
|
+
"#{ROOT_SELECTOR} #{selector}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -785,7 +785,7 @@ module Postnhost
|
|
|
785
785
|
<li>⏰ <strong>Post scheduling</strong> — Schedule publication through your application’s Active Job adapter</li>
|
|
786
786
|
<li>🌐 <strong>Localized SEO</strong> — Localized routes, language switcher, sitemap, and hreflang tags</li>
|
|
787
787
|
<li>⚙️ <strong>Settings</strong> — Update key copy and assets from the CMS</li>
|
|
788
|
-
<li>🎨 <strong>Customizable views</strong> — Override public templates in the host app (<code>rails g postnhost:views</code>)</li>
|
|
788
|
+
<li>🎨 <strong>Customizable views</strong> — Override public templates in the host app (<code>bin/rails g postnhost:views --views-scope=minimal</code>)</li>
|
|
789
789
|
<li>⚡ <strong>Hotwire UI</strong> — Turbo Frames/Streams and Stimulus</li>
|
|
790
790
|
<li>🔐 <strong>Session-based auth</strong> — Built-in admin authentication</li>
|
|
791
791
|
</ul>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "pathname"
|
|
3
|
+
require "rubygems/version"
|
|
4
|
+
|
|
5
|
+
module Postnhost
|
|
6
|
+
class TailwindCompiler
|
|
7
|
+
REQUIRED_MAJOR = 4
|
|
8
|
+
|
|
9
|
+
class MissingCompiler < StandardError; end
|
|
10
|
+
class IncompatibleVersion < StandardError; end
|
|
11
|
+
|
|
12
|
+
def initialize(root:, loaded_specs: Gem.loaded_specs)
|
|
13
|
+
@root = Pathname.new(root)
|
|
14
|
+
@loaded_specs = loaded_specs
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def command(input_path:, output_path:, watch: false)
|
|
18
|
+
command = npm_command(input_path:, output_path:) || ruby_command(input_path:, output_path:)
|
|
19
|
+
raise MissingCompiler, missing_compiler_message unless command
|
|
20
|
+
|
|
21
|
+
command << "--watch" if watch
|
|
22
|
+
command
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :root, :loaded_specs
|
|
28
|
+
|
|
29
|
+
def ruby_command(input_path:, output_path:)
|
|
30
|
+
spec = loaded_specs["tailwindcss-ruby"]
|
|
31
|
+
return unless spec
|
|
32
|
+
|
|
33
|
+
validate_version!(spec.version, "tailwindcss-ruby")
|
|
34
|
+
require "tailwindcss/ruby"
|
|
35
|
+
executable = Tailwindcss::Ruby.executable
|
|
36
|
+
return if executable.to_s.empty?
|
|
37
|
+
|
|
38
|
+
[executable, "-i", input_path.to_s, "-o", output_path.to_s, "--minify"]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def npm_command(input_path:, output_path:)
|
|
42
|
+
executable = root.join("node_modules/.bin/tailwindcss")
|
|
43
|
+
return unless executable.file?
|
|
44
|
+
|
|
45
|
+
validate_package_version!("tailwindcss")
|
|
46
|
+
validate_package_version!("@tailwindcss/cli")
|
|
47
|
+
|
|
48
|
+
[executable.to_s, "-i", input_path.to_s, "-o", output_path.to_s, "--minify"]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_package_version!(package_name)
|
|
52
|
+
package_path = root.join("node_modules", package_name, "package.json")
|
|
53
|
+
raise MissingCompiler, "[postnhost] Missing #{package_name}. Install Tailwind CSS 4 and @tailwindcss/cli 4." unless package_path.file?
|
|
54
|
+
|
|
55
|
+
version = JSON.parse(package_path.read).fetch("version")
|
|
56
|
+
validate_version!(version, package_name)
|
|
57
|
+
rescue JSON::ParserError, KeyError
|
|
58
|
+
raise MissingCompiler, "[postnhost] Cannot read the installed #{package_name} version."
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate_version!(version, source)
|
|
62
|
+
return if Gem::Version.new(version.to_s).segments.first == REQUIRED_MAJOR
|
|
63
|
+
|
|
64
|
+
raise IncompatibleVersion,
|
|
65
|
+
"[postnhost] Host CSS requires Tailwind CSS 4; found #{source} #{version}."
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def missing_compiler_message
|
|
69
|
+
<<~MESSAGE.strip
|
|
70
|
+
[postnhost] No Tailwind CSS 4 compiler found for host customization.
|
|
71
|
+
Add tailwindcss-ruby 4, or install tailwindcss 4 and @tailwindcss/cli 4.
|
|
72
|
+
MESSAGE
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "pathname"
|
|
2
|
+
|
|
3
|
+
module Postnhost
|
|
4
|
+
class TailwindInput
|
|
5
|
+
SOURCE_PATTERN = /@source\s+"([^"]+)";/
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def build(engine_source:, host_source:)
|
|
9
|
+
[
|
|
10
|
+
expand_sources(engine_source),
|
|
11
|
+
expand_sources(host_source)
|
|
12
|
+
].join("\n\n")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def expand_sources(path)
|
|
18
|
+
source_path = Pathname.new(path)
|
|
19
|
+
|
|
20
|
+
source_path.read.gsub(SOURCE_PATTERN) do
|
|
21
|
+
relative_source = Regexp.last_match(1)
|
|
22
|
+
absolute_source = source_path.dirname.join(relative_source).expand_path
|
|
23
|
+
%(@source "#{css_path(absolute_source)}";)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def css_path(path)
|
|
28
|
+
Pathname.new(path).expand_path.to_s.tr("\\", "/").gsub('"', '\\"')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/postnhost/version.rb
CHANGED