Replace Webpacker usage with Vite

This commit is contained in:
Renaud Chaput 2024-04-12 12:29:29 +02:00
parent 86c353040a
commit 73e92ac250
No known key found for this signature in database
GPG Key ID: BCFC859D49B46990
55 changed files with 272 additions and 164 deletions

View File

@ -9,6 +9,9 @@ public/system
public/assets
public/packs
public/packs-test
public/vite
public/vite-dev
public/vite-test
node_modules
neo4j
vendor/bundle

View File

@ -53,7 +53,7 @@ jobs:
- name: Archive asset artifacts
run: |
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs
- uses: actions/upload-artifact@v4
if: matrix.mode == 'test'

3
.gitignore vendored
View File

@ -69,5 +69,8 @@ yarn-debug.log
# Ignore Docker option files
docker-compose.override.yml
# Vite Ruby
/public/vite*
# Ignore dotenv .local files
.env*.local

View File

@ -22,6 +22,9 @@
/public/assets
/public/packs
/public/packs-test
/public/vite
/public/vite-dev
/public/vite-test
.env
.env.production
.env.development

View File

@ -94,8 +94,8 @@ gem 'strong_migrations', '1.8.0'
gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023'
gem 'vite_rails', '~> 3.0.17'
gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
gem 'json-ld'

View File

@ -212,6 +212,7 @@ GEM
railties (>= 5)
dotenv (3.1.2)
drb (2.2.1)
dry-cli (1.0.0)
ed25519 (1.3.0)
elasticsearch (7.17.10)
elasticsearch-api (= 7.17.10)
@ -782,7 +783,6 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
semantic_range (3.0.0)
sidekiq (6.5.12)
connection_pool (>= 2.2.5, < 3)
rack (~> 2.0)
@ -865,6 +865,13 @@ GEM
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
vite_rails (3.0.17)
railties (>= 5.1, < 8)
vite_ruby (~> 3.0, >= 3.2.2)
vite_ruby (3.5.0)
dry-cli (>= 0.7, < 2)
rack-proxy (~> 0.6, >= 0.6.1)
zeitwerk (~> 2.2)
warden (1.2.9)
rack (>= 2.0.9)
webauthn (3.1.0)
@ -883,11 +890,6 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.4.4)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webrick (1.8.1)
websocket (1.2.10)
websocket-driver (0.7.6)
@ -1046,9 +1048,9 @@ DEPENDENCIES
tty-prompt (~> 0.23)
twitter-text (~> 3.1.0)
tzinfo-data (~> 1.2023)
vite_rails (~> 3.0.17)
webauthn (~> 3.0)
webmock (~> 3.18)
webpacker (~> 5.4)
webpush!
xorcist (~> 1.1)

View File

@ -1,4 +1,4 @@
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
stream: env PORT=4000 yarn workspace @mastodon/streaming start
webpack: bin/webpack-dev-server
vite: bin/vite dev

View File

@ -4,7 +4,7 @@ module RoutingHelper
extend ActiveSupport::Concern
include ActionView::Helpers::AssetTagHelper
include Webpacker::Helper
include ViteRails::TagHelpers
included do
include Rails.application.routes.url_helpers
@ -25,7 +25,7 @@ module RoutingHelper
end
def frontend_asset_path(source, **options)
asset_pack_path("media/#{source}", **options)
vite_asset_path(source, **options)
end
def frontend_asset_url(source, **options)

View File

@ -3,10 +3,10 @@
module ThemeHelper
def theme_style_tags(theme)
if theme == 'system'
stylesheet_pack_tag('mastodon-light', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') +
stylesheet_pack_tag('default', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
vite_stylesheet_tag('styles/mastodon-light.scss', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous') +
vite_stylesheet_tag('styles/application.scss', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
else
stylesheet_pack_tag theme, media: 'all', crossorigin: 'anonymous'
vite_stylesheet_tag "styles/#{theme}.scss", media: 'all', crossorigin: 'anonymous'
end
end

View File

@ -1,4 +1,3 @@
import './public-path';
import { createRoot } from 'react-dom/client';
import Rails from '@rails/ujs';

View File

@ -1,4 +1,3 @@
import './public-path';
import main from 'mastodon/main';
import { start } from '../mastodon/common';

View File

@ -1,4 +1,3 @@
import './public-path';
import ready from '../mastodon/ready';
ready(() => {

View File

@ -1,3 +1 @@
import '../styles/mailer.scss';
require.context('../icons');

View File

@ -1,7 +1,5 @@
import { createRoot } from 'react-dom/client';
import './public-path';
import { IntlMessageFormat } from 'intl-messageformat';
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
import { defineMessages } from 'react-intl';

View File

@ -8,8 +8,6 @@ and performs no other task.
*/
import './public-path';
import axios from 'axios';
interface JRDLink {

View File

@ -1,4 +1,3 @@
import './public-path';
import { createRoot } from 'react-dom/client';
import { start } from '../mastodon/common';

View File

@ -1,4 +1,3 @@
import './public-path';
import axios from 'axios';
import ready from '../mastodon/ready';

View File

@ -2,7 +2,7 @@ import Rails from '@rails/ujs';
import 'font-awesome/css/font-awesome.css';
export function start() {
require.context('../images/', true, /\.(jpg|png|svg)$/);
// require.context('../images/', true, /\.(jpg|png|svg)$/);
try {
Rails.start();

View File

@ -1,5 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here due to preval */
// @preval
// http://www.unicode.org/Public/emoji/5.0/emoji-test.txt
// This file contains the compressed version of the emoji data from
@ -11,16 +9,16 @@
// to ensure that the prevaled file is regenerated by Babel
// version: 2
const { emojiIndex } = require('emoji-mart');
let data = require('emoji-mart/data/all.json');
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
import { emojiIndex } from 'emoji-mart';
import data from 'emoji-mart/data/all.json';
import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data';
const emojiMap = require('./emoji_map.json');
const { unicodeToFilename } = require('./unicode_to_filename');
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
import emojiMap from './emoji_map.json';
import { unicodeToFilename } from './unicode_to_filename';
import { unicodeToUnifiedName } from './unicode_to_unified_name';
if(data.compressed) {
data = emojiMartUncompress(data);
emojiMartUncompress(data);
}
const emojiMartData = data;
@ -119,7 +117,7 @@ Object.keys(emojiIndex.emojis).forEach(key => {
// JSON.parse/stringify is to emulate what @preval is doing and avoid any
// inconsistent behavior in dev mode
module.exports = JSON.parse(JSON.stringify([
const emojiData = JSON.parse(JSON.stringify([
shortCodesToEmojiData,
/*
* The property `skins` is not found in the current context.
@ -136,3 +134,5 @@ module.exports = JSON.parse(JSON.stringify([
emojiMartData.aliases,
emojisWithoutShortCodes,
]));
export default emojiData;

View File

@ -1,9 +1,6 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
// taken from:
// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
exports.unicodeToFilename = (str) => {
export function unicodeToFilename(str) {
let result = '';
let charCode = 0;
let p = 0;
@ -26,4 +23,4 @@ exports.unicodeToFilename = (str) => {
}
}
return result;
};
}

View File

@ -1,6 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
function padLeft(str, num) {
while (str.length < num) {
str = '0' + str;
@ -9,7 +6,7 @@ function padLeft(str, num) {
return str;
}
exports.unicodeToUnifiedName = (str) => {
export function unicodeToUnifiedName(str) {
let output = '';
for (let i = 0; i < str.length; i += 2) {
@ -21,4 +18,4 @@ exports.unicodeToUnifiedName = (str) => {
}
return output;
};
}

View File

@ -12,9 +12,9 @@ import { connect } from 'react-redux';
import Textarea from 'react-textarea-autosize';
import { length } from 'stringz';
// eslint-disable-next-line import/extensions
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js?url';
// eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js?url';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { Button } from 'mastodon/components/button';

View File

@ -5,6 +5,10 @@ import { isLocaleLoaded, setLocale } from './global_locale';
const localeLoadingSemaphore = new Semaphore(1);
const localeFiles = import.meta.glob<{ default: LocaleData['messages'] }>([
'./*.json',
]);
export async function loadLocale() {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en';
@ -17,13 +21,14 @@ export async function loadLocale() {
// if the locale is already set, then do nothing
if (isLocaleLoaded()) return;
const localeData = (await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "locale/[request]" */
/* webpackInclude: /\.json$/ */
/* webpackPreload: true */
`mastodon/locales/${locale}.json`
)) as LocaleData['messages'];
// If there is no locale file, then fallback to english
const localeFile = Object.hasOwn(localeFiles, '`./${locale}.json`')
? localeFiles[`./${locale}.json`]
: localeFiles[`./en.json`];
if (!localeFile) throw new Error('Could not load the locale JSON file');
const { default: localeData } = await localeFile();
setLocale({ messages: localeData, locale });
});

View File

@ -54,11 +54,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
return;
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-pluralrules/polyfill-force');
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force'
);
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`
`../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
);
}

View File

@ -1,10 +1,7 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
/* @preval */
const fs = require('fs');
const path = require('path');
import fs from 'fs';
import path from 'path';
const filtered = {};
const filenames = fs.readdirSync(path.resolve(__dirname, '../locales'));
@ -35,4 +32,6 @@ filenames.forEach(filename => {
};
});
module.exports = JSON.parse(JSON.stringify(filtered));
const locales = JSON.parse(JSON.stringify(filtered));
export default locales;

View File

@ -1,7 +1,11 @@
export function isDevelopment() {
return process.env.NODE_ENV === 'development';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'development';
else return import.meta.env.DEV;
}
export function isProduction() {
return process.env.NODE_ENV === 'production';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'production';
else return import.meta.env.PROD;
}

View File

@ -1,3 +1,5 @@
/// <reference types="vite-plugin-svgr/client" />
/* eslint-disable import/no-default-export */
declare module '*.avif' {
const path: string;
@ -19,23 +21,6 @@ declare module '*.png' {
export default path;
}
declare module '*.svg' {
const path: string;
export default path;
}
declare module '*.svg?react' {
import type React from 'react';
interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
title?: string;
}
const ReactComponent: React.FC<SVGPropsWithTitle>;
export default ReactComponent;
}
declare module '*.webp' {
const path: string;
export default path;

View File

@ -1,7 +1,7 @@
- content_for :page_title do
= t('auth.login')
= javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous'
= vite_typescript_tag 'two_factor_authentication.ts', crossorigin: 'anonymous'
- if webauthn_enabled?
= render partial: 'auth/sessions/two_factor/webauthn_form', locals: { hidden: @scheme_type != 'webauthn' }

View File

@ -1,7 +1,7 @@
- content_for :page_title do
= t('auth.setup.title')
= javascript_pack_tag 'sign_up', crossorigin: 'anonymous'
= vite_typescript_tag 'sign_up.ts', crossorigin: 'anonymous'
= simple_form_for(@user, url: auth_setup_path) do |f|
= render 'auth/shared/progress', stage: 'confirm'

View File

@ -1,7 +1,7 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'public', crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
= vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
= vite_typescript_tag 'admin.tsx', crossorigin: 'anonymous'
- content_for :content do
.admin-wrapper

View File

@ -26,17 +26,17 @@
%title= html_title
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= vite_client_tag
= vite_react_refresh_tag
= theme_style_tags current_theme
-# Needed for the wicg-inert polyfill. It needs to be on it's own <style> tag, with this `id`
= stylesheet_pack_tag 'inert', media: 'all', id: 'inert-style'
= vite_stylesheet_tag 'styles/inert.scss', media: 'all', id: 'inert-style'
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= preload_pack_asset "locale/#{I18n.locale}-json.js"
-# = preload_pack_asset "locale/#{I18n.locale}-json.js"
= csrf_meta_tags unless skip_csrf_meta_tags?
%meta{ name: 'style-nonce', content: request.content_security_policy_nonce }
= stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
-# = stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
= yield :header_tags

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'public', crossorigin: 'anonymous'
= vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
- content_for :content do
.container-alt

View File

@ -11,12 +11,12 @@
- if storage_host?
%link{ rel: 'dns-prefetch', href: storage_host }/
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= vite_client_tag
= vite_react_refresh_tag
= theme_style_tags Setting.theme # Use the admin-configured theme here, even if logged in
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= preload_pack_asset "locale/#{I18n.locale}-json.js"
= render_initial_state
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
-# = preload_pack_asset "locale/#{I18n.locale}-json.js"
= vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
%body.embed
= yield

View File

@ -5,10 +5,11 @@
%meta{ charset: 'utf-8' }/
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= vite_client_tag
= vite_react_refresh_tag
= theme_style_tags Setting.default_settings['theme']
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= javascript_pack_tag 'error', crossorigin: 'anonymous'
= vite_typescript_tag 'error.ts', crossorigin: 'anonymous'
%body.error
.dialog
.dialog__illustration

View File

@ -3,6 +3,4 @@
%head
%meta{ charset: 'utf-8' }/
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= yield :header_tags

View File

@ -18,7 +18,7 @@
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
= stylesheet_pack_tag 'mailer'
= vite_stylesheet_tag 'styles/mailer.scss'
%body
.email{ dir: locale_direction }
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'public', crossorigin: 'anonymous'
= vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
- content_for :content do
- if user_signed_in? && !@hide_header

View File

@ -1,6 +1,6 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'public', crossorigin: 'anonymous'
= vite_typescript_tag 'public.tsx', crossorigin: 'anonymous'
:ruby
meta = @media_attachment.file.meta || {}

View File

@ -2,7 +2,7 @@
= t('settings.relationships')
- content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
= vite_typescript_tag 'admin.tsx', crossorigin: 'anonymous'
.filters
.filter-subset

View File

@ -1,4 +1,4 @@
- content_for :header_tags do
%meta{ name: 'robots', content: 'noindex' }/
= javascript_pack_tag 'remote_interaction_helper', crossorigin: 'anonymous'
= vite_typescript_tag 'remote_interaction_helper.ts', crossorigin: 'anonymous'

View File

@ -13,4 +13,4 @@
.actions
= f.button :button, t('webauthn_credentials.add'), class: 'js-webauthn', type: :submit
= javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous'
= vite_typescript_tag 'two_factor_authentication.ts', crossorigin: 'anonymous'

View File

@ -1,14 +1,15 @@
- content_for :header_tags do
- if user_signed_in?
= preload_pack_asset 'features/compose.js'
= preload_pack_asset 'features/home_timeline.js'
= preload_pack_asset 'features/notifications.js'
-#
= preload_pack_asset 'features/compose.js'
= preload_pack_asset 'features/home_timeline.js'
= preload_pack_asset 'features/notifications.js'
%meta{ name: 'initialPath', content: request.path }
%meta{ name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key }
= render_initial_state
= javascript_pack_tag 'application', crossorigin: 'anonymous'
= vite_typescript_tag 'application.ts', crossorigin: 'anonymous'
.notranslate.app-holder#mastodon{ data: { props: Oj.dump(default_props) } }
%noscript

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'share', crossorigin: 'anonymous'
= vite_typescript_tag 'share.tsx', crossorigin: 'anonymous'
#mastodon-compose{ data: { props: Oj.dump(default_props) } }

27
bin/vite Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'vite' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("vite_ruby", "vite")

View File

@ -45,8 +45,6 @@ require_relative '../lib/chewy/settings_extensions'
require_relative '../lib/chewy/index_extensions'
require_relative '../lib/chewy/strategy/mastodon'
require_relative '../lib/chewy/strategy/bypass_with_warning'
require_relative '../lib/webpacker/manifest_extensions'
require_relative '../lib/webpacker/helper_extensions'
require_relative '../lib/rails/engine_extensions'
require_relative '../lib/action_dispatch/remote_ip_extensions'
require_relative '../lib/active_record/database_tasks_extensions'

View File

@ -36,7 +36,6 @@ Rails.application.config.content_security_policy do |p|
p.frame_ancestors :none
p.font_src :self, assets_host
p.img_src :self, :data, :blob, *media_hosts
p.style_src :self, assets_host
p.media_src :self, :data, *media_hosts
p.frame_src :self, :https
p.manifest_src :self, assets_host
@ -51,14 +50,15 @@ Rails.application.config.content_security_policy do |p|
p.worker_src :self, :blob, assets_host
if Rails.env.development?
webpacker_public_host = ENV.fetch('WEBPACKER_DEV_SERVER_PUBLIC', Webpacker.config.dev_server[:public])
front_end_build_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{webpacker_public_host}" }
front_end_build_urls = %w(ws http).map { |protocol| "#{protocol}#{ViteRuby.config.https ? 's' : ''}://#{ViteRuby.config.host_with_port}" }
p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url, *front_end_build_urls
p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host
p.style_src :self, assets_host, :unsafe_inline
else
p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url
p.script_src :self, assets_host, "'wasm-unsafe-eval'"
p.style_src :self, assets_host
end
end

View File

@ -4,6 +4,9 @@
"additionalEntrypoints": ["~/{icons,images}/**/*", "~/styles/*.scss"],
"watchAdditionalPaths": []
},
"production": {
"publicOutputDir": "packs"
},
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev",

View File

@ -2,21 +2,33 @@
module PremailerBundledAssetStrategy
def load(url)
asset_host = ENV['CDN_HOST'] || ENV['WEB_DOMAIN'] || ENV.fetch('LOCAL_DOMAIN', nil)
if ViteRuby.instance.dev_server_running?
return unless url.start_with?("/#{ViteRuby.config.public_output_dir}/")
if Webpacker.dev_server.running?
asset_host = "#{Webpacker.dev_server.protocol}://#{Webpacker.dev_server.host_with_port}"
url = File.join(asset_host, url)
# Request from the dev server
headers = {}
# Vite dev server wants this header for CSS files, otherwise it will respond with a JS file that inserts the CSS (to support hot reloading)
headers['Accept'] = 'text/css' if url.end_with?('.scss', '.css')
Net::HTTP.get(
URI("#{ViteRuby.config.origin}#{url}"),
headers
).presence
else
# Read the file from filesystem
vite_path = ViteRuby.instance.manifest.path_for(url)
return unless vite_path
path = Rails.public_path.join(vite_path.delete_prefix('/'))
return unless path.exist?
path.read
end
css = if url.start_with?('http')
HTTP.get(url).to_s
else
url = url[1..] if url.start_with?('/')
Rails.public_path.join(url).read
end
css.gsub(%r{url\(/}, "url(#{asset_host}/")
rescue ViteRuby::MissingEntrypointError
# If the path is not in the manifest, ignore it
end
module_function :load

View File

@ -14,7 +14,6 @@ end
if Rake::Task.task_defined?('assets:precompile')
Rake::Task['assets:precompile'].enhance do
Webpacker.manifest.refresh
Rake::Task['assets:generate_static_pages'].invoke
end
end

View File

@ -139,6 +139,7 @@
},
"devDependencies": {
"@formatjs/cli": "^6.1.1",
"@optimize-lodash/rollup-plugin": "^4.0.4",
"@testing-library/react": "^15.0.0",
"@types/babel__core": "^7.20.1",
"@types/emoji-mart": "^3.0.9",
@ -187,6 +188,7 @@
"stylelint": "^16.0.2",
"stylelint-config-standard-scss": "^13.0.0",
"typescript": "^5.0.4",
"vite-bundle-analyzer": "^0.9.4",
"vite-plugin-rails": "^0.5.0",
"vite-plugin-svgr": "^4.2.0",
"vitest": "^1.5.0",

View File

@ -1,9 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = ({ env }) => ({
const config = () => ({
plugins: [
require('postcss-preset-env'),
require('autoprefixer'),
env === 'production' ? require('cssnano') : '',
require('autoprefixer')
],
});

View File

@ -80,9 +80,6 @@ end
RSpec.configure do |config|
config.before :suite do
if streaming_examples_present?
# Compile assets
Webpacker.compile
# Start the node streaming server
streaming_server_manager.start(port: STREAMING_PORT)
end

View File

@ -2,7 +2,7 @@
"compilerOptions": {
"jsx": "react-jsx",
"target": "esnext",
"module": "CommonJS",
"module": "es2022",
"moduleResolution": "node",
"allowJs": true,
"noEmit": true,
@ -11,7 +11,7 @@
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": ["vitest/globals"],
"types": ["vite/client", "vitest/globals"],
"baseUrl": "./",
"incremental": true,
"tsBuildInfoFile": "tmp/cache/tsconfig.tsbuildinfo",

View File

@ -3,11 +3,13 @@
import fs from 'fs';
import path from 'path';
// import { optimizeLodashImports } from '@optimize-lodash/rollup-plugin';
import { optimizeLodashImports } from '@optimize-lodash/rollup-plugin';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import { analyzer } from 'vite-bundle-analyzer';
import RailsPlugin from 'vite-plugin-rails';
import svgr from 'vite-plugin-svgr';
import { defineConfig, configDefaults } from 'vitest/config';
import { configDefaults } from 'vitest/config';
import GithubActionsReporter from 'vitest-github-actions-reporter';
const sourceCodeDir = 'app/javascript';
@ -25,6 +27,40 @@ directories.forEach((directory) => {
});
export default defineConfig({
build: {
commonjsOptions: { transformMixedEsModules: true },
rollupOptions: {
output: {
chunkFileNames: (chunkInfo) => {
if (
chunkInfo.facadeModuleId?.match(
/mastodon\/locales\/[a-zA-Z-]+\.json/,
)
) {
// put all locale files in `intl/`
return `intl/[name]-[hash].js`;
} else if (
chunkInfo.facadeModuleId?.match(/node_modules\/@formatjs\//)
) {
// use a custom name for formatjs polyfill files
const name = chunkInfo.facadeModuleId.match(
/node_modules\/@formatjs\/([^/]+)\//,
);
if (name?.[1]) return `intl/[name]-${name[1]}-[hash].js`;
} else if (chunkInfo.name === 'index' && chunkInfo.facadeModuleId) {
// Use a custom name for chunks, to avoid having too many of them called "index"
const parts = chunkInfo.facadeModuleId.split('/');
const parent = parts.at(-2);
if (parent) return `${parent}-[name]-[hash].js`;
}
return `[name]-[hash].js`;
},
},
},
},
resolve: {
alias: {
...aliasesFromJavascriptRoot,
@ -45,9 +81,9 @@ export default defineConfig({
},
}),
svgr(),
// optimizeLodashImports(),
// !!process.env.ANALYZE_BUNDLE_SIZE &&
// visualizer({ open: true, gzipSize: true, brotliSize: true }),
// @ts-expect-error the types for the plugin are not up-to-date
optimizeLodashImports(),
!!process.env.ANALYZE_BUNDLE_SIZE && analyzer({ analyzerMode: 'static' }),
],
test: {
environment: 'jsdom',

View File

@ -3009,6 +3009,7 @@ __metadata:
"@formatjs/intl-pluralrules": "npm:^5.2.2"
"@gamestdio/websocket": "npm:^0.3.2"
"@github/webauthn-json": "npm:^2.1.1"
"@optimize-lodash/rollup-plugin": "npm:^4.0.4"
"@rails/ujs": "npm:7.1.3"
"@reduxjs/toolkit": "npm:^2.0.1"
"@svgr/webpack": "npm:^5.5.0"
@ -3136,6 +3137,7 @@ __metadata:
twitter-text: "npm:3.1.0"
typescript: "npm:^5.0.4"
vite: "npm:^5.2.8"
vite-bundle-analyzer: "npm:^0.9.4"
vite-plugin-rails: "npm:^0.5.0"
vite-plugin-svgr: "npm:^4.2.0"
vitest: "npm:^1.5.0"
@ -3273,6 +3275,28 @@ __metadata:
languageName: node
linkType: hard
"@optimize-lodash/rollup-plugin@npm:^4.0.4":
version: 4.0.4
resolution: "@optimize-lodash/rollup-plugin@npm:4.0.4"
dependencies:
"@optimize-lodash/transform": "npm:3.0.3"
"@rollup/pluginutils": "npm:~5.0.2"
peerDependencies:
rollup: ">=2.x"
checksum: 10c0/219f9f2f2b10efb22c0e4a6f362394483498ba7998aeac041e0c696f65e696a170657f0ac2670e32c2aa2cfdcb72bf91e8a0c32564c48970b9871aef42de227d
languageName: node
linkType: hard
"@optimize-lodash/transform@npm:3.0.3":
version: 3.0.3
resolution: "@optimize-lodash/transform@npm:3.0.3"
dependencies:
estree-walker: "npm:2.x"
magic-string: "npm:0.30.x"
checksum: 10c0/ad85a78d793d4c5d5bdd055b5bc15c45674b1bc6eaa8c170fc04f83ac49d497976dd910a854e3c7798b9701df7a5426136dcec4bc96dea4a62574fa8bf099aed
languageName: node
linkType: hard
"@pkgjs/parseargs@npm:^0.11.0":
version: 0.11.0
resolution: "@pkgjs/parseargs@npm:0.11.0"
@ -3425,6 +3449,22 @@ __metadata:
languageName: node
linkType: hard
"@rollup/pluginutils@npm:~5.0.2":
version: 5.0.5
resolution: "@rollup/pluginutils@npm:5.0.5"
dependencies:
"@types/estree": "npm:^1.0.0"
estree-walker: "npm:^2.0.2"
picomatch: "npm:^2.3.1"
peerDependencies:
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
peerDependenciesMeta:
rollup:
optional: true
checksum: 10c0/18a1f5a9afa993a76663cc2102169cd546786b39a3e92bdc8a2a0b408b509d070a02b20970a30daa9d5d0b1b591b5e7734add84b3aaf263178aef5a26cfab2cf
languageName: node
linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.14.1":
version: 4.14.1
resolution: "@rollup/rollup-android-arm-eabi@npm:4.14.1"
@ -8360,6 +8400,13 @@ __metadata:
languageName: node
linkType: hard
"estree-walker@npm:2.x, estree-walker@npm:^2.0.2":
version: 2.0.2
resolution: "estree-walker@npm:2.0.2"
checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af
languageName: node
linkType: hard
"estree-walker@npm:^1.0.1":
version: 1.0.1
resolution: "estree-walker@npm:1.0.1"
@ -8367,13 +8414,6 @@ __metadata:
languageName: node
linkType: hard
"estree-walker@npm:^2.0.2":
version: 2.0.2
resolution: "estree-walker@npm:2.0.2"
checksum: 10c0/53a6c54e2019b8c914dc395890153ffdc2322781acf4bd7d1a32d7aedc1710807bdcd866ac133903d5629ec601fbb50abe8c2e5553c7f5a0afdd9b6af6c945af
languageName: node
linkType: hard
"estree-walker@npm:^3.0.3":
version: 3.0.3
resolution: "estree-walker@npm:3.0.3"
@ -11194,6 +11234,15 @@ __metadata:
languageName: node
linkType: hard
"magic-string@npm:0.30.x, magic-string@npm:^0.30.5":
version: 0.30.9
resolution: "magic-string@npm:0.30.9"
dependencies:
"@jridgewell/sourcemap-codec": "npm:^1.4.15"
checksum: 10c0/edbeea35b4f90b58815d8b13899fa412b5bc1e81cae14fe6d24d5c383c5f04331fce2c5a75bfb7926203ab6fc8c71290cdab56703a5b82432d8a1e144d6042e1
languageName: node
linkType: hard
"magic-string@npm:^0.25.0, magic-string@npm:^0.25.7":
version: 0.25.9
resolution: "magic-string@npm:0.25.9"
@ -11212,15 +11261,6 @@ __metadata:
languageName: node
linkType: hard
"magic-string@npm:^0.30.5":
version: 0.30.9
resolution: "magic-string@npm:0.30.9"
dependencies:
"@jridgewell/sourcemap-codec": "npm:^1.4.15"
checksum: 10c0/edbeea35b4f90b58815d8b13899fa412b5bc1e81cae14fe6d24d5c383c5f04331fce2c5a75bfb7926203ab6fc8c71290cdab56703a5b82432d8a1e144d6042e1
languageName: node
linkType: hard
"make-dir@npm:^3.0.2, make-dir@npm:^3.1.0":
version: 3.1.0
resolution: "make-dir@npm:3.1.0"
@ -15649,7 +15689,7 @@ __metadata:
languageName: node
linkType: hard
"source-map@npm:^0.7.3":
"source-map@npm:^0.7.3, source-map@npm:^0.7.4":
version: 0.7.4
resolution: "source-map@npm:0.7.4"
checksum: 10c0/dc0cf3768fe23c345ea8760487f8c97ef6fca8a73c83cd7c9bf2fde8bc2c34adb9c0824d6feb14bc4f9e37fb522e18af621543f1289038a66ac7586da29aa7dc
@ -17237,6 +17277,16 @@ __metadata:
languageName: node
linkType: hard
"vite-bundle-analyzer@npm:^0.9.4":
version: 0.9.4
resolution: "vite-bundle-analyzer@npm:0.9.4"
dependencies:
picocolors: "npm:^1.0.0"
source-map: "npm:^0.7.4"
checksum: 10c0/518c8e50be44c1fef0bd754a86bb992349d8aa5df338215188c4e76df5a503f53290442fedb2ca2bd3cafff2eff9d0434bd56d9b5ac4a5a1cf14550e354a141f
languageName: node
linkType: hard
"vite-node@npm:1.5.0":
version: 1.5.0
resolution: "vite-node@npm:1.5.0"