diff --git a/docs/src/guide/plugin-legacy.md b/docs/src/guide/plugin-legacy.md index f1fdba5c..f80f2c80 100644 --- a/docs/src/guide/plugin-legacy.md +++ b/docs/src/guide/plugin-legacy.md @@ -1,8 +1,6 @@ [vite_plugin_legacy]: https://github.com/ElMassimo/vite_ruby/tree/main/vite_plugin_legacy [plugin-legacy]: https://github.com/vitejs/vite/tree/main/packages/plugin-legacy [vite_legacy_javascript_tag]: https://github.com/ElMassimo/vite_ruby/blob/main/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb -[vite_legacy_typescript_tag]: https://github.com/ElMassimo/vite_ruby/blob/main/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb -[vite_legacy_polyfill_tag]: https://github.com/ElMassimo/vite_ruby/blob/main/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb # Plugin Legacy @@ -27,11 +25,8 @@ bundle install In order to include the polyfills and script tags you can using the following helpers: -- <kbd>[vite_legacy_javascript_tag]</kbd>: Render a `<script>` tag referencing a JavaScript file. -- <kbd>[vite_legacy_typescript_tag]</kbd>: Render a `<script>` tag referencing a TypeScript file. -- <kbd>[vite_legacy_polyfill_tag]</kbd>: Renders the polyfills necessary to enable code-splitting in legacy browsers. +- <kbd>[vite_legacy_javascript_tag]</kbd>: Render a `<script>` tag referencing a JavaScript or TypeScript entrypoints. -The polyfill is included by default when using <kbd>[vite_legacy_javascript_tag]</kbd> ```erb <head> @@ -40,11 +35,11 @@ The polyfill is included by default when using <kbd>[vite_legacy_javascript_tag] <%= csp_meta_tag %> <%= vite_client_tag %> + <%= vite_legacy_javascript_tag 'application' => :javascript %> <%= vite_javascript_tag 'application' %> </head> <body> <%= yield %> - <%= vite_legacy_javascript_tag 'application' %> </body> ``` diff --git a/examples/rails/app/views/layouts/application.html.erb b/examples/rails/app/views/layouts/application.html.erb index a8d49a3e..0f123ded 100644 --- a/examples/rails/app/views/layouts/application.html.erb +++ b/examples/rails/app/views/layouts/application.html.erb @@ -9,12 +9,12 @@ <link rel="icon" type="image/png" href="<%= vite_asset_path('images/logo.png') %>"> + <%= vite_legacy_javascript_tag 'application' => :typescript %> <%= vite_stylesheet_tag 'styles.scss' %> <%= vite_typescript_tag 'application', 'data-turbo-track': 'reload', media: 'all' %> </head> <body> <%= yield %> - <%= vite_legacy_typescript_tag 'application' %> </body> </html> diff --git a/test/helper_test.rb b/test/helper_test.rb index d3f1b77c..148cb8c9 100644 --- a/test/helper_test.rb +++ b/test/helper_test.rb @@ -50,9 +50,9 @@ class LegacyHelperTest < HelperTestCase }) def test_plugin_legacy - assert_includes vite_legacy_javascript_tag('/app/assets/external'), '/vite-production/assets/external.a35ee0db-legacy.js' - assert_includes vite_legacy_typescript_tag('main.ts'), '/vite-production/assets/main.20bbd3a5-legacy.js' - assert_includes vite_legacy_polyfill_tag, '/vite-production/assets/polyfills-legacy.07477394.js' + assert_includes vite_legacy_javascript_tag('/app/assets/external' => :javascript), '/vite-production/assets/external.a35ee0db-legacy.js' + assert_includes vite_legacy_javascript_tag('main.ts' => :typescript), '/vite-production/assets/main.20bbd3a5-legacy.js' + assert_includes vite_legacy_javascript_tag('main.ts' => :typescript), '/vite-production/assets/polyfills-legacy.07477394.js' end end diff --git a/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb b/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb index eef92c4f..8e802f8a 100644 --- a/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb +++ b/vite_plugin_legacy/lib/vite_plugin_legacy/tag_helpers.rb @@ -2,29 +2,54 @@ # Public: Allows to render HTML tags for scripts and styles processed by Vite. module VitePluginLegacy::TagHelpers - # Public: Renders a <script> tag for the specified Vite entrypoints when using - # @vitejs/plugin-legacy, which injects polyfills. - def vite_legacy_javascript_tag(name, asset_type: :javascript) - return if ViteRuby.instance.dev_server_running? + VITE_SAFARI_NOMODULE_FIX = <<-JS.html_safe.freeze + !function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;console.log('preventing load',e.target);e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}(); + JS - legacy_name = name.sub(/(\..+)|$/, '-legacy\1') - import_tag = content_tag(:script, nomodule: true) { - "System.import('#{ vite_asset_path(legacy_name, type: asset_type) }')".html_safe - } + # Renders code to load vite entrypoints for legacy browsers: + # * Safari NOMODULE fix for Safari 10, which supports modules but not `nomodule` + # * vite-legacy-polyfill (System.import polyfill) for browsers that do not support modules @vitejs/plugin-legacy + # * Dynamic import code for browsers that support modules, but not dynamic imports + # This helper must be called before any other Vite import tags. + # Accepts a hash with entrypoint names as keys and asset types (:javascript or :typescript) as values. + def vite_legacy_javascript_tag(entrypoints) + return if ViteRuby.instance.dev_server_running? - safe_join [vite_legacy_polyfill_tag, import_tag] + tags = [] + safari_nomodule_fix = content_tag(:script, nil, nomodule: true) { VITE_SAFARI_NOMODULE_FIX } + tags.push(safari_nomodule_fix) + # for browsers which do not support modules at all + legacy_polyfill = content_tag(:script, nil, nomodule: true, id: 'vite-legacy-polyfill', src: vite_asset_path('legacy-polyfills', type: :virtual)) + tags.push(legacy_polyfill) + # for browsers which support modules, but don't support dynamic import + legacy_fallback_tag = content_tag(:script, nil, type: 'module') do + vite_dynamic_fallback_inline_code(entrypoints) + end + entrypoints.each do |name, asset_type| + import_tag = content_tag(:script, nomodule: true) do + vite_legacy_import_body(name, asset_type: asset_type) + end + tags.push(import_tag) + end + tags.push(legacy_fallback_tag) + safe_join(tags, "\n") end - # Public: Same as `vite_legacy_javascript_tag`, but for TypeScript entries. - def vite_legacy_typescript_tag(name) - vite_legacy_javascript_tag(name, asset_type: :typescript) + def vite_dynamic_fallback_inline_code(entrypoints) + load_body = entrypoints.map do |name, asset_type| + vite_legacy_import_body(name, asset_type: asset_type) + end + load_body = safe_join(load_body, "\n") + # rubocop:disable Layout/LineLength + %{!function(){try{new Function("m","return import(m)")}catch(o){console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){#{ load_body }},document.body.appendChild(n)}}();}.html_safe + # rubocop:enable Layout/LineLength end - # Internal: Renders the vite-legacy-polyfill to enable code splitting in - # browsers that do not support modules. - def vite_legacy_polyfill_tag - return if ViteRuby.instance.dev_server_running? + def vite_legacy_import_body(name, asset_type: :javascript) + "System.import('#{ vite_asset_path(vite_legacy_name(name), type: asset_type) }')".html_safe + end - content_tag(:script, nil, nomodule: true, src: vite_asset_path('legacy-polyfills', type: :virtual)) + def vite_legacy_name(name) + name.sub(/(\..+)|$/, '-legacy\1') end end diff --git a/vite_plugin_legacy/lib/vite_plugin_legacy/version.rb b/vite_plugin_legacy/lib/vite_plugin_legacy/version.rb index ed2afee6..17dbac2e 100644 --- a/vite_plugin_legacy/lib/vite_plugin_legacy/version.rb +++ b/vite_plugin_legacy/lib/vite_plugin_legacy/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module VitePluginLegacy - VERSION = '3.0.2' + VERSION = '4.0.0' end