UNPKG

@aws-amplify/cli-internal

Version:
457 lines 27.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Cfn = exports.MIGRATION_PLACEHOLDER_RESOURCE = exports.HOLDING_STACK_FORWARD_MAPPINGS_METADATA_KEY = exports.MIGRATION_PLACEHOLDER_LOGICAL_ID = exports.VALID_HOLDING_STACK_STATUSES = exports.HOLDING_STACK_NAME_SUFFIX = exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY = void 0; const client_cloudformation_1 = require("@aws-sdk/client-cloudformation"); const client_s3_1 = require("@aws-sdk/client-s3"); const amplify_cli_core_1 = require("@aws-amplify/amplify-cli-core"); const utils_1 = require("./utils"); const drift_formatter_1 = require("../../drift/services/drift-formatter"); const chalk_1 = __importDefault(require("chalk")); const crypto = __importStar(require("crypto")); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const MAX_WAIT_TIME_SECONDS = 900; const NO_UPDATES_MESSAGE = 'No updates are to be performed'; const CFN_IAM_CAPABILITY = 'CAPABILITY_NAMED_IAM'; exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY = '.amplify/gen2-migration/refactor.operations'; const EMPTY_HOLDING_TEMPLATE = { AWSTemplateFormatVersion: '2010-09-09', Description: 'Temporary holding stack for Gen2 migration', Resources: {}, Outputs: {}, }; exports.HOLDING_STACK_NAME_SUFFIX = '-holding'; exports.VALID_HOLDING_STACK_STATUSES = ['UPDATE_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE', 'CREATE_COMPLETE']; exports.MIGRATION_PLACEHOLDER_LOGICAL_ID = 'MigrationPlaceholder'; exports.HOLDING_STACK_FORWARD_MAPPINGS_METADATA_KEY = 'ForwardMappings'; exports.MIGRATION_PLACEHOLDER_RESOURCE = { Type: 'AWS::CloudFormation::WaitConditionHandle', Properties: {} }; class Cfn { constructor(gen1App, logger) { this.gen1App = gen1App; this.logger = logger; this.updateStackClaims = new Set(); if (!fs.existsSync(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY)) { fs.mkdirSync(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, { recursive: true }); } } isUpdateClaimed(stackName) { return this.updateStackClaims.has(stackName); } claimUpdate(stackName) { this.updateStackClaims.add(stackName); } async update(params) { const { stackName, parameters, templateBody, resource, snapshotPrefix } = params; try { const templateUrl = await this.uploadTemplate(JSON.stringify(templateBody), stackName, 'update'); const input = { TemplateURL: templateUrl, Parameters: parameters, StackName: stackName, Capabilities: [CFN_IAM_CAPABILITY], }; writeUpdateSnapshot({ stackName, templateBody: JSON.stringify(templateBody), parameters, prefix: snapshotPrefix !== null && snapshotPrefix !== void 0 ? snapshotPrefix : 'update' }); this.info(`Updating stack: ${(0, utils_1.extractStackNameFromId)(stackName)}`, resource); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.UpdateStackCommand(input)); } catch (e) { if (e && typeof e === 'object' && 'message' in e && typeof e.message === 'string' && e.message.includes(NO_UPDATES_MESSAGE)) { return; } throw e; } this.info(`Waiting for stack update to complete: ${(0, utils_1.extractStackNameFromId)(stackName)}`, resource); await (0, client_cloudformation_1.waitUntilStackUpdateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: stackName }); } async refactor(resourceMappings, resource, pre) { var _a; const sourceStackId = resourceMappings[0].Source.StackName; const targetStackId = resourceMappings[0].Destination.StackName; const sourceStackName = (0, utils_1.extractStackNameFromId)(sourceStackId); const targetStackName = (0, utils_1.extractStackNameFromId)(targetStackId); this.info(`Refactoring ${sourceStackName}${targetStackName}`, resource); const targetStack = await this.findStack(targetStackId); const sourceStack = await this.findStack(sourceStackId); if (!targetStack && !targetStackName.endsWith(exports.HOLDING_STACK_NAME_SUFFIX)) { throw new amplify_cli_core_1.AmplifyError('StackNotFoundError', { message: `Target stack ${targetStackName} does not exist` }); } if (!sourceStack) { throw new amplify_cli_core_1.AmplifyError('StackNotFoundError', { message: `Source stack ${sourceStackName} does not exist` }); } const sourceTemplate = await this.fetchTemplate(sourceStackId); const sourceTemplateClone = JSON.parse(JSON.stringify(sourceTemplate)); const targetTemplate = targetStack ? await this.fetchTemplate(targetStackId) : JSON.parse(JSON.stringify(EMPTY_HOLDING_TEMPLATE)); for (const mapping of resourceMappings) { if (mapping.Destination.LogicalResourceId in targetTemplate.Resources) { throw new amplify_cli_core_1.AmplifyError('ResourceMappingError', { message: `Unable to create stack refactor. Resource ${mapping.Destination.LogicalResourceId} already exists in stack ${targetStackName}`, }); } targetTemplate.Resources[mapping.Destination.LogicalResourceId] = sourceTemplate.Resources[mapping.Source.LogicalResourceId]; delete sourceTemplate.Resources[mapping.Source.LogicalResourceId]; } if (Object.keys(sourceTemplate.Resources).length === 0) { sourceTemplateClone.Resources[exports.MIGRATION_PLACEHOLDER_LOGICAL_ID] = exports.MIGRATION_PLACEHOLDER_RESOURCE; sourceTemplate.Resources[exports.MIGRATION_PLACEHOLDER_LOGICAL_ID] = exports.MIGRATION_PLACEHOLDER_RESOURCE; this.info(`Adding placeholder resource to source stack '${sourceStackName}'`); await this.update({ stackName: sourceStackId, templateBody: sourceTemplateClone, parameters: (_a = sourceStack.Parameters) !== null && _a !== void 0 ? _a : [], resource, }); this.info(`Finished adding placeholder to source stack '${sourceStackName}'`); } if (pre) { await pre(targetTemplate); } const sourceTemplateUrl = await this.uploadTemplate(JSON.stringify(sourceTemplate), sourceStackName, 'refactor'); const targetTemplateUrl = await this.uploadTemplate(JSON.stringify(targetTemplate), targetStackName, 'refactor'); const input = { StackDefinitions: [ { StackName: sourceStackName, TemplateURL: sourceTemplateUrl }, { StackName: targetStackName, TemplateURL: targetTemplateUrl }, ], ResourceMappings: resourceMappings, EnableStackCreation: true, }; input.Description = buildRefactorDescription(input); writeRefactorSnapshot(input, JSON.stringify(sourceTemplate), JSON.stringify(targetTemplate)); this.info(`Creating stack refactor: ${(0, utils_1.extractStackNameFromId)(sourceStackId)}${(0, utils_1.extractStackNameFromId)(targetStackId)}`, resource); const { StackRefactorId } = await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.CreateStackRefactorCommand(input)); if (!StackRefactorId) { throw new amplify_cli_core_1.AmplifyError('StackStateError', { message: 'CreateStackRefactor returned no StackRefactorId', }); } this.info(`Waiting for stack refactor creation to complete: ${StackRefactorId}`, resource); await (0, client_cloudformation_1.waitUntilStackRefactorCreateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackRefactorId }); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.ExecuteStackRefactorCommand({ StackRefactorId })); this.info(`Waiting for stack refactor execution to complete: ${StackRefactorId}`, resource); await (0, client_cloudformation_1.waitUntilStackRefactorExecuteComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackRefactorId }); this.info(`Waiting for source stack update: ${(0, utils_1.extractStackNameFromId)(sourceStackId)}`, resource); await (0, client_cloudformation_1.waitUntilStackUpdateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: sourceStackId }); this.info(`Waiting for destination stack: ${(0, utils_1.extractStackNameFromId)(targetStackId)}`, resource); if (targetStack) { await (0, client_cloudformation_1.waitUntilStackUpdateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: targetStackId }); } else { await (0, client_cloudformation_1.waitUntilStackCreateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: targetStackId }); } this.info(`Finished refactoring ${sourceStackName}${targetStackName}`, resource); } async createChangeSet(params) { var _a; const { stackName, parameters, templateBody } = params; const changeSetName = `gen2-migration-${Date.now()}`; const templateUrl = await this.uploadTemplate(JSON.stringify(templateBody), stackName, 'create-change-set'); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.CreateChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName, TemplateURL: templateUrl, Parameters: parameters, Capabilities: [CFN_IAM_CAPABILITY], })); try { await (0, client_cloudformation_1.waitUntilChangeSetCreateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: 120 }, { StackName: stackName, ChangeSetName: changeSetName }); } catch (e) { if ((_a = e.message) === null || _a === void 0 ? void 0 : _a.includes(`The submitted information didn't contain changes`)) { await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.DeleteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })); return undefined; } throw e; } return await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.DescribeChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName, IncludePropertyValues: true })); } async executeChangeSet(params) { var _a, _b; const { changeSet, templateBody, resource } = params; const displayName = (0, utils_1.extractStackNameFromId)(changeSet.StackName); if ((_a = params.captureSnapshot) !== null && _a !== void 0 ? _a : true) { writeUpdateSnapshot({ stackName: changeSet.StackName, templateBody: JSON.stringify(templateBody), parameters: (_b = changeSet.Parameters) !== null && _b !== void 0 ? _b : [], prefix: 'update', }); } this.info(`Executing change set for stack: ${displayName}`, resource); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.ExecuteChangeSetCommand({ StackName: changeSet.StackName, ChangeSetName: changeSet.ChangeSetName })); this.info(`Waiting for stack update to complete: ${displayName}`, resource); await (0, client_cloudformation_1.waitUntilStackUpdateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: changeSet.StackName }); } async orphan(params) { var _a; const { stackName, logicalIds, resource } = params; const displayName = (0, utils_1.extractStackNameFromId)(stackName); const template = await this.fetchTemplate(stackName); const missingRetain = logicalIds.filter((id) => id in template.Resources && template.Resources[id].DeletionPolicy !== 'Retain'); if (missingRetain.length > 0) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Cannot orphan resources from '${displayName}': ${missingRetain.join(', ')} ` + `missing 'DeletionPolicy: Retain' - orphaning would delete the physical resources.`, }); } const stack = await this.describeStack(stackName); for (const id of logicalIds) { delete template.Resources[id]; } writeOrphanSnapshot({ stackName, logicalIds, prefix: 'orphan' }); await this.update({ stackName, templateBody: template, parameters: (_a = stack.Parameters) !== null && _a !== void 0 ? _a : [], resource, snapshotPrefix: 'orphan', }); } async importResources(params) { const { stackName, templateAdditions, resourcesToImport, resource } = params; const displayName = (0, utils_1.extractStackNameFromId)(stackName); const changeSetName = `gen2-migration-import-${Date.now()}`; const templateBody = await this.fetchTemplate(stackName); for (const [logicalId, r] of Object.entries(templateAdditions)) { templateBody.Resources[logicalId] = r; } writeImportSnapshot({ stackName, templateBody: JSON.stringify(templateBody), parameters: [], resourcesToImport, prefix: 'import', }); this.info(`Creating import changeset for ${displayName} (${resourcesToImport.length} resource(s))`, resource); const templateUrl = await this.uploadTemplate(JSON.stringify(templateBody), stackName, 'import'); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.CreateChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName, ChangeSetType: 'IMPORT', TemplateURL: templateUrl, ResourcesToImport: resourcesToImport, Capabilities: [CFN_IAM_CAPABILITY], })); this.info(`Waiting for import changeset creation: ${displayName}`, resource); await (0, client_cloudformation_1.waitUntilChangeSetCreateComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: 120 }, { StackName: stackName, ChangeSetName: changeSetName }); this.info(`Executing import changeset: ${displayName}`, resource); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.ExecuteChangeSetCommand({ StackName: stackName, ChangeSetName: changeSetName })); this.info(`Waiting for import to complete: ${displayName}`, resource); await (0, client_cloudformation_1.waitUntilStackImportComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: MAX_WAIT_TIME_SECONDS }, { StackName: stackName }); this.info(`Import complete: ${displayName}`, resource); } async deleteChangeSet(changeSet) { await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.DeleteChangeSetCommand({ StackName: changeSet.StackName, ChangeSetName: changeSet.ChangeSetName })); } async findStack(stackName) { var _a, _b; try { const response = await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.DescribeStacksCommand({ StackName: stackName })); const stack = (_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0]; if (stack && stack.StackStatus !== 'DELETE_COMPLETE') { return stack; } return null; } catch (error) { if (error instanceof client_cloudformation_1.CloudFormationServiceException && error.name === 'ValidationError' && ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('does not exist'))) { return null; } throw error; } } async describeStack(stackName) { const stack = await this.findStack(stackName); if (!stack) { throw new amplify_cli_core_1.AmplifyError('StackNotFoundError', { message: `Stack '${(0, utils_1.extractStackNameFromId)(stackName)}' does not exist`, }); } return stack; } async fetchTemplate(stackName) { const response = await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.GetTemplateCommand({ StackName: stackName, TemplateStage: 'Original' })); if (!response.TemplateBody) { throw new amplify_cli_core_1.AmplifyError('InvalidStackError', { message: `Stack '${(0, utils_1.extractStackNameFromId)(stackName)}' returned an empty template`, }); } return JSON.parse(response.TemplateBody); } async deleteStack(stackName, resource) { var _a; try { this.info(`Deleting stack: ${(0, utils_1.extractStackNameFromId)(stackName)}`, resource); await this.gen1App.clients.cloudFormation.send(new client_cloudformation_1.DeleteStackCommand({ StackName: stackName })); this.info(`Waiting for stack deletion: ${(0, utils_1.extractStackNameFromId)(stackName)}`, resource); await (0, client_cloudformation_1.waitUntilStackDeleteComplete)({ client: this.gen1App.clients.cloudFormation, maxWaitTime: 300 }, { StackName: stackName }); } catch (error) { if (error instanceof client_cloudformation_1.CloudFormationServiceException && error.name === 'ValidationError' && ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('does not exist'))) { return; } throw error; } } renderChangeSet(changeSet) { var _a, _b, _c, _d, _e; const changes = (_a = changeSet.Changes) !== null && _a !== void 0 ? _a : []; if (changes.length === 0) return 'No changes'; const colorAction = (action) => { if (action === 'Add') return chalk_1.default.green(action); if (action === 'Remove') return chalk_1.default.red(action); return chalk_1.default.yellow(action); }; const lines = []; if (changeSet.ChangeSetId) { const consoleUrl = (0, drift_formatter_1.cfnChangesetConsoleUrl)(changeSet.ChangeSetId, changeSet.StackId); if (consoleUrl) { lines.push(chalk_1.default.dim(consoleUrl)); } } for (const change of changes) { const rc = change.ResourceChange; if (!rc) continue; const action = (_b = rc.Action) !== null && _b !== void 0 ? _b : 'Unknown'; const logicalId = (_c = rc.LogicalResourceId) !== null && _c !== void 0 ? _c : 'Unknown'; const resourceType = (_d = rc.ResourceType) !== null && _d !== void 0 ? _d : 'Unknown'; const replacement = rc.Replacement; const displayAction = action === 'Modify' && replacement === 'True' ? 'Replace' : action === 'Modify' && replacement === 'Conditional' ? 'Replace (conditional)' : action; const isReplace = displayAction === 'Replace' || displayAction === 'Replace (conditional)'; const header = isReplace ? chalk_1.default.bold.red(`${logicalId} (${resourceType}) ${displayAction}`) : [chalk_1.default.bold(logicalId), chalk_1.default.dim(`(${resourceType})`), colorAction(displayAction)].join(' '); lines.push(''); lines.push(header); const details = ((_e = rc.Details) !== null && _e !== void 0 ? _e : []).filter((d) => { var _a; return !!((_a = d.Target) === null || _a === void 0 ? void 0 : _a.Attribute); }); const paths = details.map((d) => { const { Attribute: attribute, Path: targetPath, Name: targetName } = d.Target; if (targetPath) return targetPath; if (attribute === 'Properties') return targetName ? `/Properties/${targetName}` : '/Properties'; return `/${attribute}`; }); const pathWidth = Math.max(0, ...paths.map((p) => p.length)); details.forEach((detail, i) => { const path = paths[i]; const before = detail.Target.BeforeValue; const after = detail.Target.AfterValue; const paddedPath = path.padEnd(pathWidth); if (before && after) { lines.push(` ${paddedPath} ${chalk_1.default.red(`(-) ${before}`)} ${chalk_1.default.dim('→')} ${chalk_1.default.green(`(+) ${after}`)}`); } else if (after) { lines.push(` ${paddedPath} ${chalk_1.default.green(`(+) ${after}`)}`); } else if (before) { lines.push(` ${paddedPath} ${chalk_1.default.red(`(-) ${before}`)}`); } else { lines.push(` ${paddedPath} ${chalk_1.default.dim('(changed)')}`); } }); } return lines.join('\n').trimStart(); } async uploadTemplate(templateBody, stackNameOrArn, operation) { const key = `gen2-migration/${operation}.${(0, utils_1.extractStackNameFromId)(stackNameOrArn)}.${Date.now()}.template.json`; await this.gen1App.clients.s3.send(new client_s3_1.PutObjectCommand({ Bucket: this.gen1App.deploymentBucket, Key: key, Body: templateBody, ContentType: 'application/json', })); const region = await this.gen1App.clients.s3.config.region(); return `https://s3.${region}.amazonaws.com/${this.gen1App.deploymentBucket}/${key}`; } info(message, resource) { const prefix = resource ? `[${resource.category}/${resource.resourceName}] ` : ''; this.logger.info(`${prefix}${message}`); } } exports.Cfn = Cfn; function buildRefactorDescription(input) { const logicalIds = input.ResourceMappings.map((m) => { var _a; return (_a = m.Source) === null || _a === void 0 ? void 0 : _a.LogicalResourceId; }).join(', '); const source = (0, utils_1.extractStackNameFromId)(input.StackDefinitions[0].StackName); const dest = (0, utils_1.extractStackNameFromId)(input.StackDefinitions[1].StackName); return `Move [${logicalIds}] from ${source} to ${dest}`; } function formatTemplateBody(templateBody) { return JSON.stringify(JSON.parse(templateBody), null, 2) + '\n'; } function writeImportSnapshot(input) { const stackName = (0, utils_1.extractStackNameFromId)(input.stackName); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.template.json`), formatTemplateBody(input.templateBody)); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.parameters.json`), JSON.stringify(input.parameters, null, 2) + '\n'); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.resources.json`), JSON.stringify(input.resourcesToImport, null, 2) + '\n'); } function writeUpdateSnapshot(input) { const stackName = (0, utils_1.extractStackNameFromId)(input.stackName); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.template.json`), formatTemplateBody(input.templateBody)); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.parameters.json`), JSON.stringify(input.parameters, null, 2) + '\n'); } function writeOrphanSnapshot(input) { const stackName = (0, utils_1.extractStackNameFromId)(input.stackName); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${input.prefix}.${stackName}.logicalIds.json`), JSON.stringify(input.logicalIds, null, 2) + '\n'); } function writeRefactorSnapshot(input, sourceTemplateBody, targetTemplateBody) { var _a; const source = input.StackDefinitions[0]; const target = input.StackDefinitions[1]; const sourceStackName = (0, utils_1.extractStackNameFromId)(source.StackName); const targetStackName = (0, utils_1.extractStackNameFromId)(target.StackName); const description = `refactor.__from__.${sourceStackName}.__to__.${targetStackName}`; const basePath = path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, description); writeRefactorSnapshotFile(`${basePath}.source.template.json`, formatTemplateBody(sourceTemplateBody)); writeRefactorSnapshotFile(`${basePath}.target.template.json`, formatTemplateBody(targetTemplateBody)); writeRefactorSnapshotFile(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, `${description}.mappings.json`), JSON.stringify((_a = input.ResourceMappings) !== null && _a !== void 0 ? _a : [], null, 2) + '\n'); } const FILENAME_MAPPING_FILE = 'filename-mapping.json'; function writeRefactorSnapshotFile(filename, content) { const hash = crypto.createHash('sha256').update(filename).digest('hex').slice(0, 10); const hashedFilename = `${hash}.json`; fs.writeFileSync(path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, hashedFilename), content); const mappingPath = path.join(exports.REFACTOR_SNAPSHOT_OUTPUT_DIRECTORY, FILENAME_MAPPING_FILE); const mapping = fs.existsSync(mappingPath) ? JSON.parse(fs.readFileSync(mappingPath, 'utf-8')) : {}; mapping[hash] = path.basename(filename); fs.writeFileSync(mappingPath, JSON.stringify(mapping, null, 2) + '\n'); } //# sourceMappingURL=cfn.js.map