crie
v0.1.12
Published
Generate React JSX intrinsic element typings from Angular Elements libraries — supports both legacy @Input/@Output and new signal APIs.
Downloads
34
Maintainers
Readme
crie
Generate strongly typed React JSX intrinsic element definitions from Angular Elements libraries. crie scans your Angular component sources, collects the available inputs and outputs (including the new input(), output(), and model() signal helpers), and emits .d.ts typings that let TypeScript-aware React tooling understand your custom elements. That means authoring JSX with the same confidence you have when consuming first-party React components.
Why "crie"? It stands for Convert React Intrinsic Elements. Bring the ergonomics of JSX authoring to Angular Elements projects without manually writing declaration files or duplicating prop contracts in two frameworks.
Table of contents
- Highlights
- Requirements
- Installation
- Quick start
- How it works
- Configuration
- CLI reference
- Generated output
- Recipes
- FAQ
- Development
- License
Highlights
- ✅ Supports modern Angular signals – understands
input(),output(), andmodel()usage alongside classic@Input()/@Output()decorators. - 🧭 Smart type resolution – re-exports referenced types into generated namespaces so JSX property types stay accurate.
- ⚙️ Configurable discoverability – point
crieat any Angular workspace layout with globinclude/excludepatterns. - 💡 React-friendly output – generates
JSX.IntrinsicElementsdefinitions plus optional HTML attribute mixing for easy consumption in React. - 🧱 CLI-first workflow – ship typings from CI/CD, package scripts, or local dev with a single command.
Requirements
- Node.js 18.18 or newer (matches the engine requirement declared in
package.json). - TypeScript project for your Angular Elements library. The CLI reads your
tsconfig.jsonto understand source files and module resolution.
Installation
crie can be installed globally or used through npx. For most projects, adding it as a dev dependency keeps everything versioned with your workspace.
# Local (recommended)
npm install --save-dev crie
# or
pnpm add -D crie
# or
yarn add --dev crie
# Global install (optional)
npm install --global crieQuick start
Install the package (see above).
Create a
crieconfig (optional – defaults cover many setups). See Configuration for details.Build your Angular components so TypeScript metadata is up to date. If you are using Angular's standalone components or a multi-project workspace, make sure the referenced
tsconfigmatches the compiled output you ship.Generate typings:
npx crie react-typesUse
--config <path>if your config lives outside the current directory.Verify the output – open the emitted
.d.tsfile and spot-check a few components to ensure their inputs/outputs look correct.Ship the generated file – usually under
dist/elements/alo-kit/global.d.tsby default. Commit it or publish it with your package.
How it works
crie walks your TypeScript project using ts-morph, reading Angular @Component metadata to find selectors that look like custom elements (kebab-case). For every component:
- Input sources:
- Classic
@Input()decorators on fields/getters/setters. - Signal-based
input()/model()factory calls.
- Classic
- Output sources:
- Classic
@Output()decorators. - Signal-based
output()factories. model()helpers create both an input and a correspondingon<Name>Changeevent.
- Classic
crie resolves the TypeScript types for these members. When a type comes from another module, it records the declaration and re-exports it in a generated namespace (AloTypes$<hash>). Finally, it writes a .d.ts file that augments React.JSX.IntrinsicElements with your custom tags, merging:
- Optional React HTML attributes (configurable).
- Strongly typed props for inputs.
- Typed
CustomEventhandlers for outputs (onFooby convention). - The standard
childrenprop.
Configuration
Configuration is fully optional. Without any file crie assumes:
root: the current working directorytsconfig:tsconfig.jsoninclude:['src/**/*.ts']exclude: unit test files, declaration files, and build outputoutDir:dist/elements/alo-kitoutFile:global.d.tsreact.addReactHtmlAttributes:truereact.emitWrappers:false(reserved for future use)react.wrapperDir:dist/react-wrappers
If a value sounds unfamiliar, jump to the options table or the annotated example config for a deeper explanation.
Config file locations
crie uses cosmiconfig under the hood. It searches upward from the provided --config directory (default .) for:
crie.config.jsoncrie.config.tscrie.config.mjscrie.config.cjspackage.json(criekey)
Options
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| root | string | process.cwd() | Base directory for resolving paths. Useful when your config lives in a monorepo root but you need to target a package subdirectory. |
| tsconfig | string | "tsconfig.json" | Path (relative to root) to the TypeScript configuration that describes your Angular project. |
| include | string[] | ["src/**/*.ts"] | Glob patterns (relative to root) to scan for Angular components. Use this to limit analysis to specific libraries. |
| exclude | string[] | See defaults above | Glob patterns to skip files (tests, generated output, etc.). |
| tagPrefix | string \| undefined | undefined | If provided, only selectors starting with this prefix are considered. Helpful when a library exports both elements and components. |
| outDir | string | "dist/elements/alo-kit" | Directory where the generated declaration file is written. |
| outFile | string | "global.d.ts" | File name for the generated declaration file. |
| widenPrimitivesToString | boolean | false | When true, primitive inputs are widened to string (handy if your elements are used declaratively in HTML where attributes are string-valued). |
| react.addReactHtmlAttributes | boolean | true | When true, each intrinsic element merges with React.HTMLAttributes<HTMLElement> so common props like className and style are available. Set to false if you want strictly custom inputs/outputs. |
| react.emitWrappers | boolean | false | Reserved for future wrapper generation. Currently unused by the CLI but accepted to avoid breaking config when the feature lands. |
| react.wrapperDir | string | "dist/react-wrappers" | Destination for future wrapper files. |
Annotated example config
The snippet below demonstrates every commonly used option inside crie.config.ts. Feel free to convert it to JSON/JS depending on your tooling — cosmiconfig accepts them all.
import { defineConfig } from "crie/config"; // If you prefer, export a plain object instead.
export default defineConfig({
// All paths below are resolved from this directory.
root: __dirname,
// Point at the TypeScript program that contains your Angular Elements.
tsconfig: "tsconfig.lib.json",
// Only scan the Angular library that actually exposes custom elements.
include: ["projects/storefront/src/**/*.ts"],
// Skip spec files, Storybook stories, or any generated output directories.
exclude: ["**/*.spec.ts", "**/*.stories.tsx", "dist/**"],
// Only emit React typings for selectors that start with "my-app-".
tagPrefix: "my-app-",
// Control where the generated declaration file ends up.
outDir: "dist/elements",
outFile: "global.d.ts",
// Shape how props and events surface inside React.
widenPrimitivesToString: false,
react: {
addReactHtmlAttributes: true,
emitWrappers: false,
wrapperDir: "dist/react-wrappers"
}
});Key takeaways:
rootgives you monorepo flexibility — point the config at the package folder even if the CLI runs from the repo root.include/excludeare powerful filters. Becausecrieuses fast-glob under the hood, patterns likeprojects/**/src/**/*.tsare supported.- React-specific options are grouped under
reactto keep the surface area small and future-proof. SetaddReactHtmlAttributestofalseif you want to avoid mixingclassName,style, etc. into your custom elements. widenPrimitivesToStringcomes in handy when your consumers mostly use HTML (where attributes are string-valued) instead of JSX.
Example configurations
{
"root": "packages/my-elements",
"tsconfig": "tsconfig.lib.json",
"include": ["src/**/*.ts"],
"outDir": "dist/typings",
"outFile": "elements.d.ts"
}import { defineConfig } from "crie/config"; // hypothetical helper, use plain object if not available
export default {
root: __dirname,
tsconfig: "tsconfig.lib.json",
include: ["projects/storefront/src/**/*.ts"],
tagPrefix: "my-app-",
react: {
addReactHtmlAttributes: true
}
};{
"name": "my-elements",
"version": "1.0.0",
"crie": {
"outDir": "dist/elements",
"widenPrimitivesToString": true
}
}CLI reference
crie react-types
Scan your Angular project and emit React JSX intrinsic element typings.
Usage: crie react-types [options]
Options:
--config <path> path to crie.config.* directory (default: .)
-h, --help display help for commandThe command prints the resolved config, processes your components, and writes the declaration file. If no qualifying components are found, it exits with status code 1.
Exit codes
0– Success.1– No components matched or an error occurred.
Generated output
The resulting declaration file (default dist/elements/alo-kit/global.d.ts) looks similar to:
/* Auto-generated by crie. Do not edit manually. */
declare namespace AloTypes$1a2b3c4d {
interface ProductCardInput {
/* ... */
}
}
export {};
declare global {
namespace React {
namespace JSX {
interface IntrinsicElements {
'my-product-card':
React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
& {
sku?: string;
quantity?: number;
}
& {
onAddToCart?: (e: CustomEvent<ProductPayload>) => void;
}
& { children?: React.ReactNode };
}
}
}
}Each component tag maps to an intersection type composed of:
- Optional React HTML attributes (configurable).
- A type literal for inputs (
@Input,input(), etc.). - A type literal for outputs exposed as
on<EventName>handlers returningCustomEvent. children?: React.ReactNode.
Recipes
Publishing typings with your package
Add a build step before publishing:
{ "scripts": { "prepublishOnly": "npm run build && crie react-types" } }Ensure
dist(or your configured directory) is included in thefilesarray or.npmignore.Publish as usual – the generated
global.d.tswill appear on npm thanks to the entry inpackage.json#files.
Using the declarations in a React app
Install your Angular Elements package.
Import the generated declaration file once in your app (or reference it via
typesinpackage.json). For global augmentations you can simply rely on TypeScript's automatic inclusion if the file lives insidenode_modules/<pkg>/dist/....Start writing JSX with IntelliSense:
export const Example = () => ( <my-product-card sku="SKU-42" quantity={2} onAddToCart={(e) => console.log(e.detail)} /> );
TypeScript and editors now know the valid props and event payloads for your custom elements.
FAQ
Development
To work on crie itself:
npm install
npm run build # or npm run dev for watch mode
npm test # powered by VitestBefore publishing, npm run release bumps the patch version and publishes to npm.
License
MIT © Mahdi Zarei (skyBlueDev)
