@remotion/studio-server 4.0.470 → 4.0.472

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/codemods/add-effect.d.ts +26 -0
  2. package/dist/codemods/add-effect.js +193 -0
  3. package/dist/codemods/format-file-content.js +1 -1
  4. package/dist/codemods/parse-ast.js +4 -1
  5. package/dist/codemods/paste-effects.d.ts +15 -0
  6. package/dist/codemods/paste-effects.js +234 -0
  7. package/dist/codemods/recast-mods.js +178 -31
  8. package/dist/codemods/reorder-effect.d.ts +13 -0
  9. package/dist/codemods/reorder-effect.js +61 -0
  10. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.d.ts +1 -1
  11. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.js +7 -55
  12. package/dist/codemods/update-keyframes/update-keyframes.d.ts +15 -6
  13. package/dist/codemods/update-keyframes/update-keyframes.js +152 -26
  14. package/dist/helpers/get-ast-node-path.js +6 -1
  15. package/dist/helpers/import-agnostic-node-path.d.ts +10 -0
  16. package/dist/helpers/import-agnostic-node-path.js +154 -0
  17. package/dist/helpers/imports.d.ts +16 -0
  18. package/dist/helpers/imports.js +145 -0
  19. package/dist/helpers/open-in-editor.d.ts +2 -2
  20. package/dist/helpers/open-in-editor.js +35 -2
  21. package/dist/helpers/resolve-composition-component.d.ts +15 -0
  22. package/dist/helpers/resolve-composition-component.js +332 -31
  23. package/dist/preview-server/api-routes.js +10 -4
  24. package/dist/preview-server/routes/add-effect-keyframe.js +4 -2
  25. package/dist/preview-server/routes/add-effect.d.ts +3 -0
  26. package/dist/preview-server/routes/add-effect.js +68 -0
  27. package/dist/preview-server/routes/add-sequence-keyframe.js +7 -3
  28. package/dist/preview-server/routes/apply-codemod.js +18 -0
  29. package/dist/preview-server/routes/can-update-effect-props.d.ts +3 -2
  30. package/dist/preview-server/routes/can-update-effect-props.js +75 -6
  31. package/dist/preview-server/routes/can-update-sequence-props.d.ts +1 -0
  32. package/dist/preview-server/routes/can-update-sequence-props.js +47 -30
  33. package/dist/preview-server/routes/delete-keyframes.d.ts +13 -0
  34. package/dist/preview-server/routes/delete-keyframes.js +264 -0
  35. package/dist/preview-server/routes/insert-jsx-element.d.ts +3 -0
  36. package/dist/preview-server/routes/insert-jsx-element.js +102 -0
  37. package/dist/preview-server/routes/paste-effects.d.ts +3 -0
  38. package/dist/preview-server/routes/paste-effects.js +78 -0
  39. package/dist/preview-server/routes/reorder-effect.d.ts +3 -0
  40. package/dist/preview-server/routes/reorder-effect.js +67 -0
  41. package/dist/preview-server/routes/save-effect-props.js +1 -0
  42. package/dist/preview-server/routes/subscribe-to-sequence-props.js +2 -1
  43. package/dist/preview-server/sequence-props-watchers.d.ts +2 -1
  44. package/dist/preview-server/sequence-props-watchers.js +22 -2
  45. package/dist/preview-server/undo-stack.d.ts +15 -1
  46. package/package.json +6 -6
@@ -6,6 +6,10 @@ const parse_ast_1 = require("../../codemods/parse-ast");
6
6
  const update_effect_props_1 = require("../../codemods/update-effect-props/update-effect-props");
7
7
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
8
8
  const can_update_sequence_props_1 = require("./can-update-sequence-props");
9
+ const staticStatus = (codeValue) => ({
10
+ status: 'static',
11
+ codeValue,
12
+ });
9
13
  const findEffectsAttr = (jsx) => {
10
14
  for (const attr of jsx.attributes) {
11
15
  if (attr.type !== 'JSXAttribute') {
@@ -27,6 +31,63 @@ const getEffectsArrayElements = (attr) => {
27
31
  }
28
32
  return (0, update_effect_props_1.enumerateEffectArrayElements)(expr);
29
33
  };
34
+ const getImportedName = (specifier) => {
35
+ var _a, _b;
36
+ if (!specifier.imported) {
37
+ return null;
38
+ }
39
+ if (specifier.imported.type === 'Identifier') {
40
+ return (_a = specifier.imported.name) !== null && _a !== void 0 ? _a : null;
41
+ }
42
+ return (_b = specifier.imported.value) !== null && _b !== void 0 ? _b : null;
43
+ };
44
+ const resolveEffectImport = ({ ast, call, fallbackCallee, }) => {
45
+ var _a, _b;
46
+ var _c;
47
+ const { callee } = call;
48
+ if (callee.type === 'Identifier') {
49
+ const localName = callee.name;
50
+ for (const node of ast.program.body) {
51
+ if (node.type !== 'ImportDeclaration') {
52
+ continue;
53
+ }
54
+ const matchingSpecifier = (_a = node.specifiers) === null || _a === void 0 ? void 0 : _a.find((specifier) => {
55
+ var _a;
56
+ return (specifier.type === 'ImportSpecifier' &&
57
+ ((_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) === localName);
58
+ });
59
+ if ((matchingSpecifier === null || matchingSpecifier === void 0 ? void 0 : matchingSpecifier.type) === 'ImportSpecifier') {
60
+ return {
61
+ callee: (_c = getImportedName(matchingSpecifier)) !== null && _c !== void 0 ? _c : fallbackCallee,
62
+ importPath: String(node.source.value),
63
+ };
64
+ }
65
+ }
66
+ }
67
+ if (callee.type === 'MemberExpression' &&
68
+ callee.object.type === 'Identifier' &&
69
+ callee.property.type === 'Identifier' &&
70
+ !callee.computed) {
71
+ const namespaceName = callee.object.name;
72
+ for (const node of ast.program.body) {
73
+ if (node.type !== 'ImportDeclaration') {
74
+ continue;
75
+ }
76
+ const matchingSpecifier = (_b = node.specifiers) === null || _b === void 0 ? void 0 : _b.find((specifier) => {
77
+ var _a;
78
+ return (specifier.type === 'ImportNamespaceSpecifier' &&
79
+ ((_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) === namespaceName);
80
+ });
81
+ if (matchingSpecifier) {
82
+ return {
83
+ callee: callee.property.name,
84
+ importPath: String(node.source.value),
85
+ };
86
+ }
87
+ }
88
+ }
89
+ return { callee: fallbackCallee, importPath: null };
90
+ };
30
91
  const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
31
92
  const out = {};
32
93
  for (const key of keys) {
@@ -35,7 +96,7 @@ const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
35
96
  (p.key.type === 'StringLiteral' &&
36
97
  p.key.value === key)));
37
98
  if (!prop) {
38
- out[key] = { canUpdate: true, codeValue: undefined };
99
+ out[key] = staticStatus(undefined);
39
100
  continue;
40
101
  }
41
102
  const valueExpr = prop.value;
@@ -44,13 +105,13 @@ const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
44
105
  continue;
45
106
  }
46
107
  out[key] = {
47
- canUpdate: true,
108
+ status: 'static',
48
109
  codeValue: (0, can_update_sequence_props_1.extractStaticValue)(valueExpr),
49
110
  };
50
111
  }
51
112
  return out;
52
113
  };
53
- const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
114
+ const computeEffectPropStatus = ({ ast, jsx, effectIndex, keys, }) => {
54
115
  const attr = findEffectsAttr(jsx);
55
116
  const elements = getEffectsArrayElements(attr);
56
117
  if (!elements) {
@@ -76,14 +137,20 @@ const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
76
137
  };
77
138
  }
78
139
  const call = target.node;
140
+ const effectImport = resolveEffectImport({
141
+ ast,
142
+ call,
143
+ fallbackCallee: target.callee,
144
+ });
79
145
  if (call.arguments.length === 0) {
80
146
  const emptyProps = {};
81
147
  for (const key of keys) {
82
- emptyProps[key] = { canUpdate: true, codeValue: undefined };
148
+ emptyProps[key] = staticStatus(undefined);
83
149
  }
84
150
  return {
85
151
  canUpdate: true,
86
- callee: target.callee,
152
+ callee: effectImport.callee,
153
+ importPath: effectImport.importPath,
87
154
  effectIndex,
88
155
  props: emptyProps,
89
156
  };
@@ -103,7 +170,8 @@ const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
103
170
  return {
104
171
  canUpdate: true,
105
172
  effectIndex,
106
- callee: target.callee,
173
+ callee: effectImport.callee,
174
+ importPath: effectImport.importPath,
107
175
  props: resolvedProps,
108
176
  };
109
177
  };
@@ -119,6 +187,7 @@ const computeEffectPropsStatusesFromContent = ({ fileContents, sequenceNodePath,
119
187
  }));
120
188
  }
121
189
  return effects.map((effect, effectIndex) => (0, exports.computeEffectPropStatus)({
190
+ ast,
122
191
  jsx,
123
192
  effectIndex,
124
193
  keys: keysFor(effect),
@@ -5,6 +5,7 @@ export declare const isStaticValue: (node: Expression) => boolean;
5
5
  export declare const extractStaticValue: (node: Expression) => unknown;
6
6
  export declare const getComputedStatus: (node: Expression) => CanUpdateSequencePropStatus;
7
7
  export declare const findJsxElementAtNodePath: (ast: File, nodePath: SequenceNodePath) => JSXOpeningElement | null;
8
+ export declare const findNodePathForJsxElement: (ast: File, target: JSXOpeningElement) => SequenceNodePath | null;
8
9
  export declare const lineColumnToNodePath: (ast: File, targetLine: number) => SequenceNodePath | null;
9
10
  export declare const computeSequencePropsStatusFromContent: ({ fileContents, nodePath, keys, effects, }: {
10
11
  fileContents: string;
@@ -33,15 +33,24 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
36
+ exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = exports.findNodePathForJsxElement = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
37
37
  const node_fs_1 = require("node:fs");
38
38
  const renderer_1 = require("@remotion/renderer");
39
+ const studio_shared_1 = require("@remotion/studio-shared");
39
40
  const recast = __importStar(require("recast"));
40
41
  const parse_ast_1 = require("../../codemods/parse-ast");
41
42
  const get_ast_node_path_1 = require("../../helpers/get-ast-node-path");
43
+ const import_agnostic_node_path_1 = require("../../helpers/import-agnostic-node-path");
42
44
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
43
45
  const jsx_element_not_found_at_location_error_1 = require("../jsx-element-not-found-at-location-error");
44
46
  const can_update_effect_props_1 = require("./can-update-effect-props");
47
+ const staticStatus = (codeValue) => ({
48
+ status: 'static',
49
+ codeValue,
50
+ });
51
+ const computedStatus = () => ({
52
+ status: 'computed',
53
+ });
45
54
  const isStaticValue = (node) => {
46
55
  switch (node.type) {
47
56
  case 'NumericLiteral':
@@ -305,8 +314,7 @@ const getInterpolationKeyframes = (node) => {
305
314
  }
306
315
  const callExpression = node;
307
316
  if (callExpression.callee.type !== 'Identifier' ||
308
- (callExpression.callee.name !== 'interpolate' &&
309
- callExpression.callee.name !== 'interpolateColors')) {
317
+ !(0, studio_shared_1.isKeyframeInterpolationFunction)(callExpression.callee.name)) {
310
318
  return undefined;
311
319
  }
312
320
  const interpolationFunction = callExpression.callee.name;
@@ -358,11 +366,11 @@ const getInterpolationKeyframes = (node) => {
358
366
  const getComputedStatus = (node) => {
359
367
  const interpolation = getInterpolationKeyframes(node);
360
368
  if (!interpolation) {
361
- return { canUpdate: false, reason: 'computed' };
369
+ return computedStatus();
362
370
  }
363
371
  return {
364
- canUpdate: false,
365
- reason: 'keyframed',
372
+ status: 'keyframed',
373
+ codeValue: undefined,
366
374
  interpolationFunction: interpolation.interpolationFunction,
367
375
  keyframes: interpolation.keyframes,
368
376
  easing: interpolation.easing,
@@ -386,39 +394,33 @@ const getPropsStatus = (jsxElement) => {
386
394
  }
387
395
  const { value } = attr;
388
396
  if (!value) {
389
- props[name] = { canUpdate: true, codeValue: true };
397
+ props[name] = staticStatus(true);
390
398
  continue;
391
399
  }
392
400
  if (value.type === 'StringLiteral') {
393
- props[name] = {
394
- canUpdate: true,
395
- codeValue: value.value,
396
- };
401
+ props[name] = staticStatus(value.value);
397
402
  continue;
398
403
  }
399
404
  if (value.type === 'JSXExpressionContainer') {
400
405
  const { expression } = value;
401
406
  if (expression.type === 'JSXEmptyExpression') {
402
- props[name] = { canUpdate: false, reason: 'computed' };
407
+ props[name] = computedStatus();
403
408
  continue;
404
409
  }
405
410
  if (!(0, exports.isStaticValue)(expression)) {
406
411
  props[name] = (0, exports.getComputedStatus)(expression);
407
412
  continue;
408
413
  }
409
- props[name] = {
410
- canUpdate: true,
411
- codeValue: (0, exports.extractStaticValue)(expression),
412
- };
414
+ props[name] = staticStatus((0, exports.extractStaticValue)(expression));
413
415
  continue;
414
416
  }
415
- props[name] = { canUpdate: false, reason: 'computed' };
417
+ props[name] = computedStatus();
416
418
  }
417
419
  return props;
418
420
  };
419
421
  const getNodePathForRecastPath = (
420
422
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
421
- recastPath) => {
423
+ recastPath, ast) => {
422
424
  const segments = [];
423
425
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
424
426
  let current = recastPath;
@@ -428,9 +430,9 @@ recastPath) => {
428
430
  }
429
431
  // Recast paths start with "root" which doesn't correspond to a real AST property
430
432
  if (segments.length > 0 && segments[0] === 'root') {
431
- return segments.slice(1);
433
+ return (0, import_agnostic_node_path_1.toImportAgnosticNodePath)({ ast, nodePath: segments.slice(1) });
432
434
  }
433
- return segments;
435
+ return (0, import_agnostic_node_path_1.toImportAgnosticNodePath)({ ast, nodePath: segments });
434
436
  };
435
437
  const findJsxElementAtNodePath = (ast, nodePath) => {
436
438
  const current = (0, get_ast_node_path_1.getAstNodePath)(ast, nodePath);
@@ -443,13 +445,27 @@ const findJsxElementAtNodePath = (ast, nodePath) => {
443
445
  return null;
444
446
  };
445
447
  exports.findJsxElementAtNodePath = findJsxElementAtNodePath;
448
+ const findNodePathForJsxElement = (ast, target) => {
449
+ let foundPath = null;
450
+ recast.types.visit(ast, {
451
+ visitJSXOpeningElement(p) {
452
+ if (p.node === target) {
453
+ foundPath = getNodePathForRecastPath(p, ast);
454
+ return false;
455
+ }
456
+ return this.traverse(p);
457
+ },
458
+ });
459
+ return foundPath;
460
+ };
461
+ exports.findNodePathForJsxElement = findNodePathForJsxElement;
446
462
  const lineColumnToNodePath = (ast, targetLine) => {
447
463
  let foundPath = null;
448
464
  recast.types.visit(ast, {
449
465
  visitJSXOpeningElement(p) {
450
466
  const { node } = p;
451
467
  if (node.loc && node.loc.start.line === targetLine) {
452
- foundPath = getNodePathForRecastPath(p);
468
+ foundPath = getNodePathForRecastPath(p, ast);
453
469
  return false;
454
470
  }
455
471
  return this.traverse(p);
@@ -478,16 +494,16 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
478
494
  a.name.name === parentKey);
479
495
  if (!attr || !attr.value) {
480
496
  // Parent attribute doesn't exist, nested prop can be added
481
- return { canUpdate: true, codeValue: undefined };
497
+ return staticStatus(undefined);
482
498
  }
483
499
  if (attr.value.type !== 'JSXExpressionContainer') {
484
- return { canUpdate: false, reason: 'computed' };
500
+ return computedStatus();
485
501
  }
486
502
  const { expression } = attr.value;
487
503
  if (expression.type === 'JSXEmptyExpression' ||
488
504
  expression.type !== 'ObjectExpression') {
489
505
  // Parent is not an object literal (e.g. style={myStyles})
490
- return { canUpdate: false, reason: 'computed' };
506
+ return computedStatus();
491
507
  }
492
508
  const objExpr = expression;
493
509
  const prop = objExpr.properties.find((p) => p.type === 'ObjectProperty' &&
@@ -495,7 +511,7 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
495
511
  (p.key.type === 'StringLiteral' && p.key.value === childKey)));
496
512
  if (!prop) {
497
513
  // Property not set in the object, can be added
498
- return { canUpdate: true, codeValue: undefined };
514
+ return staticStatus(undefined);
499
515
  }
500
516
  const propValue = prop.value;
501
517
  if (!(0, exports.isStaticValue)(propValue)) {
@@ -503,12 +519,13 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
503
519
  }
504
520
  const codeValue = (0, exports.extractStaticValue)(propValue);
505
521
  if (!validateStyleValue(childKey, codeValue)) {
506
- return { canUpdate: false, reason: 'computed' };
522
+ return computedStatus();
507
523
  }
508
- return { canUpdate: true, codeValue };
524
+ return staticStatus(codeValue);
509
525
  };
510
- const computeEffectsForJsx = ({ jsxElement, effects, }) => {
526
+ const computeEffectsForJsx = ({ ast, jsxElement, effects, }) => {
511
527
  return effects.map((effect, effectIndex) => (0, can_update_effect_props_1.computeEffectPropStatus)({
528
+ ast,
512
529
  jsx: jsxElement,
513
530
  effectIndex,
514
531
  keys: effect,
@@ -526,7 +543,7 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, keys, }) => {
526
543
  filteredProps[key] = allProps[key];
527
544
  }
528
545
  else {
529
- filteredProps[key] = { canUpdate: true, codeValue: undefined };
546
+ filteredProps[key] = staticStatus(undefined);
530
547
  }
531
548
  }
532
549
  return filteredProps;
@@ -538,7 +555,7 @@ const computeSequencePropsStatusFromContent = ({ fileContents, nodePath, keys, e
538
555
  throw new jsx_element_not_found_at_location_error_1.JsxElementNotFoundAtLocationError();
539
556
  }
540
557
  const filteredProps = computeSequenceOnlyPropsRecord({ jsxElement, keys });
541
- const effectsStatuses = computeEffectsForJsx({ jsxElement, effects });
558
+ const effectsStatuses = computeEffectsForJsx({ ast, jsxElement, effects });
542
559
  return {
543
560
  canUpdate: true,
544
561
  props: filteredProps,
@@ -0,0 +1,13 @@
1
+ import type { DeleteEffectKeyframe, DeleteKeyframesRequest, DeleteKeyframesResponse, DeleteSequenceKeyframe, SaveSequencePropsResult } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const deleteKeyframes: ({ sequenceKeyframes, effectKeyframes, clientId, remotionRoot, logLevel, }: {
4
+ sequenceKeyframes: DeleteSequenceKeyframe[];
5
+ effectKeyframes: DeleteEffectKeyframe[];
6
+ clientId: string;
7
+ remotionRoot: string;
8
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
9
+ }) => Promise<{
10
+ sequenceResults: SaveSequencePropsResult[];
11
+ effectResults: import("remotion").CanUpdateEffectPropsResponse[];
12
+ }>;
13
+ export declare const deleteKeyframesHandler: ApiHandler<DeleteKeyframesRequest, DeleteKeyframesResponse>;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteKeyframesHandler = exports.deleteKeyframes = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const renderer_1 = require("@remotion/renderer");
6
+ const studio_shared_1 = require("@remotion/studio-shared");
7
+ const parse_ast_1 = require("../../codemods/parse-ast");
8
+ const update_keyframes_1 = require("../../codemods/update-keyframes/update-keyframes");
9
+ const file_watcher_1 = require("../../file-watcher");
10
+ const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
11
+ const undo_stack_1 = require("../undo-stack");
12
+ const watch_ignore_next_change_1 = require("../watch-ignore-next-change");
13
+ const can_update_effect_props_1 = require("./can-update-effect-props");
14
+ const can_update_sequence_props_1 = require("./can-update-sequence-props");
15
+ const log_effect_update_1 = require("./log-updates/log-effect-update");
16
+ const log_update_1 = require("./log-updates/log-update");
17
+ const save_props_mutex_1 = require("./save-props-mutex");
18
+ const groupBy = (items, getKey) => {
19
+ var _a;
20
+ const groups = new Map();
21
+ for (const item of items) {
22
+ const key = getKey(item);
23
+ const group = (_a = groups.get(key)) !== null && _a !== void 0 ? _a : [];
24
+ group.push(item);
25
+ groups.set(key, group);
26
+ }
27
+ return [...groups.values()];
28
+ };
29
+ const getBatchDescription = ({ totalKeyframes, firstKeyframe, }) => {
30
+ if (totalKeyframes === 1) {
31
+ return {
32
+ undoMessage: `↩️ ${firstKeyframe.key} keyframe restored at frame ${firstKeyframe.frame}`,
33
+ redoMessage: `↪️ ${firstKeyframe.key} keyframe deleted at frame ${firstKeyframe.frame}`,
34
+ };
35
+ }
36
+ return {
37
+ undoMessage: `↩️ ${totalKeyframes} keyframes restored`,
38
+ redoMessage: `↪️ ${totalKeyframes} keyframes deleted`,
39
+ };
40
+ };
41
+ const deleteKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remotionRoot, logLevel, }) => {
42
+ var _a, _b;
43
+ const totalKeyframes = sequenceKeyframes.length + effectKeyframes.length;
44
+ if (totalKeyframes === 0) {
45
+ throw new Error('No keyframes were specified for deletion');
46
+ }
47
+ const fileGroups = new Map();
48
+ const resolvedSequenceKeyframes = [];
49
+ const resolvedEffectKeyframes = [];
50
+ for (const [index, keyframe] of sequenceKeyframes.entries()) {
51
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
52
+ remotionRoot,
53
+ fileName: keyframe.fileName,
54
+ action: 'modify',
55
+ });
56
+ const group = (_a = fileGroups.get(absolutePath)) !== null && _a !== void 0 ? _a : {
57
+ fileRelativeToRoot,
58
+ sequenceKeyframes: [],
59
+ effectKeyframes: [],
60
+ };
61
+ const resolved = { ...keyframe, index, absolutePath, fileRelativeToRoot };
62
+ group.sequenceKeyframes.push(resolved);
63
+ resolvedSequenceKeyframes.push(resolved);
64
+ fileGroups.set(absolutePath, group);
65
+ }
66
+ for (const [index, keyframe] of effectKeyframes.entries()) {
67
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
68
+ remotionRoot,
69
+ fileName: keyframe.fileName,
70
+ action: 'modify',
71
+ });
72
+ const group = (_b = fileGroups.get(absolutePath)) !== null && _b !== void 0 ? _b : {
73
+ fileRelativeToRoot,
74
+ sequenceKeyframes: [],
75
+ effectKeyframes: [],
76
+ };
77
+ const resolved = { ...keyframe, index, absolutePath, fileRelativeToRoot };
78
+ group.effectKeyframes.push(resolved);
79
+ resolvedEffectKeyframes.push(resolved);
80
+ fileGroups.set(absolutePath, group);
81
+ }
82
+ const snapshots = [];
83
+ const outputByPath = new Map();
84
+ const sequenceLogs = [];
85
+ const effectLogs = [];
86
+ for (const [absolutePath, group] of fileGroups) {
87
+ const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
88
+ let output = fileContents;
89
+ let firstLogLine = Number.POSITIVE_INFINITY;
90
+ for (const keyframeGroup of groupBy(group.sequenceKeyframes, (keyframe) => JSON.stringify(keyframe.nodePath.nodePath))) {
91
+ const [firstSequenceKeyframe] = keyframeGroup;
92
+ if (!firstSequenceKeyframe) {
93
+ continue;
94
+ }
95
+ const result = await (0, update_keyframes_1.updateSequenceKeyframes)({
96
+ input: output,
97
+ nodePath: firstSequenceKeyframe.nodePath.nodePath,
98
+ updates: keyframeGroup.map((keyframe) => ({
99
+ key: keyframe.key,
100
+ operation: {
101
+ type: 'remove',
102
+ frame: keyframe.frame,
103
+ },
104
+ })),
105
+ });
106
+ output = result.output;
107
+ firstLogLine = Math.min(firstLogLine, result.logLine);
108
+ for (const [keyframeIndex, keyframe] of keyframeGroup.entries()) {
109
+ sequenceLogs.push({
110
+ fileRelativeToRoot: keyframe.fileRelativeToRoot,
111
+ line: result.logLine,
112
+ key: keyframe.key,
113
+ oldValueString: result.oldValueStrings[keyframeIndex],
114
+ newValueString: result.newValueStrings[keyframeIndex],
115
+ formatted: result.formatted,
116
+ });
117
+ }
118
+ }
119
+ for (const keyframeGroup of groupBy(group.effectKeyframes, (keyframe) => `${JSON.stringify(keyframe.sequenceNodePath.nodePath)}:${keyframe.effectIndex}`)) {
120
+ const [firstEffectKeyframe] = keyframeGroup;
121
+ if (!firstEffectKeyframe) {
122
+ continue;
123
+ }
124
+ const result = await (0, update_keyframes_1.updateEffectKeyframes)({
125
+ input: output,
126
+ sequenceNodePath: firstEffectKeyframe.sequenceNodePath.nodePath,
127
+ effectIndex: firstEffectKeyframe.effectIndex,
128
+ updates: keyframeGroup.map((keyframe) => ({
129
+ key: keyframe.key,
130
+ operation: {
131
+ type: 'remove',
132
+ frame: keyframe.frame,
133
+ },
134
+ })),
135
+ });
136
+ output = result.output;
137
+ firstLogLine = Math.min(firstLogLine, result.logLine);
138
+ for (const [keyframeIndex, keyframe] of keyframeGroup.entries()) {
139
+ effectLogs.push({
140
+ fileRelativeToRoot: keyframe.fileRelativeToRoot,
141
+ line: result.logLine,
142
+ effectName: result.effectCallee,
143
+ propKey: keyframe.key,
144
+ oldValueString: result.oldValueStrings[keyframeIndex],
145
+ newValueString: result.newValueStrings[keyframeIndex],
146
+ formatted: result.formatted,
147
+ });
148
+ }
149
+ }
150
+ snapshots.push({
151
+ filePath: absolutePath,
152
+ oldContents: fileContents,
153
+ newContents: output,
154
+ logLine: Number.isFinite(firstLogLine) ? firstLogLine : 1,
155
+ });
156
+ outputByPath.set(absolutePath, output);
157
+ }
158
+ const [firstKeyframe] = sequenceKeyframes.length > 0 ? sequenceKeyframes : effectKeyframes;
159
+ if (!firstKeyframe) {
160
+ throw new Error('No keyframes were specified for deletion');
161
+ }
162
+ (0, undo_stack_1.pushTransactionToUndoStack)({
163
+ snapshots,
164
+ logLevel,
165
+ remotionRoot,
166
+ description: getBatchDescription({ totalKeyframes, firstKeyframe }),
167
+ entryType: sequenceKeyframes.length > 0 && effectKeyframes.length > 0
168
+ ? 'keyframe-delete'
169
+ : sequenceKeyframes.length > 0
170
+ ? 'sequence-props'
171
+ : 'effect-props',
172
+ suppressHmrOnFileRestore: true,
173
+ });
174
+ for (const snapshot of snapshots) {
175
+ (0, undo_stack_1.suppressUndoStackInvalidation)(snapshot.filePath);
176
+ (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(snapshot.filePath);
177
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)(snapshot.filePath, snapshot.newContents, clientId);
178
+ }
179
+ for (const log of sequenceLogs) {
180
+ (0, log_update_1.logUpdate)({
181
+ fileRelativeToRoot: log.fileRelativeToRoot,
182
+ line: log.line,
183
+ key: log.key,
184
+ oldValueString: log.oldValueString,
185
+ newValueString: log.newValueString,
186
+ defaultValueString: null,
187
+ formatted: log.formatted,
188
+ logLevel,
189
+ removedProps: [],
190
+ addedProps: [],
191
+ });
192
+ }
193
+ for (const log of effectLogs) {
194
+ (0, log_effect_update_1.logEffectUpdate)({
195
+ fileRelativeToRoot: log.fileRelativeToRoot,
196
+ line: log.line,
197
+ effectName: log.effectName,
198
+ propKey: log.propKey,
199
+ oldValueString: log.oldValueString,
200
+ newValueString: log.newValueString,
201
+ defaultValueString: null,
202
+ formatted: log.formatted,
203
+ logLevel,
204
+ removedProps: [],
205
+ addedProps: [],
206
+ });
207
+ }
208
+ (0, undo_stack_1.printUndoHint)(logLevel);
209
+ const sequenceResults = resolvedSequenceKeyframes.map((keyframe) => {
210
+ const output = outputByPath.get(keyframe.absolutePath);
211
+ if (!output) {
212
+ throw new Error('Could not compute sequence keyframe deletion status');
213
+ }
214
+ const status = (0, can_update_sequence_props_1.computeSequencePropsStatusFromContent)({
215
+ fileContents: output,
216
+ keys: (0, studio_shared_1.getAllSchemaKeys)(keyframe.schema),
217
+ nodePath: keyframe.nodePath.nodePath,
218
+ effects: [],
219
+ });
220
+ return {
221
+ fileName: keyframe.fileName,
222
+ nodePath: keyframe.nodePath,
223
+ props: status.props,
224
+ };
225
+ });
226
+ const astByPath = new Map();
227
+ const effectResults = resolvedEffectKeyframes.map((keyframe) => {
228
+ var _a;
229
+ const output = outputByPath.get(keyframe.absolutePath);
230
+ if (!output) {
231
+ throw new Error('Could not compute effect keyframe deletion status');
232
+ }
233
+ const ast = (_a = astByPath.get(keyframe.absolutePath)) !== null && _a !== void 0 ? _a : (0, parse_ast_1.parseAst)(output);
234
+ astByPath.set(keyframe.absolutePath, ast);
235
+ const jsx = (0, can_update_sequence_props_1.findJsxElementAtNodePath)(ast, keyframe.sequenceNodePath.nodePath);
236
+ if (!jsx) {
237
+ return {
238
+ canUpdate: false,
239
+ effectIndex: keyframe.effectIndex,
240
+ reason: 'not-found',
241
+ };
242
+ }
243
+ return (0, can_update_effect_props_1.computeEffectPropStatus)({
244
+ ast,
245
+ jsx,
246
+ effectIndex: keyframe.effectIndex,
247
+ keys: (0, studio_shared_1.getAllSchemaKeys)(keyframe.schema),
248
+ });
249
+ });
250
+ return { sequenceResults, effectResults };
251
+ };
252
+ exports.deleteKeyframes = deleteKeyframes;
253
+ const deleteKeyframesHandler = ({ input: { sequenceKeyframes, effectKeyframes, clientId }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
254
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-keyframes] Received request to delete ${sequenceKeyframes.length + effectKeyframes.length} keyframe(s)`);
255
+ await (0, exports.deleteKeyframes)({
256
+ sequenceKeyframes,
257
+ effectKeyframes,
258
+ clientId,
259
+ remotionRoot,
260
+ logLevel,
261
+ });
262
+ return { success: true };
263
+ });
264
+ exports.deleteKeyframesHandler = deleteKeyframesHandler;
@@ -0,0 +1,3 @@
1
+ import type { InsertJsxElementRequest, InsertJsxElementResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const insertJsxElementHandler: ApiHandler<InsertJsxElementRequest, InsertJsxElementResponse>;