@doubledigit/cli 0.9.0 → 0.10.1
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/README.md +16 -11
- package/dist/commands/actions.d.ts.map +1 -1
- package/dist/commands/actions.js +9 -6
- package/dist/commands/remotion-hub-add.d.ts.map +1 -1
- package/dist/commands/remotion-hub-add.js +4 -0
- package/dist/commands/remotion-hub-init.d.ts.map +1 -1
- package/dist/commands/remotion-hub-init.js +18 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/lib/actions-client.d.ts.map +1 -1
- package/dist/lib/actions-client.js +4 -1
- package/dist/lib/component-frameworks.d.ts +34 -0
- package/dist/lib/component-frameworks.d.ts.map +1 -0
- package/dist/lib/component-frameworks.js +121 -0
- package/dist/lib/remotion-hub-init.d.ts +25 -4
- package/dist/lib/remotion-hub-init.d.ts.map +1 -1
- package/dist/lib/remotion-hub-init.js +159 -97
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ pnpm dd doctor
|
|
|
21
21
|
pnpm onboard
|
|
22
22
|
pnpm onboard -- --no-run
|
|
23
23
|
pnpm dd actions remotion-hub
|
|
24
|
-
pnpm dd actions remotion-hub init temp-project
|
|
24
|
+
pnpm dd actions remotion-hub init temp-project --framework remotion
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
If installed globally, the binary is available as `dd`:
|
|
@@ -32,7 +32,7 @@ dd doctor
|
|
|
32
32
|
dd onboard
|
|
33
33
|
dd onboard --no-run
|
|
34
34
|
dd actions remotion-hub
|
|
35
|
-
dd actions remotion-hub init temp-project
|
|
35
|
+
dd actions remotion-hub init temp-project --framework remotion
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Requirements
|
|
@@ -117,24 +117,28 @@ Use `dd actions` to discover or call enabled micro-app actions through the runni
|
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
119
|
npx @doubledigit/cli@latest actions remotion-hub
|
|
120
|
-
npx @doubledigit/cli@latest actions remotion-hub init temp-project --yes
|
|
121
|
-
npx @doubledigit/cli@latest actions remotion-hub
|
|
122
|
-
npx @doubledigit/cli@latest actions remotion-hub
|
|
120
|
+
npx @doubledigit/cli@latest actions remotion-hub init temp-project --framework remotion --yes
|
|
121
|
+
npx @doubledigit/cli@latest actions remotion-hub init html-project --framework hyperframe --yes
|
|
122
|
+
npx @doubledigit/cli@latest actions remotion-hub list-components --framework remotion --limit 5
|
|
123
|
+
npx @doubledigit/cli@latest actions remotion-hub search-components --framework hyperframe --query "animated chart" --limit 5
|
|
123
124
|
npx @doubledigit/cli@latest actions remotion-hub register-component --json-file component.json
|
|
124
125
|
```
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
Component Hub currently uses the `remotion-hub` compatibility command. `init` and `add` are handled locally by the CLI because they create or modify files on the caller's machine. Other actions, such as search and registry lookup, call the configured Double Digit app over HTTP.
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
Pass `--framework remotion|hyperframe` when the target framework is known. Remotion remains the default for compatibility and creates Remotion's Hello World starter so `remotion studio` opens with a visible composition. HyperFrames init requires Node.js 22 or newer and uses `npx hyperframes preview` as its dev command.
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
Pass `--skip-framework-create` when adding Component Hub files to an existing project. `--skip-remotion-create` and `--hub-only` remain compatibility aliases.
|
|
132
|
+
|
|
133
|
+
After init, the CLI attempts to install framework skills and the Double Digit Component Hub skill from the created project directory. The retry command list depends on the selected framework; examples include:
|
|
131
134
|
|
|
132
135
|
```bash
|
|
133
136
|
npx skills add remotion-dev/skills --all
|
|
137
|
+
npx skills add heygen-com/hyperframes
|
|
134
138
|
npx skills add crystalphantom/double-digit --skill dd-remotion-hub --agent '*' --yes
|
|
135
139
|
```
|
|
136
140
|
|
|
137
|
-
Pass `--skip-skills` to skip skill installation. If skill installation fails, init still completes the project scaffold and prints exact retry commands. Use `--yes` for agent-run or scripted init flows; `-y` and `--non-interactive` are accepted compatibility aliases.
|
|
141
|
+
Pass `--skip-skills` to skip skill installation. If skill installation fails, init still completes the project scaffold and prints exact retry commands. Use `--yes` for agent-run or scripted init flows; `-y` and `--non-interactive` are accepted compatibility aliases and are forwarded to nested framework scaffolds where supported.
|
|
138
142
|
|
|
139
143
|
Register a component with simple JSON:
|
|
140
144
|
|
|
@@ -149,6 +153,7 @@ Minimal `component.json`:
|
|
|
149
153
|
"title": "Animated Chart",
|
|
150
154
|
"slug": "animated-chart",
|
|
151
155
|
"namespace": "doubledigit",
|
|
156
|
+
"framework": "remotion",
|
|
152
157
|
"kind": "component",
|
|
153
158
|
"description": "Animated chart scene.",
|
|
154
159
|
"tags": ["chart", "animation"],
|
|
@@ -157,7 +162,7 @@ Minimal `component.json`:
|
|
|
157
162
|
}
|
|
158
163
|
```
|
|
159
164
|
|
|
160
|
-
When no `--registry`, `--url`, or `--app-url` is passed,
|
|
165
|
+
When no `--registry`, `--url`, or `--app-url` is passed, Component Hub init writes a registry URL from exported `DD_APP_URL`, `APP_URL`, `NEXT_PUBLIC_APP_URL`, or `BETTER_AUTH_URL`; then the release-configured hosted app URL; then `http://localhost:3111` for local development builds.
|
|
161
166
|
|
|
162
167
|
For HTTP-backed actions, the command resolves the app URL from exported `DD_APP_URL`, `APP_URL`, or `BETTER_AUTH_URL`; then local env-file `DD_APP_URL`; then the release-configured hosted app URL. It prints JSON responses to stdout. It also reads `.env`, `.env.local`, `apps/main-app/.env`, and `apps/main-app/.env.local`; exported shell values take precedence.
|
|
163
168
|
|
|
@@ -171,7 +176,7 @@ npx @doubledigit/cli@latest actions remotion-hub
|
|
|
171
176
|
|
|
172
177
|
This hosted default does not require a local checkout or running app.
|
|
173
178
|
|
|
174
|
-
Before relying on the hosted default in automation, run discovery as a preflight. If the hosted action endpoint returns HTTP 500 or is unreachable, use `DD_APP_URL` or `--url` to target a local or self-hosted Double Digit app until the hosted
|
|
179
|
+
Before relying on the hosted default in automation, run discovery as a preflight. If the hosted action endpoint returns HTTP 500 or is unreachable, use `DD_APP_URL` or `--url` to target a local or self-hosted Double Digit app until the hosted Component Hub endpoint is healthy.
|
|
175
180
|
|
|
176
181
|
Local development or self-hosted environments should override the target explicitly:
|
|
177
182
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"AAqCA,wBAAgB,uBAAuB,WAKtC;AAsED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE;WACf,MAAM;WAAS,MAAM;IAuBxD;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGrD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,WAGpD;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI7D;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,EAAE,YAI5D;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3D"}
|
package/dist/commands/actions.js
CHANGED
|
@@ -10,6 +10,7 @@ import { runRemotionHubInitCommand } from './remotion-hub-init.js';
|
|
|
10
10
|
const actionValueOptions = new Set([
|
|
11
11
|
'--app-url',
|
|
12
12
|
'--dir',
|
|
13
|
+
'--framework',
|
|
13
14
|
'--id-or-slug',
|
|
14
15
|
'--kind',
|
|
15
16
|
'--limit',
|
|
@@ -44,6 +45,7 @@ Options:
|
|
|
44
45
|
--query <text> Set input.query
|
|
45
46
|
--limit <number> Set input.limit
|
|
46
47
|
--namespace <namespace> Set input namespace/filter
|
|
48
|
+
--framework <framework> Set input framework/filter
|
|
47
49
|
--slug <slug> Set input.slug
|
|
48
50
|
--id-or-slug <value> Set input.idOrSlug
|
|
49
51
|
--kind <kind> Set input kind/filter
|
|
@@ -52,13 +54,14 @@ Options:
|
|
|
52
54
|
|
|
53
55
|
Examples:
|
|
54
56
|
npx @doubledigit/cli@latest actions remotion-hub
|
|
55
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
|
|
56
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --
|
|
57
|
-
npx @doubledigit/cli@latest actions remotion-hub
|
|
58
|
-
npx @doubledigit/cli@latest actions remotion-hub
|
|
59
|
-
npx @doubledigit/cli@latest actions remotion-hub
|
|
57
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
|
|
58
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework hyperframe --yes
|
|
59
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
|
|
60
|
+
npx @doubledigit/cli@latest actions remotion-hub list-components --framework remotion --limit 5
|
|
61
|
+
npx @doubledigit/cli@latest actions remotion-hub add terminal-scene --framework remotion --dir my-video
|
|
62
|
+
npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query "animated chart" --limit 5
|
|
60
63
|
npx @doubledigit/cli@latest actions remotion-hub register-component --json-file component.json
|
|
61
|
-
npx @doubledigit/cli@latest actions remotion-hub get-registry-payload --json '{"namespace":"doubledigit","slug":"motion-strip"}'
|
|
64
|
+
npx @doubledigit/cli@latest actions remotion-hub get-registry-payload --json '{"namespace":"doubledigit","slug":"motion-strip","framework":"remotion"}'
|
|
62
65
|
`;
|
|
63
66
|
}
|
|
64
67
|
function readActionEnvFiles(paths) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotion-hub-add.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"remotion-hub-add.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AA+E9F,wBAAgB,uBAAuB,WAwBtC;AAkBD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CA4B9C"}
|
|
@@ -5,6 +5,7 @@ const valueFlags = new Set([
|
|
|
5
5
|
'namespace',
|
|
6
6
|
'registry',
|
|
7
7
|
'url',
|
|
8
|
+
'framework',
|
|
8
9
|
]);
|
|
9
10
|
function parseArgs(args) {
|
|
10
11
|
const positional = [];
|
|
@@ -72,6 +73,7 @@ Options:
|
|
|
72
73
|
--registry <url> Double Digit app URL or /api/remotion-hub/r endpoint
|
|
73
74
|
--url, --app-url <url> Alias for --registry
|
|
74
75
|
--namespace <name> Registry namespace when item is not namespace/slug
|
|
76
|
+
--framework <name> Framework tag (e.g. remotion, hyperframe)
|
|
75
77
|
--force Overwrite existing generated files
|
|
76
78
|
--skip-install Do not install npm dependencies
|
|
77
79
|
--no-install Alias for --skip-install
|
|
@@ -80,6 +82,7 @@ Options:
|
|
|
80
82
|
Examples:
|
|
81
83
|
npx @doubledigit/cli@latest actions remotion-hub add terminal-scene
|
|
82
84
|
npx @doubledigit/cli@latest actions remotion-hub add doubledigit/terminal-scene --dir ./my-video
|
|
85
|
+
npx @doubledigit/cli@latest actions remotion-hub add doubledigit/terminal-scene --framework hyperframe
|
|
83
86
|
npx @doubledigit/cli@latest actions remotion-hub add terminal-scene --registry https://double-digit.example --skip-install
|
|
84
87
|
`;
|
|
85
88
|
}
|
|
@@ -116,6 +119,7 @@ export async function runRemotionHubAddCommand(args) {
|
|
|
116
119
|
force: booleanFlag(parsed.flags, 'force'),
|
|
117
120
|
skipInstall: booleanFlag(parsed.flags, 'skip-install', 'no-install'),
|
|
118
121
|
quiet: json,
|
|
122
|
+
framework: stringFlag(parsed.flags, 'framework'),
|
|
119
123
|
});
|
|
120
124
|
if (json)
|
|
121
125
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/commands/remotion-hub-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAuFhG,wBAAgB,wBAAwB,WAgCvC;AAkCD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,wBAAwB,GAAG,SAAS,CA8B9F"}
|
|
@@ -2,6 +2,7 @@ import { runRemotionHubInit } from '../lib/remotion-hub-init.js';
|
|
|
2
2
|
const valueFlags = new Set([
|
|
3
3
|
'app-url',
|
|
4
4
|
'dir',
|
|
5
|
+
'framework',
|
|
5
6
|
'namespace',
|
|
6
7
|
'registry',
|
|
7
8
|
'url',
|
|
@@ -46,6 +47,7 @@ function parseArgs(args) {
|
|
|
46
47
|
|| rawKey === 'hub-only'
|
|
47
48
|
|| rawKey === 'json'
|
|
48
49
|
|| rawKey === 'non-interactive'
|
|
50
|
+
|| rawKey === 'skip-framework-create'
|
|
49
51
|
|| rawKey === 'skip-skills'
|
|
50
52
|
|| rawKey === 'skip-remotion-create'
|
|
51
53
|
|| rawKey === 'yes') {
|
|
@@ -69,9 +71,9 @@ function booleanFlag(flags, ...names) {
|
|
|
69
71
|
}
|
|
70
72
|
export function buildRemotionHubInitHelp() {
|
|
71
73
|
return `
|
|
72
|
-
dd actions remotion-hub init - Initialize a local
|
|
74
|
+
dd actions remotion-hub init - Initialize a local Component Hub project
|
|
73
75
|
|
|
74
|
-
Creates a Remotion
|
|
76
|
+
Creates a Remotion starter by default, then adds Component Hub config files.
|
|
75
77
|
|
|
76
78
|
Usage:
|
|
77
79
|
dd actions remotion-hub init [project-dir] [options]
|
|
@@ -81,8 +83,10 @@ Options:
|
|
|
81
83
|
--registry <url> Double Digit app URL or /api/remotion-hub/r endpoint
|
|
82
84
|
--url, --app-url <url> Alias for --registry
|
|
83
85
|
--namespace <name> Registry namespace (default: doubledigit)
|
|
84
|
-
--
|
|
85
|
-
--
|
|
86
|
+
--framework <name> Framework to scaffold (remotion or hyperframe; default: remotion)
|
|
87
|
+
--skip-framework-create Only add Component Hub files; do not run framework scaffold
|
|
88
|
+
--skip-remotion-create Alias for --skip-framework-create
|
|
89
|
+
--hub-only Alias for --skip-framework-create
|
|
86
90
|
--skip-skills Do not install agent skills
|
|
87
91
|
--yes, -y Skip prompts in nested tools and use defaults
|
|
88
92
|
--non-interactive Alias for --yes
|
|
@@ -92,15 +96,18 @@ Options:
|
|
|
92
96
|
Examples:
|
|
93
97
|
npx @doubledigit/cli@latest actions remotion-hub init
|
|
94
98
|
npx @doubledigit/cli@latest actions remotion-hub init my-video
|
|
95
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
|
|
96
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --
|
|
99
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
|
|
100
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework hyperframe --yes
|
|
101
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
|
|
97
102
|
npx @doubledigit/cli@latest actions remotion-hub init my-video --registry https://double-digit.example
|
|
98
103
|
`;
|
|
99
104
|
}
|
|
100
105
|
function printInit(result) {
|
|
101
|
-
console.log(`Initialized
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
console.log(`Initialized Component Hub in ${result.projectDir}`);
|
|
107
|
+
console.log(`Framework: ${result.framework}`);
|
|
108
|
+
console.log(`Dev command: ${result.devCommand}`);
|
|
109
|
+
if (!result.create.skipped) {
|
|
110
|
+
console.log(` Framework project: ${result.create.command} ${result.create.args.join(' ')}`);
|
|
104
111
|
}
|
|
105
112
|
for (const file of result.created) {
|
|
106
113
|
console.log(` ${file}`);
|
|
@@ -141,6 +148,8 @@ export function runRemotionHubInitCommand(args) {
|
|
|
141
148
|
registry: stringFlag(parsed.flags, 'registry', 'url', 'app-url'),
|
|
142
149
|
namespace: stringFlag(parsed.flags, 'namespace'),
|
|
143
150
|
force: booleanFlag(parsed.flags, 'force'),
|
|
151
|
+
framework: stringFlag(parsed.flags, 'framework'),
|
|
152
|
+
skipFrameworkCreate: booleanFlag(parsed.flags, 'skip-framework-create', 'skip-remotion-create', 'hub-only'),
|
|
144
153
|
skipRemotionCreate: booleanFlag(parsed.flags, 'skip-remotion-create', 'hub-only'),
|
|
145
154
|
skipSkills: booleanFlag(parsed.flags, 'skip-skills'),
|
|
146
155
|
nonInteractive: booleanFlag(parsed.flags, 'yes', 'y', 'non-interactive'),
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* without importing the full extension-management dependency graph first.
|
|
7
7
|
*/
|
|
8
8
|
declare const command: string, args: string[];
|
|
9
|
-
declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare local development and start the app\n run Start the local app with automatic DB bootstrap\n dev Start DB + app without migrations or seed data\n db <subcommand> Database helpers (status, migrate, create)\n actions <app> [action] Discover and invoke micro-app actions\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard # setup + start the dev server\n dd onboard --no-run # setup only\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd dev\n dd db status\n npx @doubledigit/cli@latest actions remotion-hub init my-video --yes\n npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills\n npx @doubledigit/cli@latest actions remotion-hub search-components --query \"animated chart\"\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
|
|
9
|
+
declare const HELP = "\n@doubledigit/cli \u2014 Manage extensions and local setup\n\nCommands:\n doctor Check local prerequisites and project health\n onboard Prepare local development and start the app\n run Start the local app with automatic DB bootstrap\n dev Start DB + app without migrations or seed data\n db <subcommand> Database helpers (status, migrate, create)\n actions <app> [action] Discover and invoke micro-app actions\n create <name> Scaffold a new micro-app from the template\n init <project-name> Scaffold a new Double Digit project\n add|install <source> Install an extension from GitHub or a marketplace\n sync Regenerate micro-apps.ts from dd-apps.config.json\n enable <name> Enable a micro-app (updates config + runs sync)\n disable <name> Disable a micro-app (updates config + runs sync)\n uninstall|remove <name> Completely remove a micro-app\n list List all discovered micro-apps with enabled/disabled status\n info <name> Show detailed info about an installed extension\n outdated Check for outdated marketplace extensions\n reconcile Detect drift between lock file, marketplace, and local files\n marketplace <sub> Manage marketplace registrations (add/list/update/remove)\n browse [marketplace] Browse available extensions in registered marketplaces\n\nOptions:\n --help, -h Show this help message\n\nExamples:\n dd doctor\n dd onboard --yes\n dd onboard # setup + start the dev server\n dd onboard --no-run # setup only\n dd init my-project # scaffold and bootstrap a fresh project\n dd init my-project --run # scaffold + bootstrap + start\n dd init my-project --skip-install --no-git\n dd run\n dd dev\n dd db status\n npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes\n npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills\n npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query \"animated chart\"\n dd create invoice-tracker\n dd add gh:owner/repo/extensions/micro-apps/habit-tracker\n dd add habit-tracker@community\n dd info habit-tracker\n dd reconcile\n dd marketplace add digitaldouble/dd-marketplace\n dd browse community\n DD_APPS=tasks,agent-v2 dd sync\n";
|
|
10
10
|
declare function requireArg(value: string | undefined, usage: string): string;
|
|
11
11
|
declare function runAddCommand(rawArgs: string[]): Promise<void>;
|
|
12
12
|
declare function main(): Promise<void>;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,QAAA,MAAW,OAAO,UAAK,IAAI,UAAgB,CAAC;AAE5C,QAAA,MAAM,IAAI,y7EAiDT,CAAC;AAEF,iBAAS,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,iBAAe,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB7D;AAED,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsInC"}
|
package/dist/index.js
CHANGED
|
@@ -45,9 +45,9 @@ Examples:
|
|
|
45
45
|
dd run
|
|
46
46
|
dd dev
|
|
47
47
|
dd db status
|
|
48
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --yes
|
|
49
|
-
npx @doubledigit/cli@latest actions remotion-hub init my-video --skip-skills
|
|
50
|
-
npx @doubledigit/cli@latest actions remotion-hub search-components --query "animated chart"
|
|
48
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --yes
|
|
49
|
+
npx @doubledigit/cli@latest actions remotion-hub init my-video --framework remotion --skip-skills
|
|
50
|
+
npx @doubledigit/cli@latest actions remotion-hub search-components --framework remotion --query "animated chart"
|
|
51
51
|
dd create invoice-tracker
|
|
52
52
|
dd add gh:owner/repo/extensions/micro-apps/habit-tracker
|
|
53
53
|
dd add habit-tracker@community
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions-client.d.ts","sourceRoot":"","sources":["../../src/lib/actions-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,MAAM,WAAW,iCAAiC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,EAAE,iCAAiC,UAKnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,GAAiB,EACjB,OAAY,EACZ,UAAU,GACX,EAAE,0BAA0B,UAS5B;AA4ED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"actions-client.d.ts","sourceRoot":"","sources":["../../src/lib/actions-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,MAAM,WAAW,iCAAiC;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,EAAE,iCAAiC,UAKnC;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,GAAiB,EACjB,OAAY,EACZ,UAAU,GACX,EAAE,0BAA0B,UAS5B;AA4ED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,iBAAiB,CA+DlE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,UAS7C;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,WAK3C;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAoBtI;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCjF"}
|
|
@@ -68,7 +68,7 @@ function setInputValue(input, key, value) {
|
|
|
68
68
|
input.limit = numeric;
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
|
-
if (key === 'namespace' || key === 'kind' || key === 'previewType') {
|
|
71
|
+
if (key === 'namespace' || key === 'kind' || key === 'previewType' || key === 'framework') {
|
|
72
72
|
input[key] = value;
|
|
73
73
|
asFilters(input)[key] = value;
|
|
74
74
|
return;
|
|
@@ -127,6 +127,9 @@ export function parseActionsArgs(args) {
|
|
|
127
127
|
else if (flag === '--namespace') {
|
|
128
128
|
setInputValue(input, 'namespace', readValue());
|
|
129
129
|
}
|
|
130
|
+
else if (flag === '--framework') {
|
|
131
|
+
setInputValue(input, 'framework', readValue());
|
|
132
|
+
}
|
|
130
133
|
else if (flag === '--slug') {
|
|
131
134
|
setInputValue(input, 'slug', readValue());
|
|
132
135
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ComponentFrameworkId = 'remotion' | 'hyperframe';
|
|
2
|
+
export interface ComponentFrameworkAdapter {
|
|
3
|
+
id: ComponentFrameworkId;
|
|
4
|
+
aliases: string[];
|
|
5
|
+
displayName: string;
|
|
6
|
+
minNodeMajor?: number;
|
|
7
|
+
defaultDevCommand: string;
|
|
8
|
+
defaultFileExtension: string;
|
|
9
|
+
buildCreateCommand: (projectDir: string, options: {
|
|
10
|
+
nonInteractive: boolean;
|
|
11
|
+
}) => {
|
|
12
|
+
command: string;
|
|
13
|
+
args: string[];
|
|
14
|
+
};
|
|
15
|
+
skillsCommands: (options: {
|
|
16
|
+
nonInteractive: boolean;
|
|
17
|
+
}) => Array<{
|
|
18
|
+
command: 'npx';
|
|
19
|
+
args: string[];
|
|
20
|
+
display: string;
|
|
21
|
+
}>;
|
|
22
|
+
buildPostAddSnippet: (input: {
|
|
23
|
+
name: string;
|
|
24
|
+
primaryFile: string;
|
|
25
|
+
source: string;
|
|
26
|
+
projectDir: string;
|
|
27
|
+
config: unknown;
|
|
28
|
+
}) => string;
|
|
29
|
+
}
|
|
30
|
+
export declare const componentFrameworkAdapters: ComponentFrameworkAdapter[];
|
|
31
|
+
export declare function normalizeFrameworkId(value?: string): ComponentFrameworkId;
|
|
32
|
+
export declare function getComponentFrameworkAdapter(value?: string): ComponentFrameworkAdapter;
|
|
33
|
+
export declare function assertFrameworkNodeRequirement(adapter: ComponentFrameworkAdapter, nodeVersion?: string): void;
|
|
34
|
+
//# sourceMappingURL=component-frameworks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-frameworks.d.ts","sourceRoot":"","sources":["../../src/lib/component-frameworks.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,YAAY,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,oBAAoB,CAAC;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,cAAc,EAAE,OAAO,CAAA;KAAE,KAAK;QAChF,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,cAAc,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,OAAO,CAAA;KAAE,KAAK,KAAK,CAAC;QAC9D,OAAO,EAAE,KAAK,CAAC;QACf,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,mBAAmB,EAAE,CAAC,KAAK,EAAE;QAC3B,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;KACjB,KAAK,MAAM,CAAC;CACd;AAsDD,eAAO,MAAM,0BAA0B,EAAE,yBAAyB,EAuDjE,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,oBAAoB,CASzE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAKtF;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,yBAAyB,EAAE,WAAW,SAAwB,QAKrH"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
function addNonInteractiveAgentFlags(command, nonInteractive) {
|
|
3
|
+
if (!nonInteractive || !command.args.includes('--skill'))
|
|
4
|
+
return command;
|
|
5
|
+
return {
|
|
6
|
+
...command,
|
|
7
|
+
args: [...command.args, '--agent', '*', '--yes'],
|
|
8
|
+
display: `${command.display} --agent '*' --yes`,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function normalizeSlashes(value) {
|
|
12
|
+
return value.split(path.sep).join('/');
|
|
13
|
+
}
|
|
14
|
+
function exportNameFromSource(source, fallback) {
|
|
15
|
+
const match = source.match(/export\s+(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/);
|
|
16
|
+
if (match?.[1])
|
|
17
|
+
return match[1];
|
|
18
|
+
return fallback
|
|
19
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
22
|
+
.join('') || 'ComponentHubComponent';
|
|
23
|
+
}
|
|
24
|
+
function importPath(fromFile, toFile) {
|
|
25
|
+
const withoutExtension = toFile.replace(/\.[cm]?[tj]sx?$/, '');
|
|
26
|
+
let relative = normalizeSlashes(path.relative(path.dirname(fromFile), withoutExtension));
|
|
27
|
+
if (!relative.startsWith('.'))
|
|
28
|
+
relative = `./${relative}`;
|
|
29
|
+
return relative;
|
|
30
|
+
}
|
|
31
|
+
function buildRemotionSnippet(input) {
|
|
32
|
+
const config = input.config;
|
|
33
|
+
const root = config.remotion?.root || 'src/Root.tsx';
|
|
34
|
+
const rootFile = path.resolve(input.projectDir, root);
|
|
35
|
+
const exportName = exportNameFromSource(input.source, input.name);
|
|
36
|
+
const importFrom = importPath(rootFile, input.primaryFile);
|
|
37
|
+
return [
|
|
38
|
+
`import { ${exportName} } from '${importFrom}';`,
|
|
39
|
+
'',
|
|
40
|
+
`<Composition id="${input.name}" component={${exportName}} durationInFrames={180} fps={30} width={1920} height={1080} />`,
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
|
43
|
+
export const componentFrameworkAdapters = [
|
|
44
|
+
{
|
|
45
|
+
id: 'remotion',
|
|
46
|
+
aliases: ['remotion', 'remotionjs'],
|
|
47
|
+
displayName: 'Remotion',
|
|
48
|
+
defaultDevCommand: 'npx remotion studio',
|
|
49
|
+
defaultFileExtension: 'tsx',
|
|
50
|
+
buildCreateCommand: (projectDir) => ({
|
|
51
|
+
command: 'npx',
|
|
52
|
+
args: ['create-video@latest', '--yes', '--hello-world', projectDir],
|
|
53
|
+
}),
|
|
54
|
+
skillsCommands: ({ nonInteractive }) => [
|
|
55
|
+
{
|
|
56
|
+
command: 'npx',
|
|
57
|
+
args: ['skills', 'add', 'remotion-dev/skills', '--all'],
|
|
58
|
+
display: 'npx skills add remotion-dev/skills --all',
|
|
59
|
+
},
|
|
60
|
+
addNonInteractiveAgentFlags({
|
|
61
|
+
command: 'npx',
|
|
62
|
+
args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
|
|
63
|
+
display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
|
|
64
|
+
}, nonInteractive),
|
|
65
|
+
],
|
|
66
|
+
buildPostAddSnippet: buildRemotionSnippet,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'hyperframe',
|
|
70
|
+
aliases: ['hyperframe', 'hyperframes'],
|
|
71
|
+
displayName: 'HyperFrames',
|
|
72
|
+
minNodeMajor: 22,
|
|
73
|
+
defaultDevCommand: 'npx hyperframes preview',
|
|
74
|
+
defaultFileExtension: 'html',
|
|
75
|
+
buildCreateCommand: (projectDir, { nonInteractive }) => ({
|
|
76
|
+
command: 'npx',
|
|
77
|
+
args: [
|
|
78
|
+
'hyperframes',
|
|
79
|
+
'init',
|
|
80
|
+
projectDir,
|
|
81
|
+
...(nonInteractive ? ['--non-interactive'] : []),
|
|
82
|
+
],
|
|
83
|
+
}),
|
|
84
|
+
skillsCommands: ({ nonInteractive }) => [
|
|
85
|
+
addNonInteractiveAgentFlags({
|
|
86
|
+
command: 'npx',
|
|
87
|
+
args: ['skills', 'add', 'heygen-com/hyperframes'],
|
|
88
|
+
display: 'npx skills add heygen-com/hyperframes',
|
|
89
|
+
}, nonInteractive),
|
|
90
|
+
addNonInteractiveAgentFlags({
|
|
91
|
+
command: 'npx',
|
|
92
|
+
args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
|
|
93
|
+
display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
|
|
94
|
+
}, nonInteractive),
|
|
95
|
+
],
|
|
96
|
+
buildPostAddSnippet: () => '',
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
export function normalizeFrameworkId(value) {
|
|
100
|
+
const normalized = (value || 'remotion').trim().toLowerCase();
|
|
101
|
+
const adapter = componentFrameworkAdapters.find((candidate) => candidate.id === normalized || candidate.aliases.includes(normalized));
|
|
102
|
+
if (!adapter) {
|
|
103
|
+
throw new Error(`Unsupported framework: ${value}. Supported frameworks: remotion, hyperframe`);
|
|
104
|
+
}
|
|
105
|
+
return adapter.id;
|
|
106
|
+
}
|
|
107
|
+
export function getComponentFrameworkAdapter(value) {
|
|
108
|
+
const id = normalizeFrameworkId(value);
|
|
109
|
+
const adapter = componentFrameworkAdapters.find((candidate) => candidate.id === id);
|
|
110
|
+
if (!adapter)
|
|
111
|
+
throw new Error(`Unsupported framework: ${value}`);
|
|
112
|
+
return adapter;
|
|
113
|
+
}
|
|
114
|
+
export function assertFrameworkNodeRequirement(adapter, nodeVersion = process.versions.node) {
|
|
115
|
+
if (!adapter.minNodeMajor)
|
|
116
|
+
return;
|
|
117
|
+
const major = Number(nodeVersion.split('.')[0]);
|
|
118
|
+
if (Number.isFinite(major) && major >= adapter.minNodeMajor)
|
|
119
|
+
return;
|
|
120
|
+
throw new Error(`${adapter.displayName} requires Node.js ${adapter.minNodeMajor}+`);
|
|
121
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
type
|
|
2
|
+
import { type ComponentFrameworkId } from './component-frameworks.js';
|
|
3
|
+
type ComponentHubSkillsCommand = {
|
|
3
4
|
command: 'npx';
|
|
4
5
|
args: string[];
|
|
5
6
|
display: string;
|
|
6
7
|
};
|
|
7
|
-
type
|
|
8
|
+
type ComponentHubSkillsInstallResult = ComponentHubSkillsCommand & {
|
|
8
9
|
skipped: boolean;
|
|
9
10
|
success: boolean;
|
|
10
11
|
status: number | null;
|
|
@@ -14,6 +15,13 @@ export interface RemotionHubConfig {
|
|
|
14
15
|
$schema: string;
|
|
15
16
|
registry: string;
|
|
16
17
|
namespace: string;
|
|
18
|
+
defaultFramework?: ComponentFrameworkId;
|
|
19
|
+
frameworks?: Partial<Record<ComponentFrameworkId, {
|
|
20
|
+
devCommand: string;
|
|
21
|
+
entrypoint?: string;
|
|
22
|
+
root?: string;
|
|
23
|
+
publicDir: string;
|
|
24
|
+
}>>;
|
|
17
25
|
paths: {
|
|
18
26
|
scenes: string;
|
|
19
27
|
components: string;
|
|
@@ -43,6 +51,7 @@ export interface RemotionHubLock {
|
|
|
43
51
|
files: string[];
|
|
44
52
|
dependencies: string[];
|
|
45
53
|
installedAt: string;
|
|
54
|
+
framework: string;
|
|
46
55
|
}>;
|
|
47
56
|
}
|
|
48
57
|
export interface RunRemotionHubInitOptions {
|
|
@@ -51,11 +60,14 @@ export interface RunRemotionHubInitOptions {
|
|
|
51
60
|
namespace?: string;
|
|
52
61
|
force?: boolean;
|
|
53
62
|
skipRemotionCreate?: boolean;
|
|
63
|
+
skipFrameworkCreate?: boolean;
|
|
54
64
|
skipSkills?: boolean;
|
|
55
65
|
nonInteractive?: boolean;
|
|
56
66
|
yes?: boolean;
|
|
57
67
|
quiet?: boolean;
|
|
58
68
|
spawnImpl?: typeof spawnSync;
|
|
69
|
+
framework?: string;
|
|
70
|
+
nodeVersion?: string;
|
|
59
71
|
}
|
|
60
72
|
export interface RunRemotionHubInitResult {
|
|
61
73
|
projectDir: string;
|
|
@@ -63,6 +75,13 @@ export interface RunRemotionHubInitResult {
|
|
|
63
75
|
lockPath: string;
|
|
64
76
|
created: string[];
|
|
65
77
|
config: RemotionHubConfig;
|
|
78
|
+
framework: ComponentFrameworkId;
|
|
79
|
+
devCommand: string;
|
|
80
|
+
create: {
|
|
81
|
+
skipped: boolean;
|
|
82
|
+
command: string;
|
|
83
|
+
args: string[];
|
|
84
|
+
};
|
|
66
85
|
remotionCreate: {
|
|
67
86
|
skipped: boolean;
|
|
68
87
|
command: string;
|
|
@@ -70,7 +89,7 @@ export interface RunRemotionHubInitResult {
|
|
|
70
89
|
};
|
|
71
90
|
skills: {
|
|
72
91
|
skipped: boolean;
|
|
73
|
-
commands:
|
|
92
|
+
commands: ComponentHubSkillsInstallResult[];
|
|
74
93
|
};
|
|
75
94
|
}
|
|
76
95
|
export interface RunRemotionHubAddOptions {
|
|
@@ -83,6 +102,7 @@ export interface RunRemotionHubAddOptions {
|
|
|
83
102
|
quiet?: boolean;
|
|
84
103
|
fetchImpl?: typeof fetch;
|
|
85
104
|
spawnImpl?: typeof spawnSync;
|
|
105
|
+
framework?: string;
|
|
86
106
|
}
|
|
87
107
|
export interface RunRemotionHubAddResult {
|
|
88
108
|
name: string;
|
|
@@ -94,6 +114,7 @@ export interface RunRemotionHubAddResult {
|
|
|
94
114
|
dependencies: string[];
|
|
95
115
|
installedDependencies: string[];
|
|
96
116
|
snippet: string;
|
|
117
|
+
framework: ComponentFrameworkId;
|
|
97
118
|
}
|
|
98
119
|
export interface ResolveRemotionHubRegistryOptions {
|
|
99
120
|
registry?: string;
|
|
@@ -103,9 +124,9 @@ export interface ResolveRemotionHubRegistryOptions {
|
|
|
103
124
|
}
|
|
104
125
|
export declare function resolveRemotionHubRegistry({ registry, env, generatedDefaultRegistry, localDefaultRegistry, }?: ResolveRemotionHubRegistryOptions): string;
|
|
105
126
|
export declare function buildRemotionSkillsCommands(options?: Pick<RunRemotionHubInitOptions, 'nonInteractive' | 'yes'>): {
|
|
127
|
+
command: "npx";
|
|
106
128
|
args: string[];
|
|
107
129
|
display: string;
|
|
108
|
-
command: "npx";
|
|
109
130
|
}[];
|
|
110
131
|
export declare function runRemotionHubInit(options?: RunRemotionHubInitOptions): RunRemotionHubInitResult;
|
|
111
132
|
export declare function runRemotionHubAdd(options: RunRemotionHubAddOptions): Promise<RunRemotionHubAddResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/lib/remotion-hub-init.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"remotion-hub-init.d.ts","sourceRoot":"","sources":["../../src/lib/remotion-hub-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,13 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
1
|
+
import { spawnSync, } from 'node:child_process';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { GENERATED_DEFAULT_ACTIONS_APP_URL } from '../generated/defaults.js';
|
|
5
5
|
import { isLocalAppUrl } from './actions-client.js';
|
|
6
|
+
import { assertFrameworkNodeRequirement, getComponentFrameworkAdapter, normalizeFrameworkId, } from './component-frameworks.js';
|
|
6
7
|
const CONFIG_FILE = 'remotion-hub.json';
|
|
7
8
|
const LOCK_FILE = 'remotion-hub.lock.json';
|
|
8
9
|
const DEFAULT_REGISTRY = 'http://localhost:3111';
|
|
9
10
|
const DEFAULT_NAMESPACE = 'doubledigit';
|
|
10
|
-
const REMOTION_SKILLS_COMMANDS = [
|
|
11
|
-
{
|
|
12
|
-
command: 'npx',
|
|
13
|
-
args: ['skills', 'add', 'remotion-dev/skills', '--all'],
|
|
14
|
-
display: 'npx skills add remotion-dev/skills --all',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
command: 'npx',
|
|
18
|
-
args: ['skills', 'add', 'crystalphantom/double-digit', '--skill', 'dd-remotion-hub'],
|
|
19
|
-
display: 'npx skills add crystalphantom/double-digit --skill dd-remotion-hub',
|
|
20
|
-
},
|
|
21
|
-
];
|
|
22
11
|
function toJson(value) {
|
|
23
12
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
24
13
|
}
|
|
@@ -107,19 +96,35 @@ export function resolveRemotionHubRegistry({ registry, env = process.env, genera
|
|
|
107
96
|
?? localDefaultRegistry;
|
|
108
97
|
}
|
|
109
98
|
function defaultConfig(projectDir, options) {
|
|
99
|
+
const framework = normalizeFrameworkId(options.framework);
|
|
110
100
|
const language = detectLanguage(projectDir);
|
|
111
101
|
const rootExtension = language === 'typescript' ? 'tsx' : 'jsx';
|
|
112
102
|
const entryExtension = language === 'typescript' ? 'ts' : 'js';
|
|
103
|
+
const remotionAdapter = getComponentFrameworkAdapter('remotion');
|
|
104
|
+
const hyperframeAdapter = getComponentFrameworkAdapter('hyperframe');
|
|
113
105
|
return {
|
|
114
106
|
$schema: 'https://remotion-hub.dev/schema/remotion-hub.json',
|
|
115
107
|
registry: resolveRemotionHubRegistry({ registry: options.registry }),
|
|
116
108
|
namespace: options.namespace?.trim() || DEFAULT_NAMESPACE,
|
|
109
|
+
defaultFramework: framework,
|
|
110
|
+
frameworks: {
|
|
111
|
+
remotion: {
|
|
112
|
+
devCommand: remotionAdapter.defaultDevCommand,
|
|
113
|
+
entrypoint: firstExisting(projectDir, ['src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx'], `src/index.${entryExtension}`),
|
|
114
|
+
root: firstExisting(projectDir, ['src/Root.tsx', 'src/Root.jsx', 'src/root.tsx', 'src/root.jsx'], `src/Root.${rootExtension}`),
|
|
115
|
+
publicDir: 'public',
|
|
116
|
+
},
|
|
117
|
+
hyperframe: {
|
|
118
|
+
devCommand: hyperframeAdapter.defaultDevCommand,
|
|
119
|
+
publicDir: 'public',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
117
122
|
paths: {
|
|
118
|
-
scenes: 'src/remotion-hub/scenes',
|
|
119
|
-
components: 'src/remotion-hub/components',
|
|
120
|
-
overlays: 'src/remotion-hub/overlays',
|
|
121
|
-
transitions: 'src/remotion-hub/transitions',
|
|
122
|
-
assets: 'public/remotion-hub/assets',
|
|
123
|
+
scenes: 'src/remotion-hub/[framework]/scenes',
|
|
124
|
+
components: 'src/remotion-hub/[framework]/components',
|
|
125
|
+
overlays: 'src/remotion-hub/[framework]/overlays',
|
|
126
|
+
transitions: 'src/remotion-hub/[framework]/transitions',
|
|
127
|
+
assets: 'public/remotion-hub/[framework]/assets',
|
|
123
128
|
registry: `src/remotion-hub/registry.${entryExtension}`,
|
|
124
129
|
},
|
|
125
130
|
remotion: {
|
|
@@ -133,6 +138,18 @@ function defaultConfig(projectDir, options) {
|
|
|
133
138
|
},
|
|
134
139
|
};
|
|
135
140
|
}
|
|
141
|
+
function getFrameworkConfig(config, framework) {
|
|
142
|
+
const adapter = getComponentFrameworkAdapter(framework);
|
|
143
|
+
return {
|
|
144
|
+
devCommand: config.frameworks?.[framework]?.devCommand ?? adapter.defaultDevCommand,
|
|
145
|
+
publicDir: config.frameworks?.[framework]?.publicDir ?? config.remotion?.publicDir ?? 'public',
|
|
146
|
+
entrypoint: config.frameworks?.[framework]?.entrypoint,
|
|
147
|
+
root: config.frameworks?.[framework]?.root,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function getConfigDefaultFramework(config) {
|
|
151
|
+
return normalizeFrameworkId(config.defaultFramework);
|
|
152
|
+
}
|
|
136
153
|
function defaultLock() {
|
|
137
154
|
return {
|
|
138
155
|
version: 1,
|
|
@@ -140,6 +157,11 @@ function defaultLock() {
|
|
|
140
157
|
};
|
|
141
158
|
}
|
|
142
159
|
function ensureDir(projectDir, relativePath, created) {
|
|
160
|
+
if (relativePath.includes('[framework]')) {
|
|
161
|
+
ensureDir(projectDir, relativePath.replace('[framework]', 'remotion'), created);
|
|
162
|
+
ensureDir(projectDir, relativePath.replace('[framework]', 'hyperframe'), created);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
143
165
|
const target = resolveInsideProject(projectDir, relativePath, relativePath);
|
|
144
166
|
if (!existsSync(target)) {
|
|
145
167
|
mkdirSync(target, { recursive: true });
|
|
@@ -155,6 +177,7 @@ function registrySource(config, lock) {
|
|
|
155
177
|
namespace: entry.namespace,
|
|
156
178
|
slug: entry.slug,
|
|
157
179
|
files: entry.files,
|
|
180
|
+
framework: entry.framework ?? 'remotion',
|
|
158
181
|
};
|
|
159
182
|
return acc;
|
|
160
183
|
}, {});
|
|
@@ -180,29 +203,50 @@ function hasPackageJson(projectDir) {
|
|
|
180
203
|
function hasEntries(projectDir) {
|
|
181
204
|
return existsSync(projectDir) && readdirSync(projectDir).length > 0;
|
|
182
205
|
}
|
|
183
|
-
function
|
|
206
|
+
function withNonInteractiveNpx(command, nonInteractive) {
|
|
207
|
+
if (!nonInteractive || command.command !== 'npx')
|
|
208
|
+
return command;
|
|
209
|
+
const args = command.args[0] === '--yes'
|
|
210
|
+
? command.args
|
|
211
|
+
: ['--yes', ...command.args];
|
|
212
|
+
const display = command.display
|
|
213
|
+
? command.display.startsWith('npx --yes ')
|
|
214
|
+
? command.display
|
|
215
|
+
: command.display.startsWith('npx ')
|
|
216
|
+
? `npx --yes ${command.display.slice('npx '.length)}`
|
|
217
|
+
: `npx --yes ${args.slice(1).join(' ')}`
|
|
218
|
+
: command.display;
|
|
184
219
|
return {
|
|
185
|
-
command
|
|
186
|
-
args
|
|
220
|
+
...command,
|
|
221
|
+
args,
|
|
222
|
+
...(display ? { display } : {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function componentHubChildSpawnOptions(command, options) {
|
|
226
|
+
return {
|
|
227
|
+
cwd: options.cwd,
|
|
228
|
+
encoding: 'buffer',
|
|
229
|
+
stdio: options.quiet ? 'pipe' : 'inherit',
|
|
230
|
+
...(options.nonInteractive && command === 'npx'
|
|
231
|
+
? { env: { ...process.env, npm_config_yes: 'true' } }
|
|
232
|
+
: {}),
|
|
187
233
|
};
|
|
188
234
|
}
|
|
235
|
+
function spawnComponentHubChild(command, options) {
|
|
236
|
+
return (options.spawnImpl ?? spawnSync)(command.command, command.args, componentHubChildSpawnOptions(command.command, options));
|
|
237
|
+
}
|
|
189
238
|
export function buildRemotionSkillsCommands(options = {}) {
|
|
190
239
|
const nonInteractive = options.yes === true || options.nonInteractive === true;
|
|
191
|
-
return
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
function runRemotionSkillsInstall(projectDir, options) {
|
|
202
|
-
const commands = buildRemotionSkillsCommands({
|
|
203
|
-
nonInteractive: options.nonInteractive,
|
|
204
|
-
yes: options.yes,
|
|
205
|
-
});
|
|
240
|
+
return getComponentFrameworkAdapter('remotion')
|
|
241
|
+
.skillsCommands({ nonInteractive })
|
|
242
|
+
.map((command) => withNonInteractiveNpx(command, nonInteractive));
|
|
243
|
+
}
|
|
244
|
+
function runFrameworkSkillsInstall(projectDir, options) {
|
|
245
|
+
const adapter = getComponentFrameworkAdapter(options.framework);
|
|
246
|
+
const nonInteractive = options.yes === true || options.nonInteractive === true;
|
|
247
|
+
const commands = adapter
|
|
248
|
+
.skillsCommands({ nonInteractive })
|
|
249
|
+
.map((command) => withNonInteractiveNpx(command, nonInteractive));
|
|
206
250
|
if (options.skipSkills) {
|
|
207
251
|
return commands.map((command) => ({
|
|
208
252
|
...command,
|
|
@@ -212,9 +256,11 @@ function runRemotionSkillsInstall(projectDir, options) {
|
|
|
212
256
|
}));
|
|
213
257
|
}
|
|
214
258
|
return commands.map((command) => {
|
|
215
|
-
const result = (
|
|
259
|
+
const result = spawnComponentHubChild(command, {
|
|
216
260
|
cwd: projectDir,
|
|
217
|
-
|
|
261
|
+
nonInteractive,
|
|
262
|
+
quiet: options.quiet,
|
|
263
|
+
spawnImpl: options.spawnImpl,
|
|
218
264
|
});
|
|
219
265
|
return {
|
|
220
266
|
...command,
|
|
@@ -225,33 +271,41 @@ function runRemotionSkillsInstall(projectDir, options) {
|
|
|
225
271
|
};
|
|
226
272
|
});
|
|
227
273
|
}
|
|
228
|
-
function
|
|
274
|
+
function assertFrameworkCreateSucceeded(result, adapterName, command, args) {
|
|
229
275
|
if (result.error) {
|
|
230
|
-
throw new Error(
|
|
276
|
+
throw new Error(`${adapterName} project creation failed: ${result.error.message}`);
|
|
231
277
|
}
|
|
232
278
|
if (result.status !== 0) {
|
|
233
|
-
throw new Error(
|
|
279
|
+
throw new Error(`${adapterName} project creation failed: ${command} ${args.join(' ')}`);
|
|
234
280
|
}
|
|
235
281
|
}
|
|
236
|
-
function
|
|
237
|
-
|
|
238
|
-
|
|
282
|
+
function shouldSkipFrameworkCreate(options) {
|
|
283
|
+
return options.skipFrameworkCreate === true || options.skipRemotionCreate === true;
|
|
284
|
+
}
|
|
285
|
+
function ensureBaseFrameworkProject(projectDir, options) {
|
|
286
|
+
const adapter = getComponentFrameworkAdapter(options.framework);
|
|
287
|
+
const nonInteractive = options.yes === true || options.nonInteractive === true;
|
|
288
|
+
const frameworkCreate = withNonInteractiveNpx(adapter.buildCreateCommand(projectDir, { nonInteractive }), nonInteractive);
|
|
289
|
+
if (shouldSkipFrameworkCreate(options) || hasPackageJson(projectDir)) {
|
|
239
290
|
return {
|
|
240
|
-
...
|
|
291
|
+
...frameworkCreate,
|
|
241
292
|
skipped: true,
|
|
242
293
|
};
|
|
243
294
|
}
|
|
295
|
+
assertFrameworkNodeRequirement(adapter, options.nodeVersion);
|
|
244
296
|
if (hasEntries(projectDir)) {
|
|
245
|
-
throw new Error(`Project directory exists but is not a
|
|
246
|
-
+
|
|
297
|
+
throw new Error(`Project directory exists but is not a ${adapter.displayName} project: ${projectDir}. `
|
|
298
|
+
+ `Remove it, choose an empty path, or run ${adapter.displayName} setup manually before Component Hub init.`);
|
|
247
299
|
}
|
|
248
|
-
const result = (
|
|
300
|
+
const result = spawnComponentHubChild(frameworkCreate, {
|
|
249
301
|
cwd: process.cwd(),
|
|
250
|
-
|
|
302
|
+
nonInteractive,
|
|
303
|
+
quiet: options.quiet,
|
|
304
|
+
spawnImpl: options.spawnImpl,
|
|
251
305
|
});
|
|
252
|
-
|
|
306
|
+
assertFrameworkCreateSucceeded(result, adapter.displayName, frameworkCreate.command, frameworkCreate.args);
|
|
253
307
|
return {
|
|
254
|
-
...
|
|
308
|
+
...frameworkCreate,
|
|
255
309
|
skipped: false,
|
|
256
310
|
};
|
|
257
311
|
}
|
|
@@ -281,25 +335,35 @@ function parseItemRef(item, namespace) {
|
|
|
281
335
|
}
|
|
282
336
|
return { namespace: namespace || DEFAULT_NAMESPACE, slug: trimmed };
|
|
283
337
|
}
|
|
284
|
-
function registryPayloadUrl(config, namespace, slug, registryOverride) {
|
|
338
|
+
function registryPayloadUrl(config, namespace, slug, registryOverride, framework) {
|
|
285
339
|
const base = normalizeRegistryBase(registryOverride || config.registry || DEFAULT_REGISTRY);
|
|
286
340
|
const encodedNamespace = encodeURIComponent(namespace);
|
|
287
341
|
const encodedSlug = encodeURIComponent(slug);
|
|
342
|
+
let url = '';
|
|
288
343
|
if (base.endsWith('/api/remotion-hub/r')) {
|
|
289
|
-
|
|
344
|
+
url = `${base}/${encodedNamespace}/${encodedSlug}`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
url = `${base}/api/remotion-hub/r/${encodedNamespace}/${encodedSlug}`;
|
|
348
|
+
}
|
|
349
|
+
if (framework) {
|
|
350
|
+
url += `?framework=${encodeURIComponent(framework)}`;
|
|
290
351
|
}
|
|
291
|
-
return
|
|
352
|
+
return url;
|
|
292
353
|
}
|
|
293
354
|
function asStringArray(value) {
|
|
294
355
|
if (!Array.isArray(value))
|
|
295
356
|
return [];
|
|
296
357
|
return value.filter((item) => typeof item === 'string' && item.trim().length > 0);
|
|
297
358
|
}
|
|
298
|
-
function readRegistryPayload(value) {
|
|
359
|
+
function readRegistryPayload(value, fallbackFramework) {
|
|
299
360
|
const name = typeof value.name === 'string' && value.name.trim() ? value.name.trim() : '';
|
|
300
361
|
const type = typeof value.type === 'string' && value.type.trim() ? value.type.trim() : 'registry:component';
|
|
301
362
|
const version = typeof value.version === 'string' && value.version.trim() ? value.version.trim() : null;
|
|
302
363
|
const files = Array.isArray(value.files) ? value.files : [];
|
|
364
|
+
const framework = typeof value.framework === 'string' && value.framework.trim()
|
|
365
|
+
? normalizeFrameworkId(value.framework)
|
|
366
|
+
: fallbackFramework;
|
|
303
367
|
if (!name)
|
|
304
368
|
throw new Error('Registry payload is missing name');
|
|
305
369
|
if (files.length === 0)
|
|
@@ -310,9 +374,10 @@ function readRegistryPayload(value) {
|
|
|
310
374
|
version,
|
|
311
375
|
files,
|
|
312
376
|
dependencies: asStringArray(value.dependencies),
|
|
377
|
+
framework,
|
|
313
378
|
};
|
|
314
379
|
}
|
|
315
|
-
async function fetchRegistryPayload(url, fetchImpl) {
|
|
380
|
+
async function fetchRegistryPayload(url, fetchImpl, fallbackFramework) {
|
|
316
381
|
const response = await fetchImpl(url);
|
|
317
382
|
const text = await response.text();
|
|
318
383
|
const parsed = text ? JSON.parse(text) : {};
|
|
@@ -322,14 +387,19 @@ async function fetchRegistryPayload(url, fetchImpl) {
|
|
|
322
387
|
: `Request failed with HTTP ${response.status}`;
|
|
323
388
|
throw new Error(error);
|
|
324
389
|
}
|
|
325
|
-
return readRegistryPayload(parsed);
|
|
390
|
+
return readRegistryPayload(parsed, fallbackFramework);
|
|
326
391
|
}
|
|
327
|
-
function targetForRegistryFile(config, file) {
|
|
392
|
+
function targetForRegistryFile(config, file, framework) {
|
|
328
393
|
const explicitTarget = typeof file.target === 'string' ? file.target.trim() : '';
|
|
329
394
|
if (explicitTarget)
|
|
330
|
-
return explicitTarget;
|
|
395
|
+
return explicitTarget.replace(/\[framework\]/g, framework);
|
|
331
396
|
const sourcePath = ensureRelativePath(typeof file.path === 'string' ? file.path : '', 'file.path');
|
|
332
397
|
const mappings = [
|
|
398
|
+
[`components/${framework}/`, config.paths.components],
|
|
399
|
+
[`scenes/${framework}/`, config.paths.scenes],
|
|
400
|
+
[`overlays/${framework}/`, config.paths.overlays],
|
|
401
|
+
[`transitions/${framework}/`, config.paths.transitions],
|
|
402
|
+
[`assets/${framework}/`, config.paths.assets],
|
|
333
403
|
['components/', config.paths.components],
|
|
334
404
|
['scenes/', config.paths.scenes],
|
|
335
405
|
['overlays/', config.paths.overlays],
|
|
@@ -338,10 +408,11 @@ function targetForRegistryFile(config, file) {
|
|
|
338
408
|
];
|
|
339
409
|
for (const [prefix, targetRoot] of mappings) {
|
|
340
410
|
if (sourcePath.startsWith(prefix)) {
|
|
341
|
-
|
|
411
|
+
const rootPath = targetRoot.replace(/\[framework\]/g, framework);
|
|
412
|
+
return path.posix.join(rootPath.replace(/\/+$/, ''), sourcePath.slice(prefix.length));
|
|
342
413
|
}
|
|
343
414
|
}
|
|
344
|
-
return path.posix.join('src/remotion-hub', sourcePath);
|
|
415
|
+
return path.posix.join('src/remotion-hub', sourcePath).replace(/\[framework\]/g, framework);
|
|
345
416
|
}
|
|
346
417
|
function packageJsonDependencies(projectDir) {
|
|
347
418
|
const packageJsonPath = path.join(projectDir, 'package.json');
|
|
@@ -381,45 +452,21 @@ function installDependencies(projectDir, packageManager, dependencies, spawnImpl
|
|
|
381
452
|
}
|
|
382
453
|
return missing;
|
|
383
454
|
}
|
|
384
|
-
function exportNameFromSource(source, fallback) {
|
|
385
|
-
const match = source.match(/export\s+(?:function|const|class)\s+([A-Z][A-Za-z0-9_]*)/);
|
|
386
|
-
if (match?.[1])
|
|
387
|
-
return match[1];
|
|
388
|
-
return fallback
|
|
389
|
-
.split(/[^a-zA-Z0-9]+/)
|
|
390
|
-
.filter(Boolean)
|
|
391
|
-
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
392
|
-
.join('') || 'RemotionHubComponent';
|
|
393
|
-
}
|
|
394
|
-
function importPath(fromFile, toFile) {
|
|
395
|
-
const withoutExtension = toFile.replace(/\.[cm]?[tj]sx?$/, '');
|
|
396
|
-
let relative = normalizeSlashes(path.relative(path.dirname(fromFile), withoutExtension));
|
|
397
|
-
if (!relative.startsWith('.'))
|
|
398
|
-
relative = `./${relative}`;
|
|
399
|
-
return relative;
|
|
400
|
-
}
|
|
401
|
-
function buildSnippet(config, projectDir, name, primaryFile, source) {
|
|
402
|
-
const rootFile = resolveInsideProject(projectDir, config.remotion.root, 'remotion.root');
|
|
403
|
-
const exportName = exportNameFromSource(source, name);
|
|
404
|
-
const importFrom = importPath(rootFile, primaryFile);
|
|
405
|
-
return [
|
|
406
|
-
`import { ${exportName} } from '${importFrom}';`,
|
|
407
|
-
'',
|
|
408
|
-
`<Composition id="${name}" component={${exportName}} durationInFrames={180} fps={30} width={1920} height={1080} />`,
|
|
409
|
-
].join('\n');
|
|
410
|
-
}
|
|
411
455
|
export function runRemotionHubInit(options = {}) {
|
|
456
|
+
const framework = normalizeFrameworkId(options.framework);
|
|
457
|
+
const adapter = getComponentFrameworkAdapter(framework);
|
|
458
|
+
const normalizedOptions = { ...options, framework };
|
|
412
459
|
const projectDir = resolveProjectDir(options.projectDir);
|
|
413
460
|
const configPath = path.join(projectDir, CONFIG_FILE);
|
|
414
461
|
const lockPath = path.join(projectDir, LOCK_FILE);
|
|
415
462
|
const created = [];
|
|
416
|
-
const
|
|
463
|
+
const create = ensureBaseFrameworkProject(projectDir, normalizedOptions);
|
|
417
464
|
mkdirSync(projectDir, { recursive: true });
|
|
418
465
|
const configExists = existsSync(configPath);
|
|
419
466
|
const lockExists = existsSync(lockPath);
|
|
420
467
|
const config = configExists && !options.force
|
|
421
468
|
? loadConfig(projectDir)
|
|
422
|
-
: defaultConfig(projectDir,
|
|
469
|
+
: defaultConfig(projectDir, normalizedOptions);
|
|
423
470
|
if (!configExists || options.force) {
|
|
424
471
|
writeJsonFile(configPath, config);
|
|
425
472
|
created.push(CONFIG_FILE);
|
|
@@ -440,14 +487,17 @@ export function runRemotionHubInit(options = {}) {
|
|
|
440
487
|
const lock = readJsonFile(lockPath, defaultLock());
|
|
441
488
|
writeRegistryFile(projectDir, config, lock);
|
|
442
489
|
created.push(config.paths.registry);
|
|
443
|
-
const skills =
|
|
490
|
+
const skills = runFrameworkSkillsInstall(projectDir, normalizedOptions);
|
|
444
491
|
return {
|
|
445
492
|
projectDir,
|
|
446
493
|
configPath,
|
|
447
494
|
lockPath,
|
|
448
495
|
created,
|
|
449
496
|
config,
|
|
450
|
-
|
|
497
|
+
framework,
|
|
498
|
+
devCommand: getFrameworkConfig(config, framework).devCommand ?? adapter.defaultDevCommand,
|
|
499
|
+
create,
|
|
500
|
+
remotionCreate: create,
|
|
451
501
|
skills: {
|
|
452
502
|
skipped: options.skipSkills === true,
|
|
453
503
|
commands: skills,
|
|
@@ -457,9 +507,12 @@ export function runRemotionHubInit(options = {}) {
|
|
|
457
507
|
export async function runRemotionHubAdd(options) {
|
|
458
508
|
const projectDir = resolveProjectDir(options.projectDir);
|
|
459
509
|
const config = loadConfig(projectDir);
|
|
510
|
+
const framework = normalizeFrameworkId(options.framework ?? getConfigDefaultFramework(config));
|
|
460
511
|
const itemRef = parseItemRef(options.item, options.namespace || config.namespace);
|
|
461
|
-
const resolved = registryPayloadUrl(config, itemRef.namespace, itemRef.slug, options.registry);
|
|
462
|
-
const payload = await fetchRegistryPayload(resolved, options.fetchImpl ?? fetch);
|
|
512
|
+
const resolved = registryPayloadUrl(config, itemRef.namespace, itemRef.slug, options.registry, framework);
|
|
513
|
+
const payload = await fetchRegistryPayload(resolved, options.fetchImpl ?? fetch, framework);
|
|
514
|
+
const payloadFramework = normalizeFrameworkId(payload.framework || framework);
|
|
515
|
+
const payloadAdapter = getComponentFrameworkAdapter(payloadFramework);
|
|
463
516
|
const lockPath = path.join(projectDir, LOCK_FILE);
|
|
464
517
|
const lock = readJsonFile(lockPath, defaultLock());
|
|
465
518
|
const written = [];
|
|
@@ -471,7 +524,7 @@ export async function runRemotionHubAdd(options) {
|
|
|
471
524
|
const content = typeof file.content === 'string' ? file.content : '';
|
|
472
525
|
if (!content)
|
|
473
526
|
throw new Error(`Registry file for ${payload.name} is missing content`);
|
|
474
|
-
const relativeTarget = targetForRegistryFile(config, file);
|
|
527
|
+
const relativeTarget = targetForRegistryFile(config, file, payloadFramework);
|
|
475
528
|
const destination = resolveInsideProject(projectDir, relativeTarget, 'file.target');
|
|
476
529
|
const relativePath = normalizeSlashes(path.relative(projectDir, destination));
|
|
477
530
|
if (seenTargets.has(relativePath)) {
|
|
@@ -500,7 +553,8 @@ export async function runRemotionHubAdd(options) {
|
|
|
500
553
|
throw new Error(`Failed to write file: ${normalizeSlashes(path.relative(projectDir, destination))}`);
|
|
501
554
|
}
|
|
502
555
|
}
|
|
503
|
-
|
|
556
|
+
const lockKey = `${itemRef.namespace}/${itemRef.slug}/${payloadFramework}`;
|
|
557
|
+
lock.installed[lockKey] = {
|
|
504
558
|
version: payload.version,
|
|
505
559
|
type: payload.type,
|
|
506
560
|
namespace: itemRef.namespace,
|
|
@@ -509,11 +563,18 @@ export async function runRemotionHubAdd(options) {
|
|
|
509
563
|
files: written,
|
|
510
564
|
dependencies: payload.dependencies,
|
|
511
565
|
installedAt: new Date().toISOString(),
|
|
566
|
+
framework: payloadFramework,
|
|
512
567
|
};
|
|
513
568
|
writeJsonFile(lockPath, lock);
|
|
514
569
|
writeRegistryFile(projectDir, config, lock);
|
|
515
570
|
const snippet = primaryFile
|
|
516
|
-
?
|
|
571
|
+
? payloadAdapter.buildPostAddSnippet({
|
|
572
|
+
name: payload.name,
|
|
573
|
+
primaryFile,
|
|
574
|
+
source: primarySource,
|
|
575
|
+
projectDir,
|
|
576
|
+
config,
|
|
577
|
+
})
|
|
517
578
|
: '';
|
|
518
579
|
return {
|
|
519
580
|
name: payload.name,
|
|
@@ -525,5 +586,6 @@ export async function runRemotionHubAdd(options) {
|
|
|
525
586
|
dependencies: payload.dependencies,
|
|
526
587
|
installedDependencies,
|
|
527
588
|
snippet,
|
|
589
|
+
framework: payloadFramework,
|
|
528
590
|
};
|
|
529
591
|
}
|