mirror of
https://github.com/fergalmoran/onearmy-community-platform.git
synced 2025-12-22 09:37:54 +00:00
revert commit a1439eb276
This commit is contained in:
6
.eslintignore
Normal file
6
.eslintignore
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
functions
|
||||
functions-cron
|
||||
build
|
||||
cypress
|
||||
lib
|
||||
11
.eslintrc
11
.eslintrc
@@ -1,5 +1,6 @@
|
||||
// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
|
||||
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"extends": [
|
||||
"plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
|
||||
@@ -12,14 +13,6 @@
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"modules": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "warn",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
asset-manifest.json,1537949860342,0d87381706f5f063fe1f904b9b48417707c24b21a182d3ee99fa89aa32eb7916
|
||||
index.html,1537949860341,0e551be718bd77c91037eea185f2f2cf153d96a0d895d82228ba17f17a885192
|
||||
manifest.json,1537949850681,65d4aa66cd0b8963e88196705111adfccad2e8b80725b6a7a82ca8a9a0de8889
|
||||
favicon.ico,1537949850677,eae62e993eb980ec8a25058c39d5a51feab118bd2100c4deebb2a9c158ec11f9
|
||||
service-worker.js,1537949860446,db8526deabb1a10c3ad5db7712a840a9e585839d6f1d924a9392fb731d3b0dab
|
||||
static/css/main.29266132.css.map,1537949860350,3d32ace435910b3df426d546fd9fe5c6403cf4581bc6c81785bfce6c569cc62d
|
||||
static/css/main.29266132.css,1537949860350,0d737eb0d4b0dc1f4aea758db2c583db32d7497cc58bde8bb1ee92df806392fd
|
||||
static/media/logo.5d5d9eef.svg,1537949860349,c0528de9db7d849577b204745e2df806c06fc14e27faf121c417111680b4280d
|
||||
static/js/main.3248e625.js,1537949860349,550f3079b50bdde4f0151fc53edddf6b2a2852142e7c540b2f83c61c0c753dfb
|
||||
static/js/main.3248e625.js.map,1537949860351,b0cd5913da52648efa5541fff343702ebd10b058a621a6f76325eaddb837d00a
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,6 +29,7 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.vscode
|
||||
.firebase/
|
||||
|
||||
# IntelliJ
|
||||
/.idea
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"linters": {
|
||||
"src/**/*.{ts,tsx}": [
|
||||
"tslint --project tsconfig.json --config tslint.json"
|
||||
],
|
||||
"cypress/**/*.{ts,tsx}": [
|
||||
"tslint --project cypress/tsconfig.json --config tslint.json"
|
||||
],
|
||||
"*.{js,jsx}": [
|
||||
"tslint"
|
||||
],
|
||||
"*.+(js|jsx|json|yml|yaml|css|less|scss|ts|tsx|md|graphql|mdx)": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"ignore": ["functions/**",".storybook/**",config/**]
|
||||
}
|
||||
@@ -82,15 +82,15 @@ jobs:
|
||||
- CI_NODE=1
|
||||
<<: *_test_defaults
|
||||
# Note, Travis jobs combined with regular stage scripts below
|
||||
# Deploy - Firebase deployment to dev/prod depending on branch
|
||||
# Deploy - Firebase deployment to dev/prod depending on branch on push (not PR)
|
||||
- stage: Deploy Dev
|
||||
if: branch = master
|
||||
if: branch = master AND type = push
|
||||
script:
|
||||
# Note - syntax different from above as merging into array instead of creating key:value
|
||||
- *_predeploy_defaults
|
||||
- bash scripts/deploy.dev.sh
|
||||
- stage: Deploy Production
|
||||
if: branch = production
|
||||
if: branch = production AND type = push
|
||||
script:
|
||||
- *_predeploy_defaults
|
||||
- bash scripts/deploy.prod.sh
|
||||
|
||||
13
UPGRADING.md
13
UPGRADING.md
@@ -1,13 +0,0 @@
|
||||
The app has been ejected from create-react-app which makes upgrading more difficult.
|
||||
|
||||
The current method consists of:
|
||||
|
||||
1. Create a new project with create react app, making sure `--typescript` flag specified
|
||||
2. Manually compare config/\*, package.json, tsconfig for changes
|
||||
3. Update accordingly
|
||||
4. Test
|
||||
5. Fix
|
||||
|
||||
Note, currently there should be no explicit changes to
|
||||
|
||||
http://russelljanderson.com/updating-create-react-app/
|
||||
86
config-overrides.js
Normal file
86
config-overrides.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Custom overrides for default create-react-app build
|
||||
Automatically read by react-app-rewired scripts
|
||||
|
||||
Overrides:
|
||||
- Add custom service worker behaviour
|
||||
*/
|
||||
//eslint-disable-next-line
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
|
||||
module.exports = function override(config, env) {
|
||||
config.plugins = config.plugins.map(plugin => {
|
||||
if (plugin.constructor.name === 'GenerateSW') {
|
||||
return swPlugin()
|
||||
}
|
||||
return plugin
|
||||
})
|
||||
return config
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom service worker plugin
|
||||
*/
|
||||
function swPlugin() {
|
||||
return new WorkboxWebpackPlugin.GenerateSW({
|
||||
cacheId: 'oa',
|
||||
clientsClaim: true,
|
||||
skipWaiting: true,
|
||||
// NOTE 2020-01-14 CC - Add support to cache firebase storage and map tiles
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: new RegExp(/.*\.(?:png|gif|jpg|jpeg|webp|svg).*/gi),
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'oa-images',
|
||||
fetchOptions: {
|
||||
credentials: 'same-origin',
|
||||
mode: 'cors',
|
||||
},
|
||||
backgroundSync: {
|
||||
name: 'oa-images-background',
|
||||
options: {
|
||||
maxRetentionTime: 60 * 60 * 24,
|
||||
},
|
||||
},
|
||||
expiration: {
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
maxEntries: 1000,
|
||||
},
|
||||
matchOptions: {
|
||||
ignoreSearch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: new RegExp('^https://firebasestorage\\.googleapis\\.com/'),
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'oa-other',
|
||||
fetchOptions: {
|
||||
credentials: 'same-origin',
|
||||
mode: 'cors',
|
||||
},
|
||||
matchOptions: {
|
||||
ignoreSearch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: new RegExp('^https://firestore\\.googleapis\\.com/'),
|
||||
handler: 'NetworkOnly',
|
||||
},
|
||||
],
|
||||
// end update 2020-01-14
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: '/index.html',
|
||||
navigateFallbackWhitelist: [/^(?!\/__)/],
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude URLs containing a dot, as they're likely a resource in
|
||||
// public/ and not a SPA route
|
||||
new RegExp('/[^/]+\\.[^/]+$'),
|
||||
],
|
||||
})
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const paths = require('./paths')
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')]
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.',
|
||||
)
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
var dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean)
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd())
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter)
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in Webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key]
|
||||
return env
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
},
|
||||
)
|
||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key])
|
||||
return env
|
||||
}, {}),
|
||||
}
|
||||
|
||||
return { raw, stringified }
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment
|
||||
@@ -1,4 +0,0 @@
|
||||
import enzyme from 'enzyme'
|
||||
import Adapter from 'enzyme-adapter-react-16'
|
||||
|
||||
enzyme.configure({ adapter: new Adapter() })
|
||||
@@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
},
|
||||
getCacheKey() {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
},
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
const assetFilename = JSON.stringify(path.basename(filename));
|
||||
|
||||
if (filename.match(/\.svg$/)) {
|
||||
return `module.exports = {
|
||||
__esModule: true,
|
||||
default: ${assetFilename},
|
||||
ReactComponent: (props) => ({
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
type: 'svg',
|
||||
ref: null,
|
||||
key: null,
|
||||
props: Object.assign({}, props, {
|
||||
children: ${assetFilename}
|
||||
})
|
||||
}),
|
||||
};`;
|
||||
}
|
||||
|
||||
return `module.exports = ${assetFilename};`;
|
||||
},
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
'use strict';
|
||||
|
||||
const tsJestPreprocessor = require('ts-jest/preprocessor');
|
||||
|
||||
module.exports = tsJestPreprocessor;
|
||||
@@ -1,89 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
const envPublicUrl = process.env.PUBLIC_URL;
|
||||
|
||||
function ensureSlash(inputPath, needsSlash) {
|
||||
const hasSlash = inputPath.endsWith('/');
|
||||
if (hasSlash && !needsSlash) {
|
||||
return inputPath.substr(0, inputPath.length - 1);
|
||||
} else if (!hasSlash && needsSlash) {
|
||||
return `${inputPath}/`;
|
||||
} else {
|
||||
return inputPath;
|
||||
}
|
||||
}
|
||||
|
||||
const getPublicUrl = appPackageJson =>
|
||||
envPublicUrl || require(appPackageJson).homepage;
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
function getServedPath(appPackageJson) {
|
||||
const publicUrl = getPublicUrl(appPackageJson);
|
||||
const servedUrl =
|
||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
|
||||
return ensureSlash(servedUrl, true);
|
||||
}
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appPath: resolveApp('.'),
|
||||
appBuild: resolveApp('build'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
||||
servedPath: getServedPath(resolveApp('package.json')),
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
||||
@@ -1,22 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
if (typeof Promise === 'undefined') {
|
||||
// Rejection tracking prevents a common issue where React gets into an
|
||||
// inconsistent state due to an error, but it gets swallowed by a Promise,
|
||||
// and the user has no idea what causes React's erratic future behavior.
|
||||
require('promise/lib/rejection-tracking').enable();
|
||||
window.Promise = require('promise/lib/es6-extensions.js');
|
||||
}
|
||||
|
||||
// fetch() polyfill for making API calls.
|
||||
require('whatwg-fetch');
|
||||
|
||||
// Object.assign() is commonly used with React.
|
||||
// It will use the native implementation if it's present and isn't buggy.
|
||||
Object.assign = require('object-assign');
|
||||
|
||||
// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
|
||||
// We don't polyfill it in the browser--this is user's responsibility.
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
require('raf').polyfill(global);
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const autoprefixer = require("autoprefixer");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin");
|
||||
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin");
|
||||
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin");
|
||||
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin");
|
||||
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
||||
const getClientEnvironment = require("./env");
|
||||
const paths = require("./paths");
|
||||
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = "/";
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
|
||||
const publicUrl = "";
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// This is the development configuration.
|
||||
// It is focused on developer experience and fast rebuilds.
|
||||
// The production configuration is different and lives in a separate file.
|
||||
module.exports = {
|
||||
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
|
||||
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
|
||||
devtool: "cheap-module-source-map",
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
|
||||
entry: [
|
||||
// We ship a few polyfills by default:
|
||||
require.resolve("./polyfills"),
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
require.resolve("react-dev-utils/webpackHotDevClient"),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
],
|
||||
output: {
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: true,
|
||||
// This does not produce a real file. It's just the virtual path that is
|
||||
// served by WebpackDevServer in development. This is the JS bundle
|
||||
// containing code from all our entry points, and the Webpack runtime.
|
||||
filename: "static/js/bundle.js",
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: "static/js/[name].chunk.js",
|
||||
// This is the URL that app is served from. We use "/" in development.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ["node_modules", paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: [
|
||||
".mjs",
|
||||
".web.ts",
|
||||
".ts",
|
||||
".web.tsx",
|
||||
".tsx",
|
||||
".web.js",
|
||||
".js",
|
||||
".json",
|
||||
".web.jsx",
|
||||
".jsx"
|
||||
],
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
"react-native": "react-native-web"
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
new TsconfigPathsPlugin({ configFile: paths.appTsConfig })
|
||||
]
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
loader: require.resolve("source-map-loader"),
|
||||
enforce: "pre",
|
||||
include: paths.appSrc
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve("url-loader"),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: "static/media/[name].[hash:8].[ext]"
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve("babel-loader"),
|
||||
options: {
|
||||
compact: true
|
||||
}
|
||||
},
|
||||
|
||||
// Compile .tsx?
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve("ts-loader"),
|
||||
options: {
|
||||
// disable type checker - we will use it in fork plugin
|
||||
transpileOnly: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use a plugin to extract that CSS to a file, but
|
||||
// in development "style" loader enables hot editing of CSS.
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
require.resolve("style-loader"),
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: require.resolve("postcss-loader"),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: "postcss",
|
||||
plugins: () => [
|
||||
require("postcss-flexbugs-fixes"),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
">1%",
|
||||
"last 4 versions",
|
||||
"Firefox ESR",
|
||||
"not ie < 9" // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: "no-2009"
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
//--------------- *** CUSTOM Add SCSS Loaders ----------------------//
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
require.resolve("style-loader"),
|
||||
require.resolve("css-loader"),
|
||||
require.resolve("sass-loader")
|
||||
]
|
||||
},
|
||||
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
// ------------ *** CUSTOM added .scss to exclude
|
||||
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/, /\.scss$/],
|
||||
loader: require.resolve("file-loader"),
|
||||
options: {
|
||||
name: "static/media/[name].[hash:8].[ext]"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml
|
||||
}),
|
||||
// Add module names to factory functions so they appear in browser profiler.
|
||||
new webpack.NamedModulesPlugin(),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/240
|
||||
new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebookincubator/create-react-app/issues/186
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Perform type checking and linting in a separate process to speed up compilation
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
async: false,
|
||||
watch: paths.appSrc,
|
||||
tsconfig: paths.appTsConfig,
|
||||
tslint: paths.appTsLint
|
||||
})
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: "empty",
|
||||
fs: "empty",
|
||||
net: "empty",
|
||||
tls: "empty",
|
||||
child_process: "empty"
|
||||
},
|
||||
// Turn off performance hints during development because we don't do any
|
||||
// splitting or minification in interest of speed. These warnings become
|
||||
// cumbersome.
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
};
|
||||
@@ -1,668 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const resolve = require('resolve')
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
|
||||
const TerserPlugin = require('terser-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const safePostCssParser = require('postcss-safe-parser')
|
||||
const ManifestPlugin = require('webpack-manifest-plugin')
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin')
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
|
||||
const paths = require('./paths')
|
||||
const getClientEnvironment = require('./env')
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt')
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter')
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'
|
||||
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig)
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/
|
||||
const cssModuleRegex = /\.module\.css$/
|
||||
const sassRegex = /\.(scss|sass)$/
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function(webpackEnv) {
|
||||
const isEnvDevelopment = webpackEnv === 'development'
|
||||
const isEnvProduction = webpackEnv === 'production'
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// In development, we always serve from the root. This makes config easier.
|
||||
const publicPath = isEnvProduction
|
||||
? paths.servedPath
|
||||
: isEnvDevelopment && '/'
|
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './'
|
||||
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = isEnvProduction
|
||||
? publicPath.slice(0, -1)
|
||||
: isEnvDevelopment && ''
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl)
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
options: Object.assign(
|
||||
{},
|
||||
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined,
|
||||
),
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
}),
|
||||
],
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
},
|
||||
},
|
||||
].filter(Boolean)
|
||||
if (preProcessor) {
|
||||
loaders.push({
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
|
||||
},
|
||||
})
|
||||
}
|
||||
return loaders
|
||||
}
|
||||
|
||||
return {
|
||||
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
|
||||
// Stop compilation early in production
|
||||
bail: isEnvProduction,
|
||||
devtool: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
? 'source-map'
|
||||
: false
|
||||
: isEnvDevelopment && 'eval-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: isEnvDevelopment,
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// In development, it does not produce real files.
|
||||
filename: isEnvProduction
|
||||
? 'static/js/[name].[chunkhash:8].js'
|
||||
: isEnvDevelopment && 'static/js/bundle.js',
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[chunkhash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
// We use "/" in development.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
},
|
||||
optimization: {
|
||||
minimize: isEnvProduction,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// we want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minfication steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending futher investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
// Use multi-process parallel running to improve the build speed
|
||||
// Default number of concurrent runs: os.cpus().length - 1
|
||||
parallel: true,
|
||||
// Enable file caching
|
||||
cache: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// This is only used in production mode
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
parser: safePostCssParser,
|
||||
map: shouldUseSourceMap
|
||||
? {
|
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false,
|
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true,
|
||||
}
|
||||
: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
name: false,
|
||||
},
|
||||
// Keep the runtime chunk separated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
runtimeChunk: true,
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules'].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: paths.moduleFileExtensions
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|mjs|jsx)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides',
|
||||
),
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-named-asset-import'),
|
||||
{
|
||||
loaderMap: {
|
||||
svg: {
|
||||
ReactComponent: '@svgr/webpack?-svgo![path]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
cacheCompression: isEnvProduction,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{
|
||||
test: /\.(js|mjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app/dependencies'),
|
||||
{ helpers: true },
|
||||
],
|
||||
],
|
||||
cacheDirectory: true,
|
||||
cacheCompression: isEnvProduction,
|
||||
|
||||
// If an error happens in a package, it's possible to be
|
||||
// because it was compiled. Thus, we don't want the browser
|
||||
// debugger to show the original code. Instead, the code
|
||||
// being evaluated would be much more helpful.
|
||||
sourceMaps: false,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: true,
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 2,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
},
|
||||
'sass-loader',
|
||||
),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 2,
|
||||
sourceMap: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
: isEnvDevelopment,
|
||||
modules: true,
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
'sass-loader',
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
),
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
// In development, this will be an empty string.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for Webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'static/css/[name].[contenthash:8].css',
|
||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: publicPath,
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
isEnvProduction &&
|
||||
new WorkboxWebpackPlugin.GenerateSW({
|
||||
cacheId: 'oa',
|
||||
clientsClaim: true,
|
||||
skipWaiting: true,
|
||||
// NOTE 2020-01-14 CC - Add support to cache firebase storage and map tiles
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: new RegExp(/.*\.(?:png|gif|jpg|jpeg|webp|svg).*/gi),
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'oa-images',
|
||||
fetchOptions: {
|
||||
credentials: 'same-origin',
|
||||
mode: 'cors',
|
||||
},
|
||||
backgroundSync: {
|
||||
name: 'oa-images-background',
|
||||
options: {
|
||||
maxRetentionTime: 60 * 60 * 24,
|
||||
},
|
||||
},
|
||||
expiration: {
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365,
|
||||
maxEntries: 1000,
|
||||
},
|
||||
matchOptions: {
|
||||
ignoreSearch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: new RegExp(
|
||||
'^https://firebasestorage\\.googleapis\\.com/',
|
||||
),
|
||||
handler: 'StaleWhileRevalidate',
|
||||
options: {
|
||||
cacheName: 'oa-other',
|
||||
fetchOptions: {
|
||||
credentials: 'same-origin',
|
||||
mode: 'cors',
|
||||
},
|
||||
matchOptions: {
|
||||
ignoreSearch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: new RegExp('^https://firestore\\.googleapis\\.com/'),
|
||||
handler: 'NetworkOnly',
|
||||
},
|
||||
],
|
||||
// end update 2020-01-14
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: publicUrl + '/index.html',
|
||||
navigateFallbackWhitelist: [/^(?!\/__)/],
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude URLs containing a dot, as they're likely a resource in
|
||||
// public/ and not a SPA route
|
||||
new RegExp('/[^/]+\\.[^/]+$'),
|
||||
],
|
||||
}),
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: false,
|
||||
checkSyntacticErrors: true,
|
||||
tsconfig: paths.appTsConfig,
|
||||
compilerOptions: {
|
||||
module: 'esnext',
|
||||
moduleResolution: 'node',
|
||||
resolveJsonModule: true,
|
||||
isolatedModules: true,
|
||||
noEmit: true,
|
||||
jsx: 'preserve',
|
||||
},
|
||||
reportFiles: [
|
||||
'**',
|
||||
'!**/*.json',
|
||||
'!**/__tests__/**',
|
||||
'!**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
watch: paths.appSrc,
|
||||
silent: true,
|
||||
formatter: typescriptFormatter,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
dns: 'mock',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
}
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
const paths = require('./paths');
|
||||
const getClientEnvironment = require('./env');
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
|
||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
const publicPath = paths.servedPath;
|
||||
// Some apps do not use client-side routing with pushState.
|
||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
||||
const shouldUseRelativeAssetPaths = publicPath === './';
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
const publicUrl = publicPath.slice(0, -1);
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(publicUrl);
|
||||
|
||||
// Assert this just to be safe.
|
||||
// Development builds of React are slow and not intended for production.
|
||||
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
|
||||
throw new Error('Production builds must have NODE_ENV=production.');
|
||||
}
|
||||
|
||||
// Note: defined here because it will be used more than once.
|
||||
const cssFilename = 'static/css/[name].[contenthash:8].css';
|
||||
|
||||
// ExtractTextPlugin expects the build output to be flat.
|
||||
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
|
||||
// However, our output is structured with css, js and media folders.
|
||||
// To have this structure working with relative paths, we have to use custom options.
|
||||
const extractTextPluginOptions = shouldUseRelativeAssetPaths ? // Making sure that the publicPath goes back to to build folder.
|
||||
{
|
||||
publicPath: Array(cssFilename.split('/').length).join('../')
|
||||
} :
|
||||
{};
|
||||
|
||||
// This is the production configuration.
|
||||
// It compiles slowly and is focused on producing a fast and minimal bundle.
|
||||
// The development configuration is different and lives in a separate file.
|
||||
module.exports = {
|
||||
// Don't attempt to continue if there are any errors.
|
||||
bail: true,
|
||||
// We generate sourcemaps in production. This is slow but gives good results.
|
||||
// You can exclude the *.map files from the build during deployment.
|
||||
devtool: shouldUseSourceMap ? 'source-map' : false,
|
||||
// In production, we only want to load the polyfills and the app code.
|
||||
entry: [require.resolve('./polyfills'), paths.appIndexJs],
|
||||
output: {
|
||||
// The build folder.
|
||||
path: paths.appBuild,
|
||||
// Generated JS file names (with nested folders).
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// We don't currently advertise code splitting but Webpack supports it.
|
||||
filename: 'static/js/[name].[chunkhash:8].js',
|
||||
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: publicPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: info =>
|
||||
path
|
||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/'),
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where Webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
// It is guaranteed to exist because we tweak it in `env.js`
|
||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebookincubator/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: [
|
||||
'.mjs',
|
||||
'.web.ts',
|
||||
'.ts',
|
||||
'.web.tsx',
|
||||
'.tsx',
|
||||
'.web.js',
|
||||
'.js',
|
||||
'.json',
|
||||
'.web.jsx',
|
||||
'.jsx',
|
||||
],
|
||||
alias: {
|
||||
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
},
|
||||
plugins: [
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
new TsconfigPathsPlugin({
|
||||
configFile: paths.appTsProdConfig
|
||||
}),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// TODO: Disable require.ensure as it's not a standard language feature.
|
||||
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
|
||||
// { parser: { requireEnsure: false } },
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
loader: require.resolve('source-map-loader'),
|
||||
enforce: 'pre',
|
||||
include: paths.appSrc,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works just like "file" loader but it also embeds
|
||||
// assets smaller than specified size as data URLs to avoid requests.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(js|jsx|mjs)$/,
|
||||
include: paths.appSrc,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
|
||||
compact: true,
|
||||
},
|
||||
},
|
||||
// Compile .tsx?
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
include: paths.appSrc,
|
||||
use: [{
|
||||
loader: require.resolve('ts-loader'),
|
||||
options: {
|
||||
// disable type checker - we will use it in fork plugin
|
||||
transpileOnly: true,
|
||||
configFile: paths.appTsProdConfig,
|
||||
},
|
||||
}, ],
|
||||
},
|
||||
// The notation here is somewhat confusing.
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader normally turns CSS into JS modules injecting <style>,
|
||||
// but unlike in development configuration, we do something different.
|
||||
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
|
||||
// (second argument), then grabs the result CSS and puts it into a
|
||||
// separate file in our build process. This way we actually ship
|
||||
// a single CSS file in production instead of JS code injecting <style>
|
||||
// tags. If you use code splitting, however, any async bundles will still
|
||||
// use the "style" loader inside the async code so CSS from them won't be
|
||||
// in the main CSS file.
|
||||
{
|
||||
test: /\.css$/,
|
||||
loader: ExtractTextPlugin.extract(
|
||||
Object.assign({
|
||||
fallback: {
|
||||
loader: require.resolve('style-loader'),
|
||||
options: {
|
||||
hmr: false,
|
||||
},
|
||||
},
|
||||
use: [{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
minimize: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
autoprefixer({
|
||||
browsers: [
|
||||
'>1%',
|
||||
'last 4 versions',
|
||||
'Firefox ESR',
|
||||
'not ie < 9', // React doesn't support IE8 anyway
|
||||
],
|
||||
flexbox: 'no-2009',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
extractTextPluginOptions
|
||||
)
|
||||
),
|
||||
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
|
||||
},
|
||||
//--------------- *** CUSTOM Add SCSS Loaders ----------------------//
|
||||
{
|
||||
test: /\.scss$/,
|
||||
loaders: [
|
||||
require.resolve("style-loader"),
|
||||
require.resolve("css-loader"),
|
||||
require.resolve("sass-loader")
|
||||
]
|
||||
},
|
||||
// "file" loader makes sure assets end up in the `build` folder.
|
||||
// When you `import` an asset, you get its filename.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// it's runtime that would otherwise processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In production, it will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(env.raw),
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV was set to production here.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// Minify the code.
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
parse: {
|
||||
// we want uglify-js to parse ecma 8 code. However we want it to output
|
||||
// ecma 5 compliant code, to avoid issues with older browsers, this is
|
||||
// whey we put `ecma: 5` to the compress and output section
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
// Use multi-process parallel running to improve the build speed
|
||||
// Default number of concurrent runs: os.cpus().length - 1
|
||||
parallel: true,
|
||||
// Enable file caching
|
||||
cache: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}), // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
|
||||
new ExtractTextPlugin({
|
||||
filename: cssFilename,
|
||||
}),
|
||||
// Generate a manifest file which contains a mapping of all asset filenames
|
||||
// to their corresponding output file so that tools can pick it up without
|
||||
// having to parse `index.html`.
|
||||
new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
}),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the Webpack build.
|
||||
new SWPrecacheWebpackPlugin({
|
||||
// By default, a cache-busting query parameter is appended to requests
|
||||
// used to populate the caches, to ensure the responses are fresh.
|
||||
// If a URL is already hashed by Webpack, then there is no concern
|
||||
// about it being stale, and the cache-busting can be skipped.
|
||||
dontCacheBustUrlsMatching: /\.\w{8}\./,
|
||||
filename: 'service-worker.js',
|
||||
logger(message) {
|
||||
if (message.indexOf('Total precache size is') === 0) {
|
||||
// This message occurs for every build and is a bit too noisy.
|
||||
return;
|
||||
}
|
||||
if (message.indexOf('Skipping static resource') === 0) {
|
||||
// This message obscures real errors so we ignore it.
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2612
|
||||
return;
|
||||
}
|
||||
console.log(message);
|
||||
},
|
||||
minify: true,
|
||||
// For unknown URLs, fallback to the index page
|
||||
navigateFallback: publicUrl + '/index.html',
|
||||
// Ignores URLs starting from /__ (useful for Firebase):
|
||||
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
|
||||
navigateFallbackWhitelist: [/^(?!\/__).*/],
|
||||
// Don't precache sourcemaps (they're large) and build asset manifest:
|
||||
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
|
||||
}),
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how Webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Perform type checking and linting in a separate process to speed up compilation
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
async: false,
|
||||
tsconfig: paths.appTsProdConfig,
|
||||
tslint: paths.appTsLint,
|
||||
}),
|
||||
],
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
};
|
||||
@@ -1,104 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const paths = require('./paths');
|
||||
const fs = require('fs');
|
||||
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebook/create-react-app/issues/2271
|
||||
// https://github.com/facebook/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through Webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the Webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// It is important to tell WebpackDevServer to use the same "root" path
|
||||
// as we specified in the config. In development, we always serve from /.
|
||||
publicPath: '/',
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebook/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebook/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
||||
https: protocol === 'https',
|
||||
host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
},
|
||||
public: allowedHost,
|
||||
proxy,
|
||||
before(app, server) {
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware());
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -2,10 +2,10 @@ import { UserMenuItem } from '../support/commands'
|
||||
|
||||
describe('[Common]', () => {
|
||||
it('[Default Page]', () => {
|
||||
cy.step('The home page is /how-to')
|
||||
cy.step('The home page is /academy')
|
||||
cy.visit('/')
|
||||
.url()
|
||||
.should('include', '/how-to')
|
||||
.should('include', '/academy')
|
||||
})
|
||||
|
||||
it('[Not-Found Page]', () => {
|
||||
|
||||
@@ -116,7 +116,7 @@ describe('[How To]', () => {
|
||||
cy.step('How-to has basic info')
|
||||
cy.get('[data-cy=how-to-basis]').then($summary => {
|
||||
expect($summary).to.contain('By howto_creator', 'Author')
|
||||
expect($summary).to.contain('Last edit:', 'Edit')
|
||||
expect($summary).to.contain('Last edit on', 'Edit')
|
||||
expect($summary).to.contain('Make an interlocking brick', 'Title')
|
||||
expect($summary).to.contain(
|
||||
'show you how to make a brick using the injection machine',
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('[Settings]', () => {
|
||||
.clear()
|
||||
.type(info.country)
|
||||
cy.get('[data-cy=country')
|
||||
.find('.flag-option')
|
||||
.find('li')
|
||||
.first()
|
||||
.click()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'cypress-file-upload'
|
||||
import { Firestore, Auth as AuthNative, firebase } from './db/firebase'
|
||||
import FileData = Cypress.FileData
|
||||
import { Firestore, firebase } from './db/firebase'
|
||||
|
||||
export enum UserMenuItem {
|
||||
Profile = 'Profile',
|
||||
@@ -10,7 +9,6 @@ export enum UserMenuItem {
|
||||
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface Chainable {
|
||||
login(
|
||||
username: string,
|
||||
@@ -163,17 +161,6 @@ const attachCustomCommands = (Cypress: Cypress.Cypress) => {
|
||||
})
|
||||
})
|
||||
|
||||
const resolveMimeType = (filePath: string) => {
|
||||
const mimeTypeMapping = [['.jpg', 'image/jpg'], ['.png', 'image/png']]
|
||||
const [_, mimeType]: any = mimeTypeMapping.find(([ext]) =>
|
||||
filePath.endsWith(ext),
|
||||
)
|
||||
if (!mimeType) {
|
||||
throw new Error(`Please define the mime type for ${filePath} here!`)
|
||||
}
|
||||
return mimeType
|
||||
}
|
||||
|
||||
Cypress.Commands.add('toggleUserMenuOn', () => {
|
||||
Cypress.log({ displayName: 'OPEN_USER_MENU' })
|
||||
cy.get('[data-cy=user-menu]').should('be.exist')
|
||||
|
||||
@@ -4,7 +4,6 @@ import { IUserPPDB, ProfileTypeLabel } from '../../src/models/user_pp.models'
|
||||
|
||||
declare global {
|
||||
namespace Chai {
|
||||
// tslint:disable-next-line:interface-name
|
||||
interface Assertion {
|
||||
containSubset(expect: any): any
|
||||
eqHowtoStep(expect: any, index: number)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
// tslint:disable-next-line
|
||||
require('cypress-plugin-retries')
|
||||
import './hooks'
|
||||
import './commands'
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../tslint.json",
|
||||
"rules": {
|
||||
"interface-name": false,
|
||||
"no-console": false
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "./tslint.json"
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
const endpoint = 'https://davehakkens.nl/wp-json/aam/v1/authenticate'
|
||||
import axios from 'axios'
|
||||
import { firebaseAdmin } from '../Firebase/admin'
|
||||
// tslint:disable no-implicit-dependencies
|
||||
import { IUser } from '../models'
|
||||
|
||||
export const DHLogin = async (username: string, password: string) => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// tslint:disable no-implicit-dependencies
|
||||
// import { IDBEndpoint } from '@OAModels/common.models'
|
||||
import * as rtdb from './realtimeDB'
|
||||
import * as firestore from './firestoreDB'
|
||||
|
||||
@@ -2,8 +2,8 @@ import * as functions from 'firebase-functions'
|
||||
import { IDBEndpoint } from '../models'
|
||||
import { db } from '../Firebase/firestoreDB'
|
||||
|
||||
const USER_ENDPOINT: IDBEndpoint = 'v3_users' as any
|
||||
// TODO - fix links to main repo and handle with prefix_endpoint type checking
|
||||
const USER_ENDPOINT = 'v3_users'
|
||||
|
||||
/**
|
||||
* Example function to show how an automated email can be triggered
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import * as functions from 'firebase-functions'
|
||||
import { DBDoc, IDBEndpoint } from '../models'
|
||||
|
||||
// TODO - fix links to main repo and handle with prefix_endpoint type checking
|
||||
const USERS_ENDPOINT = 'v3_users'
|
||||
const USERS_ENDPOINT: IDBEndpoint = 'v3_users' as any
|
||||
|
||||
/**
|
||||
* Automatically create user revision on update
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// tslint:disable no-implicit-dependencies
|
||||
// Models can be imported from the main package for use here
|
||||
// NOTE 1 - this requires adjustment main src in package.json
|
||||
// NOTE 2 - shorthand @OAModels notation defined in tsconfig
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
},
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"references": [{ "path": "../src" }],
|
||||
"references": [{ "path": "../tsconfig.shared.json" }],
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
@@ -837,9 +837,9 @@ diff@^3.2.0:
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
||||
dot-prop@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.1.0.tgz#bdd8c986a77b83e3fca524e53786df916cabbd8a"
|
||||
integrity sha512-n1oC6NBF+KM9oVXtjmen4Yo7HyAVWV2UUl50dCYJdw2924K6dX9bf9TTTWaKtYlRn0FEtxG27KS80ayVLixxJA==
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
|
||||
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
|
||||
dependencies:
|
||||
is-obj "^2.0.0"
|
||||
|
||||
@@ -1746,9 +1746,9 @@ lodash.once@^4.0.0:
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash@^4.17.10, lodash@^4.17.14:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
||||
|
||||
log-update@^3.2.0:
|
||||
version "3.2.0"
|
||||
|
||||
185
package.json
185
package.json
@@ -3,67 +3,69 @@
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
"build": "node scripts/build.js",
|
||||
"start": "react-app-rewired start",
|
||||
"build": "react-app-rewired build",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx src --color",
|
||||
"lint:fix": "npm run lint -- --fix",
|
||||
"serve": "npx serve -s build",
|
||||
"test": "node scripts/test.e2e.js",
|
||||
"functions": "cd functions && npm run --",
|
||||
"lint": "tslint --project tsconfig.json --config tslint.json",
|
||||
"format": "npm run prettier -- --write",
|
||||
"prettier": "prettier \"**/*.+(js|jsx|json|yml|yaml|css|less|scss|ts|tsx|md|graphql|mdx)\"",
|
||||
"validate": "npm run lint && npm run prettier -- --list-different",
|
||||
"test:unit": "react-app-rewired test --env=jsdom",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook",
|
||||
"analyze": "npm run build -- --stats & npx webpack-bundle-analyzer ./build/bundle-stats.json"
|
||||
"storybook:build": "build-storybook",
|
||||
"analyze": "npx cra-bundle-analyzer && npx open-cli build/report.html"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"eslintConfig": {},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"dependencies": {
|
||||
"@khanacademy/react-multi-select": "^0.3.3",
|
||||
"@material-ui/core": "3.7.1",
|
||||
"@sentry/browser": "^4.6.3",
|
||||
"@uppy/core": "^1.0.0",
|
||||
"@uppy/dashboard": "^1.8.3",
|
||||
"@uppy/react": "^1.0.0",
|
||||
"client-compress": "^2.1.2",
|
||||
"css-loader": "1.0.0",
|
||||
"date-fns": "^1.30.1",
|
||||
"debounce": "^1.2.0",
|
||||
"dexie": "^2.0.4",
|
||||
"dompurify": "^1.0.10",
|
||||
"file-loader": "2.0.0",
|
||||
"final-form": "4.18.5",
|
||||
"final-form-arrays": "^3.0.1",
|
||||
"final-form-calculate": "^1.3.1",
|
||||
"firebase": "^7.0.0",
|
||||
"geolocation-utils": "^1.2.1",
|
||||
"google-oauth-jwt": "^0.2.0",
|
||||
"googleapis": "^37.2.0",
|
||||
"html-webpack-plugin": "4.0.0-alpha.2",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"is-url": "^1.2.4",
|
||||
"jest": "23.6.0",
|
||||
"jest-pnp-resolver": "1.0.2",
|
||||
"jest-resolve": "23.6.0",
|
||||
"jest-watch-typeahead": "^0.2.1",
|
||||
"leaflet": "^1.5.1",
|
||||
"leaflet.markercluster": "^1.4.1",
|
||||
"lorem-ipsum": "^2.0.3",
|
||||
"md5": "^2.2.1",
|
||||
"mini-css-extract-plugin": "0.5.0",
|
||||
"mobx": "4.9.2",
|
||||
"mobx-react": "5.4.3",
|
||||
"places.js": "^1.16.4",
|
||||
"pubsub-js": "^1.7.0",
|
||||
"query-string": "^6.2.0",
|
||||
"react": "16.8.4",
|
||||
"react-app-polyfill": "^0.2.1",
|
||||
"react": "^16.13.1",
|
||||
"react-datepicker": "^2.9.6",
|
||||
"react-dev-utils": "^7.0.3",
|
||||
"react-dom": "16.8.4",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-dropzone": "^10.1.10",
|
||||
"react-final-form": "6.3.0",
|
||||
"react-final-form-arrays": "^3.1.1",
|
||||
@@ -91,14 +93,10 @@
|
||||
"rebass": "4.0.6",
|
||||
"styled-components": "4.1.3",
|
||||
"styled-system": "^4.0.0",
|
||||
"url-loader": "1.1.2",
|
||||
"wordpress-hash-node": "^1.0.0",
|
||||
"yup": "^0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.2.2",
|
||||
"@cypress/webpack-preprocessor": "^4.1.0",
|
||||
"@mdx-js/loader": "^1.5.1",
|
||||
"@storybook/addon-actions": "^5.2.5",
|
||||
"@storybook/addon-docs": "^5.2.5",
|
||||
"@storybook/addon-info": "^5.2.5",
|
||||
@@ -106,14 +104,15 @@
|
||||
"@storybook/addon-links": "^5.2.5",
|
||||
"@storybook/addons": "^5.2.5",
|
||||
"@storybook/react": "^5.2.5",
|
||||
"@svgr/webpack": "4.1.0",
|
||||
"@storybook/theming": "^5.3.18",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"@types/dompurify": "^0.0.32",
|
||||
"@types/flux-standard-action": "1.1.0",
|
||||
"@types/jest": "23.3.2",
|
||||
"@types/md5": "^2.1.33",
|
||||
"@types/node": "10.11.0",
|
||||
"@types/pubsub-js": "^1.5.18",
|
||||
"@types/react": "16.8.4",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-hamburger-menu": "^0.0.3",
|
||||
"@types/react-leaflet": "^1.1.6",
|
||||
@@ -131,111 +130,27 @@
|
||||
"@types/styled-components": "4.0",
|
||||
"@types/styled-system": "^3.2.2",
|
||||
"@types/yup": "^0.26.27",
|
||||
"awesome-typescript-loader": "^5.2.1",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "9.0.0",
|
||||
"babel-jest": "23.6.0",
|
||||
"babel-loader": "8.0.5",
|
||||
"babel-plugin-named-asset-import": "^0.3.1",
|
||||
"babel-preset-react-app": "^7.0.1",
|
||||
"bfj": "6.1.1",
|
||||
"case-sensitive-paths-webpack-plugin": "2.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.29.0",
|
||||
"@typescript-eslint/parser": "^2.29.0",
|
||||
"chai-subset": "^1.6.0",
|
||||
"cra-bundle-analyzer": "^0.0.3",
|
||||
"cross-env": "^6.0.3",
|
||||
"cypress": "4.4.0",
|
||||
"cypress-file-upload": "4.0.6",
|
||||
"cypress-plugin-retries": "^1.5.2",
|
||||
"dotenv": "6.0.0",
|
||||
"dotenv-expand": "4.2.0",
|
||||
"enzyme": "^3.8.0",
|
||||
"enzyme-adapter-react-16": "^1.8.0",
|
||||
"eslint": "5.12.0",
|
||||
"eslint-config-react-app": "^3.0.7",
|
||||
"eslint-loader": "2.1.1",
|
||||
"eslint-plugin-flowtype": "2.50.1",
|
||||
"eslint-plugin-import": "2.14.0",
|
||||
"eslint-plugin-jsx-a11y": "6.1.2",
|
||||
"eslint-plugin-react": "7.12.4",
|
||||
"fork-ts-checker-webpack-plugin-alt": "0.4.14",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"fs-extra": "7.0.1",
|
||||
"husky": "^1.3.1",
|
||||
"lint-staged": "^8.1.5",
|
||||
"mobx-react-devtools": "^6.0.3",
|
||||
"node-sass": "^4.11.0",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.1",
|
||||
"pnp-webpack-plugin": "1.2.1",
|
||||
"postcss-flexbugs-fixes": "4.1.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"postcss-preset-env": "6.5.0",
|
||||
"postcss-safe-parser": "4.0.1",
|
||||
"node-sass": "^4.13.1",
|
||||
"prettier": "^1.16.4",
|
||||
"react-docgen-typescript-loader": "^3.3.0",
|
||||
"resolve": "1.10.0",
|
||||
"sass-loader": "7.1.0",
|
||||
"react-app-rewired": "^2.1.5",
|
||||
"react-scripts": "3.4.1",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"style-loader": "0.23.1",
|
||||
"terser-webpack-plugin": "1.2.2",
|
||||
"ts-loader": "^6.1.1",
|
||||
"tslint": "5.7.0",
|
||||
"tslint-config-prettier": "1.10.0",
|
||||
"tslint-react": "3.2.0",
|
||||
"typescript": "^3.7.4",
|
||||
"webpack": "4.28.3",
|
||||
"webpack-dev-server": "3.1.14",
|
||||
"webpack-manifest-plugin": "2.0.4",
|
||||
"workbox-webpack-plugin": "4.3.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.4"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{js,jsx,ts,tsx}",
|
||||
"!src/**/*.d.ts"
|
||||
],
|
||||
"resolver": "jest-pnp-resolver",
|
||||
"setupFiles": [
|
||||
"react-app-polyfill/jsdom"
|
||||
],
|
||||
"testMatch": [
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
|
||||
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}"
|
||||
],
|
||||
"testEnvironment": "jsdom",
|
||||
"testURL": "http://localhost",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
|
||||
"^.+\\.module\\.(css|sass|scss)$"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^react-native$": "react-native-web",
|
||||
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"web.js",
|
||||
"js",
|
||||
"web.ts",
|
||||
"ts",
|
||||
"web.tsx",
|
||||
"tsx",
|
||||
"json",
|
||||
"web.jsx",
|
||||
"jsx",
|
||||
"node"
|
||||
],
|
||||
"watchPlugins": [
|
||||
"<rootDir>/node_modules/jest-watch-typeahead/filename.js",
|
||||
"<rootDir>/node_modules/jest-watch-typeahead/testname.js"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"react-app"
|
||||
]
|
||||
"ts-loader": "^7.0.5",
|
||||
"typescript": "^3.7.4"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
BIN
public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -6,9 +6,19 @@
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
|
||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -53,11 +53,11 @@ function getCypressEnv(sharedEnv) {
|
||||
}
|
||||
|
||||
function randomString(length) {
|
||||
var result = ''
|
||||
var characters =
|
||||
let result = ''
|
||||
const characters =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
var charactersLength = characters.length
|
||||
for (var i = 0; i < length; i++) {
|
||||
const charactersLength = characters.length
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
||||
}
|
||||
return result
|
||||
|
||||
1
src/assets/icons/icon-arrow-select.svg
Normal file
1
src/assets/icons/icon-arrow-select.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.78 12.9"><title>icon arrow down</title><g id="Calque_1" data-name="Calque 1"><path d="M12.61,5.23C11.2,6.48,9.81,7.75,8.4,9q-.35-3.9-.32-7.81a1.25,1.25,0,0,0-2.5,0q0,3.81.3,7.61C4.59,7.7,3.32,6.54,2.13,5.31S-.76,5.92.36,7.07a61.87,61.87,0,0,0,6.06,5.5,1.35,1.35,0,0,0,.53.25,1.2,1.2,0,0,0,1.24-.25c2.07-1.85,4.12-3.73,6.19-5.57C15.58,5.92,13.81,4.16,12.61,5.23Z" style="fill:#1d1d1b"/></g></svg>
|
||||
|
After Width: | Height: | Size: 451 B |
1
src/assets/icons/icon-download.svg
Normal file
1
src/assets/icons/icon-download.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Calque_1" data-name="Calque 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 43.68 43.18"><title>icon download</title><path d="M25.93,36.6a60.76,60.76,0,0,0-5,4.64c-1.12,1.16.65,2.93,1.77,1.77a62.68,62.68,0,0,1,5-4.65c1.23-1-.54-2.79-1.77-1.76Z" style="fill:#1d1d1b"/><path d="M22.57,41.31l-5.35-4.63-1.51,2,.13.09c1.13,1.15,2.9-.62,1.77-1.77a2.15,2.15,0,0,0-.64-.48c-1.2-.58-2.66,1-1.52,2l5.35,4.63c1.22,1.05,3-.71,1.77-1.77Z" style="fill:#1d1d1b"/><path d="M23,40.26q-.39-8.16-.22-16.32a1.25,1.25,0,0,0-2.5,0q-.18,8.16.22,16.32c.08,1.6,2.58,1.61,2.5,0Z" style="fill:#1d1d1b"/><path d="M15.28,29.71c-4.11.86-11.13.23-12.54-4.63a8.36,8.36,0,0,1,.54-5.7c1.34-2.66,3.62-2.62,6.21-2.84,1.59-.13,1.61-2.63,0-2.5-2.81.24-5.62.33-7.54,2.7A10.61,10.61,0,0,0,.16,24.89c1.13,7,10.05,8.43,15.79,7.23,1.57-.33.9-2.74-.67-2.41Z" style="fill:#1d1d1b"/><path d="M10.74,15.29c-.25-5.81,2.89-11.4,9-12.59A10.3,10.3,0,0,1,32,12.19c.14,1.59,2.64,1.6,2.5,0C33.92,5,28.17-.51,20.79,0,12.85.62,7.91,7.73,8.24,15.29c.07,1.6,2.57,1.61,2.5,0Z" style="fill:#1d1d1b"/><path d="M33.28,13.44c5.68-.61,9.11,5.82,7.62,10.7-1.93,6.33-8.44,5.35-13.68,5.4a1.25,1.25,0,0,0,0,2.5c3.66,0,7.76.53,11.15-1.12a9.74,9.74,0,0,0,5.31-8c.63-5.92-3.87-12.66-10.4-12-1.58.17-1.6,2.67,0,2.5Z" style="fill:#1d1d1b"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -8,6 +8,7 @@ export const log = {
|
||||
}
|
||||
export const Level = Sentry.Severity
|
||||
export const initErrorHandler = () => {
|
||||
const { location } = window
|
||||
if (
|
||||
location.search.indexOf('noSentry=true') !== -1 ||
|
||||
location.hostname === 'localhost'
|
||||
|
||||
12
src/components/DropdownIndicator/index.tsx
Normal file
12
src/components/DropdownIndicator/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react'
|
||||
import { components } from 'react-select'
|
||||
import { Image } from 'rebass'
|
||||
import ArrowSelectIcon from '../../assets/icons/icon-arrow-select.svg'
|
||||
|
||||
// https://github.com/JedWatson/react-select/issues/685#issuecomment-420213835
|
||||
export const DropdownIndicator = props =>
|
||||
components.DropdownIndicator && (
|
||||
<components.DropdownIndicator {...props}>
|
||||
<Image src={ArrowSelectIcon} sx={{ width: '12px' }} />
|
||||
</components.DropdownIndicator>
|
||||
)
|
||||
@@ -3,7 +3,7 @@ import { Box } from 'rebass'
|
||||
import checkmarkIcon from 'src/assets/icons/icon-checkmark.svg'
|
||||
|
||||
interface IProps {
|
||||
IconUrl: JSX.Element
|
||||
IconUrl: JSX.Element | string
|
||||
height?: string
|
||||
width?: string
|
||||
ticked?: boolean
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react'
|
||||
import { availableGlyphs } from '../Icons'
|
||||
import { bytesToSize } from '../ImageInput/ImageInput'
|
||||
import { IUploadedFileMeta } from 'src/stores/storage'
|
||||
import { FileDetails } from './FileDetails'
|
||||
@@ -9,60 +8,32 @@ interface IProps {
|
||||
file: File | IUploadedFileMeta | null
|
||||
allowDownload?: boolean
|
||||
}
|
||||
interface IState {
|
||||
glyph: availableGlyphs
|
||||
size: string
|
||||
}
|
||||
|
||||
const FileContainer = styled.a`
|
||||
width: 300px;
|
||||
margin: 3px 3px;
|
||||
`
|
||||
export const FileInfo: React.FC<IProps> = ({ file, allowDownload }) => {
|
||||
const meta = file as IUploadedFileMeta
|
||||
const size = file ? bytesToSize(file.size) : '0'
|
||||
|
||||
export class FileInfo extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
glyph: props.file ? this.getGlyph(props.file.type) : 'image',
|
||||
size: props.file ? bytesToSize(props.file.size) : '0',
|
||||
}
|
||||
if (!file) {
|
||||
return null
|
||||
}
|
||||
|
||||
getGlyph(filetype: string) {
|
||||
let glyph: availableGlyphs = 'image'
|
||||
switch (filetype) {
|
||||
case 'application/pdf':
|
||||
glyph = 'pdf'
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return glyph
|
||||
}
|
||||
|
||||
render() {
|
||||
const { file, allowDownload } = this.props
|
||||
const { glyph, size } = this.state
|
||||
const meta = file as IUploadedFileMeta
|
||||
|
||||
if (!file) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{allowDownload && meta.downloadUrl ? (
|
||||
<FileContainer
|
||||
href={meta.downloadUrl}
|
||||
target="_blank"
|
||||
download={file.name}
|
||||
>
|
||||
<FileDetails file={file} glyph={glyph} size={size} />
|
||||
</FileContainer>
|
||||
) : (
|
||||
<FileDetails file={file} glyph={glyph} size={size} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{allowDownload && meta.downloadUrl ? (
|
||||
<FileContainer
|
||||
href={meta.downloadUrl}
|
||||
target="_blank"
|
||||
download={file.name}
|
||||
>
|
||||
<FileDetails file={file} glyph="download-cloud" size={size} />
|
||||
</FileContainer>
|
||||
) : (
|
||||
<FileDetails file={file} glyph="download-cloud" size={size} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import theme from 'src/themes/styled.theme'
|
||||
import {
|
||||
Flex as RebassFlex,
|
||||
FlexProps as RebassFlexProps,
|
||||
@@ -62,7 +61,7 @@ export const BaseFlex = styled(RebassFlex)`
|
||||
|
||||
// TODO - incorporate custom css into rebass props to allow things like below to be passed
|
||||
export const Flex = (props: FlexProps) => (
|
||||
<BaseFlex {...props}>{props.children}</BaseFlex>
|
||||
<BaseFlex {...(props as any)}>{props.children}</BaseFlex>
|
||||
)
|
||||
|
||||
export default Flex
|
||||
|
||||
@@ -6,6 +6,7 @@ import theme from 'src/themes/styled.theme'
|
||||
import { Flex } from 'src/components/Flex'
|
||||
import { ErrorMessage, FieldContainer } from './elements'
|
||||
import { IFieldProps } from './Fields'
|
||||
import { DropdownIndicator } from '../DropdownIndicator'
|
||||
|
||||
interface ISelectOption {
|
||||
value: string
|
||||
@@ -75,6 +76,14 @@ export const SelectStyles: Partial<Styles> = {
|
||||
...provided,
|
||||
display: 'none',
|
||||
}),
|
||||
|
||||
dropdownIndicator: (provided, state) => ({
|
||||
...provided,
|
||||
':hover': {
|
||||
opacity: state.isFocused ? 1 : 0.5,
|
||||
},
|
||||
opacity: state.isFocused ? 1 : 0.3,
|
||||
}),
|
||||
}
|
||||
|
||||
export const FilterStyles: Partial<Styles> = {
|
||||
@@ -98,10 +107,13 @@ export const FilterStyles: Partial<Styles> = {
|
||||
border: '1px solid ' + theme.colors.blue,
|
||||
},
|
||||
}),
|
||||
|
||||
option: (provided, state) => ({
|
||||
color: 'black',
|
||||
placeholder: (provided, state) => ({
|
||||
...provided,
|
||||
color: 'black',
|
||||
}),
|
||||
option: (provided, state) => ({
|
||||
...provided,
|
||||
color: 'black',
|
||||
backgroundColor: 'white',
|
||||
boxShadow: 'none',
|
||||
':hover': {
|
||||
@@ -165,6 +177,7 @@ const defaultProps: Partial<ISelectFieldProps> = {
|
||||
getOptionValue: (option: ISelectOption) => option.value,
|
||||
options: [],
|
||||
}
|
||||
|
||||
export const SelectField = ({
|
||||
input,
|
||||
meta,
|
||||
@@ -187,10 +200,11 @@ export const SelectField = ({
|
||||
onCustomChange(getValueFromSelect(v as any))
|
||||
}
|
||||
}}
|
||||
onBlur={input.onBlur}
|
||||
onFocus={input.onFocus}
|
||||
onBlur={input.onBlur as any}
|
||||
onFocus={input.onFocus as any}
|
||||
value={getValueForSelect(rest.options, input.value)}
|
||||
classNamePrefix={'data-cy'}
|
||||
components={{ DropdownIndicator }}
|
||||
{...defaultProps}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
@@ -31,6 +31,16 @@ export const Input = styled.input<IFormElement>`
|
||||
padding: 10px;
|
||||
`
|
||||
|
||||
export const BlackPlaceholderInput = styled.input<IFormElement>`
|
||||
${inputStyles};
|
||||
height: 40px;
|
||||
padding: 10px;
|
||||
|
||||
::placeholder {
|
||||
color: black;
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledDatePicker = styled(DatePicker)`
|
||||
${inputStyles};
|
||||
height: 40px;
|
||||
|
||||
@@ -19,7 +19,7 @@ export const BaseHeading = styled(Text)`
|
||||
type IHeadingProps = ITextProps & RebassHeadingProps
|
||||
|
||||
const Heading = (props: IHeadingProps) => (
|
||||
<BaseHeading {...props}>{props.children}</BaseHeading>
|
||||
<BaseHeading {...(props as any)}>{props.children}</BaseHeading>
|
||||
)
|
||||
|
||||
Heading.defaultProps = {
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
import { FaSignal, FaFacebookF, FaSlack, FaInstagram } from 'react-icons/fa'
|
||||
import { IconContext } from 'react-icons'
|
||||
import SVGs from './svgs'
|
||||
import { DownloadIcon } from './DownloadIcon'
|
||||
|
||||
interface IGlyphProps {
|
||||
glyph: string
|
||||
@@ -56,6 +57,7 @@ interface IProps {
|
||||
}
|
||||
export type availableGlyphs =
|
||||
| 'download'
|
||||
| 'download-cloud'
|
||||
| 'upload'
|
||||
| 'add'
|
||||
| 'check'
|
||||
@@ -93,6 +95,7 @@ export type IGlyphs = { [k in availableGlyphs]: JSX.Element }
|
||||
|
||||
export const glyphs: IGlyphs = {
|
||||
download: <MdFileDownload />,
|
||||
'download-cloud': <DownloadIcon />,
|
||||
upload: <GoCloudUpload />,
|
||||
add: <MdAdd />,
|
||||
check: <MdCheck />,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import React from 'react'
|
||||
import AlgoliaPlaces from 'places.js'
|
||||
import { ALGOLIA_PLACES_CONFIG } from 'src/config/config'
|
||||
import { Input } from '../Form/elements'
|
||||
import { Input, BlackPlaceholderInput } from '../Form/elements'
|
||||
import { Observable, fromEvent, Subscription } from 'rxjs'
|
||||
import { debounceTime, map } from 'rxjs/operators'
|
||||
import styled from 'styled-components'
|
||||
@@ -18,7 +18,7 @@ interface IProps {
|
||||
debounceTime: number
|
||||
onChange: (selected: ILocation) => void
|
||||
onClear?: () => void
|
||||
styleVariant?: 'filter' | 'field'
|
||||
styleVariant?: 'filter' | 'field' | 'mapinput'
|
||||
}
|
||||
interface IState {
|
||||
debouncedInputValue: string
|
||||
@@ -70,7 +70,7 @@ export class LocationSearch extends React.Component<IProps, IState> {
|
||||
appId: ALGOLIA_PLACES_CONFIG.applicationID,
|
||||
apiKey: ALGOLIA_PLACES_CONFIG.searchOnlyAPIKey,
|
||||
container: this.placesInputRef.current,
|
||||
}).configure({ style: false, useDeviceLocation: false })
|
||||
}).configure({ useDeviceLocation: false })
|
||||
// add custom handler when place selected from list
|
||||
this.places.on('change', (selected: IAlgoliaResponse) =>
|
||||
this.handlePlaceSelectChange(selected),
|
||||
@@ -88,9 +88,9 @@ export class LocationSearch extends React.Component<IProps, IState> {
|
||||
|
||||
// when user changes input want to debounce and pass to places input
|
||||
subscribeToInputChanges() {
|
||||
const observable: Observable<
|
||||
React.ChangeEvent<HTMLInputElement>
|
||||
> = fromEvent(this.userInputRef.current, 'keyup')
|
||||
const observable: Observable<React.ChangeEvent<
|
||||
HTMLInputElement
|
||||
>> = fromEvent(this.userInputRef.current, 'keyup')
|
||||
this.inputValue$ = observable
|
||||
.pipe(
|
||||
map(e => e.currentTarget.value),
|
||||
@@ -146,12 +146,25 @@ export class LocationSearch extends React.Component<IProps, IState> {
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
placeholder={this.props.placeholder}
|
||||
style={styleVariant === 'filter' ? FilterStyle : SelectorStyle}
|
||||
ref={this.userInputRef}
|
||||
onBlur={() => this.places.close()}
|
||||
/>
|
||||
{/* Placeholder cannot be changed with inline notation (e.g.in FilterStyle or SelectStyle)
|
||||
So created a new Styled component
|
||||
*/}
|
||||
{styleVariant !== 'mapinput' && (
|
||||
<Input
|
||||
placeholder={this.props.placeholder}
|
||||
style={styleVariant === 'filter' ? FilterStyle : SelectorStyle}
|
||||
ref={this.userInputRef}
|
||||
onBlur={() => this.places.close()}
|
||||
/>
|
||||
)}
|
||||
{styleVariant === 'mapinput' && (
|
||||
<BlackPlaceholderInput
|
||||
placeholder={this.props.placeholder}
|
||||
style={FilterStyle}
|
||||
ref={this.userInputRef}
|
||||
onBlur={() => this.places.close()}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
{/* the second input takes debounced value from the first input and binds to algolia search */}
|
||||
<AlgoliaResults
|
||||
|
||||
@@ -45,10 +45,7 @@ const MoreModalContainer = styled(Box)`
|
||||
`
|
||||
|
||||
export const MoreContainer = (props: BoxProps) => (
|
||||
<MoreModalContainer {...props}>
|
||||
{/* <MoreText color={'black'}>{props.text}</MoreText> */}
|
||||
{props.children}
|
||||
</MoreModalContainer>
|
||||
<MoreModalContainer {...(props as any)}>{props.children}</MoreModalContainer>
|
||||
)
|
||||
|
||||
export default MoreContainer
|
||||
|
||||
@@ -101,7 +101,7 @@ export class ProfileModal extends React.Component<IProps> {
|
||||
<Flex>
|
||||
<ModalLink
|
||||
onClick={() => this.logout()}
|
||||
to={location.pathname}
|
||||
to={window.location.pathname}
|
||||
data-cy="menu-Logout"
|
||||
>
|
||||
<Flex color="rgba(27,27,27,0.5)">Log out</Flex>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FieldRenderProps } from 'react-final-form'
|
||||
import Select from 'react-select'
|
||||
import { SelectStyles, FilterStyles } from '../Form/Select.field'
|
||||
import { FieldContainer } from '../Form/elements'
|
||||
import { DropdownIndicator } from '../DropdownIndicator'
|
||||
|
||||
// we include props from react-final-form fields so it can be used as a custom field component
|
||||
export interface IProps extends FieldRenderProps<any, any> {
|
||||
@@ -71,6 +72,7 @@ class TagsSelect extends React.Component<IProps, IState> {
|
||||
return (
|
||||
<FieldContainer data-cy="tag-select">
|
||||
<Select
|
||||
components={{ DropdownIndicator }}
|
||||
styles={styleVariant === 'selector' ? SelectStyles : FilterStyles}
|
||||
isMulti
|
||||
options={categoryTags}
|
||||
|
||||
@@ -19,8 +19,7 @@ export interface ITextProps {
|
||||
medium?: boolean
|
||||
small?: boolean
|
||||
superSmall?: boolean
|
||||
// keyof colors returns full object prototype, include typeof for just named keys (i.e. color list)
|
||||
color?: keyof typeof theme.colors
|
||||
color?: string
|
||||
// clip forces text to fill max 1 line and add '...' for overflow
|
||||
clipped?: boolean
|
||||
preLine?: boolean
|
||||
@@ -150,7 +149,7 @@ type TextProps = ITextProps & RebassTextProps
|
||||
|
||||
// TODO - incorporate custom css into rebass props to allow things like below to be passed
|
||||
export const Text = (props: TextProps) => (
|
||||
<BaseText {...props}>{props.children}</BaseText>
|
||||
<BaseText {...(props as any)}>{props.children}</BaseText>
|
||||
)
|
||||
|
||||
export default Text
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
CellMeasurer,
|
||||
ListRowProps,
|
||||
} from 'react-virtualized'
|
||||
import { emStringToPx } from 'src/utils/helpers'
|
||||
|
||||
interface IProps {
|
||||
data: any[]
|
||||
@@ -130,10 +131,7 @@ export class VirtualizedFlex extends React.Component<IProps, IState> {
|
||||
}
|
||||
|
||||
static defaultProps: IProps = {
|
||||
widthBreakpoints: themes.breakpoints.map(
|
||||
// Convert theme em string to px number
|
||||
width => Number(width.replace('em', '')) * 16,
|
||||
),
|
||||
widthBreakpoints: themes.breakpoints.map(emStringToPx),
|
||||
data: [],
|
||||
renderItem: data => <div>RenderItem {data}</div>,
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ if (siteVariant === 'production') {
|
||||
searchOnlyAPIKey: e.REACT_APP_ALGOLIA_PLACES_API_KEY as string,
|
||||
}
|
||||
// disable console logs
|
||||
// tslint:disable no-empty
|
||||
// eslint-disable-next-line
|
||||
console.log = () => {}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ export const FIREBASE_CONFIG = firebaseConfigs[siteVariant]
|
||||
export const ALGOLIA_SEARCH_CONFIG = algoliaSearchConfig
|
||||
export const ALGOLIA_PLACES_CONFIG = algoliaPlacesConfig
|
||||
export const SENTRY_CONFIG = sentryConfig
|
||||
// tslint:disable no-var-requires
|
||||
export const VERSION = require('../../package.json').version
|
||||
export const GA_TRACKING_ID = process.env.REACT_APP_GA_TRACKING_ID
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ interface IState {
|
||||
selectedDate: any
|
||||
isLocationSelected?: boolean
|
||||
}
|
||||
interface IProps extends RouteComponentProps<any> {}
|
||||
type IProps = RouteComponentProps<any>
|
||||
interface IInjectedProps extends IProps {
|
||||
eventStore: EventStore
|
||||
}
|
||||
@@ -105,7 +105,6 @@ export class EventsCreate extends React.Component<IProps, IState> {
|
||||
errors,
|
||||
handleSubmit,
|
||||
}) => {
|
||||
const disabled = invalid || submitting
|
||||
return (
|
||||
<Flex mx={-2} bg={'inherit'} flexWrap="wrap">
|
||||
<Flex bg="inherit" px={2} width={[1, 1, 2 / 3]} mt={4}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { FC } from 'react'
|
||||
import TagDisplay from 'src/components/Tags/TagDisplay/TagDisplay'
|
||||
import { format } from 'date-fns'
|
||||
import { IHowtoDB } from 'src/models/howto.models'
|
||||
@@ -13,9 +13,9 @@ import TimeNeeded from 'src/assets/icons/icon-time-needed.svg'
|
||||
import DifficultyLevel from 'src/assets/icons/icon-difficulty-level.svg'
|
||||
import { Button } from 'src/components/Button'
|
||||
import { IUser } from 'src/models/user.models'
|
||||
import { isAllowToEditContent } from 'src/utils/helpers'
|
||||
import { isAllowToEditContent, emStringToPx } from 'src/utils/helpers'
|
||||
import theme from 'src/themes/styled.theme'
|
||||
import ElWithBeforeIcon from 'src/components/ElWithBeforeIcon'
|
||||
import ArrowIcon from 'src/assets/icons/icon-arrow-select.svg'
|
||||
|
||||
interface IProps {
|
||||
howto: IHowtoDB
|
||||
@@ -38,7 +38,7 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
const lastModifiedDate = format(new Date(howto._modified), 'DD-MM-YYYY')
|
||||
const creationDate = format(new Date(howto._created), 'DD-MM-YYYY')
|
||||
if (lastModifiedDate !== creationDate) {
|
||||
return 'Last edit: ' + format(new Date(howto._modified), 'DD-MM-YYYY')
|
||||
return 'Last edit on ' + format(new Date(howto._modified), 'DD-MM-YYYY')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
@@ -47,6 +47,8 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
public render() {
|
||||
const { howto, loggedInUser } = this.props
|
||||
|
||||
const iconFlexDirection =
|
||||
emStringToPx(theme.breakpoints[0]) > window.innerWidth ? 'column' : 'row'
|
||||
return (
|
||||
<Flex
|
||||
data-cy="how-to-basis"
|
||||
@@ -65,8 +67,14 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
<Flex px={4} py={4} flexDirection={'column'} width={[1, 1, 1 / 2]}>
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Link to={'/how-to/'}>
|
||||
<Button variant={'secondary'} data-cy={'go-back'}>
|
||||
Back
|
||||
<Button variant="subtle" fontSize="14px" data-cy="go-back">
|
||||
<Flex>
|
||||
<Image
|
||||
sx={{ width: '10px', marginRight: '4px', rotate: '90deg' }}
|
||||
src={ArrowIcon}
|
||||
/>
|
||||
<Text>Back</Text>
|
||||
</Flex>
|
||||
</Button>
|
||||
</Link>
|
||||
{/* Check if pin should be moderated */}
|
||||
@@ -96,18 +104,20 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
<Text auxiliary mt={3} mb={2}>
|
||||
By{' '}
|
||||
<Link
|
||||
sx={{
|
||||
textDecoration: 'underline',
|
||||
color: 'inherit',
|
||||
}}
|
||||
to={'/u/' + howto._createdBy}
|
||||
>
|
||||
{howto._createdBy}
|
||||
</Link>{' '}
|
||||
<Text inline> {this.dateCreatedByText(howto)}</Text>
|
||||
<Box mt={3} mb={2}>
|
||||
<Text inline auxiliary>
|
||||
By{' '}
|
||||
<Link
|
||||
sx={{
|
||||
textDecoration: 'underline',
|
||||
color: 'inherit',
|
||||
}}
|
||||
to={'/u/' + howto._createdBy}
|
||||
>
|
||||
{howto._createdBy}
|
||||
</Link>{' '}
|
||||
| Published on {this.dateCreatedByText(howto)}
|
||||
</Text>
|
||||
<Text auxiliary sx={{ color: '#b7b5b5 !important' }} mt={1} mb={2}>
|
||||
{this.dateLastEditText(howto)}
|
||||
</Text>
|
||||
@@ -117,18 +127,21 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
<Text preLine paragraph>
|
||||
{howto.description}
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Flex mt={4} mb={2}>
|
||||
<ElWithBeforeIcon IconUrl={StepsIcon} height="15px">
|
||||
<Flex mt="4">
|
||||
<Flex mr="4" flexDirection={iconFlexDirection}>
|
||||
<Image src={StepsIcon} height="1em" mr="2" mb="2" />
|
||||
{howto.steps.length} steps
|
||||
</ElWithBeforeIcon>
|
||||
<ElWithBeforeIcon IconUrl={TimeNeeded}>
|
||||
</Flex>
|
||||
<Flex mr="4" flexDirection={iconFlexDirection}>
|
||||
<Image src={TimeNeeded} height="1em" mr="2" mb="2" />
|
||||
{howto.time}
|
||||
</ElWithBeforeIcon>
|
||||
<ElWithBeforeIcon IconUrl={DifficultyLevel}>
|
||||
</Flex>
|
||||
<Flex mr="4" flexDirection={iconFlexDirection}>
|
||||
<Image src={DifficultyLevel} height="1em" mr="2" mb="2" />
|
||||
{howto.difficulty_level}
|
||||
</ElWithBeforeIcon>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex mt={4}>
|
||||
{howto.tags &&
|
||||
@@ -156,8 +169,8 @@ export default class HowtoDescription extends React.PureComponent<IProps, any> {
|
||||
<Image
|
||||
sx={{
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
height: '450px',
|
||||
width: 'auto',
|
||||
height: '100%',
|
||||
}}
|
||||
src={howto.cover_image.downloadUrl}
|
||||
alt="how-to cover"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Image, Card, Flex } from 'rebass/styled-components'
|
||||
import { Image, Card, Flex, CardProps } from 'rebass/styled-components'
|
||||
import { IUploadedFileMeta } from 'src/stores/storage'
|
||||
import Lightbox from 'react-image-lightbox'
|
||||
import Text from 'src/components/Text'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface IProps {
|
||||
@@ -16,7 +15,7 @@ interface IState {
|
||||
imgIndex: number
|
||||
}
|
||||
|
||||
const ThumbCard = styled(Card)`
|
||||
const ThumbCard = styled<CardProps & React.ComponentProps<any>>(Card)`
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
transition: 0.2s ease-in-out;
|
||||
|
||||
@@ -57,7 +57,7 @@ export class HowtoList extends React.Component<any, IState> {
|
||||
onChange={tags => this.props.howtoStore.updateSelectedTags(tags)}
|
||||
category="how-to"
|
||||
styleVariant="filter"
|
||||
placeholder="Filter How-tos by tag"
|
||||
placeholder="Filter by tags"
|
||||
relevantTagsItems={filteredHowtos}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
width: 100%;
|
||||
max-width: 307px;
|
||||
margin-top: 5px;
|
||||
border: 2px solid #1b1b1b;
|
||||
border-width: 2px !important;
|
||||
border-color: #1b1b1b !important;
|
||||
border-style: solid !important;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
font-size: 14px;
|
||||
@@ -12,17 +14,53 @@
|
||||
user-select: none !important;
|
||||
}
|
||||
|
||||
.multi-select .dropdown-heading {
|
||||
.multi-select .dropdown .dropdown-heading {
|
||||
height: 40px !important;
|
||||
}
|
||||
|
||||
.multi-select .dropdown-heading-value {
|
||||
.multi-select .dropdown .dropdown-heading .dropdown-heading-value {
|
||||
line-height: 42px !important;
|
||||
}
|
||||
.dropdown-heading {
|
||||
.multi-select .dropdown .dropdown-heading {
|
||||
border-style: unset !important;
|
||||
border-color: black transparent transparent !important;
|
||||
}
|
||||
.dropdown-heading-dropdown-arrow span {
|
||||
border-color: black transparent transparent !important;
|
||||
|
||||
.multi-select .dropdown .dropdown-heading .dropdown-heading-dropdown-arrow span {
|
||||
border: none !important;
|
||||
height: 10px !important;
|
||||
width: 10px !important;
|
||||
display: block !important;
|
||||
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.78 12.9'><g id='Calque_1' data-name='Calque 1'><path d='M12.61,5.23C11.2,6.48,9.81,7.75,8.4,9q-.35-3.9-.32-7.81a1.25,1.25,0,0,0-2.5,0q0,3.81.3,7.61C4.59,7.7,3.32,6.54,2.13,5.31S-.76,5.92.36,7.07a61.87,61.87,0,0,0,6.06,5.5,1.35,1.35,0,0,0,.53.25,1.2,1.2,0,0,0,1.24-.25c2.07-1.85,4.12-3.73,6.19-5.57C15.58,5.92,13.81,4.16,12.61,5.23Z'/></g></svg>") no-repeat !important;
|
||||
|
||||
/* Fixes jumping of arrow- presumably auto-set by the Multiselect component */
|
||||
top: 0px !important;
|
||||
}
|
||||
|
||||
.multi-select .dropdown-content{
|
||||
border-width: 2px !important;
|
||||
border-color: #1b1b1b !important;
|
||||
border-style: solid !important;
|
||||
margin-top: 10px !important;
|
||||
padding-left: 2px !important;
|
||||
padding-right: 2px !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.multi-select {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.multi-select * {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.multi-select .dropdown .dropdown-heading span.dropdown-heading-value span{
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.multi-select .dropdown .dropdown-heading span.dropdown-heading-value:hover{
|
||||
border-width: 1px !important;
|
||||
border-color: rgb(131, 206, 235) !important;
|
||||
border-style: solid !important;
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { Component } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import React from 'react'
|
||||
import MultiSelect from '@khanacademy/react-multi-select'
|
||||
import './GroupingFilter.css'
|
||||
import ElWithBeforeIcon from 'src/components/ElWithBeforeIcon'
|
||||
@@ -35,8 +34,8 @@ const ItemRenderer = ({ checked, option, onClick }) => {
|
||||
IconUrl={option.icon}
|
||||
ticked={checked}
|
||||
contain={true}
|
||||
width="30px"
|
||||
height="30px"
|
||||
width="20px"
|
||||
height="20px"
|
||||
>
|
||||
<h4
|
||||
style={{
|
||||
@@ -106,7 +105,7 @@ class GroupingFilterDesktop extends React.Component<IProps, IState> {
|
||||
return values.length > 0
|
||||
? null
|
||||
: entityType === 'place'
|
||||
? 'Workspaces'
|
||||
? 'All Workspaces'
|
||||
: 'Others'
|
||||
}}
|
||||
hasSelectAll={false}
|
||||
|
||||
@@ -76,7 +76,7 @@ class GroupingFilterMobile extends React.Component<IProps, IState> {
|
||||
return (
|
||||
<Flex flexDirection="column">
|
||||
<Text medium py="10px">
|
||||
{entityType === 'place' ? 'Workspaces' : 'Others'}
|
||||
{entityType === 'place' ? 'All Workspaces' : 'Others'}
|
||||
</Text>
|
||||
{options.map(filter => (
|
||||
<Flex
|
||||
|
||||
@@ -65,23 +65,20 @@ class Controls extends React.Component<IProps, IState> {
|
||||
public render() {
|
||||
const { availableFilters } = this.props
|
||||
const { showFiltersMobile, filtersSelected } = this.state
|
||||
const groupedFilters = availableFilters.reduce(
|
||||
(accumulator, current) => {
|
||||
const { grouping } = current
|
||||
if (accumulator[grouping] === undefined) {
|
||||
accumulator[grouping] = []
|
||||
}
|
||||
accumulator[grouping].push(current)
|
||||
return accumulator
|
||||
},
|
||||
{} as Record<IPinGrouping, Array<IMapGrouping>>,
|
||||
)
|
||||
const groupedFilters = availableFilters.reduce((accumulator, current) => {
|
||||
const { grouping } = current
|
||||
if (accumulator[grouping] === undefined) {
|
||||
accumulator[grouping] = []
|
||||
}
|
||||
accumulator[grouping].push(current)
|
||||
return accumulator
|
||||
}, {} as Record<IPinGrouping, Array<IMapGrouping>>)
|
||||
|
||||
return (
|
||||
<MapFlexBar
|
||||
data-cy="map-controls"
|
||||
ml={['0', '0', '0', '50px']}
|
||||
py={[0, 1, 1]}
|
||||
py={[0, 1, 0]}
|
||||
flexDirection={['column', 'column', 'column', 'row']}
|
||||
alignItems={'center'}
|
||||
onClick={() => {
|
||||
@@ -101,7 +98,7 @@ class Controls extends React.Component<IProps, IState> {
|
||||
onChange={(location: ILocation) => {
|
||||
this.props.onLocationChange(location)
|
||||
}}
|
||||
styleVariant="filter"
|
||||
styleVariant="mapinput"
|
||||
/>
|
||||
</Box>
|
||||
<Flex>
|
||||
|
||||
@@ -112,7 +112,7 @@ export class Popup extends React.Component<IProps> {
|
||||
<Text tags mb={2}>
|
||||
{group ? group.displayName : pin.type}
|
||||
</Text>
|
||||
<Link to={'u/' + name}>
|
||||
<Link to={'/u/' + name}>
|
||||
<Text medium mb={1}>
|
||||
{name}
|
||||
</Text>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.leaflet-tile {
|
||||
filter: saturate(0.3);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ export class ProfileLinkField extends Component<IProps, IState> {
|
||||
// TODO - we might want to add more formatting for cases where,
|
||||
// e.g. only a username is given for a bazar link
|
||||
public formatLink(link: string) {
|
||||
link = link && link.toLowerCase()
|
||||
switch (this.state.linkType) {
|
||||
case 'forum':
|
||||
return ensureExternalUrl(link)
|
||||
|
||||
@@ -31,7 +31,7 @@ const rewriteUrl = (link: IProps['link']) => {
|
||||
|
||||
const ProfileLinkIcon = (link: IProps['link']) => {
|
||||
const { label } = link
|
||||
const icons: { [key in IProps['link']['label']]: JSX.Element } = {
|
||||
const icons: { [key in IProps['link']['label']]: JSX.Element | string } = {
|
||||
forum: IconForum,
|
||||
website: IconWebsite,
|
||||
email: IconEmail,
|
||||
|
||||
@@ -392,9 +392,6 @@ export class UserPage extends React.Component<
|
||||
</UserCategory>
|
||||
|
||||
<Flex alignItems="center">
|
||||
<Heading medium bold color={'black'} my={3} mr={2}>
|
||||
{user.displayName}
|
||||
</Heading>
|
||||
{user.location ? (
|
||||
<FlagIconEvents code={user.location.countryCode} />
|
||||
) : (
|
||||
@@ -402,6 +399,9 @@ export class UserPage extends React.Component<
|
||||
<FlagIconEvents code={user.country.toLowerCase()} />
|
||||
)
|
||||
)}
|
||||
<Heading medium bold color={'black'} my={3} ml={2}>
|
||||
{user.displayName}
|
||||
</Heading>
|
||||
</Flex>
|
||||
{user.about && (
|
||||
<Text
|
||||
|
||||
@@ -61,7 +61,7 @@ export default class Profile extends React.Component<IProps, IState> {
|
||||
/>
|
||||
))}
|
||||
<MenuMobileLink
|
||||
path={location.pathname}
|
||||
path={window.location.pathname}
|
||||
content={'Log out'}
|
||||
style={{ color: theme.colors.silver }}
|
||||
onClick={() => this.injected.userStore.logout()}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { display, DisplayProps } from 'styled-system'
|
||||
import { observer, inject } from 'mobx-react'
|
||||
import { MobileMenuStore } from 'src/stores/MobileMenu/mobilemenu.store'
|
||||
|
||||
const ButtonSign = styled(Button)<DisplayProps>`
|
||||
const ButtonSign = styled(Button as any)<DisplayProps>`
|
||||
${display};
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
@@ -11,7 +11,7 @@ interface ILayoutProps {
|
||||
type IProps = FlexProps & ILayoutProps
|
||||
|
||||
const Main = (props: IProps) => (
|
||||
<Flex {...props} flexDirection="column">
|
||||
<Flex {...(props as any)} flexDirection="column">
|
||||
<Box
|
||||
width="100%"
|
||||
className="main-container"
|
||||
|
||||
@@ -82,8 +82,8 @@ export class LoginForm extends React.Component<IProps, IState> {
|
||||
// track change internally for validation and emit to parent for processing
|
||||
handleChange(e: React.FormEvent<any>) {
|
||||
const nextValues = { ...this.state.formValues }
|
||||
;(nextValues[e.currentTarget.id] = e.currentTarget.value),
|
||||
this.setState({ formValues: nextValues })
|
||||
nextValues[e.currentTarget.id] = e.currentTarget.value
|
||||
this.setState({ formValues: nextValues })
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
|
||||
@@ -76,8 +76,8 @@ export class LoginComponent extends React.Component<IProps, IState> {
|
||||
// (e.g. to retain the email typed when moving to forgot pw)
|
||||
public handleChange = (e: React.FormEvent<any>) => {
|
||||
const nextValues = { ...this.state.formValues }
|
||||
;(nextValues[e.currentTarget.id] = e.currentTarget.value),
|
||||
this.setState({ formValues: nextValues })
|
||||
nextValues[e.currentTarget.id] = e.currentTarget.value
|
||||
this.setState({ formValues: nextValues })
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
||||
@@ -42,8 +42,8 @@ export class ResetPWForm extends React.Component<IProps, IState> {
|
||||
// track change internally for validation and emit to parent for processing
|
||||
handleChange(e: React.FormEvent<any>) {
|
||||
const nextValues = { ...this.state.formValues }
|
||||
;(nextValues[e.currentTarget.id] = e.currentTarget.value),
|
||||
this.setState({ formValues: nextValues })
|
||||
nextValues[e.currentTarget.id] = e.currentTarget.value
|
||||
this.setState({ formValues: nextValues })
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ export class SignUpForm extends React.Component<IProps, IState> {
|
||||
// track change internally for validation and emit to parent for processing
|
||||
handleChange(e: React.FormEvent<any>) {
|
||||
const nextValues = { ...this.state.formValues }
|
||||
;(nextValues[e.currentTarget.id] = e.currentTarget.value),
|
||||
this.setState({ formValues: nextValues })
|
||||
nextValues[e.currentTarget.id] = e.currentTarget.value
|
||||
this.setState({ formValues: nextValues })
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export class SWUpdateNotification extends React.Component<IProps, IState> {
|
||||
key="reload"
|
||||
color="secondary"
|
||||
size="small"
|
||||
onClick={() => location.reload()}
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
Reload
|
||||
</Button>,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* tslint:disable:no-eval */
|
||||
import * as React from 'react'
|
||||
import { Switch, Route, BrowserRouter, Redirect } from 'react-router-dom'
|
||||
import GoogleAnalytics from 'src/components/GoogleAnalytics'
|
||||
@@ -69,7 +68,7 @@ export class Routes extends React.Component<any, IState> {
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
<Switch>
|
||||
<Route exact path="/" render={() => <Redirect to="/how-to" />} />
|
||||
<Route exact path="/" render={() => <Redirect to="/academy" />} />
|
||||
</Switch>
|
||||
</ScrollToTop>
|
||||
</BrowserRouter>
|
||||
|
||||
@@ -49,7 +49,11 @@ export class PrivacyPolicy extends React.PureComponent {
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://policies.google.com/privacy" target="_blank">
|
||||
<a
|
||||
href="https://policies.google.com/privacy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Google Analytics
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -14,7 +14,7 @@ export class TermsPolicy extends React.PureComponent<any> {
|
||||
<a
|
||||
href="https://github.com/ONEARMY/community-platform/blob/master/LICENSE"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
@@ -109,6 +109,7 @@ export class TermsPolicy extends React.PureComponent<any> {
|
||||
<a
|
||||
href="https://app-privacy-policy-generator.firebaseapp.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
platform Privacy Policy Generator
|
||||
</a>
|
||||
|
||||
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -1,4 +1,3 @@
|
||||
// tslint:disable:no-console
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
@@ -21,6 +20,7 @@ const isLocalhost = Boolean(
|
||||
const register = (onUpdateCallback?: () => any) => {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
console.log('public url', process.env.PUBLIC_URL)
|
||||
const publicUrl = new URL(
|
||||
process.env.PUBLIC_URL!,
|
||||
window.location.toString(),
|
||||
|
||||
5
src/setupTests.ts
Normal file
5
src/setupTests.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect'
|
||||
@@ -15,7 +15,6 @@ import { MAP_GROUPINGS } from './maps.groupings'
|
||||
import { generatePins, generatePinDetails } from 'src/mocks/maps.mock'
|
||||
import { IUserPP } from 'src/models/user_pp.models'
|
||||
import { IUploadedFileMeta } from '../storage'
|
||||
import { IUser } from 'src/models/user.models'
|
||||
import {
|
||||
hasAdminRights,
|
||||
needsModeration,
|
||||
@@ -212,7 +211,7 @@ export class MapsStore extends ModuleStore {
|
||||
profilePicUrl: '',
|
||||
shortDescription: '',
|
||||
name: username,
|
||||
profileUrl: `${location.origin}/u/${username}`,
|
||||
profileUrl: `${window.location.origin}/u/${username}`,
|
||||
}
|
||||
}
|
||||
const avatar = getUserAvatar(username)
|
||||
@@ -227,7 +226,7 @@ export class MapsStore extends ModuleStore {
|
||||
profilePicUrl: avatar,
|
||||
shortDescription: u.mapPinDescription ? u.mapPinDescription : '',
|
||||
name: u.userName,
|
||||
profileUrl: `${location.origin}/u/${u.userName}`,
|
||||
profileUrl: `${window.location.origin}/u/${u.userName}`,
|
||||
}
|
||||
}
|
||||
@action
|
||||
|
||||
@@ -99,6 +99,7 @@ export class DexieClient implements AbstractDBClient {
|
||||
***********************************************************************/
|
||||
|
||||
private _init() {
|
||||
const { location } = window
|
||||
this._dbInit(DB_CACHE_NUMBER, DEXIE_SCHEMA)
|
||||
// test open db, catch errors for upgrade version not defined or
|
||||
// idb not supported
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// tslint:disable max-classes-per-file
|
||||
// tslint:disable variable-name
|
||||
import { DexieClient } from './clients/dexie'
|
||||
import { FirestoreClient } from './clients/firestore'
|
||||
import { RealtimeDBClient } from './clients/rtdb'
|
||||
@@ -55,7 +53,7 @@ export class DatabaseV2 implements AbstractDatabase {
|
||||
*/
|
||||
private _getDefaultClients = (): DBClients => {
|
||||
const serverDB = new FirestoreClient()
|
||||
const cacheDB = location.search.includes('no-cache')
|
||||
const cacheDB = window.location.search.includes('no-cache')
|
||||
? serverDB
|
||||
: new DexieClient()
|
||||
return {
|
||||
|
||||
1
src/stores/databaseV2/types/index.d.ts
vendored
1
src/stores/databaseV2/types/index.d.ts
vendored
@@ -1,4 +1,3 @@
|
||||
// tslint:disable interface-name max-classes-per-file
|
||||
/**
|
||||
* The summary section should be brief. On a documentation web site,
|
||||
* it will be shown on a page that lists summaries for many different
|
||||
|
||||
@@ -35,6 +35,7 @@ export type ButtonVariants =
|
||||
| 'disabled'
|
||||
| 'dark'
|
||||
| 'light'
|
||||
| 'subtle'
|
||||
|
||||
const space = [
|
||||
0,
|
||||
@@ -176,6 +177,25 @@ const buttons = {
|
||||
},
|
||||
borderRadius: radii[1] + 'px',
|
||||
},
|
||||
subtle: {
|
||||
fontFamily: '"Varela Round", Arial, sans-serif',
|
||||
border: 'none',
|
||||
color: colors.black,
|
||||
display: 'flex',
|
||||
bg: colors.softblue,
|
||||
transition: '.2s ease-in-out',
|
||||
'&:hover': {
|
||||
bg: colors.white,
|
||||
cursor: 'pointer',
|
||||
},
|
||||
'&[disabled]': {
|
||||
opacity: 0.5,
|
||||
},
|
||||
'&[disabled]:hover': {
|
||||
bg: colors.softblue,
|
||||
},
|
||||
borderRadius: radii[1] + 'px',
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "../tsconfig.json"
|
||||
}
|
||||
@@ -36,7 +36,6 @@ const newerThan = (chosenDate: dateType, compareToDate: dateType) => {
|
||||
***********************************************************************/
|
||||
export default { olderThan, newerThan }
|
||||
|
||||
// tslint:disable variable-name
|
||||
/************************************************************************
|
||||
* Helper Methods
|
||||
***********************************************************************/
|
||||
|
||||
@@ -28,4 +28,4 @@ export const functions = firebase.functions()
|
||||
export const EmailAuthProvider = firebase.auth.EmailAuthProvider
|
||||
|
||||
// want to also expose the default firebase user
|
||||
export interface IFirebaseUser extends firebase.User {}
|
||||
export type IFirebaseUser = firebase.User
|
||||
|
||||
@@ -124,3 +124,6 @@ export const isAllowToPin = (pin: IMapPin, user?: IUser) => {
|
||||
interface IEditableDoc extends DBDoc {
|
||||
_createdBy: string
|
||||
}
|
||||
|
||||
// Convert theme em string to px number
|
||||
export const emStringToPx = width => Number(width.replace('em', '')) * 16
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"baseUrl": "./",
|
||||
"baseUrl": ".",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
@@ -13,14 +13,13 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"jsx": "react",
|
||||
"noImplicitAny": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"experimentalDecorators": true,
|
||||
"typeRoots": ["./node_modules/@types", "./types"],
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"outDir": "./lib"
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src/**/*", "types"],
|
||||
"exclude": [
|
||||
@@ -31,8 +30,6 @@
|
||||
"webpack",
|
||||
"jest",
|
||||
"src/setupTests.ts",
|
||||
"config/*.js",
|
||||
"config/jest/*.js",
|
||||
"functions-cron",
|
||||
"functions",
|
||||
"src/utils/UserMigration",
|
||||
|
||||
10
tsconfig.shared.json
Normal file
10
tsconfig.shared.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"___note___": "This config simply serves to build type declarations to allow import from src into functions",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": false,
|
||||
"outDir": "./build/types",
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
30
tslint.json
30
tslint.json
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"extends": ["tslint-config-prettier"],
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"config/**/*.js",
|
||||
"node_modules/**/*.ts",
|
||||
"coverage/lcov-report/*.js",
|
||||
"*.json",
|
||||
"**/*.json",
|
||||
"src/assets/userReportInit.js"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"ordered-imports": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"no-console": false,
|
||||
"jsx-no-lambda": false,
|
||||
"jsx-boolean-value": false,
|
||||
"no-empty": true,
|
||||
"no-any": false,
|
||||
"prefer-const": true,
|
||||
"member-access": false,
|
||||
"array-type": false,
|
||||
"no-reference": false,
|
||||
"member-ordering": false,
|
||||
"no-empty-interface": false,
|
||||
"variable-name": false
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
1
types/wordpress-hash-node/index.d.ts
vendored
1
types/wordpress-hash-node/index.d.ts
vendored
@@ -1 +0,0 @@
|
||||
declare module 'wordpress-hash-node'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user