@remotion/studio-server 4.0.485 → 4.0.487
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-keyframes/update-keyframes.js +2 -49
- 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/parse-keyframe-easing-expression.d.ts +3 -0
- package/dist/helpers/parse-keyframe-easing-expression.js +141 -0
- package/dist/helpers/resolve-composition-component.d.ts +2 -1
- package/dist/helpers/resolve-composition-component.js +212 -11
- package/dist/open-browser-shortcut.d.ts +9 -0
- package/dist/open-browser-shortcut.js +111 -0
- package/dist/preview-server/live-events.d.ts +1 -0
- package/dist/preview-server/live-events.js +10 -7
- package/dist/preview-server/routes/apply-codemod.js +19 -0
- package/dist/preview-server/routes/can-update-sequence-props.js +2 -49
- package/dist/preview-server/routes/insert-element.js +3 -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/dist/start-studio.js +17 -4
- package/package.json +6 -6
|
@@ -41,8 +41,10 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
41
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
42
42
|
const studio_shared_1 = require("@remotion/studio-shared");
|
|
43
43
|
const recast = __importStar(require("recast"));
|
|
44
|
+
const no_react_1 = require("remotion/no-react");
|
|
44
45
|
const format_file_content_1 = require("../codemods/format-file-content");
|
|
45
46
|
const parse_ast_1 = require("../codemods/parse-ast");
|
|
47
|
+
const update_nested_prop_1 = require("../codemods/update-nested-prop");
|
|
46
48
|
const imports_1 = require("./imports");
|
|
47
49
|
const allowedFileExtensions = new Set(['.tsx', '.ts', '.jsx', '.js']);
|
|
48
50
|
const extensionsToProbe = ['.tsx', '.ts', '.jsx', '.js'];
|
|
@@ -119,7 +121,7 @@ const getComponentIdentifier = (element) => {
|
|
|
119
121
|
return attribute.value.expression.name;
|
|
120
122
|
};
|
|
121
123
|
const isRecord = (value) => {
|
|
122
|
-
return typeof value === 'object' && value !== null;
|
|
124
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
123
125
|
};
|
|
124
126
|
const findDynamicImportPath = (value) => {
|
|
125
127
|
if (!isRecord(value)) {
|
|
@@ -578,11 +580,18 @@ 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
|
-
|
|
585
|
-
|
|
586
|
+
...(dimensions !== null
|
|
587
|
+
? [
|
|
588
|
+
createNumberAttribute('width', dimensions.width),
|
|
589
|
+
createNumberAttribute('height', dimensions.height),
|
|
590
|
+
]
|
|
591
|
+
: []),
|
|
592
|
+
...(durationInFrames === null
|
|
593
|
+
? []
|
|
594
|
+
: [createNumberAttribute('durationInFrames', durationInFrames)]),
|
|
586
595
|
createPositionAbsoluteStyleAttribute(position),
|
|
587
596
|
], false), recast.types.builders.jsxClosingElement(recast.types.builders.jsxIdentifier(sequenceLocalName)), [child]);
|
|
588
597
|
};
|
|
@@ -847,6 +856,167 @@ const ensureComponentImport = ({ ast, componentName, importName, importPath, })
|
|
|
847
856
|
localName: componentName,
|
|
848
857
|
});
|
|
849
858
|
};
|
|
859
|
+
const identifierRegex = /^[A-Za-z_$][0-9A-Za-z_$]*$/;
|
|
860
|
+
const toPascalCaseIdentifier = (value) => {
|
|
861
|
+
var _a;
|
|
862
|
+
const words = (_a = value.match(/[a-zA-Z0-9]+/g)) !== null && _a !== void 0 ? _a : [];
|
|
863
|
+
const candidate = words
|
|
864
|
+
.map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
|
|
865
|
+
.join('');
|
|
866
|
+
if (!candidate) {
|
|
867
|
+
return 'CompositionComponent';
|
|
868
|
+
}
|
|
869
|
+
if (/^[0-9]/.test(candidate)) {
|
|
870
|
+
return `Composition${candidate}`;
|
|
871
|
+
}
|
|
872
|
+
return identifierRegex.test(candidate) ? candidate : 'CompositionComponent';
|
|
873
|
+
};
|
|
874
|
+
const getAvailableLocalName = ({ ast, baseName, }) => {
|
|
875
|
+
if (!hasTopLevelBinding({ ast, name: baseName })) {
|
|
876
|
+
return baseName;
|
|
877
|
+
}
|
|
878
|
+
const suffixed = `${baseName}Composition`;
|
|
879
|
+
if (!hasTopLevelBinding({ ast, name: suffixed })) {
|
|
880
|
+
return suffixed;
|
|
881
|
+
}
|
|
882
|
+
for (let i = 2; i < 100; i++) {
|
|
883
|
+
const candidate = `${suffixed}${i}`;
|
|
884
|
+
if (!hasTopLevelBinding({ ast, name: candidate })) {
|
|
885
|
+
return candidate;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
throw new Error(`Cannot find a local name for ${baseName}`);
|
|
889
|
+
};
|
|
890
|
+
const getImportPathBetweenFiles = ({ fromFile, toFile, }) => {
|
|
891
|
+
let relativeImport = node_path_1.default
|
|
892
|
+
.relative(node_path_1.default.dirname(fromFile), toFile)
|
|
893
|
+
.replaceAll(node_path_1.default.sep, '/')
|
|
894
|
+
.replace(/\.(tsx|ts|jsx|js)$/, '');
|
|
895
|
+
if (!relativeImport.startsWith('.')) {
|
|
896
|
+
relativeImport = `./${relativeImport}`;
|
|
897
|
+
}
|
|
898
|
+
return relativeImport;
|
|
899
|
+
};
|
|
900
|
+
const ensureDefaultImport = ({ ast, localName, sourcePath, }) => {
|
|
901
|
+
var _a, _b;
|
|
902
|
+
var _c;
|
|
903
|
+
for (const declaration of getImportDeclarations({ ast, sourcePath })) {
|
|
904
|
+
const defaultSpecifier = (_a = declaration.specifiers) === null || _a === void 0 ? void 0 : _a.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
|
|
905
|
+
if ((_b = defaultSpecifier === null || defaultSpecifier === void 0 ? void 0 : defaultSpecifier.local) === null || _b === void 0 ? void 0 : _b.name) {
|
|
906
|
+
return defaultSpecifier.local.name;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const importSpecifier = recast.types.builders.importDefaultSpecifier(recast.types.builders.identifier(localName));
|
|
910
|
+
const existingImport = getImportDeclarations({ ast, sourcePath }).find((declaration) => {
|
|
911
|
+
var _a;
|
|
912
|
+
return !((_a = declaration.specifiers) === null || _a === void 0 ? void 0 : _a.some((specifier) => specifier.type === 'ImportNamespaceSpecifier'));
|
|
913
|
+
});
|
|
914
|
+
if (existingImport) {
|
|
915
|
+
existingImport.specifiers = [
|
|
916
|
+
importSpecifier,
|
|
917
|
+
...((_c = existingImport.specifiers) !== null && _c !== void 0 ? _c : []),
|
|
918
|
+
];
|
|
919
|
+
return localName;
|
|
920
|
+
}
|
|
921
|
+
const importDeclaration = recast.types.builders.importDeclaration([importSpecifier], recast.types.builders.stringLiteral(sourcePath));
|
|
922
|
+
(0, imports_1.insertImportDeclaration)(ast, importDeclaration);
|
|
923
|
+
return localName;
|
|
924
|
+
};
|
|
925
|
+
const ensureCompositionComponentImport = async ({ ast, compositionFile, compositionId, destinationFileName, remotionRoot, }) => {
|
|
926
|
+
const sourceLocation = await (0, exports.resolveCompositionComponentWithFile)({
|
|
927
|
+
remotionRoot,
|
|
928
|
+
compositionFile,
|
|
929
|
+
compositionId,
|
|
930
|
+
});
|
|
931
|
+
if (sourceLocation.fileName === destinationFileName) {
|
|
932
|
+
if (sourceLocation.exportName === 'default') {
|
|
933
|
+
throw new Error('Cannot insert a composition whose component is a default export in the same file');
|
|
934
|
+
}
|
|
935
|
+
if (!hasTopLevelBinding({ ast, name: sourceLocation.exportName })) {
|
|
936
|
+
throw new Error(`Cannot find component "${sourceLocation.exportName}" in this file`);
|
|
937
|
+
}
|
|
938
|
+
return sourceLocation.exportName;
|
|
939
|
+
}
|
|
940
|
+
const sourcePath = getImportPathBetweenFiles({
|
|
941
|
+
fromFile: destinationFileName,
|
|
942
|
+
toFile: sourceLocation.fileName,
|
|
943
|
+
});
|
|
944
|
+
if (sourceLocation.exportName === 'default') {
|
|
945
|
+
return ensureDefaultImport({
|
|
946
|
+
ast,
|
|
947
|
+
localName: getAvailableLocalName({
|
|
948
|
+
ast,
|
|
949
|
+
baseName: toPascalCaseIdentifier(compositionId),
|
|
950
|
+
}),
|
|
951
|
+
sourcePath,
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
return (0, imports_1.ensureNamedImport)({
|
|
955
|
+
ast,
|
|
956
|
+
importedName: sourceLocation.exportName,
|
|
957
|
+
sourcePath,
|
|
958
|
+
localName: getAvailableLocalName({
|
|
959
|
+
ast,
|
|
960
|
+
baseName: sourceLocation.exportName,
|
|
961
|
+
}),
|
|
962
|
+
});
|
|
963
|
+
};
|
|
964
|
+
const parseSerializedCompositionProps = (serializedResolvedPropsWithCustomSchema) => {
|
|
965
|
+
const parsed = JSON.parse(serializedResolvedPropsWithCustomSchema);
|
|
966
|
+
if (!isRecord(parsed)) {
|
|
967
|
+
throw new Error('Resolved composition props must be an object');
|
|
968
|
+
}
|
|
969
|
+
return parsed;
|
|
970
|
+
};
|
|
971
|
+
const containsFileToken = (value) => {
|
|
972
|
+
if (typeof value === 'string') {
|
|
973
|
+
return value.startsWith(no_react_1.NoReactInternals.FILE_TOKEN);
|
|
974
|
+
}
|
|
975
|
+
if (Array.isArray(value)) {
|
|
976
|
+
return value.some(containsFileToken);
|
|
977
|
+
}
|
|
978
|
+
if (isRecord(value)) {
|
|
979
|
+
return Object.values(value).some(containsFileToken);
|
|
980
|
+
}
|
|
981
|
+
return false;
|
|
982
|
+
};
|
|
983
|
+
const createExpressionAttribute = (name, value) => {
|
|
984
|
+
return recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(name), recast.types.builders.jsxExpressionContainer((0, update_nested_prop_1.parseValueExpression)(value)));
|
|
985
|
+
};
|
|
986
|
+
const createCompositionPropAttribute = ({ name, value, }) => {
|
|
987
|
+
if (typeof value === 'string' &&
|
|
988
|
+
!value.startsWith(no_react_1.NoReactInternals.FILE_TOKEN) &&
|
|
989
|
+
!value.startsWith(no_react_1.NoReactInternals.DATE_TOKEN)) {
|
|
990
|
+
return createStringAttribute(name, value);
|
|
991
|
+
}
|
|
992
|
+
return createExpressionAttribute(name, value);
|
|
993
|
+
};
|
|
994
|
+
const createCompositionObjectProperty = ({ name, value, }) => {
|
|
995
|
+
return recast.types.builders.objectProperty(identifierRegex.test(name)
|
|
996
|
+
? recast.types.builders.identifier(name)
|
|
997
|
+
: recast.types.builders.stringLiteral(name), (0, update_nested_prop_1.parseValueExpression)(value));
|
|
998
|
+
};
|
|
999
|
+
const createCompositionComponentElement = ({ localName, props, }) => {
|
|
1000
|
+
const directAttributes = [];
|
|
1001
|
+
const spreadProperties = [];
|
|
1002
|
+
for (const [name, value] of Object.entries(props)) {
|
|
1003
|
+
if (identifierRegex.test(name)) {
|
|
1004
|
+
directAttributes.push(createCompositionPropAttribute({ name, value }));
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
spreadProperties.push(createCompositionObjectProperty({ name, value }));
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const attributes = [
|
|
1011
|
+
...directAttributes,
|
|
1012
|
+
...(spreadProperties.length === 0
|
|
1013
|
+
? []
|
|
1014
|
+
: [
|
|
1015
|
+
recast.types.builders.jsxSpreadAttribute(recast.types.builders.objectExpression(spreadProperties)),
|
|
1016
|
+
]),
|
|
1017
|
+
];
|
|
1018
|
+
return recast.types.builders.jsxElement(recast.types.builders.jsxOpeningElement(recast.types.builders.jsxIdentifier(localName), attributes, true), null, []);
|
|
1019
|
+
};
|
|
850
1020
|
const addElementToComponentRoot = ({ ast, exportName, element, }) => {
|
|
851
1021
|
var _a;
|
|
852
1022
|
var _b;
|
|
@@ -1061,7 +1231,7 @@ const resolveCompositionComponent = async ({ remotionRoot, compositionFile, comp
|
|
|
1061
1231
|
};
|
|
1062
1232
|
};
|
|
1063
1233
|
exports.resolveCompositionComponent = resolveCompositionComponent;
|
|
1064
|
-
const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element, }) => {
|
|
1234
|
+
const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, destinationFileName, element, remotionRoot, }) => {
|
|
1065
1235
|
if (element.type === 'solid') {
|
|
1066
1236
|
const solidLocalName = ensureSolidImport(ast);
|
|
1067
1237
|
return createSolidElement({
|
|
@@ -1085,6 +1255,21 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element,
|
|
|
1085
1255
|
position: element.position,
|
|
1086
1256
|
});
|
|
1087
1257
|
}
|
|
1258
|
+
if (element.type === 'composition') {
|
|
1259
|
+
return Promise.resolve(ensureCompositionComponentImport({
|
|
1260
|
+
ast,
|
|
1261
|
+
compositionFile: element.compositionFile,
|
|
1262
|
+
compositionId: element.compositionId,
|
|
1263
|
+
destinationFileName,
|
|
1264
|
+
remotionRoot,
|
|
1265
|
+
})).then((localName) => {
|
|
1266
|
+
const props = parseSerializedCompositionProps(element.serializedResolvedPropsWithCustomSchema);
|
|
1267
|
+
if (containsFileToken(props)) {
|
|
1268
|
+
ensureStaticFileImport(ast);
|
|
1269
|
+
}
|
|
1270
|
+
return createCompositionComponentElement({ localName, props });
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1088
1273
|
if (element.type === 'asset') {
|
|
1089
1274
|
if (element.srcType === 'remote' && !(0, studio_shared_1.isUrl)(element.src)) {
|
|
1090
1275
|
throw new Error('Remote asset source must be a URL');
|
|
@@ -1121,6 +1306,7 @@ const createInsertableJsxElement = ({ addPositionStyleToComponent, ast, element,
|
|
|
1121
1306
|
throw new Error('Unsupported element type');
|
|
1122
1307
|
};
|
|
1123
1308
|
const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile, compositionId, element, prettierConfigOverride, wrapInSequence = null, }) => {
|
|
1309
|
+
var _a;
|
|
1124
1310
|
const location = await (0, exports.resolveCompositionComponentWithFile)({
|
|
1125
1311
|
remotionRoot,
|
|
1126
1312
|
compositionFile,
|
|
@@ -1134,17 +1320,32 @@ const insertJsxElementIntoComposition = async ({ remotionRoot, compositionFile,
|
|
|
1134
1320
|
fileName: location.fileName,
|
|
1135
1321
|
});
|
|
1136
1322
|
const ast = (0, parse_ast_1.parseAst)(input);
|
|
1137
|
-
|
|
1138
|
-
|
|
1323
|
+
if (element.type === 'composition' &&
|
|
1324
|
+
element.compositionId === compositionId) {
|
|
1325
|
+
throw new Error('Cannot insert a composition into itself');
|
|
1326
|
+
}
|
|
1327
|
+
const sequenceWrapper = element.type === 'composition'
|
|
1328
|
+
? {
|
|
1329
|
+
dimensions: { width: element.width, height: element.height },
|
|
1330
|
+
durationInFrames: element.durationInFrames,
|
|
1331
|
+
name: element.compositionId,
|
|
1332
|
+
position: element.position,
|
|
1333
|
+
}
|
|
1334
|
+
: wrapInSequence;
|
|
1335
|
+
const elementToInsert = await createInsertableJsxElement({
|
|
1336
|
+
addPositionStyleToComponent: sequenceWrapper === null,
|
|
1139
1337
|
ast,
|
|
1338
|
+
destinationFileName: location.fileName,
|
|
1140
1339
|
element,
|
|
1340
|
+
remotionRoot,
|
|
1141
1341
|
});
|
|
1142
|
-
const finalElementToInsert =
|
|
1342
|
+
const finalElementToInsert = sequenceWrapper
|
|
1143
1343
|
? createSequenceWrappedElement({
|
|
1144
1344
|
child: elementToInsert,
|
|
1145
|
-
dimensions:
|
|
1146
|
-
|
|
1147
|
-
|
|
1345
|
+
dimensions: sequenceWrapper.dimensions,
|
|
1346
|
+
durationInFrames: (_a = sequenceWrapper.durationInFrames) !== null && _a !== void 0 ? _a : null,
|
|
1347
|
+
name: sequenceWrapper.name,
|
|
1348
|
+
position: sequenceWrapper.position,
|
|
1148
1349
|
sequenceLocalName: ensureSequenceImport(ast),
|
|
1149
1350
|
})
|
|
1150
1351
|
: elementToInsert;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const registerOpenBrowserShortcut: ({ browserArgs, browserFlag, url, logLevel, }: {
|
|
2
|
+
browserArgs: string;
|
|
3
|
+
browserFlag: string;
|
|
4
|
+
url: string;
|
|
5
|
+
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
6
|
+
}) => {
|
|
7
|
+
registered: boolean;
|
|
8
|
+
cleanup: () => void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerOpenBrowserShortcut = void 0;
|
|
37
|
+
const readline = __importStar(require("node:readline"));
|
|
38
|
+
const renderer_1 = require("@remotion/renderer");
|
|
39
|
+
const maybe_open_browser_1 = require("./maybe-open-browser");
|
|
40
|
+
const live_events_1 = require("./preview-server/live-events");
|
|
41
|
+
const registerOpenBrowserShortcut = ({ browserArgs, browserFlag, url, logLevel, }) => {
|
|
42
|
+
if (!process.stdin.isTTY) {
|
|
43
|
+
return { registered: false, cleanup: () => undefined };
|
|
44
|
+
}
|
|
45
|
+
if (typeof process.stdin.setRawMode !== 'function') {
|
|
46
|
+
return { registered: false, cleanup: () => undefined };
|
|
47
|
+
}
|
|
48
|
+
const wasRaw = process.stdin.isRaw;
|
|
49
|
+
const shouldPauseAfterCleanup = process.stdin.isPaused();
|
|
50
|
+
let cleanedUp = false;
|
|
51
|
+
let isOpeningBrowser = false;
|
|
52
|
+
const cleanup = () => {
|
|
53
|
+
if (cleanedUp) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
cleanedUp = true;
|
|
57
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
58
|
+
process.removeListener('SIGINT', cleanup);
|
|
59
|
+
process.removeListener('exit', cleanup);
|
|
60
|
+
if (!wasRaw && process.stdin.isTTY) {
|
|
61
|
+
process.stdin.setRawMode(false);
|
|
62
|
+
}
|
|
63
|
+
if (shouldPauseAfterCleanup) {
|
|
64
|
+
process.stdin.pause();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const openStudio = () => {
|
|
68
|
+
if (isOpeningBrowser) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
isOpeningBrowser = true;
|
|
72
|
+
(0, live_events_1.clearPrintPortMessageTimeout)();
|
|
73
|
+
(0, maybe_open_browser_1.maybeOpenBrowser)({
|
|
74
|
+
browserArgs,
|
|
75
|
+
browserFlag,
|
|
76
|
+
shouldOpenBrowser: true,
|
|
77
|
+
url,
|
|
78
|
+
logLevel,
|
|
79
|
+
})
|
|
80
|
+
.then((result) => {
|
|
81
|
+
if (result.didOpenBrowser) {
|
|
82
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `Opened ${url} in browser`);
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
.catch((err) => {
|
|
86
|
+
renderer_1.RenderInternals.Log.error({ indent: false, logLevel }, 'Could not open browser:', err);
|
|
87
|
+
})
|
|
88
|
+
.finally(() => {
|
|
89
|
+
isOpeningBrowser = false;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
function onKeypress(_str, key) {
|
|
93
|
+
if ((key === null || key === void 0 ? void 0 : key.ctrl) && key.name === 'c') {
|
|
94
|
+
cleanup();
|
|
95
|
+
process.kill(process.pid, 'SIGINT');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if ((key === null || key === void 0 ? void 0 : key.meta) || (key === null || key === void 0 ? void 0 : key.ctrl) || (key === null || key === void 0 ? void 0 : key.name) !== 's') {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
openStudio();
|
|
102
|
+
}
|
|
103
|
+
readline.emitKeypressEvents(process.stdin);
|
|
104
|
+
process.stdin.setRawMode(true);
|
|
105
|
+
process.stdin.resume();
|
|
106
|
+
process.stdin.on('keypress', onKeypress);
|
|
107
|
+
process.once('SIGINT', cleanup);
|
|
108
|
+
process.once('exit', cleanup);
|
|
109
|
+
return { registered: true, cleanup };
|
|
110
|
+
};
|
|
111
|
+
exports.registerOpenBrowserShortcut = registerOpenBrowserShortcut;
|
|
@@ -7,6 +7,7 @@ export type LiveEventsServer = {
|
|
|
7
7
|
closeConnections: () => Promise<void>;
|
|
8
8
|
addNewClientListener: (cb: () => void) => () => void;
|
|
9
9
|
};
|
|
10
|
+
export declare const clearPrintPortMessageTimeout: () => void;
|
|
10
11
|
export type InitialUndoRedoState = {
|
|
11
12
|
undoFile: string | null;
|
|
12
13
|
redoFile: string | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setLiveEventsListener = exports.waitForLiveEventsListener = exports.makeLiveEventsRouter = void 0;
|
|
3
|
+
exports.setLiveEventsListener = exports.waitForLiveEventsListener = exports.makeLiveEventsRouter = exports.clearPrintPortMessageTimeout = void 0;
|
|
4
4
|
const server_ready_1 = require("../server-ready");
|
|
5
5
|
const default_props_watchers_1 = require("./default-props-watchers");
|
|
6
6
|
const file_existence_watchers_1 = require("./file-existence-watchers");
|
|
@@ -9,6 +9,13 @@ const serializeMessage = (message) => {
|
|
|
9
9
|
return `data: ${JSON.stringify(message)}\n\n`;
|
|
10
10
|
};
|
|
11
11
|
let printPortMessageTimeout = null;
|
|
12
|
+
const clearPrintPortMessageTimeout = () => {
|
|
13
|
+
if (printPortMessageTimeout) {
|
|
14
|
+
clearTimeout(printPortMessageTimeout);
|
|
15
|
+
printPortMessageTimeout = null;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
exports.clearPrintPortMessageTimeout = clearPrintPortMessageTimeout;
|
|
12
19
|
const makeLiveEventsRouter = (logLevel, getInitialUndoRedoState) => {
|
|
13
20
|
let clients = [];
|
|
14
21
|
let newClientListeners = [];
|
|
@@ -33,9 +40,7 @@ const makeLiveEventsRouter = (logLevel, getInitialUndoRedoState) => {
|
|
|
33
40
|
};
|
|
34
41
|
clients.push(newClient);
|
|
35
42
|
newClientListeners.forEach((cb) => cb());
|
|
36
|
-
|
|
37
|
-
clearTimeout(printPortMessageTimeout);
|
|
38
|
-
}
|
|
43
|
+
(0, exports.clearPrintPortMessageTimeout)();
|
|
39
44
|
request.on('close', () => {
|
|
40
45
|
(0, default_props_watchers_1.unsubscribeClientDefaultPropsWatchers)(clientId);
|
|
41
46
|
(0, file_existence_watchers_1.unsubscribeClientFileExistenceWatchers)(clientId);
|
|
@@ -43,9 +48,7 @@ const makeLiveEventsRouter = (logLevel, getInitialUndoRedoState) => {
|
|
|
43
48
|
clients = clients.filter((client) => client.id !== clientId);
|
|
44
49
|
// If all clients disconnected, print a comment so user can easily restart it.
|
|
45
50
|
if (clients.length === 0) {
|
|
46
|
-
|
|
47
|
-
clearTimeout(printPortMessageTimeout);
|
|
48
|
-
}
|
|
51
|
+
(0, exports.clearPrintPortMessageTimeout)();
|
|
49
52
|
printPortMessageTimeout = setTimeout(() => {
|
|
50
53
|
(0, server_ready_1.printServerReadyComment)('To restart', logLevel);
|
|
51
54
|
}, 2500);
|
|
@@ -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}`;
|
|
@@ -42,6 +42,7 @@ const no_react_1 = require("remotion/no-react");
|
|
|
42
42
|
const parse_ast_1 = require("../../codemods/parse-ast");
|
|
43
43
|
const get_ast_node_path_1 = require("../../helpers/get-ast-node-path");
|
|
44
44
|
const import_agnostic_node_path_1 = require("../../helpers/import-agnostic-node-path");
|
|
45
|
+
const parse_keyframe_easing_expression_1 = require("../../helpers/parse-keyframe-easing-expression");
|
|
45
46
|
const resolve_file_inside_project_1 = require("../../helpers/resolve-file-inside-project");
|
|
46
47
|
const jsx_component_identity_1 = require("../jsx-component-identity");
|
|
47
48
|
const jsx_element_not_found_at_location_error_1 = require("../jsx-element-not-found-at-location-error");
|
|
@@ -200,55 +201,7 @@ const getExtrapolateType = (node) => {
|
|
|
200
201
|
}
|
|
201
202
|
return null;
|
|
202
203
|
};
|
|
203
|
-
const getKeyframeEasing = (node) =>
|
|
204
|
-
if (node.type === 'TSAsExpression') {
|
|
205
|
-
return getKeyframeEasing(node.expression);
|
|
206
|
-
}
|
|
207
|
-
if (node.type === 'MemberExpression' &&
|
|
208
|
-
node.object.type === 'Identifier' &&
|
|
209
|
-
node.object.name === 'Easing' &&
|
|
210
|
-
node.property.type === 'Identifier' &&
|
|
211
|
-
node.property.name === 'linear' &&
|
|
212
|
-
node.computed === false) {
|
|
213
|
-
return { type: 'linear' };
|
|
214
|
-
}
|
|
215
|
-
if (node.type !== 'CallExpression' ||
|
|
216
|
-
node.callee.type !== 'MemberExpression' ||
|
|
217
|
-
node.callee.object.type !== 'Identifier' ||
|
|
218
|
-
node.callee.object.name !== 'Easing' ||
|
|
219
|
-
node.callee.property.type !== 'Identifier' ||
|
|
220
|
-
node.callee.computed) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
if (node.callee.property.name === 'spring') {
|
|
224
|
-
if (node.arguments.length > 1) {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
const springConfig = node.arguments[0];
|
|
228
|
-
if ((springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'ArgumentPlaceholder' ||
|
|
229
|
-
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'JSXNamespacedName' ||
|
|
230
|
-
(springConfig === null || springConfig === void 0 ? void 0 : springConfig.type) === 'SpreadElement') {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
return (0, studio_shared_1.parseSpringEasingConfig)(springConfig);
|
|
234
|
-
}
|
|
235
|
-
if (node.callee.property.name !== 'bezier' || node.arguments.length !== 4) {
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
const values = node.arguments.map((arg) => {
|
|
239
|
-
if (arg.type === 'ArgumentPlaceholder' ||
|
|
240
|
-
arg.type === 'JSXNamespacedName' ||
|
|
241
|
-
arg.type === 'SpreadElement') {
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
return getNumericValue(arg);
|
|
245
|
-
});
|
|
246
|
-
if (values.some((v) => v === null)) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
const [x1, y1, x2, y2] = values;
|
|
250
|
-
return { type: 'bezier', x1, y1, x2, y2 };
|
|
251
|
-
};
|
|
204
|
+
const getKeyframeEasing = (node) => (0, parse_keyframe_easing_expression_1.parseKeyframeEasingExpression)(node);
|
|
252
205
|
const getKeyframeEasingArray = ({ easingNode, segments, }) => {
|
|
253
206
|
if (segments === 0) {
|
|
254
207
|
return [];
|
|
@@ -45,6 +45,9 @@ const makeRelativeImportPath = ({ fromFile, toFile, }) => {
|
|
|
45
45
|
return relative;
|
|
46
46
|
};
|
|
47
47
|
const validateDimensions = (dimensions) => {
|
|
48
|
+
if (dimensions === null) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
48
51
|
if (!Number.isFinite(dimensions.width) ||
|
|
49
52
|
!Number.isFinite(dimensions.height) ||
|
|
50
53
|
dimensions.width <= 0 ||
|
|
@@ -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 {};
|