From 69391da7e88b98a0326dddc1ec91464842b525b3 Mon Sep 17 00:00:00 2001 From: Nirvana <1357711537@qq.com> Date: Tue, 30 Jul 2024 22:44:43 +0800 Subject: [PATCH] feat: add the pulicMiddleware (#1673) * feat: add the pulicMiddleware * feat: add the logger for the publicMiddleware --------- Co-authored-by: zengwenjie.paq Co-authored-by: ADNY <66500121+ErKeLost@users.noreply.github.com> --- examples/public-dir/src/main.tsx | 5 +- packages/core/package.json | 2 +- packages/core/src/config/index.ts | 7 ++ packages/core/src/config/types.ts | 1 + packages/core/src/index.ts | 2 +- packages/core/src/newServer/index.ts | 22 ++-- .../middlewares/htmlFallbackMiddleware.ts | 1 + .../newServer/middlewares/publicMiddleware.ts | 107 ++++++++++++++++-- packages/core/src/newServer/publicDir.ts | 3 +- packages/core/src/utils/path.ts | 8 ++ packages/core/src/utils/url.ts | 13 +++ pnpm-lock.yaml | 46 +++++++- 12 files changed, 189 insertions(+), 28 deletions(-) diff --git a/examples/public-dir/src/main.tsx b/examples/public-dir/src/main.tsx index c2abe8bc9..18dcdaa0a 100644 --- a/examples/public-dir/src/main.tsx +++ b/examples/public-dir/src/main.tsx @@ -1,7 +1,8 @@ import React, { useState } from "react"; import "./main.css"; import reactLogo from "./assets/react.svg"; -// import FarmLogo from "../public/logo.png"; +import FarmLogo from "/new-logo.png"; +// import FarmLogo from "../newPublic/new-logo.png"; export function Main() { const [count, setCount] = useState(0); @@ -9,7 +10,7 @@ export function Main() { <>
- {/* Farm logo */} + Farm logo React logo diff --git a/packages/core/package.json b/packages/core/package.json index 984a1043f..4c48b9523 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -88,7 +88,7 @@ "etag": "^1.8.1", "http-proxy": "^1.18.1", "react-refresh": "^0.14.0", - "sirv": "^2.0.3", + "sirv": "^2.0.4", "ws": "^8.14.2" }, "dependencies": { diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index 49934d101..73309b351 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -118,6 +118,7 @@ export async function resolveConfig( // configPath may be file or directory const { configFile, configPath: initialConfigPath } = inlineOptions; + const loadedUserConfig: any = await loadConfigFile( configFile, inlineOptions, @@ -697,6 +698,7 @@ export function normalizePublicDir(root: string, publicDir = 'public') { const absPublicDirPath = path.isAbsolute(publicDir) ? publicDir : path.resolve(root, publicDir); + return absPublicDirPath; } @@ -915,6 +917,11 @@ export async function resolveUserConfig( mode }; + resolvedUserConfig.publicDir = normalizePublicDir( + resolvedRootPath, + userConfig.publicDir + ); + return resolvedUserConfig; } diff --git a/packages/core/src/config/types.ts b/packages/core/src/config/types.ts index 96e454f1a..71eea65fe 100644 --- a/packages/core/src/config/types.ts +++ b/packages/core/src/config/types.ts @@ -160,6 +160,7 @@ export interface FarmCliOptions logger?: Logger; config?: string; configFile?: string; + // todo: check to delete configPath?: string; compilation?: Config['config']; mode?: string; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 21716b75b..80fdc6979 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -474,7 +474,7 @@ export async function start2( ); const compiler = await createCompiler(resolvedUserConfig, logger); - const server = new newServer(compiler, resolvedUserConfig); + const server = new newServer(compiler, resolvedUserConfig, logger); await server.createServer(); await server.listen(); // const devServer = await createDevServer( diff --git a/packages/core/src/newServer/index.ts b/packages/core/src/newServer/index.ts index a9b41de75..f00c97ac7 100644 --- a/packages/core/src/newServer/index.ts +++ b/packages/core/src/newServer/index.ts @@ -13,7 +13,7 @@ import { Compiler } from '../compiler/index.js'; import { normalizePublicPath } from '../config/normalize-config/normalize-output.js'; import { NormalizedServerConfig, ResolvedUserConfig } from '../config/types.js'; import { logError } from '../server/error.js'; -import { logger } from '../utils/logger.js'; +import { Logger, logger } from '../utils/logger.js'; import { initPublicFiles } from '../utils/publicDir.js'; import { isObject } from '../utils/share.js'; import { FileWatcher } from '../watcher/index.js'; @@ -91,11 +91,16 @@ export class newServer { publicPath?: string; httpServer?: HttpServer; watcher: FileWatcher; + logger: Logger; - constructor(compiler: CompilerType, config: ResolvedUserConfig) { + constructor( + compiler: CompilerType, + config: ResolvedUserConfig, + logger: Logger + ) { this.compiler = compiler; this.config = config; - + this.logger = logger; if (!this.compiler) return; this.publicPath = @@ -132,15 +137,8 @@ export class newServer { // middleware // middlewares.use(compression()); - if (this.publicDir) { - middlewares.use( - publicMiddleware( - this.httpServer, - this.compiler, - this.publicPath, - this.config - ) - ); + if (publicDir) { + middlewares.use(publicMiddleware(this.logger, this.config, publicFiles)); } // TODO todo add appType middlewares.use( diff --git a/packages/core/src/newServer/middlewares/htmlFallbackMiddleware.ts b/packages/core/src/newServer/middlewares/htmlFallbackMiddleware.ts index 672b4e932..91e0d0064 100644 --- a/packages/core/src/newServer/middlewares/htmlFallbackMiddleware.ts +++ b/packages/core/src/newServer/middlewares/htmlFallbackMiddleware.ts @@ -4,6 +4,7 @@ import { ResolvedUserConfig } from '../../config/types.js'; import { commonFsUtils } from '../../utils/fsUtils.js'; import { cleanUrl } from '../../utils/url.js'; import { HttpServer } from '../index.js'; + export function htmlFallbackMiddleware( server: HttpServer, compiler: Compiler, diff --git a/packages/core/src/newServer/middlewares/publicMiddleware.ts b/packages/core/src/newServer/middlewares/publicMiddleware.ts index f7e50de23..f5ea6ff29 100644 --- a/packages/core/src/newServer/middlewares/publicMiddleware.ts +++ b/packages/core/src/newServer/middlewares/publicMiddleware.ts @@ -1,16 +1,107 @@ -import { Compiler } from '../../compiler/index.js'; +import sirv from 'sirv'; import { ResolvedUserConfig } from '../../config/types.js'; -import { HttpServer } from '../index.js'; +import { colors } from '../../utils/color.js'; +import { Logger } from '../../utils/logger.js'; +import { removeHashFromPath, withTrailingSlash } from '../../utils/path.js'; +import { normalizePath } from '../../utils/share.js'; +import { + cleanUrl, + isImportRequest, + knownJavascriptExtensionRE, + removeImportQuery, + urlRE +} from '../../utils/url.js'; + +function warnAboutPublicDir(url: string, publicPath: string) { + let warning: string; + if (isImportRequest(url)) { + const rawUrl = removeImportQuery(url); + if (urlRE.test(url)) { + warning = + `Assets in the public directory are directly accessible at the root path.\n` + + `Use ${colors.brandColor( + rawUrl.replace(publicPath, '/') + )} instead of the explicit ${colors.brandColor(rawUrl)}.`; + } else { + warning = + 'Assets in the public directory should not be imported directly in JavaScript.\n' + + `To import an asset, place it inside the src directory. Use ${colors.brandColor( + rawUrl.replace(publicPath, '/src/') + )} instead of ${colors.cyan(rawUrl)}.\n` + + `For referencing the asset's path, use ${colors.brandColor( + rawUrl.replace(publicPath, '/') + )}.`; + } + } else { + warning = + `Public directory files are accessible directly at the root path.\n` + + `Use ${colors.brandColor( + url.replace(publicPath, '/') + )} directly, rather than ${colors.brandColor(`${publicPath}${url}`)}.`; + } + + return warning; +} export function publicMiddleware( - server: HttpServer, - compiler: Compiler, - publicPath: string, - config: ResolvedUserConfig + logger: Logger, + config: ResolvedUserConfig, + publicFiles?: Set ) { - return async function handlerPublicMiddleware( + const { publicDir, root } = config; + const publicPath = `${publicDir.slice(root.length)}`; + const headers = config.server.headers; + const serve = sirv(publicDir, { + dev: true, + etag: true, + extensions: [], + setHeaders: (res, path) => { + if (knownJavascriptExtensionRE.test(path)) { + res.setHeader('Content-Type', 'text/javascript'); + } + if (headers) { + for (const name in headers) { + res.setHeader(name, headers[name]!); + } + } + } + }); + const toFilePath = (url: string) => { + let filePath = cleanUrl(url); + if (filePath.indexOf('%') !== -1) { + try { + filePath = decodeURI(filePath); + } catch (err) { + // ignore + } + } + return normalizePath(filePath); + }; + + return async function farmHandlerPublicMiddleware( req: any, res: any, next: () => void - ) {}; + ) { + const url = removeHashFromPath(req.url!); + const filePath = toFilePath(url); + + // If it is not equal, it means that it is recognized as a module + if ( + publicDir.startsWith(withTrailingSlash(root)) && + publicFiles.has(url) && + req.url !== url + ) { + const publicDirWarning = warnAboutPublicDir(url, publicPath); + if (publicDirWarning) { + logger.warn(publicDirWarning); + } + } + + if (publicFiles && !publicFiles.has(filePath)) { + return next(); + } + + serve(req, res, next); + }; } diff --git a/packages/core/src/newServer/publicDir.ts b/packages/core/src/newServer/publicDir.ts index 9b6725ce5..71ff3e232 100644 --- a/packages/core/src/newServer/publicDir.ts +++ b/packages/core/src/newServer/publicDir.ts @@ -10,8 +10,7 @@ export async function initPublicFiles( config: ResolvedUserConfig ): Promise | undefined> { let fileNames: string[]; - const publicDir: string = config.compilation?.assets?.publicDir as string; - console.log(publicDir); + const publicDir: string = config.publicDir; try { fileNames = await recursiveReaddir(publicDir); diff --git a/packages/core/src/utils/path.ts b/packages/core/src/utils/path.ts index 7a8519f3f..f6ac7c7df 100644 --- a/packages/core/src/utils/path.ts +++ b/packages/core/src/utils/path.ts @@ -14,3 +14,11 @@ const postfixRE = /[?#].*$/; export function stripQueryAndHash(path: string): string { return path.replace(postfixRE, ''); } + +export function removeHashFromPath(url: string): string { + const hashPattern = /(_[a-zA-Z\d]{4,8})\./; + + const newURL = url.replace(hashPattern, '.'); + + return newURL; +} diff --git a/packages/core/src/utils/url.ts b/packages/core/src/utils/url.ts index edb507354..fa6d78c42 100644 --- a/packages/core/src/utils/url.ts +++ b/packages/core/src/utils/url.ts @@ -2,3 +2,16 @@ const postfixRE = /[?#].*$/; export function cleanUrl(url: string): string { return url.replace(postfixRE, ''); } + +const importQueryRE = /(\?|&)import=?(?:&|$)/; +export const isImportRequest = (url: string): boolean => + importQueryRE.test(url); + +const trailingSeparatorRE = /[?&]$/; +export function removeImportQuery(url: string): string { + return url.replace(importQueryRE, '$1').replace(trailingSeparatorRE, ''); +} + +export const knownJavascriptExtensionRE = /\.[tj]sx?$/; + +export const urlRE = /(\?|&)url(?:&|$)/; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbe8ddfa1..2c1998cd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2431,8 +2431,8 @@ importers: specifier: ^0.14.0 version: 0.14.0 sirv: - specifier: ^2.0.3 - version: 2.0.3 + specifier: ^2.0.4 + version: 2.0.4 ws: specifier: ^8.14.2 version: 8.14.2(bufferutil@4.0.8)(utf-8-validate@6.0.3) @@ -3115,24 +3115,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@1.7.2': resolution: {integrity: sha512-Z1CSGQE6fHz55gkiFHv9E8wEAaSUd7dHSRaxSCBa7utonHqpIeMbvj3Evm1w0WfGLFDtRXLV1fTfEdM0FMTOhA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@1.7.2': resolution: {integrity: sha512-x10LpGMepDrLS+h2TZ6/T7egpHjGKtiI4GuShNylmBQJWfTotbFf9eseHggrqJ4WZf9yrGoVYrtbxXftuB95sQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@1.7.2': resolution: {integrity: sha512-vXXyox8/CQijBxAu0+r8FfSO7JlC4tob3PbaFda8gPJFRz2uFJw39HtxVUwbTV1EcU6wSPh4SiRu5sZfP1VHrQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@1.7.2': resolution: {integrity: sha512-kRXdlKzcU7INf6/ldu0nVmkOgt7bKqmyXRRCUqqaJfA32+9InTbkD8tGrHZEVYIWr+eTuKcg16qZVDsPSDFZ8g==} @@ -4287,6 +4291,9 @@ packages: '@polka/url@1.0.0-next.23': resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + '@polka/url@1.0.0-next.25': + resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -4404,36 +4411,43 @@ packages: resolution: {integrity: sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.14.1': resolution: {integrity: sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-powerpc64le-gnu@4.14.1': resolution: {integrity: sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==} cpu: [ppc64le] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.14.1': resolution: {integrity: sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.14.1': resolution: {integrity: sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.14.1': resolution: {integrity: sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.14.1': resolution: {integrity: sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.14.1': resolution: {integrity: sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==} @@ -8026,18 +8040,21 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] farm-plugin-remove-console-linux-arm64-musl@0.1.8: resolution: {integrity: sha512-vT1fy3hBdIqLNMI+hXi7I+Wseep6v3pUY99BDOnrkkHFkIjVuDzHCrQEYGLJOLchZM9fJnqidkhGTi0K+LZd6g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] farm-plugin-remove-console-linux-x64-gnu@0.1.8: resolution: {integrity: sha512-MJkZn+OeWig78DkAb5ur/lxXYYlZwAO05WvQHJOgQLSjxyOAVTDPYn2j+mCGgPrZdDwyghODM9UdVlxe1lMb7w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] farm-plugin-remove-console-linux-x64-musl@0.1.8: resolution: {integrity: sha512-ZObFlj+/ulEqUjr1Ggpm03FRcZQ5BZepK24kWf36pxrB6cn/dSv40GsXzzKELfPrh12Q15NJZVSW/0AT4SLnTw==} @@ -8083,18 +8100,21 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] farm-plugin-replace-dirname-linux-arm64-musl@0.2.1: resolution: {integrity: sha512-m3gH8ggczbRYTHZSNp3LjIQIcqhvDO4O78bxXc8O1ozKD8M47/YfQLyQV06M7H4rZ8s6XV3Bb1kAcRAASp3M5A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] farm-plugin-replace-dirname-linux-x64-gnu@0.2.1: resolution: {integrity: sha512-MehKkoM2RFw3sCnEu9nCbXKjxtC3hfTad0h/dC+Z8iEBcLEReVLoNzHWWUa6BxkxqDtB82/BWO/ObSUj/VUnwQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] farm-plugin-replace-dirname-linux-x64-musl@0.2.1: resolution: {integrity: sha512-o1qPZi16N/sHOteZYJVv6UmZFK3QKpVQrywk/4spJI0mPH9A9Y+G6iBE2Tqjb3d+1Hb6phr++EBJHZ2x1ajtGQ==} @@ -9540,24 +9560,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.25.1: resolution: {integrity: sha512-IhxVFJoTW8wq6yLvxdPvyHv4NjzcpN1B7gjxrY3uaykQNXPHNIpChLB52+wfH+yS58zm1PL4LemUp8u9Cfp6Bw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.25.1: resolution: {integrity: sha512-RXIaru79KrREPEd6WLXfKfIp4QzoppZvD3x7vuTKkDA64PwTzKJ2jaC43RZHRt8BmyIkRRlmywNhTRMbmkPYpA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.25.1: resolution: {integrity: sha512-TdcNqFsAENEEFr8fJWg0Y4fZ/nwuqTRsIr7W7t2wmDUlA8eSXVepeeONYcb+gtTj1RaXn/WgNLB45SFkz+XBZA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-x64-msvc@1.25.1: resolution: {integrity: sha512-9KZZkmmy9oGDSrnyHuxP6iMhbsgChUiu/NSgOx+U1I/wTngBStDf2i2aGRCHvFqj19HqqBEI4WuGVQBa2V6e0A==} @@ -10066,6 +10090,10 @@ packages: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -11949,6 +11977,10 @@ packages: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} + sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -16128,6 +16160,8 @@ snapshots: '@polka/url@1.0.0-next.23': {} + '@polka/url@1.0.0-next.25': {} + '@popperjs/core@2.11.8': {} '@rc-component/color-picker@1.4.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': @@ -23428,6 +23462,8 @@ snapshots: mrmime@1.0.1: {} + mrmime@2.0.0: {} + ms@2.0.0: {} ms@2.1.2: {} @@ -25414,6 +25450,12 @@ snapshots: mrmime: 1.0.1 totalist: 3.0.1 + sirv@2.0.4: + dependencies: + '@polka/url': 1.0.0-next.25 + mrmime: 2.0.0 + totalist: 3.0.1 + sisteransi@1.0.5: {} size-sensor@1.0.2: {}