chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
require('esbuild-register');
|
||||
require('./index.ts');
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
import { Project, SyntaxKind, type SourceFile } from 'ts-morph';
|
||||
import { camelCase, upperFirst, snakeCase, toUpper } from 'lodash-es';
|
||||
import { Eta } from 'eta';
|
||||
import { input, confirm } from '@inquirer/prompts';
|
||||
|
||||
const tsProject = new Project({});
|
||||
|
||||
class InsertSourceCode {
|
||||
source: SourceFile;
|
||||
constructor(private sourcePath: string) {
|
||||
this.source = tsProject.addSourceFileAtPath(this.sourcePath);
|
||||
}
|
||||
addNamedExport(name: string, specifier: string) {
|
||||
const allExports = this.source.getExportDeclarations();
|
||||
const exist = allExports.some(
|
||||
e =>
|
||||
e.getModuleSpecifierValue() === specifier &&
|
||||
e.getNamedExports().some(i => i.getName() === name),
|
||||
);
|
||||
if (exist) {
|
||||
console.warn(
|
||||
`⚠️ export ${name} in file ${this.sourcePath} already exists.`,
|
||||
);
|
||||
}
|
||||
this.source.addExportDeclaration({
|
||||
namedExports: [name],
|
||||
moduleSpecifier: specifier,
|
||||
});
|
||||
}
|
||||
addNamedImport(name: string, specifier: string) {
|
||||
const allImports = this.source.getImportDeclarations();
|
||||
const exist = allImports.some(
|
||||
e =>
|
||||
e.getModuleSpecifierValue() === specifier &&
|
||||
e.getNamedImports().some(i => i.getName() === name),
|
||||
);
|
||||
if (exist) {
|
||||
console.warn(
|
||||
`⚠️ import ${name} in file ${this.sourcePath} already exists.`,
|
||||
);
|
||||
}
|
||||
this.source.addImportDeclaration({
|
||||
namedImports: [name],
|
||||
moduleSpecifier: specifier,
|
||||
});
|
||||
}
|
||||
getVariableValue<T extends SyntaxKind>(name: string, kind: T) {
|
||||
return this.source
|
||||
.getVariableDeclaration(name)
|
||||
?.getInitializer()
|
||||
?.asKindOrThrow<T>(kind);
|
||||
}
|
||||
save() {
|
||||
return this.source.save();
|
||||
}
|
||||
}
|
||||
|
||||
interface Options {
|
||||
name: string;
|
||||
camelCaseName: string;
|
||||
pascalCaseName: string;
|
||||
constantName: string;
|
||||
registryName: string;
|
||||
isSupportTest: boolean;
|
||||
}
|
||||
|
||||
const ROOT_DIR = process.cwd();
|
||||
|
||||
function copyTemplateFiles(options: Options) {
|
||||
const { name, camelCaseName, constantName, pascalCaseName, isSupportTest } =
|
||||
options;
|
||||
const templateDir = path.join(__dirname, 'templates');
|
||||
const sourceDir = path.join(ROOT_DIR, `./src/node-registries/${name}`);
|
||||
const eta = new Eta({ views: templateDir });
|
||||
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
fs.mkdirSync(sourceDir, { recursive: true });
|
||||
}
|
||||
|
||||
const templates = fs.readdirSync(templateDir);
|
||||
templates.forEach(temp => {
|
||||
const str = eta.render(temp, {
|
||||
PASCAL_NAME_PLACE_HOLDER: pascalCaseName,
|
||||
CAMEL_NAME_PLACE_HOLDER: camelCaseName,
|
||||
CONSTANT_NAME_PLACE_HOLDER: constantName,
|
||||
IS_SUPPORT_TEST: isSupportTest,
|
||||
});
|
||||
fs.writeFileSync(
|
||||
path.join(sourceDir, temp.replace(/\.eta$/, '')),
|
||||
str,
|
||||
'utf-8',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function insertSourceCode(options: Options) {
|
||||
const { pascalCaseName, registryName, name } = options;
|
||||
// node-registries/index.ts
|
||||
const nodeRegistriesIndex = new InsertSourceCode(
|
||||
path.join(ROOT_DIR, './src/node-registries/index.ts'),
|
||||
);
|
||||
nodeRegistriesIndex.addNamedExport(registryName, `./${name}`);
|
||||
await nodeRegistriesIndex.save();
|
||||
|
||||
// src/nodes-v2/constants.ts;
|
||||
const nodeV2Constants = new InsertSourceCode(
|
||||
path.join(ROOT_DIR, './src/nodes-v2/constants.ts'),
|
||||
);
|
||||
nodeV2Constants.addNamedImport(registryName, '@/node-registries');
|
||||
nodeV2Constants
|
||||
.getVariableValue('NODES_V2', SyntaxKind.ArrayLiteralExpression)
|
||||
?.addElement(registryName, { useNewLines: true });
|
||||
await nodeV2Constants.save();
|
||||
|
||||
// components/node-render/node-render-new/content/index.tsx
|
||||
const nodeRenderContentIndex = new InsertSourceCode(
|
||||
path.join(
|
||||
ROOT_DIR,
|
||||
'./src/components/node-render/node-render-new/content/index.tsx',
|
||||
),
|
||||
);
|
||||
nodeRenderContentIndex.addNamedImport(
|
||||
`${pascalCaseName}Content`,
|
||||
`@/node-registries/${name}`,
|
||||
);
|
||||
nodeRenderContentIndex
|
||||
.getVariableValue('ContentMap', SyntaxKind.ObjectLiteralExpression)
|
||||
?.addPropertyAssignment({
|
||||
name: `[StandardNodeType.${pascalCaseName}]`,
|
||||
initializer: `${pascalCaseName}Content`,
|
||||
});
|
||||
await nodeRenderContentIndex.save();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const name = await input({
|
||||
message:
|
||||
'Enter component name (use "-" as separator), e.g."database-create":',
|
||||
required: true,
|
||||
});
|
||||
const camelCaseName = await input({
|
||||
message: 'Use camelCase (lower camel) for variable prefixes:',
|
||||
default: camelCase(name),
|
||||
required: true,
|
||||
});
|
||||
const pascalCaseName = await input({
|
||||
message: 'Use PascalCase (Upper Camel) for class names:',
|
||||
default: upperFirst(camelCaseName),
|
||||
required: true,
|
||||
});
|
||||
const isSupportTest = await confirm({
|
||||
message: 'Is single-node testing supported?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
const constantName = toUpper(snakeCase(name));
|
||||
const registryName = `${constantName}_NODE_REGISTRY`;
|
||||
const options = {
|
||||
name,
|
||||
camelCaseName,
|
||||
pascalCaseName,
|
||||
constantName,
|
||||
registryName,
|
||||
isSupportTest,
|
||||
};
|
||||
|
||||
copyTemplateFiles(options);
|
||||
|
||||
await insertSourceCode(options);
|
||||
|
||||
console.log('done.');
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,26 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import { ViewVariableType } from '@coze-workflow/variable';
|
||||
|
||||
// 入参路径,试运行等功能依赖该路径提取参数
|
||||
export const INPUT_PATH = 'inputs.inputParameters';
|
||||
|
||||
// 定义固定出参
|
||||
export const OUTPUTS = [
|
||||
{
|
||||
key: nanoid(),
|
||||
name: 'outputList',
|
||||
type: ViewVariableType.ArrayObject,
|
||||
children: [
|
||||
{
|
||||
key: nanoid(),
|
||||
name: 'id',
|
||||
type: ViewVariableType.String,
|
||||
},
|
||||
{
|
||||
key: nanoid(),
|
||||
name: 'content',
|
||||
type: ViewVariableType.String,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { type NodeDataDTO } from '@coze-workflow/base';
|
||||
|
||||
import { type FormData } from './types';
|
||||
import { OUTPUTS } from './constants';
|
||||
|
||||
/**
|
||||
* 节点后端数据 -> 前端表单数据
|
||||
*/
|
||||
export const transformOnInit = (value: NodeDataDTO) => ({
|
||||
...(value ?? {}),
|
||||
outputs: value?.outputs ?? OUTPUTS,
|
||||
});
|
||||
|
||||
/**
|
||||
* 前端表单数据 -> 节点后端数据
|
||||
* @param value
|
||||
* @returns
|
||||
*/
|
||||
export const transformOnSubmit = (value: FormData): NodeDataDTO =>
|
||||
value as unknown as NodeDataDTO;
|
||||
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
ValidateTrigger,
|
||||
type FormMetaV2,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { createValueExpressionInputValidate } from '@/node-registries/common/validators';
|
||||
import {
|
||||
fireNodeTitleChange,
|
||||
provideNodeOutputVariablesEffect,
|
||||
} from '@/node-registries/common/effects';
|
||||
|
||||
import { type FormData } from './types';
|
||||
import { FormRender } from './form';
|
||||
import { transformOnInit, transformOnSubmit } from './data-transformer';
|
||||
|
||||
export const <%= it.CONSTANT_NAME_PLACE_HOLDER %>_FORM_META: FormMetaV2<FormData> = {
|
||||
// 节点表单渲染
|
||||
render: () => <FormRender />,
|
||||
|
||||
// 验证触发时机
|
||||
validateTrigger: ValidateTrigger.onChange,
|
||||
|
||||
// 验证规则
|
||||
validate: {
|
||||
// 必填
|
||||
'inputs.inputParameters.0.input': createValueExpressionInputValidate({
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
|
||||
// 副作用管理
|
||||
effect: {
|
||||
nodeMeta: fireNodeTitleChange,
|
||||
outputs: provideNodeOutputVariablesEffect,
|
||||
},
|
||||
|
||||
// 节点后端数据 -> 前端表单数据
|
||||
formatOnInit: transformOnInit,
|
||||
|
||||
// 前端表单数据 -> 节点后端数据
|
||||
formatOnSubmit: transformOnSubmit,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { NodeConfigForm } from '@/node-registries/common/components';
|
||||
|
||||
import { OutputsField, InputsParametersField } from '../common/fields';
|
||||
import { INPUT_PATH } from './constants';
|
||||
|
||||
export const FormRender = () => (
|
||||
<NodeConfigForm>
|
||||
<InputsParametersField
|
||||
name={INPUT_PATH}
|
||||
title={I18n.t('node_http_request_params')}
|
||||
tooltip={I18n.t('node_http_request_params_desc')}
|
||||
defaultValue={[]}
|
||||
/>
|
||||
|
||||
<OutputsField
|
||||
title={I18n.t('workflow_detail_node_output')}
|
||||
tooltip={I18n.t('node_http_response_data')}
|
||||
id="<%= it.CAMEL_NAME_PLACE_HOLDER %>-node-outputs"
|
||||
name="outputs"
|
||||
topLevelReadonly={true}
|
||||
customReadonly
|
||||
/>
|
||||
</NodeConfigForm>
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
export { <%= it.CONSTANT_NAME_PLACE_HOLDER %>_NODE_REGISTRY } from './node-registry';
|
||||
export { <%= it.PASCAL_NAME_PLACE_HOLDER %>Content } from './node-content';
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { InputParameters, Outputs } from '../common/components';
|
||||
|
||||
export function <%= it.PASCAL_NAME_PLACE_HOLDER %>Content() {
|
||||
return (
|
||||
<>
|
||||
<InputParameters />
|
||||
<Outputs />
|
||||
</>
|
||||
);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
DEFAULT_NODE_META_PATH,
|
||||
DEFAULT_OUTPUTS_PATH,
|
||||
} from '@coze-workflow/nodes';
|
||||
import {
|
||||
StandardNodeType,
|
||||
type WorkflowNodeRegistry,
|
||||
} from '@coze-workflow/base';
|
||||
|
||||
import { <%= it.CONSTANT_NAME_PLACE_HOLDER %>_FORM_META } from './form-meta';
|
||||
import { INPUT_PATH } from './constants';
|
||||
import { test, type NodeTestMeta } from './node-test';
|
||||
|
||||
export const <%= it.CONSTANT_NAME_PLACE_HOLDER %>_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> = {
|
||||
type: StandardNodeType.<%= it.PASCAL_NAME_PLACE_HOLDER %>,
|
||||
meta: {
|
||||
nodeDTOType: StandardNodeType.<%= it.PASCAL_NAME_PLACE_HOLDER %>,
|
||||
size: { width: 360, height: 130.7 },
|
||||
nodeMetaPath: DEFAULT_NODE_META_PATH,
|
||||
outputsPath: DEFAULT_OUTPUTS_PATH,
|
||||
inputParametersPath: INPUT_PATH,
|
||||
test,
|
||||
},
|
||||
formMeta: <%= it.CONSTANT_NAME_PLACE_HOLDER %>_FORM_META,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { NodeTestMeta } from '@/test-run-kit';
|
||||
|
||||
const test: NodeTestMeta = <%= it.IS_SUPPORT_TEST %>;
|
||||
|
||||
export { test, type NodeTestMeta };
|
||||
@@ -0,0 +1,5 @@
|
||||
import { type InputValueVO } from '@coze-workflow/base';
|
||||
|
||||
export interface FormData {
|
||||
inputs: { inputParameters: InputValueVO[] };
|
||||
}
|
||||
Reference in New Issue
Block a user