@remotion/studio-server 4.0.471 → 4.0.472

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/codemods/add-effect.d.ts +11 -0
  2. package/dist/codemods/add-effect.js +14 -52
  3. package/dist/codemods/format-file-content.js +1 -1
  4. package/dist/codemods/parse-ast.js +4 -1
  5. package/dist/codemods/paste-effects.d.ts +15 -0
  6. package/dist/codemods/paste-effects.js +234 -0
  7. package/dist/codemods/recast-mods.js +178 -31
  8. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.d.ts +1 -1
  9. package/dist/codemods/update-keyframes/ensure-imports-and-frame-hook.js +7 -55
  10. package/dist/codemods/update-keyframes/update-keyframes.d.ts +11 -6
  11. package/dist/codemods/update-keyframes/update-keyframes.js +127 -16
  12. package/dist/helpers/get-ast-node-path.js +6 -1
  13. package/dist/helpers/import-agnostic-node-path.d.ts +10 -0
  14. package/dist/helpers/import-agnostic-node-path.js +154 -0
  15. package/dist/helpers/imports.d.ts +16 -0
  16. package/dist/helpers/imports.js +145 -0
  17. package/dist/helpers/resolve-composition-component.js +114 -51
  18. package/dist/preview-server/api-routes.js +2 -0
  19. package/dist/preview-server/routes/add-effect-keyframe.js +2 -0
  20. package/dist/preview-server/routes/add-sequence-keyframe.js +1 -0
  21. package/dist/preview-server/routes/apply-codemod.js +18 -0
  22. package/dist/preview-server/routes/can-update-effect-props.d.ts +3 -2
  23. package/dist/preview-server/routes/can-update-effect-props.js +75 -6
  24. package/dist/preview-server/routes/can-update-sequence-props.js +33 -30
  25. package/dist/preview-server/routes/delete-keyframes.js +1 -0
  26. package/dist/preview-server/routes/insert-jsx-element.js +23 -0
  27. package/dist/preview-server/routes/paste-effects.d.ts +3 -0
  28. package/dist/preview-server/routes/paste-effects.js +78 -0
  29. package/dist/preview-server/routes/save-effect-props.js +1 -0
  30. package/dist/preview-server/undo-stack.d.ts +7 -1
  31. package/package.json +6 -6
@@ -42,6 +42,7 @@ const node_path_1 = __importDefault(require("node:path"));
42
42
  const recast = __importStar(require("recast"));
43
43
  const format_file_content_1 = require("../codemods/format-file-content");
44
44
  const parse_ast_1 = require("../codemods/parse-ast");
45
+ const imports_1 = require("./imports");
45
46
  const allowedFileExtensions = new Set(['.tsx', '.ts', '.jsx', '.js']);
46
47
  const extensionsToProbe = ['.tsx', '.ts', '.jsx', '.js'];
47
48
  const isInRemotionRoot = ({ remotionRoot, fileName, }) => {
@@ -507,13 +508,31 @@ const createSequenceElement = () => {
507
508
  const createNumberAttribute = (name, value) => {
508
509
  return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer(recast.types.builders.numericLiteral(value)));
509
510
  };
511
+ const createPositionAbsoluteStyleAttribute = () => {
512
+ return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('style'), recast.types.builders.jsxExpressionContainer(recast.types.builders.objectExpression([
513
+ recast.types.builders.objectProperty(recast.types.builders.identifier('position'), recast.types.builders.stringLiteral('absolute')),
514
+ ])));
515
+ };
516
+ const createStaticFileSrcAttribute = ({ staticFileLocalName, src, }) => {
517
+ 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
+ };
510
519
  const createSolidElement = ({ localName, width, height, }) => {
511
520
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
512
521
  createNumberAttribute('width', width),
513
522
  createNumberAttribute('height', height),
514
- recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('style'), recast.types.builders.jsxExpressionContainer(recast.types.builders.objectExpression([
515
- recast.types.builders.objectProperty(recast.types.builders.identifier('position'), recast.types.builders.stringLiteral('absolute')),
516
- ]))),
523
+ createPositionAbsoluteStyleAttribute(),
524
+ ], true), null, []);
525
+ };
526
+ const createAssetElement = ({ localName, staticFileLocalName, src, dimensions, }) => {
527
+ return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
528
+ createStaticFileSrcAttribute({ staticFileLocalName, src }),
529
+ createPositionAbsoluteStyleAttribute(),
530
+ ...(dimensions
531
+ ? [
532
+ createNumberAttribute('width', dimensions.width),
533
+ createNumberAttribute('height', dimensions.height),
534
+ ]
535
+ : []),
517
536
  ], true), null, []);
518
537
  };
519
538
  const createFragmentWithElement = (element) => {
@@ -568,12 +587,6 @@ const addElementToNullComponentReturn = ({ declaration, element, }) => {
568
587
  returnStatement.argument = createFragmentWithElement(element);
569
588
  return (_b = (_a = returnStatement.loc) === null || _a === void 0 ? void 0 : _a.start.line) !== null && _b !== void 0 ? _b : 1;
570
589
  };
571
- const getImportedName = (specifier) => {
572
- if (specifier.imported.type === 'Identifier') {
573
- return specifier.imported.name;
574
- }
575
- return specifier.imported.value;
576
- };
577
590
  const declarationBindsName = (declaration, name) => {
578
591
  var _a;
579
592
  if (declaration.type === 'FunctionDeclaration' ||
@@ -619,54 +632,90 @@ const getAvailableSolidLocalName = (ast) => {
619
632
  }
620
633
  return available;
621
634
  };
622
- const insertImportDeclaration = (ast, importDeclaration) => {
623
- const { body } = ast.program;
624
- let lastImportIndex = -1;
625
- for (let i = 0; i < body.length; i++) {
626
- if (body[i].type === 'ImportDeclaration') {
627
- lastImportIndex = i;
628
- }
629
- }
630
- body.splice(lastImportIndex + 1, 0, importDeclaration);
635
+ const ensureSolidImport = (ast) => {
636
+ return (0, imports_1.ensureNamedImport)({
637
+ ast,
638
+ importedName: 'Solid',
639
+ sourcePath: 'remotion',
640
+ localName: getAvailableSolidLocalName(ast),
641
+ });
642
+ };
643
+ const getImportDeclarations = ({ ast, sourcePath, }) => {
644
+ return ast.program.body.filter((node) => node.type === 'ImportDeclaration' &&
645
+ node.source.type === 'StringLiteral' &&
646
+ node.source.value === sourcePath);
631
647
  };
632
- const addSolidImport = ({ ast, localName, remotionImport, }) => {
648
+ const importDeclarationHasNamespaceSpecifier = (importDeclaration) => {
633
649
  var _a;
634
- var _b;
635
- const imported = recast.types.builders.identifier('Solid');
636
- const local = localName === 'Solid' ? null : recast.types.builders.identifier(localName);
637
- const specifier = recast.types.builders.importSpecifier(imported, local);
638
- const canAddToExistingRemotionImport = remotionImport &&
639
- !((_a = remotionImport.specifiers) === null || _a === void 0 ? void 0 : _a.some((importSpecifier) => importSpecifier.type === 'ImportNamespaceSpecifier'));
640
- if (canAddToExistingRemotionImport) {
641
- remotionImport.specifiers = [
642
- ...((_b = remotionImport.specifiers) !== null && _b !== void 0 ? _b : []),
643
- specifier,
650
+ return (_a = importDeclaration.specifiers) === null || _a === void 0 ? void 0 : _a.some((specifier) => specifier.type === 'ImportNamespaceSpecifier');
651
+ };
652
+ const hasOfficialLocalImport = ({ ast, importedName, sourcePath, }) => {
653
+ return getImportDeclarations({ ast, sourcePath }).some((importDeclaration) => {
654
+ var _a;
655
+ return (_a = importDeclaration.specifiers) === null || _a === void 0 ? void 0 : _a.some((specifier) => {
656
+ var _a;
657
+ var _b;
658
+ return (specifier.type === 'ImportSpecifier' &&
659
+ (0, imports_1.getImportedName)(specifier) === importedName &&
660
+ ((_b = (_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : importedName) === importedName);
661
+ });
662
+ });
663
+ };
664
+ const addOfficialNamedImport = ({ ast, importedName, sourcePath, }) => {
665
+ var _a;
666
+ const existingImport = getImportDeclarations({ ast, sourcePath }).find((candidate) => !importDeclarationHasNamespaceSpecifier(candidate));
667
+ const importSpecifier = recast.types.builders.importSpecifier(recast.types.builders.identifier(importedName));
668
+ if (existingImport) {
669
+ existingImport.specifiers = [
670
+ ...((_a = existingImport.specifiers) !== null && _a !== void 0 ? _a : []),
671
+ importSpecifier,
644
672
  ];
645
673
  return;
646
674
  }
647
- const importDeclaration = recast.types.builders.importDeclaration([], recast.types.builders.stringLiteral('remotion'));
648
- importDeclaration.specifiers = [specifier];
649
- insertImportDeclaration(ast, importDeclaration);
675
+ const importDeclaration = recast.types.builders.importDeclaration([importSpecifier], recast.types.builders.stringLiteral(sourcePath));
676
+ (0, imports_1.insertImportDeclaration)(ast, importDeclaration);
650
677
  };
651
- const ensureSolidImport = (ast) => {
652
- var _a, _b;
653
- let remotionImport = null;
654
- for (const node of ast.program.body) {
655
- if (node.type !== 'ImportDeclaration' || node.source.value !== 'remotion') {
656
- continue;
657
- }
658
- remotionImport = node;
659
- const solidSpecifier = (_a = node.specifiers) === null || _a === void 0 ? void 0 : _a.find((specifier) => {
660
- return (specifier.type === 'ImportSpecifier' &&
661
- getImportedName(specifier) === 'Solid');
662
- });
663
- if ((_b = solidSpecifier === null || solidSpecifier === void 0 ? void 0 : solidSpecifier.local) === null || _b === void 0 ? void 0 : _b.name) {
664
- return solidSpecifier.local.name;
665
- }
678
+ const ensureOfficialNamedImport = ({ ast, importedName, sourcePath, label, }) => {
679
+ if (hasOfficialLocalImport({ ast, importedName, sourcePath })) {
680
+ return importedName;
681
+ }
682
+ if (hasTopLevelBinding({ ast, name: importedName })) {
683
+ throw new Error(`Cannot add ${label} because ${importedName} is already defined`);
666
684
  }
667
- const localName = getAvailableSolidLocalName(ast);
668
- addSolidImport({ ast, localName, remotionImport });
669
- return localName;
685
+ addOfficialNamedImport({ ast, importedName, sourcePath });
686
+ return importedName;
687
+ };
688
+ const ensureStaticFileImport = (ast) => {
689
+ return ensureOfficialNamedImport({
690
+ ast,
691
+ importedName: 'staticFile',
692
+ sourcePath: 'remotion',
693
+ label: 'staticFile()',
694
+ });
695
+ };
696
+ const ensureImgImport = (ast) => {
697
+ return ensureOfficialNamedImport({
698
+ ast,
699
+ importedName: 'Img',
700
+ sourcePath: 'remotion',
701
+ label: '<Img>',
702
+ });
703
+ };
704
+ const ensureVideoImport = (ast) => {
705
+ return ensureOfficialNamedImport({
706
+ ast,
707
+ importedName: 'Video',
708
+ sourcePath: '@remotion/media',
709
+ label: '<Video>',
710
+ });
711
+ };
712
+ const ensureGifImport = (ast) => {
713
+ return ensureOfficialNamedImport({
714
+ ast,
715
+ importedName: 'Gif',
716
+ sourcePath: '@remotion/gif',
717
+ label: '<Gif>',
718
+ });
670
719
  };
671
720
  const addElementToComponentRoot = ({ ast, exportName, element, }) => {
672
721
  var _a;
@@ -695,7 +744,7 @@ const addElementToComponentRoot = ({ ast, exportName, element, }) => {
695
744
  if (rootNode.type === 'JSXElement' &&
696
745
  rootNode.openingElement.name.type === 'JSXIdentifier' &&
697
746
  CANVAS_ROOT_ELEMENTS.includes(rootNode.openingElement.name.name)) {
698
- throw new Error(`Cannot insert a <Solid> into a composition whose root element is <${rootNode.openingElement.name.name}>`);
747
+ throw new Error(`Cannot insert a JSX element into a composition whose root element is <${rootNode.openingElement.name.name}>`);
699
748
  }
700
749
  if (!rootNode.children) {
701
750
  throw new Error('Composition component root does not accept children');
@@ -890,6 +939,20 @@ const createInsertableJsxElement = ({ ast, element, }) => {
890
939
  height: element.height,
891
940
  });
892
941
  }
942
+ if (element.type === 'asset') {
943
+ const staticFileLocalName = ensureStaticFileImport(ast);
944
+ const localName = element.assetType === 'image'
945
+ ? ensureImgImport(ast)
946
+ : element.assetType === 'video'
947
+ ? ensureVideoImport(ast)
948
+ : ensureGifImport(ast);
949
+ return createAssetElement({
950
+ localName,
951
+ staticFileLocalName,
952
+ src: element.src,
953
+ dimensions: element.dimensions,
954
+ });
955
+ }
893
956
  throw new Error('Unsupported element type');
894
957
  };
895
958
  const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, }) => {
@@ -18,6 +18,7 @@ const insert_jsx_element_1 = require("./routes/insert-jsx-element");
18
18
  const install_dependency_1 = require("./routes/install-dependency");
19
19
  const open_in_editor_1 = require("./routes/open-in-editor");
20
20
  const open_in_file_explorer_1 = require("./routes/open-in-file-explorer");
21
+ const paste_effects_1 = require("./routes/paste-effects");
21
22
  const project_info_1 = require("./routes/project-info");
22
23
  const redo_1 = require("./routes/redo");
23
24
  const register_client_render_1 = require("./routes/register-client-render");
@@ -62,6 +63,7 @@ exports.allApiRoutes = {
62
63
  '/api/add-sequence-keyframe': add_sequence_keyframe_1.addSequenceKeyframeHandler,
63
64
  '/api/add-effect-keyframe': add_effect_keyframe_1.addEffectKeyframeHandler,
64
65
  '/api/delete-effect': delete_effect_1.deleteEffectHandler,
66
+ '/api/paste-effects': paste_effects_1.pasteEffectsHandler,
65
67
  '/api/delete-jsx-node': delete_jsx_node_1.deleteJsxNodeHandler,
66
68
  '/api/duplicate-jsx-node': duplicate_jsx_node_1.duplicateJsxNodeHandler,
67
69
  '/api/update-available': update_available_1.handleUpdate,
@@ -27,6 +27,7 @@ const addEffectKeyframeHandler = ({ input: { fileName, sequenceNodePath, effectI
27
27
  input: fileContents,
28
28
  sequenceNodePath: sequenceNodePath.nodePath,
29
29
  effectIndex,
30
+ schema,
30
31
  updates: [
31
32
  {
32
33
  key,
@@ -83,6 +84,7 @@ const addEffectKeyframeHandler = ({ input: { fileName, sequenceNodePath, effectI
83
84
  };
84
85
  }
85
86
  return (0, can_update_effect_props_1.computeEffectPropStatus)({
87
+ ast,
86
88
  jsx,
87
89
  effectIndex,
88
90
  keys: (0, studio_shared_1.getAllSchemaKeys)(schema),
@@ -24,6 +24,7 @@ const addSequenceKeyframeHandler = ({ input: { fileName, nodePath, key, frame, v
24
24
  const { output, oldValueStrings, newValueStrings, formatted, logLine, updatedNodePath, } = await (0, update_keyframes_1.updateSequenceKeyframes)({
25
25
  input: fileContents,
26
26
  nodePath: nodePath.nodePath,
27
+ schema,
27
28
  updates: [
28
29
  {
29
30
  key,
@@ -37,6 +37,24 @@ const getCodemodUndoDescription = (codemod) => {
37
37
  entryType: codemod.type,
38
38
  };
39
39
  }
40
+ if (codemod.type === 'delete-folder') {
41
+ const label = `folder "${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}"`;
42
+ return {
43
+ undoMessage: `↩️ Deletion of ${label}`,
44
+ redoMessage: `↪️ Deletion of ${label}`,
45
+ entryType: codemod.type,
46
+ };
47
+ }
48
+ if (codemod.type === 'rename-folder') {
49
+ const oldName = `${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}`;
50
+ const newName = `${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.newName}`;
51
+ const label = `folder "${oldName}" to "${newName}"`;
52
+ return {
53
+ undoMessage: `↩️ Rename of ${label}`,
54
+ redoMessage: `↪️ Rename of ${label}`,
55
+ entryType: codemod.type,
56
+ };
57
+ }
40
58
  return {
41
59
  undoMessage: '↩️ Visual control change',
42
60
  redoMessage: '↪️ Visual control change',
@@ -1,6 +1,7 @@
1
- import type { JSXOpeningElement } from '@babel/types';
1
+ import type { File, JSXOpeningElement } from '@babel/types';
2
2
  import type { CanUpdateEffectPropsResponse, SequenceNodePath, SequenceSchema } from 'remotion';
3
- export declare const computeEffectPropStatus: ({ jsx, effectIndex, keys, }: {
3
+ export declare const computeEffectPropStatus: ({ ast, jsx, effectIndex, keys, }: {
4
+ ast: File;
4
5
  jsx: JSXOpeningElement;
5
6
  effectIndex: number;
6
7
  keys: string[];
@@ -6,6 +6,10 @@ const parse_ast_1 = require("../../codemods/parse-ast");
6
6
  const update_effect_props_1 = require("../../codemods/update-effect-props/update-effect-props");
7
7
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
8
8
  const can_update_sequence_props_1 = require("./can-update-sequence-props");
9
+ const staticStatus = (codeValue) => ({
10
+ status: 'static',
11
+ codeValue,
12
+ });
9
13
  const findEffectsAttr = (jsx) => {
10
14
  for (const attr of jsx.attributes) {
11
15
  if (attr.type !== 'JSXAttribute') {
@@ -27,6 +31,63 @@ const getEffectsArrayElements = (attr) => {
27
31
  }
28
32
  return (0, update_effect_props_1.enumerateEffectArrayElements)(expr);
29
33
  };
34
+ const getImportedName = (specifier) => {
35
+ var _a, _b;
36
+ if (!specifier.imported) {
37
+ return null;
38
+ }
39
+ if (specifier.imported.type === 'Identifier') {
40
+ return (_a = specifier.imported.name) !== null && _a !== void 0 ? _a : null;
41
+ }
42
+ return (_b = specifier.imported.value) !== null && _b !== void 0 ? _b : null;
43
+ };
44
+ const resolveEffectImport = ({ ast, call, fallbackCallee, }) => {
45
+ var _a, _b;
46
+ var _c;
47
+ const { callee } = call;
48
+ if (callee.type === 'Identifier') {
49
+ const localName = callee.name;
50
+ for (const node of ast.program.body) {
51
+ if (node.type !== 'ImportDeclaration') {
52
+ continue;
53
+ }
54
+ const matchingSpecifier = (_a = node.specifiers) === null || _a === void 0 ? void 0 : _a.find((specifier) => {
55
+ var _a;
56
+ return (specifier.type === 'ImportSpecifier' &&
57
+ ((_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) === localName);
58
+ });
59
+ if ((matchingSpecifier === null || matchingSpecifier === void 0 ? void 0 : matchingSpecifier.type) === 'ImportSpecifier') {
60
+ return {
61
+ callee: (_c = getImportedName(matchingSpecifier)) !== null && _c !== void 0 ? _c : fallbackCallee,
62
+ importPath: String(node.source.value),
63
+ };
64
+ }
65
+ }
66
+ }
67
+ if (callee.type === 'MemberExpression' &&
68
+ callee.object.type === 'Identifier' &&
69
+ callee.property.type === 'Identifier' &&
70
+ !callee.computed) {
71
+ const namespaceName = callee.object.name;
72
+ for (const node of ast.program.body) {
73
+ if (node.type !== 'ImportDeclaration') {
74
+ continue;
75
+ }
76
+ const matchingSpecifier = (_b = node.specifiers) === null || _b === void 0 ? void 0 : _b.find((specifier) => {
77
+ var _a;
78
+ return (specifier.type === 'ImportNamespaceSpecifier' &&
79
+ ((_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) === namespaceName);
80
+ });
81
+ if (matchingSpecifier) {
82
+ return {
83
+ callee: callee.property.name,
84
+ importPath: String(node.source.value),
85
+ };
86
+ }
87
+ }
88
+ }
89
+ return { callee: fallbackCallee, importPath: null };
90
+ };
30
91
  const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
31
92
  const out = {};
32
93
  for (const key of keys) {
@@ -35,7 +96,7 @@ const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
35
96
  (p.key.type === 'StringLiteral' &&
36
97
  p.key.value === key)));
37
98
  if (!prop) {
38
- out[key] = { canUpdate: true, codeValue: undefined };
99
+ out[key] = staticStatus(undefined);
39
100
  continue;
40
101
  }
41
102
  const valueExpr = prop.value;
@@ -44,13 +105,13 @@ const getPropsFromObjectExpression = ({ objExpr, keys, }) => {
44
105
  continue;
45
106
  }
46
107
  out[key] = {
47
- canUpdate: true,
108
+ status: 'static',
48
109
  codeValue: (0, can_update_sequence_props_1.extractStaticValue)(valueExpr),
49
110
  };
50
111
  }
51
112
  return out;
52
113
  };
53
- const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
114
+ const computeEffectPropStatus = ({ ast, jsx, effectIndex, keys, }) => {
54
115
  const attr = findEffectsAttr(jsx);
55
116
  const elements = getEffectsArrayElements(attr);
56
117
  if (!elements) {
@@ -76,14 +137,20 @@ const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
76
137
  };
77
138
  }
78
139
  const call = target.node;
140
+ const effectImport = resolveEffectImport({
141
+ ast,
142
+ call,
143
+ fallbackCallee: target.callee,
144
+ });
79
145
  if (call.arguments.length === 0) {
80
146
  const emptyProps = {};
81
147
  for (const key of keys) {
82
- emptyProps[key] = { canUpdate: true, codeValue: undefined };
148
+ emptyProps[key] = staticStatus(undefined);
83
149
  }
84
150
  return {
85
151
  canUpdate: true,
86
- callee: target.callee,
152
+ callee: effectImport.callee,
153
+ importPath: effectImport.importPath,
87
154
  effectIndex,
88
155
  props: emptyProps,
89
156
  };
@@ -103,7 +170,8 @@ const computeEffectPropStatus = ({ jsx, effectIndex, keys, }) => {
103
170
  return {
104
171
  canUpdate: true,
105
172
  effectIndex,
106
- callee: target.callee,
173
+ callee: effectImport.callee,
174
+ importPath: effectImport.importPath,
107
175
  props: resolvedProps,
108
176
  };
109
177
  };
@@ -119,6 +187,7 @@ const computeEffectPropsStatusesFromContent = ({ fileContents, sequenceNodePath,
119
187
  }));
120
188
  }
121
189
  return effects.map((effect, effectIndex) => (0, exports.computeEffectPropStatus)({
190
+ ast,
122
191
  jsx,
123
192
  effectIndex,
124
193
  keys: keysFor(effect),
@@ -36,12 +36,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.computeSequencePropsStatusFromFilenameByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = exports.findNodePathForJsxElement = exports.findJsxElementAtNodePath = exports.getComputedStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
37
37
  const node_fs_1 = require("node:fs");
38
38
  const renderer_1 = require("@remotion/renderer");
39
+ const studio_shared_1 = require("@remotion/studio-shared");
39
40
  const recast = __importStar(require("recast"));
40
41
  const parse_ast_1 = require("../../codemods/parse-ast");
41
42
  const get_ast_node_path_1 = require("../../helpers/get-ast-node-path");
43
+ const import_agnostic_node_path_1 = require("../../helpers/import-agnostic-node-path");
42
44
  const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
43
45
  const jsx_element_not_found_at_location_error_1 = require("../jsx-element-not-found-at-location-error");
44
46
  const can_update_effect_props_1 = require("./can-update-effect-props");
47
+ const staticStatus = (codeValue) => ({
48
+ status: 'static',
49
+ codeValue,
50
+ });
51
+ const computedStatus = () => ({
52
+ status: 'computed',
53
+ });
45
54
  const isStaticValue = (node) => {
46
55
  switch (node.type) {
47
56
  case 'NumericLiteral':
@@ -305,8 +314,7 @@ const getInterpolationKeyframes = (node) => {
305
314
  }
306
315
  const callExpression = node;
307
316
  if (callExpression.callee.type !== 'Identifier' ||
308
- (callExpression.callee.name !== 'interpolate' &&
309
- callExpression.callee.name !== 'interpolateColors')) {
317
+ !(0, studio_shared_1.isKeyframeInterpolationFunction)(callExpression.callee.name)) {
310
318
  return undefined;
311
319
  }
312
320
  const interpolationFunction = callExpression.callee.name;
@@ -358,11 +366,11 @@ const getInterpolationKeyframes = (node) => {
358
366
  const getComputedStatus = (node) => {
359
367
  const interpolation = getInterpolationKeyframes(node);
360
368
  if (!interpolation) {
361
- return { canUpdate: false, reason: 'computed' };
369
+ return computedStatus();
362
370
  }
363
371
  return {
364
- canUpdate: false,
365
- reason: 'keyframed',
372
+ status: 'keyframed',
373
+ codeValue: undefined,
366
374
  interpolationFunction: interpolation.interpolationFunction,
367
375
  keyframes: interpolation.keyframes,
368
376
  easing: interpolation.easing,
@@ -386,39 +394,33 @@ const getPropsStatus = (jsxElement) => {
386
394
  }
387
395
  const { value } = attr;
388
396
  if (!value) {
389
- props[name] = { canUpdate: true, codeValue: true };
397
+ props[name] = staticStatus(true);
390
398
  continue;
391
399
  }
392
400
  if (value.type === 'StringLiteral') {
393
- props[name] = {
394
- canUpdate: true,
395
- codeValue: value.value,
396
- };
401
+ props[name] = staticStatus(value.value);
397
402
  continue;
398
403
  }
399
404
  if (value.type === 'JSXExpressionContainer') {
400
405
  const { expression } = value;
401
406
  if (expression.type === 'JSXEmptyExpression') {
402
- props[name] = { canUpdate: false, reason: 'computed' };
407
+ props[name] = computedStatus();
403
408
  continue;
404
409
  }
405
410
  if (!(0, exports.isStaticValue)(expression)) {
406
411
  props[name] = (0, exports.getComputedStatus)(expression);
407
412
  continue;
408
413
  }
409
- props[name] = {
410
- canUpdate: true,
411
- codeValue: (0, exports.extractStaticValue)(expression),
412
- };
414
+ props[name] = staticStatus((0, exports.extractStaticValue)(expression));
413
415
  continue;
414
416
  }
415
- props[name] = { canUpdate: false, reason: 'computed' };
417
+ props[name] = computedStatus();
416
418
  }
417
419
  return props;
418
420
  };
419
421
  const getNodePathForRecastPath = (
420
422
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
421
- recastPath) => {
423
+ recastPath, ast) => {
422
424
  const segments = [];
423
425
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
424
426
  let current = recastPath;
@@ -428,9 +430,9 @@ recastPath) => {
428
430
  }
429
431
  // Recast paths start with "root" which doesn't correspond to a real AST property
430
432
  if (segments.length > 0 && segments[0] === 'root') {
431
- return segments.slice(1);
433
+ return (0, import_agnostic_node_path_1.toImportAgnosticNodePath)({ ast, nodePath: segments.slice(1) });
432
434
  }
433
- return segments;
435
+ return (0, import_agnostic_node_path_1.toImportAgnosticNodePath)({ ast, nodePath: segments });
434
436
  };
435
437
  const findJsxElementAtNodePath = (ast, nodePath) => {
436
438
  const current = (0, get_ast_node_path_1.getAstNodePath)(ast, nodePath);
@@ -448,7 +450,7 @@ const findNodePathForJsxElement = (ast, target) => {
448
450
  recast.types.visit(ast, {
449
451
  visitJSXOpeningElement(p) {
450
452
  if (p.node === target) {
451
- foundPath = getNodePathForRecastPath(p);
453
+ foundPath = getNodePathForRecastPath(p, ast);
452
454
  return false;
453
455
  }
454
456
  return this.traverse(p);
@@ -463,7 +465,7 @@ const lineColumnToNodePath = (ast, targetLine) => {
463
465
  visitJSXOpeningElement(p) {
464
466
  const { node } = p;
465
467
  if (node.loc && node.loc.start.line === targetLine) {
466
- foundPath = getNodePathForRecastPath(p);
468
+ foundPath = getNodePathForRecastPath(p, ast);
467
469
  return false;
468
470
  }
469
471
  return this.traverse(p);
@@ -492,16 +494,16 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
492
494
  a.name.name === parentKey);
493
495
  if (!attr || !attr.value) {
494
496
  // Parent attribute doesn't exist, nested prop can be added
495
- return { canUpdate: true, codeValue: undefined };
497
+ return staticStatus(undefined);
496
498
  }
497
499
  if (attr.value.type !== 'JSXExpressionContainer') {
498
- return { canUpdate: false, reason: 'computed' };
500
+ return computedStatus();
499
501
  }
500
502
  const { expression } = attr.value;
501
503
  if (expression.type === 'JSXEmptyExpression' ||
502
504
  expression.type !== 'ObjectExpression') {
503
505
  // Parent is not an object literal (e.g. style={myStyles})
504
- return { canUpdate: false, reason: 'computed' };
506
+ return computedStatus();
505
507
  }
506
508
  const objExpr = expression;
507
509
  const prop = objExpr.properties.find((p) => p.type === 'ObjectProperty' &&
@@ -509,7 +511,7 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
509
511
  (p.key.type === 'StringLiteral' && p.key.value === childKey)));
510
512
  if (!prop) {
511
513
  // Property not set in the object, can be added
512
- return { canUpdate: true, codeValue: undefined };
514
+ return staticStatus(undefined);
513
515
  }
514
516
  const propValue = prop.value;
515
517
  if (!(0, exports.isStaticValue)(propValue)) {
@@ -517,12 +519,13 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
517
519
  }
518
520
  const codeValue = (0, exports.extractStaticValue)(propValue);
519
521
  if (!validateStyleValue(childKey, codeValue)) {
520
- return { canUpdate: false, reason: 'computed' };
522
+ return computedStatus();
521
523
  }
522
- return { canUpdate: true, codeValue };
524
+ return staticStatus(codeValue);
523
525
  };
524
- const computeEffectsForJsx = ({ jsxElement, effects, }) => {
526
+ const computeEffectsForJsx = ({ ast, jsxElement, effects, }) => {
525
527
  return effects.map((effect, effectIndex) => (0, can_update_effect_props_1.computeEffectPropStatus)({
528
+ ast,
526
529
  jsx: jsxElement,
527
530
  effectIndex,
528
531
  keys: effect,
@@ -540,7 +543,7 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, keys, }) => {
540
543
  filteredProps[key] = allProps[key];
541
544
  }
542
545
  else {
543
- filteredProps[key] = { canUpdate: true, codeValue: undefined };
546
+ filteredProps[key] = staticStatus(undefined);
544
547
  }
545
548
  }
546
549
  return filteredProps;
@@ -552,7 +555,7 @@ const computeSequencePropsStatusFromContent = ({ fileContents, nodePath, keys, e
552
555
  throw new jsx_element_not_found_at_location_error_1.JsxElementNotFoundAtLocationError();
553
556
  }
554
557
  const filteredProps = computeSequenceOnlyPropsRecord({ jsxElement, keys });
555
- const effectsStatuses = computeEffectsForJsx({ jsxElement, effects });
558
+ const effectsStatuses = computeEffectsForJsx({ ast, jsxElement, effects });
556
559
  return {
557
560
  canUpdate: true,
558
561
  props: filteredProps,
@@ -241,6 +241,7 @@ const deleteKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, r
241
241
  };
242
242
  }
243
243
  return (0, can_update_effect_props_1.computeEffectPropStatus)({
244
+ ast,
244
245
  jsx,
245
246
  effectIndex: keyframe.effectIndex,
246
247
  keys: (0, studio_shared_1.getAllSchemaKeys)(keyframe.schema),
@@ -17,12 +17,35 @@ const validateElement = (element) => {
17
17
  if (element.type === 'solid') {
18
18
  validateDimension('width', element.width);
19
19
  validateDimension('height', element.height);
20
+ return;
20
21
  }
22
+ if (element.type === 'asset') {
23
+ if (!element.src || element.src.includes('\\')) {
24
+ throw new Error('Asset path must be a static file path');
25
+ }
26
+ if (element.dimensions) {
27
+ validateDimension('width', element.dimensions.width);
28
+ validateDimension('height', element.dimensions.height);
29
+ }
30
+ return;
31
+ }
32
+ throw new Error('Unsupported element type');
21
33
  };
22
34
  const getElementLabel = (element) => {
23
35
  if (element.type === 'solid') {
24
36
  return '<Solid>';
25
37
  }
38
+ if (element.type === 'asset') {
39
+ if (element.assetType === 'image') {
40
+ return '<Img>';
41
+ }
42
+ if (element.assetType === 'video') {
43
+ return '<Video>';
44
+ }
45
+ if (element.assetType === 'gif') {
46
+ return '<Gif>';
47
+ }
48
+ }
26
49
  throw new Error('Unsupported element type');
27
50
  };
28
51
  const insertJsxElementHandler = ({ input: { compositionFile, compositionId, element }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {