@backtrace/javascript-cli
Version:
Backtrace CLI for working with Javascript files.
241 lines • 11.7 kB
JavaScript
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
;