├── .editorconfig
├── .github
└── workflows
│ └── test-new-rails-app.yml
├── .gitignore
├── .nvmrc
├── .rspec
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── Gemfile.lock
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── doc
└── release.md
├── lib
└── svelte
│ ├── rails.rb
│ ├── rails
│ ├── controller_renderer.rb
│ ├── install_generator.rb
│ ├── install_task.rake
│ ├── railtie.rb
│ ├── templates
│ │ ├── app
│ │ │ └── javascript
│ │ │ │ ├── components
│ │ │ │ └── Hello.svelte
│ │ │ │ └── packs
│ │ │ │ ├── application.js
│ │ │ │ └── server_rendering.js
│ │ └── config
│ │ │ └── webpack
│ │ │ ├── development.js
│ │ │ ├── environment.js
│ │ │ ├── loaders
│ │ │ ├── svelte-ssr.js
│ │ │ └── svelte.js
│ │ │ ├── production.js
│ │ │ └── test.js
│ ├── version.rb
│ └── view_helper.rb
│ └── renderer.rb
├── package.json
├── spec
├── spec_helper.rb
└── svelte
│ └── rails_spec.rb
├── svelte-rails.gemspec
├── svelte_ujs
├── WaitPlugin.js
├── getSvelteEnvironments.js
└── index.js
├── test.sh
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | tab_width = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.github/workflows/test-new-rails-app.yml:
--------------------------------------------------------------------------------
1 | name: Test new Rails app
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Set up Ruby
16 | uses: ruby/setup-ruby@v1
17 | with:
18 | ruby-version: 2.7
19 | - name: Install dependencies
20 | run: bundle install
21 | - name: Use Node.js 14.x
22 | uses: actions/setup-node@v1
23 | with:
24 | node-version: 14.x
25 | - name: Create new Rails app and run system tests
26 | run: ./test.sh
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /pkg/
6 | /spec/reports/
7 | /tmp/
8 | /node_modules/
9 |
10 | # rspec failure tracking
11 | .rspec_status
12 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 14
2 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: ruby
3 | cache: bundler
4 | rvm:
5 | - 2.7.1
6 | before_install: gem install bundler -v 2.1.4
7 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nning/svelte-rails/209df99682ea576b642e1ce88c4aa2746ed78a7c/CHANGELOG.md
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at henning.mueller@hmmh.de. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [https://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: https://contributor-covenant.org
74 | [version]: https://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | # Specify your gem's dependencies in svelte-rails.gemspec
4 | gemspec
5 |
6 | gem "rake", "~> 12.0"
7 | gem "rspec", "~> 3.0"
8 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | svelte-rails (0.3.4)
5 | execjs
6 | railties (>= 5.2)
7 |
8 | GEM
9 | remote: https://rubygems.org/
10 | specs:
11 | actioncable (6.0.3.4)
12 | actionpack (= 6.0.3.4)
13 | nio4r (~> 2.0)
14 | websocket-driver (>= 0.6.1)
15 | actionmailbox (6.0.3.4)
16 | actionpack (= 6.0.3.4)
17 | activejob (= 6.0.3.4)
18 | activerecord (= 6.0.3.4)
19 | activestorage (= 6.0.3.4)
20 | activesupport (= 6.0.3.4)
21 | mail (>= 2.7.1)
22 | actionmailer (6.0.3.4)
23 | actionpack (= 6.0.3.4)
24 | actionview (= 6.0.3.4)
25 | activejob (= 6.0.3.4)
26 | mail (~> 2.5, >= 2.5.4)
27 | rails-dom-testing (~> 2.0)
28 | actionpack (6.0.3.4)
29 | actionview (= 6.0.3.4)
30 | activesupport (= 6.0.3.4)
31 | rack (~> 2.0, >= 2.0.8)
32 | rack-test (>= 0.6.3)
33 | rails-dom-testing (~> 2.0)
34 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
35 | actiontext (6.0.3.4)
36 | actionpack (= 6.0.3.4)
37 | activerecord (= 6.0.3.4)
38 | activestorage (= 6.0.3.4)
39 | activesupport (= 6.0.3.4)
40 | nokogiri (>= 1.8.5)
41 | actionview (6.0.3.4)
42 | activesupport (= 6.0.3.4)
43 | builder (~> 3.1)
44 | erubi (~> 1.4)
45 | rails-dom-testing (~> 2.0)
46 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
47 | activejob (6.0.3.4)
48 | activesupport (= 6.0.3.4)
49 | globalid (>= 0.3.6)
50 | activemodel (6.0.3.4)
51 | activesupport (= 6.0.3.4)
52 | activerecord (6.0.3.4)
53 | activemodel (= 6.0.3.4)
54 | activesupport (= 6.0.3.4)
55 | activestorage (6.0.3.4)
56 | actionpack (= 6.0.3.4)
57 | activejob (= 6.0.3.4)
58 | activerecord (= 6.0.3.4)
59 | marcel (~> 0.3.1)
60 | activesupport (6.0.3.4)
61 | concurrent-ruby (~> 1.0, >= 1.0.2)
62 | i18n (>= 0.7, < 2)
63 | minitest (~> 5.1)
64 | tzinfo (~> 1.1)
65 | zeitwerk (~> 2.2, >= 2.2.2)
66 | builder (3.2.4)
67 | concurrent-ruby (1.1.7)
68 | crass (1.0.6)
69 | diff-lcs (1.4.4)
70 | erubi (1.10.0)
71 | execjs (2.7.0)
72 | globalid (0.4.2)
73 | activesupport (>= 4.2.0)
74 | i18n (1.8.5)
75 | concurrent-ruby (~> 1.0)
76 | loofah (2.8.0)
77 | crass (~> 1.0.2)
78 | nokogiri (>= 1.5.9)
79 | mail (2.7.1)
80 | mini_mime (>= 0.1.1)
81 | marcel (0.3.3)
82 | mimemagic (~> 0.3.2)
83 | method_source (1.0.0)
84 | mimemagic (0.3.5)
85 | mini_mime (1.0.2)
86 | mini_portile2 (2.4.0)
87 | minitest (5.14.2)
88 | nio4r (2.5.4)
89 | nokogiri (1.10.10)
90 | mini_portile2 (~> 2.4.0)
91 | rack (2.2.3)
92 | rack-test (1.1.0)
93 | rack (>= 1.0, < 3)
94 | rails (6.0.3.4)
95 | actioncable (= 6.0.3.4)
96 | actionmailbox (= 6.0.3.4)
97 | actionmailer (= 6.0.3.4)
98 | actionpack (= 6.0.3.4)
99 | actiontext (= 6.0.3.4)
100 | actionview (= 6.0.3.4)
101 | activejob (= 6.0.3.4)
102 | activemodel (= 6.0.3.4)
103 | activerecord (= 6.0.3.4)
104 | activestorage (= 6.0.3.4)
105 | activesupport (= 6.0.3.4)
106 | bundler (>= 1.3.0)
107 | railties (= 6.0.3.4)
108 | sprockets-rails (>= 2.0.0)
109 | rails-dom-testing (2.0.3)
110 | activesupport (>= 4.2.0)
111 | nokogiri (>= 1.6)
112 | rails-html-sanitizer (1.3.0)
113 | loofah (~> 2.3)
114 | railties (6.0.3.4)
115 | actionpack (= 6.0.3.4)
116 | activesupport (= 6.0.3.4)
117 | method_source
118 | rake (>= 0.8.7)
119 | thor (>= 0.20.3, < 2.0)
120 | rake (12.3.3)
121 | rspec (3.10.0)
122 | rspec-core (~> 3.10.0)
123 | rspec-expectations (~> 3.10.0)
124 | rspec-mocks (~> 3.10.0)
125 | rspec-core (3.10.0)
126 | rspec-support (~> 3.10.0)
127 | rspec-expectations (3.10.0)
128 | diff-lcs (>= 1.2.0, < 2.0)
129 | rspec-support (~> 3.10.0)
130 | rspec-mocks (3.10.0)
131 | diff-lcs (>= 1.2.0, < 2.0)
132 | rspec-support (~> 3.10.0)
133 | rspec-support (3.10.0)
134 | sprockets (4.0.2)
135 | concurrent-ruby (~> 1.0)
136 | rack (> 1, < 3)
137 | sprockets-rails (3.2.2)
138 | actionpack (>= 4.0)
139 | activesupport (>= 4.0)
140 | sprockets (>= 3.0.0)
141 | thor (1.0.1)
142 | thread_safe (0.3.6)
143 | tzinfo (1.2.8)
144 | thread_safe (~> 0.1)
145 | websocket-driver (0.7.3)
146 | websocket-extensions (>= 0.1.0)
147 | websocket-extensions (0.1.5)
148 | zeitwerk (2.4.2)
149 |
150 | PLATFORMS
151 | ruby
152 |
153 | DEPENDENCIES
154 | rails (>= 5.2)
155 | rake (~> 12.0)
156 | rspec (~> 3.0)
157 | svelte-rails!
158 |
159 | BUNDLED WITH
160 | 2.1.4
161 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020 henning mueller
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Svelte-Rails
2 |
3 | [](https://rubygems.org/gems/svelte-rails)
4 | [](https://www.npmjs.com/package/svelte_ujs_ng)
5 | 
6 |
7 | Svelte-Rails integrates [Svelte](https://svelte.dev/) with Ruby on Rails. It has the following features:
8 |
9 | * Automatically renders Svelte server-side and client-side
10 | * Supports Webpacker >= 4.2 and Ruby on Rails >= 6
11 |
12 | ## Usage
13 |
14 | Make sure, you have [set-up Webpacker](https://github.com/rails/webpacker#installation) and it's [Svelte integration](https://github.com/rails/webpacker/blob/master/docs/integrations.md#svelte).
15 |
16 | For a quick start with a new app, simply run:
17 |
18 | rails new demo --webpack=svelte
19 |
20 | Add this line to your application's Gemfile:
21 |
22 | ```ruby
23 | gem 'svelte-rails'
24 | ```
25 |
26 | And then execute:
27 |
28 | $ bundle
29 | $ rails svelte:install
30 |
31 | You can overwrite conflicting files if you have started with a fresh Rails app or did not change the webpack config of your existing one.
32 |
33 | An example Rails app demonstrating the integration of svelte-rails can be found here:
34 | https://github.com/nning/svelte-rails-demo/commits/master
35 |
36 | ## View Helper
37 |
38 | ```erb
39 | <%= svelte_component :Hello, name: 'Svelte' %>
40 | <%= svelte_component :Hello, {name: 'Svelte'}, {prerender: true} %>
41 | ```
42 |
43 | ## Controller Renderer
44 |
45 | ```ruby
46 | class TodoController < ApplicationController
47 | def index
48 | @todos = Todo.all
49 | render component: 'TodoList', props: { todos: @todos }
50 | end
51 | end
52 | ```
53 |
54 | `prerender` is activated by default, can be disabled with `prerender: false`.
55 |
56 | ## Missing Features
57 |
58 | * HMR and Bundle consistency (server-rendered HTML is cached and client-side updates on changes to the sources)
59 | * Generator for components
60 | * Render pools
61 | * Better documentation for setup
62 |
63 | ## Configuration Options
64 |
65 | Configuration can be changed in `config/application.rb`, for example.
66 |
67 | # Prerender (SSR) by default (i.e. without passing `prerender:true` to the view helper)
68 | config.svelte.prerender_default = false
69 |
70 | ## Contributing
71 |
72 | Bug reports and pull requests are welcome on GitHub at https://github.com/nning/svelte-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nning/svelte-rails/blob/master/CODE_OF_CONDUCT.md).
73 |
74 | ## License
75 |
76 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
77 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 |
4 | RSpec::Core::RakeTask.new(:spec)
5 |
6 | task :default => :spec
7 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require "svelte/rails"
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 |
13 | require "irb"
14 | IRB.start(__FILE__)
15 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/doc/release.md:
--------------------------------------------------------------------------------
1 | * Update versions in `package.json` and `lib/svelte/rails/version.rb`
2 | * Run `bundle`
3 | * `git commit -m 'v0.3.4'`
4 | * `git tag -s v0.3.4`
5 | * `npm publish`
6 | * `rake release`
7 | * Create GitHub release
8 | * `git push`
9 | * `git push origin v0.3.4`
10 |
--------------------------------------------------------------------------------
/lib/svelte/rails.rb:
--------------------------------------------------------------------------------
1 | require 'svelte/rails/version'
2 |
3 | module Svelte
4 | module Rails
5 | # class Error < StandardError; end
6 | end
7 | end
8 |
9 | require 'svelte/rails/view_helper'
10 | require 'svelte/rails/controller_renderer'
11 |
12 | require 'svelte/rails/railtie' if defined?(Rails)
13 |
--------------------------------------------------------------------------------
/lib/svelte/rails/controller_renderer.rb:
--------------------------------------------------------------------------------
1 | require 'svelte/rails/view_helper'
2 |
3 | module Svelte
4 | module Rails
5 | class ControllerRenderer
6 | include Svelte::Rails::ViewHelper
7 | # include ActionView::Helpers::TagHelper
8 | # include ActionView::Helpers::TextHelper
9 |
10 | attr_accessor :output_buffer
11 |
12 | # @return [String] HTML for `component_name` with `options[:props]`
13 | def call(component_name, options, &block)
14 | props = options.fetch(:props, {})
15 | options = default_options.merge(options.slice(:data, :aria, :tag, :class, :id, :prerender, :camelize_props))
16 | svelte_component(component_name, props, options, &block)
17 | end
18 |
19 | private
20 |
21 | def default_options
22 | { prerender: true }
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/svelte/rails/install_generator.rb:
--------------------------------------------------------------------------------
1 | require 'rails/generators'
2 |
3 |
4 | module Svelte
5 | class InstallGenerator < ::Rails::Generators::Base
6 | TEMPLATE_DIR = File.expand_path('../templates', __FILE__)
7 | source_root TEMPLATE_DIR
8 |
9 | desc 'Install Svelte support'
10 |
11 | def create_directories
12 | empty_directory components_dir
13 | create_file File.join(components_dir, '.keep')
14 | end
15 |
16 | def copy_templates
17 | copy_template(webpack_dir, 'environment.js')
18 | copy_template(webpack_dir, 'development.js')
19 | copy_template(webpack_dir, 'production.js')
20 | copy_template(webpack_dir, 'test.js')
21 |
22 | copy_template(webpack_dir, 'loaders', 'svelte.js')
23 | copy_template(webpack_dir, 'loaders', 'svelte-ssr.js')
24 |
25 | copy_template(packs_dir, 'server_rendering.js')
26 | copy_template(components_dir, 'Hello.svelte')
27 | end
28 |
29 | def update_application_entry
30 | path = Pathname.new('app/javascript/packs/application.js')
31 | content = File.read(File.join(TEMPLATE_DIR, path))
32 |
33 | if path.exist?
34 | append_file(path, content)
35 | else
36 | create_file(path, content)
37 | end
38 | end
39 |
40 | def install_svelte_ujs
41 | `yarn add svelte_ujs_ng svelte-preprocess`
42 | end
43 |
44 | private
45 |
46 | def rails_dir
47 | Pathname.new(destination_root).relative_path_from(::Rails.root)
48 | end
49 |
50 | def packs_dir
51 | Webpacker.config.source_entry_path.relative_path_from(::Rails.root)
52 | end
53 |
54 | def js_dir
55 | packs_dir.parent
56 | end
57 |
58 | def components_dir
59 | File.join(js_dir, 'components')
60 | end
61 |
62 | def webpack_dir
63 | File.join(rails_dir, 'config', 'webpack')
64 | end
65 |
66 | def copy_template(*path_segments)
67 | source = File.join(*path_segments)
68 | template(source, ::Rails.root.join(source))
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/svelte/rails/install_task.rake:
--------------------------------------------------------------------------------
1 | require 'svelte/rails/install_generator'
2 |
3 | namespace :svelte do
4 | desc 'Install Svelte support'
5 | task :install do
6 | Svelte::InstallGenerator.start
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/svelte/rails/railtie.rb:
--------------------------------------------------------------------------------
1 | require 'rails/railtie'
2 |
3 | module Svelte::Rails
4 | class Railtie < ::Rails::Railtie
5 | config.svelte = ActiveSupport::OrderedOptions.new
6 |
7 | # Prerender (SSR) by default (i.e. without passing `prerender:true` to the view helper)
8 | config.svelte.prerender_default = false
9 |
10 | initializer 'svelte_rails.setup_view_helpers', after: :load_config_initializers, group: :all do |app|
11 | ActiveSupport.on_load(:action_view) do
12 | include ::Svelte::Rails::ViewHelper
13 | end
14 | end
15 |
16 | initializer 'svelte_rails.add_component_renderers', group: :all do |app|
17 | render_component = lambda do |component_name, options|
18 | renderer = ::Svelte::Rails::ControllerRenderer.new
19 | html = renderer.call(component_name, options)
20 | render_options = options.merge(inline: html)
21 | render(render_options)
22 | end
23 |
24 | %i[component svelte svelte_component].each do |renderer_name|
25 | ActionController::Renderers.add renderer_name, &render_component
26 | end
27 | end
28 |
29 | rake_tasks do
30 | load 'svelte/rails/install_task.rake'
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/app/javascript/components/Hello.svelte:
--------------------------------------------------------------------------------
1 |
{time}
36 | {/if}
37 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | import SvelteRailsUJS from 'svelte_ujs_ng';
2 | self.SvelteRailsUJS = SvelteRailsUJS;
3 | SvelteRailsUJS.start();
4 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/app/javascript/packs/server_rendering.js:
--------------------------------------------------------------------------------
1 | import SvelteRailsUJS from 'svelte_ujs_ng';
2 | self.SvelteRailsUJS = SvelteRailsUJS;
3 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.map(x => x.toWebpackConfig())
6 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const clientLoader = require('./loaders/svelte')
2 | const serverLoader = require('./loaders/svelte-ssr')
3 |
4 | const getSvelteEnvironments = require('svelte_ujs_ng/getSvelteEnvironments')
5 |
6 | module.exports = getSvelteEnvironments(clientLoader, serverLoader)
7 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/loaders/svelte-ssr.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | test: /\.svelte$/,
3 | use: [{
4 | loader: 'svelte-loader',
5 | options: {
6 | generate: 'ssr',
7 | emitCss: false,
8 | css: false
9 | }
10 | }],
11 | }
12 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/loaders/svelte.js:
--------------------------------------------------------------------------------
1 | const sveltePreprocess = require('svelte-preprocess')
2 |
3 | const dev = process.env.RAILS_ENV !== 'production'
4 |
5 | module.exports = {
6 | test: /\.svelte$/,
7 | use: [
8 | {
9 | loader: 'babel-loader',
10 | options: {
11 | presets: ['@babel/preset-env'],
12 | }
13 | },
14 | {
15 | loader: 'svelte-loader',
16 | options: {
17 | dev,
18 | hotReload: true,
19 | hydratable: true,
20 | emitCss: true,
21 | preprocess: sveltePreprocess()
22 | }
23 | }
24 | ],
25 | }
26 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.map(x => x.toWebpackConfig())
6 |
--------------------------------------------------------------------------------
/lib/svelte/rails/templates/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.map(x => x.toWebpackConfig())
6 |
--------------------------------------------------------------------------------
/lib/svelte/rails/version.rb:
--------------------------------------------------------------------------------
1 | module Svelte
2 | module Rails
3 | VERSION = '0.3.4'
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/svelte/rails/view_helper.rb:
--------------------------------------------------------------------------------
1 | require 'svelte/renderer'
2 |
3 | module Svelte::Rails::ViewHelper
4 | def svelte_component(*args, &block)
5 | Svelte::Renderer.new.render(*args) { capture &block if block_given? }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/svelte/renderer.rb:
--------------------------------------------------------------------------------
1 | require 'execjs'
2 | require 'action_view'
3 |
4 | module Svelte
5 | class Renderer
6 | include ActionView::Helpers::TagHelper
7 | include ActionView::Helpers::TextHelper
8 |
9 | attr_accessor :output_buffer
10 |
11 | # This pattern matches the code that initializes the dev-server client.
12 | CLIENT_REQUIRE = %r{__webpack_require__\(.*webpack-dev-server\/client\/index\.js.*\n}
13 |
14 | def render(name, props = {}, options = {}, &block)
15 | prerender_options = options[:prerender]
16 |
17 | if prerender_options.nil?
18 | prerender_options = ::Rails.application.config.svelte.prerender_default
19 | end
20 |
21 | if prerender_options
22 | block = Proc.new { concat(prerender_component(name, props, prerender_options)) }
23 | end
24 |
25 | html_options = options.reverse_merge(:data => {})
26 | unless prerender_options == :static
27 | html_options[:data].tap do |data|
28 | data[:svelte_class] = name
29 | data[:svelte_props] = props.is_a?(String) ? props : props.to_json
30 | data[:hydrate] = 't' if prerender_options
31 | end
32 | end
33 |
34 | html_options.except!(:tag, :prerender, :camelize_props)
35 |
36 | content_tag(options[:tag] || :div, '', html_options, &block)
37 | end
38 |
39 | private
40 |
41 | def prerender_component(component_name, props, prerender_options)
42 | initial_code = <<-JS
43 | var global = global || this;
44 | var self = self || this;
45 |
46 | #{find_asset('server_rendering.js')}
47 | JS
48 |
49 | # File.write(::Rails.root.join('debug.js'), initial_code)
50 | @context = ExecJS.compile(initial_code)
51 |
52 | js_code = <<-JS
53 | (function(){
54 | return SvelteRailsUJS.serverRender('#{component_name}', #{props.to_json});
55 | })()
56 | JS
57 |
58 | @context.eval(js_code).html_safe
59 | end
60 |
61 | def find_asset(logical_path)
62 | if Webpacker.dev_server.running?
63 | ds = Webpacker.dev_server
64 |
65 | asset_path = Webpacker.manifest.lookup(logical_path).to_s
66 | # Remove the protocol and host from the asset path. Sometimes webpacker includes this, sometimes it does not
67 | asset_path.slice!("#{ds.protocol}://#{ds.host_with_port}")
68 |
69 | require 'open-uri'
70 |
71 | dev_server_asset = URI.send(:open, "#{ds.protocol}://#{ds.host_with_port}#{asset_path}").read
72 | dev_server_asset.sub!(CLIENT_REQUIRE, '//\0')
73 | dev_server_asset
74 | else
75 | File.read(::Rails.root.join('public', Webpacker.manifest.lookup(logical_path)[1..-1]))
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte_ujs_ng",
3 | "version": "0.3.4",
4 | "description": "Rails UJS for turbolinks and SSR of the svelte-rails gem",
5 | "license": "MIT",
6 | "author": "henning mueller