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)
|
def theme_style_tags(theme)
|
||||||
if theme == 'system'
|
if theme == 'system'
|
||||||
''.html_safe.tap do |tags|
|
''.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('themes/mastodon-light', type: :virtual, 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/default', type: :virtual, media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
|
||||||
end
|
end
|
||||||
elsif theme == 'default'
|
|
||||||
vite_stylesheet_tag 'styles/application.scss', media: 'all', crossorigin: 'anonymous'
|
|
||||||
else
|
else
|
||||||
vite_stylesheet_tag "styles/#{theme}.scss", media: 'all', crossorigin: 'anonymous'
|
vite_stylesheet_tag "themes/#{theme}", type: :virtual, media: 'all', crossorigin: 'anonymous'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function MastodonThemes(): Plugin {
|
||||||
throw new Error('Invalid themes.yml file');
|
throw new Error('Invalid themes.yml file');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const themePath of Object.values(themes)) {
|
for (const [themeName, themePath] of Object.entries(themes)) {
|
||||||
if (
|
if (
|
||||||
typeof themePath !== 'string' ||
|
typeof themePath !== 'string' ||
|
||||||
themePath.split('.').length !== 2 || // Ensure it has exactly one period
|
themePath.split('.').length !== 2 || // Ensure it has exactly one period
|
||||||
|
@ -40,7 +40,7 @@ export function MastodonThemes(): Plugin {
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
entrypoints[path.basename(themePath)] = path.resolve(
|
entrypoints[`themes/${themeName}`] = path.resolve(
|
||||||
userConfig.root,
|
userConfig.root,
|
||||||
themePath,
|
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) }
|
{ path: entry.fetch('file'), integrity: entry.fetch('integrity', nil) }
|
||||||
end
|
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
|
# Find a manifest entry by the *final* file name
|
||||||
def integrity_hash_for_file(file_name)
|
def integrity_hash_for_file(file_name)
|
||||||
@integrity_cache ||= {}
|
@integrity_cache ||= {}
|
||||||
|
@ -94,10 +112,10 @@ module ViteRails::TagHelpers::IntegrityExtension
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def vite_stylesheet_tag(*names, **options)
|
def vite_stylesheet_tag(*names, type: :stylesheet, **options)
|
||||||
''.html_safe.tap do |tags|
|
''.html_safe.tap do |tags|
|
||||||
names.each do |name|
|
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
|
options[:extname] = false if Rails::VERSION::MAJOR >= 7
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ RSpec.describe ThemeHelper do
|
||||||
)
|
)
|
||||||
expect(html_links.last.attributes.symbolize_keys)
|
expect(html_links.last.attributes.symbolize_keys)
|
||||||
.to include(
|
.to include(
|
||||||
href: have_attributes(value: match(/application/)),
|
href: have_attributes(value: match(/default/)),
|
||||||
media: have_attributes(value: '(prefers-color-scheme: dark)')
|
media: have_attributes(value: '(prefers-color-scheme: dark)')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -26,10 +26,10 @@ RSpec.describe ThemeHelper do
|
||||||
context 'when using "default" theme' do
|
context 'when using "default" theme' do
|
||||||
let(:theme) { 'default' }
|
let(:theme) { 'default' }
|
||||||
|
|
||||||
it 'returns the application stylesheet' do
|
it 'returns the default stylesheet' do
|
||||||
expect(html_links.last.attributes.symbolize_keys)
|
expect(html_links.last.attributes.symbolize_keys)
|
||||||
.to include(
|
.to include(
|
||||||
href: have_attributes(value: match(/application/))
|
href: have_attributes(value: match(/default/))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@ import postcssPresetEnv from 'postcss-preset-env';
|
||||||
import { MastodonServiceWorkerLocales } from './config/vite/plugin-sw-locales';
|
import { MastodonServiceWorkerLocales } from './config/vite/plugin-sw-locales';
|
||||||
import { MastodonEmojiCompressed } from './config/vite/plugin-emoji-compressed';
|
import { MastodonEmojiCompressed } from './config/vite/plugin-emoji-compressed';
|
||||||
import { MastodonThemes } from './config/vite/plugin-mastodon-themes';
|
import { MastodonThemes } from './config/vite/plugin-mastodon-themes';
|
||||||
|
import { MastodonNameLookup } from './config/vite/plugin-name-lookup';
|
||||||
|
|
||||||
const jsRoot = path.resolve(__dirname, 'app/javascript');
|
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
|
// Old library types need to be converted
|
||||||
optimizeLodashImports() as PluginOption,
|
optimizeLodashImports() as PluginOption,
|
||||||
!!process.env.ANALYZE_BUNDLE_SIZE && (visualizer() as PluginOption),
|
!!process.env.ANALYZE_BUNDLE_SIZE && (visualizer() as PluginOption),
|
||||||
|
MastodonNameLookup(),
|
||||||
],
|
],
|
||||||
} satisfies UserConfig;
|
} satisfies UserConfig;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user