@remotion/studio-server 4.0.484 → 4.0.486

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 (41) hide show
  1. package/dist/codemods/duplicate-composition.js +8 -0
  2. package/dist/codemods/recast-mods.js +246 -5
  3. package/dist/codemods/split-jsx-sequence.d.ts +14 -0
  4. package/dist/codemods/split-jsx-sequence.js +345 -0
  5. package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +3 -1
  6. package/dist/codemods/update-sequence-props/update-sequence-props.js +406 -9
  7. package/dist/helpers/import-agnostic-node-path.js +30 -4
  8. package/dist/helpers/resolve-composition-component.d.ts +1 -0
  9. package/dist/helpers/resolve-composition-component.js +206 -9
  10. package/dist/preview-server/api-routes.js +2 -0
  11. package/dist/preview-server/routes/add-effect-keyframe.js +2 -2
  12. package/dist/preview-server/routes/add-effect.js +56 -53
  13. package/dist/preview-server/routes/add-keyframes.js +2 -2
  14. package/dist/preview-server/routes/add-sequence-keyframe.js +2 -2
  15. package/dist/preview-server/routes/apply-codemod.js +123 -101
  16. package/dist/preview-server/routes/apply-visual-control-change.js +83 -80
  17. package/dist/preview-server/routes/can-update-sequence-props.d.ts +13 -1
  18. package/dist/preview-server/routes/can-update-sequence-props.js +101 -7
  19. package/dist/preview-server/routes/delete-effect.js +83 -80
  20. package/dist/preview-server/routes/delete-jsx-node.js +74 -71
  21. package/dist/preview-server/routes/delete-keyframes.js +2 -2
  22. package/dist/preview-server/routes/duplicate-effect.js +77 -74
  23. package/dist/preview-server/routes/duplicate-jsx-node.js +53 -50
  24. package/dist/preview-server/routes/insert-element.js +2 -2
  25. package/dist/preview-server/routes/insert-jsx-element.js +53 -4
  26. package/dist/preview-server/routes/move-keyframes.js +2 -2
  27. package/dist/preview-server/routes/paste-effects.js +59 -56
  28. package/dist/preview-server/routes/reorder-effect.js +55 -52
  29. package/dist/preview-server/routes/reorder-sequence.js +55 -52
  30. package/dist/preview-server/routes/save-effect-props.js +2 -2
  31. package/dist/preview-server/routes/save-sequence-props.d.ts +3 -1
  32. package/dist/preview-server/routes/save-sequence-props.js +208 -24
  33. package/dist/preview-server/routes/source-file-write-queue.d.ts +1 -0
  34. package/dist/preview-server/routes/source-file-write-queue.js +11 -0
  35. package/dist/preview-server/routes/split-jsx-sequence.d.ts +3 -0
  36. package/dist/preview-server/routes/split-jsx-sequence.js +66 -0
  37. package/dist/preview-server/routes/update-default-props.js +58 -55
  38. package/dist/preview-server/routes/update-effect-keyframe-settings.js +2 -2
  39. package/dist/preview-server/routes/update-sequence-keyframe-settings.js +2 -2
  40. package/dist/preview-server/undo-stack.d.ts +7 -1
  41. package/package.json +6 -6
@@ -41,8 +41,10 @@ const node_fs_1 = __importDefault(require("node:fs"));
41
41
  const node_path_1 = __importDefault(require("node:path"));
42
42
  const studio_shared_1 = require("@remotion/studio-shared");
43
43
  const recast = __importStar(require("recast"));
44
+ const no_react_1 = require("remotion/no-react");
44
45
  const format_file_content_1 = require("../codemods/format-file-content");
45
46
  const parse_ast_1 = require("../codemods/parse-ast");
47
+ const update_nested_prop_1 = require("../codemods/update-nested-prop");
46
48
  const imports_1 = require("./imports");
47
49
  const allowedFileExtensions = new Set(['.tsx', '.ts', '.jsx', '.js']);
48
50
  const extensionsToProbe = ['.tsx', '.ts', '.jsx', '.js'];
@@ -119,7 +121,7 @@ const getComponentIdentifier = (element) => {
119
121
  return attribute.value.expression.name;
120
122
  };
121
123
  const isRecord = (value) => {
122
- return typeof value === 'object' && value !== null;
124
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
123
125
  };
124
126
  const findDynamicImportPath = (value) => {
125
127
  if (!isRecord(value)) {
@@ -578,11 +580,14 @@ const createComponentElement = ({ addPositionStyle, localName, props, position,
578
580
  : []),
579
581
  ], true), null, []);
580
582
  };
581
- const createSequenceWrappedElement = ({ child, dimensions, name, position, sequenceLocalName, }) => {
583
+ const createSequenceWrappedElement = ({ child, dimensions, durationInFrames, name, position, sequenceLocalName, }) => {
582
584
  return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(sequenceLocalName), [
583
585
  ...(name === null ? [] : [createStringAttribute('name', name)]),
584
586
  createNumberAttribute('width', dimensions.width),
585
587
  createNumberAttribute('height', dimensions.height),
588
+ ...(durationInFrames === null
589
+ ? []
590
+ : [createNumberAttribute('durationInFrames', durationInFrames)]),
586
591
  createPositionAbsoluteStyleAttribute(position),
587
592
  ], false), recast.types.builders.jsxClosingElement(recast.types.builders.jsxIdentifier(sequenceLocalName)), [child]);
588
593
  };
@@ -847,6 +852,167 @@ const ensureComponentImport = ({ ast, componentName, importName, importPath, })
847
852
  localName: componentName,
848
853
  });
849
854
  };
855
+ const identifierRegex = /^[A-Za-z_$][0-9A-Za-z_$]*$/;
856
+ const toPascalCaseIdentifier = (value) => {
857
+ var _a;
858
+ const words = (_a = value.match(/[a-zA-Z0-9]+/g)) !== null && _a !== void 0 ? _a : [];
859
+ const candidate = words
860
+ .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
861
+ .join('');
862
+ if (!candidate) {
863
+ return 'CompositionComponent';
864
+ }
865
+ if (/^[0-9]/.test(candidate)) {
866
+ return `Composition${candidate}`;
867
+ }
868
+ return identifierRegex.test(candidate) ? candidate : 'CompositionComponent';
869
+ };
870
+ const getAvailableLocalName = ({ ast, baseName, }) => {
871
+ if (!hasTopLevelBinding({ ast, name: baseName })) {
872
+ return baseName;
873
+ }
874
+ const suffixed = `${baseName}Composition`;
875
+ if (!hasTopLevelBinding({ ast, name: suffixed })) {
876
+ return suffixed;
877
+ }
878
+ for (let i = 2; i < 100; i++) {
879
+ const candidate = `${suffixed}${i}`;
880
+ if (!hasTopLevelBinding({ ast, name: candidate })) {
881
+ return candidate;
882
+ }
883
+ }
884
+ throw new Error(`Cannot find a local name for ${baseName}`);
885
+ };
886
+ const getImportPathBetweenFiles = ({ fromFile, toFile, }) => {
887
+ let relativeImport = node_path_1.default
888
+ .relative(node_path_1.default.dirname(fromFile), toFile)
889
+ .replaceAll(node_path_1.default.sep, '/')
890
+ .replace(/\.(tsx|ts|jsx|js)$/, '');
891
+ if (!relativeImport.startsWith('.')) {
892
+ relativeImport = `./${relativeImport}`;
893
+ }
894
+ return relativeImport;
895
+ };
896
+ const ensureDefaultImport = ({ ast, localName, sourcePath, }) => {
897
+ var _a, _b;
898
+ var _c;
899
+ for (const declaration of getImportDeclarations({ ast, sourcePath })) {
900
+ const defaultSpecifier = (_a = declaration.specifiers) === null || _a === void 0 ? void 0 : _a.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
901
+ if ((_b = defaultSpecifier === null || defaultSpecifier === void 0 ? void 0 : defaultSpecifier.local) === null || _b === void 0 ? void 0 : _b.name) {
902
+ return defaultSpecifier.local.name;
903
+ }
904
+ }
905
+ const importSpecifier = recast.types.builders.importDefaultSpecifier(recast.types.builders.identifier(localName));
906
+ const existingImport = getImportDeclarations({ ast, sourcePath }).find((declaration) => {
907
+ var _a;
908
+ return !((_a = declaration.specifiers) === null || _a === void 0 ? void 0 : _a.some((specifier) => specifier.type === 'ImportNamespaceSpecifier'));
909
+ });
910
+ if (existingImport) {
911
+ existingImport.specifiers = [
912
+ importSpecifier,
913
+ ...((_c = existingImport.specifiers) !== null && _c !== void 0 ? _c : []),
914
+ ];
915
+ return localName;
916
+ }
917
+ const importDeclaration = recast.types.builders.importDeclaration([importSpecifier], recast.types.builders.stringLiteral(sourcePath));
918
+ (0, imports_1.insertImportDeclaration)(ast, importDeclaration);
919
+ return localName;
920
+ };
921
+ const ensureCompositionComponentImport = async ({ ast, compositionFile, compositionId, destinationFileName, remotionRoot, }) => {
922
+ const sourceLocation = await (0, exports.resolveCompositionComponentWithFile)({
923
+ remotionRoot,
924
+ compositionFile,
925
+ compositionId,
926
+ });
927
+ if (sourceLocation.fileName === destinationFileName) {
928
+ if (sourceLocation.exportName === 'default') {
929
+ throw new Error('Cannot insert a composition whose component is a default export in the same file');
930
+ }
931
+ if (!hasTopLevelBinding({ ast, name: sourceLocation.exportName })) {
932
+ throw new Error(`Cannot find component "${sourceLocation.exportName}" in this file`);
933
+ }
934
+ return sourceLocation.exportName;
935
+ }
936
+ const sourcePath = getImportPathBetweenFiles({
937
+ fromFile: destinationFileName,
938
+ toFile: sourceLocation.fileName,
939
+ });
940
+ if (sourceLocation.exportName === 'default') {
941
+ return ensureDefaultImport({
942
+ ast,
943
+ localName: getAvailableLocalName({
944
+ ast,
945
+ baseName: toPascalCaseIdentifier(compositionId),
946
+ }),
947
+ sourcePath,
948
+ });
949
+ }
950
+ return (0, imports_1.ensureNamedImport)({
951
+ ast,
952
+ importedName: sourceLocation.exportName,
953
+ sourcePath,
954
+ localName: getAvailableLocalName({
955
+ ast,
956
+ baseName: sourceLocation.exportName,
957
+ }),
958
+ });
959
+ };
960
+ const parseSerializedCompositionProps = (serializedResolvedPropsWithCustomSchema) => {
961
+ const parsed = JSON.parse(serializedResolvedPropsWithCustomSchema);
962
+ if (!isRecord(parsed)) {
963
+ throw new Error('Resolved composition props must be an object');
964
+ }
965
+ return parsed;
966
+ };
967
+ const containsFileToken = (value) => {
968
+ if (typeof value === 'string') {
969
+ return value.startsWith(no_react_1.NoReactInternals.FILE_TOKEN);
970
+ }
971
+ if (Array.isArray(value)) {
972
+ return value.some(containsFileToken);
973
+ }
974
+ if (isRecord(value)) {
975
+ return Object.values(value).some(containsFileToken);
976
+ }
977
+ return false;
978
+ };
979
+ const createExpressionAttribute = (name, value) => {
980
+ return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer((0, update_nested_prop_1.parseValueExpression)(value)));
981
+ };
982
+ const createCompositionPropAttribute = ({ name, value, }) => {
983
+ if (typeof value === 'string' &&
984
+ !value.startsWith(no_react_1.NoReactInternals.FILE_TOKEN) &&
985
+ !value.startsWith(no_react_1.NoReactInternals.DATE_TOKEN)) {
986
+ return createStringAttribute(name, value);
987
+ }
988
+ return createExpressionAttribute(name, value);
989
+ };
990
+ const createCompositionObjectProperty = ({ name, value, }) => {
991
+ return recast.types.builders.objectProperty(identifierRegex.test(name)
992
+ ? recast.types.builders.identifier(name)
993
+ : recast.types.builders.stringLiteral(name), (0, update_nested_prop_1.parseValueExpression)(value));
994
+ };
995
+ const createCompositionComponentElement = ({ localName, props, }) => {
996
+ const directAttributes = [];
997
+ const spreadProperties = [];
998
+ for (const [name, value] of Object.entries(props)) {
999
+ if (identifierRegex.test(name)) {
1000
+ directAttributes.push(createCompositionPropAttribute({ name, value }));
1001
+ }
1002
+ else {
1003
+ spreadProperties.push(createCompositionObjectProperty({ name, value }));
1004
+ }
1005
+ }
1006
+ const attributes = [
1007
+ ...directAttributes,
1008
+ ...(spreadProperties.length === 0
1009
+ ? []
1010
+ : [
1011
+ recast.types.builders.jsxSpreadAttribute(recast.types.builders.objectExpression(spreadProperties)),
1012
+ ]),
1013
+ ];
1014
+ return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), attributes, true), null, []);
1015
+ };
850
1016
  const addElementToComponentRoot = ({ ast, exportName, element, }) => {
851
1017
  var _a;
852
1018
  var _b;
@@ -1061,7 +1227,7 @@ const resolveCompositionComponent = async ({ remotionRoot, compositionFile, comp
1061
1227
  };
1062
1228
  };
1063
1229
  exports.resolveCompositionComponent = resolveCompositionComponent;
1064
- const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element, }) => {
1230
+ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinationFileName, element, remotionRoot, }) => {
1065
1231
  if (element.type === 'solid') {
1066
1232
  const solidLocalName = ensureSolidImport(ast);
1067
1233
  return createSolidElement({
@@ -1085,6 +1251,21 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element,
1085
1251
  position: element.position,
1086
1252
  });
1087
1253
  }
1254
+ if (element.type === 'composition') {
1255
+ return Promise.resolve(ensureCompositionComponentImport({
1256
+ ast,
1257
+ compositionFile: element.compositionFile,
1258
+ compositionId: element.compositionId,
1259
+ destinationFileName,
1260
+ remotionRoot,
1261
+ })).then((localName) => {
1262
+ const props = parseSerializedCompositionProps(element.serializedResolvedPropsWithCustomSchema);
1263
+ if (containsFileToken(props)) {
1264
+ ensureStaticFileImport(ast);
1265
+ }
1266
+ return createCompositionComponentElement({ localName, props });
1267
+ });
1268
+ }
1088
1269
  if (element.type === 'asset') {
1089
1270
  if (element.srcType === 'remote' && !(0, studio_shared_1.isUrl)(element.src)) {
1090
1271
  throw new Error('Remote asset source must be a URL');
@@ -1121,6 +1302,7 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element,
1121
1302
  throw new Error('Unsupported element type');
1122
1303
  };
1123
1304
  const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, wrapInSequence = null, }) => {
1305
+ var _a;
1124
1306
  const location = await (0, exports.resolveCompositionComponentWithFile)({
1125
1307
  remotionRoot,
1126
1308
  compositionFile,
@@ -1134,17 +1316,32 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
1134
1316
  fileName: location.fileName,
1135
1317
  });
1136
1318
  const ast = (0, parse_ast_1.parseAst)(input);
1137
- const elementToInsert = createInsertableJsxElement({
1138
- addPositionStyleToComponent: wrapInSequence === null,
1319
+ if (element.type === 'composition' &&
1320
+ element.compositionId === compositionId) {
1321
+ throw new Error('Cannot insert a composition into itself');
1322
+ }
1323
+ const sequenceWrapper = element.type === 'composition'
1324
+ ? {
1325
+ dimensions: { width: element.width, height: element.height },
1326
+ durationInFrames: element.durationInFrames,
1327
+ name: element.compositionId,
1328
+ position: element.position,
1329
+ }
1330
+ : wrapInSequence;
1331
+ const elementToInsert = await createInsertableJsxElement({
1332
+ addPositionStyleToComponent: sequenceWrapper === null,
1139
1333
  ast,
1334
+ destinationFileName: location.fileName,
1140
1335
  element,
1336
+ remotionRoot,
1141
1337
  });
1142
- const finalElementToInsert = wrapInSequence
1338
+ const finalElementToInsert = sequenceWrapper
1143
1339
  ? createSequenceWrappedElement({
1144
1340
  child: elementToInsert,
1145
- dimensions: wrapInSequence.dimensions,
1146
- name: wrapInSequence.name,
1147
- position: wrapInSequence.position,
1341
+ dimensions: sequenceWrapper.dimensions,
1342
+ durationInFrames: (_a = sequenceWrapper.durationInFrames) !== null && _a !== void 0 ? _a : null,
1343
+ name: sequenceWrapper.name,
1344
+ position: sequenceWrapper.position,
1148
1345
  sequenceLocalName: ensureSequenceImport(ast),
1149
1346
  })
1150
1347
  : elementToInsert;
@@ -35,6 +35,7 @@ const reorder_sequence_1 = require("./routes/reorder-sequence");
35
35
  const restart_studio_1 = require("./routes/restart-studio");
36
36
  const save_effect_props_1 = require("./routes/save-effect-props");
37
37
  const save_sequence_props_1 = require("./routes/save-sequence-props");
38
+ const split_jsx_sequence_1 = require("./routes/split-jsx-sequence");
38
39
  const subscribe_to_default_props_1 = require("./routes/subscribe-to-default-props");
39
40
  const subscribe_to_file_existence_1 = require("./routes/subscribe-to-file-existence");
40
41
  const subscribe_to_sequence_props_1 = require("./routes/subscribe-to-sequence-props");
@@ -82,6 +83,7 @@ exports.allApiRoutes = {
82
83
  '/api/paste-effects': paste_effects_1.pasteEffectsHandler,
83
84
  '/api/delete-jsx-node': delete_jsx_node_1.deleteJsxNodeHandler,
84
85
  '/api/duplicate-jsx-node': duplicate_jsx_node_1.duplicateJsxNodeHandler,
86
+ '/api/split-jsx-sequence': split_jsx_sequence_1.splitJsxSequenceHandler,
85
87
  '/api/update-available': update_available_1.handleUpdate,
86
88
  '/api/project-info': project_info_1.projectInfoHandler,
87
89
  '/api/delete-static-file': delete_static_file_1.deleteStaticFileHandler,
@@ -13,8 +13,8 @@ const watch_ignore_next_change_1 = require("../watch-ignore-next-change");
13
13
  const can_update_effect_props_1 = require("./can-update-effect-props");
14
14
  const can_update_sequence_props_1 = require("./can-update-sequence-props");
15
15
  const log_effect_update_1 = require("./log-updates/log-effect-update");
16
- const save_props_mutex_1 = require("./save-props-mutex");
17
- const addEffectKeyframeHandler = ({ input: { fileName, sequenceNodePath, effectIndex, key, frame, value, schema, clientId, }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
16
+ const source_file_write_queue_1 = require("./source-file-write-queue");
17
+ const addEffectKeyframeHandler = ({ input: { fileName, sequenceNodePath, effectIndex, key, frame, value, schema, clientId, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
18
18
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-effect-keyframe] Received request for fileName="${fileName}" effectIndex=${effectIndex} key="${key}" frame=${frame}`);
19
19
  const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
20
20
  remotionRoot,
@@ -10,59 +10,62 @@ 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 addEffectHandler = async ({ input: { fileName, sequenceNodePath, effectName, effectImportPath, effectConfig, clientId, }, remotionRoot, logLevel, }) => {
14
- try {
15
- renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-effect] Received request for fileName="${fileName}" effect="${effectName}"`);
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, nodeLabel, logLine } = await (0, add_effect_1.addEffect)({
23
- input: fileContents,
24
- sequenceNodePath: sequenceNodePath.nodePath,
25
- effectName,
26
- effectImportPath,
27
- effectConfig,
28
- });
29
- (0, undo_stack_1.pushToUndoStack)({
30
- filePath: absolutePath,
31
- oldContents: fileContents,
32
- newContents: null,
33
- logLevel,
34
- remotionRoot,
35
- logLine,
36
- description: {
37
- undoMessage: `↩️ Addition of ${effectLabel} to ${nodeLabel}`,
38
- redoMessage: `↪️ Addition of ${effectLabel} to ${nodeLabel}`,
39
- },
40
- entryType: 'add-effect',
41
- suppressHmrOnFileRestore: false,
42
- });
43
- (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
44
- (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
45
- const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
46
- remotionRoot,
47
- absolutePath,
48
- line: logLine,
49
- });
50
- renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Added ${(0, formatting_1.attrName)(effectLabel)} to ${nodeLabel}`);
51
- if (!formatted) {
52
- (0, log_update_1.warnAboutPrettierOnce)(logLevel);
13
+ const source_file_write_queue_1 = require("./source-file-write-queue");
14
+ const addEffectHandler = ({ input: { fileName, sequenceNodePath, effectName, effectImportPath, effectConfig, clientId, }, remotionRoot, logLevel, }) => {
15
+ return (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
16
+ try {
17
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-effect] Received request for fileName="${fileName}" effect="${effectName}"`);
18
+ const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
19
+ remotionRoot,
20
+ fileName,
21
+ action: 'modify',
22
+ });
23
+ const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
24
+ const { output, formatted, effectLabel, nodeLabel, logLine } = await (0, add_effect_1.addEffect)({
25
+ input: fileContents,
26
+ sequenceNodePath: sequenceNodePath.nodePath,
27
+ effectName,
28
+ effectImportPath,
29
+ effectConfig,
30
+ });
31
+ (0, undo_stack_1.pushToUndoStack)({
32
+ filePath: absolutePath,
33
+ oldContents: fileContents,
34
+ newContents: null,
35
+ logLevel,
36
+ remotionRoot,
37
+ logLine,
38
+ description: {
39
+ undoMessage: `↩️ Addition of ${effectLabel} to ${nodeLabel}`,
40
+ redoMessage: `↪️ Addition of ${effectLabel} to ${nodeLabel}`,
41
+ },
42
+ entryType: 'add-effect',
43
+ suppressHmrOnFileRestore: false,
44
+ });
45
+ (0, undo_stack_1.suppressUndoStackInvalidation)(absolutePath);
46
+ (0, file_watcher_1.writeFileAndNotifyFileWatchers)(absolutePath, output, clientId);
47
+ const locationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
48
+ remotionRoot,
49
+ absolutePath,
50
+ line: logLine,
51
+ });
52
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${locationLabel}`)} Added ${(0, formatting_1.attrName)(effectLabel)} to ${nodeLabel}`);
53
+ if (!formatted) {
54
+ (0, log_update_1.warnAboutPrettierOnce)(logLevel);
55
+ }
56
+ renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[add-effect] Wrote ${fileRelativeToRoot}${formatted ? ' (formatted)' : ''}`);
57
+ (0, undo_stack_1.printUndoHint)(logLevel);
58
+ return {
59
+ success: true,
60
+ };
53
61
  }
54
- renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `[add-effect] Wrote ${fileRelativeToRoot}${formatted ? ' (formatted)' : ''}`);
55
- (0, undo_stack_1.printUndoHint)(logLevel);
56
- return {
57
- success: true,
58
- };
59
- }
60
- catch (err) {
61
- return {
62
- success: false,
63
- reason: err.message,
64
- stack: err.stack,
65
- };
66
- }
62
+ catch (err) {
63
+ return {
64
+ success: false,
65
+ reason: err.message,
66
+ stack: err.stack,
67
+ };
68
+ }
69
+ });
67
70
  };
68
71
  exports.addEffectHandler = addEffectHandler;
@@ -10,7 +10,7 @@ const undo_stack_1 = require("../undo-stack");
10
10
  const watch_ignore_next_change_1 = require("../watch-ignore-next-change");
11
11
  const log_effect_update_1 = require("./log-updates/log-effect-update");
12
12
  const log_update_1 = require("./log-updates/log-update");
13
- const save_props_mutex_1 = require("./save-props-mutex");
13
+ const source_file_write_queue_1 = require("./source-file-write-queue");
14
14
  const groupBy = (items, getKey) => {
15
15
  var _a;
16
16
  const groups = new Map();
@@ -212,7 +212,7 @@ const addKeyframes = async ({ sequenceKeyframes, effectKeyframes, clientId, remo
212
212
  (0, undo_stack_1.printUndoHint)(logLevel);
213
213
  };
214
214
  exports.addKeyframes = addKeyframes;
215
- const addKeyframesHandler = ({ input: { sequenceKeyframes, effectKeyframes, clientId }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
215
+ const addKeyframesHandler = ({ input: { sequenceKeyframes, effectKeyframes, clientId }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
216
216
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-keyframes] Received request to add ${sequenceKeyframes.length + effectKeyframes.length} keyframe(s)`);
217
217
  await (0, exports.addKeyframes)({
218
218
  sequenceKeyframes,
@@ -11,8 +11,8 @@ const undo_stack_1 = require("../undo-stack");
11
11
  const watch_ignore_next_change_1 = require("../watch-ignore-next-change");
12
12
  const can_update_sequence_props_1 = require("./can-update-sequence-props");
13
13
  const log_update_1 = require("./log-updates/log-update");
14
- const save_props_mutex_1 = require("./save-props-mutex");
15
- const addSequenceKeyframeHandler = ({ input: { fileName, nodePath, key, frame, value, schema, clientId }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
14
+ const source_file_write_queue_1 = require("./source-file-write-queue");
15
+ const addSequenceKeyframeHandler = ({ input: { fileName, nodePath, key, frame, value, schema, clientId }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
16
16
  renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[add-sequence-keyframe] Received request for fileName="${fileName}" key="${key}" frame=${frame}`);
17
17
  const { absolutePath, fileRelativeToRoot } = (0, resolve_file_inside_project_1.resolveFileInsideProject)({
18
18
  remotionRoot,