🐎 Le build par défaut est conçu pour les navigateurs récents

On compile maintenant pour les navigateurs récents (qui supportent les modules es6.
On ajoute une config de build pour les browser legacy (ie11).

Cela permet :
- De ne plus être dépendant de polyfill.io (qui nous a claqué dans les doigts et a peté la prod)
- D'avoir un JS transpilé plus léger et plus proche du code écrit pour les navigateurs récents
- De pouvoir ajuster le build en fonction du navigateur (on ajoute pas le serviceWorker dans IE par exemple. A l'inverse, on
pourrait multiplier le nombre de bundle pour tirer profit de HTTP2)
pull/494/head
Johan Girod 2019-03-19 18:50:16 +01:00
parent c721a4e161
commit cb1bdb2b5b
24 changed files with 1061 additions and 1332 deletions

View File

@ -1,17 +1,5 @@
{
"presets": [
[
"@babel/env",
{
"targets": {
"browsers": ["last 2 versions", "safari >= 7", "ie 11"]
},
"modules": false
}
],
"@babel/react",
"@babel/flow"
],
"presets": ["@babel/react", "@babel/flow"],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-do-expressions",

View File

@ -57,6 +57,9 @@
}
</style>
<!--<![endif]-->
<% for (var css in htmlWebpackPlugin.files.css) { %>
<link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet" />
<% } %>
<style>
html[data-useragent*='MSIE 10.0'] #outdated-browser {
display: block !important;
@ -197,7 +200,17 @@
>
</h2>
</div>
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
<script
type="module"
src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"
></script>
<script nomodule src="<%= chunk %>.legacy.bundle.js"></script>
<% } %>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=all"></script>
<!-- Add polyfill.io for a very narrow web feature
IntersectionObserver : SAFARI 11 & 12.0 https://caniuse.com/#search=intersectionobserver
-->
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script>
</body>
</html>

View File

@ -19,8 +19,9 @@
"not ie < 11"
],
"dependencies": {
"@babel/polyfill": "^7.4.0",
"@babel/runtime": "^7.3.4",
"@researchgate/react-intersection-observer": "^0.7.3",
"babel-regenerator-runtime": "^6.5.0",
"classnames": "^2.2.5",
"color-convert": "^1.9.2",
"focus-trap-react": "^3.1.2",
@ -56,10 +57,11 @@
"redux-form": "^7.4.2",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"screenfull": "^3.3.2"
"screenfull": "^3.3.2",
"whatwg-fetch": "^3.0.0"
},
"scripts": {
"compile": "webpack --config source/webpack.prod.js",
"compile": "yarn run webpack --config source/webpack.prod.js && yarn run webpack --config source/webpack.prod.legacyBrowser.js",
"stats": "webpack --config source/webpack.prod.js --profile --json > stats.json",
"eslint-check": "eslint --print-config .eslintrc | eslint-config-prettier-check",
"eslint": "LIST=`git diff --cached --name-only --diff-filter=AMR HEAD | grep .*\\.js | grep -v json`; if [ \"$LIST\" ]; then eslint $LIST; fi",
@ -94,8 +96,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.1.0",
"@babel/preset-env": "^7.3.4",
"@babel/preset-flow": "^7.0.0-beta.51",
"@babel/preset-react": "^7.0.0",
"akh": "^3.1.2",
@ -103,7 +104,6 @@
"babel-eslint": "^11.0.0-beta.0",
"babel-loader": "^8.0.2",
"babel-plugin-ramda": "^1.6.3",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-webpack-alias": "^2.1.2",
"chai": "^4.1.2",
"co-request": "^1.0.0",
@ -155,7 +155,6 @@
"webpack-cli": "^3.1.2",
"webpack-dev-middleware": "^3.4.0",
"webpack-hot-middleware": "^2.24.2",
"webpack-pwa-manifest": "^3.6.2",
"workbox-webpack-plugin": "^3.6.1",
"yaml-loader": "^0.5.0"
},

View File

@ -60,6 +60,19 @@ export default class Provider extends PureComponent {
storeEnhancer
)
this.props.onStoreCreated && this.props.onStoreCreated(this.store)
// Remove loader
var css = document.createElement('style')
css.type = 'text/css'
css.innerHTML = `
#js {
opacity: 1 !important;
transform: translateY(0px) !important;
}
#lds-ellipsis {
display: none !important;
}`
document.body.appendChild(css)
}
render() {
return (

View File

@ -1,20 +1,28 @@
import { goBackToSimulation } from 'Actions/actions';
import { ScrollToTop } from 'Components/utils/Scroll';
import { encodeRuleName } from 'Engine/rules';
import { decodeRuleName, findRuleByDottedName, findRulesByName } from 'Engine/rules.js';
import { compose, head, path } from 'ramda';
import React, { Component } from 'react';
import emoji from 'react-easy-emoji';
import { Trans, withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import { Link, Redirect } from 'react-router-dom';
import { flatRulesSelector, noUserInputSelector, situationBranchNameSelector } from 'Selectors/analyseSelectors';
import { capitalise0 } from "../utils";
import Namespace from './rule/Namespace';
import Rule from './rule/Rule';
import './RulePage.css';
import SearchButton from './SearchButton';
import { goBackToSimulation } from 'Actions/actions'
import { ScrollToTop } from 'Components/utils/Scroll'
import { encodeRuleName } from 'Engine/rules'
import {
decodeRuleName,
findRuleByDottedName,
findRulesByName
} from 'Engine/rules.js'
import { compose, head, path } from 'ramda'
import React, { Component } from 'react'
import emoji from 'react-easy-emoji'
import { Trans, withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { withRouter } from 'react-router'
import { Link, Redirect } from 'react-router-dom'
import {
flatRulesSelector,
noUserInputSelector,
situationBranchNameSelector
} from 'Selectors/analyseSelectors'
import { capitalise0 } from '../utils'
import Namespace from './rule/Namespace'
import Rule from './rule/Rule'
import './RulePage.css'
import SearchButton from './SearchButton'
export default compose(
connect(state => ({
@ -43,7 +51,13 @@ export default compose(
if (rules.find(({ ns }) => ns == null))
return this.renderRule(decodedRuleName)
if (rules.length > 1)
return <DisambiguateRuleQuery rules={rules} flatRules={flatRules} name={name}/>
return (
<DisambiguateRuleQuery
rules={rules}
flatRules={flatRules}
name={name}
/>
)
let dottedName = head(rules).dottedName
return this.renderRule(dottedName)
}
@ -77,9 +91,10 @@ const BackToSimulation = compose(
render() {
let { goBackToSimulation } = this.props
return (
<button className="ui__ link-button" onClick={goBackToSimulation}>
{emoji('⬅️')}
<Trans i18nKey="back">Reprendre la simulation</Trans>
<button
className="ui__ simple small button"
onClick={goBackToSimulation}>
{emoji('⬅️')} <Trans i18nKey="back">Reprendre la simulation</Trans>
</button>
)
}

View File

@ -49,10 +49,9 @@ export default compose(
</Overlay>
) : (
<button
className="ui__ link-button"
className="ui__ simple small button"
onClick={() => this.setState({ visible: true })}>
{emoji('🔍')}
<Trans>Rechercher</Trans>
{emoji('🔍')} <Trans>Rechercher</Trans>
</button>
)
}

View File

@ -1,4 +1,3 @@
import { T } from 'Components'
import React, { useState } from 'react'
import emoji from 'react-easy-emoji'

View File

@ -53,11 +53,16 @@ export default compose(
{this.state.displayAnswers && (
<Answers onClose={() => this.setState({ displayAnswers: false })} />
)}
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'baseline'
}}>
{arePreviousAnswers ? (
<button
style={{ marginRight: '1em' }}
className="ui__ small button "
className="ui__ small button "
onClick={() => this.setState({ displayAnswers: true })}>
<T>Voir mes réponses</T>
</button>

View File

@ -73,11 +73,3 @@ span.ui__.enumeration:not(:last-of-type)::after {
margin: 0 0.3rem;
display: inline-block;
}
#js {
opacity: 1;
transform: translateY(0px);
}
#lds-ellipsis {
display: none;
}

View File

@ -1,61 +1,20 @@
import React from 'react'
import { Parser } from 'nearley'
import Grammar from './grammar.ne'
import {
contains,
propEq,
curry,
cond,
equals,
divide,
multiply,
map,
intersection,
keys,
propOr,
always,
head,
gte,
lte,
lt,
gt,
add,
subtract
} from 'ramda'
import { evaluateNode, rewriteNode, makeJsx, mergeMissing } from './evaluation'
import { Node } from './mecanismViews/common'
import {
treatVariable,
treatNegatedVariable,
treatVariableTransforms
} from './treatVariable'
import { treat } from './traverse'
import knownMecanisms from './known-mecanisms.yaml'
// This should be the new way to implement mecanisms
// In a specific file
// TODO import them automatically
// TODO convert the legacy functions to new files
import barème from 'Engine/mecanisms/barème.js'
import barème from 'Engine/mecanisms/barème.js';
import { Parser } from 'nearley';
import { add, always, cond, contains, curry, divide, equals, gt, gte, head, intersection, keys, lt, lte, map, multiply, propEq, propOr, subtract } from 'ramda';
import React from 'react';
import { evaluateNode, makeJsx, mergeMissing, rewriteNode } from './evaluation';
import Grammar from './grammar.ne';
import knownMecanisms from './known-mecanisms.yaml';
import { mecanismAllOf, mecanismComplement, mecanismContinuousScale, mecanismError, mecanismInversion, mecanismLinearScale, mecanismMax, mecanismMin, mecanismNumericalSwitch, mecanismOneOf, mecanismProduct, mecanismReduction, mecanismSelection, mecanismSum, mecanismSynchronisation, mecanismVariations } from './mecanisms';
import { Node } from './mecanismViews/common';
import { treat } from './traverse';
import { treatNegatedVariable, treatVariable, treatVariableTransforms } from './treatVariable';
import {
mecanismOneOf,
mecanismAllOf,
mecanismNumericalSwitch,
mecanismSum,
mecanismProduct,
mecanismLinearScale,
mecanismContinuousScale,
mecanismMax,
mecanismMin,
mecanismError,
mecanismComplement,
mecanismSelection,
mecanismInversion,
mecanismReduction,
mecanismVariations,
mecanismSynchronisation
} from './mecanisms'
let nearley = () => new Parser(Grammar.ParserRules, Grammar.ParserStart)
export let treatString = (rules, rule) => rawNode => {

View File

@ -596,3 +596,5 @@ newsletter:
description2: 'Register to our <1>monthly newsletter</1> by leaving your email:'
S'inscrire: Register
simulationWarning: This is an estimate based on <2>purely theoretical data</2> on turnover, charges and the single tax base without children, excluding any other income, which <5>cannot be the responsibility of the social security bodies concerned with regard to the actual declarations and calculations.</5>
Renseigner mon entreprise: Find my company
Simulations personnalisées: Customized simulations

View File

@ -1,3 +1,4 @@
import '@babel/polyfill'
import 'iframe-resizer'
import React from 'react'
import { render } from 'react-dom'

View File

@ -1,3 +1,4 @@
import Route404 from 'Components/Route404'
import RulePage from 'Components/RulePage'
import TrackPageView from 'Components/utils/TrackPageView'
import withSitePaths from 'Components/utils/withSitePaths'
@ -6,7 +7,7 @@ import { compose } from 'ramda'
import createRavenMiddleware from 'raven-for-redux'
import Raven from 'raven-js'
import React, { Component } from 'react'
import {Helmet} from 'react-helmet'
import { Helmet } from 'react-helmet'
import { withTranslation } from 'react-i18next'
import { Route, Switch } from 'react-router-dom'
import 'Ui/index.css'
@ -24,7 +25,6 @@ import Landing from './pages/Landing'
import Sitemap from './pages/Sitemap'
import SocialSecurity from './pages/SocialSecurity'
import { constructLocalizedSitePath } from './sitePaths'
import Route404 from 'Components/Route404'
if (process.env.NODE_ENV === 'production') {
Raven.config(

View File

@ -1,3 +1,4 @@
import '@babel/polyfill'
import React from 'react'
import { render } from 'react-dom'
import App from './App'

View File

@ -1,7 +1,7 @@
import '@babel/polyfill'
import React from 'react'
import { render } from 'react-dom'
import App from './App'
let anchor = document.querySelector('#js')
render(<App language="fr" basename="mon-entreprise" />, anchor)

View File

@ -1,18 +1,18 @@
/* @flow */
import { Component, React, T } from 'Components';
import { ScrollToTop } from 'Components/utils/Scroll';
import withLanguage from 'Components/utils/withLanguage';
import withSitePaths from 'Components/utils/withSitePaths';
import { compose } from 'ramda';
import emoji from 'react-easy-emoji';
import {Helmet} from 'react-helmet';
import { withTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { régimeSelector } from 'Selectors/companyStatusSelectors';
import * as Animate from 'Ui/animate';
import Video from './Video';
import { Component, React, T } from 'Components'
import { ScrollToTop } from 'Components/utils/Scroll'
import withLanguage from 'Components/utils/withLanguage'
import withSitePaths from 'Components/utils/withSitePaths'
import { compose } from 'ramda'
import emoji from 'react-easy-emoji'
import { Helmet } from 'react-helmet'
import { withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { régimeSelector } from 'Selectors/companyStatusSelectors'
import * as Animate from 'Ui/animate'
import Video from './Video'
import type { Match, Location } from 'react-router'
import type { TFunction } from 'react-i18next'
@ -50,29 +50,32 @@ class SocialSecurity extends Component<Props, {}> {
<strong>bien-être général de la population</strong>.
</p>
</T>
{showFindYourCompanyLink && (<>
<h2>Simulations personnalisées</h2>
<p>
<T k="sécu.entrepriseCrée">
Si vous possédez déjà une entreprise, nous pouvons{' '}
<strong>automatiquement personnaliser</strong> vos simulations
à votre situation.
</T>
</p>
<div style={{textAlign: 'center'}}>
<Link to={sitePaths.entreprise.trouver} className="ui__ button">
Renseigner mon entreprise
{showFindYourCompanyLink && (
<>
<h2>
<T>Simulations personnalisées</T>
</h2>
<p>
<T k="sécu.entrepriseCrée">
Si vous possédez déjà une entreprise, nous pouvons{' '}
<strong>automatiquement personnaliser</strong> vos
simulations à votre situation.
</T>
</p>
<div style={{ textAlign: 'center' }}>
<Link
to={sitePaths.entreprise.trouver}
className="ui__ button plain">
<T>Renseigner mon entreprise</T>
</Link>
</div>
</div>
</>
)}
{!['mycompanyinfrance.fr', 'mon-entreprise.fr'].includes(
window.location.hostname
) ? (
<>
<h2 >
Que souhaitez-vous estimer ?
</h2>
<h2>Que souhaitez-vous estimer ?</h2>
<Link
className="landing__choice "
to={

View File

@ -3,9 +3,9 @@ const HTMLPlugin = require('html-webpack-plugin')
const CopyPlugin = require('copy-webpack-plugin')
const { EnvironmentPlugin } = require('webpack')
const path = require('path')
const { universal, web } = require('./webpack.commonLoaders.js')
module.exports = {
resolve: {
alias: {
Engine: path.resolve('source/engine/'),
@ -20,18 +20,15 @@ module.exports = {
}
},
entry: {
embauche: ['./source/sites/embauche.gouv.fr/entry.js'],
'mon-entreprise': ['babel-regenerator-runtime', './source/sites/mycompanyinfrance.fr/entry.fr.js'],
infrance: ['babel-regenerator-runtime', './source/sites/mycompanyinfrance.fr/entry.en.js'],
embauche: './source/sites/embauche.gouv.fr/entry.js',
'mon-entreprise': './source/sites/mycompanyinfrance.fr/entry.fr.js',
infrance: './source/sites/mycompanyinfrance.fr/entry.en.js',
// To not introduce breaking into the iframe integration, we serve simulateur.js from a 'dist' subdirectory
'dist/simulateur': ['./source/sites/embauche.gouv.fr/iframe-script.js'],
'dist/simulateur': './source/sites/embauche.gouv.fr/iframe-script.js',
},
output: {
path: path.resolve('./dist/')
},
module: {
rules: [...web, ...universal]
path: path.resolve('./dist/'),
},
plugins: [
new EnvironmentPlugin({
@ -40,6 +37,7 @@ module.exports = {
}),
new HTMLPlugin({
template: 'index.html',
inject: false,
chunks: ['infrance'],
title:
'My company in France: A step-by-step guide to start a business in France',
@ -49,6 +47,7 @@ module.exports = {
}),
new HTMLPlugin({
template: 'index.html',
inject: false,
chunks: ['embauche'],
title: "Simulateur d'embauche 🤝",
description:
@ -57,6 +56,7 @@ module.exports = {
}),
new HTMLPlugin({
template: 'index.html',
inject: false,
chunks: ['mon-entreprise'],
title:
"Mon-entreprise.fr : Le guide officiel du créateur d'entreprise",

View File

@ -1,23 +1,45 @@
module.exports = {
web: [
module.exports.styleLoader = styleLoader => ({
test: /\.css$/,
use: [
{ loader: styleLoader },
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 1
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 1
}
},
{
loader: 'postcss-loader'
}
]
})
module.exports.commonLoaders = ({ legacy = false } = {}) => {
const babelLoader = {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: !legacy
? {
esmodules: true
}
: {
esmodules: false,
browsers: ['ie 11']
},
useBuiltIns: 'entry'
}
},
{
loader: 'postcss-loader'
}
]
]
}
],
universal: [
}
return [
{ test: /\.js$/, loader: babelLoader, exclude: /node_modules|dist/ },
{
test: /\.(jpe?g|png|svg)$/,
use: {
@ -29,16 +51,12 @@ module.exports = {
},
{
test: /\.yaml$/,
loader: 'json-loader!yaml-loader'
},
{
test: /\.js$/,
exclude: /node_modules|dist/,
loader: 'babel-loader'
use: ['json-loader', 'yaml-loader']
},
{
test: /\.ne$/,
loader: 'babel-loader!nearley-loader'
use: [babelLoader, 'nearley-loader']
}
]
}

View File

@ -1,11 +1,15 @@
const { map, concat } = require('ramda')
const { map } = require('ramda')
const webpack = require('webpack')
const common = require('./webpack.common.js')
const { commonLoaders, styleLoader } = require('./webpack.commonLoaders')
module.exports = {
...common,
module: {
rules: [...commonLoaders(), styleLoader('style-loader')]
},
mode: 'development',
entry: map(concat(['webpack-hot-middleware/client']), common.entry),
entry: map(entry => ['webpack-hot-middleware/client', entry], common.entry),
plugins: [
...common.plugins,
new webpack.EnvironmentPlugin({ NODE_ENV: 'development' }),

View File

@ -1,6 +1,6 @@
const common = require('./webpack.common.js')
const { universal } = require('./webpack.commonLoaders.js')
const path = require('path')
const { commonLoaders, styleLoader } = require('./webpack.commonLoaders')
module.exports = {
resolve: common.resolve,
@ -15,24 +15,6 @@ module.exports = {
globalObject: "(typeof window !== 'undefined' ? window : this)"
},
module: {
rules: [
...universal,
{
test: /\.css$/,
use: [
'isomorphic-style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 1
}
},
{
loader: 'postcss-loader'
}
]
}
]
rules: [...commonLoaders(), styleLoader('isomorphic-style-loader')]
}
}

View File

@ -1,10 +1,12 @@
const common = require('./webpack.common.js')
const WorkboxPlugin = require('workbox-webpack-plugin')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const WorkboxPlugin = require('workbox-webpack-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const path = require('path')
const cheerio = require('cheerio')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { commonLoaders, styleLoader } = require('./webpack.commonLoaders')
const prerenderConfig = () => ({
staticDir: path.resolve('dist'),
@ -32,27 +34,28 @@ const prerenderConfig = () => ({
`)
// Remove piwik script
$('script[src$="stats.data.gouv.fr/piwik.js"]').remove()
context.html = $.html()
return context
}
})
// Replace style-loader with MiniCssExtractPlugin.loader
common.module.rules
.find(rule => rule.test.test('a.css'))
.use.find(loader => loader.loader === 'style-loader').loader =
MiniCssExtractPlugin.loader
module.exports = {
...common,
module: {
rules: [...commonLoaders(), styleLoader(MiniCssExtractPlugin.loader)]
},
output: {
...common.output,
filename: ({ chunk }) => {
return chunk.name === 'dist/simulateur'
? '[name].js'
: '[name].[contenthash].bundle.js'
}
},
mode: 'production',
devtool: 'source-map',
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
}),
...common.plugins,
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
@ -61,7 +64,7 @@ module.exports = {
runtimeCaching: [
{
urlPattern: new RegExp(
'https://fonts.(?:googleapis|gstatic).com/(.*)|https://cdn.polyfill.io/v2/polyfill.min.js'
'https://fonts.(?:googleapis|gstatic).com/(.*)'
),
handler: 'cacheFirst',
options: {
@ -85,6 +88,12 @@ module.exports = {
/^\/sitemap\.infrance\.en\.txt$/
]
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '[name].[hash].css',
chunkFilename: '[id].[hash].css'
}),
new PrerenderSPAPlugin({
...prerenderConfig(),
outputDir: path.resolve('dist', 'prerender', 'infrance'),

View File

@ -0,0 +1,21 @@
const { map } = require('ramda')
const prod = require('./webpack.prod.js')
const { commonLoaders, styleLoader } = require('./webpack.commonLoaders')
const { EnvironmentPlugin } = require('webpack')
module.exports = {
...prod,
entry: map(entry => ['whatwg-fetch', entry], prod.entry),
output: {
filename: '[name].legacy.bundle.js'
},
module: {
rules: [...commonLoaders({ legacy: true }), styleLoader('style-loader')]
},
plugins: [
new EnvironmentPlugin({
EN_SITE: '/infrance${path}',
FR_SITE: '/mon-entreprise${path}'
})
]
}

View File

@ -1,8 +1,12 @@
var webpack = require('webpack'),
common = require('./webpack.common.js')
common = require('./webpack.common.js'),
{ commonLoaders, styleLoader } = require('./webpack.commonLoaders')
module.exports = {
...common,
module: {
rules: [...commonLoaders(), styleLoader('style-loader')]
},
mode: 'development',
plugins: [new webpack.EnvironmentPlugin({ NODE_ENV: 'development' })]
}

1948
yarn.lock

File diff suppressed because it is too large Load Diff