UNPKG

@backtrace/javascript-cli

Version:
241 lines 11.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getUploadUrl = exports.saveAssets = exports.uploadAssets = exports.uploadOrSaveAssets = exports.uploadSourcemaps = exports.uploadCmd = void 0; const sourcemap_tools_1 = require("@backtrace/sourcemap-tools"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const Command_1 = require("../commands/Command"); const common_1 = require("../helpers/common"); const errorBehavior_1 = require("../helpers/errorBehavior"); const find_1 = require("../helpers/find"); const logs_1 = require("../helpers/logs"); const normalizePaths_1 = require("../helpers/normalizePaths"); const loadOptions_1 = require("../options/loadOptions"); exports.uploadCmd = new Command_1.Command({ name: 'upload', description: 'Uploading of sourcemaps to Backtrace', }) .option({ name: 'path', type: String, description: 'Path to sourcemap files or directories containing sourcemaps to upload.', defaultOption: true, multiple: true, alias: 'p', }) .option({ name: 'include', description: 'Includes specified paths.', type: String, multiple: true, alias: 'i', }) .option({ name: 'exclude', description: 'Excludes specified paths.', type: String, multiple: true, alias: 'x', }) .option({ name: 'url', type: String, description: 'URL to upload to.', alias: 'u', }) .option({ name: 'subdomain', type: String, description: 'Subdomain to upload to. Do not use on on-premise environments.', alias: 's', }) .option({ name: 'token', type: String, description: 'Symbol submission token. Required when subdomain is provided.', alias: 't', }) .option({ name: 'include-sources', type: Boolean, description: 'Uploads the sourcemaps with "sourcesContent" key.', }) .option({ name: 'insecure', alias: 'k', type: Boolean, description: 'Disables HTTPS certificate checking.', }) .option({ name: 'dry-run', alias: 'n', type: Boolean, description: 'Does not upload the files at the end.', }) .option({ name: 'force', alias: 'f', type: Boolean, description: 'Upload files even if not processed.', }) .option({ name: 'pass-with-no-files', type: Boolean, description: 'Exits with zero exit code if no files for uploading are found.', }) .option({ name: 'asset-error-behavior', alias: 'e', type: String, description: `What to do when an asset fails. Can be one of: ${Object.keys(errorBehavior_1.ErrorBehaviors).join(', ')}.`, }) .option({ name: 'output', alias: 'o', description: 'If set, archive with sourcemaps will be outputted to this path instead of being uploaded.', type: String, }) .execute(uploadSourcemaps); /** * Uploads sourcemaps found in path(s). */ async function uploadSourcemaps({ opts, logger, getHelpMessage }) { const sourceProcessor = new sourcemap_tools_1.SourceProcessor(new sourcemap_tools_1.DebugIdGenerator()); const configPath = opts.config ?? (await (0, loadOptions_1.findConfig)()); const configResult = await (0, loadOptions_1.loadOptionsForCommand)(configPath)('upload'); if (configResult.isErr()) { return configResult; } const config = configResult.data; opts = { ...config, ...opts, path: opts.path ?? (config.path && configPath ? (0, normalizePaths_1.relativePaths)(config.path, path_1.default.dirname(configPath)) : process.cwd()), }; logger.trace(`resolved options: \n${JSON.stringify(opts, null, ' ')}`); const searchPaths = (0, normalizePaths_1.normalizePaths)(opts.path, process.cwd()); if (!searchPaths) { logger.info(getHelpMessage()); return (0, sourcemap_tools_1.Err)('path must be specified'); } const uploadUrlResult = getUploadUrl(opts); if (uploadUrlResult.isErr()) { logger.info(getHelpMessage()); return uploadUrlResult; } const outputPath = opts.output; const uploadUrl = uploadUrlResult.data; if (!outputPath && !uploadUrl) { logger.info(getHelpMessage()); return (0, sourcemap_tools_1.Err)('upload URL is required.'); } if (outputPath && uploadUrl) { logger.info(getHelpMessage()); return (0, sourcemap_tools_1.Err)('outputting archive and uploading are exclusive'); } const logDebug = (0, sourcemap_tools_1.log)(logger, 'debug'); const logTrace = (0, sourcemap_tools_1.log)(logger, 'trace'); const logDebugAsset = (0, logs_1.createAssetLogger)(logger, 'debug'); const logTraceAsset = (0, logs_1.createAssetLogger)(logger, 'trace'); const assetErrorBehaviorResult = (0, errorBehavior_1.getErrorBehavior)(opts['asset-error-behavior'] ?? 'exit'); if (assetErrorBehaviorResult.isErr()) { logger.info(getHelpMessage()); return assetErrorBehaviorResult; } const assetErrorBehavior = assetErrorBehaviorResult.data; const handleFailedAsset = (0, errorBehavior_1.handleError)(assetErrorBehavior); const logAssetBehaviorError = (asset) => (err, level) => (0, logs_1.createAssetLogger)(logger, level)(err)(asset); const isAssetProcessedCommand = (asset) => (0, sourcemap_tools_1.pipe)(asset, logTraceAsset('checking if asset is processed'), (0, common_1.isAssetProcessed)(sourceProcessor), logDebug(({ asset, result }) => `${asset.name}: ` + (result ? 'asset is processed' : 'asset is not processed'))); const filterProcessedAssetsCommand = (assets) => (0, sourcemap_tools_1.pipe)(assets, (0, sourcemap_tools_1.mapAsync)(isAssetProcessedCommand), (0, sourcemap_tools_1.filter)((f) => f.result), (0, sourcemap_tools_1.map)((f) => f.asset)); const loadSourceMapCommand = (asset) => (0, sourcemap_tools_1.pipe)(asset, logTraceAsset('loading sourcemap'), (0, common_1.readSourceMapFromPathOrFromSource)(sourceProcessor), sourcemap_tools_1.R.map(logDebugAsset('loaded sourcemap')), sourcemap_tools_1.R.mapErr((error) => `${asset.name}: ${error}`), handleFailedAsset(logAssetBehaviorError(asset))); const saveArchiveCommandResult = await uploadOrSaveAssets(uploadUrl, opts.output, (url) => uploadAssets(url, { ignoreSsl: opts.insecure ?? false }), (path) => (0, sourcemap_tools_1.flow)(saveAssets(path), sourcemap_tools_1.Ok)); if (saveArchiveCommandResult.isErr()) { return saveArchiveCommandResult; } const saveArchiveCommand = saveArchiveCommandResult.data; const includePaths = (0, normalizePaths_1.normalizePaths)(opts.include); const excludePaths = (0, normalizePaths_1.normalizePaths)(opts.exclude); const { isIncluded, isExcluded } = await (0, find_1.buildIncludeExclude)(includePaths, excludePaths, logTrace); return (0, sourcemap_tools_1.pipe)(searchPaths, find_1.findTuples, sourcemap_tools_1.R.map((0, sourcemap_tools_1.flow)((0, sourcemap_tools_1.map)(find_1.file2Or1FromTuple), logDebug((r) => `found ${r.length} files`), (0, sourcemap_tools_1.map)(logTrace((result) => `found file: ${result.path}`)), isIncluded ? (0, sourcemap_tools_1.filterAsync)(isIncluded) : sourcemap_tools_1.pass, isExcluded ? (0, sourcemap_tools_1.filterAsync)((0, sourcemap_tools_1.flow)(isExcluded, sourcemap_tools_1.not)) : sourcemap_tools_1.pass, (0, sourcemap_tools_1.filter)((t) => t.direct || (0, sourcemap_tools_1.matchSourceMapExtension)(t.path)), (0, sourcemap_tools_1.map)((t) => t.path), logDebug((r) => `found ${r.length} files for upload`), (0, sourcemap_tools_1.map)(logTrace((path) => `file for upload: ${path}`)), (0, sourcemap_tools_1.map)(common_1.toAsset), opts['pass-with-no-files'] ? sourcemap_tools_1.Ok : (0, sourcemap_tools_1.failIfEmpty)('no sourcemaps found'), sourcemap_tools_1.R.map((0, sourcemap_tools_1.flow)((0, sourcemap_tools_1.mapAsync)(loadSourceMapCommand), sourcemap_tools_1.R.flatMap)), sourcemap_tools_1.R.map(errorBehavior_1.filterBehaviorSkippedElements), sourcemap_tools_1.R.map(filterProcessedAssetsCommand), sourcemap_tools_1.R.map(opts['pass-with-no-files'] ? sourcemap_tools_1.Ok : (0, sourcemap_tools_1.failIfEmpty)('no processed sourcemaps found, make sure to run process first')), sourcemap_tools_1.R.map((0, common_1.uniqueBy)((asset) => asset.content.debugId)), sourcemap_tools_1.R.map(opts['include-sources'] ? sourcemap_tools_1.pass : (0, sourcemap_tools_1.map)(sourcemap_tools_1.stripSourcesContent)), sourcemap_tools_1.R.map((assets) => opts['dry-run'] ? (0, sourcemap_tools_1.Ok)({ rxid: '<dry-run>', assets, }) : assets.length ? saveArchiveCommand(assets) : (0, sourcemap_tools_1.Ok)({ rxid: '<no sourcemaps uploaded>', assets })), sourcemap_tools_1.R.map(output(logger))))); } exports.uploadSourcemaps = uploadSourcemaps; function uploadOrSaveAssets(uploadUrl, outputPath, upload, save) { if (uploadUrl && outputPath) { return (0, sourcemap_tools_1.Err)('outputting archive and uploading are exclusive'); } if (uploadUrl) { return (0, sourcemap_tools_1.pipe)(uploadUrl, common_1.validateUrl, sourcemap_tools_1.R.map((url) => upload(url))); } else if (outputPath) { return (0, sourcemap_tools_1.Ok)(save(outputPath)); } else { return (0, sourcemap_tools_1.Err)('upload url is required'); } } exports.uploadOrSaveAssets = uploadOrSaveAssets; function uploadAssets(uploadUrl, options) { const uploader = new sourcemap_tools_1.SymbolUploader(uploadUrl, options); return function uploadAssets(assets) { const { request, promise } = uploader.createUploadRequest(); return (0, sourcemap_tools_1.pipe)(request, pipeAssets(assets), () => promise); }; } exports.uploadAssets = uploadAssets; function saveAssets(outputPath) { return function saveAssets(assets) { const stream = fs_1.default.createWriteStream(outputPath); return (0, sourcemap_tools_1.pipe)(stream, pipeAssets(assets), () => ({ rxid: outputPath })); }; } exports.saveAssets = saveAssets; function pipeAssets(assets) { function appendToArchive(archive) { return function appendToArchive(asset) { const filename = `${asset.content.debugId}-${path_1.default.basename(asset.name)}`; archive.append(filename, JSON.stringify(asset.content)); return archive; }; } return function pipeAssets(writable) { const archive = new sourcemap_tools_1.ZipArchive(); const waitForFinish = new Promise((resolve, reject) => writable.on('finish', resolve).on('error', reject)); return (0, sourcemap_tools_1.pipe)(writable, (0, sourcemap_tools_1.pipeStream)(archive.stream), () => assets.map(appendToArchive(archive)), () => archive.finalize(), () => waitForFinish); }; } function getUploadUrl(opts) { if (opts.url && opts.subdomain) { return (0, sourcemap_tools_1.Err)('--url and --subdomain are exclusive'); } if (opts.url) { return (0, common_1.validateUrl)(opts.url); } if (opts.subdomain) { if (!opts.token) { return (0, sourcemap_tools_1.Err)('token is required with subdomain'); } return (0, sourcemap_tools_1.Ok)(`https://submit.backtrace.io/${opts.subdomain}/${opts.token}/sourcemap`); } return (0, sourcemap_tools_1.Ok)(undefined); } exports.getUploadUrl = getUploadUrl; function output(logger) { return function output(result) { logger.output(result.rxid); return result; }; } //# sourceMappingURL=upload.js.map