UNPKG

@stencil/react-output-target

Version:

React output target for @stencil/core components.

277 lines (272 loc) 9 kB
import { Project as N, VariableDeclarationKind as U } from "ts-morph"; import v from "node:path"; const y = (t) => t.toLowerCase().split("-").map((o) => o.charAt(0).toUpperCase() + o.slice(1)).join(""), P = (t) => t.replace(/-([_a-z])/g, (o, a) => a.toUpperCase()), A = (t) => t.replace(/\/([a-z])/g, (o, a) => a.toUpperCase()), j = (t) => { const o = A(t); return P(`on-${o}`); }, F = ({ components: t, stencilPackageName: o, customElementsDir: a, defaultExport: $ = !1, hydrateModule: l, excludeServerSideRenderingFor: g }) => { const p = new N({ useInMemoryFileSystem: !0 }), r = g || [], s = l ? "" : `'use client'; `, c = `/** * This file was automatically generated by the Stencil React Output Target. * Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.${l ? ` * Do __not__ import components from this file as server side rendered components * may not hydrate due to missing Stencil runtime. Instead, import these components through the generated 'components.ts' * file that re-exports all components with the 'use client' directive.` : ""} */ `, u = `/* eslint-disable */ `, m = l ? "createComponent, createSSRComponent" : "createComponent", n = p.createSourceFile( "component.ts", `${s}${c}${u} import React from 'react'; import { ${m} } from '@stencil/react-output-target/runtime'; import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime'; ` ); for (const f of t) { const d = f.tagName, h = y(d), E = `${h}Element`, _ = `${h}CustomEvent`; n.addImportDeclaration({ moduleSpecifier: `${o}/${a}/${d}.js`, namedImports: [ { name: h, alias: E }, { name: "defineCustomElement", alias: `define${h}` } ] }); const I = (f.events || []).filter((e) => e.internal === !1), C = []; for (const e of I) if (Object.keys(e.complexType.references).length > 0) { for (const R of Object.keys(e.complexType.references)) e.complexType.references[R].location === "global" || n.addImportDeclaration({ moduleSpecifier: o, namedImports: [ { name: R, isTypeOnly: !0 } ] }); n.addImportDeclaration({ moduleSpecifier: o, namedImports: [ { name: _, isTypeOnly: !0 } ] }), C.push({ originalName: e.name, name: j(e.name), type: `EventName<${_}<${e.complexType.original}>>` }); } else C.push({ originalName: e.name, name: j(e.name), type: `EventName<CustomEvent<${e.complexType.original}>>` }); const S = `${h}Events`; n.addTypeAlias({ name: S, type: C.length > 0 ? `{ ${C.map((e) => `${e.name}: ${e.type}`).join(`, `)} }` : "NonNullable<unknown>" }); const x = `/*@__PURE__*/ createComponent<${E}, ${S}>({ tagName: '${d}', elementClass: ${E}, // @ts-ignore - React type of Stencil Output Target may differ from the React version used in the Nuxt.js project, this can be ignored. react: React, events: {${C.map((e) => `${e.name}: '${e.originalName}'`).join(`, `)}} as ${S}, defineCustomElement: define${h} })`, D = `/*@__PURE__*/ createSSRComponent<${E}, ${S}>({ tagName: '${d}', properties: {${f.properties.filter((e) => !!e.attribute).map((e) => `${e.name}: '${e.attribute}'`).join(`, `)}}, hydrateModule: import('${l}') })`, O = n.addVariableStatement({ declarationKind: U.Const, // React as never is a hack to by-pass a @types/react issue. declarations: [ { name: h, type: `StencilReactComponent<${E}, ${S}>`, initializer: l && !r.includes(d) ? `typeof window !== 'undefined' ? ${x} : ${D}` : x } ] }); $ ? n.addExportAssignment({ isExportEquals: !1, expression: h }) : O.setIsExported(!0); } return n.organizeImports(), n.formatText(), n.getFullText(); }, k = ({ components: t, esModules: o }) => { const p = new N({ useInMemoryFileSystem: !0 }).createSourceFile( "component.ts", `'use client'; ` + `/** * This file was automatically generated by the Stencil React Output Target. * Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. */ ` + `/* eslint-disable */ ` ); if (o) for (const r of t) { const s = r.tagName, i = y(s), c = y(r.tagName); p.addExportDeclaration({ moduleSpecifier: `./${c}`, namedExports: o ? [`default as ${i}`] : t.map((u) => y(u.tagName)) }); } else p.addExportDeclaration({ moduleSpecifier: "./components.server", namedExports: t.map((r) => y(r.tagName)) }); return p.organizeImports(), p.formatText(), p.getFullText(); }, L = async ({ stencilPackageName: t, components: o, outDir: a, customElementsDir: $, esModules: l, excludeComponents: g, project: p, hydrateModule: r, excludeServerSideRenderingFor: s }) => { const i = [], c = o.filter((m) => !(m.internal === !0 || g != null && g.includes(m.tagName))); if (c.length === 0) return []; const u = {}; if (l === !0) for (const m of c) { const n = y(m.tagName), f = v.join(a, `${n}.ts`), d = F({ components: [m], stencilPackageName: t, customElementsDir: $, defaultExport: !0, hydrateModule: r, excludeServerSideRenderingFor: s }); u[f] = d; } else { const m = r ? ( /** * If hydrate module is provided, we bundle all components in a single file for server side rendering. * Further down we then create a re-export file that imports this file and exports all components with * the 'use client' directive to ensure that the Stencil runtime is always being loaded but components * are also being rendered server side */ v.join(a, "components.server.ts") ) : v.join(a, "components.ts"), n = F({ components: c, stencilPackageName: t, customElementsDir: $, defaultExport: !1, hydrateModule: r, excludeServerSideRenderingFor: s }); u[m] = n; } if (r) { const m = v.join(a, "components.ts"), n = k({ components: c, esModules: l }); u[m] = n; } return await Promise.all( Object.entries(u).map(async ([m, n]) => { const f = p.createSourceFile(m, n, { overwrite: !0 }); await f.save(), i.push(f); }) ), i; }, T = "react-output-target", q = "dist/components", w = "dist-custom-elements", b = "dist-hydrate-script", V = ({ outDir: t, esModules: o, stencilPackageName: a, excludeComponents: $, customElementsDir: l, hydrateModule: g, excludeServerSideRenderingFor: p }) => { let r = q; return { type: "custom", name: T, validate(s) { if (l) r = l; else { const i = (s.outputTargets || []).find( (c) => c.type === w ); if (i == null) throw new Error( `The '${T}' requires '${w}' output target. Add { type: '${w}' }, to the outputTargets config.` ); if (i.dir !== void 0 && (r = i.dir), i.externalRuntime !== !1) throw new Error( `The '${T}' requires the '${w}' output target to have 'externalRuntime: false' set in its configuration.` ); } if (g && (s.outputTargets || []).find((c) => c.type === b) == null) throw new Error( `The '${T}' requires '${b}' output target when the 'hydrateModule' option is set. Add { type: '${b}' }, to the outputTargets config.` ); if (!t) throw new Error("The 'outDir' option is required."); if (a === void 0) { if (s.sys && s.packageJsonFilePath) { const { name: i } = JSON.parse(s.sys.readFileSync(s.packageJsonFilePath, "utf8")); a = i; } if (!a) throw new Error( `Unable to find the package name in the package.json file: ${s.packageJsonFilePath}. Please provide the stencilPackageName manually to the ${T} output target.` ); } }, async generator(s, i, c) { const u = c.createTimeSpan(`generate ${T} started`, !0), m = c.components, n = new N(), f = await L({ outDir: t, components: m, stencilPackageName: a, customElementsDir: r, esModules: o === !0, excludeComponents: $, project: n, hydrateModule: g, excludeServerSideRenderingFor: p }); await Promise.all( f.map((d) => i.fs.writeFile(d.getFilePath(), d.getFullText())) ), u.finish(`generate ${T} finished`); }, __internal_getCustomElementsDir() { return r; } }; }; export { V as reactOutputTarget };