UNPKG

@anatine/esbuildnx

Version:
316 lines 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildExecutor = void 0; const tslib_1 = require("tslib"); const normalize_options_1 = require("../../utils/normalize-options"); const fs_extra_1 = require("fs-extra"); const workspace_1 = require("@nrwl/workspace"); const esbuild_1 = require("esbuild"); const child_process_1 = require("child_process"); const esbuild_decorators_1 = require("@anatine/esbuild-decorators"); const chalk_1 = require("chalk"); const node_watch_1 = tslib_1.__importDefault(require("node-watch")); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const rxjs_for_await_1 = require("rxjs-for-await"); const date_fns_1 = require("date-fns"); // import { exportDiagnostics } from '../../utils/print-diagnostics'; const util_1 = require("util"); const walk_packages_1 = require("../../utils/walk-packages"); const assets_1 = require("../../utils/assets"); const constants_1 = require("../../utils/constants"); function buildExecutor(rawOptions, context) { const { sourceRoot, root } = context.workspace.projects[context.projectName]; if (!sourceRoot) { throw new Error(`${context.projectName} does not have a sourceRoot.`); } if (!root) { throw new Error(`${context.projectName} does not have a root.`); } // Eventually, it would be great to expose more esbuild settings on command line. // For now, the app root directory can utilize an esbuild.json file for build API settings // https://esbuild.github.io/api/#build-api const esBuildExists = (0, fs_extra_1.pathExistsSync)(`${root}/esbuild.json`); const packageExists = (0, fs_extra_1.pathExistsSync)(`${root}/package.json`); const esbuildConfig = esBuildExists ? (0, workspace_1.readJsonFile)(`${root}/esbuild.json`) : { external: [] }; const projectPackage = packageExists ? (0, workspace_1.readJsonFile)(`${root}/package.json`) : {}; const options = (0, normalize_options_1.normalizeBuildOptions)(rawOptions, esbuildConfig, context.root, sourceRoot, root); const outdir = `${options.outputPath}`; const outfile = `${outdir}/${constants_1.OUTFILE_NAME}`; const watchDir = `${options.root}/${options.sourceRoot}`; const packages = packageExists ? Object.keys(projectPackage.dependencies) : []; esbuildConfig.external = [...packages, ...(esbuildConfig.external || [])]; const esbuildOptions = Object.assign(Object.assign({ logLevel: 'silent', platform: 'node', bundle: options.bundle || true, sourcemap: 'external', charset: 'utf8', color: true, conditions: options.watch ? ['development'] : ['production'], watch: options.watch || false, absWorkingDir: options.root, plugins: [ (0, esbuild_decorators_1.esbuildDecorators)({ cwd: options.root, }), ], // banner: { // js: '// Compiled by esbuildnx ', // }, tsconfig: options.tsConfig, entryPoints: [options.main], outdir }, esbuildConfig), { incremental: options.watch || false }); let buildCounter = 1; const buildSubscriber = runBuild(esbuildOptions, watchDir).pipe((0, operators_1.map)(({ buildResult, buildFailure }) => { let message = ''; const timeString = (0, date_fns_1.format)(new Date(), 'h:mm:ss a'); const count = (0, chalk_1.gray)(`[${buildCounter}]`); const prefix = `esbuild ${count} ${timeString}`; // const warnings: string[] = []; if ((buildResult === null || buildResult === void 0 ? void 0 : buildResult.warnings.length) > 0) { let warningMessage = (0, chalk_1.yellow)(`${prefix} - Warnings:`); buildResult === null || buildResult === void 0 ? void 0 : buildResult.warnings.forEach((warning) => { warningMessage += `\n ${(0, chalk_1.yellow)(warning.location.file)}(${warning.location.line},${warning.location.column}):`; warningMessage += ` ${warning.location.lineText.trim()}`; warningMessage += (0, chalk_1.gray)(`\n ${warning.text}\n`); }); // console.log(warningMessage); message += warningMessage; } if (buildFailure) { // console.log(red(`\nEsbuild Error ${count}`)); // console.error(stats.buildFailure); message += (0, chalk_1.red)(`Esbuild Error ${count}`); message += buildFailure; } else if ((buildResult === null || buildResult === void 0 ? void 0 : buildResult.warnings.length) > 0) { message += (0, chalk_1.green)(`${prefix} - Build finished with ${(0, chalk_1.yellow)(buildResult === null || buildResult === void 0 ? void 0 : buildResult.warnings.length)} warnings. \n`); } else { message += (0, chalk_1.green)(`${prefix} - Build finished \n`); } buildCounter++; return { success: !buildFailure, message, }; })); let typeCounter = 1; const tscBufferTrigger = new rxjs_1.Subject(); const tscSubscriber = runTsc({ tsconfigPath: options.tsConfig, watch: options.watch || !!esbuildOptions.watch, root: options.root, useGlobal: false, }).pipe((0, operators_1.map)(({ info, error, end }) => { let message = ''; let hasErrors = Boolean(error); const count = (0, chalk_1.gray)(`[${typeCounter}]`); const prefix = `tsc ${count}`; if (error) { message += (0, chalk_1.red)(`${prefix} ${error.replace(/\n/g, '')} \n`); } else if (info) { if (info.match(/Found\s\d*\serror/)) { if (info.includes('Found 0 errors')) { message += (0, chalk_1.green)(`${prefix} ${info.replace(/\n/g, '')} \n`); } else { hasErrors = true; message += (0, chalk_1.yellow)(`${prefix} ${info.replace(/\n/g, '')} \n`); } tscBufferTrigger.next(true); } else { message += (0, chalk_1.green)(`${prefix} ${info.replace(/\n/g, '')} \n`); } } return { info, error, end, message, hasErrors }; }), bufferUntil(({ info }) => !!(info === null || info === void 0 ? void 0 : info.match(/Found\s\d*\serror/))), // bufferUntil(({ info }) => true), (0, operators_1.map)((values) => { typeCounter++; let message = ''; values.forEach((value) => (message += value.message)); // console.log(message); return { success: !values.find((value) => value.hasErrors), message, }; })); const packageCopySubscriber = runCopyPackages(process.cwd(), options.outputPath, esbuildOptions.external).pipe((0, operators_1.map)((result) => { var _a; const message = (_a = result.error) !== null && _a !== void 0 ? _a : result.copyResult; return { success: result.success, message, }; })); const assetCopySubscriber = runCopyAssets(options.assets, '', options.outputPath).pipe((0, operators_1.map)((result) => result)); // exportDiagnostics( // `OUTPUT_LOG.ts`, // `const output = ${inspect( // { // cwd: process.cwd(), // options, // rawOptions, // context, // projGraph, // workspace, // }, // false, // 10 // )}` // ); if (options.watch) { return (0, rxjs_for_await_1.eachValueFrom)((0, rxjs_1.zip)(buildSubscriber, tscSubscriber).pipe((0, operators_1.map)(([buildResults, tscResults]) => { // console.log('\x1Bc'); console.log(tscResults.message); console.log(buildResults.message); return { success: (buildResults === null || buildResults === void 0 ? void 0 : buildResults.success) && (tscResults === null || tscResults === void 0 ? void 0 : tscResults.success), outfile, }; }))); } return (0, rxjs_for_await_1.eachValueFrom)((0, rxjs_1.zip)(buildSubscriber, tscSubscriber, packageCopySubscriber, assetCopySubscriber).pipe((0, operators_1.map)(([buildResults, tscResults, packageCopyResults, assetCopyResults]) => { // console.log('\x1Bc'); console.log(tscResults.message); console.log(buildResults.message); if (packageCopyResults.message.length !== 0) { console.log(`Copied node_modules: ${(0, util_1.inspect)(packageCopyResults.message, false, 10, true)}`); } if (assetCopyResults.error) { console.error(`Error copying assets: ${assetCopyResults.error}`); } return { success: (buildResults === null || buildResults === void 0 ? void 0 : buildResults.success) && (tscResults === null || tscResults === void 0 ? void 0 : tscResults.success) && packageCopyResults.success && assetCopyResults.success, outfile, }; }))); } exports.buildExecutor = buildExecutor; function runBuild(options, watchDir) { return new rxjs_1.Observable((subscriber) => { const cwd = watchDir || options.absWorkingDir || process.cwd(); // We will use the org watch settings with node-watch for better refresh performance const { watch: buildWatch } = options, opts = tslib_1.__rest(options, ["watch"]); (0, esbuild_1.build)(opts) .then((buildResult) => { subscriber.next({ buildResult, buildFailure: null }); // Helper to send back data for watch events & supporting existing esbuild settings const watchNext = ({ buildFailure, buildResult }) => { subscriber.next({ buildFailure, buildResult }); if (typeof buildWatch === 'object' && buildWatch.onRebuild) { buildWatch.onRebuild(buildFailure, buildResult); } }; // When in watch mode, it will continue to report back if (buildWatch) { (0, node_watch_1.default)(cwd, { recursive: true }, () => { buildResult .rebuild() .then((watchResult) => { watchNext({ buildFailure: null, buildResult: watchResult, }); }) .catch((watchFailure) => { watchNext({ buildFailure: watchFailure, buildResult: null, }); }); }); } else { subscriber.complete(); } }) .catch((buildFailure) => { subscriber.next({ buildResult: null, buildFailure }); subscriber.complete(); }); }); } function runTsc({ tsconfigPath, watch, root, useGlobal }) { return new rxjs_1.Observable((subscriber) => { // Build command const modeModulesPath = useGlobal ? '' : (root ? root + '/' : './') + 'node_modules/typescript/bin/'; const command = `${modeModulesPath}tsc`; // Build arguments const args = ['--noEmit']; // --noEmit so as to not save out data if (watch) { args.push('-w'); } args.push('-p'); args.push(tsconfigPath); let errorCount = 0; // Run command const child = (0, child_process_1.spawn)(command, args, { shell: true }); child.stdout.on('data', (data) => { const decoded = data.toString(); // eslint-disable-next-line no-control-regex if (decoded.match(/\x1Bc/g)) return; if (decoded.includes('): error T')) { errorCount++; subscriber.next({ error: decoded }); } else { subscriber.next({ info: decoded }); } }); child.stderr.on('error', (tscError) => { subscriber.next({ tscError }); }); child.stdout.on('end', () => { subscriber.next({ info: `Type check complete. Found ${errorCount} errors`, }); }); }); } function runCopyPackages(root, destination, external = []) { return new rxjs_1.Observable((subscriber) => { (0, walk_packages_1.getPackagesToCopy)(root, external) .then((modules) => (0, walk_packages_1.copyPackages)(root, destination, modules)) .then((directories) => { subscriber.next({ copyResult: directories !== null && directories !== void 0 ? directories : [], success: true, }); }) .catch((error) => { subscriber.next({ copyResult: [], success: false, error, }); }); }); } function runCopyAssets(assets, root, destination) { return new rxjs_1.Observable((subscriber) => { (0, assets_1.copyAssets)(assets, root, destination) .then((response) => { subscriber.next(response); }) .catch((error) => { subscriber.next({ success: false, error, }); }); }); } function bufferUntil(predicate) { return function (source) { const share$ = source.pipe((0, operators_1.share)()); const until$ = share$.pipe((0, operators_1.filter)(predicate), (0, operators_1.delay)(0)); return share$.pipe((0, operators_1.buffer)(until$)); }; } exports.default = buildExecutor; //# sourceMappingURL=build.impl.js.map