Packages
surface
0.8.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.1.0-alpha.2
0.1.0-alpha.1
0.1.0-alpha.0
A component based library for Phoenix LiveView
Current section
46 Versions
Jump to
Current section
46 Versions
Compare versions
149
files changed
+6228
additions
-2529
deletions
| @@ -6,7 +6,10 @@ locals_without_parens = [ | |
| 6 6 | data: 3, |
| 7 7 | data: 2, |
| 8 8 | slot: 1, |
| 9 | - slot: 2 |
| 9 | + slot: 2, |
| 10 | + catalogue_test: 1, |
| 11 | + catalogue_test: 2, |
| 12 | + load_asset: 2 |
| 10 13 | ] |
| 11 14 | |
| 12 15 | [ |
| @@ -1,8 +1,55 @@ | |
| 1 1 | # Changelog |
| 2 2 | |
| 3 | - ## v0.7.6 (2022-09-05) |
| 3 | + ## v0.8.0 |
| 4 4 | |
| 5 5 | * Support Elixir `v1.14` |
| 6 | + * Support scoped CSS styles for components using either inline `<style>` tags or colocated |
| 7 | + `.css` files (#621) |
| 8 | + * Add `render_sface/1` to allow overriding `render/1` and compute/update assigns when using |
| 9 | + external `.sface` files |
| 10 | + * Add `--tailwind` option to `mix surface.init` to bootstrap a project with TailwindCSS support |
| 11 | + * Add `--layouts` option to `mix surface.init` to replace `.heex` layout files with corresponding |
| 12 | + `.sface` files |
| 13 | + * Add `--web-module` option to `mix surface.init` to override the default web module (#638) |
| 14 | + * Support using the `:hook` directive to point to the `default` hook exported in the `.hooks.js` file |
| 15 | + * Add option `from_context` to `prop` and `data` to allow setting values directly from the context |
| 16 | + * Add `Context.put/3` and `Context.get/2` to allow manipulating the context inside |
| 17 | + lifecycle callbacks and `render/1` |
| 18 | + * Add prop `context_put` to `<#slot>` to pass context values directly to a slot without propagating |
| 19 | + context values to other components |
| 20 | + * Add config `:propagate_context_to_slots` to restrict context propagatiion, optimizing diff tracking |
| 21 | + * Add functions `Context.copy_assign/3`, `Context.maybe_copy_assign/3` and `Context.maybe_copy_assign!/3` |
| 22 | + * Add `catalogue_test/1` macro to generate basic tests for catalogue examples and playgrounds |
| 23 | + * Add module `Surface.Catalogue.Examples` to allow defining multiple stateless examples |
| 24 | + in a single module |
| 25 | + * Support editing slot values as text in playgrounds (Catalogue) |
| 26 | + * Fix context propagation in dynamic components |
| 27 | + * Fix context propagation in recursive components |
| 28 | + * New API for Slot arguments and generator (#613) |
| 29 | + |
| 30 | + ### Deprecations |
| 31 | + |
| 32 | + * Deprecate `<InputContext>` in favor of declarative option `from_context` |
| 33 | + * Slots (#613) |
| 34 | + * Option `:args` has been deprecated, use |
| 35 | + * `slot arg: :string` instead of `slot args: [:name]` |
| 36 | + * `slot arg: %{name: :string, age: number}` instead of `slot args: [:name, :age]` |
| 37 | + * Attribute `for` has been deprecated, use `<#slot {@header}>` instead of`<#slot for={@header}>` |
| 38 | + * Attributes `name` and `index` have been deprecated, use `<#slot {col}>` instead of`<#slot name={"col"} index={index}>` |
| 39 | + * Directive `:args` has been deprecated, use |
| 40 | + * `<#slot {@default, name}>` instead of `<#slot :args={name: name}>` |
| 41 | + * `<#slot {@default, name: name, age: age}>` instead of `<#slot :args={name: name, age: age}>` |
| 42 | + |
| 43 | + ### Breaking Changes |
| 44 | + |
| 45 | + * Drop support for Elixir < `v1.12` |
| 46 | + * Context values are no longer automatically propagated through slots. Components that need to |
| 47 | + pass values to the parent scope via slots must explicitly set `propagate_context_to_slots: true` |
| 48 | + in their configuration |
| 49 | + * Slots (#613) |
| 50 | + * New option `:generator_prop` use `slot default, generator_prop: :items` instead of `slot default, args: [item: ^items]`, associated prop `:items` must be of type `:generator` |
| 51 | + * New attribute `generator_value` use `<#slot generator_value={item} />` instead of `<#slot :args={item: item} />` |
| 52 | + * `<#template slot="slot_name">` has been removed in favor of `<:slot_name>` (#575) |
| 6 53 | |
| 7 54 | ## v0.7.5 (2022-07-21) |
| @@ -76,7 +76,7 @@ Add `surface` to the list of dependencies in `mix.exs`: | |
| 76 76 | ```elixir |
| 77 77 | def deps do |
| 78 78 | [ |
| 79 | - {:surface, "~> 0.7.5"} |
| 79 | + {:surface, "~> 0.8.0"} |
| 80 80 | ] |
| 81 81 | end |
| 82 82 | ``` |
| @@ -116,20 +116,7 @@ mix help surface.init | |
| 116 116 | For further information regarding installation, including how to install Surface manually, |
| 117 117 | please visit the [Getting Started](https://surface-ui.org/getting_started) guide. |
| 118 118 | |
| 119 | - ## Migrating from `v0.5.x` to `v0.6.x` |
| 120 | - |
| 121 | - Surface `v0.6.x` relies on the Liveview features available since `v0.16`. The main change |
| 122 | - from the user perspective is that the stateless `Surface.Component` now is built on top of |
| 123 | - `Phoenix.Component` instead of `Phoenix.LiveComponent`. This means the `mount/1`, `preload/1` |
| 124 | - and `update/2` callbacks are no longer available. If you initialize any assign or compute |
| 125 | - any value using those callbacks, you need to replace them with one of the new |
| 126 | - [assign helpers](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#module-assigns). |
| 127 | - |
| 128 | - ## Migrating from `v0.4.x` to `v0.5.x` |
| 129 | - |
| 130 | - Surface `v0.5.0` introduces a new syntax which requires migrating components written in previous versions. |
| 131 | - In order to make the migration process as smooth as possible, Surface `v0.5.x` ships with a converter that |
| 132 | - can automatically translate the old syntax into the new one. |
| 119 | + ## Migrating from previous versions |
| 133 120 | |
| 134 121 | Please see the [Migration Guide](MIGRATING.md) for details. |
| @@ -1,7 +1,7 @@ | |
| 1 1 | {<<"app">>,<<"surface">>}. |
| 2 2 | {<<"build_tools">>,[<<"mix">>]}. |
| 3 3 | {<<"description">>,<<"A component based library for Phoenix LiveView">>}. |
| 4 | - {<<"elixir">>,<<"~> 1.11">>}. |
| 4 | + {<<"elixir">>,<<"~> 1.12">>}. |
| 5 5 | {<<"files">>, |
| 6 6 | [<<"lib">>,<<"lib/surface">>,<<"lib/surface/renderer.ex">>, |
| 7 7 | <<"lib/surface/formatter.ex">>,<<"lib/surface/compiler.ex">>, |
| @@ -70,23 +70,27 @@ | |
| 70 70 | <<"lib/surface/directive/show.ex">>,<<"lib/surface/directive/values.ex">>, |
| 71 71 | <<"lib/surface/directive/component_props.ex">>, |
| 72 72 | <<"lib/surface/directive/for.ex">>,<<"lib/surface/directive/if.ex">>, |
| 73 | - <<"lib/surface/directive/slot_props.ex">>, |
| 74 73 | <<"lib/surface/directive/let.ex">>,<<"lib/surface/directive/debug.ex">>, |
| 75 74 | <<"lib/surface/live_view_test.ex">>,<<"lib/surface/type_handler.ex">>, |
| 76 75 | <<"lib/surface/catalogue.ex">>,<<"lib/surface/api.ex">>, |
| 77 76 | <<"lib/surface/catalogue">>,<<"lib/surface/catalogue/playground.ex">>, |
| 78 77 | <<"lib/surface/catalogue/data.ex">>,<<"lib/surface/catalogue/example.ex">>, |
| 79 | - <<"lib/surface/view.ex">>,<<"lib/surface/compiler">>, |
| 78 | + <<"lib/surface/catalogue/examples.ex">>,<<"lib/surface/view.ex">>, |
| 79 | + <<"lib/surface/compiler">>,<<"lib/surface/compiler/converter_0_8.ex">>, |
| 80 80 | <<"lib/surface/compiler/converter.ex">>, |
| 81 81 | <<"lib/surface/compiler/parse_tree_translator.ex">>, |
| 82 | + <<"lib/surface/compiler/css_tokenizer.ex">>, |
| 82 83 | <<"lib/surface/compiler/node_translator.ex">>, |
| 84 | + <<"lib/surface/compiler/css_parser_error.ex">>, |
| 83 85 | <<"lib/surface/compiler/parse_error.ex">>, |
| 84 | - <<"lib/surface/compiler/converter_0_5.ex">>, |
| 85 86 | <<"lib/surface/compiler/helpers.ex">>, |
| 86 87 | <<"lib/surface/compiler/eex_engine.ex">>, |
| 87 88 | <<"lib/surface/compiler/tokenizer.ex">>, |
| 88 | - <<"lib/surface/compiler/parser.ex">>,<<"lib/surface/live_component.ex">>, |
| 89 | - <<"lib/surface/directive.ex">>,<<"lib/surface/type_handler">>, |
| 89 | + <<"lib/surface/compiler/css_translator.ex">>, |
| 90 | + <<"lib/surface/compiler/parser.ex">>, |
| 91 | + <<"lib/surface/compiler/css_parser.ex">>, |
| 92 | + <<"lib/surface/live_component.ex">>,<<"lib/surface/directive.ex">>, |
| 93 | + <<"lib/surface/type_handler">>, |
| 90 94 | <<"lib/surface/type_handler/static_list.ex">>, |
| 91 95 | <<"lib/surface/type_handler/context_get.ex">>, |
| 92 96 | <<"lib/surface/type_handler/hook.ex">>, |
| @@ -95,44 +99,76 @@ | |
| 95 99 | <<"lib/surface/type_handler/event.ex">>, |
| 96 100 | <<"lib/surface/type_handler/list.ex">>, |
| 97 101 | <<"lib/surface/type_handler/style.ex">>, |
| 98 | - <<"lib/surface/type_handler/explict_keyword.ex">>, |
| 102 | + <<"lib/surface/type_handler/render_slot.ex">>, |
| 99 103 | <<"lib/surface/type_handler/phx_event.ex">>, |
| 100 104 | <<"lib/surface/type_handler/atom.ex">>, |
| 101 105 | <<"lib/surface/type_handler/default.ex">>, |
| 102 106 | <<"lib/surface/type_handler/dynamic.ex">>, |
| 107 | + <<"lib/surface/type_handler/let_arg.ex">>, |
| 103 108 | <<"lib/surface/type_handler/map.ex">>, |
| 104 109 | <<"lib/surface/type_handler/keyword.ex">>, |
| 105 110 | <<"lib/surface/type_handler/generator.ex">>, |
| 106 111 | <<"lib/surface/type_handler/form.ex">>, |
| 107 112 | <<"lib/surface/type_handler/context_put.ex">>, |
| 108 | - <<"lib/surface/type_handler/css_class.ex">>, |
| 109 | - <<"lib/surface/type_handler/bindings.ex">>,<<"lib/surface/ast.ex">>, |
| 113 | + <<"lib/surface/type_handler/css_class.ex">>,<<"lib/surface/ast.ex">>, |
| 110 114 | <<"lib/mix">>,<<"lib/mix/tasks">>,<<"lib/mix/tasks/surface">>, |
| 111 115 | <<"lib/mix/tasks/surface/surface.format.ex">>, |
| 112 116 | <<"lib/mix/tasks/surface/surface.init">>, |
| 117 | + <<"lib/mix/tasks/surface/surface.init/file_patchers">>, |
| 118 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/js.ex">>, |
| 119 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/formatter.ex">>, |
| 120 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/config.ex">>, |
| 121 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/component.ex">>, |
| 122 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/phoenix.ex">>, |
| 123 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/text.ex">>, |
| 124 | + <<"lib/mix/tasks/surface/surface.init/file_patchers/mix_exs.ex">>, |
| 113 125 | <<"lib/mix/tasks/surface/surface.init/ex_patcher.ex">>, |
| 114 | - <<"lib/mix/tasks/surface/surface.init/patchers">>, |
| 115 | - <<"lib/mix/tasks/surface/surface.init/patchers/js.ex">>, |
| 116 | - <<"lib/mix/tasks/surface/surface.init/patchers/formatter.ex">>, |
| 117 | - <<"lib/mix/tasks/surface/surface.init/patchers/component.ex">>, |
| 118 | - <<"lib/mix/tasks/surface/surface.init/patchers/phoenix.ex">>, |
| 119 | - <<"lib/mix/tasks/surface/surface.init/patchers/text.ex">>, |
| 120 | - <<"lib/mix/tasks/surface/surface.init/patchers/mix_exs.ex">>, |
| 121 126 | <<"lib/mix/tasks/surface/surface.init/ex_patcher_move.ex">>, |
| 122 | - <<"lib/mix/tasks/surface/surface.init/patches.ex">>, |
| 127 | + <<"lib/mix/tasks/surface/surface.init/project_patcher.ex">>, |
| 123 128 | <<"lib/mix/tasks/surface/surface.init/patcher.ex">>, |
| 129 | + <<"lib/mix/tasks/surface/surface.init/project_patchers">>, |
| 130 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/js_hooks.ex">>, |
| 131 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/formatter.ex">>, |
| 132 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/layouts.ex">>, |
| 133 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/demo.ex">>, |
| 134 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/catalogue.ex">>, |
| 135 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/scoped_css.ex">>, |
| 136 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/tailwind.ex">>, |
| 137 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/common.ex">>, |
| 138 | + <<"lib/mix/tasks/surface/surface.init/project_patchers/error_tag.ex">>, |
| 124 139 | <<"lib/mix/tasks/surface/surface.init.ex">>, |
| 125 140 | <<"lib/mix/tasks/surface/surface.convert.ex">>,<<"lib/mix/tasks/compile">>, |
| 141 | + <<"lib/mix/tasks/compile/surface">>, |
| 142 | + <<"lib/mix/tasks/compile/surface/validate_components.ex">>, |
| 126 143 | <<"lib/mix/tasks/compile/surface.ex">>, |
| 127 144 | <<"lib/mix/tasks/compile/surface.asset_generator.ex">>,<<"lib/surface.ex">>, |
| 128 145 | <<"priv">>,<<"priv/templates">>,<<"priv/templates/surface.init">>, |
| 129 146 | <<"priv/templates/surface.init/demo">>, |
| 130 | - <<"priv/templates/surface.init/demo/example02.ex">>, |
| 131 | - <<"priv/templates/surface.init/demo/playground.ex">>, |
| 132 | - <<"priv/templates/surface.init/demo/demo.ex">>, |
| 133 | - <<"priv/templates/surface.init/demo/hero.ex">>, |
| 134 | - <<"priv/templates/surface.init/demo/example01.ex">>,<<"priv/catalogue">>, |
| 135 | - <<"priv/catalogue/surface">>,<<"priv/catalogue/surface/components">>, |
| 147 | + <<"priv/templates/surface.init/demo/card_playground.ex">>, |
| 148 | + <<"priv/templates/surface.init/demo/default">>, |
| 149 | + <<"priv/templates/surface.init/demo/default/card_examples.ex">>, |
| 150 | + <<"priv/templates/surface.init/demo/default/demo.ex">>, |
| 151 | + <<"priv/templates/surface.init/demo/default/card.ex">>, |
| 152 | + <<"priv/templates/surface.init/demo/tailwind">>, |
| 153 | + <<"priv/templates/surface.init/demo/tailwind/card_examples.ex">>, |
| 154 | + <<"priv/templates/surface.init/demo/tailwind/demo.ex">>, |
| 155 | + <<"priv/templates/surface.init/demo/tailwind/card.ex">>, |
| 156 | + <<"priv/templates/surface.init/demo/card_test.exs">>, |
| 157 | + <<"priv/templates/surface.init/tailwind">>, |
| 158 | + <<"priv/templates/surface.init/tailwind/tailwind.config.js">>, |
| 159 | + <<"priv/templates/surface.init/layouts">>, |
| 160 | + <<"priv/templates/surface.init/layouts/default">>, |
| 161 | + <<"priv/templates/surface.init/layouts/default/index.sface">>, |
| 162 | + <<"priv/templates/surface.init/layouts/default/root.sface">>, |
| 163 | + <<"priv/templates/surface.init/layouts/default/live.sface">>, |
| 164 | + <<"priv/templates/surface.init/layouts/default/app.sface">>, |
| 165 | + <<"priv/templates/surface.init/layouts/tailwind">>, |
| 166 | + <<"priv/templates/surface.init/layouts/tailwind/index.sface">>, |
| 167 | + <<"priv/templates/surface.init/layouts/tailwind/root.sface">>, |
| 168 | + <<"priv/templates/surface.init/layouts/tailwind/live.sface">>, |
| 169 | + <<"priv/templates/surface.init/layouts/tailwind/app.sface">>, |
| 170 | + <<"priv/catalogue">>,<<"priv/catalogue/surface">>, |
| 171 | + <<"priv/catalogue/surface/components">>, |
| 136 172 | <<"priv/catalogue/surface/components/form">>, |
| 137 173 | <<"priv/catalogue/surface/components/form/textarea">>, |
| 138 174 | <<"priv/catalogue/surface/components/form/textarea/playground.ex">>, |
| @@ -156,10 +192,10 @@ | |
| 156 192 | {<<"name">>,<<"phoenix_live_view">>}, |
| 157 193 | {<<"optional">>,false}, |
| 158 194 | {<<"repository">>,<<"hexpm">>}, |
| 159 | - {<<"requirement">>,<<"~> 0.17.4">>}], |
| 195 | + {<<"requirement">>,<<"~> 0.17.6">>}], |
| 160 196 | [{<<"app">>,<<"sourceror">>}, |
| 161 197 | {<<"name">>,<<"sourceror">>}, |
| 162 198 | {<<"optional">>,false}, |
| 163 199 | {<<"repository">>,<<"hexpm">>}, |
| 164 200 | {<<"requirement">>,<<"~> 0.9">>}]]}. |
| 165 | - {<<"version">>,<<"0.7.6">>}. |
| 201 | + {<<"version">>,<<"0.8.0">>}. |
| @@ -4,20 +4,57 @@ defmodule Mix.Tasks.Compile.Surface.AssetGenerator do | |
| 4 4 | alias Mix.Task.Compiler.Diagnostic |
| 5 5 | |
| 6 6 | @default_hooks_output_dir "assets/js/_hooks" |
| 7 | + @default_css_output_file "assets/css/_components.css" |
| 7 8 | @supported_hooks_extensions ~W"js jsx ts tsx" |> Enum.join(",") |
| 8 9 | @hooks_tag ".hooks" |
| 9 10 | @hooks_extension "#{@hooks_tag}.{#{@supported_hooks_extensions}}" |
| 10 11 | |
| 11 | - def run() do |
| 12 | - {js_files, _css_files, diagnostics} = get_colocated_assets() |
| 13 | - generate_files(js_files) |
| 12 | + def run(components, opts \\ []) do |
| 13 | + hooks_output_dir = Keyword.get(opts, :hooks_output_dir, @default_hooks_output_dir) |
| 14 | + css_output_file = Keyword.get(opts, :css_output_file, @default_css_output_file) |
| 15 | + env = Keyword.get(opts, :env, Mix.env()) |
| 16 | + {js_files, js_diagnostics} = get_colocated_js_files(components) |
| 17 | + generate_js_files(js_files, hooks_output_dir) |
| 18 | + css_diagnostics = generate_css_file(components, css_output_file, env) |
| 19 | + diagnostics = js_diagnostics ++ css_diagnostics |
| 14 20 | diagnostics |> Enum.reject(&is_nil/1) |
| 15 21 | end |
| 16 22 | |
| 17 | - def generate_files(js_files) do |
| 18 | - opts = Application.get_env(:surface, :compiler, []) |
| 23 | + defp generate_css_file(components, css_output_file, env) do |
| 24 | + dest_file = Path.join([File.cwd!(), css_output_file]) |
| 19 25 | |
| 20 | - hooks_output_dir = Keyword.get(opts, :hooks_output_dir, @default_hooks_output_dir) |
| 26 | + dest_file_content = |
| 27 | + case File.read(dest_file) do |
| 28 | + {:ok, content} -> content |
| 29 | + _ -> nil |
| 30 | + end |
| 31 | + |
| 32 | + {content, _, diagnostics} = |
| 33 | + for mod <- Enum.sort(components, :desc), |
| 34 | + function_exported?(mod, :__style__, 0), |
| 35 | + {func, %{css: css, scope_id: scope_id, vars: vars}} = func_style <- mod.__style__(), |
| 36 | + reduce: {"", nil, []} do |
| 37 | + {content, last_mod_func_style, diagnostics} -> |
| 38 | + css = String.trim_leading(css, "\n") |
| 39 | + component_header = [inspect(mod), ".", to_string(func), "/1 (", scope_id, ")"] |
| 40 | + |
| 41 | + { |
| 42 | + ["\n/* ", component_header, " */\n\n", vars_comment(vars, env), css | content], |
| 43 | + {mod, func_style}, |
| 44 | + validate_multiple_styles({mod, func_style}, last_mod_func_style) ++ diagnostics |
| 45 | + } |
| 46 | + end |
| 47 | + |
| 48 | + content = to_string([header(), "\n" | content]) |
| 49 | + |
| 50 | + if content != dest_file_content do |
| 51 | + File.write!(dest_file, content) |
| 52 | + end |
| 53 | + |
| 54 | + diagnostics |
| 55 | + end |
| 56 | + |
| 57 | + defp generate_js_files(js_files, hooks_output_dir) do |
| 21 58 | js_output_dir = Path.join([File.cwd!(), hooks_output_dir]) |
| 22 59 | index_file = Path.join([js_output_dir, "index.js"]) |
| 23 60 | |
| @@ -48,9 +85,9 @@ defmodule Mix.Tasks.Compile.Surface.AssetGenerator do | |
| 48 85 | end |
| 49 86 | end |
| 50 87 | |
| 51 | - defp get_colocated_assets() do |
| 52 | - for mod <- components(), module_loaded?(mod), reduce: {[], [], []} do |
| 53 | - {js_files, css_files, diagnostics} -> |
| 88 | + defp get_colocated_js_files(components) do |
| 89 | + for mod <- components, module_loaded?(mod), reduce: {[], []} do |
| 90 | + {js_files, diagnostics} -> |
| 54 91 | component_file = mod.module_info() |> get_in([:compile, :source]) |> to_string() |
| 55 92 | base_file = component_file |> Path.rootname() |
| 56 93 | base_name = inspect(mod) |
| @@ -65,12 +102,7 @@ defmodule Mix.Tasks.Compile.Surface.AssetGenerator do | |
| 65 102 | js_files |
| 66 103 | end |
| 67 104 | |
| 68 | - css_file = "#{base_file}.css" |
| 69 | - dest_css_file = "#{base_name}.css" |
| 70 | - |
| 71 | - css_files = if File.exists?(css_file), do: [{css_file, dest_css_file} | css_files], else: css_files |
| 72 | - |
| 73 | - {js_files, css_files, [new_diagnostic | diagnostics]} |
| 105 | + {js_files, [new_diagnostic | diagnostics]} |
| 74 106 | end |
| 75 107 | end |
| 76 108 | |
| @@ -153,55 +185,6 @@ defmodule Mix.Tasks.Compile.Surface.AssetGenerator do | |
| 153 185 | unsused_files |
| 154 186 | end |
| 155 187 | |
| 156 | - defp components() do |
| 157 | - project_app = Mix.Project.config()[:app] |
| 158 | - :ok = Application.ensure_loaded(project_app) |
| 159 | - project_deps_apps = Application.spec(project_app, :applications) || [] |
| 160 | - opts = Application.get_env(:surface, :compiler, []) |
| 161 | - only_web_namespace = Keyword.get(opts, :only_web_namespace, false) |
| 162 | - |
| 163 | - for app <- [project_app | project_deps_apps], |
| 164 | - deps_apps = Application.spec(app)[:applications] || [], |
| 165 | - app in [:surface, project_app] or :surface in deps_apps, |
| 166 | - prefix = app_beams_prefix(app, project_app, only_web_namespace), |
| 167 | - {dir, files} = app_beams_dir_and_files(app), |
| 168 | - file <- files, |
| 169 | - List.starts_with?(file, prefix) do |
| 170 | - :filename.join(dir, file) |
| 171 | - end |
| 172 | - |> Enum.chunk_every(50) |
| 173 | - |> Task.async_stream(fn files -> |
| 174 | - for file <- files, |
| 175 | - {:ok, {_, [{_, chunk} | _]}} = :beam_lib.chunks(file, ['Attr']), |
| 176 | - chunk |> :erlang.binary_to_term() |> Keyword.get(:component_type) do |
| 177 | - file |> Path.basename(".beam") |> String.to_atom() |
| 178 | - end |
| 179 | - end) |
| 180 | - |> Enum.flat_map(fn {:ok, result} -> result end) |
| 181 | - end |
| 182 | - |
| 183 | - defp app_beams_prefix(app, project_app, only_web_namespace) do |
| 184 | - if only_web_namespace and app == project_app do |
| 185 | - Mix.Phoenix.base() |
| 186 | - |> Mix.Phoenix.web_module() |
| 187 | - |> Module.concat(".") |
| 188 | - |> to_charlist() |
| 189 | - else |
| 190 | - 'Elixir.' |
| 191 | - end |
| 192 | - end |
| 193 | - |
| 194 | - defp app_beams_dir_and_files(app) do |
| 195 | - dir = |
| 196 | - app |
| 197 | - |> Application.app_dir() |
| 198 | - |> Path.join("ebin") |
| 199 | - |> String.to_charlist() |
| 200 | - |
| 201 | - {:ok, files} = :file.list_dir(dir) |
| 202 | - {dir, files} |
| 203 | - end |
| 204 | - |
| 205 188 | defp module_loaded?(module) do |
| 206 189 | match?({:module, _mod}, Code.ensure_compiled(module)) |
| 207 190 | end |
| @@ -224,4 +207,35 @@ defmodule Mix.Tasks.Compile.Surface.AssetGenerator do | |
| 224 207 | compiler_name: "Surface" |
| 225 208 | } |
| 226 209 | end |
| 210 | + |
| 211 | + defp validate_multiple_styles({mod, {func, style}}, {mod, {func, other_style}}) do |
| 212 | + position = "#{Path.relative_to_cwd(style.file)}:#{style.line}" |
| 213 | + |
| 214 | + message = """ |
| 215 | + scoped CSS style already defined for #{inspect(mod)}.#{func}/1 at #{position}. \ |
| 216 | + Scoped styles must be defined either as the first <style> node in \ |
| 217 | + the template or in a colocated .css file. |
| 218 | + """ |
| 219 | + |
| 220 | + [warning(message, other_style.file, other_style.line)] |
| 221 | + end |
| 222 | + |
| 223 | + defp validate_multiple_styles(_, _) do |
| 224 | + [] |
| 225 | + end |
| 226 | + |
| 227 | + defp vars_comment(vars, env) do |
| 228 | + comment = |
| 229 | + vars |
| 230 | + |> Enum.reverse() |
| 231 | + |> Enum.reduce([], fn {var, {expr, _meta}}, acc -> |
| 232 | + [" #{var}: `#{expr}`\n" | acc] |
| 233 | + end) |
| 234 | + |
| 235 | + if comment == [] or env == :prod do |
| 236 | + "" |
| 237 | + else |
| 238 | + ["/* vars:\n", comment, "*/\n\n"] |
| 239 | + end |
| 240 | + end |
| 227 241 | end |
Loading more files…