Basic vite rendering config

This commit is contained in:
Miao Wang 2024-04-08 07:27:26 +08:00
parent d20e32c01f
commit e014df8126
14 changed files with 4232 additions and 6 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@
/vendor/
.vscode/
.DS_Store
/node_modules/
/.jekyll-cache/

View File

@ -19,3 +19,5 @@ group :jekyll_plugins do
gem 'jekyll-fontawesome-svg', '~> 0.4.0'
gem 'jekyll-minifier', '~> 0.1.10'
end
gem 'vite_ruby', '~> 3.2'

View File

@ -48,6 +48,10 @@ kramdown:
sass:
sass_dir: static/css
vite:
config_path: _src/vite.json
viteConfig: _vite.config.mjs
plugins:
- jekyll-relative-links
- jekyll-babel
@ -57,7 +61,19 @@ plugins:
babel_js_extensions: 'es6'
exclude: [Gemfile, Gemfile.lock, README.md, gen_robot.sh, vendor, geninfo/, gen_desc.py, Dockerfile*]
jekyll-minifier:
exclude: [ 'assets/**' ]
exclude: [gen_robot.sh, geninfo/, gen_desc.py]
addition_exclude:
- package.json
- package-lock.json
- node_modules
- Gemfile
- Gemfile.lock
- README.md
- vendor/
- Dockerfile*
sponsors:
megvii:

View File

@ -1,5 +1,6 @@
{% raw %}
<div v-if="false" id="upgrade-mask" style="position: fixed; background: white; width: 100%; height: 100%; z-index: 2000; padding-top: 40px; overflow: scroll;" data-bs-theme="light" class="container mw-100">
<div id="upgrade-mask">
<div style="position: fixed; background: white; width: 100%; height: 100%; z-index: 2000; padding-top: 40px; overflow: scroll;" data-bs-theme="light" class="container mw-100">
<div class="card"><div class="card-body">
<p>尊敬的访问者,</p>
<p>Dear visitor, </p>
@ -17,4 +18,5 @@
<p>お使いのブラウザは当サイトとの互換性はありません。ブラウザを更新しJavaScriptを有効にするか、或いはこちらの<a href="/legacy_index">代用ページ</a>をご利用ください。</p>
</div>
</div></div>
</div>
{% endraw %}

View File

@ -221,6 +221,9 @@
{% endraw %}
{% unless page.legacy or site.issue %}
<script src="/static/js/index.js?{{ site.data['hash'] }}"></script>
{% vite_javascript_tag app %}
<script src="{% vite_asset_path /vite/legacy-polyfills-legacy %}" defer nomodule></script>
<script src="{% vite_asset_path app-legacy.js %}" defer nomodule></script>
{% endunless %}
</html>
<!--

View File

@ -0,0 +1,3 @@
Jekyll::Hooks.register(:site, :after_init) do |site|
site.config['exclude'].push(*(site.config['addition_exclude'] || []))
end

263
_plugins/jekyll-vite.rb Normal file
View File

@ -0,0 +1,263 @@
require 'vite_ruby'
require "jekyll/filters"
require "ostruct"
module Jekyll::Vite
end
class Jekyll::Vite::Generator < Jekyll::Generator
safe true
priority :highest
class ViteAssetFile < Jekyll::StaticFile
# Override (4.2): Copy to the configured public_output_dir
if method_defined?(:cleaned_relative_path)
def cleaned_relative_path
replace_build_path(super)
end
end
# Override: Copy to the configured public_output_dir
def destination_rel_dir
replace_build_path(super)
end
private
def replace_build_path(src)
src.sub(
@site.vite_ruby_instance.config.build_output_dir.relative_path_from(@site.source).to_s,
@site.vite_ruby_instance.config.public_output_dir,
)
end
end
# Internal: Set the mode based on which command was run.
# Builds assets with Vite only if `jekyll build` was run.
def generate(site)
cache_dir = site.config['cache_dir'] || '.jekyll-cache'
vr = ViteRuby.new(
mode: Jekyll.env,
public_dir: cache_dir,
build_cache_dir: File.join(cache_dir, 'vite-build'),
**(site.config['vite'].transform_keys(&:to_sym) || {})
)
site.reader.read_data
vr.env['site_config'] = Jekyll_Filters.new.jsonify(site.config)
vr.env['site_data'] = Jekyll_Filters.new.jsonify(site.data)
vr.env['site_categories'] = Jekyll_Filters.new.jsonify(site.categories)
vr.logger = Jekyll.logger
class << site
def vite_ruby_instance
@__vite_ruby_instance
end
def vite_ruby_instance=(vr)
@__vite_ruby_instance = vr
end
end
site.vite_ruby_instance = vr
if Jekyll.env == 'production'
vr.commands.clobber
end
generate_vite_build(site)
end
class Jekyll_Filters
include Jekyll::Filters
end
# Internal: Build all assets with Vite and add them to the site's static files.
def generate_vite_build(site)
args = []
viteConfig = site.config['vite']&.fetch('viteConfig', "")
if viteConfig != ""
args += ["--config", viteConfig]
end
site.vite_ruby_instance.commands.build(*args) || raise("Vite build failed")
add_static_files(site, site.vite_ruby_instance.config.build_output_dir)
end
# Internal: Add generated assets to the site's static files.
def add_static_files(site, assets_dir)
relative_assets_dir = assets_dir.relative_path_from(site.source).to_s
vite_static_files = Dir.chdir(assets_dir.to_s) {
Dir.glob('**/*').select { |f| File.file?(f) }
}.map { |file|
ViteAssetFile.new(site, site.source, relative_assets_dir, file)
}
site.static_files.concat(vite_static_files)
end
end
class Jekyll::Vite::Tag < Jekyll::Tags::IncludeTag
include Jekyll::Filters::URLFilters
# Override: Set the context to make the site available in the URLFilters.
def render(context)
@context = context
@params = @params.is_a?(String) ? parse_params(context).transform_keys(&:to_sym) : @params || {}
if @file = render_variable(@file)
validate_file_name(@file)
track_file_dependency(@file)
end
block_given? ? yield : raise(NotImplementedError, "Implement render in #{ self.class.name }")
end
# Override: Modified version that can resolve recursive references.
def render_variable(variable)
variable = Liquid::Template.parse(variable).render!(@context) while VARIABLE_SYNTAX =~ variable
variable
end
protected
# Internal: Resolves the path for the specified Vite asset.
def vite_asset_path(name, **options)
vite_manifest.path_for(name, **options)
end
# Internal: Returns the current manifest loaded by Vite Ruby.
def vite_manifest
vite_ruby_instance.manifest
end
def site
@context.registers[:site]
end
def vite_ruby_instance
site.vite_ruby_instance
end
# Internal: Renders HTML attributes inside a tag.
def stringify_attrs(**attrs)
attrs.map { |key, value| %(#{ key }="#{ value }") }.join(' ')
end
# Internal: Renders an HTML tag of the specified type.
def tag(type, **attrs)
self_closing = type != :script
%i[href src].each { |key| attrs[key] = relative_url(attrs[key]) if attrs.key?(key) }
["<#{ type } ", stringify_attrs(**attrs), self_closing ? '/>' : "></#{ type }>"].join
end
# Internal: Renders HTML link tags.
def link_tags(sources, **attrs)
sources.map { |href| tag(:link, href: href, **attrs) }.join("\n")
end
# Internal: Renders HTML script tags.
def script_tags(sources, **attrs)
sources.map { |src| tag(:script, src: src, **attrs) }.join("\n")
end
# Internal: Adds entrypoint files managed by Vite as a dependency in the
# renegerator in order to support --incremental mode.
def track_file_dependency(name)
path = site.in_source_dir(File.join(vite_ruby_instance.config.source_code_dir, vite_ruby_instance.config.entrypoints_dir, name))
['', '.css', '.js', '.ts'].each do |ext|
if File.file?(asset_path = "#{ path }#{ ext }")
return [asset_path, last_build_metadata_path].each do |filename|
add_include_to_dependency(site, filename.to_s, @context)
end
end
end
end
# Internal: Adding the last build metadata file as a dependency ensures
# all pages using Vite assets are regenerated if a Vite build is triggered.
def last_build_metadata_path
vite_ruby_instance.builder.send(:last_build_path, ssr: false)
end
end
# Public: Renders a path to a Vite asset.
class Jekyll::Vite::AssetPathTag < Jekyll::Vite::Tag
def render(context)
super { vite_asset_path(@file, **@params) }
end
end
# Public: Renders the @vite/client script tag.
class Jekyll::Vite::ClientTag < Jekyll::Vite::Tag
def render(context)
@context = context
return unless src = vite_manifest.vite_client_src
super {
tag :script, src: src, type: 'module'
}
end
def syntax_example
"{% #{ @tag_name } %}"
end
end
# Public: Renders a script tag to enable HMR with React Refresh.
class Jekyll::Vite::ReactRefreshTag < Jekyll::Vite::Tag
def render(_context)
vite_manifest.react_refresh_preamble&.html_safe
end
def syntax_example
"{% #{ @tag_name } %}"
end
end
# Public: Renders a <link> tag for the specified stylesheet.
class Jekyll::Vite::StylesheetTag < Jekyll::Vite::Tag
def render(context)
super {
tag :link, **{
rel: 'stylesheet',
href: vite_asset_path(@file, type: :stylesheet),
media: 'screen',
}.merge(@params)
}
end
def syntax_example
"{% #{ @tag_name } application.scss media='screen, projection' %}"
end
end
# Public: Renders a <script> tag for the specified file.
class Jekyll::Vite::JavascriptTag < Jekyll::Vite::Tag
def render(context)
super {
media = @params.delete(:media) || 'screen'
crossorigin = @params.delete(:crossorigin) || 'anonymous'
type = @params.delete(:type) || 'module'
asset_type = @tag_name == 'vite_typescript_tag' ? :typescript : :javascript
entries = vite_manifest.resolve_entries(@file, type: asset_type)
[
script_tags(entries.fetch(:scripts), crossorigin: crossorigin, type: type, **@params),
link_tags(entries.fetch(:imports), rel: 'modulepreload', as: 'script', crossorigin: crossorigin, **@params),
link_tags(entries.fetch(:stylesheets), rel: 'stylesheet', media: media, crossorigin: crossorigin, **@params),
].join("\n")
}
end
def syntax_example
"{% #{ @tag_name } application %}"
end
end
# Recreating tag helpers in Jekyll requires considerably more code than in web
# frameworks, since Liquid does not provide HTML helpers and parsing parameters
# is more complex than a Ruby method invocation.
{
'vite_asset_path' => Jekyll::Vite::AssetPathTag,
'vite_client_tag' => Jekyll::Vite::ClientTag,
'vite_javascript_tag' => Jekyll::Vite::JavascriptTag,
'vite_typescript_tag' => Jekyll::Vite::JavascriptTag,
'vite_stylesheet_tag' => Jekyll::Vite::StylesheetTag,
'vite_react_refresh_tag' => Jekyll::Vite::ReactRefreshTag,
}.each do |name, tag|
Liquid::Template.register_tag(name, tag)
end

View File

@ -0,0 +1,5 @@
<script setup>
</script>
<template>
</template>

5
_src/entrypoints/app.js Normal file
View File

@ -0,0 +1,5 @@
import Empty from '../components/Empty.vue'
import { createApp } from 'vue';
const empty = createApp(Empty);
empty.mount("#upgrade-mask");

12
_src/vite.json Normal file
View File

@ -0,0 +1,12 @@
{
"all": {
"sourceCodeDir": "_src",
"watchAdditionalPaths": ["index.html", "_includes/**/*", "_layouts/**/*", "_config.yml", "vite.config.mjs"],
"publicOutputDir": "",
"assetsDir": "assets"
},
"development": {
"autoBuild": true,
"publicOutputDir": "vite-dev"
}
}

47
_vite.config.mjs Normal file
View File

@ -0,0 +1,47 @@
import { resolve } from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ruby from 'vite-plugin-ruby'
import components from 'unplugin-vue-components/vite'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig(({mode})=>({
build: {
emptyOutDir: true,
sourcemap: mode === 'production' ? false : true,
minify: mode === 'production',
},
plugins: [
(()=>{
const exposedData = ['config', 'data', 'categories'];
const jekyllData = Object.fromEntries(exposedData.map((key) => [key, JSON.parse(process.env[`site_${key}`] || '{}')]));
const importNamePrefix = 'virtual:jekyll-';
const loadNamePrefix = '\0' + importNamePrefix;
return {
name: 'jekyll',
resolveId(id) {
if (id.startsWith(importNamePrefix)) {
return loadNamePrefix + id.slice(importNamePrefix.length);
}
},
load(id) {
if (id.startsWith(loadNamePrefix)) {
const key = id.slice(loadNamePrefix.length);
return Object.entries(jekyllData[key]).map(([key, value]) => `export const ${key} = ${JSON.stringify(value)};`).join('\n');
}
},
};
})(),
vue(),
ruby(),
components({
dirs: [resolve(__dirname, '_src/components')],
resolvers:[
],
}),
legacy({
targets: [],
}),
],
}))

3859
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

11
package.json Normal file
View File

@ -0,0 +1,11 @@
{
"devDependencies": {
"@vitejs/plugin-legacy": "^5.3.2",
"@vitejs/plugin-vue": "^5.0.4",
"sass": "^1.74.1",
"terser": "^5.30.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.2.8",
"vite-plugin-ruby": "^5.0.0"
}
}

View File

@ -14,10 +14,6 @@ global_options.options.force_redirect_help_mirrors.forEach((m) => options[m] = {
var descriptions = {};
global_options.options.mirror_desc.forEach((m) => descriptions[m.name] = m.desc);
new Vue({
el: "#upgrade-mask",
});
const vmMirList = new Vue({
el: "#mirror-list",
data: {