├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .huskyrc ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── babel.config.js ├── boilerplates ├── react-union-boilerplate-basic │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .huskyrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── public │ │ └── SampleApp │ │ │ ├── css │ │ │ └── front.css │ │ │ ├── favicon.ico │ │ │ ├── fonts │ │ │ └── roboto │ │ │ │ ├── roboto-bold.woff │ │ │ │ ├── roboto-light.woff │ │ │ │ ├── roboto-medium.woff │ │ │ │ ├── roboto-regular.woff │ │ │ │ └── roboto-thin.woff │ │ │ └── index.ejs │ ├── src │ │ ├── apps │ │ │ └── SampleApp │ │ │ │ ├── components │ │ │ │ └── Root │ │ │ │ │ ├── Root.css │ │ │ │ │ ├── Root.js │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── routes.js │ │ └── widgets │ │ │ ├── Content │ │ │ ├── components │ │ │ │ ├── NestedComponent.js │ │ │ │ └── Root.js │ │ │ └── index.js │ │ │ └── Hero │ │ │ ├── components │ │ │ ├── Root.js │ │ │ ├── __tests__ │ │ │ │ ├── Root-test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Root-test.js.snap │ │ │ └── logo.png │ │ │ └── index.js │ └── testsSetup.js ├── react-union-boilerplate-liferay │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .huskyrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── babel.config.js │ ├── liferay │ │ ├── content-portlet │ │ │ ├── .gitignore │ │ │ ├── bnd.bnd │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── java │ │ │ │ └── eu │ │ │ │ │ └── lundegaard │ │ │ │ │ └── content │ │ │ │ │ ├── constants │ │ │ │ │ └── ContentPortletKeys.java │ │ │ │ │ └── portlet │ │ │ │ │ └── ContentPortlet.java │ │ │ │ └── resources │ │ │ │ ├── META-INF │ │ │ │ └── resources │ │ │ │ │ ├── css │ │ │ │ │ └── main.scss │ │ │ │ │ ├── init.jsp │ │ │ │ │ └── view.jsp │ │ │ │ └── content │ │ │ │ └── Language.properties │ │ ├── counter-portlet │ │ │ ├── .gitignore │ │ │ ├── bnd.bnd │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── java │ │ │ │ └── eu │ │ │ │ │ └── lundegaard │ │ │ │ │ └── counter │ │ │ │ │ ├── constants │ │ │ │ │ └── CounterPortletKeys.java │ │ │ │ │ └── portlet │ │ │ │ │ └── CounterPortlet.java │ │ │ │ └── resources │ │ │ │ ├── META-INF │ │ │ │ └── resources │ │ │ │ │ ├── css │ │ │ │ │ └── main.scss │ │ │ │ │ ├── init.jsp │ │ │ │ │ └── view.jsp │ │ │ │ └── content │ │ │ │ └── Language.properties │ │ ├── hero-portlet │ │ │ ├── .gitignore │ │ │ ├── bnd.bnd │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── java │ │ │ │ └── eu │ │ │ │ │ └── lundegaard │ │ │ │ │ └── hero │ │ │ │ │ ├── constants │ │ │ │ │ └── HeroPortletKeys.java │ │ │ │ │ └── portlet │ │ │ │ │ └── HeroPortlet.java │ │ │ │ └── resources │ │ │ │ ├── META-INF │ │ │ │ └── resources │ │ │ │ │ ├── css │ │ │ │ │ └── main.scss │ │ │ │ │ ├── init.jsp │ │ │ │ │ └── view.jsp │ │ │ │ └── content │ │ │ │ └── Language.properties │ │ └── liferay-sandbox │ │ │ └── docker-compose.yml │ ├── package.json │ ├── packages │ │ ├── app-counter │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── css │ │ │ │ │ └── front.css │ │ │ │ ├── favicon.ico │ │ │ │ ├── fonts │ │ │ │ │ └── roboto │ │ │ │ │ │ ├── roboto-bold.woff │ │ │ │ │ │ ├── roboto-light.woff │ │ │ │ │ │ ├── roboto-medium.woff │ │ │ │ │ │ ├── roboto-regular.woff │ │ │ │ │ │ └── roboto-thin.woff │ │ │ │ └── index.ejs │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root │ │ │ │ │ ├── Root.css │ │ │ │ │ ├── Root.js │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── routes.js │ │ ├── app-demo │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── css │ │ │ │ │ └── front.css │ │ │ │ ├── favicon.ico │ │ │ │ ├── fonts │ │ │ │ │ └── roboto │ │ │ │ │ │ ├── roboto-bold.woff │ │ │ │ │ │ ├── roboto-light.woff │ │ │ │ │ │ ├── roboto-medium.woff │ │ │ │ │ │ ├── roboto-regular.woff │ │ │ │ │ │ └── roboto-thin.woff │ │ │ │ └── index.ejs │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root │ │ │ │ │ ├── Root.css │ │ │ │ │ ├── Root.js │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── routes.js │ │ ├── ui-components │ │ │ ├── package.json │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Link.js │ │ │ │ └── index.js │ │ ├── widget-content │ │ │ ├── package.json │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root.js │ │ │ │ └── index.js │ │ ├── widget-counter │ │ │ ├── package.json │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root.js │ │ │ │ └── index.js │ │ └── widget-hero │ │ │ ├── package.json │ │ │ └── src │ │ │ ├── components │ │ │ ├── Root.js │ │ │ ├── __tests__ │ │ │ │ ├── Root-test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Root-test.js.snap │ │ │ └── logo.png │ │ │ └── index.js │ ├── testsSetup.js │ ├── union.config.js │ └── yarn.lock ├── react-union-boilerplate-monorepo │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .huskyrc │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── packages │ │ ├── app-sample │ │ │ ├── package.json │ │ │ ├── public │ │ │ │ ├── css │ │ │ │ │ └── front.css │ │ │ │ ├── favicon.ico │ │ │ │ ├── fonts │ │ │ │ │ └── roboto │ │ │ │ │ │ ├── roboto-bold.woff │ │ │ │ │ │ ├── roboto-light.woff │ │ │ │ │ │ ├── roboto-medium.woff │ │ │ │ │ │ ├── roboto-regular.woff │ │ │ │ │ │ └── roboto-thin.woff │ │ │ │ └── index.ejs │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root │ │ │ │ │ ├── Root.css │ │ │ │ │ ├── Root.js │ │ │ │ │ └── index.js │ │ │ │ ├── index.js │ │ │ │ └── routes.js │ │ ├── widget-content │ │ │ ├── package.json │ │ │ └── src │ │ │ │ ├── components │ │ │ │ └── Root.js │ │ │ │ └── index.js │ │ └── widget-hero │ │ │ ├── package.json │ │ │ └── src │ │ │ ├── components │ │ │ ├── Root.js │ │ │ ├── __tests__ │ │ │ │ ├── Root-test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Root-test.js.snap │ │ │ └── logo.png │ │ │ └── index.js │ ├── testsSetup.js │ └── union.config.js └── react-union-boilerplate-ssr-basic │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .huskyrc │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── public │ └── SampleApp │ │ ├── css │ │ └── front.css │ │ ├── favicon.ico │ │ ├── fonts │ │ └── roboto │ │ │ ├── roboto-bold.woff │ │ │ ├── roboto-light.woff │ │ │ ├── roboto-medium.woff │ │ │ ├── roboto-regular.woff │ │ │ └── roboto-thin.woff │ │ └── index.ejs │ ├── src │ ├── apps │ │ └── SampleApp │ │ │ ├── components │ │ │ └── Root │ │ │ │ ├── Root.css │ │ │ │ ├── Root.js │ │ │ │ └── index.js │ │ │ ├── index.js │ │ │ ├── index.ssr.js │ │ │ └── routes.js │ └── widgets │ │ ├── Content │ │ ├── components │ │ │ ├── NestedComponent.js │ │ │ └── Root.js │ │ └── index.js │ │ └── Hero │ │ ├── components │ │ ├── Root.js │ │ ├── __tests__ │ │ │ ├── Root-test.js │ │ │ └── __snapshots__ │ │ │ │ └── Root-test.js.snap │ │ └── logo.png │ │ └── index.js │ └── testsSetup.js ├── by-lundegaard.png ├── greenkeeper.json ├── jest.config.js ├── lerna.json ├── logo.svg ├── package.json ├── packages ├── babel-preset-react-union │ ├── README.md │ ├── package.json │ └── src │ │ └── index.js ├── eslint-config-react-union │ ├── .gitignore │ ├── README.md │ ├── STYLEGUIDE.md │ ├── base.js │ ├── index.js │ ├── package.json │ └── rules │ │ ├── base.js │ │ ├── imports.js │ │ └── react.js ├── react-union-liferay-build-tools │ ├── .gitignore │ ├── README.md │ ├── bin │ │ └── liferay-scripts.js │ ├── cli.js │ ├── package.json │ ├── scripts │ │ ├── .eslintrc │ │ ├── bundle.js │ │ └── run.js │ └── template-npmbundlerrc.json ├── react-union-polyfills │ ├── README.md │ ├── ie11.js │ ├── ie9.js │ ├── package.json │ └── stable.js ├── react-union-rendering-service │ ├── README.md │ ├── package.json │ └── src │ │ ├── healthMiddleware.js │ │ ├── index.js │ │ ├── isRequestForHTML.js │ │ ├── outputBufferingMiddleware.js │ │ ├── renderApplication.js │ │ ├── renderingMiddleware.js │ │ ├── resolveInitialProps.js │ │ └── startRenderingService.js ├── react-union-scripts │ ├── .gitignore │ ├── README.md │ ├── bin │ │ └── react-union-scripts.js │ ├── cli.js │ ├── package.json │ └── scripts │ │ ├── .eslintrc │ │ ├── build.js │ │ ├── jest │ │ ├── fileTransformer.js │ │ └── scssTransformer.js │ │ ├── lib │ │ ├── __mocks__ │ │ │ └── fs.js │ │ ├── __tests__ │ │ │ ├── fs-test.js │ │ │ └── utils-test.js │ │ ├── cli.js │ │ ├── fs.js │ │ └── utils.js │ │ ├── run.js │ │ ├── start.js │ │ ├── startDevServer.js │ │ ├── test.js │ │ ├── webpack.config.js │ │ └── webpack │ │ ├── common.parts.js │ │ ├── loaders.parts.js │ │ └── plugins.parts.js └── react-union │ ├── .gitignore │ ├── .travis.yml │ ├── README.md │ ├── package.json │ └── src │ ├── components │ ├── Union │ │ ├── Union.js │ │ └── index.js │ ├── Widget │ │ ├── Widget.js │ │ └── index.js │ └── index.js │ ├── constants.js │ ├── contexts.js │ ├── decorators │ ├── index.js │ └── withErrorBoundary │ │ ├── index.js │ │ └── withErrorBoundary.js │ ├── dom.js │ ├── index.js │ ├── routing.js │ ├── routing.test.js │ ├── scanning │ ├── cheerio.js │ ├── dom.js │ └── index.js │ ├── shapes.js │ └── utils.js ├── rollup.config.js ├── scripts └── CI │ ├── repoIntegrity.js │ └── verdaccio.yaml ├── tests └── enzymeSetup.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.js] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [package.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | boilerplates/ 3 | packages/*/node_modules 4 | packages/*/dist 5 | packages/*/es 6 | packages/*/lib 7 | boilerplates/*/node_modules 8 | boilerplates/*/build 9 | boilerplates/*/dist 10 | boilerplates/*/*/node_modules 11 | boilerplates/*/*/build 12 | boilerplates/*/*/dist 13 | boilerplates/react-union-boilerplate-liferay-basic/liferay 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['react-union'], 4 | }; 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged", 4 | "pre-push": "yarn lint && yarn test" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10.10.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.MD 2 | package.json 3 | packages/*/node_modules 4 | packages/*/dist 5 | packages/*/es 6 | packages/*/lib 7 | boilerplates/*/node_modules 8 | boilerplates/*/build 9 | boilerplates/*/dist 10 | boilerplates/*/*/node_modules 11 | boilerplates/*/*/build 12 | boilerplates/*/*/dist 13 | boilerplates/react-union-boilerplate-liferay-basic/liferay-theme 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "useTabs": true 8 | } 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | before_install: 5 | - curl -o- -L https://yarnpkg.com/install.sh | bash 6 | - export PATH=$HOME/.yarn/bin:$PATH 7 | cache: 8 | yarn: true 9 | directories: 10 | - node_modules 11 | script: node ./scripts/CI/repoIntegrity.js 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at developer@lundegaard.eu. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Create a new branch from an up to date master branch on your fork. 4 | 5 | 1. Fork the react-union repository on Github 6 | 2. Clone your fork to your local machine 7 | ```sh 8 | git clone git@github.com:/react-union.git 9 | ``` 10 | 3. Create a branch 11 | ```sh 12 | git checkout -b my-topic-branch` 13 | ``` 14 | 4. Make your changes, lint, test, then push to to GitHub with `git push origin my-topic-branch`. 15 | - Make one or more atomic commits. Use interactive rebase or amend if necessary. 16 | 5. Visit GitHub and make your pull request. 17 | 18 | For synchronizing master branch between fork and lundegaard repository: 19 | ```sh 20 | git remote add upstream git@github.com:lundegaard/react-union.git 21 | git checkout master 22 | git fetch upstream 23 | git merge upstream/master 24 | ``` 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lundegaard a.s. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Roadmap to v1.0.0 2 | 3 | Breaking Changes: 4 | 5 | * store arg is not in route.getComponent 6 | * START_SCAN, END_SCAN instead of START_BOOT, END_BOOT 7 | * diffrent run of scan - Using Union component 8 | * Updated to react 16.4 - Using new context and lifecycles. Breaks the compatibility with older React. 9 | 10 | ``` 11 | [*] Monorepo with Yarn workspaces 12 | [*] Travis build/release 13 | [*] Prettier 14 | [*] Husky 15 | [ ] react-union-scripts init 16 | [ ] new widget 17 | [ ] new project from boilerplate 18 | [*] READMEs 19 | [ ] react-union-scripts test 20 | [ ] Detailed documentation 21 | [ ] Github Wiki or Web docs 22 | [ ] Examples/Recipes 23 | [*] Basic 24 | [ ] Basic with Redux 25 | [*] Liferay 7 + Senna 26 | [ ] Real world - widget communication, styles, resources, JWT, INTL, Permissions 27 | [ ] Other CMS - Wordpress, Joomla, KeystoneJS, ... 28 | [ ] Tests 29 | [*] Own repos for: 30 | [*] R.injectableStore 31 | [ ] union.config.js validator 32 | [ ] missing options 33 | [ ] name of the module cannot contain special characters 34 | [ ] Minor tasks 35 | [*] Fix hardcoded webpack resolver for eslint import plugin 36 | [ ] Update babel 37 | [*] Fill in "package.json"s 38 | [*] repository 39 | [*] authors 40 | [ ] VIVO to keywords? 41 | [ ] Possible integrations "xo" 42 | ``` 43 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const { NODE_ENV } = process.env; 2 | 3 | module.exports = { 4 | presets: [ 5 | [ 6 | 'babel-preset-react-union', 7 | { 8 | library: true, 9 | test: NODE_ENV === 'test', 10 | }, 11 | ], 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.js] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [package.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | stats.json 4 | docs/node_modules/* 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['react-union'], 4 | }; 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged", 4 | "pre-push": "yarn lint && yarn test --release" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "useTabs": true 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/README.md: -------------------------------------------------------------------------------- 1 | # React Union - Basic boilerplate 2 | 3 | This project can either be used as an example of react-union and react-union-scripts working together or as a starting point for your project. 4 | 5 | See https://react-union.org/boilerplates-basic for more information. 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['babel-preset-react-union'], 3 | }; 4 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-boilerplate-basic", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "dependencies": { 11 | "@babel/polyfill": "^7.11.5", 12 | "classnames": "^2.2.6", 13 | "prop-types": "^15.6.2", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1", 16 | "react-hot-loader": "^4.12.21", 17 | "react-union": "^0.20.0", 18 | "react-universal-component": "^4.5.0" 19 | }, 20 | "devDependencies": { 21 | "@babel/core": "^7.11.6", 22 | "babel-core": "^7.0.0-bridge", 23 | "babel-eslint": "^10.1.0", 24 | "babel-preset-react-union": "^0.20.0", 25 | "enzyme": "^3.11.0", 26 | "enzyme-adapter-react-16": "^1.15.2", 27 | "enzyme-to-json": "^3.5.0", 28 | "eslint": "^7.4.0", 29 | "eslint-config-react-union": "^0.20.0", 30 | "eslint-plugin-babel": "^5.3.1", 31 | "eslint-plugin-import": "^2.22.0", 32 | "eslint-plugin-react": "^7.20.3", 33 | "husky": "^3.0.8", 34 | "lint-staged": "^9.4.1", 35 | "prettier": "^2.0.5", 36 | "react-union-scripts": "^0.20.0" 37 | }, 38 | "scripts": { 39 | "build": "react-union-scripts build", 40 | "start": "react-union-scripts start --app SampleApp", 41 | "test": "react-union-scripts test", 42 | "lint": "yarn lint:eslint", 43 | "lint:eslint": "eslint --ext .js ./" 44 | }, 45 | "jest": { 46 | "snapshotSerializers": [ 47 | "enzyme-to-json/serializer" 48 | ] 49 | }, 50 | "lint-staged": { 51 | "**/*.js": [ 52 | "prettier --write", 53 | "yarn lint --fix", 54 | "git add" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/favicon.ico -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-bold.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-light.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-medium.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/public/SampleApp/fonts/roboto/roboto-thin.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/public/SampleApp/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 |
20 | 21 | 22 |
23 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/apps/SampleApp/components/Root/Root.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', Arial, Helvetica, sans-serif; 3 | } 4 | 5 | p { 6 | margin-bottom: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/apps/SampleApp/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Union } from 'react-union'; 3 | import { hot } from 'react-hot-loader'; 4 | 5 | import routes from '../../routes'; 6 | 7 | import './Root.css'; 8 | 9 | const Root = () => ; 10 | 11 | export default hot(module)(Root); 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/apps/SampleApp/components/Root/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/apps/SampleApp/index.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | import React from 'react'; 3 | import { justRender } from 'react-union'; 4 | 5 | import Root from './components/Root'; 6 | 7 | justRender(); 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/apps/SampleApp/routes.js: -------------------------------------------------------------------------------- 1 | import universal from 'react-universal-component'; 2 | 3 | export default [ 4 | { 5 | path: 'hero', 6 | component: universal(import('../../widgets/Hero')), 7 | }, 8 | { 9 | path: 'content', 10 | component: universal(import('../../widgets/Content')), 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Content/components/NestedComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { WidgetContext } from 'react-union'; 3 | 4 | const NestedComponent = () => ( 5 | 6 | {({ namespace, data }) => ( 7 |
8 | I am a nested component of the Content widget. My namespace is {namespace} and my 9 | initial data is {JSON.stringify(data)} as well, but I use WidgetContext to access 10 | this information. 11 |
12 | )} 13 |
14 | ); 15 | 16 | export default NestedComponent; 17 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Content/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import NestedComponent from './NestedComponent'; 4 | 5 | const Root = ({ namespace, data }) => ( 6 |
7 | I am the Content widget. My namespace is {namespace} and my initial data is 8 | {JSON.stringify(data)}. 9 |
10 | ); 11 | 12 | Root.propTypes = { 13 | data: PropTypes.shape({ 14 | foo: PropTypes.string, 15 | }), 16 | namespace: PropTypes.string, 17 | }; 18 | 19 | export default Root; 20 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Content/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Hero/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from './logo.png'; 4 | 5 | const Root = () => ; 6 | 7 | export default Root; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Hero/components/__tests__/Root-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Root from '../Root'; 4 | 5 | describe('', () => { 6 | const requiredProps = {}; 7 | 8 | it('should match snapshot', () => { 9 | const wrapper = shallow(); 10 | expect(wrapper).toMatchSnapshot(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Hero/components/__tests__/__snapshots__/Root-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match snapshot 1`] = ` 4 | 7 | `; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Hero/components/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-basic/src/widgets/Hero/components/logo.png -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/src/widgets/Hero/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-basic/testsSetup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.js] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [package.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | liferay/* 4 | packages/*/node_modules/* 5 | packages/*/build/* 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['react-union'], 4 | rules: { 5 | 'import/no-extraneous-dependencies': [ 6 | 'error', 7 | { 8 | devDependencies: [ 9 | '**/tools/*', 10 | '**/__tests__/*', 11 | 'testsSetup.js', 12 | 'packages/**/scripts/*.js', 13 | '*.config.js', 14 | ], 15 | }, 16 | ], 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged", 4 | "pre-push": "yarn lint && yarn test --release" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | hero-portlet 3 | liferay-theme 4 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "useTabs": true 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/README.md: -------------------------------------------------------------------------------- 1 | # React Union - Basic Liferay boilerplate 2 | 3 | This project can either be used as an example of integrating React Union with Liferay or as a starting point for your project. 4 | 5 | See https://react-union.org/boilerplates-liferay for more information. 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | api.cache.using(() => process.env.NODE_ENV); 3 | 4 | return api.env('test') 5 | ? { 6 | presets: ['babel-preset-react-union'], 7 | plugins: ['babel-plugin-dynamic-import-node'], 8 | } 9 | : { 10 | presets: ['babel-preset-react-union'], 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .sass-cache/ 3 | build/ 4 | target/ -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/bnd.bnd: -------------------------------------------------------------------------------- 1 | Bundle-Name: content-portlet 2 | Bundle-SymbolicName: eu.lundegaard.content 3 | Bundle-Version: 1.0.0 4 | Export-Package: eu.lundegaard.content.constants 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath group: "com.liferay", name: "com.liferay.gradle.plugins", version: "4.4.5" 4 | } 5 | 6 | repositories { 7 | maven { 8 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 9 | } 10 | } 11 | } 12 | 13 | apply plugin: "com.liferay.plugin" 14 | 15 | dependencies { 16 | compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "4.4.0" 17 | compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "4.0.8" 18 | compileOnly group: "javax.portlet", name: "portlet-api", version: "3.0.0" 19 | compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1" 20 | compileOnly group: "jstl", name: "jstl", version: "1.2" 21 | compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0" 22 | } 23 | 24 | repositories { 25 | maven { 26 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 27 | } 28 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/java/eu/lundegaard/content/constants/ContentPortletKeys.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.content.constants; 2 | 3 | /** 4 | * @author tomas.konrady 5 | */ 6 | public class ContentPortletKeys { 7 | 8 | public static final String CONTENT = 9 | "eu_lundegaard_content_ContentPortlet"; 10 | 11 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/java/eu/lundegaard/content/portlet/ContentPortlet.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.content.portlet; 2 | 3 | import eu.lundegaard.content.constants.ContentPortletKeys; 4 | 5 | import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet; 6 | 7 | import javax.portlet.Portlet; 8 | 9 | import org.osgi.service.component.annotations.Component; 10 | 11 | /** 12 | * @author tomas.konrady 13 | */ 14 | @Component( 15 | immediate = true, 16 | property = { 17 | "com.liferay.portlet.display-category=category.sample", 18 | "com.liferay.portlet.header-portlet-css=/css/main.css", 19 | "com.liferay.portlet.instanceable=true", 20 | "javax.portlet.display-name=Content", 21 | "javax.portlet.init-param.template-path=/", 22 | "javax.portlet.init-param.view-template=/view.jsp", 23 | "javax.portlet.name=" + ContentPortletKeys.CONTENT, 24 | "javax.portlet.resource-bundle=content.Language", 25 | "javax.portlet.security-role-ref=power-user,user" 26 | }, 27 | service = Portlet.class 28 | ) 29 | public class ContentPortlet extends MVCPortlet { 30 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/resources/META-INF/resources/css/main.scss: -------------------------------------------------------------------------------- 1 | .content-portlet { 2 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/resources/META-INF/resources/init.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | 3 | <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> 4 | 5 | <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@ 6 | taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@ 7 | taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@ 8 | taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %> 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/resources/META-INF/resources/view.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="/init.jsp" %> 2 | 3 | <%--suppress JSUnresolvedVariable, JSUnresolvedFunction --%> 4 | 7 | 8 |
9 | 17 | 18 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/content-portlet/src/main/resources/content/Language.properties: -------------------------------------------------------------------------------- 1 | javax.portlet.title.eu_lundegaard_content_ContentPortlet=Content 2 | content.caption=Hello from Content! -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .sass-cache/ 3 | build/ 4 | target/ -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/bnd.bnd: -------------------------------------------------------------------------------- 1 | Bundle-Name: counter-portlet 2 | Bundle-SymbolicName: eu.lundegaard.counter 3 | Bundle-Version: 1.0.0 4 | Export-Package: eu.lundegaard.counter.constants 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath group: "com.liferay", name: "com.liferay.gradle.plugins", version: "4.4.5" 4 | } 5 | 6 | repositories { 7 | maven { 8 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 9 | } 10 | } 11 | } 12 | 13 | apply plugin: "com.liferay.plugin" 14 | 15 | dependencies { 16 | compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "4.4.0" 17 | compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "4.0.8" 18 | compileOnly group: "javax.portlet", name: "portlet-api", version: "3.0.0" 19 | compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1" 20 | compileOnly group: "jstl", name: "jstl", version: "1.2" 21 | compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0" 22 | } 23 | 24 | repositories { 25 | maven { 26 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 27 | } 28 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/java/eu/lundegaard/counter/constants/CounterPortletKeys.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.counter.constants; 2 | 3 | /** 4 | * @author tomas.konrady 5 | */ 6 | public class CounterPortletKeys { 7 | 8 | public static final String COUNTER = 9 | "eu_lundegaard_counter_CounterPortlet"; 10 | 11 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/java/eu/lundegaard/counter/portlet/CounterPortlet.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.counter.portlet; 2 | 3 | import eu.lundegaard.counter.constants.CounterPortletKeys; 4 | 5 | import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet; 6 | 7 | import javax.portlet.Portlet; 8 | 9 | import org.osgi.service.component.annotations.Component; 10 | 11 | /** 12 | * @author tomas.konrady 13 | */ 14 | @Component( 15 | immediate = true, 16 | property = { 17 | "com.liferay.portlet.display-category=category.sample", 18 | "com.liferay.portlet.header-portlet-css=/css/main.css", 19 | "com.liferay.portlet.instanceable=true", 20 | "javax.portlet.display-name=Counter", 21 | "javax.portlet.init-param.template-path=/", 22 | "javax.portlet.init-param.view-template=/view.jsp", 23 | "javax.portlet.name=" + CounterPortletKeys.COUNTER, 24 | "javax.portlet.resource-bundle=content.Language", 25 | "javax.portlet.security-role-ref=power-user,user" 26 | }, 27 | service = Portlet.class 28 | ) 29 | public class CounterPortlet extends MVCPortlet { 30 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/resources/META-INF/resources/css/main.scss: -------------------------------------------------------------------------------- 1 | .counter-portlet { 2 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/resources/META-INF/resources/init.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | 3 | <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> 4 | 5 | <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@ 6 | taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@ 7 | taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@ 8 | taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %> 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/resources/META-INF/resources/view.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="/init.jsp" %> 2 | 3 | <%--suppress JSUnresolvedVariable, JSUnresolvedFunction --%> 4 | 7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/counter-portlet/src/main/resources/content/Language.properties: -------------------------------------------------------------------------------- 1 | javax.portlet.title.eu_lundegaard_counter_CounterPortlet=Counter 2 | counter.caption=Hello from Counter! -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .sass-cache/ 3 | build/ 4 | target/ -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/bnd.bnd: -------------------------------------------------------------------------------- 1 | Bundle-Name: hero-portlet 2 | Bundle-SymbolicName: eu.lundegaard.hero 3 | Bundle-Version: 1.0.0 4 | Export-Package: eu.lundegaard.hero.constants 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | dependencies { 3 | classpath group: "com.liferay", name: "com.liferay.gradle.plugins", version: "4.4.5" 4 | } 5 | 6 | repositories { 7 | maven { 8 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 9 | } 10 | } 11 | } 12 | 13 | apply plugin: "com.liferay.plugin" 14 | 15 | dependencies { 16 | compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "4.4.0" 17 | compileOnly group: "com.liferay.portal", name: "com.liferay.util.taglib", version: "4.0.8" 18 | compileOnly group: "javax.portlet", name: "portlet-api", version: "3.0.0" 19 | compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1" 20 | compileOnly group: "jstl", name: "jstl", version: "1.2" 21 | compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0" 22 | } 23 | 24 | repositories { 25 | maven { 26 | url "https://repository-cdn.liferay.com/nexus/content/groups/public" 27 | } 28 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/java/eu/lundegaard/hero/constants/HeroPortletKeys.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.hero.constants; 2 | 3 | /** 4 | * @author tomas.konrady 5 | */ 6 | public class HeroPortletKeys { 7 | 8 | public static final String HERO = 9 | "eu_lundegaard_hero_HeroPortlet"; 10 | 11 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/java/eu/lundegaard/hero/portlet/HeroPortlet.java: -------------------------------------------------------------------------------- 1 | package eu.lundegaard.hero.portlet; 2 | 3 | import eu.lundegaard.hero.constants.HeroPortletKeys; 4 | 5 | import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet; 6 | 7 | import javax.portlet.Portlet; 8 | 9 | import org.osgi.service.component.annotations.Component; 10 | 11 | /** 12 | * @author tomas.konrady 13 | */ 14 | @Component( 15 | immediate = true, 16 | property = { 17 | "com.liferay.portlet.display-category=category.sample", 18 | "com.liferay.portlet.header-portlet-css=/css/main.css", 19 | "com.liferay.portlet.instanceable=true", 20 | "javax.portlet.display-name=Hero", 21 | "javax.portlet.init-param.template-path=/", 22 | "javax.portlet.init-param.view-template=/view.jsp", 23 | "javax.portlet.name=" + HeroPortletKeys.HERO, 24 | "javax.portlet.resource-bundle=content.Language", 25 | "javax.portlet.security-role-ref=power-user,user" 26 | }, 27 | service = Portlet.class 28 | ) 29 | public class HeroPortlet extends MVCPortlet { 30 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/resources/META-INF/resources/css/main.scss: -------------------------------------------------------------------------------- 1 | .hero-portlet { 2 | } -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/resources/META-INF/resources/init.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | 3 | <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> 4 | 5 | <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@ 6 | taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@ 7 | taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@ 8 | taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %> 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/resources/META-INF/resources/view.jsp: -------------------------------------------------------------------------------- 1 | <%@ include file="/init.jsp" %> 2 | 3 | <%--suppress JSUnresolvedVariable, JSUnresolvedFunction --%> 4 | 7 | 8 |
9 | 10 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/hero-portlet/src/main/resources/content/Language.properties: -------------------------------------------------------------------------------- 1 | javax.portlet.title.eu_lundegaard_hero_HeroPortlet=Hero 2 | hero.caption=Hello from Hero! -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/liferay/liferay-sandbox/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | liferay: 5 | image: liferay/portal:7.2.1-ga2 6 | ports: 7 | - "8081:8080" 8 | volumes: 9 | - ./deploy:/etc/liferay/mount/deploy 10 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-boilerplate-liferay", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "devDependencies": { 10 | "@babel/core": "^7.11.6", 11 | "babel-core": "^7.0.0-bridge", 12 | "babel-eslint": "^10.1.0", 13 | "babel-plugin-dynamic-import-node": "^2.3.3", 14 | "babel-preset-react-union": "^0.20.0", 15 | "enzyme": "^3.11.0", 16 | "enzyme-adapter-react-16": "^1.15.2", 17 | "enzyme-to-json": "^3.5.0", 18 | "eslint": "^7.4.0", 19 | "eslint-config-react-union": "^0.20.0", 20 | "eslint-plugin-babel": "^5.3.1", 21 | "eslint-plugin-import": "^2.22.0", 22 | "eslint-plugin-react": "^7.20.3", 23 | "husky": "^3.0.8", 24 | "lint-staged": "^9.4.1", 25 | "prettier": "^2.0.5", 26 | "ramda": "^0.27.0", 27 | "ramda-extension": "^0.10.3", 28 | "react-union-liferay-build-tools": "^0.20.0", 29 | "react-union-scripts": "^0.20.0", 30 | "rimraf": "^3.0.2" 31 | }, 32 | "scripts": { 33 | "build": "yarn clean && yarn build:liferay && yarn bundle", 34 | "build:dev": "yarn clean && yarn build:liferay-dev && yarn bundle", 35 | "build:liferay": "react-union-scripts build --release --target liferay", 36 | "build:liferay-dev": "react-union-scripts build --target liferay", 37 | "bundle": "liferay-scripts bundle", 38 | "clean": "rimraf build dist", 39 | "start": "react-union-scripts start --app app-demo", 40 | "start:proxy": "yarn start --proxy --target liferay", 41 | "test": "react-union-scripts test", 42 | "lint": "yarn lint:eslint", 43 | "lint:eslint": "eslint --ext .js ./", 44 | "widget-content": "yarn workspace @union-liferay/widget-content", 45 | "widget-hero": "yarn workspace @union-liferay/widget-hero", 46 | "ui-components": "yarn workspace @union-liferay/ui-components" 47 | }, 48 | "workspaces": [ 49 | "packages/*" 50 | ], 51 | "lint-staged": { 52 | "**/*.js": [ 53 | "prettier --write", 54 | "yarn lint --fix", 55 | "git add" 56 | ] 57 | }, 58 | "jest": { 59 | "snapshotSerializers": [ 60 | "enzyme-to-json/serializer" 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/app-counter", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "dependencies": { 10 | "@babel/polyfill": "^7.11.5", 11 | "@union-liferay/widget-counter": "^0.20.0", 12 | "document-ready": "^2.0.2", 13 | "prop-types": "^15.6.2", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1", 16 | "react-hot-loader": "^4.12.21", 17 | "react-union": "^0.20.0", 18 | "react-universal-component": "^4.5.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/favicon.ico -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-bold.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-light.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-medium.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/fonts/roboto/roboto-thin.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 | 10 | 11 | 14 | 15 |
16 | 17 | 18 |
19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/src/components/Root/Root.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', Arial, Helvetica, sans-serif; 3 | } 4 | 5 | p { 6 | margin-bottom: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/src/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Union } from 'react-union'; 3 | import { hot } from 'react-hot-loader'; 4 | 5 | import routes from '../../routes'; 6 | 7 | import './Root.css'; 8 | 9 | const Root = () => ; 10 | 11 | export default hot(module)(Root); 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/src/components/Root/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { justRender, justUnmountComponentAtNode } from 'react-union'; 3 | import ready from 'document-ready'; 4 | 5 | import Root from './components/Root'; 6 | 7 | const rootId = 'app-counter-root'; 8 | 9 | ready(() => { 10 | justRender(, rootId); 11 | 12 | if (window.Liferay) { 13 | window.Liferay.on('startNavigate', () => justUnmountComponentAtNode(rootId)); 14 | window.Liferay.on('endNavigate', () => justRender(, rootId)); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-counter/src/routes.js: -------------------------------------------------------------------------------- 1 | import universal from 'react-universal-component'; 2 | 3 | export default [ 4 | { 5 | path: 'counter', 6 | component: universal(import('@union-liferay/widget-counter')), 7 | }, 8 | ]; 9 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/app-demo", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "dependencies": { 10 | "@babel/polyfill": "^7.11.5", 11 | "@union-liferay/widget-content": "^0.20.0", 12 | "@union-liferay/widget-hero": "^0.20.0", 13 | "document-ready": "^2.0.2", 14 | "prop-types": "^15.6.2", 15 | "react": "^16.13.1", 16 | "react-dom": "^16.13.1", 17 | "react-hot-loader": "^4.12.21", 18 | "react-union": "^0.20.0", 19 | "react-universal-component": "^4.5.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/favicon.ico -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-bold.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-light.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-medium.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/fonts/roboto/roboto-thin.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 | 10 | 11 | 14 | 15 |
16 | 17 | 18 |
19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/src/components/Root/Root.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', Arial, Helvetica, sans-serif; 3 | } 4 | 5 | p { 6 | margin-bottom: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/src/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Union } from 'react-union'; 3 | import { hot } from 'react-hot-loader'; 4 | 5 | import routes from '../../routes'; 6 | 7 | import './Root.css'; 8 | 9 | const Root = () => ; 10 | 11 | export default hot(module)(Root); 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/src/components/Root/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { justRender, justUnmountComponentAtNode } from 'react-union'; 3 | import ready from 'document-ready'; 4 | 5 | import Root from './components/Root'; 6 | 7 | const rootId = 'app-demo-root'; 8 | 9 | ready(() => { 10 | justRender(, rootId); 11 | 12 | if (window.Liferay) { 13 | window.Liferay.on('startNavigate', () => justUnmountComponentAtNode(rootId)); 14 | window.Liferay.on('endNavigate', () => justRender(, rootId)); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/app-demo/src/routes.js: -------------------------------------------------------------------------------- 1 | import universal from 'react-universal-component'; 2 | 3 | export default [ 4 | { 5 | path: 'hero', 6 | component: universal(import('@union-liferay/widget-hero')), 7 | }, 8 | { 9 | path: 'content', 10 | component: universal(import('@union-liferay/widget-content')), 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/ui-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/ui-components", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "main": "src/index.js", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "dependencies": { 11 | "prop-types": "^15.6.2", 12 | "ramda": "^0.27.0", 13 | "ramda-extension": "^0.10.3", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/ui-components/src/components/Link.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Link = ({ href, to, ...rest }) => ( 5 | 6 | ); 7 | 8 | Link.propTypes = { 9 | /** If set, the senna navigation is turned off. */ 10 | href: PropTypes.string, 11 | /** If set, the senna navigation is used. */ 12 | to: PropTypes.string, 13 | }; 14 | 15 | export default Link; 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/ui-components/src/index.js: -------------------------------------------------------------------------------- 1 | export { default as Link } from './components/Link'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/widget-content", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "main": "src/index.js", 10 | "dependencies": { 11 | "@union-liferay/ui-components": "^0.20.0", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1" 15 | }, 16 | "devDependencies": { 17 | "enzyme": "^3.11.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-content/src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Link } from '@union-liferay/ui-components'; 4 | 5 | const Root = ({ data: { messages } }) => ( 6 |
7 |

{messages.heading}

8 |

{messages.content}

9 |

10 | Back 11 |

12 |
13 | ); 14 | 15 | Root.propTypes = { 16 | data: PropTypes.shape({ 17 | messages: PropTypes.shape({ 18 | heading: PropTypes.node, 19 | content: PropTypes.node, 20 | }), 21 | }), 22 | }; 23 | 24 | export default Root; 25 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-content/src/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-counter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/widget-counter", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "main": "src/index.js", 10 | "dependencies": { 11 | "@union-liferay/ui-components": "^0.20.0", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1" 15 | }, 16 | "devDependencies": { 17 | "enzyme": "^3.11.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-counter/src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment, useState } from 'react'; 2 | 3 | const Root = () => { 4 | const [counter, setCounter] = useState(0); 5 | 6 | return ( 7 | 8 |

9 | {counter} 10 |

11 | 14 |
15 | ); 16 | }; 17 | 18 | export default Root; 19 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-counter/src/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-liferay/widget-hero", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "engines": { 7 | "node": ">=8" 8 | }, 9 | "main": "src/index.js", 10 | "dependencies": { 11 | "@union-liferay/ui-components": "^0.20.0", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.13.1", 14 | "react-dom": "^16.13.1" 15 | }, 16 | "devDependencies": { 17 | "enzyme": "^3.11.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from '@union-liferay/ui-components'; 3 | 4 | import logo from './logo.png'; 5 | 6 | const Root = () => ( 7 |
8 | 9 | 19 |
20 | ); 21 | 22 | export default Root; 23 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/components/__tests__/Root-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Root from '../Root'; 4 | 5 | describe('', () => { 6 | const requiredProps = {}; 7 | 8 | it('should match snapshot', () => { 9 | const wrapper = shallow(); 10 | expect(wrapper).toMatchSnapshot(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/components/__tests__/__snapshots__/Root-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match snapshot 1`] = ` 4 |
5 | 8 | 26 |
27 | `; 28 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/components/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/components/logo.png -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/packages/widget-hero/src/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/testsSetup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-liferay/union.config.js: -------------------------------------------------------------------------------- 1 | const getConfig = appName => ({ 2 | name: appName, 3 | // set up the public path of Liferay AMD loader 4 | publicPath: `/o/${appName}/build/`, 5 | proxy: { 6 | // set up the URL of your locally running Liferay 7 | target: 'http://localhost:8081', 8 | // set up the public path of Liferay AMD loader 9 | publicPath: `/o/${appName}/build/`, 10 | }, 11 | }); 12 | module.exports = ({ target }) => ({ 13 | workspaces: { 14 | widgetPattern: ['widget'], 15 | appPattern: ['app'], 16 | }, 17 | sourceMaps: false, 18 | ...(target === 'liferay' 19 | ? { 20 | outputMapper: { 21 | js: 'js', 22 | css: 'css', 23 | }, 24 | apps: [getConfig('app-demo'), getConfig('app-counter')], 25 | } 26 | : {}), 27 | }); 28 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.js] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [package.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | stats.json 4 | packages/*/node_modules/* 5 | packages/*/build/* 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['react-union'], 4 | }; 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged", 4 | "pre-push": "yarn lint && yarn test --release" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/README.md: -------------------------------------------------------------------------------- 1 | # React Union - Basic monorepo boilerplate 2 | 3 | This project can either be used as an example of react-union and react-union-scripts working together or as a starting point for your project. 4 | 5 | See https://react-union.org/boilerplates-monorepo for more information. 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | api.cache.using(() => process.env.NODE_ENV); 3 | 4 | return api.env('test') 5 | ? { 6 | presets: ['babel-preset-react-union'], 7 | plugins: ['babel-plugin-dynamic-import-node'], 8 | } 9 | : { 10 | presets: ['babel-preset-react-union'], 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-boilerplate-monorepo", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "workspaces": [ 11 | "packages/*" 12 | ], 13 | "devDependencies": { 14 | "@babel/core": "^7.11.6", 15 | "babel-core": "^7.0.0-bridge", 16 | "babel-eslint": "^10.1.0", 17 | "babel-plugin-dynamic-import-node": "^2.3.3", 18 | "babel-preset-react-union": "^0.20.0", 19 | "enzyme": "^3.11.0", 20 | "enzyme-adapter-react-16": "^1.15.2", 21 | "enzyme-to-json": "^3.5.0", 22 | "eslint": "^7.4.0", 23 | "eslint-config-react-union": "^0.20.0", 24 | "eslint-plugin-babel": "^5.3.1", 25 | "eslint-plugin-import": "^2.22.0", 26 | "eslint-plugin-react": "^7.20.3", 27 | "husky": "^3.0.8", 28 | "lint-staged": "^9.4.1", 29 | "prettier": "^2.0.5", 30 | "react-union-scripts": "^0.20.0" 31 | }, 32 | "scripts": { 33 | "build": "react-union-scripts build", 34 | "start": "react-union-scripts start --app app-sample", 35 | "test": "react-union-scripts test", 36 | "lint": "yarn lint:eslint", 37 | "lint:eslint": "eslint --ext .js ./" 38 | }, 39 | "lint-staged": { 40 | "**/*.js": [ 41 | "prettier --write", 42 | "yarn lint --fix", 43 | "git add" 44 | ] 45 | }, 46 | "jest": { 47 | "snapshotSerializers": [ 48 | "enzyme-to-json/serializer" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-monorepo/app-sample", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "dependencies": { 11 | "@babel/polyfill": "^7.11.5", 12 | "@union-monorepo/widget-content": "^0.20.0", 13 | "@union-monorepo/widget-hero": "^0.20.0", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1", 16 | "react-hot-loader": "^4.12.21", 17 | "react-union": "^0.20.0", 18 | "react-universal-component": "^4.5.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/favicon.ico -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-bold.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-light.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-medium.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/fonts/roboto/roboto-thin.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/public/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= htmlWebpackPlugin.options.title %> 7 | 8 | 9 | 10 | 11 | 14 | 15 |
16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/src/components/Root/Root.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', Arial, Helvetica, sans-serif; 3 | } 4 | 5 | p { 6 | margin-bottom: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/src/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Union } from 'react-union'; 3 | import { hot } from 'react-hot-loader'; 4 | 5 | import routes from '../../routes'; 6 | 7 | import './Root.css'; 8 | 9 | const Root = () => ; 10 | 11 | export default hot(module)(Root); 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/src/components/Root/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/src/index.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | import React from 'react'; 3 | import { justRender } from 'react-union'; 4 | 5 | import Root from './components/Root'; 6 | 7 | justRender(); 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/app-sample/src/routes.js: -------------------------------------------------------------------------------- 1 | import universal from 'react-universal-component'; 2 | 3 | export default [ 4 | { 5 | path: 'hero', 6 | component: universal(import('@union-monorepo/widget-hero')), 7 | }, 8 | { 9 | path: 'content', 10 | component: universal(import('@union-monorepo/widget-content')), 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-content/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-monorepo/widget-content", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "main": "src/index.js", 7 | "repository": "https://github.com/lundegaard/react-union", 8 | "engines": { 9 | "node": ">=8" 10 | }, 11 | "dependencies": { 12 | "react": "^16.13.1", 13 | "react-dom": "^16.13.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-content/src/components/Root.js: -------------------------------------------------------------------------------- 1 | const Root = () => 'I am content.'; 2 | 3 | export default Root; 4 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-content/src/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@union-monorepo/widget-hero", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "main": "src/index.js", 7 | "repository": "https://github.com/lundegaard/react-union", 8 | "engines": { 9 | "node": ">=8" 10 | }, 11 | "dependencies": { 12 | "react": "^16.13.1", 13 | "react-dom": "^16.13.1" 14 | }, 15 | "devDependencies": { 16 | "enzyme": "^3.11.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from './logo.png'; 4 | 5 | const Root = () => ; 6 | 7 | export default Root; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/components/__tests__/Root-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Root from '../Root'; 4 | 5 | describe('', () => { 6 | const requiredProps = {}; 7 | 8 | it('should match snapshot', () => { 9 | const wrapper = shallow(); 10 | expect(wrapper).toMatchSnapshot(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/components/__tests__/__snapshots__/Root-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match snapshot 1`] = ` 4 | 7 | `; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/components/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/components/logo.png -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/src/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/testsSetup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-monorepo/union.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | workspaces: { 3 | widgetPattern: 'widget', 4 | appPattern: 'app', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.js] 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [package.json] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | stats.json 4 | docs/node_modules/* 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['react-union'], 4 | }; 5 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged", 4 | "pre-push": "yarn lint && yarn test --release" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": true, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "es5", 7 | "useTabs": true 8 | } 9 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/README.md: -------------------------------------------------------------------------------- 1 | # React Union - Basic boilerplate with SSR 2 | 3 | This project can either be used as an example of react-union and react-union-scripts working together or as a starting point for your project. 4 | 5 | See https://react-union.org/boilerplates-ssr for more information. 6 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = api => { 2 | api.cache.using(() => process.env.NODE_ENV); 3 | 4 | return api.env('test') 5 | ? { 6 | presets: ['babel-preset-react-union'], 7 | plugins: ['babel-plugin-dynamic-import-node'], 8 | } 9 | : { 10 | presets: ['babel-preset-react-union'], 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-boilerplate-ssr-basic", 3 | "version": "0.20.0", 4 | "private": true, 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "engines": { 8 | "node": ">=8" 9 | }, 10 | "dependencies": { 11 | "@babel/polyfill": "^7.11.5", 12 | "classnames": "^2.2.6", 13 | "prop-types": "^15.6.2", 14 | "react": "^16.13.1", 15 | "react-dom": "^16.13.1", 16 | "react-hot-loader": "^4.12.21", 17 | "react-union": "^0.20.0", 18 | "react-union-rendering-service": "^0.20.0", 19 | "react-universal-component": "^4.5.0" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.11.6", 23 | "babel-core": "^7.0.0-bridge", 24 | "babel-eslint": "^10.1.0", 25 | "babel-plugin-dynamic-import-node": "^2.3.3", 26 | "babel-preset-react-union": "^0.20.0", 27 | "enzyme": "^3.11.0", 28 | "enzyme-adapter-react-16": "^1.15.2", 29 | "enzyme-to-json": "^3.5.0", 30 | "eslint": "^7.4.0", 31 | "eslint-config-react-union": "^0.20.0", 32 | "eslint-plugin-babel": "^5.3.1", 33 | "eslint-plugin-import": "^2.22.0", 34 | "eslint-plugin-react": "^7.20.3", 35 | "husky": "^3.0.8", 36 | "lint-staged": "^9.4.1", 37 | "prettier": "^2.0.5", 38 | "react-union-scripts": "^0.20.0" 39 | }, 40 | "scripts": { 41 | "build": "react-union-scripts build --release", 42 | "start": "react-union-scripts start --app SampleApp", 43 | "start:server": "node build/SampleApp/server", 44 | "test": "react-union-scripts test", 45 | "lint": "yarn lint:eslint", 46 | "lint:eslint": "eslint --ext .js ./", 47 | "precommit": "lint-staged", 48 | "prepush": "yarn lint && yarn test --release" 49 | }, 50 | "jest": { 51 | "snapshotSerializers": [ 52 | "enzyme-to-json/serializer" 53 | ] 54 | }, 55 | "lint-staged": { 56 | "**/*.js": [ 57 | "prettier --write", 58 | "yarn lint --fix", 59 | "git add" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/favicon.ico -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-bold.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-light.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-medium.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-regular.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/fonts/roboto/roboto-thin.woff -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/public/SampleApp/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 |
19 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/components/Root/Root.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', Arial, Helvetica, sans-serif; 3 | } 4 | 5 | p { 6 | margin-bottom: 8px; 7 | } 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/components/Root/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Union } from 'react-union'; 3 | import { hot } from 'react-hot-loader'; 4 | 5 | import routes from '../../routes'; 6 | 7 | import './Root.css'; 8 | 9 | const Root = () => ; 10 | 11 | export default hot(module)(Root); 12 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/components/Root/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/index.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | import React from 'react'; 3 | import { justRender } from 'react-union'; 4 | 5 | import Root from './components/Root'; 6 | 7 | justRender(); 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/index.ssr.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { startRenderingService } from 'react-union-rendering-service'; 3 | 4 | import Root from './components/Root'; 5 | import routes from './routes'; 6 | 7 | const handleRequest = ({ render }) => render(, routes); 8 | 9 | export default startRenderingService(handleRequest); 10 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/apps/SampleApp/routes.js: -------------------------------------------------------------------------------- 1 | import universal from 'react-universal-component'; 2 | 3 | export default [ 4 | { 5 | path: 'hero', 6 | component: universal(import('../../widgets/Hero')), 7 | }, 8 | { 9 | path: 'content', 10 | component: universal(import('../../widgets/Content')), 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Content/components/NestedComponent.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { WidgetContext } from 'react-union'; 3 | 4 | const NestedComponent = () => ( 5 | 6 | {({ namespace, data }) => ( 7 |
8 | I am a nested component of the Content widget. My namespace is {namespace} and my 9 | initial data is {JSON.stringify(data)} as well, but I use WidgetContext to access 10 | this information. 11 |
12 | )} 13 |
14 | ); 15 | 16 | export default NestedComponent; 17 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Content/components/Root.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import NestedComponent from './NestedComponent'; 4 | 5 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); 6 | 7 | class Root extends Component { 8 | static async getInitialProps({ req }) { 9 | // NOTE: req is defined iff this method is executed in server context 10 | // This is to simulate that fetching data in a local network (Node.js to REST API) 11 | // is faster than roundtrips over the internet (browser to REST API) 12 | await sleep(req ? 10 : 1000); 13 | return { asyncData: 'This is some async content!' }; 14 | } 15 | 16 | static propTypes = { 17 | asyncData: PropTypes.string, 18 | data: PropTypes.shape({ foo: PropTypes.string }), 19 | namespace: PropTypes.string, 20 | }; 21 | 22 | static defaultProps = { 23 | asyncData: 'Fetching...', 24 | }; 25 | 26 | render() { 27 | const { asyncData, data, namespace } = this.props; 28 | 29 | return ( 30 |
31 | I am the Content widget. My namespace is {namespace} and my initial data is 32 | {JSON.stringify(data)}. 33 |
{asyncData}
34 |
35 | ); 36 | } 37 | } 38 | 39 | export default Root; 40 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Content/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/components/Root.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import logo from './logo.png'; 4 | 5 | const Root = () => ; 6 | 7 | export default Root; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/components/__tests__/Root-test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | import Root from '../Root'; 4 | 5 | describe('', () => { 6 | const requiredProps = {}; 7 | 8 | it('should match snapshot', () => { 9 | const wrapper = shallow(); 10 | expect(wrapper).toMatchSnapshot(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/components/__tests__/__snapshots__/Root-test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should match snapshot 1`] = ` 4 | 7 | `; 8 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/components/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/components/logo.png -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/src/widgets/Hero/index.js: -------------------------------------------------------------------------------- 1 | export default from './components/Root'; 2 | -------------------------------------------------------------------------------- /boilerplates/react-union-boilerplate-ssr-basic/testsSetup.js: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /by-lundegaard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lundegaard/react-union/5be21fb985b1cfa7b03465d576fbc2b941cdecb4/by-lundegaard.png -------------------------------------------------------------------------------- /greenkeeper.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": { 3 | "default": { 4 | "packages": [ 5 | "boilerplates/react-union-boilerplate-basic/package.json", 6 | "boilerplates/react-union-boilerplate-liferay-basic/package.json", 7 | "boilerplates/react-union-boilerplate-liferay-basic/packages/app-demo/package.json", 8 | "boilerplates/react-union-boilerplate-liferay-basic/packages/widget-content/package.json", 9 | "boilerplates/react-union-boilerplate-liferay-basic/packages/widget-hero/package.json", 10 | "boilerplates/react-union-boilerplate-liferay-ssr/package.json", 11 | "boilerplates/react-union-boilerplate-liferay-ssr/packages/app-demo/package.json", 12 | "boilerplates/react-union-boilerplate-liferay-ssr/packages/widget-content/package.json", 13 | "boilerplates/react-union-boilerplate-liferay-ssr/packages/widget-hero/package.json", 14 | "boilerplates/react-union-boilerplate-monorepo/package.json", 15 | "boilerplates/react-union-boilerplate-monorepo/packages/app-sample/package.json", 16 | "boilerplates/react-union-boilerplate-monorepo/packages/widget-content/package.json", 17 | "boilerplates/react-union-boilerplate-monorepo/packages/widget-hero/package.json", 18 | "boilerplates/react-union-boilerplate-ssr-basic/package.json", 19 | "package.json", 20 | "packages/babel-preset-react-union/package.json", 21 | "packages/eslint-config-react-union/package.json", 22 | "packages/react-union-scripts/package.json", 23 | "packages/react-union/package.json", 24 | "packages/react-union-rendering-service/package.json" 25 | ] 26 | } 27 | }, 28 | "ignore": ["lerna"] 29 | } 30 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const ignorePatterns = [ 2 | '/.history/', 3 | '/node_modules/', 4 | '/es/', 5 | '/dist/', 6 | '/lib/', 7 | '/boilerplates/', 8 | '/scripts/test.js', 9 | // FIXME: The tests in react-union-scripts have been broken for some time. :( 10 | '/packages/react-union-scripts', 11 | ]; 12 | 13 | module.exports = { 14 | bail: true, 15 | verbose: true, 16 | testPathIgnorePatterns: ignorePatterns, 17 | coveragePathIgnorePatterns: ignorePatterns, 18 | snapshotSerializers: ['enzyme-to-json/serializer'], 19 | setupTestFrameworkScriptFile: '/tests/enzymeSetup.js', 20 | }; 21 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.2.0", 3 | "version": "0.20.0", 4 | "command": { 5 | "init": { 6 | "exact": true 7 | }, 8 | "publish": { 9 | "ignoreChanges": [ 10 | "*.md", 11 | "test/**" 12 | ] 13 | } 14 | }, 15 | "npmClient": "yarn", 16 | "useWorkspaces": true 17 | } 18 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "description": "The React Union project is a collection of tools that allow you to build modern React applications for content management systems or enterprise portals.", 4 | "keywords": [ 5 | "react", 6 | "redux", 7 | "bootstrapper", 8 | "cms", 9 | "hybrid", 10 | "mix", 11 | "mixed", 12 | "standalone", 13 | "sitecore", 14 | "liferay", 15 | "wordpress", 16 | "drupal", 17 | "umbraco", 18 | "joomla", 19 | "magento", 20 | "adobe experience manager" 21 | ], 22 | "author": "Tomas Konrady ", 23 | "contributors": [ 24 | "Tomas Konrady ", 25 | "Vaclav Jancarik ", 26 | "Lukas Sulik ", 27 | "Jakub Kohout " 28 | ], 29 | "repository": "https://github.com/lundegaard/react-union", 30 | "license": "UNLICENSED", 31 | "devDependencies": { 32 | "@babel/core": "^7.11.6", 33 | "@babel/cli": "^7.11.6", 34 | "babel-core": "^7.0.0-bridge", 35 | "babel-eslint": "^10.1.0", 36 | "babel-jest": "^26.1.0", 37 | "babel-preset-react-union": "^0.20.0", 38 | "enzyme": "^3.11.0", 39 | "enzyme-adapter-react-16": "^1.15.2", 40 | "enzyme-to-json": "^3.5.0", 41 | "eslint": "^7.4.0", 42 | "eslint-config-react-union": "^0.20.0", 43 | "eslint-plugin-babel": "^5.3.1", 44 | "eslint-plugin-import": "^2.22.0", 45 | "eslint-plugin-react": "^7.20.3", 46 | "husky": "^3.0.8", 47 | "jest": "^24.0.0", 48 | "lerna": "^3.22.1", 49 | "lint-staged": "^9.4.1", 50 | "prettier": "^2.0.5", 51 | "ramda": "^0.27.0", 52 | "ramda-extension": "^0.10.3", 53 | "react": "^16.13.1", 54 | "react-dom": "^16.13.1", 55 | "rimraf": "^3.0.2", 56 | "rollup": "^1.1.2", 57 | "rollup-plugin-babel": "^4.3.2", 58 | "rollup-plugin-commonjs": "^10.1.0", 59 | "rollup-plugin-node-resolve": "^5.2.0", 60 | "rollup-plugin-replace": "^2.1.0", 61 | "rollup-plugin-terser": "^5.1.2" 62 | }, 63 | "workspaces": [ 64 | "packages/*", 65 | "boilerplates/*", 66 | "boilerplates/*/packages/*" 67 | ], 68 | "scripts": { 69 | "lint": "eslint --ext .js ./", 70 | "lint:fix": "yarn lint --fix", 71 | "test": "jest", 72 | "build": "lerna exec --scope react-union -- rollup -c=../../rollup.config.js", 73 | "prepublish": "yarn build", 74 | "publishAll": "lerna publish --preid=ci" 75 | }, 76 | "lint-staged": { 77 | "(packages|boilerplates)/**/*.js": [ 78 | "prettier --write", 79 | "yarn lint:fix", 80 | "git add" 81 | ] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/babel-preset-react-union/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | Shared babel config used in Lundegaard projects 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | # Babel-preset-react-union 55 | -------------------------------------------------------------------------------- /packages/babel-preset-react-union/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-react-union", 3 | "description": "Babel preset for React Union", 4 | "version": "0.20.0", 5 | "main": "src/index.js", 6 | "license": "MIT", 7 | "repository": "https://github.com/lundegaard/react-union", 8 | "contributors": [ 9 | "Tomas Konrady ", 10 | "Vaclav Jancarik " 11 | ], 12 | "peerDependencies": { 13 | "@babel/core": "^7.11.6", 14 | "core-js": "^3.0.0" 15 | }, 16 | "devDependencies": { 17 | "@babel/cli": "^7.11.6" 18 | }, 19 | "dependencies": { 20 | "@babel/plugin-external-helpers": "^7.10.4", 21 | "@babel/plugin-proposal-class-properties": "^7.10.4", 22 | "@babel/plugin-proposal-decorators": "^7.10.4", 23 | "@babel/plugin-proposal-do-expressions": "^7.10.4", 24 | "@babel/plugin-proposal-export-default-from": "^7.10.4", 25 | "@babel/plugin-proposal-export-namespace-from": "^7.10.4", 26 | "@babel/plugin-proposal-function-sent": "^7.10.4", 27 | "@babel/plugin-proposal-json-strings": "^7.10.4", 28 | "@babel/plugin-proposal-logical-assignment-operators": "^7.10.4", 29 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", 30 | "@babel/plugin-proposal-numeric-separator": "^7.10.4", 31 | "@babel/plugin-proposal-object-rest-spread": "^7.10.4", 32 | "@babel/plugin-proposal-optional-chaining": "^7.11.0", 33 | "@babel/plugin-proposal-pipeline-operator": "^7.10.4", 34 | "@babel/plugin-proposal-throw-expressions": "^7.10.4", 35 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 36 | "@babel/plugin-syntax-import-meta": "^7.10.4", 37 | "@babel/plugin-transform-modules-commonjs": "^7.10.4", 38 | "@babel/plugin-transform-react-constant-elements": "^7.10.4", 39 | "@babel/plugin-transform-regenerator": "^7.10.4", 40 | "@babel/plugin-transform-destructuring": "^7.10.4", 41 | "@babel/plugin-transform-runtime": "^7.11.5", 42 | "@babel/plugin-transform-spread": "^7.11.0", 43 | "@babel/preset-env": "^7.11.5", 44 | "@babel/preset-react": "^7.10.4", 45 | "@babel/runtime": "^7.11.2", 46 | "babel-plugin-dynamic-import-node": "^2.3.3", 47 | "babel-plugin-macros": "^2.8.0", 48 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 49 | "babel-plugin-universal-import": "^3.1.3" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/babel-preset-react-union/src/index.js: -------------------------------------------------------------------------------- 1 | const defaultTargets = { 2 | ie: 9, 3 | }; 4 | 5 | module.exports = ( 6 | api, 7 | { 8 | targets = defaultTargets, 9 | library = false, 10 | test = false, 11 | loose = true, 12 | universal = true, 13 | presetEnvOptions = {}, 14 | presetReactOptions = {}, 15 | filterPlugins = (x) => x, 16 | } 17 | ) => ({ 18 | presets: [ 19 | [ 20 | '@babel/preset-env', 21 | { 22 | loose, 23 | useBuiltIns: library ? false : 'entry', 24 | corejs: 3, 25 | modules: library ? false : 'auto', 26 | ...(targets ? { targets } : {}), 27 | ...presetEnvOptions, 28 | }, 29 | ], 30 | ['@babel/preset-react', presetReactOptions], 31 | ], 32 | plugins: [ 33 | '@babel/plugin-proposal-export-default-from', 34 | '@babel/plugin-proposal-logical-assignment-operators', 35 | ['@babel/plugin-proposal-optional-chaining', { loose }], 36 | ['@babel/plugin-proposal-pipeline-operator', { proposal: 'minimal' }], 37 | ['@babel/plugin-proposal-nullish-coalescing-operator', { loose }], 38 | '@babel/plugin-proposal-do-expressions', 39 | 40 | ['@babel/plugin-proposal-decorators', { legacy: true }], 41 | '@babel/plugin-proposal-function-sent', 42 | '@babel/plugin-proposal-export-namespace-from', 43 | '@babel/plugin-proposal-numeric-separator', 44 | '@babel/plugin-proposal-throw-expressions', 45 | 46 | '@babel/plugin-syntax-dynamic-import', 47 | '@babel/plugin-syntax-import-meta', 48 | ['@babel/plugin-proposal-class-properties', { loose }], 49 | '@babel/plugin-proposal-json-strings', 50 | 51 | '@babel/plugin-proposal-object-rest-spread', 52 | '@babel/plugin-transform-react-constant-elements', 53 | '@babel/plugin-transform-regenerator', 54 | '@babel/plugin-transform-destructuring', 55 | '@babel/plugin-transform-runtime', 56 | '@babel/plugin-transform-spread', 57 | 'babel-plugin-dynamic-import-node', 58 | 'babel-plugin-macros', 59 | 'babel-plugin-transform-react-remove-prop-types', 60 | 61 | // NOTE: In order to allow passing e.g. `import * as reducers from './reducers'` as an object, 62 | // loose option must be false. 63 | test && ['@babel/plugin-transform-modules-commonjs', { loose: false }], 64 | // NOTE: This plugin is also used when building the server in `react-union-scripts`. 65 | universal && 'babel-plugin-universal-import', 66 | ] 67 | .filter(Boolean) 68 | .filter(filterPlugins), 69 | }); 70 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | coverage 3 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | The package provides React Unions's `.eslintrc.*` as an extensible shared config. 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | # eslint-config-react-union 55 | 56 | The package provides React Unions's `.eslintrc.*` as an extensible shared config. 57 | 58 | ## Usage 59 | 60 | There are two ESLint configurations for you to use. 61 | 62 | ### eslint-config-react-union 63 | 64 | The default export contains all of our ESLint rules, including ECMAScript 6+ and React. It requires `eslint`, `babel-eslint`, `eslint-plugin-babel`, `eslint-plugin-import` and `eslint-plugin-react`. 65 | 66 | ### Installation 67 | 68 | ```sh 69 | npm install eslint-config-react-union eslint@5.5.0 babel-eslint@10.0.0 eslint-plugin-babel@5.2.1 eslint-plugin-react@7.11.1 eslint-plugin-import@2.14.0 --save-dev 70 | ``` 71 | or 72 | 73 | ```sh 74 | yarn add eslint-config-react-union eslint@5.5.0 babel-eslint@10.0.0 eslint-plugin-babel@5.2.1 eslint-plugin-react@7.11.1 eslint-plugin-import@2.14.0 -D 75 | ``` 76 | 77 | And add `"extends": "react-union"` to your .eslintrc. 78 | 79 | ### eslint-config-react-union/base 80 | 81 | The eslint-config-react-union without excluding rules for React. 82 | 83 | Add `"extends": "eslint-config-react-union/base"` to your `.eslintrc.*`. 84 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./rules/base', './rules/imports'].map(require.resolve), 3 | rules: {}, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['./rules/base', './rules/imports', './rules/react'].map(require.resolve), 3 | rules: {}, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-react-union", 3 | "version": "0.20.0", 4 | "description": "ESLint for React Union", 5 | "main": "index.js", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "contributors": [ 8 | "Tomas Konrady " 9 | ], 10 | "license": "MIT", 11 | "devDependencies": { 12 | "babel-eslint": "^10.1.0", 13 | "eslint": "^7.4.0", 14 | "eslint-plugin-babel": "^5.3.1", 15 | "eslint-plugin-import": "^2.22.0", 16 | "eslint-plugin-react": "^7.20.3" 17 | }, 18 | "peerDependencies": { 19 | "babel-eslint": "^10.0.0", 20 | "eslint": "^5.12.1", 21 | "eslint-plugin-babel": "^5.3.0", 22 | "eslint-plugin-import": "^2.14.0", 23 | "eslint-plugin-react": "^7.12.3" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/rules/base.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | parserOptions: { 4 | ecmaVersion: 7, 5 | sourceType: 'module', 6 | }, 7 | env: { 8 | browser: true, 9 | commonjs: true, 10 | es6: true, 11 | jest: true, 12 | node: true, 13 | }, 14 | globals: { 15 | __DEV__: true, 16 | }, 17 | plugins: ['babel', 'import'], 18 | rules: { 19 | 'array-bracket-spacing': ['error', 'never'], 20 | 'array-callback-return': 'error', 21 | 'arrow-body-style': ['error', 'as-needed'], 22 | 'arrow-parens': 'off', 23 | 'arrow-spacing': 'error', 24 | 'babel/new-cap': 'error', 25 | 'block-scoped-var': 'error', 26 | 'block-spacing': ['error', 'always'], 27 | 'brace-style': 'error', 28 | 'comma-dangle': ['error', 'always-multiline'], 29 | 'comma-spacing': [ 30 | 'error', 31 | { 32 | before: false, 33 | after: true, 34 | }, 35 | ], 36 | 'comma-style': ['error', 'last'], 37 | 'computed-property-spacing': ['error', 'never'], 38 | 'consistent-return': 'off', 39 | 'consistent-this': ['error', 'self'], 40 | curly: 'error', 41 | 'dot-location': ['error', 'property'], 42 | 'dot-notation': 'error', 43 | 'eol-last': 'error', 44 | eqeqeq: ['error', 'smart'], 45 | 'generator-star-spacing': 'error', 46 | 'id-blacklist': ['error'], 47 | 'key-spacing': 'error', 48 | 'keyword-spacing': 'error', 49 | 'max-len': [ 50 | 'error', 51 | { 52 | code: 120, 53 | ignoreComments: true, 54 | }, 55 | ], 56 | 'new-cap': [ 57 | 'off', 58 | { 59 | capIsNew: true, 60 | newIsCap: true, 61 | }, 62 | ], 63 | 'no-array-constructor': 'error', 64 | 'no-await-in-loop': 'error', 65 | 'no-case-declarations': 'error', 66 | 'no-cond-assign': 'error', 67 | 'no-const-assign': 'error', 68 | 'no-dupe-args': 'error', 69 | 'no-dupe-keys': 'error', 70 | 'no-duplicate-case': 'error', 71 | 'no-empty-pattern': 'error', 72 | 'no-extra-boolean-cast': 'error', 73 | 'no-extra-semi': 'error', 74 | 'no-loop-func': 'error', 75 | 'no-multi-spaces': 'error', 76 | 'no-multiple-empty-lines': 'error', 77 | 'no-shadow': 'off', 78 | 'no-spaced-func': 'error', 79 | 'no-trailing-spaces': 'error', 80 | 'no-undef': 'error', 81 | 'no-unneeded-ternary': 'error', 82 | 'no-unreachable': 'error', 83 | 'no-unused-expressions': 'error', 84 | 'no-unused-vars': 'error', 85 | 'no-var': 'error', 86 | 'object-shorthand': 'error', 87 | 'one-var': ['error', 'never'], 88 | 'operator-linebreak': 'off', 89 | 'padded-blocks': ['error', 'never'], 90 | 'prefer-arrow-callback': 'off', 91 | 'prefer-const': 'error', 92 | 'prefer-spread': 'error', 93 | 'prefer-template': 'error', 94 | quotes: ['error', 'single', 'avoid-escape'], 95 | 'space-before-blocks': ['error', 'always'], 96 | semi: ['error', 'always'], 97 | 'space-before-function-paren': [ 98 | 'error', 99 | { anonymous: 'never', named: 'never', asyncArrow: 'always' }, 100 | ], 101 | 'space-infix-ops': 'error', 102 | 'space-unary-ops': [ 103 | 'error', 104 | { 105 | words: true, 106 | nonwords: false, 107 | }, 108 | ], 109 | 'spaced-comment': 'error', 110 | strict: 'off', 111 | yoda: 'error', 112 | }, 113 | }; 114 | -------------------------------------------------------------------------------- /packages/eslint-config-react-union/rules/react.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserOptions: { 3 | ecmaFeatures: { 4 | jsx: true, 5 | }, 6 | }, 7 | plugins: ['react'], 8 | settings: { 9 | react: { 10 | version: '16.4', 11 | }, 12 | }, 13 | rules: { 14 | 'jsx-quotes': ['error', 'prefer-double'], 15 | 'react/destructuring-assignment': ['error', 'always', { ignoreClassFields: true }], 16 | 'react/display-name': 'error', 17 | 'react/forbid-prop-types': 'off', 18 | 'react/jsx-boolean-value': ['error', 'never'], 19 | 'react/jsx-closing-bracket-location': 'error', 20 | 'react/jsx-curly-spacing': 'error', 21 | 'react/jsx-equals-spacing': 'error', 22 | 'react/jsx-filename-extension': [ 23 | 'error', 24 | { 25 | extensions: ['.js'], 26 | }, 27 | ], 28 | 'react/jsx-first-prop-new-line': ['error', 'multiline'], 29 | 'react/jsx-handler-names': 'error', 30 | 'react/jsx-indent-props': ['error', 'tab'], 31 | 'react/jsx-key': 'off', 32 | 'react/jsx-max-props-per-line': [ 33 | 'error', 34 | { 35 | maximum: 3, 36 | }, 37 | ], 38 | 'react/jsx-no-bind': 'off', 39 | 'react/jsx-no-comment-textnodes': 'error', 40 | 'react/jsx-no-duplicate-props': 'error', 41 | 'react/jsx-no-literals': 'off', 42 | 'react/jsx-no-target-blank': 'off', 43 | 'react/jsx-no-undef': 'error', 44 | 'react/jsx-pascal-case': 'error', 45 | 'react/jsx-sort-props': 'off', 46 | 'react/jsx-tag-spacing': [ 47 | 'error', 48 | { 49 | beforeSelfClosing: 'always', 50 | }, 51 | ], 52 | 'react/jsx-uses-react': 'error', 53 | 'react/jsx-uses-vars': 'error', 54 | 'react/jsx-wrap-multilines': 'error', 55 | 'react/no-danger': 'error', 56 | 'react/no-deprecated': 'error', 57 | 'react/no-did-mount-set-state': 'error', 58 | 'react/no-did-update-set-state': 'error', 59 | 'react/no-direct-mutation-state': 'error', 60 | 'react/no-is-mounted': 'error', 61 | 'react/no-multi-comp': 'off', 62 | 'react/no-render-return-value': 'error', 63 | 'react/no-set-state': 'off', 64 | 'react/no-string-refs': 'error', 65 | 'react/no-typos': 'warn', 66 | 'react/no-unknown-property': 'error', 67 | 'react/no-unescaped-entities': 'error', 68 | 'react/prefer-arrow-callback': 'off', 69 | 'react/prefer-es6-class': 'error', 70 | 'react/prefer-stateless-function': [ 71 | 'error', 72 | { 73 | ignorePureComponents: true, 74 | }, 75 | ], 76 | 'react/prop-types': 'error', 77 | 'react/react-in-jsx-scope': 'error', 78 | 'react/require-optimization': 'off', 79 | 'react/require-render-return': 'error', 80 | 'react/self-closing-comp': 'error', 81 | 'react/sort-comp': 'error', 82 | 'react/sort-prop-types': 'error', 83 | 'react/style-prop-object': 'error', 84 | 'react/void-dom-elements-no-children': 'error', 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | coverage 3 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | Runs the [liferay-npm-bundler](https://www.npmjs.com/package/liferay-npm-bundler) in such way, that transforming into Liferay AMD is not done over your codebase. 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | # Liferay Build Tools 55 | 56 | 57 | Scripts are used in our Liferay boilerplate. 58 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/bin/liferay-scripts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs'); 3 | 4 | if (process.argv[2] === '-v' || process.argv[2] === '--version') { 5 | const pkg = JSON.parse(fs.readFileSync(require.resolve('../package.json'), 'utf8')); 6 | console.log(`v${pkg.version}`); 7 | process.exit(0); 8 | } 9 | 10 | if (process.versions.node.split('.')[0] < 10) { 11 | console.error('ERROR: This tool requires Node.js v10 or higher.'); 12 | process.exit(1); 13 | } else { 14 | require('../cli'); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/cli.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console, global-require, import/no-dynamic-require */ 2 | const run = require('./scripts/run'); 3 | 4 | function runCommand(command) { 5 | return run(require(`./scripts/${command}`)); 6 | } 7 | 8 | const command = process.argv[2]; 9 | 10 | if (['bundle'].includes(command)) { 11 | runCommand(command).catch(err => { 12 | console.error(process.argv.includes('--verbose') ? err.stack : `ERROR: ${err.message}`); 13 | process.exit(1); 14 | }); 15 | } else { 16 | console.error('Unknown command.'); 17 | process.exit(1); 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.20.0", 3 | "name": "react-union-liferay-build-tools", 4 | "description": "Tools used in Liferay's build pipeline.", 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "files": [ 8 | "scripts", 9 | "bin", 10 | "cli.js", 11 | "template-npmbundlerrc.json" 12 | ], 13 | "bin": { 14 | "liferay-scripts": "./bin/liferay-scripts.js" 15 | }, 16 | "engines": { 17 | "node": ">=8" 18 | }, 19 | "dependencies": { 20 | "execa": "^4.0.0", 21 | "fs-extra": "^9.0.1", 22 | "glob": "^7.1.6", 23 | "liferay-npm-bundler": "^2.13.2", 24 | "ramda": "^0.27.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "global-require": 0, 4 | "no-console": 0, 5 | "import/no-extraneous-dependencies": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/scripts/bundle.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const R = require('ramda'); 4 | const execa = require('execa'); 5 | 6 | const isDirectory = source => fs.lstatSync(source).isDirectory(); 7 | const getDirectories = source => 8 | fs.readdirSync(source).filter(name => isDirectory(path.join(source, name))); 9 | 10 | const appDirectory = fs.realpathSync(process.cwd()); 11 | 12 | const vendorFileName = 'vendor.js'; 13 | const runtimeFileName = 'runtime.js'; 14 | const mainFileName = 'main.js'; 15 | const manifestFileName = 'assetManifest.json'; 16 | const packagesFodler = 'packages'; 17 | 18 | const buildDirectory = path.join(appDirectory, 'build'); 19 | 20 | const outputJson = R.curry((path, content) => 21 | fs.outputJSON(path, content, { 22 | spaces: '\t', 23 | encoding: 'utf8', 24 | }) 25 | ); 26 | 27 | const getEntryBundlesFromManifest = manifest => 28 | R.filter(Boolean, { 29 | runtime: manifest[runtimeFileName], 30 | vendor: manifest[vendorFileName], 31 | app: manifest[mainFileName], 32 | }); 33 | 34 | const getLoadScriptsSrc = R.o( 35 | R.values, 36 | R.map( 37 | x => `Liferay.Loader._scriptLoader._loadScript({ url: '${x}', modules: [] }); 38 | ` 39 | ) 40 | ); 41 | 42 | const joinByNewline = R.join('\n'); 43 | 44 | const getLoaderSource = (appName, manifest) => { 45 | const paths = getEntryBundlesFromManifest(manifest); 46 | const loadScripts = getLoadScriptsSrc(paths); 47 | 48 | return `${joinByNewline(loadScripts)}\n`; 49 | }; 50 | 51 | const lfrBundler = async (cwd = __dirname) => 52 | execa('liferay-npm-bundler', [], { 53 | cwd, 54 | stdio: 'inherit', 55 | preferLocal: true, 56 | }); 57 | 58 | const getAppPackageJson = async appName => 59 | await fs.readJson(path.join(appDirectory, packagesFodler, appName, 'package.json')); 60 | 61 | const createLiferayConfig = async () => { 62 | const appsAvailable = getDirectories(buildDirectory); 63 | const rootPackageJson = await fs.readJson(path.join(appDirectory, 'package.json')); 64 | 65 | const dist = path.join(appDirectory, 'dist'); 66 | 67 | fs.ensureDirSync(dist); 68 | 69 | R.forEach(async appName => { 70 | const distApp = path.join(dist, appName); 71 | const distAppBuild = path.join(distApp, 'build'); 72 | const distAppSrc = path.join(distAppBuild, 'build'); 73 | const appPackageJson = await getAppPackageJson(appName); 74 | 75 | const packageJson = appPackageJson ? appPackageJson : rootPackageJson; 76 | 77 | const isManifestProvided = fs.pathExists(path.join(distApp, manifestFileName)); 78 | 79 | if (!isManifestProvided) { 80 | console.warn(`Skipping bundling of ${appName} due to lack of \`assetManifest.json\` file.`); 81 | return; 82 | } 83 | 84 | await fs.copy(path.join(buildDirectory, appName), distAppSrc); 85 | 86 | const manifest = await fs.readJson(path.join(distAppSrc, manifestFileName), 'utf8'); 87 | const templateNpmbundlerrc = await fs.readJson( 88 | path.join(__dirname, '..', 'template-npmbundlerrc.json'), 89 | 'utf8' 90 | ); 91 | await outputJson(path.join(distApp, 'package.json'), { 92 | name: appName, 93 | version: packageJson.version, 94 | description: packageJson.description, 95 | }); 96 | await outputJson( 97 | path.join(distApp, '.npmbundlerrc'), 98 | R.mergeDeepRight(templateNpmbundlerrc, { 99 | ignore: ['build/**/*.js'], 100 | 'create-jar': { 101 | 'output-filename': `${appName}.jar`, 102 | features: { 103 | 'web-context': `/${appName}`, 104 | }, 105 | }, 106 | }) 107 | ); 108 | 109 | fs.writeFileSync( 110 | path.join(distAppBuild, `${appName}.js`), 111 | getLoaderSource(appName, manifest), 112 | 'utf8' 113 | ); 114 | 115 | await lfrBundler(distApp); 116 | })(appsAvailable); 117 | }; 118 | 119 | async function bundle() { 120 | console.log('Creating Liferay AMD configuration...'); 121 | await createLiferayConfig(); 122 | console.log('✨ Successfully finished ✨'); 123 | } 124 | 125 | module.exports = bundle; 126 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/scripts/run.js: -------------------------------------------------------------------------------- 1 | function format(time) { 2 | return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); 3 | } 4 | 5 | function run(fn, options) { 6 | const task = typeof fn.default === 'undefined' ? fn : fn.default; 7 | const start = new Date(); 8 | console.log(`[${format(start)}] Starting '${task.name}${options ? `(${options})` : ''}'...`); 9 | return task(options).then(resolution => { 10 | const end = new Date(); 11 | const time = end.getTime() - start.getTime(); 12 | console.log( 13 | `[${format(end)}] Finished '${task.name}${options ? `(${options})` : ''}' after ${time} ms` 14 | ); 15 | return resolution; 16 | }); 17 | } 18 | 19 | /** Running from node-cli - babel-node scripts/run someModule */ 20 | 21 | // When a file is run directly from Node.js, require.main is set to its module 22 | if (require.main === module && process.argv.length > 2) { 23 | delete require.cache[__filename]; // eslint-disable-line no-underscore-dangle 24 | 25 | // eslint-disable-next-line import/no-dynamic-require 26 | const module = require(`./${process.argv[2]}.js`); 27 | 28 | run(module).catch(err => { 29 | console.error(err.stack); 30 | process.exit(1); 31 | }); 32 | } 33 | 34 | module.exports = run; 35 | -------------------------------------------------------------------------------- /packages/react-union-liferay-build-tools/template-npmbundlerrc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "ignore": ["app-*/**/*.js"], 4 | "create-jar": { 5 | "output-dir": "../", 6 | "features": { 7 | "js-extender": false, 8 | "web-context": "/liferay-amd-loader" 9 | } 10 | }, 11 | "verbose": true, 12 | "dump-report": false 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-union-polyfills/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | Library, that imports polyfills instead of deprecated @babel/polyfills 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | Fork of [react-app-polyfill](https://github.com/facebook/create-react-app/). 55 | 56 | 57 | ## Usage 58 | 59 | Import it as the first line in your application code. 60 | 61 | ```js 62 | import 'react-union-polyfills'; 63 | ``` 64 | 65 | ### Internet Explorer 66 | 67 | If you need to support older versions of Internet Explorer, call: 68 | 69 | ```js 70 | // for IE 11: 71 | import 'react-union-polyfills/ie11'; 72 | import 'react-union-polyfills'; 73 | 74 | 75 | // for IE 9: 76 | import 'react-union-polyfills/ie9'; 77 | import 'react-union-polyfills'; 78 | ``` 79 | -------------------------------------------------------------------------------- /packages/react-union-polyfills/ie11.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | require('promise/lib/rejection-tracking').enable(); 3 | self.Promise = require('promise/lib/es6-extensions.js'); 4 | } 5 | 6 | if (typeof window !== 'undefined') { 7 | // fetch() polyfill for making API calls. 8 | require('whatwg-fetch'); 9 | } 10 | 11 | Object.assign = require('object-assign'); 12 | 13 | require('core-js/features/symbol'); 14 | require('core-js/features/array/from'); 15 | -------------------------------------------------------------------------------- /packages/react-union-polyfills/ie9.js: -------------------------------------------------------------------------------- 1 | require('./ie11'); 2 | 3 | require('core-js/features/map'); 4 | require('core-js/features/set'); 5 | 6 | require('raf').polyfill(); 7 | -------------------------------------------------------------------------------- /packages/react-union-polyfills/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-polyfills", 3 | "version": "0.20.0", 4 | "description": "Polyfills package for react-union.", 5 | "main": "stable.js", 6 | "files": [ 7 | "ie9.js", 8 | "ie11.js", 9 | "stable.js" 10 | ], 11 | "repository": "https://github.com/lundegaard/react-union", 12 | "contributors": [ 13 | "Tomas Konrady ", 14 | "Josef Kvapil " 15 | ], 16 | "license": "MIT", 17 | "dependencies": { 18 | "core-js": "3.6.5", 19 | "object-assign": "4.1.1", 20 | "promise": "8.0.3", 21 | "raf": "3.4.1", 22 | "regenerator-runtime": "0.13.5", 23 | "whatwg-fetch": "3.0.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-union-polyfills/stable.js: -------------------------------------------------------------------------------- 1 | require('core-js/stable'); 2 | require('regenerator-runtime/runtime'); 3 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/README.md: -------------------------------------------------------------------------------- 1 | # React Union Rendering Service 2 | 3 | WIP 4 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union-rendering-service", 3 | "version": "0.20.0", 4 | "description": "HTTP service for server-side rendering.", 5 | "main": "src/index.js", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "contributors": [ 8 | "Vaclav Jancarik " 9 | ], 10 | "license": "MIT", 11 | "dependencies": { 12 | "body-parser": "^1.18.3", 13 | "cheerio": "^1.0.0-rc.2", 14 | "connect": "^3.6.6", 15 | "invariant": "^2.2.4", 16 | "morgan": "^1.9.1", 17 | "ramda": "^0.27.0", 18 | "ramda-extension": "^0.10.3", 19 | "webpack-flush-chunks": "^2.0.3" 20 | }, 21 | "peerDependencies": { 22 | "react": "^16.8.3", 23 | "react-dom": "^16.8.3", 24 | "react-union": "^0.17.0", 25 | "react-universal-component": "^4.0.0" 26 | }, 27 | "devDependencies": { 28 | "react": "^16.13.1", 29 | "react-dom": "^16.13.1", 30 | "react-union": "^0.20.0", 31 | "react-universal-component": "^4.5.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/healthMiddleware.js: -------------------------------------------------------------------------------- 1 | const healthMiddleware = () => (req, res) => { 2 | res.writeHead(200); 3 | res.end(); 4 | }; 5 | 6 | module.exports = healthMiddleware; 7 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | outputBufferingMiddleware: require('./outputBufferingMiddleware'), 3 | startRenderingService: require('./startRenderingService'), 4 | }; 5 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/isRequestForHTML.js: -------------------------------------------------------------------------------- 1 | const isRequestForHTML = req => req.headers.accept && req.headers.accept.includes('text/html'); 2 | 3 | module.exports = isRequestForHTML; 4 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/outputBufferingMiddleware.js: -------------------------------------------------------------------------------- 1 | const { noop } = require('ramda-extension'); 2 | 3 | const isRequestForHTML = require('./isRequestForHTML'); 4 | 5 | /** 6 | * Because BrowserSync only uses Connect and not Express or any other framework, 7 | * we need to gather the response body ourselves. Because `res.end` is called under the hood 8 | * to send the response to the client, we defer the call to ensure the application is rendered. 9 | */ 10 | const outputBufferingMiddleware = () => (req, res, next) => { 11 | if (isRequestForHTML(req)) { 12 | res.body = null; 13 | // TODO: This can probably be improved. 14 | res.originals = { ...res }; 15 | 16 | res.end = body => { 17 | res.body = res.body || body; 18 | next(); 19 | }; 20 | 21 | res.setHeader = noop; 22 | res.write = noop; 23 | res.writeHead = noop; 24 | } 25 | 26 | next(); 27 | }; 28 | 29 | module.exports = outputBufferingMiddleware; 30 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/renderApplication.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const ReactDOMServer = require('react-dom/server'); 3 | const cheerio = require('cheerio'); 4 | const { PrescanContext, scan, route } = require('react-union'); 5 | const { flushChunkNames } = require('react-universal-component/server'); 6 | const { default: flushChunks } = require('webpack-flush-chunks'); 7 | const invariant = require('invariant'); 8 | const { forEach, call, __, compose, map, path, isEmpty } = require('ramda'); 9 | const { rejectNil } = require('ramda-extension'); 10 | 11 | const resolveInitialProps = require('./resolveInitialProps'); 12 | 13 | const hoistComponentStatics = compose( 14 | forEach(call(__)), 15 | rejectNil, 16 | map(path(['component', 'preloadWeak'])) 17 | ); 18 | 19 | const renderApplication = async ({ handleRequest, options, originalHTML, req, res }) => { 20 | const { 21 | isMiddleware = false, 22 | clientStats = null, 23 | waveReduction = true, 24 | skipEmptyScans = false, 25 | beforeChunks = [], 26 | afterChunks = [], 27 | } = options; 28 | 29 | const original_$ = cheerio.load(originalHTML); 30 | const head = original_$('head'); 31 | const body = original_$('body'); 32 | const context = { head, body, req, res }; 33 | 34 | // NOTE: We need to pass routes here because of getInitialProps. 35 | // In order to get the initial props, we need to get the list of all rendered components. 36 | // To do that, we need to call `scan` ourselves here. 37 | const render = async (reactElement, routes, applicationContext) => { 38 | const scanResult = scan(original_$); 39 | 40 | if (skipEmptyScans && isEmpty(scanResult.widgetDescriptors)) { 41 | return { widgetConfigs: [], chunkNames: [], initialProps: {} }; 42 | } 43 | 44 | const { widgetConfigs } = route(routes, scanResult); 45 | 46 | // NOTE: https://github.com/faceyspacey/react-universal-component#static-hoisting 47 | // Without calling this function, `getInitialProps` statics will not be defined. 48 | hoistComponentStatics(widgetConfigs); 49 | 50 | const initialProps = await resolveInitialProps( 51 | { ...context, ...applicationContext }, 52 | widgetConfigs 53 | ); 54 | 55 | const wrappedElement = React.createElement( 56 | PrescanContext.Provider, 57 | { value: { initialProps, widgetConfigs } }, 58 | reactElement 59 | ); 60 | 61 | // NOTE: https://github.com/faceyspacey/react-universal-component/issues/74 62 | // We are doing this to make sure that the next `flushChunkNames()` call will only contain 63 | // the universal components from `renderToString`, not from other asynchronous requests. 64 | flushChunkNames(); 65 | const reactHTML = ReactDOMServer.renderToString(wrappedElement); 66 | const chunkNames = flushChunkNames(); 67 | 68 | const react_$ = cheerio.load(reactHTML); 69 | 70 | react_$('[data-union-portal]').each((_, widget) => { 71 | const $widget = react_$(widget); 72 | const id = $widget.data('union-portal'); 73 | const selector = `#${id}`; 74 | const $container = original_$(selector); 75 | 76 | invariant($container, `HTML element with ID "${id}" could not be found.`); 77 | 78 | const widgetHTML = $widget.html(); 79 | $container.html(widgetHTML); 80 | }); 81 | 82 | return { widgetConfigs, chunkNames, initialProps }; 83 | }; 84 | 85 | const renderResult = await handleRequest({ render, ...context }); 86 | invariant(renderResult, 'You did not call `render()` in your SSR request handler.'); 87 | const { widgetConfigs, chunkNames, initialProps } = renderResult; 88 | 89 | if (skipEmptyScans && isEmpty(widgetConfigs)) { 90 | return originalHTML; 91 | } 92 | 93 | if (clientStats && waveReduction) { 94 | const chunks = flushChunks(clientStats, { 95 | chunkNames, 96 | before: isMiddleware ? [] : beforeChunks, 97 | after: isMiddleware ? [] : afterChunks, 98 | }); 99 | 100 | const { styles, js } = chunks; 101 | 102 | head.append(styles.toString()); 103 | body.append(js.toString()); 104 | } 105 | 106 | body.prepend(``); 107 | body.prepend(''); 108 | return original_$.html(); 109 | }; 110 | 111 | module.exports = renderApplication; 112 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/renderingMiddleware.js: -------------------------------------------------------------------------------- 1 | const renderApplication = require('./renderApplication'); 2 | const isRequestForHTML = require('./isRequestForHTML'); 3 | 4 | const renderingMiddleware = (handleRequest, options) => async (req, res, next) => { 5 | const context = { handleRequest, options, req, res }; 6 | 7 | try { 8 | if (options.isMiddleware) { 9 | if (!isRequestForHTML(req)) { 10 | return next(); 11 | } 12 | 13 | const content = await renderApplication({ ...context, originalHTML: res.body }); 14 | res.statusMessage = 'OK'; 15 | res.originals.writeHead(200); 16 | res.originals.end(content); 17 | } else { 18 | const content = await renderApplication({ ...context, originalHTML: req.body }); 19 | res.writeHead(200, { 'Content-Type': 'application/html' }); 20 | res.end(content); 21 | } 22 | } catch (error) { 23 | next(error); 24 | } 25 | }; 26 | 27 | module.exports = renderingMiddleware; 28 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/resolveInitialProps.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda'); 2 | 3 | const makeGetInitialProps = context => ({ component, ...widgetConfig }) => 4 | component.getInitialProps 5 | ? component.getInitialProps({ ...context, ...widgetConfig }) 6 | : Promise.resolve(null); 7 | 8 | /** 9 | * Asynchronously returns an array of initial props with the indices corresponding 10 | * to the passed widget configs. 11 | * 12 | * @param {Array} widgetConfigs widget configs 13 | * @param {Object} context argument to call the initialProps methods with 14 | */ 15 | const resolveInitialProps = async (context, widgetConfigs) => { 16 | const getInitialProps = makeGetInitialProps(context); 17 | const promises = R.map(getInitialProps, widgetConfigs); 18 | const namespaces = R.map(R.prop('namespace'), widgetConfigs); 19 | 20 | return R.zipObj(namespaces, await Promise.all(promises)); 21 | }; 22 | 23 | module.exports = resolveInitialProps; 24 | -------------------------------------------------------------------------------- /packages/react-union-rendering-service/src/startRenderingService.js: -------------------------------------------------------------------------------- 1 | const connect = require('connect'); 2 | const http = require('http'); 3 | const bodyParser = require('body-parser'); 4 | const invariant = require('invariant'); 5 | const morgan = require('morgan'); 6 | const { mergeLeft } = require('ramda'); 7 | 8 | const healthMiddleware = require('./healthMiddleware'); 9 | const renderingMiddleware = require('./renderingMiddleware'); 10 | 11 | const startRenderingService = (handleRequest, options = {}) => { 12 | const resolvedOptions = mergeLeft(options, global.ReactUnionRenderingServiceOptions); 13 | 14 | invariant(handleRequest, 'SSR handler is undefined.'); 15 | invariant(resolvedOptions, 'SSR options are undefined.'); 16 | 17 | if (resolvedOptions.isMiddleware) { 18 | // NOTE: This is the Webpack Hot Server Middleware integration. 19 | return ({ clientStats }) => { 20 | invariant(clientStats, 'Client stats are undefined.'); 21 | 22 | return renderingMiddleware(handleRequest, { ...resolvedOptions, clientStats }); 23 | }; 24 | } 25 | 26 | const app = connect(); 27 | 28 | app.use(morgan('combined')); 29 | app.use(bodyParser.text({ type: 'text/*', limit: '10mb' })); 30 | 31 | app.use('/health', healthMiddleware()); 32 | app.use('/', renderingMiddleware(handleRequest, resolvedOptions)); 33 | 34 | const server = http.createServer(app); 35 | 36 | const port = process.env.PORT || resolvedOptions.port; 37 | server.listen(port); 38 | console.log(`🚀 React Union Rendering Service is listening on port ${port}.`); 39 | 40 | return server; 41 | }; 42 | 43 | module.exports = startRenderingService; 44 | -------------------------------------------------------------------------------- /packages/react-union-scripts/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | coverage 3 | -------------------------------------------------------------------------------- /packages/react-union-scripts/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | Extendable and configurable set of scripts for building and running your React applications that is designed for content management systems (CMS) or enterprise portals. 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | 55 | ## Main features 56 | 57 | * **simple to use** - just add the dependency to your `package.json` and roll 58 | * **use multiple entry points in one project** - useful for theming or for splitting your code to optimize your bundle size 59 | * **designed for a large codebase** - multiple entry points, code-splitting, async loading of JavaScript modules 60 | 61 | See https://react-union.org/scripts-introduction for more information. 62 | -------------------------------------------------------------------------------- /packages/react-union-scripts/bin/react-union-scripts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const fs = require('fs'); 3 | 4 | if (process.argv[2] === '-v' || process.argv[2] === '--version') { 5 | const pkg = JSON.parse(fs.readFileSync(require.resolve('../package.json'), 'utf8')); 6 | console.log(`v${pkg.version}`); 7 | process.exit(0); 8 | } 9 | 10 | if (process.versions.node.split('.')[0] < 10) { 11 | console.error('ERROR: This tool requires Node.js v10 or higher.'); 12 | process.exit(1); 13 | } else { 14 | require('../cli'); 15 | } 16 | -------------------------------------------------------------------------------- /packages/react-union-scripts/cli.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console, global-require, import/no-dynamic-require */ 2 | const run = require('./scripts/run'); 3 | 4 | function runCommand(command) { 5 | return run(require(`./scripts/${command}`)); 6 | } 7 | 8 | const command = process.argv[2]; 9 | 10 | if (['start', 'build', 'test'].includes(command)) { 11 | runCommand(command).catch(err => { 12 | console.error(process.argv.includes('--verbose') ? err.stack : `ERROR: ${err.message}`); 13 | process.exit(1); 14 | }); 15 | } else { 16 | console.error('Unknown command.'); 17 | process.exit(1); 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-union-scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.20.0", 3 | "name": "react-union-scripts", 4 | "description": "Provides scripts for building JS web apps targeted on either CMS or Portal solutions.", 5 | "license": "MIT", 6 | "repository": "https://github.com/lundegaard/react-union", 7 | "files": [ 8 | "scripts", 9 | "bin", 10 | "cli.js" 11 | ], 12 | "bin": { 13 | "react-union-scripts": "./bin/react-union-scripts.js" 14 | }, 15 | "engines": { 16 | "node": ">=8" 17 | }, 18 | "dependencies": { 19 | "@babel/core": "^7.11.6", 20 | "babel-core": "^7.0.0-bridge", 21 | "babel-jest": "^26.1.0", 22 | "babel-loader": "^8.1.0", 23 | "browser-sync": "^2.26.3", 24 | "clean-webpack-plugin": "^3.0.0", 25 | "connect-history-api-fallback": "^1.6.0", 26 | "cross-env": "^7.0.2", 27 | "css-loader": "^3.4.2", 28 | "del": "^5.1.0", 29 | "extract-css-chunks-webpack-plugin": "^4.7.5", 30 | "file-loader": "^6.0.0", 31 | "fs-extra": "^9.0.1", 32 | "glob": "^7.1.6", 33 | "html-webpack-plugin": "^4.3.0", 34 | "http-proxy-middleware": "^1.0.4", 35 | "invariant": "^2.2.4", 36 | "jest": "^26.0.1", 37 | "mkdirp": "^1.0.4", 38 | "ncp": "^2.0.0", 39 | "ramda": "^0.27.0", 40 | "ramda-extension": "^0.10.3", 41 | "raw-loader": "^3.1.0", 42 | "react-hot-loader": "^4.12.21", 43 | "react-union-rendering-service": "^0.20.0", 44 | "react-universal-component": "^4.5.0", 45 | "resolve-url-loader": "^3.1.1", 46 | "terser-webpack-plugin": "^2.1.0", 47 | "url-loader": "^2.1.0", 48 | "webpack": "^4.43.0", 49 | "webpack-bundle-analyzer": "^3.8.0", 50 | "webpack-dev-middleware": "^3.7.2", 51 | "webpack-hot-middleware": "^2.24.3", 52 | "webpack-hot-server-middleware": "^0.6.0", 53 | "webpack-manifest-plugin": "^2.2.0", 54 | "webpack-merge": "^4.2.1" 55 | }, 56 | "devDependencies": { 57 | "react": "^16.13.1", 58 | "react-dom": "^16.13.1" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "global-require": 0, 4 | "no-console": 0, 5 | "import/no-extraneous-dependencies": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/build.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const { promisify } = require('util'); 3 | const fse = require('fs-extra'); 4 | const fs = require('fs'); 5 | const { test, complement, flatten, propEq, filter, prop, forEach, o, map, head } = require('ramda'); 6 | const { rejectNil } = require('ramda-extension'); 7 | const path = require('path'); 8 | 9 | const { getUnionConfig, stats } = require('./lib/utils'); 10 | const cli = require('./lib/cli'); 11 | const webpackConfigs = require('./webpack.config'); 12 | 13 | const getServerWebpackConfigs = filter(propEq('name', 'server')); 14 | const getClientStatsList = o(filter(propEq('name', 'client')), prop('children')); 15 | 16 | const prependUnionOptions = webpackConfig => { 17 | const bundlePath = path.join(webpackConfig.output.path, webpackConfig.output.filename); 18 | 19 | if (fs.existsSync(bundlePath)) { 20 | const optionsIdentifier = 'global.ReactUnionRenderingServiceOptions'; 21 | const options = { 22 | ...webpackConfig.unionConfig.renderingService, 23 | isMiddleware: false, 24 | }; 25 | 26 | fs.writeFileSync( 27 | bundlePath, 28 | `${optionsIdentifier}=${JSON.stringify(options)};${fs.readFileSync(bundlePath)}` 29 | ); 30 | } 31 | }; 32 | 33 | const prependClientStats = clientStats => { 34 | const bundlePath = path.join(clientStats.outputPath, 'server.js'); 35 | 36 | if (fs.existsSync(bundlePath)) { 37 | const clientStatsIdentifier = 'global.ReactUnionRenderingServiceOptions.clientStats'; 38 | 39 | fs.writeFileSync( 40 | bundlePath, 41 | `${clientStatsIdentifier}=${JSON.stringify(clientStats)};${fs.readFileSync(bundlePath)}` 42 | ); 43 | } 44 | }; 45 | 46 | const getWebpackConfigsToBuild = cli.noSSR ? map(head) : o(rejectNil, flatten); 47 | 48 | async function build() { 49 | const webpackConfigsToBuild = getWebpackConfigsToBuild(webpackConfigs); 50 | const compiler = webpack(webpackConfigsToBuild); 51 | const run = promisify(compiler.run.bind(compiler)); 52 | 53 | const buildStats = await run(); 54 | console.log(buildStats.toString(stats)); 55 | 56 | if (!cli.noSSR) { 57 | forEach(prependClientStats, getClientStatsList(buildStats.toJson())); 58 | forEach(prependUnionOptions, getServerWebpackConfigs(webpackConfigsToBuild)); 59 | } 60 | 61 | copyPublicFolder(getUnionConfig()); 62 | } 63 | 64 | const copyPublicFolder = forEach(config => { 65 | fse.copySync(config.paths.public, config.paths.build, { 66 | dereference: true, 67 | filter: complement(test(config.copyToPublicIgnore)), 68 | }); 69 | }); 70 | 71 | module.exports = build; 72 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/jest/fileTransformer.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | process(src, filename) { 5 | const assetFilename = JSON.stringify(path.basename(filename)); 6 | return `module.exports = ${assetFilename};`; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/jest/scssTransformer.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | process() { 3 | return 'module.exports = {};\n'; 4 | }, 5 | getCacheKey() { 6 | return 'scssTransform'; 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/lib/__mocks__/fs.js: -------------------------------------------------------------------------------- 1 | const fs = jest.genMockFromModule('../fs'); 2 | 3 | let mockDirs = null; 4 | let mockWsPattern = null; 5 | let mockWsApps = null; 6 | let mockAppPath = null; 7 | 8 | fs.readDirs = () => mockDirs; 9 | 10 | fs.getWorkspacesPatterns = () => mockWsPattern; 11 | 12 | fs.readAllAppsFromWorkspaces = () => mockWsApps; 13 | 14 | fs.getAppPath = () => mockAppPath; 15 | 16 | fs.__setMockDirs = newMockdirs => { 17 | mockDirs = newMockdirs; 18 | }; 19 | 20 | fs.__setWsPattern = newWsPattern => { 21 | mockWsPattern = newWsPattern; 22 | }; 23 | 24 | fs.__setWsApps = newWsApps => { 25 | mockWsApps = newWsApps; 26 | }; 27 | 28 | fs.__setAppPath = newAppPath => { 29 | mockAppPath = newAppPath; 30 | }; 31 | 32 | module.exports = fs; 33 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/lib/cli.js: -------------------------------------------------------------------------------- 1 | const { always, compose, equals, findIndex, ifElse, nth } = require('ramda'); 2 | const { includes, notInclude } = require('ramda-extension'); 3 | 4 | /** 5 | * 6 | * @sig String -> [String] -> String 7 | * 8 | * @example 9 | * getArgValue( 10 | * '--app', 11 | * [ 12 | * "node", 13 | * "node/process-2.js", 14 | * "--app", 15 | * "AppName" 16 | * ] 17 | * ) // "AppName" 18 | */ 19 | const getArgValue = (arg, program) => 20 | compose( 21 | ifElse(equals(-1), always(null), x => program[x + 1]), 22 | findIndex(equals(arg)) 23 | )(program); 24 | 25 | const program = process.argv; 26 | const programIncludes = includes(program); 27 | const programNotInclude = notInclude(program); 28 | 29 | /** optimize for development */ 30 | const debug = programNotInclude('--release'); 31 | 32 | /** level of debugging messages */ 33 | const verbose = programIncludes('--verbose'); 34 | 35 | /** if true, create proxy */ 36 | const proxy = programIncludes('--proxy'); 37 | 38 | /** if true, do not suport HMR */ 39 | const noHMR = programIncludes('--no-hmr'); 40 | 41 | /** if true, do not do anything SSR related */ 42 | const noSSR = programIncludes('--no-ssr'); 43 | 44 | /** if true, runs analyze tool */ 45 | const analyze = programIncludes('--analyze'); 46 | 47 | /** if exist, runs single app. Value is provided as is in original form */ 48 | const app = getArgValue('--app', program); 49 | 50 | const target = getArgValue('--target', program); 51 | 52 | const script = nth(2)(program); 53 | 54 | module.exports = { script, target, debug, verbose, proxy, noHMR, noSSR, analyze, app }; 55 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/lib/fs.js: -------------------------------------------------------------------------------- 1 | const R = require('ramda'); 2 | const R_ = require('ramda-extension'); 3 | const fs = require('fs-extra'); 4 | const invariant = require('invariant'); 5 | const path = require('path'); 6 | const mkdirp = require('mkdirp'); 7 | const glob = require('glob'); 8 | 9 | const resolveSymlink = (...args) => fs.realpathSync(path.resolve(...args)); 10 | 11 | const intoArray = R.into([]); 12 | 13 | const writeFile = (file, contents) => 14 | new Promise((resolve, reject) => { 15 | fs.writeFile(file, contents, 'utf8', err => (err ? reject(err) : resolve())); 16 | }); 17 | 18 | const makeDir = name => 19 | new Promise((resolve, reject) => { 20 | mkdirp(name, err => (err ? reject(err) : resolve())); 21 | }); 22 | 23 | const isDirectory = source => fs.lstatSync(source).isDirectory(); 24 | 25 | const readDirs = dir => 26 | fs.existsSync(dir) 27 | ? R.compose( 28 | R.filter(name => isDirectory(path.join(dir, name))), 29 | fs.readdirSync 30 | )(dir) 31 | : []; 32 | 33 | const getRootPackageJSON = R_.memoizeWithIdentity(() => 34 | fs.readJsonSync(path.join(process.cwd(), 'package.json')) 35 | ); 36 | 37 | const readAllWorkspacesFlatten = R_.memoizeWithIdentity(() => 38 | R.o(R.flatten, R.map(glob.sync))(getWorkspacesPatterns()) 39 | ); 40 | 41 | const getWorkspacesPatterns = () => { 42 | const rootPackage = getRootPackageJSON(); 43 | return rootPackage.private && rootPackage.workspaces; 44 | }; 45 | 46 | const resolveWorkspacesPackagePattern = R.cond([ 47 | [R.is(RegExp), R.identity], 48 | [R_.isString, pattern => R_.constructRegExp(pattern, 'i')], 49 | [R_.isArray, pattern => R_.constructRegExp(`(${R.join('|', pattern)})`, 'i')], 50 | [ 51 | R.T, 52 | () => 53 | invariant( 54 | false, 55 | "Invalid property 'appPattern' or 'widgetPattern'. It should be string or list of strings." 56 | ), 57 | ], 58 | ]); 59 | 60 | const takePackageName = R.map(R.o(R.last, R.split('/'))); 61 | 62 | const readAllAppsFromWorkspaces = appPattern => 63 | intoArray(R.o(takePackageName, R.filter(R.test(resolveWorkspacesPackagePattern(appPattern)))))( 64 | readAllWorkspacesFlatten() 65 | ); 66 | 67 | const getAppPath = name => R.find(R.contains(name), readAllWorkspacesFlatten()); 68 | 69 | module.exports = { 70 | isDirectory, 71 | readDirs, 72 | writeFile, 73 | makeDir, 74 | getRootPackageJSON, 75 | getWorkspacesPatterns, 76 | readAllAppsFromWorkspaces, 77 | getAppPath, 78 | readAllWorkspacesFlatten, 79 | resolveWorkspacesPackagePattern, 80 | resolveSymlink, 81 | }; 82 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/run.js: -------------------------------------------------------------------------------- 1 | function format(time) { 2 | return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); 3 | } 4 | 5 | function run(fn, options) { 6 | const task = typeof fn.default === 'undefined' ? fn : fn.default; 7 | const start = new Date(); 8 | console.log(`[${format(start)}] Starting '${task.name}${options ? `(${options})` : ''}'...`); 9 | return task(options).then(resolution => { 10 | const end = new Date(); 11 | const time = end.getTime() - start.getTime(); 12 | console.log( 13 | `[${format(end)}] Finished '${task.name}${options ? `(${options})` : ''}' after ${time} ms` 14 | ); 15 | return resolution; 16 | }); 17 | } 18 | 19 | /** Running from node-cli - babel-node scripts/run someModule */ 20 | 21 | // When a file is run directly from Node.js, require.main is set to its module 22 | if (require.main === module && process.argv.length > 2) { 23 | delete require.cache[__filename]; // eslint-disable-line no-underscore-dangle 24 | 25 | // eslint-disable-next-line import/no-dynamic-require 26 | const module = require(`./${process.argv[2]}.js`); 27 | 28 | run(module).catch(err => { 29 | console.error(err.stack); 30 | process.exit(1); 31 | }); 32 | } 33 | 34 | module.exports = run; 35 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/start.js: -------------------------------------------------------------------------------- 1 | const run = require('./run'); 2 | 3 | function start() { 4 | return run(require('./startDevServer')); 5 | } 6 | 7 | module.exports = start; 8 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/startDevServer.js: -------------------------------------------------------------------------------- 1 | const invariant = require('invariant'); 2 | const browserSync = require('browser-sync'); 3 | const webpack = require('webpack'); 4 | const R = require('ramda'); 5 | const R_ = require('ramda-extension'); 6 | const webpackDevMiddleware = require('webpack-dev-middleware'); 7 | const webpackHotMiddleware = require('webpack-hot-middleware'); 8 | const webpackHotServerMiddleware = require('webpack-hot-server-middleware'); 9 | const proxyMiddleware = require('http-proxy-middleware'); 10 | const historyAPIFallback = require('connect-history-api-fallback'); 11 | const { outputBufferingMiddleware } = require('react-union-rendering-service'); 12 | 13 | const webpackConfigs = require('./webpack.config'); 14 | const cli = require('./lib/cli'); 15 | const { stats, getAppConfig } = require('./lib/utils'); 16 | 17 | // webpack 4.0 compatible. based on impl from webpack dev-server 18 | const getProxyMiddleware = ({ proxy } = {}) => { 19 | if (!proxy) { 20 | return []; 21 | } 22 | let normalizedConfig = proxy; 23 | if (!R_.isArray(proxy)) { 24 | normalizedConfig = R.pipe( 25 | R.mapObjIndexed((target, context) => { 26 | // for more info see https://github.com/webpack/webpack-dev-server/blob/master/lib/Server.js#L193 27 | const correctedContext = R.o(R.replace(/\/\*$/, ''), R.replace(/^\*$/, '**'))(context); 28 | if (R_.isString(target)) { 29 | return { 30 | context: correctedContext, 31 | target, 32 | }; 33 | } else { 34 | return { 35 | ...target, 36 | context: correctedContext, 37 | }; 38 | } 39 | }), 40 | R.values 41 | )(proxy); 42 | } 43 | return R.map(config => { 44 | const proxyConfig = R_.isFunction(config) ? config() : config; 45 | return proxyMiddleware(proxyConfig.context, proxyConfig); 46 | }, normalizedConfig); 47 | }; 48 | 49 | async function startDevServer() { 50 | invariant( 51 | webpackConfigs.length === 1, 52 | 'You can start the development server only for one module at a time.' 53 | ); 54 | 55 | const webpackConfigPair = webpackConfigs[0]; 56 | const clientConfig = webpackConfigPair[0]; 57 | const unionConfig = getAppConfig(); 58 | 59 | const isSSR = webpackConfigPair[1] && !cli.noSSR && !cli.proxy; 60 | 61 | if (isSSR) { 62 | global.ReactUnionRenderingServiceOptions = { 63 | ...unionConfig.renderingService, 64 | isMiddleware: true, 65 | }; 66 | } 67 | 68 | invariant(!cli.proxy || unionConfig.proxy.port, "Missing 'port' for proxy in your union.config."); 69 | invariant( 70 | !cli.proxy || unionConfig.proxy.target, 71 | "Missing 'target' for proxy in your union.config." 72 | ); 73 | 74 | const compiler = R.o(webpack, R_.rejectNil)(webpackConfigPair); 75 | const [clientCompiler] = compiler.compilers; 76 | 77 | const shouldUseHistoryAPIFallback = !cli.proxy && unionConfig.devServer.historyApiFallback; 78 | 79 | const middleware = R.filter(Boolean, [ 80 | isSSR && outputBufferingMiddleware(), 81 | webpackDevMiddleware(isSSR ? compiler : clientCompiler, { 82 | publicPath: clientConfig.output.publicPath, 83 | stats, 84 | serverSideRender: isSSR, 85 | }), 86 | webpackHotMiddleware(clientCompiler), 87 | isSSR && webpackHotServerMiddleware(compiler), 88 | shouldUseHistoryAPIFallback && 89 | historyAPIFallback({ 90 | disableDotRule: true, 91 | htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], 92 | }), 93 | ...getProxyMiddleware(clientConfig.devServer), 94 | ]); 95 | 96 | const baseDirs = [unionConfig.paths.public]; 97 | 98 | const config = cli.proxy 99 | ? { 100 | port: unionConfig.proxy.port, 101 | proxy: { 102 | target: unionConfig.proxy.target, 103 | middleware, 104 | }, 105 | // TODO: we probably shouldn't serve index.ejs when running a proxy 106 | serveStatic: baseDirs, 107 | } 108 | : { 109 | port: unionConfig.devServer.port, 110 | server: { 111 | baseDir: baseDirs, 112 | middleware, 113 | }, 114 | }; 115 | 116 | browserSync.create().init({ 117 | ui: false, 118 | ...config, 119 | }); 120 | } 121 | module.exports = startDevServer; 122 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/test.js: -------------------------------------------------------------------------------- 1 | const jest = require('jest'); 2 | const { any, anyPass, equals, pick, omit, join, has, o, keys } = require('ramda'); 3 | const { isNotEmpty } = require('ramda-extension'); 4 | const path = require('path'); 5 | const fs = require('fs'); 6 | 7 | const { debug } = require('./lib/cli'); 8 | const { resolveSymlink } = require('./lib/fs'); 9 | 10 | const hasWatch = any(anyPass([equals('--watchAll'), equals('--watch')])); 11 | const hasCoverage = any(equals('--coverage')); 12 | const joinWithCommaSpace = o(join(', '), keys); 13 | 14 | const rootDir = process.cwd(); 15 | 16 | const supportedKeys = [ 17 | 'collectCoverageFrom', 18 | 'coverageReporters', 19 | 'coverageThreshold', 20 | 'resetMocks', 21 | 'resetModules', 22 | 'snapshotSerializers', 23 | 'watchPathIgnorePatterns', 24 | ]; 25 | 26 | const pickSupported = pick(supportedKeys); 27 | const pickUnsupported = omit(supportedKeys); 28 | const containsSetupOption = has('setupTestFrameworkScriptFile'); 29 | 30 | const filePattern = 31 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$'; 32 | 33 | const getConfig = jestConfig => { 34 | const setupTestsFile = fs.existsSync(path.resolve(rootDir, 'testsSetup.js')) 35 | ? '/testsSetup.js' 36 | : undefined; 37 | 38 | const unsupported = pickUnsupported(jestConfig); 39 | 40 | if (isNotEmpty(unsupported)) { 41 | console.log( 42 | `You passed follow unsupported options for jest config: ${joinWithCommaSpace(unsupported)}. 43 | They will be ignored.` 44 | ); 45 | } 46 | 47 | if (containsSetupOption(unsupported)) { 48 | console.log( 49 | `Instead of using "setupTestFrameworkScriptFile" inside package.json#jest, 50 | create "testsSetup.js" file in root of your project.` 51 | ); 52 | } 53 | 54 | const sanitizedConfig = pickSupported(jestConfig); 55 | 56 | return { 57 | cacheDirectory: '/tmp/jest_cache', 58 | globals: { 59 | __DEV__: true, 60 | }, 61 | transform: { 62 | '^.+\\.jsx?$': require.resolve('babel-jest'), 63 | '^.+\\.s?css$': resolveSymlink(__dirname, 'jest/scssTransformer.js'), 64 | [filePattern]: resolveSymlink(__dirname, 'jest/fileTransformer.js'), 65 | }, 66 | rootDir, 67 | setupTestFrameworkScriptFile: setupTestsFile, 68 | moduleFileExtensions: ['web.js', 'js', 'json', 'web.jsx', 'jsx', 'node'], 69 | transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'], 70 | ...sanitizedConfig, 71 | }; 72 | }; 73 | 74 | function test() { 75 | const options = process.argv.slice(3); 76 | 77 | process.env.BABEL_ENV = 'test'; 78 | process.env.NODE_ENV = 'test'; 79 | 80 | // eslint-disable-next-line import/no-dynamic-require 81 | const passedJestConfig = Object.assign({}, require(`${rootDir}/package.json`).jest); 82 | 83 | const includeWatch = debug && !hasWatch(options) && !hasCoverage(options); 84 | const jestOptions = [ 85 | ...(includeWatch ? ['--watchAll'] : []), 86 | ...['--config', JSON.stringify(getConfig(passedJestConfig))], 87 | ]; 88 | 89 | return jest.run(jestOptions); 90 | } 91 | 92 | module.exports = test; 93 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/webpack/common.parts.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const optimization = () => ({ 4 | optimization: { 5 | splitChunks: { 6 | chunks: 'async', 7 | minSize: 30000, 8 | minChunks: 1, 9 | maxAsyncRequests: 5, 10 | maxInitialRequests: 3, 11 | automaticNameDelimiter: '~', 12 | name: true, 13 | cacheGroups: { 14 | vendors: { 15 | test: /[\\/]node_modules[\\/]/, 16 | name: 'vendor', 17 | chunks: 'initial', 18 | }, 19 | }, 20 | }, 21 | // Keep the runtime chunk seperated to enable long term caching 22 | // https://twitter.com/wSokra/status/969679223278505985 23 | runtimeChunk: { 24 | name: 'runtime', 25 | }, 26 | }, 27 | }); 28 | 29 | const resolve = modules => ({ 30 | resolve: { 31 | modules, 32 | extensions: ['.webpack.js', '.web.js', '.mjs', '.js', '.json'], 33 | }, 34 | }); 35 | 36 | const performanceHints = () => ({ 37 | performance: { 38 | hints: false, 39 | }, 40 | }); 41 | 42 | const context = () => ({ 43 | context: path.resolve(path.join(process.cwd(), './src')), 44 | }); 45 | 46 | module.exports = { 47 | resolve, 48 | optimization, 49 | performanceHints, 50 | context, 51 | }; 52 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/webpack/loaders.parts.js: -------------------------------------------------------------------------------- 1 | const ExtractCSSChunks = require('extract-css-chunks-webpack-plugin'); 2 | 3 | const loadJS = (cache, isServerConfig) => ({ 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.jsx?$/, 8 | exclude: /node_modules/, 9 | use: [ 10 | { 11 | loader: require.resolve('babel-loader'), 12 | options: { 13 | cacheDirectory: cache, 14 | plugins: isServerConfig 15 | ? [['@babel/plugin-transform-modules-commonjs', { loose: false }]] 16 | : [], 17 | }, 18 | }, 19 | ], 20 | }, 21 | ], 22 | }, 23 | }); 24 | 25 | const loadCSS = isServerConfig => ({ 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.css$/, 30 | use: [ 31 | ...(isServerConfig ? [] : [ExtractCSSChunks.loader]), 32 | { 33 | loader: require.resolve('css-loader'), 34 | options: { 35 | modules: { 36 | localIdentName: '[name]__[local]--[hash:base64:5]', 37 | }, 38 | onlyLocals: isServerConfig, 39 | }, 40 | }, 41 | ], 42 | }, 43 | ], 44 | }, 45 | }); 46 | 47 | const loadImages = ({ outputMapper }) => ({ 48 | module: { 49 | rules: [ 50 | { 51 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/, 52 | use: [ 53 | { 54 | loader: require.resolve('url-loader'), 55 | options: { 56 | name: `${outputMapper.media}/[name].[ext]`, 57 | }, 58 | }, 59 | ], 60 | }, 61 | ], 62 | }, 63 | }); 64 | 65 | const loadFiles = ({ outputMapper }) => ({ 66 | module: { 67 | rules: [ 68 | { 69 | test: /\.(eot|ttf|wav|mp3|otf)$/, 70 | use: [ 71 | { 72 | loader: require.resolve('file-loader'), 73 | options: { 74 | name: `${outputMapper.media}/[name].[ext]`, 75 | }, 76 | }, 77 | ], 78 | }, 79 | ], 80 | }, 81 | }); 82 | 83 | module.exports = { 84 | loadJS, 85 | loadCSS, 86 | loadImages, 87 | loadFiles, 88 | }; 89 | -------------------------------------------------------------------------------- /packages/react-union-scripts/scripts/webpack/plugins.parts.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 4 | const ManifestPlugin = require('webpack-manifest-plugin'); 5 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 6 | const path = require('path'); 7 | const TerserPlugin = require('terser-webpack-plugin'); 8 | const ExtractCSSChunks = require('extract-css-chunks-webpack-plugin'); 9 | const { getForMode } = require('../lib/utils'); 10 | 11 | const loaderOptionsPlugin = debug => ({ 12 | plugins: [ 13 | new webpack.LoaderOptionsPlugin({ 14 | debug, 15 | }), 16 | ], 17 | }); 18 | 19 | const cleanPlugin = ({ clean: { options } = {} }) => ({ 20 | plugins: [new CleanWebpackPlugin(options)], 21 | }); 22 | 23 | const definePlugin = globals => ({ 24 | plugins: [new webpack.DefinePlugin(globals)], 25 | }); 26 | 27 | const hmrPlugin = () => ({ 28 | plugins: [new webpack.HotModuleReplacementPlugin()], 29 | }); 30 | 31 | const htmlPlugin = ({ appName, outputMapper, paths, templateFilename }, outputPath) => ({ 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | title: appName, 35 | filename: path.resolve(outputPath, outputMapper.index), 36 | template: `${paths.public}/${templateFilename}`, 37 | }), 38 | ], 39 | }); 40 | 41 | const manifestPlugin = () => ({ 42 | plugins: [ 43 | new ManifestPlugin({ 44 | fileName: 'assetManifest.json', 45 | }), 46 | ], 47 | }); 48 | 49 | const analyzeBundlePlugin = () => ({ 50 | plugins: [new BundleAnalyzerPlugin()], 51 | }); 52 | 53 | const uglifyJSPlugin = (verbose, { sourceMaps, uglifyOptions: { cache, parallel, mangle } }) => ({ 54 | optimization: { 55 | minimizer: [ 56 | new TerserPlugin({ 57 | terserOptions: { 58 | parse: { 59 | ecma: 8, 60 | }, 61 | compress: { 62 | ecma: 5, 63 | warnings: false, 64 | comparisons: false, 65 | inline: 2, 66 | }, 67 | mangle: mangle || { 68 | safari10: true, 69 | }, 70 | output: { 71 | ecma: 5, 72 | comments: false, 73 | ascii_only: true, 74 | }, 75 | }, 76 | cache, 77 | parallel, 78 | sourceMap: Boolean(sourceMaps), 79 | }), 80 | ], 81 | }, 82 | }); 83 | 84 | const limitChunkCountPlugin = () => ({ 85 | plugins: [ 86 | new webpack.optimize.LimitChunkCountPlugin({ 87 | maxChunks: 1, 88 | }), 89 | ], 90 | }); 91 | 92 | const extractCSSChunksPlugin = (hot, path) => { 93 | const filename = getForMode('[name].css', '[name].[chunkhash:8].css'); 94 | const chunkFilename = getForMode('[name].chunk.css', '[name].[chunkhash:8].chunk.css'); 95 | 96 | return { 97 | plugins: [ 98 | new ExtractCSSChunks({ 99 | hot, 100 | cssModules: true, 101 | filename: `${path}/${filename}`, 102 | chunkFilename: `${path}/${chunkFilename}`, 103 | }), 104 | ], 105 | }; 106 | }; 107 | 108 | module.exports = { 109 | loaderOptionsPlugin, 110 | definePlugin, 111 | htmlPlugin, 112 | hmrPlugin, 113 | manifestPlugin, 114 | analyzeBundlePlugin, 115 | uglifyJSPlugin, 116 | cleanPlugin, 117 | limitChunkCountPlugin, 118 | extractCSSChunksPlugin, 119 | }; 120 | -------------------------------------------------------------------------------- /packages/react-union/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | es/ 3 | dist/ 4 | lib/ 5 | coverage 6 | -------------------------------------------------------------------------------- /packages/react-union/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | yarn: true 4 | directories: 5 | - node_modules 6 | - packages/react-union/node_modules 7 | - packages/react-union-scripts/node_modules 8 | node_js: 9 | - "node" 10 | - "8" 11 | -------------------------------------------------------------------------------- /packages/react-union/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | React Union 4 | 5 |

6 | 7 |

8 | 9 | by Lundegaard 10 | 11 |

12 | 13 |

14 | 🖍️ 🛡 🚀 15 |

16 | 17 |

18 | Easy React integration into legacy systems 19 |

20 | 21 |

22 | React Union is a library for rendering React applications in CMS-like environments. It allows you to treat your React applications as widgets, meaning that the user gets to choose the combination and location of widgets for any given page. 23 |

24 | 25 |

26 | See our documentation site. 27 |

28 | 29 |

30 | 31 | 32 | Build status 33 | 34 | 35 | 36 | Greenkeeper badge 37 | 38 | 39 | 40 | Github 41 | 42 | 43 | MIT License 44 | 45 | 46 | Downloads 47 | 48 | 49 | 50 | Version 51 | 52 |

53 | 54 | 55 | The main idea is that instead of rendering the application directly, we render HTML fragments which describe the widgets to render. We call them [widget descriptors](https://react-union.org/union-component-widget-descriptors). React Union then parses the HTML output and renders the React application, which uses a single virtual DOM, making it trivial to reuse e.g. Redux state across widgets. 56 | 57 | See https://react-union.org/union-component-introduction for more information. 58 | -------------------------------------------------------------------------------- /packages/react-union/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-union", 3 | "version": "0.20.0", 4 | "license": "MIT", 5 | "main": "lib/react-union.js", 6 | "module": "es/react-union.js", 7 | "unpkg": "dist/react-union.js", 8 | "repository": "https://github.com/lundegaard/react-union", 9 | "contributors": [ 10 | "Tomas Konrady ", 11 | "Vaclav Jancarik " 12 | ], 13 | "description": "Assemble a single React application from distributed HTML segments.", 14 | "files": [ 15 | "src", 16 | "lib", 17 | "es", 18 | "dist" 19 | ], 20 | "dependencies": { 21 | "@babel/runtime": "^7.11.2", 22 | "hoist-non-react-statics": "^3.3.2", 23 | "prop-types": "^15.6.2", 24 | "ramda": "^0.27.0", 25 | "ramda-extension": "^0.10.3" 26 | }, 27 | "devDependencies": { 28 | "@babel/cli": "^7.11.6", 29 | "@babel/core": "^7.11.6", 30 | "babel-eslint": "^10.1.0", 31 | "babel-jest": "^26.1.0", 32 | "enzyme": "^3.11.0", 33 | "enzyme-adapter-react-16": "^1.15.2", 34 | "enzyme-to-json": "^3.5.0", 35 | "eslint": "^7.4.0", 36 | "eslint-plugin-import": "^2.22.0", 37 | "eslint-plugin-react": "^7.20.3", 38 | "jest": "^24.0.0", 39 | "lint-staged": "^9.4.1", 40 | "prettier": "^2.0.5", 41 | "react": "^16.13.1", 42 | "react-dom": "^16.13.1", 43 | "rimraf": "^3.0.2", 44 | "rollup": "^1.1.2", 45 | "rollup-plugin-babel": "^4.3.2", 46 | "rollup-plugin-commonjs": "^10.1.0", 47 | "rollup-plugin-node-resolve": "^5.2.0", 48 | "rollup-plugin-replace": "^2.1.0" 49 | }, 50 | "peerDependencies": { 51 | "react": "^16.8.3", 52 | "react-dom": "^16.8.3" 53 | }, 54 | "sideEffects": false 55 | } 56 | -------------------------------------------------------------------------------- /packages/react-union/src/components/Union/Union.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component, Fragment } from 'react'; 3 | import { map, path } from 'ramda'; 4 | import { noop } from 'ramda-extension'; 5 | 6 | import { invariant } from '../../utils'; 7 | import { RouteShape } from '../../shapes'; 8 | import scan from '../../scanning'; 9 | import route from '../../routing'; 10 | import { IS_SERVER, RESCAN } from '../../constants'; 11 | import { PrescanContext } from '../../contexts'; 12 | 13 | import Widget from '../Widget'; 14 | 15 | /** 16 | * Renders your widgets according to found widget descriptors and passed `routes`. 17 | * Widgets are encapsulated in a single virtual DOM even though they may be spread out 18 | * in the actual mark-up. 19 | */ 20 | class Union extends Component { 21 | static scan = props => { 22 | const { onScanStart, onScanEnd, onScanError, parent, routes } = props; 23 | 24 | invariant(routes, 'Missing `routes` prop in .'); 25 | 26 | try { 27 | onScanStart(); 28 | const scanResult = scan(parent); 29 | const routeResult = route(routes, scanResult); 30 | onScanEnd(routeResult); 31 | 32 | return routeResult.widgetConfigs; 33 | } catch (error) { 34 | onScanError(error); 35 | 36 | throw error; 37 | } 38 | }; 39 | 40 | static propTypes = { 41 | /** 42 | * Children of the `Union` component. 43 | */ 44 | children: PropTypes.node, 45 | /** 46 | * Called after the scan of the HTML is successfully done. 47 | */ 48 | onScanEnd: PropTypes.func, 49 | /** 50 | * Called when an error happens while scanning the HTML. 51 | */ 52 | onScanError: PropTypes.func, 53 | /** 54 | * Called before the scan of the HTML. 55 | */ 56 | onScanStart: PropTypes.func, 57 | /** 58 | * HTML element or Cheerio wrapper in which the scan is running. Defaults to `document`. 59 | */ 60 | parent: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), 61 | /** 62 | * Array of routes that are supported by your application. 63 | */ 64 | routes: PropTypes.arrayOf(PropTypes.shape(RouteShape)), 65 | }; 66 | 67 | static defaultProps = { 68 | onScanEnd: noop, 69 | onScanError: noop, 70 | onScanStart: noop, 71 | parent: IS_SERVER ? null : document, 72 | }; 73 | 74 | static getDerivedStateFromProps(nextProps, previousState) { 75 | if (previousState.routesReference !== nextProps.routes) { 76 | return { 77 | routesReference: nextProps.routes, 78 | widgetConfigs: Union.scan(nextProps), 79 | }; 80 | } 81 | 82 | return null; 83 | } 84 | 85 | constructor(props, context) { 86 | super(props, context); 87 | const { widgetConfigs } = this.context || {}; 88 | 89 | this.state = { 90 | routesReference: props.routes, 91 | widgetConfigs: widgetConfigs || Union.scan(props), 92 | }; 93 | } 94 | 95 | componentDidMount() { 96 | document.addEventListener(RESCAN, this.rescan); 97 | } 98 | 99 | componentWillUnmount() { 100 | document.removeEventListener(RESCAN, this.rescan); 101 | } 102 | 103 | static contextType = PrescanContext; 104 | 105 | rescan = () => 106 | this.setState({ 107 | widgetConfigs: Union.scan(this.props), 108 | }); 109 | 110 | renderWidget = widgetConfig => { 111 | const { initialProps } = this.context || {}; 112 | 113 | return ( 114 | 119 | ); 120 | }; 121 | 122 | render() { 123 | const { children } = this.props; 124 | const { widgetConfigs } = this.state; 125 | 126 | return ( 127 | 128 | {children} 129 | {map(this.renderWidget, widgetConfigs)} 130 | 131 | ); 132 | } 133 | } 134 | 135 | export default Union; 136 | -------------------------------------------------------------------------------- /packages/react-union/src/components/Union/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Union'; 2 | -------------------------------------------------------------------------------- /packages/react-union/src/components/Widget/Widget.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { createPortal } from 'react-dom'; 3 | import PropTypes from 'prop-types'; 4 | import { memoizeWith, prop } from 'ramda'; 5 | import { isFunction } from 'ramda-extension'; 6 | 7 | import { invariant } from '../../utils'; 8 | import { withErrorBoundary } from '../../decorators'; 9 | import { WidgetContext } from '../../contexts'; 10 | import { WidgetConfigShape } from '../../shapes'; 11 | import { INVALID_JSON, IS_SERVER } from '../../constants'; 12 | 13 | const getGlobalInitialProps = props => 14 | !IS_SERVER && window.__INITIAL_PROPS__ && window.__INITIAL_PROPS__[props.config.namespace]; 15 | 16 | const memoizedClearContent = memoizeWith(prop('id'), element => (element.innerHTML = '')); 17 | 18 | /** 19 | * An internal component of `Union`. 20 | * 21 | * It renders a widget based on `descriptor` and `component` using React portals. 22 | * Provides context to the `component` with widget descriptor information. 23 | * 24 | */ 25 | export class Widget extends Component { 26 | static propTypes = { 27 | config: PropTypes.shape(WidgetConfigShape).isRequired, 28 | initialProps: PropTypes.object, 29 | }; 30 | 31 | constructor(props, context) { 32 | super(props, context); 33 | 34 | this.state = { 35 | initialProps: props.initialProps || getGlobalInitialProps(props) || null, 36 | }; 37 | } 38 | 39 | // NOTE: We do not use an async function to avoid bundle size issues with regenerator runtime. 40 | componentDidMount() { 41 | const { initialProps } = this.state; 42 | 43 | if (!initialProps) { 44 | const { config } = this.props; 45 | const { component } = config; 46 | 47 | if (isFunction(component.preload)) { 48 | return component.preload().then(this.getInitialProps); 49 | } 50 | 51 | this.getInitialProps(component); 52 | } 53 | } 54 | 55 | getInitialProps = component => { 56 | const { config } = this.props; 57 | 58 | if (isFunction(component.getInitialProps)) { 59 | component.getInitialProps(config).then(initialProps => this.setState({ initialProps })); 60 | } 61 | }; 62 | 63 | render() { 64 | const { config } = this.props; 65 | const { initialProps } = this.state; 66 | const { component: WidgetComponent, container, data, namespace, widget } = config; 67 | 68 | invariant( 69 | data !== INVALID_JSON, 70 | `Invalid JSON data encountered while attempting to render widget "${widget}". ` + 71 | 'This is often due to a trailing comma or missing quotation marks.' 72 | ); 73 | 74 | const widgetProps = { data, namespace }; 75 | 76 | const widgetElement = ( 77 | 78 | 79 | 80 | ); 81 | 82 | if (IS_SERVER) { 83 | return
{widgetElement}
; 84 | } 85 | 86 | const domElement = document.getElementById(container); 87 | invariant(domElement, `HTML element with ID "${container}" not found for widget "${widget}".`); 88 | 89 | // NOTE: Because React does not support hydration of portals yet, we clear the domElement's 90 | // inner HTML on the initial render. In order to prevent an ugly white flash, we need to do 91 | // this immediately before rendering the actual client-side portal. Memoization is used 92 | // to prevent clearing the same element more than once per multiple client-side renders. 93 | // TODO: Remove next line when https://github.com/facebook/react/issues/13097 is resolved. 94 | memoizedClearContent(domElement); 95 | return createPortal(widgetElement, domElement); 96 | } 97 | } 98 | 99 | export default withErrorBoundary(Widget); 100 | -------------------------------------------------------------------------------- /packages/react-union/src/components/Widget/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Widget'; 2 | -------------------------------------------------------------------------------- /packages/react-union/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Union } from './Union'; 2 | export { default as Widget } from './Widget'; 3 | -------------------------------------------------------------------------------- /packages/react-union/src/constants.js: -------------------------------------------------------------------------------- 1 | export const INVALID_JSON = 'INVALID_JSON'; 2 | 3 | export const IS_SERVER = Boolean(process && process.versions && process.versions.node); 4 | 5 | export const SHOULD_NOT_LEAK = process.env.NODE_ENV === 'production' && !IS_SERVER; 6 | 7 | export const RESCAN = 'react-union/rescan'; 8 | -------------------------------------------------------------------------------- /packages/react-union/src/contexts.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const WidgetContext = createContext({ 4 | data: undefined, 5 | namespace: undefined, 6 | }); 7 | 8 | export const PrescanContext = createContext({ 9 | initialProps: undefined, 10 | widgetConfigs: undefined, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/react-union/src/decorators/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import hoistNonReactStatics from 'hoist-non-react-statics'; 3 | 4 | import { getDisplayName } from '../utils'; 5 | import { WidgetContext } from '../contexts'; 6 | 7 | export const makeContextDecorator = (Context, displayName) => NextComponent => { 8 | const Decorator = props => ( 9 | {value => } 10 | ); 11 | 12 | hoistNonReactStatics(Decorator, NextComponent); 13 | Decorator.displayName = `With${displayName}(${getDisplayName(NextComponent)})`; 14 | 15 | return Decorator; 16 | }; 17 | 18 | export const withWidgetContext = makeContextDecorator(WidgetContext, 'WidgetContext'); 19 | export { default as withErrorBoundary } from './withErrorBoundary'; 20 | -------------------------------------------------------------------------------- /packages/react-union/src/decorators/withErrorBoundary/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './withErrorBoundary'; 2 | -------------------------------------------------------------------------------- /packages/react-union/src/decorators/withErrorBoundary/withErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { path } from 'ramda'; 4 | 5 | import { WidgetConfigShape } from '../../shapes'; 6 | import { getDisplayName } from '../../utils'; 7 | 8 | const getWidgetName = path(['props', 'config', 'widget']); 9 | 10 | const withErrorBoundary = NextComponent => { 11 | class WithErrorBoundary extends Component { 12 | static displayName = `WithErrorBoundary(${getDisplayName(NextComponent)})`; 13 | 14 | static propTypes = { 15 | config: PropTypes.shape(WidgetConfigShape).isRequired, 16 | }; 17 | 18 | state = { 19 | hasError: false, 20 | }; 21 | 22 | componentDidCatch() { 23 | this.setState({ hasError: true }); 24 | } 25 | 26 | render() { 27 | const { hasError } = this.state; 28 | const widgetName = getWidgetName(this); 29 | 30 | if (hasError) { 31 | return ( 32 |
33 | {`An error has occurred in widget "${widgetName}". See the console for more details. To 34 | avoid seeing this message in production, wrap this widget in an error boundary.`} 35 |
36 | ); 37 | } 38 | 39 | return ; 40 | } 41 | } 42 | 43 | return WithErrorBoundary; 44 | }; 45 | 46 | export default withErrorBoundary; 47 | -------------------------------------------------------------------------------- /packages/react-union/src/dom.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import { noop } from 'ramda-extension'; 3 | 4 | import { RESCAN } from './constants'; 5 | 6 | const DEFAULT_UNION_ROOT_ID = 'union'; 7 | 8 | const createElement = (id, parent, elementType) => { 9 | const newElement = document.createElement(elementType); 10 | const idAttribute = document.createAttribute('id'); 11 | idAttribute.value = id; 12 | 13 | newElement.setAttributeNode(idAttribute); 14 | parent.appendChild(newElement); 15 | 16 | return newElement; 17 | }; 18 | 19 | const createElementWithId = (id, parent, elementType = 'div') => { 20 | const element = document.getElementById(id); 21 | 22 | return element ? element : createElement(id, parent, elementType); 23 | }; 24 | 25 | export function justRender(reactElement, rootId = DEFAULT_UNION_ROOT_ID, resolve = noop) { 26 | const domElement = createElementWithId(rootId, document.body); 27 | 28 | if (window.__HYDRATE__) { 29 | // NOTE: We do not want to hydrate twice. 30 | delete window.__HYDRATE__; 31 | // TODO: Use ReactDOM.hydrate once React supports hydration of portals. 32 | // https://github.com/facebook/react/issues/13097 33 | ReactDOM.render(reactElement, domElement, resolve); 34 | } 35 | 36 | ReactDOM.render(reactElement, domElement, resolve); 37 | } 38 | 39 | export function justUnmountComponentAtNode(rootId = DEFAULT_UNION_ROOT_ID) { 40 | const rootElement = document.getElementById(rootId); 41 | 42 | ReactDOM.unmountComponentAtNode(rootElement); 43 | } 44 | 45 | export const rescan = () => { 46 | const event = document.createEvent('Event'); 47 | event.initEvent(RESCAN, true, true); 48 | document.dispatchEvent(event); 49 | }; 50 | -------------------------------------------------------------------------------- /packages/react-union/src/index.js: -------------------------------------------------------------------------------- 1 | export * from './shapes'; 2 | export * from './dom'; 3 | export * from './contexts'; 4 | export { default as scan } from './scanning'; 5 | export { default as route } from './routing'; 6 | export { withWidgetContext } from './decorators'; 7 | export { default as Union } from './components/Union'; 8 | -------------------------------------------------------------------------------- /packages/react-union/src/routing.js: -------------------------------------------------------------------------------- 1 | import { whereEq, curry, o, map, prop, ifElse, contains, always, find } from 'ramda'; 2 | import { mergeDeepRightAll, rejectNil } from 'ramda-extension'; 3 | 4 | import { invariant } from './utils'; 5 | import { INVALID_JSON } from './constants'; 6 | 7 | const mergeData = ifElse( 8 | contains(INVALID_JSON), 9 | always(INVALID_JSON), 10 | o(mergeDeepRightAll, rejectNil) 11 | ); 12 | 13 | const findRouteByDescriptor = (routes, descriptor) => 14 | find(whereEq({ path: descriptor.widget }), routes); 15 | 16 | const findComponentByDescriptor = (routes, descriptor) => { 17 | const route = findRouteByDescriptor(routes, descriptor); 18 | 19 | return route ? route.component : null; 20 | }; 21 | 22 | const makeWidgetConfig = curry((routes, commonData, descriptor) => { 23 | const component = findComponentByDescriptor(routes, descriptor); 24 | 25 | return ( 26 | component && { 27 | ...descriptor, 28 | component, 29 | data: mergeData([commonData, descriptor.data]), 30 | namespace: descriptor.namespace || descriptor.container, 31 | } 32 | ); 33 | }); 34 | 35 | const getCommonData = o(mergeData, map(prop('data'))); 36 | 37 | const route = (routes, scanResult) => { 38 | const { commonDescriptors, widgetDescriptors } = scanResult; 39 | const commonData = getCommonData(commonDescriptors); 40 | 41 | invariant( 42 | commonData !== INVALID_JSON, 43 | 'Invalid JSON data encountered in a common descriptor. ' + 44 | 'This is often due to a trailing comma or missing quotation marks.' 45 | ); 46 | 47 | const widgetConfigs = map(makeWidgetConfig(routes, commonData), widgetDescriptors).filter( 48 | Boolean 49 | ); 50 | 51 | return { commonData, widgetConfigs, scanResult, routes }; 52 | }; 53 | 54 | export default route; 55 | -------------------------------------------------------------------------------- /packages/react-union/src/routing.test.js: -------------------------------------------------------------------------------- 1 | import { noop } from 'ramda-extension'; 2 | 3 | import route from './routing'; 4 | import { INVALID_JSON } from './constants'; 5 | 6 | const routes = [{ path: 'foo', component: noop }, { path: 'bar', component: noop }]; 7 | 8 | describe('route', () => { 9 | it('can create simple configs with just widget descriptors', () => { 10 | const scanResult = { 11 | commonDescriptors: [], 12 | widgetDescriptors: [ 13 | { widget: 'foo', container: 'foo-container' }, 14 | { widget: 'bar', container: 'bar-container' }, 15 | ], 16 | }; 17 | 18 | expect(route(routes, scanResult)).toEqual({ 19 | commonData: {}, 20 | routes, 21 | scanResult, 22 | widgetConfigs: [ 23 | { 24 | component: noop, 25 | container: 'foo-container', 26 | data: {}, 27 | namespace: 'foo-container', 28 | widget: 'foo', 29 | }, 30 | { 31 | component: noop, 32 | container: 'bar-container', 33 | data: {}, 34 | namespace: 'bar-container', 35 | widget: 'bar', 36 | }, 37 | ], 38 | }); 39 | }); 40 | 41 | it('merges commonData correctly', () => { 42 | const data = { foo: 'bar', bar: 'baz' }; 43 | 44 | const scanResult = { 45 | commonDescriptors: [{ data }], 46 | widgetDescriptors: [{ widget: 'foo', container: 'foo-container', data: { foo: 'YOLO' } }], 47 | }; 48 | 49 | expect(route(routes, scanResult)).toEqual({ 50 | commonData: data, 51 | routes, 52 | scanResult, 53 | widgetConfigs: [ 54 | { 55 | component: noop, 56 | container: 'foo-container', 57 | data: { foo: 'YOLO', bar: 'baz' }, 58 | namespace: 'foo-container', 59 | widget: 'foo', 60 | }, 61 | ], 62 | }); 63 | }); 64 | 65 | it('throws when INVALID_JSON is found in a common descriptor', () => { 66 | const scanResult = { 67 | commonDescriptors: [{ data: INVALID_JSON }], 68 | widgetDescriptors: [{ widget: 'foo', container: 'foo-container' }], 69 | }; 70 | 71 | expect(() => route(routes, scanResult)).toThrow(); 72 | }); 73 | 74 | it('handles INVALID_JSON in a widget descriptor', () => { 75 | const scanResult = { 76 | commonDescriptors: [{ data: { foo: 'bar' } }], 77 | widgetDescriptors: [{ widget: 'foo', container: 'foo-container', data: INVALID_JSON }], 78 | }; 79 | 80 | expect(route(routes, scanResult)).toEqual({ 81 | commonData: { foo: 'bar' }, 82 | routes, 83 | scanResult, 84 | widgetConfigs: [ 85 | { 86 | component: noop, 87 | container: 'foo-container', 88 | data: INVALID_JSON, 89 | namespace: 'foo-container', 90 | widget: 'foo', 91 | }, 92 | ], 93 | }); 94 | }); 95 | }); 96 | -------------------------------------------------------------------------------- /packages/react-union/src/scanning/cheerio.js: -------------------------------------------------------------------------------- 1 | import { o, unary, applySpec } from 'ramda'; 2 | 3 | import { INVALID_JSON } from '../constants'; 4 | 5 | const dangerouslyParseJSONContent = o(unary(JSON.parse), wrapper => wrapper.html()); 6 | 7 | const parseJSONContent = wrapper => { 8 | try { 9 | return dangerouslyParseJSONContent(wrapper); 10 | } catch (error) { 11 | if (wrapper.html().trim()) { 12 | return INVALID_JSON; 13 | } 14 | 15 | return null; 16 | } 17 | }; 18 | 19 | const parseWidgetDescriptorWrapper = wrapper => ({ 20 | widget: wrapper.data('union-widget'), 21 | container: wrapper.data('union-container'), 22 | namespace: wrapper.data('union-namespace'), 23 | data: parseJSONContent(wrapper), 24 | }); 25 | 26 | export const getWidgetDescriptors = $ => 27 | $('[data-union-widget]') 28 | .map((_, element) => parseWidgetDescriptorWrapper($(element))) 29 | .get(); 30 | 31 | const parseCommonDescriptorWrapper = applySpec({ 32 | data: parseJSONContent, 33 | }); 34 | 35 | export const getCommonDescriptors = $ => 36 | $('[data-union-common]') 37 | .map((_, element) => parseCommonDescriptorWrapper($(element))) 38 | .get(); 39 | -------------------------------------------------------------------------------- /packages/react-union/src/scanning/dom.js: -------------------------------------------------------------------------------- 1 | import { o, unary, prop, path, applySpec, map } from 'ramda'; 2 | 3 | import { INVALID_JSON } from '../constants'; 4 | 5 | const selectWidgetDescriptorElements = parent => parent.querySelectorAll('[data-union-widget]'); 6 | const selectCommonDescriptorElements = parent => parent.querySelectorAll('[data-union-common]'); 7 | 8 | const dangerouslyParseJSONContent = o(unary(JSON.parse), prop('innerHTML')); 9 | 10 | const parseJSONContent = element => { 11 | try { 12 | return dangerouslyParseJSONContent(element); 13 | } catch (error) { 14 | if (element.innerHTML.trim()) { 15 | return INVALID_JSON; 16 | } 17 | 18 | return null; 19 | } 20 | }; 21 | 22 | const parseWidgetDescriptorElement = applySpec({ 23 | widget: path(['dataset', 'unionWidget']), 24 | container: path(['dataset', 'unionContainer']), 25 | namespace: path(['dataset', 'unionNamespace']), 26 | data: parseJSONContent, 27 | }); 28 | 29 | export const getWidgetDescriptors = o( 30 | map(parseWidgetDescriptorElement), 31 | selectWidgetDescriptorElements 32 | ); 33 | 34 | const parseCommonDescriptorElement = applySpec({ 35 | data: parseJSONContent, 36 | }); 37 | 38 | export const getCommonDescriptors = o( 39 | map(parseCommonDescriptorElement), 40 | selectCommonDescriptorElements 41 | ); 42 | -------------------------------------------------------------------------------- /packages/react-union/src/scanning/index.js: -------------------------------------------------------------------------------- 1 | import { applySpec } from 'ramda'; 2 | 3 | import { IS_SERVER } from '../constants'; 4 | import * as DOM from './dom'; 5 | import * as Cheerio from './cheerio'; 6 | 7 | const { getWidgetDescriptors, getCommonDescriptors } = IS_SERVER ? Cheerio : DOM; 8 | 9 | const scan = applySpec({ 10 | commonDescriptors: getCommonDescriptors, 11 | widgetDescriptors: getWidgetDescriptors, 12 | }); 13 | 14 | export default scan; 15 | -------------------------------------------------------------------------------- /packages/react-union/src/shapes.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | 3 | export const RouteShape = { 4 | component: PropTypes.elementType.isRequired, 5 | path: PropTypes.string.isRequired, 6 | }; 7 | 8 | export const WidgetDescriptorShape = { 9 | container: PropTypes.string, 10 | data: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), 11 | namespace: PropTypes.string, 12 | widget: PropTypes.string.isRequired, 13 | }; 14 | 15 | export const WidgetConfigShape = { 16 | // NOTE: The reason for this shallow structure is that we merge `commonData` into `data`. 17 | // If we were changing `descriptor.data` under the hood, it would be misleading. 18 | ...WidgetDescriptorShape, 19 | component: PropTypes.elementType.isRequired, 20 | // NOTE: We added `isRequired` to avoid using `namespace || container` everywhere. 21 | namespace: PropTypes.string.isRequired, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/react-union/src/utils.js: -------------------------------------------------------------------------------- 1 | import { SHOULD_NOT_LEAK } from './constants'; 2 | 3 | export const warning = (pred, msg) => { 4 | if (pred) { 5 | return; 6 | } 7 | 8 | if (SHOULD_NOT_LEAK) { 9 | return; 10 | } 11 | 12 | console.log(msg); 13 | }; 14 | 15 | export const invariant = (pred, msg) => { 16 | if (pred) { 17 | return; 18 | } 19 | 20 | if (SHOULD_NOT_LEAK) { 21 | throw new Error('An error occurred. Use a non-production build to see the details.'); 22 | } 23 | 24 | throw new Error(msg); 25 | }; 26 | 27 | export const getDisplayName = Component => Component.displayName || Component.name || 'Component'; 28 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import replacePlugin from 'rollup-plugin-replace'; 2 | import nodeResolvePlugin from 'rollup-plugin-node-resolve'; 3 | import cjsPlugin from 'rollup-plugin-commonjs'; 4 | import { terser as terserPlugin } from 'rollup-plugin-terser'; 5 | import babelPlugin from 'rollup-plugin-babel'; 6 | 7 | import path from 'path'; 8 | import { toPascalCase, toKebabCase } from 'ramda-extension'; 9 | import { keys } from 'ramda'; 10 | 11 | const { LERNA_PACKAGE_NAME, LERNA_ROOT_PATH } = process.env; 12 | 13 | const plugins = { 14 | cjs: cjsPlugin({ 15 | include: /node_modules/, 16 | }), 17 | terser: terserPlugin({ 18 | compress: { 19 | pure_getters: true, 20 | unsafe: true, 21 | unsafe_comps: true, 22 | warnings: false, 23 | }, 24 | }), 25 | nodeResolve: nodeResolvePlugin({ 26 | jsnext: true, 27 | }), 28 | babel: babelPlugin({ 29 | cwd: LERNA_ROOT_PATH, 30 | runtimeHelpers: true, 31 | }), 32 | }; 33 | 34 | const PACKAGE_ROOT_PATH = process.cwd(); 35 | const INPUT_FILE = path.join(PACKAGE_ROOT_PATH, 'src/index.js'); 36 | 37 | const globals = { 38 | 'hoist-non-react-statics': 'HoistNonReactStatics', 39 | 'prop-types': 'PropTypes', 40 | ramda: 'R', 41 | 'ramda-extension': 'R_', 42 | react: 'React', 43 | 'react-dom': 'ReactDOM', 44 | 'react-union': 'ReactUnion', 45 | }; 46 | 47 | // eslint-disable-next-line import/no-dynamic-require 48 | const pkg = require(path.join(PACKAGE_ROOT_PATH, 'package.json')); 49 | const dependencies = [...keys(pkg.dependencies), ...keys(pkg.peerDependencies)]; 50 | 51 | const external = id => dependencies.includes(id) || id.includes('@babel/runtime'); 52 | 53 | const globalName = toPascalCase(LERNA_PACKAGE_NAME); 54 | const fileName = toKebabCase(LERNA_PACKAGE_NAME); 55 | 56 | const umdExternal = ['react', 'react-dom', 'ramda', 'ramda-extension', 'prop-types']; 57 | 58 | export default [ 59 | // CJS 60 | { 61 | input: INPUT_FILE, 62 | external, 63 | output: { 64 | file: path.join(PACKAGE_ROOT_PATH, 'lib', `${fileName}.js`), 65 | format: 'cjs', 66 | indent: false, 67 | }, 68 | plugins: [plugins.babel, plugins.cjs], 69 | }, 70 | 71 | // ES 72 | { 73 | input: INPUT_FILE, 74 | external, 75 | output: { 76 | file: path.join(PACKAGE_ROOT_PATH, 'es', `${fileName}.js`), 77 | format: 'es', 78 | indent: false, 79 | }, 80 | plugins: [plugins.babel, plugins.cjs], 81 | }, 82 | 83 | // UMD Development 84 | { 85 | input: INPUT_FILE, 86 | external: umdExternal, 87 | output: { 88 | file: path.join(PACKAGE_ROOT_PATH, 'dist', `${fileName}.js`), 89 | format: 'umd', 90 | name: globalName, 91 | indent: false, 92 | globals, 93 | }, 94 | plugins: [ 95 | plugins.nodeResolve, 96 | replacePlugin({ 'process.env.NODE_ENV': JSON.stringify('development') }), 97 | plugins.babel, 98 | plugins.cjs, 99 | ], 100 | }, 101 | 102 | // UMD Production 103 | { 104 | input: INPUT_FILE, 105 | external: umdExternal, 106 | output: { 107 | file: path.join(PACKAGE_ROOT_PATH, 'dist', `${fileName}.min.js`), 108 | format: 'umd', 109 | name: globalName, 110 | indent: false, 111 | globals, 112 | }, 113 | plugins: [ 114 | plugins.nodeResolve, 115 | replacePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }), 116 | plugins.babel, 117 | plugins.cjs, 118 | plugins.terser, 119 | ], 120 | }, 121 | ]; 122 | -------------------------------------------------------------------------------- /scripts/CI/verdaccio.yaml: -------------------------------------------------------------------------------- 1 | storage: ./storage 2 | auth: 3 | htpasswd: 4 | file: ./htpasswd 5 | uplinks: 6 | npmjs: 7 | url: https://registry.npmjs.org/ 8 | packages: 9 | '@*/*': 10 | access: $all 11 | publish: $all 12 | proxy: npmjs 13 | '**': 14 | access: $all 15 | publish: $all 16 | proxy: npmjs 17 | logs: 18 | - { type: stdout, format: pretty, level: http } 19 | -------------------------------------------------------------------------------- /tests/enzymeSetup.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-react-16'; 3 | 4 | Enzyme.configure({ adapter: new Adapter() }); 5 | --------------------------------------------------------------------------------