@remotion/studio-server 4.0.481 → 4.0.483
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.
- package/dist/codemods/duplicate-composition.js +14 -0
- package/dist/codemods/effect-param-expression.js +10 -0
- package/dist/codemods/recast-mods.js +100 -0
- package/dist/codemods/update-keyframes/update-keyframes.js +50 -4
- package/dist/helpers/resolve-composition-component.d.ts +19 -2
- package/dist/helpers/resolve-composition-component.js +72 -18
- package/dist/preview-server/api-routes.js +2 -0
- package/dist/preview-server/handler.js +2 -0
- package/dist/preview-server/routes/apply-codemod.js +80 -14
- package/dist/preview-server/routes/insert-element.d.ts +3 -0
- package/dist/preview-server/routes/insert-element.js +189 -0
- package/dist/preview-server/start-server.js +1 -1
- package/dist/preview-server/undo-stack.d.ts +5 -3
- package/dist/preview-server/undo-stack.js +12 -3
- package/dist/routes.js +1 -1
- package/package.json +6 -6
|
@@ -80,6 +80,20 @@ const parseAndApplyCodemod = ({ input, codeMod, }) => {
|
|
|
80
80
|
localName: codeMod.tag,
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
|
+
if (codeMod.type === 'new-composition') {
|
|
84
|
+
(0, imports_1.ensureNamedImport)({
|
|
85
|
+
ast: newAst,
|
|
86
|
+
importedName: 'Composition',
|
|
87
|
+
sourcePath: 'remotion',
|
|
88
|
+
localName: 'Composition',
|
|
89
|
+
});
|
|
90
|
+
(0, imports_1.ensureNamedImport)({
|
|
91
|
+
ast: newAst,
|
|
92
|
+
importedName: codeMod.componentName,
|
|
93
|
+
sourcePath: codeMod.componentImportPath,
|
|
94
|
+
localName: codeMod.componentName,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
83
97
|
const output = (0, parse_ast_1.serializeAst)(newAst);
|
|
84
98
|
return { changesMade, newContents: output };
|
|
85
99
|
};
|
|
@@ -82,6 +82,16 @@ const makeEasingExpression = ({ easing, easingLocalName, }) => {
|
|
|
82
82
|
b.objectProperty(b.identifier('damping'), (0, update_nested_prop_1.parseValueExpression)(easing.damping)),
|
|
83
83
|
b.objectProperty(b.identifier('mass'), (0, update_nested_prop_1.parseValueExpression)(easing.mass)),
|
|
84
84
|
b.objectProperty(b.identifier('stiffness'), (0, update_nested_prop_1.parseValueExpression)(easing.stiffness)),
|
|
85
|
+
...(easing.allowTail === null
|
|
86
|
+
? []
|
|
87
|
+
: [
|
|
88
|
+
b.objectProperty(b.identifier('allowTail'), b.booleanLiteral(easing.allowTail)),
|
|
89
|
+
]),
|
|
90
|
+
...(easing.durationRestThreshold === null
|
|
91
|
+
? []
|
|
92
|
+
: [
|
|
93
|
+
b.objectProperty(b.identifier('durationRestThreshold'), (0, update_nested_prop_1.parseValueExpression)(easing.durationRestThreshold)),
|
|
94
|
+
]),
|
|
85
95
|
b.objectProperty(b.identifier('overshootClamping'), b.booleanLiteral(easing.overshootClamping)),
|
|
86
96
|
]),
|
|
87
97
|
]);
|
|
@@ -1,7 +1,42 @@
|
|
|
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
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.applyCodemod = void 0;
|
|
37
|
+
const recast = __importStar(require("recast"));
|
|
4
38
|
const apply_visual_control_1 = require("./apply-visual-control");
|
|
39
|
+
const b = recast.types.builders;
|
|
5
40
|
const applyCodemod = ({ file, codeMod, }) => {
|
|
6
41
|
const changesMade = [];
|
|
7
42
|
if (codeMod.type === 'apply-visual-control') {
|
|
@@ -60,6 +95,14 @@ const mapReturnStatement = (statement, transformation, changesMade, parentFolder
|
|
|
60
95
|
if (!statement.argument) {
|
|
61
96
|
return statement;
|
|
62
97
|
}
|
|
98
|
+
if (transformation.type === 'new-composition' &&
|
|
99
|
+
(statement.argument.type === 'JSXElement' ||
|
|
100
|
+
statement.argument.type === 'JSXFragment')) {
|
|
101
|
+
return {
|
|
102
|
+
...statement,
|
|
103
|
+
argument: addNewCompositionToRootJsx(statement.argument, transformation, changesMade),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
63
106
|
const replacement = transformLoneJsxElement(statement.argument, transformation, changesMade, parentFolderName);
|
|
64
107
|
if (replacement !== null) {
|
|
65
108
|
return { ...statement, argument: replacement };
|
|
@@ -99,6 +142,55 @@ const isJsxExpression = (child) => {
|
|
|
99
142
|
const isMeaningfulJsxChild = (child) => {
|
|
100
143
|
return child.type !== 'JSXText' || child.value.trim() !== '';
|
|
101
144
|
};
|
|
145
|
+
const jsxId = (name) => ({ type: 'JSXIdentifier', name });
|
|
146
|
+
const jsxAttributeWithString = (name, value) => ({
|
|
147
|
+
type: 'JSXAttribute',
|
|
148
|
+
name: jsxId(name),
|
|
149
|
+
value: { type: 'StringLiteral', value },
|
|
150
|
+
});
|
|
151
|
+
const jsxAttributeWithExpression = (name, expression) => ({
|
|
152
|
+
type: 'JSXAttribute',
|
|
153
|
+
name: jsxId(name),
|
|
154
|
+
value: {
|
|
155
|
+
type: 'JSXExpressionContainer',
|
|
156
|
+
expression,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
const newCompositionElement = (transformation) => {
|
|
160
|
+
return {
|
|
161
|
+
type: 'JSXElement',
|
|
162
|
+
openingElement: {
|
|
163
|
+
type: 'JSXOpeningElement',
|
|
164
|
+
name: jsxId('Composition'),
|
|
165
|
+
attributes: [
|
|
166
|
+
jsxAttributeWithString('id', transformation.newId),
|
|
167
|
+
jsxAttributeWithExpression('component', b.identifier(transformation.componentName)),
|
|
168
|
+
jsxAttributeWithExpression('durationInFrames', b.numericLiteral(transformation.newDurationInFrames)),
|
|
169
|
+
jsxAttributeWithExpression('fps', b.numericLiteral(transformation.newFps)),
|
|
170
|
+
jsxAttributeWithExpression('width', b.numericLiteral(transformation.newWidth)),
|
|
171
|
+
jsxAttributeWithExpression('height', b.numericLiteral(transformation.newHeight)),
|
|
172
|
+
],
|
|
173
|
+
selfClosing: true,
|
|
174
|
+
},
|
|
175
|
+
closingElement: null,
|
|
176
|
+
children: [],
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const addNewCompositionToRootJsx = (root, transformation, changesMade) => {
|
|
180
|
+
if (changesMade.length > 0) {
|
|
181
|
+
return root;
|
|
182
|
+
}
|
|
183
|
+
changesMade.push({
|
|
184
|
+
description: 'Added new composition',
|
|
185
|
+
});
|
|
186
|
+
if (root.type === 'JSXFragment') {
|
|
187
|
+
return {
|
|
188
|
+
...root,
|
|
189
|
+
children: [...root.children, newCompositionElement(transformation)],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return wrapInJsxFragment([root, newCompositionElement(transformation)]);
|
|
193
|
+
};
|
|
102
194
|
// When a <Composition> JSX element appears in a position where it cannot
|
|
103
195
|
// simply be removed from a parent's children list (e.g. as the sole return
|
|
104
196
|
// value of a wrapper component or as the concise body of an arrow function),
|
|
@@ -237,6 +329,14 @@ const mapRecognizedType = (expression, transformation, changesMade, parentFolder
|
|
|
237
329
|
}
|
|
238
330
|
if (expression.type === 'ArrowFunctionExpression' ||
|
|
239
331
|
expression.type === 'FunctionExpression') {
|
|
332
|
+
if (transformation.type === 'new-composition' &&
|
|
333
|
+
(expression.body.type === 'JSXElement' ||
|
|
334
|
+
expression.body.type === 'JSXFragment')) {
|
|
335
|
+
return {
|
|
336
|
+
...expression,
|
|
337
|
+
body: addNewCompositionToRootJsx(expression.body, transformation, changesMade),
|
|
338
|
+
};
|
|
339
|
+
}
|
|
240
340
|
if (expression.type === 'ArrowFunctionExpression' &&
|
|
241
341
|
expression.body.type === 'JSXElement') {
|
|
242
342
|
const replacement = transformLoneJsxElement(expression.body, transformation, changesMade, parentFolderName);
|
|
@@ -340,6 +340,16 @@ const createEasingExpression = (easing) => {
|
|
|
340
340
|
b.objectProperty(b.identifier('damping'), (0, update_nested_prop_1.parseValueExpression)(easing.damping)),
|
|
341
341
|
b.objectProperty(b.identifier('mass'), (0, update_nested_prop_1.parseValueExpression)(easing.mass)),
|
|
342
342
|
b.objectProperty(b.identifier('stiffness'), (0, update_nested_prop_1.parseValueExpression)(easing.stiffness)),
|
|
343
|
+
...(easing.allowTail === null
|
|
344
|
+
? []
|
|
345
|
+
: [
|
|
346
|
+
b.objectProperty(b.identifier('allowTail'), b.booleanLiteral(easing.allowTail)),
|
|
347
|
+
]),
|
|
348
|
+
...(easing.durationRestThreshold === null
|
|
349
|
+
? []
|
|
350
|
+
: [
|
|
351
|
+
b.objectProperty(b.identifier('durationRestThreshold'), (0, update_nested_prop_1.parseValueExpression)(easing.durationRestThreshold)),
|
|
352
|
+
]),
|
|
343
353
|
b.objectProperty(b.identifier('overshootClamping'), b.booleanLiteral(easing.overshootClamping)),
|
|
344
354
|
]),
|
|
345
355
|
]);
|
|
@@ -371,7 +381,7 @@ const getInlineOptionsFromExtraArgs = (extraArgs) => {
|
|
|
371
381
|
}
|
|
372
382
|
return existingOptions;
|
|
373
383
|
};
|
|
374
|
-
const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, nextSegmentCount, }) => {
|
|
384
|
+
const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, nextSegmentCount, insertedKeyframeIndex, nextKeyframeCount, }) => {
|
|
375
385
|
const options = getInlineOptionsFromExtraArgs(extraArgs);
|
|
376
386
|
if (!options) {
|
|
377
387
|
return { extraArgs, needsEasingImport: false };
|
|
@@ -383,6 +393,16 @@ const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, n
|
|
|
383
393
|
if (easing === null) {
|
|
384
394
|
return { extraArgs, needsEasingImport: false };
|
|
385
395
|
}
|
|
396
|
+
if (easing.length < nextSegmentCount) {
|
|
397
|
+
const isSplittingExistingSegment = insertedKeyframeIndex > 0 &&
|
|
398
|
+
insertedKeyframeIndex < nextKeyframeCount - 1;
|
|
399
|
+
const easingIndexToDuplicate = isSplittingExistingSegment && easing.length > 0
|
|
400
|
+
? Math.min(insertedKeyframeIndex - 1, easing.length - 1)
|
|
401
|
+
: null;
|
|
402
|
+
easing.splice(insertedKeyframeIndex, 0, easingIndexToDuplicate === null
|
|
403
|
+
? studio_shared_1.LINEAR_KEYFRAME_EASING
|
|
404
|
+
: easing[easingIndexToDuplicate]);
|
|
405
|
+
}
|
|
386
406
|
while (easing.length < nextSegmentCount) {
|
|
387
407
|
easing.push(studio_shared_1.LINEAR_KEYFRAME_EASING);
|
|
388
408
|
}
|
|
@@ -391,6 +411,15 @@ const normalizeEasingAfterAddingKeyframe = ({ extraArgs, previousSegmentCount, n
|
|
|
391
411
|
needsEasingImport: setEasingOption({ options, easing }),
|
|
392
412
|
};
|
|
393
413
|
};
|
|
414
|
+
const getEasingIndexToRemove = ({ removedKeyframeIndex, keyframeCountBeforeRemoval, }) => {
|
|
415
|
+
if (removedKeyframeIndex === 0) {
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
if (removedKeyframeIndex === keyframeCountBeforeRemoval - 1) {
|
|
419
|
+
return removedKeyframeIndex - 1;
|
|
420
|
+
}
|
|
421
|
+
return removedKeyframeIndex;
|
|
422
|
+
};
|
|
394
423
|
const normalizeEasingAfterRemovingKeyframe = ({ extraArgs, previousSegmentCount, nextSegmentCount, removedKeyframeIndex, }) => {
|
|
395
424
|
const options = getInlineOptionsFromExtraArgs(extraArgs);
|
|
396
425
|
if (!options) {
|
|
@@ -404,7 +433,10 @@ const normalizeEasingAfterRemovingKeyframe = ({ extraArgs, previousSegmentCount,
|
|
|
404
433
|
return { extraArgs, needsEasingImport: false };
|
|
405
434
|
}
|
|
406
435
|
if (easing.length > 0) {
|
|
407
|
-
const easingIndexToRemove =
|
|
436
|
+
const easingIndexToRemove = getEasingIndexToRemove({
|
|
437
|
+
removedKeyframeIndex,
|
|
438
|
+
keyframeCountBeforeRemoval: previousSegmentCount + 1,
|
|
439
|
+
});
|
|
408
440
|
easing.splice(easingIndexToRemove, 1);
|
|
409
441
|
}
|
|
410
442
|
while (easing.length > nextSegmentCount) {
|
|
@@ -430,11 +462,21 @@ const normalizeEasingAfterRemovingKeyframes = ({ extraArgs, previousSegmentCount
|
|
|
430
462
|
if (easing === null) {
|
|
431
463
|
return { extraArgs, needsEasingImport: false };
|
|
432
464
|
}
|
|
433
|
-
for (const removedKeyframeIndex
|
|
465
|
+
for (const { removedKeyframeIndex, keyframeCountBeforeRemoval } of [
|
|
466
|
+
...removedKeyframeIndexes,
|
|
467
|
+
]
|
|
468
|
+
.sort((first, second) => second - first)
|
|
469
|
+
.map((keyframeIndex, index) => ({
|
|
470
|
+
removedKeyframeIndex: keyframeIndex,
|
|
471
|
+
keyframeCountBeforeRemoval: previousSegmentCount + 1 - index,
|
|
472
|
+
}))) {
|
|
434
473
|
if (easing.length === 0) {
|
|
435
474
|
break;
|
|
436
475
|
}
|
|
437
|
-
const easingIndexToRemove =
|
|
476
|
+
const easingIndexToRemove = getEasingIndexToRemove({
|
|
477
|
+
removedKeyframeIndex,
|
|
478
|
+
keyframeCountBeforeRemoval,
|
|
479
|
+
});
|
|
438
480
|
easing.splice(easingIndexToRemove, 1);
|
|
439
481
|
}
|
|
440
482
|
while (easing.length > nextSegmentCount) {
|
|
@@ -587,6 +629,10 @@ const addKeyframe = ({ expression, key, frame, value, schema, }) => {
|
|
|
587
629
|
extraArgs: existing.extraArgs,
|
|
588
630
|
previousSegmentCount: Math.max(existing.keyframes.length - 1, 0),
|
|
589
631
|
nextSegmentCount: Math.max(nextKeyframes.length - 1, 0),
|
|
632
|
+
insertedKeyframeIndex: [...nextKeyframes]
|
|
633
|
+
.sort((first, second) => first.frame - second.frame)
|
|
634
|
+
.findIndex((keyframe) => keyframe.frame === frame),
|
|
635
|
+
nextKeyframeCount: nextKeyframes.length,
|
|
590
636
|
})
|
|
591
637
|
: { extraArgs: existing.extraArgs, needsEasingImport: false };
|
|
592
638
|
return {
|
|
@@ -1,21 +1,38 @@
|
|
|
1
|
-
import { type InsertableCompositionElement } from '@remotion/studio-shared';
|
|
1
|
+
import { type InsertableCompositionElement, type InsertableCompositionElementPosition } from '@remotion/studio-shared';
|
|
2
2
|
export type ResolvedCompositionComponent = {
|
|
3
3
|
source: string;
|
|
4
4
|
line: number;
|
|
5
5
|
column: number;
|
|
6
6
|
canAddSequence: boolean;
|
|
7
7
|
};
|
|
8
|
+
export type ResolvedCompositionComponentWithFile = ResolvedCompositionComponent & {
|
|
9
|
+
fileName: string;
|
|
10
|
+
exportName: string | 'default';
|
|
11
|
+
};
|
|
12
|
+
export declare const resolveCompositionComponentWithFile: ({ remotionRoot, compositionFile, compositionId, }: {
|
|
13
|
+
remotionRoot: string;
|
|
14
|
+
compositionFile: string;
|
|
15
|
+
compositionId: string;
|
|
16
|
+
}) => Promise<ResolvedCompositionComponentWithFile>;
|
|
8
17
|
export declare const resolveCompositionComponent: ({ remotionRoot, compositionFile, compositionId, }: {
|
|
9
18
|
remotionRoot: string;
|
|
10
19
|
compositionFile: string;
|
|
11
20
|
compositionId: string;
|
|
12
21
|
}) => Promise<ResolvedCompositionComponent>;
|
|
13
|
-
export declare const insertJsxElementIntoComposition: ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, }: {
|
|
22
|
+
export declare const insertJsxElementIntoComposition: ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, wrapInSequence, }: {
|
|
14
23
|
remotionRoot: string;
|
|
15
24
|
compositionFile: string;
|
|
16
25
|
compositionId: string;
|
|
17
26
|
element: InsertableCompositionElement;
|
|
18
27
|
prettierConfigOverride: Record<string, unknown> | null;
|
|
28
|
+
wrapInSequence?: {
|
|
29
|
+
dimensions: {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
name: string | null;
|
|
34
|
+
position: InsertableCompositionElementPosition | null;
|
|
35
|
+
} | null | undefined;
|
|
19
36
|
}) => Promise<{
|
|
20
37
|
fileName: string;
|
|
21
38
|
source: string;
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.insertJsxElementIntoComposition = exports.resolveCompositionComponent = void 0;
|
|
39
|
+
exports.insertJsxElementIntoComposition = exports.resolveCompositionComponent = exports.resolveCompositionComponentWithFile = void 0;
|
|
40
40
|
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");
|
|
@@ -522,14 +522,31 @@ const roundTranslateCoordinate = (value) => {
|
|
|
522
522
|
return Object.is(rounded, -0) ? 0 : rounded;
|
|
523
523
|
};
|
|
524
524
|
const formatTranslateValue = ({ x, y }) => `${roundTranslateCoordinate(x)}px ${roundTranslateCoordinate(y)}px`;
|
|
525
|
-
const
|
|
525
|
+
const createStyleAttribute = (properties) => {
|
|
526
|
+
return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier('style'), recast.types.builders.jsxExpressionContainer(recast.types.builders.objectExpression(properties)));
|
|
527
|
+
};
|
|
528
|
+
const getPositionStyleProperties = (position) => {
|
|
526
529
|
const properties = [
|
|
527
530
|
recast.types.builders.objectProperty(recast.types.builders.identifier('position'), recast.types.builders.stringLiteral('absolute')),
|
|
528
531
|
];
|
|
529
532
|
if (position) {
|
|
530
533
|
properties.push(recast.types.builders.objectProperty(recast.types.builders.identifier('translate'), recast.types.builders.stringLiteral(formatTranslateValue(position))));
|
|
531
534
|
}
|
|
532
|
-
return
|
|
535
|
+
return properties;
|
|
536
|
+
};
|
|
537
|
+
const createPositionAbsoluteStyleAttribute = (position) => {
|
|
538
|
+
return createStyleAttribute(getPositionStyleProperties(position));
|
|
539
|
+
};
|
|
540
|
+
const createAssetStyleAttribute = ({ dimensions, position, }) => {
|
|
541
|
+
return createStyleAttribute([
|
|
542
|
+
...getPositionStyleProperties(position),
|
|
543
|
+
...(dimensions
|
|
544
|
+
? [
|
|
545
|
+
recast.types.builders.objectProperty(recast.types.builders.identifier('width'), recast.types.builders.numericLiteral(dimensions.width)),
|
|
546
|
+
recast.types.builders.objectProperty(recast.types.builders.identifier('height'), recast.types.builders.numericLiteral(dimensions.height)),
|
|
547
|
+
]
|
|
548
|
+
: []),
|
|
549
|
+
]);
|
|
533
550
|
};
|
|
534
551
|
const createStaticFileSrcAttribute = ({ staticFileLocalName, src, }) => {
|
|
535
552
|
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)])));
|
|
@@ -553,25 +570,29 @@ const createSolidElement = ({ localName, width, height, position, }) => {
|
|
|
553
570
|
createPositionAbsoluteStyleAttribute(position),
|
|
554
571
|
], true), null, []);
|
|
555
572
|
};
|
|
556
|
-
const createComponentElement = ({ localName, props, position, }) => {
|
|
573
|
+
const createComponentElement = ({ addPositionStyle, localName, props, position, }) => {
|
|
557
574
|
return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
|
|
558
575
|
...props.map(createComponentProp),
|
|
559
|
-
|
|
576
|
+
...(addPositionStyle
|
|
577
|
+
? [createPositionAbsoluteStyleAttribute(position)]
|
|
578
|
+
: []),
|
|
560
579
|
], true), null, []);
|
|
561
580
|
};
|
|
581
|
+
const createSequenceWrappedElement = ({ child, dimensions, name, position, sequenceLocalName, }) => {
|
|
582
|
+
return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(sequenceLocalName), [
|
|
583
|
+
...(name === null ? [] : [createStringAttribute('name', name)]),
|
|
584
|
+
createNumberAttribute('width', dimensions.width),
|
|
585
|
+
createNumberAttribute('height', dimensions.height),
|
|
586
|
+
createPositionAbsoluteStyleAttribute(position),
|
|
587
|
+
], false), recast.types.builders.jsxClosingElement(recast.types.builders.jsxIdentifier(sequenceLocalName)), [child]);
|
|
588
|
+
};
|
|
562
589
|
const createAssetElement = ({ addPositionStyle, localName, staticFileLocalName, src, dimensions, position, }) => {
|
|
563
590
|
return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), [
|
|
564
591
|
staticFileLocalName === null
|
|
565
592
|
? createStringSrcAttribute(src)
|
|
566
593
|
: createStaticFileSrcAttribute({ staticFileLocalName, src }),
|
|
567
594
|
...(addPositionStyle
|
|
568
|
-
? [
|
|
569
|
-
: []),
|
|
570
|
-
...(dimensions
|
|
571
|
-
? [
|
|
572
|
-
createNumberAttribute('width', dimensions.width),
|
|
573
|
-
createNumberAttribute('height', dimensions.height),
|
|
574
|
-
]
|
|
595
|
+
? [createAssetStyleAttribute({ dimensions, position })]
|
|
575
596
|
: []),
|
|
576
597
|
], true), null, []);
|
|
577
598
|
};
|
|
@@ -680,6 +701,24 @@ const ensureSolidImport = (ast) => {
|
|
|
680
701
|
localName: getAvailableSolidLocalName(ast),
|
|
681
702
|
});
|
|
682
703
|
};
|
|
704
|
+
const getAvailableSequenceLocalName = (ast) => {
|
|
705
|
+
const candidates = ['Sequence', 'RemotionSequence'];
|
|
706
|
+
const available = candidates.find((candidate) => {
|
|
707
|
+
return !hasTopLevelBinding({ ast, name: candidate });
|
|
708
|
+
});
|
|
709
|
+
if (!available) {
|
|
710
|
+
throw new Error('Cannot add <Sequence> because Sequence is already defined');
|
|
711
|
+
}
|
|
712
|
+
return available;
|
|
713
|
+
};
|
|
714
|
+
const ensureSequenceImport = (ast) => {
|
|
715
|
+
return (0, imports_1.ensureNamedImport)({
|
|
716
|
+
ast,
|
|
717
|
+
importedName: 'Sequence',
|
|
718
|
+
sourcePath: 'remotion',
|
|
719
|
+
localName: getAvailableSequenceLocalName(ast),
|
|
720
|
+
});
|
|
721
|
+
};
|
|
683
722
|
const getImportDeclarations = ({ ast, sourcePath, }) => {
|
|
684
723
|
return ast.program.body.filter((node) => node.type === 'ImportDeclaration' &&
|
|
685
724
|
node.source.type === 'StringLiteral' &&
|
|
@@ -1007,8 +1046,9 @@ const resolveCompositionComponentWithFile = async ({ remotionRoot, compositionFi
|
|
|
1007
1046
|
visited: new Set(),
|
|
1008
1047
|
});
|
|
1009
1048
|
};
|
|
1049
|
+
exports.resolveCompositionComponentWithFile = resolveCompositionComponentWithFile;
|
|
1010
1050
|
const resolveCompositionComponent = async ({ remotionRoot, compositionFile, compositionId, }) => {
|
|
1011
|
-
const { source, line, column, canAddSequence } = await resolveCompositionComponentWithFile({
|
|
1051
|
+
const { source, line, column, canAddSequence } = await (0, exports.resolveCompositionComponentWithFile)({
|
|
1012
1052
|
remotionRoot,
|
|
1013
1053
|
compositionFile,
|
|
1014
1054
|
compositionId,
|
|
@@ -1021,7 +1061,7 @@ const resolveCompositionComponent = async ({ remotionRoot, compositionFile, comp
|
|
|
1021
1061
|
};
|
|
1022
1062
|
};
|
|
1023
1063
|
exports.resolveCompositionComponent = resolveCompositionComponent;
|
|
1024
|
-
const createInsertableJsxElement = ({ ast, element, }) => {
|
|
1064
|
+
const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element, }) => {
|
|
1025
1065
|
if (element.type === 'solid') {
|
|
1026
1066
|
const solidLocalName = ensureSolidImport(ast);
|
|
1027
1067
|
return createSolidElement({
|
|
@@ -1039,6 +1079,7 @@ const createInsertableJsxElement = ({ ast, element, }) => {
|
|
|
1039
1079
|
importPath: element.importPath,
|
|
1040
1080
|
});
|
|
1041
1081
|
return createComponentElement({
|
|
1082
|
+
addPositionStyle: addPositionStyleToComponent,
|
|
1042
1083
|
localName: componentLocalName,
|
|
1043
1084
|
props: element.props,
|
|
1044
1085
|
position: element.position,
|
|
@@ -1079,8 +1120,8 @@ const createInsertableJsxElement = ({ ast, element, }) => {
|
|
|
1079
1120
|
}
|
|
1080
1121
|
throw new Error('Unsupported element type');
|
|
1081
1122
|
};
|
|
1082
|
-
const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, }) => {
|
|
1083
|
-
const location = await resolveCompositionComponentWithFile({
|
|
1123
|
+
const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, wrapInSequence = null, }) => {
|
|
1124
|
+
const location = await (0, exports.resolveCompositionComponentWithFile)({
|
|
1084
1125
|
remotionRoot,
|
|
1085
1126
|
compositionFile,
|
|
1086
1127
|
compositionId,
|
|
@@ -1093,11 +1134,24 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
|
|
|
1093
1134
|
fileName: location.fileName,
|
|
1094
1135
|
});
|
|
1095
1136
|
const ast = (0, parse_ast_1.parseAst)(input);
|
|
1096
|
-
const elementToInsert = createInsertableJsxElement({
|
|
1137
|
+
const elementToInsert = createInsertableJsxElement({
|
|
1138
|
+
addPositionStyleToComponent: wrapInSequence === null,
|
|
1139
|
+
ast,
|
|
1140
|
+
element,
|
|
1141
|
+
});
|
|
1142
|
+
const finalElementToInsert = wrapInSequence
|
|
1143
|
+
? createSequenceWrappedElement({
|
|
1144
|
+
child: elementToInsert,
|
|
1145
|
+
dimensions: wrapInSequence.dimensions,
|
|
1146
|
+
name: wrapInSequence.name,
|
|
1147
|
+
position: wrapInSequence.position,
|
|
1148
|
+
sequenceLocalName: ensureSequenceImport(ast),
|
|
1149
|
+
})
|
|
1150
|
+
: elementToInsert;
|
|
1097
1151
|
const logLine = addElementToComponentRoot({
|
|
1098
1152
|
ast,
|
|
1099
1153
|
exportName: location.exportName,
|
|
1100
|
-
element:
|
|
1154
|
+
element: finalElementToInsert,
|
|
1101
1155
|
});
|
|
1102
1156
|
const finalFile = (0, parse_ast_1.serializeAst)(ast);
|
|
1103
1157
|
const { output, formatted } = await (0, format_file_content_1.formatFileContent)({
|
|
@@ -17,6 +17,7 @@ const delete_static_file_1 = require("./routes/delete-static-file");
|
|
|
17
17
|
const download_remote_asset_1 = require("./routes/download-remote-asset");
|
|
18
18
|
const duplicate_effect_1 = require("./routes/duplicate-effect");
|
|
19
19
|
const duplicate_jsx_node_1 = require("./routes/duplicate-jsx-node");
|
|
20
|
+
const insert_element_1 = require("./routes/insert-element");
|
|
20
21
|
const insert_jsx_element_1 = require("./routes/insert-jsx-element");
|
|
21
22
|
const install_dependency_1 = require("./routes/install-dependency");
|
|
22
23
|
const log_studio_error_1 = require("./routes/log-studio-error");
|
|
@@ -88,6 +89,7 @@ exports.allApiRoutes = {
|
|
|
88
89
|
'/api/restart-studio': restart_studio_1.handleRestartStudio,
|
|
89
90
|
'/api/install-package': install_dependency_1.handleInstallPackage,
|
|
90
91
|
'/api/insert-jsx-element': insert_jsx_element_1.insertJsxElementHandler,
|
|
92
|
+
'/api/insert-element': insert_element_1.insertElementHandler,
|
|
91
93
|
'/api/download-remote-asset': download_remote_asset_1.downloadRemoteAssetHandler,
|
|
92
94
|
'/api/undo': undo_1.undoHandler,
|
|
93
95
|
'/api/redo': redo_1.redoHandler,
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleRequest = void 0;
|
|
4
4
|
const parse_body_1 = require("./parse-body");
|
|
5
|
+
const validate_same_origin_1 = require("./validate-same-origin");
|
|
5
6
|
const handleRequest = async ({ remotionRoot, request, response, entryPoint, handler, logLevel, methods, binariesDirectory, publicDir, }) => {
|
|
6
7
|
if (request.method === 'OPTIONS') {
|
|
7
8
|
response.statusCode = 200;
|
|
8
9
|
response.end();
|
|
9
10
|
return;
|
|
10
11
|
}
|
|
12
|
+
(0, validate_same_origin_1.validateSameOrigin)(request);
|
|
11
13
|
response.setHeader('content-type', 'application/json');
|
|
12
14
|
response.writeHead(200);
|
|
13
15
|
try {
|
|
@@ -8,11 +8,20 @@ const node_fs_1 = require("node:fs");
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const renderer_1 = require("@remotion/renderer");
|
|
10
10
|
const apply_codemod_to_file_1 = require("../../codemods/apply-codemod-to-file");
|
|
11
|
+
const duplicate_composition_1 = require("../../codemods/duplicate-composition");
|
|
11
12
|
const simple_diff_1 = require("../../codemods/simple-diff");
|
|
12
13
|
const file_watcher_1 = require("../../file-watcher");
|
|
13
14
|
const project_info_1 = require("../project-info");
|
|
14
15
|
const undo_stack_1 = require("../undo-stack");
|
|
15
16
|
const can_update_default_props_1 = require("./can-update-default-props");
|
|
17
|
+
const formatNewCompositionFile = (componentName) => {
|
|
18
|
+
return (0, duplicate_composition_1.formatOutput)(`import React from 'react';
|
|
19
|
+
|
|
20
|
+
export const ${componentName}: React.FC = () => {
|
|
21
|
+
return null;
|
|
22
|
+
};
|
|
23
|
+
`);
|
|
24
|
+
};
|
|
16
25
|
const getCodemodUndoDescription = (codemod) => {
|
|
17
26
|
if (codemod.type === 'delete-composition') {
|
|
18
27
|
return {
|
|
@@ -37,6 +46,13 @@ const getCodemodUndoDescription = (codemod) => {
|
|
|
37
46
|
entryType: codemod.type,
|
|
38
47
|
};
|
|
39
48
|
}
|
|
49
|
+
if (codemod.type === 'new-composition') {
|
|
50
|
+
return {
|
|
51
|
+
undoMessage: `↩️ Creation of composition "${codemod.newId}"`,
|
|
52
|
+
redoMessage: `↪️ Creation of composition "${codemod.newId}"`,
|
|
53
|
+
entryType: codemod.type,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
40
56
|
if (codemod.type === 'delete-folder') {
|
|
41
57
|
const label = `folder "${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}"`;
|
|
42
58
|
return {
|
|
@@ -62,7 +78,7 @@ const getCodemodUndoDescription = (codemod) => {
|
|
|
62
78
|
};
|
|
63
79
|
};
|
|
64
80
|
const applyCodemodHandler = async ({ input: { codemod, dryRun, symbolicatedStack }, logLevel, remotionRoot, entryPoint, }) => {
|
|
65
|
-
var _a;
|
|
81
|
+
var _a, _b;
|
|
66
82
|
try {
|
|
67
83
|
const time = Date.now();
|
|
68
84
|
const filePath = symbolicatedStack
|
|
@@ -72,6 +88,14 @@ const applyCodemodHandler = async ({ input: { codemod, dryRun, symbolicatedStack
|
|
|
72
88
|
throw new Error('Cannot find file for composition in project');
|
|
73
89
|
}
|
|
74
90
|
(0, can_update_default_props_1.checkIfTypeScriptFile)(filePath);
|
|
91
|
+
const newCompositionComponentFilePath = codemod.type === 'new-composition'
|
|
92
|
+
? node_path_1.default.join(node_path_1.default.dirname(filePath), `${codemod.componentName}.tsx`)
|
|
93
|
+
: null;
|
|
94
|
+
if (codemod.type === 'new-composition' &&
|
|
95
|
+
newCompositionComponentFilePath &&
|
|
96
|
+
(0, node_fs_1.existsSync)(newCompositionComponentFilePath)) {
|
|
97
|
+
throw new Error(`Cannot create ${node_path_1.default.relative(remotionRoot, newCompositionComponentFilePath)} because it already exists`);
|
|
98
|
+
}
|
|
75
99
|
const input = (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
76
100
|
const formatted = await (0, apply_codemod_to_file_1.applyCodemodToFile)({
|
|
77
101
|
filePath,
|
|
@@ -83,22 +107,64 @@ const applyCodemodHandler = async ({ input: { codemod, dryRun, symbolicatedStack
|
|
|
83
107
|
});
|
|
84
108
|
if (!dryRun) {
|
|
85
109
|
const { entryType, undoMessage, redoMessage } = getCodemodUndoDescription(codemod);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
logLine: (_a = symbolicatedStack === null || symbolicatedStack === void 0 ? void 0 : symbolicatedStack.originalLineNumber) !== null && _a !== void 0 ? _a : 1,
|
|
93
|
-
description: {
|
|
94
|
-
undoMessage,
|
|
95
|
-
redoMessage,
|
|
110
|
+
const snapshots = [
|
|
111
|
+
{
|
|
112
|
+
filePath,
|
|
113
|
+
oldContents: input,
|
|
114
|
+
newContents: null,
|
|
115
|
+
logLine: (_a = symbolicatedStack === null || symbolicatedStack === void 0 ? void 0 : symbolicatedStack.originalLineNumber) !== null && _a !== void 0 ? _a : 1,
|
|
96
116
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
117
|
+
];
|
|
118
|
+
let componentFilePath = null;
|
|
119
|
+
let componentFileContents = null;
|
|
120
|
+
if (codemod.type === 'new-composition') {
|
|
121
|
+
componentFilePath = newCompositionComponentFilePath;
|
|
122
|
+
if (componentFilePath === null) {
|
|
123
|
+
throw new Error('Could not determine the new component file path');
|
|
124
|
+
}
|
|
125
|
+
componentFileContents = await formatNewCompositionFile(codemod.componentName);
|
|
126
|
+
snapshots.push({
|
|
127
|
+
filePath: componentFilePath,
|
|
128
|
+
oldContents: null,
|
|
129
|
+
newContents: componentFileContents,
|
|
130
|
+
logLine: 1,
|
|
131
|
+
});
|
|
132
|
+
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
133
|
+
snapshots,
|
|
134
|
+
logLevel,
|
|
135
|
+
remotionRoot,
|
|
136
|
+
description: {
|
|
137
|
+
undoMessage,
|
|
138
|
+
redoMessage,
|
|
139
|
+
},
|
|
140
|
+
entryType,
|
|
141
|
+
suppressHmrOnFileRestore: false,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
(0, undo_stack_1.pushToUndoStack)({
|
|
146
|
+
filePath,
|
|
147
|
+
oldContents: input,
|
|
148
|
+
newContents: null,
|
|
149
|
+
logLevel,
|
|
150
|
+
remotionRoot,
|
|
151
|
+
logLine: (_b = symbolicatedStack === null || symbolicatedStack === void 0 ? void 0 : symbolicatedStack.originalLineNumber) !== null && _b !== void 0 ? _b : 1,
|
|
152
|
+
description: {
|
|
153
|
+
undoMessage,
|
|
154
|
+
redoMessage,
|
|
155
|
+
},
|
|
156
|
+
entryType,
|
|
157
|
+
suppressHmrOnFileRestore: false,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
100
160
|
(0, undo_stack_1.suppressUndoStackInvalidation)(filePath);
|
|
161
|
+
if (componentFilePath) {
|
|
162
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(componentFilePath);
|
|
163
|
+
}
|
|
101
164
|
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(filePath, formatted, undefined);
|
|
165
|
+
if (componentFilePath && componentFileContents !== null) {
|
|
166
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(componentFilePath, componentFileContents, undefined);
|
|
167
|
+
}
|
|
102
168
|
const end = Date.now() - time;
|
|
103
169
|
const relativePath = node_path_1.default.relative(remotionRoot, filePath);
|
|
104
170
|
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blue(`Edited ${relativePath} in ${end}ms`));
|
|
@@ -0,0 +1,189 @@
|
|
|
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.insertElementHandler = void 0;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const renderer_1 = require("@remotion/renderer");
|
|
10
|
+
const studio_shared_1 = require("@remotion/studio-shared");
|
|
11
|
+
const file_watcher_1 = require("../../file-watcher");
|
|
12
|
+
const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
|
|
13
|
+
const format_log_file_location_1 = require("../format-log-file-location");
|
|
14
|
+
const undo_stack_1 = require("../undo-stack");
|
|
15
|
+
const log_update_1 = require("./log-updates/log-update");
|
|
16
|
+
const save_props_mutex_1 = require("./save-props-mutex");
|
|
17
|
+
const validatePosition = (position) => {
|
|
18
|
+
if (position === null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (!Number.isFinite(position.x) || !Number.isFinite(position.y)) {
|
|
22
|
+
throw new Error('Position must be finite');
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const isInside = ({ child, parent }) => {
|
|
26
|
+
const relative = node_path_1.default.relative(parent, child);
|
|
27
|
+
return (relative === '' ||
|
|
28
|
+
(!relative.startsWith('..') && !node_path_1.default.isAbsolute(relative)));
|
|
29
|
+
};
|
|
30
|
+
const withoutTsxExtension = (fileName) => {
|
|
31
|
+
return fileName.replace(/\.tsx$/, '');
|
|
32
|
+
};
|
|
33
|
+
const normalizeSourceForComparison = (source) => {
|
|
34
|
+
return source.replace(/\r\n/g, '\n').trim();
|
|
35
|
+
};
|
|
36
|
+
const makeRelativeImportPath = ({ fromFile, toFile, }) => {
|
|
37
|
+
const withoutExtension = withoutTsxExtension(toFile);
|
|
38
|
+
let relative = node_path_1.default
|
|
39
|
+
.relative(node_path_1.default.dirname(fromFile), withoutExtension)
|
|
40
|
+
.split(node_path_1.default.sep)
|
|
41
|
+
.join('/');
|
|
42
|
+
if (!relative.startsWith('.')) {
|
|
43
|
+
relative = `./${relative}`;
|
|
44
|
+
}
|
|
45
|
+
return relative;
|
|
46
|
+
};
|
|
47
|
+
const validateDimensions = (dimensions) => {
|
|
48
|
+
if (!Number.isFinite(dimensions.width) ||
|
|
49
|
+
!Number.isFinite(dimensions.height) ||
|
|
50
|
+
dimensions.width <= 0 ||
|
|
51
|
+
dimensions.height <= 0) {
|
|
52
|
+
throw new Error('Element dimensions must be positive finite numbers');
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const validateElement = (element) => {
|
|
56
|
+
if ((0, studio_shared_1.makeElementFileNameFromSlug)(element.slug) === null) {
|
|
57
|
+
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
58
|
+
}
|
|
59
|
+
if (typeof element.sourceCode !== 'string' ||
|
|
60
|
+
element.sourceCode.trim().length === 0 ||
|
|
61
|
+
element.sourceCode.length > 200000) {
|
|
62
|
+
throw new Error('Unsupported Element source code');
|
|
63
|
+
}
|
|
64
|
+
if ((0, studio_shared_1.getElementComponentNameFromSourceCode)(element.sourceCode) === null) {
|
|
65
|
+
throw new Error('Element source must export exactly one named component');
|
|
66
|
+
}
|
|
67
|
+
validateDimensions(element.dimensions);
|
|
68
|
+
};
|
|
69
|
+
const insertElementHandler = ({ input: { compositionFile, compositionId, element, position }, remotionRoot, logLevel, }) => (0, save_props_mutex_1.withSavePropsLock)(async () => {
|
|
70
|
+
try {
|
|
71
|
+
validateElement(element);
|
|
72
|
+
validatePosition(position);
|
|
73
|
+
const componentName = (0, studio_shared_1.getElementComponentNameFromSourceCode)(element.sourceCode);
|
|
74
|
+
if (componentName === null) {
|
|
75
|
+
throw new Error('Element source must export exactly one named component');
|
|
76
|
+
}
|
|
77
|
+
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.slug}"`);
|
|
78
|
+
const location = await (0, resolve_composition_component_1.resolveCompositionComponentWithFile)({
|
|
79
|
+
remotionRoot,
|
|
80
|
+
compositionFile,
|
|
81
|
+
compositionId,
|
|
82
|
+
});
|
|
83
|
+
if (!location.canAddSequence) {
|
|
84
|
+
throw new Error('Cannot insert Element into this composition component');
|
|
85
|
+
}
|
|
86
|
+
const derivedElementFileName = (0, studio_shared_1.makeElementFileNameFromSlug)(element.slug);
|
|
87
|
+
if (derivedElementFileName === null) {
|
|
88
|
+
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
89
|
+
}
|
|
90
|
+
const elementFileName = node_path_1.default.resolve(node_path_1.default.dirname(location.fileName), derivedElementFileName);
|
|
91
|
+
if (!isInside({ child: elementFileName, parent: remotionRoot })) {
|
|
92
|
+
throw new Error('Element file must stay inside the Remotion project');
|
|
93
|
+
}
|
|
94
|
+
const elementFileExists = (0, node_fs_1.existsSync)(elementFileName);
|
|
95
|
+
if (elementFileExists) {
|
|
96
|
+
const existingSource = (0, node_fs_1.readFileSync)(elementFileName, 'utf-8');
|
|
97
|
+
if (normalizeSourceForComparison(existingSource) !==
|
|
98
|
+
normalizeSourceForComparison(element.sourceCode)) {
|
|
99
|
+
throw new Error(`Element file already exists with different contents: ${derivedElementFileName}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const importPath = makeRelativeImportPath({
|
|
103
|
+
fromFile: location.fileName,
|
|
104
|
+
toFile: elementFileName,
|
|
105
|
+
});
|
|
106
|
+
const inserted = await (0, resolve_composition_component_1.insertJsxElementIntoComposition)({
|
|
107
|
+
remotionRoot,
|
|
108
|
+
compositionFile,
|
|
109
|
+
compositionId,
|
|
110
|
+
element: {
|
|
111
|
+
type: 'component',
|
|
112
|
+
componentName,
|
|
113
|
+
importName: componentName,
|
|
114
|
+
importPath,
|
|
115
|
+
props: [],
|
|
116
|
+
position: null,
|
|
117
|
+
},
|
|
118
|
+
prettierConfigOverride: null,
|
|
119
|
+
wrapInSequence: {
|
|
120
|
+
dimensions: element.dimensions,
|
|
121
|
+
name: element.displayName,
|
|
122
|
+
position,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
126
|
+
snapshots: [
|
|
127
|
+
...(elementFileExists
|
|
128
|
+
? []
|
|
129
|
+
: [
|
|
130
|
+
{
|
|
131
|
+
filePath: elementFileName,
|
|
132
|
+
oldContents: null,
|
|
133
|
+
newContents: element.sourceCode,
|
|
134
|
+
logLine: 1,
|
|
135
|
+
},
|
|
136
|
+
]),
|
|
137
|
+
{
|
|
138
|
+
filePath: inserted.fileName,
|
|
139
|
+
oldContents: inserted.oldContents,
|
|
140
|
+
newContents: inserted.output,
|
|
141
|
+
logLine: inserted.logLine,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
logLevel,
|
|
145
|
+
remotionRoot,
|
|
146
|
+
description: {
|
|
147
|
+
undoMessage: `↩️ Added ${element.displayName}`,
|
|
148
|
+
redoMessage: `↪️ Added ${element.displayName}`,
|
|
149
|
+
},
|
|
150
|
+
entryType: 'insert-jsx-element',
|
|
151
|
+
suppressHmrOnFileRestore: false,
|
|
152
|
+
});
|
|
153
|
+
if (!elementFileExists) {
|
|
154
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(elementFileName);
|
|
155
|
+
}
|
|
156
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(inserted.fileName);
|
|
157
|
+
if (!elementFileExists) {
|
|
158
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(elementFileName, element.sourceCode, undefined);
|
|
159
|
+
}
|
|
160
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(inserted.fileName, inserted.output, undefined);
|
|
161
|
+
const compositionLocationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
162
|
+
remotionRoot,
|
|
163
|
+
absolutePath: inserted.fileName,
|
|
164
|
+
line: inserted.logLine,
|
|
165
|
+
});
|
|
166
|
+
const elementLocationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
167
|
+
remotionRoot,
|
|
168
|
+
absolutePath: elementFileName,
|
|
169
|
+
line: 1,
|
|
170
|
+
});
|
|
171
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(elementLocationLabel)} ${elementFileExists ? 'Reused existing Element source' : 'Created Element source'}`);
|
|
172
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(compositionLocationLabel)} Added <${componentName}>`);
|
|
173
|
+
if (!inserted.formatted) {
|
|
174
|
+
(0, log_update_1.warnAboutPrettierOnce)(logLevel);
|
|
175
|
+
}
|
|
176
|
+
(0, undo_stack_1.printUndoHint)(logLevel);
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
reason: err.message,
|
|
185
|
+
stack: err.stack,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
exports.insertElementHandler = insertElementHandler;
|
|
@@ -73,7 +73,7 @@ const startServer = async (options) => {
|
|
|
73
73
|
const server = node_http_1.default.createServer((request, response) => {
|
|
74
74
|
if (options.enableCrossSiteIsolation) {
|
|
75
75
|
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
76
|
-
response.setHeader('Cross-Origin-Embedder-Policy', '
|
|
76
|
+
response.setHeader('Cross-Origin-Embedder-Policy', 'credentialless');
|
|
77
77
|
}
|
|
78
78
|
new Promise((resolve) => {
|
|
79
79
|
wdmMiddleware(request, response, () => {
|
|
@@ -3,10 +3,10 @@ export interface UndoEntryDescription {
|
|
|
3
3
|
undoMessage: string;
|
|
4
4
|
redoMessage: string;
|
|
5
5
|
}
|
|
6
|
-
type UndoEntryType = 'visual-control' | 'default-props' | 'sequence-props' | 'effect-props' | 'keyframe-add' | 'keyframe-delete' | 'add-effect' | 'delete-effect' | 'duplicate-effect' | 'paste-effects' | 'reorder-effect' | 'reorder-sequence' | 'delete-jsx-node' | 'duplicate-jsx-node' | 'insert-jsx-element' | 'delete-composition' | 'rename-composition' | 'duplicate-composition' | 'delete-folder' | 'rename-folder';
|
|
6
|
+
type UndoEntryType = 'visual-control' | 'default-props' | 'sequence-props' | 'effect-props' | 'keyframe-add' | 'keyframe-delete' | 'add-effect' | 'delete-effect' | 'duplicate-effect' | 'paste-effects' | 'reorder-effect' | 'reorder-sequence' | 'delete-jsx-node' | 'duplicate-jsx-node' | 'insert-jsx-element' | 'delete-composition' | 'rename-composition' | 'new-composition' | 'duplicate-composition' | 'delete-folder' | 'rename-folder';
|
|
7
7
|
type UndoEntrySnapshot = {
|
|
8
8
|
filePath: string;
|
|
9
|
-
oldContents: string;
|
|
9
|
+
oldContents: string | null;
|
|
10
10
|
newContents: string | null;
|
|
11
11
|
/** 1-based source line for terminal/IDE file links (e.g. path:line). */
|
|
12
12
|
logLine: number;
|
|
@@ -53,6 +53,8 @@ type UndoEntry = {
|
|
|
53
53
|
entryType: 'delete-composition';
|
|
54
54
|
} | {
|
|
55
55
|
entryType: 'rename-composition';
|
|
56
|
+
} | {
|
|
57
|
+
entryType: 'new-composition';
|
|
56
58
|
} | {
|
|
57
59
|
entryType: 'duplicate-composition';
|
|
58
60
|
} | {
|
|
@@ -74,7 +76,7 @@ export declare function pushToUndoStack({ filePath, oldContents, newContents, lo
|
|
|
74
76
|
export declare function pushTransactionToUndoStack({ snapshots, logLevel, remotionRoot, description, entryType, suppressHmrOnFileRestore }: {
|
|
75
77
|
snapshots: Array<{
|
|
76
78
|
filePath: string;
|
|
77
|
-
oldContents: string;
|
|
79
|
+
oldContents: string | null;
|
|
78
80
|
newContents: string | null;
|
|
79
81
|
logLine: number;
|
|
80
82
|
}>;
|
|
@@ -233,7 +233,9 @@ function popUndo() {
|
|
|
233
233
|
var _a;
|
|
234
234
|
return {
|
|
235
235
|
...snapshot,
|
|
236
|
-
newContents: (_a = snapshot.newContents) !== null && _a !== void 0 ? _a : (0, node_fs_1.
|
|
236
|
+
newContents: (_a = snapshot.newContents) !== null && _a !== void 0 ? _a : ((0, node_fs_1.existsSync)(snapshot.filePath)
|
|
237
|
+
? (0, node_fs_1.readFileSync)(snapshot.filePath, 'utf-8')
|
|
238
|
+
: null),
|
|
237
239
|
};
|
|
238
240
|
});
|
|
239
241
|
redoStack.push(makeUndoEntry({
|
|
@@ -250,13 +252,20 @@ function popUndo() {
|
|
|
250
252
|
if (entry.suppressHmrOnFileRestore) {
|
|
251
253
|
(0, watch_ignore_next_change_1.suppressBundlerUpdateForFile)(snapshot.filePath);
|
|
252
254
|
}
|
|
253
|
-
|
|
255
|
+
if (snapshot.oldContents === null) {
|
|
256
|
+
(0, node_fs_1.rmSync)(snapshot.filePath, { force: true });
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(snapshot.filePath, snapshot.oldContents, undefined);
|
|
260
|
+
}
|
|
254
261
|
}
|
|
255
262
|
renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel: storedLogLevel }, renderer_1.RenderInternals.chalk.gray(`Undo: restored ${entry.filePath} (undo: ${undoStack.length}, redo: ${redoStack.length})`));
|
|
256
263
|
logFileAction(entry.description.undoMessage, entry.filePath, entry.logLine);
|
|
257
264
|
if (entry.entryType === 'visual-control') {
|
|
258
265
|
for (const snapshot of entry.snapshots) {
|
|
259
|
-
|
|
266
|
+
if (snapshot.oldContents !== null) {
|
|
267
|
+
emitVisualControlChanges(snapshot.oldContents);
|
|
268
|
+
}
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
271
|
for (const filePath of getEntryFilePaths(entry)) {
|
package/dist/routes.js
CHANGED
|
@@ -105,7 +105,7 @@ const handleFallback = async ({ remotionRoot, hash, response, request, getCurren
|
|
|
105
105
|
response.setHeader('content-type', 'text/html');
|
|
106
106
|
if (enableCrossSiteIsolation) {
|
|
107
107
|
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
108
|
-
response.setHeader('Cross-Origin-Embedder-Policy', '
|
|
108
|
+
response.setHeader('Cross-Origin-Embedder-Policy', 'credentialless');
|
|
109
109
|
}
|
|
110
110
|
const packageManager = (0, get_package_manager_1.getPackageManager)({
|
|
111
111
|
remotionRoot,
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio-server"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/studio-server",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.483",
|
|
7
7
|
"description": "Run a Remotion Studio with a server backend",
|
|
8
8
|
"main": "dist",
|
|
9
9
|
"scripts": {
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@babel/parser": "7.24.1",
|
|
28
28
|
"semver": "7.5.3",
|
|
29
29
|
"prettier": "3.8.1",
|
|
30
|
-
"remotion": "4.0.
|
|
30
|
+
"remotion": "4.0.483",
|
|
31
31
|
"recast": "0.23.11",
|
|
32
|
-
"@remotion/bundler": "4.0.
|
|
33
|
-
"@remotion/renderer": "4.0.
|
|
34
|
-
"@remotion/studio-shared": "4.0.
|
|
32
|
+
"@remotion/bundler": "4.0.483",
|
|
33
|
+
"@remotion/renderer": "4.0.483",
|
|
34
|
+
"@remotion/studio-shared": "4.0.483",
|
|
35
35
|
"memfs": "3.4.3",
|
|
36
36
|
"open": "8.4.2"
|
|
37
37
|
},
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ast-types": "0.16.1",
|
|
40
40
|
"react": "19.2.3",
|
|
41
41
|
"@types/semver": "7.5.3",
|
|
42
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
42
|
+
"@remotion/eslint-config-internal": "4.0.483",
|
|
43
43
|
"eslint": "9.19.0",
|
|
44
44
|
"@types/node": "20.12.14",
|
|
45
45
|
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|