@remotion/studio-server 4.0.468 → 4.0.470

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 (49) hide show
  1. package/dist/codemods/delete-effect.d.ts +18 -0
  2. package/dist/codemods/delete-effect.js +95 -21
  3. package/dist/codemods/delete-jsx-node.d.ts +10 -0
  4. package/dist/codemods/delete-jsx-node.js +38 -14
  5. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.d.ts +4 -0
  6. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.js +152 -0
  7. package/dist/codemods/update-keyframes/update-keyframes.d.ts +8 -0
  8. package/dist/codemods/update-keyframes/update-keyframes.js +98 -31
  9. package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +32 -10
  10. package/dist/codemods/update-sequence-props/update-sequence-props.js +39 -7
  11. package/dist/index.d.ts +2 -1
  12. package/dist/preview-server/api-routes.js +16 -0
  13. package/dist/preview-server/routes/add-effect-keyframe.d.ts +3 -0
  14. package/dist/preview-server/routes/add-effect-keyframe.js +91 -0
  15. package/dist/preview-server/routes/add-sequence-keyframe.d.ts +3 -0
  16. package/dist/preview-server/routes/add-sequence-keyframe.js +84 -0
  17. package/dist/preview-server/routes/apply-codemod.js +1 -0
  18. package/dist/preview-server/routes/apply-visual-control-change.js +1 -0
  19. package/dist/preview-server/routes/can-update-sequence-props.d.ts +0 -9
  20. package/dist/preview-server/routes/can-update-sequence-props.js +192 -24
  21. package/dist/preview-server/routes/composition-component-info.d.ts +3 -0
  22. package/dist/preview-server/routes/composition-component-info.js +26 -0
  23. package/dist/preview-server/routes/delete-effect-keyframe.d.ts +3 -0
  24. package/dist/preview-server/routes/delete-effect-keyframe.js +89 -0
  25. package/dist/preview-server/routes/delete-effect.js +76 -37
  26. package/dist/preview-server/routes/delete-jsx-node.js +67 -36
  27. package/dist/preview-server/routes/delete-sequence-keyframe.d.ts +3 -0
  28. package/dist/preview-server/routes/delete-sequence-keyframe.js +82 -0
  29. package/dist/preview-server/routes/duplicate-jsx-node.js +1 -0
  30. package/dist/preview-server/routes/open-in-editor.d.ts +4 -0
  31. package/dist/preview-server/routes/open-in-editor.js +40 -0
  32. package/dist/preview-server/routes/register-client-render.d.ts +3 -0
  33. package/dist/preview-server/routes/register-client-render.js +11 -0
  34. package/dist/preview-server/routes/save-effect-props.js +1 -0
  35. package/dist/preview-server/routes/save-sequence-props.js +161 -72
  36. package/dist/preview-server/routes/unregister-client-render.d.ts +4 -0
  37. package/dist/preview-server/routes/unregister-client-render.js +11 -0
  38. package/dist/preview-server/routes/update-default-props.js +1 -0
  39. package/dist/preview-server/start-server.d.ts +1 -0
  40. package/dist/preview-server/start-server.js +1 -0
  41. package/dist/preview-server/undo-stack.d.ts +26 -3
  42. package/dist/preview-server/undo-stack.js +130 -42
  43. package/dist/preview-server/validate-same-origin.d.ts +2 -0
  44. package/dist/preview-server/validate-same-origin.js +13 -0
  45. package/dist/routes.d.ts +2 -1
  46. package/dist/routes.js +9 -136
  47. package/dist/start-studio.d.ts +2 -1
  48. package/dist/start-studio.js +2 -1
  49. package/package.json +6 -6
@@ -111,6 +111,7 @@ const applyVisualControlHandler = async ({ input: { fileName, changes }, remotio
111
111
  (0, undo_stack_1.pushToUndoStack)({
112
112
  filePath: absolutePath,
113
113
  oldContents: fileContents,
114
+ newContents: null,
114
115
  logLevel,
115
116
  remotionRoot,
116
117
  logLine,
@@ -6,15 +6,6 @@ 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
8
  export declare const lineColumnToNodePath: (ast: File, targetLine: number) => SequenceNodePath | null;
9
- export declare const computeSequencePropsOnlyStatus: ({ fileName, nodePath, keys, remotionRoot, }: {
10
- fileName: string;
11
- nodePath: SequenceNodePath;
12
- keys: string[];
13
- remotionRoot: string;
14
- }) => {
15
- canUpdate: true;
16
- props: Record<string, CanUpdateSequencePropStatus>;
17
- };
18
9
  export declare const computeSequencePropsStatusFromContent: ({ fileContents, nodePath, keys, effects, }: {
19
10
  fileContents: string;
20
11
  nodePath: SequenceNodePath;
@@ -33,7 +33,7 @@ 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.computeSequencePropsOnlyStatus = exports.lineColumnToNodePath = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
36
+ exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = 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
39
  const recast = __importStar(require("recast"));
@@ -128,6 +128,174 @@ const getNumericValue = (node) => {
128
128
  }
129
129
  return null;
130
130
  };
131
+ const getExtrapolateType = (node) => {
132
+ if (node.type === 'StringLiteral') {
133
+ if (node.value === 'extend' ||
134
+ node.value === 'identity' ||
135
+ node.value === 'clamp' ||
136
+ node.value === 'wrap') {
137
+ return node.value;
138
+ }
139
+ return null;
140
+ }
141
+ if (node.type === 'TSAsExpression') {
142
+ return getExtrapolateType(node.expression);
143
+ }
144
+ return null;
145
+ };
146
+ const getKeyframeEasing = (node) => {
147
+ if (node.type === 'TSAsExpression') {
148
+ return getKeyframeEasing(node.expression);
149
+ }
150
+ if (node.type === 'MemberExpression' &&
151
+ node.object.type === 'Identifier' &&
152
+ node.object.name === 'Easing' &&
153
+ node.property.type === 'Identifier' &&
154
+ node.property.name === 'linear' &&
155
+ node.computed === false) {
156
+ return 'linear';
157
+ }
158
+ if (node.type !== 'CallExpression' ||
159
+ node.callee.type !== 'MemberExpression' ||
160
+ node.callee.object.type !== 'Identifier' ||
161
+ node.callee.object.name !== 'Easing' ||
162
+ node.callee.property.type !== 'Identifier' ||
163
+ node.callee.property.name !== 'bezier' ||
164
+ node.callee.computed) {
165
+ return null;
166
+ }
167
+ if (node.arguments.length !== 4) {
168
+ return null;
169
+ }
170
+ const values = node.arguments.map((arg) => {
171
+ if (arg.type === 'ArgumentPlaceholder' ||
172
+ arg.type === 'JSXNamespacedName' ||
173
+ arg.type === 'SpreadElement') {
174
+ return null;
175
+ }
176
+ return getNumericValue(arg);
177
+ });
178
+ if (values.some((v) => v === null)) {
179
+ return null;
180
+ }
181
+ return values;
182
+ };
183
+ const getKeyframeEasingArray = ({ easingNode, segments, }) => {
184
+ if (segments === 0) {
185
+ return [];
186
+ }
187
+ if (easingNode.type === 'TSAsExpression') {
188
+ return getKeyframeEasingArray({
189
+ easingNode: easingNode.expression,
190
+ segments,
191
+ });
192
+ }
193
+ if (easingNode.type === 'ArrayExpression') {
194
+ if (easingNode.elements.length !== segments) {
195
+ return null;
196
+ }
197
+ const parsed = easingNode.elements.map((element) => {
198
+ if (!element || element.type === 'SpreadElement') {
199
+ return null;
200
+ }
201
+ return getKeyframeEasing(element);
202
+ });
203
+ if (parsed.some((value) => value === null)) {
204
+ return null;
205
+ }
206
+ return parsed;
207
+ }
208
+ const easing = getKeyframeEasing(easingNode);
209
+ if (!easing) {
210
+ return null;
211
+ }
212
+ return new Array(segments).fill(easing);
213
+ };
214
+ const getInterpolationMetadata = (interpolationFunction, callExpression, keyframeCount) => {
215
+ const segments = Math.max(0, keyframeCount - 1);
216
+ const defaultClamping = interpolationFunction === 'interpolateColors'
217
+ ? {
218
+ left: 'clamp',
219
+ right: 'clamp',
220
+ }
221
+ : {
222
+ left: 'extend',
223
+ right: 'extend',
224
+ };
225
+ const defaults = {
226
+ easing: new Array(segments).fill('linear'),
227
+ clamping: defaultClamping,
228
+ posterize: undefined,
229
+ };
230
+ const optionsArg = callExpression.arguments[3];
231
+ if (!optionsArg) {
232
+ return defaults;
233
+ }
234
+ if (optionsArg.type !== 'ObjectExpression') {
235
+ return null;
236
+ }
237
+ let { easing } = defaults;
238
+ let { clamping } = defaults;
239
+ let posterize;
240
+ for (const property of optionsArg.properties) {
241
+ if (property.type !== 'ObjectProperty' || property.computed) {
242
+ return null;
243
+ }
244
+ const key = property.key.type === 'Identifier'
245
+ ? property.key.name
246
+ : property.key.type === 'StringLiteral'
247
+ ? property.key.value
248
+ : null;
249
+ if (!key) {
250
+ return null;
251
+ }
252
+ const value = property.value;
253
+ if (key === 'easing') {
254
+ if (interpolationFunction === 'interpolateColors') {
255
+ return null;
256
+ }
257
+ const parsedEasing = getKeyframeEasingArray({
258
+ easingNode: value,
259
+ segments,
260
+ });
261
+ if (!parsedEasing) {
262
+ return null;
263
+ }
264
+ easing = parsedEasing;
265
+ continue;
266
+ }
267
+ if (key === 'extrapolateLeft' || key === 'extrapolateRight') {
268
+ if (interpolationFunction === 'interpolateColors') {
269
+ return null;
270
+ }
271
+ const extrapolateType = getExtrapolateType(value);
272
+ if (!extrapolateType) {
273
+ return null;
274
+ }
275
+ clamping =
276
+ key === 'extrapolateLeft'
277
+ ? { ...clamping, left: extrapolateType }
278
+ : { ...clamping, right: extrapolateType };
279
+ continue;
280
+ }
281
+ if (key === 'posterize') {
282
+ const parsedPosterize = getNumericValue(value);
283
+ if (parsedPosterize === null ||
284
+ !Number.isFinite(parsedPosterize) ||
285
+ parsedPosterize <= 0) {
286
+ return null;
287
+ }
288
+ posterize = parsedPosterize;
289
+ continue;
290
+ }
291
+ return null;
292
+ }
293
+ return {
294
+ easing,
295
+ clamping,
296
+ posterize,
297
+ };
298
+ };
131
299
  const getInterpolationKeyframes = (node) => {
132
300
  if (node.type === 'TSAsExpression') {
133
301
  return getInterpolationKeyframes(node.expression);
@@ -141,6 +309,7 @@ const getInterpolationKeyframes = (node) => {
141
309
  callExpression.callee.name !== 'interpolateColors')) {
142
310
  return undefined;
143
311
  }
312
+ const interpolationFunction = callExpression.callee.name;
144
313
  const inputArg = callExpression.arguments[1];
145
314
  const outputArg = callExpression.arguments[2];
146
315
  if (!inputArg ||
@@ -171,17 +340,34 @@ const getInterpolationKeyframes = (node) => {
171
340
  value: (0, exports.extractStaticValue)(outputElement),
172
341
  });
173
342
  }
174
- return keyframes.length > 0 ? keyframes : undefined;
343
+ if (keyframes.length === 0) {
344
+ return undefined;
345
+ }
346
+ const metadata = getInterpolationMetadata(interpolationFunction, callExpression, keyframes.length);
347
+ if (!metadata) {
348
+ return undefined;
349
+ }
350
+ return {
351
+ interpolationFunction,
352
+ keyframes,
353
+ easing: metadata.easing,
354
+ clamping: metadata.clamping,
355
+ posterize: metadata.posterize,
356
+ };
175
357
  };
176
358
  const getComputedStatus = (node) => {
177
- const keyframes = getInterpolationKeyframes(node);
178
- if (!keyframes) {
359
+ const interpolation = getInterpolationKeyframes(node);
360
+ if (!interpolation) {
179
361
  return { canUpdate: false, reason: 'computed' };
180
362
  }
181
363
  return {
182
364
  canUpdate: false,
183
- reason: 'computed',
184
- keyframes,
365
+ reason: 'keyframed',
366
+ interpolationFunction: interpolation.interpolationFunction,
367
+ keyframes: interpolation.keyframes,
368
+ easing: interpolation.easing,
369
+ clamping: interpolation.clamping,
370
+ posterize: interpolation.posterize,
185
371
  };
186
372
  };
187
373
  exports.getComputedStatus = getComputedStatus;
@@ -345,24 +531,6 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, keys, }) => {
345
531
  }
346
532
  return filteredProps;
347
533
  };
348
- const computeSequencePropsOnlyStatus = ({ fileName, nodePath, keys, remotionRoot, }) => {
349
- const { absolutePath } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
350
- remotionRoot,
351
- fileName,
352
- action: 'read',
353
- });
354
- const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
355
- const ast = (0, parse_ast_1.parseAst)(fileContents);
356
- const jsxElement = (0, exports.findJsxElementAtNodePath)(ast, nodePath);
357
- if (!jsxElement) {
358
- throw new Error('Cannot compute sequence props: Could not find a JSX element at the specified location');
359
- }
360
- return {
361
- canUpdate: true,
362
- props: computeSequenceOnlyPropsRecord({ jsxElement, keys }),
363
- };
364
- };
365
- exports.computeSequencePropsOnlyStatus = computeSequencePropsOnlyStatus;
366
534
  const computeSequencePropsStatusFromContent = ({ fileContents, nodePath, keys, effects, }) => {
367
535
  const ast = (0, parse_ast_1.parseAst)(fileContents);
368
536
  const jsxElement = (0, exports.findJsxElementAtNodePath)(ast, nodePath);
@@ -0,0 +1,3 @@
1
+ import type { CompositionComponentInfoRequest, CompositionComponentInfoResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const compositionComponentInfoHandler: ApiHandler<CompositionComponentInfoRequest, CompositionComponentInfoResponse>;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compositionComponentInfoHandler = void 0;
4
+ const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
5
+ const compositionComponentInfoHandler = async ({ input: { compositionFile, compositionId }, remotionRoot }) => {
6
+ if (typeof compositionFile !== 'string') {
7
+ throw new TypeError('Need to pass compositionFile');
8
+ }
9
+ if (typeof compositionId !== 'string') {
10
+ throw new TypeError('Need to pass compositionId');
11
+ }
12
+ const location = await (0, resolve_composition_component_1.resolveCompositionComponent)({
13
+ remotionRoot,
14
+ compositionFile,
15
+ compositionId,
16
+ });
17
+ return {
18
+ location: {
19
+ source: location.source,
20
+ line: location.line,
21
+ column: location.column,
22
+ },
23
+ canAddSequence: location.canAddSequence,
24
+ };
25
+ };
26
+ exports.compositionComponentInfoHandler = compositionComponentInfoHandler;
@@ -0,0 +1,3 @@
1
+ import type { DeleteEffectKeyframeRequest, DeleteEffectKeyframeResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const deleteEffectKeyframeHandler: ApiHandler<DeleteEffectKeyframeRequest, DeleteEffectKeyframeResponse>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteEffectKeyframeHandler = 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 save_props_mutex_1 = require("./save-props-mutex");
17
+ const deleteEffectKeyframeHandler = ({ input: { fileName, sequenceNodePath, effectIndex, key, frame, schema, clientId, }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
18
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-effect-keyframe] Received request for fileName="${fileName}" effectIndex=${effectIndex} key="${key}" frame=${frame}`);
19
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
20
+ remotionRoot,
21
+ fileName,
22
+ action: 'modify',
23
+ });
24
+ const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
25
+ const { output, oldValueStrings, newValueStrings, formatted, logLine, effectCallee, } = await (0, update_keyframes_1.updateEffectKeyframes)({
26
+ input: fileContents,
27
+ sequenceNodePath: sequenceNodePath.nodePath,
28
+ effectIndex,
29
+ updates: [
30
+ {
31
+ key,
32
+ operation: {
33
+ type: 'remove',
34
+ frame,
35
+ },
36
+ },
37
+ ],
38
+ });
39
+ const oldValueString = oldValueStrings[0];
40
+ const newValueString = newValueStrings[0];
41
+ const undoPropChange = `${key} keyframe restored at frame ${frame}`;
42
+ const redoPropChange = `${key} keyframe deleted at frame ${frame}`;
43
+ (0, undo_stack_1.pushToUndoStack)({
44
+ filePath: absolutePath,
45
+ oldContents: fileContents,
46
+ newContents: null,
47
+ logLevel,
48
+ remotionRoot,
49
+ logLine,
50
+ description: {
51
+ undoMessage: `↩️ ${undoPropChange}`,
52
+ redoMessage: `↪️ ${redoPropChange}`,
53
+ },
54
+ entryType: 'effect-props',
55
+ suppressHmrOnFileRestore: true,
56
+ });
57
+ (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
58
+ (0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(absolutePath);
59
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
60
+ (0, log_effect_update_1.logEffectUpdate)({
61
+ fileRelativeToRoot,
62
+ line: logLine,
63
+ effectName: effectCallee,
64
+ propKey: key,
65
+ oldValueString,
66
+ newValueString,
67
+ defaultValueString: null,
68
+ formatted,
69
+ logLevel,
70
+ removedProps: [],
71
+ addedProps: [],
72
+ });
73
+ (0, undo_stack_1.printUndoHint)(logLevel);
74
+ const ast = (0, parse_ast_1.parseAst)((0, node_fs_1.readFileSync)(absolutePath, 'utf-8'));
75
+ const jsx = (0, can_update_sequence_props_1.findJsxElementAtNodePath)(ast, sequenceNodePath.nodePath);
76
+ if (!jsx) {
77
+ return {
78
+ canUpdate: false,
79
+ effectIndex,
80
+ reason: 'not-found',
81
+ };
82
+ }
83
+ return (0, can_update_effect_props_1.computeEffectPropStatus)({
84
+ jsx,
85
+ effectIndex,
86
+ keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
87
+ });
88
+ });
89
+ exports.deleteEffectKeyframeHandler = deleteEffectKeyframeHandler;
@@ -10,45 +10,84 @@ const format_log_file_location_1 = require("../format-log-file-location");
10
10
  const undo_stack_1 = require("../undo-stack");
11
11
  const formatting_1 = require("./log-updates/formatting");
12
12
  const log_update_1 = require("./log-updates/log-update");
13
- const deleteEffectHandler = async ({ input: { fileName, sequenceNodePath, effectIndex }, remotionRoot, logLevel, }) => {
13
+ const getDeletedEffectDescription = (effectLabels) => {
14
+ if (effectLabels.length === 1) {
15
+ return effectLabels[0];
16
+ }
17
+ return `${effectLabels.length} effects`;
18
+ };
19
+ const deleteEffectHandler = async ({ input: effects, remotionRoot, logLevel }) => {
20
+ var _a;
14
21
  try {
15
- renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-effect] Received request for fileName="${fileName}" effectIndex=${effectIndex}`);
16
- const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
17
- remotionRoot,
18
- fileName,
19
- action: 'modify',
20
- });
21
- const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
22
- const { output, formatted, effectLabel, logLine } = await (0, delete_effect_1.deleteEffect)({
23
- input: fileContents,
24
- sequenceNodePath: sequenceNodePath.nodePath,
25
- effectIndex,
26
- });
27
- (0, undo_stack_1.pushToUndoStack)({
28
- filePath: absolutePath,
29
- oldContents: fileContents,
30
- logLevel,
31
- remotionRoot,
32
- logLine,
33
- description: {
34
- undoMessage: `↩️ Deletion of ${effectLabel}`,
35
- redoMessage: `↪️ Deletion of ${effectLabel}`,
36
- },
37
- entryType: 'delete-effect',
38
- suppressHmrOnFileRestore: false,
39
- });
40
- (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
41
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, undefined);
42
- const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
43
- remotionRoot,
44
- absolutePath,
45
- line: logLine,
46
- });
47
- renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} ${(0, formatting_1.strikeThroughOrRemovedPrefix)(effectLabel)}`);
48
- if (!formatted) {
49
- (0, log_update_1.warnAboutPrettierOnce)(logLevel);
22
+ if (effects.length === 0) {
23
+ throw new Error('No effects were specified for deletion');
24
+ }
25
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-effect] Received request to delete ${effects.length} effect target${effects.length === 1 ? '' : 's'}`);
26
+ const itemsByFileName = new Map();
27
+ for (const item of effects) {
28
+ const fileItems = (_a = itemsByFileName.get(item.fileName)) !== null && _a !== void 0 ? _a : [];
29
+ fileItems.push(item);
30
+ itemsByFileName.set(item.fileName, fileItems);
31
+ }
32
+ const updates = await Promise.all([...itemsByFileName.entries()].map(async ([fileName, fileItems]) => {
33
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
34
+ remotionRoot,
35
+ fileName,
36
+ action: 'modify',
37
+ });
38
+ const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
39
+ const { output, formatted, effectLabels, logLines } = await (0, delete_effect_1.deleteEffects)({
40
+ input: fileContents,
41
+ effects: fileItems.map((item) => item.type === 'single-effect'
42
+ ? {
43
+ type: 'single-effect',
44
+ sequenceNodePath: item.sequenceNodePath.nodePath,
45
+ effectIndex: item.effectIndex,
46
+ }
47
+ : {
48
+ type: 'all-effects',
49
+ sequenceNodePath: item.sequenceNodePath.nodePath,
50
+ }),
51
+ });
52
+ return {
53
+ absolutePath,
54
+ fileRelativeToRoot,
55
+ fileContents,
56
+ output,
57
+ formatted,
58
+ effectLabels,
59
+ logLine: Math.min(...logLines),
60
+ };
61
+ }));
62
+ for (const update of updates) {
63
+ const deletedEffectDescription = getDeletedEffectDescription(update.effectLabels);
64
+ (0, undo_stack_1.pushToUndoStack)({
65
+ filePath: update.absolutePath,
66
+ oldContents: update.fileContents,
67
+ newContents: null,
68
+ logLevel,
69
+ remotionRoot,
70
+ logLine: update.logLine,
71
+ description: {
72
+ undoMessage: `↩️ Deletion of ${deletedEffectDescription}`,
73
+ redoMessage: `↪️ Deletion of ${deletedEffectDescription}`,
74
+ },
75
+ entryType: 'delete-effect',
76
+ suppressHmrOnFileRestore: false,
77
+ });
78
+ (0, undo_stack_1.suppressUndoStackInvalidation)(update.absolutePath);
79
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)(update.absolutePath, update.output, undefined);
80
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
81
+ remotionRoot,
82
+ absolutePath: update.absolutePath,
83
+ line: update.logLine,
84
+ });
85
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} ${(0, formatting_1.strikeThroughOrRemovedPrefix)(deletedEffectDescription)}`);
86
+ if (!update.formatted) {
87
+ (0, log_update_1.warnAboutPrettierOnce)(logLevel);
88
+ }
89
+ renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[delete-effect] Wrote ${update.fileRelativeToRoot}${update.formatted ? ' (formatted)' : ''}`);
50
90
  }
51
- renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[delete-effect] Wrote ${fileRelativeToRoot}${formatted ? ' (formatted)' : ''}`);
52
91
  (0, undo_stack_1.printUndoHint)(logLevel);
53
92
  return {
54
93
  success: true,
@@ -9,44 +9,75 @@ const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside
9
9
  const format_log_file_location_1 = require("../format-log-file-location");
10
10
  const undo_stack_1 = require("../undo-stack");
11
11
  const log_update_1 = require("./log-updates/log-update");
12
- const deleteJsxNodeHandler = async ({ input: { fileName, nodePath }, remotionRoot, logLevel }) => {
12
+ const getDeletedNodeDescription = (nodeLabels) => {
13
+ if (nodeLabels.length === 1) {
14
+ return nodeLabels[0];
15
+ }
16
+ return `${nodeLabels.length} JSX nodes`;
17
+ };
18
+ const deleteJsxNodeHandler = async ({ input: { nodes }, remotionRoot, logLevel }) => {
19
+ var _a;
13
20
  try {
14
- renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-jsx-node] Received request for fileName="${fileName}"`);
15
- const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
16
- remotionRoot,
17
- fileName,
18
- action: 'modify',
19
- });
20
- const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
21
- const { output, formatted, nodeLabel, logLine } = await (0, delete_jsx_node_1.deleteJsxNode)({
22
- input: fileContents,
23
- nodePath,
24
- });
25
- (0, undo_stack_1.pushToUndoStack)({
26
- filePath: absolutePath,
27
- oldContents: fileContents,
28
- logLevel,
29
- remotionRoot,
30
- logLine,
31
- description: {
32
- undoMessage: `↩️ Deletion of ${nodeLabel}`,
33
- redoMessage: `↪️ Deletion of ${nodeLabel}`,
34
- },
35
- entryType: 'delete-jsx-node',
36
- suppressHmrOnFileRestore: false,
37
- });
38
- (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
39
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, undefined);
40
- const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
41
- remotionRoot,
42
- absolutePath,
43
- line: logLine,
44
- });
45
- renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Deleted ${nodeLabel}`);
46
- if (!formatted) {
47
- (0, log_update_1.warnAboutPrettierOnce)(logLevel);
21
+ if (nodes.length === 0) {
22
+ throw new Error('No JSX nodes were specified for deletion');
23
+ }
24
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[delete-jsx-node] Received request to delete ${nodes.length} JSX node${nodes.length === 1 ? '' : 's'}`);
25
+ const itemsByFileName = new Map();
26
+ for (const item of nodes) {
27
+ const fileItems = (_a = itemsByFileName.get(item.fileName)) !== null && _a !== void 0 ? _a : [];
28
+ fileItems.push(item);
29
+ itemsByFileName.set(item.fileName, fileItems);
30
+ }
31
+ const updates = await Promise.all([...itemsByFileName.entries()].map(async ([fileName, fileItems]) => {
32
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
33
+ remotionRoot,
34
+ fileName,
35
+ action: 'modify',
36
+ });
37
+ const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
38
+ const { output, formatted, nodeLabels, logLines } = await (0, delete_jsx_node_1.deleteJsxNodes)({
39
+ input: fileContents,
40
+ nodePaths: fileItems.map((item) => item.nodePath),
41
+ });
42
+ return {
43
+ absolutePath,
44
+ fileRelativeToRoot,
45
+ fileContents,
46
+ output,
47
+ formatted,
48
+ nodeLabels,
49
+ logLine: Math.min(...logLines),
50
+ };
51
+ }));
52
+ for (const update of updates) {
53
+ const deletedNodeDescription = getDeletedNodeDescription(update.nodeLabels);
54
+ (0, undo_stack_1.pushToUndoStack)({
55
+ filePath: update.absolutePath,
56
+ oldContents: update.fileContents,
57
+ newContents: null,
58
+ logLevel,
59
+ remotionRoot,
60
+ logLine: update.logLine,
61
+ description: {
62
+ undoMessage: `↩️ Deletion of ${deletedNodeDescription}`,
63
+ redoMessage: `↪️ Deletion of ${deletedNodeDescription}`,
64
+ },
65
+ entryType: 'delete-jsx-node',
66
+ suppressHmrOnFileRestore: false,
67
+ });
68
+ (0, undo_stack_1.suppressUndoStackInvalidation)(update.absolutePath);
69
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)(update.absolutePath, update.output, undefined);
70
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
71
+ remotionRoot,
72
+ absolutePath: update.absolutePath,
73
+ line: update.logLine,
74
+ });
75
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Deleted ${deletedNodeDescription}`);
76
+ if (!update.formatted) {
77
+ (0, log_update_1.warnAboutPrettierOnce)(logLevel);
78
+ }
79
+ renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[delete-jsx-node] Wrote ${update.fileRelativeToRoot}${update.formatted ? ' (formatted)' : ''}`);
48
80
  }
49
- renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[delete-jsx-node] Wrote ${fileRelativeToRoot}${formatted ? ' (formatted)' : ''}`);
50
81
  (0, undo_stack_1.printUndoHint)(logLevel);
51
82
  return {
52
83
  success: true,
@@ -0,0 +1,3 @@
1
+ import type { DeleteSequenceKeyframeRequest, DeleteSequenceKeyframeResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const deleteSequenceKeyframeHandler: ApiHandler<DeleteSequenceKeyframeRequest, DeleteSequenceKeyframeResponse>;