@remotion/studio-server 4.0.501 → 4.0.503
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/update-inline-caption-patches.d.ts +1 -11
- package/dist/codemods/update-inline-caption-patches.js +2 -183
- package/dist/codemods/update-sequence-props/update-sequence-props.d.ts +2 -35
- package/dist/codemods/update-sequence-props/update-sequence-props.js +7 -720
- package/dist/detect-outdated-remotion-skills.d.ts +25 -0
- package/dist/detect-outdated-remotion-skills.js +73 -0
- package/dist/helpers/custom-editor.d.ts +37 -0
- package/dist/helpers/custom-editor.js +167 -0
- package/dist/helpers/editor-registry.d.ts +18 -0
- package/dist/helpers/editor-registry.js +500 -0
- package/dist/helpers/open-in-editor.d.ts +3 -4
- package/dist/helpers/open-in-editor.js +5 -75
- package/dist/helpers/resolve-composition-component.js +18 -6
- package/dist/helpers/resolve-editor.d.ts +27 -0
- package/dist/helpers/resolve-editor.js +90 -0
- package/dist/helpers/resolve-file-inside-project.d.ts +2 -0
- package/dist/helpers/resolve-file-inside-project.js +5 -2
- package/dist/index.d.ts +17 -1
- package/dist/index.js +9 -1
- package/dist/preview-server/api-routes.js +6 -1
- package/dist/preview-server/api-types.d.ts +2 -1
- package/dist/preview-server/element-install-state.d.ts +19 -0
- package/dist/preview-server/element-install-state.js +46 -1
- package/dist/preview-server/handler.d.ts +3 -1
- package/dist/preview-server/handler.js +3 -2
- package/dist/preview-server/parse-body.d.ts +6 -1
- package/dist/preview-server/parse-body.js +60 -9
- package/dist/preview-server/routes/can-update-default-props.d.ts +1 -1
- package/dist/preview-server/routes/can-update-default-props.js +5 -132
- package/dist/preview-server/routes/can-update-sequence-props.d.ts +3 -2
- package/dist/preview-server/routes/can-update-sequence-props.js +52 -9
- package/dist/preview-server/routes/default-editor.d.ts +8 -0
- package/dist/preview-server/routes/default-editor.js +131 -0
- package/dist/preview-server/routes/delete-effect-keyframe.d.ts +3 -0
- package/dist/preview-server/routes/delete-effect-keyframe.js +89 -0
- package/dist/preview-server/routes/delete-sequence-keyframe.d.ts +3 -0
- package/dist/preview-server/routes/delete-sequence-keyframe.js +82 -0
- package/dist/preview-server/routes/element-install-plan.d.ts +32 -0
- package/dist/preview-server/routes/element-install-plan.js +122 -0
- package/dist/preview-server/routes/insert-element.d.ts +1 -1
- package/dist/preview-server/routes/insert-element.js +68 -107
- package/dist/preview-server/routes/insert-jsx-element.js +5 -5
- package/dist/preview-server/routes/open-in-editor.d.ts +5 -1
- package/dist/preview-server/routes/open-in-editor.js +42 -12
- package/dist/preview-server/routes/prepare-element-install.d.ts +3 -0
- package/dist/preview-server/routes/prepare-element-install.js +25 -0
- package/dist/preview-server/routes/safe-element-install-path.d.ts +9 -0
- package/dist/preview-server/routes/safe-element-install-path.js +58 -0
- package/dist/preview-server/routes/save-props-mutex.d.ts +1 -0
- package/dist/preview-server/routes/save-props-mutex.js +11 -0
- package/dist/preview-server/sequence-props-watchers.js +2 -1
- package/dist/preview-server/start-server.d.ts +2 -0
- package/dist/preview-server/start-server.js +1 -0
- package/dist/preview-server/studio-protocol/handle-discovery.d.ts +11 -0
- package/dist/preview-server/studio-protocol/handle-discovery.js +106 -0
- package/dist/preview-server/studio-protocol/handle-install.d.ts +10 -0
- package/dist/preview-server/studio-protocol/handle-install.js +147 -0
- package/dist/preview-server/studio-protocol/origin-policy.d.ts +11 -0
- package/dist/preview-server/studio-protocol/origin-policy.js +43 -0
- package/dist/preview-server/studio-protocol/protocol-response.d.ts +7 -0
- package/dist/preview-server/studio-protocol/protocol-response.js +13 -0
- package/dist/preview-server/update-available.d.ts +6 -0
- package/dist/preview-server/update-available.js +30 -4
- package/dist/remotion-skill-names.d.ts +2 -0
- package/dist/remotion-skill-names.js +16 -0
- package/dist/routes.d.ts +3 -1
- package/dist/routes.js +19 -157
- package/dist/start-studio.d.ts +3 -1
- package/dist/start-studio.js +2 -1
- package/package.json +9 -8
|
@@ -0,0 +1,122 @@
|
|
|
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.getElementInstallPlan = exports.makeRelativeElementImportPath = exports.validateElementForInstallation = exports.validateElementInstallPosition = exports.getElementSourceHash = exports.normalizeElementSourceForComparison = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const studio_protocol_1 = require("@remotion/studio-protocol");
|
|
11
|
+
const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
|
|
12
|
+
const safe_element_install_path_1 = require("./safe-element-install-path");
|
|
13
|
+
const normalizeElementSourceForComparison = (source) => {
|
|
14
|
+
return source.replace(/\r\n/g, '\n').trim();
|
|
15
|
+
};
|
|
16
|
+
exports.normalizeElementSourceForComparison = normalizeElementSourceForComparison;
|
|
17
|
+
const getElementSourceHash = (source) => {
|
|
18
|
+
return (0, node_crypto_1.createHash)('sha256').update(source).digest('hex');
|
|
19
|
+
};
|
|
20
|
+
exports.getElementSourceHash = getElementSourceHash;
|
|
21
|
+
const validateElementInstallPosition = (position) => {
|
|
22
|
+
if (position === null) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!Number.isFinite(position.x) || !Number.isFinite(position.y)) {
|
|
26
|
+
throw new Error('Position must be finite');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.validateElementInstallPosition = validateElementInstallPosition;
|
|
30
|
+
const validateDimensions = (dimensions) => {
|
|
31
|
+
if (dimensions === null) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (!Number.isFinite(dimensions.width) ||
|
|
35
|
+
!Number.isFinite(dimensions.height) ||
|
|
36
|
+
dimensions.width <= 0 ||
|
|
37
|
+
dimensions.height <= 0) {
|
|
38
|
+
throw new Error('Element dimensions must be positive finite numbers');
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const validateElementForInstallation = (element) => {
|
|
42
|
+
if (studio_protocol_1.StudioProtocolInternals.makeElementFileNameFromSlug(element.slug) === null) {
|
|
43
|
+
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
44
|
+
}
|
|
45
|
+
if (typeof element.sourceCode !== 'string' ||
|
|
46
|
+
element.sourceCode.trim().length === 0 ||
|
|
47
|
+
element.sourceCode.length > 200000) {
|
|
48
|
+
throw new Error('Unsupported Element source code');
|
|
49
|
+
}
|
|
50
|
+
if (studio_protocol_1.StudioProtocolInternals.getElementComponentNameFromSourceCode(element.sourceCode) === null) {
|
|
51
|
+
throw new Error('Element source must export exactly one named component');
|
|
52
|
+
}
|
|
53
|
+
validateDimensions(element.dimensions);
|
|
54
|
+
};
|
|
55
|
+
exports.validateElementForInstallation = validateElementForInstallation;
|
|
56
|
+
const makeRelativeElementImportPath = ({ fromFile, toFile, }) => {
|
|
57
|
+
const withoutExtension = toFile.replace(/\.tsx$/, '');
|
|
58
|
+
let relative = node_path_1.default
|
|
59
|
+
.relative(node_path_1.default.dirname(fromFile), withoutExtension)
|
|
60
|
+
.split(node_path_1.default.sep)
|
|
61
|
+
.join('/');
|
|
62
|
+
if (!relative.startsWith('.')) {
|
|
63
|
+
relative = `./${relative}`;
|
|
64
|
+
}
|
|
65
|
+
return relative;
|
|
66
|
+
};
|
|
67
|
+
exports.makeRelativeElementImportPath = makeRelativeElementImportPath;
|
|
68
|
+
const getExpectedFileState = (existingSource) => {
|
|
69
|
+
if (existingSource === null) {
|
|
70
|
+
return { exists: false };
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
exists: true,
|
|
74
|
+
sourceHash: (0, exports.getElementSourceHash)(existingSource),
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
const getElementInstallPlan = async ({ compositionFile, compositionId, element, remotionRoot, }) => {
|
|
78
|
+
(0, exports.validateElementForInstallation)(element);
|
|
79
|
+
const componentName = studio_protocol_1.StudioProtocolInternals.getElementComponentNameFromSourceCode(element.sourceCode);
|
|
80
|
+
if (componentName === null) {
|
|
81
|
+
throw new Error('Element source must export exactly one named component');
|
|
82
|
+
}
|
|
83
|
+
const location = await (0, resolve_composition_component_1.resolveCompositionComponentWithFile)({
|
|
84
|
+
remotionRoot,
|
|
85
|
+
compositionFile,
|
|
86
|
+
compositionId,
|
|
87
|
+
});
|
|
88
|
+
if (!location.canAddSequence) {
|
|
89
|
+
throw new Error('Cannot insert Element into this composition component');
|
|
90
|
+
}
|
|
91
|
+
const derivedElementFileName = studio_protocol_1.StudioProtocolInternals.makeElementFileNameFromSlug(element.slug);
|
|
92
|
+
if (derivedElementFileName === null) {
|
|
93
|
+
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
94
|
+
}
|
|
95
|
+
const safePaths = await (0, safe_element_install_path_1.getSafeElementInstallPaths)({
|
|
96
|
+
compositionFileName: location.fileName,
|
|
97
|
+
elementFileName: node_path_1.default.resolve(node_path_1.default.dirname(location.fileName), derivedElementFileName),
|
|
98
|
+
remotionRoot,
|
|
99
|
+
});
|
|
100
|
+
const elementFileExists = (0, node_fs_1.existsSync)(safePaths.elementFileName);
|
|
101
|
+
const existingElementSource = elementFileExists
|
|
102
|
+
? (0, node_fs_1.readFileSync)(safePaths.elementFileName, 'utf-8')
|
|
103
|
+
: null;
|
|
104
|
+
return {
|
|
105
|
+
componentName,
|
|
106
|
+
elementFileExists,
|
|
107
|
+
elementFileName: safePaths.elementFileName,
|
|
108
|
+
existingElementSource,
|
|
109
|
+
expectedFileState: getExpectedFileState(existingElementSource),
|
|
110
|
+
filePath: node_path_1.default
|
|
111
|
+
.relative(safePaths.remotionRoot, safePaths.elementFileName)
|
|
112
|
+
.split(node_path_1.default.sep)
|
|
113
|
+
.join('/'),
|
|
114
|
+
importPath: (0, exports.makeRelativeElementImportPath)({
|
|
115
|
+
fromFile: safePaths.compositionFileName,
|
|
116
|
+
toFile: safePaths.elementFileName,
|
|
117
|
+
}),
|
|
118
|
+
location,
|
|
119
|
+
safePaths,
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
exports.getElementInstallPlan = getElementInstallPlan;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { InsertElementRequest, InsertElementResponse } from '@remotion/studio-shared';
|
|
2
2
|
import type { ApiHandler } from '../api-types';
|
|
3
3
|
export declare const insertElementHandler: ApiHandler<InsertElementRequest, InsertElementResponse>;
|
|
@@ -1,138 +1,85 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.insertElementHandler = void 0;
|
|
7
|
-
const node_fs_1 = require("node:fs");
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const drag_and_drop_1 = require("@remotion/drag-and-drop");
|
|
10
4
|
const renderer_1 = require("@remotion/renderer");
|
|
11
5
|
const file_watcher_1 = require("../../file-watcher");
|
|
12
6
|
const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
|
|
13
7
|
const format_log_file_location_1 = require("../format-log-file-location");
|
|
14
8
|
const undo_stack_1 = require("../undo-stack");
|
|
9
|
+
const element_install_plan_1 = require("./element-install-plan");
|
|
15
10
|
const log_update_1 = require("./log-updates/log-update");
|
|
16
11
|
const source_file_write_queue_1 = require("./source-file-write-queue");
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
19
|
-
return;
|
|
12
|
+
const hasExpectedFileState = ({ expected, actual, }) => {
|
|
13
|
+
if (expected.exists !== actual.exists) {
|
|
14
|
+
return false;
|
|
20
15
|
}
|
|
21
|
-
if (!
|
|
22
|
-
|
|
16
|
+
if (!expected.exists) {
|
|
17
|
+
return true;
|
|
23
18
|
}
|
|
24
|
-
|
|
25
|
-
|
|
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}`;
|
|
19
|
+
if (!actual.exists) {
|
|
20
|
+
return false;
|
|
44
21
|
}
|
|
45
|
-
return
|
|
22
|
+
return actual.sourceHash === expected.sourceHash;
|
|
46
23
|
};
|
|
47
|
-
const
|
|
48
|
-
if (dimensions === null) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (!Number.isFinite(dimensions.width) ||
|
|
52
|
-
!Number.isFinite(dimensions.height) ||
|
|
53
|
-
dimensions.width <= 0 ||
|
|
54
|
-
dimensions.height <= 0) {
|
|
55
|
-
throw new Error('Element dimensions must be positive finite numbers');
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const validateElement = (element) => {
|
|
59
|
-
if (drag_and_drop_1.DragAndDropInternals.makeElementFileNameFromSlug(element.slug) === null) {
|
|
60
|
-
throw new Error('Element slug must produce a safe lowercase .tsx file name');
|
|
61
|
-
}
|
|
62
|
-
if (typeof element.sourceCode !== 'string' ||
|
|
63
|
-
element.sourceCode.trim().length === 0 ||
|
|
64
|
-
element.sourceCode.length > 200000) {
|
|
65
|
-
throw new Error('Unsupported Element source code');
|
|
66
|
-
}
|
|
67
|
-
if (drag_and_drop_1.DragAndDropInternals.getElementComponentNameFromSourceCode(element.sourceCode) === null) {
|
|
68
|
-
throw new Error('Element source must export exactly one named component');
|
|
69
|
-
}
|
|
70
|
-
validateDimensions(element.dimensions);
|
|
71
|
-
};
|
|
72
|
-
const insertElementHandler = ({ input: { compositionFile, compositionId, element, from, position, overwriteExisting, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
24
|
+
const insertElementHandler = ({ input: { compositionFile, compositionId, element, expectedFileState, from, position, overwriteExisting, }, remotionRoot, logLevel, }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
73
25
|
try {
|
|
74
|
-
|
|
75
|
-
validatePosition(position);
|
|
26
|
+
(0, element_install_plan_1.validateElementInstallPosition)(position);
|
|
76
27
|
if (from !== null &&
|
|
77
28
|
(!Number.isInteger(from) || !Number.isFinite(from) || from < 0)) {
|
|
78
29
|
throw new Error('from must be a non-negative integer');
|
|
79
30
|
}
|
|
80
|
-
const componentName = drag_and_drop_1.DragAndDropInternals.getElementComponentNameFromSourceCode(element.sourceCode);
|
|
81
|
-
if (componentName === null) {
|
|
82
|
-
throw new Error('Element source must export exactly one named component');
|
|
83
|
-
}
|
|
84
31
|
renderer_1.RenderInternals.Log.trace({ indent: false, logLevel }, `[insert-element] Received request for compositionFile="${compositionFile}" compositionId="${compositionId}" element="${element.slug}"`);
|
|
85
|
-
const
|
|
86
|
-
remotionRoot,
|
|
32
|
+
const plan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
87
33
|
compositionFile,
|
|
88
34
|
compositionId,
|
|
35
|
+
element,
|
|
36
|
+
remotionRoot,
|
|
89
37
|
});
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
38
|
+
if (expectedFileState !== null &&
|
|
39
|
+
!hasExpectedFileState({
|
|
40
|
+
actual: plan.expectedFileState,
|
|
41
|
+
expected: expectedFileState,
|
|
42
|
+
})) {
|
|
43
|
+
const { existingElementSource } = plan;
|
|
44
|
+
if (existingElementSource !== null) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
type: 'file-conflict',
|
|
48
|
+
conflict: {
|
|
49
|
+
filePath: plan.filePath,
|
|
50
|
+
existingSource: existingElementSource,
|
|
51
|
+
incomingSource: element.sourceCode,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
throw new Error('Element source changed during installation');
|
|
100
56
|
}
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
normalizeSourceForComparison(element.sourceCode);
|
|
108
|
-
if (elementSourcesDiffer && !overwriteExisting) {
|
|
57
|
+
const elementSourcesDiffer = plan.existingElementSource !== null &&
|
|
58
|
+
(0, element_install_plan_1.normalizeElementSourceForComparison)(plan.existingElementSource) !==
|
|
59
|
+
(0, element_install_plan_1.normalizeElementSourceForComparison)(element.sourceCode);
|
|
60
|
+
if (elementSourcesDiffer &&
|
|
61
|
+
!overwriteExisting &&
|
|
62
|
+
plan.existingElementSource !== null) {
|
|
109
63
|
return {
|
|
110
64
|
success: false,
|
|
111
65
|
type: 'file-conflict',
|
|
112
66
|
conflict: {
|
|
113
|
-
filePath:
|
|
114
|
-
|
|
115
|
-
.split(node_path_1.default.sep)
|
|
116
|
-
.join('/'),
|
|
117
|
-
existingSource: existingElementSource,
|
|
67
|
+
filePath: plan.filePath,
|
|
68
|
+
existingSource: plan.existingElementSource,
|
|
118
69
|
incomingSource: element.sourceCode,
|
|
119
70
|
},
|
|
120
71
|
};
|
|
121
72
|
}
|
|
122
|
-
const shouldWriteElementFile = !elementFileExists || elementSourcesDiffer;
|
|
123
|
-
const importPath = makeRelativeImportPath({
|
|
124
|
-
fromFile: location.fileName,
|
|
125
|
-
toFile: elementFileName,
|
|
126
|
-
});
|
|
73
|
+
const shouldWriteElementFile = !plan.elementFileExists || elementSourcesDiffer;
|
|
127
74
|
const inserted = await (0, resolve_composition_component_1.insertJsxElementIntoComposition)({
|
|
128
75
|
remotionRoot,
|
|
129
76
|
compositionFile,
|
|
130
77
|
compositionId,
|
|
131
78
|
element: {
|
|
132
79
|
type: 'component',
|
|
133
|
-
componentName,
|
|
134
|
-
importName: componentName,
|
|
135
|
-
importPath,
|
|
80
|
+
componentName: plan.componentName,
|
|
81
|
+
importName: plan.componentName,
|
|
82
|
+
importPath: plan.importPath,
|
|
136
83
|
props: [],
|
|
137
84
|
position: null,
|
|
138
85
|
},
|
|
@@ -145,13 +92,29 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
145
92
|
position,
|
|
146
93
|
},
|
|
147
94
|
});
|
|
95
|
+
const finalPlan = await (0, element_install_plan_1.getElementInstallPlan)({
|
|
96
|
+
compositionFile,
|
|
97
|
+
compositionId,
|
|
98
|
+
element,
|
|
99
|
+
remotionRoot,
|
|
100
|
+
});
|
|
101
|
+
if (finalPlan.safePaths.compositionFileName !==
|
|
102
|
+
plan.safePaths.compositionFileName) {
|
|
103
|
+
throw new Error('Composition source changed during Element installation');
|
|
104
|
+
}
|
|
105
|
+
if (!hasExpectedFileState({
|
|
106
|
+
actual: finalPlan.expectedFileState,
|
|
107
|
+
expected: plan.expectedFileState,
|
|
108
|
+
})) {
|
|
109
|
+
throw new Error('Element source changed during installation');
|
|
110
|
+
}
|
|
148
111
|
(0, undo_stack_1.pushTransactionToUndoStack)({
|
|
149
112
|
snapshots: [
|
|
150
113
|
...(shouldWriteElementFile
|
|
151
114
|
? [
|
|
152
115
|
{
|
|
153
|
-
filePath: elementFileName,
|
|
154
|
-
oldContents: existingElementSource,
|
|
116
|
+
filePath: plan.elementFileName,
|
|
117
|
+
oldContents: plan.existingElementSource,
|
|
155
118
|
newContents: element.sourceCode,
|
|
156
119
|
logLine: 1,
|
|
157
120
|
},
|
|
@@ -174,11 +137,11 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
174
137
|
suppressHmrOnFileRestore: false,
|
|
175
138
|
});
|
|
176
139
|
if (shouldWriteElementFile) {
|
|
177
|
-
(0, undo_stack_1.suppressUndoStackInvalidation)(elementFileName);
|
|
140
|
+
(0, undo_stack_1.suppressUndoStackInvalidation)(plan.elementFileName);
|
|
178
141
|
}
|
|
179
142
|
(0, undo_stack_1.suppressUndoStackInvalidation)(inserted.fileName);
|
|
180
143
|
if (shouldWriteElementFile) {
|
|
181
|
-
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(elementFileName, element.sourceCode, undefined);
|
|
144
|
+
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(plan.elementFileName, element.sourceCode, undefined);
|
|
182
145
|
}
|
|
183
146
|
(0, file_watcher_1.writeFileAndNotifyFileWatchers)(inserted.fileName, inserted.output, undefined);
|
|
184
147
|
const compositionLocationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
@@ -188,23 +151,21 @@ const insertElementHandler = ({ input: { compositionFile, compositionId, element
|
|
|
188
151
|
});
|
|
189
152
|
const elementLocationLabel = (0, format_log_file_location_1.formatLogFileLocation)({
|
|
190
153
|
remotionRoot,
|
|
191
|
-
absolutePath: elementFileName,
|
|
154
|
+
absolutePath: plan.elementFileName,
|
|
192
155
|
line: 1,
|
|
193
156
|
});
|
|
194
157
|
const elementFileAction = elementSourcesDiffer
|
|
195
158
|
? 'Overwrote existing Element source'
|
|
196
|
-
: elementFileExists
|
|
159
|
+
: plan.elementFileExists
|
|
197
160
|
? 'Reused existing Element source'
|
|
198
161
|
: 'Created Element source';
|
|
199
162
|
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(elementLocationLabel)} ${elementFileAction}`);
|
|
200
|
-
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(compositionLocationLabel)} Added <${componentName}>`);
|
|
163
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(compositionLocationLabel)} Added <${plan.componentName}>`);
|
|
201
164
|
if (!inserted.formatted) {
|
|
202
165
|
(0, log_update_1.warnAboutPrettierOnce)(logLevel);
|
|
203
166
|
}
|
|
204
167
|
(0, undo_stack_1.printUndoHint)(logLevel);
|
|
205
|
-
return {
|
|
206
|
-
success: true,
|
|
207
|
-
};
|
|
168
|
+
return { success: true };
|
|
208
169
|
}
|
|
209
170
|
catch (err) {
|
|
210
171
|
return {
|
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.insertJsxElementHandler = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
-
const drag_and_drop_1 = require("@remotion/drag-and-drop");
|
|
9
8
|
const renderer_1 = require("@remotion/renderer");
|
|
9
|
+
const studio_protocol_1 = require("@remotion/studio-protocol");
|
|
10
10
|
const studio_shared_1 = require("@remotion/studio-shared");
|
|
11
11
|
const file_watcher_1 = require("../../file-watcher");
|
|
12
12
|
const resolve_composition_component_1 = require("../../helpers/resolve-composition-component");
|
|
@@ -79,16 +79,16 @@ const validateElement = (element, remotionRoot) => {
|
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
if (element.type === 'component') {
|
|
82
|
-
if (!
|
|
82
|
+
if (!studio_protocol_1.StudioProtocolInternals.isComponentIdentifier(element.componentName)) {
|
|
83
83
|
throw new Error('Unsupported component name');
|
|
84
84
|
}
|
|
85
|
-
if (!
|
|
85
|
+
if (!studio_protocol_1.StudioProtocolInternals.isComponentIdentifier(element.importName)) {
|
|
86
86
|
throw new Error('Unsupported component import name');
|
|
87
87
|
}
|
|
88
|
-
if (!
|
|
88
|
+
if (!studio_protocol_1.StudioProtocolInternals.isComponentImportPath(element.importPath)) {
|
|
89
89
|
throw new Error('Unsupported component import path');
|
|
90
90
|
}
|
|
91
|
-
if (!
|
|
91
|
+
if (!studio_protocol_1.StudioProtocolInternals.areComponentProps(element.props)) {
|
|
92
92
|
throw new Error('Unsupported component props');
|
|
93
93
|
}
|
|
94
94
|
return;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { type DefaultEditor } from '@remotion/renderer';
|
|
1
2
|
import type { OpenInEditorRequest, OpenInEditorResponse } from '@remotion/studio-shared';
|
|
2
3
|
import type { ApiHandler } from '../api-types';
|
|
3
|
-
export declare const getEditorName: (
|
|
4
|
+
export declare const getEditorName: ({ getDefaultEditor, logLevel, }: {
|
|
5
|
+
getDefaultEditor: () => DefaultEditor | null;
|
|
6
|
+
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
7
|
+
}) => Promise<string | null>;
|
|
4
8
|
export declare const openInEditorHandler: ApiHandler<OpenInEditorRequest, OpenInEditorResponse>;
|
|
@@ -5,28 +5,58 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.openInEditorHandler = exports.getEditorName = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const renderer_1 = require("@remotion/renderer");
|
|
9
|
+
const custom_editor_1 = require("../../helpers/custom-editor");
|
|
8
10
|
const open_in_editor_1 = require("../../helpers/open-in-editor");
|
|
9
|
-
const
|
|
10
|
-
const getEditorName = async () => {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const resolve_editor_1 = require("../../helpers/resolve-editor");
|
|
12
|
+
const getEditorName = async ({ getDefaultEditor, logLevel, }) => {
|
|
13
|
+
var _a;
|
|
14
|
+
const editor = await (0, resolve_editor_1.resolveEditor)({
|
|
15
|
+
defaultEditor: getDefaultEditor(),
|
|
16
|
+
logLevel,
|
|
17
|
+
preferredEditor: null,
|
|
18
|
+
});
|
|
19
|
+
return (_a = editor === null || editor === void 0 ? void 0 : editor.name) !== null && _a !== void 0 ? _a : null;
|
|
13
20
|
};
|
|
14
21
|
exports.getEditorName = getEditorName;
|
|
15
|
-
const openInEditorHandler = async ({ input, remotionRoot, logLevel }) => {
|
|
22
|
+
const openInEditorHandler = async ({ input, remotionRoot, logLevel, getDefaultEditor }) => {
|
|
16
23
|
try {
|
|
17
24
|
if (!('stack' in input)) {
|
|
18
25
|
throw new TypeError('Need to pass stack');
|
|
19
26
|
}
|
|
20
27
|
const { stack } = input;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
if (input.editorId !== null &&
|
|
29
|
+
input.editorId !== 'custom' &&
|
|
30
|
+
!renderer_1.defaultEditorIds.includes(input.editorId)) {
|
|
31
|
+
return { success: false };
|
|
32
|
+
}
|
|
33
|
+
const editor = await (0, resolve_editor_1.resolveEditor)({
|
|
34
|
+
defaultEditor: getDefaultEditor(),
|
|
28
35
|
logLevel,
|
|
36
|
+
preferredEditor: input.editorId,
|
|
29
37
|
});
|
|
38
|
+
if (!editor) {
|
|
39
|
+
return { success: false };
|
|
40
|
+
}
|
|
41
|
+
const fileName = node_path_1.default.resolve(remotionRoot, stack.originalFileName);
|
|
42
|
+
const didOpen = editor.type === 'custom'
|
|
43
|
+
? await (0, custom_editor_1.launchCustomEditor)({
|
|
44
|
+
editor: editor.editor,
|
|
45
|
+
resolvedExecutable: editor,
|
|
46
|
+
targetPath: fileName,
|
|
47
|
+
lineNumber: stack.originalLineNumber,
|
|
48
|
+
columnNumber: stack.originalColumnNumber,
|
|
49
|
+
logLevel,
|
|
50
|
+
spawnProcess: null,
|
|
51
|
+
})
|
|
52
|
+
: await (0, open_in_editor_1.launchEditor)({
|
|
53
|
+
colNumber: stack.originalColumnNumber,
|
|
54
|
+
editor,
|
|
55
|
+
fileName,
|
|
56
|
+
lineNumber: stack.originalLineNumber,
|
|
57
|
+
vsCodeNewWindow: false,
|
|
58
|
+
logLevel,
|
|
59
|
+
});
|
|
30
60
|
return {
|
|
31
61
|
success: didOpen,
|
|
32
62
|
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { PrepareElementInstallRequest, PrepareElementInstallResponse } from '@remotion/studio-shared';
|
|
2
|
+
import type { ApiHandler } from '../api-types';
|
|
3
|
+
export declare const prepareElementInstallHandler: ApiHandler<PrepareElementInstallRequest, PrepareElementInstallResponse>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareElementInstallHandler = void 0;
|
|
4
|
+
const element_install_plan_1 = require("./element-install-plan");
|
|
5
|
+
const source_file_write_queue_1 = require("./source-file-write-queue");
|
|
6
|
+
const prepareElementInstallHandler = ({ input, remotionRoot }) => (0, source_file_write_queue_1.withSourceFileWriteQueue)(async () => {
|
|
7
|
+
try {
|
|
8
|
+
const plan = await (0, element_install_plan_1.getElementInstallPlan)({ ...input, remotionRoot });
|
|
9
|
+
return {
|
|
10
|
+
success: true,
|
|
11
|
+
plan: {
|
|
12
|
+
expectedFileState: plan.expectedFileState,
|
|
13
|
+
filePath: plan.filePath,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
reason: error.message,
|
|
21
|
+
stack: error.stack,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
exports.prepareElementInstallHandler = prepareElementInstallHandler;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const getSafeElementInstallPaths: ({ compositionFileName, elementFileName, remotionRoot, }: {
|
|
2
|
+
compositionFileName: string;
|
|
3
|
+
elementFileName: string;
|
|
4
|
+
remotionRoot: string;
|
|
5
|
+
}) => Promise<{
|
|
6
|
+
compositionFileName: string;
|
|
7
|
+
elementFileName: string;
|
|
8
|
+
remotionRoot: string;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,58 @@
|
|
|
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.getSafeElementInstallPaths = void 0;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const isInside = ({ child, parent }) => {
|
|
10
|
+
const relative = node_path_1.default.relative(parent, child);
|
|
11
|
+
return (relative === '' ||
|
|
12
|
+
(!relative.startsWith('..') && !node_path_1.default.isAbsolute(relative)));
|
|
13
|
+
};
|
|
14
|
+
const assertInsideProject = ({ fileName, remotionRoot, }) => {
|
|
15
|
+
if (!isInside({ child: fileName, parent: remotionRoot })) {
|
|
16
|
+
throw new Error('Element installation must stay inside the Remotion project');
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const getCanonicalProjectRoot = async (remotionRoot) => {
|
|
20
|
+
return (0, promises_1.realpath)(remotionRoot);
|
|
21
|
+
};
|
|
22
|
+
const getSafeElementInstallPaths = async ({ compositionFileName, elementFileName, remotionRoot, }) => {
|
|
23
|
+
const canonicalRoot = await getCanonicalProjectRoot(remotionRoot);
|
|
24
|
+
const canonicalCompositionFile = await (0, promises_1.realpath)(compositionFileName);
|
|
25
|
+
assertInsideProject({
|
|
26
|
+
fileName: canonicalCompositionFile,
|
|
27
|
+
remotionRoot: canonicalRoot,
|
|
28
|
+
});
|
|
29
|
+
// The Element directory already exists because it is the composition file's
|
|
30
|
+
// directory. Resolving it before appending the file name prevents writes
|
|
31
|
+
// through a directory symlink that leaves the project.
|
|
32
|
+
const canonicalElementDirectory = await (0, promises_1.realpath)(node_path_1.default.dirname(elementFileName));
|
|
33
|
+
assertInsideProject({
|
|
34
|
+
fileName: canonicalElementDirectory,
|
|
35
|
+
remotionRoot: canonicalRoot,
|
|
36
|
+
});
|
|
37
|
+
const canonicalElementFile = node_path_1.default.join(canonicalElementDirectory, node_path_1.default.basename(elementFileName));
|
|
38
|
+
try {
|
|
39
|
+
const elementStats = await (0, promises_1.lstat)(canonicalElementFile);
|
|
40
|
+
if (elementStats.isSymbolicLink()) {
|
|
41
|
+
throw new Error('Element source file must not be a symbolic link');
|
|
42
|
+
}
|
|
43
|
+
if (!elementStats.isFile()) {
|
|
44
|
+
throw new Error('Element source path must be a file');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error.code !== 'ENOENT') {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
compositionFileName: canonicalCompositionFile,
|
|
54
|
+
elementFileName: canonicalElementFile,
|
|
55
|
+
remotionRoot: canonicalRoot,
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.getSafeElementInstallPaths = getSafeElementInstallPaths;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const withSavePropsLock: <T>(fn: () => Promise<T>) => Promise<T>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withSavePropsLock = void 0;
|
|
4
|
+
let chain = Promise.resolve();
|
|
5
|
+
const withSavePropsLock = (fn) => {
|
|
6
|
+
const run = () => fn();
|
|
7
|
+
const next = chain.then(run, run);
|
|
8
|
+
chain = next.then(() => undefined, () => undefined);
|
|
9
|
+
return next;
|
|
10
|
+
};
|
|
11
|
+
exports.withSavePropsLock = withSavePropsLock;
|
|
@@ -85,9 +85,10 @@ const getSequencePropsStatus = ({ fileName, line, column, preferredNodePath, com
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
const status = (0, can_update_sequence_props_1.
|
|
88
|
+
const status = (0, can_update_sequence_props_1.computeSequencePropsStatusFromFilenameByLocation)({
|
|
89
89
|
fileName,
|
|
90
90
|
line,
|
|
91
|
+
column,
|
|
91
92
|
componentIdentity,
|
|
92
93
|
keys,
|
|
93
94
|
assetKeys,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BundlerOverrideFn, RspackOverrideFn, WebpackOverrideFn } from '@remotion/bundler';
|
|
2
|
+
import type { DefaultEditor } from '@remotion/renderer';
|
|
2
3
|
import type { GitSource, RenderDefaults, RenderJob, StudioRuntimeConfig } from '@remotion/studio-shared';
|
|
3
4
|
import type { QueueMethods } from './api-types';
|
|
4
5
|
import type { LiveEventsServer } from './live-events';
|
|
@@ -41,5 +42,6 @@ export declare const startServer: (options: {
|
|
|
41
42
|
forceNew: boolean;
|
|
42
43
|
rspack: boolean;
|
|
43
44
|
getStudioRuntimeConfig: () => StudioRuntimeConfig;
|
|
45
|
+
getDefaultEditor: () => DefaultEditor | null;
|
|
44
46
|
configFile: string | null;
|
|
45
47
|
}) => Promise<StartServerResult>;
|
|
@@ -102,6 +102,7 @@ const startServer = async (options) => {
|
|
|
102
102
|
getPreviewSampleRate: options.getPreviewSampleRate,
|
|
103
103
|
enableCrossSiteIsolation: options.enableCrossSiteIsolation,
|
|
104
104
|
getStudioRuntimeConfig: options.getStudioRuntimeConfig,
|
|
105
|
+
getDefaultEditor: options.getDefaultEditor,
|
|
105
106
|
configFile: options.configFile,
|
|
106
107
|
});
|
|
107
108
|
})
|