[FRONT COMPONENTS] Move to twenty-sdk (#17587)
Move front components from twenty-shared to twenty-sdk
This commit is contained in:
@@ -18,28 +18,20 @@
|
||||
"@prettier/sync": "^0.5.2",
|
||||
"@types/babel__preset-env": "^7",
|
||||
"@types/handlebars": "^4.1.0",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"glob": "^11.1.0",
|
||||
"ts-morph": "^25.0.0",
|
||||
"tsx": "^4.19.3",
|
||||
"vite": "^7.0.0",
|
||||
"vite-plugin-dts": "3.8.1",
|
||||
"vite-tsconfig-paths": "^4.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quilted/threads": "^4.0.1",
|
||||
"@remote-dom/core": "^1.10.1",
|
||||
"@remote-dom/react": "^1.2.2",
|
||||
"@sniptt/guards": "^0.2.0",
|
||||
"class-validator": "^0.14.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"libphonenumber-js": "^1.10.26",
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"qs": "^6.11.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-router-dom": "^6.4.4",
|
||||
"transliteration": "^2.3.5",
|
||||
"zod": "^4.1.11"
|
||||
@@ -70,16 +62,6 @@
|
||||
"import": "./dist/database-events.mjs",
|
||||
"require": "./dist/database-events.cjs"
|
||||
},
|
||||
"./front-component": {
|
||||
"types": "./dist/front-component/index.d.ts",
|
||||
"import": "./dist/front-component.mjs",
|
||||
"require": "./dist/front-component.cjs"
|
||||
},
|
||||
"./front-component-constants": {
|
||||
"types": "./dist/front-component-constants/index.d.ts",
|
||||
"import": "./dist/front-component-constants.mjs",
|
||||
"require": "./dist/front-component-constants.cjs"
|
||||
},
|
||||
"./metadata": {
|
||||
"types": "./dist/metadata/index.d.ts",
|
||||
"import": "./dist/metadata.mjs",
|
||||
@@ -122,8 +104,6 @@
|
||||
"application",
|
||||
"constants",
|
||||
"database-events",
|
||||
"front-component",
|
||||
"front-component-constants",
|
||||
"metadata",
|
||||
"testing",
|
||||
"translations",
|
||||
@@ -146,12 +126,6 @@
|
||||
"database-events": [
|
||||
"dist/database-events/index.d.ts"
|
||||
],
|
||||
"front-component": [
|
||||
"dist/front-component/index.d.ts"
|
||||
],
|
||||
"front-component-constants": [
|
||||
"dist/front-component-constants/index.d.ts"
|
||||
],
|
||||
"metadata": [
|
||||
"dist/metadata/index.d.ts"
|
||||
],
|
||||
|
||||
@@ -22,10 +22,6 @@
|
||||
"{projectRoot}/constants/dist",
|
||||
"{projectRoot}/database-events/package.json",
|
||||
"{projectRoot}/database-events/dist",
|
||||
"{projectRoot}/front-component/package.json",
|
||||
"{projectRoot}/front-component/dist",
|
||||
"{projectRoot}/front-component-constants/package.json",
|
||||
"{projectRoot}/front-component-constants/dist",
|
||||
"{projectRoot}/metadata/package.json",
|
||||
"{projectRoot}/metadata/dist",
|
||||
"{projectRoot}/testing/package.json",
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import * as prettier from '@prettier/sync';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { IndentationText, Project, QuoteKind } from 'ts-morph';
|
||||
|
||||
import { ALLOWED_HTML_ELEMENTS } from '../../src/front-component-constants/AllowedHtmlElements';
|
||||
import { COMMON_HTML_EVENTS } from '../../src/front-component-constants/CommonHtmlEvents';
|
||||
import { EVENT_TO_REACT } from '../../src/front-component-constants/EventToReact';
|
||||
import { HTML_COMMON_PROPERTIES } from '../../src/front-component-constants/HtmlCommonProperties';
|
||||
|
||||
import {
|
||||
type ComponentSchema,
|
||||
extractHtmlTag,
|
||||
generateHostRegistry,
|
||||
generateRemoteComponents,
|
||||
generateRemoteElements,
|
||||
HtmlElementConfigArrayZ,
|
||||
OUTPUT_FILES,
|
||||
} from './generators';
|
||||
|
||||
const SCRIPT_DIR = path.dirname(new URL(import.meta.url).pathname);
|
||||
const PACKAGE_PATH = path.resolve(SCRIPT_DIR, '../..');
|
||||
const FRONT_COMPONENT_PATH = path.join(PACKAGE_PATH, 'src/front-component');
|
||||
const HOST_GENERATED_DIR = path.join(FRONT_COMPONENT_PATH, 'host/generated');
|
||||
const REMOTE_GENERATED_DIR = path.join(
|
||||
FRONT_COMPONENT_PATH,
|
||||
'remote/generated',
|
||||
);
|
||||
|
||||
const formatZodError = (error: {
|
||||
issues: { path: PropertyKey[]; message: string }[];
|
||||
}): string => {
|
||||
return error.issues
|
||||
.map((issue) => ` - ${issue.path.join('.')}: ${issue.message}`)
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
const getHtmlElementSchemas = (): ComponentSchema[] => {
|
||||
const result = HtmlElementConfigArrayZ.safeParse(ALLOWED_HTML_ELEMENTS);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
`Invalid HTML element configuration:\n${formatZodError(result.error)}`,
|
||||
);
|
||||
}
|
||||
|
||||
return result.data.map((element) => ({
|
||||
name: element.name,
|
||||
tagName: element.name,
|
||||
customElementName: element.tag,
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES,
|
||||
...element.properties,
|
||||
},
|
||||
events: COMMON_HTML_EVENTS,
|
||||
isHtmlElement: true,
|
||||
htmlTag: extractHtmlTag(element.tag),
|
||||
}));
|
||||
};
|
||||
|
||||
const createProject = (): Project => {
|
||||
return new Project({
|
||||
manipulationSettings: {
|
||||
indentationText: IndentationText.TwoSpaces,
|
||||
quoteKind: QuoteKind.Single,
|
||||
useTrailingCommas: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const writeGeneratedFile = (
|
||||
dir: string,
|
||||
filename: string,
|
||||
content: string,
|
||||
): void => {
|
||||
const filePath = path.join(dir, filename);
|
||||
const formattedContent = prettier.format(content, {
|
||||
parser: 'typescript',
|
||||
filepath: filePath,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
endOfLine: 'lf',
|
||||
});
|
||||
fs.writeFileSync(filePath, formattedContent, 'utf-8');
|
||||
console.log(`✓ Generated ${filePath}`);
|
||||
};
|
||||
|
||||
const ensureDirectoriesExist = (): void => {
|
||||
if (!fs.existsSync(HOST_GENERATED_DIR)) {
|
||||
fs.mkdirSync(HOST_GENERATED_DIR, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(REMOTE_GENERATED_DIR)) {
|
||||
fs.mkdirSync(REMOTE_GENERATED_DIR, { recursive: true });
|
||||
}
|
||||
};
|
||||
|
||||
const main = (): void => {
|
||||
console.log('📖 Generating remote DOM elements...\n');
|
||||
|
||||
let htmlElements: ComponentSchema[];
|
||||
try {
|
||||
htmlElements = getHtmlElementSchemas();
|
||||
} catch (error) {
|
||||
console.error('❌ Validation failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`HTML Elements: ${htmlElements.length} elements`);
|
||||
console.log(
|
||||
` Tags: ${htmlElements.map((element) => element.htmlTag).join(', ')}`,
|
||||
);
|
||||
console.log(
|
||||
` Events: ${COMMON_HTML_EVENTS.length} common events per element`,
|
||||
);
|
||||
console.log('');
|
||||
|
||||
ensureDirectoriesExist();
|
||||
|
||||
const project = createProject();
|
||||
|
||||
console.log('Host files:');
|
||||
const hostRegistry = generateHostRegistry(
|
||||
project,
|
||||
htmlElements,
|
||||
EVENT_TO_REACT,
|
||||
);
|
||||
writeGeneratedFile(
|
||||
HOST_GENERATED_DIR,
|
||||
OUTPUT_FILES.HOST_REGISTRY,
|
||||
hostRegistry.getFullText(),
|
||||
);
|
||||
|
||||
console.log('\nRemote files:');
|
||||
const remoteElements = generateRemoteElements(
|
||||
project,
|
||||
htmlElements,
|
||||
HTML_COMMON_PROPERTIES,
|
||||
COMMON_HTML_EVENTS,
|
||||
);
|
||||
writeGeneratedFile(
|
||||
REMOTE_GENERATED_DIR,
|
||||
OUTPUT_FILES.REMOTE_ELEMENTS,
|
||||
remoteElements.getFullText(),
|
||||
);
|
||||
|
||||
const remoteComponents = generateRemoteComponents(project, htmlElements);
|
||||
writeGeneratedFile(
|
||||
REMOTE_GENERATED_DIR,
|
||||
OUTPUT_FILES.REMOTE_COMPONENTS,
|
||||
remoteComponents.getFullText(),
|
||||
);
|
||||
|
||||
console.log('\n✅ All generated files created');
|
||||
console.log(` Host: ${HOST_GENERATED_DIR}`);
|
||||
console.log(` Remote: ${REMOTE_GENERATED_DIR}`);
|
||||
};
|
||||
|
||||
main();
|
||||
@@ -1,4 +0,0 @@
|
||||
export const CUSTOM_ELEMENT_NAMES = {
|
||||
ROOT: 'remote-root',
|
||||
FRAGMENT: 'remote-fragment',
|
||||
} as const;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
export const INTERNAL_ELEMENT_CLASSES = {
|
||||
ROOT: 'RemoteRootElement',
|
||||
FRAGMENT: 'RemoteFragmentElement',
|
||||
} as const;
|
||||
@@ -1,5 +0,0 @@
|
||||
export const OUTPUT_FILES = {
|
||||
REMOTE_ELEMENTS: 'remote-elements.ts',
|
||||
REMOTE_COMPONENTS: 'remote-components.ts',
|
||||
HOST_REGISTRY: 'host-component-registry.ts',
|
||||
} as const;
|
||||
@@ -1,7 +0,0 @@
|
||||
export const TYPE_NAMES = {
|
||||
COMMON_PROPERTIES: 'HtmlCommonProperties',
|
||||
COMMON_EVENTS: 'HtmlCommonEvents',
|
||||
COMMON_PROPERTIES_CONFIG: 'HTML_COMMON_PROPERTIES_CONFIG',
|
||||
COMMON_EVENTS_ARRAY: 'HTML_COMMON_EVENTS_ARRAY',
|
||||
EMPTY_RECORD: 'Record<string, never>',
|
||||
} as const;
|
||||
@@ -1,4 +0,0 @@
|
||||
export { CUSTOM_ELEMENT_NAMES } from './CustomElementNames';
|
||||
export { INTERNAL_ELEMENT_CLASSES } from './InternalElementClasses';
|
||||
export { OUTPUT_FILES } from './OutputFiles';
|
||||
export { TYPE_NAMES } from './TypeNames';
|
||||
@@ -1,120 +0,0 @@
|
||||
import type { Project, SourceFile } from 'ts-morph';
|
||||
|
||||
import { CUSTOM_ELEMENT_NAMES } from './constants';
|
||||
import { type ComponentSchema } from './schemas';
|
||||
import { addFileHeader, addStatement } from './utils';
|
||||
|
||||
const generateRuntimeUtilities = (
|
||||
eventToReactMapping: Record<string, string>,
|
||||
): string => {
|
||||
const eventMapEntries = Object.entries(eventToReactMapping)
|
||||
.map(([domEvent, reactProp]) => ` on${domEvent}: '${reactProp}',`)
|
||||
.join('\n');
|
||||
|
||||
return `const INTERNAL_PROPS = new Set(['element', 'receiver', 'components']);
|
||||
|
||||
const EVENT_NAME_MAP: Record<string, string> = {
|
||||
${eventMapEntries}
|
||||
};
|
||||
|
||||
const parseStyle = (styleString: string | undefined): React.CSSProperties | undefined => {
|
||||
if (!styleString || typeof styleString !== 'string') {
|
||||
return styleString as React.CSSProperties | undefined;
|
||||
}
|
||||
|
||||
const style: Record<string, string> = {};
|
||||
const declarations = styleString.split(';').filter(Boolean);
|
||||
|
||||
for (const declaration of declarations) {
|
||||
const colonIndex = declaration.indexOf(':');
|
||||
if (colonIndex === -1) continue;
|
||||
|
||||
const property = declaration.slice(0, colonIndex).trim();
|
||||
const value = declaration.slice(colonIndex + 1).trim();
|
||||
|
||||
const camelProperty = property.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
||||
style[camelProperty] = value;
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
const wrapEventHandler = (handler: () => void) => {
|
||||
return (_event: unknown) => {
|
||||
handler();
|
||||
};
|
||||
};
|
||||
|
||||
const filterProps = (props: Record<string, unknown>) => {
|
||||
const filtered: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (INTERNAL_PROPS.has(key) || value === undefined) continue;
|
||||
|
||||
if (key === 'style') {
|
||||
filtered.style = parseStyle(value as string | undefined);
|
||||
} else {
|
||||
const normalizedKey = EVENT_NAME_MAP[key.toLowerCase()] || key;
|
||||
if (normalizedKey.startsWith('on') && typeof value === 'function') {
|
||||
filtered[normalizedKey] = wrapEventHandler(value as () => void);
|
||||
} else {
|
||||
filtered[normalizedKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
};`;
|
||||
};
|
||||
|
||||
const generateWrapperComponent = (component: ComponentSchema): string => {
|
||||
return `const ${component.name}Wrapper = ({ children, ...props }: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('${component.htmlTag}', filterProps(props), children);
|
||||
};`;
|
||||
};
|
||||
|
||||
const generateRegistryMap = (components: ComponentSchema[]): string => {
|
||||
const entries = components
|
||||
.map(
|
||||
(component) =>
|
||||
` ['${component.customElementName}', createRemoteComponentRenderer(${component.name}Wrapper)],`,
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
return `export const componentRegistry = new Map<string, ReturnType<typeof createRemoteComponentRenderer>>([
|
||||
${entries}
|
||||
['${CUSTOM_ELEMENT_NAMES.FRAGMENT}', RemoteFragmentRenderer],
|
||||
]);`;
|
||||
};
|
||||
|
||||
export const generateHostRegistry = (
|
||||
project: Project,
|
||||
components: ComponentSchema[],
|
||||
eventToReactMapping: Record<string, string>,
|
||||
): SourceFile => {
|
||||
const sourceFile = project.createSourceFile(
|
||||
'host-component-registry.ts',
|
||||
'',
|
||||
{ overwrite: true },
|
||||
);
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: 'react',
|
||||
defaultImport: 'React',
|
||||
});
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@remote-dom/react/host',
|
||||
namedImports: ['RemoteFragmentRenderer', 'createRemoteComponentRenderer'],
|
||||
});
|
||||
|
||||
addStatement(sourceFile, generateRuntimeUtilities(eventToReactMapping));
|
||||
|
||||
for (const component of components) {
|
||||
addStatement(sourceFile, generateWrapperComponent(component));
|
||||
}
|
||||
|
||||
addStatement(sourceFile, generateRegistryMap(components));
|
||||
|
||||
addFileHeader(sourceFile);
|
||||
|
||||
return sourceFile;
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
export * from './constants';
|
||||
export { generateHostRegistry } from './host-registry.generator';
|
||||
export { generateRemoteComponents } from './remote-components.generator';
|
||||
export { generateRemoteElements } from './remote-elements.generator';
|
||||
export * from './schemas';
|
||||
export * from './utils';
|
||||
@@ -1,64 +0,0 @@
|
||||
import type { Project, SourceFile } from 'ts-morph';
|
||||
|
||||
import { type ComponentSchema } from './schemas';
|
||||
import { addExportedConst, addFileHeader, eventToReactProp } from './utils';
|
||||
|
||||
const generateComponentDefinition = (
|
||||
sourceFile: SourceFile,
|
||||
component: ComponentSchema,
|
||||
): void => {
|
||||
const hasEvents = component.events.length > 0;
|
||||
const componentExportName = component.tagName;
|
||||
|
||||
let initializer: string;
|
||||
|
||||
if (hasEvents) {
|
||||
const eventProps = component.events
|
||||
.map((event) => {
|
||||
const propName = eventToReactProp(event);
|
||||
return ` ${propName}: { event: '${event}' },`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
initializer = `createRemoteComponent('${component.customElementName}', ${component.name}Element, {
|
||||
eventProps: {
|
||||
${eventProps}
|
||||
},
|
||||
})`;
|
||||
} else {
|
||||
initializer = `createRemoteComponent('${component.customElementName}', ${component.name}Element)`;
|
||||
}
|
||||
|
||||
addExportedConst(sourceFile, componentExportName, initializer);
|
||||
};
|
||||
|
||||
export const generateRemoteComponents = (
|
||||
project: Project,
|
||||
components: ComponentSchema[],
|
||||
): SourceFile => {
|
||||
const sourceFile = project.createSourceFile('remote-components.ts', '', {
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@remote-dom/react',
|
||||
namedImports: ['createRemoteComponent'],
|
||||
});
|
||||
|
||||
const elementImports = components.map(
|
||||
(component) => `${component.name}Element`,
|
||||
);
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: './remote-elements',
|
||||
namedImports: elementImports,
|
||||
});
|
||||
|
||||
for (const component of components) {
|
||||
generateComponentDefinition(sourceFile, component);
|
||||
}
|
||||
|
||||
addFileHeader(sourceFile);
|
||||
|
||||
return sourceFile;
|
||||
};
|
||||
@@ -1,389 +0,0 @@
|
||||
import {
|
||||
type Project,
|
||||
type SourceFile,
|
||||
VariableDeclarationKind,
|
||||
type WriterFunction,
|
||||
} from 'ts-morph';
|
||||
|
||||
import {
|
||||
CUSTOM_ELEMENT_NAMES,
|
||||
INTERNAL_ELEMENT_CLASSES,
|
||||
TYPE_NAMES,
|
||||
} from './constants';
|
||||
import { type ComponentSchema, type PropertySchema } from './schemas';
|
||||
import {
|
||||
addFileHeader,
|
||||
generatePropertiesConfig,
|
||||
schemaTypeToConstructor,
|
||||
schemaTypeToTs,
|
||||
} from './utils';
|
||||
|
||||
type ElementGenerationOptions = {
|
||||
useSharedEvents: boolean;
|
||||
useSharedPropertiesConfig: boolean;
|
||||
};
|
||||
|
||||
type ComponentWithSpecificProps = {
|
||||
component: ComponentSchema;
|
||||
specificProperties: Record<string, PropertySchema>;
|
||||
};
|
||||
|
||||
const getElementSpecificProperties = (
|
||||
component: ComponentSchema,
|
||||
commonPropertyNames: Set<string>,
|
||||
): Record<string, PropertySchema> => {
|
||||
const specific: Record<string, PropertySchema> = {};
|
||||
for (const [name, schema] of Object.entries(component.properties)) {
|
||||
if (!commonPropertyNames.has(name)) {
|
||||
specific[name] = schema;
|
||||
}
|
||||
}
|
||||
return specific;
|
||||
};
|
||||
|
||||
const generatePropertyEntries = (
|
||||
properties: Record<string, PropertySchema>,
|
||||
): string[] => {
|
||||
return Object.entries(properties).map(([name, schema]) => {
|
||||
const tsType = schemaTypeToTs(schema.type);
|
||||
const optional = schema.optional ? '?' : '';
|
||||
return `'${name}'${optional}: ${tsType}`;
|
||||
});
|
||||
};
|
||||
|
||||
const generateCommonPropertiesType = (
|
||||
sourceFile: SourceFile,
|
||||
commonProperties: Record<string, PropertySchema>,
|
||||
): void => {
|
||||
const entries = generatePropertyEntries(commonProperties);
|
||||
|
||||
sourceFile.addTypeAlias({
|
||||
isExported: true,
|
||||
name: TYPE_NAMES.COMMON_PROPERTIES,
|
||||
type: (writer) => {
|
||||
writer.block(() => {
|
||||
for (const entry of entries) {
|
||||
writer.writeLine(`${entry};`);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const generateCommonEventsType = (
|
||||
sourceFile: SourceFile,
|
||||
events: readonly string[],
|
||||
): void => {
|
||||
if (events.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sourceFile.addTypeAlias({
|
||||
isExported: true,
|
||||
name: TYPE_NAMES.COMMON_EVENTS,
|
||||
type: (writer) => {
|
||||
writer.block(() => {
|
||||
for (const event of events) {
|
||||
writer.writeLine(`${event}(event: RemoteEvent): void;`);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
sourceFile.addVariableStatement({
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: TYPE_NAMES.COMMON_EVENTS_ARRAY,
|
||||
initializer: (writer) => {
|
||||
writer.write('[');
|
||||
writer.newLine();
|
||||
writer.indent(() => {
|
||||
for (const event of events) {
|
||||
writer.writeLine(`'${event}',`);
|
||||
}
|
||||
});
|
||||
writer.write('] as const');
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const generateCommonPropertiesConfig = (
|
||||
sourceFile: SourceFile,
|
||||
commonProperties: Record<string, PropertySchema>,
|
||||
): void => {
|
||||
sourceFile.addVariableStatement({
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: TYPE_NAMES.COMMON_PROPERTIES_CONFIG,
|
||||
initializer: (writer) => {
|
||||
writer.block(() => {
|
||||
for (const [name, schema] of Object.entries(commonProperties)) {
|
||||
const constructorType = schemaTypeToConstructor(schema.type);
|
||||
writer.writeLine(`'${name}': { type: ${constructorType} },`);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const generateElementPropertyType = (
|
||||
sourceFile: SourceFile,
|
||||
component: ComponentSchema,
|
||||
specificProperties: Record<string, PropertySchema>,
|
||||
): void => {
|
||||
const hasSpecificProps = Object.keys(specificProperties).length > 0;
|
||||
|
||||
if (hasSpecificProps) {
|
||||
const entries = generatePropertyEntries(specificProperties);
|
||||
sourceFile.addTypeAlias({
|
||||
isExported: true,
|
||||
name: `${component.name}Properties`,
|
||||
type: (writer) => {
|
||||
writer.write(`${TYPE_NAMES.COMMON_PROPERTIES} & `);
|
||||
writer.block(() => {
|
||||
for (const entry of entries) {
|
||||
writer.writeLine(`${entry};`);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const createPropertiesConfigWriter = (
|
||||
properties: Record<string, PropertySchema>,
|
||||
): WriterFunction => {
|
||||
return (writer) => {
|
||||
writer.write(generatePropertiesConfig(properties));
|
||||
};
|
||||
};
|
||||
|
||||
const generateElementDefinition = (
|
||||
sourceFile: SourceFile,
|
||||
component: ComponentSchema,
|
||||
specificProperties: Record<string, PropertySchema>,
|
||||
options: ElementGenerationOptions,
|
||||
): void => {
|
||||
const { useSharedEvents, useSharedPropertiesConfig } = options;
|
||||
const hasEvents = component.events.length > 0;
|
||||
const hasSpecificProps = Object.keys(specificProperties).length > 0;
|
||||
const hasProps = Object.keys(component.properties).length > 0;
|
||||
|
||||
const propsType = hasSpecificProps
|
||||
? `${component.name}Properties`
|
||||
: hasProps
|
||||
? TYPE_NAMES.COMMON_PROPERTIES
|
||||
: TYPE_NAMES.EMPTY_RECORD;
|
||||
|
||||
const eventsType = hasEvents
|
||||
? useSharedEvents
|
||||
? TYPE_NAMES.COMMON_EVENTS
|
||||
: `{ ${component.events.map((event) => `${event}(event: RemoteEvent): void`).join('; ')} }`
|
||||
: TYPE_NAMES.EMPTY_RECORD;
|
||||
|
||||
sourceFile.addVariableStatement({
|
||||
isExported: true,
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [
|
||||
{
|
||||
name: `${component.name}Element`,
|
||||
initializer: (writer) => {
|
||||
writer.write('createRemoteElement<');
|
||||
writer.newLine();
|
||||
writer.indent(() => {
|
||||
writer.writeLine(`${propsType},`);
|
||||
writer.writeLine('Record<string, never>,');
|
||||
writer.writeLine('Record<string, never>,');
|
||||
writer.write(eventsType);
|
||||
});
|
||||
writer.newLine();
|
||||
writer.write('>');
|
||||
|
||||
const hasConfig = hasProps || hasEvents;
|
||||
if (!hasConfig) {
|
||||
writer.write('({})');
|
||||
return;
|
||||
}
|
||||
|
||||
writer.write('(');
|
||||
writer.block(() => {
|
||||
if (hasProps) {
|
||||
if (hasSpecificProps) {
|
||||
writer.write('properties: ');
|
||||
writer.block(() => {
|
||||
writer.writeLine(
|
||||
`...${TYPE_NAMES.COMMON_PROPERTIES_CONFIG},`,
|
||||
);
|
||||
for (const [name, schema] of Object.entries(
|
||||
specificProperties,
|
||||
)) {
|
||||
const constructorType = schemaTypeToConstructor(
|
||||
schema.type,
|
||||
);
|
||||
writer.writeLine(
|
||||
`'${name}': { type: ${constructorType} },`,
|
||||
);
|
||||
}
|
||||
});
|
||||
writer.write(',');
|
||||
writer.newLine();
|
||||
} else if (useSharedPropertiesConfig) {
|
||||
writer.write(
|
||||
`properties: ${TYPE_NAMES.COMMON_PROPERTIES_CONFIG},`,
|
||||
);
|
||||
writer.newLine();
|
||||
} else {
|
||||
writer.write('properties: ');
|
||||
createPropertiesConfigWriter(component.properties)(writer);
|
||||
writer.write(',');
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
if (hasEvents) {
|
||||
writer.write(
|
||||
useSharedEvents
|
||||
? `events: [...${TYPE_NAMES.COMMON_EVENTS_ARRAY}],`
|
||||
: `events: [${component.events.map((event) => `'${event}'`).join(', ')}],`,
|
||||
);
|
||||
writer.newLine();
|
||||
}
|
||||
});
|
||||
writer.write(')');
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
const generateCustomElementRegistrations = (
|
||||
sourceFile: SourceFile,
|
||||
components: ComponentSchema[],
|
||||
): void => {
|
||||
for (const component of components) {
|
||||
sourceFile.addStatements(
|
||||
`customElements.define('${component.customElementName}', ${component.name}Element);`,
|
||||
);
|
||||
}
|
||||
sourceFile.addStatements(
|
||||
`customElements.define('${CUSTOM_ELEMENT_NAMES.ROOT}', ${INTERNAL_ELEMENT_CLASSES.ROOT});`,
|
||||
);
|
||||
sourceFile.addStatements(
|
||||
`customElements.define('${CUSTOM_ELEMENT_NAMES.FRAGMENT}', ${INTERNAL_ELEMENT_CLASSES.FRAGMENT});`,
|
||||
);
|
||||
};
|
||||
|
||||
const generateTagNameMapDeclaration = (
|
||||
sourceFile: SourceFile,
|
||||
components: ComponentSchema[],
|
||||
): void => {
|
||||
sourceFile.addStatements((writer) => {
|
||||
writer.writeLine('declare global {');
|
||||
writer.indent(() => {
|
||||
writer.writeLine('interface HTMLElementTagNameMap {');
|
||||
writer.indent(() => {
|
||||
for (const component of components) {
|
||||
writer.writeLine(
|
||||
`'${component.customElementName}': InstanceType<typeof ${component.name}Element>;`,
|
||||
);
|
||||
}
|
||||
writer.writeLine(
|
||||
`'${CUSTOM_ELEMENT_NAMES.ROOT}': InstanceType<typeof ${INTERNAL_ELEMENT_CLASSES.ROOT}>;`,
|
||||
);
|
||||
writer.writeLine(
|
||||
`'${CUSTOM_ELEMENT_NAMES.FRAGMENT}': InstanceType<typeof ${INTERNAL_ELEMENT_CLASSES.FRAGMENT}>;`,
|
||||
);
|
||||
});
|
||||
writer.writeLine('}');
|
||||
});
|
||||
writer.writeLine('}');
|
||||
});
|
||||
};
|
||||
|
||||
const prepareComponentsWithSpecificProps = (
|
||||
components: ComponentSchema[],
|
||||
commonPropertyNames: Set<string>,
|
||||
): ComponentWithSpecificProps[] => {
|
||||
return components.map((component) => ({
|
||||
component,
|
||||
specificProperties: getElementSpecificProperties(
|
||||
component,
|
||||
commonPropertyNames,
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
export const generateRemoteElements = (
|
||||
project: Project,
|
||||
components: ComponentSchema[],
|
||||
commonProperties: Record<string, PropertySchema>,
|
||||
commonEvents: readonly string[] = [],
|
||||
): SourceFile => {
|
||||
const sourceFile = project.createSourceFile('remote-elements.ts', '', {
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const useSharedEvents = commonEvents.length > 0;
|
||||
const useSharedPropertiesConfig = Object.keys(commonProperties).length > 0;
|
||||
|
||||
const options: ElementGenerationOptions = {
|
||||
useSharedEvents,
|
||||
useSharedPropertiesConfig,
|
||||
};
|
||||
|
||||
sourceFile.addImportDeclaration({
|
||||
moduleSpecifier: '@remote-dom/core/elements',
|
||||
namedImports: [
|
||||
'createRemoteElement',
|
||||
INTERNAL_ELEMENT_CLASSES.ROOT,
|
||||
INTERNAL_ELEMENT_CLASSES.FRAGMENT,
|
||||
{ name: 'RemoteEvent', isTypeOnly: true },
|
||||
],
|
||||
});
|
||||
|
||||
const commonPropertyNames = new Set(Object.keys(commonProperties));
|
||||
const componentsWithProps = prepareComponentsWithSpecificProps(
|
||||
components,
|
||||
commonPropertyNames,
|
||||
);
|
||||
|
||||
generateCommonPropertiesType(sourceFile, commonProperties);
|
||||
|
||||
if (useSharedEvents) {
|
||||
generateCommonEventsType(sourceFile, commonEvents);
|
||||
}
|
||||
|
||||
if (useSharedPropertiesConfig) {
|
||||
generateCommonPropertiesConfig(sourceFile, commonProperties);
|
||||
}
|
||||
|
||||
for (const { component, specificProperties } of componentsWithProps) {
|
||||
generateElementPropertyType(sourceFile, component, specificProperties);
|
||||
generateElementDefinition(
|
||||
sourceFile,
|
||||
component,
|
||||
specificProperties,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
generateCustomElementRegistrations(sourceFile, components);
|
||||
|
||||
sourceFile.addExportDeclaration({
|
||||
namedExports: [
|
||||
INTERNAL_ELEMENT_CLASSES.ROOT,
|
||||
INTERNAL_ELEMENT_CLASSES.FRAGMENT,
|
||||
],
|
||||
});
|
||||
|
||||
generateTagNameMapDeclaration(sourceFile, components);
|
||||
|
||||
addFileHeader(sourceFile);
|
||||
|
||||
return sourceFile;
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const PropertySchemaZ = z.object({
|
||||
type: z.enum(['string', 'number', 'boolean']),
|
||||
optional: z.boolean(),
|
||||
});
|
||||
|
||||
export const HtmlElementConfigZ = z.object({
|
||||
tag: z.string().regex(/^html-[a-z0-9]+$/, 'Tag must start with "html-"'),
|
||||
name: z
|
||||
.string()
|
||||
.regex(/^Html[A-Z]/, 'Name must be PascalCase starting with Html'),
|
||||
properties: z.record(z.string(), PropertySchemaZ),
|
||||
});
|
||||
|
||||
export const HtmlElementConfigArrayZ = z.array(HtmlElementConfigZ);
|
||||
|
||||
export const ComponentSchemaZ = z.object({
|
||||
name: z.string().min(1),
|
||||
tagName: z.string().min(1),
|
||||
customElementName: z.string().min(1),
|
||||
properties: z.record(z.string(), PropertySchemaZ),
|
||||
events: z.array(z.string()).readonly(),
|
||||
isHtmlElement: z.boolean(),
|
||||
htmlTag: z.string().min(1),
|
||||
});
|
||||
|
||||
export type PropertySchema = z.infer<typeof PropertySchemaZ>;
|
||||
export type HtmlElementConfig = z.infer<typeof HtmlElementConfigZ>;
|
||||
export type ComponentSchema = z.infer<typeof ComponentSchemaZ>;
|
||||
@@ -1,13 +0,0 @@
|
||||
import { type SourceFile, VariableDeclarationKind } from 'ts-morph';
|
||||
|
||||
export const addExportedConst = (
|
||||
sourceFile: SourceFile,
|
||||
name: string,
|
||||
initializer: string,
|
||||
): void => {
|
||||
sourceFile.addVariableStatement({
|
||||
isExported: true,
|
||||
declarationKind: VariableDeclarationKind.Const,
|
||||
declarations: [{ name, initializer }],
|
||||
});
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
import { type SourceFile } from 'ts-morph';
|
||||
|
||||
export const addExportedType = (
|
||||
sourceFile: SourceFile,
|
||||
name: string,
|
||||
type: string,
|
||||
): void => {
|
||||
sourceFile.addTypeAlias({
|
||||
isExported: true,
|
||||
name,
|
||||
type,
|
||||
});
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
import { type SourceFile } from 'ts-morph';
|
||||
|
||||
import { GENERATED_FILE_HEADER } from './generated-file-header';
|
||||
|
||||
export const addFileHeader = (sourceFile: SourceFile): void => {
|
||||
sourceFile.insertText(0, GENERATED_FILE_HEADER + '\n\n');
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
import { type SourceFile } from 'ts-morph';
|
||||
|
||||
export const addStatement = (
|
||||
sourceFile: SourceFile,
|
||||
statement: string,
|
||||
): void => {
|
||||
sourceFile.addStatements(statement);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
export const eventToReactProp = (eventName: string): string => {
|
||||
return `on${eventName.charAt(0).toUpperCase()}${eventName.slice(1)}`;
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
export const extractHtmlTag = (tag: string): string => {
|
||||
return tag.startsWith('html-') ? tag.slice(5) : tag;
|
||||
};
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import { type PropertySchema } from '../schemas';
|
||||
import { schemaTypeToConstructor } from './schema-type-to-constructor';
|
||||
|
||||
export const generatePropertiesConfig = (
|
||||
properties: Record<string, PropertySchema>,
|
||||
): string => {
|
||||
const entries = Object.entries(properties);
|
||||
if (entries.length === 0) {
|
||||
return '{}';
|
||||
}
|
||||
|
||||
const props = entries
|
||||
.map(([name, schema]) => {
|
||||
const constructorType = schemaTypeToConstructor(schema.type);
|
||||
return `'${name}': { type: ${constructorType} }`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
return `{ ${props} }`;
|
||||
};
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
import { type PropertySchema } from '../schemas';
|
||||
import { schemaTypeToTs } from './schema-type-to-ts';
|
||||
|
||||
export const generatePropertiesType = (
|
||||
properties: Record<string, PropertySchema>,
|
||||
): string => {
|
||||
const entries = Object.entries(properties);
|
||||
if (entries.length === 0) {
|
||||
return 'Record<string, never>';
|
||||
}
|
||||
|
||||
const props = entries
|
||||
.map(([name, schema]) => {
|
||||
const tsType = schemaTypeToTs(schema.type);
|
||||
const optional = schema.optional ? '?' : '';
|
||||
return `'${name}'${optional}: ${tsType}`;
|
||||
})
|
||||
.join('; ');
|
||||
|
||||
return `{ ${props} }`;
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
export const GENERATED_FILE_HEADER = `/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \\ \\ /\\ / / _ \\ '_ \\| __| | | | Auto-generated file
|
||||
* | | \\ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \\_/\\_/ \\___|_| |_|\\__|\\__, |
|
||||
* |___/
|
||||
*/`;
|
||||
@@ -1,11 +0,0 @@
|
||||
export { addExportedConst } from './add-exported-const';
|
||||
export { addExportedType } from './add-exported-type';
|
||||
export { addFileHeader } from './add-file-header';
|
||||
export { addStatement } from './add-statement';
|
||||
export { eventToReactProp } from './event-to-react-prop';
|
||||
export { extractHtmlTag } from './extract-html-tag';
|
||||
export { GENERATED_FILE_HEADER } from './generated-file-header';
|
||||
export { generatePropertiesConfig } from './generate-properties-config';
|
||||
export { generatePropertiesType } from './generate-properties-type';
|
||||
export { schemaTypeToConstructor } from './schema-type-to-constructor';
|
||||
export { schemaTypeToTs } from './schema-type-to-ts';
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { type PropertySchema } from '../schemas';
|
||||
|
||||
const SCHEMA_TYPE_TO_CONSTRUCTOR: Record<PropertySchema['type'], string> = {
|
||||
boolean: 'Boolean',
|
||||
number: 'Number',
|
||||
string: 'String',
|
||||
};
|
||||
|
||||
export const schemaTypeToConstructor = (type: PropertySchema['type']): string =>
|
||||
SCHEMA_TYPE_TO_CONSTRUCTOR[type];
|
||||
@@ -1,10 +0,0 @@
|
||||
import { type PropertySchema } from '../schemas';
|
||||
|
||||
const SCHEMA_TYPE_TO_TS: Record<PropertySchema['type'], string> = {
|
||||
boolean: 'boolean',
|
||||
number: 'number',
|
||||
string: 'string',
|
||||
};
|
||||
|
||||
export const schemaTypeToTs = (type: PropertySchema['type']): string =>
|
||||
SCHEMA_TYPE_TO_TS[type];
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type ApplicationVariables } from '@/sdk';
|
||||
import { type SyncableEntityOptions } from '@/application/syncableEntityOptionsType';
|
||||
import { type ApplicationVariables } from './applicationVariablesType';
|
||||
import { type SyncableEntityOptions } from './syncableEntityOptionsType';
|
||||
|
||||
export type ApplicationMarketplaceData = {
|
||||
author?: string;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import {
|
||||
type PackageJson,
|
||||
type ApplicationManifest,
|
||||
type ObjectManifest,
|
||||
type LogicFunctionManifest,
|
||||
type AssetManifest,
|
||||
type FrontComponentManifest,
|
||||
type RoleManifest,
|
||||
type FieldManifest,
|
||||
} from '@/sdk';
|
||||
import { type Sources } from '@/types';
|
||||
import { type ApplicationManifest } from './applicationType';
|
||||
import { type AssetManifest } from './assetManifestType';
|
||||
import { type FieldManifest } from './fieldManifestType';
|
||||
import { type FrontComponentManifest } from './frontComponentManifestType';
|
||||
import { type LogicFunctionManifest } from './logicFunctionManifestType';
|
||||
import { type ObjectManifest } from './objectManifestType';
|
||||
import { type PackageJson } from './packageJsonType';
|
||||
import { type RoleManifest } from './roleManifestType';
|
||||
|
||||
export type Manifest = {
|
||||
application: ApplicationManifest;
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
type PropertySchema = {
|
||||
type: 'string' | 'number' | 'boolean';
|
||||
optional: boolean;
|
||||
};
|
||||
|
||||
export type AllowedHtmlElement = {
|
||||
tag: string;
|
||||
name: string;
|
||||
properties: Record<string, PropertySchema>;
|
||||
};
|
||||
|
||||
export const ALLOWED_HTML_ELEMENTS: AllowedHtmlElement[] = [
|
||||
{ tag: 'html-div', name: 'HtmlDiv', properties: {} },
|
||||
{ tag: 'html-span', name: 'HtmlSpan', properties: {} },
|
||||
{ tag: 'html-section', name: 'HtmlSection', properties: {} },
|
||||
{ tag: 'html-article', name: 'HtmlArticle', properties: {} },
|
||||
{ tag: 'html-header', name: 'HtmlHeader', properties: {} },
|
||||
{ tag: 'html-footer', name: 'HtmlFooter', properties: {} },
|
||||
{ tag: 'html-main', name: 'HtmlMain', properties: {} },
|
||||
{ tag: 'html-nav', name: 'HtmlNav', properties: {} },
|
||||
{ tag: 'html-aside', name: 'HtmlAside', properties: {} },
|
||||
{ tag: 'html-p', name: 'HtmlP', properties: {} },
|
||||
{ tag: 'html-h1', name: 'HtmlH1', properties: {} },
|
||||
{ tag: 'html-h2', name: 'HtmlH2', properties: {} },
|
||||
{ tag: 'html-h3', name: 'HtmlH3', properties: {} },
|
||||
{ tag: 'html-h4', name: 'HtmlH4', properties: {} },
|
||||
{ tag: 'html-h5', name: 'HtmlH5', properties: {} },
|
||||
{ tag: 'html-h6', name: 'HtmlH6', properties: {} },
|
||||
{ tag: 'html-strong', name: 'HtmlStrong', properties: {} },
|
||||
{ tag: 'html-em', name: 'HtmlEm', properties: {} },
|
||||
{ tag: 'html-small', name: 'HtmlSmall', properties: {} },
|
||||
{ tag: 'html-code', name: 'HtmlCode', properties: {} },
|
||||
{ tag: 'html-pre', name: 'HtmlPre', properties: {} },
|
||||
{ tag: 'html-blockquote', name: 'HtmlBlockquote', properties: {} },
|
||||
{
|
||||
tag: 'html-a',
|
||||
name: 'HtmlA',
|
||||
properties: {
|
||||
href: { type: 'string', optional: true },
|
||||
target: { type: 'string', optional: true },
|
||||
rel: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-img',
|
||||
name: 'HtmlImg',
|
||||
properties: {
|
||||
src: { type: 'string', optional: true },
|
||||
alt: { type: 'string', optional: true },
|
||||
width: { type: 'number', optional: true },
|
||||
height: { type: 'number', optional: true },
|
||||
},
|
||||
},
|
||||
{ tag: 'html-ul', name: 'HtmlUl', properties: {} },
|
||||
{ tag: 'html-ol', name: 'HtmlOl', properties: {} },
|
||||
{ tag: 'html-li', name: 'HtmlLi', properties: {} },
|
||||
{
|
||||
tag: 'html-form',
|
||||
name: 'HtmlForm',
|
||||
properties: {
|
||||
action: { type: 'string', optional: true },
|
||||
method: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-label',
|
||||
name: 'HtmlLabel',
|
||||
properties: {
|
||||
htmlFor: { type: 'string', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-input',
|
||||
name: 'HtmlInput',
|
||||
properties: {
|
||||
type: { type: 'string', optional: true },
|
||||
name: { type: 'string', optional: true },
|
||||
value: { type: 'string', optional: true },
|
||||
placeholder: { type: 'string', optional: true },
|
||||
disabled: { type: 'boolean', optional: true },
|
||||
checked: { type: 'boolean', optional: true },
|
||||
readOnly: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-textarea',
|
||||
name: 'HtmlTextarea',
|
||||
properties: {
|
||||
name: { type: 'string', optional: true },
|
||||
value: { type: 'string', optional: true },
|
||||
placeholder: { type: 'string', optional: true },
|
||||
disabled: { type: 'boolean', optional: true },
|
||||
readOnly: { type: 'boolean', optional: true },
|
||||
rows: { type: 'number', optional: true },
|
||||
cols: { type: 'number', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-select',
|
||||
name: 'HtmlSelect',
|
||||
properties: {
|
||||
name: { type: 'string', optional: true },
|
||||
value: { type: 'string', optional: true },
|
||||
disabled: { type: 'boolean', optional: true },
|
||||
multiple: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-option',
|
||||
name: 'HtmlOption',
|
||||
properties: {
|
||||
value: { type: 'string', optional: true },
|
||||
disabled: { type: 'boolean', optional: true },
|
||||
selected: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-button',
|
||||
name: 'HtmlButton',
|
||||
properties: {
|
||||
type: { type: 'string', optional: true },
|
||||
disabled: { type: 'boolean', optional: true },
|
||||
},
|
||||
},
|
||||
{ tag: 'html-table', name: 'HtmlTable', properties: {} },
|
||||
{ tag: 'html-thead', name: 'HtmlThead', properties: {} },
|
||||
{ tag: 'html-tbody', name: 'HtmlTbody', properties: {} },
|
||||
{ tag: 'html-tfoot', name: 'HtmlTfoot', properties: {} },
|
||||
{ tag: 'html-tr', name: 'HtmlTr', properties: {} },
|
||||
{
|
||||
tag: 'html-th',
|
||||
name: 'HtmlTh',
|
||||
properties: {
|
||||
colSpan: { type: 'number', optional: true },
|
||||
rowSpan: { type: 'number', optional: true },
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: 'html-td',
|
||||
name: 'HtmlTd',
|
||||
properties: {
|
||||
colSpan: { type: 'number', optional: true },
|
||||
rowSpan: { type: 'number', optional: true },
|
||||
},
|
||||
},
|
||||
{ tag: 'html-br', name: 'HtmlBr', properties: {} },
|
||||
{ tag: 'html-hr', name: 'HtmlHr', properties: {} },
|
||||
];
|
||||
@@ -1,22 +0,0 @@
|
||||
export const COMMON_HTML_EVENTS = [
|
||||
'click',
|
||||
'dblclick',
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
'keydown',
|
||||
'keyup',
|
||||
'keypress',
|
||||
'focus',
|
||||
'blur',
|
||||
'change',
|
||||
'input',
|
||||
'submit',
|
||||
'scroll',
|
||||
'wheel',
|
||||
'contextmenu',
|
||||
'drag',
|
||||
] as const;
|
||||
@@ -1,22 +0,0 @@
|
||||
export const EVENT_TO_REACT: Record<string, string> = {
|
||||
click: 'onClick',
|
||||
dblclick: 'onDoubleClick',
|
||||
mousedown: 'onMouseDown',
|
||||
mouseup: 'onMouseUp',
|
||||
mouseover: 'onMouseOver',
|
||||
mouseout: 'onMouseOut',
|
||||
mouseenter: 'onMouseEnter',
|
||||
mouseleave: 'onMouseLeave',
|
||||
keydown: 'onKeyDown',
|
||||
keyup: 'onKeyUp',
|
||||
keypress: 'onKeyPress',
|
||||
focus: 'onFocus',
|
||||
blur: 'onBlur',
|
||||
change: 'onChange',
|
||||
input: 'onInput',
|
||||
submit: 'onSubmit',
|
||||
scroll: 'onScroll',
|
||||
wheel: 'onWheel',
|
||||
contextmenu: 'onContextMenu',
|
||||
drag: 'onDrag',
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
import { type PropertySchema } from '../front-component/types/PropertySchema';
|
||||
|
||||
export const HTML_COMMON_PROPERTIES: Record<string, PropertySchema> = {
|
||||
id: { type: 'string', optional: true },
|
||||
className: { type: 'string', optional: true },
|
||||
style: { type: 'string', optional: true },
|
||||
title: { type: 'string', optional: true },
|
||||
tabIndex: { type: 'number', optional: true },
|
||||
role: { type: 'string', optional: true },
|
||||
'aria-label': { type: 'string', optional: true },
|
||||
'aria-hidden': { type: 'boolean', optional: true },
|
||||
'data-testid': { type: 'string', optional: true },
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { ALLOWED_HTML_ELEMENTS } from './AllowedHtmlElements';
|
||||
|
||||
export const HTML_TAG_TO_REMOTE_COMPONENT: Record<string, string> =
|
||||
Object.fromEntries(
|
||||
ALLOWED_HTML_ELEMENTS.map((element) => [
|
||||
element.tag.startsWith('html-') ? element.tag.slice(5) : element.tag,
|
||||
element.name,
|
||||
]),
|
||||
);
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export type { AllowedHtmlElement } from './AllowedHtmlElements';
|
||||
export { ALLOWED_HTML_ELEMENTS } from './AllowedHtmlElements';
|
||||
export { COMMON_HTML_EVENTS } from './CommonHtmlEvents';
|
||||
export { EVENT_TO_REACT } from './EventToReact';
|
||||
export { HTML_COMMON_PROPERTIES } from './HtmlCommonProperties';
|
||||
export { HTML_TAG_TO_REMOTE_COMPONENT } from './HtmlTagToRemoteComponent';
|
||||
@@ -1,37 +0,0 @@
|
||||
import {
|
||||
type RemoteReceiver,
|
||||
RemoteRootRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
import React, { useState } from 'react';
|
||||
import { isDefined } from '../../../utils/validation/isDefined';
|
||||
import { FrontComponentWorkerEffect } from '../../remote/components/FrontComponentWorkerEffect';
|
||||
import { componentRegistry } from '../generated/host-component-registry';
|
||||
|
||||
type FrontComponentContentProps = {
|
||||
componentUrl: string;
|
||||
onError: (error?: Error) => void;
|
||||
};
|
||||
|
||||
export const FrontComponentRenderer = ({
|
||||
componentUrl,
|
||||
onError,
|
||||
}: FrontComponentContentProps) => {
|
||||
const [receiver, setReceiver] = useState<RemoteReceiver | null>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FrontComponentWorkerEffect
|
||||
componentUrl={componentUrl}
|
||||
setReceiver={setReceiver}
|
||||
onError={onError}
|
||||
/>
|
||||
|
||||
{isDefined(receiver) && (
|
||||
<RemoteRootRenderer
|
||||
receiver={receiver}
|
||||
components={componentRegistry}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
RemoteFragmentRenderer,
|
||||
createRemoteComponentRenderer,
|
||||
} from '@remote-dom/react/host';
|
||||
const INTERNAL_PROPS = new Set(['element', 'receiver', 'components']);
|
||||
|
||||
const EVENT_NAME_MAP: Record<string, string> = {
|
||||
onclick: 'onClick',
|
||||
ondblclick: 'onDoubleClick',
|
||||
onmousedown: 'onMouseDown',
|
||||
onmouseup: 'onMouseUp',
|
||||
onmouseover: 'onMouseOver',
|
||||
onmouseout: 'onMouseOut',
|
||||
onmouseenter: 'onMouseEnter',
|
||||
onmouseleave: 'onMouseLeave',
|
||||
onkeydown: 'onKeyDown',
|
||||
onkeyup: 'onKeyUp',
|
||||
onkeypress: 'onKeyPress',
|
||||
onfocus: 'onFocus',
|
||||
onblur: 'onBlur',
|
||||
onchange: 'onChange',
|
||||
oninput: 'onInput',
|
||||
onsubmit: 'onSubmit',
|
||||
onscroll: 'onScroll',
|
||||
onwheel: 'onWheel',
|
||||
oncontextmenu: 'onContextMenu',
|
||||
ondrag: 'onDrag',
|
||||
};
|
||||
|
||||
const parseStyle = (
|
||||
styleString: string | undefined,
|
||||
): React.CSSProperties | undefined => {
|
||||
if (!styleString || typeof styleString !== 'string') {
|
||||
return styleString as React.CSSProperties | undefined;
|
||||
}
|
||||
|
||||
const style: Record<string, string> = {};
|
||||
const declarations = styleString.split(';').filter(Boolean);
|
||||
|
||||
for (const declaration of declarations) {
|
||||
const colonIndex = declaration.indexOf(':');
|
||||
if (colonIndex === -1) continue;
|
||||
|
||||
const property = declaration.slice(0, colonIndex).trim();
|
||||
const value = declaration.slice(colonIndex + 1).trim();
|
||||
|
||||
const camelProperty = property.replace(/-([a-z])/g, (_, letter: string) =>
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
style[camelProperty] = value;
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
const wrapEventHandler = (handler: () => void) => {
|
||||
return (_event: unknown) => {
|
||||
handler();
|
||||
};
|
||||
};
|
||||
|
||||
const filterProps = (props: Record<string, unknown>) => {
|
||||
const filtered: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (INTERNAL_PROPS.has(key) || value === undefined) continue;
|
||||
|
||||
if (key === 'style') {
|
||||
filtered.style = parseStyle(value as string | undefined);
|
||||
} else {
|
||||
const normalizedKey = EVENT_NAME_MAP[key.toLowerCase()] || key;
|
||||
if (normalizedKey.startsWith('on') && typeof value === 'function') {
|
||||
filtered[normalizedKey] = wrapEventHandler(value as () => void);
|
||||
} else {
|
||||
filtered[normalizedKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
};
|
||||
const HtmlDivWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('div', filterProps(props), children);
|
||||
};
|
||||
const HtmlSpanWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('span', filterProps(props), children);
|
||||
};
|
||||
const HtmlSectionWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('section', filterProps(props), children);
|
||||
};
|
||||
const HtmlArticleWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('article', filterProps(props), children);
|
||||
};
|
||||
const HtmlHeaderWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('header', filterProps(props), children);
|
||||
};
|
||||
const HtmlFooterWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('footer', filterProps(props), children);
|
||||
};
|
||||
const HtmlMainWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('main', filterProps(props), children);
|
||||
};
|
||||
const HtmlNavWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('nav', filterProps(props), children);
|
||||
};
|
||||
const HtmlAsideWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('aside', filterProps(props), children);
|
||||
};
|
||||
const HtmlPWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('p', filterProps(props), children);
|
||||
};
|
||||
const HtmlH1Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h1', filterProps(props), children);
|
||||
};
|
||||
const HtmlH2Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h2', filterProps(props), children);
|
||||
};
|
||||
const HtmlH3Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h3', filterProps(props), children);
|
||||
};
|
||||
const HtmlH4Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h4', filterProps(props), children);
|
||||
};
|
||||
const HtmlH5Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h5', filterProps(props), children);
|
||||
};
|
||||
const HtmlH6Wrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('h6', filterProps(props), children);
|
||||
};
|
||||
const HtmlStrongWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('strong', filterProps(props), children);
|
||||
};
|
||||
const HtmlEmWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('em', filterProps(props), children);
|
||||
};
|
||||
const HtmlSmallWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('small', filterProps(props), children);
|
||||
};
|
||||
const HtmlCodeWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('code', filterProps(props), children);
|
||||
};
|
||||
const HtmlPreWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('pre', filterProps(props), children);
|
||||
};
|
||||
const HtmlBlockquoteWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('blockquote', filterProps(props), children);
|
||||
};
|
||||
const HtmlAWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('a', filterProps(props), children);
|
||||
};
|
||||
const HtmlImgWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('img', filterProps(props), children);
|
||||
};
|
||||
const HtmlUlWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('ul', filterProps(props), children);
|
||||
};
|
||||
const HtmlOlWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('ol', filterProps(props), children);
|
||||
};
|
||||
const HtmlLiWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('li', filterProps(props), children);
|
||||
};
|
||||
const HtmlFormWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('form', filterProps(props), children);
|
||||
};
|
||||
const HtmlLabelWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('label', filterProps(props), children);
|
||||
};
|
||||
const HtmlInputWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('input', filterProps(props), children);
|
||||
};
|
||||
const HtmlTextareaWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('textarea', filterProps(props), children);
|
||||
};
|
||||
const HtmlSelectWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('select', filterProps(props), children);
|
||||
};
|
||||
const HtmlOptionWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('option', filterProps(props), children);
|
||||
};
|
||||
const HtmlButtonWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('button', filterProps(props), children);
|
||||
};
|
||||
const HtmlTableWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('table', filterProps(props), children);
|
||||
};
|
||||
const HtmlTheadWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('thead', filterProps(props), children);
|
||||
};
|
||||
const HtmlTbodyWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tbody', filterProps(props), children);
|
||||
};
|
||||
const HtmlTfootWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tfoot', filterProps(props), children);
|
||||
};
|
||||
const HtmlTrWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('tr', filterProps(props), children);
|
||||
};
|
||||
const HtmlThWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('th', filterProps(props), children);
|
||||
};
|
||||
const HtmlTdWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('td', filterProps(props), children);
|
||||
};
|
||||
const HtmlBrWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('br', filterProps(props), children);
|
||||
};
|
||||
const HtmlHrWrapper = ({
|
||||
children,
|
||||
...props
|
||||
}: { children?: React.ReactNode } & Record<string, unknown>) => {
|
||||
return React.createElement('hr', filterProps(props), children);
|
||||
};
|
||||
export const componentRegistry = new Map<
|
||||
string,
|
||||
ReturnType<typeof createRemoteComponentRenderer>
|
||||
>([
|
||||
['html-div', createRemoteComponentRenderer(HtmlDivWrapper)],
|
||||
['html-span', createRemoteComponentRenderer(HtmlSpanWrapper)],
|
||||
['html-section', createRemoteComponentRenderer(HtmlSectionWrapper)],
|
||||
['html-article', createRemoteComponentRenderer(HtmlArticleWrapper)],
|
||||
['html-header', createRemoteComponentRenderer(HtmlHeaderWrapper)],
|
||||
['html-footer', createRemoteComponentRenderer(HtmlFooterWrapper)],
|
||||
['html-main', createRemoteComponentRenderer(HtmlMainWrapper)],
|
||||
['html-nav', createRemoteComponentRenderer(HtmlNavWrapper)],
|
||||
['html-aside', createRemoteComponentRenderer(HtmlAsideWrapper)],
|
||||
['html-p', createRemoteComponentRenderer(HtmlPWrapper)],
|
||||
['html-h1', createRemoteComponentRenderer(HtmlH1Wrapper)],
|
||||
['html-h2', createRemoteComponentRenderer(HtmlH2Wrapper)],
|
||||
['html-h3', createRemoteComponentRenderer(HtmlH3Wrapper)],
|
||||
['html-h4', createRemoteComponentRenderer(HtmlH4Wrapper)],
|
||||
['html-h5', createRemoteComponentRenderer(HtmlH5Wrapper)],
|
||||
['html-h6', createRemoteComponentRenderer(HtmlH6Wrapper)],
|
||||
['html-strong', createRemoteComponentRenderer(HtmlStrongWrapper)],
|
||||
['html-em', createRemoteComponentRenderer(HtmlEmWrapper)],
|
||||
['html-small', createRemoteComponentRenderer(HtmlSmallWrapper)],
|
||||
['html-code', createRemoteComponentRenderer(HtmlCodeWrapper)],
|
||||
['html-pre', createRemoteComponentRenderer(HtmlPreWrapper)],
|
||||
['html-blockquote', createRemoteComponentRenderer(HtmlBlockquoteWrapper)],
|
||||
['html-a', createRemoteComponentRenderer(HtmlAWrapper)],
|
||||
['html-img', createRemoteComponentRenderer(HtmlImgWrapper)],
|
||||
['html-ul', createRemoteComponentRenderer(HtmlUlWrapper)],
|
||||
['html-ol', createRemoteComponentRenderer(HtmlOlWrapper)],
|
||||
['html-li', createRemoteComponentRenderer(HtmlLiWrapper)],
|
||||
['html-form', createRemoteComponentRenderer(HtmlFormWrapper)],
|
||||
['html-label', createRemoteComponentRenderer(HtmlLabelWrapper)],
|
||||
['html-input', createRemoteComponentRenderer(HtmlInputWrapper)],
|
||||
['html-textarea', createRemoteComponentRenderer(HtmlTextareaWrapper)],
|
||||
['html-select', createRemoteComponentRenderer(HtmlSelectWrapper)],
|
||||
['html-option', createRemoteComponentRenderer(HtmlOptionWrapper)],
|
||||
['html-button', createRemoteComponentRenderer(HtmlButtonWrapper)],
|
||||
['html-table', createRemoteComponentRenderer(HtmlTableWrapper)],
|
||||
['html-thead', createRemoteComponentRenderer(HtmlTheadWrapper)],
|
||||
['html-tbody', createRemoteComponentRenderer(HtmlTbodyWrapper)],
|
||||
['html-tfoot', createRemoteComponentRenderer(HtmlTfootWrapper)],
|
||||
['html-tr', createRemoteComponentRenderer(HtmlTrWrapper)],
|
||||
['html-th', createRemoteComponentRenderer(HtmlThWrapper)],
|
||||
['html-td', createRemoteComponentRenderer(HtmlTdWrapper)],
|
||||
['html-br', createRemoteComponentRenderer(HtmlBrWrapper)],
|
||||
['html-hr', createRemoteComponentRenderer(HtmlHrWrapper)],
|
||||
['remote-fragment', RemoteFragmentRenderer],
|
||||
]);
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { componentRegistry } from './host-component-registry';
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { FrontComponentRenderer } from './host/components/FrontComponentRenderer';
|
||||
export { componentRegistry } from './host/generated/host-component-registry';
|
||||
export { FrontComponentWorkerEffect } from './remote/components/FrontComponentWorkerEffect';
|
||||
export {
|
||||
HtmlDiv,
|
||||
HtmlSpan,
|
||||
HtmlSection,
|
||||
HtmlArticle,
|
||||
HtmlHeader,
|
||||
HtmlFooter,
|
||||
HtmlMain,
|
||||
HtmlNav,
|
||||
HtmlAside,
|
||||
HtmlP,
|
||||
HtmlH1,
|
||||
HtmlH2,
|
||||
HtmlH3,
|
||||
HtmlH4,
|
||||
HtmlH5,
|
||||
HtmlH6,
|
||||
HtmlStrong,
|
||||
HtmlEm,
|
||||
HtmlSmall,
|
||||
HtmlCode,
|
||||
HtmlPre,
|
||||
HtmlBlockquote,
|
||||
HtmlA,
|
||||
HtmlImg,
|
||||
HtmlUl,
|
||||
HtmlOl,
|
||||
HtmlLi,
|
||||
HtmlForm,
|
||||
HtmlLabel,
|
||||
HtmlInput,
|
||||
HtmlTextarea,
|
||||
HtmlSelect,
|
||||
HtmlOption,
|
||||
HtmlButton,
|
||||
HtmlTable,
|
||||
HtmlThead,
|
||||
HtmlTbody,
|
||||
HtmlTfoot,
|
||||
HtmlTr,
|
||||
HtmlTh,
|
||||
HtmlTd,
|
||||
HtmlBr,
|
||||
HtmlHr,
|
||||
} from './remote/generated/remote-components';
|
||||
export type {
|
||||
HtmlCommonProperties,
|
||||
HtmlCommonEvents,
|
||||
HtmlAProperties,
|
||||
HtmlImgProperties,
|
||||
HtmlFormProperties,
|
||||
HtmlLabelProperties,
|
||||
HtmlInputProperties,
|
||||
HtmlTextareaProperties,
|
||||
HtmlSelectProperties,
|
||||
HtmlOptionProperties,
|
||||
HtmlButtonProperties,
|
||||
HtmlThProperties,
|
||||
HtmlTdProperties,
|
||||
} from './remote/generated/remote-elements';
|
||||
export {
|
||||
HtmlDivElement,
|
||||
HtmlSpanElement,
|
||||
HtmlSectionElement,
|
||||
HtmlArticleElement,
|
||||
HtmlHeaderElement,
|
||||
HtmlFooterElement,
|
||||
HtmlMainElement,
|
||||
HtmlNavElement,
|
||||
HtmlAsideElement,
|
||||
HtmlPElement,
|
||||
HtmlH1Element,
|
||||
HtmlH2Element,
|
||||
HtmlH3Element,
|
||||
HtmlH4Element,
|
||||
HtmlH5Element,
|
||||
HtmlH6Element,
|
||||
HtmlStrongElement,
|
||||
HtmlEmElement,
|
||||
HtmlSmallElement,
|
||||
HtmlCodeElement,
|
||||
HtmlPreElement,
|
||||
HtmlBlockquoteElement,
|
||||
HtmlAElement,
|
||||
HtmlImgElement,
|
||||
HtmlUlElement,
|
||||
HtmlOlElement,
|
||||
HtmlLiElement,
|
||||
HtmlFormElement,
|
||||
HtmlLabelElement,
|
||||
HtmlInputElement,
|
||||
HtmlTextareaElement,
|
||||
HtmlSelectElement,
|
||||
HtmlOptionElement,
|
||||
HtmlButtonElement,
|
||||
HtmlTableElement,
|
||||
HtmlTheadElement,
|
||||
HtmlTbodyElement,
|
||||
HtmlTfootElement,
|
||||
HtmlTrElement,
|
||||
HtmlThElement,
|
||||
HtmlTdElement,
|
||||
HtmlBrElement,
|
||||
HtmlHrElement,
|
||||
RemoteRootElement,
|
||||
RemoteFragmentElement,
|
||||
} from './remote/generated/remote-elements';
|
||||
export { createRemoteWorker } from './remote/worker/createRemoteWorker';
|
||||
export type { HostToWorkerRenderContext } from './types/HostToWorkerRenderContext';
|
||||
export type { PropertySchema } from './types/PropertySchema';
|
||||
export type { WorkerExports } from './types/WorkerExports';
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
import { ThreadWebWorker, release, retain } from '@quilted/threads';
|
||||
import { RemoteReceiver } from '@remote-dom/core/receivers';
|
||||
import { useEffect } from 'react';
|
||||
import { type WorkerExports } from '../../types/WorkerExports';
|
||||
import { createRemoteWorker } from '../worker/createRemoteWorker';
|
||||
|
||||
type FrontComponentWorkerEffectProps = {
|
||||
componentUrl: string;
|
||||
setReceiver: React.Dispatch<React.SetStateAction<RemoteReceiver | null>>;
|
||||
onError: (error?: Error) => void;
|
||||
};
|
||||
|
||||
export const FrontComponentWorkerEffect = ({
|
||||
componentUrl,
|
||||
setReceiver,
|
||||
onError,
|
||||
}: FrontComponentWorkerEffectProps) => {
|
||||
useEffect(() => {
|
||||
const newReceiver = new RemoteReceiver({ retain, release });
|
||||
|
||||
const worker = createRemoteWorker();
|
||||
|
||||
worker.onerror = (event: ErrorEvent) => {
|
||||
onError(event.error);
|
||||
};
|
||||
|
||||
const thread = new ThreadWebWorker<WorkerExports>(worker);
|
||||
|
||||
thread.imports
|
||||
.render(newReceiver.connection, { componentUrl })
|
||||
.catch((error: Error) => {
|
||||
onError(error);
|
||||
});
|
||||
|
||||
setReceiver(newReceiver);
|
||||
|
||||
return () => {
|
||||
worker.terminate();
|
||||
};
|
||||
}, [componentUrl, onError, setReceiver]);
|
||||
|
||||
return null;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,706 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
import {
|
||||
createRemoteElement,
|
||||
RemoteRootElement,
|
||||
RemoteFragmentElement,
|
||||
type RemoteEvent,
|
||||
} from '@remote-dom/core/elements';
|
||||
|
||||
export type HtmlCommonProperties = {
|
||||
id?: string;
|
||||
className?: string;
|
||||
style?: string;
|
||||
title?: string;
|
||||
tabIndex?: number;
|
||||
role?: string;
|
||||
'aria-label'?: string;
|
||||
'aria-hidden'?: boolean;
|
||||
'data-testid'?: string;
|
||||
};
|
||||
export type HtmlCommonEvents = {
|
||||
click(event: RemoteEvent): void;
|
||||
dblclick(event: RemoteEvent): void;
|
||||
mousedown(event: RemoteEvent): void;
|
||||
mouseup(event: RemoteEvent): void;
|
||||
mouseover(event: RemoteEvent): void;
|
||||
mouseout(event: RemoteEvent): void;
|
||||
mouseenter(event: RemoteEvent): void;
|
||||
mouseleave(event: RemoteEvent): void;
|
||||
keydown(event: RemoteEvent): void;
|
||||
keyup(event: RemoteEvent): void;
|
||||
keypress(event: RemoteEvent): void;
|
||||
focus(event: RemoteEvent): void;
|
||||
blur(event: RemoteEvent): void;
|
||||
change(event: RemoteEvent): void;
|
||||
input(event: RemoteEvent): void;
|
||||
submit(event: RemoteEvent): void;
|
||||
scroll(event: RemoteEvent): void;
|
||||
wheel(event: RemoteEvent): void;
|
||||
contextmenu(event: RemoteEvent): void;
|
||||
drag(event: RemoteEvent): void;
|
||||
};
|
||||
|
||||
const HTML_COMMON_EVENTS_ARRAY = [
|
||||
'click',
|
||||
'dblclick',
|
||||
'mousedown',
|
||||
'mouseup',
|
||||
'mouseover',
|
||||
'mouseout',
|
||||
'mouseenter',
|
||||
'mouseleave',
|
||||
'keydown',
|
||||
'keyup',
|
||||
'keypress',
|
||||
'focus',
|
||||
'blur',
|
||||
'change',
|
||||
'input',
|
||||
'submit',
|
||||
'scroll',
|
||||
'wheel',
|
||||
'contextmenu',
|
||||
'drag',
|
||||
] as const;
|
||||
const HTML_COMMON_PROPERTIES_CONFIG = {
|
||||
id: { type: String },
|
||||
className: { type: String },
|
||||
style: { type: String },
|
||||
title: { type: String },
|
||||
tabIndex: { type: Number },
|
||||
role: { type: String },
|
||||
'aria-label': { type: String },
|
||||
'aria-hidden': { type: Boolean },
|
||||
'data-testid': { type: String },
|
||||
};
|
||||
export const HtmlDivElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSpanElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSectionElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlArticleElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlHeaderElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlFooterElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlMainElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlNavElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlAsideElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlPElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH1Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH2Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH3Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH4Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH5Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlH6Element = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlStrongElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlEmElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlSmallElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlCodeElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlPreElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlBlockquoteElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlAProperties = HtmlCommonProperties & {
|
||||
href?: string;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
};
|
||||
|
||||
export const HtmlAElement = createRemoteElement<
|
||||
HtmlAProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
href: { type: String },
|
||||
target: { type: String },
|
||||
rel: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlImgProperties = HtmlCommonProperties & {
|
||||
src?: string;
|
||||
alt?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
export const HtmlImgElement = createRemoteElement<
|
||||
HtmlImgProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
src: { type: String },
|
||||
alt: { type: String },
|
||||
width: { type: Number },
|
||||
height: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlUlElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlOlElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlLiElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlFormProperties = HtmlCommonProperties & {
|
||||
action?: string;
|
||||
method?: string;
|
||||
};
|
||||
|
||||
export const HtmlFormElement = createRemoteElement<
|
||||
HtmlFormProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
action: { type: String },
|
||||
method: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlLabelProperties = HtmlCommonProperties & {
|
||||
htmlFor?: string;
|
||||
};
|
||||
|
||||
export const HtmlLabelElement = createRemoteElement<
|
||||
HtmlLabelProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
htmlFor: { type: String },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlInputProperties = HtmlCommonProperties & {
|
||||
type?: string;
|
||||
name?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
readOnly?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlInputElement = createRemoteElement<
|
||||
HtmlInputProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
type: { type: String },
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
placeholder: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
checked: { type: Boolean },
|
||||
readOnly: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlTextareaProperties = HtmlCommonProperties & {
|
||||
name?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
readOnly?: boolean;
|
||||
rows?: number;
|
||||
cols?: number;
|
||||
};
|
||||
|
||||
export const HtmlTextareaElement = createRemoteElement<
|
||||
HtmlTextareaProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
placeholder: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
readOnly: { type: Boolean },
|
||||
rows: { type: Number },
|
||||
cols: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlSelectProperties = HtmlCommonProperties & {
|
||||
name?: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
multiple?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlSelectElement = createRemoteElement<
|
||||
HtmlSelectProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
name: { type: String },
|
||||
value: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
multiple: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlOptionProperties = HtmlCommonProperties & {
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlOptionElement = createRemoteElement<
|
||||
HtmlOptionProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
value: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
selected: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlButtonProperties = HtmlCommonProperties & {
|
||||
type?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const HtmlButtonElement = createRemoteElement<
|
||||
HtmlButtonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
type: { type: String },
|
||||
disabled: { type: Boolean },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTableElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTheadElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTbodyElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTfootElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlTrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlThProperties = HtmlCommonProperties & {
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
};
|
||||
|
||||
export const HtmlThElement = createRemoteElement<
|
||||
HtmlThProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
colSpan: { type: Number },
|
||||
rowSpan: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
|
||||
export type HtmlTdProperties = HtmlCommonProperties & {
|
||||
colSpan?: number;
|
||||
rowSpan?: number;
|
||||
};
|
||||
|
||||
export const HtmlTdElement = createRemoteElement<
|
||||
HtmlTdProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: {
|
||||
...HTML_COMMON_PROPERTIES_CONFIG,
|
||||
colSpan: { type: Number },
|
||||
rowSpan: { type: Number },
|
||||
},
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlBrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
export const HtmlHrElement = createRemoteElement<
|
||||
HtmlCommonProperties,
|
||||
Record<string, never>,
|
||||
Record<string, never>,
|
||||
HtmlCommonEvents
|
||||
>({
|
||||
properties: HTML_COMMON_PROPERTIES_CONFIG,
|
||||
events: [...HTML_COMMON_EVENTS_ARRAY],
|
||||
});
|
||||
customElements.define('html-div', HtmlDivElement);
|
||||
customElements.define('html-span', HtmlSpanElement);
|
||||
customElements.define('html-section', HtmlSectionElement);
|
||||
customElements.define('html-article', HtmlArticleElement);
|
||||
customElements.define('html-header', HtmlHeaderElement);
|
||||
customElements.define('html-footer', HtmlFooterElement);
|
||||
customElements.define('html-main', HtmlMainElement);
|
||||
customElements.define('html-nav', HtmlNavElement);
|
||||
customElements.define('html-aside', HtmlAsideElement);
|
||||
customElements.define('html-p', HtmlPElement);
|
||||
customElements.define('html-h1', HtmlH1Element);
|
||||
customElements.define('html-h2', HtmlH2Element);
|
||||
customElements.define('html-h3', HtmlH3Element);
|
||||
customElements.define('html-h4', HtmlH4Element);
|
||||
customElements.define('html-h5', HtmlH5Element);
|
||||
customElements.define('html-h6', HtmlH6Element);
|
||||
customElements.define('html-strong', HtmlStrongElement);
|
||||
customElements.define('html-em', HtmlEmElement);
|
||||
customElements.define('html-small', HtmlSmallElement);
|
||||
customElements.define('html-code', HtmlCodeElement);
|
||||
customElements.define('html-pre', HtmlPreElement);
|
||||
customElements.define('html-blockquote', HtmlBlockquoteElement);
|
||||
customElements.define('html-a', HtmlAElement);
|
||||
customElements.define('html-img', HtmlImgElement);
|
||||
customElements.define('html-ul', HtmlUlElement);
|
||||
customElements.define('html-ol', HtmlOlElement);
|
||||
customElements.define('html-li', HtmlLiElement);
|
||||
customElements.define('html-form', HtmlFormElement);
|
||||
customElements.define('html-label', HtmlLabelElement);
|
||||
customElements.define('html-input', HtmlInputElement);
|
||||
customElements.define('html-textarea', HtmlTextareaElement);
|
||||
customElements.define('html-select', HtmlSelectElement);
|
||||
customElements.define('html-option', HtmlOptionElement);
|
||||
customElements.define('html-button', HtmlButtonElement);
|
||||
customElements.define('html-table', HtmlTableElement);
|
||||
customElements.define('html-thead', HtmlTheadElement);
|
||||
customElements.define('html-tbody', HtmlTbodyElement);
|
||||
customElements.define('html-tfoot', HtmlTfootElement);
|
||||
customElements.define('html-tr', HtmlTrElement);
|
||||
customElements.define('html-th', HtmlThElement);
|
||||
customElements.define('html-td', HtmlTdElement);
|
||||
customElements.define('html-br', HtmlBrElement);
|
||||
customElements.define('html-hr', HtmlHrElement);
|
||||
customElements.define('remote-root', RemoteRootElement);
|
||||
customElements.define('remote-fragment', RemoteFragmentElement);
|
||||
|
||||
export { RemoteRootElement, RemoteFragmentElement };
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'html-div': InstanceType<typeof HtmlDivElement>;
|
||||
'html-span': InstanceType<typeof HtmlSpanElement>;
|
||||
'html-section': InstanceType<typeof HtmlSectionElement>;
|
||||
'html-article': InstanceType<typeof HtmlArticleElement>;
|
||||
'html-header': InstanceType<typeof HtmlHeaderElement>;
|
||||
'html-footer': InstanceType<typeof HtmlFooterElement>;
|
||||
'html-main': InstanceType<typeof HtmlMainElement>;
|
||||
'html-nav': InstanceType<typeof HtmlNavElement>;
|
||||
'html-aside': InstanceType<typeof HtmlAsideElement>;
|
||||
'html-p': InstanceType<typeof HtmlPElement>;
|
||||
'html-h1': InstanceType<typeof HtmlH1Element>;
|
||||
'html-h2': InstanceType<typeof HtmlH2Element>;
|
||||
'html-h3': InstanceType<typeof HtmlH3Element>;
|
||||
'html-h4': InstanceType<typeof HtmlH4Element>;
|
||||
'html-h5': InstanceType<typeof HtmlH5Element>;
|
||||
'html-h6': InstanceType<typeof HtmlH6Element>;
|
||||
'html-strong': InstanceType<typeof HtmlStrongElement>;
|
||||
'html-em': InstanceType<typeof HtmlEmElement>;
|
||||
'html-small': InstanceType<typeof HtmlSmallElement>;
|
||||
'html-code': InstanceType<typeof HtmlCodeElement>;
|
||||
'html-pre': InstanceType<typeof HtmlPreElement>;
|
||||
'html-blockquote': InstanceType<typeof HtmlBlockquoteElement>;
|
||||
'html-a': InstanceType<typeof HtmlAElement>;
|
||||
'html-img': InstanceType<typeof HtmlImgElement>;
|
||||
'html-ul': InstanceType<typeof HtmlUlElement>;
|
||||
'html-ol': InstanceType<typeof HtmlOlElement>;
|
||||
'html-li': InstanceType<typeof HtmlLiElement>;
|
||||
'html-form': InstanceType<typeof HtmlFormElement>;
|
||||
'html-label': InstanceType<typeof HtmlLabelElement>;
|
||||
'html-input': InstanceType<typeof HtmlInputElement>;
|
||||
'html-textarea': InstanceType<typeof HtmlTextareaElement>;
|
||||
'html-select': InstanceType<typeof HtmlSelectElement>;
|
||||
'html-option': InstanceType<typeof HtmlOptionElement>;
|
||||
'html-button': InstanceType<typeof HtmlButtonElement>;
|
||||
'html-table': InstanceType<typeof HtmlTableElement>;
|
||||
'html-thead': InstanceType<typeof HtmlTheadElement>;
|
||||
'html-tbody': InstanceType<typeof HtmlTbodyElement>;
|
||||
'html-tfoot': InstanceType<typeof HtmlTfootElement>;
|
||||
'html-tr': InstanceType<typeof HtmlTrElement>;
|
||||
'html-th': InstanceType<typeof HtmlThElement>;
|
||||
'html-td': InstanceType<typeof HtmlTdElement>;
|
||||
'html-br': InstanceType<typeof HtmlBrElement>;
|
||||
'html-hr': InstanceType<typeof HtmlHrElement>;
|
||||
'remote-root': InstanceType<typeof RemoteRootElement>;
|
||||
'remote-fragment': InstanceType<typeof RemoteFragmentElement>;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* _____ _
|
||||
*|_ _|_ _____ _ __ | |_ _ _
|
||||
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
|
||||
* | | \ V V / __/ | | | |_| |_| | Any edits to this will be overridden
|
||||
* |_| \_/\_/ \___|_| |_|\__|\__, |
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export * from './remote-elements';
|
||||
export * from './remote-components';
|
||||
@@ -1,45 +0,0 @@
|
||||
//@ts-nocheck
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { HtmlButton, HtmlDiv, HtmlH3, HtmlP } from '../generated/remote-components';
|
||||
|
||||
const FrontComponent = () => {
|
||||
const [clickCount, setClickCount] = useState(0);
|
||||
const [currentTime, setCurrentTime] = useState(
|
||||
new Date().toLocaleTimeString(),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrentTime(new Date().toLocaleTimeString());
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return React.createElement(
|
||||
HtmlDiv,
|
||||
null,
|
||||
React.createElement(HtmlH3, null, 'Remote DOM front component'),
|
||||
React.createElement(
|
||||
HtmlP,
|
||||
null,
|
||||
'Rendered in a web worker and mirrored on the host.',
|
||||
),
|
||||
React.createElement(
|
||||
HtmlButton,
|
||||
{ onClick: () => setClickCount(clickCount + 1) },
|
||||
'Click me',
|
||||
),
|
||||
React.createElement(
|
||||
HtmlP,
|
||||
null,
|
||||
'Clicked ',
|
||||
clickCount,
|
||||
' time',
|
||||
clickCount === 1 ? '' : 's',
|
||||
),
|
||||
React.createElement(HtmlP, null, 'Current time: ', currentTime),
|
||||
);
|
||||
};
|
||||
|
||||
export default React.createElement(FrontComponent);
|
||||
@@ -1,6 +0,0 @@
|
||||
// @ts-expect-error - Vite worker inline import
|
||||
import RemoteWorker from './remote-worker?worker&inline';
|
||||
|
||||
export const createRemoteWorker = (): Worker => {
|
||||
return new RemoteWorker();
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import '@remote-dom/core/polyfill';
|
||||
import '@remote-dom/react/polyfill';
|
||||
|
||||
import '../generated/remote-elements';
|
||||
|
||||
import { ThreadWebWorker } from '@quilted/threads';
|
||||
import {
|
||||
BatchingRemoteConnection,
|
||||
type RemoteConnection,
|
||||
type RemoteRootElement,
|
||||
} from '@remote-dom/core/elements';
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { jsx, jsxs } from 'react/jsx-runtime';
|
||||
import { type HostToWorkerRenderContext } from '../../types/HostToWorkerRenderContext';
|
||||
import { type WorkerExports } from '../../types/WorkerExports';
|
||||
import * as RemoteComponents from '../generated/remote-components';
|
||||
|
||||
(globalThis as Record<string, unknown>).React = React;
|
||||
(globalThis as Record<string, unknown>).RemoteComponents = RemoteComponents;
|
||||
(globalThis as Record<string, unknown>).jsx = jsx;
|
||||
(globalThis as Record<string, unknown>).jsxs = jsxs;
|
||||
|
||||
const render: WorkerExports['render'] = async (
|
||||
connection: RemoteConnection,
|
||||
renderContext: HostToWorkerRenderContext,
|
||||
) => {
|
||||
const batchedConnection = new BatchingRemoteConnection(connection);
|
||||
const root = document.createElement('remote-root') as RemoteRootElement;
|
||||
root.connect(batchedConnection);
|
||||
document.body.append(root);
|
||||
|
||||
/* @vite-ignore */
|
||||
const componentModule = await import(renderContext.componentUrl);
|
||||
|
||||
const reactRoot = createRoot(root);
|
||||
reactRoot.render(componentModule.default);
|
||||
};
|
||||
|
||||
ThreadWebWorker.self.export({ render });
|
||||
@@ -1,3 +0,0 @@
|
||||
export type HostToWorkerRenderContext = {
|
||||
componentUrl: string;
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
export type PropertySchema = {
|
||||
type: 'string' | 'number' | 'boolean';
|
||||
optional: boolean;
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { type RemoteConnection } from '@remote-dom/core/elements';
|
||||
import { type HostToWorkerRenderContext } from './HostToWorkerRenderContext';
|
||||
|
||||
export type WorkerExports = {
|
||||
render: (
|
||||
connection: RemoteConnection,
|
||||
context: HostToWorkerRenderContext,
|
||||
) => Promise<void>;
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
@@ -17,15 +16,8 @@
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"scripts/**/*.ts",
|
||||
"**/__mocks__/**/*",
|
||||
"jest.config.mjs"
|
||||
],
|
||||
"exclude": [
|
||||
"src/front-component/remote/mock/**/*",
|
||||
"src/front-component/host/generated/host-component-registry.ts",
|
||||
"src/front-component/remote/generated/remote-components.ts",
|
||||
"src/front-component/remote/generated/remote-elements.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
"outDir": "../../.cache/tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.ts"],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/__mocks__/**/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -52,24 +52,6 @@ export default defineConfig(() => {
|
||||
}),
|
||||
dts({ entryRoot: './src', tsconfigPath: tsConfigPath }),
|
||||
],
|
||||
worker: {
|
||||
format: 'iife',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
inlineDynamicImports: true,
|
||||
},
|
||||
},
|
||||
plugins: () => [
|
||||
{
|
||||
name: 'define-process-env',
|
||||
transform(code: string) {
|
||||
return code
|
||||
.replace(/process\.env\.NODE_ENV/g, JSON.stringify('production'))
|
||||
.replace(/process\.env/g, '{}');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
lib: { entry: entries, name: 'twenty-shared' },
|
||||
|
||||
Reference in New Issue
Block a user