@remotion/studio-server 4.0.485 → 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.
- package/dist/codemods/duplicate-composition.js +8 -0
- package/dist/codemods/recast-mods.js +209 -5
- package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +2 -0
- package/dist/codemods/update-sequence-props/update-sequence-props.js +344 -2
- package/dist/helpers/import-agnostic-node-path.js +30 -4
- package/dist/helpers/resolve-composition-component.d.ts +1 -0
- package/dist/helpers/resolve-composition-component.js +206 -9
- package/dist/preview-server/routes/apply-codemod.js +19 -0
- package/dist/preview-server/routes/insert-jsx-element.js +51 -2
- package/dist/preview-server/routes/save-sequence-props.d.ts +3 -1
- package/dist/preview-server/routes/save-sequence-props.js +207 -23
- package/dist/preview-server/undo-stack.d.ts +5 -1
- package/package.json +6 -6
|
@@ -49,6 +49,26 @@ const getUseCurrentFrameLocalNames = (ast) => {
|
|
|
49
49
|
}
|
|
50
50
|
return names;
|
|
51
51
|
};
|
|
52
|
+
const getGoogleFontLoadFontLocalNames = (ast) => {
|
|
53
|
+
var _a;
|
|
54
|
+
var _b;
|
|
55
|
+
const names = new Set();
|
|
56
|
+
for (const statement of ast.program.body) {
|
|
57
|
+
if (statement.type !== 'ImportDeclaration' ||
|
|
58
|
+
typeof statement.source.value !== 'string' ||
|
|
59
|
+
!statement.source.value.startsWith('@remotion/google-fonts/')) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const specifier of statement.specifiers) {
|
|
63
|
+
if (specifier.type === 'ImportSpecifier' &&
|
|
64
|
+
specifier.imported.type === 'Identifier' &&
|
|
65
|
+
specifier.imported.name === 'loadFont') {
|
|
66
|
+
names.add((_b = (_a = specifier.local) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'loadFont');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return names;
|
|
71
|
+
};
|
|
52
72
|
const isUseCurrentFrameCall = (node, useCurrentFrameLocalNames) => {
|
|
53
73
|
return ((node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' &&
|
|
54
74
|
node.callee.type === 'Identifier' &&
|
|
@@ -63,13 +83,19 @@ const isUseCurrentFrameStatement = (node, useCurrentFrameLocalNames) => {
|
|
|
63
83
|
}
|
|
64
84
|
return isUseCurrentFrameCall(node.declarations[0].init, useCurrentFrameLocalNames);
|
|
65
85
|
};
|
|
86
|
+
const isGoogleFontLoadFontStatement = (node, googleFontLoadFontLocalNames) => {
|
|
87
|
+
return (node.type === 'ExpressionStatement' &&
|
|
88
|
+
node.expression.type === 'CallExpression' &&
|
|
89
|
+
node.expression.callee.type === 'Identifier' &&
|
|
90
|
+
googleFontLoadFontLocalNames.has(node.expression.callee.name));
|
|
91
|
+
};
|
|
66
92
|
const getIgnoredNodePredicate = (ast) => {
|
|
67
93
|
const useCurrentFrameLocalNames = getUseCurrentFrameLocalNames(ast);
|
|
94
|
+
const googleFontLoadFontLocalNames = getGoogleFontLoadFontLocalNames(ast);
|
|
68
95
|
return (node, { parent, property }) => {
|
|
69
|
-
if (parent.type === 'Program' &&
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return true;
|
|
96
|
+
if (parent.type === 'Program' && property === 'body') {
|
|
97
|
+
return (node.type === 'ImportDeclaration' ||
|
|
98
|
+
isGoogleFontLoadFontStatement(node, googleFontLoadFontLocalNames));
|
|
73
99
|
}
|
|
74
100
|
if (parent.type === 'BlockStatement' && property === 'body') {
|
|
75
101
|
return isUseCurrentFrameStatement(node, useCurrentFrameLocalNames);
|
|
@@ -30,6 +30,7 @@ export declare const insertJsxElementIntoComposition: ({ remotionRoot, compositi
|
|
|
30
30
|
width: number;
|
|
31
31
|
height: number;
|
|
32
32
|
};
|
|
33
|
+
durationInFrames?: number | null | undefined;
|
|
33
34
|
name: string | null;
|
|
34
35
|
position: InsertableCompositionElementPosition | null;
|
|
35
36
|
} | null | undefined;
|
|
@@ -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
|
-
|
|
1138
|
-
|
|
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 =
|
|
1338
|
+
const finalElementToInsert = sequenceWrapper
|
|
1143
1339
|
? createSequenceWrappedElement({
|
|
1144
1340
|
child: elementToInsert,
|
|
1145
|
-
dimensions:
|
|
1146
|
-
|
|
1147
|
-
|
|
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;
|
|
@@ -47,6 +47,17 @@ const getCodemodUndoDescription = (codemod) => {
|
|
|
47
47
|
entryType: codemod.type,
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
if (codemod.type === 'move-composition-to-folder') {
|
|
51
|
+
const destination = codemod.folderName === null
|
|
52
|
+
? 'to root'
|
|
53
|
+
: `into folder "${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}"`;
|
|
54
|
+
const label = `composition "${codemod.idToMove}" ${destination}`;
|
|
55
|
+
return {
|
|
56
|
+
undoMessage: `↩️ Move of ${label}`,
|
|
57
|
+
redoMessage: `↪️ Move of ${label}`,
|
|
58
|
+
entryType: codemod.type,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
50
61
|
if (codemod.type === 'new-composition') {
|
|
51
62
|
return {
|
|
52
63
|
undoMessage: `↩️ Creation of composition "${codemod.newId}"`,
|
|
@@ -62,6 +73,14 @@ const getCodemodUndoDescription = (codemod) => {
|
|
|
62
73
|
entryType: codemod.type,
|
|
63
74
|
};
|
|
64
75
|
}
|
|
76
|
+
if (codemod.type === 'new-folder') {
|
|
77
|
+
const label = `folder "${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}"`;
|
|
78
|
+
return {
|
|
79
|
+
undoMessage: `↩️ Creation of ${label}`,
|
|
80
|
+
redoMessage: `↪️ Creation of ${label}`,
|
|
81
|
+
entryType: codemod.type,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
65
84
|
if (codemod.type === 'rename-folder') {
|
|
66
85
|
const oldName = `${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.folderName}`;
|
|
67
86
|
const newName = `${codemod.parentName ? `${codemod.parentName}/` : ''}${codemod.newName}`;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.insertJsxElementHandler = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
4
8
|
const renderer_1 = require("@remotion/renderer");
|
|
5
9
|
const studio_shared_1 = require("@remotion/studio-shared");
|
|
6
10
|
const file_watcher_1 = require("../../file-watcher");
|
|
@@ -22,7 +26,27 @@ const validatePosition = (position) => {
|
|
|
22
26
|
throw new Error('Position must be finite');
|
|
23
27
|
}
|
|
24
28
|
};
|
|
25
|
-
const
|
|
29
|
+
const isInsideRemotionRoot = ({ fileName, remotionRoot, }) => {
|
|
30
|
+
const relativePath = node_path_1.default.relative(remotionRoot, fileName);
|
|
31
|
+
return !relativePath.startsWith('..') && !node_path_1.default.isAbsolute(relativePath);
|
|
32
|
+
};
|
|
33
|
+
const hasParentDirectorySegment = (fileName) => {
|
|
34
|
+
return fileName.split('/').includes('..');
|
|
35
|
+
};
|
|
36
|
+
const validateCompositionFile = ({ compositionFile, remotionRoot, }) => {
|
|
37
|
+
if (!compositionFile ||
|
|
38
|
+
compositionFile.includes('\0') ||
|
|
39
|
+
compositionFile.includes('\\') ||
|
|
40
|
+
compositionFile.startsWith('/') ||
|
|
41
|
+
hasParentDirectorySegment(compositionFile)) {
|
|
42
|
+
throw new Error('Unsupported composition file');
|
|
43
|
+
}
|
|
44
|
+
const resolved = node_path_1.default.resolve(remotionRoot, compositionFile);
|
|
45
|
+
if (!isInsideRemotionRoot({ fileName: resolved, remotionRoot })) {
|
|
46
|
+
throw new Error('Unsupported composition file');
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const validateElement = (element, remotionRoot) => {
|
|
26
50
|
validatePosition(element.position);
|
|
27
51
|
if (element.type === 'solid') {
|
|
28
52
|
validateDimension('width', element.width);
|
|
@@ -59,6 +83,28 @@ const validateElement = (element) => {
|
|
|
59
83
|
}
|
|
60
84
|
return;
|
|
61
85
|
}
|
|
86
|
+
if (element.type === 'composition') {
|
|
87
|
+
if (typeof element.compositionId !== 'string' || !element.compositionId) {
|
|
88
|
+
throw new Error('Unsupported composition ID');
|
|
89
|
+
}
|
|
90
|
+
if (typeof element.compositionFile !== 'string') {
|
|
91
|
+
throw new Error('Unsupported composition file');
|
|
92
|
+
}
|
|
93
|
+
validateCompositionFile({
|
|
94
|
+
compositionFile: element.compositionFile,
|
|
95
|
+
remotionRoot,
|
|
96
|
+
});
|
|
97
|
+
validateDimension('width', element.width);
|
|
98
|
+
validateDimension('height', element.height);
|
|
99
|
+
validateDimension('durationInFrames', element.durationInFrames);
|
|
100
|
+
const parsedProps = JSON.parse(element.serializedResolvedPropsWithCustomSchema);
|
|
101
|
+
if (typeof parsedProps !== 'object' ||
|
|
102
|
+
parsedProps === null ||
|
|
103
|
+
Array.isArray(parsedProps)) {
|
|
104
|
+
throw new Error('Resolved composition props must be an object');
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
62
108
|
throw new Error('Unsupported element type');
|
|
63
109
|
};
|
|
64
110
|
const getElementLabel = (element) => {
|
|
@@ -85,11 +131,14 @@ const getElementLabel = (element) => {
|
|
|
85
131
|
if (element.type === 'component') {
|
|
86
132
|
return `<${element.componentName}>`;
|
|
87
133
|
}
|
|
134
|
+
if (element.type === 'composition') {
|
|
135
|
+
return `composition "${element.compositionId}"`;
|
|
136
|
+
}
|
|
88
137
|
throw new Error('Unsupported element type');
|
|
89
138
|
};
|
|
90
139
|
const insertJsxElementHandler = ({ input: { compositionFile, compositionId, element }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
91
140
|
try {
|
|
92
|
-
validateElement(element);
|
|
141
|
+
validateElement(element, remotionRoot);
|
|
93
142
|
const elementLabel = getElementLabel(element);
|
|
94
143
|
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-jsx-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.type}"`);
|
|
95
144
|
const { fileName, source, oldContents, output, formatted, logLine } = await (0, resolve_composition_component_1.insertJsxElementIntoComposition)({
|
|
@@ -11,10 +11,12 @@ type ResolvedSequencePropEdit = {
|
|
|
11
11
|
defaultValue: unknown | null;
|
|
12
12
|
defaultValueString: string | null;
|
|
13
13
|
schema: SaveSequencePropEdit['schema'];
|
|
14
|
+
sourceEdit: SaveSequencePropEdit['sourceEdit'];
|
|
14
15
|
};
|
|
15
|
-
export declare const convertSequencePropEditToCodemodChange: (edit: Pick<ResolvedSequencePropEdit, "defaultValue" | "key" | "nodePath" | "schema" | "value">) => SequencePropsNodeUpdate;
|
|
16
|
+
export declare const convertSequencePropEditToCodemodChange: (edit: Pick<ResolvedSequencePropEdit, "defaultValue" | "key" | "nodePath" | "schema" | "sourceEdit" | "value">) => SequencePropsNodeUpdate;
|
|
16
17
|
export declare const shouldSuppressHmrForSequencePropEdits: (edits: readonly {
|
|
17
18
|
key: string;
|
|
19
|
+
sourceEdit?: import("@remotion/studio-shared").SaveSequencePropSourceEdit | null | undefined;
|
|
18
20
|
}[]) => boolean;
|
|
19
21
|
export declare const saveSequencePropsHandler: ApiHandler<SaveSequencePropsRequest, SaveSequencePropsResponse>;
|
|
20
22
|
export {};
|