diff --git a/.changeset/few-bobcats-shave.md b/.changeset/few-bobcats-shave.md new file mode 100644 index 000000000..35d86a0b3 --- /dev/null +++ b/.changeset/few-bobcats-shave.md @@ -0,0 +1,5 @@ +--- +"@farmfe/core": minor +--- + +optimize @farmfe/core api usage diff --git a/examples/react/index.js b/examples/react/index.js new file mode 100644 index 000000000..e87cfb6ff --- /dev/null +++ b/examples/react/index.js @@ -0,0 +1,65 @@ +import { + createCompiler, + createDevServer, + createFileWatcher, + resolveConfig, + start, +} from "@farmfe/core"; + +const resolvedUserConfig = await resolveConfig({ + compilation: { + sourcemap: true, + persistentCache: false, + presetEnv: false, + progress: false, + output: { + publicPath: '/dist/' + }, + input: { + index: './index.html' + } + }, + server: { + port: 6532, + hmr: { + path: '/__farm_hmr' + } + }, + plugins: [ + '@farmfe/plugin-react', + '@farmfe/plugin-sass' + ], + mode: 'development', +}); + +const compiler = await createCompiler(resolvedUserConfig); + +const devServer = await createDevServer(compiler, resolvedUserConfig); + +await devServer.listen(); + +await start({ + compilation: { + sourcemap: true, + persistentCache: false, + presetEnv: false, + progress: false, + output: { + publicPath: '/dist/' + }, + input: { + index: './index.html' + } + }, + server: { + port: 6532, + hmr: { + path: '/__farm_hmr' + } + }, + plugins: [ + '@farmfe/plugin-react', + '@farmfe/plugin-sass' + ], + mode: 'development', +}); diff --git a/examples/react/src/components/welcome/index.tsx b/examples/react/src/components/welcome/index.tsx index bb8d48b12..a8ae6ca3e 100644 --- a/examples/react/src/components/welcome/index.tsx +++ b/examples/react/src/components/welcome/index.tsx @@ -27,7 +27,7 @@ export function Welcome() {

- Get started Withasdasd + Get started With React + Farm

diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx index 36a211cb0..edf6e46df 100644 --- a/examples/react/src/main.tsx +++ b/examples/react/src/main.tsx @@ -10,7 +10,6 @@ export function Main() { const store = useStore(); console.log(process.env.NODE_ENV); console.log(import.meta); - return ( <>
diff --git a/examples/vite-adapter-vue/components.d.ts b/examples/vite-adapter-vue/components.d.ts index 072df20ce..7039ab623 100644 --- a/examples/vite-adapter-vue/components.d.ts +++ b/examples/vite-adapter-vue/components.d.ts @@ -7,6 +7,11 @@ export {} declare module 'vue' { export interface GlobalComponents { + ElButton: typeof import('element-plus/es')['ElButton'] + ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] + ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElTree: typeof import('element-plus/es')['ElTree'] Formatter: typeof import('./src/components/Formatter.vue')['default'] HelloWorld: typeof import('./src/components/HelloWorld.vue')['default'] Intro: typeof import('./src/components/Intro.vue')['default'] diff --git a/packages/core/src/compiler/index.ts b/packages/core/src/compiler/index.ts index 6b9a8d3f5..0fee3a005 100644 --- a/packages/core/src/compiler/index.ts +++ b/packages/core/src/compiler/index.ts @@ -4,7 +4,7 @@ import { Compiler as BindingCompiler } from '../../binding/index.js'; import type { Resource } from '../index.js'; import type { Config, JsUpdateResult } from '../types/binding.js'; -import type { ILogger } from '../utils/logger.js'; +import { type ILogger, Logger } from '../utils/logger.js'; export const VIRTUAL_FARM_DYNAMIC_IMPORT_SUFFIX = '.farm_dynamic_import_virtual_module'; @@ -38,7 +38,7 @@ export class Compiler { constructor( public config: Config, - private logger: ILogger + private logger: ILogger = new Logger() ) { this._bindingCompiler = new BindingCompiler(this.config); } diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index fe41aa79c..34fab6c45 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -84,9 +84,10 @@ export function defineFarmConfig(config: UserConfigExport): UserConfigExport { async function getDefaultConfig( config: UserConfig, inlineOptions: FarmCLIOptions, - logger: Logger, - mode?: CompilationMode + mode?: CompilationMode, + logger?: Logger ) { + logger = logger ?? new Logger(); const resolvedUserConfig = await resolveMergedUserConfig( config, undefined, @@ -94,7 +95,7 @@ async function getDefaultConfig( logger ); - resolvedUserConfig.server = normalizeDevServerOptions({}, mode); + resolvedUserConfig.server = normalizeDevServerConfig(inlineOptions, mode); resolvedUserConfig.compilation = await normalizeUserCompilationConfig( resolvedUserConfig, @@ -129,15 +130,16 @@ async function handleServerPortConflict( * @param configPath */ export async function resolveConfig( - inlineOptions: FarmCLIOptions & UserConfig, - logger: Logger, + inlineOptions: FarmCLIOptions & UserConfig = {}, mode?: CompilationMode, + logger?: Logger, isHandleServerPortConflict = true ): Promise { // Clear the console according to the cli command + checkClearScreen(inlineOptions); + logger = logger ?? new Logger(); inlineOptions.mode = inlineOptions.mode ?? mode; - // configPath may be file or directory let { configPath } = inlineOptions; let rawConfig: UserConfig = mergeFarmCliConfig(inlineOptions, {}); @@ -163,7 +165,7 @@ export async function resolveConfig( } else { mergeConfig( rawConfig, - await getDefaultConfig(rawConfig, inlineOptions, logger, mode) + await getDefaultConfig(rawConfig, inlineOptions, mode, logger) ); } @@ -172,26 +174,11 @@ export async function resolveConfig( config: rawConfig }; - const { jsPlugins, rustPlugins } = await resolveFarmPlugins(userConfig); - - const rawJsPlugins = (await resolveAsyncPlugins(jsPlugins || [])).filter( - Boolean - ); - - let vitePluginAdapters: JsPlugin[] = []; - const vitePlugins = (userConfig?.vitePlugins ?? []).filter(Boolean); - // run config and configResolved hook - if (vitePlugins.length) { - vitePluginAdapters = await handleVitePlugins( - vitePlugins, - userConfig, - logger, - mode - ); - } + const { jsPlugins, vitePlugins, rustPlugins, vitePluginAdapters } = + await resolvePlugins(userConfig, logger, mode); const sortFarmJsPlugins = getSortedPlugins([ - ...rawJsPlugins, + ...jsPlugins, ...vitePluginAdapters, externalAdapter() ]); @@ -208,7 +195,7 @@ export async function resolveConfig( ); // normalize server config first cause it may be used in normalizeUserCompilationConfig - resolvedUserConfig.server = normalizeDevServerOptions( + resolvedUserConfig.server = normalizeDevServerConfig( resolvedUserConfig.server, mode ); @@ -247,10 +234,6 @@ export async function resolveConfig( return resolvedUserConfig; } -// type ServerConfig = { -// server?: NormalizedServerConfig; -// }; - /** * Normalize user config and transform it to rust compiler compatible config * @@ -610,7 +593,7 @@ function tryAsFileRead(value?: any): string | Buffer { return value; } -export function normalizeDevServerOptions( +export function normalizeDevServerConfig( options: UserServerConfig | undefined, mode: string ): NormalizedServerConfig { @@ -820,7 +803,7 @@ export function normalizePublicPath( function checkClearScreen(inlineConfig: FarmCLIOptions) { if ( - inlineConfig.clearScreen && + inlineConfig?.clearScreen && !__FARM_GLOBAL__.__FARM_RESTART_DEV_SERVER__ ) { clearScreen(); @@ -831,26 +814,15 @@ export async function resolveMergedUserConfig( mergedUserConfig: UserConfig, configFilePath: string | undefined, mode: 'development' | 'production' | string, - logger: Logger + logger: Logger = new Logger() ): Promise { - const serverConfig: NormalizedServerConfig = { - ...DEFAULT_DEV_SERVER_OPTIONS, - ...mergedUserConfig.server, - hmr: { - ...DEFAULT_HMR_OPTIONS, - ...(isObject(mergedUserConfig.server?.hmr) - ? mergedUserConfig.server.hmr - : {}) - } - }; - const resolvedUserConfig: ResolvedUserConfig = { + const resolvedUserConfig = { ...mergedUserConfig, compilation: { ...mergedUserConfig.compilation, external: [] - }, - server: serverConfig - }; + } + } as ResolvedUserConfig; // set internal config resolvedUserConfig.envMode = mode; @@ -1076,3 +1048,32 @@ const transformFarmPluginPath = (importers: string, root: string) => ({ } } }); +export async function resolvePlugins( + userConfig: UserConfig, + logger: Logger, + mode: CompilationMode +) { + const { jsPlugins, rustPlugins } = await resolveFarmPlugins(userConfig); + const rawJsPlugins = (await resolveAsyncPlugins(jsPlugins || [])).filter( + Boolean + ); + + let vitePluginAdapters: JsPlugin[] = []; + const vitePlugins = (userConfig?.vitePlugins ?? []).filter(Boolean); + + if (vitePlugins.length) { + vitePluginAdapters = await handleVitePlugins( + vitePlugins, + userConfig, + logger, + mode + ); + } + + return { + jsPlugins: rawJsPlugins, + vitePlugins, + rustPlugins, + vitePluginAdapters + }; +} diff --git a/packages/core/src/config/mergeConfig.ts b/packages/core/src/config/mergeConfig.ts index c8ef63e8d..f58578b62 100644 --- a/packages/core/src/config/mergeConfig.ts +++ b/packages/core/src/config/mergeConfig.ts @@ -25,9 +25,12 @@ export function mergeConfig>( if (isArray(left) || isArray(right)) { result[key] = [ - ...(isArray(left) ? left : []), - ...(isArray(right) ? right : []) + ...new Set([ + ...(isArray(left) ? left : []), + ...(isArray(right) ? right : []) + ]) ]; + continue; } @@ -78,7 +81,10 @@ export function mergeFarmCliConfig( } else { target.root = cliRoot; } + } else { + target.root = process.cwd(); } + if (configRootPath) { target.root = configRootPath; } @@ -135,3 +141,9 @@ export function mergeFarmCliConfig( return mergeConfig(left, target); } + +export function initialCliOptions(options: FarmCLIOptions): FarmCLIOptions { + return { + ...options + }; +} diff --git a/packages/core/src/config/normalize-config/normalize-persistent-cache.ts b/packages/core/src/config/normalize-config/normalize-persistent-cache.ts index 663bbfc66..742e1d7c9 100644 --- a/packages/core/src/config/normalize-config/normalize-persistent-cache.ts +++ b/packages/core/src/config/normalize-config/normalize-persistent-cache.ts @@ -21,10 +21,7 @@ export async function normalizePersistentCache( resolvedUserConfig: ResolvedUserConfig, logger: Logger ) { - if ( - config?.persistentCache === false || - resolvedUserConfig.configFilePath === undefined - ) { + if (config?.persistentCache === false) { return; } @@ -151,7 +148,6 @@ export async function normalizePersistentCache( packages.push(...(rustPlugins ?? [])); if (packages?.length) { - // console.log('packages', config); const require = createRequire(path.join(config.root, 'package.json')); for (const p of packages) { diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts index c106481ac..bea9491e4 100644 --- a/packages/core/src/config/types.ts +++ b/packages/core/src/config/types.ts @@ -56,7 +56,7 @@ export interface UserPreviewServerConfig { export type NormalizedServerConfig = Required< Omit & { - hmr: Required; + hmr?: Required; } >; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 615585e2e..174c5658f 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -47,10 +47,12 @@ export async function start( try { const resolvedUserConfig = await resolveConfig( inlineConfig, - logger, - 'development' + 'development', + logger ); + // console.log(resolvedUserConfig); + const compiler = await createCompiler(resolvedUserConfig, logger); const devServer = await createDevServer( @@ -59,19 +61,7 @@ export async function start( logger ); - const watcher = await createFileWatcher( - devServer, - resolvedUserConfig, - inlineConfig, - logger - ); - // call configureDevServer hook after both server and watcher are ready - resolvedUserConfig.jsPlugins.forEach((plugin: JsPlugin) => - plugin.configureDevServer?.(devServer) - ); - await devServer.listen(); - watcher.watchExtraFiles(); } catch (error) { logger.error(`Failed to start the server: \n ${error}`, { exit: true }); } @@ -86,8 +76,8 @@ export async function build( const resolvedUserConfig = await resolveConfig( inlineConfig, - logger, 'production', + logger, false ); @@ -105,8 +95,8 @@ export async function preview(inlineConfig?: FarmCLIOptions): Promise { const logger = inlineConfig.logger ?? new Logger(); const resolvedUserConfig = await resolveConfig( inlineConfig, - logger, - 'production' + 'production', + logger ); const { root, output } = resolvedUserConfig.compilation; @@ -154,8 +144,8 @@ export async function watch( const resolvedUserConfig = await resolveConfig( inlineConfig, - logger, 'development', + logger, true ); @@ -364,6 +354,13 @@ export async function createDevServer( ) { const server = new Server({ compiler, logger }); await server.createDevServer(resolvedUserConfig.server); + const watcher = await createFileWatcher(server, resolvedUserConfig, logger); + // call configureDevServer hook after both server and watcher are ready + resolvedUserConfig.jsPlugins.forEach((plugin: JsPlugin) => + plugin.configureDevServer?.(server) + ); + + watcher.watchExtraFiles(); return server; } @@ -371,8 +368,7 @@ export async function createDevServer( export async function createFileWatcher( devServer: Server, resolvedUserConfig: ResolvedUserConfig, - inlineConfig: FarmCLIOptions & UserConfig, - logger: Logger + logger: Logger = new Logger() ) { if ( devServer.config.hmr && @@ -386,14 +382,15 @@ export async function createFileWatcher( return; } + if (devServer.watcher) { + return; + } + const fileWatcher = new FileWatcher(devServer, resolvedUserConfig, logger); devServer.watcher = fileWatcher; await fileWatcher.watch(); - // const farmWatcher = new ConfigWatcher(resolvedUserConfig); - const configFilePath = await getConfigFilePath( - inlineConfig.configPath ?? resolvedUserConfig.root - ); + const configFilePath = await getConfigFilePath(resolvedUserConfig.root); const farmWatcher = new ConfigWatcher({ ...resolvedUserConfig, configFilePath @@ -407,7 +404,7 @@ export async function createFileWatcher( await devServer.close(); __FARM_GLOBAL__.__FARM_RESTART_DEV_SERVER__ = true; - await start(inlineConfig); + await start(resolvedUserConfig as FarmCLIOptions & UserConfig); }); }); return fileWatcher; diff --git a/packages/core/src/plugin/js/index.ts b/packages/core/src/plugin/js/index.ts index ffa2c63e0..e37c89ce2 100644 --- a/packages/core/src/plugin/js/index.ts +++ b/packages/core/src/plugin/js/index.ts @@ -5,7 +5,7 @@ import { type JsPlugin, Logger, type UserConfig, - normalizeDevServerOptions + normalizeDevServerConfig } from '../../index.js'; import merge from '../../utils/merge.js'; import { resolveAsyncPlugins } from '../index.js'; @@ -34,7 +34,7 @@ export async function handleVitePlugins( if (vitePlugins.length) { userConfig = merge({}, userConfig, { compilation: userConfig.compilation, - server: normalizeDevServerOptions( + server: normalizeDevServerConfig( userConfig.server, userConfig.compilation?.mode ?? mode ) diff --git a/packages/core/src/plugin/rust/rustPluginResolver.ts b/packages/core/src/plugin/rust/rustPluginResolver.ts index 72671d038..01ee29247 100644 --- a/packages/core/src/plugin/rust/rustPluginResolver.ts +++ b/packages/core/src/plugin/rust/rustPluginResolver.ts @@ -2,13 +2,7 @@ import { createRequire } from 'node:module'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; -export type RustPlugin = - | string - | [ - string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Record | undefined - ]; +export type RustPlugin = string | [string, Record]; type RustPluginPathObject = { binary: string; @@ -26,14 +20,13 @@ export async function rustPluginResolver( plugin: RustPlugin, root: string ): Promise<[string, string]> { - let pluginPath: string, options: string; + let pluginPath: string; + let options = '{}'; if (typeof plugin === 'string') { pluginPath = plugin; - options = '{}'; } else if (Array.isArray(plugin) && plugin.length === 2) { - pluginPath = plugin[0]; - options = JSON.stringify(plugin[1]) ?? '{}'; + [pluginPath, options] = [plugin[0], JSON.stringify(plugin[1]) ?? '{}']; } else { throw new Error( 'Invalid config: [plugins]. A rust plugin must be a string, or [string, Record]' diff --git a/packages/core/tests/binding.spec.ts b/packages/core/tests/binding.spec.ts index 0ddd0072e..292ec08a5 100644 --- a/packages/core/tests/binding.spec.ts +++ b/packages/core/tests/binding.spec.ts @@ -5,7 +5,7 @@ import { Compiler, Logger, UserConfig, - normalizeDevServerOptions, + normalizeDevServerConfig, normalizeUserCompilationConfig, resolveMergedUserConfig } from '../src/index.js'; @@ -13,7 +13,7 @@ import { // just make sure the binding works test('Binding - should parse config to rust correctly', async () => { const currentDir = path.dirname(fileURLToPath(import.meta.url)); - const serverConfig = normalizeDevServerOptions({}, 'production'); + const serverConfig = normalizeDevServerConfig({}, 'production'); const config: UserConfig = { root: path.resolve(currentDir, 'fixtures', 'binding'), diff --git a/packages/core/tests/cjs.spec.ts b/packages/core/tests/cjs.spec.ts index 18c6b0765..66ec4e929 100644 --- a/packages/core/tests/cjs.spec.ts +++ b/packages/core/tests/cjs.spec.ts @@ -2,7 +2,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { expect, test } from 'vitest'; -import { normalizeDevServerOptions, resolveConfig } from '../src/index.js'; +import { normalizeDevServerConfig, resolveConfig } from '../src/index.js'; import { Logger } from '../src/utils/logger.js'; test('resolveUserConfig', async () => { @@ -10,8 +10,8 @@ test('resolveUserConfig', async () => { const config = await resolveConfig( { configPath: path.join(filePath, 'fixtures', 'config', 'farm.config.ts') }, - new Logger(), - 'development' + 'development', + new Logger() ); console.log(config.compilation.define); @@ -66,6 +66,6 @@ test('resolveUserConfig', async () => { moduleCacheKeyStrategy: {} }); expect(config.server).toEqual( - normalizeDevServerOptions(config.server, 'development') + normalizeDevServerConfig(config.server, 'development') ); }); diff --git a/packages/core/tests/config.spec.ts b/packages/core/tests/config.spec.ts index 7ea66f0ec..6b16d707b 100644 --- a/packages/core/tests/config.spec.ts +++ b/packages/core/tests/config.spec.ts @@ -5,7 +5,7 @@ import { describe, expect, test } from 'vitest'; import { parseUserConfig } from '../src/config/schema.js'; import { DEFAULT_DEV_SERVER_OPTIONS, - normalizeDevServerOptions, + normalizeDevServerConfig, resolveConfig } from '../src/index.js'; import { Logger } from '../src/utils/logger.js'; @@ -15,8 +15,8 @@ test('resolveUserConfig', async () => { const config = await resolveConfig( { configPath: path.join(filePath, 'fixtures', 'config', 'farm.config.ts') }, - new Logger(), - 'development' + 'development', + new Logger() ); expect(config.compilation.define).toEqual({ @@ -70,7 +70,7 @@ test('resolveUserConfig', async () => { moduleCacheKeyStrategy: {} }); expect(config.server).toEqual( - normalizeDevServerOptions(config.server, 'development') + normalizeDevServerConfig(config.server, 'development') ); }); @@ -79,8 +79,8 @@ test('resolveUserConfig-prod', async () => { const config = await resolveConfig( { configPath: path.join(filePath, 'fixtures', 'config', 'farm.config.ts') }, - new Logger(), - 'production' + 'production', + new Logger() ); expect(config.compilation.define).toEqual({ @@ -128,7 +128,7 @@ test('resolveUserConfig-prod', async () => { moduleCacheKeyStrategy: {} }); expect(config.server).toEqual( - normalizeDevServerOptions(config.server, 'production') + normalizeDevServerConfig(config.server, 'production') ); }); @@ -143,8 +143,8 @@ test('resolveUserConfig-input-html-prod', async () => { ); const config = await resolveConfig( { configPath: configFilePath }, - new Logger(), - 'production' + 'production', + new Logger() ); expect(config.compilation.input).toEqual({ @@ -227,26 +227,26 @@ test('resolveUserConfig-input-html-prod', async () => { }); expect(config.server).toEqual( - normalizeDevServerOptions(config.server, 'production') + normalizeDevServerConfig(config.server, 'production') ); }); describe('normalize-dev-server-options', () => { test('default', () => { - const options = normalizeDevServerOptions({}, 'development'); + const options = normalizeDevServerConfig({}, 'development'); expect(options.https).toBe(DEFAULT_DEV_SERVER_OPTIONS.https); expect(options.port).toBe(DEFAULT_DEV_SERVER_OPTIONS.port); expect(options.hmr).not.toBe(false); }); test('custom port', () => { - const options = normalizeDevServerOptions({ port: 8080 }, 'development'); + const options = normalizeDevServerConfig({ port: 8080 }, 'development'); expect(options.https).toBe(DEFAULT_DEV_SERVER_OPTIONS.https); expect(options.port).toBe(8080); }); test('disable HMR in prod', () => { - const options = normalizeDevServerOptions({}, 'production'); + const options = normalizeDevServerConfig({}, 'production'); expect(options.hmr).toBe(false); }); }); diff --git a/packages/core/tests/js-plugins.spec.ts b/packages/core/tests/js-plugins.spec.ts index 40d33a2ef..51ff54b10 100644 --- a/packages/core/tests/js-plugins.spec.ts +++ b/packages/core/tests/js-plugins.spec.ts @@ -38,6 +38,7 @@ test('Js Plugin Execution - resolve', async () => { expect(param.source).toBe('./index.ts?foo=bar'); expect(param.importer).toBe(null); expect(param.kind).toEqual({ entry: 'index' }); + console.log(resolvedPath); return { resolvedPath,