@remotion/studio-server 4.0.473 → 4.0.475

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.
@@ -39,10 +39,7 @@ const imports_1 = require("../helpers/imports");
39
39
  const update_nested_prop_1 = require("./update-nested-prop");
40
40
  const b = recast.types.builders;
41
41
  const isNonLinearEasing = (easing) => easing !== 'linear';
42
- const keyframedParamNeedsEasingImport = (param) => {
43
- return (param.interpolationFunction !== 'interpolateColors' &&
44
- param.easing.some(isNonLinearEasing));
45
- };
42
+ const keyframedParamNeedsEasingImport = (param) => param.easing.some(isNonLinearEasing);
46
43
  const getRequiredRemotionImportsForEffectParams = (params) => {
47
44
  const requiredImports = new Set();
48
45
  for (const param of params) {
@@ -91,10 +88,10 @@ const makeKeyframedOptions = ({ param, remotionLocalNames, }) => {
91
88
  if (param.clamping.right !== 'extend') {
92
89
  properties.push(b.objectProperty(b.identifier('extrapolateRight'), b.stringLiteral(param.clamping.right)));
93
90
  }
94
- if (keyframedParamNeedsEasingImport(param)) {
95
- const easingLocalName = (_a = remotionLocalNames.Easing) !== null && _a !== void 0 ? _a : 'Easing';
96
- properties.push(b.objectProperty(b.identifier('easing'), b.arrayExpression(param.easing.map((easing) => makeEasingExpression({ easing, easingLocalName })))));
97
- }
91
+ }
92
+ if (keyframedParamNeedsEasingImport(param)) {
93
+ const easingLocalName = (_a = remotionLocalNames.Easing) !== null && _a !== void 0 ? _a : 'Easing';
94
+ properties.push(b.objectProperty(b.identifier('easing'), b.arrayExpression(param.easing.map((easing) => makeEasingExpression({ easing, easingLocalName })))));
98
95
  }
99
96
  if (param.posterize !== undefined) {
100
97
  properties.push(b.objectProperty(b.identifier('posterize'), (0, update_nested_prop_1.parseValueExpression)(param.posterize)));
@@ -1,5 +1,8 @@
1
1
  import { type KeyframeInterpolationFunction } from '@remotion/studio-shared';
2
- import type { ExtrapolateType, SequenceNodePath, SequenceSchema } from 'remotion';
2
+ import type { CanUpdateSequencePropStatus, ExtrapolateType, SequenceNodePath, SequenceSchema } from 'remotion';
3
+ type KeyframeEasing = Extract<CanUpdateSequencePropStatus, {
4
+ status: 'keyframed';
5
+ }>['easing'][number];
3
6
  export type KeyframeOperation = {
4
7
  type: 'add';
5
8
  frame: number;
@@ -14,6 +17,10 @@ export type KeyframeOperation = {
14
17
  right: ExtrapolateType;
15
18
  } | undefined;
16
19
  posterize: number | undefined;
20
+ } | {
21
+ type: 'easing';
22
+ segmentIndex: number;
23
+ easing: KeyframeEasing;
17
24
  } | {
18
25
  type: 'move';
19
26
  moves: {
@@ -32,6 +39,7 @@ export type EffectKeyframeUpdate = {
32
39
  export type IntroducedKeyframeIdentifiers = {
33
40
  calleeName: KeyframeInterpolationFunction | null;
34
41
  needsFrameHook: boolean;
42
+ needsEasingImport: boolean;
35
43
  };
36
44
  export declare const updateSequenceKeyframesAst: ({ input, nodePath, updates, schema, }: {
37
45
  input: string;
@@ -89,3 +97,4 @@ export declare const updateEffectKeyframes: ({ input, sequenceNodePath, effectIn
89
97
  effectCallee: string;
90
98
  updatedSequenceNodePath: SequenceNodePath;
91
99
  }>;
100
+ export {};
@@ -44,6 +44,62 @@ const update_effect_props_1 = require("../update-effect-props/update-effect-prop
44
44
  const update_nested_prop_1 = require("../update-nested-prop");
45
45
  const ensure_imports_and_frame_hook_1 = require("./ensure-imports-and-frame-hook");
46
46
  const b = recast.types.builders;
47
+ const getObjectPropertyNameFromUpdateKey = (key) => {
48
+ const dotIndex = key.indexOf('.');
49
+ return dotIndex === -1 ? key : key.slice(dotIndex + 1);
50
+ };
51
+ const lineStartsWithPropertyName = ({ line, propertyName, }) => {
52
+ const trimmed = line.trimStart();
53
+ return (trimmed.startsWith(`${propertyName}:`) ||
54
+ trimmed.startsWith(`'${propertyName}':`) ||
55
+ trimmed.startsWith(`"${propertyName}":`));
56
+ };
57
+ const getIndent = (line) => {
58
+ var _a;
59
+ var _b;
60
+ return (_b = (_a = line.match(/^[ \t]*/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : '';
61
+ };
62
+ const removeBlankLinesFromObjectsWithProperties = ({ input, propertyNames, }) => {
63
+ if (propertyNames.length === 0) {
64
+ return input;
65
+ }
66
+ const lines = input.split('\n');
67
+ const linesToRemove = new Set();
68
+ for (let i = 0; i < lines.length; i++) {
69
+ const propertyName = propertyNames.find((name) => lineStartsWithPropertyName({ line: lines[i], propertyName: name }));
70
+ if (!propertyName) {
71
+ continue;
72
+ }
73
+ const propertyIndentLength = getIndent(lines[i]).length;
74
+ let objectStart = i;
75
+ for (let j = i - 1; j >= 0; j--) {
76
+ const trimmed = lines[j].trim();
77
+ if (trimmed.endsWith('{') &&
78
+ getIndent(lines[j]).length < propertyIndentLength) {
79
+ objectStart = j;
80
+ break;
81
+ }
82
+ }
83
+ let objectEnd = i;
84
+ for (let j = i + 1; j < lines.length; j++) {
85
+ const trimmed = lines[j].trim();
86
+ if (trimmed.startsWith('}') &&
87
+ getIndent(lines[j]).length < propertyIndentLength) {
88
+ objectEnd = j;
89
+ break;
90
+ }
91
+ }
92
+ for (let j = objectStart + 1; j < objectEnd; j++) {
93
+ if (lines[j].trim() === '') {
94
+ linesToRemove.add(j);
95
+ }
96
+ }
97
+ }
98
+ if (linesToRemove.size === 0) {
99
+ return input;
100
+ }
101
+ return lines.filter((_, index) => !linesToRemove.has(index)).join('\n');
102
+ };
47
103
  const getSupportedCallArgument = (arg) => {
48
104
  if (arg === undefined ||
49
105
  arg.type === 'ArgumentPlaceholder' ||
@@ -168,6 +224,173 @@ const setOptionsProperty = ({ options, propertyName, value, }) => {
168
224
  }
169
225
  options.properties.push(b.objectProperty(b.identifier(propertyName), value));
170
226
  };
227
+ const isLinearEasing = (easing) => easing === 'linear';
228
+ const getKeyframeEasing = (node) => {
229
+ if (node.type === 'TSAsExpression') {
230
+ return getKeyframeEasing(node.expression);
231
+ }
232
+ if (node.type === 'MemberExpression' &&
233
+ node.object.type === 'Identifier' &&
234
+ node.object.name === 'Easing' &&
235
+ node.property.type === 'Identifier' &&
236
+ node.property.name === 'linear' &&
237
+ node.computed === false) {
238
+ return 'linear';
239
+ }
240
+ if (node.type !== 'CallExpression' ||
241
+ node.callee.type !== 'MemberExpression' ||
242
+ node.callee.object.type !== 'Identifier' ||
243
+ node.callee.object.name !== 'Easing' ||
244
+ node.callee.property.type !== 'Identifier' ||
245
+ node.callee.property.name !== 'bezier' ||
246
+ node.callee.computed ||
247
+ node.arguments.length !== 4) {
248
+ return null;
249
+ }
250
+ const values = node.arguments.map((arg) => {
251
+ if (arg.type === 'ArgumentPlaceholder' ||
252
+ arg.type === 'JSXNamespacedName' ||
253
+ arg.type === 'SpreadElement') {
254
+ return null;
255
+ }
256
+ return getNumericValue(arg);
257
+ });
258
+ if (values.some((value) => value === null)) {
259
+ return null;
260
+ }
261
+ return values;
262
+ };
263
+ const getKeyframeEasingArray = ({ easingNode, segmentCount, }) => {
264
+ if (segmentCount === 0) {
265
+ return [];
266
+ }
267
+ if (easingNode.type === 'TSAsExpression') {
268
+ return getKeyframeEasingArray({
269
+ easingNode: easingNode.expression,
270
+ segmentCount,
271
+ });
272
+ }
273
+ if (easingNode.type === 'ArrayExpression') {
274
+ if (easingNode.elements.length > segmentCount) {
275
+ return null;
276
+ }
277
+ const parsed = easingNode.elements.map((element) => {
278
+ if (!element || element.type === 'SpreadElement') {
279
+ return null;
280
+ }
281
+ return getKeyframeEasing(element);
282
+ });
283
+ if (parsed.some((value) => value === null)) {
284
+ return null;
285
+ }
286
+ const easingArray = parsed;
287
+ while (easingArray.length < segmentCount) {
288
+ easingArray.push('linear');
289
+ }
290
+ return easingArray;
291
+ }
292
+ const easing = getKeyframeEasing(easingNode);
293
+ if (!easing) {
294
+ return null;
295
+ }
296
+ return new Array(segmentCount).fill(easing);
297
+ };
298
+ const getExistingEasingArray = ({ options, segmentCount, }) => {
299
+ const { prop } = findObjectOptionProperty(options, 'easing');
300
+ if (!prop) {
301
+ return new Array(segmentCount).fill('linear');
302
+ }
303
+ const easing = getKeyframeEasingArray({
304
+ easingNode: prop.value,
305
+ segmentCount,
306
+ });
307
+ if (!easing) {
308
+ throw new Error('Cannot update easing: easing must be inline');
309
+ }
310
+ return easing;
311
+ };
312
+ const getExistingEasingArrayOrNull = ({ options, segmentCount, }) => {
313
+ const { prop } = findObjectOptionProperty(options, 'easing');
314
+ if (!prop) {
315
+ return null;
316
+ }
317
+ return getExistingEasingArray({ options, segmentCount });
318
+ };
319
+ const createEasingExpression = (easing) => {
320
+ if (easing === 'linear') {
321
+ return b.memberExpression(b.identifier('Easing'), b.identifier('linear'));
322
+ }
323
+ return b.callExpression(b.memberExpression(b.identifier('Easing'), b.identifier('bezier')), easing.map((value) => (0, update_nested_prop_1.parseValueExpression)(value)));
324
+ };
325
+ const createEasingArrayExpression = (easing) => b.arrayExpression(easing.map((easingValue) => createEasingExpression(easingValue)));
326
+ const setEasingOption = ({ options, easing, }) => {
327
+ const hasNonLinearEasing = easing.some((easingValue) => !isLinearEasing(easingValue));
328
+ setOptionsProperty({
329
+ options,
330
+ propertyName: 'easing',
331
+ value: hasNonLinearEasing ? createEasingArrayExpression(easing) : null,
332
+ });
333
+ return hasNonLinearEasing;
334
+ };
335
+ const getExtraArgsWithOptions = ({ extraArgs, options, }) => {
336
+ return options.properties.length === 0
337
+ ? extraArgs.slice(1)
338
+ : [options, ...extraArgs.slice(1)];
339
+ };
340
+ const getInlineOptionsFromExtraArgs = (extraArgs) => {
341
+ const existingOptions = extraArgs[0];
342
+ if (!existingOptions || existingOptions.type !== 'ObjectExpression') {
343
+ return null;
344
+ }
345
+ return existingOptions;
346
+ };
347
+ const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, nextSegmentCount, }) => {
348
+ const options = getInlineOptionsFromExtraArgs(extraArgs);
349
+ if (!options) {
350
+ return { extraArgs, needsEasingImport: false };
351
+ }
352
+ const easing = getExistingEasingArrayOrNull({
353
+ options,
354
+ segmentCount: previousSegmentCount,
355
+ });
356
+ if (easing === null) {
357
+ return { extraArgs, needsEasingImport: false };
358
+ }
359
+ while (easing.length < nextSegmentCount) {
360
+ easing.push('linear');
361
+ }
362
+ return {
363
+ extraArgs: getExtraArgsWithOptions({ extraArgs, options }),
364
+ needsEasingImport: setEasingOption({ options, easing }),
365
+ };
366
+ };
367
+ const normalizeEasingAfterRemovingKeyframe = ({ extraArgs, previousSegmentCount, nextSegmentCount, removedKeyframeIndex, }) => {
368
+ const options = getInlineOptionsFromExtraArgs(extraArgs);
369
+ if (!options) {
370
+ return { extraArgs, needsEasingImport: false };
371
+ }
372
+ const easing = getExistingEasingArrayOrNull({
373
+ options,
374
+ segmentCount: previousSegmentCount,
375
+ });
376
+ if (easing === null) {
377
+ return { extraArgs, needsEasingImport: false };
378
+ }
379
+ if (easing.length > 0) {
380
+ const easingIndexToRemove = removedKeyframeIndex === 0 ? 0 : removedKeyframeIndex - 1;
381
+ easing.splice(easingIndexToRemove, 1);
382
+ }
383
+ while (easing.length > nextSegmentCount) {
384
+ easing.pop();
385
+ }
386
+ while (easing.length < nextSegmentCount) {
387
+ easing.push('linear');
388
+ }
389
+ return {
390
+ extraArgs: getExtraArgsWithOptions({ extraArgs, options }),
391
+ needsEasingImport: setEasingOption({ options, easing }),
392
+ };
393
+ };
171
394
  const validatePosterize = (posterize) => {
172
395
  if (posterize === undefined) {
173
396
  return;
@@ -227,6 +450,46 @@ const updateKeyframeSettings = ({ expression, clamping, posterize, }) => {
227
450
  keyframes: existing.keyframes,
228
451
  });
229
452
  };
453
+ const updateKeyframeEasing = ({ expression, segmentIndex, easing, }) => {
454
+ const existing = getInterpolationExpression(expression);
455
+ if (!existing) {
456
+ throw new Error('Cannot update easing on non-keyframed value');
457
+ }
458
+ const segmentCount = Math.max(0, existing.keyframes.length - 1);
459
+ if (!Number.isInteger(segmentIndex) ||
460
+ segmentIndex < 0 ||
461
+ segmentIndex >= segmentCount) {
462
+ throw new Error('Cannot update easing: segment index out of range');
463
+ }
464
+ const extraArgs = [...existing.extraArgs];
465
+ const existingOptions = extraArgs[0];
466
+ if (existingOptions && existingOptions.type !== 'ObjectExpression') {
467
+ throw new Error('Cannot update easing: options must be inline');
468
+ }
469
+ const options = (existingOptions === null || existingOptions === void 0 ? void 0 : existingOptions.type) === 'ObjectExpression'
470
+ ? existingOptions
471
+ : createEmptyOptionsExpression();
472
+ const nextEasing = getExistingEasingArray({ options, segmentCount });
473
+ nextEasing[segmentIndex] = easing;
474
+ const hasNonLinearEasing = nextEasing.some((easingValue) => !isLinearEasing(easingValue));
475
+ setOptionsProperty({
476
+ options,
477
+ propertyName: 'easing',
478
+ value: hasNonLinearEasing ? createEasingArrayExpression(nextEasing) : null,
479
+ });
480
+ const nextExtraArgs = options.properties.length === 0
481
+ ? extraArgs.slice(1)
482
+ : [options, ...extraArgs.slice(1)];
483
+ return {
484
+ expression: createInterpolateExpression({
485
+ callee: existing.callee,
486
+ input: existing.input,
487
+ extraArgs: nextExtraArgs,
488
+ keyframes: existing.keyframes,
489
+ }),
490
+ needsEasingImport: hasNonLinearEasing,
491
+ };
492
+ };
230
493
  const createInterpolateExpression = ({ callee, input, extraArgs, keyframes, }) => {
231
494
  const sortedKeyframes = [...keyframes].sort((first, second) => first.frame - second.frame);
232
495
  return b.callExpression(callee, [
@@ -239,6 +502,7 @@ const createInterpolateExpression = ({ callee, input, extraArgs, keyframes, }) =
239
502
  const noIntroducedIdentifiers = {
240
503
  calleeName: null,
241
504
  needsFrameHook: false,
505
+ needsEasingImport: false,
242
506
  };
243
507
  const addKeyframe = ({ expression, key, frame, value, schema, }) => {
244
508
  if (!(0, studio_shared_1.isSchemaFieldKeyframable)({ schema, key })) {
@@ -261,11 +525,18 @@ const addKeyframe = ({ expression, key, frame, value, schema, }) => {
261
525
  : existing.keyframes.map((keyframe, index) => index === existingIndex
262
526
  ? { frame, output: newOutput, value }
263
527
  : keyframe);
528
+ const normalizedEasing = existingIndex === -1
529
+ ? normalizeEasingAfterAddingKeyframe({
530
+ extraArgs: existing.extraArgs,
531
+ previousSegmentCount: Math.max(existing.keyframes.length - 1, 0),
532
+ nextSegmentCount: Math.max(nextKeyframes.length - 1, 0),
533
+ })
534
+ : { extraArgs: existing.extraArgs, needsEasingImport: false };
264
535
  return {
265
536
  expression: createInterpolateExpression({
266
537
  callee: b.identifier(nextCalleeName),
267
538
  input: existing.input,
268
- extraArgs: existing.extraArgs,
539
+ extraArgs: normalizedEasing.extraArgs,
269
540
  keyframes: nextKeyframes,
270
541
  }),
271
542
  introduced: {
@@ -273,6 +544,7 @@ const addKeyframe = ({ expression, key, frame, value, schema, }) => {
273
544
  ? schemaCalleeName
274
545
  : null,
275
546
  needsFrameHook: false,
547
+ needsEasingImport: normalizedEasing.needsEasingImport,
276
548
  },
277
549
  };
278
550
  }
@@ -302,6 +574,7 @@ const addKeyframe = ({ expression, key, frame, value, schema, }) => {
302
574
  ? callee.name
303
575
  : null,
304
576
  needsFrameHook: true,
577
+ needsEasingImport: false,
305
578
  },
306
579
  };
307
580
  };
@@ -316,14 +589,29 @@ const removeKeyframe = ({ expression, frame, }) => {
316
589
  }
317
590
  const nextKeyframes = existing.keyframes.filter((_keyframe, index) => index !== keyframeIndex);
318
591
  if (nextKeyframes.length === 0) {
319
- return existing.keyframes[keyframeIndex].output;
592
+ return {
593
+ expression: existing.keyframes[keyframeIndex].output,
594
+ introduced: noIntroducedIdentifiers,
595
+ };
320
596
  }
321
- return createInterpolateExpression({
322
- callee: existing.callee,
323
- input: existing.input,
597
+ const normalizedEasing = normalizeEasingAfterRemovingKeyframe({
324
598
  extraArgs: existing.extraArgs,
325
- keyframes: nextKeyframes,
599
+ previousSegmentCount: Math.max(existing.keyframes.length - 1, 0),
600
+ nextSegmentCount: Math.max(nextKeyframes.length - 1, 0),
601
+ removedKeyframeIndex: keyframeIndex,
326
602
  });
603
+ return {
604
+ expression: createInterpolateExpression({
605
+ callee: existing.callee,
606
+ input: existing.input,
607
+ extraArgs: normalizedEasing.extraArgs,
608
+ keyframes: nextKeyframes,
609
+ }),
610
+ introduced: {
611
+ ...noIntroducedIdentifiers,
612
+ needsEasingImport: normalizedEasing.needsEasingImport,
613
+ },
614
+ };
327
615
  };
328
616
  const moveKeyframes = ({ expression, moves, }) => {
329
617
  var _a;
@@ -395,16 +683,27 @@ const applyKeyframeOperation = ({ expression, key, operation, schema, }) => {
395
683
  introduced: noIntroducedIdentifiers,
396
684
  };
397
685
  }
686
+ if (operation.type === 'easing') {
687
+ const updated = updateKeyframeEasing({
688
+ expression,
689
+ segmentIndex: operation.segmentIndex,
690
+ easing: operation.easing,
691
+ });
692
+ return {
693
+ expression: updated.expression,
694
+ introduced: {
695
+ ...noIntroducedIdentifiers,
696
+ needsEasingImport: updated.needsEasingImport,
697
+ },
698
+ };
699
+ }
398
700
  if (operation.type === 'move') {
399
701
  return {
400
702
  expression: moveKeyframes({ expression, moves: operation.moves }),
401
703
  introduced: noIntroducedIdentifiers,
402
704
  };
403
705
  }
404
- return {
405
- expression: removeKeyframe({ expression, frame: operation.frame }),
406
- introduced: noIntroducedIdentifiers,
407
- };
706
+ return removeKeyframe({ expression, frame: operation.frame });
408
707
  };
409
708
  const getExpressionFromJsxAttribute = (attr) => {
410
709
  if (!attr.value) {
@@ -609,6 +908,9 @@ const updateSequenceKeyframesAst = ({ input, nodePath, updates, schema, }) => {
609
908
  requiredImports.add('useCurrentFrame');
610
909
  needsFrameHook = true;
611
910
  }
911
+ if (introduced.needsEasingImport) {
912
+ requiredImports.add('Easing');
913
+ }
612
914
  }
613
915
  if (needsFrameHook) {
614
916
  const fnPath = (0, ensure_imports_and_frame_hook_1.findEnclosingFunctionPath)(jsxPath);
@@ -641,8 +943,12 @@ const updateSequenceKeyframes = async ({ input, nodePath, updates, schema, prett
641
943
  input: serialized,
642
944
  prettierConfigOverride,
643
945
  });
946
+ const outputWithoutInsertedBlankLines = removeBlankLinesFromObjectsWithProperties({
947
+ input: output,
948
+ propertyNames: updates.map((update) => getObjectPropertyNameFromUpdateKey(update.key)),
949
+ });
644
950
  return {
645
- output,
951
+ output: outputWithoutInsertedBlankLines,
646
952
  formatted,
647
953
  oldValueStrings,
648
954
  newValueStrings,
@@ -699,6 +1005,9 @@ const updateEffectKeyframesAst = ({ input, sequenceNodePath, effectIndex, update
699
1005
  requiredImports.add('useCurrentFrame');
700
1006
  needsFrameHook = true;
701
1007
  }
1008
+ if (introduced.needsEasingImport) {
1009
+ requiredImports.add('Easing');
1010
+ }
702
1011
  }
703
1012
  if (needsFrameHook) {
704
1013
  const fnPath = (0, ensure_imports_and_frame_hook_1.findEnclosingFunctionPath)(jsxPath);
@@ -733,8 +1042,12 @@ const updateEffectKeyframes = async ({ input, sequenceNodePath, effectIndex, upd
733
1042
  input: serialized,
734
1043
  prettierConfigOverride,
735
1044
  });
1045
+ const outputWithoutInsertedBlankLines = removeBlankLinesFromObjectsWithProperties({
1046
+ input: output,
1047
+ propertyNames: updates.map((update) => update.key),
1048
+ });
736
1049
  return {
737
- output,
1050
+ output: outputWithoutInsertedBlankLines,
738
1051
  formatted,
739
1052
  oldValueStrings,
740
1053
  newValueStrings,
@@ -105,18 +105,23 @@ const ensureNamedImport = ({ ast, importedName, sourcePath, localName, }) => {
105
105
  };
106
106
  exports.ensureNamedImport = ensureNamedImport;
107
107
  const ensureNamedImports = ({ ast, importedNames, sourcePath, }) => {
108
- var _a, _b;
108
+ var _a;
109
+ var _b, _c, _d;
109
110
  if (importedNames.size === 0) {
110
111
  return;
111
112
  }
112
113
  const existingImports = findImportDeclarations(ast, sourcePath);
113
114
  const existingNames = new Set();
114
115
  for (const existingImportDeclaration of existingImports) {
115
- for (const importSpecifierCandidate of (_a = existingImportDeclaration.specifiers) !== null && _a !== void 0 ? _a : []) {
116
+ for (const importSpecifierCandidate of (_b = existingImportDeclaration.specifiers) !== null && _b !== void 0 ? _b : []) {
116
117
  if (importSpecifierCandidate.type !== 'ImportSpecifier') {
117
118
  continue;
118
119
  }
119
- existingNames.add((0, exports.getImportedName)(importSpecifierCandidate));
120
+ const importedName = (0, exports.getImportedName)(importSpecifierCandidate);
121
+ const localName = (_c = (_a = importSpecifierCandidate.local) === null || _a === void 0 ? void 0 : _a.name) !== null && _c !== void 0 ? _c : importedName;
122
+ if (localName === importedName) {
123
+ existingNames.add(importedName);
124
+ }
120
125
  }
121
126
  }
122
127
  const existingImport = existingImports.find((candidateImportDeclaration) => !hasNamespaceSpecifier(candidateImportDeclaration));
@@ -126,7 +131,7 @@ const ensureNamedImports = ({ ast, importedNames, sourcePath, }) => {
126
131
  continue;
127
132
  }
128
133
  existingImport.specifiers = [
129
- ...((_b = existingImport.specifiers) !== null && _b !== void 0 ? _b : []),
134
+ ...((_d = existingImport.specifiers) !== null && _d !== void 0 ? _d : []),
130
135
  b.importSpecifier(b.identifier(importedName)),
131
136
  ];
132
137
  existingNames.add(importedName);
@@ -1,4 +1,4 @@
1
- import type { InsertableCompositionElement } from '@remotion/studio-shared';
1
+ import { type InsertableCompositionElement } from '@remotion/studio-shared';
2
2
  export type ResolvedCompositionComponent = {
3
3
  source: string;
4
4
  line: number;
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.insertJsxElementIntoComposition = exports.resolveCompositionComponent = void 0;
40
40
  const node_fs_1 = __importDefault(require("node:fs"));
41
41
  const node_path_1 = __importDefault(require("node:path"));
42
+ const studio_shared_1 = require("@remotion/studio-shared");
42
43
  const recast = __importStar(require("recast"));
43
44
  const format_file_content_1 = require("../codemods/format-file-content");
44
45
  const parse_ast_1 = require("../codemods/parse-ast");
@@ -508,6 +509,12 @@ const createSequenceElement = () => {
508
509
  const createNumberAttribute = (name, value) => {
509
510
  return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer(recast.types.builders.numericLiteral(value)));
510
511
  };
512
+ const createStringAttribute = (name, value) => {
513
+ return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.stringLiteral(value));
514
+ };
515
+ const createBooleanAttribute = (name, value) => {
516
+ return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer(recast.types.builders.booleanLiteral(value)));
517
+ };
511
518
  const createPositionAbsoluteStyleAttribute = () => {
512
519
  return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('style'), recast.types.builders.jsxExpressionContainer(recast.types.builders.objectExpression([
513
520
  recast.types.builders.objectProperty(recast.types.builders.identifier('position'), recast.types.builders.stringLiteral('absolute')),
@@ -516,6 +523,18 @@ const createPositionAbsoluteStyleAttribute = () => {
516
523
  const createStaticFileSrcAttribute = ({ staticFileLocalName, src, }) => {
517
524
  return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('src'), recast.types.builders.jsxExpressionContainer(recast.types.builders.callExpression(recast.types.builders.identifier(staticFileLocalName), [recast.types.builders.stringLiteral(src)])));
518
525
  };
526
+ const createComponentProp = ({ name, value, }) => {
527
+ if (typeof value === 'number') {
528
+ return createNumberAttribute(name, value);
529
+ }
530
+ if (typeof value === 'boolean') {
531
+ return createBooleanAttribute(name, value);
532
+ }
533
+ return createStringAttribute(name, value);
534
+ };
535
+ const createStringSrcAttribute = (src) => {
536
+ return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('src'), recast.types.builders.stringLiteral(src));
537
+ };
519
538
  const createSolidElement = ({ localName, width, height, }) => {
520
539
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
521
540
  createNumberAttribute('width', width),
@@ -523,10 +542,18 @@ const createSolidElement = ({ localName, width, height, }) => {
523
542
  createPositionAbsoluteStyleAttribute(),
524
543
  ], true), null, []);
525
544
  };
526
- const createAssetElement = ({ localName, staticFileLocalName, src, dimensions, }) => {
545
+ const createComponentElement = ({ localName, props, }) => {
527
546
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
528
- createStaticFileSrcAttribute({ staticFileLocalName, src }),
547
+ ...props.map(createComponentProp),
529
548
  createPositionAbsoluteStyleAttribute(),
549
+ ], true), null, []);
550
+ };
551
+ const createAssetElement = ({ addPositionStyle, localName, staticFileLocalName, src, dimensions, }) => {
552
+ return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
553
+ staticFileLocalName === null
554
+ ? createStringSrcAttribute(src)
555
+ : createStaticFileSrcAttribute({ staticFileLocalName, src }),
556
+ ...(addPositionStyle ? [createPositionAbsoluteStyleAttribute()] : []),
530
557
  ...(dimensions
531
558
  ? [
532
559
  createNumberAttribute('width', dimensions.width),
@@ -725,6 +752,41 @@ const ensureGifImport = (ast) => {
725
752
  label: '<Gif>',
726
753
  });
727
754
  };
755
+ const hasComponentLocalImport = ({ ast, importName, importPath, }) => {
756
+ var _a;
757
+ var _b, _c;
758
+ for (const importDeclaration of getImportDeclarations({
759
+ ast,
760
+ sourcePath: importPath,
761
+ })) {
762
+ for (const specifier of (_b = importDeclaration.specifiers) !== null && _b !== void 0 ? _b : []) {
763
+ if (specifier.type === 'ImportSpecifier' &&
764
+ (0, imports_1.getImportedName)(specifier) === importName) {
765
+ return (_c = (_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) !== null && _c !== void 0 ? _c : importName;
766
+ }
767
+ }
768
+ }
769
+ return null;
770
+ };
771
+ const ensureComponentImport = ({ ast, componentName, importName, importPath, }) => {
772
+ const existingLocalName = hasComponentLocalImport({
773
+ ast,
774
+ importName,
775
+ importPath,
776
+ });
777
+ if (existingLocalName) {
778
+ return existingLocalName;
779
+ }
780
+ if (hasTopLevelBinding({ ast, name: componentName })) {
781
+ throw new Error(`Cannot add <${componentName}> because ${componentName} is already defined`);
782
+ }
783
+ return (0, imports_1.ensureNamedImport)({
784
+ ast,
785
+ importedName: importName,
786
+ sourcePath: importPath,
787
+ localName: componentName,
788
+ });
789
+ };
728
790
  const addElementToComponentRoot = ({ ast, exportName, element, }) => {
729
791
  var _a;
730
792
  var _b;
@@ -947,8 +1009,23 @@ const createInsertableJsxElement = ({ ast, element, }) => {
947
1009
  height: element.height,
948
1010
  });
949
1011
  }
1012
+ if (element.type === 'component') {
1013
+ const componentLocalName = ensureComponentImport({
1014
+ ast,
1015
+ componentName: element.componentName,
1016
+ importName: element.importName,
1017
+ importPath: element.importPath,
1018
+ });
1019
+ return createComponentElement({
1020
+ localName: componentLocalName,
1021
+ props: element.props,
1022
+ });
1023
+ }
950
1024
  if (element.type === 'asset') {
951
- const staticFileLocalName = ensureStaticFileImport(ast);
1025
+ if (element.srcType === 'remote' && !(0, studio_shared_1.isUrl)(element.src)) {
1026
+ throw new Error('Remote asset source must be a URL');
1027
+ }
1028
+ const staticFileLocalName = element.srcType === 'remote' ? null : ensureStaticFileImport(ast);
952
1029
  let localName;
953
1030
  if (element.assetType === 'image') {
954
1031
  localName = ensureImgImport(ast);
@@ -966,6 +1043,7 @@ const createInsertableJsxElement = ({ ast, element, }) => {
966
1043
  throw new Error('Unsupported asset type');
967
1044
  }
968
1045
  return createAssetElement({
1046
+ addPositionStyle: element.assetType !== 'audio',
969
1047
  localName,
970
1048
  staticFileLocalName,
971
1049
  src: element.src,