From 2917fd0c9dbe1675a4d2ef7303689cc968f1e6a3 Mon Sep 17 00:00:00 2001 From: Alvin Lim Date: Tue, 26 Mar 2019 14:28:04 +0700 Subject: [PATCH] chore(all): init initializations --- .commitlintrc.js | 1 + .gitignore | 77 + .npmignore | 3 + .nvmrc | 1 + .prettierignore | 5 + .prettierrc | 12 + .travis.yml | 37 + CONTRIBUTING.md | 262 + LICENSE | 21 + ci/publish-stable.sh | 7 + ci/travis-checkout-branch.sh | 44 + lerna.json | 55 + package.json | 75 + packages/css-plugin/README.md | 228 + packages/css-plugin/gulpfile.js | 8 + packages/css-plugin/package.json | 33 + packages/css-plugin/pageWithStyles.js | 1 + packages/css-plugin/src/StyleContext.js | 43 + packages/css-plugin/src/index.js | 158 + packages/css-plugin/src/pageWithStyles.js | 34 + packages/css-plugin/src/withStyles.js | 57 + packages/css-plugin/withStyles.js | 1 + packages/gulp-plugin/index.js | 58 + packages/gulp-plugin/package.json | 23 + packages/redux-plugin/gulpfile.js | 8 + packages/redux-plugin/package.json | 23 + packages/redux-plugin/src/const.ts | 1 + packages/redux-plugin/src/index.ts | 3 + packages/redux-plugin/src/makeStore.ts | 11 + packages/redux-plugin/src/reduxScript.jsx | 23 + packages/redux-plugin/src/withRedux.jsx | 40 + packages/typescript-plugin/gulpfile.js | 8 + packages/typescript-plugin/package.json | 25 + packages/typescript-plugin/src/index.js | 65 + packages/typescript-plugin/tsconfig.base.json | 38 + tslint.json | 46 + yarn.lock | 8992 +++++++++++++++++ 37 files changed, 10527 insertions(+) create mode 100644 .commitlintrc.js create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .nvmrc create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 ci/publish-stable.sh create mode 100644 ci/travis-checkout-branch.sh create mode 100644 lerna.json create mode 100644 package.json create mode 100644 packages/css-plugin/README.md create mode 100644 packages/css-plugin/gulpfile.js create mode 100644 packages/css-plugin/package.json create mode 100644 packages/css-plugin/pageWithStyles.js create mode 100644 packages/css-plugin/src/StyleContext.js create mode 100644 packages/css-plugin/src/index.js create mode 100644 packages/css-plugin/src/pageWithStyles.js create mode 100644 packages/css-plugin/src/withStyles.js create mode 100644 packages/css-plugin/withStyles.js create mode 100644 packages/gulp-plugin/index.js create mode 100644 packages/gulp-plugin/package.json create mode 100644 packages/redux-plugin/gulpfile.js create mode 100644 packages/redux-plugin/package.json create mode 100644 packages/redux-plugin/src/const.ts create mode 100644 packages/redux-plugin/src/index.ts create mode 100644 packages/redux-plugin/src/makeStore.ts create mode 100644 packages/redux-plugin/src/reduxScript.jsx create mode 100644 packages/redux-plugin/src/withRedux.jsx create mode 100644 packages/typescript-plugin/gulpfile.js create mode 100644 packages/typescript-plugin/package.json create mode 100644 packages/typescript-plugin/src/index.js create mode 100644 packages/typescript-plugin/tsconfig.base.json create mode 100644 tslint.json create mode 100644 yarn.lock diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 0000000..422b194 --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44184e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,77 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# build output +.maleo +lib + +# etc +package-lock.json +.log + +# generated declaration files +*.d.ts +@types + +.vscode + +# ignore yarn lock on example +packages/*/yarn.lock +packages/*/package-lock.json + +# OSX +.DS_Store \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e220aa8 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +development +setup +webpack \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..68d1958 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +10.11.0 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ed11ed6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +node_modules +example +*.json +*.yml +*.md \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..f8e9d22 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,12 @@ +{ + "arrowParens": "always", + "jsxBracketSameLine": true, + "semi": true, + "bracketSpacing": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "printWidth": 100, + "parser": "typescript" +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..396acc6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,37 @@ +sudo: required +git: + quiet: true + +language: node_js +node_js: + - "11" + - "10" + +branches: + only: + - master + +cache: + yarn: true + +before_install: + - npm install --global yarn@1.10.1 + +script: + - yarn build + - yarn test + +before_deploy: + - yarn test:cov + - echo "access=public" >> $HOME/.npmrc 2> /dev/null + - echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" >> $HOME/.npmrc 2> /dev/null + - bash ./ci/travis-checkout-branch.sh + +deploy: + - provider: script + skip_cleanup: true + script: + - echo "Publishing Stable" + on: + branch: master + node: "10" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b5ec6ab --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,262 @@ +# Contributing Guidelines + +Hello 👋! + +Maleo.js is an un-opinionated framework to enable Universal Rendering in JavaScript using React with no hassle. + +We are here to solve the time consuming setups Universal Rendering Required. + +Feel free to contribute to this project. We are grateful for your contributions and we are excited to welcome you abroad! + +Happy contributing 🎉! + +### Setting Up Maleo.js in Local Environment + +**Clone Repo to Local Machine** + +[Fork](https://help.github.com/articles/fork-a-repo/) this repository to your GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local machine from forked repo. + +```bash +$ git clone https://github.com//maleo.js.git +$ cd maleo.js +``` + +Add Maleo.js repo as upstream to keep your fork up to date + +```bash +$ git remote add upstream https://github.com/airyrooms/maleo.js +``` + +Make sure you are currently on branch `canary`, if not you can run this command +```bash +$ git checkout canary +$ git pull upstream canary # sync with Maleo.js repo +``` + +**Setup** + +Make sure you are using the same or higher version of [Node.js](https://nodejs.org/en/) from `.nvmrc` file. (more info about [nvm](https://github.com/creationix/nvm)) + +We are using [Yarn](https://yarnpkg.com/en/) as package manager + +Install yarn as global dependency +```bash +$ npm install --global yarn +``` +Should the install fails, use `sudo` + +And then, install all the dependencies required by Maleo.js + +```bash +$ yarn +``` + +After the installation finished, run `fix:bin` command to fix binary symlink issue +```bash +$ yarn fix:bin +``` + +--- + +***Before making any changes, please check our [issues list](https://github.com/airyrooms/maleo.js/issues), for issues that you want to solve.*** + +Create a new branch based on what kind of contribution you are going to do. + +Here is the draft: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Contributing Type Branch Prefix Description
Fix fix/< name > Fixing current bug or issues
Feature feature/< name > Adding new feature
Optimization optimization/< name > Optimize current unoptimized code
Documentation docs/< name > Add or edit documentation related file
Translation translate/< name > Add or edit documentation's translation
Example example/< name > Add or edit Maleo's examples
+ +For example if you want to contribute to fix bug in Maleo.js you need to create new branch with `fix/` as the prefix. + +For example: +```bash +$ git checkout -b fix/webpack-bug +``` + +--- + +**Development** + +You are now ready to contribute to Maleo.js. Yaay 🤓! + +To build the module you can run this command: +```bash +$ yarn build +``` + +During development we are more likely watch our code changes, therefore we use this command: +```bash +$ yarn watch +``` + +The command above watches for every changes from folder `/packages/plugins` and `/packages/Maleo.js` + +**Test** + +For every new feature you are required to add unit test. + +You can add the unit test on folder `test` with filename having `[feature-name].test.js` prefix. + + +Running test: +```bash +$ yarn test +``` + +**Commit and Push Changes** + +Awesome 🎉! + +You have arrived at this stage, you are almost ready to make your changes available for other people. + +After you have made changes and tested, please don't commit the changes using `$ git commit`, instead use this command: +```bash +$ yarn commit +``` + +The command above will display a wizard for you to fill, it uses our standard commit message. After you have finished filling the answers, we will run [linting](https://stackoverflow.com/questions/8503559/what-is-linting) and `prettify` process to your code and stage those changes to the commit to make sure your code have the same formatting as the other. + +If you have passed all the process above you can now push your changes! 😙 + +```bash +$ git push +``` + +**Making Pull Request** + +YEAH!! 🎉🎉 You are ready to make your changes available for other people + +Your code are now available in your repository, but it's time to make a [Pull Request](https://help.github.com/articles/about-pull-requests/) to Maleo.js + +## Publishing Stable Guide + +--- +**Publishing stable version is only reserved for repo admin: [@alvinkl](https://github.com/alvinkl), [@AndariasSilvanus](https://github.com/AndariasSilvanus). For more info please join our [discord channel](https://discord.gg/9eArCQn)** + +--- + +Make a pull request from Canary to Master and review all the changes stated on the commit messages. +If everything looks OK you can now merge the PR using **Merge Create a Merge Commit** with Commit Message as such. +``` +Ready to Publish Stable [skip ci] +``` +This is required in order to make Master branch keep in sync with Canary branch, because Squash merging will make the history disappear and open up to other push force issue, Rebase also does the same, also we don't want [travis-ci](https://travis-ci.org/airyrooms/maleo.js) to run it's **Sync Master to Canary** Pipeline. + +After done so checkout to the master branch by using this command +```bash +$ git fetch upstream master:master +$ git checkout master +$ git log #to check if the log the same with remote's +``` + +Now in order to publish the stable version, start by running this command +```bash +$ yarn publish:version-stable +``` +The command above runs [lerna version](https://github.com/lerna/lerna/tree/master/commands/version) to let lerna show the package that need to bump it's version for the changes it has. + +--- +***Please review first the [semantic versioning](https://semver.org/), as this will affect all the user that is using this framework*** + +--- + +After everything looks good, you should see lerna doing it's magic bumping the version and make changes on the respective package's `package.json`, do the commit, and do the tagging. + +--- +**DO WITH PRECAUTIONS** + +If you happen to find lerna error during tagging, you can delete the tag on **your remote**, [how to delete all git tags on your local and remote](https://gist.github.com/okunishinishi/9424779), do git reset of the commit to have the same latest commit with remote's master, then redo the publishing again. + +--- + +After Lerna has done it's job, you should see a new commit with message `chore(release): publish stable ...` and also the new git tag with the new version such as `@airy/maleo@1.0.0`. + +Then you can publish this changes to NPM by running this command +```bash +$ bash ./ci/publish-stable.sh +``` + +The command above will run +- `yarn publish:prepublish` this command is to make sure packages are ready for publishing, usually runs package build specific for publishing. +- `yarn publish:stable` this command runs lerna publish and publish it to NPM, this command will add git head revision to the bottom of respective package's `package.json` + +Now you need to add all the git head revision changes to the latest commit by running this command +```bash +$ git add . +$ git commit --amend --no-edit +``` + +After doing all of the above, now you are good to push all this changes to upstream remote by running +```bash +$ git push upstream master +``` + +--- +**NOTICE** + +By pushing those changes to the remote master branch, it will trigger [travis-ci](https://travis-ci.org/airyrooms/maleo.js) pipeline to sync the `master` branch with `canary` branch. +During this process, the pipeline will also publish a new canary version for it and during this process you **must not** merge any PR to make sure the `canary.0` version is clean. + +--- + +## FAQ +
+ How to test Maleo.js on local development machine in the example directory? + Maleo.js is utilizing Lerna and Yarn Workspace to manage the mono repo structure. + So you can use the Maleo.js inside package folder or example folder. Because Yarn Workspace and Lerna has hoisted all the dependencies into root directory. Therefore every app inside example able to add symlinked Maleo.js as dependency. +
+ +
+ +
+ How to test Maleo.js on your own app during development? + You can run this command inside packages/Maleo.js directory +
+  $ yarn link # if you are using yarn on your app
+  $ npm link # if you are using npm on your app
+ And then go to your app directory and add @airy/maleo.js to your own app's package.json and run this command: +
$ yarn link @airy/maleo.js
+ And you are good to go! Maleo.js are now living in your node_modules directory as a symlinked module + + more: + +
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ee89ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Airy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ci/publish-stable.sh b/ci/publish-stable.sh new file mode 100644 index 0000000..fcec80e --- /dev/null +++ b/ci/publish-stable.sh @@ -0,0 +1,7 @@ +echo "Publishing Stable" +echo "Running Prepublish" +yarn publish:prepublish +# publish stable manually +# yarn publish:version-stable +echo "Publishing Stable Version to NPM" +yarn publish:stable diff --git a/ci/travis-checkout-branch.sh b/ci/travis-checkout-branch.sh new file mode 100644 index 0000000..12f0a37 --- /dev/null +++ b/ci/travis-checkout-branch.sh @@ -0,0 +1,44 @@ +# Travis olways runs on detached HEAD mode +# we need to get it attach first + +# Set the user name and email to match the API token holder +# This will make sure the git commits will have the correct photo +# and the user gets the credit for a checkin +git config --global user.email "airyrooms-engineering@users.noreply.github.com" +git config --global user.name "airyrooms-engineering" +git config --global push.default matching + +# Get the credentials from a file +git config credential.helper "store --file=.git/credentials" + +# This associates the API Key with the account +echo "https://${GH_TOKEN}:@github.com" > .git/credentials + +# Make sure that the workspace is clean +# It could be "dirty" if +# 1. yarn.lock is not aligned with package.json +# 2. yarn install is run +git checkout -- . + +# Echo the status to the log so that we can see it is OK +git status + +head_ref=$(git rev-parse HEAD) +if [[ $? -ne 0 || ! $head_ref ]]; then + echo "failed to get HEAD reference" + exit 1 +fi +branch_ref=$(git rev-parse "$TRAVIS_BRANCH") +if [[ $? -ne 0 || ! $branch_ref ]]; then + echo "failed to get $TRAVIS_BRANCH reference" + exit 1 +fi +if [[ $head_ref != $branch_ref ]]; then + echo "HEAD ref ($head_ref) does not match $TRAVIS_BRANCH ref ($branch_ref)" + echo "someone may have pushed new commits before this build cloned the repo" + exit 1 +fi +if ! git checkout "$TRAVIS_BRANCH"; then + echo "failed to checkout $TRAVIS_BRANCH" + exit 1 +fi diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..4cda23f --- /dev/null +++ b/lerna.json @@ -0,0 +1,55 @@ +{ + "lerna": "3.13.1", + "version": "independent", + "packages": [ + "packages/*" + ], + "npmClient": "yarn", + "npmClientArgs": [ + "--no-lockfile" + ], + "useWorkspaces": true, + "publishConfig": { + "access": "public" + }, + "ignoreChanges": [ + "*.md", + "*.test.*", + "**/__test__/**", + "*.yml", + "*.sh" + ], + "command": { + "version": { + "exact": false, + "githubRelease": true, + "noCommitHooks": true, + "conventionalCommits": true, + "allowBranch": [ + "master" + ], + "ignoreChanges": [ + "*.md", + "*.test.*", + "**/__test__/**", + "*.yml", + "*.sh" + ] + }, + "publish": { + "forcePublish": false, + "npmClient": "npm", + "allowBranch": [ + "master" + ], + "registry": "https://registry.npmjs.org/", + "ignoreChanges": [ + "*.md", + "*.test.*", + "**/__test__/**", + "*.yml", + "*.sh" + ] + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..fb29730 --- /dev/null +++ b/package.json @@ -0,0 +1,75 @@ +{ + "name": "airy-maleo-plugins", + "description": "Plugins for @airy/maleo", + "author": "Airy Engineering ", + "license": "MIT", + "private": true, + "homepage": "https://github.com/airyrooms/maleo-plugins#README", + "repository": { + "type": "git", + "url": "git+https://github.com/airyrooms/maleo-plugins.git" + }, + "bugs": { + "url": "https://github.com/airyrooms/maleo-plugins/issues" + }, + "keywords": [ + "airy", + "maleo", + "plugins", + "@airy/maleo", + "javascript", + "node", + "universal" + ], + "workspaces": [ + "packages/*" + ], + "typeScriptVersion": "3.1.1", + "engines": { + "node": ">=10.11.0", + "npm": ">=6.4.1", + "yarn": ">=1.10.1" + }, + "scripts": { + "bootstrap": "lerna bootstrap", + "commit": "git-cz", + "build": "lerna --stream --parallel run build", + "watch": "lerna --stream --parallel --no-bail run watch", + "clean:lib": "find packages -name lib -exec rm -rf {} +", + "clean": "yarn lerna clean -y && rm -rf @types node_modules && yarn clean:lib", + "pretty:fix": "prettier --config-precedence file-override --config .prettierrc --write 'packages/**/**+(.jsx|.tsx|.js|.ts)'", + "publish:prepublish": "lerna run prepublish", + "publish:version-stable": "lerna version --message \"chore(release): publish stable %s\"", + "publish:stable": "lerna publish from-package --no-git-reset --yes" + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged", + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" + } + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "resolutions": { + "babel-core": "7.0.0-bridge.0" + }, + "devDependencies": { + "@commitlint/cli": "^7.3.2", + "@commitlint/config-conventional": "^7.3.1", + "@types/node": "^10.12.0", + "@types/react": "^16.4.18", + "@types/react-dom": "^16.0.9", + "@types/tapable": "^1.0.4", + "@types/webpack": "^4.4.14", + "commitizen": "^3.0.5", + "cz-conventional-changelog": "^2.1.0", + "husky": "^1.3.1", + "lerna": "^3.13.1", + "nodemon": "^1.18.4", + "prettier": "^1.14.3", + "pretty-quick": "^1.10.0" + } +} diff --git a/packages/css-plugin/README.md b/packages/css-plugin/README.md new file mode 100644 index 0000000..c9e9aa9 --- /dev/null +++ b/packages/css-plugin/README.md @@ -0,0 +1,228 @@ +# Maleo CSS Plugin + +CSS support for [Maleo](https://github.com/airyrooms/maleo.js) capable of extracting css to single css file or route based css files, and server side rendering css. + +## Installation +**NPM** +```bash +$ npm install --save @airy/maleo-css-plugin +``` +**Yarn** +```bash +$ yarn add @airy/maleo-css-plugin +``` + +## How To Use + +### Basic Usage +Add maleo css plugin to your `maleo.config.js` file. +```javascript +// maleo.config.js +const cssPlugin = require('@airy/maleo-css-plugin'); + +module.exports = cssPlugin(); +``` + +Then you can import your css files to our custom `Wrap` component and your CSS will be available for all page. + +```css +/* style.css */ +.wrapper { + height: 100%; + width: 100%; + background-color: red; /* why not 😉 */ +} +``` + +```jsx +// _wrap_.jsx +import React from 'react'; +import Wrap from '@airy/maleo/wrap'; + +import './style.css'; + +export default class CustomWrap extends Wrap {} +``` + +Now all your component able to use the CSS you have defined, for example +```jsx +// Component.jsx +import React from 'react'; + +export default class Component extends React.Component { + render() { + // This component will have the .wrapper style we defined above + return ( +
+

Hello World

+
+ ) + } +} +``` + +--- + +### Advanced Usage + +#### With CSS Modules +A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. Maleo-css-plugins uses [css-loader](https://github.com/webpack-contrib/css-loader), and you can configure the `css-loader` at `maleo.config.js` through `cssPluginOptions.cssLoader` options key. + +For example: +```javascript +// maleo.config.js +const cssPlugin = require('@airy/maleo-css-plugin'); + +module.exports = cssPlugin({ + cssPluginOptions: { + cssLoader: { + modules: true, // Add this line + localIdentName: '[path][name]__[local]--[hash:base64:5]', // Optional default: '[hash:base64]' + // other options + // url: true, + // import: true, + // context: undefined, + // hashPrefix: undefined, + // getLocalIdent: undefined, + // sourceMap: false, + // camelCase: false, + // importLoaders: 0, + // exportOnlyLocals: false, + }, + }, +}); +``` + +And now on your code you can import your CSS file as such: +```jsx +// Component.jsx +import React from 'react'; + +import style from './style.css'; + +export default class Component extends React.Component { + render() { + // This component will have the .wrapper style we defined above + return ( +
+

Hello World

+
+ ) + } +} +``` + +--- +More options can be found here [css-loader](https://github.com/webpack-contrib/css-loader#options) + +--- + +#### Enable Isomorphic Style Loader +Isomorphic Style Loader is a CSS style loader for Webpack that is optimized for isomorphic (universal) web apps. [more](https://github.com/kriasoft/isomorphic-style-loader) + +By enabling ISL, you gain server side rendering with style but you can not use this when you enable extract css feature. Since it doesn't make sense to have different ways of styling both during SSR and on CSR. + +To enable ISL: +```javascript +// maleo.config.js +const cssPlugin = require('@airy/maleo-css-plugin'); + +module.exports = cssPlugin({ + enableISL: true, // Add this line + cssPluginOptions: { + cssLoader: { + modules: true, + localIdentName: '[path][name]__[local]--[hash:base64:5]', + }, + }, +}); +``` + +After having done so, you need to add provided Higher Order Component to your custom `wrap`. + +```jsx +// _wrap_.jsx +import React from 'react'; +import Wrap from '@airy/maleo/wrap'; + +import PageWithStyles from '@airy/maleo-css-plugin/pageWithStyles'; // Add this line + +@PageWithStyles // Add this line +export default class CustomWrap extends Wrap {} +``` + +And now you can use your CSS style on your component like this + +```jsx +// Component.jsx +import React from 'react'; +import withStyles from '@airy/maleo-css-plugin/withStyles'; // Add this line + +import style from './style.css'; + +@withStyles(style) // Add this line +export default class Component extends React.Component { + render() { + // This component will have the .wrapper style we defined above + return ( +
+

Hello World

+
+ ) + } +} +``` + +`withStyles` is a Higher Order Component that receives your CSS style and will pass all your style to `PageWithStyles` and render your styling. + +If you opt to enable ISL, you must add `PageWithStyles` to your custom `wrap` and use `withStyles` decorator as such to every of your component that uses your CSS style. + +#### Extract CSS to CSS File +By enabling this feature, it extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS or single file CSS, and SourceMaps. + +Extract CSS feature has already optimized and minify your CSS file and only runs on production environment. + +To enable this feature, you need to enable it on `maleo.config.js` +```javascript +// maleo.config.js +const cssPlugin = require('@airy/maleo-css-plugin'); + +module.exports = cssPlugin({ + extractCss: true, + // you can also pass the option here such as: + // extractCss: { + // Here are the default option + // filename: '[name].css', + // chunkFilename: 'style-chunk-[name].css', + // singleCssFile: false, // if enabled, it will extract all the CSS files into single css file + // publicPath: undefined, // automatically follows webpack's publicPath if set undefined + // cache: true, // will enable long term caching by appending '[contenthash]' to filename and chunkFilename + // }, + cssPluginOptions: { + cssLoader: { + modules: true, + localIdentName: '[path][name]__[local]--[hash:base64:5]', + }, + }, +}); +``` + +Having done so, you can use your CSS on your React Component like so +```jsx +// Component.jsx +import React from 'react'; + +import style from './style.css'; + +export default class Component extends React.Component { + render() { + // This component will have the .wrapper style we defined above + return ( +
+

Hello World

+
+ ) + } +} +``` + diff --git a/packages/css-plugin/gulpfile.js b/packages/css-plugin/gulpfile.js new file mode 100644 index 0000000..98a3bc1 --- /dev/null +++ b/packages/css-plugin/gulpfile.js @@ -0,0 +1,8 @@ +const gulpTaskRunner = require('@airy/maleo-gulp-plugin'); + +const distDir = '.'; +const paths = { + lib: 'src/*.js', +}; + +gulpTaskRunner(paths, distDir); diff --git a/packages/css-plugin/package.json b/packages/css-plugin/package.json new file mode 100644 index 0000000..ddc3394 --- /dev/null +++ b/packages/css-plugin/package.json @@ -0,0 +1,33 @@ +{ + "name": "@airy/maleo-css-plugin", + "version": "0.1.0", + "main": "lib/index.js", + "license": "MIT", + "scripts": { + "build": "rm -rf lib && gulp", + "watch": "rm -rf lib && gulp watch", + "prepublish": "yarn build" + }, + "files": [ + "lib", + "pageWithStyles.js", + "withStyles.js" + ], + "peerDependencies": { + "@airy/maleo": "^0.0.6-canary.0" + }, + "dependencies": { + "chalk": "^2.4.2", + "css-loader": "^1.0.1", + "hoist-non-react-statics": "^3.1.0", + "isomorphic-style-loader": "^4.0.0", + "mini-css-extract-plugin": "^0.5.0", + "optimize-css-assets-webpack-plugin": "^5.0.1", + "style-loader": "^0.23.1" + }, + "devDependencies": { + "@airy/maleo-gulp-plugin": "latest", + "gulp": "gulpjs/gulp.git#4.0" + }, + "gitHead": "a1ac0be142870f4f92a26ad44308f24309fe1859" +} diff --git a/packages/css-plugin/pageWithStyles.js b/packages/css-plugin/pageWithStyles.js new file mode 100644 index 0000000..8e2419f --- /dev/null +++ b/packages/css-plugin/pageWithStyles.js @@ -0,0 +1 @@ +module.exports = require('./lib/pageWithStyles'); diff --git a/packages/css-plugin/src/StyleContext.js b/packages/css-plugin/src/StyleContext.js new file mode 100644 index 0000000..c93306e --- /dev/null +++ b/packages/css-plugin/src/StyleContext.js @@ -0,0 +1,43 @@ +/** + * Isomorphic CSS style loader for Webpack + * + * Copyright © 2015-present Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + * + * https://github.com/kriasoft/isomorphic-style-loader/blob/master/src/StyleContext.js + */ + +import React from 'react'; + +export default () => { + let css = []; + + const StyleContext = React.createContext({ + css, + insertCss: (() => { + // Server side Insert CSS + if (typeof window === 'undefined') { + return (...styles) => { + styles.forEach((style) => { + const cssText = style._getCss(); + if (!~css.indexOf(cssText)) { + css.push(cssText); + } + }); + }; + } + + return (...styles) => { + const removeCss = styles.map((x) => x._insertCss()); + + return () => { + removeCss.forEach((f) => f()); + }; + }; + })(), + }); + + return StyleContext; +}; diff --git a/packages/css-plugin/src/index.js b/packages/css-plugin/src/index.js new file mode 100644 index 0000000..911a381 --- /dev/null +++ b/packages/css-plugin/src/index.js @@ -0,0 +1,158 @@ +const chalk = require('chalk'); + +// Default options for CSS Loader +// https://github.com/webpack-contrib/css-loader#options +const defaultOptions = { + enableISL: false, + extractCss: false, + cssLoader: { + url: true, + import: true, + modules: false, + localIdentName: '[hash:base64]', + context: undefined, + hashPrefix: undefined, + getLocalIdent: undefined, + sourceMap: false, + camelCase: false, + importLoaders: 0, + exportOnlyLocals: false, + }, +}; + +const defaultExtractCssOptions = { + filename: '[name].css', + chunkFilename: 'style-chunk-[name].css', + singleCssFile: false, + publicPath: undefined, + cache: true, +}; + +module.exports = (customConfig = {}) => { + return Object.assign({}, customConfig, { + webpack(config, context, next) { + const { isServer, env } = context; + const isDev = env === 'development'; + + const { cssPluginOptions } = customConfig; + const pluginOptions = { + ...defaultOptions, + ...(cssPluginOptions || {}), + cssLoader: { + ...defaultOptions.cssLoader, + ...(cssPluginOptions.cssLoader || {}), + }, + }; + + // enable ISL only available if user set extract css to false and vice versa + // user has to choose between Isomoprhic style loader or extract css + // since enabling both css in SSR and CSR using text css doesn't make sense + if (pluginOptions.enableISL && pluginOptions.extractCss) { + console.log( + chalk.bgRed('\n\nERROR'), + chalk.red.underline.bold('[@airy/maleo-css-plugin]'), + chalk.red( + '\nCannot enable both Isomorphic Style Loader and Extract CSS options, you have to choose either one!\n\n', + ), + ); + throw new Error(); + } + + const cssLoader = { + loader: require.resolve('css-loader'), + options: pluginOptions.cssLoader, + }; + + const styleLoader = !isServer && { + loader: require.resolve('style-loader'), + }; + + // Extract CSS into css file, optimizes, and minimize it + // Only runs in production environment, due to no HOT reload support yet + // For development we will still use style-loader + if (!isDev && pluginOptions.extractCss) { + const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); + + const { extractCss } = pluginOptions; + const extractCssOptions = + typeof extractCss === 'object' + ? { ...defaultExtractCssOptions, ...extractCss } + : defaultExtractCssOptions; + + // if user opt for caching [default] and they haven't add '[contenthash]' to the filename + // then we automatically append the '[contenthash]' for them + if (extractCssOptions.cache && !~extractCssOptions.filename.indexOf('[contenthash]')) { + extractCssOptions.filename = extractCssOptions.filename.replace( + /\.css$/, + '.[contenthash].css', + ); + extractCssOptions.chunkFilename = extractCssOptions.chunkFilename.replace( + /\.css$/, + '.[contenthash].css', + ); + } + + // minifier for css files + config.optimization = { + ...config.optimization, + minimizer: [...(config.optimization.minimizer || []), new OptimizeCSSAssetsPlugin()], + }; + + if (extractCssOptions.singleCssFile && !isServer) { + config.optimization = { + ...config.optimization, + splitChunks: { + ...(config.optimization.splitChunks || {}), + cacheGroups: { + ...(config.optimization.splitChunks.cacheGroups || {}), + styles: { + name: 'style', + test: /\.css$/, + chunks: 'all', + enforce: true, + }, + }, + }, + }; + } + + // Multiple CSS File utilizes chunk filename for optimization + // While Single CSS File uses filename therefore we need to set chunkFilename to false + !isServer && + config.plugins.push( + new MiniCssExtractPlugin({ + filename: extractCssOptions.filename, + chunkFilename: !extractCssOptions.singleCssFile && extractCssOptions.chunkFilename, + }), + ); + + config.module.rules.push({ + test: /\.css/, + use: [ + !isServer && { + loader: MiniCssExtractPlugin.loader, + options: { + publicPath: extractCssOptions.publicPath, + }, + }, + cssLoader, + ].filter(Boolean), + }); + } else if (pluginOptions.enableISL) { + config.module.rules.push({ + test: /\.css$/, + use: [require.resolve('isomorphic-style-loader'), cssLoader].filter(Boolean), + }); + } else { + // default config + config.module.rules.push({ + test: /\.css$/, + use: [styleLoader, cssLoader], + }); + } + + return next(customConfig); + }, + }); +}; diff --git a/packages/css-plugin/src/pageWithStyles.js b/packages/css-plugin/src/pageWithStyles.js new file mode 100644 index 0000000..b7bdb98 --- /dev/null +++ b/packages/css-plugin/src/pageWithStyles.js @@ -0,0 +1,34 @@ +import React from 'react'; +import hoistStatics from 'hoist-non-react-statics'; + +import generateStyleContext from './StyleContext'; + +export default (WrappedComponent) => { + const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component'; + const StyleContext = generateStyleContext(); + + class PageWithStyles extends React.Component { + static displayName = ``; + + static getInitialProps(context) { + if (typeof WrappedComponent.getInitialProps === 'function') { + return WrappedComponent.getInitialProps.call(WrappedComponent, context); + } + + return context; + } + + render() { + // Only renders style on server + // On client render, style will be moved to head + // then remove style tag here + return ( + + + + ); + } + } + + return hoistStatics(PageWithStyles, WrappedComponent); +}; diff --git a/packages/css-plugin/src/withStyles.js b/packages/css-plugin/src/withStyles.js new file mode 100644 index 0000000..2449a1d --- /dev/null +++ b/packages/css-plugin/src/withStyles.js @@ -0,0 +1,57 @@ +/** + * Isomorphic CSS style loader for Webpack + * + * Copyright © 2015-present Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + * + * https://github.com/kriasoft/isomorphic-style-loader/blob/master/src/withStyles.js + */ + +import React from 'react'; +import hoistStatics from 'hoist-non-react-statics'; + +import generateStyleContext from './StyleContext'; + +function withStyles(...styles) { + return function wrapWithStyles(ComposedComponent) { + const displayName = ComposedComponent.displayName || ComposedComponent.name || 'Component'; + const StyleContext = generateStyleContext(); + + class WithStyles extends React.PureComponent { + static displayName = `WithStyles(${displayName})`; + static contextType = StyleContext; + static ComposedComponent = ComposedComponent; + + constructor(props, context) { + super(props, context); + this.removeCss = context.insertCss(...styles); + } + + componentWillUnmount() { + if (this.removeCss) { + setTimeout(this.removeCss, 0); + } + } + + render() { + const { css } = this.context; + const isServer = typeof window === 'undefined'; + + return ( + + + {isServer && !!css.length && ( +