@remotion/studio-server 4.0.514 → 4.0.516

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 (26) hide show
  1. package/dist/codemods/delete-jsx-node.d.ts +2 -1
  2. package/dist/codemods/delete-jsx-node.js +10 -5
  3. package/dist/codemods/duplicate-jsx-node.d.ts +3 -3
  4. package/dist/codemods/duplicate-jsx-node.js +10 -313
  5. package/dist/helpers/open-file-for-writing-without-symlinks.d.ts +4 -0
  6. package/dist/helpers/open-file-for-writing-without-symlinks.js +52 -0
  7. package/dist/helpers/video-config-values.js +25 -6
  8. package/dist/preview-server/hmr-timing.d.ts +5 -0
  9. package/dist/preview-server/hmr-timing.js +9 -0
  10. package/dist/preview-server/hot-middleware/index.js +16 -1
  11. package/dist/preview-server/routes/can-update-sequence-props.js +49 -3
  12. package/dist/preview-server/routes/delete-effect-keyframe.d.ts +3 -0
  13. package/dist/preview-server/routes/delete-effect-keyframe.js +89 -0
  14. package/dist/preview-server/routes/delete-jsx-node.js +28 -0
  15. package/dist/preview-server/routes/delete-sequence-keyframe.d.ts +3 -0
  16. package/dist/preview-server/routes/delete-sequence-keyframe.js +82 -0
  17. package/dist/preview-server/routes/download-remote-asset.d.ts +2 -5
  18. package/dist/preview-server/routes/download-remote-asset.js +8 -70
  19. package/dist/preview-server/routes/install-dependency.js +8 -2
  20. package/dist/preview-server/routes/remotion-skills-info.js +11 -1
  21. package/dist/preview-server/routes/save-props-mutex.d.ts +1 -0
  22. package/dist/preview-server/routes/save-props-mutex.js +11 -0
  23. package/dist/preview-server/serve-static.d.ts +2 -1
  24. package/dist/preview-server/serve-static.js +31 -1
  25. package/dist/routes.js +38 -5
  26. package/package.json +9 -9
@@ -1,9 +1,10 @@
1
1
  import { deleteJsxElementAtPath, findJsxElementPathForDeletion, getJsxElementTagLabel } from '@remotion/studio-codemods';
2
2
  import type { SequenceNodePath } from 'remotion';
3
3
  export { deleteJsxElementAtPath, findJsxElementPathForDeletion, getJsxElementTagLabel, };
4
- export declare const deleteJsxNodes: ({ input, nodePaths, prettierConfigOverride, }: {
4
+ export declare const deleteJsxNodes: ({ input, nodePaths, onFormatFile, prettierConfigOverride, }: {
5
5
  input: string;
6
6
  nodePaths: SequenceNodePath[];
7
+ onFormatFile: ((stage: "complete" | "start") => void) | null;
7
8
  prettierConfigOverride?: Record<string, unknown> | null | undefined;
8
9
  }) => Promise<{
9
10
  output: string;
@@ -6,13 +6,18 @@ Object.defineProperty(exports, "deleteJsxElementAtPath", { enumerable: true, get
6
6
  Object.defineProperty(exports, "findJsxElementPathForDeletion", { enumerable: true, get: function () { return studio_codemods_1.findJsxElementPathForDeletion; } });
7
7
  Object.defineProperty(exports, "getJsxElementTagLabel", { enumerable: true, get: function () { return studio_codemods_1.getJsxElementTagLabel; } });
8
8
  const format_file_content_1 = require("./format-file-content");
9
- const deleteJsxNodes = ({ input, nodePaths, prettierConfigOverride, }) => (0, studio_codemods_1.deleteJsxNodes)({
9
+ const deleteJsxNodes = ({ input, nodePaths, onFormatFile, prettierConfigOverride, }) => (0, studio_codemods_1.deleteJsxNodes)({
10
10
  input,
11
11
  nodePaths,
12
- formatFile: ({ contents, prettierConfigOverride: override }) => (0, format_file_content_1.formatFileContent)({
13
- input: contents,
14
- prettierConfigOverride: override,
15
- }),
12
+ formatFile: async ({ contents, prettierConfigOverride: override }) => {
13
+ onFormatFile === null || onFormatFile === void 0 ? void 0 : onFormatFile('start');
14
+ const result = await (0, format_file_content_1.formatFileContent)({
15
+ input: contents,
16
+ prettierConfigOverride: override,
17
+ });
18
+ onFormatFile === null || onFormatFile === void 0 ? void 0 : onFormatFile('complete');
19
+ return result;
20
+ },
16
21
  prettierConfigOverride,
17
22
  });
18
23
  exports.deleteJsxNodes = deleteJsxNodes;
@@ -1,6 +1,6 @@
1
- import type { SequenceNodePathRemapping } from '@remotion/studio-shared';
1
+ import { duplicateJsxElementAtPath } from '@remotion/studio-codemods';
2
2
  import type { SequenceNodePath } from 'remotion';
3
- export declare const duplicateJsxElementAtPath: (jsxPath: import("ast-types/lib/node-path").NodePath<N, V>) => void;
3
+ export { duplicateJsxElementAtPath };
4
4
  export declare const duplicateJsxNode: ({ input, nodePath, prettierConfigOverride, }: {
5
5
  input: string;
6
6
  nodePath: SequenceNodePath;
@@ -10,5 +10,5 @@ export declare const duplicateJsxNode: ({ input, nodePath, prettierConfigOverrid
10
10
  formatted: boolean;
11
11
  nodeLabel: string;
12
12
  logLine: number;
13
- nodePathRemappings: SequenceNodePathRemapping[];
13
+ nodePathRemappings: import("@remotion/studio-shared").SequenceNodePathRemapping[];
14
14
  }>;
@@ -1,319 +1,16 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.duplicateJsxNode = exports.duplicateJsxElementAtPath = void 0;
37
- const types_1 = require("@babel/types");
38
- const recast = __importStar(require("recast"));
39
- const delete_jsx_node_1 = require("./delete-jsx-node");
4
+ const studio_codemods_1 = require("@remotion/studio-codemods");
5
+ Object.defineProperty(exports, "duplicateJsxElementAtPath", { enumerable: true, get: function () { return studio_codemods_1.duplicateJsxElementAtPath; } });
40
6
  const format_file_content_1 = require("./format-file-content");
41
- const get_node_path_remappings_1 = require("./get-node-path-remappings");
42
- const parse_ast_1 = require("./parse-ast");
43
- const { namedTypes } = recast.types;
44
- const makeFragment = (first, second) => ({
45
- type: 'JSXFragment',
46
- openingFragment: { type: 'JSXOpeningFragment' },
47
- closingFragment: { type: 'JSXClosingFragment' },
48
- children: [first, second],
7
+ const duplicateJsxNode = ({ input, nodePath, prettierConfigOverride, }) => (0, studio_codemods_1.duplicateJsxNode)({
8
+ input,
9
+ nodePath,
10
+ formatFile: ({ contents, prettierConfigOverride: override }) => (0, format_file_content_1.formatFileContent)({
11
+ input: contents,
12
+ prettierConfigOverride: override,
13
+ }),
14
+ prettierConfigOverride,
49
15
  });
50
- const uniquifyNamePropOnClone = (clone) => {
51
- for (const attr of clone.openingElement.attributes) {
52
- if (attr.type !== 'JSXAttribute' || attr.name.type !== 'JSXIdentifier') {
53
- continue;
54
- }
55
- if (attr.name.name !== 'name') {
56
- continue;
57
- }
58
- if (!attr.value) {
59
- continue;
60
- }
61
- if (attr.value.type === 'StringLiteral') {
62
- attr.value.value = `${attr.value.value}-copy`;
63
- return;
64
- }
65
- if (attr.value.type === 'JSXExpressionContainer' &&
66
- attr.value.expression.type === 'StringLiteral') {
67
- attr.value.expression.value = `${attr.value.expression.value}-copy`;
68
- return;
69
- }
70
- }
71
- };
72
- const duplicateInLogicalExpression = (parent, node, clone) => {
73
- const frag = makeFragment(node, clone);
74
- if (parent.left === node) {
75
- parent.left = frag;
76
- return true;
77
- }
78
- if (parent.right === node) {
79
- parent.right = frag;
80
- return true;
81
- }
82
- return false;
83
- };
84
- const duplicateInConditionalExpression = (parent, node, clone) => {
85
- const frag = makeFragment(node, clone);
86
- if (parent.consequent === node) {
87
- parent.consequent = frag;
88
- return true;
89
- }
90
- if (parent.alternate === node) {
91
- parent.alternate = frag;
92
- return true;
93
- }
94
- return false;
95
- };
96
- const duplicateInArrowFunctionExpression = (parent, node, clone) => {
97
- const frag = makeFragment(node, clone);
98
- if (parent.body === node) {
99
- parent.body = frag;
100
- return true;
101
- }
102
- return false;
103
- };
104
- const duplicateInReturnStatement = (parent, node, clone) => {
105
- const frag = makeFragment(node, clone);
106
- if (parent.argument === node) {
107
- parent.argument = frag;
108
- return true;
109
- }
110
- return false;
111
- };
112
- const duplicateInCallExpression = (parent, node, clone) => {
113
- const idx = parent.arguments.indexOf(node);
114
- if (idx !== -1) {
115
- parent.arguments.splice(idx + 1, 0, clone);
116
- return true;
117
- }
118
- return false;
119
- };
120
- const duplicateInOptionalCallExpression = (parent, node, clone) => {
121
- const idx = parent.arguments.indexOf(node);
122
- if (idx !== -1) {
123
- parent.arguments.splice(idx + 1, 0, clone);
124
- return true;
125
- }
126
- return false;
127
- };
128
- const duplicateInArrayExpression = (parent, node, clone) => {
129
- const idx = parent.elements.indexOf(node);
130
- if (idx !== -1) {
131
- parent.elements.splice(idx + 1, 0, clone);
132
- return true;
133
- }
134
- return false;
135
- };
136
- const duplicateInAssignmentExpression = (parent, node, clone) => {
137
- const frag = makeFragment(node, clone);
138
- if (parent.right === node) {
139
- parent.right = frag;
140
- return true;
141
- }
142
- return false;
143
- };
144
- const duplicateInVariableDeclarator = (parent, node, clone) => {
145
- const frag = makeFragment(node, clone);
146
- if (parent.init === node) {
147
- parent.init = frag;
148
- return true;
149
- }
150
- return false;
151
- };
152
- const duplicateInExportDefaultDeclaration = (parent, node, clone) => {
153
- const frag = makeFragment(node, clone);
154
- if (parent.declaration === node) {
155
- parent.declaration =
156
- frag;
157
- return true;
158
- }
159
- return false;
160
- };
161
- const duplicateInSequenceExpression = (parent, node, clone) => {
162
- const idx = parent.expressions.indexOf(node);
163
- if (idx !== -1) {
164
- parent.expressions.splice(idx + 1, 0, clone);
165
- return true;
166
- }
167
- return false;
168
- };
169
- const duplicateInNewExpression = (parent, node, clone) => {
170
- const idx = parent.arguments.indexOf(node);
171
- if (idx !== -1) {
172
- parent.arguments.splice(idx + 1, 0, clone);
173
- return true;
174
- }
175
- return false;
176
- };
177
- const duplicateInExpressionStatement = (parent, node, clone) => {
178
- const frag = makeFragment(node, clone);
179
- if (parent.expression === node) {
180
- parent.expression = frag;
181
- return true;
182
- }
183
- return false;
184
- };
185
- const duplicateInParenthesizedExpression = (parent, node, clone) => {
186
- const frag = makeFragment(node, clone);
187
- if (parent.expression === node) {
188
- parent.expression = frag;
189
- return true;
190
- }
191
- return false;
192
- };
193
- const duplicateInJSXExpressionContainer = (parent, node, clone) => {
194
- const frag = makeFragment(node, clone);
195
- if (parent.expression === node) {
196
- parent.expression = frag;
197
- return true;
198
- }
199
- return false;
200
- };
201
- const duplicateInTSAsExpression = (parent, node, clone) => {
202
- const frag = makeFragment(node, clone);
203
- if (parent.expression === node) {
204
- parent.expression = frag;
205
- return true;
206
- }
207
- return false;
208
- };
209
- const duplicateInJSXParentChildren = (parent, node, clone) => {
210
- const idx = parent.children.indexOf(node);
211
- if (idx !== -1) {
212
- parent.children.splice(idx + 1, 0, clone);
213
- return true;
214
- }
215
- return false;
216
- };
217
- const insertDuplicateForParent = (parentNode, node, clone) => {
218
- if (namedTypes.LogicalExpression.check(parentNode)) {
219
- return duplicateInLogicalExpression(parentNode, node, clone);
220
- }
221
- if (namedTypes.ConditionalExpression.check(parentNode)) {
222
- return duplicateInConditionalExpression(parentNode, node, clone);
223
- }
224
- if (namedTypes.ArrowFunctionExpression.check(parentNode)) {
225
- return duplicateInArrowFunctionExpression(parentNode, node, clone);
226
- }
227
- if (namedTypes.ReturnStatement.check(parentNode)) {
228
- return duplicateInReturnStatement(parentNode, node, clone);
229
- }
230
- if (namedTypes.CallExpression.check(parentNode)) {
231
- return duplicateInCallExpression(parentNode, node, clone);
232
- }
233
- if (parentNode.type === 'OptionalCallExpression') {
234
- return duplicateInOptionalCallExpression(parentNode, node, clone);
235
- }
236
- if (namedTypes.ArrayExpression.check(parentNode)) {
237
- return duplicateInArrayExpression(parentNode, node, clone);
238
- }
239
- if (namedTypes.AssignmentExpression.check(parentNode)) {
240
- return duplicateInAssignmentExpression(parentNode, node, clone);
241
- }
242
- if (namedTypes.VariableDeclarator.check(parentNode)) {
243
- return duplicateInVariableDeclarator(parentNode, node, clone);
244
- }
245
- if (namedTypes.ExportDefaultDeclaration.check(parentNode)) {
246
- return duplicateInExportDefaultDeclaration(parentNode, node, clone);
247
- }
248
- if (namedTypes.SequenceExpression.check(parentNode)) {
249
- return duplicateInSequenceExpression(parentNode, node, clone);
250
- }
251
- if (namedTypes.NewExpression.check(parentNode)) {
252
- return duplicateInNewExpression(parentNode, node, clone);
253
- }
254
- if (namedTypes.ExpressionStatement.check(parentNode)) {
255
- return duplicateInExpressionStatement(parentNode, node, clone);
256
- }
257
- if (namedTypes.ParenthesizedExpression.check(parentNode)) {
258
- return duplicateInParenthesizedExpression(parentNode, node, clone);
259
- }
260
- if (namedTypes.JSXExpressionContainer.check(parentNode)) {
261
- return duplicateInJSXExpressionContainer(parentNode, node, clone);
262
- }
263
- if (namedTypes.TSAsExpression.check(parentNode)) {
264
- return duplicateInTSAsExpression(parentNode, node, clone);
265
- }
266
- if (namedTypes.JSXElement.check(parentNode)) {
267
- return duplicateInJSXParentChildren(parentNode, node, clone);
268
- }
269
- if (namedTypes.JSXFragment.check(parentNode)) {
270
- return duplicateInJSXParentChildren(parentNode, node, clone);
271
- }
272
- return false;
273
- };
274
- const duplicateJsxElementAtPath = (jsxPath) => {
275
- const { node, parentPath } = jsxPath;
276
- if (!parentPath) {
277
- throw new Error('Cannot duplicate JSX element with no parent');
278
- }
279
- const jsxNode = node;
280
- const clone = (0, types_1.cloneNode)(jsxNode, true);
281
- uniquifyNamePropOnClone(clone);
282
- if (insertDuplicateForParent(parentPath.node, jsxNode, clone)) {
283
- return;
284
- }
285
- jsxPath.replace(makeFragment(jsxNode, clone));
286
- };
287
- exports.duplicateJsxElementAtPath = duplicateJsxElementAtPath;
288
- const duplicateJsxNode = async ({ input, nodePath, prettierConfigOverride, }) => {
289
- var _a, _b;
290
- var _c, _d;
291
- const ast = (0, parse_ast_1.parseAst)(input);
292
- const capturedNodePaths = (0, get_node_path_remappings_1.captureJsxNodePaths)(ast);
293
- const jsxPath = (0, delete_jsx_node_1.findJsxElementPathForDeletion)(ast, nodePath);
294
- if (!jsxPath) {
295
- throw new Error('Could not find a JSX element at the specified location to duplicate');
296
- }
297
- const jsxElement = jsxPath.node;
298
- const nodeLabel = (0, delete_jsx_node_1.getJsxElementTagLabel)(jsxElement);
299
- const logLine = (_d = (_c = (_a = jsxElement.openingElement.loc) === null || _a === void 0 ? void 0 : _a.start.line) !== null && _c !== void 0 ? _c : (_b = jsxElement.loc) === null || _b === void 0 ? void 0 : _b.start.line) !== null && _d !== void 0 ? _d : 1;
300
- (0, exports.duplicateJsxElementAtPath)(jsxPath);
301
- const finalFile = (0, parse_ast_1.serializeAst)(ast);
302
- const { output, formatted } = await (0, format_file_content_1.formatFileContent)({
303
- input: finalFile,
304
- prettierConfigOverride,
305
- });
306
- const { nodePathRemappings } = (0, get_node_path_remappings_1.getNodePathRemappings)({
307
- ast,
308
- captured: capturedNodePaths,
309
- output,
310
- });
311
- return {
312
- output,
313
- formatted,
314
- nodeLabel,
315
- logLine,
316
- nodePathRemappings,
317
- };
318
- };
319
16
  exports.duplicateJsxNode = duplicateJsxNode;
@@ -0,0 +1,4 @@
1
+ export declare const openFileForWritingWithoutSymlinks: ({ rootDirectory, absolutePath, }: {
2
+ rootDirectory: string;
3
+ absolutePath: string;
4
+ }) => number;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.openFileForWritingWithoutSymlinks = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const assertNoSymlinks = ({ rootDirectory, absolutePath, }) => {
10
+ const relativePath = node_path_1.default.relative(rootDirectory, absolutePath);
11
+ if (relativePath === '..' ||
12
+ relativePath.startsWith(`..${node_path_1.default.sep}`) ||
13
+ node_path_1.default.isAbsolute(relativePath)) {
14
+ throw new Error(`Not allowed to write to ${relativePath}`);
15
+ }
16
+ let pathToCheck = rootDirectory;
17
+ for (const segment of relativePath.split(node_path_1.default.sep)) {
18
+ pathToCheck = node_path_1.default.join(pathToCheck, segment);
19
+ try {
20
+ if (node_fs_1.default.lstatSync(pathToCheck).isSymbolicLink()) {
21
+ throw new Error(`Not allowed to write through symbolic link ${pathToCheck}`);
22
+ }
23
+ }
24
+ catch (error) {
25
+ if (error.code === 'ENOENT') {
26
+ break;
27
+ }
28
+ throw error;
29
+ }
30
+ }
31
+ };
32
+ const openFileForWritingWithoutSymlinks = ({ rootDirectory, absolutePath, }) => {
33
+ const resolvedRootDirectory = node_path_1.default.resolve(rootDirectory);
34
+ const resolvedAbsolutePath = node_path_1.default.resolve(absolutePath);
35
+ assertNoSymlinks({
36
+ rootDirectory: resolvedRootDirectory,
37
+ absolutePath: resolvedAbsolutePath,
38
+ });
39
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(resolvedAbsolutePath), { recursive: true });
40
+ assertNoSymlinks({
41
+ rootDirectory: resolvedRootDirectory,
42
+ absolutePath: resolvedAbsolutePath,
43
+ });
44
+ const flags = process.platform === 'win32'
45
+ ? 'w'
46
+ : node_fs_1.default.constants.O_CREAT |
47
+ node_fs_1.default.constants.O_WRONLY |
48
+ node_fs_1.default.constants.O_TRUNC |
49
+ node_fs_1.default.constants.O_NOFOLLOW;
50
+ return node_fs_1.default.openSync(resolvedAbsolutePath, flags);
51
+ };
52
+ exports.openFileForWritingWithoutSymlinks = openFileForWritingWithoutSymlinks;
@@ -36,15 +36,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getVideoConfigIdentifierValues = void 0;
37
37
  const recast = __importStar(require("recast"));
38
38
  const getVideoConfigIdentifierValues = ({ ast, videoConfigValues, }) => {
39
- if (videoConfigValues === null) {
40
- return {};
41
- }
42
39
  const candidates = new Map();
43
40
  const otherDeclarations = new Set();
41
+ const addCandidate = (identifier, value) => {
42
+ if (candidates.has(identifier) || otherDeclarations.has(identifier)) {
43
+ candidates.delete(identifier);
44
+ otherDeclarations.add(identifier);
45
+ return;
46
+ }
47
+ candidates.set(identifier, value);
48
+ };
44
49
  recast.types.visit(ast, {
45
50
  visitVariableDeclarator(path) {
46
51
  const { id, init } = path.node;
47
- const isVideoConfigDeclaration = id.type === 'ObjectPattern' &&
52
+ const isVideoConfigDeclaration = videoConfigValues !== null &&
53
+ id.type === 'ObjectPattern' &&
48
54
  (init === null || init === void 0 ? void 0 : init.type) === 'CallExpression' &&
49
55
  init.callee.type === 'Identifier' &&
50
56
  init.callee.name === 'useVideoConfig' &&
@@ -66,12 +72,25 @@ const getVideoConfigIdentifierValues = ({ ast, videoConfigValues, }) => {
66
72
  }
67
73
  const value = videoConfigValues[configKey];
68
74
  if (Number.isFinite(value)) {
69
- candidates.set(property.value.name, value);
75
+ addCandidate(property.value.name, value);
70
76
  }
71
77
  }
72
78
  }
73
79
  else if (id.type === 'Identifier') {
74
- otherDeclarations.add(id.name);
80
+ const declaration = path.parentPath.node;
81
+ const numericConstant = declaration.type === 'VariableDeclaration' &&
82
+ declaration.kind === 'const' &&
83
+ (init === null || init === void 0 ? void 0 : init.type) === 'NumericLiteral' &&
84
+ Number.isFinite(init.value)
85
+ ? init.value
86
+ : null;
87
+ if (numericConstant !== null) {
88
+ addCandidate(id.name, numericConstant);
89
+ }
90
+ else {
91
+ candidates.delete(id.name);
92
+ otherDeclarations.add(id.name);
93
+ }
75
94
  }
76
95
  this.traverse(path);
77
96
  },
@@ -0,0 +1,5 @@
1
+ export declare const logHmrTiming: ({ detail, logLevel, stage, }: {
2
+ detail: string | null;
3
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
4
+ stage: string;
5
+ }) => void;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logHmrTiming = void 0;
4
+ const renderer_1 = require("@remotion/renderer");
5
+ const logHmrTiming = ({ detail, logLevel, stage, }) => {
6
+ const detailSuffix = detail ? ` ${detail}` : '';
7
+ renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[hmr-timing] epoch=${Date.now()} monotonic=${performance.now().toFixed(3)} stage=${stage}${detailSuffix}`);
8
+ };
9
+ exports.logHmrTiming = logHmrTiming;
@@ -7,14 +7,23 @@
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.setupWebpackHmr = void 0;
9
9
  const renderer_1 = require("@remotion/renderer");
10
+ const hmr_timing_1 = require("../hmr-timing");
10
11
  const setupWebpackHmr = (compiler, logLevel, liveEventsServer) => {
11
12
  let latestStats = null;
12
13
  const publishHmr = (hmrEvent) => {
13
14
  liveEventsServer.sendEventToClient({ type: 'hmr', hmrEvent });
14
15
  };
15
16
  compiler.hooks.invalid.tap('remotion', onInvalid);
17
+ compiler.hooks.watchRun.tap('remotion-hmr-timing', () => {
18
+ (0, hmr_timing_1.logHmrTiming)({ detail: null, logLevel, stage: 'compiler-watch-run' });
19
+ });
16
20
  compiler.hooks.done.tap('remotion', onDone);
17
- function onInvalid() {
21
+ function onInvalid(fileName, changeTime) {
22
+ (0, hmr_timing_1.logHmrTiming)({
23
+ detail: `file=${fileName !== null && fileName !== void 0 ? fileName : 'manual'} changeTime=${changeTime}`,
24
+ logLevel,
25
+ stage: 'compiler-invalid',
26
+ });
18
27
  latestStats = null;
19
28
  renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Building...');
20
29
  publishHmr({
@@ -22,9 +31,15 @@ const setupWebpackHmr = (compiler, logLevel, liveEventsServer) => {
22
31
  });
23
32
  }
24
33
  function onDone(statsResult) {
34
+ (0, hmr_timing_1.logHmrTiming)({
35
+ detail: `reportedBuildDuration=${statsResult.endTime - statsResult.startTime}ms`,
36
+ logLevel,
37
+ stage: 'compiler-done',
38
+ });
25
39
  // Keep hold of latest stats so they can be propagated to new clients
26
40
  latestStats = statsResult;
27
41
  publishStats('built', latestStats, publishHmr);
42
+ (0, hmr_timing_1.logHmrTiming)({ detail: null, logLevel, stage: 'hmr-stats-published' });
28
43
  }
29
44
  liveEventsServer.addNewClientListener(() => {
30
45
  if (latestStats) {
@@ -775,6 +775,10 @@ const getPropsStatus = (jsxElement, ast, videoConfigValues, assetKeys) => {
775
775
  const props = {};
776
776
  for (const attr of jsxElement.attributes) {
777
777
  if (attr.type === 'JSXSpreadAttribute') {
778
+ // The spread may override every prop written before it
779
+ for (const key of Object.keys(props)) {
780
+ props[key] = computedStatus();
781
+ }
778
782
  continue;
779
783
  }
780
784
  if (attr.name.type === 'JSXNamespacedName') {
@@ -1113,14 +1117,28 @@ const getBorderRadiusShorthandStatus = ({ propValue, ast, videoConfigValues, })
1113
1117
  }
1114
1118
  return computedStatus();
1115
1119
  };
1116
- const getNestedPropStatus = ({ jsxElement, ast, parentKey, childKey, videoConfigValues, allowSpecialValues, }) => {
1117
- const attr = jsxElement.attributes.find((a) => a.type !== 'JSXSpreadAttribute' &&
1120
+ const getNestedPropStatus = ({ jsxElement, ast, parentKey, childKey, videoConfigValues, allowSpecialValues, lastSpreadIndex, }) => {
1121
+ const attrIndex = jsxElement.attributes.findIndex((a) => a.type !== 'JSXSpreadAttribute' &&
1118
1122
  a.name.type !== 'JSXNamespacedName' &&
1119
1123
  a.name.name === parentKey);
1120
- if (!attr || !attr.value) {
1124
+ const attr = attrIndex === -1
1125
+ ? undefined
1126
+ : jsxElement.attributes[attrIndex];
1127
+ if (attrIndex !== -1 && attrIndex < lastSpreadIndex) {
1128
+ // A later spread may replace the whole parent object
1129
+ return computedStatus();
1130
+ }
1131
+ if (!attr) {
1132
+ if (lastSpreadIndex !== -1) {
1133
+ // The spread may provide the parent object
1134
+ return computedStatus();
1135
+ }
1121
1136
  // Parent attribute doesn't exist, nested prop can be added
1122
1137
  return staticStatus(undefined, null);
1123
1138
  }
1139
+ if (!attr.value) {
1140
+ return staticStatus(undefined, null);
1141
+ }
1124
1142
  if (attr.value.type !== 'JSXExpressionContainer') {
1125
1143
  return computedStatus();
1126
1144
  }
@@ -1210,6 +1228,13 @@ const computeEffectsForJsx = ({ ast, jsxElement, effects, videoConfigValues, })
1210
1228
  }));
1211
1229
  };
1212
1230
  const computeSequenceOnlyPropsRecord = ({ jsxElement, jsxElementNode, ast, keys, assetKeys, videoConfigValues, }) => {
1231
+ // A JSX spread attribute ({...props}) is invisible to the parser and may
1232
+ // set or override every prop that is not explicitly written after it.
1233
+ // Affected props are treated as computed so the runtime values pass
1234
+ // through untouched and Visual Mode does not offer to edit them.
1235
+ // Attributes written after the last spread win at runtime and can be
1236
+ // parsed as usual.
1237
+ const lastSpreadIndex = jsxElement.attributes.reduce((highest, attr, index) => attr.type === 'JSXSpreadAttribute' ? index : highest, -1);
1213
1238
  const allProps = getPropsStatus(jsxElement, ast, videoConfigValues, assetKeys);
1214
1239
  const filteredProps = {};
1215
1240
  const mixedBorderRadius = hasMixedBorderRadiusRepresentation(jsxElement);
@@ -1221,6 +1246,14 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, jsxElementNode, ast, keys,
1221
1246
  continue;
1222
1247
  }
1223
1248
  if (key === 'children') {
1249
+ const childrenAttrIndex = jsxElement.attributes.findIndex((attr) => attr.type === 'JSXAttribute' &&
1250
+ attr.name.type === 'JSXIdentifier' &&
1251
+ attr.name.name === 'children');
1252
+ if (childrenAttrIndex !== -1 && childrenAttrIndex < lastSpreadIndex) {
1253
+ // A later spread may override the children attribute
1254
+ filteredProps[key] = computedStatus();
1255
+ continue;
1256
+ }
1224
1257
  const staticChildrenAttribute = (0, exports.getStaticJsxChildrenAttribute)(jsxElement);
1225
1258
  if (staticChildrenAttribute) {
1226
1259
  filteredProps[key] = staticStatus(staticChildrenAttribute.value, null);
@@ -1230,6 +1263,14 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, jsxElementNode, ast, keys,
1230
1263
  filteredProps[key] = computedStatus();
1231
1264
  continue;
1232
1265
  }
1266
+ // JSX element children are compiled after any spread and win over
1267
+ // it. Only if the element body is empty may the spread provide the
1268
+ // children.
1269
+ const hasMeaningfulJsxChildren = jsxElementNode.children.some((candidate) => !(candidate.type === 'JSXText' && candidate.value.trim() === ''));
1270
+ if (!hasMeaningfulJsxChildren && lastSpreadIndex !== -1) {
1271
+ filteredProps[key] = computedStatus();
1272
+ continue;
1273
+ }
1233
1274
  const staticTextContent = (0, exports.getStaticJsxTextContent)(jsxElementNode);
1234
1275
  filteredProps[key] = staticTextContent
1235
1276
  ? staticStatus(staticTextContent.value, null)
@@ -1245,11 +1286,16 @@ const computeSequenceOnlyPropsRecord = ({ jsxElement, jsxElementNode, ast, keys,
1245
1286
  childKey: key.slice(dotIndex + 1),
1246
1287
  videoConfigValues,
1247
1288
  allowSpecialValues: assetKeys.includes(key),
1289
+ lastSpreadIndex,
1248
1290
  });
1249
1291
  }
1250
1292
  else if (key in allProps) {
1251
1293
  filteredProps[key] = allProps[key];
1252
1294
  }
1295
+ else if (lastSpreadIndex !== -1) {
1296
+ // The spread may provide this prop
1297
+ filteredProps[key] = computedStatus();
1298
+ }
1253
1299
  else {
1254
1300
  filteredProps[key] = staticStatus(undefined, null);
1255
1301
  }
@@ -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>;