From 5c3b0e6c701be5d7d953a9150c1ef884ac969346 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 10:18:20 -0700 Subject: [PATCH 1/6] configures dynamic outdirs in react-wrapper package --- packages/react-wrappers/src/types.d.ts | 2 + packages/react-wrappers/src/utils.ts | 16 +- .../react-wrappers/src/wrapper-generator.ts | 193 ++++++++++++------ 3 files changed, 141 insertions(+), 70 deletions(-) diff --git a/packages/react-wrappers/src/types.d.ts b/packages/react-wrappers/src/types.d.ts index 4b24f57..209ffed 100644 --- a/packages/react-wrappers/src/types.d.ts +++ b/packages/react-wrappers/src/types.d.ts @@ -3,6 +3,8 @@ import { BaseOptions } from "../../../tools/configurations"; import type { Attribute } from "custom-elements-manifest"; export interface Options extends BaseOptions { + /** Path to output directory */ + outdir?: (className: string, tagName: string) => string | string; /** Used to get a specific path for a given component */ modulePath?: (className: string, tagName: string) => string; /** Indicates if the component classes are a default export rather than a named export */ diff --git a/packages/react-wrappers/src/utils.ts b/packages/react-wrappers/src/utils.ts index 36b3881..64dd3dc 100644 --- a/packages/react-wrappers/src/utils.ts +++ b/packages/react-wrappers/src/utils.ts @@ -12,7 +12,7 @@ export function getPackageJson(): any { export function getModulePath( modulePath: ((className: string, tagName: string) => string) | undefined, component: Component, - outdir: string, + outdir: (className: string, tagName: string) => string | string, packageJson: any ) { if (modulePath instanceof Function) { @@ -25,10 +25,22 @@ export function getModulePath( ); } - const directories = outdir?.split("/"); + const outdirPath = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + const directories = outdirPath?.split("/"); return path.join(directories.map((_) => "../").join(""), packageJson.module); } +export function normalizeOutdir(outdir: any) { + console.log("Normalizing outdir:", outdir, "Type:", typeof outdir); + if (typeof outdir === 'function') { + return outdir; + } + if (typeof outdir === 'string') { + return () => outdir; + } + throw new TypeError('The outdir property must be either a string or a function.'); +} + export const createEventName = (event: any) => `on${toPascalCase(event.name)}`; export const RESERVED_WORDS = [ diff --git a/packages/react-wrappers/src/wrapper-generator.ts b/packages/react-wrappers/src/wrapper-generator.ts index aad394d..f6da7b6 100644 --- a/packages/react-wrappers/src/wrapper-generator.ts +++ b/packages/react-wrappers/src/wrapper-generator.ts @@ -11,6 +11,7 @@ import { createEventName, getModulePath, getPackageJson, + normalizeOutdir, saveReactUtils, saveScopeProvider, } from "./utils.js"; @@ -30,33 +31,47 @@ import type { Parameter, } from "custom-elements-manifest"; import { has, toCamelCase } from "../../../tools/utilities/index.js"; +import path from "path"; +import { BaseOptions } from "../../../tools/configurations/index.js"; const packageJson = getPackageJson(); -let config: Options = {}; +let config: Options = { + outdir: (className: string, tagName: string) => `./react` +}; let globalEvents: GlobalEvent[] = []; -export function generateReactWrappers( - customElementsManifest: CEM, - options: Options -) { +export function generateReactWrappers(customElementsManifest: CEM, options: Options) { if (options.skip) { logYellow("[react-wrappers] - Skipped", options.hideLogs); return; } - logBlue( - "[react-wrappers] - Generating wrappers...", - options.hideLogs - ); + logBlue("[react-wrappers] - Generating wrappers...", options.hideLogs); updateConfig(options); + const components = getComponents(customElementsManifest, config.exclude); - createOutDir(config.outdir!); - saveReactUtils(config.outdir!, config.ssrSafe); - if (config.scopedTags) { - saveScopeProvider(config.outdir!, config.ssrSafe); + + const uniqueOutDirs = new Set(components.map(c => { + const outdir = config.outdir; + return typeof outdir === 'function' ? outdir(c.name, c.tagName!) : outdir; + }).filter((dir): dir is string => typeof dir === 'string')); + + const commonRoot = getCommonRoot(Array.from(uniqueOutDirs)); + + if (!commonRoot) { + throw new Error("Common root directory could not be determined. Ensure all component directories have a common path."); } + createOutDir(commonRoot); + saveScopeProvider(commonRoot, config.ssrSafe); + components.forEach((component) => { + const componentOutDir = typeof config.outdir === 'function' ? config.outdir(component.name, component.tagName!) : config.outdir; + if (!componentOutDir) return; + + createOutDir(componentOutDir); + saveReactUtils(commonRoot, config.ssrSafe); + const events = getEventNames(component); const { booleanAttributes, attributes } = getAttributes(component); const properties = getProperties(component, attributes, booleanAttributes); @@ -67,12 +82,15 @@ export function generateReactWrappers( packageJson ); + const relativePathToCommonRoot = path.relative(componentOutDir, commonRoot).replace(/\\/g, '/'); + generateReactWrapper( component, events, booleanAttributes, attributes, componentModulePath, + relativePathToCommonRoot, properties ); @@ -86,13 +104,15 @@ export function generateReactWrappers( ); }); - generateManifests(components, config.outdir!); + generateManifests(components, config.outdir!, commonRoot); + logBlue(`[react-wrappers] - Generated wrappers in "${config.outdir}".`, config.hideLogs); } function updateConfig(options: Options) { config = { - outdir: "./react", + ...config, + outdir: normalizeOutdir(options.outdir || config.outdir), exclude: [], typesSrc: "types", attributeMapping: {}, @@ -100,6 +120,7 @@ function updateConfig(options: Options) { }; globalEvents = options.globalEvents || []; + } function generateReactWrapper( @@ -108,18 +129,27 @@ function generateReactWrapper( booleanAttributes: Attribute[], attributes: Attribute[], componentModulePath: string, + relativePathToCommonRoot: string, properties?: ClassField[] ) { + // Ensure outdir is always treated as a function + const outdir = config.outdir!; + const componentOutDir = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + + // Handle empty relative path case + const adjustedRelativePathToCommonRoot = relativePathToCommonRoot === "" ? "." : relativePathToCommonRoot; + const result = getReactComponentTemplate( component, events, booleanAttributes, attributes, componentModulePath, + adjustedRelativePathToCommonRoot, properties ); - saveFile(config.outdir!, `${component.name}.js`, result, "typescript"); + saveFile(componentOutDir, `${component.name}.js`, result, "typescript"); } function generateTypeDefinition( @@ -127,7 +157,7 @@ function generateTypeDefinition( events: EventName[], booleanAttributes: Attribute[], attributes: Attribute[], - componentModulePath: string, + modulePath: string, properties?: ClassField[] ) { const result = getTypeDefinitionTemplate( @@ -135,26 +165,56 @@ function generateTypeDefinition( events, booleanAttributes, attributes, - componentModulePath, + modulePath, properties ); - saveFile(config.outdir!, `${component.name}.d.ts`, result, "typescript"); + const componentOutDir = typeof config.outdir === 'function' ? config.outdir(component.name, component.tagName!) : config.outdir; + saveFile(componentOutDir!, `${component.name}.d.ts`, result, "typescript"); } -function generateManifests(components: Component[], outdir: string) { - saveFile( - outdir, - "index.js", - getManifestContentTemplate(components), - "typescript" - ); - saveFile( - outdir, - "index.d.ts", - getManifestContentTemplate(components), - "typescript" - ); +function generateManifests(components: Component[], outdir: (className: string, tagName: string) => string | string, commonRoot: string) { + const uniqueOutDirs = new Set(); + + components.forEach(component => { + const componentOutDir = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + uniqueOutDirs.add(componentOutDir); + }); + + createOutDir(commonRoot); + + const manifestContent = getManifestContentTemplate(components, outdir, commonRoot); + + saveFile(commonRoot, "index.js", manifestContent, "typescript"); + saveFile(commonRoot, "index.d.ts", manifestContent, "typescript"); +} + +function getCommonRoot(dirs: string[]): string { + if (!dirs.length) return "./react"; + + const normalizedDirs = dirs.map(dir => path.normalize(dir)); + const splitDirs = normalizedDirs.map(dir => dir.split(path.sep)); + const minLength = Math.min(...splitDirs.map(split => split.length)); + const commonRootSegments: string[] = []; + + for (let i = 0; i < minLength; i++) { + const segment = splitDirs[0][i]; + if (splitDirs.every(split => split[i] === segment)) { + commonRootSegments.push(segment); + logBlue(`Common segment '${segment}' found at index ${i}`); + } else { + logBlue(`Segment mismatch at index ${i}, stopping`); + break; + } + } + + let commonRoot = commonRootSegments.join(path.sep); + if (!commonRoot) { + commonRoot = "./react"; + } + + logBlue(`Calculated common root: ${commonRoot}`); + return commonRoot; } function getProperties( @@ -311,6 +371,7 @@ function getReactComponentTemplate( booleanAttributes: MappedAttribute[], attributes: MappedAttribute[], modulePath: string, + relativePathToCommonRoot: string, properties?: ClassField[] ) { const eventTemplates = getEventTemplates(events); @@ -324,22 +385,15 @@ function getReactComponentTemplate( return ` ${config.ssrSafe ? '"use client"' : ""} - import React, { forwardRef, useImperativeHandle ${ - useEffect ? ", useRef, useEffect" : "" - } ${config.scopedTags ? ", useContext" : ""} } from "react"; + import React, { forwardRef, useImperativeHandle ${config.scopedTags ? ", useContext" : ""} ${useEffect ? ", useRef, useEffect" : ""} } from "react"; ${!config.ssrSafe ? `import '${modulePath}';` : ""} - ${ - has(eventTemplates) || has(propTemplates) - ? `import { - ${has(eventTemplates) ? "useEventListener," : ""} - ${has(propTemplates) ? "useProperties" : ""} - } from './react-utils.js';` - : "" - } - ${ - config.scopedTags - ? 'import { ScopeContext } from "./ScopeProvider.js";' - : "" + ${config.scopedTags ? `import { ScopeContext } from "${relativePathToCommonRoot}/ScopeProvider.js";` : ""} + ${has(eventTemplates) || has(propTemplates) + ? `import { + ${has(eventTemplates) ? "useEventListener," : ""} + ${has(propTemplates) ? "useProperties" : ""} + } from '${relativePathToCommonRoot}/react-utils.js';` + : "" } export const ${component.name} = forwardRef((props, forwardedRef) => { @@ -347,8 +401,7 @@ function getReactComponentTemplate( ${has(unusedProps) ? `const { ${unusedProps.join(", ")}, ...filteredProps } = props;` : ''} ${config.scopedTags ? "const scope = useContext(ScopeContext);" : ""} - ${ - config.ssrSafe + ${config.ssrSafe ? ` /** Waits for the client before loading the custom element */ useEffect(() => { @@ -358,22 +411,13 @@ function getReactComponentTemplate( : "" } - ${has(eventTemplates) ? "/** Event listeners - run once */" : ""} ${eventTemplates?.join("") || ""} - ${ - has(propTemplates) - ? "/** Properties - run whenever a property has changed */" - : "" - } + ${has(propTemplates) ? "/** Properties - run whenever a property has changed */" : ""} ${propTemplates?.join("") || ""} - ${ - has(methods) - ? "/** Methods - uses `useImperativeHandle` hook to pass ref to component */" - : "" - } + ${has(methods) ? "/** Methods - uses `useImperativeHandle` hook to pass ref to component */" : ""} useImperativeHandle(forwardedRef, () => ({ ${getPublicMethodsForRef(methods)} })); @@ -392,6 +436,12 @@ function getReactComponentTemplate( }); `; } +function convertOptionsToBaseOptions(options: Options): BaseOptions { + return { + ...options, + outdir: typeof options.outdir === 'string' ? options.outdir : options.outdir ? options.outdir('', '') : undefined + }; +} function getTypeDefinitionTemplate( component: Component, @@ -409,6 +459,7 @@ function getTypeDefinitionTemplate( properties ); const eventTypes = getCustomEventTypes(component); + const baseConfig = convertOptionsToBaseOptions(config); return ` import { @@ -428,7 +479,7 @@ function getTypeDefinitionTemplate( } /** - ${getComponentDetailsTemplate(component, config, true)} + ${getComponentDetailsTemplate(component, baseConfig, true)} */ export const ${component.name}: React.ForwardRefExoticComponent<${ component.name @@ -583,20 +634,26 @@ function getGlobalEventPropsTemplate(events: GlobalEvent[] | undefined) { ); } -function getManifestContentTemplate(components: Component[]) { - let exports = components - .map((component) => `export * from './${component.name}.js';`) - .join(""); +function getManifestContentTemplate(components: Component[], outdir: (className: string, tagName: string) => string | string, commonRoot: string) { + const resolveOutDir = typeof outdir === 'function' ? outdir : () => outdir; + let exports = components + .map(component => { + const componentOutDir = resolveOutDir(component.name, component.tagName!); + const relativePath = path.relative(commonRoot, componentOutDir).replace(/\\/g, '/'); + return `export * from './${relativePath}/${component.name}.js';`; + }) + .join("\n"); + + // Add ScopeProvider export directly if (config.scopedTags) { - exports += ` - export * from "./ScopeProvider.js"; - `; + exports += `\nexport * from './ScopeProvider.js';`; } return exports; } + function getEventType(eventType?: string, eventCustom?: boolean) { if (eventCustom) { return eventType; From 1b2c272171beffcdabe4c0f150252ba3267d8ba1 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 10:21:38 -0700 Subject: [PATCH 2/6] removes console log --- packages/react-wrappers/src/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-wrappers/src/utils.ts b/packages/react-wrappers/src/utils.ts index 64dd3dc..a420700 100644 --- a/packages/react-wrappers/src/utils.ts +++ b/packages/react-wrappers/src/utils.ts @@ -31,7 +31,6 @@ export function getModulePath( } export function normalizeOutdir(outdir: any) { - console.log("Normalizing outdir:", outdir, "Type:", typeof outdir); if (typeof outdir === 'function') { return outdir; } From bd9deab04b123daba34d31e93f41b5d8e62a13d6 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 10:27:00 -0700 Subject: [PATCH 3/6] formats documents --- packages/react-wrappers/src/utils.ts | 23 ++- .../react-wrappers/src/wrapper-generator.ts | 148 +++++++++++++----- 2 files changed, 126 insertions(+), 45 deletions(-) diff --git a/packages/react-wrappers/src/utils.ts b/packages/react-wrappers/src/utils.ts index a420700..b9d64f4 100644 --- a/packages/react-wrappers/src/utils.ts +++ b/packages/react-wrappers/src/utils.ts @@ -25,19 +25,24 @@ export function getModulePath( ); } - const outdirPath = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + const outdirPath = + typeof outdir === "function" + ? outdir(component.name, component.tagName!) + : outdir; const directories = outdirPath?.split("/"); return path.join(directories.map((_) => "../").join(""), packageJson.module); } export function normalizeOutdir(outdir: any) { - if (typeof outdir === 'function') { + if (typeof outdir === "function") { return outdir; } - if (typeof outdir === 'string') { + if (typeof outdir === "string") { return () => outdir; } - throw new TypeError('The outdir property must be either a string or a function.'); + throw new TypeError( + "The outdir property must be either a string or a function." + ); } export const createEventName = (event: any) => `on${toPascalCase(event.name)}`; @@ -117,7 +122,11 @@ export function saveReactUtils(outdir: string, ssrSafe?: boolean) { const reactUtils = ` import { useEffect, useLayoutEffect } from "react"; -${ssrSafe ? `const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect` : ''} +${ + ssrSafe + ? `const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect` + : "" +} export function useProperties(targetElement, propName, value) { useEffect(() => { @@ -133,7 +142,7 @@ export function useProperties(targetElement, propName, value) { } export function useEventListener(targetElement, eventName, eventHandler) { - ${ssrSafe ? 'useIsomorphicLayoutEffect' : 'useLayoutEffect'}(() => { + ${ssrSafe ? "useIsomorphicLayoutEffect" : "useLayoutEffect"}(() => { if (eventHandler !== undefined) { targetElement?.current?.addEventListener(eventName, eventHandler); } @@ -155,7 +164,7 @@ export function useEventListener(targetElement, eventName, eventHandler) { export function saveScopeProvider(outdir: string, ssrSafe?: boolean) { const scopeProvider = ` -${ssrSafe ? '"use client"' : ''} +${ssrSafe ? '"use client"' : ""} import { createContext } from 'react'; import { jsx } from "react/jsx-runtime"; diff --git a/packages/react-wrappers/src/wrapper-generator.ts b/packages/react-wrappers/src/wrapper-generator.ts index f6da7b6..177d628 100644 --- a/packages/react-wrappers/src/wrapper-generator.ts +++ b/packages/react-wrappers/src/wrapper-generator.ts @@ -15,7 +15,12 @@ import { saveReactUtils, saveScopeProvider, } from "./utils.js"; -import { createOutDir, logBlue, logYellow, saveFile } from "../../../tools/integrations/index.js"; +import { + createOutDir, + logBlue, + logYellow, + saveFile, +} from "../../../tools/integrations/index.js"; import { CEM, Component, @@ -36,11 +41,14 @@ import { BaseOptions } from "../../../tools/configurations/index.js"; const packageJson = getPackageJson(); let config: Options = { - outdir: (className: string, tagName: string) => `./react` + outdir: (className: string, tagName: string) => `./react`, }; let globalEvents: GlobalEvent[] = []; -export function generateReactWrappers(customElementsManifest: CEM, options: Options) { +export function generateReactWrappers( + customElementsManifest: CEM, + options: Options +) { if (options.skip) { logYellow("[react-wrappers] - Skipped", options.hideLogs); return; @@ -51,22 +59,33 @@ export function generateReactWrappers(customElementsManifest: CEM, options: Opti const components = getComponents(customElementsManifest, config.exclude); - const uniqueOutDirs = new Set(components.map(c => { - const outdir = config.outdir; - return typeof outdir === 'function' ? outdir(c.name, c.tagName!) : outdir; - }).filter((dir): dir is string => typeof dir === 'string')); + const uniqueOutDirs = new Set( + components + .map((c) => { + const outdir = config.outdir; + return typeof outdir === "function" + ? outdir(c.name, c.tagName!) + : outdir; + }) + .filter((dir): dir is string => typeof dir === "string") + ); const commonRoot = getCommonRoot(Array.from(uniqueOutDirs)); if (!commonRoot) { - throw new Error("Common root directory could not be determined. Ensure all component directories have a common path."); + throw new Error( + "Common root directory could not be determined. Ensure all component directories have a common path." + ); } createOutDir(commonRoot); saveScopeProvider(commonRoot, config.ssrSafe); components.forEach((component) => { - const componentOutDir = typeof config.outdir === 'function' ? config.outdir(component.name, component.tagName!) : config.outdir; + const componentOutDir = + typeof config.outdir === "function" + ? config.outdir(component.name, component.tagName!) + : config.outdir; if (!componentOutDir) return; createOutDir(componentOutDir); @@ -82,7 +101,9 @@ export function generateReactWrappers(customElementsManifest: CEM, options: Opti packageJson ); - const relativePathToCommonRoot = path.relative(componentOutDir, commonRoot).replace(/\\/g, '/'); + const relativePathToCommonRoot = path + .relative(componentOutDir, commonRoot) + .replace(/\\/g, "/"); generateReactWrapper( component, @@ -106,7 +127,10 @@ export function generateReactWrappers(customElementsManifest: CEM, options: Opti generateManifests(components, config.outdir!, commonRoot); - logBlue(`[react-wrappers] - Generated wrappers in "${config.outdir}".`, config.hideLogs); + logBlue( + `[react-wrappers] - Generated wrappers in "${config.outdir}".`, + config.hideLogs + ); } function updateConfig(options: Options) { @@ -120,7 +144,6 @@ function updateConfig(options: Options) { }; globalEvents = options.globalEvents || []; - } function generateReactWrapper( @@ -134,10 +157,14 @@ function generateReactWrapper( ) { // Ensure outdir is always treated as a function const outdir = config.outdir!; - const componentOutDir = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + const componentOutDir = + typeof outdir === "function" + ? outdir(component.name, component.tagName!) + : outdir; // Handle empty relative path case - const adjustedRelativePathToCommonRoot = relativePathToCommonRoot === "" ? "." : relativePathToCommonRoot; + const adjustedRelativePathToCommonRoot = + relativePathToCommonRoot === "" ? "." : relativePathToCommonRoot; const result = getReactComponentTemplate( component, @@ -169,21 +196,35 @@ function generateTypeDefinition( properties ); - const componentOutDir = typeof config.outdir === 'function' ? config.outdir(component.name, component.tagName!) : config.outdir; + const componentOutDir = + typeof config.outdir === "function" + ? config.outdir(component.name, component.tagName!) + : config.outdir; saveFile(componentOutDir!, `${component.name}.d.ts`, result, "typescript"); } -function generateManifests(components: Component[], outdir: (className: string, tagName: string) => string | string, commonRoot: string) { +function generateManifests( + components: Component[], + outdir: (className: string, tagName: string) => string | string, + commonRoot: string +) { const uniqueOutDirs = new Set(); - components.forEach(component => { - const componentOutDir = typeof outdir === 'function' ? outdir(component.name, component.tagName!) : outdir; + components.forEach((component) => { + const componentOutDir = + typeof outdir === "function" + ? outdir(component.name, component.tagName!) + : outdir; uniqueOutDirs.add(componentOutDir); }); createOutDir(commonRoot); - const manifestContent = getManifestContentTemplate(components, outdir, commonRoot); + const manifestContent = getManifestContentTemplate( + components, + outdir, + commonRoot + ); saveFile(commonRoot, "index.js", manifestContent, "typescript"); saveFile(commonRoot, "index.d.ts", manifestContent, "typescript"); @@ -192,14 +233,14 @@ function generateManifests(components: Component[], outdir: (className: string, function getCommonRoot(dirs: string[]): string { if (!dirs.length) return "./react"; - const normalizedDirs = dirs.map(dir => path.normalize(dir)); - const splitDirs = normalizedDirs.map(dir => dir.split(path.sep)); - const minLength = Math.min(...splitDirs.map(split => split.length)); + const normalizedDirs = dirs.map((dir) => path.normalize(dir)); + const splitDirs = normalizedDirs.map((dir) => dir.split(path.sep)); + const minLength = Math.min(...splitDirs.map((split) => split.length)); const commonRootSegments: string[] = []; for (let i = 0; i < minLength; i++) { const segment = splitDirs[0][i]; - if (splitDirs.every(split => split[i] === segment)) { + if (splitDirs.every((split) => split[i] === segment)) { commonRootSegments.push(segment); logBlue(`Common segment '${segment}' found at index ${i}`); } else { @@ -385,23 +426,35 @@ function getReactComponentTemplate( return ` ${config.ssrSafe ? '"use client"' : ""} - import React, { forwardRef, useImperativeHandle ${config.scopedTags ? ", useContext" : ""} ${useEffect ? ", useRef, useEffect" : ""} } from "react"; + import React, { forwardRef, useImperativeHandle ${ + config.scopedTags ? ", useContext" : "" + } ${useEffect ? ", useRef, useEffect" : ""} } from "react"; ${!config.ssrSafe ? `import '${modulePath}';` : ""} - ${config.scopedTags ? `import { ScopeContext } from "${relativePathToCommonRoot}/ScopeProvider.js";` : ""} - ${has(eventTemplates) || has(propTemplates) - ? `import { + ${ + config.scopedTags + ? `import { ScopeContext } from "${relativePathToCommonRoot}/ScopeProvider.js";` + : "" + } + ${ + has(eventTemplates) || has(propTemplates) + ? `import { ${has(eventTemplates) ? "useEventListener," : ""} ${has(propTemplates) ? "useProperties" : ""} } from '${relativePathToCommonRoot}/react-utils.js';` - : "" + : "" } export const ${component.name} = forwardRef((props, forwardedRef) => { ${useEffect ? `const ref = useRef(null);` : ""} - ${has(unusedProps) ? `const { ${unusedProps.join(", ")}, ...filteredProps } = props;` : ''} + ${ + has(unusedProps) + ? `const { ${unusedProps.join(", ")}, ...filteredProps } = props;` + : "" + } ${config.scopedTags ? "const scope = useContext(ScopeContext);" : ""} - ${config.ssrSafe + ${ + config.ssrSafe ? ` /** Waits for the client before loading the custom element */ useEffect(() => { @@ -414,10 +467,18 @@ function getReactComponentTemplate( ${has(eventTemplates) ? "/** Event listeners - run once */" : ""} ${eventTemplates?.join("") || ""} - ${has(propTemplates) ? "/** Properties - run whenever a property has changed */" : ""} + ${ + has(propTemplates) + ? "/** Properties - run whenever a property has changed */" + : "" + } ${propTemplates?.join("") || ""} - ${has(methods) ? "/** Methods - uses `useImperativeHandle` hook to pass ref to component */" : ""} + ${ + has(methods) + ? "/** Methods - uses `useImperativeHandle` hook to pass ref to component */" + : "" + } useImperativeHandle(forwardedRef, () => ({ ${getPublicMethodsForRef(methods)} })); @@ -436,10 +497,16 @@ function getReactComponentTemplate( }); `; } + function convertOptionsToBaseOptions(options: Options): BaseOptions { return { ...options, - outdir: typeof options.outdir === 'string' ? options.outdir : options.outdir ? options.outdir('', '') : undefined + outdir: + typeof options.outdir === "string" + ? options.outdir + : options.outdir + ? options.outdir("", "") + : undefined, }; } @@ -634,13 +701,19 @@ function getGlobalEventPropsTemplate(events: GlobalEvent[] | undefined) { ); } -function getManifestContentTemplate(components: Component[], outdir: (className: string, tagName: string) => string | string, commonRoot: string) { - const resolveOutDir = typeof outdir === 'function' ? outdir : () => outdir; +function getManifestContentTemplate( + components: Component[], + outdir: (className: string, tagName: string) => string | string, + commonRoot: string +) { + const resolveOutDir = typeof outdir === "function" ? outdir : () => outdir; let exports = components - .map(component => { + .map((component) => { const componentOutDir = resolveOutDir(component.name, component.tagName!); - const relativePath = path.relative(commonRoot, componentOutDir).replace(/\\/g, '/'); + const relativePath = path + .relative(commonRoot, componentOutDir) + .replace(/\\/g, "/"); return `export * from './${relativePath}/${component.name}.js';`; }) .join("\n"); @@ -653,7 +726,6 @@ function getManifestContentTemplate(components: Component[], outdir: (className: return exports; } - function getEventType(eventType?: string, eventCustom?: boolean) { if (eventCustom) { return eventType; From 93790a3e82d2cb9afa98efc81b46e5fa5ee43934 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 10:29:20 -0700 Subject: [PATCH 4/6] fixes code comments --- packages/react-wrappers/src/wrapper-generator.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-wrappers/src/wrapper-generator.ts b/packages/react-wrappers/src/wrapper-generator.ts index 177d628..936bc47 100644 --- a/packages/react-wrappers/src/wrapper-generator.ts +++ b/packages/react-wrappers/src/wrapper-generator.ts @@ -155,14 +155,14 @@ function generateReactWrapper( relativePathToCommonRoot: string, properties?: ClassField[] ) { - // Ensure outdir is always treated as a function + /** Ensure outdir is always treated as a function */ const outdir = config.outdir!; const componentOutDir = typeof outdir === "function" ? outdir(component.name, component.tagName!) : outdir; - // Handle empty relative path case + /** Handle empty relative path case */ const adjustedRelativePathToCommonRoot = relativePathToCommonRoot === "" ? "." : relativePathToCommonRoot; @@ -718,7 +718,6 @@ function getManifestContentTemplate( }) .join("\n"); - // Add ScopeProvider export directly if (config.scopedTags) { exports += `\nexport * from './ScopeProvider.js';`; } From 3030e622f8b20bfa903cf1a77f7535ccf4d1feb4 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 10:48:54 -0700 Subject: [PATCH 5/6] fixes dynamic import statement syntax --- packages/react-wrappers/src/wrapper-generator.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/react-wrappers/src/wrapper-generator.ts b/packages/react-wrappers/src/wrapper-generator.ts index 936bc47..cc0b515 100644 --- a/packages/react-wrappers/src/wrapper-generator.ts +++ b/packages/react-wrappers/src/wrapper-generator.ts @@ -703,7 +703,7 @@ function getGlobalEventPropsTemplate(events: GlobalEvent[] | undefined) { function getManifestContentTemplate( components: Component[], - outdir: (className: string, tagName: string) => string | string, + outdir: ((className: string, tagName: string) => string) | string, commonRoot: string ) { const resolveOutDir = typeof outdir === "function" ? outdir : () => outdir; @@ -711,10 +711,11 @@ function getManifestContentTemplate( let exports = components .map((component) => { const componentOutDir = resolveOutDir(component.name, component.tagName!); - const relativePath = path - .relative(commonRoot, componentOutDir) - .replace(/\\/g, "/"); - return `export * from './${relativePath}/${component.name}.js';`; + let relativePath = path.relative(commonRoot, componentOutDir).replace(/\\/g, "/"); + if (!relativePath.startsWith('.')) { + relativePath = `./${relativePath}`; + } + return `export * from '${relativePath}/${component.name}.js';`; }) .join("\n"); @@ -722,6 +723,9 @@ function getManifestContentTemplate( exports += `\nexport * from './ScopeProvider.js';`; } + // Remove any duplicate slashes in the path + exports = exports.replace(/\/\//g, '/'); + return exports; } From 05a4ace6df30d1c58040c8dd13459c7f9bd74a03 Mon Sep 17 00:00:00 2001 From: Brian Brady Date: Tue, 11 Jun 2024 16:05:43 -0700 Subject: [PATCH 6/6] removes unused args --- packages/react-wrappers/src/wrapper-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-wrappers/src/wrapper-generator.ts b/packages/react-wrappers/src/wrapper-generator.ts index cc0b515..b53dfe7 100644 --- a/packages/react-wrappers/src/wrapper-generator.ts +++ b/packages/react-wrappers/src/wrapper-generator.ts @@ -41,7 +41,7 @@ import { BaseOptions } from "../../../tools/configurations/index.js"; const packageJson = getPackageJson(); let config: Options = { - outdir: (className: string, tagName: string) => `./react`, + outdir: () => `./react`, }; let globalEvents: GlobalEvent[] = [];