mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-27 12:13:53 +00:00
it was added in #5935 but no explanation of why. if you look at the build output of for example `packages/@uppy/react/lib/headless/generated/Dropzone.js`: ```js import { jsx as _jsx } from "react/jsx-runtime"; // This file was generated by build-components.mjs // ANY EDITS WILL BE OVERWRITTEN! import { Dropzone as PreactDropzone, } from '@uppy/components'; import { h as preactH, render as preactRender } from 'preact'; // biome-ignore lint/correctness/noUnusedImports: it's needed import { createElement as h, useContext, useEffect, useRef } from 'react'; import { UppyContext } from '../UppyContextProvider.js'; export default function Dropzone(props) { const ref = useRef(null); const ctx = useContext(UppyContext); useEffect(() => { if (ref.current) { preactRender(preactH(PreactDropzone, { ...props, ctx, }), ref.current); } }, [ctx, props]); return _jsx("div", { ref: ref }); } ``` as can be seen there is no usage of `h`. `import { jsx as _jsx } from "react/jsx-runtime";` is being injected for React JSX. see also https://github.com/transloadit/uppy/pull/5896#issuecomment-3169210799
257 lines
7.4 KiB
JavaScript
257 lines
7.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
// When new components are added (not when existing ones are changed),
|
|
// run this script to generate React, Vue, and Svelte wrappers
|
|
import { existsSync } from 'node:fs'
|
|
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
// Get the directory of this script
|
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
const rootDir = path.resolve(scriptDir, '../../..')
|
|
|
|
// Define paths
|
|
// source:
|
|
const COMPONENTS_DIR = path.join(rootDir, 'packages/@uppy/components/src')
|
|
// destinations:
|
|
const REACT_DIR = path.join(
|
|
rootDir,
|
|
'packages/@uppy/react/src/headless/generated',
|
|
)
|
|
const VUE_DIR = path.join(rootDir, 'packages/@uppy/vue/src/headless/generated')
|
|
const SVELTE_DIR = path.join(
|
|
rootDir,
|
|
'packages/@uppy/svelte/src/lib/components/headless/generated',
|
|
)
|
|
|
|
// Templates
|
|
const REACT_TEMPLATE = `\
|
|
// This file was generated by build-components.mjs
|
|
// ANY EDITS WILL BE OVERWRITTEN!
|
|
import {
|
|
%%ComponentName%% as %%PreactComponentName%%,
|
|
type %%PropsTypeName%%,
|
|
} from '@uppy/components'
|
|
import { h as preactH, render as preactRender } from 'preact'
|
|
import { useContext, useEffect, useRef } from 'react'
|
|
import { UppyContext } from '../UppyContextProvider.js'
|
|
|
|
export default function %%ComponentName%%(props: Omit<%%PropsTypeName%%, 'ctx'>) {
|
|
const ref = useRef(null)
|
|
const ctx = useContext(UppyContext)
|
|
|
|
useEffect(() => {
|
|
if (ref.current) {
|
|
preactRender(
|
|
preactH(%%PreactComponentName%%, {
|
|
...props,
|
|
ctx,
|
|
} satisfies %%PropsTypeName%%),
|
|
ref.current,
|
|
)
|
|
}
|
|
}, [ctx, props])
|
|
|
|
return <div ref={ref} />
|
|
}
|
|
`
|
|
|
|
const VUE_TEMPLATE = `\
|
|
// This file was generated by build-components.mjs
|
|
// ANY EDITS WILL BE OVERWRITTEN!
|
|
import { defineComponent, ref, watch, onMounted, h } from 'vue'
|
|
import {
|
|
%%ComponentName%% as %%PreactComponentName%%,
|
|
type %%PropsTypeName%%,
|
|
} from '@uppy/components'
|
|
import { h as preactH, render as preactRender } from 'preact'
|
|
import { shallowEqualObjects } from 'shallow-equal'
|
|
import { useUppyContext } from '../useUppyContext.js'
|
|
|
|
export default defineComponent<Omit<%%PropsTypeName%%, 'ctx'>>({
|
|
name: '%%ComponentName%%',
|
|
setup(props, { attrs }) {
|
|
const containerRef = ref<HTMLElement | null>(null)
|
|
const ctx = useUppyContext()
|
|
|
|
function render%%ComponentName%%() {
|
|
if (containerRef.value) {
|
|
preactRender(
|
|
preactH(%%PreactComponentName%%, {
|
|
...(attrs as %%PropsTypeName%%),
|
|
ctx,
|
|
} satisfies %%PropsTypeName%%),
|
|
containerRef.value,
|
|
)
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
render%%ComponentName%%()
|
|
})
|
|
|
|
watch(ctx, () => {
|
|
render%%ComponentName%%()
|
|
})
|
|
|
|
watch(
|
|
() => props,
|
|
(current, old) => {
|
|
if (!shallowEqualObjects(current, old)) {
|
|
render%%ComponentName%%()
|
|
}
|
|
},
|
|
)
|
|
|
|
return () => h('div', { ref: containerRef })
|
|
},
|
|
})
|
|
`
|
|
|
|
const SVELTE_TEMPLATE = `\
|
|
<script lang="ts">
|
|
// This file was generated by build-components.mjs
|
|
// ANY EDITS WILL BE OVERWRITTEN!
|
|
import { getContext, mount } from 'svelte'
|
|
import {
|
|
%%ComponentName%% as %%PreactComponentName%%,
|
|
type %%PropsTypeName%%,
|
|
type UppyContext,
|
|
} from '@uppy/components'
|
|
import { h as preactH, render as preactRender } from 'preact'
|
|
import { UppyContextKey } from '../UppyContextProvider.svelte'
|
|
|
|
const props: Omit<%%PropsTypeName%%, 'ctx'> = $props()
|
|
const ctx = getContext<UppyContext>(UppyContextKey)
|
|
let container: HTMLElement
|
|
|
|
$effect(() => {
|
|
if (container) {
|
|
preactRender(
|
|
preactH(%%PreactComponentName%%, {
|
|
...props,
|
|
ctx,
|
|
} satisfies %%PropsTypeName%%),
|
|
container,
|
|
)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<div bind:this={container}></div>
|
|
`
|
|
|
|
try {
|
|
// Check if components directory exists
|
|
await fs.access(COMPONENTS_DIR).catch(() => {
|
|
throw new Error(`Components directory not found: ${COMPONENTS_DIR}`)
|
|
})
|
|
await Promise.all(
|
|
[REACT_DIR, VUE_DIR, SVELTE_DIR].map(async (dir) => {
|
|
if (!existsSync(dir)) {
|
|
await fs.mkdir(dir, { recursive: true })
|
|
}
|
|
}),
|
|
)
|
|
|
|
// Read all files in components directory
|
|
const files = await fs.readdir(COMPONENTS_DIR)
|
|
|
|
// Filter for .tsx files
|
|
const tsxFiles = files.filter((file) => file.endsWith('.tsx'))
|
|
|
|
console.log(`Found ${tsxFiles.length} Preact component(s) to process\n`)
|
|
|
|
// Track generated components for index files
|
|
const reactComponents = []
|
|
const vueComponents = []
|
|
const svelteComponents = []
|
|
|
|
// Process each tsx file
|
|
for (const file of tsxFiles) {
|
|
try {
|
|
const componentName = path.basename(file, '.tsx')
|
|
const propsTypeName = `${componentName}Props`
|
|
const preactComponentName = `Preact${componentName}`
|
|
|
|
// Generate React wrapper
|
|
const reactContent = REACT_TEMPLATE.replace(
|
|
/%%ComponentName%%/g,
|
|
componentName,
|
|
)
|
|
.replace(/%%PreactComponentName%%/g, preactComponentName)
|
|
.replace(/%%PropsTypeName%%/g, propsTypeName)
|
|
|
|
// Generate Vue wrapper
|
|
const vueContent = VUE_TEMPLATE.replace(
|
|
/%%ComponentName%%/g,
|
|
componentName,
|
|
)
|
|
.replace(/%%PreactComponentName%%/g, preactComponentName)
|
|
.replace(/%%PropsTypeName%%/g, propsTypeName)
|
|
|
|
// Generate Svelte wrapper
|
|
const svelteContent = SVELTE_TEMPLATE.replace(
|
|
/%%ComponentName%%/g,
|
|
componentName,
|
|
)
|
|
.replace(/%%PreactComponentName%%/g, preactComponentName)
|
|
.replace(/%%PropsTypeName%%/g, propsTypeName)
|
|
|
|
// Write files
|
|
const reactFilePath = path.join(REACT_DIR, `${componentName}.tsx`)
|
|
const vueFilePath = path.join(VUE_DIR, `${componentName}.ts`)
|
|
const svelteFilePath = path.join(SVELTE_DIR, `${componentName}.svelte`)
|
|
|
|
await fs.writeFile(reactFilePath, reactContent)
|
|
await fs.writeFile(vueFilePath, vueContent)
|
|
await fs.writeFile(svelteFilePath, svelteContent)
|
|
|
|
// Add to component lists for index files
|
|
reactComponents.push(componentName)
|
|
vueComponents.push(componentName)
|
|
svelteComponents.push(componentName)
|
|
|
|
console.log(`√ ${componentName}`)
|
|
} catch (error) {
|
|
console.error(`Error processing component ${file}:`, error)
|
|
}
|
|
}
|
|
|
|
// Generate index files
|
|
if (reactComponents.length > 0) {
|
|
const reactIndexContent = reactComponents
|
|
.map((name) => `export { default as ${name} } from './${name}.js'`)
|
|
.join('\n')
|
|
await fs.writeFile(
|
|
path.join(REACT_DIR, 'index.ts'),
|
|
`${reactIndexContent}\n`,
|
|
)
|
|
console.log(`\nExporting React components from ${REACT_DIR}`)
|
|
}
|
|
|
|
if (vueComponents.length > 0) {
|
|
const vueIndexContent = vueComponents
|
|
.map((name) => `export { default as ${name} } from './${name}.js'`)
|
|
.join('\n')
|
|
await fs.writeFile(path.join(VUE_DIR, 'index.ts'), `${vueIndexContent}\n`)
|
|
console.log(`Exporting Vue components from ${VUE_DIR}`)
|
|
}
|
|
|
|
if (svelteComponents.length > 0) {
|
|
const svelteIndexContent = svelteComponents
|
|
.map((name) => `export { default as ${name} } from './${name}.svelte'`)
|
|
.join('\n')
|
|
await fs.writeFile(
|
|
path.join(SVELTE_DIR, 'index.ts'),
|
|
`${svelteIndexContent}\n`,
|
|
)
|
|
console.log(`Exporting Svelte components from ${SVELTE_DIR}`)
|
|
}
|
|
|
|
console.log('\nAll wrappers and index files generated successfully!')
|
|
} catch (error) {
|
|
console.error('Error generating wrappers:', error)
|
|
process.exit(1)
|
|
}
|