├── test ├── test_helper.exs └── sveltex_test.exs ├── .formatter.exs ├── .gitignore ├── lib └── sveltex.ex ├── assets └── sveltex.js ├── mix.exs ├── LICENSE ├── config └── config.exs ├── mix.lock └── README.md /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | .elixir_ls 11 | 12 | # Where 3rd-party dependencies like ExDoc output generated docs. 13 | /doc/ 14 | 15 | # Ignore .fetch files in case you like to edit your project deps locally. 16 | /.fetch 17 | 18 | # If the VM crashes, it generates a dump, let's ignore it too. 19 | erl_crash.dump 20 | 21 | # Also ignore archive artifacts (built via "mix archive.build"). 22 | *.ez 23 | 24 | # Ignore package tarball (built via "mix hex.build"). 25 | sveltex-*.tar 26 | 27 | -------------------------------------------------------------------------------- /lib/sveltex.ex: -------------------------------------------------------------------------------- 1 | defmodule Sveltex do 2 | @moduledoc """ 3 | Sveltex is a small library to make embedding svelte component as easy as possible when it comes to works with Phoenix. 4 | """ 5 | 6 | @doc """ 7 | Main function to turn markup into html component which later on being parsed by the javascript side. 8 | 9 | """ 10 | def render(name, props) do 11 | Phoenix.HTML.Tag.tag(:div, 12 | data: [props: payload(props)], 13 | id: "sveltex-#{String.replace(name, " ", "-")}" 14 | ) 15 | end 16 | 17 | defp payload(props) do 18 | props 19 | |> Jason.encode() 20 | |> case do 21 | {:ok, message} -> message 22 | {:error, _} -> "" 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /assets/sveltex.js: -------------------------------------------------------------------------------- 1 | const context = require.context("../../../assets/js/svelte", false, /\.svelte/); 2 | window.onload = function() { 3 | context.keys().forEach(file => { 4 | const componentName = file.replace(/\.\/|\.svelte/g, ""); 5 | const targetId = `sveltex-${componentName}`; 6 | 7 | const root = document.getElementById(targetId); 8 | 9 | if (!root) { 10 | return; 11 | } 12 | 13 | const requiredApp = require(`../../../assets/js/svelte/${componentName}.svelte`); 14 | 15 | const props = root.getAttribute("data-props"); 16 | let parsedProps = {}; 17 | if (props) { 18 | parsedProps = JSON.parse(props); 19 | } 20 | 21 | new requiredApp.default({ 22 | target: root, 23 | props: parsedProps 24 | }); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Sveltex.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :sveltex, 7 | version: "0.1.2", 8 | elixir: "~> 1.7", 9 | start_permanent: Mix.env() == :prod, 10 | description: description(), 11 | package: package(), 12 | deps: deps() 13 | ] 14 | end 15 | 16 | defp description do 17 | """ 18 | Embed Svelte component easily into Phoenix App. 19 | """ 20 | end 21 | 22 | defp package do 23 | [ 24 | files: ["lib", "mix.exs", "README*", "LICENSE*"], 25 | maintainers: ["virkillz"], 26 | licenses: ["MIT"], 27 | links: %{"GitHub" => "https://github.com/virkillz/sveltex"} 28 | ] 29 | end 30 | 31 | # Run "mix help compile.app" to learn about applications. 32 | def application do 33 | [ 34 | extra_applications: [:logger] 35 | ] 36 | end 37 | 38 | # Run "mix help deps" to learn about dependencies. 39 | defp deps do 40 | [ 41 | {:phoenix_html, "~> 2.13"}, 42 | {:jason, "~> 1.1"}, 43 | {:ex_doc, "~> 0.18", only: :dev} 44 | ] 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Geoffrey Lessel 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. 22 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for 9 | # 3rd-party users, it should be done in your "mix.exs" file. 10 | 11 | # You can configure your application as: 12 | # 13 | # config :sveltex, key: :value 14 | # 15 | # and access this configuration in your application as: 16 | # 17 | # Application.get_env(:sveltex, :key) 18 | # 19 | # You can also configure a 3rd-party app: 20 | # 21 | # config :logger, level: :info 22 | # 23 | 24 | # It is also possible to import configuration files, relative to this 25 | # directory. For example, you can emulate configuration per environment 26 | # by uncommenting the line below and defining dev.exs, test.exs and such. 27 | # Configuration from the imported file will override the ones defined 28 | # here (which is why it is important to import them last). 29 | # 30 | # import_config "#{Mix.env()}.exs" 31 | -------------------------------------------------------------------------------- /test/sveltex_test.exs: -------------------------------------------------------------------------------- 1 | defmodule SveltexTest do 2 | use ExUnit.Case 3 | doctest Sveltex 4 | 5 | test "basic test" do 6 | assert Sveltex.sveltex("test", %{}) == 7 | {:safe, 8 | [ 9 | 60, 10 | "div", 11 | [ 12 | [32, "data-props", 61, 34, "{}", 34], 13 | [32, "id", 61, 34, "sveltex-test", 34] 14 | ], 15 | 62 16 | ]} 17 | end 18 | 19 | test "with props" do 20 | assert Sveltex.sveltex("header", %{name: "virkill"}) == 21 | {:safe, 22 | [ 23 | 60, 24 | "div", 25 | [ 26 | [ 27 | 32, 28 | "data-props", 29 | 61, 30 | 34, 31 | [ 32 | [ 33 | [[[[], "{" | """], "name" | """], ":" | """], 34 | "virkill" 35 | | """ 36 | ] 37 | | "}" 38 | ], 39 | 34 40 | ], 41 | [32, "id", 61, 34, "sveltex-header", 34] 42 | ], 43 | 62 44 | ]} 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm"}, 3 | "ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, 4 | "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, 5 | "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, 6 | "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, 7 | "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, 8 | "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm"}, 9 | "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, 10 | "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, 11 | "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"}, 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sveltex 2 | 3 | Building web using Phoenix is fun. 4 | Creating component using Svelte is fun. 5 | 6 | Imagine we can just create svelte component, save it as `component_name.svelte` inside our `/assets/js/svelte` and anytime we need it inside our layout, we can just call it like so: 7 | 8 | `<%= Sveltex.render "component_name", %{props_as_map: ""} %>` 9 | 10 | Sveltex does exactly this. 11 | 12 | ## Install in 4 easy step. 13 | 14 | This library works well with Phoenix 1.4.12 and above. (Last time tested with Phoenix 1.5.9) 15 | 16 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed 17 | by adding `sveltex` to your list of dependencies in `mix.exs`: 18 | 19 | 1. Install `Sveltex` as Elixir dependency. 20 | 21 | Add the dependency into your `mix.exs` 22 | 23 | ```elixir 24 | def deps do 25 | [ 26 | {:sveltex, git: "https://github.com/virkillz/sveltex.git", tag: "1.1.2"} 27 | ] 28 | end` 29 | 30 | ``` 31 | 32 | Then run: 33 | 34 | ``` 35 | mix deps.get 36 | ``` 37 | 38 | 2. Install Svelte & Svelte Loader as Javascript dependency 39 | ``` 40 | cd assets && npm install svelte svelte-loader --save 41 | ``` 42 | 43 | 3. Edit your Webpack config file `/assets/webpack.config.js` 44 | 45 | Add resolve: 46 | 47 | ```javascript 48 | 49 | module.exports = (env, options) => ({ 50 | 51 | resolve: { 52 | alias: { 53 | svelte: path.resolve('node_modules', 'svelte') 54 | }, 55 | extensions: ['.mjs', '.js', '.svelte'], 56 | mainFields: ['svelte', 'browser', 'module', 'main'], 57 | modules: ['node_modules'] 58 | }, 59 | ``` 60 | 61 | And add 2 new rules under `module rules` 62 | 63 | ```javascript 64 | 65 | module: { 66 | rules: [ 67 | { 68 | test: /\.mjs$/, 69 | include: /node_modules/, 70 | type: 'javascript/auto', 71 | }, 72 | { 73 | test: /\.(html|svelte)$/, 74 | exclude: /node_modules/, 75 | use: { 76 | loader: 'svelte-loader', 77 | options: { 78 | hotReload: true 79 | } 80 | } 81 | } 82 | ] 83 | } 84 | }); 85 | ``` 86 | 87 | 4. Import to your `app.js` (`/assets/js/app.js` file). 88 | 89 | ```javascript 90 | 91 | import "../../deps/sveltex/assets/sveltex.js"; 92 | 93 | ``` 94 | 95 | 96 | ## How to use ? 97 | 98 | Put your svelte component files (`.svelte`) under `/assets/js/svelte` directory. 99 | 100 | Example: `test.svelte` 101 | 102 | ```html 103 | 106 | 107 |