@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.
- package/dist/codemods/add-effect.d.ts +26 -0
- package/dist/codemods/add-effect.js +193 -0
- package/dist/codemods/format-file-content.js +1 -1
- package/dist/codemods/parse-ast.js +4 -1
- package/dist/codemods/paste-effects.d.ts +15 -0
- package/dist/codemods/paste-effects.js +234 -0
- package/dist/codemods/recast-mods.js +178 -31
- package/dist/codemods/reorder-effect.d.ts +13 -0
- package/dist/codemods/reorder-effect.js +61 -0
- package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.d.ts +1 -1
- package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.js +7 -55
- package/dist/codemods/update-keyframes/update-keyframes.d.ts +15 -6
- package/dist/codemods/update-keyframes/update-keyframes.js +152 -26
- package/dist/helpers/get-ast-node-path.js +6 -1
- package/dist/helpers/import-agnostic-node-path.d.ts +10 -0
- package/dist/helpers/import-agnostic-node-path.js +154 -0
- package/dist/helpers/imports.d.ts +16 -0
- package/dist/helpers/imports.js +145 -0
- package/dist/helpers/open-in-editor.d.ts +2 -2
- package/dist/helpers/open-in-editor.js +35 -2
- package/dist/helpers/resolve-composition-component.d.ts +15 -0
- package/dist/helpers/resolve-composition-component.js +332 -31
- package/dist/preview-server/api-routes.js +10 -4
- package/dist/preview-server/routes/add-effect-keyframe.js +4 -2
- package/dist/preview-server/routes/add-effect.d.ts +3 -0
- package/dist/preview-server/routes/add-effect.js +68 -0
- package/dist/preview-server/routes/add-sequence-keyframe.js +7 -3
- package/dist/preview-server/routes/apply-codemod.js +18 -0
- package/dist/preview-server/routes/can-update-effect-props.d.ts +3 -2
- package/dist/preview-server/routes/can-update-effect-props.js +75 -6
- package/dist/preview-server/routes/can-update-sequence-props.d.ts +1 -0
- package/dist/preview-server/routes/can-update-sequence-props.js +47 -30
- package/dist/preview-server/routes/delete-keyframes.d.ts +13 -0
- package/dist/preview-server/routes/delete-keyframes.js +264 -0
- package/dist/preview-server/routes/insert-jsx-element.d.ts +3 -0
- package/dist/preview-server/routes/insert-jsx-element.js +102 -0
- package/dist/preview-server/routes/paste-effects.d.ts +3 -0
- package/dist/preview-server/routes/paste-effects.js +78 -0
- package/dist/preview-server/routes/reorder-effect.d.ts +3 -0
- package/dist/preview-server/routes/reorder-effect.js +67 -0
- package/dist/preview-server/routes/save-effect-props.js +1 -0
- package/dist/preview-server/routes/subscribe-to-sequence-props.js +2 -1
- package/dist/preview-server/sequence-props-watchers.d.ts +2 -1
- package/dist/preview-server/sequence-props-watchers.js +22 -2
- package/dist/preview-server/undo-stack.d.ts +15 -1
- 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] =
|
|
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
|
-
|
|
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] =
|
|
148
|
+
emptyProps[key] = staticStatus(undefined);
|
|
83
149
|
}
|
|
84
150
|
return {
|
|
85
151
|
canUpdate: true,
|
|
86
|
-
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:
|
|
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
|
|
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
|
|
369
|
+
return computedStatus();
|
|
362
370
|
}
|
|
363
371
|
return {
|
|
364
|
-
|
|
365
|
-
|
|
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] =
|
|
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] =
|
|
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] =
|
|
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
|
|
497
|
+
return staticStatus(undefined);
|
|
482
498
|
}
|
|
483
499
|
if (attr.value.type !== 'JSXExpressionContainer') {
|
|
484
|
-
return
|
|
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
|
|
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
|
|
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
|
|
522
|
+
return computedStatus();
|
|
507
523
|
}
|
|
508
|
-
return
|
|
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] =
|
|
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;
|