'use strict'; var path = require('path'); var webpack = require('webpack'); var ESLintPlugin = require('eslint-webpack-plugin'); var ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var cliCommon = require('@backstage/cli-common'); var ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); var runScriptWebpackPlugin = require('run-script-webpack-plugin'); var index = require('./index-CCLafGQH.cjs.js'); var fs = require('fs-extra'); var getPackages = require('@manypkg/get-packages'); var nodeExternals = require('webpack-node-externals'); var pickBy = require('lodash/pickBy'); var entryPoints = require('./entryPoints-CoHH4lBA.cjs.js'); var run = require('./run-BcxUFacd.cjs.js'); var MiniCssExtractPlugin = require('mini-css-extract-plugin'); var svgrTemplate = require('./svgrTemplate-BTjBQ3by.cjs.js'); var ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); var yn = require('yn'); var config = require('@backstage/config'); var chokidar = require('chokidar'); var PQueue = require('p-queue'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } var webpack__default = /*#__PURE__*/_interopDefaultCompat(webpack); var ESLintPlugin__default = /*#__PURE__*/_interopDefaultCompat(ESLintPlugin); var ForkTsCheckerWebpackPlugin__default = /*#__PURE__*/_interopDefaultCompat(ForkTsCheckerWebpackPlugin); var HtmlWebpackPlugin__default = /*#__PURE__*/_interopDefaultCompat(HtmlWebpackPlugin); var ModuleScopePlugin__default = /*#__PURE__*/_interopDefaultCompat(ModuleScopePlugin); var fs__default = /*#__PURE__*/_interopDefaultCompat(fs); var nodeExternals__default = /*#__PURE__*/_interopDefaultCompat(nodeExternals); var pickBy__default = /*#__PURE__*/_interopDefaultCompat(pickBy); var MiniCssExtractPlugin__default = /*#__PURE__*/_interopDefaultCompat(MiniCssExtractPlugin); var ReactRefreshPlugin__default = /*#__PURE__*/_interopDefaultCompat(ReactRefreshPlugin); var yn__default = /*#__PURE__*/_interopDefaultCompat(yn); var chokidar__default = /*#__PURE__*/_interopDefaultCompat(chokidar); var PQueue__default = /*#__PURE__*/_interopDefaultCompat(PQueue); class LinkedPackageResolvePlugin { constructor(targetModules, packages) { this.targetModules = targetModules; this.packages = packages; } apply(resolver) { resolver.hooks.resolve.tapAsync( "LinkedPackageResolvePlugin", (data, context, callback) => { var _a; const pkg = this.packages.find( (pkge) => data.path && cliCommon.isChildPath(pkge.dir, data.path) ); if (!pkg) { callback(); return; } const modulesLocation = path.resolve( this.targetModules, pkg.packageJson.name ); const newContext = ((_a = data.context) == null ? void 0 : _a.issuer) ? { ...data.context, issuer: data.context.issuer.replace(pkg.dir, modulesLocation) } : data.context; resolver.doResolve( resolver.hooks.resolve, { ...data, context: newContext, path: data.path && data.path.replace(pkg.dir, modulesLocation) }, `resolve ${data.request} in ${modulesLocation}`, context, callback ); } ); } } const { EsbuildPlugin } = require("esbuild-loader"); const optimization = (options) => { const { isDev } = options; return { minimize: !isDev, minimizer: [ new EsbuildPlugin({ target: "es2019", format: "iife" }) ], runtimeChunk: "single", splitChunks: { automaticNameDelimiter: "-", cacheGroups: { default: false, // Put all vendor code needed for initial page load in individual files if they're big // enough, if they're smaller they end up in the main packages: { chunks: "initial", test(module) { var _a; return Boolean( (_a = module == null ? void 0 : module.resource) == null ? void 0 : _a.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/) ); }, name(module) { const packageName = module.resource.match( /[\\/]node_modules[\\/](.*?)([\\/]|$)/ )[1]; return packageName.replace("@", ""); }, filename: isDev ? "module-[name].js" : "static/module-[name].[chunkhash:8].js", priority: 10, minSize: 1e5, minChunks: 1, maxAsyncRequests: Infinity, maxInitialRequests: Infinity }, // filename is not included in type, but we need it // Group together the smallest modules vendor: { chunks: "initial", test: /[\\/]node_modules[\\/]/, name: "vendor", priority: 5, enforce: true } } } }; }; const transforms = (options) => { const { isDev, isBackend } = options; function insertBeforeJssStyles(element) { const head = document.head; const firstJssNode = head.querySelector("style[data-jss]"); if (!firstJssNode) { head.appendChild(element); } else { head.insertBefore(element, firstJssNode); } } const loaders = [ { test: /\.(tsx?)$/, exclude: /node_modules/, use: [ { loader: require.resolve("swc-loader"), options: { jsc: { target: "es2019", externalHelpers: !isBackend, parser: { syntax: "typescript", tsx: !isBackend, dynamicImport: true }, transform: { react: isBackend ? void 0 : { runtime: "automatic", refresh: isDev } } } } } ] }, { test: /\.(jsx?|mjs|cjs)$/, exclude: /node_modules/, use: [ { loader: require.resolve("swc-loader"), options: { jsc: { target: "es2019", externalHelpers: !isBackend, parser: { syntax: "ecmascript", jsx: !isBackend, dynamicImport: true }, transform: { react: isBackend ? void 0 : { runtime: "automatic", refresh: isDev } } } } } ] }, { test: /\.(js|mjs|cjs)$/, resolve: { fullySpecified: false } }, { test: [/\.icon\.svg$/], use: [ { loader: require.resolve("swc-loader"), options: { jsc: { target: "es2019", externalHelpers: !isBackend, parser: { syntax: "ecmascript", jsx: !isBackend, dynamicImport: true } } } }, { loader: require.resolve("@svgr/webpack"), options: { babel: false, template: svgrTemplate.svgrTemplate } } ] }, { test: [ /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.frag$/, /\.vert$/, { and: [/\.svg$/, { not: [/\.icon\.svg$/] }] }, /\.xml$/, /\.ico$/ ], type: "asset/resource", generator: { filename: "static/[name].[hash:8].[ext]" } }, { test: /\.(eot|woff|woff2|ttf)$/i, type: "asset/resource", generator: { filename: "static/[name].[hash][ext][query]" } }, { test: /\.ya?ml$/, use: require.resolve("yml-loader") }, { include: /\.(md)$/, type: "asset/resource", generator: { filename: "static/[name].[hash][ext][query]" } }, { test: /\.css$/i, use: [ isDev ? { loader: require.resolve("style-loader"), options: { insert: insertBeforeJssStyles } } : MiniCssExtractPlugin__default.default.loader, { loader: require.resolve("css-loader"), options: { sourceMap: true } } ] } ]; const plugins = new Array(); if (isDev) { if (!isBackend) { plugins.push( new ReactRefreshPlugin__default.default({ overlay: { sockProtocol: "ws" } }) ); } } else { plugins.push( new MiniCssExtractPlugin__default.default({ filename: "static/[name].[contenthash:8].css", chunkFilename: "static/[name].[id].[contenthash:8].css", insert: insertBeforeJssStyles // Only applies to async chunks }) ); } return { loaders, plugins }; }; function hasReactDomClient() { try { require.resolve("react-dom/client"); return true; } catch { return false; } } const BUILD_CACHE_ENV_VAR = "BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE"; function resolveBaseUrl(config) { const baseUrl = config.getOptionalString("app.baseUrl"); try { return new URL(baseUrl != null ? baseUrl : "/", "http://localhost:3000"); } catch (error) { throw new Error(`Invalid app.baseUrl, ${error}`); } } async function readBuildInfo() { const timestamp = Date.now(); let commit; try { commit = await run.runPlain("git", "rev-parse", "HEAD"); } catch (error) { } let gitVersion; try { gitVersion = await run.runPlain("git", "describe", "--always"); } catch (error) { } if (commit === void 0 || gitVersion === void 0) { console.info( "NOTE: Did not compute git version or commit hash, could not execute the git command line utility" ); } const { version: packageVersion } = await fs__default.default.readJson( index.paths.resolveTarget("package.json") ); return { cliVersion: index.version, gitVersion: gitVersion != null ? gitVersion : "unknown", packageVersion, timestamp, commit: commit != null ? commit : "unknown" }; } async function createConfig(paths, options) { var _a, _b; const { checksEnabled, isDev, frontendConfig, publicSubPath = "" } = options; const { plugins, loaders } = transforms(options); const { packages } = await getPackages.getPackages(index.paths.targetDir); const externalPkgs = packages.filter((p) => !cliCommon.isChildPath(paths.root, p.dir)); const validBaseUrl = resolveBaseUrl(frontendConfig); let publicPath = validBaseUrl.pathname.replace(/\/$/, ""); if (publicSubPath) { publicPath = `${publicPath}${publicSubPath}`.replace("//", "/"); } if (checksEnabled) { plugins.push( new ForkTsCheckerWebpackPlugin__default.default({ typescript: { configFile: paths.targetTsConfig, memoryLimit: 4096 } }), new ESLintPlugin__default.default({ context: paths.targetPath, files: ["**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)"] }) ); } plugins.push( new webpack.ProvidePlugin({ process: "process/browser", Buffer: ["buffer", "Buffer"] }) ); plugins.push( new HtmlWebpackPlugin__default.default({ meta: { "backstage-app-mode": (_a = options == null ? void 0 : options.appMode) != null ? _a : "public" }, template: paths.targetHtml, templateParameters: { publicPath, config: frontendConfig } }) ); const buildInfo = await readBuildInfo(); plugins.push( new webpack__default.default.DefinePlugin({ "process.env.BUILD_INFO": JSON.stringify(buildInfo), "process.env.APP_CONFIG": webpack__default.default.DefinePlugin.runtimeValue( () => JSON.stringify(options.getFrontendAppConfigs()), true ), // This allows for conditional imports of react-dom/client, since there's no way // to check for presence of it in source code without module resolution errors. "process.env.HAS_REACT_DOM_CLIENT": JSON.stringify(hasReactDomClient()) }) ); const reactRefreshFiles = [ require.resolve( "@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js" ), require.resolve("@pmmmwh/react-refresh-webpack-plugin/overlay/index.js"), require.resolve("react-refresh") ]; const withCache = yn__default.default(process.env[BUILD_CACHE_ENV_VAR], { default: false }); return { mode: isDev ? "development" : "production", profile: false, optimization: optimization(options), bail: false, performance: { hints: false // we check the gzip size instead }, devtool: isDev ? "eval-cheap-module-source-map" : "source-map", context: paths.targetPath, entry: [...(_b = options.additionalEntryPoints) != null ? _b : [], paths.targetEntry], resolve: { extensions: [".ts", ".tsx", ".mjs", ".js", ".jsx", ".json", ".wasm"], mainFields: ["browser", "module", "main"], fallback: { ...pickBy__default.default(require("node-libs-browser")), module: false, dgram: false, dns: false, fs: false, http2: false, net: false, tls: false, child_process: false, /* new ignores */ path: false, https: false, http: false, util: require.resolve("util/") }, plugins: [ new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs), new ModuleScopePlugin__default.default( [paths.targetSrc, paths.targetDev], [paths.targetPackageJson, ...reactRefreshFiles] ) ] }, module: { rules: loaders }, output: { path: paths.targetDist, publicPath: `${publicPath}/`, filename: isDev ? "[name].js" : "static/[name].[fullhash:8].js", chunkFilename: isDev ? "[name].chunk.js" : "static/[name].[chunkhash:8].chunk.js", ...isDev ? { devtoolModuleFilenameTemplate: (info) => `file:///${path.resolve(info.absoluteResourcePath).replace( /\\/g, "/" )}` } : {} }, experiments: { lazyCompilation: yn__default.default(process.env.EXPERIMENTAL_LAZY_COMPILATION) }, plugins, ...withCache ? { cache: { type: "filesystem", buildDependencies: { config: [__filename] } } } : {} }; } async function createBackendConfig(paths, options) { const { checksEnabled, isDev } = options; const { packages } = await getPackages.getPackages(index.paths.targetDir); const localPackageEntryPoints = packages.flatMap((p) => { const entryPoints$1 = entryPoints.readEntryPoints(p.packageJson); return entryPoints$1.map((e) => path.posix.join(p.packageJson.name, e.mount)); }); const moduleDirs = packages.map((p) => path.resolve(p.dir, "node_modules")); const externalPkgs = packages.filter((p) => !cliCommon.isChildPath(paths.root, p.dir)); const { loaders } = transforms({ ...options, isBackend: true }); const runScriptNodeArgs = new Array(); if (options.inspectEnabled) { const inspect = typeof options.inspectEnabled === "string" ? `--inspect=${options.inspectEnabled}` : "--inspect"; runScriptNodeArgs.push(inspect); } else if (options.inspectBrkEnabled) { const inspect = typeof options.inspectBrkEnabled === "string" ? `--inspect-brk=${options.inspectBrkEnabled}` : "--inspect-brk"; runScriptNodeArgs.push(inspect); } return { mode: isDev ? "development" : "production", profile: false, ...isDev ? { watch: true, watchOptions: { ignored: /node_modules\/(?!\@backstage)/ } } : {}, externals: [ nodeExternalsWithResolve({ modulesDir: paths.rootNodeModules, additionalModuleDirs: moduleDirs, allowlist: ["webpack/hot/poll?100", ...localPackageEntryPoints] }) ], target: "node", node: { /* eslint-disable-next-line no-restricted-syntax */ __dirname: true, __filename: true, global: true }, bail: false, performance: { hints: false // we check the gzip size instead }, devtool: isDev ? "eval-cheap-module-source-map" : "source-map", context: paths.targetPath, entry: [ "webpack/hot/poll?100", paths.targetRunFile ? paths.targetRunFile : paths.targetEntry ], resolve: { extensions: [".ts", ".mjs", ".js", ".json"], mainFields: ["main"], modules: [paths.rootNodeModules, ...moduleDirs], plugins: [ new LinkedPackageResolvePlugin(paths.rootNodeModules, externalPkgs), new ModuleScopePlugin__default.default( [paths.targetSrc, paths.targetDev], [paths.targetPackageJson] ) ] }, module: { rules: loaders }, output: { path: paths.targetDist, filename: isDev ? "[name].js" : "[name].[hash:8].js", chunkFilename: isDev ? "[name].chunk.js" : "[name].[chunkhash:8].chunk.js", ...isDev ? { devtoolModuleFilenameTemplate: (info) => `file:///${path.resolve(info.absoluteResourcePath).replace( /\\/g, "/" )}` } : {} }, plugins: [ new runScriptWebpackPlugin.RunScriptWebpackPlugin({ name: "main.js", nodeArgs: runScriptNodeArgs.length > 0 ? runScriptNodeArgs : void 0, args: process.argv.slice(3) // drop `node backstage-cli backend:dev` }), new webpack__default.default.HotModuleReplacementPlugin(), ...checksEnabled ? [ new ForkTsCheckerWebpackPlugin__default.default({ typescript: { configFile: paths.targetTsConfig } }), new ESLintPlugin__default.default({ files: ["**/*.(ts|tsx|mts|cts|js|jsx|mjs|cjs)"] }) ] : [] ] }; } function nodeExternalsWithResolve(options) { let currentContext; const externals = nodeExternals__default.default({ ...options, importType(request) { const resolved = require.resolve(request, { paths: [currentContext] }); return `commonjs ${resolved}`; } }); return ({ context, request }, callback) => { currentContext = context; return externals(context, request, callback); }; } function resolveBundlingPaths(options) { var _a; const { entry, targetDir = index.paths.targetDir } = options; const resolveTargetModule = (pathString) => { for (const ext of ["mjs", "js", "ts", "tsx", "jsx"]) { const filePath = path.resolve(targetDir, `${pathString}.${ext}`); if (fs__default.default.pathExistsSync(filePath)) { return filePath; } } return path.resolve(targetDir, `${pathString}.js`); }; let targetPublic = void 0; let targetHtml = path.resolve(targetDir, "public/index.html"); if (fs__default.default.pathExistsSync(targetHtml)) { targetPublic = path.resolve(targetDir, "public"); } else { targetHtml = path.resolve(targetDir, `${entry}.html`); if (!fs__default.default.pathExistsSync(targetHtml)) { targetHtml = index.paths.resolveOwn("templates/serve_index.html"); } } const targetRunFile = path.resolve(targetDir, "src/run.ts"); const runFileExists = fs__default.default.pathExistsSync(targetRunFile); return { targetHtml, targetPublic, targetPath: path.resolve(targetDir, "."), targetRunFile: runFileExists ? targetRunFile : void 0, targetDist: path.resolve(targetDir, (_a = options.dist) != null ? _a : "dist"), targetAssets: path.resolve(targetDir, "assets"), targetSrc: path.resolve(targetDir, "src"), targetDev: path.resolve(targetDir, "dev"), targetEntry: resolveTargetModule(entry), targetTsConfig: index.paths.resolveTargetRoot("tsconfig.json"), targetPackageJson: path.resolve(targetDir, "package.json"), rootNodeModules: index.paths.resolveTargetRoot("node_modules"), root: index.paths.targetRoot }; } async function resolveOptionalBundlingPaths(options) { const resolvedPaths = resolveBundlingPaths(options); if (await fs__default.default.pathExists(resolvedPaths.targetEntry)) { return resolvedPaths; } return void 0; } const DETECTED_MODULES_MODULE_NAME = "__backstage-autodetected-plugins__"; function readPackageDetectionConfig(config$1) { const packages = config$1.getOptional("app.experimental.packages"); if (packages === void 0 || packages === null) { return void 0; } if (typeof packages === "string") { if (packages !== "all") { throw new Error( `Invalid app.experimental.packages mode, got '${packages}', expected 'all'` ); } return {}; } if (typeof packages !== "object" || Array.isArray(packages)) { throw new Error( "Invalid config at 'app.experimental.packages', expected object" ); } const packagesConfig = new config.ConfigReader( packages, "app.experimental.packages" ); return { include: packagesConfig.getOptionalStringArray("include"), exclude: packagesConfig.getOptionalStringArray("exclude") }; } async function detectPackages(targetPath, { include, exclude }) { var _a; const pkg = await fs__default.default.readJson( path.resolve(targetPath, "package.json") ); return Object.keys((_a = pkg.dependencies) != null ? _a : {}).flatMap((depName) => { var _a2, _b; if (exclude == null ? void 0 : exclude.includes(depName)) { return []; } if (include && !include.includes(depName)) { return []; } try { const depPackageJson = require(require.resolve( `${depName}/package.json`, { paths: [targetPath] } )); if (["frontend-plugin", "frontend-plugin-module"].includes( (_b = (_a2 = depPackageJson.backstage) == null ? void 0 : _a2.role) != null ? _b : "" )) { const exp = depPackageJson.exports; if (exp && typeof exp === "object" && "./alpha" in exp) { return [ { name: depName, import: depName }, { name: depName, export: "./alpha", import: `${depName}/alpha` } ]; } return [{ name: depName, import: depName }]; } } catch { } return []; }); } const writeQueue = new PQueue__default.default({ concurrency: 1 }); async function writeDetectedPackagesModule(pkgs) { const requirePackageScript = pkgs == null ? void 0 : pkgs.map( (pkg) => `{ name: ${JSON.stringify(pkg.name)}, export: ${JSON.stringify( pkg.export )}, default: require('${pkg.import}').default }` ).join(","); await writeQueue.add( () => fs__default.default.writeFile( path.join( index.paths.targetRoot, "node_modules", `${DETECTED_MODULES_MODULE_NAME}.js` ), `window['__@backstage/discovered__'] = { modules: [${requirePackageScript}] };` ) ); } async function createDetectedModulesEntryPoint(options) { const { config, watch, targetPath } = options; const detectionConfig = readPackageDetectionConfig(config); if (!detectionConfig) { return []; } if (watch) { const watcher = chokidar__default.default.watch(path.resolve(targetPath, "package.json")); watcher.on("change", async () => { await writeDetectedPackagesModule( await detectPackages(targetPath, detectionConfig) ); watch(); }); } await writeDetectedPackagesModule( await detectPackages(targetPath, detectionConfig) ); return [DETECTED_MODULES_MODULE_NAME]; } exports.createBackendConfig = createBackendConfig; exports.createConfig = createConfig; exports.createDetectedModulesEntryPoint = createDetectedModulesEntryPoint; exports.hasReactDomClient = hasReactDomClient; exports.resolveBaseUrl = resolveBaseUrl; exports.resolveBundlingPaths = resolveBundlingPaths; exports.resolveOptionalBundlingPaths = resolveOptionalBundlingPaths; //# sourceMappingURL=packageDetection-C__AGK-g.cjs.js.map