@remotion/studio-server 4.0.429 → 4.0.430

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.
Files changed (27) hide show
  1. package/dist/codemods/update-sequence-props.d.ts +6 -2
  2. package/dist/codemods/update-sequence-props.js +44 -7
  3. package/dist/helpers/get-installed-dependencies.d.ts +8 -0
  4. package/dist/helpers/get-installed-dependencies.js +16 -10
  5. package/dist/hyperlinks/is-supported.d.ts +1 -0
  6. package/dist/hyperlinks/is-supported.js +82 -0
  7. package/dist/hyperlinks/make-link.d.ts +5 -0
  8. package/dist/hyperlinks/make-link.js +16 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.js +1 -0
  11. package/dist/preview-server/__tests__/multiple-lockfiles.test.d.ts +1 -0
  12. package/dist/preview-server/__tests__/multiple-lockfiles.test.js +64 -0
  13. package/dist/preview-server/api-routes.js +4 -2
  14. package/dist/preview-server/hmr-suppression.d.ts +2 -0
  15. package/dist/preview-server/hmr-suppression.js +18 -0
  16. package/dist/preview-server/hot-middleware/index.js +15 -1
  17. package/dist/preview-server/live-events.js +2 -0
  18. package/dist/preview-server/routes/can-update-sequence-props.d.ts +8 -3
  19. package/dist/preview-server/routes/can-update-sequence-props.js +64 -11
  20. package/dist/preview-server/routes/save-sequence-props.js +16 -3
  21. package/dist/preview-server/routes/subscribe-to-sequence-props.d.ts +3 -0
  22. package/dist/preview-server/routes/subscribe-to-sequence-props.js +16 -0
  23. package/dist/preview-server/routes/unsubscribe-from-sequence-props.d.ts +3 -0
  24. package/dist/preview-server/routes/unsubscribe-from-sequence-props.js +15 -0
  25. package/dist/preview-server/sequence-props-watchers.d.ts +17 -0
  26. package/dist/preview-server/sequence-props-watchers.js +79 -0
  27. package/package.json +2 -1
@@ -1,8 +1,12 @@
1
1
  import { type EnumPath } from '@remotion/studio-shared';
2
- export declare const updateSequenceProps: ({ input, targetLine, key, value, enumPaths, }: {
2
+ export declare const updateSequenceProps: ({ input, targetLine, key, value, enumPaths, defaultValue, }: {
3
3
  input: string;
4
4
  targetLine: number;
5
5
  key: string;
6
6
  value: unknown;
7
7
  enumPaths: EnumPath[];
8
- }) => Promise<string>;
8
+ defaultValue: unknown;
9
+ }) => Promise<{
10
+ output: string;
11
+ oldValueString: string;
12
+ }>;
@@ -37,17 +37,20 @@ exports.updateSequenceProps = void 0;
37
37
  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
- const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, }) => {
40
+ const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, defaultValue, }) => {
41
41
  const ast = (0, parse_ast_1.parseAst)(input);
42
42
  let found = false;
43
+ let oldValueString = '';
44
+ const isDefault = defaultValue !== null &&
45
+ JSON.stringify(value) === JSON.stringify(defaultValue);
43
46
  recast.types.visit(ast, {
44
47
  visitJSXOpeningElement(path) {
45
- var _a;
48
+ var _a, _b;
46
49
  const { node } = path;
47
50
  if (!node.loc || node.loc.start.line !== targetLine) {
48
51
  return this.traverse(path);
49
52
  }
50
- const attr = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.find((a) => {
53
+ const attrIndex = (_a = node.attributes) === null || _a === void 0 ? void 0 : _a.findIndex((a) => {
51
54
  if (a.type === 'JSXSpreadAttribute') {
52
55
  return false;
53
56
  }
@@ -56,12 +59,46 @@ const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, }
56
59
  }
57
60
  return a.name.name === key;
58
61
  });
59
- if (!attr || attr.type === 'JSXSpreadAttribute') {
60
- throw new Error(`Could not find attribute "${key}" on the JSX element at line ${targetLine}`);
62
+ const attr = attrIndex !== undefined && attrIndex !== -1
63
+ ? (_b = node.attributes) === null || _b === void 0 ? void 0 : _b[attrIndex]
64
+ : undefined;
65
+ if (attr && attr.type !== 'JSXSpreadAttribute' && attr.value) {
66
+ const printed = recast.print(attr.value).code;
67
+ // Strip JSX expression container braces, e.g. "{30}" -> "30"
68
+ oldValueString =
69
+ printed.startsWith('{') && printed.endsWith('}')
70
+ ? printed.slice(1, -1)
71
+ : printed;
72
+ }
73
+ else if (attr && attr.type !== 'JSXSpreadAttribute' && !attr.value) {
74
+ // JSX shorthand like `loop` (no value) is implicitly `true`
75
+ oldValueString = 'true';
76
+ }
77
+ else if (!attr && defaultValue !== null) {
78
+ oldValueString = JSON.stringify(defaultValue);
79
+ }
80
+ if (isDefault) {
81
+ if (attr && attr.type !== 'JSXSpreadAttribute' && node.attributes) {
82
+ node.attributes.splice(attrIndex, 1);
83
+ }
84
+ found = true;
85
+ return this.traverse(path);
61
86
  }
62
87
  const parsed = (0, parse_ast_1.parseAst)(`a = ${(0, studio_shared_1.stringifyDefaultProps)({ props: value, enumPaths })}`)
63
88
  .program.body[0].expression.right;
64
- attr.value = recast.types.builders.jsxExpressionContainer(parsed);
89
+ const newValue = value === true
90
+ ? null
91
+ : recast.types.builders.jsxExpressionContainer(parsed);
92
+ if (!attr || attr.type === 'JSXSpreadAttribute') {
93
+ const newAttr = recast.types.builders.jsxAttribute(recast.types.builders.jsxIdentifier(key), newValue);
94
+ if (!node.attributes) {
95
+ node.attributes = [];
96
+ }
97
+ node.attributes.push(newAttr);
98
+ }
99
+ else {
100
+ attr.value = newValue;
101
+ }
65
102
  found = true;
66
103
  return this.traverse(path);
67
104
  },
@@ -92,6 +129,6 @@ const updateSequenceProps = async ({ input, targetLine, key, value, enumPaths, }
92
129
  plugins: [],
93
130
  endOfLine: 'auto',
94
131
  });
95
- return prettified;
132
+ return { output: prettified, oldValueString };
96
133
  };
97
134
  exports.updateSequenceProps = updateSequenceProps;
@@ -1,6 +1,14 @@
1
+ type DepsWithVersions = {
2
+ dependencies: Record<string, string>;
3
+ devDependencies: Record<string, string>;
4
+ optionalDependencies: Record<string, string>;
5
+ peerDependencies: Record<string, string>;
6
+ };
7
+ export declare const getInstalledDependenciesWithVersions: (remotionRoot: string) => DepsWithVersions;
1
8
  export declare const getInstalledDependencies: (remotionRoot: string) => {
2
9
  dependencies: string[];
3
10
  devDependencies: string[];
4
11
  optionalDependencies: string[];
5
12
  peerDependencies: string[];
6
13
  };
14
+ export {};
@@ -3,22 +3,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getInstalledDependencies = void 0;
6
+ exports.getInstalledDependencies = exports.getInstalledDependenciesWithVersions = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
- const getInstalledDependencies = (remotionRoot) => {
9
+ const getInstalledDependenciesWithVersions = (remotionRoot) => {
10
10
  var _a, _b, _c, _d;
11
11
  const packageJsonFilePath = node_path_1.default.join(remotionRoot, 'package.json');
12
12
  const packageJson = JSON.parse(node_fs_1.default.readFileSync(packageJsonFilePath, 'utf-8'));
13
- const dependencies = Object.keys((_a = packageJson.dependencies) !== null && _a !== void 0 ? _a : {});
14
- const devDependencies = Object.keys((_b = packageJson.devDependencies) !== null && _b !== void 0 ? _b : {});
15
- const optionalDependencies = Object.keys((_c = packageJson.optionalDependencies) !== null && _c !== void 0 ? _c : {});
16
- const peerDependencies = Object.keys((_d = packageJson.peerDependencies) !== null && _d !== void 0 ? _d : {});
17
13
  return {
18
- dependencies,
19
- devDependencies,
20
- optionalDependencies,
21
- peerDependencies,
14
+ dependencies: (_a = packageJson.dependencies) !== null && _a !== void 0 ? _a : {},
15
+ devDependencies: (_b = packageJson.devDependencies) !== null && _b !== void 0 ? _b : {},
16
+ optionalDependencies: (_c = packageJson.optionalDependencies) !== null && _c !== void 0 ? _c : {},
17
+ peerDependencies: (_d = packageJson.peerDependencies) !== null && _d !== void 0 ? _d : {},
18
+ };
19
+ };
20
+ exports.getInstalledDependenciesWithVersions = getInstalledDependenciesWithVersions;
21
+ const getInstalledDependencies = (remotionRoot) => {
22
+ const deps = (0, exports.getInstalledDependenciesWithVersions)(remotionRoot);
23
+ return {
24
+ dependencies: Object.keys(deps.dependencies),
25
+ devDependencies: Object.keys(deps.devDependencies),
26
+ optionalDependencies: Object.keys(deps.optionalDependencies),
27
+ peerDependencies: Object.keys(deps.peerDependencies),
22
28
  };
23
29
  };
24
30
  exports.getInstalledDependencies = getInstalledDependencies;
@@ -0,0 +1 @@
1
+ export declare function supportsHyperlink(): false | string;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ // From https://github.com/jamestalmage/supports-hyperlinks/blob/master/index.js
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.supportsHyperlink = supportsHyperlink;
5
+ // MIT License
6
+ // Copyright (c) James Talmage <james@talmage.io> (github.com/jamestalmage)
7
+ // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8
+ // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
+ const renderer_1 = require("@remotion/renderer");
11
+ function parseVersion(versionString) {
12
+ if (/^\d{3,4}$/.test(versionString)) {
13
+ // Env var doesn't always use dots. example: 4601 => 46.1.0
14
+ const m = /(\d{1,2})(\d{2})/.exec(versionString) || [];
15
+ return {
16
+ major: 0,
17
+ minor: parseInt(m[1], 10),
18
+ patch: parseInt(m[2], 10),
19
+ };
20
+ }
21
+ const versions = (versionString || '').split('.').map((n) => parseInt(n, 10));
22
+ return {
23
+ major: versions[0],
24
+ minor: versions[1],
25
+ patch: versions[2],
26
+ };
27
+ }
28
+ function supportsHyperlink() {
29
+ const { CI, NETLIFY, TEAMCITY_VERSION, TERM_PROGRAM, TERM_PROGRAM_VERSION, VTE_VERSION, } = process.env;
30
+ // Netlify does not run a TTY, it does not need `supportsColor` check
31
+ if (NETLIFY) {
32
+ return 'Click';
33
+ }
34
+ // If they specify no colors, they probably don't want hyperlinks.
35
+ if (!renderer_1.RenderInternals.isColorSupported()) {
36
+ return false;
37
+ }
38
+ if (process.platform === 'win32') {
39
+ return false;
40
+ }
41
+ if (CI) {
42
+ return false;
43
+ }
44
+ if (TEAMCITY_VERSION) {
45
+ return false;
46
+ }
47
+ if (TERM_PROGRAM) {
48
+ const version = parseVersion(TERM_PROGRAM_VERSION || '');
49
+ switch (TERM_PROGRAM) {
50
+ case 'iTerm.app':
51
+ if (version.major === 3) {
52
+ return version.minor >= 1 ? 'Cmd+Click' : false;
53
+ }
54
+ return version.major > 3 ? 'Cmd+Click' : false;
55
+ case 'WezTerm':
56
+ return version.major >= 20200620 ? 'Click' : false;
57
+ case 'vscode':
58
+ // Cursor is at v0
59
+ if (version.major === 0) {
60
+ return process.platform === 'darwin' ? 'Option+Click' : 'Ctrl+Click';
61
+ }
62
+ return version.major > 1 ||
63
+ (version.major === 1 && version.minor >= 72)
64
+ ? process.platform === 'darwin'
65
+ ? 'Option+Click'
66
+ : 'Ctrl+Click'
67
+ : false;
68
+ // No default
69
+ }
70
+ }
71
+ if (VTE_VERSION) {
72
+ // 0.50.0 was supposed to support hyperlinks, but throws a segfault
73
+ if (VTE_VERSION === '0.50.0') {
74
+ return false;
75
+ }
76
+ const version = parseVersion(VTE_VERSION);
77
+ return version.major > 0 || version.minor >= 50
78
+ ? 'Click'
79
+ : false;
80
+ }
81
+ return false;
82
+ }
@@ -0,0 +1,5 @@
1
+ export declare const makeHyperlink: ({ text, url, fallback, }: {
2
+ text: string | ((clickInstruction: string) => string);
3
+ url: string;
4
+ fallback: string;
5
+ }) => string;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeHyperlink = void 0;
4
+ const is_supported_1 = require("./is-supported");
5
+ const OSC = '\u001B]';
6
+ const SEP = ';';
7
+ const BEL = '\u0007';
8
+ const makeHyperlink = ({ text, url, fallback, }) => {
9
+ const supports = (0, is_supported_1.supportsHyperlink)();
10
+ if (!supports) {
11
+ return fallback;
12
+ }
13
+ const label = typeof text === 'function' ? text(supports) : text;
14
+ return [OSC, '8', SEP, SEP, url, BEL, label, OSC, '8', SEP, SEP, BEL].join('');
15
+ };
16
+ exports.makeHyperlink = makeHyperlink;
package/dist/index.d.ts CHANGED
@@ -83,6 +83,12 @@ export declare const StudioServerInternals: {
83
83
  optionalDependencies: string[];
84
84
  peerDependencies: string[];
85
85
  };
86
+ getInstalledDependenciesWithVersions: (remotionRoot: string) => {
87
+ dependencies: Record<string, string>;
88
+ devDependencies: Record<string, string>;
89
+ optionalDependencies: Record<string, string>;
90
+ peerDependencies: Record<string, string>;
91
+ };
86
92
  getInstallCommand: ({ manager, packages, version, additionalArgs, }: {
87
93
  manager: import("@remotion/studio-shared").PackageManager;
88
94
  packages: string[];
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ exports.StudioServerInternals = {
30
30
  formatBytes: studio_shared_1.formatBytes,
31
31
  parseAndApplyCodemod: duplicate_composition_1.parseAndApplyCodemod,
32
32
  getInstalledDependencies: get_installed_dependencies_1.getInstalledDependencies,
33
+ getInstalledDependenciesWithVersions: get_installed_dependencies_1.getInstalledDependenciesWithVersions,
33
34
  getInstallCommand: install_command_1.getInstallCommand,
34
35
  addCompletedClientRender: client_render_queue_1.addCompletedClientRender,
35
36
  getCompletedClientRenders: client_render_queue_1.getCompletedClientRenders,
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+ var __importDefault =
3
+ (this && this.__importDefault) ||
4
+ function (mod) {
5
+ return mod && mod.__esModule ? mod : {default: mod};
6
+ };
7
+ Object.defineProperty(exports, '__esModule', {value: true});
8
+ const bun_test_1 = require('bun:test');
9
+ const node_fs_1 = __importDefault(require('node:fs'));
10
+ const node_os_1 = __importDefault(require('node:os'));
11
+ const node_path_1 = __importDefault(require('node:path'));
12
+ const get_package_manager_1 = require('../get-package-manager');
13
+ (0, bun_test_1.describe)('getPackageManager multiple lockfiles', () => {
14
+ let tempDir;
15
+ (0, bun_test_1.beforeEach)(() => {
16
+ tempDir = node_fs_1.default.mkdtempSync(
17
+ node_path_1.default.join(node_os_1.default.tmpdir(), 'remotion-test-'),
18
+ );
19
+ });
20
+ (0, bun_test_1.afterEach)(() => {
21
+ node_fs_1.default.rmSync(tempDir, {recursive: true, force: true});
22
+ });
23
+ (0, bun_test_1.test)(
24
+ 'should not throw error when multiple lockfiles are detected',
25
+ () => {
26
+ node_fs_1.default.writeFileSync(
27
+ node_path_1.default.join(tempDir, 'package-lock.json'),
28
+ '{}',
29
+ );
30
+ node_fs_1.default.writeFileSync(
31
+ node_path_1.default.join(tempDir, 'bun.lock'),
32
+ '',
33
+ );
34
+ // Should not throw
35
+ const manager = (0, get_package_manager_1.getPackageManager)(
36
+ tempDir,
37
+ undefined,
38
+ 0,
39
+ );
40
+ // Should return one of them (usually the first one in the list, which is npm)
41
+ (0, bun_test_1.expect)(manager).not.toBe('unknown');
42
+ if (typeof manager !== 'string') {
43
+ (0, bun_test_1.expect)(['npm', 'bun']).toContain(manager.manager);
44
+ }
45
+ },
46
+ );
47
+ (0, bun_test_1.test)(
48
+ 'should return npm if only package-lock.json exists',
49
+ () => {
50
+ node_fs_1.default.writeFileSync(
51
+ node_path_1.default.join(tempDir, 'package-lock.json'),
52
+ '{}',
53
+ );
54
+ const manager = (0, get_package_manager_1.getPackageManager)(
55
+ tempDir,
56
+ undefined,
57
+ 0,
58
+ );
59
+ if (typeof manager !== 'string') {
60
+ (0, bun_test_1.expect)(manager.manager).toBe('npm');
61
+ }
62
+ },
63
+ );
64
+ });
@@ -5,7 +5,6 @@ const add_render_1 = require("./routes/add-render");
5
5
  const apply_codemod_1 = require("./routes/apply-codemod");
6
6
  const apply_visual_control_change_1 = require("./routes/apply-visual-control-change");
7
7
  const can_update_default_props_1 = require("./routes/can-update-default-props");
8
- const can_update_sequence_props_1 = require("./routes/can-update-sequence-props");
9
8
  const cancel_render_1 = require("./routes/cancel-render");
10
9
  const delete_static_file_1 = require("./routes/delete-static-file");
11
10
  const install_dependency_1 = require("./routes/install-dependency");
@@ -15,7 +14,9 @@ const remove_render_1 = require("./routes/remove-render");
15
14
  const restart_studio_1 = require("./routes/restart-studio");
16
15
  const save_sequence_props_1 = require("./routes/save-sequence-props");
17
16
  const subscribe_to_file_existence_1 = require("./routes/subscribe-to-file-existence");
17
+ const subscribe_to_sequence_props_1 = require("./routes/subscribe-to-sequence-props");
18
18
  const unsubscribe_from_file_existence_1 = require("./routes/unsubscribe-from-file-existence");
19
+ const unsubscribe_from_sequence_props_1 = require("./routes/unsubscribe-from-sequence-props");
19
20
  const update_available_1 = require("./routes/update-available");
20
21
  const update_default_props_1 = require("./routes/update-default-props");
21
22
  exports.allApiRoutes = {
@@ -29,7 +30,8 @@ exports.allApiRoutes = {
29
30
  '/api/apply-visual-control-change': apply_visual_control_change_1.applyVisualControlHandler,
30
31
  '/api/apply-codemod': apply_codemod_1.applyCodemodHandler,
31
32
  '/api/can-update-default-props': can_update_default_props_1.canUpdateDefaultPropsHandler,
32
- '/api/can-update-sequence-props': can_update_sequence_props_1.canUpdateSequencePropsHandler,
33
+ '/api/subscribe-to-sequence-props': subscribe_to_sequence_props_1.subscribeToSequenceProps,
34
+ '/api/unsubscribe-from-sequence-props': unsubscribe_from_sequence_props_1.unsubscribeFromSequenceProps,
33
35
  '/api/save-sequence-props': save_sequence_props_1.saveSequencePropsHandler,
34
36
  '/api/update-available': update_available_1.handleUpdate,
35
37
  '/api/project-info': project_info_1.projectInfoHandler,
@@ -0,0 +1,2 @@
1
+ export declare function suppressHmrForFile(absolutePath: string): void;
2
+ export declare function shouldSuppressHmr(filename: string | null): boolean;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.suppressHmrForFile = suppressHmrForFile;
4
+ exports.shouldSuppressHmr = shouldSuppressHmr;
5
+ const suppressedFiles = new Set();
6
+ function suppressHmrForFile(absolutePath) {
7
+ suppressedFiles.add(absolutePath);
8
+ }
9
+ function shouldSuppressHmr(filename) {
10
+ if (filename === null) {
11
+ return false;
12
+ }
13
+ if (suppressedFiles.has(filename)) {
14
+ suppressedFiles.delete(filename);
15
+ return true;
16
+ }
17
+ return false;
18
+ }
@@ -9,6 +9,7 @@ exports.webpackHotMiddleware = void 0;
9
9
  const node_url_1 = require("node:url");
10
10
  const renderer_1 = require("@remotion/renderer");
11
11
  const studio_shared_1 = require("@remotion/studio-shared");
12
+ const hmr_suppression_1 = require("../hmr-suppression");
12
13
  const pathMatch = function (url, path) {
13
14
  try {
14
15
  return (0, node_url_1.parse)(url).pathname === path;
@@ -20,9 +21,16 @@ const pathMatch = function (url, path) {
20
21
  const webpackHotMiddleware = (compiler, logLevel) => {
21
22
  const eventStream = createEventStream(studio_shared_1.hotMiddlewareOptions.heartbeat);
22
23
  let latestStats = null;
24
+ let currentBuildSuppressed = false;
23
25
  compiler.hooks.invalid.tap('remotion', onInvalid);
24
26
  compiler.hooks.done.tap('remotion', onDone);
25
- function onInvalid() {
27
+ function onInvalid(filename) {
28
+ if ((0, hmr_suppression_1.shouldSuppressHmr)(filename)) {
29
+ currentBuildSuppressed = true;
30
+ latestStats = null;
31
+ return;
32
+ }
33
+ currentBuildSuppressed = false;
26
34
  latestStats = null;
27
35
  renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Building...');
28
36
  eventStream === null || eventStream === void 0 ? void 0 : eventStream.publish({
@@ -32,6 +40,12 @@ const webpackHotMiddleware = (compiler, logLevel) => {
32
40
  function onDone(statsResult) {
33
41
  // Keep hold of latest stats so they can be propagated to new clients
34
42
  latestStats = statsResult;
43
+ if (currentBuildSuppressed) {
44
+ // Still send the "built" event so the client hash stays in sync,
45
+ // but skip the "building" spinner. This avoids accumulating
46
+ // a large HMR delta for the next real build.
47
+ currentBuildSuppressed = false;
48
+ }
35
49
  publishStats('built', latestStats, eventStream);
36
50
  }
37
51
  const middleware = function (req, res, next) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.setLiveEventsListener = exports.waitForLiveEventsListener = exports.makeLiveEventsRouter = void 0;
4
4
  const server_ready_1 = require("../server-ready");
5
5
  const file_existence_watchers_1 = require("./file-existence-watchers");
6
+ const sequence_props_watchers_1 = require("./sequence-props-watchers");
6
7
  const serializeMessage = (message) => {
7
8
  return `data: ${JSON.stringify(message)}\n\n`;
8
9
  };
@@ -33,6 +34,7 @@ const makeLiveEventsRouter = (logLevel) => {
33
34
  }
34
35
  request.on('close', () => {
35
36
  (0, file_existence_watchers_1.unsubscribeClientFileExistenceWatchers)(clientId);
37
+ (0, sequence_props_watchers_1.unsubscribeClientSequencePropsWatchers)(clientId);
36
38
  clients = clients.filter((client) => client.id !== clientId);
37
39
  // If all clients disconnected, print a comment so user can easily restart it.
38
40
  if (clients.length === 0) {
@@ -1,5 +1,10 @@
1
1
  import type { Expression } from '@babel/types';
2
- import type { CanUpdateSequencePropsRequest, CanUpdateSequencePropsResponse } from '@remotion/studio-shared';
3
- import type { ApiHandler } from '../api-types';
2
+ import type { CanUpdateSequencePropsResponse } from '@remotion/studio-shared';
4
3
  export declare const isStaticValue: (node: Expression) => boolean;
5
- export declare const canUpdateSequencePropsHandler: ApiHandler<CanUpdateSequencePropsRequest, CanUpdateSequencePropsResponse>;
4
+ export declare const extractStaticValue: (node: Expression) => unknown;
5
+ export declare const computeSequencePropsStatus: ({ fileName, line, keys, remotionRoot, }: {
6
+ fileName: string;
7
+ line: number;
8
+ keys: string[];
9
+ remotionRoot: string;
10
+ }) => CanUpdateSequencePropsResponse;
@@ -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.canUpdateSequencePropsHandler = exports.isStaticValue = void 0;
39
+ exports.computeSequencePropsStatus = 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"));
@@ -62,6 +62,53 @@ const isStaticValue = (node) => {
62
62
  }
63
63
  };
64
64
  exports.isStaticValue = isStaticValue;
65
+ const extractStaticValue = (node) => {
66
+ switch (node.type) {
67
+ case 'NumericLiteral':
68
+ case 'StringLiteral':
69
+ case 'BooleanLiteral':
70
+ return node.value;
71
+ case 'NullLiteral':
72
+ return null;
73
+ case 'UnaryExpression': {
74
+ const un = node;
75
+ if (un.argument.type === 'NumericLiteral') {
76
+ const val = un.argument.value;
77
+ return un.operator === '-' ? -val : val;
78
+ }
79
+ return undefined;
80
+ }
81
+ case 'ArrayExpression':
82
+ return node.elements.map((el) => {
83
+ if (el === null || el.type === 'SpreadElement') {
84
+ return undefined;
85
+ }
86
+ return (0, exports.extractStaticValue)(el);
87
+ });
88
+ case 'ObjectExpression': {
89
+ const obj = node;
90
+ const result = {};
91
+ for (const prop of obj.properties) {
92
+ if (prop.type === 'ObjectProperty') {
93
+ const p = prop;
94
+ const key = p.key.type === 'Identifier'
95
+ ? p.key.name
96
+ : p.key.type === 'StringLiteral' ||
97
+ p.key.type === 'NumericLiteral'
98
+ ? String(p.key.value)
99
+ : undefined;
100
+ if (key !== undefined) {
101
+ result[key] = (0, exports.extractStaticValue)(p.value);
102
+ }
103
+ }
104
+ }
105
+ return result;
106
+ }
107
+ default:
108
+ return undefined;
109
+ }
110
+ };
111
+ exports.extractStaticValue = extractStaticValue;
65
112
  const getPropsStatus = (jsxElement) => {
66
113
  const props = {};
67
114
  for (const attr of jsxElement.attributes) {
@@ -77,11 +124,14 @@ const getPropsStatus = (jsxElement) => {
77
124
  }
78
125
  const { value } = attr;
79
126
  if (!value) {
80
- props[name] = { canUpdate: true };
127
+ props[name] = { canUpdate: true, codeValue: true };
81
128
  continue;
82
129
  }
83
130
  if (value.type === 'StringLiteral') {
84
- props[name] = { canUpdate: true };
131
+ props[name] = {
132
+ canUpdate: true,
133
+ codeValue: value.value,
134
+ };
85
135
  continue;
86
136
  }
87
137
  if (value.type === 'JSXExpressionContainer') {
@@ -91,7 +141,10 @@ const getPropsStatus = (jsxElement) => {
91
141
  props[name] = { canUpdate: false, reason: 'computed' };
92
142
  continue;
93
143
  }
94
- props[name] = { canUpdate: true };
144
+ props[name] = {
145
+ canUpdate: true,
146
+ codeValue: (0, exports.extractStaticValue)(expression),
147
+ };
95
148
  continue;
96
149
  }
97
150
  props[name] = { canUpdate: false, reason: 'computed' };
@@ -112,7 +165,7 @@ const findJsxElementAtLine = (ast, targetLine) => {
112
165
  });
113
166
  return found;
114
167
  };
115
- const canUpdateSequencePropsHandler = ({ input: { fileName, line, column: _column, keys }, remotionRoot }) => {
168
+ const computeSequencePropsStatus = ({ fileName, line, keys, remotionRoot, }) => {
116
169
  try {
117
170
  const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
118
171
  const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
@@ -132,19 +185,19 @@ const canUpdateSequencePropsHandler = ({ input: { fileName, line, column: _colum
132
185
  filteredProps[key] = allProps[key];
133
186
  }
134
187
  else {
135
- filteredProps[key] = { canUpdate: false, reason: 'computed' };
188
+ filteredProps[key] = { canUpdate: true, codeValue: undefined };
136
189
  }
137
190
  }
138
- return Promise.resolve({
191
+ return {
139
192
  canUpdate: true,
140
193
  props: filteredProps,
141
- });
194
+ };
142
195
  }
143
196
  catch (err) {
144
- return Promise.resolve({
197
+ return {
145
198
  canUpdate: false,
146
199
  reason: err.message,
147
- });
200
+ };
148
201
  }
149
202
  };
150
- exports.canUpdateSequencePropsHandler = canUpdateSequencePropsHandler;
203
+ exports.computeSequencePropsStatus = computeSequencePropsStatus;
@@ -6,8 +6,11 @@ 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");
9
10
  const update_sequence_props_1 = require("../../codemods/update-sequence-props");
10
- const saveSequencePropsHandler = async ({ input: { fileName, line, column: _column, key, value, enumPaths }, remotionRoot, }) => {
11
+ const make_link_1 = require("../../hyperlinks/make-link");
12
+ const hmr_suppression_1 = require("../hmr-suppression");
13
+ const saveSequencePropsHandler = async ({ input: { fileName, line, column, key, value, enumPaths, defaultValue }, remotionRoot, logLevel, }) => {
11
14
  try {
12
15
  const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
13
16
  const fileRelativeToRoot = node_path_1.default.relative(remotionRoot, absolutePath);
@@ -15,14 +18,24 @@ const saveSequencePropsHandler = async ({ input: { fileName, line, column: _colu
15
18
  throw new Error('Cannot modify a file outside the project');
16
19
  }
17
20
  const fileContents = (0, node_fs_1.readFileSync)(absolutePath, 'utf-8');
18
- const updated = await (0, update_sequence_props_1.updateSequenceProps)({
21
+ const { output, oldValueString } = await (0, update_sequence_props_1.updateSequenceProps)({
19
22
  input: fileContents,
20
23
  targetLine: line,
21
24
  key,
22
25
  value: JSON.parse(value),
23
26
  enumPaths,
27
+ defaultValue: defaultValue !== null ? JSON.parse(defaultValue) : null,
24
28
  });
25
- (0, node_fs_1.writeFileSync)(absolutePath, updated);
29
+ (0, hmr_suppression_1.suppressHmrForFile)(absolutePath);
30
+ (0, node_fs_1.writeFileSync)(absolutePath, output);
31
+ const newValueString = JSON.stringify(JSON.parse(value));
32
+ const locationLabel = `${fileRelativeToRoot}:${line}:${column}`;
33
+ const fileLink = (0, make_link_1.makeHyperlink)({
34
+ url: `file://${absolutePath}`,
35
+ text: locationLabel,
36
+ fallback: locationLabel,
37
+ });
38
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, renderer_1.RenderInternals.chalk.blueBright(`${fileLink} updated: ${key} ${oldValueString} \u2192 ${newValueString}`));
26
39
  return {
27
40
  success: true,
28
41
  };
@@ -0,0 +1,3 @@
1
+ import type { SubscribeToSequencePropsRequest, SubscribeToSequencePropsResponse } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const subscribeToSequenceProps: ApiHandler<SubscribeToSequencePropsRequest, SubscribeToSequencePropsResponse>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.subscribeToSequenceProps = void 0;
4
+ const sequence_props_watchers_1 = require("../sequence-props-watchers");
5
+ const subscribeToSequenceProps = ({ input: { fileName, line, column, keys, clientId }, remotionRoot }) => {
6
+ const result = (0, sequence_props_watchers_1.subscribeToSequencePropsWatchers)({
7
+ fileName,
8
+ line,
9
+ column,
10
+ keys,
11
+ remotionRoot,
12
+ clientId,
13
+ });
14
+ return Promise.resolve(result);
15
+ };
16
+ exports.subscribeToSequenceProps = subscribeToSequenceProps;
@@ -0,0 +1,3 @@
1
+ import type { UnsubscribeFromSequencePropsRequest } from '@remotion/studio-shared';
2
+ import type { ApiHandler } from '../api-types';
3
+ export declare const unsubscribeFromSequenceProps: ApiHandler<UnsubscribeFromSequencePropsRequest, undefined>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.unsubscribeFromSequenceProps = void 0;
4
+ const sequence_props_watchers_1 = require("../sequence-props-watchers");
5
+ const unsubscribeFromSequenceProps = ({ input: { fileName, line, column, clientId }, remotionRoot }) => {
6
+ (0, sequence_props_watchers_1.unsubscribeFromSequencePropsWatchers)({
7
+ fileName,
8
+ line,
9
+ column,
10
+ remotionRoot,
11
+ clientId,
12
+ });
13
+ return Promise.resolve(undefined);
14
+ };
15
+ exports.unsubscribeFromSequenceProps = unsubscribeFromSequenceProps;
@@ -0,0 +1,17 @@
1
+ import type { CanUpdateSequencePropsResponse } from '@remotion/studio-shared';
2
+ export declare const subscribeToSequencePropsWatchers: ({ fileName, line, column, keys, remotionRoot, clientId, }: {
3
+ fileName: string;
4
+ line: number;
5
+ column: number;
6
+ keys: string[];
7
+ remotionRoot: string;
8
+ clientId: string;
9
+ }) => CanUpdateSequencePropsResponse;
10
+ export declare const unsubscribeFromSequencePropsWatchers: ({ fileName, line, column, remotionRoot, clientId, }: {
11
+ fileName: string;
12
+ line: number;
13
+ column: number;
14
+ remotionRoot: string;
15
+ clientId: string;
16
+ }) => void;
17
+ export declare const unsubscribeClientSequencePropsWatchers: (clientId: string) => void;
@@ -0,0 +1,79 @@
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.unsubscribeClientSequencePropsWatchers = exports.unsubscribeFromSequencePropsWatchers = exports.subscribeToSequencePropsWatchers = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const file_watcher_1 = require("../file-watcher");
9
+ const live_events_1 = require("./live-events");
10
+ const can_update_sequence_props_1 = require("./routes/can-update-sequence-props");
11
+ const sequencePropsWatchers = {};
12
+ const makeWatcherKey = ({ absolutePath, line, column, }) => {
13
+ return `${absolutePath}:${line}:${column}`;
14
+ };
15
+ const subscribeToSequencePropsWatchers = ({ fileName, line, column, keys, remotionRoot, clientId, }) => {
16
+ var _a;
17
+ const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
18
+ const watcherKey = makeWatcherKey({ absolutePath, line, column });
19
+ // Unwatch any existing watcher for the same key
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)({
24
+ fileName,
25
+ line,
26
+ keys,
27
+ remotionRoot,
28
+ });
29
+ const { unwatch } = (0, file_watcher_1.installFileWatcher)({
30
+ file: absolutePath,
31
+ onChange: (type) => {
32
+ if (type === 'deleted') {
33
+ return;
34
+ }
35
+ const result = (0, can_update_sequence_props_1.computeSequencePropsStatus)({
36
+ fileName,
37
+ line,
38
+ keys,
39
+ remotionRoot,
40
+ });
41
+ (0, live_events_1.waitForLiveEventsListener)().then((listener) => {
42
+ listener.sendEventToClient({
43
+ type: 'sequence-props-updated',
44
+ fileName,
45
+ line,
46
+ column,
47
+ result,
48
+ });
49
+ });
50
+ },
51
+ });
52
+ if (!sequencePropsWatchers[clientId]) {
53
+ sequencePropsWatchers[clientId] = {};
54
+ }
55
+ sequencePropsWatchers[clientId][watcherKey] = { unwatch };
56
+ return initialResult;
57
+ };
58
+ exports.subscribeToSequencePropsWatchers = subscribeToSequencePropsWatchers;
59
+ const unsubscribeFromSequencePropsWatchers = ({ fileName, line, column, remotionRoot, clientId, }) => {
60
+ var _a;
61
+ const absolutePath = node_path_1.default.resolve(remotionRoot, fileName);
62
+ const watcherKey = makeWatcherKey({ absolutePath, line, column });
63
+ if (!sequencePropsWatchers[clientId]) {
64
+ return;
65
+ }
66
+ (_a = sequencePropsWatchers[clientId][watcherKey]) === null || _a === void 0 ? void 0 : _a.unwatch();
67
+ delete sequencePropsWatchers[clientId][watcherKey];
68
+ };
69
+ exports.unsubscribeFromSequencePropsWatchers = unsubscribeFromSequencePropsWatchers;
70
+ const unsubscribeClientSequencePropsWatchers = (clientId) => {
71
+ if (!sequencePropsWatchers[clientId]) {
72
+ return;
73
+ }
74
+ Object.values(sequencePropsWatchers[clientId]).forEach((watcher) => {
75
+ watcher.unwatch();
76
+ });
77
+ delete sequencePropsWatchers[clientId];
78
+ };
79
+ exports.unsubscribeClientSequencePropsWatchers = unsubscribeClientSequencePropsWatchers;
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.429",
6
+ "version": "4.0.430",
7
7
  "description": "Run a Remotion Studio with a server backend",
8
8
  "main": "dist",
9
9
  "sideEffects": false,
@@ -11,6 +11,7 @@
11
11
  "lint": "eslint src",
12
12
  "test": "bun test src",
13
13
  "formatting": "oxfmt src --check",
14
+ "format": "oxfmt src",
14
15
  "make": "tsgo -d"
15
16
  },
16
17
  "author": "Jonny Burger <jonny@remotion.dev>",