UNPKG

repacked

Version:

To initialize a new project with `repacked`, you can use the following command:

681 lines (654 loc) 19.8 kB
// src/utils/cwd.ts import path from "path"; var cwd = (...paths) => { return path.join(process.cwd(), ...paths); }; var cwd_default = cwd; // src/features/test/jestConfig.ts import path2 from "path"; // src/features/swc/swcOptions.ts var getSwcOptions = (options) => { return { jsc: { parser: { syntax: "typescript", tsx: options.tsx }, externalHelpers: false, transform: { react: { runtime: "automatic", development: !options.isProduction, refresh: !options.isProduction && !options.isServer } } }, env: { targets: "Chrome >= 48" } }; }; // src/utils/dirname.ts import { fileURLToPath } from "url"; import { dirname } from "path"; var __filename = fileURLToPath(import.meta.url); var __dirname = dirname(__filename); // src/features/test/jestConfig.ts var getJestConfig = () => { return { rootDir: cwd_default(""), roots: ["<rootDir>/src"], collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts"], setupFilesAfterEnv: ["@testing-library/jest-dom"], testMatch: [ "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}", "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}" ], testEnvironment: "jsdom", moduleFileExtensions: ["ts", "tsx", "js", "jsx"], transform: { "^.+\\.(js|ts|mjs|cjs)$": "@swc/jest", "^.+\\.(jsx|tsx)$": [ "@swc/jest", getSwcOptions({ isProduction: true, tsx: true }) ], "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": path2.resolve( __dirname, "./features/test/transformers/fileTransformer.js" ), "^.+\\.css$": path2.resolve( __dirname, "./features/test/transformers/cssTransformer.js" ) } }; }; var getJestConfigAsJSON = (override) => { return JSON.stringify(override(getJestConfig())); }; // src/features/test/test.ts import jest from "jest"; var runTest = async (argv, appConfig) => { const jestConfig = getJestConfigAsJSON(appConfig.jest); argv.push("--config", jestConfig); jest.run(argv); }; // src/features/serve/index.ts import { rspack as rspack2 } from "@rspack/core"; import { RspackDevServer } from "@rspack/dev-server"; // src/features/server/server.ts import express from "express"; var expressServer = () => { const app = express(); return app; }; // src/features/serve/index.ts import history from "connect-history-api-fallback"; import rspackHotMiddleware from "webpack-hot-middleware"; import rspackDevMiddleware from "webpack-dev-middleware"; import { createProxyMiddleware } from "http-proxy-middleware"; // src/features/server/getServerRspackConfig.ts import { LicenseWebpackPlugin } from "license-webpack-plugin"; // src/features/rspack/getRspackConfig.ts import "@rspack/dev-server"; var getRspackConfig = async (mode, appConfig, options) => { const isDevelopment = mode === "development"; const isServer = options?.target === "server"; const configOverride = options?.override ?? ((config) => config); const outputDirectory = cwd_default(appConfig.output.dir); const rspackConfig = { mode, watch: options?.watch, watchOptions: options?.watch ? { ignored: /node_modules/, poll: 1e3, aggregateTimeout: 300 } : void 0, cache: false, entry: cwd_default(appConfig.client.entry), devtool: "source-map", output: { uniqueName: appConfig.appName, publicPath: appConfig.client.publicPath, path: outputDirectory, filename: "js/[name].[fullhash].js", clean: typeof options?.clean === "boolean" ? options.clean : true }, plugins: [], module: { rules: [ { test: /\.(j|t)s?$/, use: { loader: "builtin:swc-loader", options: getSwcOptions({ tsx: false, isProduction: !isDevelopment, isServer }) }, exclude: /node_modules/ }, { test: /\.(js|ts)x?$/, use: { loader: "builtin:swc-loader", options: getSwcOptions({ tsx: true, isProduction: !isDevelopment, isServer }) }, exclude: /node_modules/ }, { test: /\.css$/, exclude: /node_modules/, use: ["style-loader", "css-loader"] }, { test: /\.(png|svg|jpg|gif)$/, exclude: /node_modules/, use: [ isServer ? { loader: "file-loader", options: { emitFile: false, publicPath: appConfig.client.publicPath } } : "file-loader" ] } ] }, resolve: { extensions: ["*", ".jsx", ".tsx", ".ts", ".js"] }, performance: { hints: false } }; appConfig.plugins.forEach((plugin) => { const customPlugin = plugin({ target: options.target, appConfig }); rspackConfig.plugins?.push(customPlugin); customPlugin.updateConfig?.(rspackConfig); }); return appConfig.rspack( configOverride(rspackConfig), options?.target ?? "client" ); }; var getRspackConfig_default = getRspackConfig; // src/features/server/plugins/hotReloadServer.ts import cluster from "node:cluster"; import path3 from "path"; var HotReloadServer = class _HotReloadServer { constructor(appConfig) { this.appConfig = appConfig; this.callback = () => { }; this.workers = []; cluster.setupPrimary({ exec: path3.resolve(cwd_default(appConfig.output.dir, "index.cjs")), args: [`--port=${appConfig.development.port + 1}`] }); cluster.on("online", (worker) => { this.workers.push(worker); this.callback(); }); } apply(compiler) { const pluginName = _HotReloadServer.name; compiler.hooks.afterEmit.tapAsync(pluginName, (compilation, callback) => { this.callback = callback; this.workers.forEach((worker) => { try { worker.process.kill("SIGTERM"); } catch (e) { console.warn(`Unable to kill worker ${worker.process.pid}`); } }); this.workers = []; cluster.fork(); }); } }; // src/features/server/getServerRspackConfig.ts import path4 from "path"; import { DefinePlugin } from "@rspack/core"; var getServerRspackConfig = async (mode, appConfig, options) => { const runtimeEnv = mode === "production" ? "prod" : "dev"; return await getRspackConfig_default(mode, appConfig, { ...options ?? {}, target: "server", override: (config) => { config.module?.rules?.push({ test: path4.join( __dirname, "/features/server/runtimes/", `runtime.${runtimeEnv}.js` ), use: [ { loader: path4.resolve( __dirname, "./features/server/loaders/loadServer.js" ), options: { entry: cwd_default(appConfig.server.entry) } } ] }); config.plugins?.push( new DefinePlugin({ "process.env.__INTERNAL_REPACKED_SERVER_CONFIG": { client: { enabled: appConfig.client.enabled }, development: { port: appConfig.development.port } } }) ); if (mode === "production") { config.plugins?.push( new LicenseWebpackPlugin({ outputFilename: "LICENSE", perChunkOutput: false, addBanner: true, stats: { warnings: false } }) ); } if (mode === "development") { config.plugins?.push(new HotReloadServer(appConfig)); } config.target = "node"; config.entry = { index: path4.join( __dirname, "/features/server/runtimes/", `runtime.${runtimeEnv}.js` ) }; config.output.libraryTarget = "commonjs2"; config.output.filename = "[name].cjs"; config.output.publicPath = appConfig.client.publicPath; config.optimization = { sideEffects: true }; delete config.devServer; return config; } }); }; var getServerRspackConfig_default = getServerRspackConfig; // src/features/rspack/utils.ts var filterKnownWarnings = (warnings) => { const knownPackages = ["yargs", "express", "repacked"].map( (pkg) => `node_modules/${pkg}` ); const containsAny = (str, values) => { return values.some((value) => str.includes(value)); }; return warnings.filter((warning) => { if (warning.message.includes("Critical dependency") && (!warning.moduleName || containsAny(warning.moduleName ?? "", knownPackages))) { return false; } return true; }); }; var logRspackErrors = (err, stats) => { if (err) { console.error(err.stack || err); if (err.details) { console.error(err.details); } process.exit(1); } const statsData = stats?.toJson(); if (stats?.hasErrors()) { console.error(statsData?.errors); process.exit(1); } if (stats?.hasWarnings()) { const filteredWarnings = filterKnownWarnings(statsData?.warnings ?? []); if (filteredWarnings.length > 0) { console.warn(filteredWarnings); } } }; // src/features/client/getClientRspackConfig.ts import "@rspack/dev-server"; import { HotModuleReplacementPlugin, HtmlRspackPlugin as HtmlRspackPlugin2, rspack } from "@rspack/core"; import ReactRefreshRspackPlugin from "@rspack/plugin-react-refresh"; import { ModuleFederationPlugin } from "@module-federation/enhanced/rspack"; // src/features/client/plugins/envVariables.ts import { DefinePlugin as DefinePlugin2 } from "@rspack/core"; var getEnvValues = (filterCallback) => { const envs = process.env || {}; const filteredEnvs = {}; Object.entries(envs).forEach(([key, value]) => { if (filterCallback(key, value)) { filteredEnvs[key] = value; } }); return filteredEnvs; }; var EnvVariablesPlugin = (filterCallback) => { return new DefinePlugin2({ "process.env": JSON.stringify({ ...getEnvValues(filterCallback) }) }); }; // src/features/client/plugins/htmlMFWebpackPlugin.ts import { HtmlRspackPlugin } from "@rspack/core"; var HtmlMFRspackPlugin = class { constructor(entryFile = "remoteEntry.js") { this.entryFile = entryFile; } apply(compiler) { compiler.hooks.compilation.tap( "HtmlMFRspackPlugin", (compilation) => { HtmlRspackPlugin.getCompilationHooks( compilation ).alterAssetTags.tapAsync("HtmlMFRspackPlugin", (data, cb) => { const entryFile = `${data.publicPath.toLowerCase() === "auto" ? "" : data.publicPath}${this.entryFile}`; data.assetTags.scripts = data.assetTags.scripts.filter( (script) => script.attributes.src !== entryFile ); cb(null, data); }); } ); } }; var htmlMFWebpackPlugin_default = HtmlMFRspackPlugin; // src/features/client/plugins/manifestPlugin.ts import { sources } from "@rspack/core"; var defaultOptions = { fileName: "client-assets.json" }; var ManifestPlugin = class _ManifestPlugin { constructor(options) { this.options = { ...defaultOptions, ...options }; } apply(compiler) { compiler.hooks.emit.tapAsync( _ManifestPlugin.name, (compilation, callback) => { const manifest = {}; for (const [entryName, entrypoint] of compilation.entrypoints) { const chunks = entrypoint.chunks.filter( (chunk) => chunk.canBeInitial?.() ); const jsFiles = /* @__PURE__ */ new Set(); const cssFiles = /* @__PURE__ */ new Set(); for (const chunk of chunks) { for (const file of chunk.files) { if (file.endsWith(".hot-update.js")) { continue; } if (file.endsWith(".js")) { jsFiles.add(file); } else if (file.endsWith(".css")) { cssFiles.add(file); } } } manifest[entryName] = { js: Array.from(jsFiles), css: Array.from(cssFiles) }; } const json = JSON.stringify(manifest, null, 2); compilation.emitAsset( this.options.fileName, new sources.RawSource(json) ); callback(); } ); } }; // src/constants.ts var CLIENT_MANIFEST_FILENAME = "client-assets.json"; // src/features/client/getClientRspackConfig.ts var getClientRspackConfig = async (mode, appConfig, options) => { const isDevelopment = mode === "development"; const outputDirectory = cwd_default(appConfig.output.dir); let entry = []; if (appConfig.server.enabled && isDevelopment) { entry = [ "webpack-hot-middleware/client?reload=true", cwd_default(appConfig.client.entry) ]; } else { entry = cwd_default(appConfig.client.entry); } const plugins = []; plugins.push( new HtmlRspackPlugin2({ template: cwd_default(appConfig.client.template) }) ); plugins.push(EnvVariablesPlugin(appConfig.client.envFilter)); isDevelopment && plugins.push(new HotModuleReplacementPlugin()); isDevelopment && plugins.push(new ReactRefreshRspackPlugin({ library: appConfig.appName })); plugins.push( new rspack.CopyRspackPlugin({ patterns: [ { from: cwd_default(appConfig.client.assetsDir), to: outputDirectory } ] }) ); plugins.push(new ManifestPlugin({ fileName: CLIENT_MANIFEST_FILENAME })); if (appConfig.moduleFederation) { plugins.push(new ModuleFederationPlugin(appConfig.moduleFederation)); plugins.push(new htmlMFWebpackPlugin_default(appConfig.moduleFederation.filename)); } return await getRspackConfig_default(mode, appConfig, { ...options ?? {}, target: "client", override: (config) => { config.entry = entry; config.plugins = [...config.plugins, ...plugins]; config.devServer = { hot: true, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization" }, ...appConfig.development }; return config; } }); }; var getClientRspackConfig_default = getClientRspackConfig; // src/features/serve/utils/copyHeaders.ts function copyHeaders(proxyRes, res) { if (proxyRes.statusCode) { res.statusCode = proxyRes.statusCode; } if (proxyRes.statusMessage) { res.statusMessage = proxyRes.statusMessage; } if (typeof res.setHeader === "function") { let keys = Object.keys(proxyRes.headers); keys = keys.filter( (key) => !["content-encoding", "transfer-encoding"].includes(key) ); keys.forEach((key) => { let value = proxyRes.headers[key]; if (value === void 0) { return; } if (key === "set-cookie") { value = Array.isArray(value) ? value : [value]; value = value.map((x) => x.replace(/Domain=[^;]+?/i, "")); } res.setHeader(key, value); }); } else { res.headers = proxyRes.headers; } } // src/features/serve/index.ts var serveClientOnly = async (mode, appConfig) => { const rspackConfig = await getClientRspackConfig_default(mode, appConfig); rspackConfig.devServer.historyApiFallback = true; const compiler = rspack2(rspackConfig); const server = new RspackDevServer(rspackConfig.devServer ?? {}, compiler); const runServer = async () => { console.log("Starting server..."); await server.start(); }; runServer(); }; var serveServer = async (mode, appConfig) => { const serverRspackConfig = await getServerRspackConfig_default(mode, appConfig, { watch: true }); rspack2(serverRspackConfig, logRspackErrors); const clientRspackConfig = await getClientRspackConfig_default(mode, appConfig, {}); clientRspackConfig.output.publicPath = appConfig.client.publicPath; const clientCompiler = rspack2(clientRspackConfig); const port = appConfig.development.port || 3e3; const clientEnabled = appConfig.client.enabled; const app = expressServer(); const nextWeakMap = /* @__PURE__ */ new WeakMap(); const proxy = createProxyMiddleware({ target: `http://localhost:${port + 1}`, changeOrigin: true, selfHandleResponse: true, on: { proxyRes: (proxyRes, req, res) => { if (!clientEnabled) { proxyRes.pipe(res); return; } if (proxyRes.headers?.["x-dev-repacked-route-status"] === "unhandled") { const next = nextWeakMap.get(req); return next(); } else { copyHeaders(proxyRes, res); proxyRes.pipe(res); } } } }); app.use((req, res, next) => { if (req.headers["x-dev-repacked-client-only"]) { return next(); } nextWeakMap.set(req, next); proxy(req, res, next); }); if (clientEnabled) { app.use(history()); const devMiddleware = rspackDevMiddleware( //@todo: fix once rspack dev middleware released clientCompiler, { publicPath: clientRspackConfig.output?.publicPath } ); app.use(devMiddleware); app.use(rspackHotMiddleware(clientCompiler)); } app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); }); }; var serve = async (mode, appConfig) => { if (appConfig.server.enabled) { serveServer(mode, appConfig); } else { serveClientOnly(mode, appConfig); } }; var serve_default = serve; // src/features/client/build.ts import { rspack as rspack3 } from "@rspack/core"; var buildClient = async (mode, appConfig) => { const rspackConfig = await getClientRspackConfig_default(mode, appConfig); rspack3(rspackConfig, logRspackErrors); }; // src/features/server/build.ts import { rspack as rspack4 } from "@rspack/core"; var buildServer = async (mode, appConfig) => { const rspackConfig = await getServerRspackConfig_default(mode, appConfig, { clean: false }); return new Promise((resolve, reject) => { rspack4(rspackConfig, (err, stats) => { logRspackErrors(err, stats); if (err) { reject(err); return; } if (stats?.hasErrors()) { const info = stats.toJson(); reject( new Error(`Build failed with errors: ${info.errors?.join("\n")}`) ); return; } resolve(true); }); }); }; // src/features/build/index.ts import path5 from "path"; // src/features/build/utils/removeFolder.ts import { promises as fs } from "fs"; var removeFolder = async (folder) => { try { await fs.rm(folder, { recursive: true, force: true }); console.log(`Folder "${folder}" removed successfully.`); } catch (error) { console.error(`Error removing folder: ${error.message}`); throw error; } }; var removeFolder_default = removeFolder; // src/features/build/index.ts var build = async (mode, appConfig) => { const serverEnabled = appConfig.server.enabled; const clientEnabled = appConfig.client.enabled; const clientOutputPath = serverEnabled ? path5.join(appConfig.output.dir, "client") : appConfig.output.dir; await removeFolder_default(cwd_default(appConfig.output.dir)); clientEnabled && await buildClient(mode, { ...appConfig, output: { ...appConfig.output, dir: clientOutputPath } }); if (serverEnabled) { await buildServer(mode, appConfig); } }; var build_default = build; export { build_default as build, serve_default as serve, runTest as test };