From 0b58f24e5b8a5e5e6f575a20f6e99ee5d05872ef Mon Sep 17 00:00:00 2001 From: Dan Green Date: Tue, 10 Jul 2018 17:37:22 +0700 Subject: [PATCH] init --- .babelrc | 11 ++ .browserslistrc | 1 + .editorconfig | 16 ++ .env | 1 + .eslintrc | 6 + .gitignore | 45 +++++ .huskyrc | 5 + .postcssrc | 3 + .stylelintrc | 3 + LICENSE | 21 +++ README.md | 43 +++++ jest.config.json | 38 +++++ manifest.json | 13 ++ package.json | 86 ++++++++++ scripts/configs/browserSync.js | 5 + scripts/configs/htmlmin.js | 13 ++ scripts/configs/webpack/index.js | 199 ++++++++++++++++++++++ scripts/configs/webpack/stylableLoader.js | 88 ++++++++++ scripts/configs/webpack/svgLoader.js | 43 +++++ scripts/configs/webpack/swLoader.js | 39 +++++ scripts/helpers/IconComponent.d.ts | 32 ++++ scripts/helpers/IconComponent.js | 110 ++++++++++++ scripts/helpers/addDevScripts.js | 9 + scripts/helpers/applyReducers.js | 7 + scripts/helpers/findIndex.js | 12 ++ scripts/helpers/notify.js | 15 ++ scripts/helpers/svgToComponent.js | 56 ++++++ scripts/startServer.js | 22 +++ scripts/webpackBuild.js | 30 ++++ scripts/webpackDevServer.js | 44 +++++ src/App/App.st.css | 0 src/App/App.ts | 0 src/App/index.ts | 0 src/index.html | 14 ++ tsconfig.json | 23 +++ tslint.json | 3 + 36 files changed, 1056 insertions(+) create mode 100644 .babelrc create mode 100644 .browserslistrc create mode 100644 .editorconfig create mode 100644 .env create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .huskyrc create mode 100644 .postcssrc create mode 100644 .stylelintrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 jest.config.json create mode 100644 manifest.json create mode 100644 package.json create mode 100644 scripts/configs/browserSync.js create mode 100644 scripts/configs/htmlmin.js create mode 100644 scripts/configs/webpack/index.js create mode 100644 scripts/configs/webpack/stylableLoader.js create mode 100644 scripts/configs/webpack/svgLoader.js create mode 100644 scripts/configs/webpack/swLoader.js create mode 100644 scripts/helpers/IconComponent.d.ts create mode 100644 scripts/helpers/IconComponent.js create mode 100644 scripts/helpers/addDevScripts.js create mode 100644 scripts/helpers/applyReducers.js create mode 100644 scripts/helpers/findIndex.js create mode 100644 scripts/helpers/notify.js create mode 100644 scripts/helpers/svgToComponent.js create mode 100644 scripts/startServer.js create mode 100644 scripts/webpackBuild.js create mode 100644 scripts/webpackDevServer.js create mode 100644 src/App/App.st.css create mode 100644 src/App/App.ts create mode 100644 src/App/index.ts create mode 100644 src/index.html create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..69d7456 --- /dev/null +++ b/.babelrc @@ -0,0 +1,11 @@ +{ + "exclude": "node_modules/**", + "presets": [ + ["@babel/preset-env", { "modules": false }], + ["@babel/preset-stage-0", { "decoratorsLegacy": true }], + "@babel/preset-react" + ], + "plugins": [ + "@babel/plugin-transform-runtime" + ] +} diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..5641ecb --- /dev/null +++ b/.browserslistrc @@ -0,0 +1 @@ +extends browserslist-config-trigen diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..defd06d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +indent_style = tab +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[package.json] +indent_style = space +indent_size = 2 + +[*.md] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false diff --git a/.env b/.env new file mode 100644 index 0000000..a80c6e6 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +PROXY_API_URI= \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8d3ded4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "trigen", + "env": { + "browser": true + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58dbcc6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# OS stuff +._* +.DS_Store + +# Some caches +.*cache + +# Compiled dist +lib +package diff --git a/.huskyrc b/.huskyrc new file mode 100644 index 0000000..ebb0bb2 --- /dev/null +++ b/.huskyrc @@ -0,0 +1,5 @@ +{ + "hooks": { + "pre-push": "npm test" + } +} diff --git a/.postcssrc b/.postcssrc new file mode 100644 index 0000000..e5b9db8 --- /dev/null +++ b/.postcssrc @@ -0,0 +1,3 @@ +{ + "plugins": {} +} diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..2906216 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,3 @@ +{ + "extends": "stylelint-config-trigen/scss" +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9ecd6d6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 - present, TrigenSoftware + +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/README.md b/README.md new file mode 100644 index 0000000..bc38125 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ + +# <%= pkg.name %> + +<%= pkg.description %> + +## Basic commands + +Start development server: + +```bash +yarn start # or +npm start +``` + +Build sources for production: + +```bash +yarn build # or +npm run build +``` + +Lint sources: + +```bash +yarn test # or +npm test +```<% if (gulpTasks.includes('storybook')) { %> + +Start storybook: + +```bash +yarn storybook # or +npm run storybook +``` + +Build storybook: + +```bash +yarn build:storybook # or +npm run build:storybook # or +```<% } %> + +> This project generated with [generator-trigen-frontend](https://www.npmjs.com/package/generator-trigen-frontend) diff --git a/jest.config.json b/jest.config.json new file mode 100644 index 0000000..516a7b8 --- /dev/null +++ b/jest.config.json @@ -0,0 +1,38 @@ +{ + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json" + ], + "testRegex": "/test/.*\\.spec\\.ts$", + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "collectCoverage": true, + "collectCoverageFrom": [ + "src/**/*.{ts,tsx}", + "!**/node_modules/**" + ], + "coverageReporters": [ + "lcovonly", + "text" + ], + "globals": { + "ts-jest": { + "babelConfig": { + "presets": [ + ["env", { + "modules": false, + "targets": { "node": 6 } + }], + ["stage-0"] + ], + "plugins": [ + "transform-runtime" + ] + } + } + } +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..ce31f13 --- /dev/null +++ b/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "My App", + "short_name": "My App", + "description": "This is my application", + "dir": "auto", + "lang": "en-US", + "display": "standalone", + "orientation": "portrait", + "scope": "/", + "start_url": "/?homescreen=1", + "background_color": "#42c88a", + "theme_color": "#42c88a" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..535cd16 --- /dev/null +++ b/package.json @@ -0,0 +1,86 @@ +{ + "name": "trigen-frontend-boilerplate", + "version": "0.6.0", + "description": "Boilerplate for frontend development.", + "author": "dangreen", + "license": "MIT", + "private": true, + "repository": { + "type": "git", + "url": "git+https://github.com/TrigenSoftware/flexis-redux.git" + }, + "bugs": { + "url": "https://github.com/TrigenSoftware/flexis-redux/issues" + }, + "scripts": { + "generateDocs": "typedoc ./src --out ./docs --excludeExternals --mode modules; touch docs/.nojekyll", + "checkDeps": "npm-check -s; exit 0", + "lint:styles": "stylelint 'src/**/*.css'", + "lint:scripts": "tslint -p . -t stylish 'src/**/*.{ts,tsx}'", + "lint": "npm run lint:styles && npm run lint:scripts", + "jest": "NODE_ENV=test jest -c jest.config.json", + "test": "npm run checkDeps && npm run lint && npm run jest", + "start": "NODE_ENV=development babel-node scripts/webpackDevServer", + "build": "NODE_ENV=production babel-node scripts/webpackBuild", + "serve": "NODE_ENV=production babel-node scripts/startServer" + }, + "dependencies": { + "@babel/runtime": "^7.0.0-beta.49", + "@types/prop-types": "^15.5.3", + "@types/react": "16.4.2", + "prop-types": "^15.6.0", + "react": "^16.3.1", + "react-dom": "^16.3.1" + }, + "devDependencies": { + "@babel/cli": "^7.0.0-beta.52", + "@babel/core": "^7.0.0-beta.49", + "@babel/plugin-transform-runtime": "^7.0.0-beta.49", + "@babel/preset-env": "^7.0.0-beta.49", + "@babel/preset-react": "^7.0.0-beta.52", + "@babel/preset-stage-0": "^7.0.0-beta.49", + "@types/jest": "^23.1.3", + "autoprefixer": "^8.6.5", + "awesome-typescript-loader": "^4.0.0", + "browserslist-config-trigen": "^1.0.0", + "babel-core": "^6.23.1", + "babel-loader": "^8.0.0-beta", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-minify-webpack-plugin": "^0.2.0", + "babel-preset-env": "^1.1.8", + "babel-preset-react": "^6.23.0", + "babel-preset-stage-0": "^6.22.0", + "browser-sync": "^2.18.13", + "connect-history-api-fallback": "^1.5.0", + "dotenv": "^4.0.0", + "eslint": "^5.0.1", + "eslint-config-trigen": "^3.2.0", + "husky": "^1.0.0-rc.10", + "npm-check": "^5.7.1", + "postcss-reporter": "^5.0.0", + "http-proxy-middleware": "^0.17.4", + "immutability-helper": "^2.1.1", + "node-notifier": "*", + "react-hot-loader": "next", + "stylelint": "^9.3.0", + "stylelint-config-trigen": "^2.0.1", + "webpack": "^3.5.5", + "webpack-dev-middleware": "^1.12.0", + "webpack-hot-middleware": "^2.18.2", + "humps": "^2.0.1", + "loader-utils": "^1.1.0", + "svg-sprite-loader": "^3.2.3", + "svgo": "^0.7.2", + "svgo-loader": "^1.2.1", + "tslint": "^5.10.0", + "tslint-config-trigen": "^3.0.5", + "tslint-loader": "^3.6.0", + "typedoc": "^0.11.1", + "typescript": "2.9.1", + "service-worker-loader": "*", + "stylable-webpack-plugin": "*", + "postcss": "*", + "file-loader": "*", + "html-webpack-plugin": "*" + } +} diff --git a/scripts/configs/browserSync.js b/scripts/configs/browserSync.js new file mode 100644 index 0000000..b1a45b6 --- /dev/null +++ b/scripts/configs/browserSync.js @@ -0,0 +1,5 @@ + +export default { + open: false, + notify: false +}; diff --git a/scripts/configs/htmlmin.js b/scripts/configs/htmlmin.js new file mode 100644 index 0000000..06fad43 --- /dev/null +++ b/scripts/configs/htmlmin.js @@ -0,0 +1,13 @@ + +export default { + removeComments: true, + collapseWhitespace: true, + collapseBooleanAttributes: true, + removeAttributeQuotes: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + minifyJS: true +}; diff --git a/scripts/configs/webpack/index.js b/scripts/configs/webpack/index.js new file mode 100644 index 0000000..61cf322 --- /dev/null +++ b/scripts/configs/webpack/index.js @@ -0,0 +1,199 @@ +import path from 'path'; +import webpack from 'webpack'; +import HtmlPlugin from 'html-webpack-plugin'; +import BabelMinifyPlugin from 'babel-minify-webpack-plugin'; +import update from 'immutability-helper'; +import { decamelize } from 'humps'; +import findIndex from '../../helpers/find-index'; +import applyReducers from '../../helpers/applyReducers'; +import addDevScripts from '../../helpers/addDevScripts'; +import babelrc from '../../../.babelrc'; +import htmlminConfig from '../htmlmin'; +import * as stylableLoader from './stylableLoader'; +import * as svgLoader from './svg-loader'; +import * as swLoader from './sw-loader'; + +const cwd = process.cwd(); +const loaders = [ + stylableLoader, + svgLoader, + swLoader +]; +const baseLoaders = loaders.map(_ => _.base); +const devLoaders = loaders.map(_ => _.dev); +const buildLoaders = loaders.map(_ => _.build); +const babelOptions = { + ...babelrc, + babelrc: false +}; + +function base({ + envify = {} +}) { + return applyReducers(baseLoaders, { + entry: { + main: path.join(cwd, 'src/App/index.ts') + }, + output: { + path: path.join(cwd, 'build', 'app'), + filename: '[name].js', + chunkFilename: '[name].js', + hashDigestLength: 10, + publicPath: '/app/' + }, + resolve: { + alias: { + '~': path.join(cwd, 'src/App') + } + }, + module: { + rules: [{ + test: /\.js$/, + parser: { + amd: false + } + }, { + test: /\.js$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: babelOptions + }, + 'eslint-loader' + ] + }, { + test: /\.tsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'awesome-typescript-loader', + options: { + forceIsolatedModules: true, + useCache: true, + reportFiles: [ + 'src/**/*.{ts,tsx}', + '!src/globals.d.ts' + ], + useBabel: true, + babelCore: '@babel/core', + babelOptions + } + }, + { + loader: 'tslint-loader', + options: { + configFile: './tsconfig.json' + } + } + ] + }] + }, + plugins: [ + new webpack.EnvironmentPlugin(Object.keys(process.env)), + new webpack.DefinePlugin( + Object.entries(envify).reduce((env, [key, value]) => ({ + ...env, + [`process.env.${decamelize(key).toUpperCase()}`]: JSON.stringify(value) + }), {}) + ) + ] + }); +} + +export function dev(params) { + + const config = base(params); + const { rules } = config.module; + const devScripts = [ + 'webpack-hot-middleware/client?http://localhost:3000/&reload=true' + ]; + + return applyReducers(devLoaders, update(config, { + entry: { $apply: entry => addDevScripts(entry, devScripts) }, + mode: { $set: 'development' }, + module: { + rules: { + [findIndex('test', '/\\.js$/', rules)]: { + use: { 0: { + options: { + plugins: { $push: [ + 'react-hot-loader/babel' + ] } + } + } } + }, + [findIndex('test', '/\\.tsx?$/', rules)]: { + use: { 0: { + options: { babelOptions: { + plugins: { $push: [ + 'react-hot-loader/babel' + ] } + } } + } } + } + } + }, + optimization: { $set: { + noEmitOnErrors: true + } }, + plugins: { $push: [ + new webpack.HotModuleReplacementPlugin(), + new HtmlPlugin({ + template: 'src/index.html', + inject: 'head' + }) + ] } + })); +} + +export function build(params) { + + const config = base(params); + + return applyReducers(buildLoaders, update(config, { + output: { + filename: { $set: '[name].[chunkhash].js' }, + chunkFilename: { $set: '[name].[chunkhash].js' } + }, + mode: { $set: 'production' }, + optimization: { $set: { + runtimeChunk: 'single', + splitChunks: { + name: true, + cacheGroups: { + vendor: { + priority: -10, + test(chunk) { + + // if (module.resource && !/\.js$/.test(module.resource)) { + // return false; + // } + + // return module.context + // && module.context.includes('node_modules') + // && !module.context.includes('@flexis/ui/components'); // sad hack + + console.log(chunk); + return true; + } + }, + default: { + priority: -20, + minChunks: 2, + reuseExistingChunk: true + } + } + } + } }, + plugins: { $push: [ + new webpack.HashedModuleIdsPlugin(), + new BabelMinifyPlugin(), + new HtmlPlugin({ + template: 'src/index.html', + inject: 'head', + minify: htmlminConfig + }) + ] } + })); +} diff --git a/scripts/configs/webpack/stylableLoader.js b/scripts/configs/webpack/stylableLoader.js new file mode 100644 index 0000000..62b4e2b --- /dev/null +++ b/scripts/configs/webpack/stylableLoader.js @@ -0,0 +1,88 @@ +import StylablePlugin from 'stylable-webpack-plugin'; +import postcss from 'postcss'; +import stylelint from 'stylelint'; +import postcssReporter from 'postcss-reporter'; +import autoprefixer from 'autoprefixer'; +import update from 'immutability-helper'; +import findIndex from '../../helpers/find-index'; + +export function base(config) { + return update(config, { + module: { + rules: { $push: [{ + test: /\.(eot|woff|ttf|jpg|webp|png|gif)$/, + loader: 'file-loader', + options: { + name: '[name].[hash:10].[ext]' + } + }] } + } + }); +} + +export function dev(config) { + + const postProcessor = postcss([ + stylelint(), + postcssReporter({ clearReportedMessages: true }), + autoprefixer() + ]); + const stylablePlugin = new StylablePlugin({ + rootScope: false, + transformHooks: { + postProcessor(stylableResult) { + postProcessor.process(stylableResult.meta.outputAst).sync(); + return stylableResult; + } + } + }); + + return update(config, { + plugins: { $push: [stylablePlugin] } + }); +} + +export function build(config) { + + const postProcessor = postcss([ + stylelint(), + postcssReporter({ + clearReportedMessages: true, + throwError: true + }), + autoprefixer() + ]); + const stylablePlugin = new StylablePlugin({ + filename: '[name].[chunkhash].css', + rootScope: false, + outputCSS: true, + includeCSSInJS: false, + transformHooks: { + postProcessor(stylableResult) { + postProcessor.process(stylableResult.meta.outputAst).sync(); + return stylableResult; + } + }, + optimize: { + removeUnusedComponents: true, + removeComments: true, + removeStylableDirectives: true, + classNameOptimizations: true, + shortNamespaces: true, + minify: true + } + }); + + return update(config, { + module: { + rules: { + [findIndex('loader', 'file-loader', config.module.rules)]: { + options: { + name: { $set: '[name].[hash:10].[ext]' } + } + } + } + }, + plugins: { $push: [stylablePlugin] } + }); +} diff --git a/scripts/configs/webpack/svgLoader.js b/scripts/configs/webpack/svgLoader.js new file mode 100644 index 0000000..49717ef --- /dev/null +++ b/scripts/configs/webpack/svgLoader.js @@ -0,0 +1,43 @@ +import path from 'path'; +import update from 'immutability-helper'; +import findIndex from '../../helpers/findIndex'; + +const cwd = process.cwd(); + +export function base(config) { + return update(config, { + module: { + rules: { $push: [{ + test: /\.svg$/, + use: [{ + loader: 'svg-sprite-loader', + options: { + runtimeGenerator: path.join(cwd, 'scripts/helpers/svgToComponent.js'), + runtimeOptions: { + iconModule: path.join(cwd, 'scripts/helpers/IconComponent.js') + } + } + }] + }] } + } + }); +} + +export function dev(config) { + return config; +} + +export function build(config) { + + const { rules } = config.module; + + return update(config, { + module: { + rules: { + [findIndex('test', '/\\.svg$/', rules)]: { + use: { $push: ['svgo-loader'] } + } + } + } + }); +} diff --git a/scripts/configs/webpack/swLoader.js b/scripts/configs/webpack/swLoader.js new file mode 100644 index 0000000..bf43dad --- /dev/null +++ b/scripts/configs/webpack/swLoader.js @@ -0,0 +1,39 @@ +import path from 'path'; +import update from 'immutability-helper'; +import findIndex from '../../helpers/findIndex'; + +const cwd = process.cwd(); + +export function base(config) { + return update(config, { + module: { + rules: { $push: [{ + test: /\/sw\.js$/, + exclude: /node_modules/, + loader: 'service-worker-loader', + options: { + filename: '[name].js', + outputPath: path.join(cwd, 'build') + } + }] } + } + }); +} + +export function dev(config) { + return config; +} + +export function build(config) { + return update(config, { + module: { + rules: { + [findIndex('loader', 'service-worker-loader', config.module.rules)]: { + options: { + filename: { $set: '[name].[chunkhash].js' } + } + } + } + } + }); +} diff --git a/scripts/helpers/IconComponent.d.ts b/scripts/helpers/IconComponent.d.ts new file mode 100644 index 0000000..91d5740 --- /dev/null +++ b/scripts/helpers/IconComponent.d.ts @@ -0,0 +1,32 @@ +import { + AllHTMLAttributes, + PureComponent +} from 'react'; +import PropTypes from 'prop-types'; + +interface ISelfProps { + glyph?: boolean; + width?: number; + height?: number; +} + +export declare type IProps = ISelfProps & AllHTMLAttributes; + +export declare function setIconClassName(className: string); + +export default class Icon extends PureComponent { + + static propTypes: { + glyph: PropTypes.Requireable; + width: PropTypes.Requireable; + height: PropTypes.Requireable; + }; + + static defaultProps: { + glyph: boolean; + width: number; + height: number; + }; + + render(): JSX.Element; +} diff --git a/scripts/helpers/IconComponent.js b/scripts/helpers/IconComponent.js new file mode 100644 index 0000000..ad6ab61 --- /dev/null +++ b/scripts/helpers/IconComponent.js @@ -0,0 +1,110 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; + +const headBase = typeof document != 'undefined' + ? document.querySelector('head > base') + : null; +const shoudlPrepandPathname = headBase && headBase.hasAttribute('href'); +let iconClassName = null; + +export function setIconClassName(className) { + iconClassName = className; +} + +export default class Icon extends PureComponent { + + static propTypes = { + className: PropTypes.string, + glyph: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number + }; + + static defaultProps = { + className: undefined, + glyph: '', + width: undefined, + height: undefined + }; + + hrefListenerRemover = null; + + render() { + + const { + className, + glyph, + width, + height, + ...props + } = this.props; + + return ( + + + + ); + } + + componentDidMount() { + + if (shoudlPrepandPathname) { + this.hrefListenerRemover = addHrefListener(() => { + this.forceUpdate(); + }); + } + } + + componentWillUnmount() { + + const { hrefListenerRemover } = this; + + if (shoudlPrepandPathname + && typeof hrefListenerRemover == 'function' + ) { + hrefListenerRemover(); + } + } + + // https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2 + getPathname() { + + if (shoudlPrepandPathname) { + return `${location.pathname}${location.search}`; + } + + return ''; + } +} + +const hrefListeners = []; + +function addHrefListener(listener) { + hrefListeners.push(listener); + return hrefListeners.splice.bind( + hrefListeners, + hrefListeners.indexOf(listener), + 1 + ); +} + +let prevHref = location.href; + +setInterval(() => { + + if (prevHref != location.href) { + prevHref = location.href; + hrefListeners.forEach((listener) => { + listener(); + }); + } + +}, 1500); diff --git a/scripts/helpers/addDevScripts.js b/scripts/helpers/addDevScripts.js new file mode 100644 index 0000000..1c851cc --- /dev/null +++ b/scripts/helpers/addDevScripts.js @@ -0,0 +1,9 @@ + +export default function addDevScripts(entries, devScripts) { + return Object.entries(entries).reduce((entry, [name, src]) => ({ + ...entry, + [name]: Array.isArray(src) + ? [...devScripts, ...src] + : [...devScripts, src] + }), {}); +} diff --git a/scripts/helpers/applyReducers.js b/scripts/helpers/applyReducers.js new file mode 100644 index 0000000..a0332e5 --- /dev/null +++ b/scripts/helpers/applyReducers.js @@ -0,0 +1,7 @@ + +export default function applyReducers(reducers, object) { + return reducers.reduce( + (object, reducer) => reducer(object), + object + ); +} diff --git a/scripts/helpers/findIndex.js b/scripts/helpers/findIndex.js new file mode 100644 index 0000000..384e6c0 --- /dev/null +++ b/scripts/helpers/findIndex.js @@ -0,0 +1,12 @@ + +export default function findIndex(key, value, array) { + + for (const index in array) { + + if (String(array[index][key]) == value) { + return index; + } + } + + return -1; +} diff --git a/scripts/helpers/notify.js b/scripts/helpers/notify.js new file mode 100644 index 0000000..683128e --- /dev/null +++ b/scripts/helpers/notify.js @@ -0,0 +1,15 @@ +import notifier from 'node-notifier'; + +export function notify(message) { + notifier.notify({ + message, + sound: 'Glass' + }); +} + +export function notifyError(error) { + notifier.notify({ + message: `Error: ${error.message}`, + sound: 'Frog' + }); +} diff --git a/scripts/helpers/svgToComponent.js b/scripts/helpers/svgToComponent.js new file mode 100644 index 0000000..0a57915 --- /dev/null +++ b/scripts/helpers/svgToComponent.js @@ -0,0 +1,56 @@ +import path from 'path'; +import { + stringifySymbol, + stringify, + generateImport, + generateExport +} from 'svg-sprite-loader/lib/utils'; +import { stringifyRequest } from 'loader-utils'; +import { pascalize } from 'humps'; + +module.exports = runtimeGenerator; + +function runtimeGenerator({ + symbol, config, + context, loaderContext +}) { + + const { + spriteModule, + symbolModule, + runtimeOptions, + esModule + } = config; + const compilerContext = loaderContext._compiler.context; + const iconModulePath = path.resolve(compilerContext, runtimeOptions.iconModule); + const iconModuleRequest = stringify( + path.relative(path.dirname(symbol.request.file), iconModulePath) + ); + const spriteRequest = stringifyRequest({ context }, spriteModule); + const symbolRequest = stringifyRequest({ context }, symbolModule); + const displayName = `Icon${pascalize(symbol.id)}`; + + return ` + ${generateImport('React', 'react', esModule)} + ${generateImport('SpriteSymbol', symbolRequest, esModule)} + ${generateImport('sprite', spriteRequest, esModule)} + ${generateImport(esModule ? 'Icon' : '{ default: Icon }', iconModuleRequest, esModule)} + + var symbol = new SpriteSymbol(${stringifySymbol(symbol)}); + sprite.add(symbol); + + function ${displayName}() { + Reflect.apply(Icon, this, arguments); + } + + ${displayName}.prototype = Object.create(Icon.prototype); + + ${displayName}.defaultProps = Object.assign( + {}, + Icon.defaultProps, + { glyph: '${symbol.id}' } + ); + + ${generateExport(displayName, esModule)} + `; +} diff --git a/scripts/startServer.js b/scripts/startServer.js new file mode 100644 index 0000000..e1a531a --- /dev/null +++ b/scripts/startServer.js @@ -0,0 +1,22 @@ +import 'dotenv/config'; +import { create } from 'browser-sync'; +import HttpProxyMiddleware from 'http-proxy-middleware'; +import HistoryApiFallbackMiddleware from 'connect-history-api-fallback'; +import notify from './helpers/notify'; +import paths from './configs/paths'; +import browserSyncConfigBase from './configs/browserSync'; + +const server = create(); +const middleware = [ + process.env.PROXY_SERVER_URI && HttpProxyMiddleware(process.env.PROXY_SERVER_URI), + HistoryApiFallbackMiddleware() +].filter(Boolean); +const browserSyncConfig = { + ...browserSyncConfigBase, + server: paths.build.rootDir, + middleware +}; + +server.init(browserSyncConfig, () => { + notify('Server is working...', true); +}); diff --git a/scripts/webpackBuild.js b/scripts/webpackBuild.js new file mode 100644 index 0000000..3cf29ed --- /dev/null +++ b/scripts/webpackBuild.js @@ -0,0 +1,30 @@ +import 'dotenv/config'; +import webpack from 'webpack'; +import { + notify, + notifyError +} from './helpers/notify'; +import * as webpackConfig from './configs/webpack'; + +const webpackBuildCompiler = webpack(webpackConfig.build()); + +webpackBuildCompiler.run((error, stats) => { + + if (error) { + notifyError(error); + return; + } + + if (stats.hasErrors()) { + notifyError(new Error('Compilation has failed.')); + } else { + notify('Compilation was done.'); + } + + process.stdout.write(`${stats.toString({ + chunks: false, + children: false, + modules: false, + colors: true + })}\n`); +}); diff --git a/scripts/webpackDevServer.js b/scripts/webpackDevServer.js new file mode 100644 index 0000000..c3a865e --- /dev/null +++ b/scripts/webpackDevServer.js @@ -0,0 +1,44 @@ +import 'dotenv/config'; +import webpack from 'webpack'; +import { create } from 'browser-sync'; +import WebpackDevMiddleware from 'webpack-dev-middleware'; +import WebpackHotMiddleware from 'webpack-hot-middleware'; +import HttpProxyMiddleware from 'http-proxy-middleware'; +import HistoryApiFallbackMiddleware from 'connect-history-api-fallback'; +import { notify } from './helpers/notify'; +import browserSyncConfigBase from './configs/browserSync'; +import * as webpackConfig from './configs/webpack'; + +const server = create(); +const webpackDevCompiler = webpack(webpackConfig.dev()); + +webpackDevCompiler.plugin('done', () => { + notify('Recompilation was done.'); +}); + +// Listen Error + +const middleware = [ + WebpackDevMiddleware(webpackDevCompiler, { + publicPath: webpackDevCompiler.options.output.publicPath, + stats: { + chunks: false, + children: false, + modules: false, + colors: true + } + }), + WebpackHotMiddleware(webpackDevCompiler, { + reload: true + }), + process.env.PROXY_API_URI && HttpProxyMiddleware(process.env.PROXY_API_URI), + HistoryApiFallbackMiddleware() +].filter(Boolean); +const browserSyncWebpackOptions = { + ...browserSyncConfigBase, + middleware +}; + +server.init(browserSyncWebpackOptions, () => { + notify('Webpack dev server is working...'); +}); diff --git a/src/App/App.st.css b/src/App/App.st.css new file mode 100644 index 0000000..e69de29 diff --git a/src/App/App.ts b/src/App/App.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/App/index.ts b/src/App/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..abf4184 --- /dev/null +++ b/src/index.html @@ -0,0 +1,14 @@ + + + + + + + + Ttitle + + + +
+ + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9f93f40 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "declaration": false, + "declarationMap": false, + "module": "esnext", + "moduleResolution": "node", + "target": "esnext", + "jsx": "react", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitAny": false, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "inlineSourceMap": false, + "lib": [ + "dom", + "esnext" + ] + }, + "exclude": [ + "build/**/*" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..8109646 --- /dev/null +++ b/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "tslint-config-trigen" +}