create-promptframe-component 0.0.0 → 0.1.0
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 +2 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +66 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/templates/react-remotion/README.md +88 -0
- package/templates/react-remotion/eslint.config.js +36 -0
- package/templates/react-remotion/manifest.json +38 -0
- package/templates/react-remotion/package.json +37 -0
- package/templates/react-remotion/prettier.config.js +5 -0
- package/templates/react-remotion/src/Component.test.tsx +10 -0
- package/templates/react-remotion/src/Component.tsx +32 -0
- package/templates/react-remotion/src/index.ts +2 -0
- package/templates/react-remotion/src/preview-props.json +12 -0
- package/templates/react-remotion/src/schema.ts +12 -0
- package/templates/react-remotion/tsconfig.json +20 -0
- package/templates/react-remotion/vite.config.ts +16 -0
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# create-promptframe-component
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
This `0.0.0` release is a placeholder that reserves the package name and trusted publishing route. The first functional release will start at `0.1.0`.
|
|
3
|
+
Create a PromptFrame-compatible Remotion component project from the standard React template.
|
|
6
4
|
|
|
5
|
+
The generated project has no Git remote by default. It is meant to be validated and uploaded through `@promptframe/cli`.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,4 +1,68 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const targetArg = process.argv[2];
|
|
6
|
+
if (!targetArg || targetArg === '--help' || targetArg === '-h') {
|
|
7
|
+
console.log('Usage: npm create promptframe-component <component-dir> [--display-name "Sales Funnel"]');
|
|
8
|
+
process.exit(targetArg ? 0 : 1);
|
|
9
|
+
}
|
|
10
|
+
const targetDir = resolve(targetArg);
|
|
11
|
+
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
12
|
+
console.error(`create.target_not_empty: ${targetDir} is not empty`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const componentName = toKebabName(targetArg.split(/[\\/]/).filter(Boolean).at(-1) ?? 'promptframe-component');
|
|
16
|
+
const displayName = valueAfter(process.argv.slice(3), '--display-name') ?? toTitle(componentName);
|
|
17
|
+
const description = valueAfter(process.argv.slice(3), '--description') ?? `${displayName} PromptFrame component`;
|
|
18
|
+
const packageRoot = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const templateDir = resolve(packageRoot, '../templates/react-remotion');
|
|
20
|
+
copyTemplate(templateDir, targetDir, {
|
|
21
|
+
__COMPONENT_NAME__: componentName,
|
|
22
|
+
__DISPLAY_NAME__: displayName,
|
|
23
|
+
__DESCRIPTION__: description,
|
|
24
|
+
});
|
|
25
|
+
console.log(`Created PromptFrame component at ${targetDir}`);
|
|
26
|
+
console.log('Next steps: npm install && npm run validate');
|
|
27
|
+
function copyTemplate(from, to, replacements) {
|
|
28
|
+
mkdirSync(to, { recursive: true });
|
|
29
|
+
for (const entry of readdirSync(from)) {
|
|
30
|
+
const src = join(from, entry);
|
|
31
|
+
const dest = join(to, entry);
|
|
32
|
+
const stat = statSync(src);
|
|
33
|
+
if (stat.isDirectory()) {
|
|
34
|
+
copyTemplate(src, dest, replacements);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (isTextFile(src)) {
|
|
38
|
+
let text = readFileSync(src, 'utf8');
|
|
39
|
+
for (const [key, value] of Object.entries(replacements))
|
|
40
|
+
text = text.replaceAll(key, value);
|
|
41
|
+
writeFileSync(dest, text);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
copyFileSync(src, dest);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function isTextFile(path) {
|
|
49
|
+
return /\.(json|md|js|ts|tsx|yml|yaml|css|html)$/.test(path);
|
|
50
|
+
}
|
|
51
|
+
function valueAfter(argv, flag) {
|
|
52
|
+
const index = argv.indexOf(flag);
|
|
53
|
+
if (index < 0)
|
|
54
|
+
return undefined;
|
|
55
|
+
return argv[index + 1];
|
|
56
|
+
}
|
|
57
|
+
function toKebabName(value) {
|
|
58
|
+
return value
|
|
59
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
60
|
+
.toLowerCase()
|
|
61
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
62
|
+
.replace(/^-+|-+$/g, '')
|
|
63
|
+
.slice(0, 63) || 'promptframe-component';
|
|
64
|
+
}
|
|
65
|
+
function toTitle(value) {
|
|
66
|
+
return value.split('-').filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join(' ');
|
|
67
|
+
}
|
|
4
68
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-promptframe-component",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
8
|
+
"templates",
|
|
8
9
|
"README.md"
|
|
9
10
|
],
|
|
10
11
|
"bin": {
|
|
@@ -41,8 +42,8 @@
|
|
|
41
42
|
"test": "node -e \"console.log('placeholder test: create-promptframe-component')\"",
|
|
42
43
|
"clean": "rm -rf dist"
|
|
43
44
|
},
|
|
44
|
-
"dependencies": {},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@types/node": "^24.12.4",
|
|
46
47
|
"typescript": "~5.7.0"
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# __DISPLAY_NAME__
|
|
2
|
+
|
|
3
|
+
平台标准 Remotion 组件市场组件。
|
|
4
|
+
|
|
5
|
+
## 本地开发
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npm run validate
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
如果你是冷启动 AI,先不要发散改目录结构。请按下面顺序做:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx promptframe standard
|
|
16
|
+
npx promptframe doctor .
|
|
17
|
+
npx promptframe validate .
|
|
18
|
+
npx promptframe upload . --endpoint <promptframe-api-base>
|
|
19
|
+
npx promptframe status <jobId> --endpoint <promptframe-api-base>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
如果上传入口不可用,仍然要完成本地开发和 `validate`,然后按组件作者报告模板写清本地结果。
|
|
23
|
+
|
|
24
|
+
## 必需文件
|
|
25
|
+
|
|
26
|
+
- `src/Component.tsx`:确定性的 Remotion 组件。
|
|
27
|
+
- `src/schema.ts`:Zod props schema(参数 schema)和默认 props。
|
|
28
|
+
- `src/preview-props.json`:有边界的默认预览参数。
|
|
29
|
+
- `manifest.json`:组件身份、版本、分类、审核状态和 hash 信息。
|
|
30
|
+
|
|
31
|
+
## 时间轴规则
|
|
32
|
+
|
|
33
|
+
- 所有动画必须由 `useCurrentFrame()` / `useVideoConfig()` 驱动。
|
|
34
|
+
- `manifest.json` 的 `designedDurationRange` 表示舒服播放区间,不是硬编码场景时长。
|
|
35
|
+
- 组件要能适配不同 `durationFrames`:时间不足时加速关键动作,时间富裕时保持最终态。
|
|
36
|
+
- 禁止 CSS `transition`、`@keyframes`、计时器、`Date.now()`、`Math.random()`。
|
|
37
|
+
|
|
38
|
+
项目级规则见 PromptFrame authoring skill 的 `rules/timing-ssot.md`。
|
|
39
|
+
|
|
40
|
+
## 检索元数据
|
|
41
|
+
|
|
42
|
+
组件作者可以维护标题、摘要、描述、标签和参数说明。平台会根据源码、schema、依赖、预览画面和渲染结果生成不可手改的证据层,用于防止描述和真实能力不一致。
|
|
43
|
+
|
|
44
|
+
如果描述写得夸张但源码/预览证据不支持,组件不会一定被拦截,但会收到提示并影响公开市场排序。
|
|
45
|
+
|
|
46
|
+
动态数据组件可以参考 PromptFrame authoring skill 的 `rules/schema-recipes.md`,里面有增长指标、对比指标、漏斗阶段和正负 delta 的推荐 schema 写法。
|
|
47
|
+
|
|
48
|
+
本地校验通过后,使用 `@promptframe/cli` 上传。
|
|
49
|
+
|
|
50
|
+
## 安全策略
|
|
51
|
+
|
|
52
|
+
安全规则来自平台标准 API。CLI 本地 `promptframe validate .` 和服务端准入会使用同一套公开 ruleId 口径,并在报错中显示 `ruleId`。
|
|
53
|
+
|
|
54
|
+
- 不要使用 `eval`、`new Function`、字符串定时器、`node:fs`、`child_process`、`process.env`。
|
|
55
|
+
- 不要直接使用 `fetch()` / XHR / WebSocket / Beacon。
|
|
56
|
+
- 即使使用 `componentRuntime.fetchJson()`,也必须等待平台提供白名单配置;未配置时会进入 `manual_review`。
|
|
57
|
+
- 不要读取 `localStorage` / `sessionStorage` / cookie。当前可能只是 warning,但不建议保留。
|
|
58
|
+
- 需要素材、状态或外部数据时,让平台通过 props / asset / 后续受控 wrapper 注入。
|
|
59
|
+
|
|
60
|
+
## 上传与状态
|
|
61
|
+
|
|
62
|
+
推荐直接上传组件目录:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx promptframe upload . --endpoint <promptframe-api-base>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`upload .` 是推荐的一步式入口,会自动完成 validate、package 和上传。`package` 只用于离线传输或排查 zip 内容。
|
|
69
|
+
|
|
70
|
+
如果平台入口不可用,先保留本地校验结果和错误输出,不要把服务地址写进源码、manifest 或组件 README。
|
|
71
|
+
|
|
72
|
+
查看构建验收状态:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx promptframe status <jobId> --endpoint <promptframe-api-base>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
上传成功只代表服务端已接收源码并执行构建验收,不等于组件已经公开发布。
|
|
79
|
+
|
|
80
|
+
如果平台提示预览资产是平台环境问题,平台维护者修复后可重新生成预览:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npx promptframe reindex <jobId> --endpoint <promptframe-api-base>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## 组件包边界
|
|
87
|
+
|
|
88
|
+
本组件目录是上传、构建和验收边界。源码文件可以引用组件目录内部文件,但不能引用组件目录外的本地文件,也不要依赖本地 path alias。需要复用的公共能力应来自 npm 包或平台标准包,例如 `@promptframe/component-kit`。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import js from '@eslint/js';
|
|
2
|
+
import globals from 'globals';
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
5
|
+
import tseslint from 'typescript-eslint';
|
|
6
|
+
|
|
7
|
+
export default tseslint.config(
|
|
8
|
+
{ ignores: ['dist'] },
|
|
9
|
+
{
|
|
10
|
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
ecmaVersion: 2020,
|
|
14
|
+
globals: globals.browser,
|
|
15
|
+
},
|
|
16
|
+
plugins: {
|
|
17
|
+
'react-hooks': reactHooks,
|
|
18
|
+
'react-refresh': reactRefresh,
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
...reactHooks.configs.recommended.rules,
|
|
22
|
+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
|
23
|
+
'no-restricted-syntax': [
|
|
24
|
+
'error',
|
|
25
|
+
{
|
|
26
|
+
selector: "CallExpression[callee.object.name='Math'][callee.property.name='random']",
|
|
27
|
+
message: 'Use remotion random(seed) instead of Math.random().',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
selector: "CallExpression[callee.object.name='Date'][callee.property.name='now']",
|
|
31
|
+
message: 'Frame output must not depend on Date.now().',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "component-manifest.v0.1.0",
|
|
3
|
+
"standardVersion": "component-standard.v0.1.0",
|
|
4
|
+
"standardSourceHash": "sha256:8c1e01c36155b4b646981064d24df9bd8cda501fd9cd9da93e5b62f40db22d52",
|
|
5
|
+
"id": "@marketplace/__COMPONENT_NAME__",
|
|
6
|
+
"name": "__COMPONENT_NAME__",
|
|
7
|
+
"displayName": "__DISPLAY_NAME__",
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"componentType": "scene_template",
|
|
10
|
+
"trustLevel": "untrusted_temporary",
|
|
11
|
+
"author": {
|
|
12
|
+
"id": "local-user",
|
|
13
|
+
"name": "Local User"
|
|
14
|
+
},
|
|
15
|
+
"description": "__DESCRIPTION__",
|
|
16
|
+
"tags": ["custom", "marketplace", "remotion"],
|
|
17
|
+
"designedDurationRange": {
|
|
18
|
+
"min": 90,
|
|
19
|
+
"max": 180
|
|
20
|
+
},
|
|
21
|
+
"entry": {
|
|
22
|
+
"sourcePath": "src/Component.tsx",
|
|
23
|
+
"componentExport": "default",
|
|
24
|
+
"propsSchemaPath": "src/schema.ts",
|
|
25
|
+
"sourceHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000",
|
|
26
|
+
"schemaHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": "^19.0.0",
|
|
31
|
+
"remotion": "^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"assets": {},
|
|
34
|
+
"capabilityHints": ["animated scene template"],
|
|
35
|
+
"reviewStatus": "draft",
|
|
36
|
+
"license": "proprietary",
|
|
37
|
+
"createdAt": "2026-05-13T00:00:00.000Z"
|
|
38
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__COMPONENT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --host 127.0.0.1",
|
|
8
|
+
"build": "tsc --noEmit && vite build",
|
|
9
|
+
"lint": "eslint .",
|
|
10
|
+
"format": "prettier --check .",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"validate": "npm run lint && npm run test && npm run build"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@promptframe/component-kit": "^0.1.3",
|
|
16
|
+
"@promptframe/contracts": "^0.1.0",
|
|
17
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
18
|
+
"vite": "^8.0.10",
|
|
19
|
+
"typescript": "~6.0.2",
|
|
20
|
+
"react": "^19.0.0",
|
|
21
|
+
"react-dom": "^19.0.0",
|
|
22
|
+
"remotion": "^4.0.0",
|
|
23
|
+
"zod": "^3.24.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@eslint/js": "^10.0.1",
|
|
27
|
+
"@types/react": "^19.0.0",
|
|
28
|
+
"@types/react-dom": "^19.0.0",
|
|
29
|
+
"eslint": "^10.2.1",
|
|
30
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
31
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
32
|
+
"globals": "^17.5.0",
|
|
33
|
+
"prettier": "^3.5.0",
|
|
34
|
+
"typescript-eslint": "^8.58.2",
|
|
35
|
+
"vitest": "^3.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { defaultProps, propsSchema } from './schema';
|
|
3
|
+
|
|
4
|
+
describe('__COMPONENT_NAME__ props schema', () => {
|
|
5
|
+
it('provides bounded default preview props', () => {
|
|
6
|
+
expect(propsSchema.parse(defaultProps)).toMatchObject({
|
|
7
|
+
title: '__DISPLAY_NAME__',
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from 'remotion';
|
|
2
|
+
import type { ComponentProps } from './schema';
|
|
3
|
+
|
|
4
|
+
export default function Component(props: ComponentProps) {
|
|
5
|
+
const frame = useCurrentFrame();
|
|
6
|
+
const { fps } = useVideoConfig();
|
|
7
|
+
const progress = interpolate(frame, [0, Math.max(1, fps * 2)], [0, 1], {
|
|
8
|
+
extrapolateLeft: 'clamp',
|
|
9
|
+
extrapolateRight: 'clamp',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<AbsoluteFill
|
|
14
|
+
style={{
|
|
15
|
+
background: props.background,
|
|
16
|
+
color: props.foreground,
|
|
17
|
+
fontFamily: 'Inter, ui-sans-serif, system-ui, sans-serif',
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
padding: 72,
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<div style={{ opacity: progress, transform: `translateY(${(1 - progress) * 24}px)` }}>
|
|
23
|
+
<p style={{ margin: 0, fontSize: 24, letterSpacing: 0, opacity: 0.72 }}>
|
|
24
|
+
{props.kicker}
|
|
25
|
+
</p>
|
|
26
|
+
<h1 style={{ margin: '12px 0 0', fontSize: 68, lineHeight: 1.02, letterSpacing: 0 }}>
|
|
27
|
+
{props.title}
|
|
28
|
+
</h1>
|
|
29
|
+
</div>
|
|
30
|
+
</AbsoluteFill>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const propsSchema = z.object({
|
|
4
|
+
kicker: z.string().default('Marketplace Component'),
|
|
5
|
+
title: z.string().default('__DISPLAY_NAME__'),
|
|
6
|
+
background: z.string().default('#101114'),
|
|
7
|
+
foreground: z.string().default('#f4f1ea'),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export type ComponentProps = z.infer<typeof propsSchema>;
|
|
11
|
+
|
|
12
|
+
export const defaultProps: ComponentProps = propsSchema.parse({});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
6
|
+
"allowJs": false,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"moduleResolution": "Bundler",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx"
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"]
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [react()],
|
|
6
|
+
build: {
|
|
7
|
+
lib: {
|
|
8
|
+
entry: 'src/index.ts',
|
|
9
|
+
formats: ['es'],
|
|
10
|
+
fileName: () => 'index.js',
|
|
11
|
+
},
|
|
12
|
+
rollupOptions: {
|
|
13
|
+
external: ['react', 'react-dom', 'react/jsx-runtime', 'remotion'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|