├── .babelrc.js
├── .circleci
└── config.yml
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── README.md
├── codegen
├── componentCode
│ ├── index.ts
│ ├── lib
│ │ ├── camelCase.ts
│ │ └── propsCodeReducer.ts
│ ├── preamble.tsx
│ └── templates
│ │ ├── componentOverrideTemplate.ts
│ │ ├── componentTemplate.ts
│ │ └── mandatoryComponentOverrideTemplate.ts
├── constants.ts
├── duplicateWrapperComponentCode.ts
├── index.ts
├── rules.ts
└── tagNameToComponentName.ts
├── helpers.d.ts
├── helpers.js
├── index.d.ts
├── index.js
├── jest.config.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── setup.d.ts
├── setup.js
├── setupTest.ts
├── src
├── __tests__
│ ├── __snapshots__
│ │ └── react-amphtml.spec.tsx.snap
│ └── react-amphtml.spec.tsx
├── amphtml
│ └── components
│ │ ├── AmpState.tsx
│ │ ├── Html.tsx
│ │ ├── Script.tsx
│ │ ├── __tests__
│ │ ├── AmpState.spec.tsx
│ │ ├── Html.spec.tsx
│ │ └── Script.spec.tsx
│ │ └── jsx.d.ts
├── constants.ts
├── helpers
│ ├── Action.ts
│ ├── Bind.ts
│ └── helpers.ts
├── lib
│ ├── __tests__
│ │ └── getScriptSource.spec.ts
│ ├── contextHelper.ts
│ └── getScriptSource.ts
└── setup
│ ├── AmpScripts.tsx
│ ├── AmpScriptsManager.tsx
│ ├── headerBoilerplate.tsx
│ └── setup.ts
├── tsconfig.declarations.json
└── tsconfig.json
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@babel/preset-typescript',
4 | [
5 | '@babel/preset-env',
6 | {
7 | targets: {
8 | node: '8',
9 | },
10 | },
11 | ],
12 | '@babel/preset-react',
13 | ],
14 | plugins: [
15 | '@babel/plugin-proposal-export-default-from',
16 | 'babel-plugin-codegen',
17 | ],
18 | };
19 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node:8.10.0
6 | working_directory: ~/react-amphtml
7 | steps:
8 | - checkout
9 | - restore_cache:
10 | keys:
11 | - v1-node-{{ arch }}-{{ .Branch }}-{{ checksum "package-lock.json" }}
12 | - v1-node-{{ arch }}-{{ .Branch }}-
13 | - v1-node-{{ arch }}-
14 | - run: npm install
15 | - run: npm run codegen
16 | - run: npm run build
17 | - run: npm run typecheck
18 | - run: npm run lint
19 | - run: npm run test
20 | - save_cache:
21 | key: v1-node-{{ arch }}-{{ .Branch }}-{{ checksum "package-lock.json" }}
22 | paths:
23 | - node_modules
24 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /src/amphtml/amphtml.tsx
4 | !.eslintrc.js
5 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-var-requires */
2 | const typescriptEslintRecommended = require('@typescript-eslint/eslint-plugin/dist/configs/recommended.json');
3 | const typescriptEslintPrettier = require('eslint-config-prettier/@typescript-eslint');
4 |
5 | module.exports = {
6 | env: {
7 | node: true,
8 | },
9 | parser: 'babel-eslint',
10 | extends: [
11 | 'airbnb',
12 | 'plugin:@typescript-eslint/recommended',
13 | 'prettier',
14 | 'prettier/react',
15 | 'prettier/@typescript-eslint',
16 | ],
17 | plugins: ['prettier'],
18 | rules: {
19 | 'prettier/prettier': 'error',
20 | 'react/jsx-filename-extension': 'off',
21 | 'import/no-extraneous-dependencies': [
22 | 'error',
23 | {
24 | devDependencies: [
25 | '.eslintrc.js',
26 | 'rollup.config.js',
27 | '**/__tests__/**/*',
28 | 'setupTest.ts',
29 | 'codegen/**/*',
30 | ],
31 | },
32 | ],
33 | '@typescript-eslint/no-unused-vars': [
34 | 'error',
35 | {
36 | devDependencies: [
37 | 'rollup.config.js',
38 | '.eslintrc.js',
39 | '**/__tests__/**/*',
40 | ],
41 | },
42 | ],
43 | },
44 | settings: {
45 | 'import/resolver': {
46 | node: {
47 | extensions: ['.js', '.ts', '.tsx'],
48 | },
49 | },
50 | },
51 | overrides: [
52 | {
53 | files: ['*.ts', '*.tsx'],
54 | parser: '@typescript-eslint/parser',
55 | // NOTE: Workaround for no nested extends possible.
56 | // See https://github.com/eslint/eslint/issues/8813.
57 | // Working solution would be following, if we had nested extends:
58 | // ```
59 | // extends: [
60 | // 'airbnb-base',
61 | // 'plugin:@typescript-eslint/recommended',
62 | // 'prettier/@typescript-eslint',
63 | // 'prettier',
64 | // ],
65 | // ```
66 | plugins: ['@typescript-eslint', 'prettier'],
67 | rules: Object.assign(
68 | typescriptEslintRecommended.rules,
69 | typescriptEslintPrettier.rules,
70 | {
71 | '@typescript-eslint/explicit-function-return-type': 'error',
72 | },
73 | ),
74 | },
75 | {
76 | files: [
77 | 'setupTest.js',
78 | 'setupTest.ts',
79 | '*.spec.js',
80 | '*.spec.ts',
81 | '*.spec.tsx',
82 | ],
83 | env: {
84 | jest: true,
85 | },
86 | },
87 | ],
88 | };
89 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules
3 | /dist
4 | /coverage
5 | /src/amphtml/amphtml.tsx
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | !.eslintrc.js
4 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | trailingComma: 'all',
3 | singleQuote: true,
4 | };
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-amphtml
2 |
3 | Use [`amphtml`][amp repo] components inside your React apps easily!
4 |
5 | ## Usage
6 |
7 | `react-amphtml` exports React components and functions to easily create AMP HTML
8 | pages. Each exported React component has a TypeScript interface and PropTypes
9 | derived from AMP HTML's own validator rules to speed up development and make it
10 | safer. Boilerplate and the inclusion of AMP directive-specific scripts is all
11 | handled for you!
12 |
13 | ```js
14 | // All AMP elements
15 | import * as Amp from 'react-amphtml';
16 |
17 | // Helper render props for actions and bindings
18 | import * as AmpHelpers from 'react-amphtml/helpers';
19 |
20 | // Components and functions to render pages
21 | import {
22 | AmpScripts,
23 | AmpScriptsManager,
24 | headerBoilerplate,
25 | } from 'react-amphtml/setup';
26 | ```
27 |
28 | ### Amp Components
29 |
30 | ```js
31 | import * as Amp from 'react-amphtml';
32 | // ...
33 |
34 | ```
35 |
36 | The main file exported by `react-amphtml` contains all of the AMP HTML
37 | directives as React components. This includes the custom element `amp-*`
38 | directives, normal HTML directives with validations required by AMP, and some
39 | components with added functionality: `Html`, `AmpState` (`amp-state` directive)
40 | and `Script`.
41 |
42 | To see a list of available components and their relative documentation see the
43 | official AMP components documentation: [The AMP component catalogue][].
44 |
45 | [The AMP component catalogue]: https://amp.dev/documentation/components/
46 |
47 | ### Amp Helpers
48 |
49 | ```js
50 | import * as Amp from 'react-amphtml';
51 | import * as AmpHelpers from 'react-amphtml/helpers';
52 |
53 | // Example of attaching actions to elements
54 |
55 | {(props) => (
56 |
59 | )}
60 |
61 |
62 | // Example of using state and bindings together
63 | const defaultHeading = {
64 | text: 'Hello, World!',
65 | };
66 | // ...
67 |
68 | {defaultHeading}
69 |
70 |
71 | {(props): ReactElement => {defaultHeading.text}
}
72 |
73 | ```
74 |
75 | The `helpers` file contains render prop components that help add AMP attribute
76 | directives for actions and bindings. Wondering what actions and bindings are all
77 | about? Check out these official guides on the subjects:
78 |
79 | * [Actions and events][]
80 | * [Create interactive AMP pages][]
81 |
82 | [Create interactive AMP pages]: https://amp.dev/documentation/guides-and-tutorials/develop/interactivity/
83 | [Actions and events]: https://amp.dev/documentation/guides-and-tutorials/learn/amp-actions-and-events
84 |
85 | ### Amp Setup
86 |
87 | ```js
88 | import * as Amp from 'react-amphtml';
89 | import {
90 | AmpScripts,
91 | AmpScriptsManager,
92 | headerBoilerplate,
93 | } from 'react-amphtml/setup';
94 |
95 | const ampScripts = new AmpScripts();
96 |
97 | const bodyContent = renderToStaticMarkup(
98 |
99 |
110 | ,
111 | );
112 |
113 | /* eslint-disable react/no-danger */
114 | const html = renderToStaticMarkup(
115 |
116 |
117 | {headerBoilerplate('/')}
118 | react-amphtml
119 | {ampScripts.getScriptElements()}
120 |
121 |
122 | ,
123 | );
124 | /* eslint-enable */
125 |
126 | const htmlPage = `
127 |
128 | ${html}
129 | `;
130 | ```
131 |
132 | The `setup` file makes creating pages for AMP HTML a breeze. It helps insert all
133 | the necessary boilerplate and also the scripts needed for AMP directives.
134 |
135 | The code is based on the requirements from AMP documented in
136 | [Create your AMP HTML page: Required mark-up][].
137 |
138 | [Create your AMP HTML page: Required mark-up]: https://amp.dev/documentation/guides-and-tutorials/start/create/basic_markup#required-mark-up
139 |
140 | ## Examples
141 |
142 | ### Full Example
143 |
144 | **Go checkout [`ampreact`][]!**
145 |
146 | If you are looking for an example that is in combination with one or more of
147 | these tools:
148 |
149 | * [AMP HTML][]
150 | * [Next.js][]
151 | * [React][]
152 | * [styled-components][]
153 | * [GraphQL][]
154 | * [TypeScript][]
155 |
156 | [`ampreact`][] gives a very nice setup to get started with or learn from!
157 |
158 | [AMP HTML]: https://github.com/ampproject/amphtml/
159 | [Next.js]: https://github.com/zeit/next.js/
160 | [React]: https://github.com/facebook/react/
161 | [styled-components]: https://github.com/styled-components/styled-components/
162 | [GraphQL]: https://github.com/graphql/graphql-js
163 | [TypeScript]: https://github.com/microsoft/TypeScript
164 | [`ampreact`]: https://github.com/dfrankland/ampreact
165 |
166 | ### Simple Example
167 |
168 | For simple usage examples of `react-amphtml`, check the Jest unit tests in
169 | [`react-amphtml/src/__tests__/react-amphtml.spec.tsx`][]. The best test to look
170 | at is `can server-side render valid html` for a good complete usage of
171 | `react-amphtml`.
172 |
173 | [`react-amphtml/src/__tests__/react-amphtml.spec.tsx`]: https://github.com/dfrankland/react-amphtml/blob/master/src/__tests__/react-amphtml.spec.tsx
174 |
175 | ## Development
176 |
177 | ### About
178 |
179 | The code for `react-amphtml` is generated from [AMP HTML's own validator][] via
180 | [`amphtml-validator-rules`][].
181 |
182 | Want to learn about AMP HTML validation? See the guide: [Validate AMP pages][].
183 |
184 | Need to run the validator? Use either the online tool [The AMP Validator][] or
185 | the npm package [`amphtml-validator`][].
186 |
187 | [AMP HTML's own validator]: https://amp.dev/documentation/guides-and-tutorials/learn/validation-workflow/validate_amp
188 | [Validate AMP pages]: https://github.com/ampproject/amphtml/tree/master/validator#amp-html--validator
189 | [The AMP Validator]: https://validator.ampproject.org/
190 | [`amphtml-validator`]: https://www.npmjs.com/package/amphtml-validator
191 |
192 | ### Commands
193 |
194 | Use the following commands to develop on `react-amphtml`.
195 |
196 | * `npm run codegen`: Create components based on AMP HTML's validator. This
197 | must be done at least once prior to running `npm run build`, and can be done
198 | afterwards anytime code in `codegen` is modified.
199 |
200 | * `npm run build`: Bundles the source files into `dist`.
201 |
202 | * `npm run typecheck`: Uses TypeScript to ensure type safety. Should be run
203 | after running `npm run build` to check the files in `dist` that are bundled.
204 |
205 | * `npm run lint`: Use ESLint to check source files.
206 |
207 | * `npm run test`: Use Jest to run tests.
208 |
209 | ## Resources
210 |
211 | - [`amphtml-validator-rules`][]: the rules that get used to generate
212 | components
213 |
214 | - AMP Project's [`amphtml` repo][amp repo]
215 |
216 | - [Builtins][]
217 |
218 | - [Extensions][]
219 |
220 | [`amphtml-validator-rules`]: https://github.com/dfrankland/amphtml-validator-rules
221 | [Builtins]: https://github.com/ampproject/amphtml/tree/master/builtins
222 | [Extensions]: https://github.com/ampproject/amphtml/tree/master/extensions
223 |
224 | [amp repo]: https://github.com/ampproject/amphtml
225 |
--------------------------------------------------------------------------------
/codegen/componentCode/index.ts:
--------------------------------------------------------------------------------
1 | import { readFileSync } from 'fs';
2 | import { resolve as resolvePath } from 'path';
3 | import newRules, { NewTag } from '../rules';
4 | import {
5 | MANDATORY_COMPONENT_OVERRIDES,
6 | COMPONENT_OVERRIDES,
7 | BLACKLIST,
8 | } from '../constants';
9 | import tagNameToComponentName from '../tagNameToComponentName';
10 | import propsCodeReducer from './lib/propsCodeReducer';
11 | import mandatoryComponentOverrideTemplate from './templates/mandatoryComponentOverrideTemplate';
12 | import componentOverrideTemplate from './templates/componentOverrideTemplate';
13 | import componentTemplate from './templates/componentTemplate';
14 |
15 | const EXTENSION_TYPE_CUSTOM_TEMPLATE = 'CUSTOM_TEMPLATE';
16 |
17 | export default newRules.tags.reduce(
18 | (
19 | code: string,
20 | {
21 | tagName,
22 | dupeName,
23 | attrs,
24 | attrLists,
25 | requiresExtension,
26 | extensionSpec,
27 | mandatoryAncestorSuggestedAlternative,
28 | }: NewTag,
29 | ): string => {
30 | if (BLACKLIST[tagName] || mandatoryAncestorSuggestedAlternative)
31 | return code;
32 |
33 | const componentName = tagNameToComponentName(dupeName || tagName);
34 |
35 | if (MANDATORY_COMPONENT_OVERRIDES[tagNameToComponentName(tagName)]) {
36 | return mandatoryComponentOverrideTemplate({
37 | code,
38 | tagName,
39 | componentName,
40 | });
41 | }
42 |
43 | const propsCode = propsCodeReducer({ tagName, attrs, attrLists });
44 |
45 | const requiresExtensionContext = (Array.isArray(requiresExtension)
46 | ? requiresExtension
47 | : []
48 | ).reduce(
49 | (requiresExtensionContextCode, requiredExtension): string => `
50 | ${requiresExtensionContextCode}
51 | contextHelper({ context, extension: '${requiredExtension}', version: props.version });
52 | `,
53 | '',
54 | );
55 |
56 | const [extensionPropsGiven, extensionProps] =
57 | extensionSpec && typeof extensionSpec === 'object'
58 | ? [
59 | true,
60 | {
61 | extension: extensionSpec.name,
62 | isCustomTemplate:
63 | extensionSpec.extensionType === EXTENSION_TYPE_CUSTOM_TEMPLATE,
64 | },
65 | ]
66 | : [false, {}];
67 |
68 | const contextArgument = requiresExtensionContext
69 | ? ', context: AmpScriptsManagerContext'
70 | : '';
71 |
72 | if (COMPONENT_OVERRIDES[tagNameToComponentName(tagName)]) {
73 | return componentOverrideTemplate({
74 | code,
75 | tagName,
76 | componentName,
77 | dupeName,
78 | extensionSpec,
79 | extensionPropsGiven,
80 | extensionProps,
81 | requiresExtensionContext,
82 | contextArgument,
83 | propsCode,
84 | });
85 | }
86 |
87 | return componentTemplate({
88 | code,
89 | tagName,
90 | componentName,
91 | dupeName,
92 | requiresExtensionContext,
93 | contextArgument,
94 | propsCode,
95 | });
96 | },
97 | readFileSync(resolvePath(__dirname, './preamble.tsx')).toString('utf8'),
98 | );
99 |
--------------------------------------------------------------------------------
/codegen/componentCode/lib/camelCase.ts:
--------------------------------------------------------------------------------
1 | export default (tagName: string): string =>
2 | tagName.toLowerCase().replace(/-(.)/, (_, m): string => m.toUpperCase());
3 |
--------------------------------------------------------------------------------
/codegen/componentCode/lib/propsCodeReducer.ts:
--------------------------------------------------------------------------------
1 | import { TagAttr, AttrListAttr } from 'amphtml-validator-rules';
2 | import newRules, { NewTag } from '../../rules';
3 | import camelCase from './camelCase';
4 | import { JSX_INTRINSICS } from '../../constants';
5 |
6 | export interface PropsCode {
7 | propTypesCode: { [key: string]: string };
8 | defaultPropsCode: { [key: string]: string };
9 | propsInterfaceCode: { [key: string]: string };
10 | }
11 |
12 | interface TypeProp {
13 | propertyName: string;
14 | type: 'string' | 'boolean';
15 | propType: string;
16 | interfaceProperty: string;
17 | defaultProp?: string;
18 | }
19 |
20 | export default ({
21 | tagName,
22 | attrs,
23 | attrLists,
24 | }: Pick): PropsCode =>
25 | [
26 | ...(attrs || []),
27 | ...(attrLists || []).reduce(
28 | (
29 | allAttrFromLists: AttrListAttr[],
30 | attrListName: string,
31 | ): AttrListAttr[] => [
32 | ...allAttrFromLists,
33 | ...(
34 | newRules.attrLists.find(
35 | ({ name }): boolean => name === attrListName,
36 | ) || { attrs: [] }
37 | ).attrs,
38 | ],
39 | [],
40 | ),
41 | ].reduce(
42 | (
43 | { propTypesCode, defaultPropsCode, propsInterfaceCode },
44 | attr: TagAttr | AttrListAttr,
45 | ): PropsCode => {
46 | const {
47 | name,
48 | value,
49 | // TODO: Use these as well
50 | // valueCasei,
51 | // blacklistedValueRegex,
52 | // valueUrl,
53 | // valueRegex,
54 | // valueProperties,
55 | // valueRegexCasei,
56 | mandatory: mandatoryAttr,
57 | } = attr;
58 |
59 | const isCustomElement = !JSX_INTRINSICS[camelCase(tagName)];
60 | if ((isCustomElement && name === 'style') || name === 'version') {
61 | return { propTypesCode, defaultPropsCode, propsInterfaceCode };
62 | }
63 |
64 | const type = ((): TypeProp => {
65 | const propertyName = JSON.stringify(name);
66 | const mandatoryPropType = mandatoryAttr ? '.isRequired' : '';
67 | const mandatoryType = mandatoryAttr ? '' : ' | undefined';
68 | if (!value) {
69 | return {
70 | propertyName,
71 | type: 'string',
72 | interfaceProperty: `string${mandatoryType}`,
73 | propType: `PropTypes.string${mandatoryPropType}`,
74 | };
75 | }
76 |
77 | if (value.length === 1 && value[0] === '') {
78 | return {
79 | propertyName,
80 | type: 'boolean',
81 | interfaceProperty: `boolean${mandatoryType}`,
82 | propType: `PropTypes.bool${mandatoryPropType}`,
83 | defaultProp: JSON.stringify(false),
84 | };
85 | }
86 |
87 | const [firstValue] = value;
88 | return {
89 | propertyName,
90 | type: 'string',
91 | interfaceProperty: `'${value.join("' | '")}'${mandatoryType}`,
92 | propType: `PropTypes.oneOf<'${value.join("' | '")}'>(${JSON.stringify(
93 | value,
94 | )})${mandatoryPropType}`,
95 | defaultProp: JSON.stringify(firstValue),
96 | };
97 | })();
98 |
99 | const newPropTypesCode = {
100 | ...propTypesCode,
101 | [type.propertyName]: type.propType,
102 | };
103 |
104 | const newDefaultPropsCode =
105 | mandatoryAttr || !type.defaultProp
106 | ? defaultPropsCode
107 | : {
108 | ...defaultPropsCode,
109 | [type.propertyName]: type.defaultProp,
110 | };
111 |
112 | const newPropsInterfaceCode = {
113 | ...propsInterfaceCode,
114 | [type.propertyName]: type.interfaceProperty,
115 | };
116 |
117 | return {
118 | propTypesCode: newPropTypesCode,
119 | defaultPropsCode: newDefaultPropsCode,
120 | propsInterfaceCode: newPropsInterfaceCode,
121 | };
122 | },
123 | {
124 | propTypesCode: {},
125 | defaultPropsCode: {},
126 | propsInterfaceCode: {},
127 | },
128 | );
129 |
--------------------------------------------------------------------------------
/codegen/componentCode/preamble.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars, import/no-unresolved, import/extensions */
2 |
3 | // TODO: Remove `{ Component }` when Rollup fixes its code splitting.
4 | // Currently, this fixes an `React__default is undefined` error.
5 | // @ts-ignore
6 | import React, { Component, ReactNode } from 'react';
7 | // @ts-ignore
8 | import PropTypes from 'prop-types';
9 |
10 | // These are relative to the `src/amphtml/amphtml.js` file
11 | // @ts-ignore
12 | import { CONTEXT_KEY } from '../constants';
13 | // @ts-ignore
14 | import contextHelper from '../lib/contextHelper';
15 | // @ts-ignore
16 | import { AmpScriptsManagerContext } from '../setup/AmpScriptsManager';
17 |
18 | // @ts-ignore
19 | type Omit = Pick>;
20 |
21 | // @ts-ignore
22 | const REACT_AMPHTML_CONTEXT = {
23 | [CONTEXT_KEY]: PropTypes.shape({
24 | addExtension: PropTypes.func.isRequired,
25 | }),
26 | };
27 |
28 | // The following were all copied from the global JSX IntrinsicElements
29 | // interface.
30 | // The properties of the interface were copied here and then had a regex replace
31 | // run over all of them to make them into actual types:
32 | //
33 | // Find: ^"?([a-z].*?)"?:.*;
34 | //
35 | // Replace: // @ts-ignore\nexport type JSXIntrinsicElements$1 = JSX.IntrinsicElements['$1'];
36 |
37 | // HTML
38 | // @ts-ignore
39 | export type JSXIntrinsicElementsa = JSX.IntrinsicElements['a'];
40 | // @ts-ignore
41 | export type JSXIntrinsicElementsabbr = JSX.IntrinsicElements['abbr'];
42 | // @ts-ignore
43 | export type JSXIntrinsicElementsaddress = JSX.IntrinsicElements['address'];
44 | // @ts-ignore
45 | export type JSXIntrinsicElementsarea = JSX.IntrinsicElements['area'];
46 | // @ts-ignore
47 | export type JSXIntrinsicElementsarticle = JSX.IntrinsicElements['article'];
48 | // @ts-ignore
49 | export type JSXIntrinsicElementsaside = JSX.IntrinsicElements['aside'];
50 | // @ts-ignore
51 | export type JSXIntrinsicElementsaudio = JSX.IntrinsicElements['audio'];
52 | // @ts-ignore
53 | export type JSXIntrinsicElementsb = JSX.IntrinsicElements['b'];
54 | // @ts-ignore
55 | export type JSXIntrinsicElementsbase = JSX.IntrinsicElements['base'];
56 | // @ts-ignore
57 | export type JSXIntrinsicElementsbdi = JSX.IntrinsicElements['bdi'];
58 | // @ts-ignore
59 | export type JSXIntrinsicElementsbdo = JSX.IntrinsicElements['bdo'];
60 | // @ts-ignore
61 | export type JSXIntrinsicElementsbig = JSX.IntrinsicElements['big'];
62 | // @ts-ignore
63 | export type JSXIntrinsicElementsblockquote = JSX.IntrinsicElements['blockquote'];
64 | // @ts-ignore
65 | export type JSXIntrinsicElementsbody = JSX.IntrinsicElements['body'];
66 | // @ts-ignore
67 | export type JSXIntrinsicElementsbr = JSX.IntrinsicElements['br'];
68 | // @ts-ignore
69 | export type JSXIntrinsicElementsbutton = JSX.IntrinsicElements['button'];
70 | // @ts-ignore
71 | export type JSXIntrinsicElementscanvas = JSX.IntrinsicElements['canvas'];
72 | // @ts-ignore
73 | export type JSXIntrinsicElementscaption = JSX.IntrinsicElements['caption'];
74 | // @ts-ignore
75 | export type JSXIntrinsicElementscite = JSX.IntrinsicElements['cite'];
76 | // @ts-ignore
77 | export type JSXIntrinsicElementscode = JSX.IntrinsicElements['code'];
78 | // @ts-ignore
79 | export type JSXIntrinsicElementscol = JSX.IntrinsicElements['col'];
80 | // @ts-ignore
81 | export type JSXIntrinsicElementscolgroup = JSX.IntrinsicElements['colgroup'];
82 | // @ts-ignore
83 | export type JSXIntrinsicElementsdata = JSX.IntrinsicElements['data'];
84 | // @ts-ignore
85 | export type JSXIntrinsicElementsdatalist = JSX.IntrinsicElements['datalist'];
86 | // @ts-ignore
87 | export type JSXIntrinsicElementsdd = JSX.IntrinsicElements['dd'];
88 | // @ts-ignore
89 | export type JSXIntrinsicElementsdel = JSX.IntrinsicElements['del'];
90 | // @ts-ignore
91 | export type JSXIntrinsicElementsdetails = JSX.IntrinsicElements['details'];
92 | // @ts-ignore
93 | export type JSXIntrinsicElementsdfn = JSX.IntrinsicElements['dfn'];
94 | // @ts-ignore
95 | export type JSXIntrinsicElementsdialog = JSX.IntrinsicElements['dialog'];
96 | // @ts-ignore
97 | export type JSXIntrinsicElementsdiv = JSX.IntrinsicElements['div'];
98 | // @ts-ignore
99 | export type JSXIntrinsicElementsdl = JSX.IntrinsicElements['dl'];
100 | // @ts-ignore
101 | export type JSXIntrinsicElementsdt = JSX.IntrinsicElements['dt'];
102 | // @ts-ignore
103 | export type JSXIntrinsicElementsem = JSX.IntrinsicElements['em'];
104 | // @ts-ignore
105 | export type JSXIntrinsicElementsembed = JSX.IntrinsicElements['embed'];
106 | // @ts-ignore
107 | export type JSXIntrinsicElementsfieldset = JSX.IntrinsicElements['fieldset'];
108 | // @ts-ignore
109 | export type JSXIntrinsicElementsfigcaption = JSX.IntrinsicElements['figcaption'];
110 | // @ts-ignore
111 | export type JSXIntrinsicElementsfigure = JSX.IntrinsicElements['figure'];
112 | // @ts-ignore
113 | export type JSXIntrinsicElementsfooter = JSX.IntrinsicElements['footer'];
114 | // @ts-ignore
115 | export type JSXIntrinsicElementsform = JSX.IntrinsicElements['form'];
116 | // @ts-ignore
117 | export type JSXIntrinsicElementsh1 = JSX.IntrinsicElements['h1'];
118 | // @ts-ignore
119 | export type JSXIntrinsicElementsh2 = JSX.IntrinsicElements['h2'];
120 | // @ts-ignore
121 | export type JSXIntrinsicElementsh3 = JSX.IntrinsicElements['h3'];
122 | // @ts-ignore
123 | export type JSXIntrinsicElementsh4 = JSX.IntrinsicElements['h4'];
124 | // @ts-ignore
125 | export type JSXIntrinsicElementsh5 = JSX.IntrinsicElements['h5'];
126 | // @ts-ignore
127 | export type JSXIntrinsicElementsh6 = JSX.IntrinsicElements['h6'];
128 | // @ts-ignore
129 | export type JSXIntrinsicElementshead = JSX.IntrinsicElements['head'];
130 | // @ts-ignore
131 | export type JSXIntrinsicElementsheader = JSX.IntrinsicElements['header'];
132 | // @ts-ignore
133 | export type JSXIntrinsicElementshgroup = JSX.IntrinsicElements['hgroup'];
134 | // @ts-ignore
135 | export type JSXIntrinsicElementshr = JSX.IntrinsicElements['hr'];
136 | // @ts-ignore
137 | export type JSXIntrinsicElementshtml = JSX.IntrinsicElements['html'];
138 | // @ts-ignore
139 | export type JSXIntrinsicElementsi = JSX.IntrinsicElements['i'];
140 | // @ts-ignore
141 | export type JSXIntrinsicElementsiframe = JSX.IntrinsicElements['iframe'];
142 | // @ts-ignore
143 | export type JSXIntrinsicElementsimg = JSX.IntrinsicElements['img'];
144 | // @ts-ignore
145 | export type JSXIntrinsicElementsinput = JSX.IntrinsicElements['input'];
146 | // @ts-ignore
147 | export type JSXIntrinsicElementsins = JSX.IntrinsicElements['ins'];
148 | // @ts-ignore
149 | export type JSXIntrinsicElementskbd = JSX.IntrinsicElements['kbd'];
150 | // @ts-ignore
151 | export type JSXIntrinsicElementskeygen = JSX.IntrinsicElements['keygen'];
152 | // @ts-ignore
153 | export type JSXIntrinsicElementslabel = JSX.IntrinsicElements['label'];
154 | // @ts-ignore
155 | export type JSXIntrinsicElementslegend = JSX.IntrinsicElements['legend'];
156 | // @ts-ignore
157 | export type JSXIntrinsicElementsli = JSX.IntrinsicElements['li'];
158 | // @ts-ignore
159 | export type JSXIntrinsicElementslink = JSX.IntrinsicElements['link'];
160 | // @ts-ignore
161 | export type JSXIntrinsicElementsmain = JSX.IntrinsicElements['main'];
162 | // @ts-ignore
163 | export type JSXIntrinsicElementsmap = JSX.IntrinsicElements['map'];
164 | // @ts-ignore
165 | export type JSXIntrinsicElementsmark = JSX.IntrinsicElements['mark'];
166 | // @ts-ignore
167 | export type JSXIntrinsicElementsmenu = JSX.IntrinsicElements['menu'];
168 | // @ts-ignore
169 | export type JSXIntrinsicElementsmenuitem = JSX.IntrinsicElements['menuitem'];
170 | // @ts-ignore
171 | export type JSXIntrinsicElementsmeta = JSX.IntrinsicElements['meta'];
172 | // @ts-ignore
173 | export type JSXIntrinsicElementsmeter = JSX.IntrinsicElements['meter'];
174 | // @ts-ignore
175 | export type JSXIntrinsicElementsnav = JSX.IntrinsicElements['nav'];
176 | // @ts-ignore
177 | export type JSXIntrinsicElementsnoindex = JSX.IntrinsicElements['noindex'];
178 | // @ts-ignore
179 | export type JSXIntrinsicElementsnoscript = JSX.IntrinsicElements['noscript'];
180 | // @ts-ignore
181 | export type JSXIntrinsicElementsobject = JSX.IntrinsicElements['object'];
182 | // @ts-ignore
183 | export type JSXIntrinsicElementsol = JSX.IntrinsicElements['ol'];
184 | // @ts-ignore
185 | export type JSXIntrinsicElementsoptgroup = JSX.IntrinsicElements['optgroup'];
186 | // @ts-ignore
187 | export type JSXIntrinsicElementsoption = JSX.IntrinsicElements['option'];
188 | // @ts-ignore
189 | export type JSXIntrinsicElementsoutput = JSX.IntrinsicElements['output'];
190 | // @ts-ignore
191 | export type JSXIntrinsicElementsp = JSX.IntrinsicElements['p'];
192 | // @ts-ignore
193 | export type JSXIntrinsicElementsparam = JSX.IntrinsicElements['param'];
194 | // @ts-ignore
195 | export type JSXIntrinsicElementspicture = JSX.IntrinsicElements['picture'];
196 | // @ts-ignore
197 | export type JSXIntrinsicElementspre = JSX.IntrinsicElements['pre'];
198 | // @ts-ignore
199 | export type JSXIntrinsicElementsprogress = JSX.IntrinsicElements['progress'];
200 | // @ts-ignore
201 | export type JSXIntrinsicElementsq = JSX.IntrinsicElements['q'];
202 | // @ts-ignore
203 | export type JSXIntrinsicElementsrp = JSX.IntrinsicElements['rp'];
204 | // @ts-ignore
205 | export type JSXIntrinsicElementsrt = JSX.IntrinsicElements['rt'];
206 | // @ts-ignore
207 | export type JSXIntrinsicElementsruby = JSX.IntrinsicElements['ruby'];
208 | // @ts-ignore
209 | export type JSXIntrinsicElementss = JSX.IntrinsicElements['s'];
210 | // @ts-ignore
211 | export type JSXIntrinsicElementssamp = JSX.IntrinsicElements['samp'];
212 | // @ts-ignore
213 | export type JSXIntrinsicElementsscript = JSX.IntrinsicElements['script'];
214 | // @ts-ignore
215 | export type JSXIntrinsicElementssection = JSX.IntrinsicElements['section'];
216 | // @ts-ignore
217 | export type JSXIntrinsicElementsselect = JSX.IntrinsicElements['select'];
218 | // @ts-ignore
219 | export type JSXIntrinsicElementssmall = JSX.IntrinsicElements['small'];
220 | // @ts-ignore
221 | export type JSXIntrinsicElementssource = JSX.IntrinsicElements['source'];
222 | // @ts-ignore
223 | export type JSXIntrinsicElementsspan = JSX.IntrinsicElements['span'];
224 | // @ts-ignore
225 | export type JSXIntrinsicElementsstrong = JSX.IntrinsicElements['strong'];
226 | // @ts-ignore
227 | export type JSXIntrinsicElementsstyle = JSX.IntrinsicElements['style'];
228 | // @ts-ignore
229 | export type JSXIntrinsicElementssub = JSX.IntrinsicElements['sub'];
230 | // @ts-ignore
231 | export type JSXIntrinsicElementssummary = JSX.IntrinsicElements['summary'];
232 | // @ts-ignore
233 | export type JSXIntrinsicElementssup = JSX.IntrinsicElements['sup'];
234 | // @ts-ignore
235 | export type JSXIntrinsicElementstable = JSX.IntrinsicElements['table'];
236 | // @ts-ignore
237 | export type JSXIntrinsicElementstemplate = JSX.IntrinsicElements['template'];
238 | // @ts-ignore
239 | export type JSXIntrinsicElementstbody = JSX.IntrinsicElements['tbody'];
240 | // @ts-ignore
241 | export type JSXIntrinsicElementstd = JSX.IntrinsicElements['td'];
242 | // @ts-ignore
243 | export type JSXIntrinsicElementstextarea = JSX.IntrinsicElements['textarea'];
244 | // @ts-ignore
245 | export type JSXIntrinsicElementstfoot = JSX.IntrinsicElements['tfoot'];
246 | // @ts-ignore
247 | export type JSXIntrinsicElementsth = JSX.IntrinsicElements['th'];
248 | // @ts-ignore
249 | export type JSXIntrinsicElementsthead = JSX.IntrinsicElements['thead'];
250 | // @ts-ignore
251 | export type JSXIntrinsicElementstime = JSX.IntrinsicElements['time'];
252 | // @ts-ignore
253 | export type JSXIntrinsicElementstitle = JSX.IntrinsicElements['title'];
254 | // @ts-ignore
255 | export type JSXIntrinsicElementstr = JSX.IntrinsicElements['tr'];
256 | // @ts-ignore
257 | export type JSXIntrinsicElementstrack = JSX.IntrinsicElements['track'];
258 | // @ts-ignore
259 | export type JSXIntrinsicElementsu = JSX.IntrinsicElements['u'];
260 | // @ts-ignore
261 | export type JSXIntrinsicElementsul = JSX.IntrinsicElements['ul'];
262 | // @ts-ignore
263 | export type JSXIntrinsicElementsvar = JSX.IntrinsicElements['var'];
264 | // @ts-ignore
265 | export type JSXIntrinsicElementsvideo = JSX.IntrinsicElements['video'];
266 | // @ts-ignore
267 | export type JSXIntrinsicElementswbr = JSX.IntrinsicElements['wbr'];
268 | // @ts-ignore
269 | export type JSXIntrinsicElementswebview = JSX.IntrinsicElements['webview'];
270 |
271 | // SVG
272 | // @ts-ignore
273 | export type JSXIntrinsicElementssvg = JSX.IntrinsicElements['svg'];
274 |
275 | // @ts-ignore
276 | export type JSXIntrinsicElementsanimate = JSX.IntrinsicElements['animate']; // TODO: It is SVGAnimateElement but is not in TypeScript's lib.dom.d.ts for now.
277 | // @ts-ignore
278 | export type JSXIntrinsicElementsanimateMotion = JSX.IntrinsicElements['animateMotion'];
279 | // @ts-ignore
280 | export type JSXIntrinsicElementsanimateTransform = JSX.IntrinsicElements['animateTransform']; // TODO: It is SVGAnimateTransformElement but is not in TypeScript's lib.dom.d.ts for now.
281 | // @ts-ignore
282 | export type JSXIntrinsicElementscircle = JSX.IntrinsicElements['circle'];
283 | // @ts-ignore
284 | export type JSXIntrinsicElementsclipPath = JSX.IntrinsicElements['clipPath'];
285 | // @ts-ignore
286 | export type JSXIntrinsicElementsdefs = JSX.IntrinsicElements['defs'];
287 | // @ts-ignore
288 | export type JSXIntrinsicElementsdesc = JSX.IntrinsicElements['desc'];
289 | // @ts-ignore
290 | export type JSXIntrinsicElementsellipse = JSX.IntrinsicElements['ellipse'];
291 | // @ts-ignore
292 | export type JSXIntrinsicElementsfeBlend = JSX.IntrinsicElements['feBlend'];
293 | // @ts-ignore
294 | export type JSXIntrinsicElementsfeColorMatrix = JSX.IntrinsicElements['feColorMatrix'];
295 | // @ts-ignore
296 | export type JSXIntrinsicElementsfeComponentTransfer = JSX.IntrinsicElements['feComponentTransfer'];
297 | // @ts-ignore
298 | export type JSXIntrinsicElementsfeComposite = JSX.IntrinsicElements['feComposite'];
299 | // @ts-ignore
300 | export type JSXIntrinsicElementsfeConvolveMatrix = JSX.IntrinsicElements['feConvolveMatrix'];
301 | // @ts-ignore
302 | export type JSXIntrinsicElementsfeDiffuseLighting = JSX.IntrinsicElements['feDiffuseLighting'];
303 | // @ts-ignore
304 | export type JSXIntrinsicElementsfeDisplacementMap = JSX.IntrinsicElements['feDisplacementMap'];
305 | // @ts-ignore
306 | export type JSXIntrinsicElementsfeDistantLight = JSX.IntrinsicElements['feDistantLight'];
307 | // @ts-ignore
308 | export type JSXIntrinsicElementsfeDropShadow = JSX.IntrinsicElements['feDropShadow'];
309 | // @ts-ignore
310 | export type JSXIntrinsicElementsfeFlood = JSX.IntrinsicElements['feFlood'];
311 | // @ts-ignore
312 | export type JSXIntrinsicElementsfeFuncA = JSX.IntrinsicElements['feFuncA'];
313 | // @ts-ignore
314 | export type JSXIntrinsicElementsfeFuncB = JSX.IntrinsicElements['feFuncB'];
315 | // @ts-ignore
316 | export type JSXIntrinsicElementsfeFuncG = JSX.IntrinsicElements['feFuncG'];
317 | // @ts-ignore
318 | export type JSXIntrinsicElementsfeFuncR = JSX.IntrinsicElements['feFuncR'];
319 | // @ts-ignore
320 | export type JSXIntrinsicElementsfeGaussianBlur = JSX.IntrinsicElements['feGaussianBlur'];
321 | // @ts-ignore
322 | export type JSXIntrinsicElementsfeImage = JSX.IntrinsicElements['feImage'];
323 | // @ts-ignore
324 | export type JSXIntrinsicElementsfeMerge = JSX.IntrinsicElements['feMerge'];
325 | // @ts-ignore
326 | export type JSXIntrinsicElementsfeMergeNode = JSX.IntrinsicElements['feMergeNode'];
327 | // @ts-ignore
328 | export type JSXIntrinsicElementsfeMorphology = JSX.IntrinsicElements['feMorphology'];
329 | // @ts-ignore
330 | export type JSXIntrinsicElementsfeOffset = JSX.IntrinsicElements['feOffset'];
331 | // @ts-ignore
332 | export type JSXIntrinsicElementsfePointLight = JSX.IntrinsicElements['fePointLight'];
333 | // @ts-ignore
334 | export type JSXIntrinsicElementsfeSpecularLighting = JSX.IntrinsicElements['feSpecularLighting'];
335 | // @ts-ignore
336 | export type JSXIntrinsicElementsfeSpotLight = JSX.IntrinsicElements['feSpotLight'];
337 | // @ts-ignore
338 | export type JSXIntrinsicElementsfeTile = JSX.IntrinsicElements['feTile'];
339 | // @ts-ignore
340 | export type JSXIntrinsicElementsfeTurbulence = JSX.IntrinsicElements['feTurbulence'];
341 | // @ts-ignore
342 | export type JSXIntrinsicElementsfilter = JSX.IntrinsicElements['filter'];
343 | // @ts-ignore
344 | export type JSXIntrinsicElementsforeignObject = JSX.IntrinsicElements['foreignObject'];
345 | // @ts-ignore
346 | export type JSXIntrinsicElementsg = JSX.IntrinsicElements['g'];
347 | // @ts-ignore
348 | export type JSXIntrinsicElementsimage = JSX.IntrinsicElements['image'];
349 | // @ts-ignore
350 | export type JSXIntrinsicElementsline = JSX.IntrinsicElements['line'];
351 | // @ts-ignore
352 | export type JSXIntrinsicElementslinearGradient = JSX.IntrinsicElements['linearGradient'];
353 | // @ts-ignore
354 | export type JSXIntrinsicElementsmarker = JSX.IntrinsicElements['marker'];
355 | // @ts-ignore
356 | export type JSXIntrinsicElementsmask = JSX.IntrinsicElements['mask'];
357 | // @ts-ignore
358 | export type JSXIntrinsicElementsmetadata = JSX.IntrinsicElements['metadata'];
359 | // @ts-ignore
360 | export type JSXIntrinsicElementsmpath = JSX.IntrinsicElements['mpath'];
361 | // @ts-ignore
362 | export type JSXIntrinsicElementspath = JSX.IntrinsicElements['path'];
363 | // @ts-ignore
364 | export type JSXIntrinsicElementspattern = JSX.IntrinsicElements['pattern'];
365 | // @ts-ignore
366 | export type JSXIntrinsicElementspolygon = JSX.IntrinsicElements['polygon'];
367 | // @ts-ignore
368 | export type JSXIntrinsicElementspolyline = JSX.IntrinsicElements['polyline'];
369 | // @ts-ignore
370 | export type JSXIntrinsicElementsradialGradient = JSX.IntrinsicElements['radialGradient'];
371 | // @ts-ignore
372 | export type JSXIntrinsicElementsrect = JSX.IntrinsicElements['rect'];
373 | // @ts-ignore
374 | export type JSXIntrinsicElementsstop = JSX.IntrinsicElements['stop'];
375 | // @ts-ignore
376 | export type JSXIntrinsicElementsswitch = JSX.IntrinsicElements['switch'];
377 | // @ts-ignore
378 | export type JSXIntrinsicElementssymbol = JSX.IntrinsicElements['symbol'];
379 | // @ts-ignore
380 | export type JSXIntrinsicElementstext = JSX.IntrinsicElements['text'];
381 | // @ts-ignore
382 | export type JSXIntrinsicElementstextPath = JSX.IntrinsicElements['textPath'];
383 | // @ts-ignore
384 | export type JSXIntrinsicElementstspan = JSX.IntrinsicElements['tspan'];
385 | // @ts-ignore
386 | export type JSXIntrinsicElementsuse = JSX.IntrinsicElements['use'];
387 | // @ts-ignore
388 | export type JSXIntrinsicElementsview = JSX.IntrinsicElements['view'];
389 |
--------------------------------------------------------------------------------
/codegen/componentCode/templates/componentOverrideTemplate.ts:
--------------------------------------------------------------------------------
1 | import { NewTag } from '../../rules';
2 | import tagNameToComponentName from '../../tagNameToComponentName';
3 | import { PropsCode } from '../lib/propsCodeReducer';
4 |
5 | const propsInterfaceReducer = ({
6 | componentName,
7 | dupeName,
8 | propsCode,
9 | extensionSpec,
10 | }: {
11 | componentName: string;
12 | dupeName?: string;
13 | propsCode: PropsCode;
14 | extensionSpec: NewTag['extensionSpec'];
15 | }): string => {
16 | const propsInterfaceProperties = Object.entries(
17 | propsCode.propsInterfaceCode,
18 | ).reduce((acc, [key, value]): string => {
19 | const optional = /undefined/.test(value) ? '?' : '';
20 | return `
21 | ${acc}
22 | ${key}${optional}: ${value};
23 | `;
24 | }, '');
25 | const versionProperty = extensionSpec
26 | ? extensionSpec.version.map((v): string => JSON.stringify(v)).join('|')
27 | : "ScriptProps['version']";
28 | const exportInterface = dupeName ? '' : 'export';
29 | return `
30 | ${exportInterface} interface ${componentName} {
31 | ${propsInterfaceProperties}
32 | version?: ${versionProperty};
33 | on?: string;
34 | }
35 | `;
36 | };
37 |
38 | const propTypesReducer = ({
39 | componentName,
40 | propsCode,
41 | extensionSpec,
42 | }: {
43 | componentName: string;
44 | propsCode: PropsCode;
45 | extensionSpec: NewTag['extensionSpec'];
46 | }): string => {
47 | const propTypesEntries = Object.entries(propsCode.propTypesCode);
48 | if (propTypesEntries.length === 0 && !extensionSpec) return '';
49 | const versionProperty = extensionSpec
50 | ? `
51 | PropTypes.oneOf<'${extensionSpec.version.join(
52 | "' | '",
53 | )}'>(${JSON.stringify(extensionSpec.version)})
54 | `
55 | : "PropTypes.string as PropTypes.Requireable