@stencil/react-output-target
Version:
React output target for @stencil/core components.
401 lines (392 loc) • 13.9 kB
JavaScript
import N from "node:path";
import { Project as P, VariableDeclarationKind as M } from "ts-morph";
const _ = (o) => o.toLowerCase().split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(""), z = (o) => o.replace(/-([_a-z])/g, (s, u) => u.toUpperCase()), H = (o) => o.replace(/\/([a-z])/g, (s, u) => u.toUpperCase()), J = (o) => {
const s = H(o);
return z(`on-${s}`);
}, O = (o) => o.replace(/\/\/.*$/gm, "").replace(/\n/g, " ").replace(/\s{2,}/g, " ").trim(), K = async ({
components: o,
project: s,
outDir: u
}) => {
const i = s || new P({ useInMemoryFileSystem: !0 }), n = `/* eslint-disable */
`, $ = `/**
* 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.
*/
`, f = N.join(u || "", "components.ts"), d = i.createSourceFile(f, $ + n, {
overwrite: !0
});
for (const y of o) {
const b = y.tagName, v = _(b), T = y.tagName;
d.addExportDeclaration({
moduleSpecifier: `./${T}.js`,
namedExports: [v]
});
}
return d.organizeImports(), d.formatText(), await d.save(), d;
}, A = ({
components: o,
stencilPackageName: s,
customElementsDir: u,
hydrateModule: i,
clientModule: n,
serializeShadowRoot: $,
transformTag: f
}) => {
const d = new P({ useInMemoryFileSystem: !0 }), y = i ? "" : `'use client';
`, b = `/**
* 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.
*/
`, v = `/* eslint-disable */
`, T = f ? `import { getTagTransformer } from './tag-transformer.js';
` : "", l = i ? [
"// @ts-ignore - ignore potential type issues as the project is importing itself",
`import * as clientComponents from '${n}';`,
T,
"import { createComponent, type SerializeShadowRootOptions, type HydrateModule, type ReactWebComponent, type DynamicFunction } from '@stencil/react-output-target/ssr';"
].filter(Boolean).join(`
`) : "import { createComponent } from '@stencil/react-output-target/runtime';", c = f ? `import { transformTag } from './tag-transformer.js';
` : "", e = d.createSourceFile(
"component.ts",
`${y}${b}${v}
import React from 'react';
${l}
import type { EventName, StencilReactComponent } from '@stencil/react-output-target/runtime';
${c}
`
);
i && e.addVariableStatement({
isExported: !0,
declarationKind: M.Const,
declarations: [
{
name: "serializeShadowRoot",
type: "SerializeShadowRootOptions",
initializer: $ ? JSON.stringify($) : '{ default: "declarative-shadow-dom" }'
}
]
});
for (const h of o) {
const a = h.tagName, r = _(a), t = `${r}Element`, g = `${r}CustomEvent`;
e.addImportDeclaration({
moduleSpecifier: `${s}/${u}/${a}.js`,
namedImports: [
{
name: r,
alias: t
},
{
name: "defineCustomElement",
alias: `define${r}`
}
]
});
const E = (h.events || []).filter((m) => m.internal === !1), S = [], p = /* @__PURE__ */ new Set();
let j = !1;
for (const m of E) {
if (Object.keys(m.complexType.references).length > 0)
for (const F of Object.keys(m.complexType.references))
!(m.complexType.references[F].location === "global") && !p.has(F) && (p.add(F), e.addImportDeclaration({
moduleSpecifier: s,
namedImports: [
{
name: F,
isTypeOnly: !0
}
]
}));
j || (j = !0, e.addImportDeclaration({
moduleSpecifier: s,
namedImports: [
{
name: g,
isTypeOnly: !0
}
]
})), S.push({
originalName: m.name,
name: J(m.name),
type: `EventName<${g}<${O(m.complexType.original)}>>`
});
}
const w = `${r}Events`;
e.addTypeAlias({
isExported: !0,
name: w,
type: S.length > 0 ? `{ ${S.map((m) => `${m.name}: ${m.type}`).join(`,
`)} }` : "NonNullable<unknown>"
});
const R = f ? `,
transformTag` : "", L = `/*@__PURE__*/ createComponent<${t}, ${w}>({
tagName: '${a}',
elementClass: ${t},
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
react: React,
events: {${S.map((m) => `${m.name}: '${m.originalName}'`).join(`,
`)}} as ${w},
defineCustomElement: define${r}${R}
})`, D = f ? `,
getTagTransformer` : "", U = `/*@__PURE__*/ createComponent<${t}, ${w}>({
tagName: '${a}',
properties: {${h.properties.filter((m) => !!m.attribute).map((m) => `${m.name}: '${m.attribute}'`).join(`,
`)}},
hydrateModule: import('${i}') as Promise<HydrateModule>,
clientModule: clientComponents.${r} as ReactWebComponent<${t}, ${w}>,
serializeShadowRoot${D}
})`;
e.addVariableStatement({
isExported: !0,
declarationKind: M.Const,
// React as never is a hack to by-pass a @types/react issue.
declarations: [
{
name: r,
type: `StencilReactComponent<${t}, ${w}>`,
initializer: i ? U : L
}
]
});
}
return e.organizeImports(), e.formatText(), e.getFullText();
}, V = ({
stencilPackageName: o,
customElementsDir: s
}) => `/* eslint-disable */
/* tslint:disable */
import { setTagTransformer as clientSetTagTransformer } from '${o}/${s}/index.js';
let tagTransformer: ((tagName: string) => string) | undefined;
export const setTagTransformer = (transformer: (tagName: string) => string) => {
clientSetTagTransformer(transformer);
tagTransformer = transformer;
};
export const transformTag = (tag: string): string => {
return tagTransformer ? tagTransformer(tag) : tag;
};
export const getTagTransformer = () => tagTransformer;
`, q = async ({
stencilPackageName: o,
components: s,
outDir: u,
esModules: i,
customElementsDir: n,
excludeComponents: $,
project: f,
hydrateModule: d,
clientModule: y,
excludeServerSideRenderingFor: b,
serializeShadowRoot: v,
transformTag: T
}) => {
const l = [], c = s.filter((a) => !(a.internal === !0 || $ != null && $.includes(a.tagName)));
if (c.length === 0)
return [];
const e = {};
function h(a, r = "components") {
const t = N.join(u, `${r}.ts`), g = A({
components: a,
stencilPackageName: o,
customElementsDir: n,
transformTag: T
});
if (e[t] = g, T) {
const E = N.join(u, "tag-transformer.ts");
e[E] = V({ stencilPackageName: o, customElementsDir: n });
}
if (d) {
const E = N.join(u, `${r}.server.ts`), S = A({
components: a.filter(
(p) => !b || !b.includes(p.tagName)
),
stencilPackageName: o,
customElementsDir: n,
hydrateModule: d,
clientModule: y,
serializeShadowRoot: v,
transformTag: T
});
e[E] = S;
}
}
if (i) {
for (const r of c)
h([r], r.tagName);
const a = await K({ components: c, project: f, outDir: u });
l.push(a);
} else
h(c);
return await Promise.all(
Object.entries(e).map(async ([a, r]) => {
const t = f.createSourceFile(a, r, { overwrite: !0 });
await t.save(), l.push(t);
})
), l;
}, G = (o) => `on${o.toLowerCase()}`, W = ({
components: o,
stencilPackageName: s,
excludeComponents: u
}) => {
var y, b, v, T, l, c;
const i = o.filter((e) => !(e.internal === !0 || u != null && u.includes(e.tagName)));
if (i.length === 0)
return "";
const n = [];
n.push(`/**
* 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.
*
* This file provides TypeScript type definitions for using Stencil web components
* as native custom elements in React 19+.
*
* Usage:
* Import this file in your React application to get type support for custom elements:
* \`\`\`tsx
* import '${s}/react-native-types';
* \`\`\`
*/
/* eslint-disable */
/* tslint:disable */
`), n.push("// @ts-ignore - React types may not be available in all build contexts"), n.push("import 'react';"), n.push("// @ts-ignore - React types may not be available in all build contexts"), n.push("import type { DetailedHTMLProps, HTMLAttributes } from 'react';");
const $ = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set();
for (const e of i) {
const h = _(e.tagName), a = (e.events || []).filter((t) => !t.internal);
a.length > 0 && $.add(`${h}CustomEvent`);
for (const t of a)
if ((y = t.complexType) != null && y.references)
for (const [g, E] of Object.entries(t.complexType.references))
E.location !== "global" && f.add(g);
const r = (e.properties || []).filter((t) => !t.internal);
for (const t of r)
if ((b = t.complexType) != null && b.references)
for (const [g, E] of Object.entries(t.complexType.references))
E.location !== "global" && f.add(g);
}
const d = /* @__PURE__ */ new Set([...$, ...f]);
d.size > 0 && n.push(`import type { ${Array.from(d).sort().join(", ")} } from '${s}';`), n.push("");
for (const e of i) {
const h = e.tagName, a = _(h), r = `${a}NativeProps`, t = `${a}CustomEvent`, g = [], E = (e.properties || []).filter((p) => !p.internal);
for (const p of E) {
const j = ((v = p.complexType) == null ? void 0 : v.original) || "any", w = (T = p.docs) != null && T.text ? ` /** ${p.docs.text.trim()} */
` : "", R = p.name;
g.push(`${w} '${R}'?: ${O(j)};`);
}
const S = (e.events || []).filter((p) => !p.internal);
for (const p of S) {
const j = O(((l = p.complexType) == null ? void 0 : l.original) || "void"), w = (c = p.docs) != null && c.text ? ` /** Event: ${p.name} - ${p.docs.text.trim()} */
` : ` /** Event: ${p.name} */
`, R = G(p.name);
g.push(`${w} '${R}'?: (event: ${t}<${j}>) => void;`);
}
g.length > 0 ? (n.push(`interface ${r} {`), n.push(g.join(`
`)), n.push("}")) : n.push(`interface ${r} {}`), n.push("");
}
n.push("declare module 'react/jsx-runtime' {"), n.push(" namespace JSX {"), n.push(" interface IntrinsicElements {");
for (const e of i) {
const h = e.tagName, a = _(h), r = `${a}NativeProps`, t = `HTML${a}Element`;
n.push(
` '${h}': DetailedHTMLProps<HTMLAttributes<${t}> & ${r}, ${t}>;`
);
}
return n.push(" }"), n.push(" }"), n.push("}"), n.push(""), n.join(`
`);
}, k = "react-native-types.d.ts", C = "react-output-target", B = "dist/components", I = "dist-custom-elements", x = "dist-hydrate-script", ee = ({
outDir: o,
nativeTypesPath: s,
esModules: u,
stencilPackageName: i,
excludeComponents: n,
customElementsDir: $,
hydrateModule: f,
clientModule: d,
excludeServerSideRenderingFor: y,
serializeShadowRoot: b,
transformTag: v
}) => {
let T = B;
return {
type: "custom",
name: C,
validate(l) {
if (!o && !s)
throw new Error(`The '${C}' requires either 'outDir' or 'nativeTypesPath' to be specified.`);
if (o) {
if ($)
T = $;
else {
const c = (l.outputTargets || []).find(
(e) => e.type === I
);
if (c == null)
throw new Error(
`The '${C}' requires '${I}' output target when 'outDir' is specified. Add { type: '${I}' }, to the outputTargets config.`
);
if (c.dir !== void 0 && (T = c.dir), c.externalRuntime !== !1)
throw new Error(
`The '${C}' requires the '${I}' output target to have 'externalRuntime: false' set in its configuration.`
);
}
if (f) {
if ((l.outputTargets || []).find((e) => e.type === x) == null)
throw new Error(
`The '${C}' requires '${x}' output target when the 'hydrateModule' option is set. Add { type: '${x}' }, to the outputTargets config.`
);
if (d == null)
throw new Error(
`The '${C}' requires the 'clientModule' option when the 'hydrateModule' option is set. Please provide the clientModule manually to the ${C} output target.`
);
}
}
if (i === void 0) {
if (l.sys && l.packageJsonFilePath) {
const { name: c } = JSON.parse(l.sys.readFileSync(l.packageJsonFilePath, "utf8"));
i = c;
}
if (!i)
throw new Error(
`Unable to find the package name in the package.json file: ${l.packageJsonFilePath}. Please provide the stencilPackageName manually to the ${C} output target.`
);
}
},
async generator(l, c, e) {
const h = e.createTimeSpan(`generate ${C} started`, !0), a = e.components;
if (o) {
const r = new P(), t = await q({
outDir: o,
components: a,
stencilPackageName: i,
customElementsDir: T,
excludeComponents: n,
esModules: u === !0,
project: r,
hydrateModule: f,
clientModule: d,
excludeServerSideRenderingFor: y,
serializeShadowRoot: b,
transformTag: v
});
await Promise.all(
t.map((g) => c.fs.writeFile(g.getFilePath(), g.getFullText()))
);
}
if (s) {
const r = W({
components: a,
stencilPackageName: i,
excludeComponents: n
});
if (r) {
let t = s.endsWith(".d.ts") ? s : N.join(s, k);
!N.isAbsolute(t) && l.rootDir && (t = N.join(l.rootDir, t)), await c.fs.writeFile(t, r);
}
}
h.finish(`generate ${C} finished`);
},
__internal_getCustomElementsDir() {
return T;
}
};
};
export {
ee as reactOutputTarget
};
//# sourceMappingURL=index.js.map