UNPKG

@lynx-js/rspeedy

Version:

A webpack/rspack-based frontend toolchain for Lynx

336 lines (335 loc) 12.4 kB
import { __webpack_require__ } from "./src_cli_main_ts-node_child_process-node_events-node_fs-node_path-node_process.js"; import node_path, { extname, isAbsolute, join } from "node:path"; import { createRsbuild, mergeRsbuildConfig } from "@rsbuild/core"; import node_fs from "node:fs"; import { pathToFileURL } from "node:url"; import { register } from "#register"; import { debug, isDebug, debugList } from "./src_cli_main_ts-node_child_process-node_events-node_fs-node_path-node_process.js"; function applyDefaultRspeedyConfig(config) { const enableChunkSplitting = getEnableChunkSplitting(config); return mergeRsbuildConfig({ mode: (()=>{ if (config.mode) return config.mode; const nodeEnv = process.env['NODE_ENV']; return 'production' === nodeEnv || 'development' === nodeEnv ? nodeEnv : 'none'; })(), output: { filename: getFilename(config.output?.filename), sourceMap: { css: true }, inlineScripts: !enableChunkSplitting, cssModules: { localIdentName: '[local]-[hash:base64:6]' } }, performance: { profile: isDebug() ? true : void 0 }, tools: { rsdoctor: { experiments: { enableNativePlugin: true } } } }, config); } function getEnableChunkSplitting(config) { if (void 0 !== config.splitChunks) return false !== config.splitChunks; const strategy = config.performance?.chunkSplit?.strategy; return Boolean(strategy && 'all-in-one' !== strategy); } const DEFAULT_FILENAME = '[name].[platform].bundle'; function getFilename(filename) { if ('string' == typeof filename) return { bundle: filename, template: filename }; const finalFilename = filename?.bundle ?? filename?.template ?? DEFAULT_FILENAME; if ('function' == typeof finalFilename) return { bundle: finalFilename }; return { bundle: finalFilename, template: finalFilename }; } const DEFAULT_ENTRY = './src/index.js'; function toRsbuildEntry(entry) { if (void 0 === entry) { debug(`Using default entry ${DEFAULT_ENTRY}`); return { main: DEFAULT_ENTRY }; } if (Array.isArray(entry) || 'string' == typeof entry) { debug(()=>`Using single entry ${[ '' ].concat(entry).join('\n - ')}`); return { main: entry }; } return Object.fromEntries(Object.entries(entry).map(([key, value])=>{ if (Array.isArray(value) || 'string' == typeof value) { debugList(`Using multiple entries - ${key}`, value); return [ key, { import: value } ]; } debugList(`Using multiple entries - ${key}`, value.import ?? DEFAULT_ENTRY); if (void 0 === value.import) return [ key, { ...value, import: DEFAULT_ENTRY } ]; return [ key, value ]; })); } const defaultDataUriLimit = 2048; function toRsbuildConfig(config) { return { dev: { hmr: config.dev?.hmr ?? true, lazyCompilation: false, liveReload: config.dev?.liveReload ?? true, watchFiles: config.dev?.watchFiles, writeToDisk: config.dev?.writeToDisk ?? true, progressBar: config.dev?.progressBar ?? true }, environments: config.environments ?? { lynx: {} }, mode: config.mode, output: { assetPrefix: config.output?.assetPrefix, charset: 'utf8', cleanDistPath: config.output?.cleanDistPath, copy: config.output?.copy, cssModules: config.output?.cssModules, dataUriLimit: config.output?.dataUriLimit ?? defaultDataUriLimit, distPath: config.output?.distPath, filenameHash: config.output?.filenameHash, inlineScripts: config.output?.inlineScripts, legalComments: config.output?.legalComments ?? 'none', polyfill: 'off', sourceMap: config.output?.sourceMap }, resolve: { alias: toRsbuildAlias(config), aliasStrategy: config.resolve?.aliasStrategy, dedupe: config.resolve?.dedupe, extensions: config.resolve?.extensions }, source: { assetsInclude: config.source?.assetsInclude, decorators: config.source?.decorators, define: config.source?.define, entry: toRsbuildEntry(config.source?.entry), exclude: config.source?.exclude, include: config.source?.include, preEntry: config.source?.preEntry, transformImport: config.source?.transformImport, tsconfigPath: config.source?.tsconfigPath }, splitChunks: toRsbuildSplitChunks(config), server: { base: config.server?.base, compress: config.server?.compress, cors: config.server?.cors, headers: config.server?.headers, host: config.server?.host ?? '0.0.0.0', port: config.server?.port, proxy: config.server?.proxy, strictPort: config.server?.strictPort }, plugins: config.plugins, performance: { buildCache: config.performance?.buildCache, chunkSplit: config.performance?.chunkSplit, removeConsole: toRsbuildRemoveConsole(config), printFileSize: config.performance?.printFileSize ?? true }, tools: { bundlerChain: config.tools?.bundlerChain, cssExtract: config.tools?.cssExtract, cssLoader: config.tools?.cssLoader, htmlPlugin: false, rspack: config.tools?.rspack, swc: config.tools?.swc } }; } function toRsbuildRemoveConsole(config) { if (config.performance?.removeConsole === true) return [ 'log', 'warn', 'error', 'info', 'debug', 'profile', 'profileEnd' ]; return config.performance?.removeConsole; } function toRsbuildSplitChunks(config) { if (void 0 !== config.splitChunks) return config.splitChunks; const legacyStrategy = config.performance?.chunkSplit?.strategy; if (legacyStrategy && 'all-in-one' !== legacyStrategy) return; return false; } function toRsbuildAlias(config) { const sourceAlias = config.source?.alias; const resolveAlias = config.resolve?.alias; if (void 0 === sourceAlias && void 0 === resolveAlias) return; return { ...resolveAlias, ...sourceAlias }; } async function createRspeedy({ cwd = process.cwd(), rspeedyConfig = {}, loadEnv = true, environment = [], callerName = 'rspeedy' }) { const config = applyDefaultRspeedyConfig(rspeedyConfig); const [rspeedy, { applyDefaultPlugins }] = await Promise.all([ createRsbuild({ cwd, loadEnv, rsbuildConfig: toRsbuildConfig(config), environment, callerName }), import("./1~plugins.js") ]); await applyDefaultPlugins(rspeedy, config); const inspectConfig = rspeedy.inspectConfig.bind(rspeedy); return Object.assign(rspeedy, { getRspeedyConfig: ()=>config, async inspectConfig (options) { const result = await inspectConfig(options); const { inspectRspeedyConfig } = await import("./1~inspect.plugin.js"); await inspectRspeedyConfig(rspeedyConfig, node_path.resolve(options.outputPath ?? rspeedy.context.distPath, '.rsbuild/rspeedy.config.js'), options.verbose ?? false); return result; } }); } const picocolors = __webpack_require__("../../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"); var picocolors_default = /*#__PURE__*/ __webpack_require__.n(picocolors); const resolveConfigPath = (root, customConfig)=>{ if (customConfig) { debug(`load custom config file ${customConfig} from ${root}`); const customConfigPath = isAbsolute(customConfig) ? customConfig : join(root, customConfig); if (node_fs.existsSync(customConfigPath)) return customConfigPath; throw new Error(`Cannot find config file: ${picocolors_default().dim(customConfigPath)}`); } const CONFIG_FILES = [ 'lynx.config.ts', 'lynx.config.js', 'lynx.config.mts', 'lynx.config.mjs' ]; for (const file of CONFIG_FILES){ debug(`load default config file ${file} from ${root}`); const configFile = join(root, file); if (node_fs.existsSync(configFile)) { debug(`default config ${configFile} found`); return configFile; } } throw new Error([ `Cannot find the default config file: ${picocolors_default().dim(join(root, CONFIG_FILES[0]))}.`, `Use custom config with ${picocolors_default().green('`--config <config>`')} options.` ].join(' ')); }; async function loadConfig(loadConfigOptions) { let { configPath } = loadConfigOptions; if (!configPath || !isAbsolute(configPath)) configPath = resolveConfigPath(loadConfigOptions.cwd ?? process.cwd(), configPath); const specifier = pathToFileURL(configPath).toString(); let unregister; unregister = shouldUseNativeImport(configPath) ? ()=>{} : register({ load: !hasNativeTSSupport(), resolve: true }); try { const [exports, { validate }] = await Promise.all([ import(`${specifier}?t=${Date.now()}`), import("./1~validate.js").then((m)=>m.validate_namespaceObject) ]); const configExport = 'default' in exports ? exports.default : exports; const rawContent = 'function' == typeof configExport ? await configExport({ command: process.argv[2] ?? 'build', env: process.env['NODE_ENV'] ?? 'production' }) : await configExport; return { configPath, content: validate(rawContent, configPath) }; } finally{ unregister(); } } function shouldUseNativeImport(configPath) { return isJavaScriptPath(configPath) || isDeno(); } function hasNativeTSSupport() { if (isDeno()) return true; if (process.features.typescript) return true; if (false === process.features.typescript) return false; const { NODE_OPTIONS } = process.env; if (!NODE_OPTIONS) return false; return NODE_OPTIONS.includes('--experimental-transform-types') || NODE_OPTIONS.includes('--experimental-strip-types'); } function isJavaScriptPath(configPath) { const ext = extname(configPath); return [ '.js', '.mjs', '.cjs' ].includes(ext); } function isDeno() { if ("u" > typeof Deno || process.versions?.deno) return true; return false; } async function init(cwd, options) { const { content: rspeedyConfig, configPath } = await loadConfig({ cwd, configPath: options.config }); if (rspeedyConfig.performance?.buildCache) if (true === rspeedyConfig.performance.buildCache) rspeedyConfig.performance.buildCache = { buildDependencies: [ configPath ] }; else { rspeedyConfig.performance.buildCache.buildDependencies ??= []; rspeedyConfig.performance.buildCache.buildDependencies.push(configPath); } const createRspeedyOptions = { cwd, rspeedyConfig }; if (options.noEnv) createRspeedyOptions.loadEnv = false; else if (options.envMode) createRspeedyOptions.loadEnv = { mode: options.envMode }; if ('base' in options && options.base) { rspeedyConfig.server ??= {}; rspeedyConfig.server.base = options.base; } if ('environment' in options && options.environment) createRspeedyOptions.environment = options.environment; if (options.mode) rspeedyConfig.mode = options.mode; return { createRspeedyOptions, configPath, rspeedyConfig }; } export { createRspeedy, init };