@remotion/studio-server 4.0.432 → 4.0.433
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-nested-prop.d.ts +4 -6
- package/dist/codemods/update-nested-prop.js +8 -10
- package/dist/codemods/update-sequence-props.d.ts +5 -4
- package/dist/codemods/update-sequence-props.js +82 -70
- package/dist/preview-server/routes/can-update-sequence-props.d.ts +12 -3
- package/dist/preview-server/routes/can-update-sequence-props.js +113 -31
- package/dist/preview-server/routes/log-update.d.ts +10 -0
- package/dist/preview-server/routes/log-update.js +73 -0
- package/dist/preview-server/routes/save-sequence-props.js +14 -12
- package/dist/preview-server/routes/subscribe-to-sequence-props.js +1 -2
- package/dist/preview-server/routes/unsubscribe-from-sequence-props.js +2 -3
- package/dist/preview-server/sequence-props-watchers.d.ts +4 -6
- package/dist/preview-server/sequence-props-watchers.js +23 -17
- package/package.json +7 -7
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { namedTypes } from 'ast-types';
|
|
1
|
+
import type { JSXOpeningElement } from '@babel/types';
|
|
3
2
|
import type { ExpressionKind } from 'ast-types/lib/gen/kinds';
|
|
4
|
-
export declare const parseValueExpression: (value: unknown
|
|
5
|
-
export declare const updateNestedProp: ({ node, parentKey, childKey, value,
|
|
6
|
-
node:
|
|
3
|
+
export declare const parseValueExpression: (value: unknown) => ExpressionKind;
|
|
4
|
+
export declare const updateNestedProp: ({ node, parentKey, childKey, value, defaultValue, isDefault, }: {
|
|
5
|
+
node: JSXOpeningElement;
|
|
7
6
|
parentKey: string;
|
|
8
7
|
childKey: string;
|
|
9
8
|
value: unknown;
|
|
10
|
-
enumPaths: EnumPath[];
|
|
11
9
|
defaultValue: unknown;
|
|
12
10
|
isDefault: boolean;
|
|
13
11
|
}) => string;
|
|
@@ -38,8 +38,8 @@ const studio_shared_1 = require("@remotion/studio-shared");
|
|
|
38
38
|
const recast = __importStar(require("recast"));
|
|
39
39
|
const parse_ast_1 = require("./parse-ast");
|
|
40
40
|
const b = recast.types.builders;
|
|
41
|
-
const parseValueExpression = (value
|
|
42
|
-
return (0, parse_ast_1.parseAst)(`a = ${(0, studio_shared_1.stringifyDefaultProps)({ props: value, enumPaths })}`)
|
|
41
|
+
const parseValueExpression = (value) => {
|
|
42
|
+
return (0, parse_ast_1.parseAst)(`a = ${(0, studio_shared_1.stringifyDefaultProps)({ props: value, enumPaths: [] })}`)
|
|
43
43
|
.program.body[0].expression.right;
|
|
44
44
|
};
|
|
45
45
|
exports.parseValueExpression = parseValueExpression;
|
|
@@ -64,11 +64,9 @@ const findJsxAttribute = (attributes, name) => {
|
|
|
64
64
|
const findObjectProperty = (objExpr, propertyName) => {
|
|
65
65
|
const propIndex = objExpr.properties.findIndex((p) => p.type === 'ObjectProperty' &&
|
|
66
66
|
((p.key.type === 'Identifier' &&
|
|
67
|
-
p.key.name ===
|
|
68
|
-
propertyName) ||
|
|
67
|
+
p.key.name === propertyName) ||
|
|
69
68
|
(p.key.type === 'StringLiteral' &&
|
|
70
|
-
p.key
|
|
71
|
-
.value === propertyName)));
|
|
69
|
+
p.key.value === propertyName)));
|
|
72
70
|
return {
|
|
73
71
|
propIndex,
|
|
74
72
|
prop: propIndex !== -1
|
|
@@ -118,8 +116,8 @@ const removeNestedProp = ({ attr, attrIndex, attributes, childKey, }) => {
|
|
|
118
116
|
attributes.splice(attrIndex, 1);
|
|
119
117
|
}
|
|
120
118
|
};
|
|
121
|
-
const setNestedProp = ({ attr, attributes, parentKey, childKey, value,
|
|
122
|
-
const parsedValue = (0, exports.parseValueExpression)(value
|
|
119
|
+
const setNestedProp = ({ attr, attributes, parentKey, childKey, value, }) => {
|
|
120
|
+
const parsedValue = (0, exports.parseValueExpression)(value);
|
|
123
121
|
if (attr) {
|
|
124
122
|
const objExpr = getObjectExpression(attr);
|
|
125
123
|
if (objExpr) {
|
|
@@ -140,7 +138,7 @@ const setNestedProp = ({ attr, attributes, parentKey, childKey, value, enumPaths
|
|
|
140
138
|
attributes.push(newAttr);
|
|
141
139
|
}
|
|
142
140
|
};
|
|
143
|
-
const updateNestedProp = ({ node, parentKey, childKey, value,
|
|
141
|
+
const updateNestedProp = ({ node, parentKey, childKey, value, defaultValue, isDefault, }) => {
|
|
144
142
|
if (!node.attributes) {
|
|
145
143
|
node.attributes = [];
|
|
146
144
|
}
|
|
@@ -151,7 +149,7 @@ const updateNestedProp = ({ node, parentKey, childKey, value, enumPaths, default
|
|
|
151
149
|
removeNestedProp({ attr, attrIndex, attributes, childKey });
|
|
152
150
|
}
|
|
153
151
|
else {
|
|
154
|
-
setNestedProp({ attr, attributes, parentKey, childKey, value
|
|
152
|
+
setNestedProp({ attr, attributes, parentKey, childKey, value });
|
|
155
153
|
}
|
|
156
154
|
return oldValueString;
|
|
157
155
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const updateSequenceProps: ({ input,
|
|
1
|
+
import type { SequenceNodePath } from '@remotion/studio-shared';
|
|
2
|
+
export declare const updateSequenceProps: ({ input, nodePath, key, value, defaultValue, prettierConfigOverride, }: {
|
|
3
3
|
input: string;
|
|
4
|
-
|
|
4
|
+
nodePath: SequenceNodePath;
|
|
5
5
|
key: string;
|
|
6
6
|
value: unknown;
|
|
7
|
-
enumPaths: EnumPath[];
|
|
8
7
|
defaultValue: unknown;
|
|
8
|
+
prettierConfigOverride?: Record<string, unknown> | null | undefined;
|
|
9
9
|
}) => Promise<{
|
|
10
10
|
output: string;
|
|
11
11
|
oldValueString: string;
|
|
12
|
+
formatted: boolean;
|
|
12
13
|
}>;
|
|
@@ -35,12 +35,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.updateSequenceProps = void 0;
|
|
37
37
|
const recast = __importStar(require("recast"));
|
|
38
|
+
const can_update_sequence_props_1 = require("../preview-server/routes/can-update-sequence-props");
|
|
38
39
|
const parse_ast_1 = require("./parse-ast");
|
|
39
40
|
const update_nested_prop_1 = require("./update-nested-prop");
|
|
40
41
|
const b = recast.types.builders;
|
|
41
|
-
const updateSequenceProps = async ({ input,
|
|
42
|
+
const updateSequenceProps = async ({ input, nodePath, key, value, defaultValue, prettierConfigOverride, }) => {
|
|
43
|
+
var _a, _b;
|
|
42
44
|
const ast = (0, parse_ast_1.parseAst)(input);
|
|
43
|
-
let found = false;
|
|
44
45
|
let oldValueString = '';
|
|
45
46
|
const isDefault = defaultValue !== null &&
|
|
46
47
|
JSON.stringify(value) === JSON.stringify(defaultValue);
|
|
@@ -48,61 +49,55 @@ const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, d
|
|
|
48
49
|
const isNested = dotIndex !== -1;
|
|
49
50
|
const parentKey = isNested ? key.slice(0, dotIndex) : key;
|
|
50
51
|
const childKey = isNested ? key.slice(dotIndex + 1) : '';
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return this.traverse(path);
|
|
70
|
-
}
|
|
71
|
-
const attrIndex = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.findIndex((a) => {
|
|
72
|
-
if (a.type === 'JSXSpreadAttribute') {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
if (a.name.type === 'JSXNamespacedName') {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
return a.name.name === key;
|
|
79
|
-
});
|
|
80
|
-
const attr = attrIndex !== undefined && attrIndex !== -1
|
|
81
|
-
? (_b = node.attributes) === null || _b === void 0 ? void 0 : _b[attrIndex]
|
|
82
|
-
: undefined;
|
|
83
|
-
if (attr && attr.type !== 'JSXSpreadAttribute' && attr.value) {
|
|
84
|
-
const printed = recast.print(attr.value).code;
|
|
85
|
-
// Strip JSX expression container braces, e.g. "{30}" -> "30"
|
|
86
|
-
oldValueString =
|
|
87
|
-
printed.startsWith('{') && printed.endsWith('}')
|
|
88
|
-
? printed.slice(1, -1)
|
|
89
|
-
: printed;
|
|
90
|
-
}
|
|
91
|
-
else if (attr && attr.type !== 'JSXSpreadAttribute' && !attr.value) {
|
|
92
|
-
// JSX shorthand like `loop` (no value) is implicitly `true`
|
|
93
|
-
oldValueString = 'true';
|
|
52
|
+
const node = (0, can_update_sequence_props_1.findJsxElementAtNodePath)(ast, nodePath);
|
|
53
|
+
if (!node) {
|
|
54
|
+
throw new Error('Could not find a JSX element at the specified line to update');
|
|
55
|
+
}
|
|
56
|
+
if (isNested) {
|
|
57
|
+
oldValueString = (0, update_nested_prop_1.updateNestedProp)({
|
|
58
|
+
node,
|
|
59
|
+
parentKey,
|
|
60
|
+
childKey,
|
|
61
|
+
value,
|
|
62
|
+
defaultValue,
|
|
63
|
+
isDefault,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const attrIndex = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.findIndex((a) => {
|
|
68
|
+
if (a.type === 'JSXSpreadAttribute') {
|
|
69
|
+
return false;
|
|
94
70
|
}
|
|
95
|
-
|
|
96
|
-
|
|
71
|
+
if (a.name.type === 'JSXNamespacedName') {
|
|
72
|
+
return false;
|
|
97
73
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
74
|
+
return a.name.name === key;
|
|
75
|
+
});
|
|
76
|
+
const attr = attrIndex !== undefined && attrIndex !== -1
|
|
77
|
+
? (_b = node.attributes) === null || _b === void 0 ? void 0 : _b[attrIndex]
|
|
78
|
+
: undefined;
|
|
79
|
+
if (attr && attr.type !== 'JSXSpreadAttribute' && attr.value) {
|
|
80
|
+
const printed = recast.print(attr.value).code;
|
|
81
|
+
// Strip JSX expression container braces, e.g. "{30}" -> "30"
|
|
82
|
+
oldValueString =
|
|
83
|
+
printed.startsWith('{') && printed.endsWith('}')
|
|
84
|
+
? printed.slice(1, -1)
|
|
85
|
+
: printed;
|
|
86
|
+
}
|
|
87
|
+
else if (attr && attr.type !== 'JSXSpreadAttribute' && !attr.value) {
|
|
88
|
+
// JSX shorthand like `loop` (no value) is implicitly `true`
|
|
89
|
+
oldValueString = 'true';
|
|
90
|
+
}
|
|
91
|
+
else if (!attr && defaultValue !== null) {
|
|
92
|
+
oldValueString = JSON.stringify(defaultValue);
|
|
93
|
+
}
|
|
94
|
+
if (isDefault) {
|
|
95
|
+
if (attr && attr.type !== 'JSXSpreadAttribute' && node.attributes) {
|
|
96
|
+
node.attributes.splice(attrIndex, 1);
|
|
104
97
|
}
|
|
105
|
-
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const parsed = (0, update_nested_prop_1.parseValueExpression)(value);
|
|
106
101
|
const newValue = value === true ? null : b.jsxExpressionContainer(parsed);
|
|
107
102
|
if (!attr || attr.type === 'JSXSpreadAttribute') {
|
|
108
103
|
const newAttr = b.jsxAttribute(b.jsxIdentifier(key), newValue);
|
|
@@ -114,36 +109,53 @@ const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, d
|
|
|
114
109
|
else {
|
|
115
110
|
attr.value = newValue;
|
|
116
111
|
}
|
|
117
|
-
|
|
118
|
-
return this.traverse(path);
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
if (!found) {
|
|
122
|
-
throw new Error('Could not find a JSX element at the specified line to update');
|
|
112
|
+
}
|
|
123
113
|
}
|
|
114
|
+
const finalFile = (0, parse_ast_1.serializeAst)(ast);
|
|
124
115
|
let prettier = null;
|
|
125
116
|
try {
|
|
126
117
|
prettier = await Promise.resolve().then(() => __importStar(require('prettier')));
|
|
127
118
|
}
|
|
128
|
-
catch (
|
|
129
|
-
|
|
119
|
+
catch (_c) {
|
|
120
|
+
return {
|
|
121
|
+
output: finalFile,
|
|
122
|
+
oldValueString,
|
|
123
|
+
formatted: false,
|
|
124
|
+
};
|
|
130
125
|
}
|
|
131
126
|
const { format, resolveConfig, resolveConfigFile } = prettier;
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
127
|
+
let prettierConfig;
|
|
128
|
+
if (prettierConfigOverride !== undefined) {
|
|
129
|
+
prettierConfig = prettierConfigOverride;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const configFilePath = await resolveConfigFile();
|
|
133
|
+
if (!configFilePath) {
|
|
134
|
+
return {
|
|
135
|
+
output: finalFile,
|
|
136
|
+
oldValueString,
|
|
137
|
+
formatted: false,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
prettierConfig = await resolveConfig(configFilePath);
|
|
135
141
|
}
|
|
136
|
-
const prettierConfig = await resolveConfig(configFilePath);
|
|
137
142
|
if (!prettierConfig) {
|
|
138
|
-
|
|
143
|
+
return {
|
|
144
|
+
output: finalFile,
|
|
145
|
+
oldValueString,
|
|
146
|
+
formatted: false,
|
|
147
|
+
};
|
|
139
148
|
}
|
|
140
|
-
const finalFile = (0, parse_ast_1.serializeAst)(ast);
|
|
141
149
|
const prettified = await format(finalFile, {
|
|
142
150
|
...prettierConfig,
|
|
143
151
|
filepath: 'test.tsx',
|
|
144
152
|
plugins: [],
|
|
145
|
-
endOfLine: '
|
|
153
|
+
endOfLine: 'lf',
|
|
146
154
|
});
|
|
147
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
output: prettified,
|
|
157
|
+
oldValueString,
|
|
158
|
+
formatted: true,
|
|
159
|
+
};
|
|
148
160
|
};
|
|
149
161
|
exports.updateSequenceProps = updateSequenceProps;
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import type { Expression } from '@babel/types';
|
|
2
|
-
import type { CanUpdateSequencePropsResponse } from '@remotion/studio-shared';
|
|
1
|
+
import type { Expression, File, JSXOpeningElement } from '@babel/types';
|
|
2
|
+
import type { CanUpdateSequencePropsResponse, SequenceNodePath } from '@remotion/studio-shared';
|
|
3
3
|
export declare const isStaticValue: (node: Expression) => boolean;
|
|
4
4
|
export declare const extractStaticValue: (node: Expression) => unknown;
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const findJsxElementAtNodePath: (ast: File, nodePath: SequenceNodePath) => JSXOpeningElement | null;
|
|
6
|
+
export declare const lineColumnToNodePath: (ast: File, targetLine: number) => SequenceNodePath | null;
|
|
7
|
+
export declare const computeSequencePropsStatusFromContent: (fileContents: string, nodePath: SequenceNodePath, keys: string[]) => CanUpdateSequencePropsResponse;
|
|
8
|
+
export declare const computeSequencePropsStatus: ({ fileName, nodePath, keys, remotionRoot, }: {
|
|
9
|
+
fileName: string;
|
|
10
|
+
nodePath: SequenceNodePath;
|
|
11
|
+
keys: string[];
|
|
12
|
+
remotionRoot: string;
|
|
13
|
+
}) => CanUpdateSequencePropsResponse;
|
|
14
|
+
export declare const computeSequencePropsStatusByLine: ({ fileName, line, keys, remotionRoot, }: {
|
|
6
15
|
fileName: string;
|
|
7
16
|
line: number;
|
|
8
17
|
keys: string[];
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.computeSequencePropsStatus = exports.extractStaticValue = exports.isStaticValue = void 0;
|
|
39
|
+
exports.computeSequencePropsStatusByLine = exports.computeSequencePropsStatus = exports.computeSequencePropsStatusFromContent = exports.lineColumnToNodePath = exports.findJsxElementAtNodePath = exports.extractStaticValue = exports.isStaticValue = void 0;
|
|
40
40
|
const node_fs_1 = require("node:fs");
|
|
41
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
42
42
|
const recast = __importStar(require("recast"));
|
|
@@ -151,19 +151,64 @@ const getPropsStatus = (jsxElement) => {
|
|
|
151
151
|
}
|
|
152
152
|
return props;
|
|
153
153
|
};
|
|
154
|
-
const
|
|
155
|
-
|
|
154
|
+
const getNodePathForRecastPath = (
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
|
+
recastPath) => {
|
|
157
|
+
const segments = [];
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
159
|
+
let current = recastPath;
|
|
160
|
+
while (current && current.parentPath) {
|
|
161
|
+
segments.unshift(current.name);
|
|
162
|
+
current = current.parentPath;
|
|
163
|
+
}
|
|
164
|
+
// Recast paths start with "root" which doesn't correspond to a real AST property
|
|
165
|
+
if (segments.length > 0 && segments[0] === 'root') {
|
|
166
|
+
return segments.slice(1);
|
|
167
|
+
}
|
|
168
|
+
return segments;
|
|
169
|
+
};
|
|
170
|
+
const findJsxElementAtNodePath = (ast, nodePath) => {
|
|
171
|
+
let current = new recast.types.NodePath(ast);
|
|
172
|
+
for (const segment of nodePath) {
|
|
173
|
+
current = current.get(segment);
|
|
174
|
+
if (current.value === null || current.value === undefined) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (recast.types.namedTypes.JSXOpeningElement.check(current.value)) {
|
|
179
|
+
return current.value;
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
};
|
|
183
|
+
exports.findJsxElementAtNodePath = findJsxElementAtNodePath;
|
|
184
|
+
const lineColumnToNodePath = (ast, targetLine) => {
|
|
185
|
+
let foundPath = null;
|
|
156
186
|
recast.types.visit(ast, {
|
|
157
|
-
visitJSXOpeningElement(
|
|
158
|
-
const { node } =
|
|
187
|
+
visitJSXOpeningElement(p) {
|
|
188
|
+
const { node } = p;
|
|
159
189
|
if (node.loc && node.loc.start.line === targetLine) {
|
|
160
|
-
|
|
190
|
+
foundPath = getNodePathForRecastPath(p);
|
|
161
191
|
return false;
|
|
162
192
|
}
|
|
163
|
-
return this.traverse(
|
|
193
|
+
return this.traverse(p);
|
|
164
194
|
},
|
|
165
195
|
});
|
|
166
|
-
return
|
|
196
|
+
return foundPath;
|
|
197
|
+
};
|
|
198
|
+
exports.lineColumnToNodePath = lineColumnToNodePath;
|
|
199
|
+
const PIXEL_VALUE_REGEX = /^-?\d+(\.\d+)?px$/;
|
|
200
|
+
const isSupportedTranslateValue = (value) => {
|
|
201
|
+
const parts = value.split(/\s+/);
|
|
202
|
+
if (parts.length === 1 || parts.length === 2) {
|
|
203
|
+
return parts.every((part) => PIXEL_VALUE_REGEX.test(part));
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
};
|
|
207
|
+
const validateStyleValue = (childKey, value) => {
|
|
208
|
+
if (childKey === 'translate' && typeof value === 'string') {
|
|
209
|
+
return isSupportedTranslateValue(value);
|
|
210
|
+
}
|
|
211
|
+
return true;
|
|
167
212
|
};
|
|
168
213
|
const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
|
|
169
214
|
const attr = jsxElement.attributes.find((a) => a.type !== 'JSXSpreadAttribute' &&
|
|
@@ -194,9 +239,58 @@ const getNestedPropStatus = (jsxElement, parentKey, childKey) => {
|
|
|
194
239
|
if (!(0, exports.isStaticValue)(propValue)) {
|
|
195
240
|
return { canUpdate: false, reason: 'computed' };
|
|
196
241
|
}
|
|
197
|
-
|
|
242
|
+
const codeValue = (0, exports.extractStaticValue)(propValue);
|
|
243
|
+
if (!validateStyleValue(childKey, codeValue)) {
|
|
244
|
+
return { canUpdate: false, reason: 'computed' };
|
|
245
|
+
}
|
|
246
|
+
return { canUpdate: true, codeValue };
|
|
247
|
+
};
|
|
248
|
+
const computeSequencePropsStatusFromContent = (fileContents, nodePath, keys) => {
|
|
249
|
+
const ast = (0, parse_ast_1.parseAst)(fileContents);
|
|
250
|
+
const jsxElement = (0, exports.findJsxElementAtNodePath)(ast, nodePath);
|
|
251
|
+
if (!jsxElement) {
|
|
252
|
+
throw new Error('Could not find a JSX element at the specified location');
|
|
253
|
+
}
|
|
254
|
+
const allProps = getPropsStatus(jsxElement);
|
|
255
|
+
const filteredProps = {};
|
|
256
|
+
for (const key of keys) {
|
|
257
|
+
const dotIndex = key.indexOf('.');
|
|
258
|
+
if (dotIndex !== -1) {
|
|
259
|
+
filteredProps[key] = getNestedPropStatus(jsxElement, key.slice(0, dotIndex), key.slice(dotIndex + 1));
|
|
260
|
+
}
|
|
261
|
+
else if (key in allProps) {
|
|
262
|
+
filteredProps[key] = allProps[key];
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
filteredProps[key] = { canUpdate: true, codeValue: undefined };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
canUpdate: true,
|
|
270
|
+
props: filteredProps,
|
|
271
|
+
nodePath,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
exports.computeSequencePropsStatusFromContent = computeSequencePropsStatusFromContent;
|
|
275
|
+
const computeSequencePropsStatus = ({ fileName, nodePath, keys, remotionRoot, }) => {
|
|
276
|
+
try {
|
|
277
|
+
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
|
|
278
|
+
const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
|
|
279
|
+
if (fileRelativeToRoot.startsWith('..')) {
|
|
280
|
+
throw new Error('Cannot read a file outside the project');
|
|
281
|
+
}
|
|
282
|
+
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
283
|
+
return (0, exports.computeSequencePropsStatusFromContent)(fileContents, nodePath, keys);
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
return {
|
|
287
|
+
canUpdate: false,
|
|
288
|
+
reason: err.message,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
198
291
|
};
|
|
199
|
-
|
|
292
|
+
exports.computeSequencePropsStatus = computeSequencePropsStatus;
|
|
293
|
+
const computeSequencePropsStatusByLine = ({ fileName, line, keys, remotionRoot, }) => {
|
|
200
294
|
try {
|
|
201
295
|
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
|
|
202
296
|
const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
|
|
@@ -205,28 +299,16 @@ const computeSequencePropsStatus = ({ fileName, line, keys, remotionRoot, }) =>
|
|
|
205
299
|
}
|
|
206
300
|
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
207
301
|
const ast = (0, parse_ast_1.parseAst)(fileContents);
|
|
208
|
-
const
|
|
209
|
-
if (!
|
|
302
|
+
const resolvedNodePath = (0, exports.lineColumnToNodePath)(ast, line);
|
|
303
|
+
if (!resolvedNodePath) {
|
|
210
304
|
throw new Error('Could not find a JSX element at the specified location');
|
|
211
305
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
else if (key in allProps) {
|
|
220
|
-
filteredProps[key] = allProps[key];
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
filteredProps[key] = { canUpdate: true, codeValue: undefined };
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
canUpdate: true,
|
|
228
|
-
props: filteredProps,
|
|
229
|
-
};
|
|
306
|
+
return (0, exports.computeSequencePropsStatus)({
|
|
307
|
+
fileName,
|
|
308
|
+
nodePath: resolvedNodePath,
|
|
309
|
+
keys,
|
|
310
|
+
remotionRoot,
|
|
311
|
+
});
|
|
230
312
|
}
|
|
231
313
|
catch (err) {
|
|
232
314
|
return {
|
|
@@ -235,4 +317,4 @@ const computeSequencePropsStatus = ({ fileName, line, keys, remotionRoot, }) =>
|
|
|
235
317
|
};
|
|
236
318
|
}
|
|
237
319
|
};
|
|
238
|
-
exports.
|
|
320
|
+
exports.computeSequencePropsStatusByLine = computeSequencePropsStatusByLine;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const logUpdate: ({ absolutePath, fileRelativeToRoot, key, oldValueString, newValueString, defaultValueString, formatted, logLevel, }: {
|
|
2
|
+
absolutePath: string;
|
|
3
|
+
fileRelativeToRoot: string;
|
|
4
|
+
key: string;
|
|
5
|
+
oldValueString: string;
|
|
6
|
+
newValueString: string;
|
|
7
|
+
defaultValueString: string | null;
|
|
8
|
+
formatted: boolean;
|
|
9
|
+
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
10
|
+
}) => void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logUpdate = void 0;
|
|
4
|
+
const renderer_1 = require("@remotion/renderer");
|
|
5
|
+
const make_link_1 = require("../../hyperlinks/make-link");
|
|
6
|
+
let warnedAboutPrettier = false;
|
|
7
|
+
const normalizeQuotes = (str) => {
|
|
8
|
+
if (str.length >= 2 &&
|
|
9
|
+
((str.startsWith("'") && str.endsWith("'")) ||
|
|
10
|
+
(str.startsWith('"') && str.endsWith('"')))) {
|
|
11
|
+
return `'${str.slice(1, -1)}'`;
|
|
12
|
+
}
|
|
13
|
+
return str;
|
|
14
|
+
};
|
|
15
|
+
const formatValueChange = ({ oldValueString, newValueString, defaultValueString, }) => {
|
|
16
|
+
// Changed to default value (prop gets deleted) → show only old value in red
|
|
17
|
+
if (defaultValueString !== null && newValueString === defaultValueString) {
|
|
18
|
+
return renderer_1.RenderInternals.chalk.red(oldValueString);
|
|
19
|
+
}
|
|
20
|
+
// Changed from default value (prop gets added) → show only new value in green
|
|
21
|
+
if (defaultValueString !== null && oldValueString === defaultValueString) {
|
|
22
|
+
return renderer_1.RenderInternals.chalk.green(newValueString);
|
|
23
|
+
}
|
|
24
|
+
return `${renderer_1.RenderInternals.chalk.red(oldValueString)} \u2192 ${renderer_1.RenderInternals.chalk.green(newValueString)}`;
|
|
25
|
+
};
|
|
26
|
+
const formatPropChange = ({ key, oldValueString, newValueString, defaultValueString, }) => {
|
|
27
|
+
const isResetToDefault = defaultValueString !== null && newValueString === defaultValueString;
|
|
28
|
+
const isChangeFromDefault = defaultValueString !== null && oldValueString === defaultValueString;
|
|
29
|
+
const valueChange = formatValueChange({
|
|
30
|
+
oldValueString,
|
|
31
|
+
newValueString,
|
|
32
|
+
defaultValueString,
|
|
33
|
+
});
|
|
34
|
+
const dotIndex = key.indexOf('.');
|
|
35
|
+
if (dotIndex === -1) {
|
|
36
|
+
if (isResetToDefault) {
|
|
37
|
+
return renderer_1.RenderInternals.chalk.red(`${key}={${oldValueString}}`);
|
|
38
|
+
}
|
|
39
|
+
if (isChangeFromDefault) {
|
|
40
|
+
return renderer_1.RenderInternals.chalk.green(`${key}={${newValueString}}`);
|
|
41
|
+
}
|
|
42
|
+
return `${key}={${valueChange}}`;
|
|
43
|
+
}
|
|
44
|
+
const parentKey = key.slice(0, dotIndex);
|
|
45
|
+
const childKey = key.slice(dotIndex + 1);
|
|
46
|
+
if (isResetToDefault) {
|
|
47
|
+
return `${parentKey}={{${renderer_1.RenderInternals.chalk.red(`${childKey}: ${oldValueString}`)}}}`;
|
|
48
|
+
}
|
|
49
|
+
if (isChangeFromDefault) {
|
|
50
|
+
return `${parentKey}={{${renderer_1.RenderInternals.chalk.green(`${childKey}: ${newValueString}`)}}}`;
|
|
51
|
+
}
|
|
52
|
+
return `${parentKey}={{${childKey}: ${valueChange}}}`;
|
|
53
|
+
};
|
|
54
|
+
const logUpdate = ({ absolutePath, fileRelativeToRoot, key, oldValueString, newValueString, defaultValueString, formatted, logLevel, }) => {
|
|
55
|
+
const locationLabel = `${fileRelativeToRoot}`;
|
|
56
|
+
const fileLink = (0, make_link_1.makeHyperlink)({
|
|
57
|
+
url: `file://${absolutePath}`,
|
|
58
|
+
text: locationLabel,
|
|
59
|
+
fallback: locationLabel,
|
|
60
|
+
});
|
|
61
|
+
const propChange = formatPropChange({
|
|
62
|
+
key,
|
|
63
|
+
oldValueString: normalizeQuotes(oldValueString),
|
|
64
|
+
newValueString: normalizeQuotes(newValueString),
|
|
65
|
+
defaultValueString: defaultValueString !== null ? normalizeQuotes(defaultValueString) : null,
|
|
66
|
+
});
|
|
67
|
+
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, `${renderer_1.RenderInternals.chalk.blueBright(`${fileLink}:`)} ${propChange}`);
|
|
68
|
+
if (!formatted && !warnedAboutPrettier) {
|
|
69
|
+
warnedAboutPrettier = true;
|
|
70
|
+
renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.yellow('Could not format with Prettier. File will need to be formatted manually.'));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
exports.logUpdate = logUpdate;
|
|
@@ -6,11 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.saveSequencePropsHandler = void 0;
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const renderer_1 = require("@remotion/renderer");
|
|
10
9
|
const update_sequence_props_1 = require("../../codemods/update-sequence-props");
|
|
11
|
-
const make_link_1 = require("../../hyperlinks/make-link");
|
|
12
10
|
const hmr_suppression_1 = require("../hmr-suppression");
|
|
13
|
-
const
|
|
11
|
+
const log_update_1 = require("./log-update");
|
|
12
|
+
const saveSequencePropsHandler = async ({ input: { fileName, nodePath, key, value, defaultValue }, remotionRoot, logLevel, }) => {
|
|
14
13
|
try {
|
|
15
14
|
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
|
|
16
15
|
const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
|
|
@@ -18,24 +17,27 @@ const saveSequencePropsHandler = async ({ input: { fileName, line, column, key,
|
|
|
18
17
|
throw new Error('Cannot modify a file outside the project');
|
|
19
18
|
}
|
|
20
19
|
const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
|
|
21
|
-
const { output, oldValueString } = await (0, update_sequence_props_1.updateSequenceProps)({
|
|
20
|
+
const { output, oldValueString, formatted } = await (0, update_sequence_props_1.updateSequenceProps)({
|
|
22
21
|
input: fileContents,
|
|
23
|
-
|
|
22
|
+
nodePath,
|
|
24
23
|
key,
|
|
25
24
|
value: JSON.parse(value),
|
|
26
|
-
enumPaths,
|
|
27
25
|
defaultValue: defaultValue !== null ? JSON.parse(defaultValue) : null,
|
|
28
26
|
});
|
|
29
27
|
(0, hmr_suppression_1.suppressHmrForFile)(absolutePath);
|
|
30
28
|
(0, node_fs_1.writeFileSync)(absolutePath, output);
|
|
31
29
|
const newValueString = JSON.stringify(JSON.parse(value));
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const parsedDefault = defaultValue !== null ? JSON.parse(defaultValue) : null;
|
|
31
|
+
(0, log_update_1.logUpdate)({
|
|
32
|
+
absolutePath,
|
|
33
|
+
fileRelativeToRoot,
|
|
34
|
+
key,
|
|
35
|
+
oldValueString,
|
|
36
|
+
newValueString,
|
|
37
|
+
defaultValueString: parsedDefault !== null ? JSON.stringify(parsedDefault) : null,
|
|
38
|
+
formatted,
|
|
39
|
+
logLevel,
|
|
37
40
|
});
|
|
38
|
-
renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blueBright(`${fileLink} updated: ${key} ${oldValueString} \u2192 ${newValueString}`));
|
|
39
41
|
return {
|
|
40
42
|
success: true,
|
|
41
43
|
};
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.subscribeToSequenceProps = void 0;
|
|
4
4
|
const sequence_props_watchers_1 = require("../sequence-props-watchers");
|
|
5
|
-
const subscribeToSequenceProps = ({ input: { fileName, line,
|
|
5
|
+
const subscribeToSequenceProps = ({ input: { fileName, line, keys, clientId }, remotionRoot }) => {
|
|
6
6
|
const result = (0, sequence_props_watchers_1.subscribeToSequencePropsWatchers)({
|
|
7
7
|
fileName,
|
|
8
8
|
line,
|
|
9
|
-
column,
|
|
10
9
|
keys,
|
|
11
10
|
remotionRoot,
|
|
12
11
|
clientId,
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.unsubscribeFromSequenceProps = void 0;
|
|
4
4
|
const sequence_props_watchers_1 = require("../sequence-props-watchers");
|
|
5
|
-
const unsubscribeFromSequenceProps = ({ input: { fileName,
|
|
5
|
+
const unsubscribeFromSequenceProps = ({ input: { fileName, nodePath, clientId }, remotionRoot }) => {
|
|
6
6
|
(0, sequence_props_watchers_1.unsubscribeFromSequencePropsWatchers)({
|
|
7
7
|
fileName,
|
|
8
|
-
|
|
9
|
-
column,
|
|
8
|
+
nodePath,
|
|
10
9
|
remotionRoot,
|
|
11
10
|
clientId,
|
|
12
11
|
});
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import type { CanUpdateSequencePropsResponse } from '@remotion/studio-shared';
|
|
2
|
-
export declare const subscribeToSequencePropsWatchers: ({ fileName, line,
|
|
1
|
+
import type { CanUpdateSequencePropsResponse, SequenceNodePath } from '@remotion/studio-shared';
|
|
2
|
+
export declare const subscribeToSequencePropsWatchers: ({ fileName, line, keys, remotionRoot, clientId, }: {
|
|
3
3
|
fileName: string;
|
|
4
4
|
line: number;
|
|
5
|
-
column: number;
|
|
6
5
|
keys: string[];
|
|
7
6
|
remotionRoot: string;
|
|
8
7
|
clientId: string;
|
|
9
8
|
}) => CanUpdateSequencePropsResponse;
|
|
10
|
-
export declare const unsubscribeFromSequencePropsWatchers: ({ fileName,
|
|
9
|
+
export declare const unsubscribeFromSequencePropsWatchers: ({ fileName, nodePath, remotionRoot, clientId, }: {
|
|
11
10
|
fileName: string;
|
|
12
|
-
|
|
13
|
-
column: number;
|
|
11
|
+
nodePath: SequenceNodePath;
|
|
14
12
|
remotionRoot: string;
|
|
15
13
|
clientId: string;
|
|
16
14
|
}) => void;
|
|
@@ -9,23 +9,28 @@ const file_watcher_1 = require("../file-watcher");
|
|
|
9
9
|
const live_events_1 = require("./live-events");
|
|
10
10
|
const can_update_sequence_props_1 = require("./routes/can-update-sequence-props");
|
|
11
11
|
const sequencePropsWatchers = {};
|
|
12
|
-
const makeWatcherKey = ({ absolutePath,
|
|
13
|
-
return `${absolutePath}:${
|
|
12
|
+
const makeWatcherKey = ({ absolutePath, nodePath, }) => {
|
|
13
|
+
return `${absolutePath}:${JSON.stringify(nodePath)}`;
|
|
14
14
|
};
|
|
15
|
-
const subscribeToSequencePropsWatchers = ({ fileName, line,
|
|
15
|
+
const subscribeToSequencePropsWatchers = ({ fileName, line, keys, remotionRoot, clientId, }) => {
|
|
16
16
|
var _a;
|
|
17
17
|
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if ((_a = sequencePropsWatchers[clientId]) === null || _a === void 0 ? void 0 : _a[watcherKey]) {
|
|
21
|
-
sequencePropsWatchers[clientId][watcherKey].unwatch();
|
|
22
|
-
}
|
|
23
|
-
const initialResult = (0, can_update_sequence_props_1.computeSequencePropsStatus)({
|
|
18
|
+
// Initial lookup by line+column to resolve the nodePath
|
|
19
|
+
const initialResult = (0, can_update_sequence_props_1.computeSequencePropsStatusByLine)({
|
|
24
20
|
fileName,
|
|
25
21
|
line,
|
|
26
22
|
keys,
|
|
27
23
|
remotionRoot,
|
|
28
24
|
});
|
|
25
|
+
if (!initialResult.canUpdate) {
|
|
26
|
+
return initialResult;
|
|
27
|
+
}
|
|
28
|
+
const { nodePath } = initialResult;
|
|
29
|
+
const watcherKey = makeWatcherKey({ absolutePath, nodePath });
|
|
30
|
+
// Unwatch any existing watcher for the same key
|
|
31
|
+
if ((_a = sequencePropsWatchers[clientId]) === null || _a === void 0 ? void 0 : _a[watcherKey]) {
|
|
32
|
+
sequencePropsWatchers[clientId][watcherKey].unwatch();
|
|
33
|
+
}
|
|
29
34
|
const { unwatch } = (0, file_watcher_1.installFileWatcher)({
|
|
30
35
|
file: absolutePath,
|
|
31
36
|
onChange: (type) => {
|
|
@@ -34,7 +39,7 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, keys, remoti
|
|
|
34
39
|
}
|
|
35
40
|
const result = (0, can_update_sequence_props_1.computeSequencePropsStatus)({
|
|
36
41
|
fileName,
|
|
37
|
-
|
|
42
|
+
nodePath,
|
|
38
43
|
keys,
|
|
39
44
|
remotionRoot,
|
|
40
45
|
});
|
|
@@ -42,8 +47,7 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, keys, remoti
|
|
|
42
47
|
listener.sendEventToClient({
|
|
43
48
|
type: 'sequence-props-updated',
|
|
44
49
|
fileName,
|
|
45
|
-
|
|
46
|
-
column,
|
|
50
|
+
nodePath,
|
|
47
51
|
result,
|
|
48
52
|
});
|
|
49
53
|
});
|
|
@@ -56,14 +60,16 @@ const subscribeToSequencePropsWatchers = ({ fileName, line, column, keys, remoti
|
|
|
56
60
|
return initialResult;
|
|
57
61
|
};
|
|
58
62
|
exports.subscribeToSequencePropsWatchers = subscribeToSequencePropsWatchers;
|
|
59
|
-
const unsubscribeFromSequencePropsWatchers = ({ fileName,
|
|
60
|
-
var _a;
|
|
63
|
+
const unsubscribeFromSequencePropsWatchers = ({ fileName, nodePath, remotionRoot, clientId, }) => {
|
|
61
64
|
const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
|
|
62
|
-
const watcherKey = makeWatcherKey({ absolutePath,
|
|
63
|
-
if (!sequencePropsWatchers[clientId]
|
|
65
|
+
const watcherKey = makeWatcherKey({ absolutePath, nodePath });
|
|
66
|
+
if (!sequencePropsWatchers[clientId] ||
|
|
67
|
+
!sequencePropsWatchers[clientId][watcherKey]) {
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.warn(`Unexpected: unsubscribe for sequence props watcher that does not exist (clientId=${clientId}, key=${watcherKey})`);
|
|
64
70
|
return;
|
|
65
71
|
}
|
|
66
|
-
|
|
72
|
+
sequencePropsWatchers[clientId][watcherKey].unwatch();
|
|
67
73
|
delete sequencePropsWatchers[clientId][watcherKey];
|
|
68
74
|
};
|
|
69
75
|
exports.unsubscribeFromSequencePropsWatchers = unsubscribeFromSequencePropsWatchers;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/studio-server"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/studio-server",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.433",
|
|
7
7
|
"description": "Run a Remotion Studio with a server backend",
|
|
8
8
|
"main": "dist",
|
|
9
9
|
"sideEffects": false,
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@babel/parser": "7.24.1",
|
|
28
28
|
"semver": "7.5.3",
|
|
29
29
|
"prettier": "3.8.1",
|
|
30
|
-
"remotion": "4.0.
|
|
30
|
+
"remotion": "4.0.433",
|
|
31
31
|
"recast": "0.23.11",
|
|
32
|
-
"@remotion/bundler": "4.0.
|
|
33
|
-
"@remotion/renderer": "4.0.
|
|
34
|
-
"@remotion/studio-shared": "4.0.
|
|
32
|
+
"@remotion/bundler": "4.0.433",
|
|
33
|
+
"@remotion/renderer": "4.0.433",
|
|
34
|
+
"@remotion/studio-shared": "4.0.433",
|
|
35
35
|
"memfs": "3.4.3",
|
|
36
36
|
"source-map": "0.7.3",
|
|
37
37
|
"open": "^8.4.2"
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"react": "19.2.3",
|
|
42
42
|
"@babel/types": "7.24.0",
|
|
43
43
|
"@types/semver": "^7.3.4",
|
|
44
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
44
|
+
"@remotion/eslint-config-internal": "4.0.433",
|
|
45
45
|
"eslint": "9.19.0",
|
|
46
46
|
"@types/node": "20.12.14",
|
|
47
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
47
|
+
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
48
48
|
},
|
|
49
49
|
"exports": {
|
|
50
50
|
".": "./dist/index.js",
|