mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-15 08:48:15 +00:00
Fix theme name requirement regression with efficient lookup by name (#35007)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
2254f47702
commit
825312d4b0
|
@ -4,13 +4,11 @@ module ThemeHelper
|
|||
def theme_style_tags(theme)
|
||||
if theme == 'system'
|
||||
''.html_safe.tap do |tags|
|
||||
tags << vite_stylesheet_tag('styles/mastodon-light.scss', media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||
tags << vite_stylesheet_tag('styles/application.scss', media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||
tags << vite_stylesheet_tag('themes/mastodon-light', type: :virtual, media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||
tags << vite_stylesheet_tag('themes/default', type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||
end
|
||||
elsif theme == 'default'
|
||||
vite_stylesheet_tag 'styles/application.scss', media: 'all', crossorigin: 'anonymous'
|
||||
else
|
||||
vite_stylesheet_tag "styles/#{theme}.scss", media: 'all', crossorigin: 'anonymous'
|
||||
vite_stylesheet_tag "themes/#{theme}", type: :virtual, media: 'all', crossorigin: 'anonymous'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export function MastodonThemes(): Plugin {
|
|||
throw new Error('Invalid themes.yml file');
|
||||
}
|
||||
|
||||
for (const themePath of Object.values(themes)) {
|
||||
for (const [themeName, themePath] of Object.entries(themes)) {
|
||||
if (
|
||||
typeof themePath !== 'string' ||
|
||||
themePath.split('.').length !== 2 || // Ensure it has exactly one period
|
||||
|
@ -40,7 +40,7 @@ export function MastodonThemes(): Plugin {
|
|||
);
|
||||
continue;
|
||||
}
|
||||
entrypoints[path.basename(themePath)] = path.resolve(
|
||||
entrypoints[`themes/${themeName}`] = path.resolve(
|
||||
userConfig.root,
|
||||
themePath,
|
||||
);
|
||||
|
|
68
config/vite/plugin-name-lookup.ts
Normal file
68
config/vite/plugin-name-lookup.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
import { relative, extname } from 'node:path';
|
||||
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
export function MastodonNameLookup(): Plugin {
|
||||
const nameMap: Record<string, string> = {};
|
||||
|
||||
let root = '';
|
||||
|
||||
return {
|
||||
name: 'mastodon-name-lookup',
|
||||
applyToEnvironment(environment) {
|
||||
return !!environment.config.build.manifest;
|
||||
},
|
||||
configResolved(userConfig) {
|
||||
root = userConfig.root;
|
||||
},
|
||||
generateBundle(options, bundle) {
|
||||
if (!root) {
|
||||
throw new Error(
|
||||
'MastodonNameLookup plugin requires the root to be set in the config.',
|
||||
);
|
||||
}
|
||||
|
||||
// Iterate over all chunks in the bundle and create a lookup map
|
||||
for (const file in bundle) {
|
||||
const chunk = bundle[file];
|
||||
if (
|
||||
chunk?.type !== 'chunk' ||
|
||||
!chunk.isEntry ||
|
||||
!chunk.facadeModuleId
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const relativePath = relative(
|
||||
root,
|
||||
sanitizeFileName(chunk.facadeModuleId),
|
||||
);
|
||||
const ext = extname(relativePath);
|
||||
const name = chunk.name.replace(ext, '');
|
||||
|
||||
if (nameMap[name]) {
|
||||
throw new Error(
|
||||
`Entrypoint ${relativePath} conflicts with ${nameMap[name]}`,
|
||||
);
|
||||
}
|
||||
|
||||
nameMap[name] = relativePath;
|
||||
}
|
||||
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: '.vite/manifest-lookup.json',
|
||||
source: JSON.stringify(nameMap, null, 2),
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Taken from https://github.com/rollup/rollup/blob/4f69d33af3b2ec9320c43c9e6c65ea23a02bdde3/src/utils/sanitizeFileName.ts
|
||||
// https://datatracker.ietf.org/doc/html/rfc2396
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const INVALID_CHAR_REGEX = /[\u0000-\u001F"#$%&*+,:;<=>?[\]^`{|}\u007F]/g;
|
||||
|
||||
function sanitizeFileName(name: string): string {
|
||||
return name.replace(INVALID_CHAR_REGEX, '');
|
||||
}
|
|
@ -7,6 +7,24 @@ module ViteRuby::ManifestIntegrityExtension
|
|||
{ path: entry.fetch('file'), integrity: entry.fetch('integrity', nil) }
|
||||
end
|
||||
|
||||
def load_manifest
|
||||
# Invalidate the name lookup cache when reloading manifest
|
||||
@name_lookup_cache = load_name_lookup_cache
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def load_name_lookup_cache
|
||||
Oj.load(config.build_output_dir.join('.vite/manifest-lookup.json').read)
|
||||
end
|
||||
|
||||
# Upstream's `virtual` type is a hack, re-implement it with efficient exact name lookup
|
||||
def resolve_virtual_entry(name)
|
||||
@name_lookup_cache ||= load_name_lookup_cache
|
||||
|
||||
@name_lookup_cache.fetch(name)
|
||||
end
|
||||
|
||||
# Find a manifest entry by the *final* file name
|
||||
def integrity_hash_for_file(file_name)
|
||||
@integrity_cache ||= {}
|
||||
|
@ -94,10 +112,10 @@ module ViteRails::TagHelpers::IntegrityExtension
|
|||
end
|
||||
end
|
||||
|
||||
def vite_stylesheet_tag(*names, **options)
|
||||
def vite_stylesheet_tag(*names, type: :stylesheet, **options)
|
||||
''.html_safe.tap do |tags|
|
||||
names.each do |name|
|
||||
entry = vite_manifest.path_and_integrity_for(name, type: :stylesheet)
|
||||
entry = vite_manifest.path_and_integrity_for(name, type:)
|
||||
|
||||
options[:extname] = false if Rails::VERSION::MAJOR >= 7
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe ThemeHelper do
|
|||
)
|
||||
expect(html_links.last.attributes.symbolize_keys)
|
||||
.to include(
|
||||
href: have_attributes(value: match(/application/)),
|
||||
href: have_attributes(value: match(/default/)),
|
||||
media: have_attributes(value: '(prefers-color-scheme: dark)')
|
||||
)
|
||||
end
|
||||
|
@ -26,10 +26,10 @@ RSpec.describe ThemeHelper do
|
|||
context 'when using "default" theme' do
|
||||
let(:theme) { 'default' }
|
||||
|
||||
it 'returns the application stylesheet' do
|
||||
it 'returns the default stylesheet' do
|
||||
expect(html_links.last.attributes.symbolize_keys)
|
||||
.to include(
|
||||
href: have_attributes(value: match(/application/))
|
||||
href: have_attributes(value: match(/default/))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ import postcssPresetEnv from 'postcss-preset-env';
|
|||
import { MastodonServiceWorkerLocales } from './config/vite/plugin-sw-locales';
|
||||
import { MastodonEmojiCompressed } from './config/vite/plugin-emoji-compressed';
|
||||
import { MastodonThemes } from './config/vite/plugin-mastodon-themes';
|
||||
import { MastodonNameLookup } from './config/vite/plugin-name-lookup';
|
||||
|
||||
const jsRoot = path.resolve(__dirname, 'app/javascript');
|
||||
|
||||
|
@ -125,6 +126,7 @@ export const config: UserConfigFnPromise = async ({ mode, command }) => {
|
|||
// Old library types need to be converted
|
||||
optimizeLodashImports() as PluginOption,
|
||||
!!process.env.ANALYZE_BUNDLE_SIZE && (visualizer() as PluginOption),
|
||||
MastodonNameLookup(),
|
||||
],
|
||||
} satisfies UserConfig;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user