├── .nvmrc
├── examples
└── simple-server
│ ├── .gitignore
│ ├── docs
│ └── preview.png
│ ├── server
│ ├── assets
│ │ └── bg.png
│ ├── render.js
│ ├── index.js
│ └── data.json
│ ├── src
│ ├── globalStyles.js
│ ├── App.jsx
│ └── Components
│ │ ├── Heading.jsx
│ │ ├── SocialShare.jsx
│ │ ├── Head.jsx
│ │ ├── PostCard.jsx
│ │ └── Body.jsx
│ ├── .babelrc
│ ├── README.md
│ └── package.json
├── test
├── snapshots
│ ├── index.test.js.snap
│ └── index.test.js.md
├── Components
│ ├── snapshots
│ │ ├── index.test.js.snap
│ │ ├── Components.test.js.snap
│ │ ├── index.test.js.md
│ │ └── Components.test.js.md
│ ├── Head.test.js
│ └── Components.test.js
└── index.test.js
├── .npmignore
├── src
├── Components
│ ├── Link.jsx
│ ├── Meta.jsx
│ ├── Title.jsx
│ ├── Tag.jsx
│ ├── Head.jsx
│ ├── Script.jsx
│ └── index.js
├── constants.js
├── index.jsx
└── store.js
├── .gitignore
├── .travis.yml
├── config
├── .babelrc
└── .eslintrc.json
├── .editorconfig
├── LICENSE.md
├── coverage.lcov
├── package.json
└── README.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 6
2 |
--------------------------------------------------------------------------------
/examples/simple-server/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | **/node_modules
3 | public
4 |
--------------------------------------------------------------------------------
/test/snapshots/index.test.js.snap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ariel-Rodriguez/react-amp-template/HEAD/test/snapshots/index.test.js.snap
--------------------------------------------------------------------------------
/examples/simple-server/docs/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ariel-Rodriguez/react-amp-template/HEAD/examples/simple-server/docs/preview.png
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .travis.yml
2 | examples
3 | .nyc_output
4 | src
5 | .editorconfig
6 | yarn-error.log
7 | tmp
8 | coverage
9 | test
10 | config
11 |
--------------------------------------------------------------------------------
/examples/simple-server/server/assets/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ariel-Rodriguez/react-amp-template/HEAD/examples/simple-server/server/assets/bg.png
--------------------------------------------------------------------------------
/test/Components/snapshots/index.test.js.snap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ariel-Rodriguez/react-amp-template/HEAD/test/Components/snapshots/index.test.js.snap
--------------------------------------------------------------------------------
/test/Components/snapshots/Components.test.js.snap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ariel-Rodriguez/react-amp-template/HEAD/test/Components/snapshots/Components.test.js.snap
--------------------------------------------------------------------------------
/src/Components/Link.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Tag from './Tag'
3 |
4 | const Link = props =>
5 |
6 |
7 | export default Link
8 |
--------------------------------------------------------------------------------
/src/Components/Meta.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Tag from './Tag'
3 |
4 | const Meta = props =>
5 |
6 |
7 | export default Meta
8 |
--------------------------------------------------------------------------------
/examples/simple-server/src/globalStyles.js:
--------------------------------------------------------------------------------
1 | import { injectGlobal } from 'styled-components'
2 |
3 | injectGlobal`
4 | p {
5 | font-size: 1.2rem;
6 | font-family: monospace;
7 | }
8 | `
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage/**
2 | node_modules/**
3 | npm-debug.log
4 | .DS_Store
5 | yarn-error.log
6 | lib
7 | dist
8 | examples/build
9 | examples/simple/node_modules
10 | test-debug.html
11 | .nyc_output
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "stable"
4 | cache:
5 | yarn: false
6 | script:
7 | - npm install
8 | - npm test
9 | after_script:
10 | - npm install -g codecov
11 | - npm run report-coverage
12 |
--------------------------------------------------------------------------------
/examples/simple-server/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "sourceMaps": true,
3 | "presets": [
4 | "react",
5 | ["env", {
6 | "targets": {
7 | "node": "current"
8 | }
9 | }]],
10 | "plugins": [
11 | ["babel-plugin-styled-components", { "ssr": true }]
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/config/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "react",
4 | ["env", {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }]
9 | ],
10 | "plugins": [
11 | "transform-object-rest-spread"
12 | ],
13 | "env": {
14 | "test": {
15 | "plugins": ["istanbul"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Components/Title.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Tag from './Tag'
4 |
5 | const Title = ({ children }) =>
6 |
7 |
8 | Title.propTypes = {
9 | children: PropTypes.string.isRequired,
10 | }
11 |
12 | export default Title
13 |
--------------------------------------------------------------------------------
/examples/simple-server/README.md:
--------------------------------------------------------------------------------
1 | ## DEMO
2 |
3 | This is a full boilerplate application.
4 | Demostrates how to implement RAMPT using Babel + React 16 + StyledComponents v3.
5 |
6 |
7 |
8 | ### Install
9 | - `npm install`
10 | - `npm start`
11 |
12 | ### Dev
13 | Launch server live reload.
14 | - `npm run dev`
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 | # https://github.com/jokeyrhyme/standard-editorconfig
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Unix-style newlines with a newline ending every file
8 | [*]
9 | end_of_line = lf
10 | insert_final_newline = true
11 |
12 | # Set default charset
13 | [*]
14 | charset = utf-8
15 |
16 | # Other good defaults
17 | [*]
18 | indent_size = 2
19 | indent_style = space
20 | trim_trailing_whitespace = true
21 |
--------------------------------------------------------------------------------
/examples/simple-server/src/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react'
2 | import { renderToString } from 'react-amp-template'
3 | import Head from './Components/Head'
4 | import Body from './Components/Body'
5 | import './globalStyles'
6 |
7 | const App = ({ title, date, json }) => (
8 |
9 |
10 |
11 |
12 |
18 |
26 |
31 |
38 |
39 |
40 | )
41 |
42 | export default Body
43 |
--------------------------------------------------------------------------------
/src/Components/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from './Link'
3 | import Meta from './Meta'
4 | import Script from './Script'
5 | import Tag from './Tag'
6 | import Title from './Title'
7 |
8 | /**
9 | * Transforms input to hyphened lowercase for each upper case coincidence.
10 | * @param {String} str React custom-element name e.g StickyAd
11 | * @returns {String} the input as kebabCase with leaded by a hyphen e.g. sticky-ad
12 | */
13 | const toKebabCase = str =>
14 | str.replace(/([A-Z])/g, '-$1').slice(1).toLowerCase()
15 |
16 | function derivatedScriptComponent(name) {
17 | if (!/^[A-Z]/.test(name)) {
18 | throw Error(`Failed to fallback component name ${name}. Component to import must begin with uppercase.`)
19 | }
20 | return function scriptComponent(props) {
21 | return React.createElement(Script, {
22 | _name: `amp-${toKebabCase(name)}`,
23 | ...props,
24 | })
25 | }
26 | }
27 |
28 | /**
29 | * @description Exposes a factory to generate AMP components in runtime.
30 | * @returns {Object} React component
31 | */
32 | export default new Proxy({
33 | Link, Meta, Script, Tag, Title, toKebabCase,
34 | }, {
35 | get(thisModule, componentName) {
36 | return thisModule[componentName] || derivatedScriptComponent(componentName)
37 | },
38 | })
39 |
--------------------------------------------------------------------------------
/test/snapshots/index.test.js.md:
--------------------------------------------------------------------------------
1 | # Snapshot report for `test/index.test.js`
2 |
3 | The actual snapshot is saved in `index.test.js.snap`.
4 |
5 | Generated by [AVA](https://ava.li).
6 |
7 | ## it renders all AMP node element
8 |
9 | > Snapshot 1
10 |
11 | `␊
12 | ␊
13 | ␊
14 | ␊
15 | ␊
16 | title␊
17 | ␊
18 | ␊
19 | ␊
20 | ␊
21 | ␊
22 | `
23 |
--------------------------------------------------------------------------------
/examples/simple-server/server/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "@context": "http://schema.org",
3 | "@type": "LiveBlogPosting",
4 | "url": "https://ampbyexample.com/samples_templates/live_blog/",
5 | "articleBody": "This is the initial text in the blog post",
6 | "datePublished": "2016-09-08T23:04:28.24337",
7 | "about": {
8 | "@type": "Event",
9 | "description": "This is my great live blog sample",
10 | "startDate": "2016-07-23T13:00:00-07:00",
11 | "endDate": "2016-07-23T15:00:00-07:00",
12 | "name": "An AMP Live Blog",
13 | "url": "https://ampbyexample.com/samples_templates/live_blog/",
14 | "location": {
15 | "@type": "EventVenue",
16 | "name": "The Venue Name",
17 | "address" : {
18 | "@type": "PostalAddress",
19 | "streetAddress": "701 Mission St",
20 | "addressLocality": "San Francisco",
21 | "addressRegion": "CA",
22 | "postalCode": "94103",
23 | "addressCountry": "US"
24 | }
25 | }
26 | },
27 | "publisher": {
28 | "@type": "Organization",
29 | "name": "Google",
30 | "logo": {
31 | "@type": "ImageObject",
32 | "url": "https://ampbyexample.com/img/favicon.png",
33 | "width": "512",
34 | "height": "512"
35 | }
36 | },
37 | "image": {
38 | "@type": "ImageObject",
39 | "url": "https://ampbyexample.com/img/abe_preview.png",
40 | "height": "1532",
41 | "width": "2046"
42 | },
43 | "coverageStartTime": "2016-07-23T11:30:00-07:00",
44 | "coverageEndTime": "2016-07-23T16:00:00-07:00",
45 | "headline": "An AMP Live Blog",
46 | "description": "A Live Blog implementation with AMP",
47 | "liveBlogUpdate": []
48 | }
49 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { renderToStaticMarkup } from 'react-dom/server'
3 | import { ServerStyleSheet } from 'styled-components'
4 | import pretty from 'pretty'
5 |
6 | import store, { setOptions } from './store'
7 | import Components from './Components'
8 | import Head from './Components/Head'
9 |
10 | /**
11 | * Transform React component into HTML AMP format.
12 | * @returns {String} html
13 | * @param {Class|Object} body React component to render
14 | * @param {Object} options Settings
15 | * @property {string} options.cdnURI absolute URL to AMP CDN
16 | * @property {string} options.boilerplate HTML string which contains AMP boilerplate styles
17 | * @property {object} options.extensions Key map of references to specify an extension version
18 | * @property {object} options.extensions.default default version for all amp-extensions e.g '0.1'
19 | * @property {object} options.extensions.extension [extension-name]
20 | ** specify custom version for derived extension e.g: 'amp-sticky-ad': '1.0'
21 | */
22 | export const renderToString = (body, options = {}) => {
23 | setOptions(options)
24 | const sheet = new ServerStyleSheet()
25 | const bodyStyless = pretty(renderToStaticMarkup(sheet.collectStyles(body)))
26 | const styles = sheet.getStyleElement()[0]
27 | // eslint-disable-next-line no-underscore-dangle
28 | const css = styles ? styles.props.dangerouslySetInnerHTML.__html : ''
29 | const head = pretty(renderToStaticMarkup(<\/body>/, 'Renders HTML template with body element.')
10 | })
11 |
12 | const renderAllAMPComponents = key => {
13 | const Title = React.createElement(AMP.Title, { key: `title-${key}`}, 'title')
14 | const Link = React.createElement(AMP.Link, { src: 'https://link', key: `link-${key}` }, 'link')
15 | const body = React.createElement('body', {}, [Title, Link])
16 | return renderToString(body)
17 | }
18 |
19 | test('it renders all AMP node element', t => {
20 | const render1 = renderAllAMPComponents()
21 | const render2 = renderAllAMPComponents()
22 | t.true(render1 === render2, 'Each render should not mix the state between each other.')
23 | t.snapshot(render1)
24 | })
25 |
26 |
27 | test('RAMPT render with styles', async t => {
28 | const styles = "background: url('https://www.somedomain.com');"
29 | const styledBody = styled.body`${styles}`
30 | const body = React.createElement(styledBody, {})
31 | const output = renderToString(body)
32 |
33 | const unScapedStyles = styles.replace(/\(/g, '\\(').replace(/\)/g, '\\)')
34 |
35 | t.regex(output, new RegExp(unScapedStyles), 'Renders HTML template with body element and styles.')
36 | })
37 |
38 | test('RAMPT renderToString multiple calls', async t => {
39 | const stylesFirstRender = 'background: red;'
40 | const stylesLastRender = 'background: blue;'
41 |
42 | const styledBody = styled.body`${stylesFirstRender}`
43 | const body = React.createElement(styledBody, {})
44 |
45 | const styledBody2 = styled.body`${stylesLastRender}`
46 | const body2 = React.createElement(styledBody2, {})
47 |
48 | // First render with background: red
49 | renderToString(body)
50 | // 2nd render with background: blue
51 | const output2 = renderToString(body2)
52 |
53 | t.regex(output2, new RegExp(stylesLastRender), 'It should render unique styles from each render.')
54 | t.notRegex(output2, new RegExp(stylesFirstRender), 'It should not mix class styles from each render.')
55 | })
56 |
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-amp-template",
3 | "version": "4.1.0",
4 | "description": "AMP react server rendering.",
5 | "keywords": [
6 | "preact",
7 | "react",
8 | "amp",
9 | "server-rendering",
10 | "react-amp",
11 | "amp-react",
12 | "template",
13 | "aphrodite",
14 | "styledcomponent"
15 | ],
16 | "main": "lib/index.js",
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/Ariel-Rodriguez/react-amp-template.git"
20 | },
21 | "scripts": {
22 | "build": "rimraf lib && babel src -d lib -s",
23 | "dev": "nodemon --watch ./src --ext js,jsx --exec 'npm run prepare && npm run build && npm run test'",
24 | "prepare": "npm test",
25 | "prepublishOnly": "NODE_ENV=production && npm run build && echo build using $NODE_ENV env",
26 | "test": "npm run -s lint && BABEL_ENV=test nyc ava",
27 | "test:watch": "npm test -- --watch",
28 | "lint": "eslint --fix --ext .jsx --ext .js src/.",
29 | "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov -t $CODECOV_TOKEN"
30 | },
31 | "author": "Ariel Fernando Rodriguez",
32 | "license": "Apache-2.0",
33 | "ava": {
34 | "require": [
35 | "babel-register"
36 | ],
37 | "babel": "inherit"
38 | },
39 | "babel": {
40 | "extends": "./config/.babelrc"
41 | },
42 | "eslintConfig": {
43 | "extends": "./config/.eslintrc.json"
44 | },
45 | "engines": {
46 | "node": ">=8.0.0",
47 | "yarn": ">=1.5.0"
48 | },
49 | "dependencies": {
50 | "pretty": "^2.0.0",
51 | "prop-types": "^15.6.2",
52 | "react": "^16.6.0",
53 | "react-dom": "^16.6.0",
54 | "styled-components": "^3.4.10"
55 | },
56 | "devDependencies": {
57 | "ava": "^0.25.0",
58 | "babel-cli": "^6.26.0",
59 | "babel-core": "^6.26.3",
60 | "babel-eslint": "^8.2.6",
61 | "babel-plugin-istanbul": "^5.1.0",
62 | "babel-plugin-styled-components": "^1.8.0",
63 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
64 | "babel-preset-env": "^1.7.0",
65 | "babel-preset-react": "^6.24.1",
66 | "babel-runtime": "^6.26.0",
67 | "debug": "^4.1.1",
68 | "eslint": "^4.19.1",
69 | "eslint-config-airbnb": "^16.1.0",
70 | "eslint-config-airbnb-base": "^12.1.0",
71 | "eslint-plugin-import": "^2.14.0",
72 | "eslint-plugin-jsx-a11y": "^6.1.2",
73 | "eslint-plugin-react": "^7.11.1",
74 | "mkdirp": "^0.5.1",
75 | "nyc": "^11.9.0",
76 | "react-test-renderer": "^16.6.0",
77 | "rimraf": "^2.6.2"
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import * as AMP from './constants'
3 |
4 |
5 | /**
6 | * Store type definition
7 | * @typedef {Object} state
8 | * @property {string} cdnURI absolute URL to AMP CDN
9 | * @property {string} boilerplate
10 | * @property {object} extensions Key map of references to specify an extension version
11 | * @property {Array} elements List of runtime generated elements to be appended into Head
12 | */
13 | const state = {
14 | cdnURI: AMP.CDN,
15 | boilerplate: AMP.BOILERPLATE,
16 | extensions: AMP.EXTENSION_VERSION,
17 | runtimeURI: AMP.RUNTIME,
18 | elements: [],
19 | }
20 |
21 | let hashRegister = []
22 |
23 | const createElement = (tag, attrs, key) =>
24 | React.createElement(tag, { ...attrs, key })
25 |
26 | /**
27 | * Resolves the source URI for the extension script.
28 | * @returns {String} CDN URI of script extension
29 | * @param {String} extensionName script extension e.g "amp-fit-text"
30 | */
31 | const getScriptSRC = extensionName =>
32 | `${state.cdnURI}/${extensionName}-${state.extensions[extensionName] || state.extensions.default}.js`
33 |
34 | /**
35 | * Updates the store with the given element to be appended into Head.
36 | * @param {String} tag html tag e.g "script"
37 | * @param {Object} attrs html element attributes
38 | */
39 | const registerElement = (tag, attrs) => {
40 | state.elements.push(createElement(tag, attrs, `${tag}-${state.elements.length}`))
41 | }
42 |
43 | /**
44 | * Same as registerElement but only updates the store if the element was not registered before.
45 | * It is required for managing script in head.
46 | * It is desired to do not load the same script twice.
47 | * @param {String} tag html tag e.g "script"
48 | * @param {Object} attrs html element attributes
49 | * @param {String} name custom-element name e.g "amp-fit-text"
50 | */
51 | const registerUniqueElement = (tag, attrs, name) => {
52 | if (hashRegister.indexOf(name) === -1) {
53 | hashRegister.push(name)
54 | state.elements.push(createElement(tag, attrs, name))
55 | }
56 | }
57 |
58 | const setOptions = ({
59 | boilerplate, extensions, runtimeURI, cdnURI,
60 | }) => {
61 | state.cdnURI = cdnURI || state.cdnURI
62 | state.boilerplate = boilerplate || state.boilerplate
63 | state.runtimeURI = runtimeURI || state.runtimeURI
64 | state.extensions = { ...state.extensions, ...extensions }
65 | state.elements.length = 0
66 | hashRegister = []
67 | }
68 |
69 | const { Provider, Consumer } = React.createContext({
70 | store: {
71 | state,
72 | getScriptSRC,
73 | registerElement,
74 | registerUniqueElement,
75 | },
76 | })
77 |
78 | export {
79 | Consumer,
80 | createElement,
81 | registerElement,
82 | registerUniqueElement,
83 | getScriptSRC,
84 | Provider,
85 | setOptions,
86 | }
87 | export default state
88 |
--------------------------------------------------------------------------------
/test/Components/Components.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava'
2 | import React from 'react'
3 | import TestRenderer from 'react-test-renderer'
4 | import Components from '../../src/Components'
5 |
6 | test('Script component', async t => {
7 | const Script = TestRenderer.create(React.createElement(Components.Script, { _name: 'amp-test' }))
8 | t.is(Script.root.props._name, 'amp-test')
9 | t.snapshot(Script.toJSON())
10 | })
11 |
12 | test('Custom script ldjson with children', async t => {
13 | const Div = React.createElement('div', { id: 'child' })
14 | const Script = TestRenderer.create(React.createElement(Components.Script, { type: 'application/ld+json' }, Div))
15 | t.is(Script.root.props.type, 'application/ld+json', 'should identify ld+json type to append in head')
16 | // should return the child element to append in body
17 | t.snapshot(Script.toJSON())
18 | })
19 |
20 | test('Renders amp-live-list element', async t => {
21 | const LiveList = TestRenderer.create(React.createElement(Components.LiveList, {
22 | layout: 'container',
23 | 'data-poll-interval': '15000',
24 | 'data-max-items-per-page': '5',
25 | }))
26 | t.snapshot(LiveList.toJSON())
27 | })
28 |
29 | test('Renders amp-live-list with children', async t => {
30 | const Div = React.createElement('div', { id: 'child' })
31 | const LiveList = TestRenderer.create(React.createElement(Components.LiveList, null, Div))
32 | t.is(LiveList.root.findByType('div').props.id, 'child', 'should render a child with respective props.')
33 | t.snapshot(LiveList.toJSON())
34 | })
35 |
36 | test('Custom Tag with children', async t => {
37 | const Div = React.createElement('div', { id: 'child' })
38 | const Script = TestRenderer.create(React.createElement(Components.Script, { type: 'application/ld+json' }, Div))
39 | t.is(Script.root.props.type, 'application/ld+json', 'should identify ld+json type to append in head')
40 | // should return the child element to append in body
41 | t.snapshot(Script.toJSON())
42 | })
43 |
44 | test('Meta component', async t => {
45 | const Child = React.createElement('div', { id: 'child' })
46 | const testRenderer = TestRenderer.create(React.createElement(Components.Meta, { content: 'test' }, Child))
47 | t.is(testRenderer.toJSON(), null, 'Meta tag should return null.')
48 | })
49 |
50 | test('Link component', async t => {
51 | const Child = React.createElement('div', { id: 'child' })
52 | const testRenderer = TestRenderer.create(React.createElement(Components.Link, { content: 'test' }, Child))
53 | t.is(testRenderer.toJSON(), null, 'Link tag should return null.')
54 | })
55 |
56 | test('Title component', async t => {
57 | const Child = 'A title'
58 | const testRenderer = TestRenderer.create(React.createElement(Components.Title, null, Child))
59 | t.is(testRenderer.toJSON(), null, 'Title tag should return null.')
60 | })
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
RAMPT v4
3 |
AMP components aliases and shims for React SSR 16+ & styled-components v3
4 |
5 |
6 |
7 |
8 |
9 |
10 |

11 |
12 |
13 |
14 |
15 |
16 |

17 |
18 |

19 |
20 |
21 |
22 | Write AMP pages using React syntaxt right the way and style with your preferred style manager
23 |
24 |
25 | - :zap: AMP elements
26 | - Ready to render any AMP component
27 | - :nail_care: Modular CSS
28 | - Style with the power of Styled Components or Aphrodite or Your Own custom StyleManager!
29 |
30 |
31 |
32 |
33 |
34 | ## Contents
35 |
36 | - [Usage](#usage)
37 | - [Demo](#demo)
38 | - [API](#api)
39 | - [Configuration](#configuration)
40 | - [Contribute](#contributing)
41 |
42 |
43 | ## Usage
44 |
45 | ### Install
46 |
47 | - `npm i react-amp-template`
48 |
49 | ### Static Render
50 |
51 | ```javascript
52 | import React, { Fragment } from 'react'
53 | import styled from 'styled-components'
54 | import { renderToString, AMP } from 'react-amp-template'
55 |
56 | const { Title, Link, Carousel } = AMP
57 |
58 | const Body = styled.body`
59 | margin: 0 1rem;
60 | `
61 |
62 | const App = ({ title }) => (
63 |
64 | {title}
65 |
66 |
67 | Hello World
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | )
76 |
77 | export default props => renderToString()
78 | ```
79 |
80 |
81 | ## Demo
82 | [See complete example here](https://github.com/Ariel-Rodriguez/react-amp-template/tree/master/examples/simple-server)
83 |
84 |
85 | ## API
86 |
87 | ### renderToString
88 |
89 | ```javascript
90 | /**
91 | * Transform React component into HTML AMP format.
92 | *
93 | * @returns {String} html
94 | * @param {Class|Object} body React component to render
95 | * @param {Object} options Settings
96 | * @property {string} options.cdnURI absolute URL to AMP CDN
97 | * @property {string} options.boilerplate HTML string which contains AMP boilerplate styles
98 | * @property {object} options.extensions Key map of references to specify an extension version
99 | * @property {object} options.extensions.default default version for all amp-extensions e.g '0.1'
100 | * @property {object} options.extensions.extension [extension-name]
101 | ** specify custom version for derived extension e.g: 'amp-sticky-ad': '1.0'
102 | import { renderToString } from 'react-amp-template'
103 | ```
104 |
105 | #### AMP components
106 |
107 | ```javascript
108 | import { AMP } from 'react-amp-template'
109 |
110 | const AdUnit = () =>
111 | ```
112 | - RAMPT provides shorthands for amp-custom-elements. A \[ get \] operation on { AMP } module returns Node element and automatically registers the `` tag required by AMP.
113 |
114 | - The following components could be used in case of need to ad elements into `` element
115 |
116 | ```javascript
117 |
118 | ```
119 |
120 | - By default every amp-script address to version 0.1. However it can be customized.
121 |
122 | ```javascript
123 | renderToString(, {
124 | extensions: {
125 | default: 0.2,
126 | 'amp-sticky-unit': 1.0,
127 | }
128 | })
129 | ```
130 |
131 | #### LD+JSON
132 |
133 | ```javascript
134 |
138 | ```
139 |
140 |
141 | ## Configuration
142 |
143 | ### Babel
144 | - Setup the environment as recomends React and Styled-Components server rendering.
145 |
146 | #### React | Styled Components
147 |
148 | `npm i -D babel-plugin-styled-components babel-preset-react`
149 |
150 | ```json
151 | {
152 | "presets": [
153 | "stage-0",
154 | "react"
155 | ],
156 | "plugins": [
157 | ["babel-plugin-styled-components", { "ssr": true }]
158 | ]
159 | }
160 | ```
161 |
162 |
163 | ## Contributing
164 |
165 | - Fork the repository
166 | - `npm install`
167 | - `npm run dev`
168 | - Create pull request
169 |
170 | ### Build examples
171 |
172 | - `cd examples/simple-server`
173 | - `npm install && npm start`
174 |
175 | ## License
176 |
177 | This project is licensed under the Apache License, Version 2.0. Copyright (c) 2016 Ariel Fernando Rodriguez. For more information see `LICENSE.md`.
178 |
--------------------------------------------------------------------------------