UNPKG

@nx/rsbuild

Version:

The Nx Plugin for Rsbuild contains an Nx plugin, executors and utilities that support building applications using Rsbuild.

241 lines (240 loc) • 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNodesV2 = exports.createNodes = void 0; const internal_1 = require("@nx/devkit/internal"); const devkit_1 = require("@nx/devkit"); const file_hasher_1 = require("nx/src/hasher/file-hasher"); const cache_directory_1 = require("nx/src/utils/cache-directory"); const internal_2 = require("@nx/js/internal"); const js_1 = require("@nx/js"); const fs_1 = require("fs"); const path_1 = require("path"); const minimatch_1 = require("minimatch"); const rsbuildConfigGlob = '**/rsbuild.config.{js,ts,mjs,mts,cjs,cts}'; exports.createNodes = [ rsbuildConfigGlob, async (configFilePaths, options, context) => { const optionsHash = (0, file_hasher_1.hashObject)(options); const cachePath = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `rsbuild-${optionsHash}.hash`); const targetsCache = new internal_1.PluginCache(cachePath); const isUsingTsSolutionSetup = (0, internal_2.isUsingTsSolutionSetup)(); const packageManager = (0, devkit_1.detectPackageManager)(context.workspaceRoot); const pmc = (0, devkit_1.getPackageManagerCommand)(packageManager); const lockFileName = (0, js_1.getLockFileName)(packageManager); const normalizedOptions = normalizeOptions(options); try { const { entries, preErrors } = await filterRsbuildConfigs(configFilePaths, context); const projectHashes = await (0, internal_1.calculateHashesForCreateNodes)(entries.map((e) => e.projectRoot), { ...normalizedOptions, isUsingTsSolutionSetup }, context, entries.map(() => [lockFileName])); let results = []; let nodeErrors = []; try { results = await (0, devkit_1.createNodesFromFiles)((configFile, _, ctx, idx) => createNodesInternal(configFile, normalizedOptions, ctx, targetsCache, isUsingTsSolutionSetup, pmc, entries[idx].tsConfigFiles, projectHashes[idx]), entries.map((e) => e.configFile), options, context); } catch (e) { if (e instanceof devkit_1.AggregateCreateNodesError) { results = e.partialResults ?? []; nodeErrors = e.errors; } else { throw e; } } const allErrors = [...preErrors, ...nodeErrors]; if (allErrors.length > 0) { throw new devkit_1.AggregateCreateNodesError(allErrors, results); } return results; } finally { targetsCache.writeToDisk(); } }, ]; /** * @deprecated Use {@link createNodes} instead. This will be removed in Nx 24. */ exports.createNodesV2 = exports.createNodes; async function createNodesInternal(configFilePath, normalizedOptions, context, targetsCache, isUsingTsSolutionSetup, pmc, tsConfigFiles, hash) { const projectRoot = (0, path_1.dirname)(configFilePath); if (!targetsCache.has(hash)) { targetsCache.set(hash, await createRsbuildTargets(configFilePath, projectRoot, normalizedOptions, tsConfigFiles, isUsingTsSolutionSetup, context, pmc)); } const { targets, metadata } = targetsCache.get(hash); return { projects: { [projectRoot]: { root: projectRoot, targets, metadata, }, }, }; } async function createRsbuildTargets(configFilePath, projectRoot, options, tsConfigFiles, isUsingTsSolutionSetup, context, pmc) { const absoluteConfigFilePath = (0, devkit_1.joinPathFragments)(context.workspaceRoot, configFilePath); // Required lazily: `@rsbuild/core` is an optional peer dependency, so it // may be absent when the plugin is loaded in a workspace that doesn't use // Rsbuild yet (e.g. before a generator installs it). const { loadConfig } = require('@rsbuild/core'); const rsbuildConfig = await loadConfig({ path: absoluteConfigFilePath, }); if (!rsbuildConfig.filePath) { return { targets: {}, metadata: {} }; } const namedInputs = (0, internal_1.getNamedInputs)(projectRoot, context); const { buildOutputs } = getOutputs(rsbuildConfig.content, projectRoot, context.workspaceRoot); const targets = {}; targets[options.buildTargetName] = { command: `rsbuild build`, options: { cwd: projectRoot, args: ['--mode=production'] }, cache: true, dependsOn: [`^${options.buildTargetName}`], inputs: [ ...('production' in namedInputs ? ['production', '^production'] : ['default', '^default']), { externalDependencies: ['@rsbuild/core'], }, ], outputs: buildOutputs, metadata: { technologies: ['rsbuild'], description: `Run Rsbuild build`, help: { command: `${pmc.exec} rsbuild build --help`, example: { options: { watch: false, }, }, }, }, }; targets[options.devTargetName] = { continuous: true, command: `rsbuild dev`, options: { cwd: projectRoot, args: ['--mode=development'], }, }; targets[options.previewTargetName] = { continuous: true, command: `rsbuild preview`, dependsOn: [`${options.buildTargetName}`, `^${options.buildTargetName}`], options: { cwd: projectRoot, args: ['--mode=production'], }, }; targets[options.inspectTargetName] = { command: `rsbuild inspect`, options: { cwd: projectRoot, }, }; if (tsConfigFiles.length) { const tsConfigToUse = ['tsconfig.app.json', 'tsconfig.lib.json', 'tsconfig.json'].find((t) => tsConfigFiles.includes(t)) ?? tsConfigFiles[0]; targets[options.typecheckTargetName] = { cache: true, inputs: [ ...('production' in namedInputs ? ['production', '^production'] : ['default', '^default']), { externalDependencies: ['typescript'] }, ], command: isUsingTsSolutionSetup ? `tsc --build --emitDeclarationOnly` : `tsc -p ${tsConfigToUse} --noEmit`, options: { cwd: (0, devkit_1.joinPathFragments)(projectRoot) }, metadata: { description: `Runs type-checking for the project.`, technologies: ['typescript'], help: { command: isUsingTsSolutionSetup ? `${pmc.exec} tsc --build --help` : `${pmc.exec} tsc -p ${tsConfigToUse} --help`, example: isUsingTsSolutionSetup ? { args: ['--force'] } : { options: { noEmit: true } }, }, }, }; if (isUsingTsSolutionSetup) { targets[options.typecheckTargetName].dependsOn = [ `^${options.typecheckTargetName}`, ]; targets[options.typecheckTargetName].syncGenerators = [ '@nx/js:typescript-sync', ]; } } (0, internal_2.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, options, pmc); return { targets, metadata: {} }; } function getOutputs(rsbuildConfig, projectRoot, workspaceRoot) { // `output.distPath.root` is the directory Rsbuild emits the build into, so // it is the build output as-is. (Don't take its `dirname` - that points at // the parent directory, which can capture sibling projects' outputs.) const buildOutputPath = normalizeOutputPath(rsbuildConfig?.output?.distPath?.root, projectRoot, workspaceRoot, 'dist'); return { buildOutputs: [buildOutputPath], }; } function normalizeOutputPath(outputPath, projectRoot, workspaceRoot, path) { if (!outputPath) { if (projectRoot === '.') { return `{projectRoot}/${path}`; } else { return `{workspaceRoot}/${path}/{projectRoot}`; } } else { if ((0, path_1.isAbsolute)(outputPath)) { return `{workspaceRoot}/${(0, path_1.relative)(workspaceRoot, outputPath)}`; } else { if (outputPath.startsWith('..')) { return (0, path_1.join)('{workspaceRoot}', (0, path_1.join)(projectRoot, outputPath)); } else { return (0, path_1.join)('{projectRoot}', outputPath); } } } } async function filterRsbuildConfigs(configFiles, context) { const preErrors = []; const candidates = await Promise.all(configFiles.map(async (configFile) => { try { const projectRoot = (0, path_1.dirname)(configFile); const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot)); if (!siblingFiles.includes('package.json') && !siblingFiles.includes('project.json')) { return null; } const tsConfigFiles = siblingFiles.filter((p) => (0, minimatch_1.minimatch)(p, 'tsconfig*{.json,.*.json}')) ?? []; return { configFile, projectRoot, tsConfigFiles }; } catch (e) { preErrors.push([configFile, e]); return null; } })); return { entries: candidates.filter((c) => c !== null), preErrors, }; } function normalizeOptions(options) { options ??= {}; options.buildTargetName ??= 'build'; options.devTargetName ??= 'dev'; options.previewTargetName ??= 'preview'; options.inspectTargetName ??= 'inspect'; options.typecheckTargetName ??= 'typecheck'; return options; }