From 36512446fe3ea521102a1062ad0261b7efd15925 Mon Sep 17 00:00:00 2001 From: shulan Date: Tue, 22 Oct 2024 22:52:59 +0800 Subject: [PATCH] feat: add assets mode for asset path generate (#1852) * feat: add assets mode for asset path generate * test: update test failed --- .changeset/heavy-rabbits-smile.md | 5 +++ crates/core/src/config/asset.rs | 31 ++++++++++++++ crates/core/src/config/custom.rs | 36 ++++++++++------- crates/core/src/config/mod.rs | 11 ++--- crates/plugin_static_assets/src/lib.rs | 38 ++++++++++++------ examples/react-ssr/farm.config.server.mjs | 6 ++- examples/react-ssr/src/logo.png | Bin 0 -> 52789 bytes examples/react-ssr/src/main.tsx | 7 +++- examples/react-ssr/src/types.d.ts | 4 ++ examples/tree-shake-antd/e2e.spec.ts | 5 +++ packages/core/src/config/constants.ts | 3 +- packages/core/src/config/index.ts | 2 + .../normalize-config/normalize-asset.ts | 14 +++++++ .../normalize-config/normalize-output.ts | 4 ++ packages/core/src/config/schema.ts | 4 +- packages/core/src/config/types.ts | 1 + packages/core/src/types/binding.ts | 1 + packages/core/tests/config/index.spec.ts | 23 ++++++++++- vitest.config.ts | 3 +- 19 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 .changeset/heavy-rabbits-smile.md create mode 100644 crates/core/src/config/asset.rs create mode 100644 examples/react-ssr/src/logo.png create mode 100644 examples/react-ssr/src/types.d.ts create mode 100644 packages/core/src/config/normalize-config/normalize-asset.ts diff --git a/.changeset/heavy-rabbits-smile.md b/.changeset/heavy-rabbits-smile.md new file mode 100644 index 000000000..e62138a87 --- /dev/null +++ b/.changeset/heavy-rabbits-smile.md @@ -0,0 +1,5 @@ +--- +"@farmfe/core": patch +--- + +add assets mode for asset path generate diff --git a/crates/core/src/config/asset.rs b/crates/core/src/config/asset.rs new file mode 100644 index 000000000..862a2c0c0 --- /dev/null +++ b/crates/core/src/config/asset.rs @@ -0,0 +1,31 @@ +use serde::{Deserialize, Serialize}; + +use super::TargetEnv; + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum AssetFormatMode { + Node, + Browser, +} + +impl From for AssetFormatMode { + fn from(value: TargetEnv) -> Self { + if value.is_browser() { + AssetFormatMode::Browser + } else { + AssetFormatMode::Node + } + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", default)] +pub struct AssetsConfig { + pub include: Vec, + /// Used internally, this option will be not exposed to user. + pub public_dir: Option, + // TODO: v2 + // for ssr mode, should specify asset path format, default from `output.targetEnv` + // pub mode: Option, +} diff --git a/crates/core/src/config/custom.rs b/crates/core/src/config/custom.rs index 22ba5e1bb..5c002baf2 100644 --- a/crates/core/src/config/custom.rs +++ b/crates/core/src/config/custom.rs @@ -1,8 +1,11 @@ use std::{collections::HashMap, sync::Arc}; +use serde::{de::DeserializeOwned, Deserialize}; + use crate::context::CompilationContext; use super::{ + asset::AssetFormatMode, config_regex::ConfigRegex, css::NameConversion, external::{ExternalConfig, ExternalObject}, @@ -13,6 +16,7 @@ const CUSTOM_CONFIG_RUNTIME_ISOLATE: &str = "runtime.isolate"; pub const CUSTOM_CONFIG_EXTERNAL_RECORD: &str = "external.record"; pub const CUSTOM_CONFIG_RESOLVE_DEDUPE: &str = "resolve.dedupe"; pub const CUSTOM_CONFIG_CSS_MODULES_LOCAL_CONVERSION: &str = "css.modules.locals_conversion"; +pub const CUSTOM_CONFIG_ASSETS_MODE: &str = "assets.mode"; pub fn get_config_runtime_isolate(context: &Arc) -> bool { if let Some(val) = context.config.custom.get(CUSTOM_CONFIG_RUNTIME_ISOLATE) { @@ -28,8 +32,8 @@ pub fn get_config_external_record(config: &Config) -> ExternalConfig { return ExternalConfig::new(); } - let external: HashMap = serde_json::from_str(val) - .unwrap_or_else(|_| panic!("failed parse record external {val:?}")); + let external: HashMap = + serde_json::from_str(val).unwrap_or_else(|_| panic!("failed parse record external {val:?}")); let mut external_config = ExternalConfig::new(); @@ -50,20 +54,24 @@ pub fn get_config_external_record(config: &Config) -> ExternalConfig { } pub fn get_config_resolve_dedupe(config: &Config) -> Vec { - if let Some(val) = config.custom.get(CUSTOM_CONFIG_RESOLVE_DEDUPE) { - serde_json::from_str(val).unwrap_or_else(|_| vec![]) - } else { - vec![] - } + get_field_or_default_from_custom(config, CUSTOM_CONFIG_RESOLVE_DEDUPE) } pub fn get_config_css_modules_local_conversion(config: &Config) -> NameConversion { - if let Some(val) = config + get_field_or_default_from_custom(config, CUSTOM_CONFIG_CSS_MODULES_LOCAL_CONVERSION) +} + +pub fn get_config_assets_mode(config: &Config) -> Option { + get_field_or_default_from_custom(config, CUSTOM_CONFIG_ASSETS_MODE) +} + +fn get_field_or_default_from_custom( + config: &Config, + field: &str, +) -> T { + config .custom - .get(CUSTOM_CONFIG_CSS_MODULES_LOCAL_CONVERSION) - { - serde_json::from_str(val).unwrap_or_default() - } else { - Default::default() - } + .get(field) + .map(|val| serde_json::from_str(val).unwrap_or_default()) + .unwrap_or_default() } diff --git a/crates/core/src/config/mod.rs b/crates/core/src/config/mod.rs index f6ba19c12..cfaa05cfe 100644 --- a/crates/core/src/config/mod.rs +++ b/crates/core/src/config/mod.rs @@ -18,6 +18,7 @@ pub const FARM_REQUIRE: &str = "farmRequire"; pub const FARM_MODULE: &str = "module"; pub const FARM_MODULE_EXPORT: &str = "exports"; +pub mod asset; pub mod bool_or_obj; pub mod comments; pub mod config_regex; @@ -33,6 +34,8 @@ pub mod preset_env; pub mod script; pub mod tree_shaking; +use asset::AssetsConfig; + pub use output::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -294,14 +297,6 @@ impl Default for RuntimeConfig { } } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", default)] -pub struct AssetsConfig { - pub include: Vec, - /// Used internally, this option will be not exposed to user. - pub public_dir: Option, -} - #[derive(Debug, Clone, Serialize, Deserialize)] pub enum SourcemapConfig { /// Generate inline sourcemap instead of a separate file for mutable resources. diff --git a/crates/plugin_static_assets/src/lib.rs b/crates/plugin_static_assets/src/lib.rs index e9328902b..2a1738811 100644 --- a/crates/plugin_static_assets/src/lib.rs +++ b/crates/plugin_static_assets/src/lib.rs @@ -9,7 +9,7 @@ use std::{ use base64::engine::{general_purpose, Engine}; use farmfe_core::{ cache_item, - config::{Config}, + config::{asset::AssetFormatMode, custom::get_config_assets_mode, Config}, context::{CompilationContext, EmitFileParams}, deserialize, module::ModuleType, @@ -18,6 +18,7 @@ use farmfe_core::{ resource::{Resource, ResourceOrigin, ResourceType}, rkyv::Deserialize, serialize, + swc_common::sync::OnceCell, }; use farmfe_toolkit::{ fs::{read_file_raw, read_file_utf8, transform_output_filename}, @@ -42,11 +43,15 @@ fn is_asset_query(query: &Vec<(String, String)>) -> bool { query_map.contains_key("raw") || query_map.contains_key("inline") || query_map.contains_key("url") } -pub struct FarmPluginStaticAssets {} +pub struct FarmPluginStaticAssets { + asset_format_mode: OnceCell, +} impl FarmPluginStaticAssets { pub fn new(_: &Config) -> Self { - Self {} + Self { + asset_format_mode: OnceCell::new(), + } } fn is_asset(&self, ext: &str, context: &Arc) -> bool { @@ -173,9 +178,7 @@ impl Plugin for FarmPluginStaticAssets { let mime_type = mime_guess::from_ext(ext).first_or_octet_stream(); let mime_type_str = mime_type.to_string(); - let content = format!( - "export default \"data:{mime_type_str};base64,{file_base64}\"" - ); + let content = format!("export default \"data:{mime_type_str};base64,{file_base64}\""); return Ok(Some(farmfe_core::plugin::PluginTransformHookResult { content, @@ -231,12 +234,23 @@ impl Plugin for FarmPluginStaticAssets { format!("/{resource_name}") }; - let content = if context.config.output.target_env.is_node() { - format!( - "export default new URL(/* {FARM_IGNORE_ACTION_COMMENT} */{assets_path:?}, import.meta.url)" - ) - } else { - format!("export default {assets_path:?};") + let mode = self.asset_format_mode.get_or_init(|| { + get_config_assets_mode(&context.config) + .unwrap_or_else(|| (context.config.output.target_env.clone().into())) + }); + + let content = match mode { + AssetFormatMode::Node => { + format!( + r#" + import {{ fileURLToPath }} from "node:url"; + export default fileURLToPath(new URL(/* {FARM_IGNORE_ACTION_COMMENT} */{assets_path:?}, import.meta.url)) + "# + ) + } + AssetFormatMode::Browser => { + format!("export default {assets_path:?};") + } }; context.emit_file(EmitFileParams { diff --git a/examples/react-ssr/farm.config.server.mjs b/examples/react-ssr/farm.config.server.mjs index 3e82fb218..0f45dbc31 100644 --- a/examples/react-ssr/farm.config.server.mjs +++ b/examples/react-ssr/farm.config.server.mjs @@ -11,13 +11,17 @@ export default { output: { path: './dist', targetEnv: 'node', - format: 'cjs' + format: 'cjs', + publicPath: '/' }, external: [...builtinModules.map((m) => `^${m}$`)], css: { prefixer: { targets: ['last 2 versions', 'Firefox ESR', '> 1%', 'ie >= 11'] } + }, + assets: { + mode: 'browser' } }, plugins: [ diff --git a/examples/react-ssr/src/logo.png b/examples/react-ssr/src/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..67112a5ca48fb28d87cf4a76e5eef3f12c6587ff GIT binary patch literal 52789 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 z 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 + logo +

This is great for search engines that need to index this page. It's also great for users because server-rendered pages tend to load more quickly diff --git a/examples/react-ssr/src/types.d.ts b/examples/react-ssr/src/types.d.ts new file mode 100644 index 000000000..b9b2dd613 --- /dev/null +++ b/examples/react-ssr/src/types.d.ts @@ -0,0 +1,4 @@ +declare module "*.png" { + declare const v: string; + export default v; +} \ No newline at end of file diff --git a/examples/tree-shake-antd/e2e.spec.ts b/examples/tree-shake-antd/e2e.spec.ts index 2f5a14882..db6ac6a1a 100644 --- a/examples/tree-shake-antd/e2e.spec.ts +++ b/examples/tree-shake-antd/e2e.spec.ts @@ -2,6 +2,7 @@ import { test, expect, describe } from 'vitest'; import { startProjectAndTest } from '../../e2e/vitestSetup'; import { basename, dirname } from 'path'; import { fileURLToPath } from 'url'; +import { execa } from 'execa'; const name = basename(import.meta.url); const projectPath = dirname(fileURLToPath(import.meta.url)); @@ -23,6 +24,10 @@ describe(`e2e tests - ${name}`, async () => { command ); + await execa('npm', ['run', 'build'], { + cwd: projectPath, + }) + test(`exmaples ${name} run start`, async () => { await runTest(); }); diff --git a/packages/core/src/config/constants.ts b/packages/core/src/config/constants.ts index 34ca7d11b..08bbc34f3 100644 --- a/packages/core/src/config/constants.ts +++ b/packages/core/src/config/constants.ts @@ -13,7 +13,8 @@ export const CUSTOM_KEYS = { external_record: 'external.record', runtime_isolate: 'runtime.isolate', resolve_dedupe: 'resolve.dedupe', - css_locals_conversion: 'css.modules.locals_conversion' + css_locals_conversion: 'css.modules.locals_conversion', + assets_mode: 'assets.mode' }; export const FARM_RUST_PLUGIN_FUNCTION_ENTRY = 'func.js'; diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index 7ebe6c15f..55b30a15a 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -56,6 +56,7 @@ import { FARM_DEFAULT_NAMESPACE } from './constants.js'; import { mergeConfig, mergeFarmCliConfig } from './mergeConfig.js'; +import { normalizeAsset } from './normalize-config/normalize-asset.js'; import { normalizeCss } from './normalize-config/normalize-css.js'; import { normalizeExternal } from './normalize-config/normalize-external.js'; import { normalizeResolve } from './normalize-config/normalize-resolve.js'; @@ -560,6 +561,7 @@ export async function normalizeUserCompilationConfig( normalizeResolve(userConfig, resolvedCompilation); normalizeCss(userConfig, resolvedCompilation); + normalizeAsset(userConfig, resolvedCompilation); return resolvedCompilation; } diff --git a/packages/core/src/config/normalize-config/normalize-asset.ts b/packages/core/src/config/normalize-config/normalize-asset.ts new file mode 100644 index 000000000..c894762d6 --- /dev/null +++ b/packages/core/src/config/normalize-config/normalize-asset.ts @@ -0,0 +1,14 @@ +import { CUSTOM_KEYS } from '../constants.js'; +import { ResolvedCompilation, UserConfig } from '../types.js'; + +export function normalizeAsset( + config: UserConfig, + resolvedCompilation: ResolvedCompilation +) { + if (config.compilation?.assets?.mode) { + const mode = config.compilation.assets.mode; + + // biome-ignore lint/style/noNonNullAssertion: + resolvedCompilation.custom![CUSTOM_KEYS.assets_mode] = JSON.stringify(mode); + } +} diff --git a/packages/core/src/config/normalize-config/normalize-output.ts b/packages/core/src/config/normalize-config/normalize-output.ts index fadede763..386d1fb11 100644 --- a/packages/core/src/config/normalize-config/normalize-output.ts +++ b/packages/core/src/config/normalize-config/normalize-output.ts @@ -188,6 +188,10 @@ function tryGetDefaultPublicPath( return publicPath; } + if (publicPath) { + return publicPath; + } + if (targetEnv === 'node' && isAbsolute(publicPath)) { // vitejs plugin maybe set absolute path, should transform to relative path const relativePath = './' + path.posix.normalize(publicPath).slice(1); diff --git a/packages/core/src/config/schema.ts b/packages/core/src/config/schema.ts index 0dbd4f02f..e843350b5 100644 --- a/packages/core/src/config/schema.ts +++ b/packages/core/src/config/schema.ts @@ -102,7 +102,9 @@ const compilationConfigSchema = z .optional(), assets: z .object({ - include: z.array(z.string()).optional() + include: z.array(z.string()).optional(), + publicDir: z.string().optional(), + mode: z.enum(['browser', 'node']).optional() }) .strict() .optional(), diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts index 02fbf8189..4f72214f1 100644 --- a/packages/core/src/config/types.ts +++ b/packages/core/src/config/types.ts @@ -121,6 +121,7 @@ export interface ResolvedCompilation resolve?: { dedupe?: never; } & Config['config']['resolve']; + assets?: Omit; css?: ResolvedCss; } diff --git a/packages/core/src/types/binding.ts b/packages/core/src/types/binding.ts index b5d853122..fb2a31851 100644 --- a/packages/core/src/types/binding.ts +++ b/packages/core/src/types/binding.ts @@ -434,6 +434,7 @@ export interface Config { assets?: { include?: string[]; publicDir?: string; + mode?: 'node' | 'browser'; }; script?: ScriptConfig; css?: CssConfig; diff --git a/packages/core/tests/config/index.spec.ts b/packages/core/tests/config/index.spec.ts index f07a42a7f..cd92fbef9 100644 --- a/packages/core/tests/config/index.spec.ts +++ b/packages/core/tests/config/index.spec.ts @@ -115,7 +115,7 @@ describe('normalizeOutput', () => { expect(resolvedConfig.output?.publicPath).toEqual('./'); }); - test('normalizeOutput with node targetEnv and absolute publicPath', () => { + test('normalizeOutput with node targetEnv and absolute publicPath shoud use user input publicPath', () => { const resolvedConfig: ResolvedCompilation = { output: { targetEnv: 'node', @@ -125,6 +125,25 @@ describe('normalizeOutput', () => { normalizeOutput(resolvedConfig, true, new NoopLogger()); expect(resolvedConfig.output.targetEnv).toEqual('node'); - expect(resolvedConfig.output.publicPath).toEqual('./public/'); + expect(resolvedConfig.output.publicPath).toEqual('/public/'); + }); + + test('normalizeOutput with node targetEnv shoud use default publicPath by targetEnv', () => { + ( + [ + { targetEnv: 'node', expectPublic: './' }, + { targetEnv: 'browser', expectPublic: '/' } + ] as const + ).forEach((item) => { + const resolvedConfig: ResolvedCompilation = { + output: { + targetEnv: item.targetEnv + } + }; + + normalizeOutput(resolvedConfig, true, new NoopLogger()); + expect(resolvedConfig.output.targetEnv).toEqual(item.targetEnv); + expect(resolvedConfig.output.publicPath).toEqual(item.expectPublic); + }); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 795774721..3ed29d88e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -10,6 +10,7 @@ export default defineConfig({ environment: 'node', deps: { interopDefault: false - } + }, + retry: 5 } });