Skip to content

Commit

Permalink
feat: add assets mode for asset path generate (#1852)
Browse files Browse the repository at this point in the history
* feat: add assets mode for asset path generate

* test: update test failed
  • Loading branch information
shulandmimi authored Oct 22, 2024
1 parent 92ce692 commit 3651244
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-rabbits-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@farmfe/core": patch
---

add assets mode for asset path generate
31 changes: 31 additions & 0 deletions crates/core/src/config/asset.rs
Original file line number Diff line number Diff line change
@@ -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<TargetEnv> 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<String>,
/// Used internally, this option will be not exposed to user.
pub public_dir: Option<String>,
// TODO: v2
// for ssr mode, should specify asset path format, default from `output.targetEnv`
// pub mode: Option<AssetFormatMode>,
}
36 changes: 22 additions & 14 deletions crates/core/src/config/custom.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -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<CompilationContext>) -> bool {
if let Some(val) = context.config.custom.get(CUSTOM_CONFIG_RUNTIME_ISOLATE) {
Expand All @@ -28,8 +32,8 @@ pub fn get_config_external_record(config: &Config) -> ExternalConfig {
return ExternalConfig::new();
}

let external: HashMap<String, String> = serde_json::from_str(val)
.unwrap_or_else(|_| panic!("failed parse record external {val:?}"));
let external: HashMap<String, String> =
serde_json::from_str(val).unwrap_or_else(|_| panic!("failed parse record external {val:?}"));

let mut external_config = ExternalConfig::new();

Expand All @@ -50,20 +54,24 @@ pub fn get_config_external_record(config: &Config) -> ExternalConfig {
}

pub fn get_config_resolve_dedupe(config: &Config) -> Vec<String> {
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<AssetFormatMode> {
get_field_or_default_from_custom(config, CUSTOM_CONFIG_ASSETS_MODE)
}

fn get_field_or_default_from_custom<T: Default + DeserializeOwned>(
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()
}
11 changes: 3 additions & 8 deletions crates/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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<String>,
/// Used internally, this option will be not exposed to user.
pub public_dir: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SourcemapConfig {
/// Generate inline sourcemap instead of a separate file for mutable resources.
Expand Down
38 changes: 26 additions & 12 deletions crates/plugin_static_assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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},
Expand All @@ -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<AssetFormatMode>,
}

impl FarmPluginStaticAssets {
pub fn new(_: &Config) -> Self {
Self {}
Self {
asset_format_mode: OnceCell::new(),
}
}

fn is_asset(&self, ext: &str, context: &Arc<CompilationContext>) -> bool {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion examples/react-ssr/farm.config.server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
Binary file added examples/react-ssr/src/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions examples/react-ssr/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { Routes, Route, Outlet, Link } from 'react-router-dom';
import React from "react";
import { Routes, Route, Outlet, Link } from "react-router-dom";
import logo from "./logo.png";

export default function App() {
return (
Expand All @@ -12,6 +13,8 @@ export default function App() {
server!
</p>

<img style={{ width: 350, height: 100 }} src={logo} alt="logo" />

<p>
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
Expand Down
4 changes: 4 additions & 0 deletions examples/react-ssr/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module "*.png" {
declare const v: string;
export default v;
}
5 changes: 5 additions & 0 deletions examples/tree-shake-antd/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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();
});
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 2 additions & 0 deletions packages/core/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -560,6 +561,7 @@ export async function normalizeUserCompilationConfig(

normalizeResolve(userConfig, resolvedCompilation);
normalizeCss(userConfig, resolvedCompilation);
normalizeAsset(userConfig, resolvedCompilation);

return resolvedCompilation;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/config/normalize-config/normalize-asset.ts
Original file line number Diff line number Diff line change
@@ -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: <explanation>
resolvedCompilation.custom![CUSTOM_KEYS.assets_mode] = JSON.stringify(mode);
}
}
4 changes: 4 additions & 0 deletions packages/core/src/config/normalize-config/normalize-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export interface ResolvedCompilation
resolve?: {
dedupe?: never;
} & Config['config']['resolve'];
assets?: Omit<Config['config']['assets'], 'mode'>;
css?: ResolvedCss;
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types/binding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ export interface Config {
assets?: {
include?: string[];
publicDir?: string;
mode?: 'node' | 'browser';
};
script?: ScriptConfig;
css?: CssConfig;
Expand Down
23 changes: 21 additions & 2 deletions packages/core/tests/config/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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);
});
});
});
3 changes: 2 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineConfig({
environment: 'node',
deps: {
interopDefault: false
}
},
retry: 5
}
});

0 comments on commit 3651244

Please sign in to comment.